blob: b74159be2f0af2bccfd4e1b577eb76e3964310bc [file] [log] [blame]
package org.eclipse.jface.text.source;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved.
*/
import java.util.Iterator;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextListener;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.IViewportListener;
import org.eclipse.jface.text.Position;
import org.eclipse.jface.text.TextEvent;
/**
* A vertical ruler column displaying the graphics of annotations *
*/
public final class AnnotationColumn implements IVerticalRulerColumn {
/**
* Internal listener class.
*/
class InternalListener implements IViewportListener, IAnnotationModelListener, ITextListener {
/*
* @see IViewportListener#viewportChanged
*/
public void viewportChanged(int verticalPosition) {
if (verticalPosition != fScrollPos)
redraw();
}
/*
* @see IAnnotationModelListener#modelChanged
*/
public void modelChanged(IAnnotationModel model) {
postRedraw();
}
/*
* @see ITextListener#textChanged
*/
public void textChanged(TextEvent e) {
redraw();
}
};
/** This column's parent ruler */
private CompositeRuler fParentRuler;
/** The cached text viewer */
private ITextViewer fCachedTextViewer;
/** The cached text widget */
private StyledText fCachedTextWidget;
/** The ruler's canvas */
private Canvas fCanvas;
/** The vertical ruler's model */
private IAnnotationModel fModel;
/** Cache for the actual scroll position in pixels */
private int fScrollPos;
/** The drawable for double buffering */
private Image fBuffer;
/** The internal listener */
private InternalListener fInternalListener= new InternalListener();
/** The width of this vertical ruler */
private int fWidth;
/**
* Constructs this column with the given width.
*
* @param width the width of the vertical ruler
*/
public AnnotationColumn(int width) {
fWidth= width;
}
/*
* @see IVerticalRulerColumn#getControl()
*/
public Control getControl() {
return fCanvas;
}
/*
* @see IVerticalRulerColumn#getWidth()
*/
public int getWidth() {
return fWidth;
}
/*
* @see IVerticalRulerColumn#createControl(CompositeRuler, Composite)
*/
public Control createControl(CompositeRuler parentRuler, Composite parentControl) {
fParentRuler= parentRuler;
fCachedTextViewer= parentRuler.getTextViewer();
fCachedTextWidget= fCachedTextViewer.getTextWidget();
fCanvas= new Canvas(parentControl, SWT.NO_BACKGROUND);
fCanvas.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event) {
if (fCachedTextViewer != null)
doubleBufferPaint(event.gc);
}
});
fCanvas.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
handleDispose();
fCachedTextViewer= null;
fCachedTextWidget= null;
}
});
fCanvas.addMouseListener(new MouseListener() {
public void mouseUp(MouseEvent event) {
}
public void mouseDown(MouseEvent event) {
fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
}
public void mouseDoubleClick(MouseEvent event) {
fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
}
});
if (fCachedTextViewer != null) {
fCachedTextViewer.addViewportListener(fInternalListener);
fCachedTextViewer.addTextListener(fInternalListener);
}
return fCanvas;
}
/**
* Disposes the ruler's resources.
*/
private void handleDispose() {
if (fCachedTextViewer != null) {
fCachedTextViewer.removeViewportListener(fInternalListener);
fCachedTextViewer.removeTextListener(fInternalListener);
}
if (fModel != null)
fModel.removeAnnotationModelListener(fInternalListener);
if (fBuffer != null) {
fBuffer.dispose();
fBuffer= null;
}
}
/**
* Double buffer drawing.
*/
private void doubleBufferPaint(GC dest) {
Point size= fCanvas.getSize();
if (size.x <= 0 || size.y <= 0)
return;
if (fBuffer != null) {
Rectangle r= fBuffer.getBounds();
if (r.width != size.x || r.height != size.y) {
fBuffer.dispose();
fBuffer= null;
}
}
if (fBuffer == null)
fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y);
GC gc= new GC(fBuffer);
try {
gc.setBackground(fCanvas.getBackground());
gc.fillRectangle(0, 0, size.x, size.y);
doPaint(gc);
} finally {
gc.dispose();
}
dest.drawImage(fBuffer, 0, 0);
}
/**
* Draws the vertical ruler w/o drawing the Canvas background.
*/
private void doPaint(GC gc) {
if (fModel == null || fCachedTextViewer == null)
return;
int topLeft= fCachedTextViewer.getTopIndexStartOffset();
int bottomRight= fCachedTextViewer.getBottomIndexEndOffset();
int viewPort= bottomRight - topLeft;
fScrollPos= fCachedTextWidget.getTopPixel();
int lineheight= fCachedTextWidget.getLineHeight();
Point dimension= fCanvas.getSize();
int shift= fCachedTextViewer.getTopInset();
IDocument doc= fCachedTextViewer.getDocument();
int topLine= -1, bottomLine= -1;
try {
IRegion region= fCachedTextViewer.getVisibleRegion();
topLine= doc.getLineOfOffset(region.getOffset());
bottomLine= doc.getLineOfOffset(region.getOffset() + region.getLength());
} catch (BadLocationException x) {
return;
}
// draw Annotations
Rectangle r= new Rectangle(0, 0, 0, 0);
int maxLayer= 1; // loop at least once thru layers.
for (int layer= 0; layer < maxLayer; layer++) {
Iterator iter= fModel.getAnnotationIterator();
while (iter.hasNext()) {
Annotation annotation= (Annotation) iter.next();
int lay= annotation.getLayer();
maxLayer= Math.max(maxLayer, lay+1); // dynamically update layer maximum
if (lay != layer) // wrong layer: skip annotation
continue;
Position position= fModel.getPosition(annotation);
if (position == null)
continue;
if (!position.overlapsWith(topLeft, viewPort))
continue;
try {
int offset= position.getOffset();
int length= position.getLength();
int startLine= doc.getLineOfOffset(offset);
if (startLine < topLine)
startLine= topLine;
startLine -= topLine;
int endLine= startLine;
if (length > 0)
endLine= doc.getLineOfOffset(offset + length - 1);
if (endLine > bottomLine)
endLine= bottomLine;
endLine -= topLine;
r.x= 0;
r.y= (startLine * lineheight) - fScrollPos + shift;
r.width= dimension.x;
int lines= endLine - startLine;
if (lines < 0)
lines= -lines;
r.height= (lines+1) * lineheight;
if (r.y < dimension.y) // annotation within visible area
annotation.paint(gc, fCanvas, r);
} catch (BadLocationException e) {
}
}
}
}
/**
* Post a redraw request for thsi column into the UI thread.
*/
private void postRedraw() {
if (fCanvas != null && !fCanvas.isDisposed()) {
Display d= fCanvas.getDisplay();
if (d != null) {
d.asyncExec(new Runnable() {
public void run() {
redraw();
}
});
}
}
}
/*
* @see IVerticalRulerColumn#redraw()
*/
public void redraw() {
if (fCanvas != null && !fCanvas.isDisposed()) {
GC gc= new GC(fCanvas);
doubleBufferPaint(gc);
gc.dispose();
}
}
/*
* @see IVerticalRulerColumn#setModel
*/
public void setModel(IAnnotationModel model) {
if (model != fModel) {
if (fModel != null)
fModel.removeAnnotationModelListener(fInternalListener);
fModel= model;
if (fModel != null)
fModel.addAnnotationModelListener(fInternalListener);
postRedraw();
}
}
/*
* @see IVerticalRulerColumn#setFont(Font)
*/
public void setFont(Font font) {
}
}