001/* A top-level frame for editing the layout of a customizable run control panel.
002
003 Copyright (c) 2007-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 */
027package ptolemy.actor.gui.run;
028
029import java.awt.BorderLayout;
030import java.awt.Component;
031import java.awt.Container;
032import java.util.ArrayList;
033import java.util.HashMap;
034import java.util.List;
035import java.util.Map;
036
037import javax.swing.JTabbedPane;
038
039import org.mlc.swing.layout.ContainerLayout;
040import org.mlc.swing.layout.LayoutConstraintsManager;
041import org.mlc.swing.layout.LayoutFrame;
042import org.mlc.swing.layout.MultiContainerFrame;
043
044import ptolemy.actor.CompositeActor;
045import ptolemy.actor.gui.Tableau;
046import ptolemy.actor.gui.TableauFrame;
047import ptolemy.kernel.util.Attribute;
048import ptolemy.kernel.util.ConfigurableAttribute;
049import ptolemy.kernel.util.IllegalActionException;
050import ptolemy.kernel.util.InternalErrorException;
051import ptolemy.kernel.util.KernelException;
052import ptolemy.util.CancelException;
053import ptolemy.util.MessageHandler;
054
055///////////////////////////////////////////////////////////////////
056//// RunLayoutFrame
057
058/**
059 A top-level frame for editing the layout of a customizable run control panel.
060 <p>
061 This is based on the LayoutFrame class by Michael Connor (mlconnor&#064;yahoo.com).
062
063 @see LayoutFrame
064 @author Edward A. Lee
065 @version $Id$
066 @since Ptolemy II 8.0
067 @Pt.ProposedRating Yellow (eal)
068 @Pt.AcceptedRating Red (cxh)
069 */
070@SuppressWarnings("serial")
071public class RunLayoutFrame extends TableauFrame
072        implements MultiContainerFrame {
073    /** Construct a frame to control the specified Ptolemy II model.
074     *  After constructing this, it is necessary
075     *  to call setVisible(true) to make the frame appear.
076     *  This is typically accomplished by calling show() on
077     *  enclosing tableau.
078     *  @param model The model to put in this frame, or null if none.
079     *  @param tableau The tableau responsible for this frame.
080     *  @param pane The run pane whose layout is being edited.
081     *  @exception IllegalActionException If the XML to be parsed has errors.
082     */
083    public RunLayoutFrame(CompositeActor model, Tableau tableau,
084            CustomizableRunPane pane) throws IllegalActionException {
085        super(tableau);
086        _model = model;
087        _constraintsManager = pane.getLayoutConstraintsManager();
088        _pane = pane;
089
090        List<ContainerLayout> layouts = _constraintsManager.getLayouts();
091        for (int index = 0; index < layouts.size(); index++) {
092            ContainerLayout containerLayout = layouts.get(index);
093            Container container = _constraintsManager
094                    .getContainer(containerLayout);
095            if (container == null) {
096                // If data is malformed, issue a warning and proceed.
097                try {
098                    MessageHandler.warning("A container with name "
099                            + containerLayout.getName()
100                            + " was found in the contstraints file but was not found in the container");
101                } catch (CancelException ex) {
102                    throw new IllegalActionException(model, "Canceled");
103                }
104
105            } else {
106                addContainerLayout(containerLayout, container);
107            }
108        }
109
110        getContentPane().setLayout(new BorderLayout(3, 3));
111        getContentPane().add(_tabs, BorderLayout.CENTER);
112
113        pack();
114    }
115
116    ///////////////////////////////////////////////////////////////////
117    ////                         public methods                    ////
118
119    /** Add a container with the specified name.
120     *  @param name The name of the container.
121     *  @param container The container.
122     */
123    @Override
124    public void addContainer(String name, Container container) {
125        // check to see if another panel with this name already exists
126        ContainerLayout layout = _constraintsManager.getContainerLayout(name);
127        if (layout != null) {
128            throw new IllegalArgumentException(
129                    "A container with name " + name + " already exists");
130        }
131
132        layout = new ContainerLayout(name, "pref", "pref");
133        _constraintsManager.addLayout(layout);
134        container.setLayout(layout);
135        _newLayouts.add(layout);
136
137        addContainerLayout(layout, container);
138    }
139
140    /** Return true if the frame has a container with the specified name.
141     *  @param name The name of the container.
142     *  @return true if the frame has a container with the specified name.
143     */
144    @Override
145    public boolean hasContainer(String name) {
146        return _constraintsManager.getContainerLayout(name) != null;
147    }
148
149    /** Remove the container with the specified name.
150     *  This may throw an InternalErrorException if the container does not exist.
151     *  @param name The name of the container.
152     */
153    @Override
154    public void removeContainer(String name) {
155        ContainerLayout layout = _constraintsManager.getContainerLayout(name);
156        if (layout == null) {
157            throw new InternalErrorException(
158                    "Container " + name + " does not exist");
159        }
160        // Also have to remove any contained containers!
161        Container container = _constraintsManager.getContainer(layout);
162        Component[] components = container.getComponents();
163        for (Component component2 : components) {
164            if (component2 instanceof Container) {
165                String componentName = layout.getComponentName(component2);
166                if (hasContainer(componentName)) {
167                    removeContainer(componentName);
168                }
169            }
170        }
171        _constraintsManager.removeLayout(layout);
172        PtolemyFormEditor editor = _editors.get(layout);
173        _tabs.remove(editor);
174        _newLayouts.remove(layout);
175    }
176
177    ///////////////////////////////////////////////////////////////////
178    ////                         protected methods                 ////
179
180    /** Close the window.  This overrides the base class to record
181     *  the modified layout, if it has been modified.
182     *  @return False if the user cancels on a save query.
183     */
184    @Override
185    protected boolean _close() {
186        if (super._close()) {
187            // Save the XML.
188            // FIXME: Do this only if the layout has been modified.
189            String xml = _constraintsManager.getXML();
190            Attribute layoutAttribute = _model
191                    .getAttribute("_runLayoutAttribute");
192            if (layoutAttribute == null) {
193                try {
194                    layoutAttribute = new ConfigurableAttribute(_model,
195                            "_runLayoutAttribute");
196                } catch (KernelException e) {
197                    throw new InternalErrorException(e);
198                }
199            }
200            if (!(layoutAttribute instanceof ConfigurableAttribute)) {
201                MessageHandler.error("Model contains an attribute named "
202                        + "'_runLayoutAttribute' that is not the right class "
203                        + "to save a run layout in: " + _model.getFullName());
204            }
205            try {
206                ((ConfigurableAttribute) layoutAttribute).configure(null, null,
207                        xml);
208            } catch (Exception e) {
209                throw new InternalErrorException(e);
210            }
211            return true;
212        }
213        return false;
214    }
215
216    ///////////////////////////////////////////////////////////////////
217    ////                         protected variables               ////
218
219    /** The run pane whose layout is being edited. */
220    protected CustomizableRunPane _pane;
221
222    /** The associated model. */
223    protected CompositeActor _model;
224
225    ///////////////////////////////////////////////////////////////////
226    ////                         private methods                   ////
227
228    /** Add an instance of PtolemyFormEditor for the specified containerLayout
229     *  to the specified container.
230     *  @param containerLayout The layout to edit.
231     *  @param container The container into which to put the editor.
232     */
233    private void addContainerLayout(ContainerLayout containerLayout,
234            Container container) {
235        PtolemyFormEditor formEditor = new PtolemyFormEditor(this,
236                containerLayout, container);
237        _editors.put(containerLayout, formEditor);
238        _tabs.addTab(containerLayout.getName(), formEditor);
239    }
240
241    ///////////////////////////////////////////////////////////////////
242    ////                         private variables                 ////
243
244    /** The constraints manager being edited. */
245    private LayoutConstraintsManager _constraintsManager;
246
247    /** Set of editors indexed by layout. */
248    private Map<ContainerLayout, PtolemyFormEditor> _editors = new HashMap<ContainerLayout, PtolemyFormEditor>();
249
250    /** A list of new layouts. */
251    private List<ContainerLayout> _newLayouts = new ArrayList<ContainerLayout>();
252
253    /** Tabbed pane for showing nested layouts. */
254    private JTabbedPane _tabs = new JTabbedPane();
255}