KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > internal > text > source > DiffPainter


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  *******************************************************************************/

11 package org.eclipse.jface.internal.text.source;
12
13 import org.eclipse.swt.SWT;
14 import org.eclipse.swt.custom.StyledText;
15 import org.eclipse.swt.events.DisposeEvent;
16 import org.eclipse.swt.events.DisposeListener;
17 import org.eclipse.swt.graphics.Color;
18 import org.eclipse.swt.graphics.GC;
19 import org.eclipse.swt.graphics.RGB;
20 import org.eclipse.swt.widgets.Canvas;
21 import org.eclipse.swt.widgets.Control;
22 import org.eclipse.swt.widgets.Display;
23
24 import org.eclipse.core.runtime.Assert;
25
26 import org.eclipse.jface.text.ITextViewer;
27 import org.eclipse.jface.text.JFaceTextUtil;
28 import org.eclipse.jface.text.source.CompositeRuler;
29 import org.eclipse.jface.text.source.IAnnotationHover;
30 import org.eclipse.jface.text.source.IAnnotationModel;
31 import org.eclipse.jface.text.source.IAnnotationModelExtension;
32 import org.eclipse.jface.text.source.IAnnotationModelListener;
33 import org.eclipse.jface.text.source.IChangeRulerColumn;
34 import org.eclipse.jface.text.source.ILineDiffInfo;
35 import org.eclipse.jface.text.source.ILineDiffer;
36 import org.eclipse.jface.text.source.ILineDifferExtension2;
37 import org.eclipse.jface.text.source.ILineRange;
38 import org.eclipse.jface.text.source.ISharedTextColors;
39 import org.eclipse.jface.text.source.IVerticalRulerColumn;
40
41
42 /**
43  * A strategy for painting the quick diff colors onto the vertical ruler column. It also manages the
44  * quick diff hover.
45  *
46  * @since 3.2
47  */

