blob: a2280149ac15f1d6c42a05aae9d41a787fc15022 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jface.text.source;
import java.util.Iterator;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
import org.eclipse.jface.text.AbstractHoverInformationControlManager;
import org.eclipse.jface.text.AbstractInformationControlManager;
import org.eclipse.jface.text.Assert;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IInformationControl;
import org.eclipse.jface.text.IInformationControlCreator;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewerExtension5;
/**
* This manager controls the layout, content, and visibility of an information
* control in reaction to mouse hover events issued by the vertical ruler of a
* source viewer.
* @since 2.0
*/
public class AnnotationBarHoverManager extends AbstractHoverInformationControlManager {
/**
* The information control closer for the hover information. Closes the information control as
* soon as the mouse pointer leaves the subject area, a mouse button is pressed, the user presses a key,
* or the subject control is resized or moved.
*/
protected class Closer extends MouseTrackAdapter
implements IInformationControlCloser, MouseListener, MouseMoveListener, ControlListener, KeyListener, DisposeListener {
/** The closer's subject control */
private Control fSubjectControl;
/** The subject area */
private Rectangle fSubjectArea;
/** Indicates whether this closer is active */
private boolean fIsActive= false;
/** The information control. */
private IInformationControl fInformationControl;
boolean fIsInformationListener;
/**
* Creates a new information control closer.
*/
public Closer() {
}
/*
* @see IInformationControlCloser#setSubjectControl(Control)
*/
public void setSubjectControl(Control control) {
fSubjectControl= control;
}
/*
* @see IInformationControlCloser#setHoverControl(IHoverControl)
*/
public void setInformationControl(IInformationControl control) {
fInformationControl= control;
}
/*
* @see IInformationControlCloser#start(Rectangle)
*/
public void start(Rectangle subjectArea) {
if (fIsActive) return;
fIsActive= true;
fSubjectArea= subjectArea;
fInformationControl.addDisposeListener(this);
if (fSubjectControl != null && !fSubjectControl.isDisposed()) {
fSubjectControl.addMouseListener(this);
fSubjectControl.addMouseMoveListener(this);
fSubjectControl.addMouseTrackListener(this);
}
// install control and key listeners on subject control in any case
if (fSubjectControl != null && !fSubjectControl.isDisposed()) {
fSubjectControl.addControlListener(this);
fSubjectControl.addKeyListener(this);
}
}
/*
* @see IInformationControlCloser#stop()
*/
public void stop() {
stop(false);
}
/**
* Stops the information control and if <code>delayRestart</code> is set
* allows restart only after a certain delay.
*
* @param delayRestart <code>true</code> if restart should be delayed
*/
protected void stop(boolean delayRestart) {
if (!fIsActive) return;
fIsActive= false;
hideInformationControl();
if (fSubjectControl != null && !fSubjectControl.isDisposed()) {
fSubjectControl.removeMouseListener(this);
fSubjectControl.removeMouseMoveListener(this);
fSubjectControl.removeMouseTrackListener(this);
}
if (fSubjectControl != null && !fSubjectControl.isDisposed()) {
fSubjectControl.removeControlListener(this);
fSubjectControl.removeKeyListener(this);
}
}
/*
* @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
*/
public void mouseMove(MouseEvent event) {
if (!fSubjectArea.contains(event.x, event.y))
stop();
}
/*
* @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
*/
public void mouseUp(MouseEvent event) {
}
/*
* @see MouseListener#mouseDown(MouseEvent)
*/
public void mouseDown(MouseEvent event) {
stop();
}
/*
* @see MouseListener#mouseDoubleClick(MouseEvent)
*/
public void mouseDoubleClick(MouseEvent event) {
stop();
}
/*
* @see MouseTrackAdapter#mouseExit(MouseEvent)
*/
public void mouseExit(MouseEvent event) {
if (!fAllowMouseExit)
stop();
}
/*
* @see ControlListener#controlResized(ControlEvent)
*/
public void controlResized(ControlEvent event) {
stop();
}
/*
* @see ControlListener#controlMoved(ControlEvent)
*/
public void controlMoved(ControlEvent event) {
stop();
}
/*
* @see KeyListener#keyReleased(KeyEvent)
*/
public void keyReleased(KeyEvent event) {
}
/*
* @see KeyListener#keyPressed(KeyEvent)
*/
public void keyPressed(KeyEvent event) {
stop(true);
}
/*
* @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent)
*/
public void widgetDisposed(DisposeEvent e) {
stop();
}
}
/** The source viewer the manager is connected to */
private ISourceViewer fSourceViewer;
/** The vertical ruler the manager is registered with */
private IVerticalRulerInfo fVerticalRulerInfo;
/** The annotation hover the manager uses to retrieve the information to display */
private IAnnotationHover fAnnotationHover;
protected boolean fAllowMouseExit= false;
/**
* Creates an annotation hover manager with the given parameters. In addition,
* the hovers anchor is RIGHT and the margin is 5 points to the right.
*
* @param sourceViewer the source viewer this manager connects to
* @param ruler the vertical ruler this manager connects to
* @param annotationHover the annotation hover providing the information to be displayed
* @param creator the information control creator
* @deprecated As of 2.1, replaced by {@link AnnotationBarHoverManager#AnnotationBarHoverManager(IVerticalRulerInfo, ISourceViewer, IAnnotationHover, IInformationControlCreator)}
*/
public AnnotationBarHoverManager(ISourceViewer sourceViewer, IVerticalRuler ruler, IAnnotationHover annotationHover, IInformationControlCreator creator) {
this(ruler, sourceViewer, annotationHover, creator);
}
/**
* Creates an annotation hover manager with the given parameters. In addition,
* the hovers anchor is RIGHT and the margin is 5 points to the right.
*
* @param rulerInfo the vertical ruler this manager connects to
* @param sourceViewer the source viewer this manager connects to
* @param annotationHover the annotation hover providing the information to be displayed
* @param creator the information control creator
* @since 2.1
*/
public AnnotationBarHoverManager(IVerticalRulerInfo rulerInfo, ISourceViewer sourceViewer, IAnnotationHover annotationHover, IInformationControlCreator creator) {
super(creator);
Assert.isNotNull(sourceViewer);
Assert.isNotNull(annotationHover);
fSourceViewer= sourceViewer;
fVerticalRulerInfo= rulerInfo;
fAnnotationHover= annotationHover;
setAnchor(ANCHOR_RIGHT);
setMargins(5, 0);
setCloser(new Closer());
}
/*
* @see AbstractHoverInformationControlManager#computeInformation()
*/
protected void computeInformation() {
fAllowMouseExit= false;
MouseEvent event= getHoverEvent();
IAnnotationHover hover= getHover(event);
int line= getHoverLine(event);
if (hover instanceof IAnnotationHoverExtension2) {
IAnnotationHoverExtension2 expandHover= (IAnnotationHoverExtension2) hover;
setCustomInformationControlCreator(expandHover.getInformationControlCreator());
setInformation(expandHover.getHoverInfo2(fSourceViewer, line), computeArea(line));
} else if (hover instanceof IAnnotationHoverExtension) {
IAnnotationHoverExtension extension= (IAnnotationHoverExtension) hover;
setCustomInformationControlCreator(extension.getInformationControlCreator());
int startLine= fSourceViewer.getTopIndex();
int endLine= fSourceViewer.getBottomIndex();
setInformation(extension.getHoverInfo(fSourceViewer, line, startLine, endLine - startLine), computeArea(line));
} else {
setCustomInformationControlCreator(null);
setInformation(hover.getHoverInfo(fSourceViewer, line), computeArea(line));
}
}
/**
* Determines the hover to be used to display information based on the source of the
* mouse hover event. If <code>fVerticalRulerInfo</code> is not a composite ruler, the
* standard hover is returned.
*
* @param source the source of the mouse hover event
* @return the hover depending on <code>source</code>, or <code>fAnnotationHover</code> if none can be found.
* @since 3.0
*/
private IAnnotationHover getHover(MouseEvent event) {
if (event == null || event.getSource() == null)
return fAnnotationHover;
if (fVerticalRulerInfo instanceof CompositeRuler) {
CompositeRuler comp= (CompositeRuler) fVerticalRulerInfo;
for (Iterator it= comp.getDecoratorIterator(); it.hasNext();) {
Object o= it.next();
if (o instanceof IVerticalRulerInfoExtension && o instanceof IVerticalRulerInfo) {
if (((IVerticalRulerInfo) o).getControl() == event.getSource()) {
IAnnotationHover hover= ((IVerticalRulerInfoExtension) o).getHover();
if (hover != null)
return hover;
}
}
}
}
return fAnnotationHover;
}
/**
* Returns the line of interest deduced from the mouse hover event.
*
* @param event a mouse hover event that triggered hovering
* @return the document model line number on which the hover event occurred or <code>-1</code> if there is no event
* @since 3.0
*/
private int getHoverLine(MouseEvent event) {
return event == null ? -1 : fVerticalRulerInfo.toDocumentLineNumber(event.y);
}
/**
* Returns for the widget line number for the given document line number.
*
* @param line the absolute line number
* @return the line number relative to the viewer's visible region
* @throws BadLocationException if <code>line</code> is not valid in the viewer's document
*/
private int getWidgetLineNumber(int line) throws BadLocationException {
if (fSourceViewer instanceof ITextViewerExtension5) {
ITextViewerExtension5 extension= (ITextViewerExtension5) fSourceViewer;
return extension.modelLine2WidgetLine(line);
}
IRegion region= fSourceViewer.getVisibleRegion();
int firstLine= fSourceViewer.getDocument().getLineOfOffset(region.getOffset());
return line - firstLine;
}
/**
* Determines graphical area covered by the given line.
*
* @param line the number of the line in the viewer whose graphical extend in the vertical ruler must be computed
* @return the graphical extend of the given line
*/
private Rectangle computeArea(int line) {
try {
StyledText text= fSourceViewer.getTextWidget();
int lineHeight= text.getLineHeight();
int y= getWidgetLineNumber(line) * lineHeight - text.getTopPixel();
Point size= fVerticalRulerInfo.getControl().getSize();
return new Rectangle(0, y, size.x, lineHeight);
} catch (BadLocationException x) {
}
return null;
}
/**
* Returns the annotation hover for this hover manager.
*
* @return the annotation hover for this hover manager
* @since 2.1
*/
protected IAnnotationHover getAnnotationHover() {
return fAnnotationHover;
}
/**
* Returns the source viewer for this hover manager.
*
* @return the source viewer for this hover manager
* @since 2.1
*/
protected ISourceViewer getSourceViewer() {
return fSourceViewer;
}
/**
* Returns the vertical ruler info for this hover manager
*
* @return the vertical ruler info for this hover manager
* @since 2.1
*/
protected IVerticalRulerInfo getVerticalRulerInfo() {
return fVerticalRulerInfo;
}
/*
* @see org.eclipse.jface.text.AbstractInformationControlManager#computeSizeConstraints(org.eclipse.swt.widgets.Control, org.eclipse.jface.text.IInformationControl)
* @since 3.0
*/
protected Point computeSizeConstraints(Control subjectControl, IInformationControl informationControl) {
/* limit the hover to the size of the styled text's client area. */
StyledText styledText= fSourceViewer.getTextWidget();
if (styledText == null)
return super.computeSizeConstraints(subjectControl, informationControl);
Rectangle r= styledText.getClientArea();
if (r == null)
return super.computeSizeConstraints(subjectControl, informationControl);
return new Point(r.width, r.height);
}
/*
* @see org.eclipse.jface.text.AbstractInformationControlManager#computeInformationControlLocation(org.eclipse.swt.graphics.Rectangle, org.eclipse.swt.graphics.Point)
* @since 3.0
*/
protected Point computeInformationControlLocation(Rectangle subjectArea, Point controlSize) {
MouseEvent event= getHoverEvent();
IAnnotationHover hover= getHover(event);
if (hover instanceof IAnnotationHoverExtension) {
int startLine= fSourceViewer.getTopIndex();
int endLine= fSourceViewer.getBottomIndex();
ITextSelection lineRange= ((IAnnotationHoverExtension) hover).getLineRange(fSourceViewer, getHoverLine(event), startLine, endLine - startLine);
if (lineRange != null && !lineRange.isEmpty())
return computeViewerRange(lineRange);
}
return super.computeInformationControlLocation(subjectArea, controlSize);
}
/*
* @see org.eclipse.jface.text.AbstractInformationControlManager#computeLocation(org.eclipse.swt.graphics.Rectangle, org.eclipse.swt.graphics.Point, org.eclipse.jface.text.AbstractInformationControlManager.Anchor)
*/
protected Point computeLocation(Rectangle subjectArea, Point controlSize, Anchor anchor) {
MouseEvent event= getHoverEvent();
IAnnotationHover hover= getHover(event);
if (hover instanceof IAnnotationHoverExtension2) {
fAllowMouseExit= true;
Control subjectControl= getSubjectControl();
// return a location that just overlaps the annotation on the bar
if (anchor == AbstractInformationControlManager.ANCHOR_RIGHT)
return subjectControl.toDisplay(subjectArea.x - 4, subjectArea.y - 2);
else if (anchor == AbstractHoverInformationControlManager.ANCHOR_LEFT)
return subjectControl.toDisplay(subjectArea.x + subjectArea.width - controlSize.x + 4, subjectArea.y - 2);
}
fAllowMouseExit= false;
return super.computeLocation(subjectArea, controlSize, anchor);
}
/**
* Computes the hover location for the given line range.
*
* @param lineRange the first and last line covered by the hover, encoded as the <code>x</code> and <code>y</code> fields of a <code>Point</code>
* @return a <code>Point</code>containing the display coordinates of the hover location
* @since 3.0
*/
private Point computeViewerRange(ITextSelection lineRange) {
final int topLine= fSourceViewer.getTopIndex();
// compute pixel offset taking in account partially visible lines.
int lineDelta= lineRange.getStartLine() - topLine;
StyledText textWidget= fSourceViewer.getTextWidget();
int lineHeight= textWidget.getLineHeight();
// note that this works independently of the widget2model mapping, since we just get the
// pixels of the first partially visible line, if there is one.
int partial= (lineHeight - (textWidget.getTopPixel() % lineHeight)) % lineHeight;
int y= lineDelta * lineHeight + partial;
int x= 1; // avoids line overlay of the hover and the editor border.
return textWidget.toDisplay(x, y);
}
}