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