blob: bf000bd5e2c6688153786bee21ad1fd32a35ec07 [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;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
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.events.MouseTrackListener;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
/**
* An information control manager that shows information on mouse hover events.
* The mouse hover events are caught by registering a <code>MouseTrackListener</code>
* on the manager's subject control. The manager has by default an information control closer
* that closes the information control as soon as the mouse pointer leaves the subject area, the
* user presses a key, or the subject control is resized, moved, or deactivated.<p>
* When being activated by a mouse hover event, the manager disables itself, until the mouse
* leaves the subject area. Thus, the manager is usually still disabled, when the information control
* has already been closed by the closer.
*
* @see org.eclipse.swt.events.MouseTrackListener
* @since 2.0
*/
abstract public class AbstractHoverInformationControlManager extends AbstractInformationControlManager {
/**
* 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.
*/
class Closer extends MouseTrackAdapter
implements IInformationControlCloser, MouseListener, MouseMoveListener, ControlListener, KeyListener {
/** The closer's subject control */
private Control fSubjectControl;
/** The subject area */
private Rectangle fSubjectArea;
/** Indicates whether this closer is active */
private boolean fIsActive= false;
/**
* 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) {
}
/*
* @see IInformationControlCloser#start(Rectangle)
*/
public void start(Rectangle subjectArea) {
if (fIsActive) return;
fIsActive= true;
fSubjectArea= subjectArea;
if (fSubjectControl != null && !fSubjectControl.isDisposed()) {
fSubjectControl.addMouseListener(this);
fSubjectControl.addMouseMoveListener(this);
fSubjectControl.addMouseTrackListener(this);
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);
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) {
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);
}
}
/**
* To be installed on the manager's subject control. Serves two different purposes:
* <ul>
* <li> start function: initiates the computation of the information to be presented. This happens on
* receipt of a mouse hover event and disables the information control manager,
* <li> restart function: tracks mouse move and shell activation event to determine when the information
* control manager needs to be reactivated.
* </ul>
*/
class MouseTracker extends ShellAdapter implements MouseTrackListener, MouseMoveListener {
/** Margin around the original hover event location for coputing the hover area. */
private final static int EPSILON= 3;
/** The area in which the original hover event occurred. */
private Rectangle fHoverArea;
/** The area for which is computed information is valid. */
private Rectangle fSubjectArea;
/** The tracker's subject control. */
private Control fSubjectControl;
/** Indicates whether the tracker is in restart mode ignoring hover events. */
private boolean fIsInRestartMode= false;
/** Indicates whether the tracker is computing the information to be presented. */
private boolean fIsComputing= false;
/** Indicates whether the mouse has been lost. */
private boolean fMouseLostWhileComputing= false;
/** Indicates whether the subject control's shell has been deactivated. */
private boolean fShellDeactivatedWhileComputing= false;
/**
* Creates a new mouse tracker.
*/
public MouseTracker() {
}
/**
* Sets this mouse tracker's subject area, the area to be tracked in order
* to re-enable the information control manager.
*
* @param subjectArea the subject area
*/
public void setSubjectArea(Rectangle subjectArea) {
Assert.isNotNull(subjectArea);
fSubjectArea= subjectArea;
}
/**
* Starts this mouse tracker. The given control becomes this tracker's subject control.
* Installs itself as mouse track listener on the subject control.
*
* @param subjectControl the subject control
*/
public void start(Control subjectControl) {
fSubjectControl= subjectControl;
if (fSubjectControl != null && !fSubjectControl.isDisposed())
fSubjectControl.addMouseTrackListener(this);
fIsInRestartMode= false;
fIsComputing= false;
fMouseLostWhileComputing= false;
fShellDeactivatedWhileComputing= false;
}
/**
* Stops this mouse tracker. Removes itself as mouse track, mouse move, and
* shell listener from the subject control.
*/
public void stop() {
if (fSubjectControl != null && !fSubjectControl.isDisposed()) {
fSubjectControl.removeMouseTrackListener(this);
fSubjectControl.removeMouseMoveListener(this);
fSubjectControl.getShell().removeShellListener(this);
}
}
/**
* Initiates the computation of the information to be presented. Sets the initial hover area
* to a small rectangle around the hover event location. Adds mouse move and shell activation listeners
* to track whether the computed information is, after completion, useful for presentation and to
* implement the restart function.
*
* @param event the mouse hover event
*/
public void mouseHover(MouseEvent event) {
if (fIsComputing || fIsInRestartMode) return;
fIsInRestartMode= true;
fIsComputing= true;
fMouseLostWhileComputing= false;
fShellDeactivatedWhileComputing= false;
fHoverEventStateMask= event.stateMask;
fHoverEvent= event;
fHoverArea= new Rectangle(event.x - EPSILON, event.y - EPSILON, 2 * EPSILON, 2 * EPSILON );
if (fHoverArea.x < 0) fHoverArea.x= 0;
if (fHoverArea.y < 0) fHoverArea.y= 0;
setSubjectArea(fHoverArea);
if (fSubjectControl != null && !fSubjectControl.isDisposed()) {
fSubjectControl.addMouseMoveListener(this);
fSubjectControl.getShell().addShellListener(this);
}
doShowInformation();
}
/**
* Deactivates this tracker's restart function and enables the information control
* manager. Does not have any effect if the tracker is still executing the start function (i.e.
* computing the information to be presented.
*/
protected void deactivate() {
if (fIsComputing) return;
fIsInRestartMode= false;
if (fSubjectControl != null && !fSubjectControl.isDisposed()) {
fSubjectControl.removeMouseMoveListener(this);
fSubjectControl.getShell().removeShellListener(this);
}
}
/*
* @see MouseTrackListener#mouseEnter(MouseEvent)
*/
public void mouseEnter(MouseEvent e) {
}
/*
* @see MouseTrackListener#mouseExit(MouseEvent)
*/
public void mouseExit(MouseEvent e) {
fMouseLostWhileComputing= true;
deactivate();
}
/*
* @see MouseMoveListener#mouseMove(MouseEvent)
*/
public void mouseMove(MouseEvent event) {
if (!fSubjectArea.contains(event.x, event.y))
deactivate();
}
/*
* @see ShellListener#shellDeactivated(ShellEvent)
*/
public void shellDeactivated(ShellEvent e) {
fShellDeactivatedWhileComputing= true;
deactivate();
}
/*
* @see ShellListener#shellIconified(ShellEvent)
*/
public void shellIconified(ShellEvent e) {
fShellDeactivatedWhileComputing= true;
deactivate();
}
/**
* Tells this tracker that the start function processing has been completed.
*/
public void computationCompleted() {
fIsComputing= false;
fMouseLostWhileComputing= false;
fShellDeactivatedWhileComputing= false;
}
/**
* Determines whether the computed information is still useful for presentation.
* This is not the case, if the shell of the subject control has been deactivated, the mouse
* left the subject control, or the mouse moved on, so that it is no longer in the subject
* area.
*
* @return <code>true</code> if information is still useful for presentation, <code>false</code> otherwise
*/
public boolean isMouseLost() {
if (fMouseLostWhileComputing || fShellDeactivatedWhileComputing)
return true;
if (fSubjectControl != null && !fSubjectControl.isDisposed()) {
Display display= fSubjectControl.getDisplay();
Point p= display.getCursorLocation();
p= fSubjectControl.toControl(p);
if (!fSubjectArea.contains(p) && !fHoverArea.contains(p))
return true;
}
return false;
}
}
/** The mouse tracker on the subject control */
private MouseTracker fMouseTracker= new MouseTracker();
/** The remembered hover event */
private MouseEvent fHoverEvent= null;
/** The remembered hover event sate mask of the keyboard modifiers */
private int fHoverEventStateMask= 0;
/**
* Creates a new hover information control manager using the given information control creator.
* By default a <code>Closer</code> instance is set as this manager's closer.
*
* @param creator the information control creator
*/
protected AbstractHoverInformationControlManager(IInformationControlCreator creator) {
super(creator);
setCloser(new Closer());
}
/*
* @see AbstractInformationControlManager#presentInformation()
*/
protected void presentInformation() {
if (fMouseTracker == null) {
super.presentInformation();
return;
}
Rectangle area= getSubjectArea();
if (area != null)
fMouseTracker.setSubjectArea(area);
if (fMouseTracker.isMouseLost()) {
fMouseTracker.computationCompleted();
fMouseTracker.deactivate();
} else {
fMouseTracker.computationCompleted();
super.presentInformation();
}
}
/*
* @see AbstractInformationControlManager#setEnabled(boolean)
*/
public void setEnabled(boolean enabled) {
boolean was= isEnabled();
super.setEnabled(enabled);
boolean is= isEnabled();
if (was != is && fMouseTracker != null) {
if (is)
fMouseTracker.start(getSubjectControl());
else
fMouseTracker.stop();
}
}
/**
* Disposes this manager's information control.
*/
public void dispose() {
if (fMouseTracker != null) {
fMouseTracker.stop();
fMouseTracker.fSubjectControl= null;
fMouseTracker= null;
}
super.dispose();
}
/**
* Returns the location at which the most recent mouse hover event
* has been issued.
*
* @return the location of the most recent mouse hover event
*/
protected Point getHoverEventLocation() {
return fHoverEvent != null ? new Point(fHoverEvent.x, fHoverEvent.y) : new Point(-1, -1);
}
/**
* Returns the most recent mouse hover event.
*
* @return the most recent mouse hover event or <code>null</code>
* @since 3.0
*/
protected MouseEvent getHoverEvent() {
return fHoverEvent;
}
/**
* Returns the SWT event state of the most recent mouse hover event.
*
* @return the SWT event state of the most recent mouse hover event
*/
protected int getHoverEventStateMask() {
return fHoverEventStateMask;
}
}