001/* A panel containing customizable controls for a Ptolemy II model.
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 */
027package ptolemy.actor.gui.run;
028
029import java.awt.Component;
030import java.awt.LayoutManager;
031import java.awt.Window;
032import java.awt.event.ActionEvent;
033import java.awt.event.ActionListener;
034import java.io.ByteArrayInputStream;
035import java.io.IOException;
036import java.io.InputStream;
037import java.io.UnsupportedEncodingException;
038import java.util.HashMap;
039import java.util.Iterator;
040
041import javax.swing.JButton;
042import javax.swing.JLabel;
043import javax.swing.JPanel;
044import javax.xml.parsers.DocumentBuilder;
045import javax.xml.parsers.DocumentBuilderFactory;
046
047import org.mlc.swing.layout.ContainerLayout;
048import org.mlc.swing.layout.LayoutConstraintsManager;
049import org.w3c.dom.Document;
050import org.w3c.dom.Node;
051import org.w3c.dom.NodeList;
052
053import ptolemy.actor.CompositeActor;
054import ptolemy.actor.Manager;
055import ptolemy.actor.gui.AWTContainer;
056import ptolemy.actor.gui.Configurer;
057import ptolemy.actor.gui.Placeable;
058import ptolemy.actor.injection.PortablePlaceable;
059import ptolemy.gui.CloseListener;
060import ptolemy.kernel.ComponentEntity;
061import ptolemy.kernel.util.Attribute;
062import ptolemy.kernel.util.ConfigurableAttribute;
063import ptolemy.kernel.util.IllegalActionException;
064import ptolemy.kernel.util.InternalErrorException;
065import ptolemy.kernel.util.NamedObj;
066import ptolemy.util.CancelException;
067import ptolemy.util.MessageHandler;
068
069///////////////////////////////////////////////////////////////////
070//// CustomizableRunPane
071
072/**
073
074 A panel for interacting with an executing Ptolemy II model.
075 This panel can be customized by inserting
076
077 FIXME: more
078
079 @see Placeable
080 @author Edward A. Lee
081 @version $Id$
082 @since Ptolemy II 8.0
083 @Pt.ProposedRating Yellow (eal)
084 @Pt.AcceptedRating Red (cxh)
085 */
086@SuppressWarnings("serial")
087public class CustomizableRunPane extends JPanel implements CloseListener {
088
089    /** Construct a panel for interacting with the specified Ptolemy II model.
090     *  @param model The model to control.
091     *  @param xml The XML specification of the layout, or null to use the default.
092     *  @exception IllegalActionException If the XML to be parsed has errors.
093     */
094    public CustomizableRunPane(CompositeActor model, String xml)
095            throws IllegalActionException {
096        super();
097
098        _model = model;
099
100        // If no XML is specified, then see whether the model has one.
101        if (xml == null) {
102            Attribute layoutAttribute = _model
103                    .getAttribute("_runLayoutAttribute");
104            if (layoutAttribute instanceof ConfigurableAttribute) {
105                try {
106                    xml = ((ConfigurableAttribute) layoutAttribute).value();
107                } catch (IOException e) {
108                    throw new InternalErrorException(e);
109                }
110            }
111        }
112
113        if (xml == null) {
114            xml = _defaultLayout();
115        }
116        // Parse the XML
117        InputStream stream;
118        try {
119            stream = new ByteArrayInputStream(xml.getBytes("UTF-8"));
120        } catch (UnsupportedEncodingException e1) {
121            throw new InternalErrorException(e1);
122        }
123        Document dataDocument = null;
124        try {
125            DocumentBuilderFactory factory = DocumentBuilderFactory
126                    .newInstance();
127            // Without the following, then carriage returns in the XML
128            // mess up the parsing, incredibly enough!
129            factory.setIgnoringElementContentWhitespace(true);
130            DocumentBuilder documentBuilder = factory.newDocumentBuilder();
131            dataDocument = documentBuilder.parse(stream);
132        } catch (Exception e) {
133            throw new IllegalActionException(model, e,
134                    "Unable to parse layout specification.");
135        }
136        Node root = dataDocument.getDocumentElement();
137        _layoutConstraintsManager = LayoutConstraintsManager
138                .getLayoutConstraintsManager(root);
139
140        ContainerLayout layout = _layoutConstraintsManager.createLayout("top",
141                this);
142        this.setLayout(layout);
143
144        // Walk through the XML, creating an interface as specified in it.
145        // This assumes a very specific structure to the XML.
146        NodeList components = root.getChildNodes();
147        // Go through the subpanels.
148        for (int i = 0; i < components.getLength(); i++) {
149            Node subpanelNode = components.item(i);
150            if (!subpanelNode.getNodeName().equals("container")) {
151                continue;
152            }
153            String subpanelName = subpanelNode.getAttributes()
154                    .getNamedItem("name").getNodeValue();
155            if (subpanelName.equals("top")) {
156                NodeList nodeList = components.item(i).getChildNodes();
157                for (int j = 0; j < nodeList.getLength(); j++) {
158                    Node node = nodeList.item(j);
159                    // Skip over nodes that are not cellconstraints.
160                    if (!node.getNodeName().equals("cellconstraints")) {
161                        continue;
162                    }
163                    // The attributes will be null if the node represents the contents text.
164                    if (node.getAttributes() != null) {
165                        String name = node.getAttributes().getNamedItem("name")
166                                .getNodeValue();
167                        _addComponent(name, this);
168                    }
169                }
170            } else {
171                if (_subpanels == null) {
172                    throw new IllegalActionException(_model,
173                            "Panel 'top' is required to be first. Found instead: "
174                                    + subpanelName);
175                }
176                JPanel panel = _subpanels.get(subpanelName);
177                if (panel == null) {
178                    // FIXME: FormsLayout has a bug where if a subpanel that contains
179                    // a subpanel is deleted, then the subsubpanel layout is not deleted.
180                    try {
181                        MessageHandler.warning(
182                                "A layout is given for a subpanel named '"
183                                        + subpanelName
184                                        + "', but there is no instance of this subpanel.");
185                    } catch (CancelException e) {
186                        throw new IllegalActionException(_model, "Canceled");
187                    }
188                    continue;
189                }
190                NodeList nodeList = subpanelNode.getChildNodes();
191                for (int j = 0; j < nodeList.getLength(); j++) {
192                    Node node = nodeList.item(j);
193                    // The attributes will be null if the node represents the contents text.
194                    if (node.getAttributes() != null) {
195                        String name = node.getAttributes().getNamedItem("name")
196                                .getNodeValue();
197                        _addComponent(name, panel);
198                    }
199                }
200            }
201        }
202    }
203
204    ///////////////////////////////////////////////////////////////////
205    ////                         public methods                    ////
206
207    /** Return the layout constraints manager for this pane.
208     *  @return A layout constraints manager.
209     */
210    public LayoutConstraintsManager getLayoutConstraintsManager() {
211        return _layoutConstraintsManager;
212    }
213
214    /** If the model has a manager and is executing, then
215     *  pause execution by calling the pause() method of the manager.
216     *  If there is no manager, do nothing.
217     */
218    public void pauseRun() {
219        Manager manager = _model.getManager();
220        if (manager != null) {
221            manager.pause();
222        }
223    }
224
225    /** If the model has a manager and is executing, then
226     *  resume execution by calling the resume() method of the manager.
227     *  If there is no manager, do nothing.
228     */
229    public void resumeRun() {
230        Manager manager = _model.getManager();
231        if (manager != null) {
232            manager.resume();
233        }
234    }
235
236    /** If the model has a manager and is not already running,
237     *  then execute the model in a new thread.  Otherwise, do nothing.
238     */
239    public void startRun() {
240        Manager manager = _model.getManager();
241        if (manager != null) {
242            try {
243                manager.startRun();
244            } catch (IllegalActionException ex) {
245                // Model is already running.  Ignore.
246            }
247        }
248    }
249
250    /** If the model has a manager and is executing, then
251     *  stop execution by calling the stop() method of the manager.
252     *  If there is no manager, do nothing.
253     */
254    public void stopRun() {
255        Manager manager = _model.getManager();
256        if (manager != null) {
257            manager.stop();
258        }
259    }
260
261    /** Notify the contained instances of PtolemyQuery that the window
262     *  has been closed, and remove all Placeable displays by calling
263     *  place(null).  This method is called if this pane is contained
264     *  within a container that supports such notification.
265     *  @param window The window that closed.
266     *  @param button The name of the button that was used to close the window.
267     */
268    @Override
269    public void windowClosed(Window window, String button) {
270        // FIXME: This is not getting invoked. Need to override
271        // TableauFrame above with an override to _close().
272        // Better yet, should use ModelFrame instead of TableauFrame,
273        // but then ModelPane needs to be converted to an interface
274        // so that this pane can be used instead.
275        if (_model != null) {
276            _closeDisplays();
277        }
278    }
279
280    ///////////////////////////////////////////////////////////////////
281    ////                         public variables                  ////
282
283    ///////////////////////////////////////////////////////////////////
284    ////                         protected methods                 ////
285
286    /** Return a component with the specified name. The name is of the form
287     *  TYPE:DETAIL, where TYPE defines the type of component to add
288     *  and DETAIL specifies details.
289     *  @param name The name.
290     *  @return A component, or null if the specification is not recognized.
291     *  @exception IllegalActionException If there is an error in the name.
292     */
293    protected Component _getComponent(String name)
294            throws IllegalActionException {
295        // Figure out what type of component to create.
296        if (name.startsWith("Placeable:")) {
297            // Display of an actor that implements the Placeable
298            // interface is given with "Placeable:NAME" where
299            // NAME is the name of the actor relative to the model.
300            String actorName = name.substring(10);
301            ComponentEntity entity = _model.getEntity(actorName);
302            if (!(entity instanceof Placeable
303                    || entity instanceof PortablePlaceable)) {
304                throw new IllegalActionException(_model,
305                        "Entity that does not implement Placeable is specified in a display.");
306            }
307            // Regrettably, it seems we need an intermediate JPanel.
308            JPanel dummy = new JPanel();
309            if (entity instanceof Placeable) {
310                ((Placeable) entity).place(dummy);
311            } else if (entity instanceof PortablePlaceable) {
312                ((PortablePlaceable) entity).place(new AWTContainer(dummy));
313            }
314            return dummy;
315        } else if (name.startsWith("Label")) {
316            // Default is the text after the colon in the name, if
317            // there is one.
318            int colon = name.indexOf(":");
319            String label = "Label";
320            if (name.length() > 5 && colon > 4) {
321                label = name.substring(colon + 1);
322            }
323            return new JLabel(label);
324        } else if (name.startsWith("GoButton")) {
325            // Go button is specified with "GoButton", where the label
326            // on the button is given by either the "label" or "text"
327            // Java Bean property, or if there is none, by the default
328            // label "Go".
329            JButton goButton = new JButton("Go");
330            goButton.setToolTipText("Execute the model");
331            goButton.addActionListener(new ActionListener() {
332                @Override
333                public void actionPerformed(ActionEvent event) {
334                    startRun();
335                }
336            });
337            return goButton;
338        } else if (name.startsWith("PauseButton")) {
339            // Button is specified with "PauseButton", where the label
340            // on the button is given by either the "label" or "text"
341            // Java Bean property, or if there is none, by the default
342            // label "Pause".
343            JButton pauseButton = new JButton("Pause");
344            pauseButton.setToolTipText("Pause the model");
345            pauseButton.addActionListener(new ActionListener() {
346                @Override
347                public void actionPerformed(ActionEvent event) {
348                    pauseRun();
349                }
350            });
351            return pauseButton;
352        } else if (name.startsWith("ResumeButton")) {
353            // Button is specified with "ResumeButton", where the label
354            // on the button is given by either the "label" or "text"
355            // Java Bean property, or if there is none, by the default
356            // label "Resume".
357            JButton button = new JButton("Resume");
358            button.setToolTipText("Resume the model");
359            button.addActionListener(new ActionListener() {
360                @Override
361                public void actionPerformed(ActionEvent event) {
362                    resumeRun();
363                }
364            });
365            return button;
366        } else if (name.startsWith("StopButton")) {
367            // Button is specified with "StopButton", where the label
368            // on the button is given by either the "label" or "text"
369            // Java Bean property, or if there is none, by the default
370            // label "Stop".
371            JButton button = new JButton("Stop");
372            button.setToolTipText("Stop the model");
373            button.addActionListener(new ActionListener() {
374                @Override
375                public void actionPerformed(ActionEvent event) {
376                    stopRun();
377                }
378            });
379            return button;
380        } else if (name.equals("ConfigureTopLevel")) {
381            // A parameter editor for top-level parameters
382            return new Configurer(_model);
383        } else if (name.equals("ConfigureDirector")) {
384            // A parameter editor for the director
385            if (_model.getDirector() == null) {
386                throw new IllegalActionException(_model,
387                        "Does not have a director. A director is needed to have a contol panel.");
388            }
389            return new Configurer(_model.getDirector());
390        } else if (name.startsWith("ConfigureEntity:")) {
391            // A parameter editor for an entity is specified with
392            // "ConfigureEntity:NAME", where NAME is the name of
393            // the entity to configure relative to the top level.
394            String entityName = name.substring(16);
395            ComponentEntity entity = _model.getEntity(entityName);
396            if (entity == null) {
397                throw new IllegalActionException(_model,
398                        "Nonexistent entity: " + entityName);
399            }
400            return new Configurer(entity);
401        } else if (name.startsWith("Subpanel:")) {
402            // Subpanel is specified with "Subpanel:NAME", where NAME
403            // is the name of the the subpanel.
404            JPanel subpanel = new JPanel();
405            LayoutManager layout = _layoutConstraintsManager.createLayout(name,
406                    subpanel);
407            subpanel.setLayout(layout);
408            if (_subpanels == null) {
409                _subpanels = new HashMap<String, JPanel>();
410            }
411            _subpanels.put(name, subpanel);
412            return subpanel;
413        } else {
414            // FIXME: When a component is dragged from once
415            // cell to another, the FormsLayout package messes
416            // up and puts in a spurious entry in the XML
417            // with entity name matching the enclosing panel
418            // and layout constraints matching the result of
419            // the drag.  Why?
420            try {
421                MessageHandler.warning(
422                        "Unrecognized entry in control panel layout: " + name);
423            } catch (CancelException e) {
424                throw new IllegalActionException(_model, "Canceled");
425            }
426            return null;
427        }
428    }
429
430    ///////////////////////////////////////////////////////////////////
431    ////                         protected variables               ////
432
433    /** The associated model. */
434    protected CompositeActor _model;
435
436    ///////////////////////////////////////////////////////////////////
437    ////                         private methods                   ////
438
439    /** Add a component with the specified name. The name is of the form
440     *  TYPE:DETAIL, where TYPE defines the type of component to add
441     *  and DETAIL specifies details.
442     *  @param name The name.
443     *  @param panel The panel into which to add the component.
444     *  @exception IllegalActionException If there is an error in the XML.
445     */
446    private void _addComponent(String name, JPanel panel)
447            throws IllegalActionException {
448        Component component = _getComponent(name);
449        if (component != null) {
450            panel.add(component, name);
451        }
452    }
453
454    /** Close any open displays by calling place(null).
455     */
456    private void _closeDisplays() {
457        if (_model != null) {
458            Iterator atomicEntities = _model.allAtomicEntityList().iterator();
459
460            while (atomicEntities.hasNext()) {
461                Object object = atomicEntities.next();
462
463                if (object instanceof Placeable) {
464                    ((Placeable) object).place(null);
465                } else if (object instanceof PortablePlaceable) {
466                    ((PortablePlaceable) object).place(null);
467                }
468            }
469        }
470    }
471
472    /** Create a default layout for the associated model. */
473    private String _defaultLayout() {
474        StringBuffer xml = new StringBuffer(
475                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
476        xml.append("<containers>\n");
477
478        // Top-level panel has two columns and one row.
479        xml.append("<container name=\"top\" "
480                + "columnSpecs=\"default,3dlu,default:grow\" "
481                + "rowSpecs=\"default\">\n");
482        xml.append(
483                "<cellconstraints name=\"Subpanel:ControlPanel\" gridX=\"1\" gridY=\"1\" "
484                        + "gridWidth=\"1\" gridHeight=\"1\" horizontalAlignment=\"default\" "
485                        + "verticalAlignment=\"top\" topInset=\"0\" bottomInset=\"0\" "
486                        + "rightInset=\"0\" leftInset=\"0\"/>\n");
487        xml.append(
488                "<cellconstraints name=\"Subpanel:PlaceablePanel\" gridX=\"3\" gridY=\"1\" "
489                        + "gridWidth=\"1\" gridHeight=\"1\" horizontalAlignment=\"default\" "
490                        + "verticalAlignment=\"top\" topInset=\"0\" bottomInset=\"0\" "
491                        + "rightInset=\"0\" leftInset=\"0\"/>\n");
492        xml.append("</container>\n");
493
494        // Subpanel with the run control buttons, the top-level parameters,
495        // and the director parameters.
496        xml.append("<container name=\"Subpanel:ControlPanel\" "
497                + "columnSpecs=\"default\" "
498                + "rowSpecs=\"default,5dlu,default,5dlu,default,5dlu,default,5dlu,"
499                + "default,5dlu,default,5dlu,default\">\n");
500        xml.append(
501                "<cellconstraints name=\"Label:Run Control\" gridX=\"1\" gridY=\"1\" "
502                        + "gridWidth=\"1\" gridHeight=\"1\" "
503                        + "horizontalAlignment=\"default\" verticalAlignment=\"default\" "
504                        + "topInset=\"0\" bottomInset=\"0\" rightInset=\"0\" leftInset=\"0\"/>\n");
505        xml.append(
506                "<cellconstraints name=\"Subpanel:Run Control\" gridX=\"1\" gridY=\"3\" "
507                        + "gridWidth=\"1\" gridHeight=\"1\" "
508                        + "horizontalAlignment=\"default\" verticalAlignment=\"top\" "
509                        + "topInset=\"0\" bottomInset=\"0\" rightInset=\"0\" leftInset=\"0\"/>\n");
510        // Place a configurer for the top-level settables here.
511        xml.append(
512                "<cellconstraints name=\"Label:Top-Level Parameters\" gridX=\"1\" gridY=\"5\" "
513                        + "gridWidth=\"1\" gridHeight=\"1\" "
514                        + "horizontalAlignment=\"default\" verticalAlignment=\"default\" "
515                        + "topInset=\"0\" bottomInset=\"0\" rightInset=\"0\" leftInset=\"0\"/>\n");
516        xml.append(
517                "<cellconstraints name=\"ConfigureTopLevel\" gridX=\"1\" gridY=\"7\" "
518                        + "gridWidth=\"1\" gridHeight=\"1\" horizontalAlignment=\"default\" "
519                        + "verticalAlignment=\"top\" topInset=\"0\" bottomInset=\"0\" "
520                        + "rightInset=\"0\" leftInset=\"0\"/>\n");
521        // Place a configurer for the director settables here.
522        xml.append(
523                "<cellconstraints name=\"Label:Director Parameters\" gridX=\"1\" gridY=\"9\" "
524                        + "gridWidth=\"1\" gridHeight=\"1\" "
525                        + "horizontalAlignment=\"default\" verticalAlignment=\"default\" "
526                        + "topInset=\"0\" bottomInset=\"0\" rightInset=\"0\" leftInset=\"0\"/>\n");
527        xml.append(
528                "<cellconstraints name=\"ConfigureDirector\" gridX=\"1\" gridY=\"11\" "
529                        + "gridWidth=\"1\" gridHeight=\"1\" horizontalAlignment=\"default\" "
530                        + "verticalAlignment=\"top\" topInset=\"0\" bottomInset=\"0\" "
531                        + "rightInset=\"0\" leftInset=\"0\"/>\n");
532        xml.append("</container>\n");
533
534        // Subpanel with the run control buttons.
535        xml.append("<container name=\"Subpanel:Run Control\" "
536                + "columnSpecs=\"default,3dlu,default,3dlu,default,3dlu,default\" "
537                + "rowSpecs=\"default\">\n");
538        xml.append("<cellconstraints name=\"GoButton\" gridX=\"1\" gridY=\"1\" "
539                + "gridWidth=\"1\" gridHeight=\"1\" "
540                + "horizontalAlignment=\"default\" verticalAlignment=\"default\" "
541                + "topInset=\"0\" bottomInset=\"0\" rightInset=\"0\" leftInset=\"0\"/>\n");
542        xml.append(
543                "<cellconstraints name=\"PauseButton\" gridX=\"3\" gridY=\"1\" "
544                        + "gridWidth=\"1\" gridHeight=\"1\" "
545                        + "horizontalAlignment=\"default\" verticalAlignment=\"default\" "
546                        + "topInset=\"0\" bottomInset=\"0\" rightInset=\"0\" leftInset=\"0\"/>\n");
547        xml.append(
548                "<cellconstraints name=\"ResumeButton\" gridX=\"5\" gridY=\"1\" "
549                        + "gridWidth=\"1\" gridHeight=\"1\" "
550                        + "horizontalAlignment=\"default\" verticalAlignment=\"default\" "
551                        + "topInset=\"0\" bottomInset=\"0\" rightInset=\"0\" leftInset=\"0\"/>\n");
552        xml.append(
553                "<cellconstraints name=\"StopButton\" gridX=\"7\" gridY=\"1\" "
554                        + "gridWidth=\"1\" gridHeight=\"1\" "
555                        + "horizontalAlignment=\"default\" verticalAlignment=\"default\" "
556                        + "topInset=\"0\" bottomInset=\"0\" rightInset=\"0\" leftInset=\"0\"/>\n");
557        xml.append("</container>\n");
558
559        // Subpanel for each object that implements Placeable.
560        xml.append("<container name=\"Subpanel:PlaceablePanel\" ");
561        xml.append(
562                "columnSpecs=\"default,3dlu,default,3dlu,default,3dlu,default\" ");
563        // FIXME: Need some way to resize plot windows here...
564        xml.append("rowSpecs=\"default");
565        StringBuffer constraints = new StringBuffer();
566        if (_model != null) {
567            Iterator atomicEntities = _model.allAtomicEntityList().iterator();
568            int row = 1;
569            while (atomicEntities.hasNext()) {
570                Object object = atomicEntities.next();
571                if (object instanceof Placeable
572                        || object instanceof PortablePlaceable) {
573                    if (row > 1) {
574                        xml.append(",5dlu,default");
575                    }
576                    constraints.append("<cellconstraints name=\"Placeable:");
577                    constraints.append(((NamedObj) object).getName(_model));
578                    constraints.append("\" gridX=\"3\" gridY=\"");
579                    constraints.append(row);
580                    constraints.append(
581                            "\" gridWidth=\"1\" gridHeight=\"1\" horizontalAlignment=\"default\" "
582                                    + "verticalAlignment=\"default\" topInset=\"0\" bottomInset=\"0\" "
583                                    + "rightInset=\"0\" leftInset=\"0\"/>\n");
584                    row = row + 2;
585                }
586            }
587        }
588        // End the row specs.
589        xml.append("\">\n");
590        // Add the constraints.
591        xml.append(constraints);
592        xml.append("</container>\n");
593
594        xml.append("</containers>\n");
595        return xml.toString();
596    }
597
598    ///////////////////////////////////////////////////////////////////
599    ////                         private variables                 ////
600
601    /** The layout constraint manager. */
602    private LayoutConstraintsManager _layoutConstraintsManager;
603
604    /** A collection of subpanels. */
605    private HashMap<String, JPanel> _subpanels;
606}