001/*
002 * Copyright (c) 2006-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2015-01-26 21:41:18 +0000 (Mon, 26 Jan 2015) $' 
007 * '$Revision: 33211 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.kepler.gui;
031
032import java.awt.Color;
033import java.awt.Component;
034import java.awt.Dimension;
035import java.awt.Font;
036import java.awt.Frame;
037import java.io.File;
038import java.net.URL;
039import java.util.Enumeration;
040import java.util.Hashtable;
041import java.util.Iterator;
042import java.util.LinkedList;
043import java.util.List;
044
045import javax.swing.BorderFactory;
046import javax.swing.Box;
047import javax.swing.BoxLayout;
048import javax.swing.JComponent;
049import javax.swing.JLabel;
050import javax.swing.JOptionPane;
051import javax.swing.JPanel;
052import javax.swing.JScrollPane;
053import javax.swing.JTextArea;
054import javax.swing.JTextField;
055import javax.swing.text.Caret;
056import javax.swing.text.JTextComponent;
057
058import org.kepler.kar.SuperClassPathFinderDoclet;
059
060import ptolemy.actor.IOPort;
061import ptolemy.actor.gui.Effigy;
062import ptolemy.gui.ComponentDialog;
063import ptolemy.kernel.Entity;
064import ptolemy.kernel.util.Attribute;
065import ptolemy.kernel.util.ConfigurableAttribute;
066import ptolemy.kernel.util.NamedObj;
067import ptolemy.moml.MoMLChangeRequest;
068import ptolemy.util.ExecuteCommands;
069import ptolemy.util.StringUtilities;
070import ptolemy.vergil.actor.DocApplicationSpecializer;
071import ptolemy.vergil.basic.KeplerDocumentationAttribute;
072
073//////////////////////////////////////////////////////////////////////////
074//// KeplerDocApplicationSpecializer
075
076/**
077 * Kepler specific specialization of the Ptolemy II Documentation system.
078 * 
079 * @author Christopher Brooks
080 * @version $Id: KeplerDocApplicationSpecializer.java 14354 2008-03-04 21:07:33Z
081 *          berkley $
082 * @since Ptolemy II 6.0
083 * @Pt.ProposedRating Red (cxh)
084 * @Pt.AcceptedRating Red (cxh)
085 */
086public class KeplerDocApplicationSpecializer implements
087                DocApplicationSpecializer {
088
089        // the standard width of the components in the edit dialog
090        private int standardX = 500;
091        // a hash of the editable components in the edit dialog
092        private Hashtable<String, JTextComponent> editableComponents;
093
094        // /////////////////////////////////////////////////////////////////
095        // // public methods ////
096
097        /**
098         * Given a dot separated class name, return the URL of the PtDoc .xml file
099         * documentation or the Actor Index. All other document types (Javadoc,
100         * source documentation) are currently not not searched for. Thus the either
101         * the lookForPtDoc or lookForActorIndex parameter must be true, otherwise
102         * this method will return null.
103         * 
104         * <p>
105         * If the KEPLER_DOCS property is set, then its value is assumed to point to
106         * the Kepler documentation. If KEPLER_DOCS is not set, then the file
107         * ./doc/docsInfo.txt is read, and if that file contains the string of the
108         * format <code>KeplerDocs: <i>path to kepler-docs</i></code> then the value
109         * of <code><i>path to kepler-docs</i></code> is used. For example,
110         * docsInfo.txt might contain <code>KeplerDocs: c:\src\kepler-docs</code>,
111         * which means that the documentation is to be found in
112         * <code>c:\src\kepler-docs</code>. docsInfo.txt is created by running
113         * <code>cd $KEPLER; ant generateDoc</code>. The documentation for
114         * <code><i>className</i></code> is assumed to be in
115         * <code>$KEPLER_DOCS/dev/documentationFramework/generatedJavadocs/<i>className</i>.doc.xml</code>.
116         * 
117         * @param remoteDocumentationURLBase
118         *            If non-null, the URL of the documentation. Usually, this is
119         *            set by reading the _remoteDocumentationBase parameter from the
120         *            configuration in the caller.
121         * @param className
122         *            The dot separated class name.
123         * @param lookForPtDoc
124         *            True if we should look for ptdoc .xml files.
125         * @param lookForJavadoc
126         *            Ignored
127         * @param lookForSource
128         *            Ignored
129         * @param lookForActorIndex
130         *            True if we should search for the actor index *Idx.htm file.
131         * @return The URL of the documentation, if any. If no documentation was
132         *         found, return null.
133         */
134        @Override
135    public URL docClassNameToURL(String remoteDocumentationURLBase,
136                        String className, boolean lookForPtDoc, boolean lookForJavadoc,
137                        boolean lookForSource, boolean lookForActorIndex) {
138                if (!lookForPtDoc && !lookForActorIndex) {
139                        return null;
140                }
141                URL toRead = null;
142                try {
143                        String actorName = className
144                                        .substring(className.lastIndexOf('.') + 1);
145                        
146                        /** $KEPLER_DOCS no longer set in Kepler 2.0
147                        
148                        // Javadocs located under a elsewhere in Kepler
149
150                        // Use StringUtilities.getProperty() so that we properly
151                        // handle getting properties under applets or webstart.
152                        String keplerDocsHome = StringUtilities.getProperty("KEPLER_DOCS");
153
154                        if (keplerDocsHome.equals("")
155                                        || keplerDocsHome.equals("${env.KEPLER_DOCS}")) {
156                                // Read ./doc/docsInfo.txt
157                                BufferedReader inDocsInfo = new BufferedReader(new FileReader(
158                                                "./doc/docsInfo.txt"));
159                                String info = inDocsInfo.readLine();
160
161                                if (info != null) {
162                                        String docsInfo = info.substring(info.indexOf(":") + 1);
163                                        docsInfo = docsInfo.replace('\\', '/');
164                                        docsInfo = docsInfo.trim();
165                                        keplerDocsHome = docsInfo;
166                                }
167                        }
168                        if (keplerDocsHome.equals("")
169                                        || keplerDocsHome.equals("${env.KEPLER_DOCS}")) {
170                                // If $KEPLER_DOCS is not set, look for
171                                // $KEPLER/dev/documentationFramework.
172
173                                // Use StringUtilities.getProperty() so that we properly
174                                // handle getting properties under applets or webstart.
175                                String keplerHome = StringUtilities.getProperty("KEPLER");
176                                File file = new File(keplerHome + "/dev/documentationFramework");
177                                if (file.isDirectory()) {
178                                        keplerDocsHome = keplerHome;
179                                }
180                        }
181
182                        InputStream toReadStream = null;
183                        if (!keplerDocsHome.equals("")
184                                        && !keplerDocsHome.equals("${env.KEPLER_DOCS}")) {
185
186                                keplerDocsHome = keplerDocsHome.replace('\\', '/');
187
188                                File file = null;
189                                if (lookForActorIndex) {
190                                        file = new File(keplerDocsHome
191                                                        + "/dev/documentationFramework/generatedJavadocs/"
192                                                        + className.replace('.', '/') + "Idx.htm");
193                                } else {
194                                        file = new File(keplerDocsHome
195                                                        + "/dev/documentationFramework/generatedJavadocs/"
196                                                        + actorName + ".doc.xml");
197
198                                }
199                                toRead = file.toURL();
200                                // Verify that we can read the URL.
201                                try {
202                                        toReadStream = toRead.openStream();
203                                } catch (IOException ex) {
204                                        toRead = null;
205                                } finally {
206                                        if (toReadStream != null) {
207                                                try {
208                                                        toReadStream.close();
209                                                } catch (IOException ex2) {
210                                                        // Ignore.
211                                                }
212                                        }
213                                }
214                        }
215                        if (toRead == null && remoteDocumentationURLBase != null) {
216                                try {
217                                        if (lookForActorIndex) {
218                                                toRead = new URL(remoteDocumentationURLBase
219                                                                + className.replace('.', '/') + "Idx.htm");
220                                        } else {
221                                                toRead = new URL(remoteDocumentationURLBase + actorName
222                                                                + ".doc.xml");
223                                        }
224                                        toReadStream = toRead.openStream();
225                                } catch (Exception ex) {
226                                        toRead = null;
227                                } finally {
228                                        if (toReadStream != null) {
229                                                try {
230                                                        toReadStream.close();
231                                                } catch (IOException ex2) {
232                                                        // Ignore.
233                                                }
234                                        }
235                                }
236                        }
237        */
238                        
239                if(lookForSource) {
240                    String path = SuperClassPathFinderDoclet.getFileNameForClassName(className);
241                    File file = new File(path);
242                    if(file.isFile()) {
243                        toRead = file.toURI().toURL();
244                    }
245                }
246
247                } catch (Throwable throwable) {
248                        // Ignore and return null.
249                        return null;
250                }
251                
252                        
253                return toRead;
254        }
255
256        /**
257         * Set up the commands necessary to build the documentation. For Kepler, we
258         * run "ant generateDoc" in $KEPLER>
259         * 
260         * @param executeCommands
261         *            The command execution environment necessary to build the
262         *            documentation.
263         * @return A List of Strings, where each String represents the a command to
264         *         be executed.
265         */
266        @Override
267    public List buildCommands(ExecuteCommands executeCommands) {
268                List<String> commands = new LinkedList<String>();
269
270                String osName = StringUtilities.getProperty("os.name");
271                if (osName != null && osName.startsWith("Windows")) {
272                        commands.add("ant.bat generateDoc");
273                } else {
274                        commands.add("ant generateDoc");
275                }
276                executeCommands.setCommands(commands);
277
278                File kepler = new File(StringUtilities.getProperty("KEPLER"));
279                executeCommands.setWorkingDirectory(kepler);
280                return commands;
281        }
282
283        /**
284         * returns the name of the documentation attribute
285         */
286        @Override
287    public String getDocumentationAttributeClassName() {
288                return "ptolemy.vergil.basic.KeplerDocumentationAttribute";
289        }
290
291        /**
292         * create a gui to edit the documentation in the attribute
293         * 
294         * @param owner
295         *            the editors gui parent
296         * @param attribute
297         *            the documentation attribute to edit
298         * @param target
299         *            the parent component to the attribute
300         */
301        @Override
302    public void editDocumentation(Frame owner, Attribute attribute,
303                        NamedObj target) {
304                String actorName = target.getName();
305                String title = "Editing Documentation for " + actorName;
306                JPanel panel = createEditingPanel(attribute, target, owner
307                                .getBackground());
308                JScrollPane scrollPane = new JScrollPane(panel);
309                scrollPane.setViewportBorder(BorderFactory.createEmptyBorder());
310                ComponentDialog editor = new ComponentDialog(owner, title, scrollPane);         
311                String buttonpressed = editor.buttonPressed();
312                // System.out.println("the " + buttonpressed + " button was pressed.");
313                if (buttonpressed.equals("OK")) { 
314                        // save the documentation. if cancel was clicked, do nothing
315                        saveDocumentation((KeplerDocumentationAttribute) attribute, panel);
316                }
317        }
318
319        /**
320         * handle the case where the component does not have a KeplerDocumentation
321         * attribute
322         * 
323         * @param target
324         *            the component
325         */
326        @Override
327    public void handleDocumentationAttributeDoesNotExist(Frame owner,
328                        NamedObj target) {
329                try {
330                        KeplerDocumentationAttribute kda = new KeplerDocumentationAttribute(
331                                        target, "KeplerDocumentation");
332                        kda.createEmptyFields(target);
333                        editDocumentation(owner, kda, target);
334                } catch (Exception e) {
335                        System.out.println("Error adding documentation attribute");
336                }
337        }
338
339        /**
340         * handle the case where the user tried to edit documentation and the
341         * attribute does not exist.
342         */
343        @Override
344    public void handleDocumentationNotFound(String classname, Effigy effigy) {
345                String msg = "Sorry, but there is no documentation associated with this "
346                                + "component.\n"
347                                + "If you built Kepler from sources, please run "
348                                + "'ant javadoc' and restart Kepler.\n"
349                                + "To add custom "
350                                + "documentation, please right click on the actor and "
351                                + "choose 'Documentation/Customize'.";
352
353                try {
354                        JOptionPane.showMessageDialog(effigy.getTableauFactory()
355                                        .createTableau(effigy).getFrame(), msg, "alert",
356                                        JOptionPane.ERROR_MESSAGE);
357                } catch (Exception e) {
358                }
359        }
360
361        /**
362         * save the user entered documentation in the panel to the attribute
363         */
364        private void saveDocumentation(KeplerDocumentationAttribute att,
365                        JPanel panel) {
366                Enumeration<String> keys = editableComponents.keys();
367                while (keys.hasMoreElements()) {
368                        String name = (String) keys.nextElement();
369                        JTextComponent comp = (JTextComponent) editableComponents.get(name);
370                        String value = comp.getText();
371                        ConfigurableAttribute a = (ConfigurableAttribute) att
372                                        .getAttribute(name);
373                        if (a == null) {
374                                try {
375                                        a = new ConfigurableAttribute(att, name);
376                                        a.setExpression(value);
377                                } catch (Exception e) {
378                                        System.out.println("error saving documentation: "
379                                                        + e.getMessage());
380                                }
381                        } else {
382                                String oldval = a.getExpression();
383                                if (!value.equals(oldval)) {
384                                        try {
385                                                a.setExpression(value);
386                                        } catch (Exception e) {
387                                                JOptionPane.showMessageDialog(null,
388                                                                "Error saving attribute " + name + " : "
389                                                                                + e.getMessage(), "Error",
390                                                                JOptionPane.ERROR_MESSAGE);
391                                        }
392                                }
393                        }
394                }
395
396                // Make sure the revision gets rolled on the lsid by issuing a change
397                // request on one of the fields, chose here to change "author", could be
398                // any of them
399                JTextComponent component = (JTextComponent) editableComponents
400                                .get("author");
401                String value = component.getText();
402                String caStr = "ptolemy.kernel.util.ConfigurableAttribute";
403
404                String updateMoml = "<property name=\"author\" " + "class=\"" + caStr
405                                + "\" value=\"" + value + "\"/>";
406                MoMLChangeRequest updateRequest = new MoMLChangeRequest(this, att
407                                .getContainer(), updateMoml);
408                att.requestChange(updateRequest);
409        }
410
411        /**
412         * create the panel that does the editing
413         */
414        private JPanel createEditingPanel(Attribute attribute, NamedObj target,
415                        Color background) {
416                editableComponents = new Hashtable<String, JTextComponent>();
417                // System.out.println("attribute: " + attribute.exportMoML());
418                KeplerDocumentationAttribute att = (KeplerDocumentationAttribute) attribute;
419                att.createInstanceFromExisting(att);
420                JPanel outerPanel = new JPanel();
421                // outerPanel.setBackground(background);
422                BoxLayout outerBox = new BoxLayout(outerPanel, BoxLayout.Y_AXIS);
423                outerPanel.setLayout(outerBox);
424
425                // header - name of component
426                JLabel componentNameLabel = new JLabel(target.getName());
427                componentNameLabel.setFont(new Font("Times", Font.BOLD, 20));
428                componentNameLabel.setOpaque(false);
429                JPanel componentNamePanel = new JPanel();
430                componentNamePanel.add(componentNameLabel);
431                componentNamePanel.setMaximumSize(new Dimension(standardX, 50));
432                componentNamePanel.setOpaque(false);
433                outerPanel.add(componentNamePanel);
434                outerPanel.add(Box.createVerticalStrut(10));
435
436                // author and version
437                JTextField authorTextField = new JTextField(att.getAuthor());
438                editableComponents.put("author", authorTextField);
439                authorTextField.setColumns(50);
440                JLabel authorLabel = new JLabel("Author(s):");
441                JPanel authorPanel = createEditBox(authorLabel, authorTextField);
442                JTextField versionTextField = new JTextField(att.getVersion());
443                editableComponents.put("version", versionTextField);
444                versionTextField.setColumns(10);
445                JLabel versionLabel = new JLabel("Version:");
446                JPanel versionPanel = createEditBox(versionLabel, versionTextField);
447
448                JPanel authorVersionPanel = new JPanel();
449                BoxLayout authorVersionBox = new BoxLayout(authorVersionPanel,
450                                BoxLayout.X_AXIS);
451                authorVersionPanel.setLayout(authorVersionBox);
452                authorVersionPanel.add(authorPanel);
453                authorVersionPanel.add(Box.createHorizontalStrut(10));
454                authorVersionPanel.add(versionPanel);
455                authorVersionPanel.setOpaque(false);
456                authorVersionPanel.setMaximumSize(new Dimension(standardX, 40));
457
458                outerPanel.add(authorVersionPanel);
459                outerPanel.add(Box.createVerticalStrut(10));
460
461                // description
462                JTextArea descriptionTextArea = new JTextArea(att.getDescription());
463                editableComponents.put("description", descriptionTextArea);
464                descriptionTextArea.setLineWrap(true);
465                JScrollPane descriptionScrollPane = new JScrollPane(descriptionTextArea);
466                descriptionTextArea.setColumns(50);
467                descriptionTextArea.setRows(5);
468                JLabel descriptionLabel = new JLabel("Description:");
469                JPanel descriptionPanel = createEditBox(descriptionLabel,
470                                descriptionScrollPane);
471                descriptionPanel.setMaximumSize(new Dimension(standardX, 100));
472                outerPanel.add(descriptionPanel);
473                outerPanel.add(Box.createVerticalStrut(10));
474
475                // userLevelDescription
476                JTextArea uldTextArea = new JTextArea(att.getUserLevelDocumentation());
477                editableComponents.put("userLevelDocumentation", uldTextArea);
478                uldTextArea.setLineWrap(true);
479                JScrollPane uldScrollPane = new JScrollPane(uldTextArea);
480                uldTextArea.setColumns(50);
481                uldTextArea.setRows(8);
482                JLabel uldLabel = new JLabel("Documentation:");
483                JPanel uldPanel = createEditBox(uldLabel, uldScrollPane);
484                uldPanel.setMaximumSize(new Dimension(standardX, 100));
485                outerPanel.add(uldPanel);
486                outerPanel.add(Box.createVerticalStrut(10));
487
488                // TODO: compare the kepler documentation att props and ports with the
489                // actual props and ports in the component. if there are any missing,
490                // add them or remove ones from the docs that no longer exist
491
492                // check for properties that do not have a documentation entry
493                Iterator atts = target.attributeList().iterator();
494                while (atts.hasNext()) {
495                        Attribute a = (Attribute) atts.next();
496                        String aName = a.getName();
497                        Hashtable props = att.getPropertyHash();
498                        Enumeration keys = props.keys();
499                        boolean add = true;
500                        while (keys.hasMoreElements()) {
501                                String name = (String) keys.nextElement();
502                                if (name.equals(aName)) {
503                                        add = false;
504                                }
505                        }
506
507                        if (!aName.trim().equals("") && add) {
508                                if (!aName.substring(0, 1).equals("_")
509                                                && !aName.equals("KeplerDocumentation")
510                                                && !aName.equals("entityId") && !aName.equals("class")
511                                                && !aName.equals("derivedFrom")
512                                                && aName.indexOf("semanticType") == -1) {
513                                        try {
514                                                att.addProperty(aName, "");
515                                                // ConfigurableAttribute ca = new
516                                                // ConfigurableAttribute(att, "prop:" + aName);
517                                                // ca.setExpression("");
518                                        } catch (Exception e) {
519                                                JOptionPane.showMessageDialog(null,
520                                                                "Error adding attribute " + aName + " : "
521                                                                                + e.getMessage(), "Error",
522                                                                JOptionPane.ERROR_MESSAGE);
523                                        }
524
525                                }
526                        }
527                }
528
529                // check for ports that have been added that don't have a doc property
530                if (target instanceof Entity) {
531                        Enumeration portEnum = ((Entity) target).getPorts();
532                        while (portEnum.hasMoreElements()) {
533                                IOPort iop = (IOPort) portEnum.nextElement();
534                                String iopName = iop.getName();
535                                Hashtable ports = att.getPortHash();
536                                Enumeration keys = ports.keys();
537                                boolean add = true;
538                                while (keys.hasMoreElements()) {
539                                        String name = (String) keys.nextElement();
540                                        if (name.equals(iopName)) {
541                                                add = false;
542                                        }
543                                }
544
545                                if (add) {
546                                        try {
547                                                att.addPort(iopName, "");
548                                        } catch (Exception e) {
549                                                JOptionPane.showMessageDialog(null,
550                                                                "Error adding port " + iopName + " : "
551                                                                                + e.getMessage(), "Error",
552                                                                JOptionPane.ERROR_MESSAGE);
553                                        }
554                                }
555                        }
556
557                        // ports
558                        JPanel portHeaderLabelPanel = new JPanel();
559                        JLabel portHeaderLabel = new JLabel("Ports");
560                        portHeaderLabel.setFont(new Font("Times", Font.BOLD, 20));
561                        portHeaderLabelPanel.add(portHeaderLabel);
562                        portHeaderLabelPanel.setMaximumSize(new Dimension(standardX, 50));
563                        portHeaderLabelPanel.setOpaque(false);
564                        outerPanel.add(portHeaderLabelPanel);
565
566                        Hashtable portHash = att.getPortHash();
567                        JPanel portPanel = createHashPanel(portHash, "port");
568                        portPanel.setMaximumSize(new Dimension(standardX, 1000));
569                        outerPanel.add(portPanel);
570                        outerPanel.add(Box.createVerticalStrut(20));
571                }
572
573                // params
574                JPanel propHeaderLabelPanel = new JPanel();
575                JLabel propHeaderLabel = new JLabel("Properties");
576                propHeaderLabel.setFont(new Font("Times", Font.BOLD, 20));
577
578                propHeaderLabelPanel.add(propHeaderLabel);
579                propHeaderLabelPanel.setMaximumSize(new Dimension(standardX, 50));
580                propHeaderLabelPanel.setOpaque(false);
581                outerPanel.add(propHeaderLabelPanel);
582
583                Hashtable propHash = att.getPropertyHash();
584                JPanel propPanel = createHashPanel(propHash, "prop");
585                propPanel.setMaximumSize(new Dimension(standardX, 1000));
586                outerPanel.add(propPanel);
587                outerPanel.add(Box.createVerticalStrut(20));
588
589                Component[] comps = outerPanel.getComponents();
590                int y = 0;
591                for (int i = 0; i < comps.length; i++) {
592                        // set the size of the dialog based on the size of the components in
593                        // it
594                        y += comps[i].getMinimumSize().getHeight();
595                }
596
597                outerPanel.setPreferredSize(new Dimension(standardX + 50, y + 100));
598                return outerPanel;
599        }
600
601        /**
602         * create a panel of inputs based on a hashtable
603         */
604        private JPanel createHashPanel(Hashtable hash, String type) {
605                JPanel propLabelPanel = new JPanel();
606                BoxLayout propLabelPanelBox = new BoxLayout(propLabelPanel,
607                                BoxLayout.Y_AXIS);
608                propLabelPanel.setLayout(propLabelPanelBox);
609                propLabelPanel.setMaximumSize(new Dimension(100, 1000));
610
611                JPanel propTextBoxPanel = new JPanel();
612                BoxLayout propTextBoxPanelBox = new BoxLayout(propTextBoxPanel,
613                                BoxLayout.Y_AXIS);
614                propTextBoxPanel.setLayout(propTextBoxPanelBox);
615                propTextBoxPanel.setMaximumSize(new Dimension(400, 1000));
616
617                JPanel propPanel = new JPanel();
618                BoxLayout propPanelBox = new BoxLayout(propPanel, BoxLayout.Y_AXIS);
619                propPanel.setLayout(propPanelBox);
620
621                Enumeration propEnum = hash.keys();
622                while (propEnum.hasMoreElements()) {
623                        String name = (String) propEnum.nextElement();
624                        String value = (String) hash.get(name);
625                        JLabel propLabel = new JLabel(name.trim() + ":");
626                        propLabel.setMaximumSize(new Dimension(150, 1000));
627                        JTextField propTextField = new JTextField(value.trim());
628                        propTextField.setMaximumSize(new Dimension(350, 1000));
629                        propTextField.setMinimumSize(new Dimension(350, 20));
630                        propTextField.setPreferredSize(new Dimension(350, 20));
631                        Caret caret = propTextField.getCaret();
632                        // move the carot in the text to the beginning instead of the end
633                        caret.setDot(0);
634                        propTextField.setCaret(caret);
635                        editableComponents.put(type + ":" + name, propTextField);
636                        JPanel editBoxPanel = createEditBox(propLabel, propTextField);
637
638                        propPanel.add(editBoxPanel);
639                }
640
641                propPanel.setMaximumSize(new Dimension(standardX, 1000));
642
643                return propPanel;
644        }
645
646        /**
647         * create an edit box with a label
648         */
649        private JPanel createEditBox(JLabel label, JComponent textbox) {
650                JPanel panel = new JPanel();
651                BoxLayout box = new BoxLayout(panel, BoxLayout.X_AXIS);
652                panel.setLayout(box);
653                double labelWidth = label.getMinimumSize().getWidth();
654                double textboxWidth = textbox.getMaximumSize().getWidth();
655                int offset = (int) (150 - labelWidth);
656
657                panel.add(label);
658                panel.add(Box.createHorizontalStrut(offset));
659                panel.add(textbox);
660                panel.setOpaque(false);
661
662                return panel;
663        }
664
665}