001/*
002 * Copyright (c) 2004-2007 by Michael Connor. All Rights Reserved.
003 *
004 * Redistribution and use in source and binary forms, with or without
005 * modification, are permitted provided that the following conditions are met:
006 *
007 *  o Redistributions of source code must retain the above copyright notice,
008 *    this list of conditions and the following disclaimer.
009 *
010 *  o Redistributions in binary form must reproduce the above copyright notice,
011 *    this list of conditions and the following disclaimer in the documentation
012 *    and/or other materials provided with the distribution.
013 *
014 *  o Neither the name of FormLayoutBuilder or Michael Connor nor the names of
015 *    its contributors may be used to endorse or promote products derived
016 *    from this software without specific prior written permission.
017 *
018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
020 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
021 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
022 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
025 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
026 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
027 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
028 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030/*
031 * NewComponentDialog.java
032 *
033 * Created on March 24, 2005, 6:49 PM
034 */
035
036package org.mlc.swing.layout;
037
038import java.awt.Component;
039import java.awt.Window;
040import java.awt.event.ActionEvent;
041import java.awt.event.ActionListener;
042
043import javax.swing.JButton;
044import javax.swing.JDialog;
045import javax.swing.JFrame;
046import javax.swing.JLabel;
047import javax.swing.JPanel;
048import javax.swing.JScrollPane;
049import javax.swing.JTextArea;
050import javax.swing.JTextField;
051import javax.swing.WindowConstants;
052
053import com.jgoodies.forms.factories.Borders;
054import com.jgoodies.forms.factories.ButtonBarFactory;
055
056import bsh.Interpreter;
057
058/**
059 * When performing drag-and-drop from the component palette to the table,
060 * this class is used to define or edit a component's settings. These
061 * settings include the import, declaration, configuration lines which
062 * will be used to generate the code for the component. The same settings
063 * are used to create a 'preview' component to be shown in the preview
064 * window.
065 *
066 * @author Michael Connor
067@version $Id$
068@since Ptolemy II 8.0
069 */
070@SuppressWarnings("serial")
071public class NewComponentDialog extends JPanel {
072    JLabel componentNameLabel = new JLabel("Name");
073    JTextField componentNameTextField = new JTextField();
074
075    JLabel importsLabel = new JLabel("Imports");
076    JTextArea importsComponent = createTextArea(3, 40);
077
078    JLabel declarationsLabel = new JLabel("Declarations");
079    JTextArea declarationsComponent = createTextArea(3, 40);
080
081    JLabel configureLabel = new JLabel("Configure");
082    JTextArea configureComponent = createTextArea(4, 40);
083
084    JLabel addToContainerLabel = new JLabel("Add");
085    JTextArea addToContainerComponent = createTextArea(3, 40);
086
087    JLabel removeFromContainerLabel = new JLabel("Remove");
088    JTextArea removeFromContainerComponent = createTextArea(2, 40);
089
090    JLabel previewLabel = new JLabel("Preview");
091    JScrollPane previewComponent = new JScrollPane();
092
093    JButton prevButton = new JButton("Preview");
094    JButton okButton = new JButton("OK");
095    JButton cancelButton = new JButton("Cancel");
096    Component buttonBar = ButtonBarFactory.buildRightAlignedBar(
097            new JButton[] { prevButton, okButton, cancelButton });
098
099    ComponentDef componentDef;
100    private String preview;
101    Window myOwner;
102    private boolean success = false;
103
104    public boolean succeeded() {
105        return success;
106    }
107
108    /** Creates a new instance of NewComponentDialog */
109    public NewComponentDialog(Window owner) {
110        myOwner = owner;
111        LayoutConstraintsManager layoutConstraintsManager = LayoutConstraintsManager
112                .getLayoutConstraintsManager(this.getClass()
113                        .getResourceAsStream("editableLayoutConstraints.xml"));
114
115        this.setBorder(Borders.DIALOG_BORDER);
116
117        layoutConstraintsManager.setLayout("newComponentPanel", this);
118
119        // here we add the controls to the container. you may
120        // need to change the name of panel
121        add(new JScrollPane(removeFromContainerComponent),
122                "removeFromContainerComponent");
123
124        add(configureLabel, "configureLabel");
125        add(new JScrollPane(importsComponent), "importsComponent");
126        add(new JScrollPane(declarationsComponent), "declarationsComponent");
127        add(new JScrollPane(configureComponent), "configureComponent");
128        add(new JScrollPane(addToContainerComponent),
129                "addToContainerComponent");
130        add(buttonBar, "buttonBar");
131        add(declarationsLabel, "declarationsLabel");
132        add(componentNameLabel, "componentNameLabel");
133        add(importsLabel, "importsLabel");
134        add(addToContainerLabel, "addToContainerLabel");
135        add(componentNameTextField, "componentNameTextField");
136        add(removeFromContainerLabel, "removeFromContainerLabel");
137
138        add(previewLabel, "previewLabel");
139        add(previewComponent, "previewComponent");
140
141        prevButton.addActionListener(new ActionListener() {
142            @Override
143            public void actionPerformed(ActionEvent e) {
144                doPreview();
145            }
146        });
147        okButton.addActionListener(new ActionListener() {
148            @Override
149            public void actionPerformed(ActionEvent e) {
150                success = true;
151                componentDef.add = getAdd();
152                componentDef.configure = getConfiguration();
153                componentDef.declarations = getDeclarations();
154                componentDef.name = componentNameTextField.getText().trim();
155                componentDef.imports = getImports();
156                UserPrefs.getPrefs().saveWinLoc("newcomp", myOwner);
157                myOwner.setVisible(false);
158            }
159        });
160        cancelButton.addActionListener(new ActionListener() {
161            @Override
162            public void actionPerformed(ActionEvent e) {
163                success = false;
164                UserPrefs.getPrefs().saveWinLoc("newcomp", myOwner);
165                myOwner.setVisible(false);
166            }
167        });
168    }
169
170    // class PreviewThread implements Runnable {
171    //
172    // boolean running = true;
173    //
174    // public void run() {
175    // while (running) {
176    // }
177    // }
178    // }
179
180    /**
181    Get an instance of the specified component. In FormLayoutMaker,
182    this instance is placed in the preview panel.
183    <p>
184    For example, if the component is a JButton, this method is the
185    equivalent of <code>new JButton(<i>text</i>)</code>.
186
187    @return Component An instance of the component, null if there
188    is a problem in the specification. [It is recommended that the
189    'Preview' button should be clicked before exiting the dialog.]
190     */
191    public Component getInstance() {
192        Component component = null;
193        String script;
194        if (preview == null || preview.length() == 0) {
195            script = getImports() + "\n" + getDeclarations() + "\n"
196                    + getConfiguration();
197        } else {
198            script = preview.trim();
199        }
200        String componentName = componentNameTextField.getText();
201        script = script.replaceAll("\\$\\{name\\}", componentName);
202
203        Interpreter interpreter = new Interpreter();
204        interpreter.setStrictJava(true);
205
206        JPanel temporaryContainer = null;
207        try {
208
209            interpreter.set("container", temporaryContainer);
210            interpreter.eval(script);
211            component = (Component) interpreter.get(componentName);
212
213        } catch (bsh.EvalError error) {
214            System.out.println(error);
215        }
216        return component;
217    }
218
219    private void doPreview() {
220        Component component = getInstance();
221        if (component == null) {
222            return;
223        }
224        JPanel temporaryContainer = new JPanel();
225        temporaryContainer.add(component);
226        if (temporaryContainer != null) {
227            previewComponent.setViewportView(temporaryContainer);
228        }
229    }
230
231    /** Get the component's name */
232    public String getComponentName() {
233        return componentNameTextField.getText();
234    }
235
236    public void setComponentName(String componentName) {
237        componentNameTextField.setText(componentName);
238    }
239
240    /** Get the component's &lt;imports&gt; section. */
241    public String getImports() {
242        return importsComponent.getText().trim();
243    }
244
245    /** Get the component's &lt;declarations&gt; section. */
246    public String getDeclarations() {
247        return declarationsComponent.getText().trim();
248    }
249
250    /** Get the component's &lt;configuration&gt; section. */
251    public String getConfiguration() {
252        return configureComponent.getText().trim();
253    }
254
255    public String getAdd() {
256        String res = addToContainerComponent.getText();
257        return cleanString(res);
258    }
259
260    public void setRemove(String remove) {
261        removeFromContainerComponent.setText(remove);
262    }
263
264    public void setComponentDef(ComponentDef componentDef) {
265        editComponentDef(componentDef.clone());
266    }
267
268    public void editComponentDef(ComponentDef componentDef) {
269        this.componentDef = componentDef;
270
271        importsComponent.setText(cleanString(componentDef.imports));
272        declarationsComponent.setText(cleanString(componentDef.declarations));
273        configureComponent.setText(cleanString(componentDef.configure));
274        addToContainerComponent.setText(cleanString(componentDef.add));
275        removeFromContainerComponent.setText(cleanString(componentDef.remove));
276        preview = cleanString(componentDef.preview);
277    }
278
279    /** Cleans a string. Removes extra newlines.
280     *
281     * @param instr
282     * @return A clean string.
283     */
284    private String cleanString(String instr) {
285        if (instr == null) {
286            return instr;
287        }
288
289        // Java 1.5 library method
290        //    while ( res.contains("\n\n") )
291        // KBR 09/05/05 Reworked to deal with leading space for multiline
292        // sections.
293        String[] outstrs = instr.split("\n");
294        String outstr = "";
295        for (String outstr2 : outstrs) {
296            String tmp = outstr2.trim();
297            outstr += tmp + (tmp.length() > 0 ? "\n" : "");
298        }
299        return outstr;
300    }
301
302    private static JTextArea createTextArea(int rows, int cols) {
303        JTextArea textArea = new JTextArea(rows, cols);
304        //KBR linewrap makes longer sections hard to work with
305        //    textArea.setWrapStyleWord(true);
306        //    textArea.setLineWrap(true);
307        return textArea;
308    }
309
310    /**
311     * Creates and displays a dialog for editing a component's settings. See
312     * {@link #doDialog(JFrame,ComponentDef)} for an example.
313     */
314    public static NewComponentDialog editDialog(JFrame owner,
315            ComponentDef componentDef) {
316        JDialog dlg = new JDialog(owner, "Edit Component", true);
317        UserPrefs.getPrefs().useSavedBounds("newcomp", dlg);
318        dlg.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
319        NewComponentDialog panel = new NewComponentDialog(dlg);
320        panel.editComponentDef(componentDef);
321        panel.setComponentName(componentDef.name);
322        dlg.getContentPane().add(panel);
323        dlg.pack();
324        dlg.setVisible(true);
325        return panel;
326    }
327
328    /**
329     * Creates and displays a dialog for defining a new component's settings. The
330     * dialog should be used as follows:
331     * <code>
332     * NewComponentDialog dlg = NewComponentDialog.doDialog(frame, componentDef);
333     * if (dlg.succeeded()) {
334     *    [do something with dlg.componentDef]
335     * }
336     * </code>
337     */
338    public static NewComponentDialog doDialog(JFrame owner,
339            ComponentDef componentDef) {
340        JDialog dlg = new JDialog(owner, "New Component", true);
341        UserPrefs.getPrefs().useSavedBounds("newcomp", dlg);
342        dlg.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
343        NewComponentDialog panel = new NewComponentDialog(dlg);
344        panel.setComponentDef(componentDef);
345        panel.setComponentName("untitled");
346        dlg.getContentPane().add(panel);
347        dlg.pack();
348        dlg.setVisible(true);
349        return panel;
350    }
351
352    /** Unit testing.
353     */
354    public static void main(String[] args) {
355        JFrame frame = new JFrame();
356        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
357        NewComponentDialog dialog = new NewComponentDialog(frame);
358
359        ComponentDef componentDef = new ComponentDef();
360        componentDef.imports = "import javax.swing.JLabel;";
361        componentDef.declarations = "JLabel ${name} = new JLabel(\"Hello World\");";
362        componentDef.configure = "";
363        componentDef.add = "${container}.add(${name}, \"${name}\");";
364        componentDef.remove = "${container}.remove($name);";
365        dialog.setComponentName("untitled");
366        dialog.setComponentDef(componentDef);
367
368        frame.getContentPane().add(dialog);
369        frame.pack();
370        frame.setVisible(true);
371    }
372
373}