001: /*
002: * $Id: JXDialog.java,v 1.9 2007/01/23 13:15:22 kleopatra 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.Dialog;
024: import java.awt.Dimension;
025: import java.awt.Frame;
026: import java.awt.event.KeyEvent;
027:
028: import javax.swing.Action;
029: import javax.swing.BorderFactory;
030: import javax.swing.Box;
031: import javax.swing.BoxLayout;
032: import javax.swing.InputMap;
033: import javax.swing.JButton;
034: import javax.swing.JComponent;
035: import javax.swing.JDialog;
036: import javax.swing.JPanel;
037: import javax.swing.KeyStroke;
038: import javax.swing.UIManager;
039: import javax.swing.plaf.basic.BasicOptionPaneUI;
040:
041: import org.jdesktop.swingx.action.BoundAction;
042: import org.jdesktop.swingx.plaf.LookAndFeelAddons;
043:
044: /**
045: * First cut for enhanced Dialog. The idea is to have a pluggable content
046: * from which the dialog auto-configures all its "dialogueness".
047: *
048: * <ul>
049: * <li> accepts a content and configures itself from content's properties -
050: * replaces the execute action from the appropriate action in content's action map (if any)
051: * and set's its title from the content's name.
052: * <li> registers stand-in actions for close/execute with the dialog's RootPane
053: * <li> registers keyStrokes for esc/enter to trigger the close/execute actions
054: * <li> takes care of building the button panel using the close/execute actions.
055: * </ul>
056: *
057: * <ul>
058: * <li>TODO: add link to forum discussion, wiki summary?
059: * <li>PENDING: add support for vetoing the close.
060: * <li>PENDING: add complete set of constructors
061: * <li>PENDING: add windowListener to delegate to close action
062: * </ul>
063: *
064: * @author Jeanette Winzenburg
065: */
066: public class JXDialog extends JDialog {
067:
068: static {
069: // Hack to enforce loading of SwingX framework ResourceBundle
070: LookAndFeelAddons.getAddon();
071: }
072:
073: public static final String EXECUTE_ACTION_COMMAND = "execute";
074: public static final String CLOSE_ACTION_COMMAND = "close";
075: public static final String UIPREFIX = "XDialog.";
076:
077: protected JComponent content;
078:
079: /**
080: * Creates a non-modal dialog with the given component as
081: * content and without specified owner. A shared, hidden frame will be
082: * set as the owner of the dialog.
083: * <p>
084: * @param content the component to show and to auto-configure from.
085: */
086: public JXDialog(JComponent content) {
087: super ();
088: setContent(content);
089: }
090:
091: /**
092: * Creates a non-modal dialog with the given component as content and the
093: * specified <code>Frame</code> as owner.
094: * <p>
095: * @param frame the owner
096: * @param content the component to show and to auto-configure from.
097: */
098: public JXDialog(Frame frame, JComponent content) {
099: super (frame);
100: setContent(content);
101: }
102:
103: /**
104: * Creates a non-modal dialog with the given component as content and the
105: * specified <code>Dialog</code> as owner.
106: * <p>
107: * @param dialog the owner
108: * @param content the component to show and to auto-configure from.
109: */
110: public JXDialog(Dialog dialog, JComponent content) {
111: super (dialog);
112: setContent(content);
113: }
114:
115: /**
116: * PENDING: widen access - this could be public to make the content really
117: * pluggable?
118: *
119: * @param content
120: */
121: private void setContent(JComponent content) {
122: if (this .content != null) {
123: throw new IllegalStateException(
124: "content must not be set more than once");
125: }
126: initActions();
127: Action contentCloseAction = content.getActionMap().get(
128: CLOSE_ACTION_COMMAND);
129: if (contentCloseAction != null) {
130: putAction(CLOSE_ACTION_COMMAND, contentCloseAction);
131: }
132: Action contentExecuteAction = content.getActionMap().get(
133: EXECUTE_ACTION_COMMAND);
134: if (contentExecuteAction != null) {
135: putAction(EXECUTE_ACTION_COMMAND, contentExecuteAction);
136: }
137: this .content = content;
138: build();
139: setTitle(content.getName());
140: }
141:
142: /**
143: * pre: content != null.
144: *
145: */
146: private void build() {
147: JComponent contentBox = new Box(BoxLayout.PAGE_AXIS);
148: contentBox.add(content);
149: JComponent buttonPanel = createButtonPanel();
150: contentBox.add(buttonPanel);
151: contentBox.setBorder(BorderFactory.createEmptyBorder(14, 14,
152: 14, 14));
153: // content.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
154:
155: // fieldPanel.setAlignmentX();
156: // buttonPanel.setAlignmentX(Component.RIGHT_ALIGNMENT);
157: add(contentBox);
158:
159: }
160:
161: /**
162: * {@inheritDoc}
163: *
164: * Overridden to check if content is available. <p>
165: * PENDING: doesn't make sense - the content is immutable and guaranteed
166: * to be not null.
167: */
168: public void setVisible(boolean visible) {
169: if (content == null)
170: throw new IllegalStateException(
171: "content must be built before showing the dialog");
172: super .setVisible(visible);
173: }
174:
175: /**
176: * The callback method executed when closing the dialog. <p>
177: * Here: calls dispose.
178: *
179: */
180: public void doClose() {
181: dispose();
182: }
183:
184: private void initActions() {
185: Action defaultAction = createCloseAction();
186: putAction(CLOSE_ACTION_COMMAND, defaultAction);
187: putAction(EXECUTE_ACTION_COMMAND, defaultAction);
188: }
189:
190: private Action createCloseAction() {
191: String actionName = getUIString(CLOSE_ACTION_COMMAND);
192: BoundAction action = new BoundAction(actionName,
193: CLOSE_ACTION_COMMAND);
194: action.registerCallback(this , "doClose");
195: return action;
196: }
197:
198: /**
199: * create the dialog button controls.
200: *
201: *
202: * @return panel containing button controls
203: */
204: protected JComponent createButtonPanel() {
205: // PENDING: this is a hack until we have a dedicated ButtonPanel!
206: JPanel panel = new JPanel(
207: new BasicOptionPaneUI.ButtonAreaLayout(true, 6)) {
208: public Dimension getMaximumSize() {
209: return getPreferredSize();
210: }
211: };
212:
213: panel.setBorder(BorderFactory.createEmptyBorder(9, 0, 0, 0));
214: Action findAction = getAction(EXECUTE_ACTION_COMMAND);
215: Action closeAction = getAction(CLOSE_ACTION_COMMAND);
216:
217: JButton findButton = new JButton(findAction);
218: panel.add(findButton);
219: if (findAction != closeAction) {
220: panel.add(new JButton(closeAction));
221: }
222:
223: KeyStroke enterKey = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,
224: 0, false);
225: KeyStroke escapeKey = KeyStroke.getKeyStroke(
226: KeyEvent.VK_ESCAPE, 0, false);
227:
228: InputMap inputMap = getRootPane().getInputMap(
229: JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
230: inputMap.put(enterKey, EXECUTE_ACTION_COMMAND);
231: inputMap.put(escapeKey, CLOSE_ACTION_COMMAND);
232:
233: getRootPane().setDefaultButton(findButton);
234: return panel;
235: }
236:
237: /**
238: * convenience wrapper to access rootPane's actionMap.
239: * @param key
240: * @param action
241: */
242: private void putAction(Object key, Action action) {
243: getRootPane().getActionMap().put(key, action);
244: }
245:
246: /**
247: * convenience wrapper to access rootPane's actionMap.
248: *
249: * @param key
250: * @return root pane's <code>ActionMap</code>
251: */
252: private Action getAction(Object key) {
253: return getRootPane().getActionMap().get(key);
254: }
255:
256: /**
257: * tries to find a String value from the UIManager, prefixing the
258: * given key with the UIPREFIX.
259: *
260: * TODO: move to utilities?
261: *
262: * @param key
263: * @return the String as returned by the UIManager or key if the returned
264: * value was null.
265: */
266: private String getUIString(String key) {
267: String text = UIManager.getString(UIPREFIX + key);
268: return text != null ? text : key;
269: }
270:
271: }
|