Mercurial > projects > dwt-addons
comparison dwtx/jface/text/source/VerticalRuler.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 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.jface.text.source.VerticalRuler; | |
14 | |
15 import dwt.dwthelper.utils; | |
16 | |
17 | |
18 import java.util.Iterator; | |
19 | |
20 import dwt.DWT; | |
21 import dwt.custom.StyledText; | |
22 import dwt.events.DisposeEvent; | |
23 import dwt.events.DisposeListener; | |
24 import dwt.events.MouseEvent; | |
25 import dwt.events.MouseListener; | |
26 import dwt.events.PaintEvent; | |
27 import dwt.events.PaintListener; | |
28 import dwt.graphics.Font; | |
29 import dwt.graphics.GC; | |
30 import dwt.graphics.Image; | |
31 import dwt.graphics.Point; | |
32 import dwt.graphics.Rectangle; | |
33 import dwt.widgets.Canvas; | |
34 import dwt.widgets.Composite; | |
35 import dwt.widgets.Control; | |
36 import dwt.widgets.Display; | |
37 import dwtx.jface.text.BadLocationException; | |
38 import dwtx.jface.text.IDocument; | |
39 import dwtx.jface.text.IRegion; | |
40 import dwtx.jface.text.ITextListener; | |
41 import dwtx.jface.text.ITextViewer; | |
42 import dwtx.jface.text.ITextViewerExtension5; | |
43 import dwtx.jface.text.IViewportListener; | |
44 import dwtx.jface.text.JFaceTextUtil; | |
45 import dwtx.jface.text.Position; | |
46 import dwtx.jface.text.Region; | |
47 import dwtx.jface.text.TextEvent; | |
48 | |
49 | |
50 /** | |
51 * A vertical ruler which is connected to a text viewer. Single column standard | |
52 * implementation of {@link dwtx.jface.text.source.IVerticalRuler}. | |
53 * <p> | |
54 * The same can be achieved by using <code>CompositeRuler</code> configured | |
55 * with an <code>AnnotationRulerColumn</code>. Clients may use this class as | |
56 * is. | |
57 * | |
58 * @see dwtx.jface.text.ITextViewer | |
59 */ | |
60 public final class VerticalRuler : IVerticalRuler, IVerticalRulerExtension { | |
61 | |
62 /** | |
63 * Internal listener class. | |
64 */ | |
65 class InternalListener : IViewportListener, IAnnotationModelListener, ITextListener { | |
66 | |
67 /* | |
68 * @see IViewportListener#viewportChanged(int) | |
69 */ | |
70 public void viewportChanged(int verticalPosition) { | |
71 if (verticalPosition !is fScrollPos) | |
72 redraw(); | |
73 } | |
74 | |
75 /* | |
76 * @see IAnnotationModelListener#modelChanged(IAnnotationModel) | |
77 */ | |
78 public void modelChanged(IAnnotationModel model) { | |
79 update(); | |
80 } | |
81 | |
82 /* | |
83 * @see ITextListener#textChanged(TextEvent) | |
84 */ | |
85 public void textChanged(TextEvent e) { | |
86 if (fTextViewer !is null && e.getViewerRedrawState()) | |
87 redraw(); | |
88 } | |
89 } | |
90 | |
91 /** The vertical ruler's text viewer */ | |
92 private ITextViewer fTextViewer; | |
93 /** The ruler's canvas */ | |
94 private Canvas fCanvas; | |
95 /** The vertical ruler's model */ | |
96 private IAnnotationModel fModel; | |
97 /** Cache for the actual scroll position in pixels */ | |
98 private int fScrollPos; | |
99 /** The buffer for double buffering */ | |
100 private Image fBuffer; | |
101 /** The line of the last mouse button activity */ | |
102 private int fLastMouseButtonActivityLine= -1; | |
103 /** The internal listener */ | |
104 private InternalListener fInternalListener= new InternalListener(); | |
105 /** The width of this vertical ruler */ | |
106 private int fWidth; | |
107 /** | |
108 * The annotation access of this vertical ruler | |
109 * @since 3.0 | |
110 */ | |
111 private IAnnotationAccess fAnnotationAccess; | |
112 | |
113 /** | |
114 * Constructs a vertical ruler with the given width. | |
115 * | |
116 * @param width the width of the vertical ruler | |
117 */ | |
118 public VerticalRuler(int width) { | |
119 this(width, null); | |
120 } | |
121 | |
122 /** | |
123 * Constructs a vertical ruler with the given width and the given annotation | |
124 * access. | |
125 * | |
126 * @param width the width of the vertical ruler | |
127 * @param annotationAcccess the annotation access | |
128 * @since 3.0 | |
129 */ | |
130 public VerticalRuler(int width, IAnnotationAccess annotationAcccess) { | |
131 fWidth= width; | |
132 fAnnotationAccess= annotationAcccess; | |
133 } | |
134 | |
135 /* | |
136 * @see IVerticalRuler#getControl() | |
137 */ | |
138 public Control getControl() { | |
139 return fCanvas; | |
140 } | |
141 | |
142 /* | |
143 * @see IVerticalRuler#createControl(Composite, ITextViewer) | |
144 */ | |
145 public Control createControl(Composite parent, ITextViewer textViewer) { | |
146 | |
147 fTextViewer= textViewer; | |
148 | |
149 fCanvas= new Canvas(parent, DWT.NO_BACKGROUND); | |
150 | |
151 fCanvas.addPaintListener(new PaintListener() { | |
152 public void paintControl(PaintEvent event) { | |
153 if (fTextViewer !is null) | |
154 doubleBufferPaint(event.gc); | |
155 } | |
156 }); | |
157 | |
158 fCanvas.addDisposeListener(new DisposeListener() { | |
159 public void widgetDisposed(DisposeEvent e) { | |
160 handleDispose(); | |
161 fTextViewer= null; | |
162 } | |
163 }); | |
164 | |
165 fCanvas.addMouseListener(new MouseListener() { | |
166 public void mouseUp(MouseEvent event) { | |
167 } | |
168 | |
169 public void mouseDown(MouseEvent event) { | |
170 fLastMouseButtonActivityLine= toDocumentLineNumber(event.y); | |
171 } | |
172 | |
173 public void mouseDoubleClick(MouseEvent event) { | |
174 fLastMouseButtonActivityLine= toDocumentLineNumber(event.y); | |
175 } | |
176 }); | |
177 | |
178 if (fTextViewer !is null) { | |
179 fTextViewer.addViewportListener(fInternalListener); | |
180 fTextViewer.addTextListener(fInternalListener); | |
181 } | |
182 | |
183 return fCanvas; | |
184 } | |
185 | |
186 /** | |
187 * Disposes the ruler's resources. | |
188 */ | |
189 private void handleDispose() { | |
190 | |
191 if (fTextViewer !is null) { | |
192 fTextViewer.removeViewportListener(fInternalListener); | |
193 fTextViewer.removeTextListener(fInternalListener); | |
194 fTextViewer= null; | |
195 } | |
196 | |
197 if (fModel !is null) | |
198 fModel.removeAnnotationModelListener(fInternalListener); | |
199 | |
200 if (fBuffer !is null) { | |
201 fBuffer.dispose(); | |
202 fBuffer= null; | |
203 } | |
204 } | |
205 | |
206 | |
207 /** | |
208 * Double buffer drawing. | |
209 * | |
210 * @param dest the GC to draw into | |
211 */ | |
212 private void doubleBufferPaint(GC dest) { | |
213 | |
214 Point size= fCanvas.getSize(); | |
215 | |
216 if (size.x <= 0 || size.y <= 0) | |
217 return; | |
218 | |
219 if (fBuffer !is null) { | |
220 Rectangle r= fBuffer.getBounds(); | |
221 if (r.width !is size.x || r.height !is size.y) { | |
222 fBuffer.dispose(); | |
223 fBuffer= null; | |
224 } | |
225 } | |
226 if (fBuffer is null) | |
227 fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y); | |
228 | |
229 GC gc= new GC(fBuffer); | |
230 gc.setFont(fTextViewer.getTextWidget().getFont()); | |
231 try { | |
232 gc.setBackground(fCanvas.getBackground()); | |
233 gc.fillRectangle(0, 0, size.x, size.y); | |
234 | |
235 if (fTextViewer instanceof ITextViewerExtension5) | |
236 doPaint1(gc); | |
237 else | |
238 doPaint(gc); | |
239 | |
240 } finally { | |
241 gc.dispose(); | |
242 } | |
243 | |
244 dest.drawImage(fBuffer, 0, 0); | |
245 } | |
246 | |
247 /** | |
248 * Returns the document offset of the upper left corner of the | |
249 * widgets view port, possibly including partially visible lines. | |
250 * | |
251 * @return the document offset of the upper left corner including partially visible lines | |
252 * @since 2.0 | |
253 */ | |
254 private int getInclusiveTopIndexStartOffset() { | |
255 | |
256 StyledText textWidget= fTextViewer.getTextWidget(); | |
257 if (textWidget !is null && !textWidget.isDisposed()) { | |
258 int top= JFaceTextUtil.getPartialTopIndex(fTextViewer); | |
259 try { | |
260 IDocument document= fTextViewer.getDocument(); | |
261 return document.getLineOffset(top); | |
262 } catch (BadLocationException x) { | |
263 } | |
264 } | |
265 | |
266 return -1; | |
267 } | |
268 | |
269 | |
270 | |
271 /** | |
272 * Draws the vertical ruler w/o drawing the Canvas background. | |
273 * | |
274 * @param gc the GC to draw into | |
275 */ | |
276 protected void doPaint(GC gc) { | |
277 | |
278 if (fModel is null || fTextViewer is null) | |
279 return; | |
280 | |
281 IAnnotationAccessExtension annotationAccessExtension= null; | |
282 if (fAnnotationAccess instanceof IAnnotationAccessExtension) | |
283 annotationAccessExtension= (IAnnotationAccessExtension) fAnnotationAccess; | |
284 | |
285 StyledText styledText= fTextViewer.getTextWidget(); | |
286 IDocument doc= fTextViewer.getDocument(); | |
287 | |
288 int topLeft= getInclusiveTopIndexStartOffset(); | |
289 int bottomRight= fTextViewer.getBottomIndexEndOffset(); | |
290 int viewPort= bottomRight - topLeft; | |
291 | |
292 Point d= fCanvas.getSize(); | |
293 fScrollPos= styledText.getTopPixel(); | |
294 | |
295 int topLine= -1, bottomLine= -1; | |
296 try { | |
297 IRegion region= fTextViewer.getVisibleRegion(); | |
298 topLine= doc.getLineOfOffset(region.getOffset()); | |
299 bottomLine= doc.getLineOfOffset(region.getOffset() + region.getLength()); | |
300 } catch (BadLocationException x) { | |
301 return; | |
302 } | |
303 | |
304 // draw Annotations | |
305 Rectangle r= new Rectangle(0, 0, 0, 0); | |
306 int maxLayer= 1; // loop at least once though layers. | |
307 | |
308 for (int layer= 0; layer < maxLayer; layer++) { | |
309 Iterator iter= fModel.getAnnotationIterator(); | |
310 while (iter.hasNext()) { | |
311 IAnnotationPresentation annotationPresentation= null; | |
312 Annotation annotation= (Annotation) iter.next(); | |
313 | |
314 int lay= IAnnotationAccessExtension.DEFAULT_LAYER; | |
315 if (annotationAccessExtension !is null) | |
316 lay= annotationAccessExtension.getLayer(annotation); | |
317 else if (annotation instanceof IAnnotationPresentation) { | |
318 annotationPresentation= (IAnnotationPresentation)annotation; | |
319 lay= annotationPresentation.getLayer(); | |
320 } | |
321 maxLayer= Math.max(maxLayer, lay+1); // dynamically update layer maximum | |
322 if (lay !is layer) // wrong layer: skip annotation | |
323 continue; | |
324 | |
325 Position position= fModel.getPosition(annotation); | |
326 if (position is null) | |
327 continue; | |
328 | |
329 if (!position.overlapsWith(topLeft, viewPort)) | |
330 continue; | |
331 | |
332 try { | |
333 | |
334 int offset= position.getOffset(); | |
335 int length= position.getLength(); | |
336 | |
337 int startLine= doc.getLineOfOffset(offset); | |
338 if (startLine < topLine) | |
339 startLine= topLine; | |
340 | |
341 int endLine= startLine; | |
342 if (length > 0) | |
343 endLine= doc.getLineOfOffset(offset + length - 1); | |
344 if (endLine > bottomLine) | |
345 endLine= bottomLine; | |
346 | |
347 startLine -= topLine; | |
348 endLine -= topLine; | |
349 | |
350 r.x= 0; | |
351 r.y= JFaceTextUtil.computeLineHeight(styledText, 0, startLine, startLine) - fScrollPos; | |
352 | |
353 r.width= d.x; | |
354 int lines= endLine - startLine; | |
355 | |
356 r.height= JFaceTextUtil.computeLineHeight(styledText, startLine, endLine + 1, (lines+1)); | |
357 | |
358 if (r.y < d.y && annotationAccessExtension !is null) // annotation within visible area | |
359 annotationAccessExtension.paint(annotation, gc, fCanvas, r); | |
360 else if (annotationPresentation !is null) | |
361 annotationPresentation.paint(gc, fCanvas, r); | |
362 | |
363 } catch (BadLocationException e) { | |
364 } | |
365 } | |
366 } | |
367 } | |
368 | |
369 /** | |
370 * Draws the vertical ruler w/o drawing the Canvas background. Uses | |
371 * <code>ITextViewerExtension5</code> for its implementation. Will replace | |
372 * <code>doPaint(GC)</code>. | |
373 * | |
374 * @param gc the GC to draw into | |
375 */ | |
376 protected void doPaint1(GC gc) { | |
377 | |
378 if (fModel is null || fTextViewer is null) | |
379 return; | |
380 | |
381 IAnnotationAccessExtension annotationAccessExtension= null; | |
382 if (fAnnotationAccess instanceof IAnnotationAccessExtension) | |
383 annotationAccessExtension= (IAnnotationAccessExtension) fAnnotationAccess; | |
384 | |
385 ITextViewerExtension5 extension= (ITextViewerExtension5) fTextViewer; | |
386 StyledText textWidget= fTextViewer.getTextWidget(); | |
387 | |
388 fScrollPos= textWidget.getTopPixel(); | |
389 Point dimension= fCanvas.getSize(); | |
390 | |
391 // draw Annotations | |
392 Rectangle r= new Rectangle(0, 0, 0, 0); | |
393 int maxLayer= 1; // loop at least once through layers. | |
394 | |
395 for (int layer= 0; layer < maxLayer; layer++) { | |
396 Iterator iter= fModel.getAnnotationIterator(); | |
397 while (iter.hasNext()) { | |
398 IAnnotationPresentation annotationPresentation= null; | |
399 Annotation annotation= (Annotation) iter.next(); | |
400 | |
401 int lay= IAnnotationAccessExtension.DEFAULT_LAYER; | |
402 if (annotationAccessExtension !is null) | |
403 lay= annotationAccessExtension.getLayer(annotation); | |
404 else if (annotation instanceof IAnnotationPresentation) { | |
405 annotationPresentation= (IAnnotationPresentation)annotation; | |
406 lay= annotationPresentation.getLayer(); | |
407 } | |
408 maxLayer= Math.max(maxLayer, lay+1); // dynamically update layer maximum | |
409 if (lay !is layer) // wrong layer: skip annotation | |
410 continue; | |
411 | |
412 Position position= fModel.getPosition(annotation); | |
413 if (position is null) | |
414 continue; | |
415 | |
416 IRegion widgetRegion= extension.modelRange2WidgetRange(new Region(position.getOffset(), position.getLength())); | |
417 if (widgetRegion is null) | |
418 continue; | |
419 | |
420 int startLine= extension.widgetLineOfWidgetOffset(widgetRegion.getOffset()); | |
421 if (startLine is -1) | |
422 continue; | |
423 | |
424 int endLine= extension.widgetLineOfWidgetOffset(widgetRegion.getOffset() + Math.max(widgetRegion.getLength() -1, 0)); | |
425 if (endLine is -1) | |
426 continue; | |
427 | |
428 r.x= 0; | |
429 r.y= JFaceTextUtil.computeLineHeight(textWidget, 0, startLine, startLine) - fScrollPos; | |
430 | |
431 r.width= dimension.x; | |
432 int lines= endLine - startLine; | |
433 | |
434 r.height= JFaceTextUtil.computeLineHeight(textWidget, startLine, endLine + 1, lines+1); | |
435 | |
436 if (r.y < dimension.y && annotationAccessExtension !is null) // annotation within visible area | |
437 annotationAccessExtension.paint(annotation, gc, fCanvas, r); | |
438 else if (annotationPresentation !is null) | |
439 annotationPresentation.paint(gc, fCanvas, r); | |
440 } | |
441 } | |
442 } | |
443 | |
444 /** | |
445 * Thread-safe implementation. | |
446 * Can be called from any thread. | |
447 */ | |
448 /* | |
449 * @see IVerticalRuler#update() | |
450 */ | |
451 public void update() { | |
452 if (fCanvas !is null && !fCanvas.isDisposed()) { | |
453 Display d= fCanvas.getDisplay(); | |
454 if (d !is null) { | |
455 d.asyncExec(new Runnable() { | |
456 public void run() { | |
457 redraw(); | |
458 } | |
459 }); | |
460 } | |
461 } | |
462 } | |
463 | |
464 /** | |
465 * Redraws the vertical ruler. | |
466 */ | |
467 private void redraw() { | |
468 if (fCanvas !is null && !fCanvas.isDisposed()) { | |
469 GC gc= new GC(fCanvas); | |
470 doubleBufferPaint(gc); | |
471 gc.dispose(); | |
472 } | |
473 } | |
474 | |
475 /* | |
476 * @see IVerticalRuler#setModel(IAnnotationModel) | |
477 */ | |
478 public void setModel(IAnnotationModel model) { | |
479 if (model !is fModel) { | |
480 | |
481 if (fModel !is null) | |
482 fModel.removeAnnotationModelListener(fInternalListener); | |
483 | |
484 fModel= model; | |
485 | |
486 if (fModel !is null) | |
487 fModel.addAnnotationModelListener(fInternalListener); | |
488 | |
489 update(); | |
490 } | |
491 } | |
492 | |
493 /* | |
494 * @see IVerticalRuler#getModel() | |
495 */ | |
496 public IAnnotationModel getModel() { | |
497 return fModel; | |
498 } | |
499 | |
500 /* | |
501 * @see IVerticalRulerInfo#getWidth() | |
502 */ | |
503 public int getWidth() { | |
504 return fWidth; | |
505 } | |
506 | |
507 /* | |
508 * @see IVerticalRulerInfo#getLineOfLastMouseButtonActivity() | |
509 */ | |
510 public int getLineOfLastMouseButtonActivity() { | |
511 IDocument doc= fTextViewer.getDocument(); | |
512 if (doc is null || fLastMouseButtonActivityLine >= fTextViewer.getDocument().getNumberOfLines()) | |
513 fLastMouseButtonActivityLine= -1; | |
514 return fLastMouseButtonActivityLine; | |
515 } | |
516 | |
517 /* | |
518 * @see IVerticalRulerInfo#toDocumentLineNumber(int) | |
519 */ | |
520 public int toDocumentLineNumber(int y_coordinate) { | |
521 if (fTextViewer is null || y_coordinate is -1) | |
522 return -1; | |
523 | |
524 StyledText text= fTextViewer.getTextWidget(); | |
525 int line= text.getLineIndex(y_coordinate); | |
526 | |
527 if (line is text.getLineCount() - 1) { | |
528 // check whether y_coordinate exceeds last line | |
529 if (y_coordinate > text.getLinePixel(line + 1)) | |
530 return -1; | |
531 } | |
532 | |
533 return widgetLine2ModelLine(fTextViewer, line); | |
534 } | |
535 | |
536 /** | |
537 * Returns the line of the viewer's document that corresponds to the given widget line. | |
538 * | |
539 * @param viewer the viewer | |
540 * @param widgetLine the widget line | |
541 * @return the corresponding line of the viewer's document | |
542 * @since 2.1 | |
543 */ | |
544 protected final static int widgetLine2ModelLine(ITextViewer viewer, int widgetLine) { | |
545 | |
546 if (viewer instanceof ITextViewerExtension5) { | |
547 ITextViewerExtension5 extension= (ITextViewerExtension5) viewer; | |
548 return extension.widgetLine2ModelLine(widgetLine); | |
549 } | |
550 | |
551 try { | |
552 IRegion r= viewer.getVisibleRegion(); | |
553 IDocument d= viewer.getDocument(); | |
554 return widgetLine += d.getLineOfOffset(r.getOffset()); | |
555 } catch (BadLocationException x) { | |
556 } | |
557 return widgetLine; | |
558 } | |
559 | |
560 /* | |
561 * @see IVerticalRulerExtension#setFont(Font) | |
562 * @since 2.0 | |
563 */ | |
564 public void setFont(Font font) { | |
565 } | |
566 | |
567 /* | |
568 * @see IVerticalRulerExtension#setLocationOfLastMouseButtonActivity(int, int) | |
569 * @since 2.0 | |
570 */ | |
571 public void setLocationOfLastMouseButtonActivity(int x, int y) { | |
572 fLastMouseButtonActivityLine= toDocumentLineNumber(y); | |
573 } | |
574 | |
575 /** | |
576 * Adds the given mouse listener. | |
577 * | |
578 * @param listener the listener to be added | |
579 * @deprecated will be removed | |
580 * @since 2.0 | |
581 */ | |
582 public void addMouseListener(MouseListener listener) { | |
583 if (fCanvas !is null && !fCanvas.isDisposed()) | |
584 fCanvas.addMouseListener(listener); | |
585 } | |
586 | |
587 /** | |
588 * Removes the given mouse listener. | |
589 * | |
590 * @param listener the listener to be removed | |
591 * @deprecated will be removed | |
592 * @since 2.0 | |
593 */ | |
594 public void removeMouseListener(MouseListener listener) { | |
595 if (fCanvas !is null && !fCanvas.isDisposed()) | |
596 fCanvas.removeMouseListener(listener); | |
597 } | |
598 } |