/******************************************************************************* * 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.controller; import java.io.File; import java.io.IOException; import edu.cmu.cs.hcii.cogtool.CogTool; import edu.cmu.cs.hcii.cogtool.CogToolLID; import edu.cmu.cs.hcii.cogtool.CogToolPref; import edu.cmu.cs.hcii.cogtool.model.Project; import edu.cmu.cs.hcii.cogtool.ui.Interaction; import edu.cmu.cs.hcii.cogtool.ui.UI; import edu.cmu.cs.hcii.cogtool.util.AListenerAction; import edu.cmu.cs.hcii.cogtool.util.IListenerAction; import edu.cmu.cs.hcii.cogtool.util.RcvrCannotUndoRedoException; import edu.cmu.cs.hcii.cogtool.util.RcvrIOException; import edu.cmu.cs.hcii.cogtool.util.RcvrIOSaveException; import edu.cmu.cs.hcii.cogtool.util.UndoManager; /** * For CogTool, this class provides default implementations for standard * Controller semantic actions that all windows editing CogTool model * objects should support (specifically excluding, for example, the * "invisible" root window when executing on a Mac). * <p> * Semantic actions and parameters (in addition to <code>AController</code>): * SaveProject <no parameters> * SaveProjectAs <no parameters> * CloseWindow <no parameters> * <p> * Note that CloseWindow is not in AController because one should not * close the "invisible" root window without explicitly exiting CogTool. * * @author mlh */ public abstract class DefaultController extends Controller { protected Project project; // Note that all concrete subclasses are expected // to initialize undoMgr. protected UndoManager undoMgr = null; /** * Constructor; all CogTool model editing controllers keep track * of the <code>Project</code> instance that contains (or is) the * model object being edited. * * @param proj the Project instance that is or contains the model * object being edited * @author mlh */ public DefaultController(Project proj) { super(); project = proj; } /** * Returns the <code>Project</code> instance that contains (or is) the * model object being edited. * * @return the Project instance that contains (or is) the * model object being edited * @author mlh */ protected Project getProject() { return project; } /** * Registers the set of <code>IListenerAction</code> instances * that implement the semantic actions that are possible. * <p> * For this class, this consists of the actions that all CogTool * model editing windows support. * * @author mlh */ @Override protected void assignActions() { super.assignActions(); UI ui = getUI(); final Interaction interaction = getUI().getStandardInteraction(); if (ui != null) { // Set "save as" action ui.setAction(CogToolLID.SaveProjectAs, new AListenerAction() { public boolean performAction(Object prms) { return saveAs(); } }); // Set "save" action ui.setAction(CogToolLID.SaveProject, new AListenerAction() { public boolean performAction(Object prms) { return forceSave(); } }); ui.setAction(CogToolLID.CloseWindow, createCloseWindowAction()); ui.setAction(CogToolLID.CloseProject, new AListenerAction() { public boolean performAction(Object prms) { return closeProject(project, true); } }); ui.setAction(CogToolLID.Properties, new AListenerAction() { public boolean performAction(Object prms) { return showProperties(); } }); ui.setAction(CogToolLID.SetAttribute, new IListenerAction() { public Class<?> getParameterClass() { return UI.SetAttributeParameters.class; } public boolean performAction(Object prms) { UI.SetAttributeParameters p = (UI.SetAttributeParameters) prms; return DefaultCmd.setAttribute(p.target, null, p.attrName, p.value, interaction, undoMgr); } }); } } /** * If the model has a stored save location, "Save", else "Save As...". * * @return true if file was saved, false otherwise */ protected boolean forceSave() { // If the project has been saved in the past, no need to ask // for a location. if (persist.isPermanent(project)) { return save(); } return saveAs(); } /** * Perform a Save operation, using a stored save location. * * @return true if file was saved, false otherwise * @throws RcvrIOException if the save operation fails */ protected boolean save() { try { // Save to this file's original location. project.setBuildVersion(CogTool.getVersion()); persist.save(project, null); // Tell undo manager(s) that a save has just occurred try { UndoManager.markSavePoint(project); } catch (IllegalStateException ex) { throw new RcvrCannotUndoRedoException("Marking save point", ex); } return true; } catch (IOException e) { throw new RcvrIOSaveException("Error persisting project: " + e.getMessage(), e); } } /** * Perform a Save As... operation, prompting for a save location. * * @return true if file was saved, false otherwise * @throws RcvrIOException if the save operation fails */ protected boolean saveAs() { Interaction stdInteraction = getUI().getStandardInteraction(); try { boolean nameIsInUse; File saveLoc; do { // Request a new file name, using the project's current name // as the default. // TODO: It is "evil" that the test for existing files is // hidden within this call; separate at some point (mlh) saveLoc = stdInteraction.selectFileDest(project.getName()); // If the save dialog was canceled, do nothing. if (saveLoc == null) { return false; } // If saveLoc is already open, refuse to save there. nameIsInUse = persist.isRegistered(saveLoc, project); if (nameIsInUse) { switch (stdInteraction.protestBeingEdited(saveLoc)) { case Interaction.SAVE: { nameIsInUse = false; break; } case Interaction.NO_SAVE: { // Simply loop to ask for a new name break; } case Interaction.CANCEL: { return false; } } } } while (nameIsInUse); CogToolPref.setRecent(saveLoc.getCanonicalPath()); // Set the title BEFORE saving the project. String name = saveLoc.getName(); // Remove the .cgt, if it is there. int periodIndex = name.lastIndexOf('.'); if (periodIndex > 0) { name = name.substring(0, periodIndex); } project.setName(name); // Save to the selected location. project.setBuildVersion(CogTool.getVersion()); persist.save(project, saveLoc); // Tell undo manager(s) that a save has just occurred try { UndoManager.markSavePoint(project); } catch (IllegalStateException ex) { throw new RcvrCannotUndoRedoException("Marking save point", ex); } return true; } catch (IOException e) { throw new RcvrIOSaveException("Error persisting project: " + e.getMessage(), e); } } public void saveAsFilename(String pathname) { File f = new File(pathname); project.setName(f.getName()); project.setBuildVersion(CogTool.getVersion()); try { persist.save(project, f); } catch (IOException e) { throw new RcvrIOSaveException("Error persisting project: " + e.getMessage(), e); } save(); } /** * Return the primary model object this controller is responsible for * editing. * * @return the primary model object this controller is responsible for * editing * @author mlh */ protected abstract Object getModelObject(); /** * Close the window associated with this controller. * <p> * If the associated <code>Project</code> instance or any of its * "children" have been modified, we may want to try to save the changes. * <p> * Two policies have been implemented: (1) if this is the last window * open for the associated project, then check to save; OR (2) if this * is the project's window (that is, a <code>ProjectController</code>), * then check to save. This policy is controlled by the * <code>PROJECT_MANAGES_OTHERS</code> flag in <code>CogTool</code>; * if true, then policy (2) is used, otherwise (1) is used. * <p> * Note: Policy (2) assumes that all other open editor windows managing * objects belonging to the project are closed when its window is closed * (see <code>ProjectController.dispose()</code>). * <p> * If saving, the user is asked whether to save the associated project. * One response is to abort/cancel the window closing. * <p> * Sometimes, a window may be closed in the process of opening another * window (such as when the Frame Chooser window is closed to open the * corresponding Script Demonstration window). In this case, we don't * want to check to save -- thus, the parameter. * * @param checkToSave whether to check to save the associated project * @return true if and only if the close was approved and successful * @throws RcvrIOSaveException if the save operation fails * TODO: Change the exception to one that should be caught and managed! * @author mlh */ protected boolean closeWindow(boolean checkToSave) { // If checking to save, determine policy. If the project manages // the other windows, then check if this is the project's editor // window. If the policy is otherwise, then simply check whether // this is the last open window for objects associated with this // project (regardless of whether it is the ProjectController). if (checkToSave && ((CogTool.projectManagesOthers && (project == getModelObject())) || // this: ProjectController (CogTool.controllerNexus.getControllerCount(project) == 1))) { boolean needToAsk; try { needToAsk = ! UndoManager.isAtSavePoint(project); } catch (IllegalStateException ex) { System.err.println("Ignoring that isAtSavePoint failed."); needToAsk = true; } if (needToAsk) { Interaction interaction = getUI().getStandardInteraction(); // The related project and/or "children" are modified; // ask to save. switch (interaction.askSaveBeforeClose(project.getName())) { case Interaction.SAVE: { // Try to save; user may get the chance to abort/cancel if (! forceSave()) { return false; // close aborted/canceled } // ok to close break; } case Interaction.NO_SAVE: { break; // ok to close } case Interaction.CANCEL: { return false; // aborted/canceled } } } // Close window and recover resources. dispose(); recoverManagers(project); } else { // No need to attempt to save (perhaps not modified). // Close window and recover resources. dispose(); } return true; } // closeWindow protected IListenerAction createCloseWindowAction() { return new AListenerAction() { public boolean performAction(Object prms) { return closeWindow(true); } }; } protected boolean showProperties() { Interaction interaction = getUI().getStandardInteraction(); interaction.showProjectProperties(project); return true; } protected void recoverManagers(Object editObject) { UndoManagerRecovery.recoverManagers(project, editObject); } /** * Close the associated window, recovering all associated resources. */ @Override public void dispose() { ControllerRegistry.ONLY.removeOpenController(this); super.dispose(); } }