blob: 188d1e0d5578d5fbe34f5e6a3df34d43a62e969b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 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
*******************************************************************************/
package org.eclipse.ui.forms.widgets;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.swt.SWT;
import org.eclipse.swt.accessibility.ACC;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.graphics.GC;
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.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.events.IHyperlinkListener;
import org.eclipse.ui.internal.forms.widgets.FormsResources;
/**
* This is the base class for custom hyperlink widget. It is responsible for
* processing mouse and keyboard events, and converting them into unified
* hyperlink events. Subclasses are responsible for rendering the hyperlink in
* the client area.
*
* @since 3.0
*/
public abstract class AbstractHyperlink extends Canvas {
private boolean hasFocus;
boolean paintFocus=true;
/*
* Armed link is one that will activate on a mouse up event, i.e.
* it has received a mouse down and mouse still on top of it.
*/
private boolean armed;
private ListenerList listeners;
/**
* Amount of the margin width around the hyperlink (default is 1).
*/
protected int marginWidth = 1;
/**
* Amount of the margin height around the hyperlink (default is 1).
*/
protected int marginHeight = 1;
/**
* Creates a new hyperlink in the provided parent.
*
* @param parent
* the control parent
* @param style
* the widget style
*/
public AbstractHyperlink(Composite parent, int style) {
super(parent, style);
addListener(SWT.KeyDown, new Listener() {
public void handleEvent(Event e) {
if (e.character == '\r') {
handleActivate(e);
}
}
});
addPaintListener(new PaintListener() {
public void paintControl(PaintEvent e) {
paint(e);
}
});
addListener(SWT.Traverse, new Listener() {
public void handleEvent(Event e) {
switch (e.detail) {
case SWT.TRAVERSE_PAGE_NEXT:
case SWT.TRAVERSE_PAGE_PREVIOUS:
case SWT.TRAVERSE_ARROW_NEXT:
case SWT.TRAVERSE_ARROW_PREVIOUS:
case SWT.TRAVERSE_RETURN:
e.doit = false;
return;
}
e.doit = true;
}
});
Listener listener = new Listener() {
public void handleEvent(Event e) {
switch (e.type) {
case SWT.FocusIn:
hasFocus = true;
handleEnter(e);
break;
case SWT.FocusOut:
hasFocus = false;
handleExit(e);
break;
case SWT.DefaultSelection:
handleActivate(e);
break;
case SWT.MouseEnter:
handleEnter(e);
break;
case SWT.MouseExit:
handleExit(e);
break;
case SWT.MouseDown:
handleMouseDown(e);
break;
case SWT.MouseUp:
handleMouseUp(e);
break;
case SWT.MouseMove:
handleMouseMove(e);
break;
}
}
};
addListener(SWT.MouseEnter, listener);
addListener(SWT.MouseExit, listener);
addListener(SWT.MouseDown, listener);
addListener(SWT.MouseUp, listener);
addListener(SWT.MouseMove, listener);
addListener(SWT.FocusIn, listener);
addListener(SWT.FocusOut, listener);
setCursor(FormsResources.getHandCursor());
}
/**
* Adds the event listener to this hyperlink.
*
* @param listener
* the event listener to add
*/
public void addHyperlinkListener(IHyperlinkListener listener) {
if (listeners == null)
listeners = new ListenerList();
listeners.add(listener);
}
/**
* Removes the event listener from this hyperlink.
*
* @param listener
* the event listener to remove
*/
public void removeHyperlinkListener(IHyperlinkListener listener) {
if (listeners == null)
return;
listeners.remove(listener);
}
/**
* Returns the selection state of the control. When focus is gained, the
* state will be <samp>true </samp>; it will switch to <samp>false </samp>
* when the control looses focus.
*
* @return <code>true</code> if the widget has focus, <code>false</code>
* otherwise.
*/
public boolean getSelection() {
return hasFocus;
}
/**
* Called when hyperlink is entered. Subclasses that override this method
* must call 'super'.
*/
protected void handleEnter(Event e) {
redraw();
if (listeners == null)
return;
int size = listeners.size();
HyperlinkEvent he = new HyperlinkEvent(this, getHref(), getText(),
e.stateMask);
Object[] listenerList = listeners.getListeners();
for (int i = 0; i < size; i++) {
IHyperlinkListener listener = (IHyperlinkListener) listenerList[i];
listener.linkEntered(he);
}
}
/**
* Called when hyperlink is exited. Subclasses that override this method
* must call 'super'.
*/
protected void handleExit(Event e) {
// disarm the link; won't activate on mouseup
armed = false;
redraw();
if (listeners == null)
return;
int size = listeners.size();
HyperlinkEvent he = new HyperlinkEvent(this, getHref(), getText(),
e.stateMask);
Object[] listenerList = listeners.getListeners();
for (int i = 0; i < size; i++) {
IHyperlinkListener listener = (IHyperlinkListener) listenerList[i];
listener.linkExited(he);
}
}
/**
* Called when hyperlink has been activated. Subclasses that override this
* method must call 'super'.
*/
protected void handleActivate(Event e) {
// disarm link, back to normal state
armed = false;
getAccessible().setFocus(ACC.CHILDID_SELF);
if (listeners == null)
return;
int size = listeners.size();
setCursor(FormsResources.getBusyCursor());
HyperlinkEvent he = new HyperlinkEvent(this, getHref(), getText(),
e.stateMask);
Object[] listenerList = listeners.getListeners();
for (int i = 0; i < size; i++) {
IHyperlinkListener listener = (IHyperlinkListener) listenerList[i];
listener.linkActivated(he);
}
if (!isDisposed())
setCursor(FormsResources.getHandCursor());
}
/**
* Sets the object associated with this hyperlink. Concrete implementation
* of this class can use if to store text, URLs or model objects that need
* to be processed on hyperlink events.
*
* @param href
* the hyperlink object reference
*/
public void setHref(Object href) {
setData("href", href); //$NON-NLS-1$
}
/**
* Returns the object associated with this hyperlink.
*
* @see #setHref
* @return the hyperlink object reference
*/
public Object getHref() {
return getData("href"); //$NON-NLS-1$
}
/**
* Returns the textual representation of this hyperlink suitable for showing
* in tool tips or on the status line.
*
* @return the hyperlink text
*/
public String getText() {
return getToolTipText();
}
/**
* Paints the hyperlink as a reaction to the provided paint event.
*
* @param gc
* graphic context
*/
protected abstract void paintHyperlink(GC gc);
/**
* Paints the control as a reaction to the provided paint event.
*
* @param e
* the paint event
*/
protected void paint(PaintEvent e) {
GC gc = e.gc;
Rectangle clientArea = getClientArea();
if (clientArea.width == 0 || clientArea.height == 0)
return;
paintHyperlink(gc);
if (paintFocus && hasFocus) {
Rectangle carea = getClientArea();
gc.setForeground(getForeground());
gc.drawFocus(0, 0, carea.width, carea.height);
}
}
private void handleMouseDown(Event e) {
if (e.button != 1)
return;
// armed and ready to activate on mouseup
armed = true;
}
private void handleMouseUp(Event e) {
if (!armed || e.button != 1)
return;
Point size = getSize();
// Filter out mouse up events outside
// the link. This can happen when mouse is
// clicked, dragged outside the link, then
// released.
if (e.x < 0)
return;
if (e.y < 0)
return;
if (e.x >= size.x)
return;
if (e.y >= size.y)
return;
handleActivate(e);
}
private void handleMouseMove(Event e) {
// disarm link if we move out of bounds
if (armed) {
Point size = getSize();
armed = (e.x >= 0 && e.y >= 0 && e.x < size.x && e.y < size.y);
}
}
/*
* (non-Javadoc)
* @see org.eclipse.swt.widgets.Control#setEnabled(boolean)
*/
public void setEnabled (boolean enabled) {
super.setEnabled(enabled);
redraw();
}
}