/*******************************************************************************
* CogTool Copyright Notice and Distribution Terms
* CogTool 1.3, Copyright (c) 2005-2013 Carnegie Mellon University
* This software is distributed under the terms of the FSF Lesser
* Gnu Public License (see LGPL.txt).
*
* CogTool is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* CogTool is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CogTool; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* CogTool makes use of several third-party components, with the
* following notices:
*
* Eclipse SWT version 3.448
* Eclipse GEF Draw2D version 3.2.1
*
* Unless otherwise indicated, all Content made available by the Eclipse
* Foundation is provided to you under the terms and conditions of the Eclipse
* Public License Version 1.0 ("EPL"). A copy of the EPL is provided with this
* Content and is also available at http://www.eclipse.org/legal/epl-v10.html.
*
* CLISP version 2.38
*
* Copyright (c) Sam Steingold, Bruno Haible 2001-2006
* This software is distributed under the terms of the FSF Gnu Public License.
* See COPYRIGHT file in clisp installation folder for more information.
*
* ACT-R 6.0
*
* Copyright (c) 1998-2007 Dan Bothell, Mike Byrne, Christian Lebiere &
* John R Anderson.
* This software is distributed under the terms of the FSF Lesser
* Gnu Public License (see LGPL.txt).
*
* Apache Jakarta Commons-Lang 2.1
*
* This product contains software developed by the Apache Software Foundation
* (http://www.apache.org/)
*
* jopt-simple version 1.0
*
* Copyright (c) 2004-2013 Paul R. Holser, Jr.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Mozilla XULRunner 1.9.0.5
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/.
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* The J2SE(TM) Java Runtime Environment version 5.0
*
* Copyright 2009 Sun Microsystems, Inc., 4150
* Network Circle, Santa Clara, California 95054, U.S.A. All
* rights reserved. U.S.
* See the LICENSE file in the jre folder for more information.
******************************************************************************/
package edu.cmu.cs.hcii.cogtool.ui;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.InputEvent;
import org.eclipse.draw2d.KeyEvent;
import org.eclipse.draw2d.PolygonDecoration;
import org.eclipse.draw2d.PolylineConnection;
import org.eclipse.draw2d.RectangleFigure;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.SWT;
import edu.cmu.cs.hcii.cogtool.CogToolLID;
import edu.cmu.cs.hcii.cogtool.model.DoublePoint;
import edu.cmu.cs.hcii.cogtool.model.Frame;
import edu.cmu.cs.hcii.cogtool.model.Transition;
import edu.cmu.cs.hcii.cogtool.model.TransitionSource;
import edu.cmu.cs.hcii.cogtool.uimodel.DesignEditorFrame;
import edu.cmu.cs.hcii.cogtool.uimodel.DesignEditorTransition;
import edu.cmu.cs.hcii.cogtool.uimodel.GraphicalChildWidget;
import edu.cmu.cs.hcii.cogtool.uimodel.GraphicalParentWidget;
import edu.cmu.cs.hcii.cogtool.uimodel.GraphicalSource;
import edu.cmu.cs.hcii.cogtool.uimodel.StructureViewUIModel;
import edu.cmu.cs.hcii.cogtool.util.OSUtils;
import edu.cmu.cs.hcii.cogtool.util.PrecisionUtilities;
import edu.cmu.cs.hcii.cogtool.util.WindowUtil;
import edu.cmu.cs.hcii.cogtool.view.InteractionFigure;
import edu.cmu.cs.hcii.cogtool.view.ResizeThumb;
import edu.cmu.cs.hcii.cogtool.view.StandardDrawingEditor;
/**
* This class is responsible for mouse operations for the
* Design/Structure Editor.
*
* States
*
* MouseUp:
* Double-click on a Frame's label causes an interactive rename.
* Double-click on any other part of a Frame opens editor on the Frame.
* Down on an unselected Frame deselects others and selects Frame;
* enters PotentialMovingFrame
* Down on a selected Frame;
* enters PotentialMovingSelection
* Shift-down on a Frame;
* enters PotentialTogglingSelection
* Down in space deselects others;
* enters PotentialSelectingFrames
* Shift-down in space;
* enters PotentialTogglingSelection
* Down on a Widget/Device remembers;
* enters PotentialCreatingTransition
* Down on a Transition remembers;
* enters PotentialSelectTransition
* Shift-down on a Transition remembers;
* enters PotentialToggleTransition
* Down on a Transition source/target handle remembers;
* enters PotentialChangeTarget/Source
* Ctrl-down on a selected Frame;
* enters PotentialDuplicatingFrame
*
* PotentialMovingFrame
* Drag moves selected Frame
* enters MovingFrames
* Up cancels;
* enters MouseUp
* ESC cancels;
* enters MouseUp
*
* PotentialMovingSelection
* Drag moves selected Frames
* enters MovingFrames
* Up deselects others and selects Frame
* enters MouseUp
* ESC cancels;
* enters MouseUp
*
* MovingFrames
* Drag (potentially) moves selected Frames
* Up (actually) moves selected Frames;
* enters MouseUp
* ESC cancels move;
* enters MouseUp
*
* PotentialTogglingSelection
* Drag creates dynamic toggling box;
* enters TogglingSelection
* Up cancels;
* enters MouseUp
* ESC cancels;
* enters MouseUp
*
* TogglingSelection
* Drag (potentially) toggles Frames
* Up (actually) toggles Frames;
* enters MouseUp
* ESC cancels toggling;
* enters MouseUp
*
* PotentialSelectingFrames
* Drag selects Frames
* enters SelectingFrames
* Up cancels;
* enters MouseUp
* ESC cancels;
* enters MouseUp
*
* SelectingFrames
* Drag (potentially) selects Frames
* Up (actually) selects Frames;
* enters MouseUp
* ESC cancels selecting;
* enters MouseUp
*
* PotentialCreatingTransition
* Drag creates dynamic Transition
* enters CreatingTransition
* Up cancels;
* enters MouseUp
* ESC cancels;
* enters MouseUp
*
* CreatingTransition
* Drag (potentially) creates Transition
* Up on a Frame creates Transition to that Frame from remembered Widget;
* enters MouseUp
* Up in space creates a new Frame and
* then creates a Transition to that Frame from remembered Widget;
* enters MouseUp
* ESC cancels creation;
* enters MouseUp
*
* PotentialSelectTransition
* Drag
* enters SelectingFrames/MovingFrames/CreatingTransition
* Up sets the remembered Transition as the only selected one
* enters MouseUp
*
* PotentialToggleTransition
* Drag
* enters TogglingSelection
* Up toggles the selection state of the remembered Transition
* enters MouseUp
*
* PotentialChangeTarget/Source
* Up sets the remembered's Transition as the only selected one
* enters MouseUp
* ESC cancels;
* enters MouseUp
* Drag
* enters ChangingTarget/Source
*
* ChangingTarget/Source
* Up attempts to change the transition's target/source
* enters MouseUp
* ESC cancels;
* enters MouseUp
* Drag
* (potentially) selects new target/source
*
* PotentialDuplicatingFrame
* Drag duplicates selected Frame and moves new Frames
* enters DuplicatingFrames
* Up cancels;
* enters MouseUp
* ESC cancels;
* enters MouseUp
*
* DuplicatingFrames
* Drag (potentially) moves new Frames
* Up (actually) moves new Frames;
* enters MouseUp
* ESC cancels move;
* enters MouseUp
*
*
* @author mlh
*/
public class DesignEditorMouseState extends Draw2DMouseState
{
// States
public static final int MouseUp = 0;
public static final int PotentialMovingFrame = 1;
public static final int PotentialMovingSelection = 2;
public static final int MovingFrames = 3;
public static final int PotentialTogglingSelection = 4;
public static final int TogglingSelection = 5;
public static final int PotentialSelectingFrames = 6;
public static final int SelectingFrames = 7;
public static final int PotentialCreatingTransition = 8;
public static final int CreatingTransition = 9;
public static final int PotentialSelectTransition = 10;
public static final int PotentialToggleTransition = 11;
public static final int PotentialChangeTarget = 12;
public static final int PotentialChangeSource = 13;
public static final int ChangingTarget = 14;
public static final int ChangingSource = 15;
public static final int PotentialDuplicatingFrame = 16;
public static final int DuplicatingFrames = 17;
protected DesignEditorUI ui;
protected DesignEditorSelectionState selection; // cached from ui
protected boolean stopMouseDragTimer = true;
protected MouseDragTimerTask mouseDragTask = new MouseDragTimerTask();
/**
* Root interaction figure for call to mouseDragged in MouseDragTimerTask
*/
protected IFigure rootFigure = null;
// Mouse state
protected boolean mouseDown = false;
protected int mouseDownX = 0;
protected int mouseDownY = 0;
protected int lastX = 0;
protected int lastY = 0;
protected int mouseDownState = 0;
protected double scaledMouseDownX = 0.0;
protected double scaledMouseDownY = 0.0;
protected int mouseListenerState = MouseUp;
// Holds rectangle figures for duplicating via ctrl + drag
protected List<RectangleFigure> duplicatingDynamic = null;
// State for creating a new transition
protected GraphicalSource<?> potentialTransitionSource = null;
protected PolylineConnection potentialTransition = null;
protected PolygonDecoration endPtDecoration;
// For highlighting potential target
protected DesignEditorFrame potentialTarget = null;
// State for selection area feedback
protected RectangleFigure dynamicSelectionArea = null;
// State for selection or changing target/source of a transition
protected DesignEditorTransition hitTransition = null;
// hysteresis for dragging
protected final int HYSTERESIS = 3;
protected class MouseDragTimerTask implements Runnable
{
protected int mouseX;
protected int mouseY;
protected int mouseButton;
protected int mouseState;
// Loop and fire the mouseDrag function updating the mouse
// cursor as needed.
// Stop, when mouse-up has been fired.
// May want to stop when loseFocus is fired as well.
public void run()
{
if (stopMouseDragTimer) {
// Do nothing.
}
else {
DesignEditorMouseState.this.mouseDragged(rootFigure,
mouseButton,
mouseX,
mouseY,
mouseState);
}
}
public void setNextMousePosition(int newMouseX,
int newMouseY,
int button,
int state)
{
mouseX = newMouseX;
mouseY = newMouseY;
mouseButton = button;
mouseState = state;
}
}
public DesignEditorMouseState(DesignEditorUI editorUI)
{
super(editorUI);
ui = editorUI;
selection = ui.selection;
}
protected void setMouseDown(int x, int y, int state)
{
mouseDown = true;
mouseDownX = x;
mouseDownY = y;
mouseDownState = state;
double zoom = ui.getZoom();
scaledMouseDownX = x / zoom;
scaledMouseDownY = y / zoom;
}
protected void ensureDynamicTransition(int startX, int startY)
{
if (potentialTransition == null) {
potentialTransition = new PolylineConnection();
endPtDecoration = new PolygonDecoration();
endPtDecoration.setTemplate(PolygonDecoration.TRIANGLE_TIP);
potentialTransition.setStart(new Point(startX, startY));
ui.getViewEditor().addInteractionFigure(potentialTransition);
}
else if (! potentialTransition.isVisible()) {
potentialTransition.setVisible(true);
potentialTransition.setStart(new Point(startX, startY));
}
}
protected void dynamicTransitionTarget(int x, int y)
{
potentialTransition.setTargetDecoration(null);
potentialTransition.setTargetDecoration(endPtDecoration);
potentialTransition.setEnd(new Point(x, y));
ui.performRepaintUpdates();
}
protected void dynamicTransitionSource(int x, int y)
{
potentialTransition.setStart(new Point(x, y));
potentialTransition.setTargetDecoration(null);
potentialTransition.setTargetDecoration(endPtDecoration);
ui.performRepaintUpdates();
}
protected void dynamicTransition(int x, int y)
{
if (potentialTransitionSource != null) {
ensureDynamicTransition(mouseDownX, mouseDownY);
dynamicTransitionTarget(x, y);
}
}
protected void stopDynamicTransition()
{
if (potentialTransition != null) {
potentialTransition.setVisible(false);
}
}
protected void updateDynamicMove(int x, int y, boolean updateModel)
{
double zoom = ui.getZoom();
double dx = (x / zoom) - scaledMouseDownX;
double dy = (y / zoom) - scaledMouseDownY;
Iterator<DesignEditorFrame> frameFigs =
selection.getSelectedFrameFigures();
while (frameFigs.hasNext()) {
DesignEditorFrame frameFig = frameFigs.next();
DoublePoint frameOrigin = frameFig.getFrame().getFrameOrigin();
double newX = frameOrigin.x + dx;
double newY = frameOrigin.y + dy;
// Prevent the frame from moving past 0,0
if (newX < 0) {
newX = 0;
}
if (newY < 0 ) {
newY = 0;
}
ui.setFigureOrigin(frameFig, newX, newY);
}
ui.performRepaintUpdates();
if (updateModel) {
ui.performAction(DesignEditorLID.MoveFrames,
new DesignEditorUI.MoveParameters(dx, dy, selection));
}
}
protected void updateDynamicDuplicate(int x, int y)
{
double dx = x - lastX;
double dy = y - lastY;
Iterator<RectangleFigure> frameFigs = duplicatingDynamic.iterator();
while (frameFigs.hasNext()) {
RectangleFigure frameFig = frameFigs.next();
Point frameOrigin = frameFig.getLocation();
frameOrigin.x += dx;
frameOrigin.y += dy;
frameFig.setLocation(frameOrigin);
}
ui.performRepaintUpdates();
}
protected void updateDynamicSelectionArea(int x, int y)
{
if (! dynamicSelectionArea.isVisible()) {
dynamicSelectionArea.setVisible(true);
}
Rectangle bounds = new Rectangle();
if (x < mouseDownX) {
bounds.x = (x < 0) ? 0 : x;
bounds.width = mouseDownX - bounds.x;
}
else {
bounds.x = mouseDownX;
bounds.width = x - mouseDownX;
}
if (y < mouseDownY) {
bounds.y = (y < 0) ? 0 : y;
bounds.height = mouseDownY - bounds.y;
}
else {
bounds.y = mouseDownY;
bounds.height = y - mouseDownY;
}
dynamicSelectionArea.setBounds(bounds);
ui.performRepaintUpdates();
}
protected void setMouseState(int newState)
{
ui.getViewEditor().captureMouseEvents(newState != MouseUp);
mouseListenerState = newState;
}
protected int getMouseState()
{
return mouseListenerState;
}
@Override
protected boolean dealWithMouseDoubleClicked(IFigure figure,
int button,
int x,
int y,
int state)
{
boolean goForward =
super.dealWithMouseDoubleClicked(figure, button, x, y, state);
ui.confirmRenameFrame();
if (goForward) {
IFigure target =
ui.structureView.getFigureAtXY(x, y,
StructureViewUIModel.FRAME_LABEL);
if (target instanceof DesignEditorFrame.FrameLabel) {
DesignEditorFrame.FrameLabel frameLabel =
(DesignEditorFrame.FrameLabel) target;
ui.initiateFrameRename(frameLabel.getFrameFigure());
}
else if (target instanceof DesignEditorFrame) {
Frame frame = ((DesignEditorFrame) target).getFrame();
ui.performAction(DesignEditorLID.EditFrame,
new SingleFrameSelection(frame));
}
}
setMouseState(MouseUp);
cleanup();
return goForward;
} // dealWithMouseDoubleClicked
protected void handleMousePressed(int filter)
{
if (filter == StructureViewUIModel.NO_LABEL) {
ResizeThumb thumb =
ui.getResizeAtXY(mouseDownX, mouseDownY);
if (thumb != null) {
hitTransition =
(DesignEditorTransition) thumb.getData();
selection.setSelectedTransition(hitTransition);
if (thumb.thumbType == DesignEditorUI.SOURCE) {
setMouseState(PotentialChangeSource);
}
else {
setMouseState(PotentialChangeTarget);
}
return;
}
}
IFigure target = ui.structureView.getFigureAtXY(mouseDownX,
mouseDownY,
filter);
if (target instanceof DesignEditorTransition) {
hitTransition = (DesignEditorTransition) target;
if ((mouseDownState & InputEvent.SHIFT) != 0) {
setMouseState(PotentialToggleTransition);
}
else {
setMouseState(PotentialSelectTransition);
}
}
else if (target instanceof GraphicalSource<?>) {
potentialTransitionSource = (GraphicalSource<?>) target;
setMouseState(PotentialCreatingTransition);
// drag creates arrow
}
else if (target instanceof DesignEditorFrame) {
DesignEditorFrame frameFigure = (DesignEditorFrame) target;
Frame frame = frameFigure.getFrame();
if ((mouseDownState & InputEvent.SHIFT) != 0) {
setMouseState(PotentialTogglingSelection);
}
else if ((mouseDownState & platformDuplicateModifierKey()) != 0)
{
if (! selection.isFrameSelected(frame)) {
selection.setSelectedFrame(frameFigure);
}
setMouseState(PotentialDuplicatingFrame);
}
else {
if (selection.isFrameSelected(frame)) {
setMouseState(PotentialMovingSelection);
}
else {
selection.setSelectedFrame(frameFigure);
setMouseState(PotentialMovingFrame);
}
}
}
else { // out in space
if ((mouseDownState & InputEvent.SHIFT) != 0) {
setMouseState(PotentialTogglingSelection);
}
else {
selection.deselectAll();
setMouseState(PotentialSelectingFrames);
}
}
} // handleMousePressed
protected void showSelectionArea(RectangleFigure rf)
{
rf.setOutline(true);
rf.setFill(false);
ui.getViewEditor().addInteractionFigure(rf);
}
@Override
protected boolean dealWithMousePressed(IFigure figure,
int button, int x, int y, int state)
{
boolean goForward =
super.dealWithMousePressed(figure, button, x, y, state);
ui.confirmRenameFrame();
if (goForward) {
if (dynamicSelectionArea == null) {
dynamicSelectionArea = new RectangleFigure();
if (OSUtils.MACOSX) {
dynamicSelectionArea.setLineStyle(SWT.LINE_DOT);
}
else {
dynamicSelectionArea.setLineStyle(SWT.LINE_DASH);
}
dynamicSelectionArea.setVisible(false);
showSelectionArea(dynamicSelectionArea);
}
setMouseDown(x, y, state);
switch (getMouseState()) {
case MouseUp: {
handleMousePressed(StructureViewUIModel.NO_LABEL);
break;
} // case MouseUp:
default: {
// TODO: Throw exception?
break;
}
} // switch (getMouseState())
}
cleanup();
return goForward;
} // dealWithMousePressed
protected void clearRectFigures()
{
for (int i = 0; i < duplicatingDynamic.size(); i++) {
RectangleFigure rf = duplicatingDynamic.get(i);
ui.getViewEditor().removeInteractionFigure(rf);
}
duplicatingDynamic.clear();
}
@Override
protected boolean dealWithMouseReleased(IFigure figure,
int button, int x, int y, int state)
{
boolean goForward =
super.dealWithMouseReleased(figure, button, x, y, state);
// Clear any mouse drag timer, that may be running
stopMouseDragTimer = true;
if (goForward) {
// This must be done before the hideAllMenuItems!
IFigure figureAtXY =
ui.structureView.getFigureAtXY(mouseDownX,
mouseDownY,
StructureViewUIModel.SOURCE_ONLY);
// Record the mouse down position.
// Convert mouse coordinates into model coordinates
double zoom = ui.getZoom();
double scaledMouseUpX = x / zoom;
double scaledMouseUpY = y / zoom;
DesignEditorFrame frameAtXY =
ui.structureView.getFrameAtXY(mouseDownX,
mouseDownY);
if (frameAtXY != null) {
frameAtXY.hideAllChildren();
ui.resetHiddenTransitionSources();
}
int leftX =
PrecisionUtilities.round((scaledMouseUpX < scaledMouseDownX)
? scaledMouseUpX
: scaledMouseDownX);
int topY =
PrecisionUtilities.round((scaledMouseUpY < scaledMouseDownY)
? scaledMouseUpY
: scaledMouseDownY);
int width =
PrecisionUtilities.round((scaledMouseUpX < scaledMouseDownX)
? (scaledMouseDownX - scaledMouseUpX + 1)
: (scaledMouseUpX - scaledMouseDownX + 1));
int height =
PrecisionUtilities.round((scaledMouseUpY < scaledMouseDownY)
? (scaledMouseDownY - scaledMouseUpY + 1)
: (scaledMouseUpY - scaledMouseDownY + 1));
Rectangle selectionBox = new Rectangle(leftX, topY, width, height);
switch (getMouseState()) {
case PotentialMovingFrame: {
// Nothing is really required, since the selection for this was
// set on mouse down.
break;
}
case PotentialMovingSelection: {
DesignEditorFrame frameFig =
ui.structureView.getFrameAtXY(mouseDownX,
mouseDownY);
selection.setSelectedFrame(frameFig);
break;
}
case PotentialSelectingFrames: {
break;
}
case PotentialCreatingTransition: {
if (figureAtXY instanceof GraphicalParentWidget<?, ?>) {
GraphicalParentWidget<?, ?> parentToOpen =
(GraphicalParentWidget<?, ?>) figureAtXY;
if (! parentToOpen.canHaveChildren()) {
parentToOpen =
((GraphicalChildWidget<?, ?>) parentToOpen).getParentFigure();
}
parentToOpen.openChildren();
ui.resetHiddenTransitionSources();
}
// else, nothing to do
break;
}
case MovingFrames: {
updateDynamicMove(x, y, true);
break;
}
case PotentialTogglingSelection: {
dynamicSelectionArea.setVisible(false);
DesignEditorFrame frameFig =
ui.structureView.getFrameAtXY(mouseDownX,
mouseDownY);
if (frameFig != null) {
if (selection.isFrameSelected(frameFig.getFrame())) {
selection.deselectFrame(frameFig);
}
else {
if (selection.getSelectedTransitionCount() > 0) {
selection.deselectAll();
}
selection.selectFrame(frameFig);
}
}
break;
}
case TogglingSelection: {
dynamicSelectionArea.setVisible(false);
Iterator<DesignEditorFrame> frameFigures =
ui.structureView.getAllFrameFigures();
while (frameFigures.hasNext()) {
DesignEditorFrame frameFig = frameFigures.next();
Frame frame = frameFig.getFrame();
if (frameFig.intersects(selectionBox)) {
if (selection.isFrameSelected(frame)) {
selection.deselectFrame(frameFig);
}
else {
if (selection.getSelectedTransitionCount() > 0)
{
selection.deselectAll();
}
selection.selectFrame(frameFig);
}
}
}
break;
}
case SelectingFrames: {
dynamicSelectionArea.setVisible(false);
Iterator<DesignEditorFrame> frameFigures =
ui.structureView.getAllFrameFigures();
while (frameFigures.hasNext()) {
DesignEditorFrame frameFig = frameFigures.next();
if (frameFig.intersects(selectionBox)) {
selection.selectFrame(frameFig);
}
}
break;
}
case CreatingTransition: {
stopDynamicTransition();
if (potentialTarget != null) {
potentialTarget.dynamicHighlight(false);
potentialTarget = null;
}
if (potentialTransitionSource != null) {
DesignEditorFrame targetFigure =
ui.structureView.getFrameAtXY(x, y);
TransitionSource source =
potentialTransitionSource.getModel();
Frame target =
(targetFigure != null) ? targetFigure.getFrame()
: (Frame) null;
// Convert mouse coordinates into model coordinates
DesignEditorUI.NewTransitionParameters prms =
new DesignEditorUI.NewTransitionParameters(source, target,
scaledMouseUpX,
scaledMouseUpY);
ui.performAction(DesignEditorLID.NewTransition, prms);
}
break;
}
case PotentialSelectTransition:
case PotentialChangeTarget:
case PotentialChangeSource: {
selection.setSelectedTransition(hitTransition);
Transition transition = hitTransition.getTransition();
ui.getInteraction().setTransitionStatusMessage(transition);
hitTransition = null;
break;
}
case PotentialToggleTransition: {
Transition transition = hitTransition.getTransition();
if (selection.isTransitionSelected(transition)) {
selection.deselectTransition(hitTransition);
}
else {
if (selection.getSelectedFrameCount() > 0) {
selection.deselectAll();
}
selection.selectTransition(hitTransition);
}
hitTransition = null;
break;
}
case ChangingTarget: {
stopDynamicTransition();
if (potentialTarget != null) {
potentialTarget.dynamicHighlight(false);
potentialTarget = null;
}
DesignEditorFrame newTargetFigure =
ui.structureView.getFrameAtXY(x, y);
if (newTargetFigure != null) {
Transition transition =
hitTransition.getTransition();
DesignEditorUI.ChangeTargetParameters prms =
new DesignEditorUI.ChangeTargetParameters(transition,
newTargetFigure.getFrame());
ui.performAction(DesignEditorLID.ChangeTarget, prms);
ui.getInteraction().setTransitionStatusMessage(transition);
}
hitTransition.setVisible(true);
break;
}
case ChangingSource: {
stopDynamicTransition();
InteractionFigure drawLayer =
ui.getViewEditor().getInteractionFigure();
drawLayer.setCursor(WindowUtil.getCursor(WindowUtil.SELECT_CURSOR));
GraphicalSource<?> newSourceFigure =
ui.structureView.getSourceAtXY(x, y);
hitTransition.setVisible(true);
if (newSourceFigure != null) {
Transition transition =
hitTransition.getTransition();
DesignEditorUI.ChangeSourceParameters prms =
new DesignEditorUI.ChangeSourceParameters(transition,
newSourceFigure.getModel());
ui.performAction(DesignEditorLID.ChangeSource,
prms);
ui.getInteraction().setTransitionStatusMessage(transition);
}
hitTransition = null;
break;
}
case PotentialDuplicatingFrame: {
DesignEditorFrame frameFig =
ui.structureView.getFrameAtXY(mouseDownX,
mouseDownY);
selection.setSelectedFrame(frameFig);
break;
}
case DuplicatingFrames: {
double dx = scaledMouseUpX - scaledMouseDownX;
double dy = scaledMouseUpY - scaledMouseDownY;
// Remove all the rectangle figures from the display, clear the list
clearRectFigures();
DesignEditorUI.DuplicateParameters prm =
new DesignEditorUI.DuplicateParameters(dx, dy,
selection);
ui.performAction(DesignEditorLID.DuplicateFrame, prm);
break;
}
}
}
setMouseState(MouseUp);
mouseDown = false;
cleanup();
return goForward;
} // dealWithMouseReleased
protected boolean withinHysteresis(int eventX, int eventY)
{
return (Math.abs(eventX - mouseDownX) < HYSTERESIS) &&
(Math.abs(eventY - mouseDownY) < HYSTERESIS);
}
/**
* Only needed below in mouseDragged!
*/
protected org.eclipse.swt.graphics.Point updateDelta =
new org.eclipse.swt.graphics.Point(0, 0);
@Override
protected boolean dealWithMouseDragged(IFigure figure,
int button,
int eventX,
int eventY,
int state)
{
boolean goForward =
super.dealWithMouseDragged(figure, button, eventX, eventY, state);
if (goForward && mouseDown) {
StandardDrawingEditor editor = ui.getViewEditor();
// Update VIEW to ensure ME point is visible.
// If outside of the visible canvas, up-click should cancel!
stopMouseDragTimer = true;
stopDynamic =
editor.movePointNearEdge(eventX, eventY, updateDelta);
switch (getMouseState()) {
case PotentialMovingFrame:
case PotentialMovingSelection: {
if (withinHysteresis(eventX, eventY)) {
break;
}
setMouseState(MovingFrames);
// fall through!
}
case MovingFrames: {
updateDynamicMove(eventX, eventY, false);
break;
}
case PotentialTogglingSelection: {
if (withinHysteresis(eventX, eventY)) {
break;
}
setMouseState(TogglingSelection);
// fall through!
}
case TogglingSelection: {
updateDynamicSelectionArea(eventX, eventY);
break;
}
case PotentialSelectingFrames: {
if (withinHysteresis(eventX, eventY)) {
break;
}
setMouseState(SelectingFrames);
// fall through!
}
case SelectingFrames: {
updateDynamicSelectionArea(eventX, eventY);
break;
}
case PotentialCreatingTransition: {
if (withinHysteresis(eventX, eventY)) {
break;
}
// Do not allow transitions from menu headers or submenus
IFigure target =
ui.structureView.getFigureAtXY(mouseDownX,
mouseDownY,
StructureViewUIModel.SOURCE_ONLY);
if ((target instanceof GraphicalParentWidget<?, ?>) &&
(((GraphicalParentWidget<?, ?>) target).canHaveChildren()))
{
break;
}
setMouseState(CreatingTransition);
// Ensures proper default action
selection.deselectAll();
// fall through!
}
case CreatingTransition: {
dynamicTransition(eventX, eventY);
DesignEditorFrame targetFigure =
ui.structureView.getFrameAtXY(eventX, eventY);
if (targetFigure != potentialTarget) {
if (potentialTarget != null) {
potentialTarget.dynamicHighlight(false);
}
potentialTarget = targetFigure;
if (potentialTarget != null) {
potentialTarget.dynamicHighlight(true);
}
}
break;
}
case PotentialSelectTransition: {
if (withinHysteresis(eventX, eventY)) {
break;
}
handleMousePressed(StructureViewUIModel.NO_TRANSITION);
switch (getMouseState()) {
case PotentialMovingFrame: {
setMouseState(MovingFrames);
updateDynamicMove(eventX, eventY, false);
break;
}
case PotentialSelectingFrames: {
setMouseState(SelectingFrames);
updateDynamicSelectionArea(eventX, eventY);
break;
}
case PotentialCreatingTransition: {
setMouseState(CreatingTransition);
dynamicTransition(eventX, eventY);
break;
}
}
break;
}
case PotentialToggleTransition: {
if (withinHysteresis(eventX, eventY)) {
break;
}
handleMousePressed(StructureViewUIModel.NO_TRANSITION);
setMouseState(TogglingSelection);
updateDynamicSelectionArea(eventX, eventY);
break;
}
case PotentialChangeTarget: {
if (withinHysteresis(eventX, eventY)) {
break;
}
setMouseState(ChangingTarget);
Point start = hitTransition.getStart();
hitTransition.setVisible(false);
double zoom = ui.getZoom();
double startX = start.x * zoom;
double startY = start.y * zoom;
ensureDynamicTransition(PrecisionUtilities.round(startX),
PrecisionUtilities.round(startY));
// fall through!
}
case ChangingTarget: {
dynamicTransitionTarget(eventX, eventY);
DesignEditorFrame targetFigure =
ui.structureView.getFrameAtXY(eventX, eventY);
if (targetFigure != potentialTarget) {
if (potentialTarget != null) {
potentialTarget.dynamicHighlight(false);
}
potentialTarget = targetFigure;
if (potentialTarget != null) {
potentialTarget.dynamicHighlight(true);
}
}
break;
}
case PotentialChangeSource: {
if (withinHysteresis(eventX, eventY)) {
break;
}
setMouseState(ChangingSource);
hitTransition.setVisible(false);
ensureDynamicTransition(mouseDownX, mouseDownY);
Point endPt = hitTransition.getEnd();
double zoom = ui.getZoom();
double endX = endPt.x * zoom;
double endY = endPt.y * zoom;
dynamicTransitionTarget(PrecisionUtilities.round(endX),
PrecisionUtilities.round(endY));
break;
}
case ChangingSource: {
dynamicTransitionSource(eventX, eventY);
GraphicalSource<?> sourceFigure =
ui.structureView.getSourceAtXY(eventX, eventY);
InteractionFigure drawLayer =
ui.getViewEditor().getInteractionFigure();
if (sourceFigure != null) {
drawLayer.setCursor(WindowUtil.getCursor(WindowUtil.DRAW_CURSOR));
}
else {
drawLayer.setCursor(WindowUtil.getCursor(WindowUtil.SELECT_CURSOR));
}
break;
}
case PotentialDuplicatingFrame: {
if (withinHysteresis(eventX, eventY)) {
break;
}
duplicatingDynamic = new ArrayList<RectangleFigure>();
//populate this from selection state
double zoom = ui.getZoom();
Iterator<DesignEditorFrame> selectedFrames =
selection.getSelectedFrameFigures();
while (selectedFrames.hasNext()) {
RectangleFigure rect = new RectangleFigure();
DesignEditorFrame currFrame = selectedFrames.next();
//set size of new rectangle object
Rectangle frameSize = currFrame.getBounds();
frameSize.height = PrecisionUtilities.round(frameSize.height / zoom);
frameSize.width = PrecisionUtilities.round(frameSize.width / zoom);
frameSize.x = PrecisionUtilities.round(frameSize.x * zoom);
frameSize.y = PrecisionUtilities.round(frameSize.y * zoom);
rect.setBounds(frameSize);
//add new rectangle object to the array list
duplicatingDynamic.add(rect);
//display the new rectangle object
showSelectionArea(rect);
}
setMouseState(DuplicatingFrames);
// fall through!
}
case DuplicatingFrames: {
updateDynamicDuplicate(eventX, eventY);
break;
}
}
// Repeating timer for causing events to repeat.
if ((updateDelta.x != 0) || (updateDelta.y != 0)) {
if (rootFigure == null) {
rootFigure =
ui.getViewEditor().getInteractionFigure();
}
stopMouseDragTimer = false;
// Determine the new point the mouse "moved" too.
mouseDragTask.setNextMousePosition(eventX + updateDelta.x,
eventY + updateDelta.y,
button,
state);
// Queue the event for 0.1 sec.
WindowUtil.GLOBAL_DISPLAY.timerExec(100, mouseDragTask);
}
}
lastX = eventX;
lastY = eventY;
cleanup();
return goForward;
} // mouseDragged
@Override
protected boolean dealWithMouseExited(IFigure figure, int x, int y, int state)
{
boolean goForward = super.dealWithMouseExited(figure, x, y, state);
// Stop the MouseDragTimerTask
stopMouseDragTimer = true;
return goForward;
}
@Override
protected void cancelDynamicOperation()
{
super.cancelDynamicOperation();
switch (getMouseState()) {
case MovingFrames: {
updateDynamicMove(mouseDownX, mouseDownY, false);
break;
}
case DuplicatingFrames: {
clearRectFigures();
break;
}
case TogglingSelection:
case SelectingFrames: {
dynamicSelectionArea.setVisible(false);
break;
}
case CreatingTransition: {
stopDynamicTransition();
if (potentialTarget != null) {
potentialTarget.dynamicHighlight(false);
potentialTarget = null;
}
break;
}
case ChangingTarget:
case ChangingSource: {
stopDynamicTransition();
if (potentialTarget != null) {
potentialTarget.dynamicHighlight(false);
potentialTarget = null;
}
hitTransition.setVisible(true);
break;
}
}
setMouseState(MouseUp);
ui.resetVisibleArea();
} // cancelDynamicOperation
/**
* Detects key presses in order to move widgets
* @param ke contains details of the key press
*/
@Override
protected boolean dealWithKeyPressed(KeyEvent ke)
{
if (! super.dealWithKeyPressed(ke)) {
return false;
}
switch (ke.keycode) {
case '-': {
if (getMouseState() == MouseUp) {
if ((ke.getState() & CTRL_SHIFT_MASK) == CTRL_SHIFT_MASK) {
ui.performAction(CogToolLID.ZoomOut);
}
}
break;
}
case '=': {
if (getMouseState() == MouseUp) {
if ((ke.getState() & CTRL_SHIFT_MASK) == CTRL_SHIFT_MASK) {
ui.performAction(CogToolLID.ZoomIn);
}
}
break;
}
case SWT.ESC: {
cancelDynamicOperation();
break;
}
case SWT.ARROW_UP: {
if (getMouseState() == MouseUp) {
ui.performAction(CogToolLID.NudgeUp);
}
break;
}
case SWT.ARROW_DOWN: {
if (getMouseState() == MouseUp) {
ui.performAction(CogToolLID.NudgeDown);
}
break;
}
case SWT.ARROW_LEFT: {
if (getMouseState() == MouseUp) {
ui.performAction(CogToolLID.NudgeLeft);
}
break;
}
case SWT.ARROW_RIGHT: {
if (getMouseState() == MouseUp) {
ui.performAction(CogToolLID.NudgeRight);
}
break;
}
case SWT.CR: {
if (getMouseState() == MouseUp) {
ui.performAction(DesignEditorLID.InitiateFrameRename);
}
break;
}
// Handle delete key & backspace keys when the item is selected,
// and the text area is not. TODO: Not a full solution.
case SWT.DEL:
case SWT.BS: { // Backspace
if (getMouseState() == MouseUp) {
ui.performAction(CogToolLID.Delete);
}
break;
}
}
return true;
} // dealWithKeyPressed
}