001/* An Object for changing the style of parameters.
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.style;
029
030import java.util.ArrayList;
031import java.util.HashMap;
032import java.util.Iterator;
033import java.util.List;
034import java.util.Map;
035
036import javax.swing.BoxLayout;
037
038import ptolemy.actor.gui.Configurer;
039import ptolemy.data.expr.Parameter;
040import ptolemy.gui.Query;
041import ptolemy.gui.QueryListener;
042import ptolemy.gui.Top;
043import ptolemy.kernel.util.Attribute;
044import ptolemy.kernel.util.IllegalActionException;
045import ptolemy.kernel.util.InternalErrorException;
046import ptolemy.kernel.util.KernelException;
047import ptolemy.kernel.util.NameDuplicationException;
048import ptolemy.kernel.util.NamedObj;
049import ptolemy.kernel.util.Settable;
050import ptolemy.moml.MoMLChangeRequest;
051
052///////////////////////////////////////////////////////////////////
053//// StyleConfigurer
054
055/**
056 This class is an editor for the styles of the parameters of an object.
057 It allows a user to graphically change the ParameterEditorStyles contained
058 within the user settable attributes of a named object.
059 It is very similar in spirit and style to Configurer, which edits the actual
060 values of the attributes.
061 <p>
062 The restore() method restores the values of the parameters of the
063 object to their values when this object was created.  This can be used
064 in a modal dialog to implement a cancel button, which restores
065 the styles to those before the dialog was opened.
066
067 @see ptolemy.actor.gui.Configurer
068 @see ParameterEditorStyle
069 @author Steve Neuendorffer and Edward A. Lee
070 @version $Id$
071 @since Ptolemy II 1.0
072 @Pt.ProposedRating Yellow (neuendor)
073 @Pt.AcceptedRating Yellow (neuendor)
074 */
075@SuppressWarnings("serial")
076public class StyleConfigurer extends Query implements QueryListener {
077    /** Construct a configurer for the specified object.
078     *  @param object The object to configure.
079     *  @exception IllegalActionException If the specified object has
080     *   no editor factories, and refuses to acceptable as an attribute
081     *   an instance of EditorPaneFactory.
082     */
083    public StyleConfigurer(NamedObj object) throws IllegalActionException {
084        super();
085        this.addQueryListener(this);
086        setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
087
088        _object = object;
089
090        setTextWidth(25);
091
092        try {
093            // FIXME this list should not be statically specified.
094            // Note that fixing this will probably move the accept method
095            // into some sort of factory object (instead of cloning
096            // existing styles).
097            // NOTE: These styles need to have a container so
098            // that exportMoML() doesn't generate XML header information.
099            Parameter container = new Parameter();
100            _parameterStyles = new ParameterEditorStyle[8];
101            _parameterStyles[0] = new LineStyle(container, "Line");
102            _parameterStyles[1] = new CheckBoxStyle(container, "Check Box");
103            _parameterStyles[2] = new ChoiceStyle(container, "Choice");
104            _parameterStyles[3] = new EditableChoiceStyle(container,
105                    "EditableChoice");
106            _parameterStyles[4] = new TextStyle(container, "Text");
107            _parameterStyles[5] = new FileChooserStyle(container,
108                    "FileChooser");
109            _parameterStyles[6] = new NotEditableLineStyle(container, "Fixed");
110            _parameterStyles[7] = new HiddenStyle(container, "Hidden");
111        } catch (NameDuplicationException ex) {
112            throw new InternalErrorException(ex.getMessage());
113        }
114
115        Iterator parameters = object.attributeList(Settable.class).iterator();
116
117        while (parameters.hasNext()) {
118            Settable param = (Settable) parameters.next();
119
120            // Skip if the parameter is not visible.
121            if (!Configurer.isVisible(_object, param)) {
122                continue;
123            }
124
125            // Get the current style.
126            boolean foundOne = false;
127            Iterator styles = ((NamedObj) param)
128                    .attributeList(ParameterEditorStyle.class).iterator();
129            ParameterEditorStyle foundStyle = null;
130
131            while (styles.hasNext()) {
132                foundOne = true;
133                foundStyle = (ParameterEditorStyle) styles.next();
134            }
135
136            List styleList = new ArrayList();
137
138            // The index of the default;
139            int defaultIndex = 0;
140
141            _originalExpertMode = _object.getAttribute("_expertMode") != null;
142            if (param.getVisibility() == Settable.NOT_EDITABLE
143                    && !_originalExpertMode && !foundOne) {
144                // If the parameter is set to NOT_EDITABLE visibility and not expert mode
145                // then only a fixed style is possible.
146                styleList.add("Fixed");
147                defaultIndex = 0;
148            } else {
149                int count = 0;
150
151                // Reduce the list of parameters
152                for (int i = 0; i < _parameterStyles.length
153                        && _parameterStyles[i] != null; i++) {
154                    if (foundOne && _parameterStyles[i].getClass() == foundStyle
155                            .getClass()) {
156                        defaultIndex = count;
157
158                        if (foundStyle.acceptable(param)) {
159                            styleList.add(_parameterStyles[i].getName());
160                            count++;
161                        }
162                    } else if (_parameterStyles[i].acceptable(param)) {
163                        styleList.add(_parameterStyles[i].getName());
164                        count++;
165                    }
166                }
167            }
168
169            String[] styleArray = (String[]) styleList
170                    .toArray(new String[styleList.size()]);
171
172            addChoice(param.getName(), param.getName(), styleArray,
173                    styleArray[defaultIndex]);
174        }
175
176        // Add the expert mode box.
177        addCheckBox("expertMode", "expert mode", _originalExpertMode);
178    }
179
180    ///////////////////////////////////////////////////////////////////
181    ////                         public methods                    ////
182
183    /** Generate a change request to apply the changes.
184     *  This is called to notify that one of the entries has changed.
185     *  The name of the entry is passed as an argument.
186     *  @param name The name of the entry.
187     */
188    @Override
189    public void changed(String name) {
190        StringBuffer moml = new StringBuffer();
191
192        // Treat the expertMode entry specially.
193        if (name.equals("expertMode")) {
194            Attribute previousExpert = _object.getAttribute("_expertMode");
195            boolean isExpert = previousExpert != null;
196            boolean toExpert = getBooleanValue("expertMode");
197
198            if (isExpert != toExpert) {
199                if (isExpert) {
200                    moml.append("<deleteProperty name=\"_expertMode\"/>");
201                } else {
202                    moml.append("<property name=\"_expertMode\" "
203                            + "class=\"ptolemy.kernel.util.SingletonAttribute\"/>");
204                }
205            }
206        } else {
207            // Entry is not expertMode.
208            // Figure out which style is being requested.
209            ParameterEditorStyle found = null;
210
211            for (int i = 0; i < _parameterStyles.length && found == null; i++) {
212                if (getStringValue(name)
213                        .equals(_parameterStyles[i].getName())) {
214                    found = _parameterStyles[i];
215                }
216            }
217
218            // First remove all pre-existing styles.
219            moml.append("<group>");
220
221            Attribute param = _object.getAttribute(name);
222            moml.append("<property name=\"" + param.getName() + "\">");
223
224            Iterator styles = param.attributeList(ParameterEditorStyle.class)
225                    .iterator();
226            boolean foundOne = false;
227
228            while (styles.hasNext()) {
229                foundOne = true;
230
231                ParameterEditorStyle style = (ParameterEditorStyle) styles
232                        .next();
233                moml.append(
234                        "<deleteProperty name=\"" + style.getName() + "\"/>\n");
235            }
236
237            if (foundOne) {
238                // Have to close and re-open the context to ensure
239                // that deletions occur before additions.
240                moml.append("</property>");
241                moml.append("<property name=\"" + param.getName() + "\">");
242            }
243
244            moml.append("<group name=\"auto\">");
245            if (found != null) {
246                // Coverity: found could be null if there was an internal error
247                // and the parameter style was not found/
248                moml.append(found.exportMoML("style"));
249            }
250            moml.append("</group></property></group>");
251        }
252
253        MoMLChangeRequest change = new MoMLChangeRequest(this, _object,
254                moml.toString());
255        _object.requestChange(change);
256    }
257
258    /** Request restoration of the parameter values to what they
259     *  were when this object was created.  The actual restoration
260     *  occurs later, in the UI thread, in order to allow all pending
261     *  changes to the parameter values to be processed first.
262     */
263    public void restore() {
264        // This is done in the UI thread in order to
265        // ensure that all pending UI events have been
266        // processed.  In particular, some of these events
267        // may trigger notification of new parameter values,
268        // which must not be allowed to occur after this
269        // restore is done.  In particular, the default
270        // parameter editor has lines where notification
271        // of updates occurs when the line loses focus.
272        // That notification occurs some time after the
273        // window is destroyed.
274        Top.deferIfNecessary(new Runnable() {
275            @Override
276            public void run() {
277                // Treat the expertMode entry specially.
278                Attribute currentExpert = _object.getAttribute("_expertMode");
279                boolean isExpert = currentExpert != null;
280
281                if (isExpert != _originalExpertMode) {
282                    try {
283                        if (isExpert) {
284                            currentExpert.setContainer(null);
285                        } else {
286                            // FIXME: This won't propagate.
287                            new Attribute(_object, "_expertMode");
288                        }
289                    } catch (KernelException e) {
290                        // This should not occur.
291                        throw new InternalErrorException(e);
292                    }
293                }
294
295                // FIXME: This code is nonsensical... _originalValues never
296                // gets anything added to it!
297                Iterator entries = _originalValues.entrySet().iterator();
298
299                while (entries.hasNext()) {
300                    Map.Entry entry = (Map.Entry) entries.next();
301                    Settable param = (Settable) _object
302                            .getAttribute((String) entry.getKey());
303
304                    try {
305                        param.setExpression((String) entry.getValue());
306                    } catch (IllegalActionException ex) {
307                        throw new InternalErrorException(
308                                "Cannot restore style value!");
309                    }
310                }
311            }
312        });
313    }
314
315    ///////////////////////////////////////////////////////////////////
316    ////                         private variables                 ////
317    // The object that this configurer configures.
318    private NamedObj _object;
319
320    // Indicator of what the expert mode was upon entry.
321    private boolean _originalExpertMode = false;
322
323    // The original values of the parameters.
324    private Map _originalValues = new HashMap();
325
326    // The list of the possible styles.
327    private ParameterEditorStyle[] _parameterStyles;
328}