blob: d5a1785ebb1c4b78caecf3b41a441f06532e0938 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.debug.internal.ui.views.console;
import org.eclipse.debug.internal.ui.DebugUIPlugin;
import org.eclipse.debug.internal.ui.IInternalDebugUIConstants;
import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants;
import org.eclipse.debug.ui.console.IConsoleColorProvider;
import org.eclipse.debug.ui.console.IConsoleHyperlink;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.BadPositionCategoryException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IDocumentPartitioner;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.LineStyleEvent;
import org.eclipse.swt.custom.LineStyleListener;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
public class ConsoleViewer extends TextViewer implements IPropertyChangeListener, MouseTrackListener, MouseMoveListener, MouseListener, PaintListener, LineStyleListener, Listener {
/**
* Hand cursor
*/
private Cursor fHandCursor;
/**
* Text cursor
*/
private Cursor fTextCursor;
/**
* The active hyperlink, or <code>null</code>
*/
private IConsoleHyperlink fHyperLink = null;
protected InternalDocumentListener fInternalDocumentListener= new InternalDocumentListener();
/**
* Whether the console scrolls as output is appended.
*/
private boolean fAutoScroll = true;
/**
* Internal document listener.
*/
class InternalDocumentListener implements IDocumentListener {
/**
* @see IDocumentListener#documentAboutToBeChanged(DocumentEvent)
*/
public void documentAboutToBeChanged(DocumentEvent e) {
}
/**
* @see IDocumentListener#documentChanged(DocumentEvent)
*/
public void documentChanged(DocumentEvent e) {
ConsoleDocument doc= (ConsoleDocument)getDocument();
if (doc == null) {
getTextWidget().setEditable(false);
return;
}
getTextWidget().setEditable(!doc.isReadOnly());
revealEndOfDocument();
}
}
/**
* Creates a new console viewer and adds verification checking
* to only allow text modification if the text is being modified
* in the editable portion of the underlying document.
*
* @see org.eclipse.swt.events.VerifyListener
*/
public ConsoleViewer(Composite parent) {
super(parent, getSWTStyles());
getTextWidget().setDoubleClickEnabled(true);
DebugUIPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this);
JFaceResources.getFontRegistry().addListener(this);
getTextWidget().setFont(JFaceResources.getFont(IInternalDebugUIConstants.CONSOLE_FONT));
getTextWidget().addMouseTrackListener(this);
getTextWidget().addPaintListener(this);
getTextWidget().addLineStyleListener(this);
getTextWidget().addListener(SWT.KeyUp, this);
}
/**
* Returns the SWT style flags used when instantiating this viewer
*/
private static int getSWTStyles() {
int styles= SWT.H_SCROLL | SWT.V_SCROLL;
return styles;
}
/**
* Reveals (makes visible) the end of the current document
*/
protected void revealEndOfDocument() {
if (isAutoScroll()) {
IDocument doc = getDocument();
int lines = doc.getNumberOfLines();
try {
// lines are 0-based
int lineStartOffset = doc.getLineOffset(lines - 1);
StyledText widget= getTextWidget();
if (lineStartOffset > 0) {
widget.setCaretOffset(lineStartOffset);
widget.showSelection();
}
int lineEndOffset = lineStartOffset + doc.getLineLength(lines - 1);
if (lineEndOffset > 0) {
widget.setCaretOffset(lineEndOffset);
}
} catch (BadLocationException e) {
}
}
}
/**
* @see ITextViewer#setDocument(IDocument)
*/
public void setDocument(IDocument doc) {
IDocument oldDoc= getDocument();
IDocument document= doc;
if (oldDoc == null && document == null) {
return;
}
if (oldDoc != null) {
oldDoc.removeDocumentListener(fInternalDocumentListener);
if (oldDoc.equals(document)) {
document.addDocumentListener(fInternalDocumentListener);
return;
}
}
super.setDocument(document);
if (document != null) {
revealEndOfDocument();
document.addDocumentListener(fInternalDocumentListener);
}
}
/**
* @see IFindReplaceTarget#canPerformFind()
*/
protected boolean canPerformFind() {
return (getTextWidget() != null && getVisibleDocument() != null && getVisibleDocument().getLength() > 0);
}
/**
* @see IPropertyChangeListener#propertyChange(PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent event) {
String propertyName= event.getProperty();
if (propertyName.equals(IDebugPreferenceConstants.CONSOLE_SYS_IN_RGB) ||
propertyName.equals(IDebugPreferenceConstants.CONSOLE_SYS_OUT_RGB) ||
propertyName.equals(IDebugPreferenceConstants.CONSOLE_SYS_ERR_RGB)) {
getTextWidget().redraw();
} else if (propertyName.equals(IInternalDebugUIConstants.CONSOLE_FONT)) {
getTextWidget().setFont(JFaceResources.getFont(IInternalDebugUIConstants.CONSOLE_FONT));
}
}
/**
* Dispose this viewer and resources
*/
public void dispose() {
Control control = getTextWidget();
if (control != null) {
control.removeMouseTrackListener(this);
control.removePaintListener(this);
}
if (fHandCursor != null) {
fHandCursor.dispose();
}
if (fTextCursor != null) {
fTextCursor.dispose();
}
DebugUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this);
JFaceResources.getFontRegistry().removeListener(this);
}
/**
* Only allow text to be typed at the end of the document.
*
* @see org.eclipse.swt.events.VerifyListener#verifyText(org.eclipse.swt.events.VerifyEvent)
*/
protected void handleVerifyEvent(VerifyEvent e) {
ConsoleDocument doc= (ConsoleDocument)getDocument();
if (doc != null) {
if (doc.isReadOnly()) {
e.doit = false;
return;
}
IDocumentPartitioner partitioner = doc.getDocumentPartitioner();
if (partitioner != null) {
int length = doc.getLength();
ITypedRegion[] partitions = partitioner.computePartitioning(length, 0);
if (partitions.length == 0) {
} else {
ITypedRegion partition = partitions[partitions.length - 1];
if (partition.getType().equals(InputPartition.INPUT_PARTITION_TYPE)) {
// > 1 char in the input buffer
e.doit = (e.start >= partition.getOffset()) && (e.end <= (partition.getLength() + partition.getOffset()));
} else {
// first character in the input buffer
e.doit = length == e.start;
}
}
}
}
}
/**
* @see org.eclipse.swt.events.MouseTrackListener#mouseEnter(org.eclipse.swt.events.MouseEvent)
*/
public void mouseEnter(MouseEvent e) {
getTextWidget().addMouseMoveListener(this);
}
/**
* @see org.eclipse.swt.events.MouseTrackListener#mouseExit(org.eclipse.swt.events.MouseEvent)
*/
public void mouseExit(MouseEvent e) {
getTextWidget().removeMouseMoveListener(this);
if (fHyperLink != null) {
linkExited(fHyperLink);
}
}
/**
* @see org.eclipse.swt.events.MouseTrackListener#mouseHover(org.eclipse.swt.events.MouseEvent)
*/
public void mouseHover(MouseEvent e) {
}
/**
* @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
*/
public void mouseMove(MouseEvent e) {
int offset = -1;
try {
Point p = new Point(e.x, e.y);
offset = getTextWidget().getOffsetAtLocation(p);
} catch (IllegalArgumentException ex) {
// out of the document range
}
updateLinks(offset);
}
public IConsoleHyperlink getHyperlink(int offset) {
if (offset >= 0 && getDocument() != null) {
Position[] positions = null;
try {
positions = getDocument().getPositions(HyperlinkPosition.HYPER_LINK_CATEGORY);
} catch (BadPositionCategoryException ex) {
// no links have been added
return null;
}
for (int i = 0; i < positions.length; i++) {
Position position = positions[i];
if (offset >= position.getOffset() && offset <= (position.getOffset() + position.getLength())) {
return ((HyperlinkPosition)position).getHyperLink();
}
}
}
return null;
}
protected void linkEntered(IConsoleHyperlink link) {
Control control = getTextWidget();
control.setRedraw(false);
if (fHyperLink != null) {
linkExited(fHyperLink);
}
fHyperLink = link;
fHyperLink.linkEntered();
control.setCursor(getHandCursor());
control.setRedraw(true);
control.redraw();
control.addMouseListener(this);
}
protected void linkExited(IConsoleHyperlink link) {
link.linkExited();
fHyperLink = null;
Control control = getTextWidget();
control.setCursor(getTextCursor());
control.redraw();
control.removeMouseListener(this);
}
/**
* @see org.eclipse.swt.events.PaintListener#paintControl(org.eclipse.swt.events.PaintEvent)
*/
public void paintControl(PaintEvent e) {
if (fHyperLink != null) {
IDocument doc = getDocument();
if (doc == null) {
return;
}
ConsoleDocumentPartitioner partitioner = (ConsoleDocumentPartitioner)doc.getDocumentPartitioner();
if (partitioner == null) {
return;
}
IRegion linkRegion = partitioner.getRegion(fHyperLink);
if (linkRegion != null) {
int start = linkRegion.getOffset();
int end = start + linkRegion.getLength();
IConsoleColorProvider contentProvider = partitioner.getContentProvider();
try {
ITypedRegion partition = doc.getPartition(start);
Color fontColor = e.gc.getForeground();
if (partition instanceof StreamPartition) {
StreamPartition streamPartition = (StreamPartition)partition;
fontColor = contentProvider.getColor(streamPartition.getStreamIdentifier());
}
int startLine = doc.getLineOfOffset(start);
int endLine = doc.getLineOfOffset(end);
for (int i = startLine; i <= endLine; i++) {
IRegion lineRegion = doc.getLineInformation(i);
int lineStart = lineRegion.getOffset();
int lineEnd = lineStart + lineRegion.getLength();
Color color = e.gc.getForeground();
e.gc.setForeground(fontColor);
if (lineStart < end) {
lineStart = Math.max(start, lineStart);
lineEnd = Math.min(end, lineEnd);
Point p1 = getTextWidget().getLocationAtOffset(lineStart);
Point p2 = getTextWidget().getLocationAtOffset(lineEnd);
FontMetrics metrics = e.gc.getFontMetrics();
int height = metrics.getHeight();
e.gc.drawLine(p1.x, p1.y + height, p2.x, p2.y + height);
}
e.gc.setForeground(color);
}
} catch (BadLocationException ex) {
}
}
}
}
protected Cursor getHandCursor() {
if (fHandCursor == null) {
fHandCursor = new Cursor(DebugUIPlugin.getStandardDisplay(), SWT.CURSOR_HAND);
}
return fHandCursor;
}
protected Cursor getTextCursor() {
if (fTextCursor == null) {
fTextCursor = new Cursor(DebugUIPlugin.getStandardDisplay(), SWT.CURSOR_IBEAM);
}
return fTextCursor;
}
/**
* @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
*/
public void mouseDoubleClick(MouseEvent e) {
}
/**
* @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
*/
public void mouseDown(MouseEvent e) {
}
/**
* @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
*/
public void mouseUp(MouseEvent e) {
if (fHyperLink != null) {
String selection = getTextWidget().getSelectionText();
if (selection.length() <= 0) {
if (e.button == 1) {
fHyperLink.linkActivated();
}
}
}
}
/**
* @see org.eclipse.swt.custom.LineStyleListener#lineGetStyle(org.eclipse.swt.custom.LineStyleEvent)
*/
public void lineGetStyle(LineStyleEvent event) {
IDocument document = getDocument();
if (document != null) {
ConsoleDocumentPartitioner partitioner = (ConsoleDocumentPartitioner)document.getDocumentPartitioner();
if (partitioner != null) {
IConsoleColorProvider contentProvider = partitioner.getContentProvider();
ITypedRegion[] regions = partitioner.computePartitioning(event.lineOffset, event.lineOffset + event.lineText.length());
StyleRange[] styles = new StyleRange[regions.length];
for (int i = 0; i < regions.length; i++) {
StreamPartition partition = (StreamPartition)regions[i];
Color color = contentProvider.getColor(partition.getStreamIdentifier());
styles[i] = new StyleRange(partition.getOffset(), partition.getLength(), color, null);
}
event.styles = styles;
}
}
}
/**
* Sets whether this viewer should auto-scroll as output is appended to the
* document.
*
* @param scroll
*/
public void setAutoScroll(boolean scroll) {
fAutoScroll = scroll;
}
/**
* Returns whether this viewer should auto-scroll as output is appended to
* the document.
*/
public boolean isAutoScroll() {
return fAutoScroll;
}
/**
* On KeyUp events, see if we need to enter/exit a link.
*
* @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
*/
public void handleEvent(Event event) {
int offset = getTextWidget().getCaretOffset();
updateLinks(offset);
}
/**
* The cursor has just be moved to the given offset, the mouse has
* hovered over the given offset. Update link rendering.
*
* @param offset
*/
protected void updateLinks(int offset) {
if (offset >= 0) {
IConsoleHyperlink link = getHyperlink(offset);
if (link != null) {
if (link.equals(fHyperLink)) {
return;
} else {
linkEntered(link);
return;
}
}
}
if (fHyperLink != null) {
linkExited(fHyperLink);
}
}
}