001/* Base class for the properties that can be used to configure a tableau.
002
003 Copyright (c) 2008-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 */
028package ptolemy.actor.gui.properties;
029
030import java.awt.Dimension;
031
032import javax.swing.JComponent;
033import javax.swing.JFrame;
034
035import ptolemy.actor.gui.Tableau;
036import ptolemy.data.IntMatrixToken;
037import ptolemy.data.expr.Parameter;
038import ptolemy.data.type.BaseType;
039import ptolemy.kernel.util.Attribute;
040import ptolemy.kernel.util.IllegalActionException;
041import ptolemy.kernel.util.NameDuplicationException;
042import ptolemy.kernel.util.NamedObj;
043import ptolemy.kernel.util.Workspace;
044
045//////////////////////////////////////////////////////////////////////////
046//// GUIProperty
047
048/**
049 Base class for the properties that can be used to configure a tableau.
050
051 @author Thomas Huining Feng
052 @version $Id$
053 @since Ptolemy II 8.0
054 @Pt.ProposedRating Red (tfeng)
055 @Pt.AcceptedRating Red (tfeng)
056 */
057public abstract class GUIProperty extends Attribute {
058
059    /** Construct a GUI property with the given name contained by the specified
060     *  entity. The container argument must not be null, or a
061     *  NullPointerException will be thrown.  This attribute will use the
062     *  workspace of the container for synchronization and version counts.
063     *  If the name argument is null, then the name is set to the empty string.
064     *  Increment the version of the workspace.
065     *  @param container The container.
066     *  @param name The name of this attribute.
067     *  @exception IllegalActionException If the attribute is not of an
068     *   acceptable class for the container, or if the name contains a period.
069     *  @exception NameDuplicationException If the name coincides with
070     *   an attribute already in the container.
071     */
072    public GUIProperty(NamedObj container, String name)
073            throws IllegalActionException, NameDuplicationException {
074        this(container, name, null, null);
075    }
076
077    /** Construct a GUI property with the given name contained by the specified
078     *  entity with the given Java Swing component. The container argument must
079     *  not be null, or a NullPointerException will be thrown.  This attribute
080     *  will use the workspace of the container for synchronization and version
081     *  counts. If the name argument is null, then the name is set to the empty
082     *  string. Increment the version of the workspace.
083     *  @param container The container.
084     *  @param name The name of this attribute.
085     *  @param component The Java Swing component.
086     *  @exception IllegalActionException If the attribute is not of an
087     *   acceptable class for the container, or if the name contains a period.
088     *  @exception NameDuplicationException If the name coincides with
089     *   an attribute already in the container.
090     */
091    public GUIProperty(NamedObj container, String name, JComponent component)
092            throws IllegalActionException, NameDuplicationException {
093        this(container, name, component, null);
094    }
095
096    /** Construct a GUI property with the given name contained by the specified
097     *  entity with the given Java Swing component and the given layout
098     *  constraint. The container argument must not be null, or a
099     *  NullPointerException will be thrown.  This attribute
100     *  will use the workspace of the container for synchronization and version
101     *  counts. If the name argument is null, then the name is set to the empty
102     *  string. Increment the version of the workspace.
103     *  @param container The container.
104     *  @param name The name of this attribute.
105     *  @param component The Java Swing component.
106     *  @param constraint The layout constraint.
107     *  @exception IllegalActionException If the attribute is not of an
108     *   acceptable class for the container, or if the name contains a period.
109     *  @exception NameDuplicationException If the name coincides with
110     *   an attribute already in the container.
111     */
112    public GUIProperty(NamedObj container, String name, JComponent component,
113            Object constraint)
114            throws IllegalActionException, NameDuplicationException {
115        super(container, name);
116
117        preferredSize = new Parameter(this, "preferredSize");
118        preferredSize.setTypeEquals(BaseType.INT_MATRIX);
119        preferredSize.setToken("[-1, -1]");
120
121        _component = component == null ? _createComponent() : component;
122        _constraint = constraint;
123        _add(container);
124    }
125
126    /** Construct a GUI property with the given name contained by the specified
127     *  entity with the given layout
128     *  constraint. The container argument must not be null, or a
129     *  NullPointerException will be thrown.  This attribute
130     *  will use the workspace of the container for synchronization and version
131     *  counts. If the name argument is null, then the name is set to the empty
132     *  string. Increment the version of the workspace.
133     *  @param container The container.
134     *  @param name The name of this attribute.
135     *  @param constraint The layout constraint.
136     *  @exception IllegalActionException If the attribute is not of an
137     *   acceptable class for the container, or if the name contains a period.
138     *  @exception NameDuplicationException If the name coincides with
139     *   an attribute already in the container.
140     */
141    public GUIProperty(NamedObj container, String name, Object constraint)
142            throws IllegalActionException, NameDuplicationException {
143        this(container, name, null, constraint);
144    }
145
146    /** React to a change in an attribute.  This method is called by
147     *  a contained attribute when its value changes. If the changed attribute
148     *  is {@link #preferredSize}, then the preferred size of the Swing
149     *  component in this GUI property is adjusted accordingly.
150     *  @param attribute The attribute that changed.
151     *  @exception IllegalActionException If the change is not acceptable
152     *   to this container (not thrown in this base class).
153     */
154    @Override
155    public void attributeChanged(Attribute attribute)
156            throws IllegalActionException {
157        if (attribute == preferredSize) {
158            JComponent component = getComponent();
159            if (component != null) {
160                IntMatrixToken size = (IntMatrixToken) preferredSize.getToken();
161                int width = size.getElementAt(0, 0);
162                int height = size.getElementAt(0, 1);
163                if (width >= 0 && height >= 0) {
164                    component.setPreferredSize(new Dimension(width, height));
165                }
166            }
167        } else {
168            super.attributeChanged(attribute);
169        }
170    }
171
172    /** Clone the property into the specified workspace.
173     *  @param workspace The workspace in to which the object is cloned.
174     *  @return A new property
175     *  @exception CloneNotSupportedException If a derived class contains
176     *   an attribute that cannot be cloned.
177     */
178    @Override
179    public Object clone(Workspace workspace) throws CloneNotSupportedException {
180        GUIProperty newObject = (GUIProperty) super.clone(workspace);
181        newObject._component = null;
182        newObject._constraint = null;
183
184        return newObject;
185    }
186
187    /** Return the Swing component.
188     *  @return The Swing component.
189     */
190    public JComponent getComponent() {
191        return _component;
192    }
193
194    /** Specify the container NamedObj, adding this attribute to the
195     *  list of attributes in the container.  If the container already
196     *  contains an attribute with the same name, then throw an exception
197     *  and do not make any changes.  Similarly, if the container is
198     *  not in the same workspace as this attribute, throw an exception.
199     *  If this attribute is already contained by the NamedObj, do nothing.
200     *  If the attribute already has a container, remove
201     *  this attribute from its attribute list first.  Otherwise, remove
202     *  it from the directory of the workspace, if it is there.
203     *  If the argument is null, then remove it from its container.
204     *  It is not added to the workspace directory, so this could result in
205     *  this object being garbage collected.
206     *  Note that since an Attribute is a NamedObj, it can itself have
207     *  attributes.  However, recursive containment is not allowed, where
208     *  an attribute is an attribute of itself, or indirectly of any attribute
209     *  it contains.  This method is write-synchronized on the
210     *  workspace and increments its version number.
211     *  <p>
212     *  Subclasses may constrain the type of container by overriding
213     *  {@link #setContainer(NamedObj)}.
214     *  @param container The container to attach this attribute to..
215     *  @exception IllegalActionException If this attribute is not of the
216     *   expected class for the container, or it has no name,
217     *   or the attribute and container are not in the same workspace, or
218     *   the proposed container would result in recursive containment.
219     *  @exception NameDuplicationException If the container already has
220     *   an attribute with the name of this attribute.
221     *  @see #getContainer()
222     */
223    @Override
224    public void setContainer(NamedObj container)
225            throws IllegalActionException, NameDuplicationException {
226        if (getComponent() == null || _add(container)) {
227            super.setContainer(container);
228        } else {
229            throw new IllegalActionException(getName()
230                    + " cannot be contained by " + container.getName());
231        }
232    }
233
234    /** The preferred size of the Swing component. It would be a matrix of 2
235     *  integers, such as [100, 20]. It is ignored if any integer is less than
236     *  0.
237     */
238    public Parameter preferredSize;
239
240    /** Create a new Java Swing component.
241     *
242     *  @return A Swing component that can be enclosed in this GUI property.
243     *  @exception IllegalActionException Not thrown in this base class.
244     */
245    protected abstract JComponent _createComponent()
246            throws IllegalActionException;
247
248    /** Add the Java Swing component to the tableau or GUI property that
249     *  contains this GUI property. If this GUI property is already added to one
250     *  such container, it will be removed from its old container first.
251     *
252     *  @param container The new container.
253     *  @return true if the container can be used; false if the adding is not
254     *   successful.
255     */
256    private boolean _add(NamedObj container) {
257        if (container == null) {
258            _remove();
259            return true;
260        } else {
261            JComponent component = getComponent();
262            if (component != null) {
263                if (container instanceof Tableau) {
264                    _remove();
265                    JFrame frame = ((Tableau) container).getFrame();
266                    frame.getContentPane().add(component, _constraint);
267                    return true;
268                } else if (container instanceof GUIProperty) {
269                    _remove();
270                    ((GUIProperty) container).getComponent().add(component,
271                            _constraint);
272                    return true;
273                }
274            }
275            return false;
276        }
277    }
278
279    /** Remove the Java Swing component from the tableau or GUI property that
280     *  contains this GUI property.
281     */
282    private void _remove() {
283        NamedObj container = getContainer();
284        if (container instanceof Tableau) {
285            JFrame frame = ((Tableau) container).getFrame();
286            frame.getContentPane().remove(getComponent());
287        } else if (container instanceof GUIProperty) {
288            JComponent component = ((GUIProperty) container).getComponent();
289            component.remove(getComponent());
290        }
291    }
292
293    /** The Java Swing component.
294     */
295    private JComponent _component;
296
297    /** The layout constraint.
298     */
299    private Object _constraint;
300}