/*******************************************************************************
* 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.Collection;
import java.util.EventObject;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import edu.cmu.cs.hcii.cogtool.CogTool;
import edu.cmu.cs.hcii.cogtool.CogToolLID;
import edu.cmu.cs.hcii.cogtool.ResultDisplayPolicy;
import edu.cmu.cs.hcii.cogtool.model.AAction;
import edu.cmu.cs.hcii.cogtool.model.DefaultModelGeneratorState;
import edu.cmu.cs.hcii.cogtool.model.Demonstration;
import edu.cmu.cs.hcii.cogtool.model.Design;
import edu.cmu.cs.hcii.cogtool.model.DeviceType;
import edu.cmu.cs.hcii.cogtool.model.DoubleRectangle;
import edu.cmu.cs.hcii.cogtool.model.Frame;
import edu.cmu.cs.hcii.cogtool.model.HandLocation;
import edu.cmu.cs.hcii.cogtool.model.ActionScriptStep;
import edu.cmu.cs.hcii.cogtool.model.ContextMenu;
import edu.cmu.cs.hcii.cogtool.model.DelayScriptStep;
import edu.cmu.cs.hcii.cogtool.model.HearScriptStep;
import edu.cmu.cs.hcii.cogtool.model.InputDevice;
import edu.cmu.cs.hcii.cogtool.model.MenuHeader;
import edu.cmu.cs.hcii.cogtool.model.PullDownHeader;
import edu.cmu.cs.hcii.cogtool.model.Script;
import edu.cmu.cs.hcii.cogtool.model.AScriptStep;
import edu.cmu.cs.hcii.cogtool.model.TaskApplication;
import edu.cmu.cs.hcii.cogtool.model.Transition;
import edu.cmu.cs.hcii.cogtool.model.TransitionSource;
import edu.cmu.cs.hcii.cogtool.model.IWidget;
import edu.cmu.cs.hcii.cogtool.model.Project;
import edu.cmu.cs.hcii.cogtool.model.ThinkScriptStep;
import edu.cmu.cs.hcii.cogtool.model.TransitionSourceType;
import edu.cmu.cs.hcii.cogtool.model.WidgetAttributes;
import edu.cmu.cs.hcii.cogtool.model.WidgetType;
import edu.cmu.cs.hcii.cogtool.ui.SEDemoLID.SEDemoTransitionLID;
import edu.cmu.cs.hcii.cogtool.uimodel.DefaultSEUIModel;
import edu.cmu.cs.hcii.cogtool.uimodel.FrameUIModel;
import edu.cmu.cs.hcii.cogtool.uimodel.GraphicalSource;
import edu.cmu.cs.hcii.cogtool.uimodel.SEDemoUIModel;
import edu.cmu.cs.hcii.cogtool.util.GraphicsUtil;
import edu.cmu.cs.hcii.cogtool.util.AlertHandler;
import edu.cmu.cs.hcii.cogtool.util.KeyDisplayUtil;
import edu.cmu.cs.hcii.cogtool.util.L10N;
import edu.cmu.cs.hcii.cogtool.util.ListenerIdentifier;
import edu.cmu.cs.hcii.cogtool.util.ListenerIdentifierMap;
import edu.cmu.cs.hcii.cogtool.util.MenuUtil;
import edu.cmu.cs.hcii.cogtool.util.NullSafe;
import edu.cmu.cs.hcii.cogtool.util.OSUtils;
import edu.cmu.cs.hcii.cogtool.util.PrecisionUtilities;
import edu.cmu.cs.hcii.cogtool.util.RcvrImageException;
import edu.cmu.cs.hcii.cogtool.util.SWTContextMenuUtil;
import edu.cmu.cs.hcii.cogtool.util.UndoManager;
import edu.cmu.cs.hcii.cogtool.util.MenuUtil.MenuItemDefinition;
import edu.cmu.cs.hcii.cogtool.util.MenuUtil.SimpleMenuItemDefinition;
import edu.cmu.cs.hcii.cogtool.view.MenuFactory;
import edu.cmu.cs.hcii.cogtool.view.SEDemoView;
import edu.cmu.cs.hcii.cogtool.view.SWTList;
import edu.cmu.cs.hcii.cogtool.view.SWTListMultiColumn;
import edu.cmu.cs.hcii.cogtool.view.ScriptView;
import edu.cmu.cs.hcii.cogtool.view.View;
public class SEDemoUI extends SEDefaultUI
{
protected class SEDemoDelayedSelection extends DelayedSelection
{
protected AScriptStep selectedStep = null;
public SEDemoDelayedSelection()
{
super(selection);
}
@Override
public void addToSelection(Object selectionKey, Object itemToSelect)
{
if (itemsToSelect.size() == 0) {
selectedStep = (AScriptStep) itemToSelect;
super.addToSelection(selectionKey, itemToSelect);
}
}
@Override
protected void selectItem(Object item)
{
int selectIndex =
script.getStepStateIndex((AScriptStep) item);
if (selectIndex >= script.getStepStateCount()) {
// Select the "last" item
selection.setSelectedState(null);
}
else {
selection.setSelectedStateIndex(selectIndex);
}
}
public boolean hasSelectedStep()
{
return itemsToSelect.size() > 0;
}
public AScriptStep getSelectedStep()
{
if (itemsToSelect.size() > 0) {
return selectedStep;
}
throw new IllegalStateException("Should check hasSelectedStep first");
}
};
/**
* Common shared class for demonstration action parameters.
*/
public abstract static class DemoTransition
{
public SEDemoSelectionState selection;
public DemoTransition(SEDemoSelectionState seln)
{
selection = seln;
}
public abstract String getLocalizedString();
}
/**
* Support sending a request to perform transition.
*/
public static class FollowTransition extends DemoTransition
{
public Transition transition;
public FollowTransition(SEDemoSelectionState seln, Transition t)
{
super(seln);
transition = t;
}
@Override
public String getLocalizedString()
{
return L10N.get("SE.Perform", "Perform")
+ " " + transition.getAction().getLocalizedString()
+ " " + L10N.get("SE.On", "On")
+ " " + transition.getSource().getName();
}
}
public static class SelfTransition extends DemoTransition
{
public TransitionSource target;
public AAction action;
public SelfTransition(SEDemoSelectionState seln,
TransitionSource src,
AAction transitionAction)
{
super(seln);
target = src;
action = transitionAction;
}
@Override
public String getLocalizedString()
{
return L10N.get("SE.PerformSelfTransition", "Self-transition");
}
}
/**
* For sending look-at information or other information
* which does not need an ButtonAction
*/
public static class LookAtTransition extends DemoTransition
{
public IWidget target;
public LookAtTransition(SEDemoSelectionState seln, IWidget t)
{
super(seln);
target = t;
}
@Override
public String getLocalizedString()
{
return L10N.get("SE.PerformLookAt", "Perform Look At")
+ " " + target.getName();
}
}
protected TaskApplication taskApp; // cached from uiModel
protected Script script;
protected SEDemoView view;
protected SEDemoMouseState seMouseState;
protected SEDemoSelectionState selection;
protected SEDemoContextSelectionState contextSelection;
protected SEDemoInteraction interaction;
protected SelectionListener SWTselectionChangeHandler;
protected SEDemoUIModel uiModel;
protected DelayedRepaint delayedRepainting;
protected SEDemoDelayedSelection delayedStateSelection;
protected boolean editable;
protected static final String STEP_LABEL =
L10N.get("SED.StepLabel", "Step");
protected static final String LAST_STEP_LABEL =
L10N.get("SED.LastStepLabel", "Last Step");
protected static final String THINK_LABEL =
L10N.get("SED.Mental", "Think");
protected static final String CHANGE_START_FRAME_LABEL =
L10N.get("SED.ChangeStartFrame", "Change Start Frame");
protected static final String EDIT_STEP_LABEL =
L10N.get("SED.EditStepLabel", "Edit Step");
protected static final String EDIT_THINK_LABEL =
L10N.get("SED.EditThinkLabel", "Edit Think Properties");
protected SWTContextMenuUtil.MenuListener frameContextListener =
new SWTContextMenuUtil.MenuListenerAdapter() {
@Override
public void handleEvent(Event evt)
{
showFrameContextMenu((Label) evt.widget);
}
};
@Override
protected DefaultSEUIModel getDefaultUIModel()
{
return getUIModel();
}
protected SWTContextMenuUtil.MenuListener contextMenuListener =
new SWTContextMenuUtil.MenuListenerAdapter()
{
@Override
public void handleEvent(Event evt)
{
showDeviceMenu((InputDevice) evt.widget.getData());
}
@Override
public void mouseDown(MouseEvent evt)
{
if (OSUtils.MACOSX &&
(evt.button == 1) &&
! SWTContextMenuUtil.isContextMenu(evt))
{
if (isLookAtSelected()) {
boolean tryAgain =
interaction.protestInvalidLookAtTarget();
setLookAtSelected(tryAgain);
}
else {
showDeviceMenu((InputDevice) evt.widget.getData());
}
}
}
@Override
public void mouseUp(MouseEvent evt)
{
if (OSUtils.WINDOWS &&
(evt.button == 1) &&
! SWTContextMenuUtil.isContextMenu(evt))
{
if (isLookAtSelected()) {
boolean tryAgain =
interaction.protestInvalidLookAtTarget();
setLookAtSelected(tryAgain);
}
else {
showDeviceMenu((InputDevice) evt.widget.getData());
}
}
}
};
protected TaskApplication.TaskApplicationResultChange designAlert;
protected String getPrediction()
{
return ResultDisplayPolicy.getTaskApplicationCell(project,
taskApp,
taskApp.getFirstModelGenerator(),
true,
ResultDisplayPolicy.WITH_SECS);
}
protected AlertHandler updatePredictionHandler =
new AlertHandler()
{
public void handleAlert(EventObject alert)
{
TaskApplication.TaskApplicationResultChange chg =
(TaskApplication.TaskApplicationResultChange) alert;
TaskApplication alertTA =
project.getTaskApplication(chg.task,
(Design) chg.getSource());
if (alertTA == taskApp) {
view.updatePrediction(getPrediction());
setEnabled(CogToolLID.ShowModelVisualization,
ListenerIdentifierMap.NORMAL,
taskApp.hasComputedResult()
&& taskApp.hasResultSteps());
}
}
};
public SEDemoUI(Script modelScript,
Project scriptProject,
UndoManager undoMgr)
{
super(modelScript.getDemonstration().getTaskApplication().getTask(),
modelScript.getDemonstration().getTaskApplication().getDesign(),
scriptProject,
undoMgr,
true);
Demonstration demo = modelScript.getDemonstration();
taskApp = demo.getTaskApplication();
designAlert = new TaskApplication.TaskApplicationResultChange(taskApp);
editable = demo.isEditable();
uiModel = new SEDemoUIModel(modelScript,
scriptProject,
frameLabelListener,
inputDeviceChangeHandler,
editable);
uiModel.setWidgetShapeChangeHandler(handleWidgetShapeChange);
// Cache model values
script = modelScript;
seMouseState = new SEDemoMouseState(this, editable);
int deviceTypes =
DeviceType.buildDeviceSet(design.getDeviceTypes());
SEDemoView v = new SEDemoView(deviceTypes,
lIDMap,
this,
menuData,
uiModel,
null,
seMouseState,
contextMenuListener,
frameContextListener,
this,
getWindowLocation(),
editable);
setViewUIModel(v, uiModel);
view = v;
setZoomEditor(view.getEditor());
view.updatePrediction(getPrediction());
design.addHandler(this,
Demonstration.StatusChange.class,
new AlertHandler() {
public void handleAlert(EventObject alert)
{
view.updatePrediction(getPrediction());
}
});
design.addHandler(this,
TaskApplication.TaskApplicationResultChange.class,
updatePredictionHandler);
design.addHandler(this,
Design.DeviceTypeChange.class,
new AlertHandler() {
public void handleAlert(EventObject alert)
{
Set<DeviceType> deviceTypeSet =
design.getDeviceTypes();
int deviceTypes =
DeviceType.buildDeviceSet(deviceTypeSet);
view.resetDeviceTypes(deviceTypes);
}
});
DefaultModelGeneratorState initialState = demo.getInitialState();
boolean userMouseHand = demo.getMouseHand();
HandLocation mouseHandLoc = initialState.getHandLocation(userMouseHand);
view.setStartMouseHandLocation(mouseHandLoc);
view.setUserMouseHand(userMouseHand);
Table historyTable = view.getHistoryTable();
selection = new SEDemoSelectionState(historyTable);
contextSelection = new SEDemoContextSelectionState();
interaction = new SEDemoInteraction(getView());
updateTitle();
AlertHandler designFrameChangeHandler =
new AlertHandler() {
public void handleAlert(EventObject alert) {
Design.FrameChange evt = (Design.FrameChange) alert;
addRemoveFrameNameChgHandler((Frame) evt.element,
evt.isAdd);
}
};
design.addHandler(this,
Design.FrameChange.class,
designFrameChangeHandler);
// Listen to Menu events on the History table.
// Used to detect up-context click on the selected row.
historyTable.addListener(SWT.MenuDetect,
new Listener() {
public void handleEvent(Event evt)
{
//xyzzy TODO: must select/deselect by pos! for Mac (unless selection occurs before menudetect!
showSelectionContextMenu(evt.x,
evt.y);
}
});
addSelectionChangeListeners();
AlertHandler stepStateChangeHandler =
new AlertHandler()
{
public void handleAlert(EventObject alert)
{
updateView();
// Check if change to generated step states requires us to
// determine the selected step state from currentOverride
if (delayedStateSelection.hasSelectedStep()) {
AScriptStep selectedStep =
delayedStateSelection.getSelectedStep();
DefaultModelGeneratorState stepState;
if (selectedStep != null) {
int selectIndex =
script.getStepStateIndex(selectedStep);
stepState = script.getStepState(selectIndex);
// stepState = script.getPreviousState(stepState);
}
else {
stepState = script.getLastState();
}
uiModel.setCurrentOverride(script, stepState);
}
else {
DefaultModelGeneratorState currentOverride =
uiModel.getCurrentOverrideState();
if (currentOverride != null) {
AScriptStep selectionOwner =
currentOverride.getScriptStep().getOwner();
delayedStateSelection.addToSelection(selectionOwner);
}
}
}
};
script.addHandler(this,
Script.StepStateChange.class,
stepStateChangeHandler);
demo.addHandler(this,
Demonstration.ScriptStepChange.class,
createScriptChangeAlert());
AlertHandler initialStateChangeHandler =
new AlertHandler() {
public void handleAlert(EventObject alert)
{
Demonstration.InitialStateChange chg = (Demonstration.InitialStateChange) alert;
Demonstration demo = taskApp.getDemonstration();
DefaultModelGeneratorState initialState = demo.getInitialState();
boolean mouseHand = initialState.getMouseHand();
if (chg.involvesChange(Demonstration.InitialStateChange.MOUSE_HAND)) {
view.setUserMouseHand(mouseHand);
}
if (chg.involvesChange(Demonstration.InitialStateChange.GENERATOR_STATE)) {
view.setStartMouseHandLocation(initialState.getHandLocation(mouseHand));
}
}
};
demo.addHandler(this,
Demonstration.InitialStateChange.class,
initialStateChangeHandler);
delayedStateSelection = new SEDemoDelayedSelection();
delayedRepainting =
new DelayedRepaint() {
@Override
protected void performRepaint()
{
updateView();
}
@Override
public void doWork()
{
needsRepaint = REPAINT_ALL;
super.doWork();
undoMgrViewHandler.resetView(undoManager);
// Update the enabled items selection state.
setViewEnabledState(selection,
ListenerIdentifierMap.NORMAL);
}
};
CogTool.selectionPhase.addDelayedWork(delayedStateSelection);
CogTool.repaintPhase.addDelayedWork(delayedRepainting);
updateView();
// Set the initial selection to the LAST element in the filtered list.
selectLastHistoryItem();
// Restore zoom level
restoreZoom();
setInitiallyEnabled(true);
} // ctor
@Override
public void dispose()
{
CogTool.selectionPhase.removeDelayedWork(delayedStateSelection);
CogTool.repaintPhase.removeDelayedWork(delayedRepainting);
uiModel.dispose();
script.removeAllHandlers(this);
script.getDemonstration().removeAllHandlers(this);
selection.removeAllHandlers(this);
view.removeSWTListSelectionHandler(SWTselectionChangeHandler);
super.dispose();
}
/**
* @return Interaction Object
*/
public SEDemoInteraction getInteraction()
{
return interaction;
}
@Override
public Interaction getStandardInteraction()
{
return interaction;
}
public SEDemoSelectionState getSelection()
{
return selection;
}
/**
* Set the last item in the history list to be highlighted.
*/
protected void selectLastHistoryItem()
{
// To select the last item in the list, simply
// set selection to null!
selection.setSelectedState(null);
}
@Override
public View getView()
{
return view;
}
protected ScriptView getScriptView()
{
return view;
}
// For use solely by mouse state and tests
public SEDemoUIModel getUIModel()
{
return uiModel;
}
@Override
protected Object getModelObject()
{
return script;
}
public GraphicalSource<?> getSourceAtXY(int x, int y)
{
return uiModel.getSourceAtXY(x, y);
}
/**
* Set the internal property of the lookAt button being selected.
* This will tell the MouseState object to perform a "lookat" transition
* instead of a normal perform action transition.
*/
public void setLookAtSelected(boolean selected)
{
view.setLookAtSelected(selected);
}
public boolean isLookAtSelected()
{
return editable && view.isLookAtSelected();
}
public void setCurrentFrame(Frame frame)
{
if (uiModel.setCurrentFrame(frame)) {
view.getEditor().setContents(uiModel.getCurrentFrame().getContents());
}
}
protected boolean isEditable(AScriptStep step)
{
if ((step instanceof ActionScriptStep) &&
step.isDemonstrated() &&
step.isInsertedByUser())
{
return true;
}
step = step.getOwner();
return (step instanceof ActionScriptStep) &&
step.isDemonstrated() &&
step.isInsertedByUser();
}
/**
* Add the selection Change event listeners.
*/
protected void addSelectionChangeListeners()
{
SWTselectionChangeHandler =
new SelectionListener() {
public void widgetSelected(SelectionEvent evt)
{
SWTList swtList = view.getScriptEditorList();
TableItem[] selectedItems = swtList.getSelectionObject();
for (TableItem selectedItem : selectedItems) {
// TODO: Currently supports only single selection.
Object data = selectedItem.getData();
if (data instanceof Frame) {
selection.setSelectedState(null);
}
else { // should be instanceof DefaultModelGeneratorState!!!
AScriptStep step =
((DefaultModelGeneratorState) data).getScriptStep();
if (step instanceof HearScriptStep) {
if (interaction.askEditFrame()) {
Frame f = step.getCurrentFrame();
performAction(DesignEditorLID.EditFrame, f);
}
TableItem previousSelectedRow =
swtList.getRowItem(selection.getPreviousSelection());
selection.setSelectedState(previousSelectedRow);
}
else {
selection.setSelectedState(selectedItem);
}
}
}
centerSelectedRegion();
setViewEnabledState(selection,
ListenerIdentifierMap.NORMAL);
// Let selection change handle changing the frame
}
public void widgetDefaultSelected(SelectionEvent evt)
{
SWTList swtList = view.getScriptEditorList();
TableItem[] selectedItems = swtList.getSelectionObject();
// TODO: Currently supports only single selection.
for (TableItem selectedItem : selectedItems) {
Object data = selectedItem.getData();
if (data instanceof DefaultModelGeneratorState) {
DefaultModelGeneratorState stepState =
(DefaultModelGeneratorState) data;
AScriptStep step = stepState.getScriptStep();
// In case we need this; not a context selection
// in truth, but we can re-use the structure.
contextSelection.setSelectedState(stepState);
if (editable) {
if (step instanceof DelayScriptStep) {
performAction(SEDemoLID.ChangeWaitProperties,
contextSelection);
}
else if (step instanceof ThinkScriptStep) {
performAction(SEDemoLID.ChangeThinkProperties,
contextSelection);
}
else if (isEditable(step)) {
performAction(SEDemoLID.Edit,
contextSelection);
}
}
}
}
}
};
view.addSWTListSelectionHandler(SWTselectionChangeHandler);
AlertHandler selectionChangeHandler =
new AlertHandler() {
public void handleAlert(EventObject alert)
{
SEDemoSelectionState.StepStateSelectionChange event =
(SEDemoSelectionState.StepStateSelectionChange) alert;
if (event != null) {
if (event.selected) {
DefaultModelGeneratorState stepState = event.changedState;
uiModel.setCurrentOverride(script,
script.getPreviousState(stepState));
Frame resultFrame = null;
if (stepState != null) {
resultFrame =
stepState.getScriptStep().getCurrentFrame();
}
else {
resultFrame =
script.getDemonstration().getResultFrame();
}
try {
setCurrentFrame(resultFrame);
}
catch (GraphicsUtil.ImageException ex) {
throw new RcvrImageException("Changing current demonstration frame",
ex);
}
}
else {
// deselect item.
uiModel.setCurrentOverride(script,
script.getLastState());
}
}
} // handleAlert
};
selection.addHandler(this,
SEDemoSelectionState.StepStateSelectionChange.class,
selectionChangeHandler);
}
protected AlertHandler createScriptChangeAlert()
{
return new AlertHandler()
{
public void handleAlert(EventObject alert)
{
Demonstration.ScriptStepChange evt =
(Demonstration.ScriptStepChange) alert;
boolean computableChange =
determineNextSelection(script,
evt.scriptSteps,
evt.action,
evt.index,
delayedStateSelection);
if (computableChange) {
design.raiseAlert(designAlert);
}
}
};
} // createScriptChangeAlert
@Override
public void showContextMenu()
{
view.showStandardMenu();
}
/**
* Sets the "always-enabled" widgets;
* call this at the end of the subclass constructor!
*/
@Override
protected void setInitiallyEnabled(boolean forConstruction)
{
super.setInitiallyEnabled(forConstruction);
setEnabled(CogToolLID.RecomputeScript,
ListenerIdentifierMap.ALL,
MenuUtil.ENABLED);
// Delete always on because a step is always selected!
setEnabled(CogToolLID.Delete,
ListenerIdentifierMap.ALL,
MenuUtil.ENABLED);
setEnabled(CogToolLID.ExportScriptToCSV,
ListenerIdentifierMap.ALL,
MenuUtil.ENABLED);
setViewEnabledState(selection, ListenerIdentifierMap.NORMAL);
}
public void setLIDEnabledState()
{
setViewEnabledState(selection, ListenerIdentifierMap.NORMAL);
}
/**
* Enables or disables LIDs as appropriate
*
* Primarily used to enable Edit Think Duration or edit Delay Duration.
*
* @param selnState the selection state on which to base enabling/disabling
* @param availability NORMAL or CONTEXT
* @see ListenerIdentifierMap
*/
protected void setViewEnabledState(SEDemoSelectionState selnState,
Boolean availability)
{
Demonstration demo = taskApp.getDemonstration();
boolean isValid = ! demo.isInvalid();
boolean isObsolete = isValid && demo.isObsolete();
String label = isObsolete ? SEDemoView.REGENERATE_THEN_COMPUTE
: SEDemoView.COMPUTE;
setEnabled(SEDemoLID.RecomputeScript,
availability,
isValid && editable,
label);
setEnabled(SEDemoLID.RegenerateScript, availability, isObsolete);
label = "";
// Anything selected?
DefaultModelGeneratorState selectedState = selnState.getSelectedState();
if (selectedState != null) {
AScriptStep scriptStep = selectedState.getScriptStep();
label = MenuFactory.DELETE_STRING + " " + STEP_LABEL;
if (scriptStep instanceof ThinkScriptStep) {
if (! scriptStep.isInsertedByUser()) {
label = MenuFactory.DELETE_STRING + " " + THINK_LABEL;
}
setEnabled(SEDemoLID.ChangeThinkProperties,
availability,
true);
setEnabled(SEDemoLID.ChangeWaitProperties,
availability,
false);
setEnabled(SEDemoLID.Edit, availability, editable,
EDIT_THINK_LABEL);
}
else {
setEnabled(SEDemoLID.ChangeThinkProperties,
availability,
false);
setEnabled(SEDemoLID.ChangeWaitProperties,
availability,
scriptStep instanceof DelayScriptStep);
setEnabled(SEDemoLID.Edit,
availability,
editable &&
((scriptStep instanceof DelayScriptStep)
|| isEditable(scriptStep)),
EDIT_STEP_LABEL);
}
}
else {
setEnabled(SEDemoLID.ChangeWaitProperties, availability, false);
setEnabled(SEDemoLID.ChangeThinkProperties, availability, false);
if (script.getStepStateCount() > 0) {
label = MenuFactory.DELETE_STRING + " " + LAST_STEP_LABEL;
setEnabled(SEDemoLID.Edit, availability, false,
EDIT_STEP_LABEL + " " + LAST_STEP_LABEL);
}
else {
label = CHANGE_START_FRAME_LABEL;
setEnabled(SEDemoLID.Edit, availability, false,
EDIT_STEP_LABEL);
}
}
String deleteString = label;
setEnabled(CogToolLID.Delete, availability, editable, deleteString);
setEnabled(CogToolLID.ShowModelVisualization,
availability,
taskApp.hasComputedResult() && taskApp.hasResultSteps());
}
// LID Transmuter Stuff
@Override
public ListenerIdentifier transmute(ListenerIdentifier id,
boolean isContextSelection)
{
ListenerIdentifier specificLID =
super.transmute(id, isContextSelection);
// Check if super has already specialized this
if (specificLID != id) {
return specificLID;
}
// Check to see if it's a dynamic LID
if (id instanceof SEDemoLID.SEDemoTransitionLID) {
// check to see what kind of PerformAction it is.
SEDemoLID.SEDemoTransitionLID demoLID =
(SEDemoLID.SEDemoTransitionLID) id;
if (demoLID.transition instanceof SEDemoUI.LookAtTransition) {
return SEDemoLID.InsertLookAt;
}
if (demoLID.transition instanceof SEDemoUI.SelfTransition) {
return SEDemoLID.InsertSelfTransition;
}
// Return the standard PerformAction LID
return SEDemoLID.PerformTransition;
}
return id;
}
@Override
public Object getParameters(ListenerIdentifier originalLID,
ListenerIdentifier transmutedLID,
boolean isContextSelection)
{
Object parameters = super.getParameters(originalLID,
transmutedLID,
isContextSelection);
if (parameters != UNSET) {
return parameters;
}
if (originalLID instanceof SEDemoLID.SEDemoTransitionLID) {
// Return the demoTransition object which is the expected param.
SEDemoLID.SEDemoTransitionLID demoLID =
(SEDemoLID.SEDemoTransitionLID) originalLID;
return demoLID.transition;
}
// All other LIDs use only the selection.
// This is because they are all buttons.. although this might change
// in the future.
if (isContextSelection) {
return contextSelection;
}
if (DesignEditorLID.EditFrame.equals(originalLID)) {
return uiModel.getCurrentFrame().getFrame();
}
return selection;
}
/**
* Allows the interfaces to clean up any feedback provided to the
* user before and during a performAction.
*
* @param okToContinue the return value from performAction
* @param menuHidden whether or not the context menu is dismissed
* without selecting an operation to perform
* @author mlh
*/
@Override
public void cleanup(boolean okToContinue, boolean menuHidden)
{
if (isLookAtSelected()) {
setLookAtSelected(false);
}
super.cleanup(okToContinue, menuHidden);
}
/**
* Do any set-up before <code>performAction</code> is invoked.
*
* @param id the transmuted key specifying the semantic nature of the
* action to be performed
*/
@Override
protected void setUpPerformAction(ListenerIdentifier id)
{
super.setUpPerformAction(id);
delayedStateSelection.setActive(true);
}
/**
* Support for centering selection when zooming
*/
@Override
protected Rectangle getSelectedRegion()
{
DefaultModelGeneratorState selectedState = selection.getSelectedState();
if (selectedState != null) {
TransitionSource src =
selectedState.getScriptStep().getStepFocus();
if (src instanceof IWidget) {
DoubleRectangle widgetBds = ((IWidget) src).getEltBounds();
return PrecisionUtilities.getDraw2DRectangle(widgetBds);
}
}
return super.getSelectedRegion();
}
@Override
protected boolean centerSelectedRegion()
{
if (! super.centerSelectedRegion()) {
setScrollOrigin(0, 0);
return false;
}
return true;
}
/**
* Update the view: Set the View's List with the current ScriptSteps
*/
@Override
protected void updateView()
{
Iterator<DefaultModelGeneratorState> states =
script.getStepStates().iterator();
SWTListMultiColumn swtList = view.getScriptEditorList();
swtList.setListContents(states);
swtList.addListItem(script.getDemonstration().getResultFrame());
TableItem item = selection.getSelectedStateRow();
Table t = view.getHistoryTable();
if (item == null) {
TableItem[] items = t.getItems();
if (items.length > 0) {
t.showItem(t.getItem(items.length - 1));
}
}
else {
t.showItem(item);
}
}
/**
* Mouse event occurred on the table, use the selected item to
* display an appropriate menu.
*/
public void showSelectionContextMenu(int x, int y)
{
contextSelection.deselectAll();
Table historyTable = view.getHistoryTable();
org.eclipse.swt.graphics.Point atPoint = historyTable.toControl(x, y);
TableItem ti = historyTable.getItem(atPoint);
if ((ti != null) && (ti.getData() instanceof DefaultModelGeneratorState)) {
contextSelection.setSelectedState((DefaultModelGeneratorState) ti.getData());
}
else {
// this is the result Step, so no selection
historyTable.select(historyTable.getItemCount() - 1);
}
showContextMenu(contextSelection, true);
}
protected void showContextMenu(SEDemoSelectionState selnState,
boolean context)
{
if (! editable) {
return;
}
setViewEnabledState(selnState,
(context) ? ListenerIdentifierMap.CONTEXT
: ListenerIdentifierMap.NORMAL);
view.showScriptStepSelectionMenu();
}
protected List<MenuItemDefinition> populateContextMenu(List<GraphicalSource<? extends TransitionSource>> graphicalSources)
{
List<MenuItemDefinition> menuItems = new ArrayList<MenuItemDefinition>();
Iterator<GraphicalSource<? extends TransitionSource>> sources =
graphicalSources.iterator();
if (sources.hasNext()) {
GraphicalSource<? extends TransitionSource> graphicalSource =
sources.next();
populateContextMenu(graphicalSource.getModel(), menuItems);
while (sources.hasNext()) {
menuItems.add(MenuUtil.SEPARATOR);
menuItems.add(MenuUtil.SEPARATOR);
graphicalSource = sources.next();
populateContextMenu(graphicalSource.getModel(), menuItems);
}
}
return menuItems;
}
protected void populateContextMenu(TransitionSource source,
List<MenuItemDefinition> menuItems)
{
Collection<Transition> values = source.getTransitions().values();
Iterator<Transition> iter = values.iterator();
SEDemoTransitionLID itemLID;
while (iter.hasNext() ) {
Transition trans = iter.next();
SEDemoUI.DemoTransition ftrans = new SEDemoUI.FollowTransition(selection, trans);
// Add the ftrans to the list in the context menus
itemLID = new SEDemoTransitionLID("PerformDemoTransition", ftrans);
String transitionStr =
KeyDisplayUtil.convertActionToMenuText(ftrans.getLocalizedString());
MenuItemDefinition mItem =
new SimpleMenuItemDefinition(transitionStr,
itemLID,
MenuUtil.ENABLED);
menuItems.add(mItem);
}
// Check to see if any transitions are actually available.
if (values.size() == 0) {
MenuItemDefinition mItem =
new SimpleMenuItemDefinition(L10N.get("SE.DemoNoPredefinedTransitions",
"No defined transitions for ")
+ source.getName(),
null);
// Add a default disabled message
menuItems.add(mItem);
}
boolean selfTransitionOK = true;
// Add a default look-at transition for all regions except Devices
if (source.getTransitionSourceType() == TransitionSourceType.Widget) {
IWidget widget = (IWidget) source;
Object isSep =
widget.getAttribute(WidgetAttributes.IS_SEPARATOR_ATTR);
if ((NullSafe.equals(WidgetAttributes.IS_SEPARATOR, isSep)) ||
(widget.getWidgetType() == WidgetType.Noninteractive) ||
((widget instanceof MenuHeader) ||
(widget instanceof PullDownHeader) ||
(widget instanceof ContextMenu)))
{
selfTransitionOK = false;
}
// Add standard menus to the list of items
menuItems.add(MenuUtil.SEPARATOR);
SEDemoUI.DemoTransition lookAtTrans =
new SEDemoUI.LookAtTransition(selection, widget);
// Add default transition options.
String itemLabel =
L10N.get("SE.DemoLookAt", "Look at") + " " + widget.getName();
itemLID = new SEDemoTransitionLID("PerformDemoLookAtTransition",
lookAtTrans);
menuItems.add(new SimpleMenuItemDefinition(itemLabel,
itemLID,
MenuUtil.ENABLED));
}
if (selfTransitionOK) {
menuItems.add(MenuUtil.SEPARATOR);
itemLID = new SEDemoTransitionLID("PerformSelfTransition",
new SEDemoUI.SelfTransition(selection,
source,
null));
MenuItemDefinition mItem =
new SimpleMenuItemDefinition(L10N.get("SE.SelfTransition",
"Perform Self-transition"),
itemLID,
MenuUtil.ENABLED);
menuItems.add(mItem);
}
} // populateContextMenu
protected void showDeviceMenu(InputDevice device)
{
List<MenuItemDefinition> menuItems = new ArrayList<MenuItemDefinition>();
populateContextMenu(device, menuItems);
view.showDynamicMenu(menuItems);
}
public void showContextMenu(TransitionSource src)
{
if (! editable) {
return;
}
if (src instanceof InputDevice) {
showDeviceMenu((InputDevice) src);
}
else {
GraphicalSource<? extends TransitionSource> sourceFig =
uiModel.getCurrentFrame().getWidgetFigure((IWidget) src);
List<GraphicalSource<? extends TransitionSource>> singleton =
new ArrayList<GraphicalSource<? extends TransitionSource>>();
singleton.add(sourceFig);
showContextMenu(singleton);
}
}
public void showContextMenu(List<GraphicalSource<? extends TransitionSource>> sources)
{
if ((sources == null) || (sources.size() == 0)) {
view.showStandardMenu();
}
else {
if (! editable) {
return;
}
List<MenuItemDefinition> menuItems = populateContextMenu(sources);
view.showDynamicMenu(menuItems);
}
}
@Override
public void showContextMenu(int x, int y)
{
// Check to see what transitions are available under X,Y and then
// generate a Menu from them.
if (uiModel.getCurrentFrame() != null) {
showContextMenu(uiModel.getSourcesAtXY(x, y));
}
}
protected void showFrameContextMenu(Label frameLabel)
{
view.showFrameMenu();
}
public void hideAllChildren()
{
FrameUIModel frameFig = uiModel.getCurrentFrame();
if (frameFig != null) {
frameFig.hideAllChildren();
}
}
}