/*******************************************************************************
* Copyright (c) 2000, 2009 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
* Anton Leherbauer (Wind River Systems)
*******************************************************************************/
package org.eclipse.cdt.dsf.debug.internal.ui.disassembly;
import java.util.Arrays;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextListener;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.ITextViewerExtension;
import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.IViewportListener;
import org.eclipse.jface.text.TextEvent;
import org.eclipse.jface.text.source.CompositeRuler;
import org.eclipse.jface.text.source.IAnnotationModel;
import org.eclipse.jface.text.source.IVerticalRulerColumn;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackListener;
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.FontMetrics;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
/**
* Vertical ruler column for use with disassembly parts.
* <p>
* Derived from {@link org.eclipse.jface.text.source.LineNumberRulerColumn}.
* </p>
*/
public class DisassemblyRulerColumn implements IVerticalRulerColumn {
protected final static String DOTS = "......................................................................"; //$NON-NLS-1$
protected final static String SPACES = " "; //$NON-NLS-1$
/**
* Internal listener class.
*/
class InternalListener implements IViewportListener, ITextListener, ISelectionChangedListener {
/*
* @see IViewportListener#viewportChanged(int)
*/
public void viewportChanged(int verticalPosition) {
if (verticalPosition != fScrollPos)
redraw();
}
/*
* @see ITextListener#textChanged(TextEvent)
*/
public void textChanged(TextEvent event) {
if (updateNumberOfDigits()) {
computeIndentations();
layout(event.getViewerRedrawState());
return;
}
if (!event.getViewerRedrawState())
return;
if (fSensitiveToTextChanges || event.getDocumentEvent() == null)
postRedraw();
}
/*
* @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
*/
public void selectionChanged(SelectionChangedEvent event) {
postRedraw();
}
}
/**
* Handles all the mouse interaction in this line number ruler column.
*/
class MouseHandler implements MouseListener, MouseMoveListener, MouseTrackListener {
/** The cached view port size */
private int fCachedViewportSize;
/** The area of the line at which line selection started */
private IRegion fStartLine;
/** The number of the line at which line selection started */
private int fStartLineNumber;
/** The auto scroll direction */
private int fAutoScrollDirection;
/*
* @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
*/
public void mouseUp(MouseEvent event) {
// see bug 45700
if (event.button == 1) {
stopSelecting();
stopAutoScroll();
postRedraw();
}
}
/*
* @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
*/
public void mouseDown(MouseEvent event) {
fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
// see bug 45700
if (event.button == 1) {
startSelecting();
}
}
/*
* @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
*/
public void mouseDoubleClick(MouseEvent event) {
fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
stopSelecting();
stopAutoScroll();
}
/*
* @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
*/
public void mouseMove(MouseEvent event) {
if (!autoScroll(event)) {
int newLine = fParentRuler.toDocumentLineNumber(event.y);
expandSelection(newLine);
}
}
/*
* @see org.eclipse.swt.events.MouseTrackListener#mouseEnter(org.eclipse.swt.events.MouseEvent)
*/
public void mouseEnter(MouseEvent event) {
}
/*
* @see org.eclipse.swt.events.MouseTrackListener#mouseExit(org.eclipse.swt.events.MouseEvent)
*/
public void mouseExit(MouseEvent event) {
}
/*
* @see org.eclipse.swt.events.MouseTrackListener#mouseHover(org.eclipse.swt.events.MouseEvent)
*/
public void mouseHover(MouseEvent event) {
}
/**
* Called when line drag selection started. Adds mouse move and track
* listeners to this column's control.
*/
private void startSelecting() {
try {
// select line
IDocument document = fCachedTextViewer.getDocument();
fStartLineNumber = fParentRuler.getLineOfLastMouseButtonActivity();
fStartLine = document.getLineInformation(fStartLineNumber);
fCachedTextViewer.setSelectedRange(fStartLine.getOffset(), fStartLine.getLength());
fCachedViewportSize = getVisibleLinesInViewport();
// prepare for drag selection
fCanvas.addMouseMoveListener(this);
fCanvas.addMouseTrackListener(this);
} catch (BadLocationException x) {
}
}
/**
* Called when line drag selection stopped. Removes all previously
* installed listeners from this column's control.
*/
private void stopSelecting() {
// drag selection stopped
fCanvas.removeMouseMoveListener(this);
fCanvas.removeMouseTrackListener(this);
}
/**
* Expands the line selection from the remembered start line to the
* given line.
*
* @param lineNumber
* the line to which to expand the selection
*/
private void expandSelection(int lineNumber) {
try {
IDocument document = fCachedTextViewer.getDocument();
IRegion lineInfo = document.getLineInformation(lineNumber);
int start = Math.min(fStartLine.getOffset(), lineInfo.getOffset());
int end = Math.max(fStartLine.getOffset() + fStartLine.getLength(), lineInfo.getOffset()
+ lineInfo.getLength());
if (lineNumber < fStartLineNumber)
fCachedTextViewer.setSelectedRange(end, start - end);
else
fCachedTextViewer.setSelectedRange(start, end - start);
} catch (BadLocationException x) {
}
}
/**
* Called when auto scrolling stopped. Clears the auto scroll direction.
*/
private void stopAutoScroll() {
fAutoScrollDirection = SWT.NULL;
}
/**
* Called on drag selection.
*
* @param event
* the mouse event caught by the mouse move listener
* @return <code>true</code> if scrolling happened, <code>false</code>
* otherwise
*/
private boolean autoScroll(MouseEvent event) {
Rectangle area = fCanvas.getClientArea();
if (event.y > area.height) {
autoScroll(SWT.DOWN);
return true;
}
if (event.y < 0) {
autoScroll(SWT.UP);
return true;
}
stopAutoScroll();
return false;
}
/**
* Scrolls the viewer into the given direction.
*
* @param direction
* the scroll direction
*/
private void autoScroll(int direction) {
if (fAutoScrollDirection == direction)
return;
final int TIMER_INTERVAL = 5;
final Display display = fCanvas.getDisplay();
Runnable timer = null;
switch (direction) {
case SWT.UP:
timer = new Runnable() {
public void run() {
if (fAutoScrollDirection == SWT.UP) {
int top = getInclusiveTopIndex();
if (top > 0) {
fCachedTextViewer.setTopIndex(top - 1);
expandSelection(top - 1);
display.timerExec(TIMER_INTERVAL, this);
}
}
}
};
break;
case SWT.DOWN:
timer = new Runnable() {
public void run() {
if (fAutoScrollDirection == SWT.DOWN) {
int top = getInclusiveTopIndex();
fCachedTextViewer.setTopIndex(top + 1);
expandSelection(top + 1 + fCachedViewportSize);
display.timerExec(TIMER_INTERVAL, this);
}
}
};
break;
}
if (timer != null) {
fAutoScrollDirection = direction;
display.timerExec(TIMER_INTERVAL, timer);
}
}
/**
* Returns the viewer's first visible line, even if only partially
* visible.
*
* @return the viewer's first visible line
*/
private int getInclusiveTopIndex() {
if (fCachedTextWidget != null && !fCachedTextWidget.isDisposed()) {
int top = fCachedTextViewer.getTopIndex();
if ((fCachedTextWidget.getTopPixel() % fCachedTextWidget.getLineHeight()) != 0)
--top;
return top;
}
return -1;
}
}
/** This column's parent ruler */
private CompositeRuler fParentRuler;
/** Cached text viewer */
private ITextViewer fCachedTextViewer;
/** Cached text widget */
private StyledText fCachedTextWidget;
/** The columns canvas */
private Canvas fCanvas;
/** Cache for the actual scroll position in pixels */
private int fScrollPos;
/** The drawable for double buffering */
private Image fBuffer;
/** The internal listener */
private InternalListener fInternalListener = new InternalListener();
/** The font of this column */
private Font fFont;
/** The indentation cache */
private int[] fIndentation;
/** Indicates whether this column reacts on text change events */
private boolean fSensitiveToTextChanges = false;
/** The foreground color */
private Color fForeground;
/** The background color */
private Color fBackground;
/** Cached number of displayed digits */
private int fCachedNumberOfDigits = -1;
/** Flag indicating whether a relayout is required */
private boolean fRelayoutRequired = false;
/**
* Redraw runnable lock
*/
private Object fRunnableLock = new Object();
/**
* Redraw runnable state
*/
private boolean fIsRunnablePosted = false;
/**
* Redraw runnable
*/
private Runnable fRunnable = new Runnable() {
public void run() {
synchronized (fRunnableLock) {
fIsRunnablePosted = false;
}
redraw();
}
};
private boolean fAlignRight;
private boolean fPaintStyleBackground;
private boolean fPaintSelectionBackground;
/**
* Constructs a new vertical ruler column.
*
*/
public DisassemblyRulerColumn() {
this(SWT.LEFT);
// default constructor
}
public DisassemblyRulerColumn(int align) {
this(align, true, false);
}
public DisassemblyRulerColumn(int align, boolean paintSelection, boolean paintStyle) {
fAlignRight = (align & SWT.RIGHT) != 0;
fPaintSelectionBackground = paintSelection;
fPaintStyleBackground = paintStyle;
}
/**
* Sets the foreground color of this column.
*
* @param foreground
* the foreground color
*/
public void setForeground(Color foreground) {
fForeground = foreground;
}
/**
* Returns the foreground color being used to print the line numbers.
*
* @return the configured foreground color
*/
protected Color getForeground() {
return fForeground;
}
/**
* Sets the background color of this column.
*
* @param background
* the background color
*/
public void setBackground(Color background) {
fBackground = background;
if (fCanvas != null && !fCanvas.isDisposed())
fCanvas.setBackground(getBackground(fCanvas.getDisplay()));
}
/**
* Returns the System background color for list widgets.
*
* @param display
* the display
* @return the System background color for list widgets
*/
protected Color getBackground(Display display) {
if (fBackground == null)
return display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
return fBackground;
}
/*
* @see IVerticalRulerColumn#getControl()
*/
public Control getControl() {
return fCanvas;
}
/*
* @see IVerticalRuleColumnr#getWidth
*/
public int getWidth() {
return fIndentation[0];
}
/**
* Computes the number of digits to be displayed. Returns <code>true</code>
* if the number of digits changed compared to the previous call of this
* method. If the method is called for the first time, the return value is
* also <code>true</code>.
*
* @return whether the number of digits has been changed
*/
protected boolean updateNumberOfDigits() {
if (fCachedTextViewer == null)
return false;
int digits = computeNumberOfCharacters();
if (fCachedNumberOfDigits != digits) {
fCachedNumberOfDigits = digits;
return true;
}
return false;
}
/**
* Does the real computation of the number of characters. The default
* implementation computes the number of digits for the line number.
* Subclasses may override this method if they need extra space on the ruler.
*
* @return the number of characters to be displayed on the ruler.
*/
protected int computeNumberOfCharacters() {
IDocument document = fCachedTextViewer.getDocument();
int lines= document == null ? 0 : document.getNumberOfLines();
int digits= 2;
while (lines > Math.pow(10, digits) - 1) {
++digits;
}
return digits;
}
/**
* Layouts the enclosing viewer to adapt the layout to changes of the size
* of the individual components.
*
* @param redraw
* <code>true</code> if this column can be redrawn
*/
protected void layout(boolean redraw) {
if (!redraw) {
fRelayoutRequired= true;
return;
}
fRelayoutRequired= false;
if (fCachedTextViewer instanceof ITextViewerExtension) {
ITextViewerExtension extension= (ITextViewerExtension) fCachedTextViewer;
Control control= extension.getControl();
if (control instanceof Composite && !control.isDisposed()) {
Composite composite= (Composite) control;
composite.layout(true);
}
}
}
/**
* Computes the indentations for the given font and stores them in
* <code>fIndentation</code>.
*/
protected void computeIndentations() {
if (fCanvas == null)
return;
GC gc= new GC(fCanvas);
try {
gc.setFont(fCanvas.getFont());
fIndentation= new int[fCachedNumberOfDigits + 1];
char[] digitStr= new char[fCachedNumberOfDigits + 1];
Arrays.fill(digitStr, '9');
Point p= gc.stringExtent(new String(digitStr, 0, fCachedNumberOfDigits + 1));
fIndentation[0]= p.x;
for (int i= 1; i <= fCachedNumberOfDigits; i++) {
p= gc.stringExtent(new String(digitStr, 0, i));
fIndentation[i]= fIndentation[0] - p.x;
}
} finally {
gc.dispose();
}
}
/*
* @see IVerticalRulerColumn#createControl(CompositeRuler, Composite)
*/
public Control createControl(CompositeRuler parentRuler, Composite parentControl) {
fParentRuler= parentRuler;
fCachedTextViewer= parentRuler.getTextViewer();
fCachedTextWidget= fCachedTextViewer.getTextWidget();
fCanvas= new Canvas(parentControl, SWT.NONE);
fCanvas.setBackground(getBackground(fCanvas.getDisplay()));
fCanvas.setForeground(fForeground);
fCanvas.addPaintListener(new PaintListener() {
public void paintControl(PaintEvent event) {
if (fCachedTextViewer != null)
doubleBufferPaint(event.gc);
}
});
fCanvas.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
handleDispose();
fCachedTextViewer= null;
fCachedTextWidget= null;
}
});
fCanvas.addMouseListener(new MouseHandler());
if (fCachedTextViewer != null) {
fCachedTextViewer.addViewportListener(fInternalListener);
fCachedTextViewer.addTextListener(fInternalListener);
fCachedTextViewer.getSelectionProvider().addSelectionChangedListener(fInternalListener);
if (fFont == null) {
if (fCachedTextWidget != null && !fCachedTextWidget.isDisposed())
fFont= fCachedTextWidget.getFont();
}
}
if (fFont != null)
fCanvas.setFont(fFont);
updateNumberOfDigits();
computeIndentations();
return fCanvas;
}
/**
* Disposes the column's resources.
*/
protected void handleDispose() {
if (fCachedTextViewer != null) {
fCachedTextViewer.removeViewportListener(fInternalListener);
fCachedTextViewer.removeTextListener(fInternalListener);
fCachedTextViewer.getSelectionProvider().removeSelectionChangedListener(fInternalListener);
}
if (fBuffer != null) {
fBuffer.dispose();
fBuffer= null;
}
}
/**
* Double buffer drawing.
*
* @param dest
* the gc to draw into
*/
private void doubleBufferPaint(GC dest) {
Point size= fCanvas.getSize();
if (size.x <= 0 || size.y <= 0)
return;
if (fBuffer != null) {
Rectangle r= fBuffer.getBounds();
if (r.width != size.x || r.height != size.y) {
fBuffer.dispose();
fBuffer= null;
}
}
if (fBuffer == null)
fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y);
GC gc= new GC(fBuffer);
gc.setFont(fCanvas.getFont());
if (fForeground != null)
gc.setForeground(fForeground);
try {
gc.setBackground(getBackground(fCanvas.getDisplay()));
gc.fillRectangle(0, 0, size.x, size.y);
if (fCachedTextViewer instanceof ITextViewerExtension5)
doPaint1(gc);
else
doPaint(gc);
} finally {
gc.dispose();
}
dest.drawImage(fBuffer, 0, 0);
}
/**
* Returns the viewport height in lines.
*
* @return the viewport height in lines
*/
protected int getVisibleLinesInViewport() {
Rectangle clArea= fCachedTextWidget.getClientArea();
return clArea.height / fCachedTextWidget.getLineHeight();
}
/**
* Draws the ruler column.
*
* @param gc
* the gc to draw into
*/
private void doPaint(GC gc) {
if (fCachedTextViewer == null)
return;
if (fCachedTextWidget == null)
return;
int firstLine= 0;
int topLine= fCachedTextWidget.getTopIndex();
fScrollPos= fCachedTextWidget.getTopPixel();
int lineheight= fCachedTextWidget.getLineHeight();
int partialLineHidden= fScrollPos % lineheight;
if (partialLineHidden > 0 && topLine > 0) // widgetTopLine shows the
// first fully visible line
--topLine;
int bottomLine;
try {
IRegion region= fCachedTextViewer.getVisibleRegion();
IDocument doc= fCachedTextViewer.getDocument();
if (doc == null)
return;
firstLine= doc.getLineOfOffset(region.getOffset());
if (firstLine > topLine)
topLine= firstLine;
bottomLine= doc.getLineOfOffset(region.getOffset() + region.getLength());
} catch (BadLocationException x) {
return;
}
fSensitiveToTextChanges= bottomLine - topLine < getVisibleLinesInViewport();
int baselineBias= getBaselineBias(gc);
int topInset= fCachedTextViewer.getTopInset();
int y= topInset - partialLineHidden;
Point canvasSize= fCanvas.getSize();
Point selection= fCachedTextWidget.getSelection();
boolean selectedLine= false;
Color defaultForeground= gc.getForeground();
Color defaultBackground= gc.getBackground();
for (int line= topLine; y < canvasSize.y && line <= bottomLine; line++, y += lineheight) {
int widgetOffset= fCachedTextWidget.getOffsetAtLine(line);
if (fPaintSelectionBackground && widgetOffset >= selection.x && widgetOffset < selection.y) {
if (!selectedLine) {
selectedLine= true;
gc.setForeground(fCachedTextWidget.getSelectionForeground());
gc.setBackground(fCachedTextWidget.getSelectionBackground());
}
} else if (selectedLine) {
selectedLine= false;
gc.setForeground(defaultForeground);
gc.setBackground(defaultBackground);
}
if (selectedLine) {
gc.fillRectangle(0, y, canvasSize.x, lineheight);
} else if (fPaintStyleBackground && widgetOffset >= 0 && widgetOffset < fCachedTextWidget.getCharCount()) {
StyleRange style= fCachedTextWidget.getStyleRangeAtOffset(widgetOffset);
if (style != null && style.background != null) {
gc.setBackground(style.background);
gc.fillRectangle(0, y + baselineBias, canvasSize.x, lineheight - baselineBias);
gc.setBackground(defaultBackground);
}
}
paintLine(line, y, lineheight, gc, fCachedTextWidget.getDisplay());
String s= createDisplayString(line);
int indentation= fAlignRight ? fIndentation[s.length()] : 0;
gc.drawString(s, indentation, y + baselineBias, true);
}
}
/**
* Computes the string to be printed for <code>line</code>. The default
* implementation returns <code>Integer.toString(line + 1)</code>.
*
* @param line
* the line number for which the string is generated
* @return the string to be printed on the ruler column for <code>line</code>
*/
String createDisplayString(int line) {
return Integer.toString(line + 1);
}
/**
* Draws the ruler column. Uses <code>ITextViewerExtension5</code> for the
* implementation. Will replace <code>doPinat(GC)</code>.
*
* @param gc
* the gc to draw into
*/
private void doPaint1(GC gc) {
if (fCachedTextViewer == null)
return;
ITextViewerExtension5 extension= (ITextViewerExtension5) fCachedTextViewer;
int widgetTopLine= fCachedTextWidget.getTopIndex();
fScrollPos= fCachedTextWidget.getTopPixel();
int lineheight= fCachedTextWidget.getLineHeight();
int partialLineHidden= fScrollPos % lineheight;
if (partialLineHidden > 0 && widgetTopLine > 0) // widgetTopLine shows
// the first fully
// visible line
--widgetTopLine;
int modelTopLine= extension.widgetLine2ModelLine(widgetTopLine);
int modelBottomLine= fCachedTextViewer.getBottomIndex();
if (modelBottomLine >= 0)
++modelBottomLine;
try {
IRegion region= extension.getModelCoverage();
IDocument doc= fCachedTextViewer.getDocument();
if (doc == null)
return;
int coverageTopLine= doc.getLineOfOffset(region.getOffset());
if (coverageTopLine > modelTopLine || modelTopLine == -1)
modelTopLine= coverageTopLine;
int coverageBottomLine= doc.getLineOfOffset(region.getOffset() + region.getLength());
if (coverageBottomLine < modelBottomLine || modelBottomLine == -1)
modelBottomLine= coverageBottomLine;
} catch (BadLocationException x) {
return;
}
fSensitiveToTextChanges= modelBottomLine - modelTopLine < getVisibleLinesInViewport();
int baselineBias= getBaselineBias(gc);
int topInset= fCachedTextViewer.getTopInset();
int y= topInset - partialLineHidden;
Point canvasSize= fCanvas.getSize();
Point selection= fCachedTextWidget.getSelection();
boolean selectedLine= false;
Color defaultForeground= gc.getForeground();
Color defaultBackground= gc.getBackground();
for (int modelLine= modelTopLine; y < canvasSize.y && modelLine <= modelBottomLine; modelLine++) {
// don't draw hidden (e.g. folded) lines
int widgetLine= extension.modelLine2WidgetLine(modelLine);
if (widgetLine == -1)
continue;
int widgetOffset= fCachedTextWidget.getOffsetAtLine(widgetLine);
if (fPaintSelectionBackground && widgetOffset >= selection.x && widgetOffset < selection.y) {
if (!selectedLine) {
selectedLine= true;
gc.setForeground(fCachedTextWidget.getSelectionForeground());
gc.setBackground(fCachedTextWidget.getSelectionBackground());
}
} else if (selectedLine) {
selectedLine= false;
gc.setForeground(defaultForeground);
gc.setBackground(defaultBackground);
}
if (selectedLine) {
gc.fillRectangle(0, y, canvasSize.x, lineheight);
} else if (fPaintStyleBackground && widgetOffset >= 0 && widgetOffset < fCachedTextWidget.getCharCount()) {
StyleRange style= fCachedTextWidget.getStyleRangeAtOffset(widgetOffset);
if (style != null && style.background != null) {
gc.setBackground(style.background);
gc.fillRectangle(0, y + baselineBias, canvasSize.x, lineheight - baselineBias);
gc.setBackground(defaultBackground);
}
}
paintLine(modelLine, y, lineheight, gc, fCachedTextWidget.getDisplay());
String s= createDisplayString(modelLine);
int indentation= fAlignRight ? fIndentation[s.length()] : 0;
gc.drawString(s, indentation, y + baselineBias, true);
y += lineheight;
}
}
/**
* Returns the difference between the baseline of the widget and the
* baseline as specified by the font for <code>gc</code>. When drawing
* text, the returned bias should be added to obtain text line up on
* the correct base line of the text widget.
*
* @param gc
* the <code>GC</code> to get the font metrics from
* @return the baseline bias to use when drawing text that is line up with
* <code>fCachedTextWidget</code>
*/
private int getBaselineBias(GC gc) {
/*
* https://bugs.eclipse.org/bugs/show_bug.cgi?id=62951 widget line
* height may be more than the font height used for the text,
* since font styles (bold, italics...) can have larger font metrics
* than the simple font used for the numbers.
*/
int widgetBaseline= fCachedTextWidget.getBaseline();
FontMetrics fm= gc.getFontMetrics();
int fontBaseline= fm.getAscent() + fm.getLeading();
Assert.isTrue(widgetBaseline >= fontBaseline);
int baselineBias= widgetBaseline - fontBaseline;
return baselineBias;
}
/**
* Paints the line. After this method is called the text is painted
* on top of the result of this method.
* <p>
* This default implementation does nothing.
* </p>
*
* @param line
* the line of the document which the ruler is painted for
* @param y
* the y-coordinate of the box being painted for
* <code>line</code>, relative to <code>gc</code>
* @param lineheight
* the height of one line (and therefore of the box being
* painted)
* @param gc
* the drawing context the client may choose to draw on.
* @param display
* the display the drawing occurs on
*/
protected void paintLine(int line, int y, int lineheight, GC gc, Display display) {
}
/**
* Triggers a redraw in the display thread.
*/
protected final void postRedraw() {
if (fCanvas != null && !fCanvas.isDisposed()) {
Display d= fCanvas.getDisplay();
if (d != null) {
synchronized (fRunnableLock) {
if (fIsRunnablePosted)
return;
fIsRunnablePosted= true;
}
d.asyncExec(fRunnable);
}
}
}
/*
* @see IVerticalRulerColumn#redraw()
*/
public void redraw() {
if (fRelayoutRequired) {
layout(true);
return;
}
if (fCanvas != null && !fCanvas.isDisposed()) {
GC gc= new GC(fCanvas);
doubleBufferPaint(gc);
gc.dispose();
}
}
/*
* @see IVerticalRulerColumn#setModel(IAnnotationModel)
*/
public void setModel(IAnnotationModel model) {
}
/*
* @see IVerticalRulerColumn#setFont(Font)
*/
public void setFont(Font font) {
fFont= font;
if (fCanvas != null && !fCanvas.isDisposed()) {
fCanvas.setFont(fFont);
updateNumberOfDigits();
computeIndentations();
}
}
/**
* Returns the parent (composite) ruler of this ruler column.
*
* @return the parent ruler
*/
protected CompositeRuler getParentRuler() {
return fParentRuler;
}
}