KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > text > source > OverviewRuler


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jface.text.source;
12
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.HashMap JavaDoc;
16 import java.util.HashSet JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.List JavaDoc;
19 import java.util.Map JavaDoc;
20 import java.util.Set JavaDoc;
21
22 import org.eclipse.swt.SWT;
23 import org.eclipse.swt.custom.StyledText;
24 import org.eclipse.swt.events.DisposeEvent;
25 import org.eclipse.swt.events.DisposeListener;
26 import org.eclipse.swt.events.MouseAdapter;
27 import org.eclipse.swt.events.MouseEvent;
28 import org.eclipse.swt.events.MouseMoveListener;
29 import org.eclipse.swt.events.MouseTrackAdapter;
30 import org.eclipse.swt.events.PaintEvent;
31 import org.eclipse.swt.events.PaintListener;
32 import org.eclipse.swt.graphics.Color;
33 import org.eclipse.swt.graphics.Cursor;
34 import org.eclipse.swt.graphics.GC;
35 import org.eclipse.swt.graphics.Image;
36 import org.eclipse.swt.graphics.Point;
37 import org.eclipse.swt.graphics.RGB;
38 import org.eclipse.swt.graphics.Rectangle;
39 import org.eclipse.swt.widgets.Canvas;
40 import org.eclipse.swt.widgets.Composite;
41 import org.eclipse.swt.widgets.Control;
42 import org.eclipse.swt.widgets.Display;
43
44 import org.eclipse.jface.text.BadLocationException;
45 import org.eclipse.jface.text.IDocument;
46 import org.eclipse.jface.text.IRegion;
47 import org.eclipse.jface.text.ITextListener;
48 import org.eclipse.jface.text.ITextViewer;
49 import org.eclipse.jface.text.ITextViewerExtension5;
50 import org.eclipse.jface.text.JFaceTextUtil;
51 import org.eclipse.jface.text.Position;
52 import org.eclipse.jface.text.Region;
53 import org.eclipse.jface.text.TextEvent;
54 import org.eclipse.jface.text.source.projection.AnnotationBag;
55
56
57
58 /**
59  * Ruler presented next to a source viewer showing all annotations of the
60  * viewer's annotation model in a compact format. The ruler has the same height
61  * as the source viewer.
62  * <p>
63  * Clients usually instantiate and configure objects of this class.</p>
64  *
65  * @since 2.1
66  */

