001/* An actor whose output is controlled by a slider in the run window. 002 003 @Copyright (c) 2001-2014 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.Color; 031import java.awt.Container; 032 033import javax.swing.BorderFactory; 034import javax.swing.JFrame; 035import javax.swing.JPanel; 036import javax.swing.JSlider; 037import javax.swing.SwingConstants; 038import javax.swing.SwingUtilities; 039import javax.swing.border.EmptyBorder; 040import javax.swing.border.LineBorder; 041import javax.swing.event.ChangeEvent; 042import javax.swing.event.ChangeListener; 043 044import ptolemy.actor.gui.Placeable; 045import ptolemy.actor.lib.Source; 046import ptolemy.data.IntToken; 047import ptolemy.data.expr.Parameter; 048import ptolemy.data.type.BaseType; 049import ptolemy.kernel.CompositeEntity; 050import ptolemy.kernel.util.Attribute; 051import ptolemy.kernel.util.IllegalActionException; 052import ptolemy.kernel.util.NameDuplicationException; 053import ptolemy.kernel.util.StringAttribute; 054import ptolemy.kernel.util.Workspace; 055 056/////////////////////////////////////////////////////////////////// 057//// SliderSource 058 059/** 060 The output of this actor is controlled by a slider in the run window. 061 The range of the output is specified by two parameters, <i>minimum</i> and 062 <i>maximum</i>. The type of these parameters and the output is integer. 063 064 @author Xiaojun Liu, Gang Zhou 065 @version $Id$ 066 @since Ptolemy II 2.0 067 @Pt.ProposedRating Red (liuxj) 068 @Pt.AcceptedRating Red (liuxj) 069 */ 070public class SliderSource extends Source implements ChangeListener, Placeable { 071 /** Construct an actor with an input multiport of type GENERAL. 072 * @param container The container. 073 * @param name The name of this actor. 074 * @exception IllegalActionException If the actor cannot be contained 075 * by the proposed container. 076 * @exception NameDuplicationException If the container already has an 077 * entity with this name. 078 */ 079 public SliderSource(CompositeEntity container, String name) 080 throws IllegalActionException, NameDuplicationException { 081 super(container, name); 082 083 // Set the type of the output port. 084 output.setTypeEquals(BaseType.INT); 085 086 minimum = new Parameter(this, "minimum", new IntToken(-10)); 087 minimum.setTypeEquals(BaseType.INT); 088 maximum = new Parameter(this, "maximum", new IntToken(10)); 089 maximum.setTypeEquals(BaseType.INT); 090 majorTickSpacing = new Parameter(this, "majorTickSpacing", 091 new IntToken(10)); 092 majorTickSpacing.setTypeEquals(BaseType.INT); 093 minorTickSpacing = new Parameter(this, "minorTickSpacing", 094 new IntToken(1)); 095 minorTickSpacing.setTypeEquals(BaseType.INT); 096 097 title = new StringAttribute(this, "title"); 098 title.setExpression(""); 099 } 100 101 /////////////////////////////////////////////////////////////////// 102 //// public variables and parameters //// 103 104 /** The slider that controls the output of this actor. */ 105 public JSlider slider; 106 107 /** The minimum value of the slider. The value must be an integer. 108 * The default value is -10. 109 */ 110 public Parameter minimum; 111 112 /** The maximum value of the slider. The value must be an integer. 113 * The default value is 10. 114 */ 115 public Parameter maximum; 116 117 /** The major tick spacing of the slider. The value must be an integer. 118 * The default value is 10. 119 */ 120 public Parameter majorTickSpacing; 121 122 /** The minor tick spacing of the slider. The value must be an integer. 123 * The default value is 1. 124 */ 125 public Parameter minorTickSpacing; 126 127 /** The title to put on top. */ 128 public StringAttribute title; 129 130 /////////////////////////////////////////////////////////////////// 131 //// public methods //// 132 133 /** If the specified attribute is <i>minimum</i> or <i>maximum</i>, 134 * then set the range of the slider. 135 * @param attribute The attribute that has changed. 136 * @exception IllegalActionException If the specified range for the 137 * slider is invalid. 138 */ 139 @Override 140 public void attributeChanged(Attribute attribute) 141 throws IllegalActionException { 142 if (attribute == minimum || attribute == maximum 143 || attribute == majorTickSpacing 144 || attribute == minorTickSpacing) { 145 int min = ((IntToken) minimum.getToken()).intValue(); 146 int max = ((IntToken) maximum.getToken()).intValue(); 147 int major = ((IntToken) majorTickSpacing.getToken()).intValue(); 148 int minor = ((IntToken) minorTickSpacing.getToken()).intValue(); 149 150 if (min > max) { 151 throw new IllegalActionException(this, "The minimum value " 152 + "of the slider cannot be larger than the maximum " 153 + "value."); 154 } 155 156 if (slider != null) { 157 slider.setMaximum(max); 158 slider.setMinimum(min); 159 slider.setMajorTickSpacing(major); 160 slider.setMinorTickSpacing(minor); 161 } 162 } else { 163 super.attributeChanged(attribute); 164 } 165 } 166 167 /** Clone the actor into the specified workspace. This calls the 168 * base class and then sets the slider public variable to null. 169 * @param workspace The workspace for the new object. 170 * @return A new actor. 171 * @exception CloneNotSupportedException If a derived class contains 172 * an attribute that cannot be cloned. 173 */ 174 @Override 175 public Object clone(Workspace workspace) throws CloneNotSupportedException { 176 SliderSource newObject = (SliderSource) super.clone(workspace); 177 newObject.slider = null; 178 newObject._frame = null; 179 return newObject; 180 } 181 182 /** Output the value of the slider recorded when prefire() is last called. 183 */ 184 @Override 185 public void fire() throws IllegalActionException { 186 super.fire(); 187 output.send(0, _outputVal); 188 } 189 190 /** Return the background. 191 * @return The background color. 192 * @see #setBackground(Color) 193 */ 194 public Color getBackground() { 195 return _panel.getBackground(); 196 } 197 198 /** Create a slider on the screen, if necessary. If a graphical container 199 * has not been specified, place the slider into its own frame. 200 * Otherwise, place it in the specified container. 201 * @exception IllegalActionException If the parent class throws it. 202 */ 203 @Override 204 public void initialize() throws IllegalActionException { 205 super.initialize(); 206 207 if (slider == null) { 208 int min = ((IntToken) minimum.getToken()).intValue(); 209 int max = ((IntToken) maximum.getToken()).intValue(); 210 int major = ((IntToken) majorTickSpacing.getToken()).intValue(); 211 int minor = ((IntToken) minorTickSpacing.getToken()).intValue(); 212 String titleSpec = title.getExpression(); 213 214 // place the slider in its own frame. 215 // FIXME: This probably needs to be a PtolemyFrame, when one 216 // exists, so that the close button is dealt with, etc. 217 _frame = new SliderFrame(min, max, major, minor, titleSpec); 218 _panel = (JPanel) _frame.getContentPane().getComponent(0); 219 slider = (JSlider) _panel.getComponent(0); 220 slider.addChangeListener(this); 221 } 222 223 if (_frame != null) { 224 // Do not use show() as it overrides manual placement. 225 // FIXME: So does setVisible()... But with neither one used, 226 // then if the user dismisses the window, it does not reappear 227 // on re-running! 228 _frame.pack(); 229 _frame.setVisible(true); 230 // _frame.toFront(); 231 } 232 } 233 234 /** Specify the container in which the slider should be displayed. 235 * An instance of JSlider will be added to that container. 236 * This method needs to be called before the first call to initialize(). 237 * Otherwise, an instance of JSlider will be placed in its own frame. 238 * The slider is also placed in its own frame if this method 239 * is called with a null argument. 240 * The background of the slider is set equal to that of the container 241 * (unless it is null). 242 * @param container The container into which to place the slider. 243 */ 244 @Override 245 public void place(Container container) { 246 247 _container = container; 248 249 if (_container == null) { 250 if (_frame != null) { 251 _frame.dispose(); 252 } 253 254 _frame = null; 255 _panel = null; 256 slider = null; 257 return; 258 } 259 260 int min = -10; 261 int max = 10; 262 int major = 10; 263 int minor = 1; 264 String titleSpec = title.getExpression(); 265 266 try { 267 min = ((IntToken) minimum.getToken()).intValue(); 268 max = ((IntToken) maximum.getToken()).intValue(); 269 major = ((IntToken) majorTickSpacing.getToken()).intValue(); 270 minor = ((IntToken) minorTickSpacing.getToken()).intValue(); 271 272 } catch (IllegalActionException ex) { 273 // ignore 274 } 275 276 _panel = SliderFrame.createSliderPanel(min, max, major, minor, 277 titleSpec); 278 _container.add(_panel); 279 280 // java.awt.Component.setBackground(color) says that 281 // if the color "parameter is null then this component 282 // will inherit the background color of its parent." 283 // plot.setBackground(_container.getBackground()); 284 // _scrollPane.setBackground(_container.getBackground()); 285 _panel.setBackground(null); 286 _panel.setBorder(new EmptyBorder(10, 10, 10, 10)); 287 _panel.setBorder(new LineBorder(Color.black)); 288 slider = (JSlider) _panel.getComponent(0); 289 slider.addChangeListener(this); 290 } 291 292 /** Record the current value of the slider. This value is output in the 293 * subsequent firings of this actor. 294 */ 295 @Override 296 public boolean prefire() throws IllegalActionException { 297 _outputVal = new IntToken(slider.getValue()); 298 return super.prefire(); 299 } 300 301 /** Set the background color of the panel that contains the slider. 302 * @param background The background color. 303 * @see #getBackground() 304 */ 305 public void setBackground(Color background) { 306 _panel.setBackground(background); 307 } 308 309 /** Override the base class to remove the display from its graphical 310 * container if the argument is null. 311 * @param container The proposed container. 312 * @exception IllegalActionException If the base class throws it. 313 * @exception NameDuplicationException If the base class throws it. 314 */ 315 @Override 316 public void setContainer(CompositeEntity container) 317 throws IllegalActionException, NameDuplicationException { 318 super.setContainer(container); 319 320 if (container == null) { 321 _remove(); 322 } 323 } 324 325 /** The value of the slider changed, record the new value. */ 326 @Override 327 public void stateChanged(ChangeEvent e) { 328 slider.getValue(); 329 } 330 331 /** The frame for the slider. */ 332 @SuppressWarnings("serial") 333 public static class SliderFrame extends JFrame { 334 335 /** Create a frame for the slider. 336 * @param minimum the minimum value. 337 * @param maximum the maximum value. 338 * @param majorTickSpacing the space between major ticks. 339 * @param minorTickSpacing the space between minor ticks. 340 * @param title the title. 341 */ 342 public SliderFrame(int minimum, int maximum, int majorTickSpacing, 343 int minorTickSpacing, String title) { 344 345 JPanel panel = createSliderPanel(minimum, maximum, majorTickSpacing, 346 minorTickSpacing, title); 347 _slider = (JSlider) panel.getComponent(0); 348 getContentPane().add(panel); 349 pack(); 350 setVisible(true); 351 } 352 353 /** Create a slider panel. 354 * @param minimum the minimum value. 355 * @param maximum the maximum value. 356 * @param majorTickSpacing the space between major ticks. 357 * @param minorTickSpacing the space between minor ticks. 358 * @param title the title. 359 * @return The slider panel. 360 */ 361 public static JPanel createSliderPanel(int minimum, int maximum, 362 int majorTickSpacing, int minorTickSpacing, String title) { 363 364 JSlider slider = new JSlider(SwingConstants.HORIZONTAL, minimum, 365 maximum, (maximum + minimum) / 2); 366 slider.setBackground(null); 367 slider.setMajorTickSpacing(majorTickSpacing); 368 slider.setMinorTickSpacing(minorTickSpacing); 369 slider.setPaintTicks(true); 370 slider.setPaintLabels(true); 371 372 //slider.addChangeListener(this); 373 JPanel panel = new JPanel(); 374 panel.add(slider); 375 if (!title.trim().equals("")) { 376 panel.setBorder(BorderFactory.createTitledBorder(title)); 377 } 378 379 return panel; 380 } 381 382 /** Get the value of the slider. 383 * @return the slider value. 384 */ 385 public int getValue() { 386 return _slider.getValue(); 387 } 388 389 private JSlider _slider; 390 } 391 392 /////////////////////////////////////////////////////////////////// 393 //// private methods //// 394 395 /** Remove the display from the current container, if there is one. 396 */ 397 private void _remove() { 398 SwingUtilities.invokeLater(new Runnable() { 399 @Override 400 public void run() { 401 if (slider != null) { 402 if (_container != null) { 403 _container.remove(_panel); 404 _container.invalidate(); 405 _container.repaint(); 406 } else if (_frame != null) { 407 _frame.dispose(); 408 } 409 } 410 } 411 }); 412 } 413 414 /////////////////////////////////////////////////////////////////// 415 //// private members //// 416 417 /** The JPanel that contains the slider. */ 418 private JPanel _panel; 419 420 private Container _container; 421 422 private IntToken _outputVal; 423 424 // The frame into which to put the text widget, if any. 425 private JFrame _frame; 426}