blob: de0a5064117d3fca0bf3e986c7c4255298802cbd [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.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 which is connected to a text viewer.
* Standard implementation of <code>IVerticalRuler</code>.
* Clients may use this class as is.
*
* @see ITextViewer
*/
public final class VerticalRuler implements IVerticalRuler, IVerticalRulerInfo {
/**
* Internal listener class.
*/
class InternalListener implements IViewportListener, IAnnotationModelListener, ITextListener {
/*
* @see IViewportListener#viewportChanged(int)
*/
public void viewportChanged(int verticalPosition) {
if (verticalPosition != fScrollPos)
redraw();
}
/*
* @see IAnnotationModelListener#modelChanged(IAnnotationModel)
*/
public void modelChanged(IAnnotationModel model) {
update();
}
/*
* @see ITextListener#textChanged(TextEvent)
*/
public void textChanged(TextEvent e) {
if (fTextViewer != null)
redraw();
}
};
/** The vertical ruler's text viewer */
private ITextViewer fTextViewer;
/** 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 line of the last mouse button activity */
private int fLastMouseButtonActivityLine= -1;
/** The internal listener */
private InternalListener fInternalListener= new InternalListener();
/** The width of this vertical ruler */
private int fWidth;
/**
* Constructs a vertical ruler with the given width.
*
* @param width the width of the vertical ruler
*/
public VerticalRuler(int width) {
fWidth= width;
}
/*
* @see IVerticalRuler#getControl()
*/
public Control getControl() {
return fCanvas;
}
/*
* @see IVerticalRuler#createControl(Composite, ITextViewer)
*/
public Control createControl(Composite parent, ITextViewer textViewer) {
fTextViewer= textViewer;
fCanvas= new Canvas(parent, SWT.NO_BACKGROUND);
fCanvas.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event) {
if (fTextViewer != null)
doubleBufferPaint(event.gc);
}
});
fCanvas.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
handleDispose();
fTextViewer= null;
}
});
fCanvas.addMouseListener(new MouseListener() {
public void mouseUp(MouseEvent event) {
}
public void mouseDown(MouseEvent event) {
fLastMouseButtonActivityLine= toDocumentLineNumber(event.y);
}
public void mouseDoubleClick(MouseEvent event) {
fLastMouseButtonActivityLine= toDocumentLineNumber(event.y);
}
});
if (fTextViewer != null) {
fTextViewer.addViewportListener(fInternalListener);
fTextViewer.addTextListener(fInternalListener);
}
return fCanvas;
}
/**
* Disposes the ruler's resources.
*/
private void handleDispose() {
if (fTextViewer != null) {
fTextViewer.removeViewportListener(fInternalListener);
fTextViewer.removeTextListener(fInternalListener);
fTextViewer= null;
}
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 || fTextViewer == null)
return;
int topLeft= fTextViewer.getTopIndexStartOffset();
int bottomRight= fTextViewer.getBottomIndexEndOffset();
int viewPort= bottomRight - topLeft;
IDocument doc= fTextViewer.getDocument();
StyledText styledText= fTextViewer.getTextWidget();
Point d= fCanvas.getSize();
fScrollPos= styledText.getTopPixel();
int lineheight= styledText.getLineHeight();
int shift= fTextViewer.getTopInset();
int topLine= -1, bottomLine= -1;
try {
IRegion region= fTextViewer.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= d.x;
int lines= endLine - startLine;
if (lines < 0)
lines= -lines;
r.height= (lines+1) * lineheight;
if (r.y < d.y) // annotation within visible area
annotation.paint(gc, fCanvas, r);
} catch (BadLocationException e) {
}
}
}
}
/**
* Thread-safe implementation.
* Can be called from any thread.
*/
/*
* @see IVerticalRuler#update()
*/
public void update() {
if (fCanvas != null && !fCanvas.isDisposed()) {
Display d= fCanvas.getDisplay();
if (d != null) {
d.asyncExec(new Runnable() {
public void run() {
redraw();
}
});
}
}
}
/**
* Redraws the vertical ruler.
*/
private void redraw() {
if (fCanvas != null && !fCanvas.isDisposed()) {
GC gc= new GC(fCanvas);
doubleBufferPaint(gc);
gc.dispose();
}
}
/*
* @see IVerticalRuler#setModel(IAnnotationModel)
*/
public void setModel(IAnnotationModel model) {
if (model != fModel) {
if (fModel != null)
fModel.removeAnnotationModelListener(fInternalListener);
fModel= model;
if (fModel != null)
fModel.addAnnotationModelListener(fInternalListener);
update();
}
}
/*
* @see IVerticalRuler#getModel()
*/
public IAnnotationModel getModel() {
return fModel;
}
/*
* @see IVerticalRulerInfo#getWidth()
*/
public int getWidth() {
return fWidth;
}
/*
* @see IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
*/
public int getLineOfLastMouseButtonActivity() {
return fLastMouseButtonActivityLine;
}
/*
* @see IVerticalRulerInfo#toDocumentLineNumber(int)
*/
public int toDocumentLineNumber(int y_coordinate) {
if (fTextViewer == null)
return -1;
StyledText text= fTextViewer.getTextWidget();
int line= ((y_coordinate + fScrollPos) / text.getLineHeight());
try {
IRegion r= fTextViewer.getVisibleRegion();
IDocument d= fTextViewer.getDocument();
line += d.getLineOfOffset(r.getOffset());
} catch (BadLocationException x) {
}
return line;
}
/*
* @see IVerticalRulerInfo#addMouseListener
*/
public void addMouseListener(MouseListener listener) {
fCanvas.addMouseListener(listener);
}
/*
* @see IVerticalRulerInfo#addMouseListener
*/
public void removeMouseListener(MouseListener listener) {
fCanvas.removeMouseListener(listener);
}
}