48 public final class DiffPainter {
49     /**
50      * Internal listener class that will update the ruler when the underlying model changes.
51      */

52     private class AnnotationListener implements IAnnotationModelListener {
53         /*
54          * @see org.eclipse.jface.text.source.IAnnotationModelListener#modelChanged(org.eclipse.jface.text.source.IAnnotationModel)
55          */

56         public void modelChanged(IAnnotationModel model) {
57             postRedraw();
58         }
59     }
60
61     /** The vertical ruler column that delegates painting to this painter. */
62     private final IVerticalRulerColumn fColumn;
63     /** The parent ruler. */
64     private CompositeRuler fParentRuler;
65     /** The column's control, typically a {@link Canvas}, possibly <code>null</code>. */
66     private Control fControl;
67     /** The text viewer that the column is attached to. */
68     private ITextViewer fViewer;
69     /** The viewer's text widget. */
70     private StyledText fWidget;
71     /** The line differ extracted from the annotation model. */
72     private ILineDiffer fLineDiffer= null;
73     /** Color for changed lines */
74     private Color fAddedColor;
75     /** Color for added lines */
76     private Color fChangedColor;
77     /** Color for the deleted line indicator */
78     private Color fDeletedColor;
79     /** The background color. */
80     private Color fBackground;
81     /** The ruler's hover */
82     private IAnnotationHover fHover;
83     /** The internal listener */
84     private final AnnotationListener fAnnotationListener= new AnnotationListener();
85     /** The shared color provider, possibly <code>null</code>. */
86     private final ISharedTextColors fSharedColors;
87
88     /**
89      * Creates a new diff painter for a vertical ruler column.
90      *
91      * @param column the column that will delegate{@link #paint(GC, ILineRange) painting} to the
92      * newly created painter.
93      * @param sharedColors a shared colors object to store shaded colors in, may be
94      * <code>null</code>
95      */

96     public DiffPainter(IVerticalRulerColumn column, ISharedTextColors sharedColors) {
97         Assert.isLegal(column != null);
98         fColumn= column;
99         fSharedColors= sharedColors;
100     }
101
102     /**
103      * Sets the parent ruler - the delegating column must call this method as soon as it creates its
104      * control.
105      *
106      * @param parentRuler the parent ruler
107      */

108     public void setParentRuler(CompositeRuler parentRuler) {
109         fParentRuler= parentRuler;
110     }
111
112     /**
113      * Sets the quick diff hover later returned by {@link #getHover()}.
114      *
115      * @param hover the hover
116      */

117     public void setHover(IAnnotationHover hover) {
118         fHover= hover;
119     }
120
121     /**
122      * Returns the quick diff hover set by {@link #setHover(IAnnotationHover)}.
123      *
124      * @return the quick diff hover set by {@link #setHover(IAnnotationHover)}
125      */

126     public IAnnotationHover getHover() {
127         return fHover;
128     }
129
130     /**
131      * Sets the background color.
132      *
133      * @param background the background color, <code>null</code> to use the platform's list background
134      */

135     public void setBackground(Color background) {
136         fBackground= background;
137     }
138
139     /**
140      * Delegates the painting of the quick diff colors to this painter. The painter will draw the
141      * color boxes onto the passed {@link GC} for all model (document) lines in
142      * <code>visibleModelLines</code>.
143      *
144      * @param gc the {@link GC} to draw onto
145      * @param visibleModelLines the lines (in document offsets) that are currently (perhaps only
146      * partially) visible
147      */

148     public void paint(GC gc, ILineRange visibleModelLines) {
149         connectIfNeeded();
150         if (!isConnected())
151             return;
152
153         // draw diff info
154
final int lastLine= end(visibleModelLines);
155         final int width= getWidth();
156         final Color deletionColor= getDeletionColor();
157         for (int line= visibleModelLines.getStartLine(); line < lastLine; line++) {
158             paintLine(line, gc, width, deletionColor);
159         }
160     }
161
162     /**
163      * Ensures that the column is fully instantiated, i.e. has a control, and that the viewer is
164      * visible.
165      */

166     private void connectIfNeeded() {
167         if (isConnected() || fParentRuler == null)
168             return;
169
170         fViewer= fParentRuler.getTextViewer();
171         if (fViewer == null)
172             return;
173
174         fWidget= fViewer.getTextWidget();
175         if (fWidget == null)
176             return;
177
178         fControl= fColumn.getControl();
179         if (fControl == null)
180             return;
181
182         fControl.addDisposeListener(new DisposeListener() {
183             /*
184              * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
185              */

186             public void widgetDisposed(DisposeEvent e) {
187                 handleDispose();
188             }
189         });
190     }
191
192     /**
193      * Returns <code>true</code> if the column is fully connected.
194      *
195      * @return <code>true</code> if the column is fully connected, false otherwise
196      */

197     private boolean isConnected() {
198         return fControl != null;
199     }
200
201     /**
202      * Disposes of this painter and releases any resources.
203      */

204     private void handleDispose() {
205         if (fLineDiffer != null) {
206             ((IAnnotationModel) fLineDiffer).removeAnnotationModelListener(fAnnotationListener);
207             fLineDiffer= null;
208         }
209     }
210
211     /**
212      * Paints a single model line onto <code>gc</code>.
213      *
214      * @param line the model line to paint
215      * @param gc the {@link GC} to paint onto
216      * @param width the width of the column
217      * @param deletionColor the background color used to indicate deletions
218      */

219     private void paintLine(int line, GC gc, int width, Color deletionColor) {
220         int widgetLine= JFaceTextUtil.modelLineToWidgetLine(fViewer, line);
221         if (widgetLine == -1)
222             return;
223
224         ILineDiffInfo info= getDiffInfo(line);
225
226         if (info != null) {
227             int y= fWidget.getLinePixel(widgetLine);
228             int lineHeight= fWidget.getLineHeight(fWidget.getOffsetAtLine(widgetLine));
229
230             // draw background color if special
231
if (hasSpecialColor(info)) {
232                 gc.setBackground(getColor(info));
233                 gc.fillRectangle(0, y, width, lineHeight);
234             }
235
236             /* Deletion Indicator: Simply a horizontal line */
237             int delBefore= info.getRemovedLinesAbove();
238             int delBelow= info.getRemovedLinesBelow();
239             if (delBefore > 0 || delBelow > 0) {
240                 gc.setForeground(deletionColor);
241                 if (delBefore > 0)
242                     gc.drawLine(0, y, width, y);
243                 if (delBelow > 0)
244                     gc.drawLine(0, y + lineHeight - 1, width, y + lineHeight - 1);
245             }
246         }
247     }
248
249     /**
250      * Returns whether the line background differs from the default.
251      *
252      * @param info the info being queried
253      * @return <code>true</code> if <code>info</code> describes either a changed or an added
254      * line.
255      */

256     private boolean hasSpecialColor(ILineDiffInfo info) {
257         return info.getChangeType() == ILineDiffInfo.ADDED || info.getChangeType() == ILineDiffInfo.CHANGED;
258     }
259
260     /**
261      * Retrieves the <code>ILineDiffInfo</code> for <code>line</code> from the model. There are
262      * optimizations for direct access and sequential access patterns.
263      *
264      * @param line the line we want the info for.
265      * @return the <code>ILineDiffInfo</code> for <code>line</code>, or <code>null</code>.
266      */

267     private ILineDiffInfo getDiffInfo(int line) {
268         if (fLineDiffer != null)
269             return fLineDiffer.getLineInfo(line);
270
271         return null;
272     }
273
274     /**
275      * Returns the color for deleted lines.
276      *
277      * @return the color to be used for the deletion indicator
278      */

279     private Color getDeletionColor() {
280         return fDeletedColor == null ? getBackground() : fDeletedColor;
281     }
282
283     /**
284      * Returns the color for the given line diff info.
285      *
286      * @param info the <code>ILineDiffInfo</code> being queried
287      * @return the correct background color for the line type being described by <code>info</code>
288      */

289     private Color getColor(ILineDiffInfo info) {
290         Assert.isTrue(info != null && info.getChangeType() != ILineDiffInfo.UNCHANGED);
291         Color ret= null;
292         switch (info.getChangeType()) {
293             case ILineDiffInfo.CHANGED:
294                 ret= getShadedColor(fChangedColor);
295                 break;
296             case ILineDiffInfo.ADDED:
297                 ret= getShadedColor(fAddedColor);
298                 break;
299         }
300         return ret == null ? getBackground() : ret;
301     }
302
303     /**
304      * Sets the background color for changed lines.
305      *
306      * @param color the new color to be used for the changed lines background
307      * @return the shaded color
308      */

309     private Color getShadedColor(Color color) {
310         if (color == null)
311             return null;
312
313         if (fSharedColors == null)
314             return color;
315
316         RGB baseRGB= color.getRGB();
317         RGB background= getBackground().getRGB();
318
319         boolean darkBase= isDark(baseRGB);
320         boolean darkBackground= isDark(background);
321         if (darkBase && darkBackground)
322             background= new RGB(255, 255, 255);
323         else if (!darkBase && !darkBackground)
324             background= new RGB(0, 0, 0);
325
326         return fSharedColors.getColor(interpolate(baseRGB, background, 0.6));
327     }
328
329     /**
330      * Sets the annotation model.
331      *
332      * @param model the annotation model, possibly <code>null</code>
333      * @see IVerticalRulerColumn#setModel(IAnnotationModel)
334      */

335     public void setModel(IAnnotationModel model) {
336         IAnnotationModel newModel;
337         if (model instanceof IAnnotationModelExtension)
338             newModel= ((IAnnotationModelExtension) model).getAnnotationModel(IChangeRulerColumn.QUICK_DIFF_MODEL_ID);
339         else
340             newModel= model;
341
342         setDiffer(newModel);
343     }
344
345     /**
346      * Sets the line differ.
347      *
348      * @param differ the line differ
349      */

350     private void setDiffer(IAnnotationModel differ) {
351         if (differ instanceof ILineDiffer) {
352             if (fLineDiffer != differ) {
353                 if (fLineDiffer != null)
354                     ((IAnnotationModel) fLineDiffer).removeAnnotationModelListener(fAnnotationListener);
355                 fLineDiffer= (ILineDiffer) differ;
356                 if (fLineDiffer != null)
357                     ((IAnnotationModel) fLineDiffer).addAnnotationModelListener(fAnnotationListener);
358             }
359         }
360     }
361
362     /**
363      * Triggers a redraw in the display thread.
364      */

365     private final void postRedraw() {
366         if (isConnected() && !fControl.isDisposed()) {
367             Display d= fControl.getDisplay();
368             if (d != null) {
369                 d.asyncExec(new Runnable JavaDoc() {
370                     public void run() {
371                         redraw();
372                     }
373                 });
374             }
375         }
376     }
377
378     /**
379      * Triggers redrawing of the column.
380      */

381     private void redraw() {
382         fColumn.redraw();
383     }
384
385     /**
386      * Returns the width of the column.
387      *
388      * @return the width of the column
389      */

390     private int getWidth() {
391         return fColumn.getWidth();
392     }
393
394     /**
395      * Computes the end index of a line range.
396      *
397      * @param range a line range
398      * @return the last line (exclusive) of <code>range</code>
399      */

400     private static int end(ILineRange range) {
401         return range.getStartLine() + range.getNumberOfLines();
402     }
403
404     /**
405      * Returns the System background color for list widgets or the set background.
406      *
407      * @return the System background color for list widgets
408      */

409     private Color getBackground() {
410         if (fBackground == null)
411             return fWidget.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND);
412         return fBackground;
413     }
414
415     /**
416      * Sets the color for added lines.
417      *
418      * @param addedColor the color for added lines
419      * @see org.eclipse.jface.text.source.IChangeRulerColumn#setAddedColor(org.eclipse.swt.graphics.Color)
420      */

