All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jface.text.source.AbstractRulerColumn Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright (c) 2006, 2008 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
 *******************************************************************************/
package org.eclipse.jface.text.source;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;

import org.eclipse.core.runtime.Assert;

import org.eclipse.jface.resource.JFaceResources;

import org.eclipse.jface.text.ITextListener;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.IViewportListener;
import org.eclipse.jface.text.JFaceTextUtil;
import org.eclipse.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.
 *
 * 

Painting

* Subclasses can hook into the paint loop at three levels: *
    *
  • Override {@link #paint(GC, ILineRange)} to control the entire painting of * the ruler.
  • *
  • Override {@link #paintLine(GC, int, int, int, int)} to control the * painting of a line.
  • *
  • Leave the painting to the default implementation, but override {@link #computeBackground(int)}, * {@link #computeForeground(int)} and {@link #computeText(int)} * to specify the ruler appearance for a line.
  • *
* *

Invalidation

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

Configuration

* Subclasses can set the following properties. Setting them may trigger redrawing. *
    *
  • The {@link #setFont(Font) font} used to draw text in {@link #paintLine(GC, int, int, int, int)}.
  • *
  • The horizontal {@link #setTextInset(int) text inset} for text drawn.
  • *
  • The {@link #setDefaultBackground(Color) default background color} of the ruler.
  • *
  • The {@link #setWidth(int) width} of the ruler.
  • *
* * @since 3.3 */ public abstract class AbstractRulerColumn implements IVerticalRulerColumn, IVerticalRulerInfo, IVerticalRulerInfoExtension { private static final int DEFAULT_WIDTH= 12; private static final int DEFAULT_TEXT_INSET= 2; /** * Handles all the mouse interaction in this line number ruler column. */ private final class MouseHandler implements MouseListener, MouseMoveListener { @Override public void mouseUp(MouseEvent event) { } @Override public void mouseDown(MouseEvent event) { fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); } @Override public void mouseDoubleClick(MouseEvent event) { fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y); } @Override 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 implements IViewportListener, ITextListener { @Override public void viewportChanged(int topPixel) { int delta= topPixel - fLastTopPixel; if (scrollVertical(delta)) fCanvas.update(); // force update the invalidated regions } @Override 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() == null || JFaceTextUtil.isShowingEntireContents(fStyledText)) redraw(); } } /* Listeners */ /** The viewport listener. */ private final InternalListener fInternalListener= new InternalListener(); /** The mouse handler. */ private final MouseHandler fMouseHandler= new MouseHandler(); /* * Implementation and context of this ruler - created and set in createControl(), disposed of in * columnRemoved(). */ /** The parent ruler, possibly null. */ private CompositeRuler fParentRuler; /** The canvas, the only widget used to draw this ruler, possibly null. */ private Canvas fCanvas; /** The text viewer, possibly null. */ private ITextViewer fTextViewer; /** The text viewer's widget, possibly null. */ 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 boolean 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, null to use the text viewer's background color. */ private Color fBackground; /** The font, null to use the default font. */ private Font fFont; /** The annotation model, possibly null. */ private IAnnotationModel fModel; /** The annotation hover, possibly null. */ private IAnnotationHover fHover; /** * Creates a new ruler. */ protected AbstractRulerColumn() { } @Override public Control createControl(CompositeRuler parentRuler, Composite parentControl) { Assert.isLegal(parentControl != null); Assert.isLegal(parentRuler != null); Assert.isLegal(fParentRuler == 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 PaintListener() { @Override public void paintControl(PaintEvent event) { AbstractRulerColumn.this.paintControl(event); } }); fCanvas.addMouseListener(fMouseHandler); fCanvas.addMouseMoveListener(fMouseHandler); return fCanvas; } /** * Returns the SWT style bits used when creating the ruler canvas. *

* The default implementation returns SWT.NO_BACKGROUND.

*

* Clients may reimplement this method to create a canvas with their * desired style bits.

* * @return the SWT style bits, or SWT.NONE if none */ protected int getCanvasStyle() { return SWT.NO_BACKGROUND; } @Override public final Control getControl() { return fCanvas; } /** * The new width in pixels. The DEFAULT_WIDTH constant * specifies the default width. * * @param width the new width */ protected final void setWidth(int width) { Assert.isLegal(width >= 0); if (fWidth != width) { fWidth= width; CompositeRuler composite= getParentRuler(); if (composite != null) composite.relayout(); } } @Override public final int getWidth() { return fWidth; } /** * Returns the parent ruler, null before * {@link #createControl(CompositeRuler, Composite)} has been called. * * @return the parent ruler or null */ protected final CompositeRuler getParentRuler() { return fParentRuler; } /** * {@inheritDoc} * * @param font the font or null to use the default font */ @Override public final void setFont(Font font) { if (fFont != 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 != null) return fFont; if (fStyledText != 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 != fTextInset) { fTextInset= textInset; redraw(); } } /** * Returns the text inset for text drawn by {@link #paintLine(GC, int, int, int, int)}. The * DEFAULT_TEXT_INSET constant specifies the default inset in pixels. * * @return the text inset for text */ protected final int getTextInset() { return fTextInset; } @Override public void setModel(IAnnotationModel model) { if (fModel != model) { fModel= model; redraw(); } } @Override 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, null to use the text widget's * background */ protected final void setDefaultBackground(Color background) { if (fBackground != background) { fBackground= background; if (fCanvas != null && !fCanvas.isDisposed()) fCanvas.setBackground(getDefaultBackground()); redraw(); } } /** * Returns the background color. May return null if the system is shutting down. * * @return the background color */ protected final Color getDefaultBackground() { if (fBackground != null) return fBackground; if (fStyledText != null && !fStyledText.isDisposed()) return fStyledText.getBackground(); Display display; if (fCanvas != null && !fCanvas.isDisposed()) display= fCanvas.getDisplay(); else display= Display.getCurrent(); if (display != null) return display.getSystemColor(SWT.COLOR_LIST_BACKGROUND); return null; } /** * Sets the annotation hover. * * @param hover the annotation hover, null for no hover */ protected final void setHover(IAnnotationHover hover) { if (fHover != hover) fHover= hover; } @Override public IAnnotationHover getHover() { return fHover; } /** * Disposes this ruler column. *

* Subclasses may extend this method.

*

* Clients who created this column are responsible to call this method * once the column is no longer used.

*/ public void dispose() { if (fTextViewer != null) { fTextViewer.removeViewportListener(fInternalListener); fTextViewer.removeTextListener(fInternalListener); fTextViewer= null; } if (fStyledText != null) fStyledText= null; if (fCanvas != null) { fCanvas.dispose(); fCanvas= null; } } @Override public final void redraw() { if (fCanvas != null && !fCanvas.isDisposed()) fCanvas.redraw(); } /** * Marks the region covered by lines as needing to be redrawn. * * @param lines the lines to be redrawn in document coordinates */ protected final void redraw(ILineRange lines) { if (fCanvas == 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 == 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 == null) return; fWasShowingEntireContents= JFaceTextUtil.isShowingEntireContents(fStyledText); fLastTopPixel= fStyledText.getTopPixel(); ILineRange lines= computeDirtyWidgetLines(event); GC gc= event.gc; paint(gc, lines); if ((fCanvas.getStyle() & SWT.NO_BACKGROUND) != 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 lines reference widget line indices, and that * lines may not cover the entire viewport, but only the lines that need to be * painted. The lines may not be entirely visible. *

* Subclasses may replace or extend. The default implementation calls * {@link #paintLine(GC, int, int, int, int)} for every visible line. *

* * @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 == -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. *

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

* * @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 modelLine * @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 != 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)}, * null for no text. The default implementation returns null. *

* Subclasses may replace or extend. *

* * @param line the document line number * @return the text to be drawn for the given line, null 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()}. *

* Subclasses may replace or extend. *

* * @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 SWT#COLOR_DARK_GRAY} color. *

* Subclasses may replace or extend. *

* * @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(SWT.COLOR_DARK_GRAY); } @Override public final int getLineOfLastMouseButtonActivity() { return getParentRuler().getLineOfLastMouseButtonActivity(); } @Override public final int toDocumentLineNumber(int y_coordinate) { return getParentRuler().toDocumentLineNumber(y_coordinate); } @Override public void addVerticalRulerListener(IVerticalRulerListener listener) { throw new UnsupportedOperationException(); } @Override 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 true if the widget was scrolled, false if the widget * was not scrolled */ private boolean scrollVertical(int pixels) { if (pixels == 0 || fCanvas == 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; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy