001/*
002 * Copyright (c) 2004-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: welker $'
006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 
007 * '$Revision: 24234 $'
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.BorderLayout;
033import java.awt.Color;
034import java.awt.Dimension;
035import java.awt.event.ActionEvent;
036import java.awt.event.ActionListener;
037import java.net.URL;
038import java.util.Iterator;
039import java.util.List;
040
041import javax.swing.BorderFactory;
042import javax.swing.Box;
043import javax.swing.BoxLayout;
044import javax.swing.JButton;
045import javax.swing.JDialog;
046import javax.swing.JPanel;
047import javax.swing.JTabbedPane;
048import javax.swing.border.Border;
049
050import org.apache.commons.logging.Log;
051import org.apache.commons.logging.LogFactory;
052import org.kepler.util.StaticResources;
053
054import ptolemy.actor.gui.Configuration;
055import ptolemy.actor.gui.HTMLViewer;
056import ptolemy.actor.gui.TableauFrame;
057import ptolemy.data.expr.FileParameter;
058import ptolemy.util.CancelException;
059import ptolemy.util.MessageHandler;
060
061/**
062 * Base class for Actor, Director and Workflow dialogs, containing a JTabbedPane
063 * to which any number of tabs may be added at runtime. Also has OK, Apply,
064 * Cancel and Help buttons at the bottom of the dialog
065 * 
066 * @author Matthew Brooke
067 * @since 27 February 2006
068 */
069public abstract class TabbedDialog extends JDialog {
070
071        // ////////////////////////////////////////////////////////////////////////////
072        // LOCALIZABLE RESOURCES - NOTE that these default values are later
073        // overridden by values from the uiDisplayText resourcebundle file
074        // ////////////////////////////////////////////////////////////////////////////
075        private static final String OK_BUTTON_TEXT = StaticResources
076                        .getDisplayString("general.OK", "OK");
077        private static String APPLY_BUTTON_TEXT = StaticResources.getDisplayString(
078                        "general.APPLY", "Apply");
079        private static String CANCEL_BUTTON_TEXT = StaticResources
080                        .getDisplayString("general.CANCEL", "Cancel");
081        private static String HELP_BUTTON_TEXT = StaticResources.getDisplayString(
082                        "general.HELP", "Help");
083        // ////////////////////////////////////////////////////////////////////////////
084
085        private static Dimension BUTTON_DIMS = StaticGUIResources.getDimension(
086                        "dialogs.buttons.width", "dialogs.buttons.height", 70, 20);
087
088        private static final boolean IS_RESIZABLE = true;
089
090        private static final Dimension TAB_PANEL_DIMS = StaticGUIResources
091                        .getDimension("dialogs.tabPanels.width",
092                                        "dialogs.tabPanels.height", 630, 200);
093
094        // Note that only the second number (the y value) is observed.
095        // X-axis is stretched to fit the container
096        private static final Dimension BUTTON_PANEL_DIMS = StaticGUIResources
097                        .getDimension("dialogs.buttonPanel.width",
098                                        "dialogs.buttonPanel.height", 40, 40);
099
100        // blank margin width (pixels) between jTabbedPane and dialog container at
101        // sides & top. Bottom is set to 0, because button panel sets space there
102        private static final int JTABPANE_MARGINS = StaticResources.getSize(
103                        "dialogs.tabPanels.margins", 15);
104
105        private JPanel mainPanel = new JPanel();
106        private JPanel buttonPanel = new JPanel();
107        private JTabbedPane tabPane = new JTabbedPane();
108        private JButton okButton = new JButton();
109        private JButton applyButton = new JButton();
110        private JButton cancelButton = new JButton();
111        private JButton helpButton = new JButton();
112
113        private static Log log = LogFactory.getLog(
114                        TabbedDialog.class.getName());
115        private static boolean isDebugging = log.isDebugEnabled();
116
117        /**
118         * Construct an instance of this TabbedDialog, with the specified owner
119         * Frame and title string. Optionally make dialog modal.
120         * 
121         * @param frame
122         *            Frame
123         * @param title
124         *            String
125         * @param modal
126         *            boolean
127         */
128        public TabbedDialog(TableauFrame frame, String title, boolean modal) {
129
130                super(frame, title, modal);
131                this._frame = frame;
132                setDefaultCloseOperation(DISPOSE_ON_CLOSE);
133
134                init();
135        }
136
137        /**
138         * Add a new tab to the TabbedDialog, which will act as a container for the
139         * passed JPanel, and which will have the title text specified in tabText.
140         * Tab will be added at the next available position
141         * 
142         * @param panel
143         *            JPanel
144         * @param tabText
145         *            String
146         */
147        protected void _addTab(JPanel panel, String tabText) {
148
149                WidgetFactory.setPrefMinSizes(panel, TAB_PANEL_DIMS);
150                tabPane.add(panel, tabText);
151        }
152
153        /**
154         * bring the requested tab to the front in the TabbedDialog.
155         * 
156         * @param dialogTab
157         *            AbstractDialogTab
158         */
159        protected void _selectTab(AbstractDialogTab dialogTab) {
160                tabPane.setSelectedComponent(dialogTab);
161        }
162
163        /**
164         * method that is called when user clicks the "OK" button. May be overridden
165         * by derived classes
166         * 
167         */
168        protected void _okAction() {
169
170                boolean savedOK = _applyAction();
171                if (savedOK) {
172                        closeNoSave();
173                } else {
174                        if (isDebugging) {
175                                log
176                                                .info("Validation problem with user input; call to _applyAction() "
177                                                                + "in implementing class returned FALSE");
178                        }
179                        // implementing _applyAction class should check for _applyAction
180                        // returning false, and if so, should display instructions to the
181                        // user
182                }
183        }
184
185        /**
186         * method that is called when user clicks the "Apply" button. May be
187         * overridden by derived classes. this method contains a call to
188         * _validateInput() on ach of the contained AbstractDialogTabs; if
189         * _validateInput() returns false for any of these tab-panels, then this
190         * method, in turn, will return false
191         * 
192         * @return boolean true if apply worked, false if not (eg if there were
193         *         validation errors etc.)
194         */
195        protected boolean _applyAction() {
196
197                boolean allValid = true;
198                AbstractDialogTab nextTab = null;
199                int totTabs = tabPane.getTabCount();
200
201                // first, call validateInput() on all tabs to
202                // make sure we can proceed with save
203                for (int i = 0; i < totTabs; i++) {
204                        nextTab = (AbstractDialogTab) tabPane.getComponent(i);
205                        if (nextTab == null) {
206                                continue;
207                        }
208                        if (!nextTab.validateInput()) {
209                                _selectTab(nextTab);
210                                allValid = false;
211                        }
212                }
213
214                // if everything is valid, call save() on all
215                // tabs; if not, show an error message
216                if (allValid) {
217                        for (int i = 0; i < totTabs; i++) {
218                                nextTab = (AbstractDialogTab) tabPane.getComponent(i);
219                                if (nextTab == null) {
220                                        continue;
221                                }
222                                nextTab.save();
223                        }
224                        return true;
225                } else {
226                        /** @todo - FIXME - need a dialog to tell user somethign's not valid */
227                        return false;
228                }
229        }
230
231        /**
232         * method that is called when user clicks the "Cancel" button. May be
233         * overridden by derived classes
234         */
235        protected void _cancelAction() {
236                closeNoSave();
237        }
238
239        /**
240         * method that is called when user clicks the "Help" button. Shows top-level
241         * help pages, but may be overridden by derived classes to provide
242         * context-sensitive help
243         */
244        protected void _helpAction() {
245
246                try {
247                        URL doc = null;
248                        FileParameter helpAttribute = null;
249                        Configuration config = getConfiguration();
250
251                        if (config != null) {
252                                helpAttribute = (FileParameter) config.getAttribute("_help",
253                                                FileParameter.class);
254                        }
255
256                        if (helpAttribute != null) {
257                                doc = helpAttribute.asURL();
258                                config.openModel(null, doc, doc.toExternalForm());
259                        } else {
260                                HTMLViewer viewer = new HTMLViewer();
261                                doc = getClass().getClassLoader().getResource(
262                                                StaticResources.getSettingsString(
263                                                                "dialogs.defaultHelpURL",
264                                                                "ptolemy/configs/doc/basicHelp.htm"));
265                                viewer.setPage(doc);
266                                viewer.pack();
267                                viewer.setVisible(true);
268                        }
269
270                } catch (Exception ex) {
271
272                        try {
273                                MessageHandler.warning(StaticResources.getDisplayString(
274                                                "general.errors.noHelpAvailable",
275                                                "Cannot open help page"), ex);
276                        } catch (CancelException exception) {
277                                // Ignore the cancel.
278                        }
279                }
280        }
281
282        // ////////////////////////////////////////////////////////////////////////////
283        // protected variables //
284        // ////////////////////////////////////////////////////////////////////////////
285
286        /**
287         * Dimensions of the JLabels to left of text input fields:
288         */
289        protected static final Dimension jLabelDims = StaticGUIResources.getDimension(
290                        "dialogs.labels.width", "dialogs.labels.height", 10, 10);
291
292        /**
293         * dims of dialog text input fields
294         */
295        protected static final Dimension textFieldDims = StaticGUIResources
296                        .getDimension("dialogs.textfields.width",
297                                        "dialogs.textfields.height", 10, 10);
298
299        /**
300         * dims of "ID" label on dialog "general" tab
301         */
302        protected static final Dimension idLabelDims = StaticGUIResources
303                        .getDimension("dialogs.labels.id.width",
304                                        "dialogs.labels.id.height", 10, 10);
305
306        /**
307         * dims of "ID" value on dialog "general" tab
308         */
309        protected static final Dimension idValueDims = StaticGUIResources
310                        .getDimension("dialogs.labels.idvalue.width",
311                                        "dialogs.labels.idvalue.height", 10, 10);
312
313        /**
314         * dims of paddign inside tab panes
315         */
316        protected static final Border tabPanePadding = BorderFactory
317                        .createEmptyBorder(
318                                        // top, left, bottom, right
319                                        StaticResources.getSize("dialogs.tabPanels.padding.top", 0),
320                                        StaticResources
321                                                        .getSize("dialogs.tabPanels.padding.left", 0),
322                                        StaticResources.getSize("dialogs.tabPanels.padding.bottom",
323                                                        0), StaticResources.getSize(
324                                                        "dialogs.tabPanels.padding.right", 0));
325
326        protected TableauFrame _frame;
327
328        // ////////////////////////////////////////////////////////////////////////////
329        // private methods //
330        // ////////////////////////////////////////////////////////////////////////////
331
332        private void init() {
333
334                this.setResizable(IS_RESIZABLE);
335                mainPanel.setLayout(new BorderLayout());
336
337                getContentPane().add(mainPanel);
338
339                tabPane.setBorder(BorderFactory.createEmptyBorder(JTABPANE_MARGINS,
340                                JTABPANE_MARGINS, 0, JTABPANE_MARGINS));
341                mainPanel.add(tabPane, java.awt.BorderLayout.CENTER);
342                buttonPanel.setBorder(BorderFactory.createEmptyBorder(0,
343                                JTABPANE_MARGINS, 0, JTABPANE_MARGINS));
344                WidgetFactory.setPrefMinSizes(buttonPanel, BUTTON_PANEL_DIMS);
345                buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.X_AXIS));
346                mainPanel.add(buttonPanel, java.awt.BorderLayout.SOUTH);
347
348                Color bgColor = StaticGUIResources.getColor("dialogs.mainPanel.bgcolor.r",
349                                "dialogs.mainPanel.bgcolor.g", "dialogs.mainPanel.bgcolor.b");
350                if (bgColor != null) {
351                        mainPanel.setOpaque(true);
352                        mainPanel.setBackground(bgColor);
353                        buttonPanel.setOpaque(true);
354                        buttonPanel.setBackground(bgColor);
355                }
356
357                initButtons();
358                buttonPanel.add(Box.createHorizontalGlue());
359                buttonPanel.add(okButton);
360                buttonPanel.add(WidgetFactory.getDefaultSpacer());
361                buttonPanel.add(applyButton);
362                buttonPanel.add(WidgetFactory.getDefaultSpacer());
363                buttonPanel.add(cancelButton);
364                buttonPanel.add(WidgetFactory.getDefaultSpacer());
365                buttonPanel.add(helpButton);
366
367        }
368
369        protected static Configuration getConfiguration() {
370
371                List configsList = Configuration.configurations();
372                Configuration config = null;
373                for (Iterator it = configsList.iterator(); it.hasNext();) {
374                        config = (Configuration) it.next();
375                        if (config != null)
376                                break;
377                }
378                return config;
379        }
380
381        private void initButtons() {
382
383                // text
384                okButton.setText(OK_BUTTON_TEXT);
385                applyButton.setText(APPLY_BUTTON_TEXT);
386                cancelButton.setText(CANCEL_BUTTON_TEXT);
387                helpButton.setText(HELP_BUTTON_TEXT);
388
389                // Dims
390                WidgetFactory.setPrefMinSizes(okButton, BUTTON_DIMS);
391                WidgetFactory.setPrefMinSizes(applyButton, BUTTON_DIMS);
392                WidgetFactory.setPrefMinSizes(cancelButton, BUTTON_DIMS);
393                WidgetFactory.setPrefMinSizes(helpButton, BUTTON_DIMS);
394
395                // actionListeners
396                okButton.addActionListener(new ActionListener() {
397                        public void actionPerformed(ActionEvent e) {
398                                _okAction();
399                        }
400                });
401                applyButton.addActionListener(new ActionListener() {
402                        public void actionPerformed(ActionEvent e) {
403                                _applyAction();
404                        }
405                });
406                cancelButton.addActionListener(new ActionListener() {
407                        public void actionPerformed(ActionEvent e) {
408                                _cancelAction();
409                        }
410                });
411                helpButton.addActionListener(new ActionListener() {
412                        public void actionPerformed(ActionEvent e) {
413                                _helpAction();
414                        }
415                });
416        }
417
418        private void closeNoSave() {
419                TabbedDialog.this.setVisible(false);
420                TabbedDialog.this.dispose();
421        }
422}