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