421     public void setAddedColor(Color addedColor) {
422         fAddedColor= addedColor;
423     }
424
425     /**
426      * Sets the color for changed lines.
427      *
428      * @param changedColor the color for changed lines
429      * @see org.eclipse.jface.text.source.IChangeRulerColumn#setChangedColor(org.eclipse.swt.graphics.Color)
430      */

431     public void setChangedColor(Color changedColor) {
432         fChangedColor= changedColor;
433     }
434
435     /**
436      * Sets the color for deleted lines.
437      *
438      * @param deletedColor the color for deleted lines
439      * @see org.eclipse.jface.text.source.IChangeRulerColumn#setDeletedColor(org.eclipse.swt.graphics.Color)
440      */

441     public void setDeletedColor(Color deletedColor) {
442         fDeletedColor= deletedColor;
443     }
444
445     /**
446      * Returns <code>true</code> if the receiver can provide a hover for a certain document line.
447      *
448      * @param activeLine the document line of interest
449      * @return <code>true</code> if the receiver can provide a hover
450      */

451     public boolean hasHover(int activeLine) {
452         return true;
453     }
454
455     /**
456      * Returns the display character for the accessibility mode for a certain model line.
457      *
458      * @param line the document line of interest
459      * @return the display character for <code>line</code>
460      */

