2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
10 package eu
.etaxonomy
.taxeditor
.editor
.name
;
12 import java
.util
.Iterator
;
14 import org
.apache
.log4j
.Logger
;
15 import org
.eclipse
.jface
.text
.BadLocationException
;
16 import org
.eclipse
.jface
.text
.IDocument
;
17 import org
.eclipse
.jface
.text
.IRegion
;
18 import org
.eclipse
.jface
.text
.ITextListener
;
19 import org
.eclipse
.jface
.text
.ITextViewer
;
20 import org
.eclipse
.jface
.text
.ITextViewerExtension5
;
21 import org
.eclipse
.jface
.text
.IViewportListener
;
22 import org
.eclipse
.jface
.text
.JFaceTextUtil
;
23 import org
.eclipse
.jface
.text
.Position
;
24 import org
.eclipse
.jface
.text
.Region
;
25 import org
.eclipse
.jface
.text
.TextEvent
;
26 import org
.eclipse
.jface
.text
.source
.Annotation
;
27 import org
.eclipse
.jface
.text
.source
.IAnnotationAccess
;
28 import org
.eclipse
.jface
.text
.source
.IAnnotationAccessExtension
;
29 import org
.eclipse
.jface
.text
.source
.IAnnotationModel
;
30 import org
.eclipse
.jface
.text
.source
.IAnnotationModelListener
;
31 import org
.eclipse
.jface
.text
.source
.IAnnotationPresentation
;
32 import org
.eclipse
.jface
.text
.source
.IVerticalRuler
;
33 import org
.eclipse
.jface
.text
.source
.IVerticalRulerExtension
;
34 import org
.eclipse
.swt
.SWT
;
35 import org
.eclipse
.swt
.custom
.StyledText
;
36 import org
.eclipse
.swt
.events
.DisposeEvent
;
37 import org
.eclipse
.swt
.events
.DisposeListener
;
38 import org
.eclipse
.swt
.events
.MouseEvent
;
39 import org
.eclipse
.swt
.events
.MouseListener
;
40 import org
.eclipse
.swt
.events
.PaintEvent
;
41 import org
.eclipse
.swt
.events
.PaintListener
;
42 import org
.eclipse
.swt
.graphics
.Font
;
43 import org
.eclipse
.swt
.graphics
.GC
;
44 import org
.eclipse
.swt
.graphics
.Image
;
45 import org
.eclipse
.swt
.graphics
.Point
;
46 import org
.eclipse
.swt
.graphics
.Rectangle
;
47 import org
.eclipse
.swt
.widgets
.Canvas
;
48 import org
.eclipse
.swt
.widgets
.Composite
;
49 import org
.eclipse
.swt
.widgets
.Control
;
50 import org
.eclipse
.swt
.widgets
.Display
;
51 import org
.eclipse
.ui
.forms
.widgets
.TableWrapData
;
54 * 99% of the code in this class was copied from org.eclipse.jface.text.source.VerticalRuler,
55 * which allows neither access to its paint methods nor subclassing to do same.
57 * Changes made in method doubleBufferPaint(doubleBufferPaint(GC dest)).
59 * @see org.eclipse.jface.text.source.VerticalRuler
65 public class RulerWithIcon
implements IVerticalRuler
, IVerticalRulerExtension
{
66 private static final Logger logger
= Logger
.getLogger(RulerWithIcon
.class);
69 * Internal listener class.
71 class InternalListener
implements IViewportListener
, IAnnotationModelListener
, ITextListener
{
74 * @see IViewportListener#viewportChanged(int)
76 public void viewportChanged(int verticalPosition
) {
77 if (verticalPosition
!= fScrollPos
)
82 * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
84 public void modelChanged(IAnnotationModel model
) {
89 * @see ITextListener#textChanged(TextEvent)
91 public void textChanged(TextEvent e
) {
92 if (fTextViewer
!= null && e
.getViewerRedrawState())
97 /** The vertical ruler's text viewer */
98 private ITextViewer fTextViewer
;
99 /** The ruler's canvas */
100 private Canvas fCanvas
;
101 /** The vertical ruler's model */
102 private IAnnotationModel fModel
;
103 /** Cache for the actual scroll position in pixels */
104 private int fScrollPos
;
105 /** The buffer for double buffering */
106 private Image fBuffer
;
107 /** The line of the last mouse button activity */
108 private int fLastMouseButtonActivityLine
= -1;
109 /** The internal listener */
110 private InternalListener fInternalListener
= new InternalListener();
111 /** The width of this vertical ruler */
114 * The annotation access of this vertical ruler
117 private IAnnotationAccess fAnnotationAccess
;
121 * Constructs a vertical ruler with the given width.
123 * @param width the width of the vertical ruler
125 public RulerWithIcon(int width
) {
130 * Constructs a vertical ruler with the given width and the given annotation
133 * @param width the width of the vertical ruler
134 * @param annotationAcccess the annotation access
137 public RulerWithIcon(int width
, IAnnotationAccess annotationAcccess
) {
139 fAnnotationAccess
= annotationAcccess
;
143 * @see IVerticalRuler#getControl()
145 public Control
getControl() {
150 * @see IVerticalRuler#createControl(Composite, ITextViewer)
152 public Control
createControl(Composite parent
, ITextViewer textViewer
) {
154 fTextViewer
= textViewer
;
156 fCanvas
= new Canvas(parent
, SWT
.NO_BACKGROUND
);
158 TableWrapData layout
= new TableWrapData(TableWrapData
.LEFT
);
159 layout
.heightHint
= 20;
160 layout
.maxWidth
= fWidth
;
161 fCanvas
.setLayoutData(layout
);
163 fCanvas
.addPaintListener(new PaintListener() {
164 public void paintControl(PaintEvent event
) {
165 if (fTextViewer
!= null)
166 doubleBufferPaint(event
.gc
);
170 fCanvas
.addDisposeListener(new DisposeListener() {
171 public void widgetDisposed(DisposeEvent e
) {
177 fCanvas
.addMouseListener(new MouseListener() {
178 public void mouseUp(MouseEvent event
) {
181 public void mouseDown(MouseEvent event
) {
182 fLastMouseButtonActivityLine
= toDocumentLineNumber(event
.y
);
185 public void mouseDoubleClick(MouseEvent event
) {
186 fLastMouseButtonActivityLine
= toDocumentLineNumber(event
.y
);
190 if (fTextViewer
!= null) {
191 fTextViewer
.addViewportListener(fInternalListener
);
192 fTextViewer
.addTextListener(fInternalListener
);
199 * Disposes the ruler's resources.
201 private void handleDispose() {
203 if (fTextViewer
!= null) {
204 fTextViewer
.removeViewportListener(fInternalListener
);
205 fTextViewer
.removeTextListener(fInternalListener
);
210 fModel
.removeAnnotationModelListener(fInternalListener
);
212 if (fBuffer
!= null) {
220 * Double buffer drawing.
222 * @param dest the GC to draw into
224 private void doubleBufferPaint(GC dest
) {
226 Point size
= fCanvas
.getSize();
228 if (size
.x
<= 0 || size
.y
<= 0)
231 if (fBuffer
!= null) {
232 Rectangle r
= fBuffer
.getBounds();
233 if (r
.width
!= size
.x
|| r
.height
!= size
.y
) {
239 fBuffer
= new Image(fCanvas
.getDisplay(), size
.x
, size
.y
);
241 GC gc
= new GC(fBuffer
);
242 gc
.setFont(fTextViewer
.getTextWidget().getFont());
244 gc
.setBackground(fCanvas
.getBackground());
245 gc
.fillRectangle(0, 0, size
.x
, size
.y
);
247 // Code added to VerticalRuler starts here
250 Rectangle r
= icon
.getBounds();
252 if (r
.width
> size
.x
|| r
.height
> size
.y
) {
253 logger
.warn(r
.width
+ "x" + r
.height
+ " icon too big for " + size
.x
+ "x" + size
.y
+ " Canvas.");
256 // Set destination coordinates to center icon
257 int x
= (size
.x
- r
.width
) / 2;
258 int y
= (size
.y
- r
.height
) / 2;
260 gc
.drawImage(icon
, 0, 0, r
.width
, r
.height
, x
, y
, r
.width
, r
.height
);
263 // Code added to VerticalRuler ends here
265 if (fTextViewer
instanceof ITextViewerExtension5
)
274 dest
.drawImage(fBuffer
, 0, 0);
278 * Returns the document offset of the upper left corner of the
279 * widgets view port, possibly including partially visible lines.
281 * @return the document offset of the upper left corner including partially visible lines
284 private int getInclusiveTopIndexStartOffset() {
286 StyledText textWidget
= fTextViewer
.getTextWidget();
287 if (textWidget
!= null && !textWidget
.isDisposed()) {
288 int top
= JFaceTextUtil
.getPartialTopIndex(fTextViewer
);
290 IDocument document
= fTextViewer
.getDocument();
291 return document
.getLineOffset(top
);
292 } catch (BadLocationException x
) {
302 * Draws the vertical ruler w/o drawing the Canvas background.
304 * @param gc the GC to draw into
306 protected void doPaint(GC gc
) {
308 if (fModel
== null || fTextViewer
== null)
311 IAnnotationAccessExtension annotationAccessExtension
= null;
312 if (fAnnotationAccess
instanceof IAnnotationAccessExtension
)
313 annotationAccessExtension
= (IAnnotationAccessExtension
) fAnnotationAccess
;
315 StyledText styledText
= fTextViewer
.getTextWidget();
316 IDocument doc
= fTextViewer
.getDocument();
318 int topLeft
= getInclusiveTopIndexStartOffset();
319 int bottomRight
= fTextViewer
.getBottomIndexEndOffset();
320 int viewPort
= bottomRight
- topLeft
;
322 Point d
= fCanvas
.getSize();
323 fScrollPos
= styledText
.getTopPixel();
325 int topLine
= -1, bottomLine
= -1;
327 IRegion region
= fTextViewer
.getVisibleRegion();
328 topLine
= doc
.getLineOfOffset(region
.getOffset());
329 bottomLine
= doc
.getLineOfOffset(region
.getOffset() + region
.getLength());
330 } catch (BadLocationException x
) {
335 Rectangle r
= new Rectangle(0, 0, 0, 0);
336 int maxLayer
= 1; // loop at least once though layers.
338 for (int layer
= 0; layer
< maxLayer
; layer
++) {
339 Iterator
<?
> iter
= fModel
.getAnnotationIterator();
340 while (iter
.hasNext()) {
341 IAnnotationPresentation annotationPresentation
= null;
342 Annotation annotation
= (Annotation
) iter
.next();
344 int lay
= IAnnotationAccessExtension
.DEFAULT_LAYER
;
345 if (annotationAccessExtension
!= null)
346 lay
= annotationAccessExtension
.getLayer(annotation
);
347 else if (annotation
instanceof IAnnotationPresentation
) {
348 annotationPresentation
= (IAnnotationPresentation
)annotation
;
349 lay
= annotationPresentation
.getLayer();
351 maxLayer
= Math
.max(maxLayer
, lay
+1); // dynamically update layer maximum
352 if (lay
!= layer
) // wrong layer: skip annotation
355 Position position
= fModel
.getPosition(annotation
);
356 if (position
== null)
359 if (!position
.overlapsWith(topLeft
, viewPort
))
364 int offset
= position
.getOffset();
365 int length
= position
.getLength();
367 int startLine
= doc
.getLineOfOffset(offset
);
368 if (startLine
< topLine
)
371 int endLine
= startLine
;
373 endLine
= doc
.getLineOfOffset(offset
+ length
- 1);
374 if (endLine
> bottomLine
)
377 startLine
-= topLine
;
381 r
.y
= JFaceTextUtil
.computeLineHeight(styledText
, 0, startLine
, startLine
) - fScrollPos
;
384 int lines
= endLine
- startLine
;
386 r
.height
= JFaceTextUtil
.computeLineHeight(styledText
, startLine
, endLine
+ 1, (lines
+1));
388 if (r
.y
< d
.y
&& annotationAccessExtension
!= null) // annotation within visible area
389 annotationAccessExtension
.paint(annotation
, gc
, fCanvas
, r
);
390 else if (annotationPresentation
!= null)
391 annotationPresentation
.paint(gc
, fCanvas
, r
);
393 } catch (BadLocationException e
) {
400 * Draws the vertical ruler w/o drawing the Canvas background. Uses
401 * <code>ITextViewerExtension5</code> for its implementation. Will replace
402 * <code>doPaint(GC)</code>.
404 * @param gc the GC to draw into
406 protected void doPaint1(GC gc
) {
408 if (fModel
== null || fTextViewer
== null)
411 IAnnotationAccessExtension annotationAccessExtension
= null;
412 if (fAnnotationAccess
instanceof IAnnotationAccessExtension
)
413 annotationAccessExtension
= (IAnnotationAccessExtension
) fAnnotationAccess
;
415 ITextViewerExtension5 extension
= (ITextViewerExtension5
) fTextViewer
;
416 StyledText textWidget
= fTextViewer
.getTextWidget();
418 fScrollPos
= textWidget
.getTopPixel();
419 Point dimension
= fCanvas
.getSize();
422 Rectangle r
= new Rectangle(0, 0, 0, 0);
423 int maxLayer
= 1; // loop at least once through layers.
425 for (int layer
= 0; layer
< maxLayer
; layer
++) {
426 Iterator
<?
> iter
= fModel
.getAnnotationIterator();
427 while (iter
.hasNext()) {
428 IAnnotationPresentation annotationPresentation
= null;
429 Annotation annotation
= (Annotation
) iter
.next();
431 int lay
= IAnnotationAccessExtension
.DEFAULT_LAYER
;
432 if (annotationAccessExtension
!= null)
433 lay
= annotationAccessExtension
.getLayer(annotation
);
434 else if (annotation
instanceof IAnnotationPresentation
) {
435 annotationPresentation
= (IAnnotationPresentation
)annotation
;
436 lay
= annotationPresentation
.getLayer();
438 maxLayer
= Math
.max(maxLayer
, lay
+1); // dynamically update layer maximum
439 if (lay
!= layer
) // wrong layer: skip annotation
442 Position position
= fModel
.getPosition(annotation
);
443 if (position
== null)
446 IRegion widgetRegion
= extension
.modelRange2WidgetRange(new Region(position
.getOffset(), position
.getLength()));
447 if (widgetRegion
== null)
450 int startLine
= extension
.widgetLineOfWidgetOffset(widgetRegion
.getOffset());
454 int endLine
= extension
.widgetLineOfWidgetOffset(widgetRegion
.getOffset() + Math
.max(widgetRegion
.getLength() -1, 0));
459 r
.y
= JFaceTextUtil
.computeLineHeight(textWidget
, 0, startLine
, startLine
) - fScrollPos
;
461 r
.width
= dimension
.x
;
462 int lines
= endLine
- startLine
;
464 r
.height
= JFaceTextUtil
.computeLineHeight(textWidget
, startLine
, endLine
+ 1, lines
+1);
466 if (r
.y
< dimension
.y
&& annotationAccessExtension
!= null) // annotation within visible area
467 annotationAccessExtension
.paint(annotation
, gc
, fCanvas
, r
);
468 else if (annotationPresentation
!= null)
469 annotationPresentation
.paint(gc
, fCanvas
, r
);
475 * Thread-safe implementation.
476 * Can be called from any thread.
479 * @see IVerticalRuler#update()
481 public void update() {
482 if (fCanvas
!= null && !fCanvas
.isDisposed()) {
483 Display d
= fCanvas
.getDisplay();
485 d
.asyncExec(new Runnable() {
495 * Redraws the vertical ruler.
497 private void redraw() {
498 if (fCanvas
!= null && !fCanvas
.isDisposed()) {
499 GC gc
= new GC(fCanvas
);
500 doubleBufferPaint(gc
);
506 * @see IVerticalRuler#setModel(IAnnotationModel)
508 public void setModel(IAnnotationModel model
) {
509 if (model
!= fModel
) {
512 fModel
.removeAnnotationModelListener(fInternalListener
);
517 fModel
.addAnnotationModelListener(fInternalListener
);
524 * @see IVerticalRuler#getModel()
526 public IAnnotationModel
getModel() {
531 * @see IVerticalRulerInfo#getWidth()
533 public int getWidth() {
538 * @see IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
540 public int getLineOfLastMouseButtonActivity() {
541 return fLastMouseButtonActivityLine
;
545 * @see IVerticalRulerInfo#toDocumentLineNumber(int)
547 public int toDocumentLineNumber(int y_coordinate
) {
548 if (fTextViewer
== null || y_coordinate
== -1)
551 StyledText text
= fTextViewer
.getTextWidget();
552 int line
= text
.getLineIndex(y_coordinate
);
554 if (line
== text
.getLineCount() - 1) {
555 // check whether y_coordinate exceeds last line
556 if (y_coordinate
> text
.getLinePixel(line
+ 1))
560 return widgetLine2ModelLine(fTextViewer
, line
);
564 * Returns the line of the viewer's document that corresponds to the given widget line.
566 * @param viewer the viewer
567 * @param widgetLine the widget line
568 * @return the corresponding line of the viewer's document
571 protected final static int widgetLine2ModelLine(ITextViewer viewer
, int widgetLine
) {
573 if (viewer
instanceof ITextViewerExtension5
) {
574 ITextViewerExtension5 extension
= (ITextViewerExtension5
) viewer
;
575 return extension
.widgetLine2ModelLine(widgetLine
);
579 IRegion r
= viewer
.getVisibleRegion();
580 IDocument d
= viewer
.getDocument();
581 return widgetLine
+= d
.getLineOfOffset(r
.getOffset());
582 } catch (BadLocationException x
) {
588 * @see IVerticalRulerExtension#setFont(Font)
591 public void setFont(Font font
) {
595 * @see IVerticalRulerExtension#setLocationOfLastMouseButtonActivity(int, int)
598 public void setLocationOfLastMouseButtonActivity(int x
, int y
) {
599 fLastMouseButtonActivityLine
= toDocumentLineNumber(y
);
603 * Adds the given mouse listener.
605 * @param listener the listener to be added
606 * @deprecated will be removed
609 public void addMouseListener(MouseListener listener
) {
610 if (fCanvas
!= null && !fCanvas
.isDisposed())
611 fCanvas
.addMouseListener(listener
);
615 * Removes the given mouse listener.
617 * @param listener the listener to be removed
618 * @deprecated will be removed
621 public void removeMouseListener(MouseListener listener
) {
622 if (fCanvas
!= null && !fCanvas
.isDisposed())
623 fCanvas
.removeMouseListener(listener
);
626 public void setIcon(Image icon
) {