Mercurial > projects > dwt-addons
annotate dwtx/jface/internal/text/source/DiffPainter.d @ 134:51e6e63f930e
Regex fix for casts
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Sun, 24 Aug 2008 01:46:20 +0200 |
parents | 7d818bd32d63 |
children | 65801ad2b265 |
rev | line source |
---|---|
129 | 1 /******************************************************************************* |
2 * Copyright (c) 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 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.jface.internal.text.source.DiffPainter; | |
14 | |
131 | 15 |
16 | |
129 | 17 import dwt.dwthelper.utils; |
18 | |
19 | |
20 | |
21 | |
22 import dwt.DWT; | |
23 import dwt.custom.StyledText; | |
24 import dwt.events.DisposeEvent; | |
25 import dwt.events.DisposeListener; | |
26 import dwt.graphics.Color; | |
27 import dwt.graphics.GC; | |
28 import dwt.graphics.RGB; | |
29 import dwt.widgets.Canvas; | |
30 import dwt.widgets.Control; | |
31 import dwt.widgets.Display; | |
32 import dwtx.core.runtime.Assert; | |
33 import dwtx.jface.text.ITextViewer; | |
34 import dwtx.jface.text.JFaceTextUtil; | |
35 import dwtx.jface.text.source.CompositeRuler; | |
36 import dwtx.jface.text.source.IAnnotationHover; | |
37 import dwtx.jface.text.source.IAnnotationModel; | |
38 import dwtx.jface.text.source.IAnnotationModelExtension; | |
39 import dwtx.jface.text.source.IAnnotationModelListener; | |
40 import dwtx.jface.text.source.IChangeRulerColumn; | |
41 import dwtx.jface.text.source.ILineDiffInfo; | |
42 import dwtx.jface.text.source.ILineDiffer; | |
43 import dwtx.jface.text.source.ILineDifferExtension2; | |
44 import dwtx.jface.text.source.ILineRange; | |
45 import dwtx.jface.text.source.ISharedTextColors; | |
46 import dwtx.jface.text.source.IVerticalRulerColumn; | |
47 | |
48 | |
49 /** | |
50 * A strategy for painting the quick diff colors onto the vertical ruler column. It also manages the | |
51 * quick diff hover. | |
52 * | |
53 * @since 3.2 | |
54 */ | |
55 public final class DiffPainter { | |
56 /** | |
57 * Internal listener class that will update the ruler when the underlying model changes. | |
58 */ | |
59 private class AnnotationListener : IAnnotationModelListener { | |
60 /* | |
61 * @see dwtx.jface.text.source.IAnnotationModelListener#modelChanged(dwtx.jface.text.source.IAnnotationModel) | |
62 */ | |
63 public void modelChanged(IAnnotationModel model) { | |
64 postRedraw(); | |
65 } | |
66 } | |
67 | |
68 /** The vertical ruler column that delegates painting to this painter. */ | |
69 private final IVerticalRulerColumn fColumn; | |
70 /** The parent ruler. */ | |
71 private CompositeRuler fParentRuler; | |
72 /** The column's control, typically a {@link Canvas}, possibly <code>null</code>. */ | |
73 private Control fControl; | |
74 /** The text viewer that the column is attached to. */ | |
75 private ITextViewer fViewer; | |
76 /** The viewer's text widget. */ | |
77 private StyledText fWidget; | |
78 /** The line differ extracted from the annotation model. */ | |
79 private ILineDiffer fLineDiffer= null; | |
80 /** Color for changed lines */ | |
81 private Color fAddedColor; | |
82 /** Color for added lines */ | |
83 private Color fChangedColor; | |
84 /** Color for the deleted line indicator */ | |
85 private Color fDeletedColor; | |
86 /** The background color. */ | |
87 private Color fBackground; | |
88 /** The ruler's hover */ | |
89 private IAnnotationHover fHover; | |
90 /** The internal listener */ | |
91 private final AnnotationListener fAnnotationListener= new AnnotationListener(); | |
92 /** The shared color provider, possibly <code>null</code>. */ | |
93 private final ISharedTextColors fSharedColors; | |
94 | |
95 /** | |
96 * Creates a new diff painter for a vertical ruler column. | |
97 * | |
98 * @param column the column that will delegate{@link #paint(GC, ILineRange) painting} to the | |
99 * newly created painter. | |
100 * @param sharedColors a shared colors object to store shaded colors in, may be | |
101 * <code>null</code> | |
102 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
103 public this(IVerticalRulerColumn column, ISharedTextColors sharedColors) { |
129 | 104 Assert.isLegal(column !is null); |
105 fColumn= column; | |
106 fSharedColors= sharedColors; | |
107 } | |
108 | |
109 /** | |
110 * Sets the parent ruler - the delegating column must call this method as soon as it creates its | |
111 * control. | |
112 * | |
113 * @param parentRuler the parent ruler | |
114 */ | |
115 public void setParentRuler(CompositeRuler parentRuler) { | |
116 fParentRuler= parentRuler; | |
117 } | |
118 | |
119 /** | |
120 * Sets the quick diff hover later returned by {@link #getHover()}. | |
121 * | |
122 * @param hover the hover | |
123 */ | |
124 public void setHover(IAnnotationHover hover) { | |
125 fHover= hover; | |
126 } | |
127 | |
128 /** | |
129 * Returns the quick diff hover set by {@link #setHover(IAnnotationHover)}. | |
130 * | |
131 * @return the quick diff hover set by {@link #setHover(IAnnotationHover)} | |
132 */ | |
133 public IAnnotationHover getHover() { | |
134 return fHover; | |
135 } | |
136 | |
137 /** | |
138 * Sets the background color. | |
139 * | |
140 * @param background the background color, <code>null</code> to use the platform's list background | |
141 */ | |
142 public void setBackground(Color background) { | |
143 fBackground= background; | |
144 } | |
145 | |
146 /** | |
147 * Delegates the painting of the quick diff colors to this painter. The painter will draw the | |
148 * color boxes onto the passed {@link GC} for all model (document) lines in | |
149 * <code>visibleModelLines</code>. | |
150 * | |
151 * @param gc the {@link GC} to draw onto | |
152 * @param visibleModelLines the lines (in document offsets) that are currently (perhaps only | |
153 * partially) visible | |
154 */ | |
155 public void paint(GC gc, ILineRange visibleModelLines) { | |
156 connectIfNeeded(); | |
157 if (!isConnected()) | |
158 return; | |
159 | |
160 // draw diff info | |
161 final int lastLine= end(visibleModelLines); | |
162 final int width= getWidth(); | |
163 final Color deletionColor= getDeletionColor(); | |
164 for (int line= visibleModelLines.getStartLine(); line < lastLine; line++) { | |
165 paintLine(line, gc, width, deletionColor); | |
166 } | |
167 } | |
168 | |
169 /** | |
170 * Ensures that the column is fully instantiated, i.e. has a control, and that the viewer is | |
171 * visible. | |
172 */ | |
173 private void connectIfNeeded() { | |
174 if (isConnected() || fParentRuler is null) | |
175 return; | |
176 | |
177 fViewer= fParentRuler.getTextViewer(); | |
178 if (fViewer is null) | |
179 return; | |
180 | |
181 fWidget= fViewer.getTextWidget(); | |
182 if (fWidget is null) | |
183 return; | |
184 | |
185 fControl= fColumn.getControl(); | |
186 if (fControl is null) | |
187 return; | |
188 | |
189 fControl.addDisposeListener(new DisposeListener() { | |
190 /* | |
191 * @see dwt.events.DisposeListener#widgetDisposed(dwt.events.DisposeEvent) | |
192 */ | |
193 public void widgetDisposed(DisposeEvent e) { | |
194 handleDispose(); | |
195 } | |
196 }); | |
197 } | |
198 | |
199 /** | |
200 * Returns <code>true</code> if the column is fully connected. | |
201 * | |
202 * @return <code>true</code> if the column is fully connected, false otherwise | |
203 */ | |
204 private bool isConnected() { | |
205 return fControl !is null; | |
206 } | |
207 | |
208 /** | |
209 * Disposes of this painter and releases any resources. | |
210 */ | |
211 private void handleDispose() { | |
212 if (fLineDiffer !is null) { | |
134 | 213 (cast(IAnnotationModel) fLineDiffer).removeAnnotationModelListener(fAnnotationListener); |
129 | 214 fLineDiffer= null; |
215 } | |
216 } | |
217 | |
218 /** | |
219 * Paints a single model line onto <code>gc</code>. | |
220 * | |
221 * @param line the model line to paint | |
222 * @param gc the {@link GC} to paint onto | |
223 * @param width the width of the column | |
224 * @param deletionColor the background color used to indicate deletions | |
225 */ | |
226 private void paintLine(int line, GC gc, int width, Color deletionColor) { | |
227 int widgetLine= JFaceTextUtil.modelLineToWidgetLine(fViewer, line); | |
228 if (widgetLine is -1) | |
229 return; | |
230 | |
231 ILineDiffInfo info= getDiffInfo(line); | |
232 | |
233 if (info !is null) { | |
234 int y= fWidget.getLinePixel(widgetLine); | |
235 int lineHeight= fWidget.getLineHeight(fWidget.getOffsetAtLine(widgetLine)); | |
236 | |
237 // draw background color if special | |
238 if (hasSpecialColor(info)) { | |
239 gc.setBackground(getColor(info)); | |
240 gc.fillRectangle(0, y, width, lineHeight); | |
241 } | |
242 | |
243 /* Deletion Indicator: Simply a horizontal line */ | |
244 int delBefore= info.getRemovedLinesAbove(); | |
245 int delBelow= info.getRemovedLinesBelow(); | |
246 if (delBefore > 0 || delBelow > 0) { | |
247 gc.setForeground(deletionColor); | |
248 if (delBefore > 0) | |
249 gc.drawLine(0, y, width, y); | |
250 if (delBelow > 0) | |
251 gc.drawLine(0, y + lineHeight - 1, width, y + lineHeight - 1); | |
252 } | |
253 } | |
254 } | |
255 | |
256 /** | |
257 * Returns whether the line background differs from the default. | |
258 * | |
259 * @param info the info being queried | |
260 * @return <code>true</code> if <code>info</code> describes either a changed or an added | |
261 * line. | |
262 */ | |
263 private bool hasSpecialColor(ILineDiffInfo info) { | |
264 return info.getChangeType() is ILineDiffInfo.ADDED || info.getChangeType() is ILineDiffInfo.CHANGED; | |
265 } | |
266 | |
267 /** | |
268 * Retrieves the <code>ILineDiffInfo</code> for <code>line</code> from the model. There are | |
269 * optimizations for direct access and sequential access patterns. | |
270 * | |
271 * @param line the line we want the info for. | |
272 * @return the <code>ILineDiffInfo</code> for <code>line</code>, or <code>null</code>. | |
273 */ | |
274 private ILineDiffInfo getDiffInfo(int line) { | |
275 if (fLineDiffer !is null) | |
276 return fLineDiffer.getLineInfo(line); | |
277 | |
278 return null; | |
279 } | |
280 | |
281 /** | |
282 * Returns the color for deleted lines. | |
283 * | |
284 * @return the color to be used for the deletion indicator | |
285 */ | |
286 private Color getDeletionColor() { | |
287 return fDeletedColor is null ? getBackground() : fDeletedColor; | |
288 } | |
289 | |
290 /** | |
291 * Returns the color for the given line diff info. | |
292 * | |
293 * @param info the <code>ILineDiffInfo</code> being queried | |
294 * @return the correct background color for the line type being described by <code>info</code> | |
295 */ | |
296 private Color getColor(ILineDiffInfo info) { | |
297 Assert.isTrue(info !is null && info.getChangeType() !is ILineDiffInfo.UNCHANGED); | |
298 Color ret= null; | |
299 switch (info.getChangeType()) { | |
300 case ILineDiffInfo.CHANGED: | |
301 ret= getShadedColor(fChangedColor); | |
302 break; | |
303 case ILineDiffInfo.ADDED: | |
304 ret= getShadedColor(fAddedColor); | |
305 break; | |
306 } | |
307 return ret is null ? getBackground() : ret; | |
308 } | |
309 | |
310 /** | |
311 * Sets the background color for changed lines. | |
312 * | |
313 * @param color the new color to be used for the changed lines background | |
314 * @return the shaded color | |
315 */ | |
316 private Color getShadedColor(Color color) { | |
317 if (color is null) | |
318 return null; | |
319 | |
320 if (fSharedColors is null) | |
321 return color; | |
322 | |
323 RGB baseRGB= color.getRGB(); | |
324 RGB background= getBackground().getRGB(); | |
325 | |
326 bool darkBase= isDark(baseRGB); | |
327 bool darkBackground= isDark(background); | |
328 if (darkBase && darkBackground) | |
329 background= new RGB(255, 255, 255); | |
330 else if (!darkBase && !darkBackground) | |
331 background= new RGB(0, 0, 0); | |
332 | |
333 return fSharedColors.getColor(interpolate(baseRGB, background, 0.6)); | |
334 } | |
335 | |
336 /** | |
337 * Sets the annotation model. | |
338 * | |
339 * @param model the annotation model, possibly <code>null</code> | |
340 * @see IVerticalRulerColumn#setModel(IAnnotationModel) | |
341 */ | |
342 public void setModel(IAnnotationModel model) { | |
343 IAnnotationModel newModel; | |
344 if (model instanceof IAnnotationModelExtension) | |
134 | 345 newModel= (cast(IAnnotationModelExtension) model).getAnnotationModel(IChangeRulerColumn.QUICK_DIFF_MODEL_ID); |
129 | 346 else |
347 newModel= model; | |
348 | |
349 setDiffer(newModel); | |
350 } | |
351 | |
352 /** | |
353 * Sets the line differ. | |
354 * | |
355 * @param differ the line differ | |
356 */ | |
357 private void setDiffer(IAnnotationModel differ) { | |
358 if (differ instanceof ILineDiffer) { | |
359 if (fLineDiffer !is differ) { | |
360 if (fLineDiffer !is null) | |
134 | 361 (cast(IAnnotationModel) fLineDiffer).removeAnnotationModelListener(fAnnotationListener); |
362 fLineDiffer= cast(ILineDiffer) differ; | |
129 | 363 if (fLineDiffer !is null) |
134 | 364 (cast(IAnnotationModel) fLineDiffer).addAnnotationModelListener(fAnnotationListener); |
129 | 365 } |
366 } | |
367 } | |
368 | |
369 /** | |
370 * Triggers a redraw in the display thread. | |
371 */ | |
372 private final void postRedraw() { | |
373 if (isConnected() && !fControl.isDisposed()) { | |
374 Display d= fControl.getDisplay(); | |
375 if (d !is null) { | |
376 d.asyncExec(new Runnable() { | |
377 public void run() { | |
378 redraw(); | |
379 } | |
380 }); | |
381 } | |
382 } | |
383 } | |
384 | |
385 /** | |
386 * Triggers redrawing of the column. | |
387 */ | |
388 private void redraw() { | |
389 fColumn.redraw(); | |
390 } | |
391 | |
392 /** | |
393 * Returns the width of the column. | |
394 * | |
395 * @return the width of the column | |
396 */ | |
397 private int getWidth() { | |
398 return fColumn.getWidth(); | |
399 } | |
400 | |
401 /** | |
402 * Computes the end index of a line range. | |
403 * | |
404 * @param range a line range | |
405 * @return the last line (exclusive) of <code>range</code> | |
406 */ | |
407 private static int end(ILineRange range) { | |
408 return range.getStartLine() + range.getNumberOfLines(); | |
409 } | |
410 | |
411 /** | |
412 * Returns the System background color for list widgets or the set background. | |
413 * | |
414 * @return the System background color for list widgets | |
415 */ | |
416 private Color getBackground() { | |
417 if (fBackground is null) | |
418 return fWidget.getDisplay().getSystemColor(DWT.COLOR_LIST_BACKGROUND); | |
419 return fBackground; | |
420 } | |
421 | |
422 /** | |
423 * Sets the color for added lines. | |
424 * | |
425 * @param addedColor the color for added lines | |
426 * @see dwtx.jface.text.source.IChangeRulerColumn#setAddedColor(dwt.graphics.Color) | |
427 */ | |
428 public void setAddedColor(Color addedColor) { | |
429 fAddedColor= addedColor; | |
430 } | |
431 | |
432 /** | |
433 * Sets the color for changed lines. | |
434 * | |
435 * @param changedColor the color for changed lines | |
436 * @see dwtx.jface.text.source.IChangeRulerColumn#setChangedColor(dwt.graphics.Color) | |
437 */ | |
438 public void setChangedColor(Color changedColor) { | |
439 fChangedColor= changedColor; | |
440 } | |
441 | |
442 /** | |
443 * Sets the color for deleted lines. | |
444 * | |
445 * @param deletedColor the color for deleted lines | |
446 * @see dwtx.jface.text.source.IChangeRulerColumn#setDeletedColor(dwt.graphics.Color) | |
447 */ | |
448 public void setDeletedColor(Color deletedColor) { | |
449 fDeletedColor= deletedColor; | |
450 } | |
451 | |
452 /** | |
453 * Returns <code>true</code> if the receiver can provide a hover for a certain document line. | |
454 * | |
455 * @param activeLine the document line of interest | |
456 * @return <code>true</code> if the receiver can provide a hover | |
457 */ | |
458 public bool hasHover(int activeLine) { | |
459 return true; | |
460 } | |
461 | |
462 /** | |
463 * Returns the display character for the accessibility mode for a certain model line. | |
464 * | |
465 * @param line the document line of interest | |
466 * @return the display character for <code>line</code> | |
467 */ | |
468 public String getDisplayCharacter(int line) { | |
469 return getDisplayCharacter(getDiffInfo(line)); | |
470 } | |
471 | |
472 /** | |
473 * Returns the character to display in character display mode for the given | |
474 * <code>ILineDiffInfo</code> | |
475 * | |
476 * @param info the <code>ILineDiffInfo</code> being queried | |
477 * @return the character indication for <code>info</code> | |
478 */ | |
479 private String getDisplayCharacter(ILineDiffInfo info) { | |
480 if (info is null) | |
481 return " "; //$NON-NLS-1$ | |
482 switch (info.getChangeType()) { | |
483 case ILineDiffInfo.CHANGED: | |
484 return "~"; //$NON-NLS-1$ | |
485 case ILineDiffInfo.ADDED: | |
486 return "+"; //$NON-NLS-1$ | |
487 } | |
488 return " "; //$NON-NLS-1$ | |
489 } | |
490 | |
491 /** | |
492 * Returns a specification of a color that lies between the given foreground and background | |
493 * color using the given scale factor. | |
494 * | |
495 * @param fg the foreground color | |
496 * @param bg the background color | |
497 * @param scale the scale factor | |
498 * @return the interpolated color | |
499 */ | |
500 private static RGB interpolate(RGB fg, RGB bg, double scale) { | |
134 | 501 return new RGB(cast(int) ((1.0 - scale) * fg.red + scale * bg.red), cast(int) ((1.0 - scale) * fg.green + scale * bg.green), cast(int) ((1.0 - scale) * fg.blue + scale * bg.blue)); |
129 | 502 } |
503 | |
504 /** | |
505 * Returns the grey value in which the given color would be drawn in grey-scale. | |
506 * | |
507 * @param rgb the color | |
508 * @return the grey-scale value | |
509 */ | |
510 private static double greyLevel(RGB rgb) { | |
511 if (rgb.red is rgb.green && rgb.green is rgb.blue) | |
512 return rgb.red; | |
513 return (0.299 * rgb.red + 0.587 * rgb.green + 0.114 * rgb.blue + 0.5); | |
514 } | |
515 | |
516 /** | |
517 * Returns whether the given color is dark or light depending on the colors grey-scale level. | |
518 * | |
519 * @param rgb the color | |
520 * @return <code>true</code> if the color is dark, <code>false</code> if it is light | |
521 */ | |
522 private static bool isDark(RGB rgb) { | |
523 return greyLevel(rgb) > 128; | |
524 } | |
525 | |
526 /** | |
527 * Returns <code>true</code> if diff information is being displayed, <code>false</code> otherwise. | |
528 * | |
529 * @return <code>true</code> if diff information is being displayed, <code>false</code> otherwise | |
530 * @since 3.3 | |
531 */ | |
532 public bool hasInformation() { | |
533 if (fLineDiffer instanceof ILineDifferExtension2) | |
134 | 534 return !(cast(ILineDifferExtension2) fLineDiffer).isSuspended(); |
129 | 535 return fLineDiffer !is null; |
536 } | |
537 | |
538 } |