67 public class OverviewRuler implements IOverviewRuler {
68
69     /**
70      * Internal listener class.
71      */

72     class InternalListener implements ITextListener, IAnnotationModelListener, IAnnotationModelListenerExtension {
73
74         /*
75          * @see ITextListener#textChanged
76          */

77         public void textChanged(TextEvent e) {
78             if (fTextViewer != null && e.getDocumentEvent() == null && e.getViewerRedrawState()) {
79                 // handle only changes of visible document
80
redraw();
81             }
82         }
83
84         /*
85          * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
86          */

87         public void modelChanged(IAnnotationModel model) {
88             update();
89         }
90
91         /*
92          * @see org.eclipse.jface.text.source.IAnnotationModelListenerExtension#modelChanged(org.eclipse.jface.text.source.AnnotationModelEvent)
93          * @since 3.3
94          */

95         public void modelChanged(AnnotationModelEvent event) {
96             if (!event.isValid())
97                 return;
98             
99             if (event.isWorldChange()) {
100                 update();
101                 return;
102             }
103             
104             Annotation[] annotations= event.getAddedAnnotations();
105             int length= annotations.length;
106             for (int i= 0; i < length; i++) {
107                 if (!skip(annotations[i].getType())) {
108                     update();
109                     return;
110                 }
111             }
112             
113             annotations= event.getRemovedAnnotations();
114             length= annotations.length;
115             for (int i= 0; i < length; i++) {
116                 if (!skip(annotations[i].getType())) {
117                     update();
118                     return;
119                 }
120             }
121             
122             annotations= event.getChangedAnnotations();
123             length= annotations.length;
124             for (int i= 0; i < length; i++) {
125                 if (!skip(annotations[i].getType())) {
126                     update();
127                     return;
128                 }
129             }
130             
131         }
132     }
133
134     /**
135      * Enumerates the annotations of a specified type and characteristics
136      * of the associated annotation model.
137      */

138     class FilterIterator implements Iterator JavaDoc {
139
140         final static int TEMPORARY= 1 << 1;
141         final static int PERSISTENT= 1 << 2;
142         final static int IGNORE_BAGS= 1 << 3;
143
144         private Iterator JavaDoc fIterator;
145         private Object JavaDoc fType;
146         private Annotation fNext;
147         private int fStyle;
148
149         /**
150          * Creates a new filter iterator with the given specification.
151          *
152          * @param annotationType the annotation type
153          * @param style the style
154          */

155         public FilterIterator(Object JavaDoc annotationType, int style) {
156             fType= annotationType;
157             fStyle= style;
158             if (fModel != null) {
159                 fIterator= fModel.getAnnotationIterator();
160                 skip();
161             }
162         }
163
164         /**
165          * Creates a new filter iterator with the given specification.
166          *
167          * @param annotationType the annotation type
168          * @param style the style
169          * @param iterator the iterator
170          */

171         public FilterIterator(Object JavaDoc annotationType, int style, Iterator JavaDoc iterator) {
172             fType= annotationType;
173             fStyle= style;
174             fIterator= iterator;
175             skip();
176         }
177
178         private void skip() {
179
180             boolean temp= (fStyle & TEMPORARY) != 0;
181             boolean pers= (fStyle & PERSISTENT) != 0;
182             boolean ignr= (fStyle & IGNORE_BAGS) != 0;
183
184             while (fIterator.hasNext()) {
185                 Annotation next= (Annotation) fIterator.next();
186
187                 if (next.isMarkedDeleted())
188                     continue;
189
190                 if (ignr && (next instanceof AnnotationBag))
191                     continue;
192
193                 fNext= next;
194                 Object JavaDoc annotationType= next.getType();
195                 if (fType == null || isSubtype(annotationType)) {
196                     if (temp && pers) return;
197                     if (pers && next.isPersistent()) return;
198                     if (temp && !next.isPersistent()) return;
199                 }
200             }
201             fNext= null;
202         }
203
204         private boolean isSubtype(Object JavaDoc annotationType) {
205             if (fAnnotationAccess instanceof IAnnotationAccessExtension) {
206                 IAnnotationAccessExtension extension= (IAnnotationAccessExtension) fAnnotationAccess;
207                 return extension.isSubtype(annotationType, fType);
208             }
209             return fType.equals(annotationType);
210         }
211
212         /*
213          * @see Iterator#hasNext()
214          */

215         public boolean hasNext() {
216             return fNext != null;
217         }
218         /*
219          * @see Iterator#next()
220          */

221         public Object JavaDoc next() {
222             try {
223                 return fNext;
224             } finally {
225                 if (fIterator != null)
226                     skip();
227             }
228         }
229         /*
230          * @see Iterator#remove()
231          */

232         public void remove() {
233             throw new UnsupportedOperationException JavaDoc();
234         }
235     }
236
237     /**
238      * The painter of the overview ruler's header.
239      */

240     class HeaderPainter implements PaintListener {
241
242         private Color fIndicatorColor;
243         private Color fSeparatorColor;
244
245         /**
246          * Creates a new header painter.
247          */

248         public HeaderPainter() {
249             fSeparatorColor= fHeader.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
250         }
251
252         /**
253          * Sets the header color.
254          *
255          * @param color the header color
256          */

257         public void setColor(Color color) {
258             fIndicatorColor= color;
259         }
260
261         private void drawBevelRect(GC gc, int x, int y, int w, int h, Color topLeft, Color bottomRight) {
262             gc.setForeground(topLeft == null ? fSeparatorColor : topLeft);
263             gc.drawLine(x, y, x + w -1, y);
264             gc.drawLine(x, y, x, y + h -1);
265
266             gc.setForeground(bottomRight == null ? fSeparatorColor : bottomRight);
267             gc.drawLine(x + w, y, x + w, y + h);
268             gc.drawLine(x, y + h, x + w, y + h);
269         }
270
271         public void paintControl(PaintEvent e) {
272
273             Point s= fHeader.getSize();
274
275             if (fIndicatorColor != null) {
276                 e.gc.setBackground(fIndicatorColor);
277                 Rectangle r= new Rectangle(INSET, (s.y - (2*ANNOTATION_HEIGHT)) / 2, s.x - (2*INSET), 2*ANNOTATION_HEIGHT);
278                 e.gc.fillRectangle(r);
279                 Display d= fHeader.getDisplay();
280                 if (d != null)
281 // drawBevelRect(e.gc, r.x, r.y, r.width -1, r.height -1, d.getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW), d.getSystemColor(SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW));
282
drawBevelRect(e.gc, r.x, r.y, r.width -1, r.height -1, null, null);
283             }
284
285             e.gc.setForeground(fSeparatorColor);
286             e.gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance
287
e.gc.drawLine(0, s.y -1, s.x -1, s.y -1);
288         }
289     }
290
291     private static final int INSET= 2;
292     private static final int ANNOTATION_HEIGHT= 4;
293     private static boolean ANNOTATION_HEIGHT_SCALABLE= true;
294
295
296     /** The model of the overview ruler */
297     private IAnnotationModel fModel;
298     /** The view to which this ruler is connected */
299     private ITextViewer fTextViewer;
300     /** The ruler's canvas */
301     private Canvas fCanvas;
302     /** The ruler's header */
303     private Canvas fHeader;
304     /** The buffer for double buffering */
305     private Image fBuffer;
306     /** The internal listener */
307     private InternalListener fInternalListener= new InternalListener();
308     /** The width of this vertical ruler */
309     private int fWidth;
310     /** The hit detection cursor */
311     private Cursor fHitDetectionCursor;
312     /** The last cursor */
313     private Cursor fLastCursor;
314     /** The line of the last mouse button activity */
315     private int fLastMouseButtonActivityLine= -1;
316     /** The actual annotation height */
317     private int fAnnotationHeight= -1;
318     /** The annotation access */
319     private IAnnotationAccess fAnnotationAccess;
320     /** The header painter */
321     private HeaderPainter fHeaderPainter;
322     /**
323      * The list of annotation types to be shown in this ruler.
324      * @since 3.0
325      */

326     private Set JavaDoc fConfiguredAnnotationTypes= new HashSet JavaDoc();
327     /**
328      * The list of annotation types to be shown in the header of this ruler.
329      * @since 3.0
330      */

331     private Set JavaDoc fConfiguredHeaderAnnotationTypes= new HashSet JavaDoc();
332     /** The mapping between annotation types and colors */
333     private Map JavaDoc fAnnotationTypes2Colors= new HashMap JavaDoc();
334     /** The color manager */
335     private ISharedTextColors fSharedTextColors;
336     /**
337      * All available annotation types sorted by layer.
338      *
339      * @since 3.0
340      */

341     private List JavaDoc fAnnotationsSortedByLayer= new ArrayList JavaDoc();
342     /**
343      * All available layers sorted by layer.
344      * This list may contain duplicates.
345      * @since 3.0
346      */

347     private List JavaDoc fLayersSortedByLayer= new ArrayList JavaDoc();
348     /**
349      * Map of allowed annotation types.
350      * An allowed annotation type maps to <code>true</code>, a disallowed
351      * to <code>false</code>.
352      * @since 3.0
353      */

354     private Map JavaDoc fAllowedAnnotationTypes= new HashMap JavaDoc();
355     /**
356      * Map of allowed header annotation types.
357      * An allowed annotation type maps to <code>true</code>, a disallowed
358      * to <code>false</code>.
359      * @since 3.0
360      */

361     private Map JavaDoc fAllowedHeaderAnnotationTypes= new HashMap JavaDoc();
362     /**
363      * The cached annotations.
364      * @since 3.0
365      */

366     private List JavaDoc fCachedAnnotations= new ArrayList JavaDoc();
367     
368     /**
369      * Redraw runnable lock
370      * @since 3.3
371      */

372     private Object JavaDoc fRunnableLock= new Object JavaDoc();
373     /**
374      * Redraw runnable state
375      * @since 3.3
376      */

377     private boolean fIsRunnablePosted= false;
378     /**
379      * Redraw runnable
380      * @since 3.3
381      */

382     private Runnable JavaDoc fRunnable= new Runnable JavaDoc() {
383         public void run() {
384             synchronized (fRunnableLock) {
385                 fIsRunnablePosted= false;
386             }
387             redraw();
388             updateHeader();
389         }
390     };
391
392
393     /**
394      * Constructs a overview ruler of the given width using the given annotation access and the given
395      * color manager.
396      *
397      * @param annotationAccess the annotation access
398      * @param width the width of the vertical ruler
399      * @param sharedColors the color manager
400      */

401     public OverviewRuler(IAnnotationAccess annotationAccess, int width, ISharedTextColors sharedColors) {
402         fAnnotationAccess= annotationAccess;
403         fWidth= width;
404         fSharedTextColors= sharedColors;
405     }
406
407     /*
408      * @see org.eclipse.jface.text.source.IVerticalRulerInfo#getControl()
409      */

410     public Control getControl() {
411         return fCanvas;
412     }
413
414     /*
415      * @see org.eclipse.jface.text.source.IVerticalRulerInfo#getWidth()
416      */

417     public int getWidth() {
418         return fWidth;
419     }
420
421     /*
422      * @see org.eclipse.jface.text.source.IVerticalRuler#setModel(org.eclipse.jface.text.source.IAnnotationModel)
423      */

424     public void setModel(IAnnotationModel model) {
425         if (model != fModel || model != null) {
426
427             if (fModel != null)
428                 fModel.removeAnnotationModelListener(fInternalListener);
429
430             fModel= model;
431
432             if (fModel != null)
433                 fModel.addAnnotationModelListener(fInternalListener);
434
435             update();
436         }
437     }
438
439     /*
440      * @see org.eclipse.jface.text.source.IVerticalRuler#createControl(org.eclipse.swt.widgets.Composite, org.eclipse.jface.text.ITextViewer)
441      */

442     public Control createControl(Composite parent, ITextViewer textViewer) {
443
444         fTextViewer= textViewer;
445
446         fHitDetectionCursor= new Cursor(parent.getDisplay(), SWT.CURSOR_HAND);
447
448         fHeader= new Canvas(parent, SWT.NONE);
449
450         if (fAnnotationAccess instanceof IAnnotationAccessExtension) {
451             fHeader.addMouseTrackListener(new MouseTrackAdapter() {
452                 /*
453                  * @see org.eclipse.swt.events.MouseTrackAdapter#mouseHover(org.eclipse.swt.events.MouseEvent)
454                  * @since 3.3
455                  */

456                 public void mouseEnter(MouseEvent e) {
457                     updateHeaderToolTipText();
458                 }
459             });
460         }
461
462         fCanvas= new Canvas(parent, SWT.NO_BACKGROUND);
463
464         fCanvas.addPaintListener(new PaintListener() {
465             public void paintControl(PaintEvent event) {
466                 if (fTextViewer != null)
467                     doubleBufferPaint(event.gc);
468             }
469         });
470
471         fCanvas.addDisposeListener(new DisposeListener() {
472             public void widgetDisposed(DisposeEvent event) {
473                 handleDispose();
474                 fTextViewer= null;
475             }
476         });
477
478         fCanvas.addMouseListener(new MouseAdapter() {
479             public void mouseDown(MouseEvent event) {
480                 handleMouseDown(event);
481             }
482         });
483
484         fCanvas.addMouseMoveListener(new MouseMoveListener() {
485             public void mouseMove(MouseEvent event) {
486                 handleMouseMove(event);
487             }
488         });
489
490         if (fTextViewer != null)
491             fTextViewer.addTextListener(fInternalListener);
492
493         return fCanvas;
494     }
495
496     /**
497      * Disposes the ruler's resources.
498      */

499     private void handleDispose() {
500
501         if (fTextViewer != null) {
502             fTextViewer.removeTextListener(fInternalListener);
503             fTextViewer= null;
504         }
505
506         if (fModel != null)
507             fModel.removeAnnotationModelListener(fInternalListener);
508
509         if (fBuffer != null) {
510             fBuffer.dispose();
511             fBuffer= null;
512         }
513
514         if (fHitDetectionCursor != null) {
515             fHitDetectionCursor.dispose();
516             fHitDetectionCursor= null;
517         }
518
519         fConfiguredAnnotationTypes.clear();
520         fAllowedAnnotationTypes.clear();
521         fConfiguredHeaderAnnotationTypes.clear();
522         fAllowedHeaderAnnotationTypes.clear();
523         fAnnotationTypes2Colors.clear();
524         fAnnotationsSortedByLayer.clear();
525         fLayersSortedByLayer.clear();
526     }
527
528     /**
529      * Double buffer drawing.
530      *
531      * @param dest the GC to draw into
532      */

533     private void doubleBufferPaint(GC dest) {
534
535         Point size= fCanvas.getSize();
536
537         if (size.x <= 0 || size.y <= 0)
538             return;
539
540         if (fBuffer != null) {
541             Rectangle r= fBuffer.getBounds();
542             if (r.width != size.x || r.height != size.y) {
543                 fBuffer.dispose();
544                 fBuffer= null;
545             }
546         }
547         if (fBuffer == null)
548             fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y);
549
550         GC gc= new GC(fBuffer);
551         try {
552             gc.setBackground(fCanvas.getBackground());
553             gc.fillRectangle(0, 0, size.x, size.y);
554
555             cacheAnnotations();
556             
557             if (fTextViewer instanceof ITextViewerExtension5)
558                 doPaint1(gc);
559             else
560                 doPaint(gc);
561
562         } finally {
563             gc.dispose();
564         }
565
566         dest.drawImage(fBuffer, 0, 0);
567     }
568
569     /**
570      * Draws this overview ruler.
571      *
572      * @param gc the GC to draw into
573      */

