001/* An action for moving an object up or down in its list. 002 003 Copyright (c) 2004-2016 The Regents of the University of California. 004 All rights reserved. 005 Permission is hereby granted, without written agreement and without 006 license or royalty fees, to use, copy, modify, and distribute this 007 software and its documentation for any purpose, provided that the above 008 copyright notice and the following two paragraphs appear in all copies 009 of this software. 010 011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 015 SUCH DAMAGE. 016 017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 022 ENHANCEMENTS, OR MODIFICATIONS. 023 024 PT_COPYRIGHT_VERSION_2 025 COPYRIGHTENDKEY 026 027 */ 028package ptolemy.vergil.toolbox; 029 030import java.awt.Event; 031import java.awt.event.ActionEvent; 032import java.awt.event.KeyEvent; 033import java.util.Iterator; 034import java.util.LinkedList; 035import java.util.List; 036import java.util.ListIterator; 037 038import javax.swing.KeyStroke; 039 040import diva.gui.GUIUtilities; 041import ptolemy.kernel.undo.UndoAction; 042import ptolemy.kernel.undo.UndoStackAttribute; 043import ptolemy.kernel.util.ChangeRequest; 044import ptolemy.kernel.util.IllegalActionException; 045import ptolemy.kernel.util.InternalErrorException; 046import ptolemy.kernel.util.NamedObj; 047import ptolemy.util.MessageHandler; 048 049/////////////////////////////////////////////////////////////////// 050//// MoveAction 051 052/** 053 An action to move an object up or down in its list. 054 This can be used, for example, to move icon elements towards 055 the foreground or to control the order in which attributes 056 or ports appear. 057 058 @author Edward A. Lee 059 @version $Id$ 060 @since Ptolemy II 4.1 061 @Pt.ProposedRating Yellow (eal) 062 @Pt.AcceptedRating Red (johnr) 063 */ 064@SuppressWarnings("serial") 065public class MoveAction extends FigureAction { 066 /** Construct a new action. The type of move is specified by 067 * the public fields DOWN, TO_FIRST, TO_LAST, and UP. 068 * @param description A description. 069 * @param type Indicator of the type of move. 070 */ 071 public MoveAction(String description, MoveType type) { 072 super(description); 073 _type = type; 074 // Add key bindings. 075 // FIXME: these bindings are also set in vergil.basic.BasicGraphFrame. 076 077 // Front and back are a little confusing here. TO_FIRST means 078 // that the element is first in the moml file, which means it is 079 // drawn first and then obscured. 080 081 if (_type == TO_FIRST) { 082 putValue(GUIUtilities.ACCELERATOR_KEY, 083 KeyStroke.getKeyStroke(KeyEvent.VK_B, Event.CTRL_MASK)); 084 } else if (_type == TO_LAST) { 085 putValue(GUIUtilities.ACCELERATOR_KEY, 086 KeyStroke.getKeyStroke(KeyEvent.VK_F, Event.CTRL_MASK)); 087 } 088 } 089 090 /////////////////////////////////////////////////////////////////// 091 //// public variables //// 092 093 /** Indicator for move down. */ 094 public static final MoveType DOWN = new MoveType(); 095 096 /** Indicator for move to first. */ 097 public static final MoveType TO_FIRST = new MoveType(); 098 099 /** Indicator for move to last. */ 100 public static final MoveType TO_LAST = new MoveType(); 101 102 /** Indicator for move up. */ 103 public static final MoveType UP = new MoveType(); 104 105 /////////////////////////////////////////////////////////////////// 106 //// public methods //// 107 108 /** Perform the move action and register the undo action. 109 * @param event The event. 110 */ 111 @Override 112 public void actionPerformed(final ActionEvent event) { 113 // Determine which entity was selected for the look inside action. 114 super.actionPerformed(event); 115 116 final NamedObj target = getTarget(); 117 118 if (target == null) { 119 return; 120 } 121 122 if (target.getDerivedLevel() < Integer.MAX_VALUE) { 123 MessageHandler.error( 124 "Cannot change the position of " + target.getFullName() 125 + " because the position is set by the class."); 126 return; 127 } 128 129 ChangeRequest request = new ChangeRequest(target, "Move towards last") { 130 @Override 131 protected void _execute() throws IllegalActionException { 132 // Static method takes a list, so we construct a 133 // list with one element. 134 LinkedList targets = new LinkedList(); 135 targets.add(target); 136 move(targets, _type, target); 137 } 138 }; 139 140 target.requestChange(request); 141 } 142 143 /** Move the objects in the specified list up or down in the list 144 * of similar objects in their container, as specified by the move type. 145 * If the type is TO_FIRST or UP, then 146 * the objects in the specified list are processed in reverse order, 147 * under the assumption that they will already be sorted into the order 148 * in which they appear in the list of similar objects in their container. 149 * This is factored out as a separate static method so 150 * that it can be called in the redo action and so that it can be 151 * used elsewhere. The context is what is used to register an 152 * undo action. It should be a common container, or if there is 153 * only one target, then the target itself. 154 * @param targets The list of objects to move. 155 * @param type One of DOWN, TO_FIRST, TO_LAST, and UP. 156 * @param context The context. 157 */ 158 public static void move(final List targets, final MoveType type, 159 final NamedObj context) { 160 final int[] priorIndexes = new int[targets.size()]; 161 boolean movedOne = false; 162 163 try { 164 if (type == TO_FIRST || type == UP) { 165 // Traverse the list in reverse order. 166 ListIterator targetIterator = targets 167 .listIterator(targets.size()); 168 169 for (int i = targets.size() - 1; i >= 0; i--) { 170 NamedObj target = (NamedObj) targetIterator.previous(); 171 172 if (type == DOWN) { 173 priorIndexes[i] = target.moveDown(); 174 } else if (type == TO_FIRST) { 175 priorIndexes[i] = target.moveToFirst(); 176 } else if (type == TO_LAST) { 177 priorIndexes[i] = target.moveToLast(); 178 } else { 179 priorIndexes[i] = target.moveUp(); 180 } 181 182 if (priorIndexes[i] >= 0) { 183 movedOne = true; 184 } 185 } 186 } else { 187 // Traverse the list in forward order. 188 Iterator targetIterator = targets.iterator(); 189 190 for (int i = 0; i < targets.size(); i++) { 191 NamedObj target; 192 target = (NamedObj) targetIterator.next(); 193 194 if (type == DOWN) { 195 priorIndexes[i] = target.moveDown(); 196 } else if (type == TO_FIRST) { 197 priorIndexes[i] = target.moveToFirst(); 198 } else if (type == TO_LAST) { 199 priorIndexes[i] = target.moveToLast(); 200 } else { 201 priorIndexes[i] = target.moveUp(); 202 } 203 204 if (priorIndexes[i] >= 0) { 205 movedOne = true; 206 } 207 } 208 } 209 } catch (IllegalActionException e) { 210 // This should only be thrown if the target 211 // has no container, which in theory is not 212 // possible. 213 throw new InternalErrorException(e); 214 } 215 216 if (!movedOne) { 217 // Do not generate any undo action if no move happened. 218 return; 219 } 220 221 UndoAction undoAction = new UndoAction() { 222 @Override 223 public void execute() { 224 try { 225 // Undo has to reverse the order of the do. 226 if (type == TO_FIRST || type == UP) { 227 // Traverse the list in forward order. 228 Iterator targetIterator = targets.iterator(); 229 230 for (int i = 0; i < targets.size(); i++) { 231 NamedObj target = (NamedObj) targetIterator.next(); 232 target.moveToIndex(priorIndexes[i]); 233 } 234 } else { 235 // Traverse the list in reverse order. 236 ListIterator targetIterator = targets 237 .listIterator(targets.size()); 238 239 for (int i = targets.size() - 1; i >= 0; i--) { 240 NamedObj target = (NamedObj) targetIterator 241 .previous(); 242 target.moveToIndex(priorIndexes[i]); 243 } 244 } 245 } catch (IllegalActionException e) { 246 // This should only be thrown if the target 247 // has no container, which in theory is not 248 // possible. 249 throw new InternalErrorException(e); 250 } 251 252 // Create redo action. 253 UndoAction redoAction = new UndoAction() { 254 @Override 255 public void execute() { 256 move(targets, type, context); 257 } 258 }; 259 260 UndoStackAttribute undoInfo = UndoStackAttribute 261 .getUndoInfo(context); 262 undoInfo.push(redoAction); 263 } 264 }; 265 266 UndoStackAttribute undoInfo = UndoStackAttribute.getUndoInfo(context); 267 undoInfo.push(undoAction); 268 } 269 270 /////////////////////////////////////////////////////////////////// 271 //// private variables //// 272 273 /** The type of move. */ 274 private MoveType _type; 275 276 /////////////////////////////////////////////////////////////////// 277 //// inner classes //// 278 279 /** For a type-safe enumeration. */ 280 private static class MoveType { 281 } 282}