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 }