574     private void doPaint(GC gc) {
575
576         Rectangle r= new Rectangle(0, 0, 0, 0);
577         int yy, hh= ANNOTATION_HEIGHT;
578
579         IDocument document= fTextViewer.getDocument();
580         IRegion visible= fTextViewer.getVisibleRegion();
581
582         StyledText textWidget= fTextViewer.getTextWidget();
583         int maxLines= textWidget.getLineCount();
584
585         Point size= fCanvas.getSize();
586         int writable= JFaceTextUtil.computeLineHeight(textWidget, 0, maxLines, maxLines);
587         
588         if (size.y > writable)
589             size.y= Math.max(writable - fHeader.getSize().y, 0);
590
591         for (Iterator JavaDoc iterator= fAnnotationsSortedByLayer.iterator(); iterator.hasNext();) {
592             Object JavaDoc annotationType= iterator.next();
593
594             if (skip(annotationType))
595                 continue;
596
597             int[] style= new int[] { FilterIterator.PERSISTENT, FilterIterator.TEMPORARY };
598             for (int t=0; t < style.length; t++) {
599
600                 Iterator JavaDoc e= new FilterIterator(annotationType, style[t], fCachedAnnotations.iterator());
601                 Color fill= getFillColor(annotationType, style[t] == FilterIterator.TEMPORARY);
602                 Color stroke= getStrokeColor(annotationType, style[t] == FilterIterator.TEMPORARY);
603
604                 for (int i= 0; e.hasNext(); i++) {
605
606                     Annotation a= (Annotation) e.next();
607                     Position p= fModel.getPosition(a);
608
609                     if (p == null || !p.overlapsWith(visible.getOffset(), visible.getLength()))
610                         continue;
611
612                     int annotationOffset= Math.max(p.getOffset(), visible.getOffset());
613                     int annotationEnd= Math.min(p.getOffset() + p.getLength(), visible.getOffset() + visible.getLength());
614                     int annotationLength= annotationEnd - annotationOffset;
615
616                     try {
617                         if (ANNOTATION_HEIGHT_SCALABLE) {
618                             int numbersOfLines= document.getNumberOfLines(annotationOffset, annotationLength);
619                             // don't count empty trailing lines
620
IRegion lastLine= document.getLineInformationOfOffset(annotationOffset + annotationLength);
621                             if (lastLine.getOffset() == annotationOffset + annotationLength) {
622                                 numbersOfLines -= 2;
623                                 hh= (numbersOfLines * size.y) / maxLines + ANNOTATION_HEIGHT;
624                                 if (hh < ANNOTATION_HEIGHT)
625                                     hh= ANNOTATION_HEIGHT;
626                             } else
627                                 hh= ANNOTATION_HEIGHT;
628                         }
629                         fAnnotationHeight= hh;
630
631                         int startLine= textWidget.getLineAtOffset(annotationOffset - visible.getOffset());
632                         yy= Math.min((startLine * size.y) / maxLines, size.y - hh);
633
634                         if (fill != null) {
635                             gc.setBackground(fill);
636                             gc.fillRectangle(INSET, yy, size.x-(2*INSET), hh);
637                         }
638
639                         if (stroke != null) {
640                             gc.setForeground(stroke);
641                             r.x= INSET;
642                             r.y= yy;
643                             r.width= size.x - (2 * INSET);
644                             r.height= hh;
645                             gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance
646
gc.drawRectangle(r);
647                         }
648                     } catch (BadLocationException x) {
649                     }
650                 }
651             }
652         }
653     }
654
655     private void cacheAnnotations() {
656         fCachedAnnotations.clear();
657         if (fModel != null) {
658             Iterator JavaDoc iter= fModel.getAnnotationIterator();
659             while (iter.hasNext()) {
660                 Annotation annotation= (Annotation) iter.next();
661
662                 if (annotation.isMarkedDeleted())
663                     continue;
664
665                 if (skip(annotation.getType()))
666                     continue;
667
668                 fCachedAnnotations.add(annotation);
669             }
670         }
671     }
672
673     /**
674      * Draws this overview ruler. Uses <code>ITextViewerExtension5</code> for
675      * its implementation. Will replace <code>doPaint(GC)</code>.
676      *
677      * @param gc the GC to draw into
678      */

