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
.logging
.log4j
.LogManager
;import org
.apache
.logging
.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
63 public class RulerWithIcon
implements IVerticalRuler
, IVerticalRulerExtension
{
64 private static final Logger logger
= LogManager
.getLogger(RulerWithIcon
.class);
67 * Internal listener class.
69 class InternalListener
implements IViewportListener
, IAnnotationModelListener
, ITextListener
{
72 * @see IViewportListener#viewportChanged(int)
75 public void viewportChanged(int verticalPosition
) {
76 if (verticalPosition
!= fScrollPos
) {
82 * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
85 public void modelChanged(IAnnotationModel model
) {
90 * @see ITextListener#textChanged(TextEvent)
93 public void textChanged(TextEvent e
) {
94 if (fTextViewer
!= null && e
.getViewerRedrawState()) {
100 /** The vertical ruler's text viewer */
101 private ITextViewer fTextViewer
;
102 /** The ruler's canvas */
103 private Canvas fCanvas
;
104 /** The vertical ruler's model */
105 private IAnnotationModel fModel
;
106 /** Cache for the actual scroll position in pixels */
107 private int fScrollPos
;
108 /** The buffer for double buffering */
109 private Image fBuffer
;
110 /** The line of the last mouse button activity */
111 private int fLastMouseButtonActivityLine
= -1;
112 /** The internal listener */
113 private InternalListener fInternalListener
= new InternalListener();
114 /** The width of this vertical ruler */
117 * The annotation access of this vertical ruler
120 private IAnnotationAccess fAnnotationAccess
;
124 * Constructs a vertical ruler with the given width.
126 * @param width the width of the vertical ruler
128 public RulerWithIcon(int width
) {
133 * Constructs a vertical ruler with the given width and the given annotation
136 * @param width the width of the vertical ruler
137 * @param annotationAcccess the annotation access
140 public RulerWithIcon(int width
, IAnnotationAccess annotationAcccess
) {
142 fAnnotationAccess
= annotationAcccess
;
146 * @see IVerticalRuler#getControl()
151 * @return a {@link org.eclipse.swt.widgets.Control} object.
154 public Control
getControl() {
159 public Control
createControl(Composite parent
, ITextViewer textViewer
) {
161 fTextViewer
= textViewer
;
163 fCanvas
= new Canvas(parent
, SWT
.NO_BACKGROUND
);
165 TableWrapData layout
= new TableWrapData(TableWrapData
.LEFT
);
166 layout
.heightHint
= 20;
167 layout
.maxWidth
= fWidth
;
168 fCanvas
.setLayoutData(layout
);
170 fCanvas
.addPaintListener(new PaintListener() {
172 public void paintControl(PaintEvent event
) {
173 if (fTextViewer
!= null) {
174 doubleBufferPaint(event
.gc
);
179 fCanvas
.addDisposeListener(new DisposeListener() {
181 public void widgetDisposed(DisposeEvent e
) {
187 fCanvas
.addMouseListener(new MouseListener() {
189 public void mouseUp(MouseEvent event
) {
193 public void mouseDown(MouseEvent event
) {
194 fLastMouseButtonActivityLine
= toDocumentLineNumber(event
.y
);
198 public void mouseDoubleClick(MouseEvent event
) {
199 fLastMouseButtonActivityLine
= toDocumentLineNumber(event
.y
);
203 if (fTextViewer
!= null) {
204 fTextViewer
.addViewportListener(fInternalListener
);
205 fTextViewer
.addTextListener(fInternalListener
);
212 * Disposes the ruler's resources.
214 private void handleDispose() {
216 if (fTextViewer
!= null) {
217 fTextViewer
.removeViewportListener(fInternalListener
);
218 fTextViewer
.removeTextListener(fInternalListener
);
222 if (fModel
!= null) {
223 fModel
.removeAnnotationModelListener(fInternalListener
);
226 if (fBuffer
!= null) {
234 * Double buffer drawing.
236 * @param dest the GC to draw into
238 private void doubleBufferPaint(GC dest
) {
240 Point size
= fCanvas
.getSize();
242 if (size
.x
<= 0 || size
.y
<= 0) {
246 if (fBuffer
!= null) {
247 Rectangle r
= fBuffer
.getBounds();
248 if (r
.width
!= size
.x
|| r
.height
!= size
.y
) {
253 if (fBuffer
== null) {
254 fBuffer
= new Image(fCanvas
.getDisplay(), size
.x
, size
.y
);
257 GC gc
= new GC(fBuffer
);
258 gc
.setFont(fTextViewer
.getTextWidget().getFont());
260 gc
.setBackground(fCanvas
.getBackground());
261 gc
.fillRectangle(0, 0, size
.x
, size
.y
);
263 // Code added to VerticalRuler starts here
266 Rectangle r
= icon
.getBounds();
268 if (r
.width
> size
.x
|| r
.height
> size
.y
) {
269 logger
.warn(r
.width
+ "x" + r
.height
+ " icon too big for " + size
.x
+ "x" + size
.y
+ " Canvas."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
272 // Set destination coordinates to center icon
273 int x
= (size
.x
- r
.width
) / 2;
274 int y
= (size
.y
- r
.height
) / 2;
276 gc
.drawImage(icon
, 0, 0, r
.width
, r
.height
, x
, y
, r
.width
, r
.height
);
279 // Code added to VerticalRuler ends here
281 if (fTextViewer
instanceof ITextViewerExtension5
) {
291 dest
.drawImage(fBuffer
, 0, 0);
295 * Returns the document offset of the upper left corner of the
296 * widgets view port, possibly including partially visible lines.
298 * @return the document offset of the upper left corner including partially visible lines
301 private int getInclusiveTopIndexStartOffset() {
303 StyledText textWidget
= fTextViewer
.getTextWidget();
304 if (textWidget
!= null && !textWidget
.isDisposed()) {
305 int top
= JFaceTextUtil
.getPartialTopIndex(fTextViewer
);
307 IDocument document
= fTextViewer
.getDocument();
308 return document
.getLineOffset(top
);
309 } catch (BadLocationException x
) {
319 * Draws the vertical ruler w/o drawing the Canvas background.
321 * @param gc the GC to draw into
323 protected void doPaint(GC gc
) {
325 if (fModel
== null || fTextViewer
== null) {
329 IAnnotationAccessExtension annotationAccessExtension
= null;
330 if (fAnnotationAccess
instanceof IAnnotationAccessExtension
) {
331 annotationAccessExtension
= (IAnnotationAccessExtension
) fAnnotationAccess
;
334 StyledText styledText
= fTextViewer
.getTextWidget();
335 IDocument doc
= fTextViewer
.getDocument();
337 int topLeft
= getInclusiveTopIndexStartOffset();
338 int bottomRight
= fTextViewer
.getBottomIndexEndOffset();
339 int viewPort
= bottomRight
- topLeft
;
341 Point d
= fCanvas
.getSize();
342 fScrollPos
= styledText
.getTopPixel();
344 int topLine
= -1, bottomLine
= -1;
346 IRegion region
= fTextViewer
.getVisibleRegion();
347 topLine
= doc
.getLineOfOffset(region
.getOffset());
348 bottomLine
= doc
.getLineOfOffset(region
.getOffset() + region
.getLength());
349 } catch (BadLocationException x
) {
354 Rectangle r
= new Rectangle(0, 0, 0, 0);
355 int maxLayer
= 1; // loop at least once though layers.
357 for (int layer
= 0; layer
< maxLayer
; layer
++) {
358 Iterator
<?
> iter
= fModel
.getAnnotationIterator();
359 while (iter
.hasNext()) {
360 IAnnotationPresentation annotationPresentation
= null;
361 Annotation annotation
= (Annotation
) iter
.next();
363 int lay
= IAnnotationAccessExtension
.DEFAULT_LAYER
;
364 if (annotationAccessExtension
!= null) {
365 lay
= annotationAccessExtension
.getLayer(annotation
);
366 } else if (annotation
instanceof IAnnotationPresentation
) {
367 annotationPresentation
= (IAnnotationPresentation
)annotation
;
368 lay
= annotationPresentation
.getLayer();
370 maxLayer
= Math
.max(maxLayer
, lay
+1); // dynamically update layer maximum
375 Position position
= fModel
.getPosition(annotation
);
376 if (position
== null) {
380 if (!position
.overlapsWith(topLeft
, viewPort
)) {
386 int offset
= position
.getOffset();
387 int length
= position
.getLength();
389 int startLine
= doc
.getLineOfOffset(offset
);
390 if (startLine
< topLine
) {
394 int endLine
= startLine
;
396 endLine
= doc
.getLineOfOffset(offset
+ length
- 1);
398 if (endLine
> bottomLine
) {
402 startLine
-= topLine
;
406 r
.y
= JFaceTextUtil
.computeLineHeight(styledText
, 0, startLine
, startLine
) - fScrollPos
;
409 int lines
= endLine
- startLine
;
411 r
.height
= JFaceTextUtil
.computeLineHeight(styledText
, startLine
, endLine
+ 1, (lines
+1));
413 if (r
.y
< d
.y
&& annotationAccessExtension
!= null) {
414 annotationAccessExtension
.paint(annotation
, gc
, fCanvas
, r
);
415 } else if (annotationPresentation
!= null) {
416 annotationPresentation
.paint(gc
, fCanvas
, r
);
419 } catch (BadLocationException e
) {
426 * Draws the vertical ruler w/o drawing the Canvas background. Uses
427 * <code>ITextViewerExtension5</code> for its implementation. Will replace
428 * <code>doPaint(GC)</code>.
430 * @param gc the GC to draw into
432 protected void doPaint1(GC gc
) {
434 if (fModel
== null || fTextViewer
== null) {
438 IAnnotationAccessExtension annotationAccessExtension
= null;
439 if (fAnnotationAccess
instanceof IAnnotationAccessExtension
) {
440 annotationAccessExtension
= (IAnnotationAccessExtension
) fAnnotationAccess
;
443 ITextViewerExtension5 extension
= (ITextViewerExtension5
) fTextViewer
;
444 StyledText textWidget
= fTextViewer
.getTextWidget();
446 fScrollPos
= textWidget
.getTopPixel();
447 Point dimension
= fCanvas
.getSize();
450 Rectangle r
= new Rectangle(0, 0, 0, 0);
451 int maxLayer
= 1; // loop at least once through layers.
453 for (int layer
= 0; layer
< maxLayer
; layer
++) {
454 Iterator
<?
> iter
= fModel
.getAnnotationIterator();
455 while (iter
.hasNext()) {
456 IAnnotationPresentation annotationPresentation
= null;
457 Annotation annotation
= (Annotation
) iter
.next();
459 int lay
= IAnnotationAccessExtension
.DEFAULT_LAYER
;
460 if (annotationAccessExtension
!= null) {
461 lay
= annotationAccessExtension
.getLayer(annotation
);
462 } else if (annotation
instanceof IAnnotationPresentation
) {
463 annotationPresentation
= (IAnnotationPresentation
)annotation
;
464 lay
= annotationPresentation
.getLayer();
466 maxLayer
= Math
.max(maxLayer
, lay
+1); // dynamically update layer maximum
471 Position position
= fModel
.getPosition(annotation
);
472 if (position
== null) {
476 IRegion widgetRegion
= extension
.modelRange2WidgetRange(new Region(position
.getOffset(), position
.getLength()));
477 if (widgetRegion
== null) {
481 int startLine
= extension
.widgetLineOfWidgetOffset(widgetRegion
.getOffset());
482 if (startLine
== -1) {
486 int endLine
= extension
.widgetLineOfWidgetOffset(widgetRegion
.getOffset() + Math
.max(widgetRegion
.getLength() -1, 0));
492 r
.y
= JFaceTextUtil
.computeLineHeight(textWidget
, 0, startLine
, startLine
) - fScrollPos
;
494 r
.width
= dimension
.x
;
495 int lines
= endLine
- startLine
;
497 r
.height
= JFaceTextUtil
.computeLineHeight(textWidget
, startLine
, endLine
+ 1, lines
+1);
499 if (r
.y
< dimension
.y
&& annotationAccessExtension
!= null) {
500 annotationAccessExtension
.paint(annotation
, gc
, fCanvas
, r
);
501 } else if (annotationPresentation
!= null) {
502 annotationPresentation
.paint(gc
, fCanvas
, r
);
509 * Thread-safe implementation.
510 * Can be called from any thread.
513 * @see IVerticalRuler#update()
516 public void update() {
517 if (fCanvas
!= null && !fCanvas
.isDisposed()) {
518 Display d
= fCanvas
.getDisplay();
520 d
.asyncExec(new Runnable() {
531 * Redraws the vertical ruler.
533 private void redraw() {
534 if (fCanvas
!= null && !fCanvas
.isDisposed()) {
535 GC gc
= new GC(fCanvas
);
536 doubleBufferPaint(gc
);
542 public void setModel(IAnnotationModel model
) {
543 if (model
!= fModel
) {
545 if (fModel
!= null) {
546 fModel
.removeAnnotationModelListener(fInternalListener
);
551 if (fModel
!= null) {
552 fModel
.addAnnotationModelListener(fInternalListener
);
560 * @see IVerticalRuler#getModel()
565 * @return a {@link org.eclipse.jface.text.source.IAnnotationModel} object.
568 public IAnnotationModel
getModel() {
573 * @see IVerticalRulerInfo#getWidth()
581 public int getWidth() {
586 * @see IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
589 * <p>getLineOfLastMouseButtonActivity</p>
594 public int getLineOfLastMouseButtonActivity() {
595 return fLastMouseButtonActivityLine
;
599 public int toDocumentLineNumber(int y_coordinate
) {
600 if (fTextViewer
== null || y_coordinate
== -1) {
604 StyledText text
= fTextViewer
.getTextWidget();
605 int line
= text
.getLineIndex(y_coordinate
);
607 if (line
== text
.getLineCount() - 1) {
608 // check whether y_coordinate exceeds last line
609 if (y_coordinate
> text
.getLinePixel(line
+ 1)) {
614 return widgetLine2ModelLine(fTextViewer
, line
);
618 * Returns the line of the viewer's document that corresponds to the given widget line.
620 * @param viewer the viewer
621 * @param widgetLine the widget line
622 * @return the corresponding line of the viewer's document
625 protected final static int widgetLine2ModelLine(ITextViewer viewer
, int widgetLine
) {
627 if (viewer
instanceof ITextViewerExtension5
) {
628 ITextViewerExtension5 extension
= (ITextViewerExtension5
) viewer
;
629 return extension
.widgetLine2ModelLine(widgetLine
);
633 IRegion r
= viewer
.getVisibleRegion();
634 IDocument d
= viewer
.getDocument();
635 return widgetLine
+= d
.getLineOfOffset(r
.getOffset());
636 } catch (BadLocationException x
) {
642 public void setFont(Font font
) {
646 public void setLocationOfLastMouseButtonActivity(int x
, int y
) {
647 fLastMouseButtonActivityLine
= toDocumentLineNumber(y
);
651 * Adds the given mouse listener.
653 * @param listener the listener to be added
654 * @deprecated will be removed
658 public void addMouseListener(MouseListener listener
) {
659 if (fCanvas
!= null && !fCanvas
.isDisposed()) {
660 fCanvas
.addMouseListener(listener
);
665 * Removes the given mouse listener.
667 * @param listener the listener to be removed
668 * @deprecated will be removed
672 public void removeMouseListener(MouseListener listener
) {
673 if (fCanvas
!= null && !fCanvas
.isDisposed()) {
674 fCanvas
.removeMouseListener(listener
);
679 * <p>Setter for the field <code>icon</code>.</p>
681 * @param icon a {@link org.eclipse.swt.graphics.Image} object.
683 public void setIcon(Image icon
) {