001    /*
002     * $Id: JXDialog.java,v 1.6 2006/05/14 08:12:15 dmouse Exp $
003     *
004     * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005     * Santa Clara, California 95054, U.S.A. All rights reserved.
006     *
007     * This library is free software; you can redistribute it and/or
008     * modify it under the terms of the GNU Lesser General Public
009     * License as published by the Free Software Foundation; either
010     * version 2.1 of the License, or (at your option) any later version.
011     * 
012     * This library is distributed in the hope that it will be useful,
013     * but WITHOUT ANY WARRANTY; without even the implied warranty of
014     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015     * Lesser General Public License for more details.
016     * 
017     * You should have received a copy of the GNU Lesser General Public
018     * License along with this library; if not, write to the Free Software
019     * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
020     */
021    package org.jdesktop.swingx;
022    
023    import java.awt.Dimension;
024    import java.awt.Frame;
025    import java.awt.event.KeyEvent;
026    
027    import javax.swing.Action;
028    import javax.swing.BorderFactory;
029    import javax.swing.Box;
030    import javax.swing.BoxLayout;
031    import javax.swing.InputMap;
032    import javax.swing.JButton;
033    import javax.swing.JComponent;
034    import javax.swing.JDialog;
035    import javax.swing.JPanel;
036    import javax.swing.KeyStroke;
037    import javax.swing.UIManager;
038    import javax.swing.plaf.basic.BasicOptionPaneUI;
039    
040    import org.jdesktop.swingx.action.BoundAction;
041    import org.jdesktop.swingx.plaf.LookAndFeelAddons;
042    
043    /**
044     * First cut for enhanced Dialog.
045     * 
046     * <ul>
047     * <li> registers stand-in actions for close/execute with the dialog's RootPane
048     * <li> registers keyStrokes for esc/enter to trigger the close/execute actions
049     * <li> takes care of building the button panel using the close/execute actions.
050     * <li> accepts a content and configures itself from content's properties - 
051     *  replaces the execute action from the appropriate action in content's action map (if any)
052     *  and set's its title from the content's name. 
053     * </ul> 
054     * 
055     * 
056     * PENDING: add support for vetoing the close.
057     * PENDING: add complete set of constructors
058     * PENDING: add windowListener to delegate to close action
059     * 
060     * @author Jeanette Winzenburg
061     */
062    public class JXDialog extends JDialog {
063    
064        static {
065            // Hack to enforce loading of SwingX framework ResourceBundle
066            LookAndFeelAddons.getAddon();
067        }
068        
069        public static final String EXECUTE_ACTION_COMMAND = "execute";
070        public static final String CLOSE_ACTION_COMMAND = "close";
071        public static final String UIPREFIX = "XDialog.";
072    
073        JComponent content;
074        
075        public JXDialog(Frame frame, JComponent content) {
076            super(frame);
077            setContent(content);
078        }
079        
080        private void setContent(JComponent content) {
081            if (this.content != null) {
082                throw new IllegalStateException("content must not be set more than once");
083            }
084            initActions();
085            Action contentCloseAction = content.getActionMap().get(CLOSE_ACTION_COMMAND);
086            if (contentCloseAction != null) {
087                putAction(CLOSE_ACTION_COMMAND, contentCloseAction);
088            }
089            Action contentExecuteAction = content.getActionMap().get(EXECUTE_ACTION_COMMAND);
090            if (contentExecuteAction != null) {
091                putAction(EXECUTE_ACTION_COMMAND, contentExecuteAction);
092            }
093            this.content = content;
094            build();
095            setTitle(content.getName());
096        }
097    
098        /**
099         * pre: content != null.
100         *
101         */
102        private void build() {
103            JComponent contentBox = new Box(BoxLayout.PAGE_AXIS); 
104            contentBox.add(content);
105            JComponent buttonPanel = createButtonPanel();
106            contentBox.add(buttonPanel);
107            contentBox.setBorder(BorderFactory.createEmptyBorder(14, 14, 14, 14));
108    //        content.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
109            
110    //        fieldPanel.setAlignmentX();
111    //      buttonPanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
112            add(contentBox);
113            
114        }
115    
116    //    /**
117    //     * 
118    //     */
119    //    private void locate() {
120    //        GraphicsConfiguration gc =
121    //            GraphicsEnvironment.getLocalGraphicsEnvironment().
122    //            getDefaultScreenDevice().getDefaultConfiguration();
123    //        Rectangle bounds = gc.getBounds();
124    //        int x = bounds.x+bounds.width/3;
125    //        int y = bounds.y+bounds.height/3;
126    //
127    //        setLocation(x, y);
128    //    }
129    
130        public void setVisible(boolean visible) {
131            if (content == null) throw 
132                new IllegalStateException("content must be built before showing the dialog");
133            super.setVisible(visible);
134        }
135    
136        public void doClose() {
137            dispose();
138        }
139        
140        private void initActions() {
141            // PENDING: factor a common dialog containing the following
142            Action defaultAction = createCloseAction();
143            putAction(CLOSE_ACTION_COMMAND, defaultAction);
144            putAction(EXECUTE_ACTION_COMMAND, defaultAction);
145        }
146    
147        private Action createCloseAction() {
148            String actionName = getUIString(CLOSE_ACTION_COMMAND);
149            BoundAction action = new BoundAction(actionName,
150                    CLOSE_ACTION_COMMAND);
151            action.registerCallback(this, "doClose");
152            return action;
153        }
154    
155        /**
156         * create the dialog button controls.
157         * 
158         * 
159         * @return panel containing button controls
160         */
161        protected JComponent createButtonPanel() {
162            // PENDING: this is a hack until we have a dedicated ButtonPanel!
163            JPanel panel = new JPanel(new BasicOptionPaneUI.ButtonAreaLayout(true, 6))
164            {
165                public Dimension getMaximumSize() {
166                    return getPreferredSize();
167                }
168            };
169    
170            panel.setBorder(BorderFactory.createEmptyBorder(9, 0, 0, 0));
171            Action findAction = getAction(EXECUTE_ACTION_COMMAND);
172            Action closeAction = getAction(CLOSE_ACTION_COMMAND);
173    
174            JButton findButton;
175            panel.add(findButton = new JButton(findAction));
176            panel.add(new JButton(closeAction));
177    
178    
179            KeyStroke enterKey = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0, false);
180            KeyStroke escapeKey = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, false);
181    
182            InputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
183            inputMap.put(enterKey, EXECUTE_ACTION_COMMAND);
184            inputMap.put(escapeKey, CLOSE_ACTION_COMMAND);
185    
186            getRootPane().setDefaultButton(findButton);
187            return panel;
188        }
189    
190        /**
191         * convenience wrapper to access rootPane's actionMap.
192         * @param key
193         * @param action
194         */
195        private void putAction(Object key, Action action) {
196            getRootPane().getActionMap().put(key, action);
197        }
198        
199        /**
200         * convenience wrapper to access rootPane's actionMap.
201         * 
202         * @param key
203         * @return root pane's <code>ActionMap</code>
204         */
205        private Action getAction(Object key) {
206            return getRootPane().getActionMap().get(key);
207        }
208        /**
209         * tries to find a String value from the UIManager, prefixing the
210         * given key with the UIPREFIX. 
211         * 
212         * TODO: move to utilities?
213         * 
214         * @param key 
215         * @return the String as returned by the UIManager or key if the returned
216         *   value was null.
217         */
218        private String getUIString(String key) {
219            String text = UIManager.getString(UIPREFIX + key);
220            return text != null ? text : key;
221        }
222    
223    
224    }