679     private void doPaint1(GC gc) {
680
681         Rectangle r= new Rectangle(0, 0, 0, 0);
682         int yy, hh= ANNOTATION_HEIGHT;
683
684         ITextViewerExtension5 extension= (ITextViewerExtension5) fTextViewer;
685         IDocument document= fTextViewer.getDocument();
686         StyledText textWidget= fTextViewer.getTextWidget();
687
688         int maxLines= textWidget.getLineCount();
689         Point size= fCanvas.getSize();
690         int writable= JFaceTextUtil.computeLineHeight(textWidget, 0, maxLines, maxLines);
691         if (size.y > writable)
692             size.y= Math.max(writable - fHeader.getSize().y, 0);
693
694         for (Iterator JavaDoc iterator= fAnnotationsSortedByLayer.iterator(); iterator.hasNext();) {
695             Object JavaDoc annotationType= iterator.next();
696
697             if (skip(annotationType))
698                 continue;
699
700             int[] style= new int[] { FilterIterator.PERSISTENT, FilterIterator.TEMPORARY };
701             for (int t=0; t < style.length; t++) {
702
703                 Iterator JavaDoc e= new FilterIterator(annotationType, style[t], fCachedAnnotations.iterator());
704                 Color fill= getFillColor(annotationType, style[t] == FilterIterator.TEMPORARY);
705                 Color stroke= getStrokeColor(annotationType, style[t] == FilterIterator.TEMPORARY);
706
707                 for (int i= 0; e.hasNext(); i++) {
708
709                     Annotation a= (Annotation) e.next();
710                     Position p= fModel.getPosition(a);
711
712                     if (p == null)
713                         continue;
714
715                     IRegion widgetRegion= extension.modelRange2WidgetRange(new Region(p.getOffset(), p.getLength()));
716                     if (widgetRegion == null)
717                         continue;
718
719                     try {
720                         if (ANNOTATION_HEIGHT_SCALABLE) {
721                             int numbersOfLines= document.getNumberOfLines(p.getOffset(), p.getLength());
722                             // don't count empty trailing lines
723
IRegion lastLine= document.getLineInformationOfOffset(p.getOffset() + p.getLength());
724                             if (lastLine.getOffset() == p.getOffset() + p.getLength()) {
725                                 numbersOfLines -= 2;
726                                 hh= (numbersOfLines * size.y) / maxLines + ANNOTATION_HEIGHT;
727                                 if (hh < ANNOTATION_HEIGHT)
728                                     hh= ANNOTATION_HEIGHT;
729                             } else
730                                 hh= ANNOTATION_HEIGHT;
731                         }
732                         fAnnotationHeight= hh;
733
734                         int startLine= textWidget.getLineAtOffset(widgetRegion.getOffset());
735                         yy= Math.min((startLine * size.y) / maxLines, size.y - hh);
736
737                         if (fill != null) {
738                             gc.setBackground(fill);
739                             gc.fillRectangle(INSET, yy, size.x-(2*INSET), hh);
740                         }
741
742                         if (stroke != null) {
743                             gc.setForeground(stroke);
744                             r.x= INSET;
745                             r.y= yy;
746                             r.width= size.x - (2 * INSET);
747                             r.height= hh;
748                             gc.setLineWidth(0); // NOTE: 0 means width is 1 but with optimized performance
749
gc.drawRectangle(r);
750                         }
751                     } catch (BadLocationException x) {
752                     }
753                 }
754             }
755         }
756     }
757
758     /*
759      * @see org.eclipse.jface.text.source.IVerticalRuler#update()
760      */

