Mercurial > projects > dwt-addons
comparison dwtx/jface/text/source/AnnotationRulerColumn.d @ 129:eb30df5ca28b
Added JFace Text sources
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 23 Aug 2008 19:10:48 +0200 |
parents | |
children | c4fb132a086c |
comparison
equal
deleted
inserted
replaced
128:8df1d4193877 | 129:eb30df5ca28b |
---|---|
1 /******************************************************************************* | |
2 * Copyright (c) 2000, 2008 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 * Nikolay Botev <bono8106@hotmail.com> - [projection] Editor loses keyboard focus when expanding folded region - https://bugs.eclipse.org/bugs/show_bug.cgi?id=184255 | |
11 * Port to the D programming language: | |
12 * Frank Benoit <benoit@tionex.de> | |
13 *******************************************************************************/ | |
14 module dwtx.jface.text.source.AnnotationRulerColumn; | |
15 | |
16 import dwt.dwthelper.utils; | |
17 | |
18 import java.util.ArrayList; | |
19 import java.util.Collections; | |
20 import java.util.Comparator; | |
21 import java.util.HashMap; | |
22 import java.util.HashSet; | |
23 import java.util.Iterator; | |
24 import java.util.List; | |
25 import java.util.Map; | |
26 import java.util.Set; | |
27 | |
28 import dwt.DWT; | |
29 import dwt.custom.StyledText; | |
30 import dwt.events.DisposeEvent; | |
31 import dwt.events.DisposeListener; | |
32 import dwt.events.MouseEvent; | |
33 import dwt.events.MouseListener; | |
34 import dwt.events.MouseMoveListener; | |
35 import dwt.events.PaintEvent; | |
36 import dwt.events.PaintListener; | |
37 import dwt.graphics.Cursor; | |
38 import dwt.graphics.Font; | |
39 import dwt.graphics.GC; | |
40 import dwt.graphics.Image; | |
41 import dwt.graphics.Point; | |
42 import dwt.graphics.Rectangle; | |
43 import dwt.widgets.Canvas; | |
44 import dwt.widgets.Composite; | |
45 import dwt.widgets.Control; | |
46 import dwt.widgets.Display; | |
47 import dwtx.jface.text.BadLocationException; | |
48 import dwtx.jface.text.IDocument; | |
49 import dwtx.jface.text.IRegion; | |
50 import dwtx.jface.text.ITextListener; | |
51 import dwtx.jface.text.ITextViewer; | |
52 import dwtx.jface.text.ITextViewerExtension5; | |
53 import dwtx.jface.text.IViewportListener; | |
54 import dwtx.jface.text.JFaceTextUtil; | |
55 import dwtx.jface.text.Position; | |
56 import dwtx.jface.text.TextEvent; | |
57 | |
58 | |
59 /** | |
60 * A vertical ruler column showing graphical representations of annotations. | |
61 * Will become final. | |
62 * <p> | |
63 * Do not subclass. | |
64 * </p> | |
65 * | |
66 * @since 2.0 | |
67 */ | |
68 public class AnnotationRulerColumn : IVerticalRulerColumn, IVerticalRulerInfo, IVerticalRulerInfoExtension { | |
69 | |
70 /** | |
71 * Internal listener class. | |
72 */ | |
73 class InternalListener : IViewportListener, IAnnotationModelListener, ITextListener { | |
74 | |
75 /* | |
76 * @see IViewportListener#viewportChanged(int) | |
77 */ | |
78 public void viewportChanged(int verticalPosition) { | |
79 if (verticalPosition !is fScrollPos) | |
80 redraw(); | |
81 } | |
82 | |
83 /* | |
84 * @see IAnnotationModelListener#modelChanged(IAnnotationModel) | |
85 */ | |
86 public void modelChanged(IAnnotationModel model) { | |
87 postRedraw(); | |
88 } | |
89 | |
90 /* | |
91 * @see ITextListener#textChanged(TextEvent) | |
92 */ | |
93 public void textChanged(TextEvent e) { | |
94 if (e.getViewerRedrawState()) | |
95 postRedraw(); | |
96 } | |
97 } | |
98 | |
99 /** | |
100 * Implementation of <code>IRegion</code> that can be reused | |
101 * by setting the offset and the length. | |
102 */ | |
103 private static class ReusableRegion : Position , IRegion {} | |
104 | |
105 /** | |
106 * Pair of an annotation and their associated position. Used inside the paint method | |
107 * for sorting annotations based on the offset of their position. | |
108 * @since 3.0 | |
109 */ | |
110 private static class Tuple { | |
111 Annotation annotation; | |
112 Position position; | |
113 | |
114 Tuple(Annotation annotation, Position position) { | |
115 this.annotation= annotation; | |
116 this.position= position; | |
117 } | |
118 } | |
119 | |
120 /** | |
121 * Comparator for <code>Tuple</code>s. | |
122 * @since 3.0 | |
123 */ | |
124 private static class TupleComparator : Comparator { | |
125 /* | |
126 * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) | |
127 */ | |
128 public int compare(Object o1, Object o2) { | |
129 Position p1= ((Tuple) o1).position; | |
130 Position p2= ((Tuple) o2).position; | |
131 return p1.getOffset() - p2.getOffset(); | |
132 } | |
133 } | |
134 | |
135 /** This column's parent ruler */ | |
136 private CompositeRuler fParentRuler; | |
137 /** The cached text viewer */ | |
138 private ITextViewer fCachedTextViewer; | |
139 /** The cached text widget */ | |
140 private StyledText fCachedTextWidget; | |
141 /** The ruler's canvas */ | |
142 private Canvas fCanvas; | |
143 /** The vertical ruler's model */ | |
144 private IAnnotationModel fModel; | |
145 /** Cache for the actual scroll position in pixels */ | |
146 private int fScrollPos; | |
147 /** The buffer for double buffering */ | |
148 private Image fBuffer; | |
149 /** The internal listener */ | |
150 private InternalListener fInternalListener= new InternalListener(); | |
151 /** The width of this vertical ruler */ | |
152 private int fWidth; | |
153 /** Switch for enabling/disabling the setModel method. */ | |
154 private bool fAllowSetModel= true; | |
155 /** | |
156 * The list of annotation types to be shown in this ruler. | |
157 * @since 3.0 | |
158 */ | |
159 private Set fConfiguredAnnotationTypes= new HashSet(); | |
160 /** | |
161 * The list of allowed annotation types to be shown in this ruler. | |
162 * An allowed annotation type maps to <code>true</code>, a disallowed | |
163 * to <code>false</code>. | |
164 * @since 3.0 | |
165 */ | |
166 private Map fAllowedAnnotationTypes= new HashMap(); | |
167 /** | |
168 * The annotation access extension. | |
169 * @since 3.0 | |
170 */ | |
171 private IAnnotationAccessExtension fAnnotationAccessExtension; | |
172 /** | |
173 * The hover for this column. | |
174 * @since 3.0 | |
175 */ | |
176 private IAnnotationHover fHover; | |
177 /** | |
178 * The cached annotations. | |
179 * @since 3.0 | |
180 */ | |
181 private List fCachedAnnotations= new ArrayList(); | |
182 /** | |
183 * The comparator for sorting annotations according to the offset of their position. | |
184 * @since 3.0 | |
185 */ | |
186 private Comparator fTupleComparator= new TupleComparator(); | |
187 /** | |
188 * The hit detection cursor. | |
189 * @since 3.0 | |
190 */ | |
191 private Cursor fHitDetectionCursor; | |
192 /** | |
193 * The last cursor. | |
194 * @since 3.0 | |
195 */ | |
196 private Cursor fLastCursor; | |
197 /** | |
198 * This ruler's mouse listener. | |
199 * @since 3.0 | |
200 */ | |
201 private MouseListener fMouseListener; | |
202 | |
203 /** | |
204 * Constructs this column with the given arguments. | |
205 * | |
206 * @param model the annotation model to get the annotations from | |
207 * @param width the width of the vertical ruler | |
208 * @param annotationAccess the annotation access | |
209 * @since 3.0 | |
210 */ | |
211 public AnnotationRulerColumn(IAnnotationModel model, int width, IAnnotationAccess annotationAccess) { | |
212 this(width, annotationAccess); | |
213 fAllowSetModel= false; | |
214 fModel= model; | |
215 fModel.addAnnotationModelListener(fInternalListener); | |
216 } | |
217 | |
218 /** | |
219 * Constructs this column with the given arguments. | |
220 * | |
221 * @param width the width of the vertical ruler | |
222 * @param annotationAccess the annotation access | |
223 * @since 3.0 | |
224 */ | |
225 public AnnotationRulerColumn(int width, IAnnotationAccess annotationAccess) { | |
226 fWidth= width; | |
227 if (annotationAccess instanceof IAnnotationAccessExtension) | |
228 fAnnotationAccessExtension= (IAnnotationAccessExtension) annotationAccess; | |
229 } | |
230 | |
231 /** | |
232 * Constructs this column with the given arguments. | |
233 * | |
234 * @param model the annotation model to get the annotations from | |
235 * @param width the width of the vertical ruler | |
236 */ | |
237 public AnnotationRulerColumn(IAnnotationModel model, int width) { | |
238 fWidth= width; | |
239 fAllowSetModel= false; | |
240 fModel= model; | |
241 fModel.addAnnotationModelListener(fInternalListener); | |
242 } | |
243 | |
244 /** | |
245 * Constructs this column with the given width. | |
246 * | |
247 * @param width the width of the vertical ruler | |
248 */ | |
249 public AnnotationRulerColumn(int width) { | |
250 fWidth= width; | |
251 } | |
252 | |
253 /* | |
254 * @see IVerticalRulerColumn#getControl() | |
255 */ | |
256 public Control getControl() { | |
257 return fCanvas; | |
258 } | |
259 | |
260 /* | |
261 * @see IVerticalRulerColumn#getWidth() | |
262 */ | |
263 public int getWidth() { | |
264 return fWidth; | |
265 } | |
266 | |
267 /* | |
268 * @see IVerticalRulerColumn#createControl(CompositeRuler, Composite) | |
269 */ | |
270 public Control createControl(CompositeRuler parentRuler, Composite parentControl) { | |
271 | |
272 fParentRuler= parentRuler; | |
273 fCachedTextViewer= parentRuler.getTextViewer(); | |
274 fCachedTextWidget= fCachedTextViewer.getTextWidget(); | |
275 | |
276 fHitDetectionCursor= new Cursor(parentControl.getDisplay(), DWT.CURSOR_HAND); | |
277 | |
278 fCanvas= createCanvas(parentControl); | |
279 | |
280 fCanvas.addPaintListener(new PaintListener() { | |
281 public void paintControl(PaintEvent event) { | |
282 if (fCachedTextViewer !is null) | |
283 doubleBufferPaint(event.gc); | |
284 } | |
285 }); | |
286 | |
287 fCanvas.addDisposeListener(new DisposeListener() { | |
288 public void widgetDisposed(DisposeEvent e) { | |
289 handleDispose(); | |
290 fCachedTextViewer= null; | |
291 fCachedTextWidget= null; | |
292 } | |
293 }); | |
294 | |
295 fMouseListener= new MouseListener() { | |
296 public void mouseUp(MouseEvent event) { | |
297 int lineNumber; | |
298 if (isPropagatingMouseListener()) { | |
299 fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); | |
300 lineNumber= fParentRuler.getLineOfLastMouseButtonActivity(); | |
301 } else | |
302 lineNumber= fParentRuler.toDocumentLineNumber(event.y); | |
303 | |
304 if (1 is event.button) | |
305 mouseClicked(lineNumber); | |
306 } | |
307 | |
308 public void mouseDown(MouseEvent event) { | |
309 if (isPropagatingMouseListener()) | |
310 fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); | |
311 } | |
312 | |
313 public void mouseDoubleClick(MouseEvent event) { | |
314 int lineNumber; | |
315 if (isPropagatingMouseListener()) { | |
316 fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); | |
317 lineNumber= fParentRuler.getLineOfLastMouseButtonActivity(); | |
318 } else | |
319 lineNumber= fParentRuler.toDocumentLineNumber(event.y); | |
320 | |
321 if (1 is event.button) | |
322 mouseDoubleClicked(lineNumber); | |
323 } | |
324 }; | |
325 fCanvas.addMouseListener(fMouseListener); | |
326 | |
327 fCanvas.addMouseMoveListener(new MouseMoveListener() { | |
328 /* | |
329 * @see dwt.events.MouseMoveListener#mouseMove(dwt.events.MouseEvent) | |
330 * @since 3.0 | |
331 */ | |
332 public void mouseMove(MouseEvent e) { | |
333 handleMouseMove(e); | |
334 } | |
335 }); | |
336 | |
337 if (fCachedTextViewer !is null) { | |
338 fCachedTextViewer.addViewportListener(fInternalListener); | |
339 fCachedTextViewer.addTextListener(fInternalListener); | |
340 } | |
341 | |
342 return fCanvas; | |
343 } | |
344 | |
345 /** | |
346 * Creates a canvas with the given parent. | |
347 * | |
348 * @param parent the parent | |
349 * @return the created canvas | |
350 */ | |
351 private Canvas createCanvas(Composite parent) { | |
352 return new Canvas(parent, DWT.NO_BACKGROUND | DWT.NO_FOCUS) { | |
353 /* | |
354 * @see dwt.widgets.Control#addMouseListener(dwt.events.MouseListener) | |
355 * @since 3.0 | |
356 */ | |
357 public void addMouseListener(MouseListener listener) { | |
358 if (isPropagatingMouseListener() || listener is fMouseListener) | |
359 super.addMouseListener(listener); | |
360 } | |
361 }; | |
362 } | |
363 | |
364 /** | |
365 * Tells whether this ruler column propagates mouse listener | |
366 * events to its parent. | |
367 * | |
368 * @return <code>true</code> if propagating to parent | |
369 * @since 3.0 | |
370 */ | |
371 protected bool isPropagatingMouseListener() { | |
372 return true; | |
373 } | |
374 | |
375 /** | |
376 * Hook method for a mouse double click event on the given ruler line. | |
377 * | |
378 * @param rulerLine the ruler line | |
379 */ | |
380 protected void mouseDoubleClicked(int rulerLine) { | |
381 } | |
382 | |
383 /** | |
384 * Hook method for a mouse click event on the given ruler line. | |
385 * | |
386 * @param rulerLine the ruler line | |
387 * @since 3.0 | |
388 */ | |
389 protected void mouseClicked(int rulerLine) { | |
390 } | |
391 | |
392 /** | |
393 * Handles mouse moves. | |
394 * | |
395 * @param event the mouse move event | |
396 */ | |
397 private void handleMouseMove(MouseEvent event) { | |
398 fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); | |
399 if (fCachedTextViewer !is null) { | |
400 int line= toDocumentLineNumber(event.y); | |
401 Cursor cursor= (hasAnnotation(line) ? fHitDetectionCursor : null); | |
402 if (cursor !is fLastCursor) { | |
403 fCanvas.setCursor(cursor); | |
404 fLastCursor= cursor; | |
405 } | |
406 } | |
407 } | |
408 | |
409 /** | |
410 * Tells whether the given line contains an annotation. | |
411 * | |
412 * @param lineNumber the line number | |
413 * @return <code>true</code> if the given line contains an annotation | |
414 */ | |
415 protected bool hasAnnotation(int lineNumber) { | |
416 | |
417 IAnnotationModel model= fModel; | |
418 if (fModel instanceof IAnnotationModelExtension) | |
419 model= ((IAnnotationModelExtension)fModel).getAnnotationModel(SourceViewer.MODEL_ANNOTATION_MODEL); | |
420 | |
421 if (model is null) | |
422 return false; | |
423 | |
424 IRegion line; | |
425 try { | |
426 IDocument d= fCachedTextViewer.getDocument(); | |
427 if (d is null) | |
428 return false; | |
429 | |
430 line= d.getLineInformation(lineNumber); | |
431 } catch (BadLocationException ex) { | |
432 return false; | |
433 } | |
434 | |
435 int lineStart= line.getOffset(); | |
436 int lineLength= line.getLength(); | |
437 | |
438 Iterator e; | |
439 if (fModel instanceof IAnnotationModelExtension2) | |
440 e= ((IAnnotationModelExtension2)fModel).getAnnotationIterator(lineStart, lineLength + 1, true, true); | |
441 else | |
442 e= model.getAnnotationIterator(); | |
443 | |
444 while (e.hasNext()) { | |
445 Annotation a= (Annotation) e.next(); | |
446 | |
447 if (a.isMarkedDeleted()) | |
448 continue; | |
449 | |
450 if (skip(a)) | |
451 continue; | |
452 | |
453 Position p= model.getPosition(a); | |
454 if (p is null || p.isDeleted()) | |
455 continue; | |
456 | |
457 if (p.overlapsWith(lineStart, lineLength) || p.length is 0 && p.offset is lineStart + lineLength) | |
458 return true; | |
459 } | |
460 | |
461 return false; | |
462 } | |
463 | |
464 /** | |
465 * Disposes the ruler's resources. | |
466 */ | |
467 private void handleDispose() { | |
468 | |
469 if (fCachedTextViewer !is null) { | |
470 fCachedTextViewer.removeViewportListener(fInternalListener); | |
471 fCachedTextViewer.removeTextListener(fInternalListener); | |
472 } | |
473 | |
474 if (fModel !is null) | |
475 fModel.removeAnnotationModelListener(fInternalListener); | |
476 | |
477 if (fBuffer !is null) { | |
478 fBuffer.dispose(); | |
479 fBuffer= null; | |
480 } | |
481 | |
482 if (fHitDetectionCursor !is null) { | |
483 fHitDetectionCursor.dispose(); | |
484 fHitDetectionCursor= null; | |
485 } | |
486 | |
487 fConfiguredAnnotationTypes.clear(); | |
488 fAllowedAnnotationTypes.clear(); | |
489 fAnnotationAccessExtension= null; | |
490 } | |
491 | |
492 /** | |
493 * Double buffer drawing. | |
494 * | |
495 * @param dest the GC to draw into | |
496 */ | |
497 private void doubleBufferPaint(GC dest) { | |
498 | |
499 Point size= fCanvas.getSize(); | |
500 | |
501 if (size.x <= 0 || size.y <= 0) | |
502 return; | |
503 | |
504 if (fBuffer !is null) { | |
505 Rectangle r= fBuffer.getBounds(); | |
506 if (r.width !is size.x || r.height !is size.y) { | |
507 fBuffer.dispose(); | |
508 fBuffer= null; | |
509 } | |
510 } | |
511 if (fBuffer is null) | |
512 fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y); | |
513 | |
514 GC gc= new GC(fBuffer); | |
515 gc.setFont(fCachedTextWidget.getFont()); | |
516 try { | |
517 gc.setBackground(fCanvas.getBackground()); | |
518 gc.fillRectangle(0, 0, size.x, size.y); | |
519 | |
520 if (fCachedTextViewer instanceof ITextViewerExtension5) | |
521 doPaint1(gc); | |
522 else | |
523 doPaint(gc); | |
524 } finally { | |
525 gc.dispose(); | |
526 } | |
527 | |
528 dest.drawImage(fBuffer, 0, 0); | |
529 } | |
530 | |
531 /** | |
532 * Returns the document offset of the upper left corner of the source viewer's | |
533 * view port, possibly including partially visible lines. | |
534 * | |
535 * @return document offset of the upper left corner including partially visible lines | |
536 */ | |
537 protected int getInclusiveTopIndexStartOffset() { | |
538 if (fCachedTextWidget is null || fCachedTextWidget.isDisposed()) | |
539 return -1; | |
540 | |
541 IDocument document= fCachedTextViewer.getDocument(); | |
542 if (document is null) | |
543 return -1; | |
544 | |
545 int top= JFaceTextUtil.getPartialTopIndex(fCachedTextViewer); | |
546 try { | |
547 return document.getLineOffset(top); | |
548 } catch (BadLocationException x) { | |
549 return -1; | |
550 } | |
551 } | |
552 | |
553 /** | |
554 * Returns the first invisible document offset of the lower right corner of the source viewer's view port, | |
555 * possibly including partially visible lines. | |
556 * | |
557 * @return the first invisible document offset of the lower right corner of the view port | |
558 */ | |
559 private int getExclusiveBottomIndexEndOffset() { | |
560 if (fCachedTextWidget is null || fCachedTextWidget.isDisposed()) | |
561 return -1; | |
562 | |
563 IDocument document= fCachedTextViewer.getDocument(); | |
564 if (document is null) | |
565 return -1; | |
566 | |
567 int bottom= JFaceTextUtil.getPartialBottomIndex(fCachedTextViewer); | |
568 try { | |
569 if (bottom >= document.getNumberOfLines()) | |
570 bottom= document.getNumberOfLines() - 1; | |
571 return document.getLineOffset(bottom) + document.getLineLength(bottom); | |
572 } catch (BadLocationException x) { | |
573 return -1; | |
574 } | |
575 } | |
576 | |
577 /** | |
578 * Draws the vertical ruler w/o drawing the Canvas background. | |
579 * | |
580 * @param gc the GC to draw into | |
581 */ | |
582 protected void doPaint(GC gc) { | |
583 | |
584 if (fModel is null || fCachedTextViewer is null) | |
585 return; | |
586 | |
587 int topLeft= getInclusiveTopIndexStartOffset(); | |
588 // http://dev.eclipse.org/bugs/show_bug.cgi?id=14938 | |
589 // http://dev.eclipse.org/bugs/show_bug.cgi?id=22487 | |
590 // we want the exclusive offset (right after the last character) | |
591 int bottomRight= getExclusiveBottomIndexEndOffset(); | |
592 int viewPort= bottomRight - topLeft; | |
593 | |
594 fScrollPos= fCachedTextWidget.getTopPixel(); | |
595 Point dimension= fCanvas.getSize(); | |
596 | |
597 IDocument doc= fCachedTextViewer.getDocument(); | |
598 if (doc is null) | |
599 return; | |
600 | |
601 int topLine= -1, bottomLine= -1; | |
602 try { | |
603 IRegion region= fCachedTextViewer.getVisibleRegion(); | |
604 topLine= doc.getLineOfOffset(region.getOffset()); | |
605 bottomLine= doc.getLineOfOffset(region.getOffset() + region.getLength()); | |
606 } catch (BadLocationException x) { | |
607 return; | |
608 } | |
609 | |
610 // draw Annotations | |
611 Rectangle r= new Rectangle(0, 0, 0, 0); | |
612 int maxLayer= 1; // loop at least once through layers. | |
613 | |
614 for (int layer= 0; layer < maxLayer; layer++) { | |
615 Iterator iter; | |
616 if (fModel instanceof IAnnotationModelExtension2) | |
617 iter= ((IAnnotationModelExtension2)fModel).getAnnotationIterator(topLeft, viewPort + 1, true, true); | |
618 else | |
619 iter= fModel.getAnnotationIterator(); | |
620 | |
621 while (iter.hasNext()) { | |
622 Annotation annotation= (Annotation) iter.next(); | |
623 | |
624 int lay= IAnnotationAccessExtension.DEFAULT_LAYER; | |
625 if (fAnnotationAccessExtension !is null) | |
626 lay= fAnnotationAccessExtension.getLayer(annotation); | |
627 maxLayer= Math.max(maxLayer, lay+1); // dynamically update layer maximum | |
628 if (lay !is layer) // wrong layer: skip annotation | |
629 continue; | |
630 | |
631 if (skip(annotation)) | |
632 continue; | |
633 | |
634 Position position= fModel.getPosition(annotation); | |
635 if (position is null) | |
636 continue; | |
637 | |
638 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=20284 | |
639 // Position.overlapsWith returns false if the position just starts at the end | |
640 // of the specified range. If the position has zero length, we want to include it anyhow | |
641 int viewPortSize= position.getLength() is 0 ? viewPort + 1 : viewPort; | |
642 if (!position.overlapsWith(topLeft, viewPortSize)) | |
643 continue; | |
644 | |
645 try { | |
646 | |
647 int offset= position.getOffset(); | |
648 int length= position.getLength(); | |
649 | |
650 int startLine= doc.getLineOfOffset(offset); | |
651 if (startLine < topLine) | |
652 startLine= topLine; | |
653 | |
654 int endLine= startLine; | |
655 if (length > 0) | |
656 endLine= doc.getLineOfOffset(offset + length - 1); | |
657 if (endLine > bottomLine) | |
658 endLine= bottomLine; | |
659 | |
660 startLine -= topLine; | |
661 endLine -= topLine; | |
662 | |
663 r.x= 0; | |
664 r.y= JFaceTextUtil.computeLineHeight(fCachedTextWidget, 0, startLine, startLine) - fScrollPos; | |
665 | |
666 r.width= dimension.x; | |
667 int lines= endLine - startLine; | |
668 | |
669 r.height= JFaceTextUtil.computeLineHeight(fCachedTextWidget, startLine, endLine + 1, lines + 1); | |
670 | |
671 if (r.y < dimension.y && fAnnotationAccessExtension !is null) // annotation within visible area | |
672 fAnnotationAccessExtension.paint(annotation, gc, fCanvas, r); | |
673 | |
674 } catch (BadLocationException x) { | |
675 } | |
676 } | |
677 } | |
678 } | |
679 | |
680 /** | |
681 * Draws the vertical ruler w/o drawing the Canvas background. Implementation based | |
682 * on <code>ITextViewerExtension5</code>. Will replace <code>doPaint(GC)</code>. | |
683 * | |
684 * @param gc the GC to draw into | |
685 */ | |
686 protected void doPaint1(GC gc) { | |
687 | |
688 if (fModel is null || fCachedTextViewer is null) | |
689 return; | |
690 | |
691 ITextViewerExtension5 extension= (ITextViewerExtension5) fCachedTextViewer; | |
692 | |
693 fScrollPos= fCachedTextWidget.getTopPixel(); | |
694 Point dimension= fCanvas.getSize(); | |
695 | |
696 int vOffset= getInclusiveTopIndexStartOffset(); | |
697 int vLength= getExclusiveBottomIndexEndOffset() - vOffset; | |
698 | |
699 // draw Annotations | |
700 Rectangle r= new Rectangle(0, 0, 0, 0); | |
701 ReusableRegion range= new ReusableRegion(); | |
702 | |
703 int minLayer= Integer.MAX_VALUE, maxLayer= Integer.MIN_VALUE; | |
704 fCachedAnnotations.clear(); | |
705 Iterator iter; | |
706 if (fModel instanceof IAnnotationModelExtension2) | |
707 iter= ((IAnnotationModelExtension2)fModel).getAnnotationIterator(vOffset, vLength + 1, true, true); | |
708 else | |
709 iter= fModel.getAnnotationIterator(); | |
710 | |
711 while (iter.hasNext()) { | |
712 Annotation annotation= (Annotation) iter.next(); | |
713 | |
714 if (skip(annotation)) | |
715 continue; | |
716 | |
717 Position position= fModel.getPosition(annotation); | |
718 if (position is null) | |
719 continue; | |
720 | |
721 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=217710 | |
722 int extendedVLength= position.getLength() is 0 ? vLength + 1 : vLength; | |
723 if (!position.overlapsWith(vOffset, extendedVLength)) | |
724 continue; | |
725 | |
726 int lay= IAnnotationAccessExtension.DEFAULT_LAYER; | |
727 if (fAnnotationAccessExtension !is null) | |
728 lay= fAnnotationAccessExtension.getLayer(annotation); | |
729 | |
730 minLayer= Math.min(minLayer, lay); | |
731 maxLayer= Math.max(maxLayer, lay); | |
732 fCachedAnnotations.add(new Tuple(annotation, position)); | |
733 } | |
734 Collections.sort(fCachedAnnotations, fTupleComparator); | |
735 | |
736 for (int layer= minLayer; layer <= maxLayer; layer++) { | |
737 for (int i= 0, n= fCachedAnnotations.size(); i < n; i++) { | |
738 Tuple tuple= (Tuple) fCachedAnnotations.get(i); | |
739 Annotation annotation= tuple.annotation; | |
740 Position position= tuple.position; | |
741 | |
742 int lay= IAnnotationAccessExtension.DEFAULT_LAYER; | |
743 if (fAnnotationAccessExtension !is null) | |
744 lay= fAnnotationAccessExtension.getLayer(annotation); | |
745 if (lay !is layer) // wrong layer: skip annotation | |
746 continue; | |
747 | |
748 range.setOffset(position.getOffset()); | |
749 range.setLength(position.getLength()); | |
750 IRegion widgetRegion= extension.modelRange2WidgetRange(range); | |
751 if (widgetRegion is null) | |
752 continue; | |
753 | |
754 int startLine= extension.widgetLineOfWidgetOffset(widgetRegion.getOffset()); | |
755 if (startLine is -1) | |
756 continue; | |
757 | |
758 int endLine= extension.widgetLineOfWidgetOffset(widgetRegion.getOffset() + Math.max(widgetRegion.getLength() -1, 0)); | |
759 if (endLine is -1) | |
760 continue; | |
761 | |
762 r.x= 0; | |
763 r.y= JFaceTextUtil.computeLineHeight(fCachedTextWidget, 0, startLine, startLine) - fScrollPos; | |
764 | |
765 r.width= dimension.x; | |
766 int lines= endLine - startLine; | |
767 r.height= JFaceTextUtil.computeLineHeight(fCachedTextWidget, startLine, endLine + 1, lines + 1); | |
768 | |
769 if (r.y < dimension.y && fAnnotationAccessExtension !is null) // annotation within visible area | |
770 fAnnotationAccessExtension.paint(annotation, gc, fCanvas, r); | |
771 } | |
772 } | |
773 | |
774 fCachedAnnotations.clear(); | |
775 } | |
776 | |
777 | |
778 /** | |
779 * Post a redraw request for this column into the UI thread. | |
780 */ | |
781 private void postRedraw() { | |
782 if (fCanvas !is null && !fCanvas.isDisposed()) { | |
783 Display d= fCanvas.getDisplay(); | |
784 if (d !is null) { | |
785 d.asyncExec(new Runnable() { | |
786 public void run() { | |
787 redraw(); | |
788 } | |
789 }); | |
790 } | |
791 } | |
792 } | |
793 | |
794 /* | |
795 * @see IVerticalRulerColumn#redraw() | |
796 */ | |
797 public void redraw() { | |
798 if (fCanvas !is null && !fCanvas.isDisposed()) { | |
799 GC gc= new GC(fCanvas); | |
800 doubleBufferPaint(gc); | |
801 gc.dispose(); | |
802 } | |
803 } | |
804 | |
805 /* | |
806 * @see IVerticalRulerColumn#setModel | |
807 */ | |
808 public void setModel(IAnnotationModel model) { | |
809 if (fAllowSetModel && model !is fModel) { | |
810 | |
811 if (fModel !is null) | |
812 fModel.removeAnnotationModelListener(fInternalListener); | |
813 | |
814 fModel= model; | |
815 | |
816 if (fModel !is null) | |
817 fModel.addAnnotationModelListener(fInternalListener); | |
818 | |
819 postRedraw(); | |
820 } | |
821 } | |
822 | |
823 /* | |
824 * @see IVerticalRulerColumn#setFont(Font) | |
825 */ | |
826 public void setFont(Font font) { | |
827 } | |
828 | |
829 /** | |
830 * Returns the cached text viewer. | |
831 * | |
832 * @return the cached text viewer | |
833 */ | |
834 protected ITextViewer getCachedTextViewer() { | |
835 return fCachedTextViewer; | |
836 } | |
837 | |
838 /* | |
839 * @see dwtx.jface.text.source.IVerticalRulerInfoExtension#getModel() | |
840 */ | |
841 public IAnnotationModel getModel() { | |
842 return fModel; | |
843 } | |
844 | |
845 /** | |
846 * Adds the given annotation type to this annotation ruler column. Starting | |
847 * with this call, annotations of the given type are shown in this annotation | |
848 * ruler column. | |
849 * | |
850 * @param annotationType the annotation type | |
851 * @since 3.0 | |
852 */ | |
853 public void addAnnotationType(Object annotationType) { | |
854 fConfiguredAnnotationTypes.add(annotationType); | |
855 fAllowedAnnotationTypes.clear(); | |
856 } | |
857 | |
858 /* | |
859 * @see dwtx.jface.text.source.IVerticalRulerInfo#getLineOfLastMouseButtonActivity() | |
860 * @since 3.0 | |
861 */ | |
862 public int getLineOfLastMouseButtonActivity() { | |
863 return fParentRuler.getLineOfLastMouseButtonActivity(); | |
864 } | |
865 | |
866 /* | |
867 * @see dwtx.jface.text.source.IVerticalRulerInfo#toDocumentLineNumber(int) | |
868 * @since 3.0 | |
869 */ | |
870 public int toDocumentLineNumber(int y_coordinate) { | |
871 return fParentRuler.toDocumentLineNumber(y_coordinate); | |
872 } | |
873 | |
874 /** | |
875 * Removes the given annotation type from this annotation ruler column. | |
876 * Annotations of the given type are no longer shown in this annotation | |
877 * ruler column. | |
878 * | |
879 * @param annotationType the annotation type | |
880 * @since 3.0 | |
881 */ | |
882 public void removeAnnotationType(Object annotationType) { | |
883 fConfiguredAnnotationTypes.remove(annotationType); | |
884 fAllowedAnnotationTypes.clear(); | |
885 } | |
886 | |
887 /** | |
888 * Returns whether the given annotation should be skipped by the drawing | |
889 * routine. | |
890 * | |
891 * @param annotation the annotation | |
892 * @return <code>true</code> if annotation of the given type should be | |
893 * skipped, <code>false</code> otherwise | |
894 * @since 3.0 | |
895 */ | |
896 private bool skip(Annotation annotation) { | |
897 Object annotationType= annotation.getType(); | |
898 Boolean allowed= (Boolean) fAllowedAnnotationTypes.get(annotationType); | |
899 if (allowed !is null) | |
900 return !allowed.booleanValue(); | |
901 | |
902 bool skip= skip(annotationType); | |
903 fAllowedAnnotationTypes.put(annotationType, !skip ? Boolean.TRUE : Boolean.FALSE); | |
904 return skip; | |
905 } | |
906 | |
907 /** | |
908 * Computes whether the annotation of the given type should be skipped or | |
909 * not. | |
910 * | |
911 * @param annotationType the annotation type | |
912 * @return <code>true</code> if annotation should be skipped, <code>false</code> | |
913 * otherwise | |
914 * @since 3.0 | |
915 */ | |
916 private bool skip(Object annotationType) { | |
917 if (fAnnotationAccessExtension !is null) { | |
918 Iterator e= fConfiguredAnnotationTypes.iterator(); | |
919 while (e.hasNext()) { | |
920 if (fAnnotationAccessExtension.isSubtype(annotationType, e.next())) | |
921 return false; | |
922 } | |
923 return true; | |
924 } | |
925 return !fConfiguredAnnotationTypes.contains(annotationType); | |
926 } | |
927 | |
928 /* | |
929 * @see dwtx.jface.text.source.IVerticalRulerInfoExtension#getHover() | |
930 * @since 3.0 | |
931 */ | |
932 public IAnnotationHover getHover() { | |
933 return fHover; | |
934 } | |
935 | |
936 /** | |
937 * @param hover The hover to set. | |
938 * @since 3.0 | |
939 */ | |
940 public void setHover(IAnnotationHover hover) { | |
941 fHover= hover; | |
942 } | |
943 | |
944 /* | |
945 * @see dwtx.jface.text.source.IVerticalRulerInfoExtension#addVerticalRulerListener(dwtx.jface.text.source.IVerticalRulerListener) | |
946 * @since 3.0 | |
947 */ | |
948 public void addVerticalRulerListener(IVerticalRulerListener listener) { | |
949 throw new UnsupportedOperationException(); | |
950 } | |
951 | |
952 /* | |
953 * @see dwtx.jface.text.source.IVerticalRulerInfoExtension#removeVerticalRulerListener(dwtx.jface.text.source.IVerticalRulerListener) | |
954 * @since 3.0 | |
955 */ | |
956 public void removeVerticalRulerListener(IVerticalRulerListener listener) { | |
957 throw new UnsupportedOperationException(); | |
958 } | |
959 } |