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
353 if (lay
!= layer
) // wrong layer: skip annotation
356 Position position
= fModel
.getPosition(annotation
);
357 if (position
== null)
361 * Commented this out because of following sequence:
362 * 1) empty name composite w/ annotation, i.e. "no sec reference"
363 * 2) focus in this composite worked as expected, i.e. annotation drawn in li. 397 below
364 * 3) lose focus, however, and for some reason (adding empty text "Click to add name?"),
365 * composite failed this overlapsWith() test, and annotation not redrawn
367 // if (!position.overlapsWith(topLeft, viewPort))
372 int offset
= position
.getOffset();
373 int length
= position
.getLength();
375 int startLine
= doc
.getLineOfOffset(offset
);
376 if (startLine
< topLine
)
379 int endLine
= startLine
;
381 endLine
= doc
.getLineOfOffset(offset
+ length
- 1);
382 if (endLine
> bottomLine
)
385 startLine
-= topLine
;
389 r
.y
= JFaceTextUtil
.computeLineHeight(styledText
, 0, startLine
, startLine
) - fScrollPos
;
392 int lines
= endLine
- startLine
;
394 r
.height
= JFaceTextUtil
.computeLineHeight(styledText
, startLine
, endLine
+ 1, (lines
+1));
396 if (r
.y
< d
.y
&& annotationAccessExtension
!= null) // annotation within visible area
397 annotationAccessExtension
.paint(annotation
, gc
, fCanvas
, r
);
398 else if (annotationPresentation
!= null)
399 annotationPresentation
.paint(gc
, fCanvas
, r
);
401 } catch (BadLocationException e
) {
408 * Draws the vertical ruler w/o drawing the Canvas background. Uses
409 * <code>ITextViewerExtension5</code> for its implementation. Will replace
410 * <code>doPaint(GC)</code>.
412 * @param gc the GC to draw into
414 protected void doPaint1(GC gc
) {
416 if (fModel
== null || fTextViewer
== null)
419 IAnnotationAccessExtension annotationAccessExtension
= null;
420 if (fAnnotationAccess
instanceof IAnnotationAccessExtension
)
421 annotationAccessExtension
= (IAnnotationAccessExtension
) fAnnotationAccess
;
423 ITextViewerExtension5 extension
= (ITextViewerExtension5
) fTextViewer
;
424 StyledText textWidget
= fTextViewer
.getTextWidget();
426 fScrollPos
= textWidget
.getTopPixel();
427 Point dimension
= fCanvas
.getSize();
430 Rectangle r
= new Rectangle(0, 0, 0, 0);
431 int maxLayer
= 1; // loop at least once through layers.
433 for (int layer
= 0; layer
< maxLayer
; layer
++) {
434 Iterator iter
= fModel
.getAnnotationIterator();
435 while (iter
.hasNext()) {
436 IAnnotationPresentation annotationPresentation
= null;
437 Annotation annotation
= (Annotation
) iter
.next();
439 int lay
= IAnnotationAccessExtension
.DEFAULT_LAYER
;
440 if (annotationAccessExtension
!= null)
441 lay
= annotationAccessExtension
.getLayer(annotation
);
442 else if (annotation
instanceof IAnnotationPresentation
) {
443 annotationPresentation
= (IAnnotationPresentation
)annotation
;
444 lay
= annotationPresentation
.getLayer();
446 maxLayer
= Math
.max(maxLayer
, lay
+1); // dynamically update layer maximum
447 if (lay
!= layer
) // wrong layer: skip annotation
450 Position position
= fModel
.getPosition(annotation
);
451 if (position
== null)
454 IRegion widgetRegion
= extension
.modelRange2WidgetRange(new Region(position
.getOffset(), position
.getLength()));
455 if (widgetRegion
== null)
458 int startLine
= extension
.widgetLineOfWidgetOffset(widgetRegion
.getOffset());
462 int endLine
= extension
.widgetLineOfWidgetOffset(widgetRegion
.getOffset() + Math
.max(widgetRegion
.getLength() -1, 0));
467 r
.y
= JFaceTextUtil
.computeLineHeight(textWidget
, 0, startLine
, startLine
) - fScrollPos
;
469 r
.width
= dimension
.x
;
470 int lines
= endLine
- startLine
;
472 r
.height
= JFaceTextUtil
.computeLineHeight(textWidget
, startLine
, endLine
+ 1, lines
+1);
474 if (r
.y
< dimension
.y
&& annotationAccessExtension
!= null) // annotation within visible area
475 annotationAccessExtension
.paint(annotation
, gc
, fCanvas
, r
);
476 else if (annotationPresentation
!= null)
477 annotationPresentation
.paint(gc
, fCanvas
, r
);
483 * Thread-safe implementation.
484 * Can be called from any thread.
487 * @see IVerticalRuler#update()
489 public void update() {
490 if (fCanvas
!= null && !fCanvas
.isDisposed()) {
491 Display d
= fCanvas
.getDisplay();
493 d
.asyncExec(new Runnable() {
503 * Redraws the vertical ruler.
505 private void redraw() {
506 if (fCanvas
!= null && !fCanvas
.isDisposed()) {
507 GC gc
= new GC(fCanvas
);
508 doubleBufferPaint(gc
);
514 * @see IVerticalRuler#setModel(IAnnotationModel)
516 public void setModel(IAnnotationModel model
) {
517 if (model
!= fModel
) {
520 fModel
.removeAnnotationModelListener(fInternalListener
);
525 fModel
.addAnnotationModelListener(fInternalListener
);
532 * @see IVerticalRuler#getModel()
534 public IAnnotationModel
getModel() {
539 * @see IVerticalRulerInfo#getWidth()
541 public int getWidth() {
546 * @see IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
548 public int getLineOfLastMouseButtonActivity() {
549 return fLastMouseButtonActivityLine
;
553 * @see IVerticalRulerInfo#toDocumentLineNumber(int)
555 public int toDocumentLineNumber(int y_coordinate
) {
556 if (fTextViewer
== null || y_coordinate
== -1)
559 StyledText text
= fTextViewer
.getTextWidget();
560 int line
= text
.getLineIndex(y_coordinate
);
562 if (line
== text
.getLineCount() - 1) {
563 // check whether y_coordinate exceeds last line
564 if (y_coordinate
> text
.getLinePixel(line
+ 1))
568 return widgetLine2ModelLine(fTextViewer
, line
);
572 * Returns the line of the viewer's document that corresponds to the given widget line.
574 * @param viewer the viewer
575 * @param widgetLine the widget line
576 * @return the corresponding line of the viewer's document
579 protected final static int widgetLine2ModelLine(ITextViewer viewer
, int widgetLine
) {
581 if (viewer
instanceof ITextViewerExtension5
) {
582 ITextViewerExtension5 extension
= (ITextViewerExtension5
) viewer
;
583 return extension
.widgetLine2ModelLine(widgetLine
);
587 IRegion r
= viewer
.getVisibleRegion();
588 IDocument d
= viewer
.getDocument();
589 return widgetLine
+= d
.getLineOfOffset(r
.getOffset());
590 } catch (BadLocationException x
) {
596 * @see IVerticalRulerExtension#setFont(Font)
599 public void setFont(Font font
) {
603 * @see IVerticalRulerExtension#setLocationOfLastMouseButtonActivity(int, int)
606 public void setLocationOfLastMouseButtonActivity(int x
, int y
) {
607 fLastMouseButtonActivityLine
= toDocumentLineNumber(y
);
611 * Adds the given mouse listener.
613 * @param listener the listener to be added
614 * @deprecated will be removed
617 public void addMouseListener(MouseListener listener
) {
618 if (fCanvas
!= null && !fCanvas
.isDisposed())
619 fCanvas
.addMouseListener(listener
);
623 * Removes the given mouse listener.
625 * @param listener the listener to be removed
626 * @deprecated will be removed
629 public void removeMouseListener(MouseListener listener
) {
630 if (fCanvas
!= null && !fCanvas
.isDisposed())
631 fCanvas
.removeMouseListener(listener
);
634 public void setIcon(Image icon
) {