001/* 002 * Copyright (c) 2009-2010 The Regents of the University of California. 003 * All rights reserved. 004 * 005 * '$Author: crawl $' 006 * '$Date: 2015-08-19 23:46:56 +0000 (Wed, 19 Aug 2015) $' 007 * '$Revision: 33599 $' 008 * 009 * Permission is hereby granted, without written agreement and without 010 * license or royalty fees, to use, copy, modify, and distribute this 011 * software and its documentation for any purpose, provided that the above 012 * copyright notice and the following two paragraphs appear in all copies 013 * of this software. 014 * 015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 019 * SUCH DAMAGE. 020 * 021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 026 * ENHANCEMENTS, OR MODIFICATIONS. 027 * 028 */ 029 030package org.kepler.gui.frame; 031 032import java.awt.event.ActionEvent; 033import java.awt.event.KeyEvent; 034import java.util.HashSet; 035import java.util.Iterator; 036import java.util.List; 037import java.util.Set; 038 039import org.kepler.gui.state.StateChangeEvent; 040import org.kepler.gui.state.StateChangeListener; 041import org.kepler.gui.state.StateChangeMonitor; 042 043import ptolemy.actor.CompositeActor; 044import ptolemy.actor.gui.PtolemyEffigy; 045import ptolemy.actor.lib.hoc.MultiCompositeActor; 046import ptolemy.actor.lib.hoc.Refinement; 047import ptolemy.gui.ComponentDialog; 048import ptolemy.gui.Query; 049import ptolemy.kernel.CompositeEntity; 050import ptolemy.kernel.Port; 051import ptolemy.kernel.util.IllegalActionException; 052import ptolemy.kernel.util.NameDuplicationException; 053import ptolemy.kernel.util.Nameable; 054import ptolemy.kernel.util.NamedObj; 055import ptolemy.kernel.util.Workspace; 056import ptolemy.moml.LibraryAttribute; 057import ptolemy.moml.MoMLChangeRequest; 058import ptolemy.util.MessageHandler; 059import ptolemy.util.StringUtilities; 060import ptolemy.vergil.toolbox.FigureAction; 061 062////////////////////////////////////////////////////////////////////////// 063//// MultiCompositeTableau 064 065/** An editor tableau for MultiCompositeActors. 066 * 067 @author Daniel Crawl, Possibly based on CaseGraphTableau by Edward A. Lee. 068 @version $Id: MultiCompositeTableau.java 33599 2015-08-19 23:46:56Z crawl $ 069 */ 070public abstract class MultiCompositeTableau extends TabbedKeplerGraphTableau implements StateChangeListener { 071 /** Create a new multicomposite editor tableau with the specified 072 * container and name. 073 * @param container The container. 074 * @param name The name. 075 * @exception IllegalActionException If the model associated with 076 * the container effigy is not an instance of CompositeEntity. 077 * @exception NameDuplicationException If the container already 078 * contains an object with the specified name. 079 */ 080 public MultiCompositeTableau(PtolemyEffigy container, String name) 081 throws IllegalActionException, NameDuplicationException { 082 this(container, name, null); 083 } 084 085 /** Create a new multicomposite editor tableau with the specified 086 * container, name, and default library. 087 * @param container The container. 088 * @param name The name. 089 * @param defaultLibrary The default library, or null to not specify one. 090 * @exception IllegalActionException If the model associated with 091 * the container effigy is not an instance of CompositeEntity. 092 * @exception NameDuplicationException If the container already 093 * contains an object with the specified name. 094 */ 095 public MultiCompositeTableau(PtolemyEffigy container, String name, 096 LibraryAttribute defaultLibrary) throws IllegalActionException, 097 NameDuplicationException { 098 super(container, name); 099 } 100 101 /////////////////////////////////////////////////////////////////// 102 //// public methods //// 103 104 /** Clone the object into the specified workspace. */ 105 @Override 106 public Object clone(Workspace workspace) throws CloneNotSupportedException { 107 MultiCompositeTableau newObject = (MultiCompositeTableau) super.clone(workspace); 108 newObject._multiCompositeActor = null; 109 return newObject; 110 } 111 112 @Override 113 public void createGraphFrame(CompositeEntity model, 114 LibraryAttribute defaultLibrary) 115 { 116 TabbedKeplerGraphFrame frame = null; 117 118 frame = _addCompositeToExistingFrame(model, defaultLibrary); 119 _createdNewFrame = false; 120 121 _configureFrame(frame, model); 122 123 } 124 125 /** React to changes in the selected tab in a TabbedKeplerGraphFrame. */ 126 @Override 127 public void handleStateChange(StateChangeEvent event) { 128 129 if(event instanceof TabbedKeplerGraphFrameEvent) { 130 131 TabbedKeplerGraphFrameEvent frameEvent = (TabbedKeplerGraphFrameEvent)event; 132 133 // see if the event is from the frame that this tableau created 134 // and the event is for a tab change. 135 if(frameEvent.getFrame() == _frame && 136 frameEvent.getChangedState().equals(TabbedKeplerGraphFrameEvent.CHANGE_TAB)) { 137 138 NamedObj model = frameEvent.getReference(); 139 140 boolean multiCompositeIsNowSelected = 141 (model == _multiCompositeActor || 142 model.getContainer() == _multiCompositeActor); 143 144 // see if the tab containing our MultiCompositeActor is now selected 145 if(!_multiCompositeIsSelected && multiCompositeIsNowSelected) { 146 _multiCompositeSelected(true); 147 } 148 // see if the tab containing our MultiCompositeActor was selected 149 // and now is not selected. 150 else if(_multiCompositeIsSelected && !multiCompositeIsNowSelected) { 151 _multiCompositeSelected(false); 152 } 153 } 154 } 155 } 156 157 /** Set the container for this tableau. If the tab for the MultiCompositeActor 158 * is closed, the container will be null. In this case, stop listening for 159 * tab changes, and the tab for the MultiCompositeActor is no longer selected. 160 */ 161 @Override 162 public void setContainer(CompositeEntity container) throws IllegalActionException, NameDuplicationException { 163 164 // see if the graph was closed. 165 if(container == null) { 166 167 // stop listening for when the graph's tab is selected 168 StateChangeMonitor.getInstance(). 169 removeStateChangeListener(TabbedKeplerGraphFrameEvent.CHANGE_TAB, this); 170 171 // see if the graph is currently selected 172 if(_multiCompositeIsSelected) { 173 _multiCompositeSelected(false); 174 } 175 } 176 177 super.setContainer(container); 178 } 179 180 /////////////////////////////////////////////////////////////////// 181 //// protected methods //// 182 183 184 /** Configure the frame for the opened multi composite model. */ 185 @Override 186 protected void _configureFrame(TabbedKeplerGraphFrame frame, CompositeEntity model) { 187 188 super._configureFrame(frame, model); 189 190 _multiCompositeActor = model; 191 _frame = frame; 192 193 // listen for state change events so we know then the tab is changed. 194 StateChangeMonitor.getInstance(). 195 addStateChangeListener(TabbedKeplerGraphFrameEvent.CHANGE_TAB, this); 196 197 // the tab has just been created and is selected for the multi composite 198 _multiCompositeSelected(true); 199 } 200 201 202 /** This method is called whenever the tab containing the MultiCompositeActor 203 * is selected or unselected in the frame. 204 */ 205 protected void _multiCompositeSelected(boolean isSelected) { 206 _multiCompositeIsSelected = isSelected; 207 } 208 209 /////////////////////////////////////////////////////////////////// 210 //// protected variables //// 211 212 /** The frame displaying the MultiCompositeActor. */ 213 protected TabbedKeplerGraphFrame _frame; 214 215 /////////////////////////////////////////////////////////////////// 216 //// private variables //// 217 218 /** The MultiCompositeActor associated with this tableau. */ 219 private CompositeEntity _multiCompositeActor; 220 221 /** If true, the tab displaying the MultiCompositeActor is selected. */ 222 private boolean _multiCompositeIsSelected; 223 224 /////////////////////////////////////////////////////////////////// 225 //// inner classes //// 226 227 /** Class implementing the Add Refinement menu command. */ 228 public abstract class AddRefinementAction extends FigureAction { 229 230 /** Create an action. 231 * @param title the window title 232 * @param refinementTypeName the type of sub-workflow to add 233 * @param nameQuery the string displayed in the dialog 234 */ 235 public AddRefinementAction(String title, String refinementTypeName, 236 String nameQuery) { 237 super(title); 238 _title = title; 239 _refinementTypeName = refinementTypeName; 240 _nameQuery = nameQuery; 241 putValue(MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_A)); 242 } 243 244 /////////////////////////////////////////////////////////////////////////////// 245 //// public methods //// 246 247 /** Perform the action. */ 248 @Override 249 public void actionPerformed(ActionEvent e) { 250 super.actionPerformed(e); 251 // Dialog to ask for a refinement name. 252 Query query = new Query(); 253 query.addLine(_refinementTypeName, _nameQuery, ""); 254 ComponentDialog dialog = new ComponentDialog(_frame, 255 _title, query); 256 if (dialog.buttonPressed().equals("OK")) { 257 final String pattern = query.getStringValue(_refinementTypeName); 258 // NOTE: We do not use a TransitionRefinement because we don't 259 // want the sibling input ports that come with output ports. 260 261 // FIXME: refinementClassName() should be a method of MultiCompositeActor 262 //String refinementClassName = _case.refinementClassName(); 263 String refinementClassName = "ptolemy.actor.lib.hoc.Refinement"; 264 265 String moml = "<entity name=\"" 266 + StringUtilities.escapeForXML(pattern) + "\" class=\"" 267 + refinementClassName + "\"/>"; 268 269 // The following is, regrettably, copied from ModalTransitionController. 270 MoMLChangeRequest change = new MoMLChangeRequest(this, _multiCompositeActor, 271 moml) { 272 @Override 273 protected void _execute() throws Exception { 274 super._execute(); 275 276 277 // Mirror the ports of the container in the refinement. 278 // Note that this is done here rather than as part of 279 // the MoML because we have set protected variables 280 // in the refinement to prevent it from trying to again 281 // mirror the changes in the container. 282 Refinement entity = (Refinement) _multiCompositeActor 283 .getEntity(pattern); 284 285 // Get the initial port configuration from the container. 286 Iterator<?> ports = ((CompositeActor)entity.getContainer()).portList().iterator(); 287 Set<Port> portsToMirror = new HashSet<Port>(); 288 while (ports.hasNext()) { 289 Port port = (Port) ports.next(); 290 291 // see if we should mirror the port 292 if(mirrorPort(port)) { 293 portsToMirror.add(port); 294 } 295 } 296 297 MultiCompositeActor.mirrorContainerPortsInRefinement(entity, portsToMirror); 298 299 //JGraph jgraph = _addTabbedPane(entity, true); 300 _frame.addSubComposite(entity); 301 //((TabbedKeplerController) _controller)._addHotKeys(jgraph); 302 } 303 }; 304 305 _multiCompositeActor.requestChange(change); 306 } 307 } 308 309 /** Returns true if port should be mirrored. */ 310 public abstract boolean mirrorPort(Port port); 311 312 /////////////////////////////////////////////////////////////////// 313 //// private variables //// 314 315 /** The type of the sub-workflow. */ 316 private String _refinementTypeName; 317 318 /** The string displayed in the dialog. */ 319 private String _nameQuery; 320 321 /** The title of the window. */ 322 private String _title; 323 } 324 325 /** Class implementing the Remove Refinement menu command. */ 326 public class RemoveRefinementAction extends FigureAction { 327 328 /** Create a tabbed composite action with a label. */ 329 public RemoveRefinementAction(String refinementTypeName, 330 String refinementTypePluralName) { 331 super("Remove " + refinementTypeName); 332 _refinementTypeName = refinementTypeName; 333 _refinementTypePluralName = refinementTypePluralName; 334 putValue(MNEMONIC_KEY, Integer.valueOf(KeyEvent.VK_R)); 335 } 336 337 /////////////////////////////////////////////////////////////////////////////// 338 //// public methods //// 339 340 /** Perform the action. */ 341 @Override 342 public void actionPerformed(ActionEvent e) { 343 super.actionPerformed(e); 344 // Dialog to ask for a refinement name. 345 Query query = new Query(); 346 List<?> refinements = _multiCompositeActor.entityList(Refinement.class); 347 if (refinements.size() < 2) { 348 MessageHandler.error("No " + _refinementTypePluralName + " to remove."); 349 } else { 350 String[] refinementNames = new String[refinements.size() - 1]; 351 Iterator<?> refinementIterator = refinements.iterator(); 352 int i = 0; 353 while (refinementIterator.hasNext()) { 354 String name = ((Nameable) refinementIterator.next()).getName(); 355 if (!name.equals("default")) { 356 refinementNames[i] = name; 357 i++; 358 } 359 } 360 String title = "Remove " + _refinementTypeName; 361 query.addChoice(_refinementTypeName, title, refinementNames, 362 refinementNames[0]); 363 ComponentDialog dialog = new ComponentDialog( 364 _frame, title, query); 365 if (dialog.buttonPressed().equals("OK")) { 366 final String name = query.getStringValue(_refinementTypeName); 367 String moml = "<deleteEntity name=\"" 368 + StringUtilities.escapeForXML(name) + "\"/>"; 369 370 // The following is, regrettably, copied from ModalTransitionController. 371 MoMLChangeRequest change = new MoMLChangeRequest(this, 372 _multiCompositeActor, moml) { 373 @Override 374 protected void _execute() throws Exception { 375 super._execute(); 376 _frame.updateTabsForComposite(_multiCompositeActor); 377 /* 378 // Find the tabbed pane that matches the name and remove it. 379 int count = _multiRefinementTabbedPane.getTabCount(); 380 for (int i = 0; i < count; i++) { 381 if (name.equals(_multiRefinementTabbedPane.getTitleAt(i))) { 382 _multiRefinementTabbedPane.remove(i); 383 break; 384 } 385 } 386 */ 387 } 388 }; 389 _multiCompositeActor.requestChange(change); 390 } 391 } 392 } 393 394 /////////////////////////////////////////////////////////////////// 395 //// private variables //// 396 397 /** The type of sub-workflow. */ 398 private String _refinementTypeName; 399 400 /** The plural of the type of sub-workflow. */ 401 private String _refinementTypePluralName; 402 403 } 404 405}