761      public void update() {
762         if (fCanvas != null && !fCanvas.isDisposed()) {
763             Display d= fCanvas.getDisplay();
764             if (d != null) {
765                 synchronized (fRunnableLock) {
766                     if (fIsRunnablePosted)
767                         return;
768                     fIsRunnablePosted= true;
769                 }
770                 d.asyncExec(fRunnable);
771             }
772         }
773     }
774
775     /**
776      * Redraws the overview ruler.
777      */

778     private void redraw() {
779         if (fTextViewer == null || fModel == null)
780             return;
781
782         if (fCanvas != null && !fCanvas.isDisposed()) {
783             GC gc= new GC(fCanvas);
784             doubleBufferPaint(gc);
785             gc.dispose();
786         }
787     }
788
789     /**
790      * Translates a given y-coordinate of this ruler into the corresponding
791      * document lines. The number of lines depends on the concrete scaling
792      * given as the ration between the height of this ruler and the length
793      * of the document.
794      *
795      * @param y_coordinate the y-coordinate
796      * @return the corresponding document lines
797      */

798     private int[] toLineNumbers(int y_coordinate) {
799
800         StyledText textWidget= fTextViewer.getTextWidget();
801         int maxLines= textWidget.getContent().getLineCount();
802
803         int rulerLength= fCanvas.getSize().y;
804         int writable= JFaceTextUtil.computeLineHeight(textWidget, 0, maxLines, maxLines);
805
806         if (rulerLength > writable)
807             rulerLength= Math.max(writable - fHeader.getSize().y, 0);
808
809         if (y_coordinate >= writable || y_coordinate >= rulerLength)
810             return new int[] {-1, -1};
811
812         int[] lines= new int[2];
813
814         int pixel0= Math.max(y_coordinate - 1, 0);
815         int pixel1= Math.min(rulerLength, y_coordinate + 1);
816         rulerLength= Math.max(rulerLength, 1);
817
818         lines[0]= (pixel0 * maxLines) / rulerLength;
819         lines[1]= (pixel1 * maxLines) / rulerLength;
820
821         if (fTextViewer instanceof ITextViewerExtension5) {
822             ITextViewerExtension5 extension= (ITextViewerExtension5) fTextViewer;
823             lines[0]= extension.widgetLine2ModelLine(lines[0]);
824             lines[1]= extension.widgetLine2ModelLine(lines[1]);
825         } else {
826             try {
827                 IRegion visible= fTextViewer.getVisibleRegion();
828                 int lineNumber= fTextViewer.getDocument().getLineOfOffset(visible.getOffset());
829                 lines[0] += lineNumber;
830                 lines[1] += lineNumber;
831             } catch (BadLocationException x) {
832             }
833         }
834
835         return lines;
836     }
837
838     /**
839      * Returns the position of the first annotation found in the given line range.
840      *
841      * @param lineNumbers the line range
842      * @param ignoreSelectedAnnotation whether to ignore the current selection
843      * @return the position of the first found annotation
844      */

