Mercurial > projects > dwt2
comparison org.eclipse.jface.text/src/org/eclipse/jface/text/source/LineNumberRulerColumn.d @ 12:bc29606a740c
Added dwt-addons in original directory structure of eclipse.org
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sat, 14 Mar 2009 18:23:29 +0100 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
11:43904fec5dca | 12:bc29606a740c |
---|---|
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> - [rulers] Shift clicking in line number column doesn't select range - https://bugs.eclipse.org/bugs/show_bug.cgi?id=32166 | |
11 * Nikolay Botev <bono8106@hotmail.com> - [rulers] Clicking in line number ruler should not trigger annotation ruler - https://bugs.eclipse.org/bugs/show_bug.cgi?id=40889 | |
12 * Port to the D programming language: | |
13 * Frank Benoit <benoit@tionex.de> | |
14 *******************************************************************************/ | |
15 module org.eclipse.jface.text.source.LineNumberRulerColumn; | |
16 | |
17 import org.eclipse.jface.text.source.ISharedTextColors; // packageimport | |
18 import org.eclipse.jface.text.source.ILineRange; // packageimport | |
19 import org.eclipse.jface.text.source.IAnnotationPresentation; // packageimport | |
20 import org.eclipse.jface.text.source.IVerticalRulerInfoExtension; // packageimport | |
21 import org.eclipse.jface.text.source.ICharacterPairMatcher; // packageimport | |
22 import org.eclipse.jface.text.source.TextInvocationContext; // packageimport | |
23 import org.eclipse.jface.text.source.LineChangeHover; // packageimport | |
24 import org.eclipse.jface.text.source.IChangeRulerColumn; // packageimport | |
25 import org.eclipse.jface.text.source.IAnnotationMap; // packageimport | |
26 import org.eclipse.jface.text.source.IAnnotationModelListenerExtension; // packageimport | |
27 import org.eclipse.jface.text.source.ISourceViewerExtension2; // packageimport | |
28 import org.eclipse.jface.text.source.IAnnotationHover; // packageimport | |
29 import org.eclipse.jface.text.source.ContentAssistantFacade; // packageimport | |
30 import org.eclipse.jface.text.source.IAnnotationAccess; // packageimport | |
31 import org.eclipse.jface.text.source.IVerticalRulerExtension; // packageimport | |
32 import org.eclipse.jface.text.source.IVerticalRulerColumn; // packageimport | |
33 import org.eclipse.jface.text.source.MatchingCharacterPainter; // packageimport | |
34 import org.eclipse.jface.text.source.IAnnotationModelExtension; // packageimport | |
35 import org.eclipse.jface.text.source.ILineDifferExtension; // packageimport | |
36 import org.eclipse.jface.text.source.DefaultCharacterPairMatcher; // packageimport | |
37 import org.eclipse.jface.text.source.LineNumberChangeRulerColumn; // packageimport | |
38 import org.eclipse.jface.text.source.IAnnotationAccessExtension; // packageimport | |
39 import org.eclipse.jface.text.source.ISourceViewer; // packageimport | |
40 import org.eclipse.jface.text.source.AnnotationModel; // packageimport | |
41 import org.eclipse.jface.text.source.ILineDifferExtension2; // packageimport | |
42 import org.eclipse.jface.text.source.IAnnotationModelListener; // packageimport | |
43 import org.eclipse.jface.text.source.IVerticalRuler; // packageimport | |
44 import org.eclipse.jface.text.source.DefaultAnnotationHover; // packageimport | |
45 import org.eclipse.jface.text.source.SourceViewer; // packageimport | |
46 import org.eclipse.jface.text.source.SourceViewerConfiguration; // packageimport | |
47 import org.eclipse.jface.text.source.AnnotationBarHoverManager; // packageimport | |
48 import org.eclipse.jface.text.source.CompositeRuler; // packageimport | |
49 import org.eclipse.jface.text.source.ImageUtilities; // packageimport | |
50 import org.eclipse.jface.text.source.VisualAnnotationModel; // packageimport | |
51 import org.eclipse.jface.text.source.IAnnotationModel; // packageimport | |
52 import org.eclipse.jface.text.source.ISourceViewerExtension3; // packageimport | |
53 import org.eclipse.jface.text.source.ILineDiffInfo; // packageimport | |
54 import org.eclipse.jface.text.source.VerticalRulerEvent; // packageimport | |
55 import org.eclipse.jface.text.source.ChangeRulerColumn; // packageimport | |
56 import org.eclipse.jface.text.source.ILineDiffer; // packageimport | |
57 import org.eclipse.jface.text.source.AnnotationModelEvent; // packageimport | |
58 import org.eclipse.jface.text.source.AnnotationColumn; // packageimport | |
59 import org.eclipse.jface.text.source.AnnotationRulerColumn; // packageimport | |
60 import org.eclipse.jface.text.source.IAnnotationHoverExtension; // packageimport | |
61 import org.eclipse.jface.text.source.AbstractRulerColumn; // packageimport | |
62 import org.eclipse.jface.text.source.ISourceViewerExtension; // packageimport | |
63 import org.eclipse.jface.text.source.AnnotationMap; // packageimport | |
64 import org.eclipse.jface.text.source.IVerticalRulerInfo; // packageimport | |
65 import org.eclipse.jface.text.source.IAnnotationModelExtension2; // packageimport | |
66 import org.eclipse.jface.text.source.LineRange; // packageimport | |
67 import org.eclipse.jface.text.source.IAnnotationAccessExtension2; // packageimport | |
68 import org.eclipse.jface.text.source.VerticalRuler; // packageimport | |
69 import org.eclipse.jface.text.source.JFaceTextMessages; // packageimport | |
70 import org.eclipse.jface.text.source.IOverviewRuler; // packageimport | |
71 import org.eclipse.jface.text.source.Annotation; // packageimport | |
72 import org.eclipse.jface.text.source.IVerticalRulerListener; // packageimport | |
73 import org.eclipse.jface.text.source.ISourceViewerExtension4; // packageimport | |
74 import org.eclipse.jface.text.source.AnnotationPainter; // packageimport | |
75 import org.eclipse.jface.text.source.IAnnotationHoverExtension2; // packageimport | |
76 import org.eclipse.jface.text.source.OverviewRuler; // packageimport | |
77 import org.eclipse.jface.text.source.OverviewRulerHoverManager; // packageimport | |
78 | |
79 | |
80 import java.lang.all; | |
81 import java.util.Arrays; | |
82 import java.util.Set; | |
83 | |
84 | |
85 import org.eclipse.swt.SWT; | |
86 import org.eclipse.swt.custom.StyledText; | |
87 import org.eclipse.swt.events.DisposeEvent; | |
88 import org.eclipse.swt.events.DisposeListener; | |
89 import org.eclipse.swt.events.MouseEvent; | |
90 import org.eclipse.swt.events.MouseListener; | |
91 import org.eclipse.swt.events.MouseMoveListener; | |
92 import org.eclipse.swt.events.PaintEvent; | |
93 import org.eclipse.swt.events.PaintListener; | |
94 import org.eclipse.swt.graphics.Color; | |
95 import org.eclipse.swt.graphics.Font; | |
96 import org.eclipse.swt.graphics.FontMetrics; | |
97 import org.eclipse.swt.graphics.GC; | |
98 import org.eclipse.swt.graphics.Image; | |
99 import org.eclipse.swt.graphics.Point; | |
100 import org.eclipse.swt.graphics.Rectangle; | |
101 import org.eclipse.swt.widgets.Canvas; | |
102 import org.eclipse.swt.widgets.Composite; | |
103 import org.eclipse.swt.widgets.Control; | |
104 import org.eclipse.swt.widgets.Display; | |
105 import org.eclipse.swt.widgets.TypedListener; | |
106 import org.eclipse.jface.text.BadLocationException; | |
107 import org.eclipse.jface.text.IDocument; | |
108 import org.eclipse.jface.text.IRegion; | |
109 import org.eclipse.jface.text.ITextListener; | |
110 import org.eclipse.jface.text.ITextViewer; | |
111 import org.eclipse.jface.text.ITextViewerExtension; | |
112 import org.eclipse.jface.text.ITextViewerExtension5; | |
113 import org.eclipse.jface.text.IViewportListener; | |
114 import org.eclipse.jface.text.JFaceTextUtil; | |
115 import org.eclipse.jface.text.TextEvent; | |
116 | |
117 | |
118 /** | |
119 * A vertical ruler column displaying line numbers. | |
120 * Clients usually instantiate and configure object of this class. | |
121 * | |
122 * @since 2.0 | |
123 */ | |
124 public class LineNumberRulerColumn : IVerticalRulerColumn { | |
125 | |
126 /** | |
127 * Internal listener class. | |
128 */ | |
129 class InternalListener : IViewportListener, ITextListener { | |
130 | |
131 /** | |
132 * @since 3.1 | |
133 */ | |
134 private bool fCachedRedrawState= true; | |
135 | |
136 /* | |
137 * @see IViewportListener#viewportChanged(int) | |
138 */ | |
139 public void viewportChanged(int verticalPosition) { | |
140 if (fCachedRedrawState && verticalPosition !is fScrollPos) | |
141 redraw(); | |
142 } | |
143 | |
144 /* | |
145 * @see ITextListener#textChanged(TextEvent) | |
146 */ | |
147 public void textChanged(TextEvent event) { | |
148 | |
149 fCachedRedrawState= event.getViewerRedrawState(); | |
150 if (!fCachedRedrawState) | |
151 return; | |
152 | |
153 if (updateNumberOfDigits()) { | |
154 computeIndentations(); | |
155 layout(event.getViewerRedrawState()); | |
156 return; | |
157 } | |
158 | |
159 bool viewerCompletelyShown= isViewerCompletelyShown(); | |
160 if (viewerCompletelyShown || fSensitiveToTextChanges || event.getDocumentEvent() is null) | |
161 postRedraw(); | |
162 fSensitiveToTextChanges= viewerCompletelyShown; | |
163 } | |
164 } | |
165 | |
166 /** | |
167 * Handles all the mouse interaction in this line number ruler column. | |
168 */ | |
169 class MouseHandler : MouseListener, MouseMoveListener { | |
170 | |
171 /** The cached view port size. */ | |
172 private int fCachedViewportSize; | |
173 /** The area of the line at which line selection started. */ | |
174 private int fStartLineOffset; | |
175 /** The number of the line at which line selection started. */ | |
176 private int fStartLineNumber; | |
177 /** The auto scroll direction. */ | |
178 private int fAutoScrollDirection; | |
179 /* @since 3.2 */ | |
180 private bool fIsListeningForMove= false; | |
181 | |
182 /* | |
183 * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent) | |
184 */ | |
185 public void mouseUp(MouseEvent event) { | |
186 // see bug 45700 | |
187 if (event.button is 1) { | |
188 stopSelecting(); | |
189 stopAutoScroll(); | |
190 } | |
191 } | |
192 | |
193 /* | |
194 * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent) | |
195 */ | |
196 public void mouseDown(MouseEvent event) { | |
197 fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); | |
198 // see bug 45700 | |
199 if (event.button is 1) { | |
200 startSelecting((event.stateMask & SWT.SHIFT) !is 0); | |
201 } | |
202 } | |
203 | |
204 /* | |
205 * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent) | |
206 */ | |
207 public void mouseDoubleClick(MouseEvent event) { | |
208 fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); | |
209 stopSelecting(); | |
210 stopAutoScroll(); | |
211 } | |
212 | |
213 /* | |
214 * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent) | |
215 */ | |
216 public void mouseMove(MouseEvent event) { | |
217 if (fIsListeningForMove && !autoScroll(event)) { | |
218 int newLine= fParentRuler.toDocumentLineNumber(event.y); | |
219 expandSelection(newLine); | |
220 } | |
221 fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); | |
222 } | |
223 | |
224 /** | |
225 * Called when line drag selection started. Adds mouse move and track | |
226 * listeners to this column's control. | |
227 * | |
228 * @param expandExistingSelection if <code>true</code> the existing selection will be expanded, | |
229 * otherwise a new selection is started | |
230 */ | |
231 private void startSelecting(bool expandExistingSelection) { | |
232 try { | |
233 | |
234 // select line | |
235 IDocument document= fCachedTextViewer.getDocument(); | |
236 int lineNumber= fParentRuler.getLineOfLastMouseButtonActivity(); | |
237 if (expandExistingSelection && cast(ITextViewerExtension5)fCachedTextViewer | |
238 && fCachedTextViewer.getTextWidget() !is null) { | |
239 ITextViewerExtension5 extension5= (cast(ITextViewerExtension5)fCachedTextViewer); | |
240 // Find model curosr position | |
241 int widgetCaret= fCachedTextViewer.getTextWidget().getCaretOffset(); | |
242 int modelCaret= extension5.widgetOffset2ModelOffset(widgetCaret); | |
243 // Find model selection range | |
244 Point selection= fCachedTextViewer.getSelectedRange(); | |
245 // Start from tail of selection range (opposite of cursor position) | |
246 int startOffset= modelCaret is selection.x ? selection.x + selection.y : selection.x; | |
247 | |
248 fStartLineNumber= document.getLineOfOffset(startOffset); | |
249 fStartLineOffset= startOffset; | |
250 | |
251 expandSelection(lineNumber); | |
252 } else { | |
253 fStartLineNumber= lineNumber; | |
254 fStartLineOffset= document.getLineInformation(fStartLineNumber).getOffset(); | |
255 fCachedTextViewer.setSelectedRange(fStartLineOffset, 0); | |
256 } | |
257 fCachedViewportSize= getVisibleLinesInViewport(); | |
258 | |
259 // prepare for drag selection | |
260 fIsListeningForMove= true; | |
261 | |
262 } catch (BadLocationException x) { | |
263 } | |
264 } | |
265 | |
266 /** | |
267 * Called when line drag selection stopped. Removes all previously | |
268 * installed listeners from this column's control. | |
269 */ | |
270 private void stopSelecting() { | |
271 // drag selection stopped | |
272 fIsListeningForMove= false; | |
273 } | |
274 | |
275 /** | |
276 * Expands the line selection from the remembered start line to the | |
277 * given line. | |
278 * | |
279 * @param lineNumber the line to which to expand the selection | |
280 */ | |
281 private void expandSelection(int lineNumber) { | |
282 try { | |
283 | |
284 IDocument document= fCachedTextViewer.getDocument(); | |
285 IRegion lineInfo= document.getLineInformation(lineNumber); | |
286 | |
287 Display display= fCachedTextWidget.getDisplay(); | |
288 Point absolutePosition= display.getCursorLocation(); | |
289 Point relativePosition= fCachedTextWidget.toControl(absolutePosition); | |
290 | |
291 int offset; | |
292 | |
293 if (relativePosition.x < 0) | |
294 offset= lineInfo.getOffset(); | |
295 else { | |
296 try { | |
297 int widgetOffset= fCachedTextWidget.getOffsetAtLocation(relativePosition); | |
298 Point p= fCachedTextWidget.getLocationAtOffset(widgetOffset); | |
299 if (p.x > relativePosition.x) | |
300 widgetOffset--; | |
301 | |
302 // Convert to model offset | |
303 if ( cast(ITextViewerExtension5)fCachedTextViewer ) { | |
304 ITextViewerExtension5 extension= cast(ITextViewerExtension5)fCachedTextViewer; | |
305 offset= extension.widgetOffset2ModelOffset(widgetOffset); | |
306 } else | |
307 offset= widgetOffset + fCachedTextViewer.getVisibleRegion().getOffset(); | |
308 | |
309 } catch (IllegalArgumentException ex) { | |
310 int lineEndOffset= lineInfo.getOffset() + lineInfo.getLength(); | |
311 | |
312 // Convert to widget offset | |
313 int lineEndWidgetOffset; | |
314 if ( cast(ITextViewerExtension5)fCachedTextViewer ) { | |
315 ITextViewerExtension5 extension= cast(ITextViewerExtension5)fCachedTextViewer; | |
316 lineEndWidgetOffset= extension.modelOffset2WidgetOffset(lineEndOffset); | |
317 } else | |
318 lineEndWidgetOffset= lineEndOffset - fCachedTextViewer.getVisibleRegion().getOffset(); | |
319 | |
320 Point p= fCachedTextWidget.getLocationAtOffset(lineEndWidgetOffset); | |
321 if (p.x < relativePosition.x) | |
322 offset= lineEndOffset; | |
323 else | |
324 offset= lineInfo.getOffset(); | |
325 } | |
326 } | |
327 | |
328 int start= Math.min(fStartLineOffset, offset); | |
329 int end= Math.max(fStartLineOffset, offset); | |
330 | |
331 if (lineNumber < fStartLineNumber) | |
332 fCachedTextViewer.setSelectedRange(end, start - end); | |
333 else | |
334 fCachedTextViewer.setSelectedRange(start, end - start); | |
335 | |
336 } catch (BadLocationException x) { | |
337 } | |
338 } | |
339 | |
340 /** | |
341 * Called when auto scrolling stopped. Clears the auto scroll direction. | |
342 */ | |
343 private void stopAutoScroll() { | |
344 fAutoScrollDirection= SWT.NULL; | |
345 } | |
346 | |
347 /** | |
348 * Called on drag selection. | |
349 * | |
350 * @param event the mouse event caught by the mouse move listener | |
351 * @return <code>true</code> if scrolling happened, <code>false</code> otherwise | |
352 */ | |
353 private bool autoScroll(MouseEvent event) { | |
354 Rectangle area= fCanvas.getClientArea(); | |
355 | |
356 if (event.y > area.height) { | |
357 autoScroll(SWT.DOWN); | |
358 return true; | |
359 } | |
360 | |
361 if (event.y < 0) { | |
362 autoScroll(SWT.UP); | |
363 return true; | |
364 } | |
365 | |
366 stopAutoScroll(); | |
367 return false; | |
368 } | |
369 | |
370 /** | |
371 * Scrolls the viewer into the given direction. | |
372 * | |
373 * @param direction the scroll direction | |
374 */ | |
375 private void autoScroll(int direction) { | |
376 | |
377 if (fAutoScrollDirection is direction) | |
378 return; | |
379 | |
380 final int TIMER_INTERVAL= 5; | |
381 final Display display= fCanvas.getDisplay(); | |
382 Runnable timer= null; | |
383 switch (direction) { | |
384 case SWT.UP: | |
385 timer= new class() Runnable { | |
386 public void run() { | |
387 if (fAutoScrollDirection is SWT.UP) { | |
388 int top= getInclusiveTopIndex(); | |
389 if (top > 0) { | |
390 fCachedTextViewer.setTopIndex(top -1); | |
391 expandSelection(top -1); | |
392 display.timerExec(TIMER_INTERVAL, this); | |
393 } | |
394 } | |
395 } | |
396 }; | |
397 break; | |
398 case SWT.DOWN: | |
399 timer= new class() Runnable { | |
400 public void run() { | |
401 if (fAutoScrollDirection is SWT.DOWN) { | |
402 int top= getInclusiveTopIndex(); | |
403 fCachedTextViewer.setTopIndex(top +1); | |
404 expandSelection(top +1 + fCachedViewportSize); | |
405 display.timerExec(TIMER_INTERVAL, this); | |
406 } | |
407 } | |
408 }; | |
409 break; | |
410 default: | |
411 } | |
412 | |
413 if (timer !is null) { | |
414 fAutoScrollDirection= direction; | |
415 display.timerExec(TIMER_INTERVAL, timer); | |
416 } | |
417 } | |
418 | |
419 /** | |
420 * Returns the viewer's first visible line, even if only partially visible. | |
421 * | |
422 * @return the viewer's first visible line | |
423 */ | |
424 private int getInclusiveTopIndex() { | |
425 if (fCachedTextWidget !is null && !fCachedTextWidget.isDisposed()) { | |
426 return JFaceTextUtil.getPartialTopIndex(fCachedTextViewer); | |
427 } | |
428 return -1; | |
429 } | |
430 } | |
431 | |
432 /** This column's parent ruler */ | |
433 private CompositeRuler fParentRuler; | |
434 /** Cached text viewer */ | |
435 private ITextViewer fCachedTextViewer; | |
436 /** Cached text widget */ | |
437 private StyledText fCachedTextWidget; | |
438 /** The columns canvas */ | |
439 private Canvas fCanvas; | |
440 /** Cache for the actual scroll position in pixels */ | |
441 private int fScrollPos; | |
442 /** The drawable for double buffering */ | |
443 private Image fBuffer; | |
444 /** The internal listener */ | |
445 private InternalListener fInternalListener; | |
446 /** The font of this column */ | |
447 private Font fFont; | |
448 /** The indentation cache */ | |
449 private int[] fIndentation; | |
450 /** Indicates whether this column reacts on text change events */ | |
451 private bool fSensitiveToTextChanges= false; | |
452 /** The foreground color */ | |
453 private Color fForeground; | |
454 /** The background color */ | |
455 private Color fBackground; | |
456 /** Cached number of displayed digits */ | |
457 private int fCachedNumberOfDigits= -1; | |
458 /** Flag indicating whether a relayout is required */ | |
459 private bool fRelayoutRequired= false; | |
460 /** | |
461 * Redraw runnable lock | |
462 * @since 3.0 | |
463 */ | |
464 private Object fRunnableLock; | |
465 /** | |
466 * Redraw runnable state | |
467 * @since 3.0 | |
468 */ | |
469 private bool fIsRunnablePosted= false; | |
470 /** | |
471 * Redraw runnable | |
472 * @since 3.0 | |
473 */ | |
474 private Runnable fRunnable; | |
475 private void fRunnable_init() { | |
476 fRunnable = new class() Runnable { | |
477 public void run() { | |
478 synchronized (fRunnableLock) { | |
479 fIsRunnablePosted= false; | |
480 } | |
481 redraw(); | |
482 } | |
483 }; | |
484 } | |
485 /* @since 3.2 */ | |
486 private MouseHandler fMouseHandler; | |
487 | |
488 | |
489 /** | |
490 * Constructs a new vertical ruler column. | |
491 */ | |
492 public this() { | |
493 fInternalListener= new InternalListener(); | |
494 fRunnableLock= new Object(); | |
495 fRunnable_init(); | |
496 } | |
497 | |
498 /** | |
499 * Sets the foreground color of this column. | |
500 * | |
501 * @param foreground the foreground color | |
502 */ | |
503 public void setForeground(Color foreground) { | |
504 fForeground= foreground; | |
505 } | |
506 | |
507 /** | |
508 * Returns the foreground color being used to print the line numbers. | |
509 * | |
510 * @return the configured foreground color | |
511 * @since 3.0 | |
512 */ | |
513 protected Color getForeground() { | |
514 return fForeground; | |
515 } | |
516 | |
517 /** | |
518 * Sets the background color of this column. | |
519 * | |
520 * @param background the background color | |
521 */ | |
522 public void setBackground(Color background) { | |
523 fBackground= background; | |
524 if (fCanvas !is null && !fCanvas.isDisposed()) | |
525 fCanvas.setBackground(getBackground(fCanvas.getDisplay())); | |
526 } | |
527 | |
528 /** | |
529 * Returns the System background color for list widgets. | |
530 * | |
531 * @param display the display | |
532 * @return the System background color for list widgets | |
533 */ | |
534 protected Color getBackground(Display display) { | |
535 if (fBackground is null) | |
536 return display.getSystemColor(SWT.COLOR_LIST_BACKGROUND); | |
537 return fBackground; | |
538 } | |
539 | |
540 /* | |
541 * @see IVerticalRulerColumn#getControl() | |
542 */ | |
543 public Control getControl() { | |
544 return fCanvas; | |
545 } | |
546 | |
547 /* | |
548 * @see IVerticalRuleColumnr#getWidth | |
549 */ | |
550 public int getWidth() { | |
551 return fIndentation[0]; | |
552 } | |
553 | |
554 /** | |
555 * Computes the number of digits to be displayed. Returns | |
556 * <code>true</code> if the number of digits changed compared | |
557 * to the previous call of this method. If the method is called | |
558 * for the first time, the return value is also <code>true</code>. | |
559 * | |
560 * @return whether the number of digits has been changed | |
561 * @since 3.0 | |
562 */ | |
563 protected bool updateNumberOfDigits() { | |
564 if (fCachedTextViewer is null) | |
565 return false; | |
566 | |
567 int digits= computeNumberOfDigits(); | |
568 | |
569 if (fCachedNumberOfDigits !is digits) { | |
570 fCachedNumberOfDigits= digits; | |
571 return true; | |
572 } | |
573 | |
574 return false; | |
575 } | |
576 | |
577 /** | |
578 * Does the real computation of the number of digits. Subclasses may override this method if | |
579 * they need extra space on the line number ruler. | |
580 * | |
581 * @return the number of digits to be displayed on the line number ruler. | |
582 */ | |
583 protected int computeNumberOfDigits() { | |
584 IDocument document= fCachedTextViewer.getDocument(); | |
585 int lines= document is null ? 0 : document.getNumberOfLines(); | |
586 | |
587 int digits= 2; | |
588 while (lines > Math.pow(cast(real)10.0, cast(uint)digits) -1) { | |
589 ++digits; | |
590 } | |
591 return digits; | |
592 } | |
593 | |
594 /** | |
595 * Layouts the enclosing viewer to adapt the layout to changes of the | |
596 * size of the individual components. | |
597 * | |
598 * @param redraw <code>true</code> if this column can be redrawn | |
599 */ | |
600 protected void layout(bool redraw) { | |
601 if (!redraw) { | |
602 fRelayoutRequired= true; | |
603 return; | |
604 } | |
605 | |
606 fRelayoutRequired= false; | |
607 if ( cast(ITextViewerExtension)fCachedTextViewer ) { | |
608 ITextViewerExtension extension= cast(ITextViewerExtension) fCachedTextViewer; | |
609 Control control= extension.getControl(); | |
610 if ( cast(Composite)control && !control.isDisposed()) { | |
611 Composite composite= cast(Composite) control; | |
612 composite.layout(true); | |
613 } | |
614 } | |
615 } | |
616 | |
617 /** | |
618 * Computes the indentations for the given font and stores them in | |
619 * <code>fIndentation</code>. | |
620 */ | |
621 protected void computeIndentations() { | |
622 if (fCanvas is null || fCanvas.isDisposed()) | |
623 return; | |
624 | |
625 GC gc= new GC(fCanvas); | |
626 try { | |
627 | |
628 gc.setFont(fCanvas.getFont()); | |
629 | |
630 fIndentation= new int[fCachedNumberOfDigits + 1]; | |
631 | |
632 char[] nines= new char[fCachedNumberOfDigits]; | |
633 Arrays.fill(nines, '9'); | |
634 String nineString= new_String(nines); | |
635 Point p= gc.stringExtent(nineString); | |
636 fIndentation[0]= p.x; | |
637 | |
638 for (int i= 1; i <= fCachedNumberOfDigits; i++) { | |
639 p= gc.stringExtent(nineString.substring(0, i)); | |
640 fIndentation[i]= fIndentation[0] - p.x; | |
641 } | |
642 | |
643 } finally { | |
644 gc.dispose(); | |
645 } | |
646 } | |
647 | |
648 /* | |
649 * @see IVerticalRulerColumn#createControl(CompositeRuler, Composite) | |
650 */ | |
651 public Control createControl(CompositeRuler parentRuler, Composite parentControl) { | |
652 | |
653 fParentRuler= parentRuler; | |
654 fCachedTextViewer= parentRuler.getTextViewer(); | |
655 fCachedTextWidget= fCachedTextViewer.getTextWidget(); | |
656 | |
657 fCanvas= new class(parentControl, SWT.NO_FOCUS ) Canvas { | |
658 this(Composite c, int s ){ | |
659 super(c,s); | |
660 } | |
661 /* | |
662 * @see org.eclipse.swt.widgets.Control#addMouseListener(org.eclipse.swt.events.MouseListener) | |
663 * @since 3.4 | |
664 */ | |
665 public void addMouseListener(MouseListener listener) { | |
666 // see bug 40889, bug 230073 and AnnotationRulerColumn#isPropagatingMouseListener() | |
667 if (listener is fMouseHandler) | |
668 super.addMouseListener(listener); | |
669 else { | |
670 TypedListener typedListener= null; | |
671 if (listener !is null) | |
672 typedListener= new TypedListener(listener); | |
673 addListener(SWT.MouseDoubleClick, typedListener); | |
674 } | |
675 } | |
676 }; | |
677 fCanvas.setBackground(getBackground(fCanvas.getDisplay())); | |
678 fCanvas.setForeground(fForeground); | |
679 | |
680 fCanvas.addPaintListener(new class() PaintListener { | |
681 public void paintControl(PaintEvent event) { | |
682 if (fCachedTextViewer !is null) | |
683 doubleBufferPaint(event.gc); | |
684 } | |
685 }); | |
686 | |
687 fCanvas.addDisposeListener(new class() DisposeListener { | |
688 public void widgetDisposed(DisposeEvent e) { | |
689 handleDispose(); | |
690 fCachedTextViewer= null; | |
691 fCachedTextWidget= null; | |
692 } | |
693 }); | |
694 | |
695 fMouseHandler= new MouseHandler(); | |
696 fCanvas.addMouseListener(fMouseHandler); | |
697 fCanvas.addMouseMoveListener(fMouseHandler); | |
698 | |
699 if (fCachedTextViewer !is null) { | |
700 | |
701 fCachedTextViewer.addViewportListener(fInternalListener); | |
702 fCachedTextViewer.addTextListener(fInternalListener); | |
703 | |
704 if (fFont is null) { | |
705 if (fCachedTextWidget !is null && !fCachedTextWidget.isDisposed()) | |
706 fFont= fCachedTextWidget.getFont(); | |
707 } | |
708 } | |
709 | |
710 if (fFont !is null) | |
711 fCanvas.setFont(fFont); | |
712 | |
713 updateNumberOfDigits(); | |
714 computeIndentations(); | |
715 return fCanvas; | |
716 } | |
717 | |
718 /** | |
719 * Disposes the column's resources. | |
720 */ | |
721 protected void handleDispose() { | |
722 | |
723 if (fCachedTextViewer !is null) { | |
724 fCachedTextViewer.removeViewportListener(fInternalListener); | |
725 fCachedTextViewer.removeTextListener(fInternalListener); | |
726 } | |
727 | |
728 if (fBuffer !is null) { | |
729 fBuffer.dispose(); | |
730 fBuffer= null; | |
731 } | |
732 } | |
733 | |
734 /** | |
735 * Double buffer drawing. | |
736 * | |
737 * @param dest the GC to draw into | |
738 */ | |
739 private void doubleBufferPaint(GC dest) { | |
740 | |
741 Point size= fCanvas.getSize(); | |
742 | |
743 if (size.x <= 0 || size.y <= 0) | |
744 return; | |
745 | |
746 if (fBuffer !is null) { | |
747 Rectangle r= fBuffer.getBounds(); | |
748 if (r.width !is size.x || r.height !is size.y) { | |
749 fBuffer.dispose(); | |
750 fBuffer= null; | |
751 } | |
752 } | |
753 if (fBuffer is null) | |
754 fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y); | |
755 | |
756 GC gc= new GC(fBuffer); | |
757 gc.setFont(fCanvas.getFont()); | |
758 if (fForeground !is null) | |
759 gc.setForeground(fForeground); | |
760 | |
761 try { | |
762 gc.setBackground(getBackground(fCanvas.getDisplay())); | |
763 gc.fillRectangle(0, 0, size.x, size.y); | |
764 | |
765 ILineRange visibleLines= JFaceTextUtil.getVisibleModelLines(fCachedTextViewer); | |
766 if (visibleLines is null) | |
767 return; | |
768 fScrollPos= fCachedTextWidget.getTopPixel(); | |
769 doPaint(gc, visibleLines); | |
770 } finally { | |
771 gc.dispose(); | |
772 } | |
773 | |
774 dest.drawImage(fBuffer, 0, 0); | |
775 } | |
776 | |
777 /** | |
778 * Returns the view port height in lines. | |
779 * | |
780 * @return the view port height in lines | |
781 * @deprecated as of 3.2 the number of lines in the viewport cannot be computed because | |
782 * StyledText supports variable line heights | |
783 */ | |
784 protected int getVisibleLinesInViewport() { | |
785 return getVisibleLinesInViewport(fCachedTextWidget); | |
786 } | |
787 | |
788 | |
789 /** | |
790 * Returns <code>true</code> if the viewport displays the entire viewer contents, i.e. the | |
791 * viewer is not vertically scrollable. | |
792 * | |
793 * @return <code>true</code> if the viewport displays the entire contents, <code>false</code> otherwise | |
794 * @since 3.2 | |
795 */ | |
796 protected final bool isViewerCompletelyShown() { | |
797 return JFaceTextUtil.isShowingEntireContents(fCachedTextWidget); | |
798 } | |
799 | |
800 /** | |
801 * Draws the ruler column. | |
802 * | |
803 * @param gc the GC to draw into | |
804 * @param visibleLines the visible model lines | |
805 * @since 3.2 | |
806 */ | |
807 void doPaint(GC gc, ILineRange visibleLines) { | |
808 Display display= fCachedTextWidget.getDisplay(); | |
809 | |
810 // draw diff info | |
811 int y= -JFaceTextUtil.getHiddenTopLinePixels(fCachedTextWidget); | |
812 | |
813 int lastLine= end(visibleLines); | |
814 for (int line= visibleLines.getStartLine(); line < lastLine; line++) { | |
815 int widgetLine= JFaceTextUtil.modelLineToWidgetLine(fCachedTextViewer, line); | |
816 if (widgetLine is -1) | |
817 continue; | |
818 | |
819 int lineHeight= fCachedTextWidget.getLineHeight(fCachedTextWidget.getOffsetAtLine(widgetLine)); | |
820 paintLine(line, y, lineHeight, gc, display); | |
821 y += lineHeight; | |
822 } | |
823 } | |
824 | |
825 /* @since 3.2 */ | |
826 private static int end(ILineRange range) { | |
827 return range.getStartLine() + range.getNumberOfLines(); | |
828 } | |
829 | |
830 /** | |
831 * Computes the string to be printed for <code>line</code>. The default implementation returns | |
832 * <code>Integer.toString(line + 1)</code>. | |
833 * | |
834 * @param line the line number for which the line number string is generated | |
835 * @return the string to be printed on the line number bar for <code>line</code> | |
836 * @since 3.0 | |
837 */ | |
838 protected String createDisplayString(int line) { | |
839 return Integer.toString(line + 1); | |
840 } | |
841 | |
842 /** | |
843 * Returns the difference between the baseline of the widget and the | |
844 * baseline as specified by the font for <code>gc</code>. When drawing | |
845 * line numbers, the returned bias should be added to obtain text lined up | |
846 * on the correct base line of the text widget. | |
847 * | |
848 * @param gc the <code>GC</code> to get the font metrics from | |
849 * @param widgetLine the widget line | |
850 * @return the baseline bias to use when drawing text that is lined up with | |
851 * <code>fCachedTextWidget</code> | |
852 * @since 3.2 | |
853 */ | |
854 private int getBaselineBias(GC gc, int widgetLine) { | |
855 /* | |
856 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=62951 | |
857 * widget line height may be more than the font height used for the | |
858 * line numbers, since font styles (bold, italics...) can have larger | |
859 * font metrics than the simple font used for the numbers. | |
860 */ | |
861 int offset= fCachedTextWidget.getOffsetAtLine(widgetLine); | |
862 int widgetBaseline= fCachedTextWidget.getBaseline(offset); | |
863 | |
864 FontMetrics fm= gc.getFontMetrics(); | |
865 int fontBaseline= fm.getAscent() + fm.getLeading(); | |
866 int baselineBias= widgetBaseline - fontBaseline; | |
867 return Math.max(0, baselineBias); | |
868 } | |
869 | |
870 /** | |
871 * Paints the line. After this method is called the line numbers are painted on top | |
872 * of the result of this method. | |
873 * | |
874 * @param line the line of the document which the ruler is painted for | |
875 * @param y the y-coordinate of the box being painted for <code>line</code>, relative to <code>gc</code> | |
876 * @param lineheight the height of one line (and therefore of the box being painted) | |
877 * @param gc the drawing context the client may choose to draw on. | |
878 * @param display the display the drawing occurs on | |
879 * @since 3.0 | |
880 */ | |
881 protected void paintLine(int line, int y, int lineheight, GC gc, Display display) { | |
882 int widgetLine= JFaceTextUtil.modelLineToWidgetLine(fCachedTextViewer, line); | |
883 | |
884 String s= createDisplayString(line); | |
885 int indentation= fIndentation[s.length]; | |
886 int baselineBias= getBaselineBias(gc, widgetLine); | |
887 gc.drawString(s, indentation, y + baselineBias, true); | |
888 } | |
889 | |
890 /** | |
891 * Triggers a redraw in the display thread. | |
892 * | |
893 * @since 3.0 | |
894 */ | |
895 protected final void postRedraw() { | |
896 if (fCanvas !is null && !fCanvas.isDisposed()) { | |
897 Display d= fCanvas.getDisplay(); | |
898 if (d !is null) { | |
899 synchronized (fRunnableLock) { | |
900 if (fIsRunnablePosted) | |
901 return; | |
902 fIsRunnablePosted= true; | |
903 } | |
904 d.asyncExec(fRunnable); | |
905 } | |
906 } | |
907 } | |
908 | |
909 /* | |
910 * @see IVerticalRulerColumn#redraw() | |
911 */ | |
912 public void redraw() { | |
913 | |
914 if (fRelayoutRequired) { | |
915 layout(true); | |
916 return; | |
917 } | |
918 | |
919 if (fCachedTextViewer !is null && fCanvas !is null && !fCanvas.isDisposed()) { | |
920 GC gc= new GC(fCanvas); | |
921 doubleBufferPaint(gc); | |
922 gc.dispose(); | |
923 } | |
924 } | |
925 | |
926 /* | |
927 * @see IVerticalRulerColumn#setModel(IAnnotationModel) | |
928 */ | |
929 public void setModel(IAnnotationModel model) { | |
930 } | |
931 | |
932 /* | |
933 * @see IVerticalRulerColumn#setFont(Font) | |
934 */ | |
935 public void setFont(Font font) { | |
936 fFont= font; | |
937 if (fCanvas !is null && !fCanvas.isDisposed()) { | |
938 fCanvas.setFont(fFont); | |
939 updateNumberOfDigits(); | |
940 computeIndentations(); | |
941 } | |
942 } | |
943 | |
944 /** | |
945 * Returns the parent (composite) ruler of this ruler column. | |
946 * | |
947 * @return the parent ruler | |
948 * @since 3.0 | |
949 */ | |
950 protected CompositeRuler getParentRuler() { | |
951 return fParentRuler; | |
952 } | |
953 | |
954 | |
955 /** | |
956 * Returns the number of lines in the view port. | |
957 * | |
958 * @param textWidget | |
959 * @return the number of lines visible in the view port <code>-1</code> if there's no client area | |
960 * @deprecated this method should not be used - it relies on the widget using a uniform line height | |
961 */ | |
962 static int getVisibleLinesInViewport(StyledText textWidget) { | |
963 if (textWidget !is null) { | |
964 Rectangle clArea= textWidget.getClientArea(); | |
965 if (!clArea.isEmpty()) { | |
966 int firstPixel= 0; | |
967 int lastPixel= clArea.height - 1; // XXX what about margins? don't take trims as they include scrollbars | |
968 int first= JFaceTextUtil.getLineIndex(textWidget, firstPixel); | |
969 int last= JFaceTextUtil.getLineIndex(textWidget, lastPixel); | |
970 return last - first; | |
971 } | |
972 } | |
973 return -1; | |
974 } | |
975 | |
976 } |