461     public String JavaDoc getDisplayCharacter(int line) {
462         return getDisplayCharacter(getDiffInfo(line));
463     }
464
465     /**
466      * Returns the character to display in character display mode for the given
467      * <code>ILineDiffInfo</code>
468      *
469      * @param info the <code>ILineDiffInfo</code> being queried
470      * @return the character indication for <code>info</code>
471      */

472     private String JavaDoc getDisplayCharacter(ILineDiffInfo info) {
473         if (info == null)
474             return " "; //$NON-NLS-1$
475
switch (info.getChangeType()) {
476             case ILineDiffInfo.CHANGED:
477                 return "~"; //$NON-NLS-1$
478
case ILineDiffInfo.ADDED:
479                 return "+"; //$NON-NLS-1$
480
}
481         return " "; //$NON-NLS-1$
482
}
483
484     /**
485      * Returns a specification of a color that lies between the given foreground and background
486      * color using the given scale factor.
487      *
488      * @param fg the foreground color
489      * @param bg the background color
490      * @param scale the scale factor
491      * @return the interpolated color
492      */

493     private static RGB interpolate(RGB fg, RGB bg, double scale) {
494         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));
495     }
496
497     /**
498      * Returns the grey value in which the given color would be drawn in grey-scale.
499      *
500      * @param rgb the color
501      * @return the grey-scale value
502      */

503     private static double greyLevel(RGB rgb) {
504         if (rgb.red == rgb.green && rgb.green == rgb.blue)
505             return rgb.red;
506         return (0.299 * rgb.red + 0.587 * rgb.green + 0.114 * rgb.blue + 0.5);
507     }
508
509     /**
510      * Returns whether the given color is dark or light depending on the colors grey-scale level.
511      *
512      * @param rgb the color
513      * @return <code>true</code> if the color is dark, <code>false</code> if it is light
514      */

515     private static boolean isDark(RGB rgb) {
516         return greyLevel(rgb) > 128;
517     }
518
519     /**
520      * Returns <code>true</code> if diff information is being displayed, <code>false</code> otherwise.
521      *
522      * @return <code>true</code> if diff information is being displayed, <code>false</code> otherwise
523      * @since 3.3
524      */

525     public boolean hasInformation() {
526         if (fLineDiffer instanceof ILineDifferExtension2)
527             return !((ILineDifferExtension2) fLineDiffer).isSuspended();
528         return fLineDiffer != null;
529     }
530
531 }
532
Popular Tags