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