had to rename the packages to make them compliant with buckminster
[taxeditor.git] / eu.etaxonomy.taxeditor.editor / src / main / java / eu / etaxonomy / taxeditor / editor / name / container / RulerWithIcon.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
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.
8 */
9
10 package eu.etaxonomy.taxeditor.editor.name.container;
11
12 import java.util.Iterator;
13
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;
52
53 /**
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.
56 * <p>
57 * Changes made in method doubleBufferPaint(doubleBufferPaint(GC dest)).
58 *
59 * @see org.eclipse.jface.text.source.VerticalRuler
60 * @author p.ciardelli
61 * @created 27.01.2009
62 * @version 1.0
63 */
64 public class RulerWithIcon implements IVerticalRuler, IVerticalRulerExtension {
65 private static final Logger logger = Logger.getLogger(RulerWithIcon.class);
66
67 /**
68 * Internal listener class.
69 */
70 class InternalListener implements IViewportListener, IAnnotationModelListener, ITextListener {
71
72 /*
73 * @see IViewportListener#viewportChanged(int)
74 */
75 public void viewportChanged(int verticalPosition) {
76 if (verticalPosition != fScrollPos)
77 redraw();
78 }
79
80 /*
81 * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
82 */
83 public void modelChanged(IAnnotationModel model) {
84 update();
85 }
86
87 /*
88 * @see ITextListener#textChanged(TextEvent)
89 */
90 public void textChanged(TextEvent e) {
91 if (fTextViewer != null && e.getViewerRedrawState())
92 redraw();
93 }
94 }
95
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 */
111 private int fWidth;
112 /**
113 * The annotation access of this vertical ruler
114 * @since 3.0
115 */
116 private IAnnotationAccess fAnnotationAccess;
117 private Image icon;
118
119 /**
120 * Constructs a vertical ruler with the given width.
121 *
122 * @param width the width of the vertical ruler
123 */
124 public RulerWithIcon(int width) {
125 this(width, null);
126 }
127
128 /**
129 * Constructs a vertical ruler with the given width and the given annotation
130 * access.
131 *
132 * @param width the width of the vertical ruler
133 * @param annotationAcccess the annotation access
134 * @since 3.0
135 */
136 public RulerWithIcon(int width, IAnnotationAccess annotationAcccess) {
137 fWidth= width;
138 fAnnotationAccess= annotationAcccess;
139 }
140
141 /*
142 * @see IVerticalRuler#getControl()
143 */
144 /**
145 * <p>getControl</p>
146 *
147 * @return a {@link org.eclipse.swt.widgets.Control} object.
148 */
149 public Control getControl() {
150 return fCanvas;
151 }
152
153 /*
154 * @see IVerticalRuler#createControl(Composite, ITextViewer)
155 */
156 /** {@inheritDoc} */
157 public Control createControl(Composite parent, ITextViewer textViewer) {
158
159 fTextViewer= textViewer;
160
161 fCanvas= new Canvas(parent, SWT.NO_BACKGROUND);
162
163 TableWrapData layout = new TableWrapData(TableWrapData.LEFT);
164 layout.heightHint = 20;
165 layout.maxWidth = fWidth;
166 fCanvas.setLayoutData(layout);
167
168 fCanvas.addPaintListener(new PaintListener() {
169 public void paintControl(PaintEvent event) {
170 if (fTextViewer != null)
171 doubleBufferPaint(event.gc);
172 }
173 });
174
175 fCanvas.addDisposeListener(new DisposeListener() {
176 public void widgetDisposed(DisposeEvent e) {
177 handleDispose();
178 fTextViewer= null;
179 }
180 });
181
182 fCanvas.addMouseListener(new MouseListener() {
183 public void mouseUp(MouseEvent event) {
184 }
185
186 public void mouseDown(MouseEvent event) {
187 fLastMouseButtonActivityLine= toDocumentLineNumber(event.y);
188 }
189
190 public void mouseDoubleClick(MouseEvent event) {
191 fLastMouseButtonActivityLine= toDocumentLineNumber(event.y);
192 }
193 });
194
195 if (fTextViewer != null) {
196 fTextViewer.addViewportListener(fInternalListener);
197 fTextViewer.addTextListener(fInternalListener);
198 }
199
200 return fCanvas;
201 }
202
203 /**
204 * Disposes the ruler's resources.
205 */
206 private void handleDispose() {
207
208 if (fTextViewer != null) {
209 fTextViewer.removeViewportListener(fInternalListener);
210 fTextViewer.removeTextListener(fInternalListener);
211 fTextViewer= null;
212 }
213
214 if (fModel != null)
215 fModel.removeAnnotationModelListener(fInternalListener);
216
217 if (fBuffer != null) {
218 fBuffer.dispose();
219 fBuffer= null;
220 }
221 }
222
223
224 /**
225 * Double buffer drawing.
226 *
227 * @param dest the GC to draw into
228 */
229 private void doubleBufferPaint(GC dest) {
230
231 Point size= fCanvas.getSize();
232
233 if (size.x <= 0 || size.y <= 0)
234 return;
235
236 if (fBuffer != null) {
237 Rectangle r= fBuffer.getBounds();
238 if (r.width != size.x || r.height != size.y) {
239 fBuffer.dispose();
240 fBuffer= null;
241 }
242 }
243 if (fBuffer == null)
244 fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y);
245
246 GC gc= new GC(fBuffer);
247 gc.setFont(fTextViewer.getTextWidget().getFont());
248 try {
249 gc.setBackground(fCanvas.getBackground());
250 gc.fillRectangle(0, 0, size.x, size.y);
251
252 // Code added to VerticalRuler starts here
253 if (icon != null) {
254
255 Rectangle r = icon.getBounds();
256
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.");
259 } else {
260
261 // Set destination coordinates to center icon
262 int x = (size.x - r.width) / 2;
263 int y = (size.y - r.height) / 2;
264
265 gc.drawImage(icon, 0, 0, r.width, r.height, x, y, r.width, r.height);
266 }
267 }
268 // Code added to VerticalRuler ends here
269
270 if (fTextViewer instanceof ITextViewerExtension5)
271 doPaint1(gc);
272 else
273 doPaint(gc);
274
275 } finally {
276 gc.dispose();
277 }
278
279 dest.drawImage(fBuffer, 0, 0);
280 }
281
282 /**
283 * Returns the document offset of the upper left corner of the
284 * widgets view port, possibly including partially visible lines.
285 *
286 * @return the document offset of the upper left corner including partially visible lines
287 * @since 2.0
288 */
289 private int getInclusiveTopIndexStartOffset() {
290
291 StyledText textWidget= fTextViewer.getTextWidget();
292 if (textWidget != null && !textWidget.isDisposed()) {
293 int top= JFaceTextUtil.getPartialTopIndex(fTextViewer);
294 try {
295 IDocument document= fTextViewer.getDocument();
296 return document.getLineOffset(top);
297 } catch (BadLocationException x) {
298 }
299 }
300
301 return -1;
302 }
303
304
305
306 /**
307 * Draws the vertical ruler w/o drawing the Canvas background.
308 *
309 * @param gc the GC to draw into
310 */
311 protected void doPaint(GC gc) {
312
313 if (fModel == null || fTextViewer == null)
314 return;
315
316 IAnnotationAccessExtension annotationAccessExtension= null;
317 if (fAnnotationAccess instanceof IAnnotationAccessExtension)
318 annotationAccessExtension= (IAnnotationAccessExtension) fAnnotationAccess;
319
320 StyledText styledText= fTextViewer.getTextWidget();
321 IDocument doc= fTextViewer.getDocument();
322
323 int topLeft= getInclusiveTopIndexStartOffset();
324 int bottomRight= fTextViewer.getBottomIndexEndOffset();
325 int viewPort= bottomRight - topLeft;
326
327 Point d= fCanvas.getSize();
328 fScrollPos= styledText.getTopPixel();
329
330 int topLine= -1, bottomLine= -1;
331 try {
332 IRegion region= fTextViewer.getVisibleRegion();
333 topLine= doc.getLineOfOffset(region.getOffset());
334 bottomLine= doc.getLineOfOffset(region.getOffset() + region.getLength());
335 } catch (BadLocationException x) {
336 return;
337 }
338
339 // draw Annotations
340 Rectangle r= new Rectangle(0, 0, 0, 0);
341 int maxLayer= 1; // loop at least once though layers.
342
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();
348
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();
355 }
356 maxLayer= Math.max(maxLayer, lay+1); // dynamically update layer maximum
357 if (lay != layer) // wrong layer: skip annotation
358 continue;
359
360 Position position= fModel.getPosition(annotation);
361 if (position == null)
362 continue;
363
364 if (!position.overlapsWith(topLeft, viewPort))
365 continue;
366
367 try {
368
369 int offset= position.getOffset();
370 int length= position.getLength();
371
372 int startLine= doc.getLineOfOffset(offset);
373 if (startLine < topLine)
374 startLine= topLine;
375
376 int endLine= startLine;
377 if (length > 0)
378 endLine= doc.getLineOfOffset(offset + length - 1);
379 if (endLine > bottomLine)
380 endLine= bottomLine;
381
382 startLine -= topLine;
383 endLine -= topLine;
384
385 r.x= 0;
386 r.y= JFaceTextUtil.computeLineHeight(styledText, 0, startLine, startLine) - fScrollPos;
387
388 r.width= d.x;
389 int lines= endLine - startLine;
390
391 r.height= JFaceTextUtil.computeLineHeight(styledText, startLine, endLine + 1, (lines+1));
392
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);
397
398 } catch (BadLocationException e) {
399 }
400 }
401 }
402 }
403
404 /**
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>.
408 *
409 * @param gc the GC to draw into
410 */
411 protected void doPaint1(GC gc) {
412
413 if (fModel == null || fTextViewer == null)
414 return;
415
416 IAnnotationAccessExtension annotationAccessExtension= null;
417 if (fAnnotationAccess instanceof IAnnotationAccessExtension)
418 annotationAccessExtension= (IAnnotationAccessExtension) fAnnotationAccess;
419
420 ITextViewerExtension5 extension= (ITextViewerExtension5) fTextViewer;
421 StyledText textWidget= fTextViewer.getTextWidget();
422
423 fScrollPos= textWidget.getTopPixel();
424 Point dimension= fCanvas.getSize();
425
426 // draw Annotations
427 Rectangle r= new Rectangle(0, 0, 0, 0);
428 int maxLayer= 1; // loop at least once through layers.
429
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();
435
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();
442 }
443 maxLayer= Math.max(maxLayer, lay+1); // dynamically update layer maximum
444 if (lay != layer) // wrong layer: skip annotation
445 continue;
446
447 Position position= fModel.getPosition(annotation);
448 if (position == null)
449 continue;
450
451 IRegion widgetRegion= extension.modelRange2WidgetRange(new Region(position.getOffset(), position.getLength()));
452 if (widgetRegion == null)
453 continue;
454
455 int startLine= extension.widgetLineOfWidgetOffset(widgetRegion.getOffset());
456 if (startLine == -1)
457 continue;
458
459 int endLine= extension.widgetLineOfWidgetOffset(widgetRegion.getOffset() + Math.max(widgetRegion.getLength() -1, 0));
460 if (endLine == -1)
461 continue;
462
463 r.x= 0;
464 r.y= JFaceTextUtil.computeLineHeight(textWidget, 0, startLine, startLine) - fScrollPos;
465
466 r.width= dimension.x;
467 int lines= endLine - startLine;
468
469 r.height= JFaceTextUtil.computeLineHeight(textWidget, startLine, endLine + 1, lines+1);
470
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);
475 }
476 }
477 }
478
479 /**
480 * Thread-safe implementation.
481 * Can be called from any thread.
482 */
483 /*
484 * @see IVerticalRuler#update()
485 */
486 public void update() {
487 if (fCanvas != null && !fCanvas.isDisposed()) {
488 Display d= fCanvas.getDisplay();
489 if (d != null) {
490 d.asyncExec(new Runnable() {
491 public void run() {
492 redraw();
493 }
494 });
495 }
496 }
497 }
498
499 /**
500 * Redraws the vertical ruler.
501 */
502 private void redraw() {
503 if (fCanvas != null && !fCanvas.isDisposed()) {
504 GC gc= new GC(fCanvas);
505 doubleBufferPaint(gc);
506 gc.dispose();
507 }
508 }
509
510 /*
511 * @see IVerticalRuler#setModel(IAnnotationModel)
512 */
513 /** {@inheritDoc} */
514 public void setModel(IAnnotationModel model) {
515 if (model != fModel) {
516
517 if (fModel != null)
518 fModel.removeAnnotationModelListener(fInternalListener);
519
520 fModel= model;
521
522 if (fModel != null)
523 fModel.addAnnotationModelListener(fInternalListener);
524
525 update();
526 }
527 }
528
529 /*
530 * @see IVerticalRuler#getModel()
531 */
532 /**
533 * <p>getModel</p>
534 *
535 * @return a {@link org.eclipse.jface.text.source.IAnnotationModel} object.
536 */
537 public IAnnotationModel getModel() {
538 return fModel;
539 }
540
541 /*
542 * @see IVerticalRulerInfo#getWidth()
543 */
544 /**
545 * <p>getWidth</p>
546 *
547 * @return a int.
548 */
549 public int getWidth() {
550 return fWidth;
551 }
552
553 /*
554 * @see IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
555 */
556 /**
557 * <p>getLineOfLastMouseButtonActivity</p>
558 *
559 * @return a int.
560 */
561 public int getLineOfLastMouseButtonActivity() {
562 return fLastMouseButtonActivityLine;
563 }
564
565 /*
566 * @see IVerticalRulerInfo#toDocumentLineNumber(int)
567 */
568 /** {@inheritDoc} */
569 public int toDocumentLineNumber(int y_coordinate) {
570 if (fTextViewer == null || y_coordinate == -1)
571 return -1;
572
573 StyledText text= fTextViewer.getTextWidget();
574 int line= text.getLineIndex(y_coordinate);
575
576 if (line == text.getLineCount() - 1) {
577 // check whether y_coordinate exceeds last line
578 if (y_coordinate > text.getLinePixel(line + 1))
579 return -1;
580 }
581
582 return widgetLine2ModelLine(fTextViewer, line);
583 }
584
585 /**
586 * Returns the line of the viewer's document that corresponds to the given widget line.
587 *
588 * @param viewer the viewer
589 * @param widgetLine the widget line
590 * @return the corresponding line of the viewer's document
591 * @since 2.1
592 */
593 protected final static int widgetLine2ModelLine(ITextViewer viewer, int widgetLine) {
594
595 if (viewer instanceof ITextViewerExtension5) {
596 ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
597 return extension.widgetLine2ModelLine(widgetLine);
598 }
599
600 try {
601 IRegion r= viewer.getVisibleRegion();
602 IDocument d= viewer.getDocument();
603 return widgetLine += d.getLineOfOffset(r.getOffset());
604 } catch (BadLocationException x) {
605 }
606 return widgetLine;
607 }
608
609 /*
610 * @see IVerticalRulerExtension#setFont(Font)
611 * @since 2.0
612 */
613 /** {@inheritDoc} */
614 public void setFont(Font font) {
615 }
616
617 /*
618 * @see IVerticalRulerExtension#setLocationOfLastMouseButtonActivity(int, int)
619 * @since 2.0
620 */
621 /** {@inheritDoc} */
622 public void setLocationOfLastMouseButtonActivity(int x, int y) {
623 fLastMouseButtonActivityLine= toDocumentLineNumber(y);
624 }
625
626 /**
627 * Adds the given mouse listener.
628 *
629 * @param listener the listener to be added
630 * @deprecated will be removed
631 * @since 2.0
632 */
633 public void addMouseListener(MouseListener listener) {
634 if (fCanvas != null && !fCanvas.isDisposed())
635 fCanvas.addMouseListener(listener);
636 }
637
638 /**
639 * Removes the given mouse listener.
640 *
641 * @param listener the listener to be removed
642 * @deprecated will be removed
643 * @since 2.0
644 */
645 public void removeMouseListener(MouseListener listener) {
646 if (fCanvas != null && !fCanvas.isDisposed())
647 fCanvas.removeMouseListener(listener);
648 }
649
650 /**
651 * <p>Setter for the field <code>icon</code>.</p>
652 *
653 * @param icon a {@link org.eclipse.swt.graphics.Image} object.
654 */
655 public void setIcon(Image icon) {
656 this.icon = icon;
657 redraw();
658 }
659 }
660