/******************************************************************************* * 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.model; import java.util.ArrayList; import java.util.Collection; import java.util.EventObject; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Set; import edu.cmu.cs.hcii.cogtool.util.GlobalAttributed; import edu.cmu.cs.hcii.cogtool.util.ObjectLoader; import edu.cmu.cs.hcii.cogtool.util.ObjectSaver; public class Demonstration extends GlobalAttributed implements DemonstrationState { public static class InitialStateChange extends EventObject { // These may be or'ed together public static final int START_FRAME = 1; public static final int MOUSE_HAND = 2; public static final int GENERATOR_STATE = 6; public static final int ALL_CHANGES = -1; /** * The changes this alert represents to the initial state. */ public int changes; public InitialStateChange(Demonstration d, int chg) { super(d); changes = chg; } public boolean involvesChange(int chg) { return (changes & chg) != 0; } } /** * Inform observers of changes to the list of contained script steps. * Handles both singleton and multiple step changes; if * <code>scriptStep</code> is <code>null</code>, then the change * is "multiple" and <code>scriptSteps</code> should be used. */ public static class ScriptStepChange extends EventObject { public static final int ADD_STEP = 0; public static final int REMOVE_STEP = 1; /** * The index most relevent to the change. * Can be the item changed/added/deleted or the last item in a * multiple item change. */ public int index; /** * The single script item which caused the change. */ public AScriptStep scriptStep; /** * For a multiple item change, this includes all the changed items. */ public Collection<AScriptStep> scriptSteps; /** * The action type being performed. * Should be a value from the list above. */ public int action; /** * Constructor for a singleton ScriptStep change. * Provides the source, script step, the index and the action taken. * * Throws an exception if the action is invalid for this constructor * @param source * @param changed * @param indx * @param chgAction */ public ScriptStepChange(Demonstration d, AScriptStep changed, int indx, int chgAction) { super(d); scriptStep = changed; scriptSteps = null; index = indx; action = chgAction; } /** * Constructor for a multiple ScriptStep change. * Provides the source, script steps, the index and the action taken. * * Throws an exception if the action is invalid for this constructor * @param source * @param changed * @param indx * @param chgAction */ public ScriptStepChange(Demonstration d, Collection<AScriptStep> changed, int indx, int chgAction) { super(d); scriptStep = null; scriptSteps = changed; index = indx; action = chgAction; } } /** * For efficiency, the demonstration's TaskApplication's observers are * notified about changes in validity or obsoleting status. */ public static class StatusChange extends EventObject { public StatusChange(Design design) { super(design); } } public static final int edu_cmu_cs_hcii_cogtool_model_Demonstration_version = 0; // startFrame{,Chosen}VAR is used for loading versions 0 and 1 Scripts public static final String startFrameVAR = "startFrame"; public static final String startFrameChosenVAR = "startFrameChosen"; protected static final String initialStateVAR = "initialState"; protected static final String stepsVAR = "steps"; protected static final String invalidCountVAR = "invalidCount"; protected static final String obsoleteCountVAR = "obsoleteCount"; protected static final String editableVAR = "editable"; protected TaskApplication taskApp; protected Frame startFrame = null; protected boolean startFrameChosen = false; protected List<AScriptStep> steps = new ArrayList<AScriptStep>(); protected DefaultModelGeneratorState initialState; /** * Count of invalid steps plus invalid objects in the initial state * (including the startFrame). */ protected int invalidCount = 0; /** * Count of obsolete steps. */ protected int obsoleteCount = 0; /** * Whether or not this demonstration should be editable in the Script Editor */ protected boolean editable = true; private static ObjectSaver.IDataSaver<Demonstration> SAVER = new ObjectSaver.ADataSaver<Demonstration>() { @Override public int getVersion() { return edu_cmu_cs_hcii_cogtool_model_Demonstration_version; } @Override public void saveData(Demonstration v, ObjectSaver saver) throws java.io.IOException { // Store the start frame and the sequence of steps. saver.saveObject(v.startFrame, startFrameVAR); saver.saveBoolean(v.startFrameChosen, startFrameChosenVAR); saver.saveObject(v.initialState, initialStateVAR); saver.saveObject(v.steps, stepsVAR); saver.saveInt(v.invalidCount, invalidCountVAR); saver.saveInt(v.obsoleteCount, obsoleteCountVAR); saver.saveBoolean(v.editable, editableVAR); } }; public static void registerSaver() { ObjectSaver.registerSaver(Demonstration.class.getName(), SAVER); } private static ObjectLoader.IObjectLoader<Demonstration> LOADER = new ObjectLoader.AObjectLoader<Demonstration>() { @Override public Demonstration createObject() { return new Demonstration(); } /** * Set object that handles object values. * In this case, the design and the start frame */ @Override public void set(Demonstration target, String variable, Object value) { if (variable != null) { if (variable.equals(startFrameVAR)) { target.startFrame = (Frame) value; if ((target.startFrame != null) && (target.startFrame.getDesign() == null)) { Design design = target.getTaskApplication().getDesign(); target.startFrame.setDesign(design); } } else if (variable.equals(initialStateVAR)) { target.initialState = (DefaultModelGeneratorState) value; } } } @Override public void set(Demonstration target, String variable, int value) { if (variable != null) { if (variable.equals(invalidCountVAR)) { target.invalidCount = value; } else if (variable.equals(obsoleteCountVAR)) { target.obsoleteCount = value; // Some older files seem to have negative numbers if (target.obsoleteCount < 0) { target.obsoleteCount = 0; } } } } @Override public void set(Demonstration target, String variable, boolean value) { if (variable != null) { if (variable.equals(startFrameChosenVAR)) { target.startFrameChosen = value; } else if (variable.equals(editableVAR)) { target.editable = value; } } } /** * Collection creation utility for ScriptStep list. */ @Override public Collection<?> createCollection(Demonstration target, String variable, int size) { if (variable != null) { if (variable.equals(stepsVAR)) { return target.steps; } } return null; } }; /** * Function to register the object so it can be loaded correctly. */ public static void registerLoader() { ObjectLoader.registerLoader(Demonstration.class.getName(), edu_cmu_cs_hcii_cogtool_model_Demonstration_version, LOADER); } public static ObjectLoader.IObjectLoader<Demonstration> fetchCurrentLoader() { return LOADER; } protected Demonstration.InitialStateChange startFrameChange = new Demonstration.InitialStateChange(this, Demonstration.InitialStateChange.START_FRAME); protected Demonstration.InitialStateChange mouseHandChange = new Demonstration.InitialStateChange(this, Demonstration.InitialStateChange.MOUSE_HAND); protected Demonstration.InitialStateChange genStateChange = new Demonstration.InitialStateChange(this, Demonstration.InitialStateChange.GENERATOR_STATE); protected Demonstration.StatusChange statusChange = null; protected Demonstration() { // For loading } public Demonstration(TaskApplication ta) { taskApp = ta; initialState = new DefaultModelGeneratorState(); Set<DeviceType> designDevices = taskApp.getDesign().getDeviceTypes(); if (! designDevices.contains(DeviceType.Keyboard)) { boolean mouseHand = initialState.getMouseHand(); if (designDevices.contains(DeviceType.Mouse)) { initialState.setHandLocation(mouseHand, HandLocation.OnMouse); } else if (designDevices.contains(DeviceType.Touchscreen)) { initialState.setHandLocation(mouseHand, HandLocation.OnMouse); // TODO: HandLocation.OnScreen); } } } /** * Fetch the TaskApplication containing this demonstration; * from the TaskApplication, one can fetch the associated Design/Task. */ public TaskApplication getTaskApplication() { return taskApp; } public void setTaskApplication(TaskApplication ta) { taskApp = ta; } /** * Fetch the initial generic model generator state (hand, cursor locations) */ public DefaultModelGeneratorState getInitialState() { return initialState; } /** * Alert observers that the specified change(s) have occurred to * the demonstration's initial state. This should be used if * changes were made directly to the demonstration's initial state * as returned by getInitialState(); */ public void alertInitialStateChange() { raiseAlert(genStateChange); } /** * Fetch with which hand the user manipulates the mouse; * see HandLocation for the boolean constants indicating handedness. */ public boolean getMouseHand() { return initialState.getMouseHand(); } /** * Set with which hand the user manipulates the mouse; * see HandLocation for the boolean constants indicating handedness. */ public void setMouseHand(boolean newMouseHand) { if (initialState.getMouseHand() != newMouseHand) { initialState.setMouseHand(newMouseHand); raiseAlert(mouseHandChange); } } /** * Fetch the initial frame for the demonstration */ public Frame getStartFrame() { return startFrame; } /** * Fetches whether the start frame is "chosen"; if so, demonstration * can start, if not, then the user should be asked to verify/select * the actual start frame to use. */ public boolean isStartFrameChosen() { return startFrameChosen; } /** * Indicate that the start frame must be chosen; we keep the last * start frame unchanged so that the user sees it selected. */ public void setStartFrameChosen(boolean chosen) { startFrameChosen = chosen; Design design = taskApp.getDesign(); design.raiseAlert(new TaskApplication.TaskApplicationResultChange(taskApp)); } protected boolean isStartFrameInvalid() { Design design = getTaskApplication().getDesign(); return (startFrame != null) && ! design.containsFrame(startFrame); } /** * Set the initial frame for the demonstration */ public void setStartFrame(Frame newStartFrame) { if (startFrame != newStartFrame) { if (isStartFrameInvalid()) { revertEdit(this, INVALIDATING); } startFrame = newStartFrame; if (isStartFrameInvalid()) { noteEdit(INVALIDATING); } raiseAlert(startFrameChange); } } /** * Fetch the frame at which the demonstration ends */ public Frame getResultFrame() { int index = steps.size(); if (index == 0) { return getStartFrame(); } AScriptStep lastStep = steps.get(index - 1); return lastStep.getDestinationFrame(); } /** * Fetch the sequence of contained AScriptStep instances. * Use this to find the number of steps, if desired. */ public List<AScriptStep> getSteps() { return steps; } /** * Return the number of steps in the demonstration. */ public int getStepCount() { return steps.size(); } /** * Get the step at the given index. */ public AScriptStep getStepAt(int atIndex) { if (atIndex < steps.size()) { return steps.get(atIndex); } return null; } /** * Get the last step of the demonstration. */ public AScriptStep getLastStep() { int stepCount = steps.size(); return (stepCount == 0) ? null : steps.get(stepCount - 1); } /** * Generate the steps starting at the given index. */ public Iterator<AScriptStep> getStepsAt(int startIndex) { return steps.listIterator(startIndex); } /** * Fetch the script step following the given one. * If the given one is the last one, <code>null</code> is returned. * The given one may not be <code>null</code>; throws * IllegalStateException if it is. */ public AScriptStep getNextStep(AScriptStep previousStep) { if (previousStep == null) { throw new IllegalStateException("Cannot find step following null"); } int atIndex = steps.indexOf(previousStep); if (atIndex < (steps.size() - 1)) { return steps.get(atIndex + 1); } return null; } /** * Fetch the script step preceding the given one. * If the given one is the first one, <code>null</code> is returned. * The given one may not be <code>null</code>; throws * IllegalStateException if it is. */ public AScriptStep getPreviousStep(AScriptStep nextStep) { if (nextStep == null) { throw new IllegalStateException("Cannot find step preceding null"); } int atIndex = steps.indexOf(nextStep); if (atIndex > 0) { return steps.get(atIndex - 1); } return null; } /** * Insert given step before the given one; if the given one is * <code>null</code>, append. Return the index of the inserted step. * The given step may not be <code>null</code>. */ public int insertStep(AScriptStep step, AScriptStep beforeStep) { int atIndex; if (beforeStep == null) { atIndex = steps.size(); } else { atIndex = steps.indexOf(beforeStep); if (atIndex == -1) { throw new IllegalStateException("Step to insert before must be in the list"); } } insertStep(step, atIndex); return atIndex; } /** * Insert given step at the given index. * The given step may not be <code>null</code>. */ public void insertStep(AScriptStep step, int atIndex) { if (step == null) { throw new IllegalArgumentException("Step to insert may not be null"); } steps.add(atIndex, step); invalidCount += step.invalidatingCount(); obsoleteCount += step.obsoletingCount(); raiseAlert(new Demonstration.ScriptStepChange(this, step, atIndex, Demonstration.ScriptStepChange.ADD_STEP)); } /** * Insert given step at the end of this Demonstration. */ public void appendStep(AScriptStep step) { insertStep(step, null); } /** * Insert given steps at the end of this Demonstration. */ public void appendSteps(Collection<AScriptStep> newSteps) { int atIndex = steps.size(); steps.addAll(newSteps); Iterator<AScriptStep> allNewSteps = newSteps.iterator(); while (allNewSteps.hasNext()) { AScriptStep step = allNewSteps.next(); invalidCount += step.invalidatingCount(); obsoleteCount += step.obsoletingCount(); } raiseAlert(new Demonstration.ScriptStepChange(this, newSteps, atIndex, Demonstration.ScriptStepChange.ADD_STEP)); } /** * Determine the index of the given step. */ public int getStepIndex(AScriptStep step) { return steps.indexOf(step); } /** * Replaces the given step and all subsequent steps with the given list * of steps. If the given step to replace is <code>null</code>, appends * the given list of steps. Enters each replaced step into the given * replacedSteps. Returns the index of the first step replaced. */ public int replaceSteps(AScriptStep stepToReplace, Collection<AScriptStep> newSteps, Collection<AScriptStep> replacedSteps) { int atIndex; if (stepToReplace != null) { atIndex = steps.indexOf(stepToReplace); if (atIndex < 0) { throw new IllegalStateException("Given step is not part of the demonstration"); } ListIterator<AScriptStep> stepsToRemove = steps.listIterator(atIndex); while (stepsToRemove.hasNext()) { AScriptStep stepToRemove = stepsToRemove.next(); replacedSteps.add(stepToRemove); invalidCount -= stepToRemove.invalidatingCount(); obsoleteCount -= stepToRemove.obsoletingCount(); stepsToRemove.remove(); } raiseAlert(new Demonstration.ScriptStepChange(this, replacedSteps, atIndex, Demonstration.ScriptStepChange.REMOVE_STEP)); } else { atIndex = steps.size(); } appendSteps(newSteps); return atIndex; } /** * Replaces all the steps starting at the given index with the * given list of steps. Suitable for implementing undo/redo. */ public void replaceSteps(int atIndex, Collection<AScriptStep> newSteps) { int deleteIndex = steps.size(); while (deleteIndex > atIndex) { AScriptStep removedStep = steps.remove(--deleteIndex); invalidCount -= removedStep.invalidatingCount(); obsoleteCount -= removedStep.obsoletingCount(); } appendSteps(newSteps); } /** * Delete given step, returning its index */ public int removeStep(AScriptStep step) { if (step == null) { throw new IllegalArgumentException("Step to remove may not be null"); } int atIndex = steps.indexOf(step); if (atIndex == -1) { throw new IllegalStateException("Step to remove must be in the list"); } removeStep(atIndex); return atIndex; } /** * Remove the step at the given index. There must be a step at * the given index. */ public void removeStep(int atIndex) { AScriptStep removedStep = steps.remove(atIndex); invalidCount -= removedStep.invalidatingCount(); obsoleteCount -= removedStep.obsoletingCount(); raiseAlert(new Demonstration.ScriptStepChange(this, removedStep, atIndex, Demonstration.ScriptStepChange.REMOVE_STEP)); } /** * Determines if this demonstration uses the given Frame (used by a Project * to invalidate results when the given object changes). */ public boolean usesFrame(Frame frame) { if (startFrame == frame) { return true; } Iterator<AScriptStep> allSteps = steps.iterator(); while (allSteps.hasNext()) { AScriptStep step = allSteps.next(); if (step.usesFrame(frame)) { return true; } } return false; } /** * Checks to see if this demonstration uses the given Widget (used by a * Project to invalidate results when the given object changes). */ public boolean usesWidget(IWidget widget) { if (initialState.usesWidget(widget)) { return true; } Iterator<AScriptStep> allSteps = steps.iterator(); while (allSteps.hasNext()) { AScriptStep step = allSteps.next(); if (step.usesWidget(widget)) { return true; } } return false; } /** * Check to see if this demonstration uses the given Transition (used by a * Project to invalidate results when the given object changes). */ public boolean usesTransition(Transition transition) { Iterator<AScriptStep> allSteps = steps.iterator(); while (allSteps.hasNext()) { AScriptStep step = allSteps.next(); if (step.usesTransition(transition)) { return true; } } return false; } public boolean isInvalid() { return invalidCount > 0; } public boolean isObsolete() { return obsoleteCount > 0; } public void noteEdit(boolean invalidating) { if (invalidating) { invalidCount++; } else { obsoleteCount++; } // During load, the design will be null; ignore Design design = taskApp.getDesign(); if (design != null) { if (statusChange == null) { statusChange = new Demonstration.StatusChange(design); } design.raiseAlert(statusChange); } } public DemonstrationState noteFrameEdit(Frame frame, boolean invalidating) { if (startFrame == frame) { noteEdit(invalidating); return this; } Iterator<AScriptStep> allSteps = steps.iterator(); while (allSteps.hasNext()) { AScriptStep step = allSteps.next(); DemonstrationState state = step.noteFrameEdit(frame, invalidating); if (state != null) { noteEdit(invalidating); return state; } } return null; } public DemonstrationState noteWidgetEdit(IWidget widget, boolean invalidating) { if (! editable) { noteEdit(INVALIDATING); return this; } if (initialState.usesWidget(widget)) { noteEdit(invalidating); return this; } Iterator<AScriptStep> allSteps = steps.iterator(); while (allSteps.hasNext()) { AScriptStep step = allSteps.next(); DemonstrationState state = step.noteWidgetEdit(widget, invalidating); if (state != null) { noteEdit(invalidating); return state; } } return null; } public DemonstrationState noteTransitionEdit(Transition transition, boolean invalidating) { if (! editable) { noteEdit(INVALIDATING); return this; } Iterator<AScriptStep> allSteps = steps.iterator(); while (allSteps.hasNext()) { AScriptStep step = allSteps.next(); // If the step is an ActionScriptStep, then this edit // *has* to be INVALIDATING since some part of the transition's // definition has changed; if the ActionScriptStep uses the // transition, then it shares that definition with the transition // and, therefore, it no longer shares that definition. boolean reallyInvalidating = invalidating || (step instanceof ActionScriptStep); DemonstrationState state = step.noteTransitionEdit(transition, reallyInvalidating); if (state != null) { noteEdit(reallyInvalidating); return state; } } return null; } public void revertEdit(DemonstrationState state, boolean invalidating) { if (state != this) { state.revertEdit(state, invalidating); } if (invalidating) { invalidCount--; } else { obsoleteCount--; } if (statusChange == null) { statusChange = new Demonstration.StatusChange(taskApp.getDesign()); } taskApp.getDesign().raiseAlert(statusChange); } /** * If the demonstration uses any of the given frames, alter its state * based on whether the edit is invalidating or obsoleting. Return a * non-null value if this object uses the given frame. * In this case, we want to return the earliest step possible * (or the demo itself if it uses the frame). */ public DemonstrationState noteFramesEdit(Frame[] frames, boolean invalidating) { int earliestStepIndex = steps.size(); AScriptStep step = null; for (Frame frame : frames) { if (startFrame == frame) { noteEdit(invalidating); return this; } for (int checkIdx = 0; checkIdx < earliestStepIndex; checkIdx++) { AScriptStep checkStep = steps.get(checkIdx); if (checkStep.usesFrame(frame)) { earliestStepIndex = checkIdx; step = checkStep; break; } } } if (step != null) { step.noteEdit(invalidating); noteEdit(invalidating); } return step; } /** * If the demonstration uses any of the given widgets, alter its state * based on whether the edit is invalidating or obsoleting. Return a * non-null value if this object uses the given widget. * In this case, we want to return the earliest step possible * (or the demo itself if it uses the widget in the initial state). */ public DemonstrationState noteWidgetsEdit(Iterator<? extends IWidget> widgets, boolean invalidating) { if (! editable) { noteEdit(INVALIDATING); return this; } int earliestStepIndex = steps.size(); AScriptStep step = null; while (widgets.hasNext()) { IWidget widget = widgets.next(); if (initialState.usesWidget(widget)) { noteEdit(invalidating); return this; } for (int checkIdx = 0; checkIdx < earliestStepIndex; checkIdx++) { AScriptStep checkStep = steps.get(checkIdx); if (checkStep.usesWidget(widget)) { earliestStepIndex = checkIdx; step = checkStep; break; } } } if (step != null) { step.noteEdit(invalidating); noteEdit(invalidating); } return step; } /** * If the demonstration uses any of the given transitions, alter its state * based on whether the edit is invalidating or obsoleting. Return a * non-null value if this object uses the given transition. * In this case, we want to return the earliest step possible. */ public DemonstrationState noteTransitionsEdit(Transition[] transitions, boolean invalidating) { if (! editable) { noteEdit(INVALIDATING); return this; } int earliestStepIndex = steps.size(); AScriptStep step = null; for (Transition transition : transitions) { for (int checkIdx = 0; checkIdx < earliestStepIndex; checkIdx++) { AScriptStep checkStep = steps.get(checkIdx); if (checkStep.usesTransition(transition)) { if (checkIdx == 0) { checkStep.noteEdit(invalidating); noteEdit(invalidating); return checkStep; } earliestStepIndex = checkIdx; step = checkStep; break; } } } if (step != null) { step.noteEdit(invalidating); noteEdit(invalidating); } return step; } /** * Restore the noting of an invalidating/obsoleting edit; the given * IDemonstrationState is either the Demonstration or one of its steps. */ public void restoreEdit(DemonstrationState demoState, boolean invalidating) { if (demoState != this) { demoState.noteEdit(invalidating); } noteEdit(invalidating); } /** * Reset so that all saved obsoleting counts are set to zero. * If the state is still obsolete, it should be added to the given * set to support undo/redo; the count is no longer pertinent. * * @param obsoleteStates holds those states that had saved obsolete counts */ public void restoreConformance(Set<DemonstrationState> obsoleteStates) { if (obsoleteCount > 0) { obsoleteStates.add(this); obsoleteCount = 0; Iterator<AScriptStep> allSteps = steps.iterator(); while (allSteps.hasNext()) { AScriptStep step = allSteps.next(); step.restoreConformance(obsoleteStates); } if (statusChange == null) { statusChange = new Demonstration.StatusChange(taskApp.getDesign()); } taskApp.getDesign().raiseAlert(statusChange); } } /** * Clear the current set of steps (if necessary) and duplicate the * given list of AScriptStep instances. */ public void copySteps(List<AScriptStep> stepsToCopy, TaskApplication.DemoDuplicateScope duplicateScope) { Iterator<AScriptStep> stepsIt = stepsToCopy.iterator(); while (stepsIt.hasNext()) { AScriptStep step = stepsIt.next(); appendStep(step.duplicate(duplicateScope)); } } public int invalidatingCount() { return invalidCount; } public int obsoletingCount() { return obsoleteCount; } public boolean isEditable() { return editable; } public void setEditable(boolean edit) { editable = edit; } }