/* ********************************************************************** **
** Copyright notice **
** **
** (c) 2005-2006 RSSOwl Development Team **
** http://www.rssowl.org/ **
** **
** 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.rssowl.org/legal/epl-v10.html **
** **
** A copy is found in the file epl-v10.html and important notices to the **
** license from the team is found in the textfile LICENSE.txt distributed **
** in this package. **
** **
** This copyright notice MUST APPEAR in all copies of the file! **
** **
** Contributors: **
** IBM Corporation - initial API and implementation **
** RSSOwl Development Team - additional API and implementation **
** **
** ********************************************************************** */
package org.rssowl.ui.internal.util;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.jface.util.IOpenEventListener;
import org.eclipse.jface.util.OpenStrategy;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
/**
* The <code>ViewerOpenStrategy</code> is a subset of the JFace
* <code>OpenStrategy</code> with some additional API.
* <p>
* TODO Remove me once Bug "[OpenModes] OpenStrategy not working properly for
* the Expand-Event" is fixed (164372).
* </p>
*
* @author bpasero
*/
public class ViewerOpenStrategy implements Listener {
/* Default behavior. Double click to open the item. */
private static final int DOUBLE_CLICK = 0;
/* Single click will open the item. */
private static final int SINGLE_CLICK = 1;
/* Hover will select the item. */
private static final int SELECT_ON_HOVER = 1 << 1;
/* Open item when using arrow keys */
private static final int ARROW_KEYS_OPEN = 1 << 2;
/* Time used in FILE_EXPLORER and ACTIVE_DESKTOP */
private static final int TIME = 500;
/* Listeners */
private ListenerList fOpenEventListeners = new ListenerList();
/* Event Fields */
private boolean fTimerStarted;
private Event fMouseUpEvent;
private Event fMouseMoveEvent;
private SelectionEvent fSelectionPendent;
private boolean fEnterKeyDown;
private SelectionEvent fDefaultSelectionPendent;
private boolean fArrowKeyDown;
private final int[] fCount = new int[1];
private long fStartTime = System.currentTimeMillis();
private boolean fCollapseOccurred;
private boolean fExpandOccurred;
private Display fDisplay;
/**
* @param control the control the strategy is applied to
*/
public ViewerOpenStrategy(Control control) {
fDisplay = control.getDisplay();
addListener(control);
}
/**
* Adds an IOpenEventListener to the collection of openEventListeners
*
* @param listener the listener to add
*/
public void addOpenListener(IOpenEventListener listener) {
fOpenEventListeners.add(listener);
}
/**
* Removes an IOpenEventListener to the collection of openEventListeners
*
* @param listener the listener to remove
*/
public void removeOpenListener(IOpenEventListener listener) {
fOpenEventListeners.remove(listener);
}
/**
* Sets the expandOccurred flag back to false.
*/
public void clearExpandFlag() {
fExpandOccurred = false;
}
/*
* Adds all needed listener to the control in order to implement
* single-click/double-click strategies.
*/
private void addListener(Control c) {
c.addListener(SWT.MouseEnter, this);
c.addListener(SWT.MouseExit, this);
c.addListener(SWT.MouseMove, this);
c.addListener(SWT.MouseDown, this);
c.addListener(SWT.MouseUp, this);
c.addListener(SWT.KeyDown, this);
c.addListener(SWT.Selection, this);
c.addListener(SWT.DefaultSelection, this);
c.addListener(SWT.Collapse, this);
c.addListener(SWT.Expand, this);
}
/*
* Fire the open event to all openEventListeners
*/
private void fireOpenEvent(SelectionEvent e) {
if (e.item != null && e.item.isDisposed())
return;
Object listeners[] = fOpenEventListeners.getListeners();
for (int i = 0; i < listeners.length; i++)
((IOpenEventListener) listeners[i]).handleOpen(e);
}
/*
* @see org.eclipse.swt.widgets.Listener#handleEvent(org.eclipse.swt.widgets.Event)
*/
public void handleEvent(final Event e) {
/* Default Selection */
if (e.type == SWT.DefaultSelection) {
SelectionEvent event = new SelectionEvent(e);
if (OpenStrategy.getOpenMethod() == DOUBLE_CLICK) {
fireOpenEvent(event);
} else {
if (fEnterKeyDown) {
fireOpenEvent(event);
fEnterKeyDown = false;
fDefaultSelectionPendent = null;
} else {
fDefaultSelectionPendent = event;
}
}
return;
}
switch (e.type) {
/* Mouse Enter / Exit */
case SWT.MouseEnter:
case SWT.MouseExit:
fMouseUpEvent = null;
fMouseMoveEvent = null;
fSelectionPendent = null;
break;
/* Mouse Move */
case SWT.MouseMove:
if ((OpenStrategy.getOpenMethod() & SELECT_ON_HOVER) == 0)
return;
if (e.stateMask != 0)
return;
if (e.widget.getDisplay().getFocusControl() != e.widget)
return;
fMouseMoveEvent = e;
final Runnable runnable[] = new Runnable[1];
runnable[0] = new Runnable() {
public void run() {
long time = System.currentTimeMillis();
int diff = (int) (time - fStartTime);
if (diff <= TIME) {
fDisplay.timerExec(diff * 2 / 3, runnable[0]);
} else {
fTimerStarted = false;
setSelection(fMouseMoveEvent);
}
}
};
fStartTime = System.currentTimeMillis();
if (!fTimerStarted) {
fTimerStarted = true;
fDisplay.timerExec(TIME * 2 / 3, runnable[0]);
}
break;
/* Mouse Down */
case SWT.MouseDown:
fMouseUpEvent = null;
fArrowKeyDown = false;
break;
/* TreeItem Expand */
case SWT.Expand:
fExpandOccurred = true;
break;
/* TreeItem Collapse */
case SWT.Collapse:
fCollapseOccurred = true;
break;
/* Mouse Up */
case SWT.MouseUp:
fMouseMoveEvent = null;
if ((e.button != 1) || ((e.stateMask & ~SWT.BUTTON1) != 0))
return;
if (fSelectionPendent != null && !(fCollapseOccurred || fExpandOccurred)) {
mouseSelectItem(fSelectionPendent);
} else {
fMouseUpEvent = e;
fCollapseOccurred = false;
fExpandOccurred = false;
}
break;
/* Key Down */
case SWT.KeyDown:
fMouseMoveEvent = null;
fMouseUpEvent = null;
fArrowKeyDown = ((e.keyCode == SWT.ARROW_UP) || (e.keyCode == SWT.ARROW_DOWN)) && e.stateMask == 0;
if (e.character == SWT.CR) {
if (fDefaultSelectionPendent != null) {
fireOpenEvent(new SelectionEvent(e));
fEnterKeyDown = false;
fDefaultSelectionPendent = null;
} else {
fEnterKeyDown = true;
}
}
break;
/* Selection */
case SWT.Selection:
SelectionEvent event = new SelectionEvent(e);
fMouseMoveEvent = null;
if (fMouseUpEvent != null)
mouseSelectItem(event);
else
fSelectionPendent = event;
fCount[0]++;
final int id = fCount[0];
if (fArrowKeyDown) {
if (id == fCount[0]) {
if ((OpenStrategy.getOpenMethod() & ARROW_KEYS_OPEN) != 0)
fireOpenEvent(new SelectionEvent(e));
}
}
break;
}
}
void mouseSelectItem(SelectionEvent e) {
if ((OpenStrategy.getOpenMethod() & SINGLE_CLICK) != 0)
fireOpenEvent(e);
fMouseUpEvent = null;
fSelectionPendent = null;
}
void setSelection(Event e) {
if (e == null)
return;
Widget w = e.widget;
if (w.isDisposed())
return;
SelectionEvent selEvent = new SelectionEvent(e);
/*
* ISSUE: May have to create a interface with method: setSelection(Point p)
* so that user's custom widgets can use this class. If we keep this option.
*/
if (w instanceof Tree) {
Tree tree = (Tree) w;
TreeItem item = tree.getItem(new Point(e.x, e.y));
if (item != null)
tree.setSelection(new TreeItem[] { item });
selEvent.item = item;
} else if (w instanceof Table) {
Table table = (Table) w;
TableItem item = table.getItem(new Point(e.x, e.y));
if (item != null)
table.setSelection(new TableItem[] { item });
selEvent.item = item;
} else
return;
if (selEvent.item == null)
return;
}
}