845     private Position getAnnotationPosition(int[] lineNumbers, boolean ignoreSelectedAnnotation) {
846         if (lineNumbers[0] == -1)
847             return null;
848
849         Position found= null;
850
851         try {
852             IDocument d= fTextViewer.getDocument();
853             IRegion line= d.getLineInformation(lineNumbers[0]);
854
855             Point currentSelection= fTextViewer.getSelectedRange();
856
857             int start= line.getOffset();
858
859             line= d.getLineInformation(lineNumbers[lineNumbers.length - 1]);
860             int end= line.getOffset() + line.getLength();
861
862             for (int i= fAnnotationsSortedByLayer.size() -1; i >= 0; i--) {
863
864                 Object JavaDoc annotationType= fAnnotationsSortedByLayer.get(i);
865
866                 Iterator JavaDoc e= new FilterIterator(annotationType, FilterIterator.PERSISTENT | FilterIterator.TEMPORARY);
867                 while (e.hasNext() && found == null) {
868                     Annotation a= (Annotation) e.next();
869                     if (a.isMarkedDeleted())
870                         continue;
871
872                     if (skip(a.getType()))
873                         continue;
874
875                     Position p= fModel.getPosition(a);
876                     if (p == null)
877                         continue;
878
879                     int posOffset= p.getOffset();
880                     int posEnd= posOffset + p.getLength();
881                     IRegion region= d.getLineInformationOfOffset(posEnd);
882                     // trailing empty lines don't count
883
if (posEnd > posOffset && region.getOffset() == posEnd) {
884                         posEnd--;
885                         region= d.getLineInformationOfOffset(posEnd);
886                     }
887
888                     if (posOffset <= end && posEnd >= start) {
889                         if (ignoreSelectedAnnotation || currentSelection.x != posOffset || currentSelection.y != p.getLength())
890                             found= p;
891                     }
892                 }
893             }
894         } catch (BadLocationException x) {
895         }
896
897         return found;
898     }
899
900     /**
901      * Returns the line which corresponds best to one of
902      * the underlying annotations at the given y-coordinate.
903      *
904      * @param lineNumbers the line numbers
905      * @return the best matching line or <code>-1</code> if no such line can be found
906      */

907     private int findBestMatchingLineNumber(int[] lineNumbers) {
908         if (lineNumbers == null || lineNumbers.length < 1)
909             return -1;
910
911         try {
912             Position pos= getAnnotationPosition(lineNumbers, true);
913             if (pos == null)
914                 return -1;
915             return fTextViewer.getDocument().getLineOfOffset(pos.getOffset());
916         } catch (BadLocationException ex) {
917             return -1;
918         }
919     }
920
921     /**
922      * Handles mouse clicks.
923      *
924      * @param event the mouse button down event
925      */

926     private void handleMouseDown(MouseEvent event) {
927         if (fTextViewer != null) {
928             int[] lines= toLineNumbers(event.y);
929             Position p= getAnnotationPosition(lines, false);
930             if (p != null) {
931                 fTextViewer.revealRange(p.getOffset(), p.getLength());
932                 fTextViewer.setSelectedRange(p.getOffset(), p.getLength());
933             }
934             fTextViewer.getTextWidget().setFocus();
935         }
936         fLastMouseButtonActivityLine= toDocumentLineNumber(event.y);
937     }
938
939     /**
940      * Handles mouse moves.
941      *
942      * @param event the mouse move event
943      */

944     private void handleMouseMove(MouseEvent event) {
945         if (fTextViewer != null) {
946             int[] lines= toLineNumbers(event.y);
947             Position p= getAnnotationPosition(lines, true);
948             Cursor cursor= (p != null ? fHitDetectionCursor : null);
949             if (cursor != fLastCursor) {
950                 fCanvas.setCursor(cursor);
951                 fLastCursor= cursor;
952             }
953         }
954     }
955
956     /*
957      * @see org.eclipse.jface.text.source.IOverviewRuler#addAnnotationType(java.lang.Object)
958      */

959     public void addAnnotationType(Object JavaDoc annotationType) {
960         fConfiguredAnnotationTypes.add(annotationType);
961         fAllowedAnnotationTypes.clear();
962     }
963
964     /*
965      * @see org.eclipse.jface.text.source.IOverviewRuler#removeAnnotationType(java.lang.Object)
966      */

967     public void removeAnnotationType(Object JavaDoc annotationType) {
968         fConfiguredAnnotationTypes.remove(annotationType);
969         fAllowedAnnotationTypes.clear();
970     }
971
972     /*
973      * @see org.eclipse.jface.text.source.IOverviewRuler#setAnnotationTypeLayer(java.lang.Object, int)
974      */

975     public void setAnnotationTypeLayer(Object JavaDoc annotationType, int layer) {
976         int j= fAnnotationsSortedByLayer.indexOf(annotationType);
977         if (j != -1) {
978             fAnnotationsSortedByLayer.remove(j);
979             fLayersSortedByLayer.remove(j);
980         }
981
982         if (layer >= 0) {
983             int i= 0;
984             int size= fLayersSortedByLayer.size();
985             while (i < size && layer >= ((Integer JavaDoc)fLayersSortedByLayer.get(i)).intValue())
986                 i++;
987             Integer JavaDoc layerObj= new Integer JavaDoc(layer);
988             fLayersSortedByLayer.add(i, layerObj);
989             fAnnotationsSortedByLayer.add(i, annotationType);
990         }
991     }
992
993     /*
994      * @see org.eclipse.jface.text.source.IOverviewRuler#setAnnotationTypeColor(java.lang.Object, org.eclipse.swt.graphics.Color)
995      */

996     public void setAnnotationTypeColor(Object JavaDoc annotationType, Color color) {
997         if (color != null)
998             fAnnotationTypes2Colors.put(annotationType, color);
999         else
1000            fAnnotationTypes2Colors.remove(annotationType);
1001    }
1002
1003    /**
1004     * Returns whether the given annotation type should be skipped by the drawing routine.
1005     *
1006     * @param annotationType the annotation type
1007     * @return <code>true</code> if annotation of the given type should be skipped
1008     */

1009    private boolean skip(Object JavaDoc annotationType) {
1010        return !contains(annotationType, fAllowedAnnotationTypes, fConfiguredAnnotationTypes);
1011    }
1012
1013    /**
1014     * Returns whether the given annotation type should be skipped by the drawing routine of the header.
1015     *
1016     * @param annotationType the annotation type
1017     * @return <code>true</code> if annotation of the given type should be skipped
1018     * @since 3.0
1019     */

