001/* An actor that generates an empty token in response to a click of a button. 002 003 @Copyright (c) 1998-2018 The Regents of the University of California. 004 All rights reserved. 005 006 Permission is hereby granted, without written agreement and without 007 license or royalty fees, to use, copy, modify, and distribute this 008 software and its documentation for any purpose, provided that the 009 above copyright notice and the following two paragraphs appear in all 010 copies of this software. 011 012 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY 013 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 014 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 015 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 016 SUCH DAMAGE. 017 018 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 019 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 020 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 021 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 022 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 023 ENHANCEMENTS, OR MODIFICATIONS. 024 025 PT_COPYRIGHT_VERSION 2 026 COPYRIGHTENDKEY 027 */ 028package ptolemy.actor.lib.gui; 029 030import java.awt.Frame; 031 032import ptolemy.actor.Director; 033import ptolemy.actor.TypedAtomicActor; 034import ptolemy.actor.TypedIOPort; 035import ptolemy.actor.gui.EditorFactory; 036import ptolemy.actor.util.Time; 037import ptolemy.data.DoubleToken; 038import ptolemy.data.expr.Parameter; 039import ptolemy.data.type.BaseType; 040import ptolemy.kernel.CompositeEntity; 041import ptolemy.kernel.util.IllegalActionException; 042import ptolemy.kernel.util.NameDuplicationException; 043import ptolemy.kernel.util.NamedObj; 044import ptolemy.kernel.util.Settable; 045import ptolemy.kernel.util.Workspace; 046import ptolemy.moml.MoMLChangeRequest; 047import ptolemy.util.CancelException; 048import ptolemy.util.MessageHandler; 049 050/////////////////////////////////////////////////////////////////// 051//// EventButton 052 053/** 054 Output a token when the actor is fired. 055 This actor customizes its interaction to request a firing 056 whenever the icon is double clicked. 057 058 By default, the value of the output is a boolean true. 059 To change this, Alt-click on the icon. 060 061 @author Edward A. Lee 062 @version $Id$ 063 @since Ptolemy II 11.0 064 @Pt.ProposedRating Red (winthrop) 065 @Pt.AcceptedRating Red (winthrop) 066 */ 067public class EventButton extends TypedAtomicActor { 068 069 /** Construct an actor. 070 * @param container The container. 071 * @param name The name of this actor. 072 * @exception IllegalActionException If the entity cannot be contained 073 * by the proposed container. 074 * @exception NameDuplicationException If the container already has an 075 * actor with this name. 076 */ 077 public EventButton(CompositeEntity container, String name) 078 throws IllegalActionException, NameDuplicationException { 079 super(container, name); 080 081 output = new TypedIOPort(this, "output", false, true); 082 083 value = new Parameter(this, "value"); 084 value.setExpression("true"); 085 086 output.setTypeAtLeast(value); 087 088 new DoubleClickHandler(this, "_doubleClickHandler"); 089 090 buttonPressed = new Parameter(this, "buttonPressed"); 091 buttonPressed.setTypeEquals(BaseType.BOOLEAN); 092 buttonPressed.setExpression("false"); 093 buttonPressed.setVisibility(Settable.NONE); 094 095 pressDuration = new Parameter(this, "pressDuration"); 096 pressDuration.setExpression("0.2"); 097 pressDuration.setTypeEquals(BaseType.DOUBLE); 098 } 099 100 /////////////////////////////////////////////////////////////////// 101 //// public variables and parameters //// 102 103 /** Hidden parameter controlling the visual rendition of the button. 104 */ 105 public Parameter buttonPressed; 106 107 /** The output port. The type of this port is the same as that of 108 * the value parameter. 109 */ 110 public TypedIOPort output; 111 112 /** Amount of time to keep the button depressed, in seconds. 113 * Additional button presses during this time will be ignored. 114 * This is a double with default value 0.2. 115 */ 116 public Parameter pressDuration; 117 118 /** The value produced. This is a boolean true by default. 119 */ 120 public Parameter value; 121 122 /////////////////////////////////////////////////////////////////// 123 //// public methods //// 124 125 /** Clone the actor into the specified workspace. 126 * This method sets the type constraint of the output 127 * to be at least the type of the value. 128 * @param workspace The workspace for the new object. 129 * @return A new actor. 130 * @exception CloneNotSupportedException If a derived class contains 131 * an attribute that cannot be cloned. 132 */ 133 @Override 134 public Object clone(Workspace workspace) throws CloneNotSupportedException { 135 EventButton newObject = (EventButton) super.clone(workspace); 136 137 // set the type constraints. 138 newObject.output.setTypeAtLeast(newObject.value); 139 return newObject; 140 } 141 142 /** Fire the actor. 143 */ 144 @Override 145 public synchronized void fire() throws IllegalActionException { 146 super.fire(); 147 148 // Do not produce an output if the purpose of the firing is 149 // to restore the button. 150 // Here, we assume that firings occur _only_ in response 151 // to double clicks. 152 if (_bounceBackTime != null) { 153 // Restore the button. 154 _setButtonPressed(false); 155 _bounceBackTime = null; 156 } else { 157 output.broadcast(value.getToken()); 158 159 Director director = getDirector(); 160 Time currentTime = director.getModelTime(); 161 162 double pressDurationValue = ((DoubleToken) pressDuration.getToken()) 163 .doubleValue(); 164 // Mark that the button is depressed. 165 _bounceBackTime = currentTime.add(pressDurationValue); 166 // Request a firing in the future to restore the button. 167 director.fireAt(EventButton.this, _bounceBackTime); 168 169 // Mark the button pressed. 170 _setButtonPressed(true); 171 } 172 } 173 174 /** Mark that the model is now executing. 175 * @exception IllegalActionException If the superclass throws it. 176 */ 177 @Override 178 public void initialize() throws IllegalActionException { 179 super.initialize(); 180 _running = true; 181 _bounceBackTime = null; 182 } 183 184 /** Mark that the model is no longer executing. 185 * @exception IllegalActionException If the superclass throws it. 186 */ 187 @Override 188 public void wrapup() throws IllegalActionException { 189 super.wrapup(); 190 _running = false; 191 _setButtonPressed(false); 192 _bounceBackTime = null; 193 } 194 195 /////////////////////////////////////////////////////////////////// 196 //// private methods //// 197 198 /** Set whether the button is pressed. 199 * @param pressed True to be pressed. 200 */ 201 private void _setButtonPressed(boolean pressed) { 202 String moml = "<property name=\"buttonPressed\" value=\"" + pressed 203 + "\"/>"; 204 MoMLChangeRequest request = new MoMLChangeRequest(this, this, moml); 205 request.setPersistent(false); 206 requestChange(request); 207 } 208 209 /////////////////////////////////////////////////////////////////// 210 //// private members //// 211 212 /** Time at which the button should revert to full size. */ 213 private Time _bounceBackTime; 214 215 /** Indicator that the model is running. */ 216 private boolean _running; 217 218 /////////////////////////////////////////////////////////////////// 219 //// inner classes //// 220 221 /** Class to respond to double click. */ 222 class DoubleClickHandler extends EditorFactory { 223 224 public DoubleClickHandler(NamedObj container, String name) 225 throws IllegalActionException, NameDuplicationException { 226 super(container, name); 227 } 228 229 /** Respond to double click. */ 230 @Override 231 public void createEditor(NamedObj object, Frame parent) { 232 if (!_running) { 233 try { 234 MessageHandler.warning( 235 "Model is not running. No output produced."); 236 } catch (CancelException e) { 237 // Ignore. 238 } 239 return; 240 } 241 Director director = getDirector(); 242 if (director != null) { 243 try { 244 // This will be called in the Swing event thread. 245 // Make sure that the actor is not currently firing. 246 synchronized (EventButton.this) { 247 if (_bounceBackTime != null) { 248 // Ignore. Still in the interval of the previous button press. 249 return; 250 } 251 // Request a firing now to produce an output. 252 director.fireAtCurrentTime(EventButton.this); 253 } 254 } catch (IllegalActionException e) { 255 MessageHandler.error( 256 "Director is unable to fire the actor as requested.", 257 e); 258 } 259 } else { 260 MessageHandler.error("No director!"); 261 } 262 } 263 } 264}