001/* An object that represents a graphical view of a ptolemy model. 002 003 Copyright (c) 1998-2014 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 */ 027package ptolemy.actor.gui; 028 029import java.awt.Frame; 030import java.awt.Window; 031import java.awt.event.WindowAdapter; 032import java.awt.event.WindowEvent; 033 034import javax.swing.JFrame; 035 036import ptolemy.gui.MemoryCleaner; 037import ptolemy.gui.Top; 038import ptolemy.kernel.CompositeEntity; 039import ptolemy.kernel.util.Attribute; 040import ptolemy.kernel.util.IllegalActionException; 041import ptolemy.kernel.util.KernelException; 042import ptolemy.kernel.util.NameDuplicationException; 043import ptolemy.kernel.util.Workspace; 044import ptolemy.util.CancelException; 045import ptolemy.util.MessageHandler; 046import ptolemy.util.StringUtilities; 047 048/////////////////////////////////////////////////////////////////// 049//// Tableau 050 051/** 052 A tableau is a visual representation of a Ptolemy II model in a top-level 053 window. This class represents such a top level window. The top-level 054 is always a frame, which is a window with a border and title bar. The 055 window itself is specified by the setFrame() method, and accessed by 056 the getFrame() method. An instance of this class will be contained 057 by the instance of Effigy that represents the model that is depicted 058 in the top-level window. 059 <p> 060 By convention, the constructor for a tableau does not (necessarily) 061 make the associated frame visible. To do that, call show(). 062 063 @author Steve Neuendorffer and Edward A. Lee 064 @version $Id$ 065 @since Ptolemy II 1.0 066 @Pt.ProposedRating Yellow (neuendor) 067 @Pt.AcceptedRating Red (neuendor) 068 @see Effigy 069 */ 070public class Tableau extends CompositeEntity { 071 /** Construct a tableau in the specified workspace. 072 * @param workspace The workspace. 073 * @exception IllegalActionException If an error occurs creating 074 * the size attribute (should not occur). 075 * @exception NameDuplicationException If the base class has already 076 * created an attribute with name "size" (should not occur). 077 */ 078 public Tableau(Workspace workspace) 079 throws IllegalActionException, NameDuplicationException { 080 super(workspace); 081 082 size = new SizeAttribute(this, "size"); 083 } 084 085 /** Construct a tableau with the given name and container. 086 * @param container The container. 087 * @param name The name of the tableau. 088 * @exception IllegalActionException If the tableau cannot be contained 089 * by the proposed container. 090 * @exception NameDuplicationException If the name coincides with 091 * an entity already in the container. 092 */ 093 public Tableau(CompositeEntity container, String name) 094 throws IllegalActionException, NameDuplicationException { 095 super(container, name); 096 097 size = new SizeAttribute(this, "size"); 098 } 099 100 /////////////////////////////////////////////////////////////////// 101 //// public parameters //// 102 103 /** A specification for the size of the frame. 104 */ 105 public SizeAttribute size; 106 107 /////////////////////////////////////////////////////////////////// 108 //// public methods //// 109 110 /** If the argument is the <i>size</i> parameter, and a 111 * frame has been specified with setFrame(), then set the size 112 * of the frame. 113 * @param attribute The attribute that changed. 114 * @exception IllegalActionException If the size 115 * specification is not correctly formatted, or if the base 116 * class throws it. 117 */ 118 @Override 119 public void attributeChanged(Attribute attribute) 120 throws IllegalActionException { 121 if (attribute == size && _frame != null) { 122 size.setSize(_frame); 123 } else { 124 super.attributeChanged(attribute); 125 } 126 } 127 128 /** Clone the object into the specified workspace. This calls the 129 * base class and then sets the associated frame to null. 130 * Thus, the resulting tableau has no frame associated with it. 131 * @param workspace The workspace for the new object. 132 * @return A new object. 133 * @exception CloneNotSupportedException If a derived class contains 134 * an attribute that cannot be cloned. 135 */ 136 @Override 137 public Object clone(Workspace workspace) throws CloneNotSupportedException { 138 Tableau newObject = (Tableau) super.clone(workspace); 139 newObject._frame = null; 140 return newObject; 141 } 142 143 /** Close this tableau by calling dispose() on the associated 144 * frame, or if the associated frame is an instance of TableauFrame, 145 * by calling _close() on it. 146 * @return False if the user cancels on a save query. 147 */ 148 public boolean close() { 149 if (_debugClosing) { 150 System.out.println("Tableau.close() : " + getFrame().getName()); 151 } 152 153 JFrame frame = getFrame(); 154 155 if (frame instanceof TableauFrame) { 156 // NOTE: Calling a protected method, but this class is in the 157 // same package. 158 if (!((TableauFrame) frame)._close()) { 159 return false; 160 } 161 } else if (this instanceof DialogTableau 162 && frame instanceof PortConfigurerDialog) { 163 if (!((PortConfigurerDialog) frame).close()) { 164 return false; 165 } 166 } else if (frame != null) { 167 frame.dispose(); 168 } 169 170 return true; 171 } 172 173 /** Return the top-level window that implements the display of 174 * this tableau. 175 * @return A top-level window. 176 * @see #setFrame(JFrame) 177 */ 178 public JFrame getFrame() { 179 return _frame; 180 } 181 182 /** Return the title of this tableau. Subclasses can override this to 183 * provide a better description of themselves for use in the title. 184 * This base class returns the value set by a call to setTitle(), 185 * if it has been called. If not, then it returns an identifier 186 * of the effigy containing this tableau, or the string "Unnamed" 187 * if there is no such identifier. 188 * The title is used as the title of the top-level window in 189 * the setFrame() method. 190 * @return The title to put on the window. 191 * @see #setTitle(String) 192 */ 193 public String getTitle() { 194 if (_title == null) { 195 Effigy effigy = (Effigy) getContainer(); 196 197 // Abbreviate the title to 80 chars for use on the Mac. 198 return StringUtilities 199 .abbreviate(effigy.identifier.getExpression()); 200 } else { 201 return _title; 202 } 203 } 204 205 /** Return true if the tableau is editable. This base class returns 206 * whatever value has been set by setEditable(), or <i>true</i> if 207 * none has been specified. 208 * @see #setEditable(boolean) 209 * @return True if the tableau is editable. 210 */ 211 public boolean isEditable() { 212 return _editable; 213 } 214 215 /** Return true if this tableau is a master, which means that 216 * if that if its window is closed, then all other windows associated 217 * with the model are also closed. A tableau is a master if its 218 * container effigy is a master (its masterEffigy() method returns 219 * itself). 220 * @return True if the tableau is a master. 221 */ 222 public boolean isMaster() { 223 return _master; 224 } 225 226 /** Override the base class so that if the argument is null and the 227 * window is a master, then all other windows associated with the 228 * container are closed and the model is removed from the ModelDirectory. 229 * If this window is not a master, but after removing it there are 230 * no more windows associated with the model, then also remove it 231 * from the ModelDirectory. 232 * @param container The container to attach this attribute to. 233 * @exception IllegalActionException If the proposed container is not 234 * an instance of Effigy, or if this attribute is not of the 235 * expected class for the container, or it has no name, 236 * or the attribute and container are not in the same workspace, or 237 * the proposed container would result in recursive containment. 238 * @exception NameDuplicationException If the container already has 239 * an attribute with the name of this attribute. 240 */ 241 @Override 242 public void setContainer(CompositeEntity container) 243 throws IllegalActionException, NameDuplicationException { 244 if (container == null) { 245 Effigy oldContainer = (Effigy) getContainer(); 246 super.setContainer(/* container*/null); 247 248 // Blow away the frame. 249 if (_frame != null) { 250 if (!isMaster()) { 251 if (_frame instanceof Top) { 252 Top top = (Top) _frame; 253 if (!top.isDisposed()) { 254 top.dispose(); 255 } 256 } 257 } 258 } 259 260 if (isMaster() && oldContainer != null) { 261 // Window is a master. Close the model which will close all 262 // other tableaux. 263 oldContainer.setContainer(null); 264 } 265 } else if (container instanceof Effigy) { 266 super.setContainer(container); 267 } else { 268 throw new IllegalActionException(this, container, 269 "The container can only be set to an " 270 + "instance of Effigy"); 271 } 272 } 273 274 /** Make the tableau editable or uneditable. Notice that this does 275 * not change whether the effigy is modifiable, so other tableaux 276 * on the same effigy may still modify the associated file. 277 * Derived class will usually need to override this method to 278 * set whether their associated interfaces are editable or not. 279 * They should call this superclass method so that isEditable() 280 * returns the value specified here. 281 * @see #isEditable() 282 * @param flag False to make the tableau uneditable. 283 */ 284 public void setEditable(boolean flag) { 285 _editable = flag; 286 } 287 288 /** Set the top-level window associated with this tableau. 289 * @param frame The top-level window associated with the tableau. 290 * @exception IllegalActionException If the frame is not acceptable 291 * (not thrown in this base class). 292 * @see #getFrame() 293 */ 294 public void setFrame(JFrame frame) throws IllegalActionException { 295 if (_frame == frame) { 296 return; 297 } 298 if (frame == null) { 299 _frame = null; 300 return; 301 } 302 303 _frame = frame; 304 305 size.setSize(frame); 306 307 // Truncate the name so that dialogs under Web Start on the Mac 308 // work better. 309 frame.setTitle(getTitle()); 310 311 // Set up a listener for window closing events. 312 _windowClosedAdapter = new WindowClosedAdapter(); 313 frame.addWindowListener(_windowClosedAdapter); 314 } 315 316 /** Specify whether the window associated with this tableau 317 * is a master, which means that if that window is closed, then 318 * all windows associated with the model are closed. 319 * @param flag If true, makes the window a master. 320 */ 321 public void setMaster(boolean flag) { 322 _master = flag; 323 } 324 325 /** Set the title of this tableau, changing the title of the 326 * associated top-level window. Call this with a null argument 327 * to use the identifier of the containing effigy as a title. 328 * @param title The title to put on the window. 329 * @see #getTitle() 330 */ 331 public void setTitle(String title) { 332 _title = StringUtilities.abbreviate(title); 333 334 if (_frame != null) { 335 _frame.setTitle(getTitle()); 336 } 337 } 338 339 /** Make this tableau visible by calling setVisible(true), and 340 * raising or deiconifying its window. 341 * If no frame has been set, then do nothing. 342 */ 343 public void show() { 344 JFrame frame = getFrame(); 345 346 if (frame != null) { 347 if (!frame.isVisible()) { 348 size.setSize(frame); 349 350 // NOTE: This used to override the location that might 351 // have been set in the _windowProperties attribute. 352 /* 353 if (frame instanceof Top) { 354 ((Top)frame).centerOnScreen(); 355 } 356 */ 357 frame.pack(); 358 frame.setVisible(true); 359 360 // NOTE: The above calls Component.show()... 361 // We used to override Top.show() (Top extends JFrame) 362 // to call pack(), but this had the unfortunate side 363 // effect of overriding manual placement of windows. 364 // However, due to some weirdness in Swing or the AWT, 365 // calling pack before setVisible() does not have the 366 // same effect as calling pack() within show(). 367 // Calling it after, however, is not sufficient. 368 369 // We used to have to call pack() both before and 370 // after setVisible() because the the HTML welcome 371 // window that appears when vergil starts up was the 372 // wrong size. However, if we call pack() twice, then 373 // under Java 1.5, the View XML window is too large, 374 // the horizontal scrollbar ends up off the screen. 375 376 //frame.pack(); 377 } 378 379 // Deiconify the window. 380 frame.setState(Frame.NORMAL); 381 frame.toFront(); 382 } 383 } 384 385 /////////////////////////////////////////////////////////////////// 386 //// inner classes //// 387 388 class WindowClosedAdapter extends WindowAdapter { 389 // This is invoked if the window 390 // is disposed by the _close() method of Top. 391 @Override 392 public void windowClosed(WindowEvent e) { 393 if (_debugClosing) { 394 System.out.println("Tableau$WindowClosedAdapter.windowClosed(" 395 + e.getWindow().getName() + ")"); 396 } 397 398 Window frame = e.getWindow(); 399 try { 400 Tableau.this.setContainer(null); 401 } catch (KernelException ex) { 402 try { 403 MessageHandler.warning("Cannot remove tableau: " + ex); 404 } catch (CancelException exception) { 405 } 406 } 407 // System.out.println(frame.getWindowListeners().length); 408 /*int removed =*/MemoryCleaner.removeWindowListeners(frame); 409 //System.out.println("Window listeners removed: " + removed); 410 _windowClosedAdapter = null; 411 _frame = null; 412 } 413 414 // NOTE: We do not want to do the same in windowClosing() 415 // because this will override saving if modified as implemented 416 // in Top. 417 } 418 419 /////////////////////////////////////////////////////////////////// 420 //// protected variables //// 421 422 /** Set to true to print closing sequence information to standard 423 * out. 424 */ 425 protected boolean _debugClosing = false; 426 427 /////////////////////////////////////////////////////////////////// 428 //// private variables //// 429 430 /** Flag indicating whether the tableau is editable. */ 431 private boolean _editable; 432 433 /** The frame that the tableau is shown in. 434 */ 435 private JFrame _frame; 436 437 /** The adapter responsible for the windowClosed event. 438 */ 439 private WindowClosedAdapter _windowClosedAdapter; 440 441 /** True if this tableau is a master tableau. Default value is false. 442 */ 443 private boolean _master = false; 444 445 /** The title set by setTitle(). */ 446 private String _title; 447}