package prefuse.controls;
import java.awt.Cursor;
import java.awt.event.MouseEvent;
import java.util.logging.Logger;
import prefuse.Display;
import prefuse.Visualization;
import prefuse.data.expression.Predicate;
import prefuse.data.tuple.TupleSet;
import prefuse.util.StringLib;
import prefuse.util.ui.UILib;
import prefuse.visual.VisualItem;
/**
* <p>Updates the contents of a TupleSet of focus items in response to mouse
* actions. For example, clicking a node or double-clicking a node could
* update its focus status. This Control supports monitoring a specified
* number of clicks to executing a focus change. By default a click pattern
* will cause a VisualItem to become the sole member of the focus group.
* Hold down the control key while clicking to add an item to a group
* without removing the current members.</p>
*
* <p>Updating a focus group does not necessarily cause
* the display to change. For this functionality, either register an action
* with this control, or register a TupleSetListener with the focus group.
* </p>
*
* @author <a href="http://jheer.org">jeffrey heer</a>
*/
public class FocusControl extends ControlAdapter {
private String group = Visualization.FOCUS_ITEMS;
protected String activity;
protected VisualItem curFocus;
protected int ccount;
protected int button = Control.LEFT_MOUSE_BUTTON;
protected Predicate filter = null;
/**
* Creates a new FocusControl that changes the focus to another item
* when that item is clicked once.
*/
public FocusControl() {
this(1);
}
/**
* Creates a new FocusControl that changes the focus to another item
* when that item is clicked once.
* @param focusGroup the name of the focus group to use
*/
public FocusControl(String focusGroup) {
this(1);
group = focusGroup;
}
/**
* Creates a new FocusControl that changes the focus when an item is
* clicked the specified number of times. A click value of zero indicates
* that the focus should be changed in response to mouse-over events.
* @param clicks the number of clicks needed to switch the focus.
*/
public FocusControl(int clicks) {
ccount = clicks;
}
/**
* Creates a new FocusControl that changes the focus when an item is
* clicked the specified number of times. A click value of zero indicates
* that the focus should be changed in response to mouse-over events.
* @param focusGroup the name of the focus group to use
* @param clicks the number of clicks needed to switch the focus.
*/
public FocusControl(String focusGroup, int clicks) {
ccount = clicks;
group = focusGroup;
}
/**
* Creates a new FocusControl that changes the focus when an item is
* clicked the specified number of times. A click value of zero indicates
* that the focus should be changed in response to mouse-over events.
* @param clicks the number of clicks needed to switch the focus.
* @param act an action run to upon focus change
*/
public FocusControl(int clicks, String act) {
ccount = clicks;
activity = act;
}
/**
* Creates a new FocusControl that changes the focus when an item is
* clicked the specified number of times. A click value of zero indicates
* that the focus should be changed in response to mouse-over events.
* @param focusGroup the name of the focus group to use
* @param clicks the number of clicks needed to switch the focus.
* @param act an action run to upon focus change
*/
public FocusControl(String focusGroup, int clicks, String act) {
ccount = clicks;
activity = act;
this.group = focusGroup;
}
// ------------------------------------------------------------------------
/**
* Set a filter for processing items by this focus control. Only items for
* which the predicate returns true (or doesn't throw an exception) will
* be considered by this control. A null value indicates that no filtering
* should be applied. That is, all items will be considered.
* @param p the filtering predicate to apply
*/
public void setFilter(Predicate p) {
this.filter = p;
}
/**
* Get the filter for processing items by this focus control. Only items
* for which the predicate returns true (or doesn't throw an exception)
* are considered by this control. A null value indicates that no
* filtering is applied.
* @return the filtering predicate
*/
public Predicate getFilter() {
return filter;
}
/**
* Perform a filtering check on the input item.
* @param item the item to check against the filter
* @return true if the item should be considered, false otherwise
*/
protected boolean filterCheck(VisualItem item) {
if ( filter == null )
return true;
try {
return filter.getBoolean(item);
} catch ( Exception e ) {
Logger.getLogger(getClass().getName()).warning(
e.getMessage() + "\n" + StringLib.getStackTrace(e));
return false;
}
}
// ------------------------------------------------------------------------
/**
* @see prefuse.controls.Control#itemEntered(prefuse.visual.VisualItem, java.awt.event.MouseEvent)
*/
public void itemEntered(VisualItem item, MouseEvent e) {
if ( !filterCheck(item) ) return;
Display d = (Display)e.getSource();
d.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
if ( ccount == 0 ) {
Visualization vis = item.getVisualization();
TupleSet ts = vis.getFocusGroup(group);
ts.setTuple(item);
curFocus = item;
runActivity(vis);
}
}
/**
* @see prefuse.controls.Control#itemExited(prefuse.visual.VisualItem, java.awt.event.MouseEvent)
*/
public void itemExited(VisualItem item, MouseEvent e) {
if ( !filterCheck(item) ) return;
Display d = (Display)e.getSource();
d.setCursor(Cursor.getDefaultCursor());
if ( ccount == 0 ) {
curFocus = null;
Visualization vis = item.getVisualization();
TupleSet ts = vis.getFocusGroup(group);
ts.removeTuple(item);
runActivity(vis);
}
}
/**
* @see prefuse.controls.Control#itemClicked(prefuse.visual.VisualItem, java.awt.event.MouseEvent)
*/
public void itemClicked(VisualItem item, MouseEvent e) {
if ( !filterCheck(item) ) return;
if ( UILib.isButtonPressed(e, button) &&
e.getClickCount() == ccount )
{
if ( item != curFocus ) {
Visualization vis = item.getVisualization();
TupleSet ts = vis.getFocusGroup(group);
boolean ctrl = e.isControlDown();
if ( !ctrl ) {
curFocus = item;
ts.setTuple(item);
} else if ( ts.containsTuple(item) ) {
ts.removeTuple(item);
} else {
ts.addTuple(item);
}
runActivity(vis);
} else if ( e.isControlDown() ) {
Visualization vis = item.getVisualization();
TupleSet ts = vis.getFocusGroup(group);
ts.removeTuple(item);
curFocus = null;
runActivity(vis);
}
}
}
private void runActivity(Visualization vis) {
if ( activity != null ) {
vis.run(activity);
}
}
} // end of class FocusControl