001/* The node controller for entities 002 003 Copyright (c) 1998-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.actor; 029 030import java.awt.Frame; 031import java.awt.event.ActionEvent; 032import java.awt.event.KeyEvent; 033 034import javax.swing.Action; 035import javax.swing.KeyStroke; 036 037import diva.graph.GraphController; 038import diva.graph.JGraph; 039import diva.gui.GUIUtilities; 040import ptolemy.actor.gui.Configuration; 041import ptolemy.actor.gui.DebugListenerTableau; 042import ptolemy.actor.gui.DialogTableau; 043import ptolemy.actor.gui.Effigy; 044import ptolemy.actor.gui.OpenInstanceDialog; 045import ptolemy.actor.gui.Tableau; 046import ptolemy.actor.gui.TableauFrame; 047import ptolemy.actor.gui.TextEffigy; 048import ptolemy.actor.gui.UserActorLibrary; 049import ptolemy.data.expr.Parameter; 050import ptolemy.data.expr.StringParameter; 051import ptolemy.kernel.CompositeEntity; 052import ptolemy.kernel.Entity; 053import ptolemy.kernel.util.KernelException; 054import ptolemy.kernel.util.NamedObj; 055import ptolemy.util.MessageHandler; 056import ptolemy.vergil.basic.BasicGraphController; 057import ptolemy.vergil.basic.BasicGraphFrame; 058import ptolemy.vergil.basic.LookInsideAction; 059import ptolemy.vergil.debugger.BreakpointDialogFactory; 060import ptolemy.vergil.kernel.AttributeController; 061import ptolemy.vergil.kernel.PortDialogAction; 062import ptolemy.vergil.toolbox.EditIconAction; 063import ptolemy.vergil.toolbox.FigureAction; 064import ptolemy.vergil.toolbox.MenuActionFactory; 065import ptolemy.vergil.toolbox.MenuItemFactory; 066import ptolemy.vergil.toolbox.RemoveIconAction; 067import ptolemy.vergil.toolbox.RotateOrFlipPorts; 068import ptolemy.vergil.unit.ConfigureUnitsAction; 069 070/////////////////////////////////////////////////////////////////// 071//// ActorController 072 073/** 074 * This class provides interaction with nodes that represent Ptolemy II 075 * entities. It provides a double click binding and context menu entry to edit 076 * the parameters of the node ("Configure"), a command to get documentation, and 077 * a command to open an actor. It can have one of two access levels, FULL or 078 * PARTIAL. If the access level is FULL, the the context menu also contains a 079 * command to rename the node and to configure its ports. In addition, a layout 080 * algorithm is applied so that the figures for ports are automatically placed 081 * on the sides of the figure for the entity. 082 * <p> 083 * NOTE: This class is abstract because it is missing the code for laying out 084 * ports. Use the concrete subclasses ActorInstanceController or 085 * ClassDefinitionController instead. 086 * 087 * @author Steve Neuendorffer and Edward A. Lee, Elaine Cheong, Contributor: Sven Koehler 088 * @version $Id$ 089 * @since Ptolemy II 2.0 090 * @Pt.ProposedRating Red (eal) 091 * @Pt.AcceptedRating Red (johnr) 092 * @see ActorInstanceController 093 * @see ClassDefinitionController 094 */ 095public abstract class ActorController extends AttributeController { 096 /** 097 * Create an entity controller associated with the specified graph 098 * controller with full access. 099 * 100 * @param controller 101 * The associated graph controller. 102 */ 103 public ActorController(GraphController controller) { 104 this(controller, FULL); 105 } 106 107 /** 108 * Create an entity controller associated with the specified graph 109 * controller. 110 * 111 * @param controller 112 * The associated graph controller. 113 * @param access 114 * The access level. 115 */ 116 public ActorController(GraphController controller, Access access) { 117 super(controller, access); 118 119 _access = access; 120 121 // "Configure Ports" 122 if (access == FULL) { 123 // Add to the context menu, configure submenu. 124 _portDialogAction = new PortDialogAction("Ports"); 125 _configureMenuFactory.addAction(_portDialogAction, "Customize"); 126 _configureUnitsAction = new ConfigureUnitsAction( 127 "Units Constraints"); 128 _configureMenuFactory.addAction(_configureUnitsAction, "Customize"); 129 } 130 131 // NOTE: The following requires that the configuration be 132 // non-null, or it will report an error. However, in order to 133 // get the "Look Inside" menu to work for composite actors in 134 // Kepler, we create these menu items now. 135 _menuFactory 136 .addMenuItemFactory(new MenuActionFactory(_lookInsideAction)); 137 _menuFactory 138 .addMenuItemFactory(new MenuActionFactory(_openInstanceAction)); 139 140 if (_configuration != null) { 141 if (access == FULL) { 142 // Create an Appearance submenu. 143 _createAppearanceSubmenu(); 144 } 145 } 146 147 /* 148 * The following proves not so useful since atomic actors do not 149 * typically have suitable constructors (that take only a Workspace 150 * argument) to be usable at the top level. 151 * _menuFactory.addMenuItemFactory( new MenuActionFactory(new 152 * SaveInFileAction())); 153 */ 154 // if (((BasicGraphController) getController()).getFrame() != null) { 155 // If we are in an applet, then we have no frame, so no need 156 // for a "Listen to Actor" or "Save in Library" menu choices. 157 // FIXME: this is not perfect, it would be better if we 158 // could just test if we are in an applet or else fix this 159 // so we have a frame. 160 // NOTE: This requires that the configuration be non null, or it 161 // will report an error. 162 _menuFactory.addMenuItemFactory( 163 new MenuActionFactory(new SaveInLibraryAction())); 164 165 // } 166 // "Set Breakpoints" 167 if (access == FULL) { 168 // Add to the context menu. 169 // FIXME: does this work outside of SDF? Should 170 // we check to see if the director is an SDF director? 171 // We should use reflection to check this so that 172 // this class does not require SDFDirector. 173 // See $PTII/doc/coding/debugging.htm 174 _breakpointDialogFactory = new BreakpointDialogFactory( 175 (BasicGraphController) getController()); 176 _menuFactory.addMenuItemFactory(_breakpointDialogFactory); 177 } 178 } 179 180 /////////////////////////////////////////////////////////////////// 181 //// public methods //// 182 183 /** 184 * If access is FULL, then add the jni.ArgumentDailogFactory() to 185 * _menuFactory. If access is not FULL, then do nothing. 186 * 187 * @param menuItemFactory 188 * The MenuItemFactory to be added. 189 */ 190 public void addMenuItemFactory(MenuItemFactory menuItemFactory) { 191 // This method is called by jni.ThalesGraphFrame to add a context 192 // menu. 193 if (_access == FULL) { 194 _menuFactory.addMenuItemFactory(menuItemFactory); 195 } 196 } 197 198 /** 199 * Add hot keys to the actions in the given JGraph. It would be better that 200 * this method was added higher in the hierarchy. Now most controllers 201 * 202 * @param jgraph 203 * The JGraph to which hot keys are to be added. 204 */ 205 @Override 206 public void addHotKeys(JGraph jgraph) { 207 super.addHotKeys(jgraph); 208 GUIUtilities.addHotKey(jgraph, _lookInsideAction); 209 GUIUtilities.addHotKey(jgraph, _openInstanceAction); 210 } 211 212 /** 213 * Set the configuration. This is used to open documentation files. 214 * 215 * @param configuration 216 * The configuration. 217 */ 218 @Override 219 public void setConfiguration(Configuration configuration) { 220 super.setConfiguration(configuration); 221 222 _lookInsideAction.setConfiguration(configuration); 223 224 if (_portDialogAction != null) { 225 _portDialogAction.setConfiguration(configuration); 226 } 227 if (_configureUnitsAction != null) { 228 _configureUnitsAction.setConfiguration(configuration); 229 } 230 231 if (_configuration != null) { 232 if (_access == FULL) { 233 // Create an Appearance submenu. 234 _createAppearanceSubmenu(); 235 } 236 } 237 } 238 239 /////////////////////////////////////////////////////////////////// 240 //// protected methods //// 241 242 /** 243 * Get the class label of the component. 244 * 245 * @return the class label of the component. 246 */ 247 @Override 248 protected String _getComponentType() { 249 return "Actor"; 250 } 251 252 /////////////////////////////////////////////////////////////////// 253 //// protected variables //// 254 255 /** The access level defined in the constructor. */ 256 protected Access _access; 257 258 /** The action that handles edit custom icon. */ 259 protected EditIconAction _editIconAction = new EditIconAction(); 260 261 /** An action that handles flipping the ports horizontally. */ 262 protected RotateOrFlipPorts _flipPortsHorizontal = new RotateOrFlipPorts( 263 RotateOrFlipPorts.FLIP_HORIZONTAL, "Flip Ports Horizontally"); 264 265 /** An action that handles flipping the ports vertically. */ 266 protected RotateOrFlipPorts _flipPortsVertical = new RotateOrFlipPorts( 267 RotateOrFlipPorts.FLIP_VERTICAL, "Flip Ports Vertically"); 268 269 /** 270 * The action that handles opening an actor. This is accessed by by 271 * ActorViewerController to create a hot key for the editor. The name 272 * "lookInside" is historical and preserved to keep backward compatibility 273 * with subclasses. 274 */ 275 protected LookInsideAction _lookInsideAction = new LookInsideAction( 276 "Open Actor"); 277 278 /** 279 * The action that handles opening an instance. 280 */ 281 protected OpenInstanceAction _openInstanceAction = new OpenInstanceAction(); 282 283 /** The action that handles removing a custom icon. */ 284 protected RemoveIconAction _removeIconAction = new RemoveIconAction(); 285 286 /** An action that handles rotating the ports by 90 degrees. */ 287 protected RotateOrFlipPorts _rotatePortsClockwise = new RotateOrFlipPorts( 288 RotateOrFlipPorts.CLOCKWISE, "Rotate Ports Clockwise"); 289 290 /** An action that handles rotating the ports by 90 degrees. */ 291 protected RotateOrFlipPorts _rotatePortsCounterclockwise = new RotateOrFlipPorts( 292 RotateOrFlipPorts.COUNTERCLOCKWISE, 293 "Rotate Ports Counterclockwise"); 294 295 /////////////////////////////////////////////////////////////////// 296 //// private methods //// 297 298 /** 299 * Create an Appearance submenu. 300 */ 301 private void _createAppearanceSubmenu() { 302 _editIconAction.setConfiguration(_configuration); 303 _removeIconAction.setConfiguration(_configuration); 304 Action[] actions = { _editIconAction, _removeIconAction, 305 _flipPortsHorizontal, _flipPortsVertical, _rotatePortsClockwise, 306 _rotatePortsCounterclockwise }; 307 _appearanceMenuActionFactory.addActions(actions, "Appearance"); 308 } 309 310 /////////////////////////////////////////////////////////////////// 311 //// private variables //// 312 313 private BreakpointDialogFactory _breakpointDialogFactory; 314 315 private ConfigureUnitsAction _configureUnitsAction; 316 317 private PortDialogAction _portDialogAction; 318 319 /////////////////////////////////////////////////////////////////// 320 //// inner classes //// 321 322 /////////////////////////////////////////////////////////////////// 323 //// ListenToActorAction 324 325 /** 326 * An action to listen to debug messages in the actor. This is static so 327 * that other classes can use it. 328 */ 329 @SuppressWarnings("serial") 330 public static class ListenToActorAction extends FigureAction { 331 // Kepler uses this action. 332 333 /** Create an action to listen to debug messages. 334 * 335 * @param tableauFrame The associated TableauFrame. 336 */ 337 public ListenToActorAction(TableauFrame tableauFrame) { 338 super("Listen to Actor"); 339 _tableauFrame = tableauFrame; 340 } 341 342 /** Create an action to listen to debug messages in the actor. 343 * 344 * @param controller The controller associated with this action. 345 */ 346 public ListenToActorAction(BasicGraphController controller) { 347 super("Listen to Actor"); 348 _controller = controller; 349 } 350 351 /** Create an action to listen to debug messages in the actor. 352 * 353 * @param target The actor to which to listen. 354 * @param controller The controller associated with this action. 355 */ 356 public ListenToActorAction(NamedObj target, 357 BasicGraphController controller) { 358 super("Listen to Actor"); 359 _target = target; 360 _controller = controller; 361 } 362 363 /** Perform the action. 364 * @param event The action event 365 */ 366 @Override 367 public void actionPerformed(ActionEvent event) { 368 if (_configuration == null && _tableauFrame == null) { 369 MessageHandler.error( 370 "Cannot listen to actor without a configuration."); 371 return; 372 } 373 374 // Determine which entity was selected for the listen to 375 // actor action. 376 super.actionPerformed(event); 377 378 NamedObj object = _target; 379 380 if (object == null) { 381 object = getTarget(); 382 } 383 Tableau tableau = null; 384 try { 385 if (_tableauFrame == null) { 386 BasicGraphFrame frame = _controller.getFrame(); 387 tableau = frame.getTableau(); 388 } else { 389 tableau = _tableauFrame.getTableau(); 390 } 391 392 // effigy is the whole model. 393 Effigy effigy = (Effigy) tableau.getContainer(); 394 395 // We want to open a new window that behaves as a 396 // child of the model window. So, we create a new text 397 // effigy inside this one. Specify model's effigy as 398 // a container for this new effigy. 399 Effigy textEffigy = new TextEffigy(effigy, 400 effigy.uniqueName("debugListener" + object.getName())); 401 402 DebugListenerTableau debugTableau = new DebugListenerTableau( 403 textEffigy, textEffigy.uniqueName( 404 "debugListener" + object.getName())); 405 debugTableau.setDebuggable(object); 406 } catch (KernelException ex) { 407 MessageHandler.error("Failed to create debug listener.", ex); 408 } 409 } 410 411 /** 412 * Set the configuration for use by the help screen. 413 * 414 * @param configuration 415 * The configuration. 416 */ 417 public void setConfiguration(Configuration configuration) { 418 _configuration = configuration; 419 } 420 421 private Configuration _configuration; 422 private BasicGraphController _controller; 423 private NamedObj _target; 424 private TableauFrame _tableauFrame = null; 425 } 426 427 /////////////////////////////////////////////////////////////////// 428 //// OpenInstanceAction 429 430 /** 431 * An action to open an instance. This is similar to LookInsideAction except 432 * that it does not open the class definition, but rather opens the 433 * instance. 434 */ 435 @SuppressWarnings("serial") 436 private class OpenInstanceAction extends FigureAction { 437 public OpenInstanceAction() { 438 super("Open Instance"); 439 putValue(GUIUtilities.ACCELERATOR_KEY, KeyStroke 440 .getKeyStroke(KeyEvent.VK_L, java.awt.Event.ALT_MASK)); 441 } 442 443 @Override 444 public void actionPerformed(ActionEvent event) { 445 if (_configuration == null) { 446 MessageHandler.error("Cannot open an instance " 447 + "without a configuration."); 448 return; 449 } 450 451 // Determine which entity was selected for the open actor action. 452 super.actionPerformed(event); 453 NamedObj object = getTarget(); 454 455 try { 456 StringParameter actorInteractionAddonParameter; 457 actorInteractionAddonParameter = (StringParameter) _configuration 458 .getAttribute("_actorInteractionAddon", 459 Parameter.class); 460 461 if (actorInteractionAddonParameter != null) { 462 String actorInteractionAddonClassName = actorInteractionAddonParameter 463 .stringValue(); 464 465 Class actorInteractionAddonClass = Class 466 .forName(actorInteractionAddonClassName); 467 468 ActorInteractionAddon actorInteractionAddon = (ActorInteractionAddon) actorInteractionAddonClass 469 .newInstance(); 470 471 if (actorInteractionAddon 472 .isActorOfInterestForAddonController(object)) { 473 actorInteractionAddon.openInstanceAction(this, object); 474 } 475 476 } 477 478 } catch (Exception e) { 479 e.printStackTrace(); 480 } 481 482 if (object instanceof CompositeEntity) { 483 try { 484 _configuration.openInstance(object); 485 } catch (Exception ex) { 486 MessageHandler.error("Open instance failed.", ex); 487 } 488 } else if (object instanceof Entity) { 489 // If this is not a CompositeEntity, need to 490 // do something different here as the method above will 491 // open the source code as a last resort. 492 Frame parent = getFrame(); 493 DialogTableau dialogTableau = DialogTableau.createDialog(parent, 494 _configuration, ((TableauFrame) parent).getEffigy(), 495 OpenInstanceDialog.class, (Entity) object); 496 497 if (dialogTableau != null) { 498 dialogTableau.show(); 499 } 500 } 501 } 502 } 503 504 /////////////////////////////////////////////////////////////////// 505 //// SaveInFileAction 506 507 /** 508 * An action to save this actor in a file. 509 */ 510 // private class SaveInFileAction extends FigureAction { 511 // /** Create a new action to save a model in a file. 512 // */ 513 // public SaveInFileAction() { 514 // super("Save Actor In File"); 515 // putValue("tooltip", "Save actor in a file"); 516 // } 517 // 518 // /** Save the target object in a file. 519 // * @param event The action event. 520 // */ 521 // public void actionPerformed(ActionEvent event) { 522 //// Find the target. 523 // super.actionPerformed(event); 524 // 525 // NamedObj object = getTarget(); 526 // 527 // if (object instanceof Entity) { 528 // Entity entity = (Entity) object; 529 // 530 // BasicGraphController controller = (BasicGraphController) getController(); 531 // BasicGraphFrame frame = controller.getFrame(); 532 // 533 // try { 534 // frame.saveComponentInFile(entity); 535 // } catch (Exception e) { 536 // MessageHandler.error("Save failed.", e); 537 // } 538 // } 539 // } 540 // } 541 542 /////////////////////////////////////////////////////////////////// 543 //// SaveInLibraryAction 544 /** 545 * An action to save this actor in the library. 546 */ 547 @SuppressWarnings("serial") 548 private class SaveInLibraryAction extends FigureAction { 549 /** 550 * Create a new action to save an actor in a library. 551 */ 552 public SaveInLibraryAction() { 553 super("Save Actor In Library"); 554 putValue("tooltip", 555 "Save the actor as a component in the user library"); 556 } 557 558 /** 559 * Create a new instance of the current model in the actor library of 560 * the configuration. 561 * 562 * @param event 563 * The action event. 564 */ 565 @Override 566 public void actionPerformed(ActionEvent event) { 567 // Find the target. 568 super.actionPerformed(event); 569 570 NamedObj object = getTarget(); 571 572 if (object instanceof Entity) { 573 Entity entity = (Entity) object; 574 try { 575 UserActorLibrary.saveComponentInLibrary(_configuration, 576 entity); 577 } catch (Exception ex) { 578 // We catch exceptions here because this method used to 579 // not throw Exceptions, and we don't want to break 580 // compatibility. 581 MessageHandler.error( 582 "Failed to save \"" + entity.getName() + "\"."); 583 } 584 } 585 } 586 } 587}