Mercurial > projects > dwt-addons
view dwtx/jface/internal/text/source/DiffPainter.d @ 159:7926b636c282
...
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Wed, 27 Aug 2008 01:57:58 +0200 |
parents | 75302ef3f92f |
children | 1a5b8f8129df |
line wrap: on
line source
/******************************************************************************* * Copyright (c) 2006 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Port to the D programming language: * Frank Benoit <benoit@tionex.de> *******************************************************************************/ module dwtx.jface.internal.text.source.DiffPainter; import dwt.dwthelper.utils; import dwt.DWT; import dwt.custom.StyledText; import dwt.events.DisposeEvent; import dwt.events.DisposeListener; import dwt.graphics.Color; import dwt.graphics.GC; import dwt.graphics.RGB; import dwt.widgets.Canvas; import dwt.widgets.Control; import dwt.widgets.Display; import dwtx.core.runtime.Assert; import dwtx.jface.text.ITextViewer; import dwtx.jface.text.JFaceTextUtil; import dwtx.jface.text.source.CompositeRuler; import dwtx.jface.text.source.IAnnotationHover; import dwtx.jface.text.source.IAnnotationModel; import dwtx.jface.text.source.IAnnotationModelExtension; import dwtx.jface.text.source.IAnnotationModelListener; import dwtx.jface.text.source.IChangeRulerColumn; import dwtx.jface.text.source.ILineDiffInfo; import dwtx.jface.text.source.ILineDiffer; import dwtx.jface.text.source.ILineDifferExtension2; import dwtx.jface.text.source.ILineRange; import dwtx.jface.text.source.ISharedTextColors; import dwtx.jface.text.source.IVerticalRulerColumn; /** * A strategy for painting the quick diff colors onto the vertical ruler column. It also manages the * quick diff hover. * * @since 3.2 */ public final class DiffPainter { /** * Internal listener class that will update the ruler when the underlying model changes. */ private class AnnotationListener : IAnnotationModelListener { /* * @see dwtx.jface.text.source.IAnnotationModelListener#modelChanged(dwtx.jface.text.source.IAnnotationModel) */ public void modelChanged(IAnnotationModel model) { postRedraw(); } } /** The vertical ruler column that delegates painting to this painter. */ private const IVerticalRulerColumn fColumn; /** The parent ruler. */ private CompositeRuler fParentRuler; /** The column's control, typically a {@link Canvas}, possibly <code>null</code>. */ private Control fControl; /** The text viewer that the column is attached to. */ private ITextViewer fViewer; /** The viewer's text widget. */ private StyledText fWidget; /** The line differ extracted from the annotation model. */ private ILineDiffer fLineDiffer= null; /** Color for changed lines */ private Color fAddedColor; /** Color for added lines */ private Color fChangedColor; /** Color for the deleted line indicator */ private Color fDeletedColor; /** The background color. */ private Color fBackground; /** The ruler's hover */ private IAnnotationHover fHover; /** The internal listener */ private const AnnotationListener fAnnotationListener; /** The shared color provider, possibly <code>null</code>. */ private const ISharedTextColors fSharedColors; /** * Creates a new diff painter for a vertical ruler column. * * @param column the column that will delegate{@link #paint(GC, ILineRange) painting} to the * newly created painter. * @param sharedColors a shared colors object to store shaded colors in, may be * <code>null</code> */ public this(IVerticalRulerColumn column, ISharedTextColors sharedColors) { fAnnotationListener= new AnnotationListener(); Assert.isLegal(column !is null); fColumn= column; fSharedColors= sharedColors; } /** * Sets the parent ruler - the delegating column must call this method as soon as it creates its * control. * * @param parentRuler the parent ruler */ public void setParentRuler(CompositeRuler parentRuler) { fParentRuler= parentRuler; } /** * Sets the quick diff hover later returned by {@link #getHover()}. * * @param hover the hover */ public void setHover(IAnnotationHover hover) { fHover= hover; } /** * Returns the quick diff hover set by {@link #setHover(IAnnotationHover)}. * * @return the quick diff hover set by {@link #setHover(IAnnotationHover)} */ public IAnnotationHover getHover() { return fHover; } /** * Sets the background color. * * @param background the background color, <code>null</code> to use the platform's list background */ public void setBackground(Color background) { fBackground= background; } /** * Delegates the painting of the quick diff colors to this painter. The painter will draw the * color boxes onto the passed {@link GC} for all model (document) lines in * <code>visibleModelLines</code>. * * @param gc the {@link GC} to draw onto * @param visibleModelLines the lines (in document offsets) that are currently (perhaps only * partially) visible */ public void paint(GC gc, ILineRange visibleModelLines) { connectIfNeeded(); if (!isConnected()) return; // draw diff info final int lastLine= end(visibleModelLines); final int width= getWidth(); final Color deletionColor= getDeletionColor(); for (int line= visibleModelLines.getStartLine(); line < lastLine; line++) { paintLine(line, gc, width, deletionColor); } } /** * Ensures that the column is fully instantiated, i.e. has a control, and that the viewer is * visible. */ private void connectIfNeeded() { if (isConnected() || fParentRuler is null) return; fViewer= fParentRuler.getTextViewer(); if (fViewer is null) return; fWidget= fViewer.getTextWidget(); if (fWidget is null) return; fControl= fColumn.getControl(); if (fControl is null) return; fControl.addDisposeListener(new class() DisposeListener { /* * @see dwt.events.DisposeListener#widgetDisposed(dwt.events.DisposeEvent) */ public void widgetDisposed(DisposeEvent e) { handleDispose(); } }); } /** * Returns <code>true</code> if the column is fully connected. * * @return <code>true</code> if the column is fully connected, false otherwise */ private bool isConnected() { return fControl !is null; } /** * Disposes of this painter and releases any resources. */ private void handleDispose() { if (fLineDiffer !is null) { (cast(IAnnotationModel) fLineDiffer).removeAnnotationModelListener(fAnnotationListener); fLineDiffer= null; } } /** * Paints a single model line onto <code>gc</code>. * * @param line the model line to paint * @param gc the {@link GC} to paint onto * @param width the width of the column * @param deletionColor the background color used to indicate deletions */ private void paintLine(int line, GC gc, int width, Color deletionColor) { int widgetLine= JFaceTextUtil.modelLineToWidgetLine(fViewer, line); if (widgetLine is -1) return; ILineDiffInfo info= getDiffInfo(line); if (info !is null) { int y= fWidget.getLinePixel(widgetLine); int lineHeight= fWidget.getLineHeight(fWidget.getOffsetAtLine(widgetLine)); // draw background color if special if (hasSpecialColor(info)) { gc.setBackground(getColor(info)); gc.fillRectangle(0, y, width, lineHeight); } /* Deletion Indicator: Simply a horizontal line */ int delBefore= info.getRemovedLinesAbove(); int delBelow= info.getRemovedLinesBelow(); if (delBefore > 0 || delBelow > 0) { gc.setForeground(deletionColor); if (delBefore > 0) gc.drawLine(0, y, width, y); if (delBelow > 0) gc.drawLine(0, y + lineHeight - 1, width, y + lineHeight - 1); } } } /** * Returns whether the line background differs from the default. * * @param info the info being queried * @return <code>true</code> if <code>info</code> describes either a changed or an added * line. */ private bool hasSpecialColor(ILineDiffInfo info) { return info.getChangeType() is ILineDiffInfo.ADDED || info.getChangeType() is ILineDiffInfo.CHANGED; } /** * Retrieves the <code>ILineDiffInfo</code> for <code>line</code> from the model. There are * optimizations for direct access and sequential access patterns. * * @param line the line we want the info for. * @return the <code>ILineDiffInfo</code> for <code>line</code>, or <code>null</code>. */ private ILineDiffInfo getDiffInfo(int line) { if (fLineDiffer !is null) return fLineDiffer.getLineInfo(line); return null; } /** * Returns the color for deleted lines. * * @return the color to be used for the deletion indicator */ private Color getDeletionColor() { return fDeletedColor is null ? getBackground() : fDeletedColor; } /** * Returns the color for the given line diff info. * * @param info the <code>ILineDiffInfo</code> being queried * @return the correct background color for the line type being described by <code>info</code> */ private Color getColor(ILineDiffInfo info) { Assert.isTrue(info !is null && info.getChangeType() !is ILineDiffInfo.UNCHANGED); Color ret= null; switch (info.getChangeType()) { case ILineDiffInfo.CHANGED: ret= getShadedColor(fChangedColor); break; case ILineDiffInfo.ADDED: ret= getShadedColor(fAddedColor); break; } return ret is null ? getBackground() : ret; } /** * Sets the background color for changed lines. * * @param color the new color to be used for the changed lines background * @return the shaded color */ private Color getShadedColor(Color color) { if (color is null) return null; if (fSharedColors is null) return color; RGB baseRGB= color.getRGB(); RGB background= getBackground().getRGB(); bool darkBase= isDark(baseRGB); bool darkBackground= isDark(background); if (darkBase && darkBackground) background= new RGB(255, 255, 255); else if (!darkBase && !darkBackground) background= new RGB(0, 0, 0); return fSharedColors.getColor(interpolate(baseRGB, background, 0.6)); } /** * Sets the annotation model. * * @param model the annotation model, possibly <code>null</code> * @see IVerticalRulerColumn#setModel(IAnnotationModel) */ public void setModel(IAnnotationModel model) { IAnnotationModel newModel; if ( cast(IAnnotationModelExtension)model ) newModel= (cast(IAnnotationModelExtension) model).getAnnotationModel(IChangeRulerColumn.QUICK_DIFF_MODEL_ID); else newModel= model; setDiffer(newModel); } /** * Sets the line differ. * * @param differ the line differ */ private void setDiffer(IAnnotationModel differ) { if ( cast(ILineDiffer)differ ) { if (fLineDiffer !is differ) { if (fLineDiffer !is null) (cast(IAnnotationModel) fLineDiffer).removeAnnotationModelListener(fAnnotationListener); fLineDiffer= cast(ILineDiffer) differ; if (fLineDiffer !is null) (cast(IAnnotationModel) fLineDiffer).addAnnotationModelListener(fAnnotationListener); } } } /** * Triggers a redraw in the display thread. */ private final void postRedraw() { if (isConnected() && !fControl.isDisposed()) { Display d= fControl.getDisplay(); if (d !is null) { d.asyncExec(new class() Runnable { public void run() { redraw(); } }); } } } /** * Triggers redrawing of the column. */ private void redraw() { fColumn.redraw(); } /** * Returns the width of the column. * * @return the width of the column */ private int getWidth() { return fColumn.getWidth(); } /** * Computes the end index of a line range. * * @param range a line range * @return the last line (exclusive) of <code>range</code> */ private static int end(ILineRange range) { return range.getStartLine() + range.getNumberOfLines(); } /** * Returns the System background color for list widgets or the set background. * * @return the System background color for list widgets */ private Color getBackground() { if (fBackground is null) return fWidget.getDisplay().getSystemColor(DWT.COLOR_LIST_BACKGROUND); return fBackground; } /** * Sets the color for added lines. * * @param addedColor the color for added lines * @see dwtx.jface.text.source.IChangeRulerColumn#setAddedColor(dwt.graphics.Color) */ public void setAddedColor(Color addedColor) { fAddedColor= addedColor; } /** * Sets the color for changed lines. * * @param changedColor the color for changed lines * @see dwtx.jface.text.source.IChangeRulerColumn#setChangedColor(dwt.graphics.Color) */ public void setChangedColor(Color changedColor) { fChangedColor= changedColor; } /** * Sets the color for deleted lines. * * @param deletedColor the color for deleted lines * @see dwtx.jface.text.source.IChangeRulerColumn#setDeletedColor(dwt.graphics.Color) */ public void setDeletedColor(Color deletedColor) { fDeletedColor= deletedColor; } /** * Returns <code>true</code> if the receiver can provide a hover for a certain document line. * * @param activeLine the document line of interest * @return <code>true</code> if the receiver can provide a hover */ public bool hasHover(int activeLine) { return true; } /** * Returns the display character for the accessibility mode for a certain model line. * * @param line the document line of interest * @return the display character for <code>line</code> */ public String getDisplayCharacter(int line) { return getDisplayCharacter(getDiffInfo(line)); } /** * Returns the character to display in character display mode for the given * <code>ILineDiffInfo</code> * * @param info the <code>ILineDiffInfo</code> being queried * @return the character indication for <code>info</code> */ private String getDisplayCharacter(ILineDiffInfo info) { if (info is null) return " "; //$NON-NLS-1$ switch (info.getChangeType()) { case ILineDiffInfo.CHANGED: return "~"; //$NON-NLS-1$ case ILineDiffInfo.ADDED: return "+"; //$NON-NLS-1$ } return " "; //$NON-NLS-1$ } /** * Returns a specification of a color that lies between the given foreground and background * color using the given scale factor. * * @param fg the foreground color * @param bg the background color * @param scale the scale factor * @return the interpolated color */ private static RGB interpolate(RGB fg, RGB bg, double scale) { 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)); } /** * Returns the grey value in which the given color would be drawn in grey-scale. * * @param rgb the color * @return the grey-scale value */ private static double greyLevel(RGB rgb) { if (rgb.red is rgb.green && rgb.green is rgb.blue) return rgb.red; return (0.299 * rgb.red + 0.587 * rgb.green + 0.114 * rgb.blue + 0.5); } /** * Returns whether the given color is dark or light depending on the colors grey-scale level. * * @param rgb the color * @return <code>true</code> if the color is dark, <code>false</code> if it is light */ private static bool isDark(RGB rgb) { return greyLevel(rgb) > 128; } /** * Returns <code>true</code> if diff information is being displayed, <code>false</code> otherwise. * * @return <code>true</code> if diff information is being displayed, <code>false</code> otherwise * @since 3.3 */ public bool hasInformation() { if ( cast(ILineDifferExtension2)fLineDiffer ) return !(cast(ILineDifferExtension2) fLineDiffer).isSuspended(); return fLineDiffer !is null; } }