/*
* The University of Wales, Cardiff Triana Project Software License (Based
* on the Apache Software License Version 1.1)
*
* Copyright (c) 2007 University of Wales, Cardiff. All rights reserved.
*
* Redistribution and use of the software in source and binary forms, with
* or without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. The end-user documentation included with the redistribution, if any,
* must include the following acknowledgment: "This product includes
* software developed by the University of Wales, Cardiff for the Triana
* Project (http://www.trianacode.org)." Alternately, this
* acknowledgment may appear in the software itself, if and wherever
* such third-party acknowledgments normally appear.
*
* 4. The names "Triana" and "University of Wales, Cardiff" must not be
* used to endorse or promote products derived from this software
* without prior written permission. For written permission, please
* contact triana@trianacode.org.
*
* 5. Products derived from this software may not be called "Triana," nor
* may Triana appear in their name, without prior written permission of
* the University of Wales, Cardiff.
*
* 6. This software may not be sold, used or incorporated into any product
* for sale to third parties.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL UNIVERSITY OF WALES, CARDIFF OR ITS CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*
* ------------------------------------------------------------------------
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Triana Project. For more information on the
* Triana Project, please see. http://www.trianacode.org.
*
* This license is based on the BSD license as adopted by the Apache
* Foundation and is governed by the laws of England and Wales.
*
*/
package org.trianacode.gui.hci;
import org.trianacode.gui.hci.tools.TaskGraphViewManager;
import org.trianacode.gui.main.*;
import org.trianacode.taskgraph.Task;
import org.trianacode.taskgraph.TaskLayoutUtils;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
/**
* ToolMouseHandler handles all mouse events on task components and on the panel taskgraph window.
*
* @author Ian Taylor
* @version $Revision: 4048 $
*/
public class ToolMouseHandler implements
MouseListener, MouseMotionListener, ContainerListener {
private static final int DO_NOTHING_ON_RELEASE = 0;
private static final int SELECT_ONLY_ON_RELEASE = 1;
private static final int DESELECT_ON_RELEASE = 2;
private static final int SELECT_NONE_ON_RELEASE = 4;
/**
* the panel taskgraph component this mouse handler deals with
*/
private TaskGraphPanel panel;
/**
* the handler for node components
*/
private NodeMouseHandler nodehandler;
/**
* the point on the component where the drag started
*/
private Point dragpoint;
/**
* a flag indicating the action on mouse release
*/
private int onrelease = DO_NOTHING_ON_RELEASE;
public ToolMouseHandler(TaskGraphPanel panel) {
this.panel = panel;
this.nodehandler = new NodeMouseHandler(panel);
monitorComponent(panel.getContainer());
panel.getContainer().addMouseListener(this);
panel.getContainer().addMouseMotionListener(this);
}
/**
* Called when a new component is added, recursively sets the tool/node mouse handler for that component and any
* subcomponents.
*/
private void monitorComponent(Component comp) {
if (comp instanceof NodeComponent) {
comp.addMouseListener(nodehandler);
comp.addMouseMotionListener(nodehandler);
} else if ((comp instanceof TaskComponent) || (comp instanceof TaskSubComponent)) {
comp.addMouseListener(this);
comp.addMouseMotionListener(this);
}
if (comp instanceof Container) {
((Container) comp).addContainerListener(this);
Component[] comps = ((Container) comp).getComponents();
for (int count = 0; count < comps.length; count++) {
monitorComponent(comps[count]);
}
}
}
/**
* Called when a component is removed, recursively removes the tool/node mouse handler for that component and any
* subcomponents.
*/
private void unmonitorComponent(Component comp) {
if (comp instanceof NodeComponent) {
comp.removeMouseListener(nodehandler);
comp.removeMouseMotionListener(nodehandler);
} else if ((comp instanceof TaskComponent) || (comp instanceof TaskSubComponent)) {
comp.removeMouseListener(this);
comp.removeMouseMotionListener(this);
}
if (comp instanceof Container) {
((Container) comp).removeContainerListener(this);
Component[] comps = ((Container) comp).getComponents();
for (int count = 0; count < comps.length; count++) {
unmonitorComponent(comps[count]);
}
}
}
/**
* @return the source for the mouse event
*/
private TaskComponent getTaskComponent(MouseEvent event) {
if (event.getSource() instanceof TaskComponent) {
return (TaskComponent) event.getSource();
} else if (event.getSource() instanceof TaskSubComponent) {
return ((TaskSubComponent) event.getSource()).getMainTaskComponent();
} else {
return null;
}
}
/**
* Select all the task components in the taskgraph
*/
private void selectAll(boolean selected) {
TaskComponent[] comps = panel.getTaskComponents();
for (int count = 0; count < comps.length; count++) {
comps[count].setSelected(selected);
}
}
/**
* Select all the task components in the taskgraph
*/
private void selectOnly(TaskComponent comp) {
TaskComponent[] comps = panel.getTaskComponents();
for (int count = 0; count < comps.length; count++) {
comps[count].setSelected(comps[count] == comp);
}
}
/**
* Select all the task components in the taskgraph
*/
private TaskComponent[] getSelected() {
TaskComponent[] comps = panel.getTaskComponents();
ArrayList selected = new ArrayList();
for (int count = 0; count < comps.length; count++) {
if (comps[count].isSelected()) {
selected.add(comps[count]);
}
}
return (TaskComponent[]) selected.toArray(new TaskComponent[selected.size()]);
}
/**
* Moves the unit specified and any other that are selected to the new location, clipping if necessary.
*/
public void moveSelected(TaskComponent comp, Point newPosition) {
int xmove = newPosition.x - comp.getComponent().getLocation().x;
int ymove = newPosition.y - comp.getComponent().getLocation().y;
TaskComponent[] tools = getSelected();
Task[] tasks = new Task[tools.length];
for (int count = 0; count < tasks.length; count++) {
tasks[count] = tools[count].getTaskInterface();
}
TaskLayoutUtils.translate(tasks, xmove, ymove, panel.getLayoutDetails());
panel.getContainer().invalidate();
panel.getContainer().validate();
panel.getContainer().repaint();
}
/**
* Sets the origin and size of the temporary selection box
*/
public void setSelectionBox(int x, int y, int width, int height) {
if (panel instanceof SelectionBoxInterface) {
((SelectionBoxInterface) panel).setSelectionBox(new Point(x, y), new Dimension(width, height));
int originx = Math.min(x, x + width);
int originy = Math.min(y, y + height);
TaskComponent[] tasks = panel.getTaskComponents();
Rectangle selbounds = new Rectangle(originx, originy, Math.abs(width), Math.abs(height));
Rectangle bounds;
for (int count = 0; count < tasks.length; count++) {
bounds = tasks[count].getComponent().getBounds();
tasks[count].setSelected(selbounds.intersects(bounds));
}
}
}
/**
* Clears the temporary selection box so it is no longer shown
*/
public void clearSelectionBox() {
if (panel instanceof SelectionBoxInterface) {
((SelectionBoxInterface) panel).clearSelectionBox();
}
}
/**
* Invoked when the mouse button has been clicked (pressed and released) on a component.
*/
public void mouseClicked(MouseEvent event) {
TaskComponent task = getTaskComponent(event);
if (task != null) {
if (SwingUtilities.isLeftMouseButton(event) && (event.getClickCount() >= 2)) {
selectOnly(task);
handleDoubleClick(task.getTaskInterface(), event);
}
}
}
private void handleDoubleClick(Task task, MouseEvent mevt) {
Action action = TaskGraphViewManager.getTaskAction(task);
ActionEvent event = new ActionEvent(mevt.getSource(), ActionEvent.ACTION_PERFORMED,
(String) action.getValue(Action.ACTION_COMMAND_KEY), mevt.getWhen(), mevt.getModifiers());
action.actionPerformed(event);
}
/**
* Invoked when the mouse enters a component.
*/
public void mouseEntered(MouseEvent event) {
}
/**
* Invoked when the mouse exits a component.
*/
public void mouseExited(MouseEvent event) {
}
/**
* Invoked when a mouse button has been pressed on a component.
*/
public void mousePressed(MouseEvent event) {
TaskComponent task = getTaskComponent(event);
if (event.isPopupTrigger()) {
doPopupMenu(event, task);
} else if (task == null) {
onrelease = SELECT_NONE_ON_RELEASE;
} else {
if (event.isControlDown()) {
if (task.isSelected()) {
onrelease = DESELECT_ON_RELEASE;
} else {
onrelease = DO_NOTHING_ON_RELEASE;
}
} else if (SwingUtilities.isRightMouseButton(event)) {
if (!task.isSelected()) {
selectOnly(task);
}
onrelease = DO_NOTHING_ON_RELEASE;
} else {
if (task.isSelected()) {
onrelease = DO_NOTHING_ON_RELEASE;
//onrelease = SELECT_NONE_ON_RELEASE;
} else {
selectOnly(task);
}
}
task.setSelected(true);
}
dragpoint = event.getPoint();
}
private void doPopupMenu(MouseEvent event, TaskComponent task) {
JPopupMenu menu = null;
if (task == null) {
menu = TaskGraphViewManager.getOpenGroupPopup(panel.getTaskGraph());
} else if (task.isSelected() && (getSelected().length > 1)) {
TaskComponent[] comps = getSelected();
Task[] tasks = new Task[comps.length];
for (int count = 0; count < comps.length; count++) {
tasks[count] = comps[count].getTaskInterface();
}
menu = TaskGraphViewManager.getMultipleSelectionPopup(panel.getTaskGraph(), tasks);
} else {
selectOnly(task);
onrelease = DO_NOTHING_ON_RELEASE;
menu = TaskGraphViewManager.getWorkspacePopup(task.getTaskInterface());
}
if ((menu != null) && (event.getSource() instanceof Component)) {
menu.show((Component) event.getSource(), event.getX(), event.getY());
}
}
/**
* Invoked when a mouse button has been released on a component.
*/
public void mouseReleased(MouseEvent event) {
TaskComponent task = getTaskComponent(event);
if (event.isPopupTrigger()) {
doPopupMenu(event, task);
} else if ((onrelease == DESELECT_ON_RELEASE) && (task != null)) {
task.setSelected(false);
} else if ((onrelease == SELECT_ONLY_ON_RELEASE) && (task != null)) {
selectOnly(task);
} else if (onrelease == SELECT_NONE_ON_RELEASE) {
selectAll(false);
}
clearSelectionBox();
dragpoint = null;
onrelease = DO_NOTHING_ON_RELEASE;
}
/**
* Invoked when a mouse button is pressed on a component and then dragged. <code>MOUSE_DRAGGED</code> events will
* continue to be delivered to the component where the drag originated until the mouse button is released
* (regardless of whether the mouse position is within the bounds of the component).
* <p/>
* Due to platform-dependent Drag&Drop implementations, <code>MOUSE_DRAGGED</code> events may not be delivered
* during a native Drag&Drop operation.
*/
public void mouseDragged(MouseEvent event) {
TaskComponent task = getTaskComponent(event);
if (dragpoint != null) {
if (task == null) {
setSelectionBox(dragpoint.x, dragpoint.y, event.getPoint().x - dragpoint.x,
event.getPoint().y - dragpoint.y);
} else if (task != null) {
Point dest = SwingUtilities.convertPoint(task.getComponent(), event.getPoint(), panel.getContainer());
dest.translate(-dragpoint.x, -dragpoint.y);
if (panel.getContainer().contains(dest)) {
moveSelected(task, dest);
}
}
}
onrelease = DO_NOTHING_ON_RELEASE;
}
/**
* Invoked when the mouse cursor has been moved onto a component but no buttons have been pushed.
*/
public void mouseMoved(MouseEvent event) {
}
/**
* Invoked when a component has been added to the container.
*/
public void componentAdded(ContainerEvent event) {
monitorComponent(event.getChild());
}
/**
* Invoked when a component has been removed from the container.
*/
public void componentRemoved(ContainerEvent event) {
unmonitorComponent(event.getChild());
}
}