1020    private boolean skipInHeader(Object JavaDoc annotationType) {
1021        return !contains(annotationType, fAllowedHeaderAnnotationTypes, fConfiguredHeaderAnnotationTypes);
1022    }
1023
1024    /**
1025     * Returns whether the given annotation type is mapped to <code>true</code>
1026     * in the given <code>allowed</code> map or covered by the <code>configured</code>
1027     * set.
1028     *
1029     * @param annotationType the annotation type
1030     * @param allowed the map with allowed annotation types mapped to booleans
1031     * @param configured the set with configured annotation types
1032     * @return <code>true</code> if annotation is contained, <code>false</code>
1033     * otherwise
1034     * @since 3.0
1035     */

1036    private boolean contains(Object JavaDoc annotationType, Map JavaDoc allowed, Set JavaDoc configured) {
1037        Boolean JavaDoc cached= (Boolean JavaDoc) allowed.get(annotationType);
1038        if (cached != null)
1039            return cached.booleanValue();
1040
1041        boolean covered= isCovered(annotationType, configured);
1042        allowed.put(annotationType, covered ? Boolean.TRUE : Boolean.FALSE);
1043        return covered;
1044    }
1045
1046    /**
1047     * Computes whether the annotations of the given type are covered by the given <code>configured</code>
1048     * set. This is the case if either the type of the annotation or any of its
1049     * super types is contained in the <code>configured</code> set.
1050     *
1051     * @param annotationType the annotation type
1052     * @param configured the set with configured annotation types
1053     * @return <code>true</code> if annotation is covered, <code>false</code>
1054     * otherwise
1055     * @since 3.0
1056     */

1057    private boolean isCovered(Object JavaDoc annotationType, Set JavaDoc configured) {
1058        if (fAnnotationAccess instanceof IAnnotationAccessExtension) {
1059            IAnnotationAccessExtension extension= (IAnnotationAccessExtension) fAnnotationAccess;
1060            Iterator JavaDoc e= configured.iterator();
1061            while (e.hasNext()) {
1062                if (extension.isSubtype(annotationType,e.next()))
1063                    return true;
1064            }
1065            return false;
1066        }
1067        return configured.contains(annotationType);
1068    }
1069
1070    /**
1071     * Returns a specification of a color that lies between the given
1072     * foreground and background color using the given scale factor.
1073     *
1074     * @param fg the foreground color
1075     * @param bg the background color
1076     * @param scale the scale factor
1077     * @return the interpolated color
1078     */

1079    private static RGB interpolate(RGB fg, RGB bg, double scale) {
1080        return new RGB(
1081            (int) ((1.0-scale) * fg.red + scale * bg.red),
1082            (int) ((1.0-scale) * fg.green + scale * bg.green),
1083            (int) ((1.0-scale) * fg.blue + scale * bg.blue)
1084        );
1085    }
1086
1087    /**
1088     * Returns the grey value in which the given color would be drawn in grey-scale.
1089     *
1090     * @param rgb the color
1091     * @return the grey-scale value
1092     */

1093    private static double greyLevel(RGB rgb) {
1094        if (rgb.red == rgb.green && rgb.green == rgb.blue)
1095            return rgb.red;
1096        return (0.299 * rgb.red + 0.587 * rgb.green + 0.114 * rgb.blue + 0.5);
1097    }
1098
1099    /**
1100     * Returns whether the given color is dark or light depending on the colors grey-scale level.
1101     *
1102     * @param rgb the color
1103     * @return <code>true</code> if the color is dark, <code>false</code> if it is light
1104     */

1105    private static boolean isDark(RGB rgb) {
1106        return greyLevel(rgb) > 128;
1107    }
1108
1109    /**
1110     * Returns a color based on the color configured for the given annotation type and the given scale factor.
1111     *
1112     * @param annotationType the annotation type
1113     * @param scale the scale factor
1114     * @return the computed color
1115     */

1116    private Color getColor(Object JavaDoc annotationType, double scale) {
1117        Color base= findColor(annotationType);
1118        if (base == null)
1119            return null;
1120
1121        RGB baseRGB= base.getRGB();
1122        RGB background= fCanvas.getBackground().getRGB();
1123
1124        boolean darkBase= isDark(baseRGB);
1125        boolean darkBackground= isDark(background);
1126        if (darkBase && darkBackground)
1127            background= new RGB(255, 255, 255);
1128        else if (!darkBase && !darkBackground)
1129            background= new RGB(0, 0, 0);
1130
1131        return fSharedTextColors.getColor(interpolate(baseRGB, background, scale));
1132    }
1133
1134    /**
1135     * Returns the color for the given annotation type
1136     *
1137     * @param annotationType the annotation type
1138     * @return the color
1139     * @since 3.0
1140     */

1141    private Color findColor(Object JavaDoc annotationType) {
1142        Color color= (Color) fAnnotationTypes2Colors.get(annotationType);
1143        if (color != null)
1144            return color;
1145
1146        if (fAnnotationAccess instanceof IAnnotationAccessExtension) {
1147            IAnnotationAccessExtension extension= (IAnnotationAccessExtension) fAnnotationAccess;
1148            Object JavaDoc[] superTypes= extension.getSupertypes(annotationType);
1149            if (superTypes != null) {
1150                for (int i= 0; i < superTypes.length; i++) {
1151                    color= (Color) fAnnotationTypes2Colors.get(superTypes[i]);
1152                    if (color != null)
1153                        return color;
1154                }
1155            }
1156        }
1157
1158        return null;
1159    }
1160
1161    /**
1162     * Returns the stroke color for the given annotation type and characteristics.
1163     *
1164     * @param annotationType the annotation type
1165     * @param temporary <code>true</code> if for temporary annotations
1166     * @return the stroke color
1167     */

1168    private Color getStrokeColor(Object JavaDoc annotationType, boolean temporary) {
1169        return getColor(annotationType, temporary ? 0.5 : 0.2);
1170    }
1171
1172    /**
1173     * Returns the fill color for the given annotation type and characteristics.
1174     *
1175     * @param annotationType the annotation type
1176     * @param temporary <code>true</code> if for temporary annotations
1177     * @return the fill color
1178     */

