001/* An attribute representing the size of a component.
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
027 */
028package ptolemy.actor.gui;
029
030import java.awt.Component;
031import java.awt.Dimension;
032import java.awt.Rectangle;
033import java.awt.event.ComponentEvent;
034import java.awt.event.ComponentListener;
035
036import javax.swing.JComponent;
037
038import ptolemy.data.IntMatrixToken;
039import ptolemy.data.expr.Parameter;
040import ptolemy.kernel.util.IllegalActionException;
041import ptolemy.kernel.util.InternalErrorException;
042import ptolemy.kernel.util.NameDuplicationException;
043import ptolemy.kernel.util.NamedObj;
044import ptolemy.kernel.util.Settable;
045import ptolemy.kernel.util.Workspace;
046
047///////////////////////////////////////////////////////////////////
048//// SizeAttribute
049
050/**
051 This attribute stores the width and height of a graphical component.
052 The token in this attribute is an IntMatrixToken containing a matrix
053 of dimension 1x2, containing the width and the height, in that order.
054 By default, this attribute has visibility NONE, so the user will not
055 see it in parameter editing dialogs.
056 <p>
057 Note that this attribute is typically used to record the size of
058 a component within a frame (a top-level window). To record the size
059 and position of the frame, use WindowPropertiesAttribute.
060
061 @author Edward A. Lee
062 @version $Id$
063 @since Ptolemy II 1.0
064 @Pt.ProposedRating Red (eal)
065 @Pt.AcceptedRating Red (johnr)
066 @see WindowPropertiesAttribute
067 */
068public class SizeAttribute extends Parameter implements ComponentListener {
069    /** Construct an attribute with the given name contained by the specified
070     *  entity. The container argument must not be null, or a
071     *  NullPointerException will be thrown.  This attribute will use the
072     *  workspace of the container for synchronization and version counts.
073     *  If the name argument is null, then the name is set to the empty string.
074     *  Increment the version of the workspace.
075     *  @param container The container.
076     *  @param name The name of this attribute.
077     *  @exception IllegalActionException If the attribute is not of an
078     *   acceptable class for the container, or if the name contains a period.
079     *  @exception NameDuplicationException If the name coincides with
080     *   an attribute already in the container.
081     */
082    public SizeAttribute(NamedObj container, String name)
083            throws IllegalActionException, NameDuplicationException {
084        super(container, name);
085        setVisibility(Settable.NONE);
086
087        // The following line, if uncommented, results in
088        // icons that are defined in an external file always being
089        // exported along with the model that defines them.  This bloats
090        // the MoML files with information that is not needed. I suspect
091        // the line was put there because it wasn't clear that you need
092        // to invoke File->Save in a submodel if you want the location
093        // and position of the submodel window to be saved. It is not
094        // sufficient to invoke save just at the top level.
095        // setPersistent(true);
096    }
097
098    ///////////////////////////////////////////////////////////////////
099    ////                         public methods                    ////
100
101    /** Clone the attribute into the specified workspace. This calls the
102     *  base class and then sets the attribute public members to refer
103     *  to the attributes of the new attribute.
104     *  @param workspace The workspace for the new attribute
105     *  @return A new director.
106     *  @exception CloneNotSupportedException If a derived class contains
107     *  an attribute that cannot be cloned.
108     */
109    @Override
110    public Object clone(Workspace workspace) throws CloneNotSupportedException {
111        SizeAttribute newObject = (SizeAttribute) super.clone(workspace);
112        newObject._listeningTo = null;
113        return newObject;
114    }
115
116    /** Do nothing. This method is
117     *  invoked when the component has been made invisible.
118     *  @param event The component event.
119     */
120    @Override
121    public void componentHidden(ComponentEvent event) {
122    }
123
124    /** Do nothing. This method is
125     *  invoked when the component's position changes.
126     *  @param event The component event.
127     */
128    @Override
129    public void componentMoved(ComponentEvent event) {
130    }
131
132    /** Record the new size. This method is
133     *  invoked when the component's size changes.
134     *  @param event The component event.
135     */
136    @Override
137    public void componentResized(ComponentEvent event) {
138        // FIXME: Due to Swing's lame approach to sizes,
139        // the size that is reported by this event is actually,
140        // apparently, a random number, the off by just enough
141        // from the actual size to cause scroll bars to be
142        // squished and the progress bar to not be shown.
143        // So we don't record the size.
144        // recordSize(_listeningTo);
145    }
146
147    /** Do nothing. This method is
148     *  invoked when the component has been made visible.
149     *  @param event The component event.
150     */
151    @Override
152    public void componentShown(ComponentEvent event) {
153    }
154
155    /** Set the value of the attribute to match those of the specified
156     *  component.
157     *  @param component The component whose size is to be recorded.
158     */
159    public void recordSize(Component component) {
160        try {
161            Rectangle bounds = component.getBounds();
162            setToken("[" + bounds.width + ", " + bounds.height + "]");
163            // Not clear why the following is needed, but if it isn't there,
164            // then window properties may not be recorded.
165            propagateValue();
166        } catch (IllegalActionException ex) {
167            throw new InternalErrorException("Can't set bounds value!");
168        }
169    }
170
171    /** Set the size of the specified component to match the
172     *  current value of the attribute.  If the value of the attribute
173     *  has not been set, then do nothing.  If it has not already
174     *  done so, this method also registers with the component
175     *  as a listener for component events like resizing.
176     *  @param component The component whose size is to be set.
177     *  @return True if successful.
178     */
179    public boolean setSize(Component component) {
180        if (component == null) {
181            if (_listeningTo != null) {
182                _listeningTo.removeComponentListener(this);
183                _listeningTo = null;
184            }
185            return true;
186        }
187        if (_listeningTo != component) {
188            if (_listeningTo != null) {
189                _listeningTo.removeComponentListener(this);
190            }
191
192            component.addComponentListener(this);
193            _listeningTo = component;
194        }
195
196        try {
197            IntMatrixToken token = (IntMatrixToken) getToken();
198
199            if (token != null) {
200                int width = token.getElementAt(0, 0);
201                int height = token.getElementAt(0, 1);
202
203                // NOTE: As usual with swing, it's not obvious what the
204                // right way to do this is. The following seems to work,
205                // found by trial and error.  Even then, the layout
206                // manager feels free to override it.
207                Dimension dimension = new Dimension(width, height);
208                component.setSize(dimension);
209
210                // NOTE: If it's a JComponent, the setSize() is
211                // insufficient to set the size (you will have to ask
212                // Sun why this is so).  We also have to do the
213                // following.
214                if (component instanceof JComponent) {
215                    ((JComponent) component).setPreferredSize(dimension);
216                    ((JComponent) component).setMinimumSize(dimension);
217                }
218            } else {
219                // Unset the size.
220                if (component instanceof JComponent) {
221                    ((JComponent) component).setPreferredSize(null);
222                }
223            }
224
225            return true;
226        } catch (Exception ex) {
227            return false;
228        }
229    }
230
231    ///////////////////////////////////////////////////////////////////
232    ////                         private variables                 ////
233
234    /** The component we are listening to. */
235    private Component _listeningTo;
236    // FIXME: should the above we a weak reference like what we have
237    // for WindowPropertiesAttribute?
238
239}