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
.container
;
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
64 public class RulerWithIcon
implements IVerticalRuler
, IVerticalRulerExtension
{
65 private static final Logger logger
= Logger
.getLogger(RulerWithIcon
.class);
68 * Internal listener class.
70 class InternalListener
implements IViewportListener
, IAnnotationModelListener
, ITextListener
{
73 * @see IViewportListener#viewportChanged(int)
75 public void viewportChanged(int verticalPosition
) {
76 if (verticalPosition
!= fScrollPos
)
81 * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
83 public void modelChanged(IAnnotationModel model
) {
88 * @see ITextListener#textChanged(TextEvent)
90 public void textChanged(TextEvent e
) {
91 if (fTextViewer
!= null && e
.getViewerRedrawState())
96 /** The vertical ruler's text viewer */
97 private ITextViewer fTextViewer
;
98 /** The ruler's canvas */
99 private Canvas fCanvas
;
100 /** The vertical ruler's model */
101 private IAnnotationModel fModel
;
102 /** Cache for the actual scroll position in pixels */
103 private int fScrollPos
;
104 /** The buffer for double buffering */
105 private Image fBuffer
;
106 /** The line of the last mouse button activity */
107 private int fLastMouseButtonActivityLine
= -1;
108 /** The internal listener */
109 private InternalListener fInternalListener
= new InternalListener();
110 /** The width of this vertical ruler */
113 * The annotation access of this vertical ruler
116 private IAnnotationAccess fAnnotationAccess
;
120 * Constructs a vertical ruler with the given width.
122 * @param width the width of the vertical ruler
124 public RulerWithIcon(int width
) {
129 * Constructs a vertical ruler with the given width and the given annotation
132 * @param width the width of the vertical ruler
133 * @param annotationAcccess the annotation access
136 public RulerWithIcon(int width
, IAnnotationAccess annotationAcccess
) {
138 fAnnotationAccess
= annotationAcccess
;
142 * @see IVerticalRuler#getControl()
147 * @return a {@link org.eclipse.swt.widgets.Control} object.
149 public Control
getControl() {
154 * @see IVerticalRuler#createControl(Composite, ITextViewer)
157 public Control
createControl(Composite parent
, ITextViewer textViewer
) {
159 fTextViewer
= textViewer
;
161 fCanvas
= new Canvas(parent
, SWT
.NO_BACKGROUND
);
163 TableWrapData layout
= new TableWrapData(TableWrapData
.LEFT
);
164 layout
.heightHint
= 20;
165 layout
.maxWidth
= fWidth
;
166 fCanvas
.setLayoutData(layout
);
168 fCanvas
.addPaintListener(new PaintListener() {
169 public void paintControl(PaintEvent event
) {
170 if (fTextViewer
!= null)
171 doubleBufferPaint(event
.gc
);
175 fCanvas
.addDisposeListener(new DisposeListener() {
176 public void widgetDisposed(DisposeEvent e
) {
182 fCanvas
.addMouseListener(new MouseListener() {
183 public void mouseUp(MouseEvent event
) {
186 public void mouseDown(MouseEvent event
) {
187 fLastMouseButtonActivityLine
= toDocumentLineNumber(event
.y
);
190 public void mouseDoubleClick(MouseEvent event
) {
191 fLastMouseButtonActivityLine
= toDocumentLineNumber(event
.y
);
195 if (fTextViewer
!= null) {
196 fTextViewer
.addViewportListener(fInternalListener
);
197 fTextViewer
.addTextListener(fInternalListener
);
204 * Disposes the ruler's resources.
206 private void handleDispose() {
208 if (fTextViewer
!= null) {
209 fTextViewer
.removeViewportListener(fInternalListener
);
210 fTextViewer
.removeTextListener(fInternalListener
);
215 fModel
.removeAnnotationModelListener(fInternalListener
);
217 if (fBuffer
!= null) {
225 * Double buffer drawing.
227 * @param dest the GC to draw into
229 private void doubleBufferPaint(GC dest
) {
231 Point size
= fCanvas
.getSize();
233 if (size
.x
<= 0 || size
.y
<= 0)
236 if (fBuffer
!= null) {
237 Rectangle r
= fBuffer
.getBounds();
238 if (r
.width
!= size
.x
|| r
.height
!= size
.y
) {
244 fBuffer
= new Image(fCanvas
.getDisplay(), size
.x
, size
.y
);
246 GC gc
= new GC(fBuffer
);
247 gc
.setFont(fTextViewer
.getTextWidget().getFont());
249 gc
.setBackground(fCanvas
.getBackground());
250 gc
.fillRectangle(0, 0, size
.x
, size
.y
);
252 // Code added to VerticalRuler starts here
255 Rectangle r
= icon
.getBounds();
257 if (r
.width
> size
.x
|| r
.height
> size
.y
) {
258 logger
.warn(r
.width
+ "x" + r
.height
+ " icon too big for " + size
.x
+ "x" + size
.y
+ " Canvas.");
261 // Set destination coordinates to center icon
262 int x
= (size
.x
- r
.width
) / 2;
263 int y
= (size
.y
- r
.height
) / 2;
265 gc
.drawImage(icon
, 0, 0, r
.width
, r
.height
, x
, y
, r
.width
, r
.height
);
268 // Code added to VerticalRuler ends here
270 if (fTextViewer
instanceof ITextViewerExtension5
)
279 dest
.drawImage(fBuffer
, 0, 0);
283 * Returns the document offset of the upper left corner of the
284 * widgets view port, possibly including partially visible lines.
286 * @return the document offset of the upper left corner including partially visible lines
289 private int getInclusiveTopIndexStartOffset() {
291 StyledText textWidget
= fTextViewer
.getTextWidget();
292 if (textWidget
!= null && !textWidget
.isDisposed()) {
293 int top
= JFaceTextUtil
.getPartialTopIndex(fTextViewer
);
295 IDocument document
= fTextViewer
.getDocument();
296 return document
.getLineOffset(top
);
297 } catch (BadLocationException x
) {
307 * Draws the vertical ruler w/o drawing the Canvas background.
309 * @param gc the GC to draw into
311 protected void doPaint(GC gc
) {
313 if (fModel
== null || fTextViewer
== null)
316 IAnnotationAccessExtension annotationAccessExtension
= null;
317 if (fAnnotationAccess
instanceof IAnnotationAccessExtension
)
318 annotationAccessExtension
= (IAnnotationAccessExtension
) fAnnotationAccess
;
320 StyledText styledText
= fTextViewer
.getTextWidget();
321 IDocument doc
= fTextViewer
.getDocument();
323 int topLeft
= getInclusiveTopIndexStartOffset();
324 int bottomRight
= fTextViewer
.getBottomIndexEndOffset();
325 int viewPort
= bottomRight
- topLeft
;
327 Point d
= fCanvas
.getSize();
328 fScrollPos
= styledText
.getTopPixel();
330 int topLine
= -1, bottomLine
= -1;
332 IRegion region
= fTextViewer
.getVisibleRegion();
333 topLine
= doc
.getLineOfOffset(region
.getOffset());
334 bottomLine
= doc
.getLineOfOffset(region
.getOffset() + region
.getLength());
335 } catch (BadLocationException x
) {
340 Rectangle r
= new Rectangle(0, 0, 0, 0);
341 int maxLayer
= 1; // loop at least once though layers.
343 for (int layer
= 0; layer
< maxLayer
; layer
++) {
344 Iterator
<?
> iter
= fModel
.getAnnotationIterator();
345 while (iter
.hasNext()) {
346 IAnnotationPresentation annotationPresentation
= null;
347 Annotation annotation
= (Annotation
) iter
.next();
349 int lay
= IAnnotationAccessExtension
.DEFAULT_LAYER
;
350 if (annotationAccessExtension
!= null)
351 lay
= annotationAccessExtension
.getLayer(annotation
);
352 else if (annotation
instanceof IAnnotationPresentation
) {
353 annotationPresentation
= (IAnnotationPresentation
)annotation
;
354 lay
= annotationPresentation
.getLayer();
356 maxLayer
= Math
.max(maxLayer
, lay
+1); // dynamically update layer maximum
357 if (lay
!= layer
) // wrong layer: skip annotation
360 Position position
= fModel
.getPosition(annotation
);
361 if (position
== null)
364 if (!position
.overlapsWith(topLeft
, viewPort
))
369 int offset
= position
.getOffset();
370 int length
= position
.getLength();
372 int startLine
= doc
.getLineOfOffset(offset
);
373 if (startLine
< topLine
)
376 int endLine
= startLine
;
378 endLine
= doc
.getLineOfOffset(offset
+ length
- 1);
379 if (endLine
> bottomLine
)
382 startLine
-= topLine
;
386 r
.y
= JFaceTextUtil
.computeLineHeight(styledText
, 0, startLine
, startLine
) - fScrollPos
;
389 int lines
= endLine
- startLine
;
391 r
.height
= JFaceTextUtil
.computeLineHeight(styledText
, startLine
, endLine
+ 1, (lines
+1));
393 if (r
.y
< d
.y
&& annotationAccessExtension
!= null) // annotation within visible area
394 annotationAccessExtension
.paint(annotation
, gc
, fCanvas
, r
);
395 else if (annotationPresentation
!= null)
396 annotationPresentation
.paint(gc
, fCanvas
, r
);
398 } catch (BadLocationException e
) {
405 * Draws the vertical ruler w/o drawing the Canvas background. Uses
406 * <code>ITextViewerExtension5</code> for its implementation. Will replace
407 * <code>doPaint(GC)</code>.
409 * @param gc the GC to draw into
411 protected void doPaint1(GC gc
) {
413 if (fModel
== null || fTextViewer
== null)
416 IAnnotationAccessExtension annotationAccessExtension
= null;
417 if (fAnnotationAccess
instanceof IAnnotationAccessExtension
)
418 annotationAccessExtension
= (IAnnotationAccessExtension
) fAnnotationAccess
;
420 ITextViewerExtension5 extension
= (ITextViewerExtension5
) fTextViewer
;
421 StyledText textWidget
= fTextViewer
.getTextWidget();
423 fScrollPos
= textWidget
.getTopPixel();
424 Point dimension
= fCanvas
.getSize();
427 Rectangle r
= new Rectangle(0, 0, 0, 0);
428 int maxLayer
= 1; // loop at least once through layers.
430 for (int layer
= 0; layer
< maxLayer
; layer
++) {
431 Iterator
<?
> iter
= fModel
.getAnnotationIterator();
432 while (iter
.hasNext()) {
433 IAnnotationPresentation annotationPresentation
= null;
434 Annotation annotation
= (Annotation
) iter
.next();
436 int lay
= IAnnotationAccessExtension
.DEFAULT_LAYER
;
437 if (annotationAccessExtension
!= null)
438 lay
= annotationAccessExtension
.getLayer(annotation
);
439 else if (annotation
instanceof IAnnotationPresentation
) {
440 annotationPresentation
= (IAnnotationPresentation
)annotation
;
441 lay
= annotationPresentation
.getLayer();
443 maxLayer
= Math
.max(maxLayer
, lay
+1); // dynamically update layer maximum
444 if (lay
!= layer
) // wrong layer: skip annotation
447 Position position
= fModel
.getPosition(annotation
);
448 if (position
== null)
451 IRegion widgetRegion
= extension
.modelRange2WidgetRange(new Region(position
.getOffset(), position
.getLength()));
452 if (widgetRegion
== null)
455 int startLine
= extension
.widgetLineOfWidgetOffset(widgetRegion
.getOffset());
459 int endLine
= extension
.widgetLineOfWidgetOffset(widgetRegion
.getOffset() + Math
.max(widgetRegion
.getLength() -1, 0));
464 r
.y
= JFaceTextUtil
.computeLineHeight(textWidget
, 0, startLine
, startLine
) - fScrollPos
;
466 r
.width
= dimension
.x
;
467 int lines
= endLine
- startLine
;
469 r
.height
= JFaceTextUtil
.computeLineHeight(textWidget
, startLine
, endLine
+ 1, lines
+1);
471 if (r
.y
< dimension
.y
&& annotationAccessExtension
!= null) // annotation within visible area
472 annotationAccessExtension
.paint(annotation
, gc
, fCanvas
, r
);
473 else if (annotationPresentation
!= null)
474 annotationPresentation
.paint(gc
, fCanvas
, r
);
480 * Thread-safe implementation.
481 * Can be called from any thread.
484 * @see IVerticalRuler#update()
486 public void update() {
487 if (fCanvas
!= null && !fCanvas
.isDisposed()) {
488 Display d
= fCanvas
.getDisplay();
490 d
.asyncExec(new Runnable() {
500 * Redraws the vertical ruler.
502 private void redraw() {
503 if (fCanvas
!= null && !fCanvas
.isDisposed()) {
504 GC gc
= new GC(fCanvas
);
505 doubleBufferPaint(gc
);
511 * @see IVerticalRuler#setModel(IAnnotationModel)
514 public void setModel(IAnnotationModel model
) {
515 if (model
!= fModel
) {
518 fModel
.removeAnnotationModelListener(fInternalListener
);
523 fModel
.addAnnotationModelListener(fInternalListener
);
530 * @see IVerticalRuler#getModel()
535 * @return a {@link org.eclipse.jface.text.source.IAnnotationModel} object.
537 public IAnnotationModel
getModel() {
542 * @see IVerticalRulerInfo#getWidth()
549 public int getWidth() {
554 * @see IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
557 * <p>getLineOfLastMouseButtonActivity</p>
561 public int getLineOfLastMouseButtonActivity() {
562 return fLastMouseButtonActivityLine
;
566 * @see IVerticalRulerInfo#toDocumentLineNumber(int)
569 public int toDocumentLineNumber(int y_coordinate
) {
570 if (fTextViewer
== null || y_coordinate
== -1)
573 StyledText text
= fTextViewer
.getTextWidget();
574 int line
= text
.getLineIndex(y_coordinate
);
576 if (line
== text
.getLineCount() - 1) {
577 // check whether y_coordinate exceeds last line
578 if (y_coordinate
> text
.getLinePixel(line
+ 1))
582 return widgetLine2ModelLine(fTextViewer
, line
);
586 * Returns the line of the viewer's document that corresponds to the given widget line.
588 * @param viewer the viewer
589 * @param widgetLine the widget line
590 * @return the corresponding line of the viewer's document
593 protected final static int widgetLine2ModelLine(ITextViewer viewer
, int widgetLine
) {
595 if (viewer
instanceof ITextViewerExtension5
) {
596 ITextViewerExtension5 extension
= (ITextViewerExtension5
) viewer
;
597 return extension
.widgetLine2ModelLine(widgetLine
);
601 IRegion r
= viewer
.getVisibleRegion();
602 IDocument d
= viewer
.getDocument();
603 return widgetLine
+= d
.getLineOfOffset(r
.getOffset());
604 } catch (BadLocationException x
) {
610 * @see IVerticalRulerExtension#setFont(Font)
614 public void setFont(Font font
) {
618 * @see IVerticalRulerExtension#setLocationOfLastMouseButtonActivity(int, int)
622 public void setLocationOfLastMouseButtonActivity(int x
, int y
) {
623 fLastMouseButtonActivityLine
= toDocumentLineNumber(y
);
627 * Adds the given mouse listener.
629 * @param listener the listener to be added
630 * @deprecated will be removed
633 public void addMouseListener(MouseListener listener
) {
634 if (fCanvas
!= null && !fCanvas
.isDisposed())
635 fCanvas
.addMouseListener(listener
);
639 * Removes the given mouse listener.
641 * @param listener the listener to be removed
642 * @deprecated will be removed
645 public void removeMouseListener(MouseListener listener
) {
646 if (fCanvas
!= null && !fCanvas
.isDisposed())
647 fCanvas
.removeMouseListener(listener
);
651 * <p>Setter for the field <code>icon</code>.</p>
653 * @param icon a {@link org.eclipse.swt.graphics.Image} object.
655 public void setIcon(Image icon
) {