blob: dae6e8f2886a48aefa9f524e299253422d18ae46 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2009, 2019 Stephan Wahlbrink and others.
#
# This program and the accompanying materials are made available under the
# terms of the Eclipse Public License 2.0 which is available at
# https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.internal.nico.ui.console;
import org.eclipse.jface.text.AbstractDocument;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentExtension4;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.console.TextConsole;
import org.eclipse.ui.console.TextConsoleViewer;
import org.eclipse.statet.ecommons.ui.util.UIAccess;
public class OutputViewer extends TextConsoleViewer {
/**
* will always scroll with output if value is true.
*/
private boolean autoScroll= true;
private IDocumentListener documentListener;
public OutputViewer(final Composite parent, final TextConsole console) {
super(parent, console);
setEditable(false);
}
public boolean isAutoScroll() {
return this.autoScroll;
}
public void setAutoScroll(final boolean scroll) {
this.autoScroll= scroll;
}
@Override
public void setDocument(final IDocument document) {
final IDocument oldDocument= getDocument();
if (oldDocument != null) {
oldDocument.removeDocumentListener(getDocumentListener());
}
super.setDocument(document);
if (document != null) {
document.addDocumentListener(getDocumentListener());
}
}
private IDocumentListener getDocumentListener() {
if (this.documentListener == null) {
this.documentListener= new IDocumentListener() {
@Override
public void documentAboutToBeChanged(final DocumentEvent event) {
}
@Override
public void documentChanged(final DocumentEvent event) {
if (OutputViewer.this.autoScroll) {
revealEndOfDocument();
}
}
};
}
return this.documentListener;
}
@Override
public void revealEndOfDocument() {
final Display display= UIAccess.getDisplay();
display.asyncExec(new Runnable() {
@Override
public void run() {
final StyledText textWidget= getTextWidget();
if (!UIAccess.isOkToUse(textWidget)) {
return;
}
final AbstractDocument document= (AbstractDocument) getDocument();
final long timestamp= document.getModificationStamp();
final int lineCount= textWidget.getLineCount();
final int lineToShow= ((lineCount > 1 &&
textWidget.getCharCount() == textWidget.getOffsetAtLine(lineCount - 1)) ?
(lineCount - 1) : (lineCount));
final int visiblePixel= textWidget.getClientArea().height;
final int linePixel= textWidget.getLineHeight();
final int topPixel= (linePixel * (lineToShow - 1)) - visiblePixel + 2;
if (topPixel + linePixel > 0) {
if (topPixel < 0) {
textWidget.setTopPixel(
topPixel + linePixel );
}
else {
final int[] move= new int[] {
topPixel,
topPixel + linePixel - 2,
topPixel + linePixel - 1,
topPixel + linePixel
};
textWidget.setTopPixel(move[0]);
final int[] state= new int[] { 1 };
display.timerExec(75, new Runnable() {
@Override
public void run() {
int i= state[0];
if (!UIAccess.isOkToUse(textWidget)
|| timestamp != ((IDocumentExtension4) getDocument()).getModificationStamp()
|| move[i-1] != textWidget.getTopPixel()) {
return;
}
textWidget.setTopPixel(move[i++]);
if (i < move.length) {
state[0]= i;
display.timerExec(25, this);
}
}
});
}
}
}
});
}
}