001/* An actor that displays matrix inputs. 002 003 Copyright (c) 1997-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 027 028 */ 029package ptolemy.actor.lib.gui; 030 031import java.awt.BorderLayout; 032import java.awt.Container; 033import java.awt.Dimension; 034import java.util.LinkedList; 035import java.util.List; 036 037import javax.swing.JFrame; 038import javax.swing.SwingUtilities; 039 040import ptolemy.actor.TypedIOPort; 041import ptolemy.actor.gui.AbstractPlaceableActor; 042import ptolemy.actor.gui.Configuration; 043import ptolemy.actor.gui.Effigy; 044import ptolemy.actor.gui.MatrixPane; 045import ptolemy.actor.gui.MatrixTokenTableau; 046import ptolemy.actor.gui.TableauFrame; 047import ptolemy.actor.gui.TokenEffigy; 048import ptolemy.actor.gui.TokenTableau; 049import ptolemy.data.IntToken; 050import ptolemy.data.MatrixToken; 051import ptolemy.data.Token; 052import ptolemy.data.expr.Parameter; 053import ptolemy.data.type.BaseType; 054import ptolemy.kernel.CompositeEntity; 055import ptolemy.kernel.util.Attribute; 056import ptolemy.kernel.util.IllegalActionException; 057import ptolemy.kernel.util.InternalErrorException; 058import ptolemy.kernel.util.KernelException; 059import ptolemy.kernel.util.NameDuplicationException; 060import ptolemy.kernel.util.NamedObj; 061import ptolemy.kernel.util.Workspace; 062 063/////////////////////////////////////////////////////////////////// 064//// MatrixViewer 065 066/** 067 An actor that displays the contents of a matrix input. This 068 actor has a single input port, which only accepts MatrixTokens. One 069 token is consumed per firing (in postfire()). The data in the MatrixToken is 070 displayed in a table format with scrollbars, using the swing JTable 071 class. 072 073 @author Bart Kienhuis and Edward A. Lee 074 @version $Id$ 075 @since Ptolemy II 1.0 076 @Pt.ProposedRating Red (kienhuis) 077 @Pt.AcceptedRating Red (kienhuis) 078 */ 079public class MatrixViewer extends AbstractPlaceableActor { 080 /** Construct an actor with the given container and name. 081 * @param container The container. 082 * @param name The name of this actor. 083 * @exception IllegalActionException If the actor cannot be contained 084 * by the proposed container. 085 * @exception NameDuplicationException If the container already has an 086 * actor with this name. 087 */ 088 public MatrixViewer(CompositeEntity container, String name) 089 throws NameDuplicationException, IllegalActionException { 090 super(container, name); 091 input = new TypedIOPort(this, "input", true, false); 092 input.setTypeEquals(BaseType.MATRIX); 093 094 width = new Parameter(this, "width", new IntToken(500)); 095 width.setTypeEquals(BaseType.INT); 096 height = new Parameter(this, "height", new IntToken(300)); 097 height.setTypeEquals(BaseType.INT); 098 } 099 100 /////////////////////////////////////////////////////////////////// 101 //// ports and parameters //// 102 103 /** The input port. 104 */ 105 public TypedIOPort input; 106 107 /** The width of the table in pixels. This must contain an 108 * integer. If the table is larger than this specified width, 109 * then scrollbars will appear, or the column width is adjusted. 110 * The default value is 500. 111 */ 112 public Parameter width; 113 114 /** The height of the table in pixels. This must contain an 115 * integer. If the table is larger than this specified width, 116 * then scrollbars will appear. The default value is 300. 117 */ 118 public Parameter height; 119 120 /////////////////////////////////////////////////////////////////// 121 //// public methods //// 122 123 /** Notification that an attribute has changed. If the attribute is 124 * width or height then read the value of the attribute. 125 * @param attribute The attribute that changed. 126 * @exception IllegalActionException If the expression of the 127 * attribute cannot be parsed or cannot be evaluated. 128 */ 129 @Override 130 public void attributeChanged(Attribute attribute) 131 throws IllegalActionException { 132 if (attribute == width) { 133 _width = ((IntToken) width.getToken()).intValue(); 134 } else if (attribute == height) { 135 _height = ((IntToken) height.getToken()).intValue(); 136 } else { 137 super.attributeChanged(attribute); 138 } 139 } 140 141 /** Clone the actor into the specified workspace. This calls the 142 * base class and then removes association with graphical objects 143 * belonging to the original class. 144 * @param workspace The workspace for the new object. 145 * @return A new actor. 146 * @exception CloneNotSupportedException If a derived class contains 147 * an attribute that cannot be cloned. 148 */ 149 @Override 150 public Object clone(Workspace workspace) throws CloneNotSupportedException { 151 MatrixViewer newObject = (MatrixViewer) super.clone(workspace); 152 153 newObject._container = null; 154 newObject._effigy = null; 155 newObject._frame = null; 156 newObject._pane = null; 157 newObject._tableau = null; 158 159 return newObject; 160 } 161 162 /** Initialize this matrix viewer. If place() has not been called 163 * with a container into which to place the display, then create a 164 * new frame into which to put it. 165 * @exception IllegalActionException If the parent class 166 * throws it. 167 */ 168 @Override 169 public void initialize() throws IllegalActionException { 170 super.initialize(); 171 172 if (_container == null) { 173 // No current container for the pane. 174 // Need an effigy and a tableau so that menu ops work properly. 175 if (_tableau == null) { 176 Effigy containerEffigy = Configuration.findEffigy(toplevel()); 177 178 if (containerEffigy == null) { 179 throw new IllegalActionException(this, 180 "Cannot find effigy for top level: " 181 + toplevel().getFullName()); 182 } 183 184 try { 185 _effigy = new TokenEffigy(containerEffigy, 186 containerEffigy.uniqueName("tokenEffigy")); 187 188 // The default identifier is "Unnamed", which is 189 // no good for two reasons: Wrong title bar label, 190 // and it causes a save-as to destroy the original window. 191 _effigy.identifier.setExpression(getFullName()); 192 193 // The second argument prevents a status bar. 194 _frame = new TableauFrame(null, null, this); 195 _tableau = new MatrixTokenTableau(_effigy, "tokenTableau", 196 (TableauFrame) _frame); 197 ((TableauFrame) _frame).setTableau(_tableau); 198 setFrame(_frame); 199 _tableau.show(); 200 } catch (Exception ex) { 201 throw new IllegalActionException(this, null, ex, 202 "Error creating effigy and tableau"); 203 } 204 } else { 205 // Erase previous text. 206 _effigy.clear(); 207 208 if (_frame != null) { 209 // Do not use show() as it overrides manual placement. 210 // FIXME: So does setVisible()... But with neither one used, 211 // then if the user dismisses the window, it does not reappear 212 // on re-running! 213 // _frame.setVisible(true); 214 _frame.toFront(); 215 } 216 } 217 } 218 } 219 220 /** Specify the container in which the data should be displayed. 221 * An instance of JTable will be added to that container. The 222 * table is configured such that a user cannot reorder the 223 * columns of the table. Also, the table maintains a fixed 224 * preferred size, and will employ scrollbars if the table is 225 * larger than the preferred size. If this method is not called, 226 * the JTable will be placed in its own frame. The table is also 227 * placed in its own frame if this method is called with a null 228 * argument. The background of the table is set equal to that of 229 * the container (unless it is null). 230 * 231 * @param container The container into which to place the table. 232 */ 233 @Override 234 public void place(Container container) { 235 // If there was a previous container that doesn't match this one, 236 // remove the pane from it. 237 if (_container != null && _pane != null) { 238 _container.remove(_pane); 239 _container = null; 240 } 241 242 if (_frame != null) { 243 _frame.dispose(); 244 _frame = null; 245 } 246 247 _container = container; 248 249 if (container == null) { 250 // Reset everything. 251 if (_tableau != null) { 252 // This will have the side effect of removing the effigy 253 // from the directory if there are no more tableaux in it. 254 try { 255 _tableau.setContainer(null); 256 } catch (KernelException ex) { 257 throw new InternalErrorException(ex); 258 } 259 } 260 261 _tableau = null; 262 _effigy = null; 263 _pane = null; 264 265 return; 266 } 267 268 if (_pane == null) { 269 // Create the pane. 270 _pane = new MatrixPane(); 271 272 // FIXME: The following, as usual with Swing, doesn't work. 273 Dimension size = new Dimension(_width, _height); 274 _pane.setPreferredSize(size); 275 _pane.setSize(size); 276 } 277 278 // Place the pane in supplied container. 279 _container.add(_pane, BorderLayout.CENTER); 280 } 281 282 /** Consume a matrix token from the <i>input</i> port 283 * and display the token in a table. If a token is not available, 284 * do nothing. 285 * @exception IllegalActionException If there is no director, or 286 * if the base class throws it. 287 */ 288 @Override 289 public boolean postfire() throws IllegalActionException { 290 if (input.hasToken(0)) { 291 Token in = input.get(0); 292 293 if (_frame != null) { 294 List<Token> tokens = new LinkedList<Token>(); 295 tokens.add(in); 296 _effigy.setTokens(tokens); 297 } else if (_pane != null) { 298 _pane.display((MatrixToken) in); 299 } 300 } 301 302 return super.postfire(); 303 } 304 305 /** Override the base class to remove the display from its graphical 306 * container if the argument is null. 307 * @param container The proposed container. 308 * @exception IllegalActionException If the base class throws it. 309 * @exception NameDuplicationException If the base class throws it. 310 */ 311 @Override 312 public void setContainer(CompositeEntity container) 313 throws IllegalActionException, NameDuplicationException { 314 super.setContainer(container); 315 316 if (container == null) { 317 _remove(); 318 } 319 } 320 321 /** Set a name to present to the user. 322 * <p>If the MatrixViewer window has been rendered, then the title of the 323 * MatrixViewer window will be updated to the value of the name parameter.</p> 324 * @param name A name to present to the user. 325 * @see #getDisplayName() 326 */ 327 @Override 328 public void setDisplayName(String name) { 329 super.setDisplayName(name); 330 // See http://bugzilla.ecoinformatics.org/show_bug.cgi?id=4302 331 if (_tableau != null) { 332 _tableau.setTitle(name); 333 } 334 } 335 336 /** Specify the associated frame and set its properties (size, etc.) 337 * to match those stored in the _windowProperties attribute. In this 338 * class, if the frame is null, close the tableau and set it to null. 339 * Once closed, the matrix window will not reopen unless _tableau is 340 * null. 341 * @param frame The associated frame. 342 */ 343 @Override 344 public void setFrame(JFrame frame) { 345 346 super.setFrame(frame); 347 348 if (frame == null && _tableau != null) { 349 try { 350 _tableau.setContainer(null); 351 } catch (KernelException ex) { 352 throw new InternalErrorException(ex); 353 } 354 _tableau = null; 355 _effigy = null; 356 } 357 } 358 359 /** Set or change the name. If a null argument is given the 360 * name is set to an empty string. 361 * Increment the version of the workspace. 362 * This method is write-synchronized on the workspace. 363 * <p>If the MatrixViewer window has been rendered, then the title of the 364 * MatrixViewer window will be updated to the value of the name parameter.</p> 365 * @param name The new name. 366 * @exception IllegalActionException If the name contains a period 367 * or if the object is a derived object and the name argument does 368 * not match the current name. 369 * @exception NameDuplicationException Not thrown in this base class. 370 * May be thrown by derived classes if the container already contains 371 * an object with this name. 372 * @see #getName() 373 * @see #getName(NamedObj) 374 */ 375 @Override 376 public void setName(String name) 377 throws IllegalActionException, NameDuplicationException { 378 super.setName(name); 379 // See http://bugzilla.ecoinformatics.org/show_bug.cgi?id=4302 380 if (_tableau != null) { 381 _tableau.setTitle(name); 382 } 383 } 384 385 /////////////////////////////////////////////////////////////////// 386 //// private methods //// 387 388 /** Remove the display from the current container, if there is one. 389 */ 390 private void _remove() { 391 SwingUtilities.invokeLater(new Runnable() { 392 @Override 393 public void run() { 394 if (_container != null && _pane != null) { 395 _container.remove(_pane); 396 _container = null; 397 } 398 399 if (_frame != null) { 400 _frame.dispose(); 401 _frame = null; 402 } 403 } 404 }); 405 } 406 407 /////////////////////////////////////////////////////////////////// 408 //// private variables //// 409 410 /** Container with the display, if any. */ 411 private Container _container = null; 412 413 /** The effigy for the token data. */ 414 private TokenEffigy _effigy; 415 416 /** Height of the matrix viewer in pixels. */ 417 private int _height; 418 419 /** Pane with the matrix display. */ 420 private MatrixPane _pane = null; 421 422 /** The tableau with the display, if any. */ 423 private TokenTableau _tableau; 424 425 /** Width of the matrix viewer in pixels. */ 426 private int _width; 427}