Mercurial > projects > dwt-addons
view dwtx/jface/text/source/AbstractRulerColumn.d @ 159:7926b636c282
...
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Wed, 27 Aug 2008 01:57:58 +0200 |
parents | 000f9136b8f7 |
children |
line wrap: on
line source
/******************************************************************************* * Copyright (c) 2006, 2007 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.text.source.AbstractRulerColumn; import dwtx.jface.text.source.ISharedTextColors; // packageimport import dwtx.jface.text.source.ILineRange; // packageimport import dwtx.jface.text.source.IAnnotationPresentation; // packageimport import dwtx.jface.text.source.IVerticalRulerInfoExtension; // packageimport import dwtx.jface.text.source.ICharacterPairMatcher; // packageimport import dwtx.jface.text.source.TextInvocationContext; // packageimport import dwtx.jface.text.source.LineChangeHover; // packageimport import dwtx.jface.text.source.IChangeRulerColumn; // packageimport import dwtx.jface.text.source.IAnnotationMap; // packageimport import dwtx.jface.text.source.IAnnotationModelListenerExtension; // packageimport import dwtx.jface.text.source.ISourceViewerExtension2; // packageimport import dwtx.jface.text.source.IAnnotationHover; // packageimport import dwtx.jface.text.source.ContentAssistantFacade; // packageimport import dwtx.jface.text.source.IAnnotationAccess; // packageimport import dwtx.jface.text.source.IVerticalRulerExtension; // packageimport import dwtx.jface.text.source.IVerticalRulerColumn; // packageimport import dwtx.jface.text.source.LineNumberRulerColumn; // packageimport import dwtx.jface.text.source.MatchingCharacterPainter; // packageimport import dwtx.jface.text.source.IAnnotationModelExtension; // packageimport import dwtx.jface.text.source.ILineDifferExtension; // packageimport import dwtx.jface.text.source.DefaultCharacterPairMatcher; // packageimport import dwtx.jface.text.source.LineNumberChangeRulerColumn; // packageimport import dwtx.jface.text.source.IAnnotationAccessExtension; // packageimport import dwtx.jface.text.source.ISourceViewer; // packageimport import dwtx.jface.text.source.AnnotationModel; // packageimport import dwtx.jface.text.source.ILineDifferExtension2; // packageimport import dwtx.jface.text.source.IAnnotationModelListener; // packageimport import dwtx.jface.text.source.IVerticalRuler; // packageimport import dwtx.jface.text.source.DefaultAnnotationHover; // packageimport import dwtx.jface.text.source.SourceViewer; // packageimport import dwtx.jface.text.source.SourceViewerConfiguration; // packageimport import dwtx.jface.text.source.AnnotationBarHoverManager; // packageimport import dwtx.jface.text.source.CompositeRuler; // packageimport import dwtx.jface.text.source.ImageUtilities; // packageimport import dwtx.jface.text.source.VisualAnnotationModel; // packageimport import dwtx.jface.text.source.IAnnotationModel; // packageimport import dwtx.jface.text.source.ISourceViewerExtension3; // packageimport import dwtx.jface.text.source.ILineDiffInfo; // packageimport import dwtx.jface.text.source.VerticalRulerEvent; // packageimport import dwtx.jface.text.source.ChangeRulerColumn; // packageimport import dwtx.jface.text.source.ILineDiffer; // packageimport import dwtx.jface.text.source.AnnotationModelEvent; // packageimport import dwtx.jface.text.source.AnnotationColumn; // packageimport import dwtx.jface.text.source.AnnotationRulerColumn; // packageimport import dwtx.jface.text.source.IAnnotationHoverExtension; // packageimport import dwtx.jface.text.source.ISourceViewerExtension; // packageimport import dwtx.jface.text.source.AnnotationMap; // packageimport import dwtx.jface.text.source.IVerticalRulerInfo; // packageimport import dwtx.jface.text.source.IAnnotationModelExtension2; // packageimport import dwtx.jface.text.source.LineRange; // packageimport import dwtx.jface.text.source.IAnnotationAccessExtension2; // packageimport import dwtx.jface.text.source.VerticalRuler; // packageimport import dwtx.jface.text.source.JFaceTextMessages; // packageimport import dwtx.jface.text.source.IOverviewRuler; // packageimport import dwtx.jface.text.source.Annotation; // packageimport import dwtx.jface.text.source.IVerticalRulerListener; // packageimport import dwtx.jface.text.source.ISourceViewerExtension4; // packageimport import dwtx.jface.text.source.AnnotationPainter; // packageimport import dwtx.jface.text.source.IAnnotationHoverExtension2; // packageimport import dwtx.jface.text.source.OverviewRuler; // packageimport import dwtx.jface.text.source.OverviewRulerHoverManager; // packageimport import dwt.dwthelper.utils; import dwt.DWT; import dwt.custom.StyledText; import dwt.events.MouseEvent; import dwt.events.MouseListener; import dwt.events.MouseMoveListener; import dwt.events.PaintEvent; import dwt.events.PaintListener; import dwt.graphics.Color; import dwt.graphics.Font; import dwt.graphics.GC; import dwt.widgets.Canvas; import dwt.widgets.Composite; import dwt.widgets.Control; import dwt.widgets.Display; import dwtx.core.runtime.Assert; import dwtx.jface.resource.JFaceResources; import dwtx.jface.text.ITextListener; import dwtx.jface.text.ITextViewer; import dwtx.jface.text.IViewportListener; import dwtx.jface.text.JFaceTextUtil; import dwtx.jface.text.TextEvent; /** * Abstract implementation of a {@link IVerticalRulerColumn} that * uses a {@link Canvas} to draw the ruler contents and which * handles scrolling and mouse selection. * * <h3>Painting</h3> * Subclasses can hook into the paint loop at three levels: * <ul> * <li>Override <strong>{@link #paint(GC, ILineRange)}</strong> to control the entire painting of * the ruler.</li> * <li>Override <strong>{@link #paintLine(GC, int, int, int, int)}</strong> to control the * painting of a line.</li> * <li>Leave the painting to the default implementation, but override <strong>{@link #computeBackground(int)}</strong>, * <strong>{@link #computeForeground(int)}</strong> and <strong>{@link #computeText(int)}</strong> * to specify the ruler appearance for a line.</li> * </ul> * * <h3>Invalidation</h3> * Subclasses may call {@link #redraw()} to mark the entire ruler as needing to be redrawn. * Alternatively, use {@link #redraw(ILineRange)} to only invalidate a certain line range, for * example due to changes to the display model. * * <h3>Configuration</h3> * Subclasses can set the following properties. Setting them may trigger redrawing. * <ul> * <li>The {@link #setFont(Font) font} used to draw text in {@link #paintLine(GC, int, int, int, int)}.</li> * <li>The horizontal {@link #setTextInset(int) text inset} for text drawn.</li> * <li>The {@link #setDefaultBackground(Color) default background color} of the ruler.</li> * <li>The {@link #setWidth(int) width} of the ruler.</li> * </ul> * * @since 3.3 */ public abstract class AbstractRulerColumn : IVerticalRulerColumn, IVerticalRulerInfo, IVerticalRulerInfoExtension { private static const int DEFAULT_WIDTH= 12; private static const int DEFAULT_TEXT_INSET= 2; /** * Handles all the mouse interaction in this line number ruler column. */ private final class MouseHandler : MouseListener, MouseMoveListener { /* * @see dwt.events.MouseListener#mouseUp(dwt.events.MouseEvent) */ public void mouseUp(MouseEvent event) { } /* * @see dwt.events.MouseListener#mouseDown(dwt.events.MouseEvent) */ public void mouseDown(MouseEvent event) { fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); } /* * @see dwt.events.MouseListener#mouseDoubleClick(dwt.events.MouseEvent) */ public void mouseDoubleClick(MouseEvent event) { fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); } /* * @see dwt.events.MouseMoveListener#mouseMove(dwt.events.MouseEvent) */ public void mouseMove(MouseEvent event) { fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); } } /** * Internal listener class that updates the ruler upon scrolling and text modifications. */ private final class InternalListener : IViewportListener, ITextListener { /* * @see IViewportListener#viewportChanged(int) */ public void viewportChanged(int topPixel) { int delta= topPixel - fLastTopPixel; if (scrollVertical(delta)) fCanvas.update(); // force update the invalidated regions } /* * @see ITextListener#textChanged(TextEvent) */ public void textChanged(TextEvent event) { /* * Redraw: - when the viewer is drawing, and any of the following - the widget was not * full before the change - the widget is not full after the change - the document event * was a visual modification (no document event attached) - for example when the * projection changes. */ if (!event.getViewerRedrawState()) return; if (fWasShowingEntireContents || event.getDocumentEvent() is null || JFaceTextUtil.isShowingEntireContents(fStyledText)) redraw(); } } /* Listeners */ /** The viewport listener. */ private const InternalListener fInternalListener; /** The mouse handler. */ private const MouseHandler fMouseHandler; /* * Implementation and context of this ruler - created and set in createControl(), disposed of in * columnRemoved(). */ /** The parent ruler, possibly <code>null</code>. */ private CompositeRuler fParentRuler; /** The canvas, the only widget used to draw this ruler, possibly <code>null</code>. */ private Canvas fCanvas; /** The text viewer, possibly <code>null</code>. */ private ITextViewer fTextViewer; /** The text viewer's widget, possibly <code>null</code>. */ private StyledText fStyledText; /* State when the canvas was last painted. */ /** The text widget's top pixel when the ruler was last painted. */ private int fLastTopPixel= -1; /** Whether the text widget was showing the entire contents when the ruler was last painted. */ private bool fWasShowingEntireContents= false; /* Configuration */ /** The width of this ruler. */ private int fWidth= DEFAULT_WIDTH; /** The text inset. */ private int fTextInset= DEFAULT_TEXT_INSET; /** The default background color, <code>null</code> to use the text viewer's background color. */ private Color fBackground; /** The font, <code>null</code> to use the default font. */ private Font fFont; /** The annotation model, possibly <code>null</code>. */ private IAnnotationModel fModel; /** The annotation hover, possibly <code>null</code>. */ private IAnnotationHover fHover; /** * Creates a new ruler. */ protected this() { fMouseHandler= new MouseHandler(); fInternalListener= new InternalListener(); } /* * @see dwtx.jface.text.source.IVerticalRulerColumn#createControl(dwtx.jface.text.source.CompositeRuler, * dwt.widgets.Composite) */ public Control createControl(CompositeRuler parentRuler, Composite parentControl) { Assert.isLegal(parentControl !is null); Assert.isLegal(parentRuler !is null); Assert.isLegal(fParentRuler is null); // only call when not yet initialized! fParentRuler= parentRuler; fTextViewer= getParentRuler().getTextViewer(); fTextViewer.addViewportListener(fInternalListener); fTextViewer.addTextListener(fInternalListener); fStyledText= fTextViewer.getTextWidget(); fCanvas= new Canvas(parentControl, getCanvasStyle()); fCanvas.setBackground(getDefaultBackground()); fCanvas.setFont(getFont()); fCanvas.addPaintListener(new class() PaintListener { public void paintControl(PaintEvent event) { this.outer.paintControl(event); } }); fCanvas.addMouseListener(fMouseHandler); fCanvas.addMouseMoveListener(fMouseHandler); return fCanvas; } /** * Returns the DWT style bits used when creating the ruler canvas. * <p> * The default implementation returns <code>DWT.NO_BACKGROUND</code>.</p> * <p> * Clients may reimplement this method to create a canvas with their * desired style bits.</p> * * @return the DWT style bits, or <code>DWT.NONE</code> if none */ protected int getCanvasStyle() { return DWT.NO_BACKGROUND; } /* * @see dwtx.jface.text.source.IVerticalRulerColumn#getControl() */ public final Control getControl() { return fCanvas; } /** * The new width in pixels. The <code>DEFAULT_WIDTH</code> constant * specifies the default width. * * @param width the new width */ protected final void setWidth(int width) { Assert.isLegal(width >= 0); if (fWidth !is width) { fWidth= width; CompositeRuler composite= getParentRuler(); if (composite !is null) composite.relayout(); } } /* * @see dwtx.jface.text.source.IVerticalRulerColumn#getWidth() */ public final int getWidth() { return fWidth; } /** * Returns the parent ruler, <code>null</code> before * {@link #createControl(CompositeRuler, Composite)} has been called. * * @return the parent ruler or <code>null</code> */ protected final CompositeRuler getParentRuler() { return fParentRuler; } /** * {@inheritDoc} * * @param font the font or <code>null</code> to use the default font */ public final void setFont(Font font) { if (fFont !is font) { fFont= font; redraw(); } } /** * Returns the current font. If a font has not been explicitly set, the widget's font is * returned. * * @return the font used to render text on the ruler. */ protected final Font getFont() { if (fFont !is null) return fFont; if (fStyledText !is null && !fStyledText.isDisposed()) return fStyledText.getFont(); return JFaceResources.getTextFont(); } /** * Sets the text inset (padding) used to draw text in {@link #paintLine(GC, int, int, int, int)}. * * @param textInset the new text inset */ protected final void setTextInset(int textInset) { if (textInset !is fTextInset) { fTextInset= textInset; redraw(); } } /** * Returns the text inset for text drawn by {@link #paintLine(GC, int, int, int, int)}. The * <code>DEFAULT_TEXT_INSET</code> constant specifies the default inset in pixels. * * @return the text inset for text */ protected final int getTextInset() { return fTextInset; } /* * @see dwtx.jface.text.source.IVerticalRulerColumn#setModel(dwtx.jface.text.source.IAnnotationModel) */ public void setModel(IAnnotationModel model) { if (fModel !is model) { fModel= model; redraw(); } } /* * @see dwtx.jface.text.source.IVerticalRulerInfoExtension#getModel() */ public final IAnnotationModel getModel() { return fModel; } /** * Sets the default background color for this column. The default background is used as default * implementation of {@link #computeBackground(int)} and also to paint the area of the ruler * that does not correspond to any lines (when the viewport is not entirely filled with lines). * * @param background the default background color, <code>null</code> to use the text widget's * background */ protected final void setDefaultBackground(Color background) { if (fBackground !is background) { fBackground= background; if (fCanvas !is null && !fCanvas.isDisposed()) fCanvas.setBackground(getDefaultBackground()); redraw(); } } /** * Returns the background color. May return <code>null</code> if the system is shutting down. * * @return the background color */ protected final Color getDefaultBackground() { if (fBackground !is null) return fBackground; if (fStyledText !is null && !fStyledText.isDisposed()) return fStyledText.getBackground(); Display display; if (fCanvas !is null && !fCanvas.isDisposed()) display= fCanvas.getDisplay(); else display= Display.getCurrent(); if (display !is null) return display.getSystemColor(DWT.COLOR_LIST_BACKGROUND); return null; } /** * Sets the annotation hover. * * @param hover the annotation hover, <code>null</code> for no hover */ protected final void setHover(IAnnotationHover hover) { if (fHover !is hover) fHover= hover; } /* * @see dwtx.jface.text.source.IVerticalRulerInfoExtension#getHover() */ public IAnnotationHover getHover() { return fHover; } /** * Disposes this ruler column. * <p> * Subclasses may extend this method.</p> * <p> * Clients who created this column are responsible to call this method * once the column is no longer used.</p> */ public void dispose() { if (fTextViewer !is null) { fTextViewer.removeViewportListener(fInternalListener); fTextViewer.removeTextListener(fInternalListener); fTextViewer= null; } if (fStyledText !is null) fStyledText= null; if (fCanvas !is null) { fCanvas.dispose(); fCanvas= null; } } /* * @see dwtx.jface.text.source.IVerticalRulerColumn#redraw() */ public final void redraw() { if (fCanvas !is null && !fCanvas.isDisposed()) fCanvas.redraw(); } /** * Marks the region covered by <code>lines</code> as needing to be redrawn. * * @param lines the lines to be redrawn in document coordinates */ protected final void redraw(ILineRange lines) { if (fCanvas is null || fCanvas.isDisposed()) return; int firstModelLine= lines.getStartLine(); int lastModelLine= firstModelLine + lines.getNumberOfLines(); int firstWidgetLine= JFaceTextUtil.modelLineToWidgetLine(fTextViewer, firstModelLine); int lastWidgetLine= JFaceTextUtil.modelLineToWidgetLine(fTextViewer, lastModelLine); int from= Math.max(0, fStyledText.getLinePixel(firstWidgetLine)); // getLinePixel will return the last pixel of the last line if line is lineCount int to= Math.min(fCanvas.getSize().y, fStyledText.getLinePixel(lastWidgetLine + 1)); fCanvas.redraw(0, from, fWidth, to - from, false); } /** * Paints the ruler column. * * @param event the paint event */ private void paintControl(PaintEvent event) { if (fTextViewer is null) return; fWasShowingEntireContents= JFaceTextUtil.isShowingEntireContents(fStyledText); fLastTopPixel= fStyledText.getTopPixel(); ILineRange lines= computeDirtyWidgetLines(event); GC gc= event.gc; paint(gc, lines); if ((fCanvas.getStyle() & DWT.NO_BACKGROUND) !is 0) { // fill empty area below any lines int firstEmpty= Math.max(event.y, fStyledText.getLinePixel(fStyledText.getLineCount())); int lastEmpty= event.y + event.height; if (lastEmpty > firstEmpty) { gc.setBackground(getDefaultBackground()); gc.fillRectangle(0, firstEmpty, getWidth(), lastEmpty - firstEmpty); } } } /** * Computes the widget lines that need repainting given the clipping region of a paint event. * * @param event the paint event * @return the lines in widget coordinates that need repainting */ private ILineRange computeDirtyWidgetLines(PaintEvent event) { int firstLine= fStyledText.getLineIndex(event.y); int lastLine= fStyledText.getLineIndex(event.y + event.height - 1); return new LineRange(firstLine, lastLine - firstLine + 1); } /** * Paints the ruler. Note that <code>lines</code> reference widget line indices, and that * <code>lines</code> may not cover the entire viewport, but only the lines that need to be * painted. The lines may not be entirely visible. * <p> * Subclasses may replace or extend. The default implementation calls * {@link #paintLine(GC, int, int, int, int)} for every visible line. * </p> * * @param gc the graphics context to paint on * @param lines the lines to paint in widget coordinates */ protected void paint(GC gc, ILineRange lines) { final int firstLine= lines.getStartLine(); final int lastLine= firstLine + lines.getNumberOfLines(); for (int line= firstLine; line < lastLine; line++) { int modelLine= JFaceTextUtil.widgetLine2ModelLine(fTextViewer, line); if (modelLine is -1) continue; int linePixel= fStyledText.getLinePixel(line); int lineHeight= fStyledText.getLineHeight(fStyledText.getOffsetAtLine(line)); paintLine(gc, modelLine, line, linePixel, lineHeight); } } /** * Paints the ruler representation of a single line. * <p> * Subclasses may replace or extend. The default implementation draws the text obtained by * {@link #computeText(int)} in the {@link #computeForeground(int) foreground color} and fills * the entire width using the {@link #computeBackground(int) background color}. The text is * drawn {@link #getTextInset()} pixels to the right of the left border. * </p> * * @param gc the graphics context to paint on * @param modelLine the model line (based on document coordinates) * @param widgetLine the line in the text widget corresponding to <code>modelLine</code> * @param linePixel the first y-pixel of the widget line * @param lineHeight the line height in pixels */ protected void paintLine(GC gc, int modelLine, int widgetLine, int linePixel, int lineHeight) { gc.setBackground(computeBackground(modelLine)); gc.fillRectangle(0, linePixel, getWidth(), lineHeight); String text= computeText(modelLine); if (text !is null) { gc.setForeground(computeForeground(modelLine)); gc.drawString(text, getTextInset(), linePixel, true); } } /** * Returns the text to be drawn for a certain line by {@link #paintLine(GC, int, int, int, int)}, * <code>null</code> for no text. The default implementation returns <code>null</code>. * <p> * Subclasses may replace or extend. * </p> * * @param line the document line number * @return the text to be drawn for the given line, <code>null</code> for no text */ protected String computeText(int line) { return null; } /** * Returns the background color drawn for a certain line by * {@link #paintLine(GC, int, int, int, int)}. The default implementation returns * {@link #getDefaultBackground()}. * <p> * Subclasses may replace or extend. * </p> * * @param line the document line number * @return the background color for drawn for the given line */ protected Color computeBackground(int line) { return getDefaultBackground(); } /** * Returns the foreground color drawn for a certain line by * {@link #paintLine(GC, int, int, int, int)}. The default implementation returns a * {@link DWT#COLOR_DARK_GRAY} color. * <p> * Subclasses may replace or extend. * </p> * * @param line the document line number * @return the foreground color for drawn for the given line */ protected Color computeForeground(int line) { return fStyledText.getDisplay().getSystemColor(DWT.COLOR_DARK_GRAY); } /* * @see dwtx.jface.text.source.IVerticalRulerInfo#getLineOfLastMouseButtonActivity() */ public final int getLineOfLastMouseButtonActivity() { return getParentRuler().getLineOfLastMouseButtonActivity(); } /* * @see dwtx.jface.text.source.IVerticalRulerInfo#toDocumentLineNumber(int) */ public final int toDocumentLineNumber(int y_coordinate) { return getParentRuler().toDocumentLineNumber(y_coordinate); } /* * @see dwtx.jface.text.source.IVerticalRulerInfoExtension#addVerticalRulerListener(dwtx.jface.text.source.IVerticalRulerListener) */ public void addVerticalRulerListener(IVerticalRulerListener listener) { throw new UnsupportedOperationException(); } /* * @see dwtx.jface.text.source.IVerticalRulerInfoExtension#removeVerticalRulerListener(dwtx.jface.text.source.IVerticalRulerListener) */ public void removeVerticalRulerListener(IVerticalRulerListener listener) { throw new UnsupportedOperationException(); } /** * Scrolls the canvas vertically (adapted from * {@linkplain StyledText StyledText.scrollVertical()}). * * @param pixels the number of pixels to scroll (negative to scroll upwards) * @return <code>true</code> if the widget was scrolled, <code>false</code> if the widget * was not scrolled */ private bool scrollVertical(int pixels) { if (pixels is 0 || fCanvas is null || fCanvas.isDisposed()) return false; final int width= getWidth(); final int clientAreaHeight= fStyledText.getClientArea().height; final int topMargin= 0; final int leftMargin= 0; final int bottomMargin= 0; if (pixels > 0) { // downwards scrolling - content moves upwards int sourceY= topMargin + pixels; int scrollHeight= clientAreaHeight - sourceY - bottomMargin; if (scrollHeight > 0) // scroll recycled area fCanvas.scroll(leftMargin, topMargin, leftMargin, sourceY, width, scrollHeight, true); if (sourceY > scrollHeight) { // redraw in-between area int redrawY= Math.max(0, topMargin + scrollHeight); int redrawHeight= Math.min(clientAreaHeight, pixels - scrollHeight); fCanvas.redraw(leftMargin, redrawY, width, redrawHeight, true); } } else { // upwards scrolling - content moves downwards int destinationY= topMargin - pixels; int scrollHeight= clientAreaHeight - destinationY - bottomMargin; if (scrollHeight > 0) // scroll recycled area fCanvas.scroll(leftMargin, destinationY, leftMargin, topMargin, width, scrollHeight, true); if (destinationY > scrollHeight) { // redraw in-between area int redrawY= Math.max(0, topMargin + scrollHeight); int redrawHeight= Math.min(clientAreaHeight, -pixels - scrollHeight); fCanvas.redraw(leftMargin, redrawY, width, redrawHeight, true); } } return true; } }