1179    private Color getFillColor(Object JavaDoc annotationType, boolean temporary) {
1180        return getColor(annotationType, temporary ? 0.9 : 0.6);
1181    }
1182
1183    /*
1184     * @see IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
1185     */

1186    public int getLineOfLastMouseButtonActivity() {
1187        return fLastMouseButtonActivityLine;
1188    }
1189
1190    /*
1191     * @see IVerticalRulerInfo#toDocumentLineNumber(int)
1192     */

1193    public int toDocumentLineNumber(int y_coordinate) {
1194
1195        if (fTextViewer == null || y_coordinate == -1)
1196            return -1;
1197
1198        int[] lineNumbers= toLineNumbers(y_coordinate);
1199        int bestLine= findBestMatchingLineNumber(lineNumbers);
1200        if (bestLine == -1 && lineNumbers.length > 0)
1201            return lineNumbers[0];
1202        return bestLine;
1203    }
1204
1205    /*
1206     * @see org.eclipse.jface.text.source.IVerticalRuler#getModel()
1207     */

1208    public IAnnotationModel getModel() {
1209        return fModel;
1210    }
1211
1212    /*
1213     * @see org.eclipse.jface.text.source.IOverviewRuler#getAnnotationHeight()
1214     */

1215    public int getAnnotationHeight() {
1216        return fAnnotationHeight;
1217    }
1218
1219    /*
1220     * @see org.eclipse.jface.text.source.IOverviewRuler#hasAnnotation(int)
1221     */

1222    public boolean hasAnnotation(int y) {
1223        return findBestMatchingLineNumber(toLineNumbers(y)) != -1;
1224    }
1225
1226    /*
1227     * @see org.eclipse.jface.text.source.IOverviewRuler#getHeaderControl()
1228     */

1229    public Control getHeaderControl() {
1230        return fHeader;
1231    }
1232
1233    /*
1234     * @see org.eclipse.jface.text.source.IOverviewRuler#addHeaderAnnotationType(java.lang.Object)
1235     */

1236    public void addHeaderAnnotationType(Object JavaDoc annotationType) {
1237        fConfiguredHeaderAnnotationTypes.add(annotationType);
1238        fAllowedHeaderAnnotationTypes.clear();
1239    }
1240
1241    /*
1242     * @see org.eclipse.jface.text.source.IOverviewRuler#removeHeaderAnnotationType(java.lang.Object)
1243     */

1244    public void removeHeaderAnnotationType(Object JavaDoc annotationType) {
1245        fConfiguredHeaderAnnotationTypes.remove(annotationType);
1246        fAllowedHeaderAnnotationTypes.clear();
1247    }
1248
1249    /**
1250     * Updates the header of this ruler.
1251     */

1252    private void updateHeader() {
1253        if (fHeader == null || fHeader.isDisposed())
1254            return;
1255        
1256        fHeader.setToolTipText(null);
1257
1258        Object JavaDoc colorType= null;
1259        outer: for (int i= fAnnotationsSortedByLayer.size() -1; i >= 0; i--) {
1260            Object JavaDoc annotationType= fAnnotationsSortedByLayer.get(i);
1261            if (skipInHeader(annotationType) || skip(annotationType))
1262                continue;
1263            
1264            Iterator JavaDoc e= new FilterIterator(annotationType, FilterIterator.PERSISTENT | FilterIterator.TEMPORARY | FilterIterator.IGNORE_BAGS, fCachedAnnotations.iterator());
1265            while (e.hasNext()) {
1266                if (e.next() != null) {
1267                    colorType= annotationType;
1268                    break outer;
1269                }
1270            }
1271        }
1272
1273        Color color= null;
1274        if (colorType != null)
1275            color= findColor(colorType);
1276
1277        if (color == null) {
1278            if (fHeaderPainter != null)
1279                fHeaderPainter.setColor(null);
1280        } else {
1281            if (fHeaderPainter == null) {
1282                fHeaderPainter= new HeaderPainter();
1283                fHeader.addPaintListener(fHeaderPainter);
1284            }
1285            fHeaderPainter.setColor(color);
1286        }
1287        
1288        fHeader.redraw();
1289
1290    }
1291    
1292    /**
1293     * Updates the header tool tip text of this ruler.
1294     */

1295    private void updateHeaderToolTipText() {
1296        if (fHeader == null || fHeader.isDisposed())
1297            return;
1298        
1299        if (fHeader.getToolTipText() != null)
1300            return;
1301
1302        String JavaDoc overview= ""; //$NON-NLS-1$
1303

1304        for (int i= fAnnotationsSortedByLayer.size() -1; i >= 0; i--) {
1305
1306            Object JavaDoc annotationType= fAnnotationsSortedByLayer.get(i);
1307
1308            if (skipInHeader(annotationType) || skip(annotationType))
1309                continue;
1310
1311            int count= 0;
1312            String JavaDoc annotationTypeLabel= null;
1313
1314            Iterator JavaDoc e= new FilterIterator(annotationType, FilterIterator.PERSISTENT | FilterIterator.TEMPORARY | FilterIterator.IGNORE_BAGS, fCachedAnnotations.iterator());
1315            while (e.hasNext()) {
1316                Annotation annotation= (Annotation)e.next();
1317                if (annotation != null) {
1318                    if (annotationTypeLabel == null)
1319                        annotationTypeLabel= ((IAnnotationAccessExtension)fAnnotationAccess).getTypeLabel(annotation);
1320                    count++;
1321                }
1322            }
1323
1324            if (annotationTypeLabel != null) {
1325                if (overview.length() > 0)
1326                    overview += "\n"; //$NON-NLS-1$
1327
overview += JFaceTextMessages.getFormattedString("OverviewRulerHeader.toolTipTextEntry", new Object JavaDoc[] {annotationTypeLabel, new Integer JavaDoc(count)}); //$NON-NLS-1$
1328
}
1329        }
1330        
1331        if (overview.length() > 0)
1332            fHeader.setToolTipText(overview);
1333    }
1334}
1335
Popular Tags