/*******************************************************************************
* Copyright (c) 2002, 2010 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* QNX Software Systems - Initial API and implementation
* Dmitry Kozlov (CodeSourcery) - Build error highlighting and navigation
*******************************************************************************/
package org.eclipse.cdt.internal.ui.buildconsole;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.ITypedRegion;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.LineBackgroundEvent;
import org.eclipse.swt.custom.LineBackgroundListener;
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.MouseTrackListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.cdt.ui.CUIPlugin;
public class BuildConsoleViewer extends TextViewer
implements LineStyleListener,
LineBackgroundListener,
MouseTrackListener,
MouseListener {
protected InternalDocumentListener fInternalDocumentListener = new InternalDocumentListener();
/**
* Whether the console scrolls as output is appended.
*/
private boolean fAutoScroll = true;
/**
* Internal document listener.
*/
class InternalDocumentListener implements IDocumentListener {
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
*/
public void documentAboutToBeChanged(DocumentEvent e) {
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
*/
public void documentChanged(DocumentEvent e) {
revealEndOfDocument();
}
}
/**
* 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;
}
/**
* 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 BuildConsoleViewer(Composite parent) {
super(parent, getSWTStyles());
StyledText styledText = getTextWidget();
styledText.addLineStyleListener(this);
styledText.addLineBackgroundListener(this);
styledText.addMouseTrackListener(this);
styledText.setFont(parent.getFont());
styledText.setDoubleClickEnabled(true);
styledText.setEditable(false);
styledText.setWordWrap(true);
}
/**
* 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) {
}
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.text.ITextViewer#setDocument(org.eclipse.jface.text.IDocument)
*/
@Override
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);
}
}
/*
* (non-Javadoc)
*
* @see org.eclipse.swt.custom.LineStyleListener#lineGetStyle(org.eclipse.swt.custom.LineStyleEvent)
*/
public void lineGetStyle(LineStyleEvent event) {
IDocument document = getDocument();
if (document == null) return;
BuildConsolePartitioner partitioner = (BuildConsolePartitioner) document.getDocumentPartitioner();
if (partitioner == null) return;
BuildConsolePartition p = partitioner.fDocumentMarkerManager.getCurrentPartition();
Color problemHighlightedColor = partitioner.fManager.getProblemHighlightedColor();
// Note, computePartitioning actually doesn't change anything in partitioning,
// but only computes number of affected regions.
ITypedRegion[] regions = partitioner.computePartitioning(event.lineOffset, event.lineText.length());
StyleRange[] styles = new StyleRange[regions.length];
for (int i = 0; i < regions.length; i++) {
BuildConsolePartition partition = (BuildConsolePartition) regions[i];
if (partition.getStream()== null) return;
Color colorFG = partition.getStream().getColor();
Color colorBG = null;
// Highlight current partition
if ( partition == p ) {
colorFG = problemHighlightedColor;
}
StyleRange styleRange = new StyleRange(partition.getOffset(), partition.getLength(), colorFG, colorBG);
styles[i] = styleRange;
}
event.styles = styles;
}
public void selectPartition(BuildConsolePartitioner partitioner, BuildConsolePartition p) {
try {
int start = partitioner.getDocument().getLineOfOffset(p.getOffset());
int end = partitioner.getDocument().getLineOfOffset(p.getOffset()+p.getLength()-1);
if ( fAutoScroll ) {
// Check if area around this line is visible, scroll if needed
int top = getTopIndex();
int bottom = getBottomIndex();
if ( start < top + 1 ) {
setTopIndex(start - 1 > 0 ? start - 1 : 0);
} else if ( end > bottom -1 ) {
setTopIndex(top + start - bottom + 1);
}
}
// Select line
StyledText st = getTextWidget();
st.redrawRange(0, partitioner.getDocument().getLength(), true);
} catch (BadLocationException e) {
CUIPlugin.log(e);
}
}
public void mouseEnter(MouseEvent e) {
getTextWidget().addMouseListener(this);
}
public void mouseExit(MouseEvent e) {
getTextWidget().removeMouseListener(this);
}
public void mouseHover(MouseEvent e) {
}
public void mouseDoubleClick(MouseEvent e) {
int offset = -1;
try {
Point p = new Point(e.x, e.y);
offset = getTextWidget().getOffsetAtLocation(p);
BuildConsole.getCurrentPage().moveToError(offset);
} catch (IllegalArgumentException ex) {
}
}
public void mouseDown(MouseEvent e) {
}
public void mouseUp(MouseEvent e) {
}
public void lineGetBackground(LineBackgroundEvent event) {
IDocument document = getDocument();
if (document == null) return;
BuildConsolePartitioner partitioner = (BuildConsolePartitioner) document.getDocumentPartitioner();
if (partitioner == null) return;
BuildConsolePartition partition = (BuildConsolePartition) partitioner.getPartition(event.lineOffset);
// Set background for error partitions
if (partition!=null) {
String type = partition.getType();
if (type==BuildConsolePartition.ERROR_PARTITION_TYPE) {
event.lineBackground = partitioner.fManager.getProblemBackgroundColor();
} else if (type==BuildConsolePartition.WARNING_PARTITION_TYPE) {
event.lineBackground = partitioner.fManager.getWarningBackgroundColor();
} else if (type==BuildConsolePartition.INFO_PARTITION_TYPE) {
event.lineBackground = partitioner.fManager.getInfoBackgroundColor();
}
}
}
}