001/* 002 * $Id: JXDialog.java 4172 2012-04-17 14:54:00Z kschaefe $ 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 */ 021package org.jdesktop.swingx; 022 023import java.awt.Dialog; 024import java.awt.Dimension; 025import java.awt.Frame; 026import java.awt.Window; 027import java.util.Locale; 028 029import javax.swing.Action; 030import javax.swing.BorderFactory; 031import javax.swing.Box; 032import javax.swing.BoxLayout; 033import javax.swing.JButton; 034import javax.swing.JComponent; 035import javax.swing.JDialog; 036import javax.swing.JPanel; 037import javax.swing.JToolBar; 038import javax.swing.plaf.basic.BasicOptionPaneUI; 039 040import org.jdesktop.beans.JavaBean; 041import org.jdesktop.swingx.action.BoundAction; 042import org.jdesktop.swingx.plaf.LookAndFeelAddons; 043import org.jdesktop.swingx.plaf.UIManagerExt; 044 045/** 046 * First cut for enhanced Dialog. The idea is to have a pluggable content 047 * from which the dialog auto-configures all its "dialogueness". 048 * 049 * <ul> 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 * <li> registers stand-in actions for close/execute with the dialog's RootPane 054 * <li> registers keyStrokes for esc/enter to trigger the close/execute actions 055 * <li> takes care of building the button panel using the close/execute actions. 056 * </ul> 057 * 058 * <ul> 059 * <li>TODO: add link to forum discussion, wiki summary? 060 * <li>PENDING: add support for vetoing the close. 061 * <li>PENDING: add complete set of constructors 062 * <li>PENDING: add windowListener to delegate to close action 063 * </ul> 064 * 065 * @author Jeanette Winzenburg 066 * @author Karl Schaefer 067 */ 068@JavaBean 069public class JXDialog extends JDialog { 070 071 static { 072 // Hack to enforce loading of SwingX framework ResourceBundle 073 LookAndFeelAddons.getAddon(); 074 } 075 076 public static final String EXECUTE_ACTION_COMMAND = "execute"; 077 public static final String CLOSE_ACTION_COMMAND = "close"; 078 public static final String UIPREFIX = "XDialog."; 079 080 protected JComponent content; 081 082 /** 083 * Creates a non-modal dialog with the given component as 084 * content and without specified owner. A shared, hidden frame will be 085 * set as the owner of the dialog. 086 * <p> 087 * @param content the component to show and to auto-configure from. 088 */ 089 public JXDialog(JComponent content) { 090 super(); 091 setContent(content); 092 } 093 094 095 /** 096 * Creates a non-modal dialog with the given component as content and the 097 * specified <code>Frame</code> as owner. 098 * <p> 099 * @param frame the owner 100 * @param content the component to show and to auto-configure from. 101 */ 102 public JXDialog(Frame frame, JComponent content) { 103 super(frame); 104 setContent(content); 105 } 106 107 /** 108 * Creates a non-modal dialog with the given component as content and the 109 * specified <code>Dialog</code> as owner. 110 * <p> 111 * @param dialog the owner 112 * @param content the component to show and to auto-configure from. 113 */ 114 public JXDialog(Dialog dialog, JComponent content) { 115 super(dialog); 116 setContent(content); 117 } 118 119 /** 120 * Creates a non-modal dialog with the given component as content and the 121 * specified <code>Window</code> as owner. 122 * <p> 123 * @param window the owner 124 * @param content the component to show and to auto-configure from. 125 */ 126 public JXDialog(Window window, JComponent content) { 127 super(window); 128 setContentPane(content); 129 } 130 131 /** 132 * {@inheritDoc} 133 */ 134 @Override 135 protected JXRootPane createRootPane() { 136 return new JXRootPane(); 137 } 138 139 /** 140 * {@inheritDoc} 141 */ 142 @Override 143 public JXRootPane getRootPane() { 144 return (JXRootPane) super.getRootPane(); 145 } 146 147 /** 148 * Sets the status bar property on the underlying {@code JXRootPane}. 149 * 150 * @param statusBar 151 * the {@code JXStatusBar} which is to be the status bar 152 * @see #getStatusBar() 153 * @see JXRootPane#setStatusBar(JXStatusBar) 154 */ 155 public void setStatusBar(JXStatusBar statusBar) { 156 getRootPane().setStatusBar(statusBar); 157 } 158 159 /** 160 * Returns the value of the status bar property from the underlying 161 * {@code JXRootPane}. 162 * 163 * @return the {@code JXStatusBar} which is the current status bar 164 * @see #setStatusBar(JXStatusBar) 165 * @see JXRootPane#getStatusBar() 166 */ 167 public JXStatusBar getStatusBar() { 168 return getRootPane().getStatusBar(); 169 } 170 171 /** 172 * Sets the tool bar property on the underlying {@code JXRootPane}. 173 * 174 * @param toolBar 175 * the {@code JToolBar} which is to be the tool bar 176 * @see #getToolBar() 177 * @see JXRootPane#setToolBar(JToolBar) 178 */ 179 public void setToolBar(JToolBar toolBar) { 180 getRootPane().setToolBar(toolBar); 181 } 182 183 /** 184 * Returns the value of the tool bar property from the underlying 185 * {@code JXRootPane}. 186 * 187 * @return the {@code JToolBar} which is the current tool bar 188 * @see #setToolBar(JToolBar) 189 * @see JXRootPane#getToolBar() 190 */ 191 public JToolBar getToolBar() { 192 return getRootPane().getToolBar(); 193 } 194 195 /** 196 * PENDING: widen access - this could be public to make the content really 197 * pluggable? 198 * 199 * @param content 200 */ 201 private void setContent(JComponent content) { 202 if (this.content != null) { 203 throw new IllegalStateException("content must not be set more than once"); 204 } 205 initActions(); 206 Action contentCloseAction = content.getActionMap().get(CLOSE_ACTION_COMMAND); 207 if (contentCloseAction != null) { 208 putAction(CLOSE_ACTION_COMMAND, contentCloseAction); 209 } 210 Action contentExecuteAction = content.getActionMap().get(EXECUTE_ACTION_COMMAND); 211 if (contentExecuteAction != null) { 212 putAction(EXECUTE_ACTION_COMMAND, contentExecuteAction); 213 } 214 this.content = content; 215 build(); 216 setTitleFromContent(); 217 } 218 219 /** 220 * Infers and sets this dialog's title from the the content. 221 * Does nothing if content is null. 222 * 223 * Here: uses the content's name as title. 224 */ 225 protected void setTitleFromContent() { 226 if (content == null) return; 227 setTitle(content.getName()); 228 } 229 230 /** 231 * pre: content != null. 232 * 233 */ 234 private void build() { 235 JComponent contentBox = new Box(BoxLayout.PAGE_AXIS); 236 contentBox.add(content); 237 JComponent buttonPanel = createButtonPanel(); 238 contentBox.add(buttonPanel); 239 contentBox.setBorder(BorderFactory.createEmptyBorder(14, 14, 14, 14)); 240// content.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); 241 242// fieldPanel.setAlignmentX(); 243// buttonPanel.setAlignmentX(Component.RIGHT_ALIGNMENT); 244 add(contentBox); 245 246 } 247 248 /** 249 * {@inheritDoc} 250 * 251 * Overridden to check if content is available. <p> 252 * PENDING: doesn't make sense - the content is immutable and guaranteed 253 * to be not null. 254 */ 255 @Override 256 public void setVisible(boolean visible) { 257 if (content == null) throw 258 new IllegalStateException("content must be built before showing the dialog"); 259 super.setVisible(visible); 260 } 261 262//------------------------ dynamic locale support 263 264 265 /** 266 * {@inheritDoc} <p> 267 * 268 * Overridden to set the content's Locale and then updated 269 * this dialog's internal state. <p> 270 * 271 * 272 */ 273 @Override 274 public void setLocale(Locale l) { 275 /* 276 * NOTE: this is called from super's constructor as one of the 277 * first methods (prior to setting the rootPane!). So back out 278 * 279 */ 280 if (content != null) { 281 content.setLocale(l); 282 updateLocaleState(l); 283 } 284 super.setLocale(l); 285 } 286 287 /** 288 * Updates this dialog's locale-dependent state. 289 * 290 * Here: updates title and actions. 291 * <p> 292 * 293 * 294 * @see #setLocale(Locale) 295 */ 296 protected void updateLocaleState(Locale locale) { 297 setTitleFromContent(); 298 for (Object key : getRootPane().getActionMap().allKeys()) { 299 if (key instanceof String) { 300 Action contentAction = content.getActionMap().get(key); 301 Action rootPaneAction = getAction(key); 302 if ((!rootPaneAction.equals(contentAction))) { 303 String keyString = getUIString((String) key, locale); 304 if (!key.equals(keyString)) { 305 rootPaneAction.putValue(Action.NAME, keyString); 306 } 307 } 308 } 309 } 310 } 311 312 /** 313 * The callback method executed when closing the dialog. <p> 314 * Here: calls dispose. 315 * 316 */ 317 public void doClose() { 318 dispose(); 319 } 320 321 private void initActions() { 322 Action defaultAction = createCloseAction(); 323 putAction(CLOSE_ACTION_COMMAND, defaultAction); 324 putAction(EXECUTE_ACTION_COMMAND, defaultAction); 325 } 326 327 private Action createCloseAction() { 328 String actionName = getUIString(CLOSE_ACTION_COMMAND); 329 BoundAction action = new BoundAction(actionName, 330 CLOSE_ACTION_COMMAND); 331 action.registerCallback(this, "doClose"); 332 return action; 333 } 334 335 /** 336 * create the dialog button controls. 337 * 338 * 339 * @return panel containing button controls 340 */ 341 protected JComponent createButtonPanel() { 342 // PENDING: this is a hack until we have a dedicated ButtonPanel! 343 JPanel panel = new JPanel(new BasicOptionPaneUI.ButtonAreaLayout(true, 6)) 344 { 345 @Override 346 public Dimension getMaximumSize() { 347 return getPreferredSize(); 348 } 349 }; 350 351 panel.setBorder(BorderFactory.createEmptyBorder(9, 0, 0, 0)); 352 Action executeAction = getAction(EXECUTE_ACTION_COMMAND); 353 Action closeAction = getAction(CLOSE_ACTION_COMMAND); 354 355 JButton defaultButton = new JButton(executeAction); 356 panel.add(defaultButton); 357 getRootPane().setDefaultButton(defaultButton); 358 359 if (executeAction != closeAction) { 360 JButton b = new JButton(closeAction); 361 panel.add(b); 362 getRootPane().setCancelButton(b); 363 } 364 365 return panel; 366 } 367 368 /** 369 * convenience wrapper to access rootPane's actionMap. 370 * @param key 371 * @param action 372 */ 373 private void putAction(Object key, Action action) { 374 getRootPane().getActionMap().put(key, action); 375 } 376 377 /** 378 * convenience wrapper to access rootPane's actionMap. 379 * 380 * @param key 381 * @return root pane's <code>ActionMap</code> 382 */ 383 private Action getAction(Object key) { 384 return getRootPane().getActionMap().get(key); 385 } 386 387 /** 388 * Returns a potentially localized value from the UIManager. The given key 389 * is prefixed by this component|s <code>UIPREFIX</code> before doing the 390 * lookup. The lookup respects this table's current <code>locale</code> 391 * property. Returns the key, if no value is found. 392 * 393 * @param key the bare key to look up in the UIManager. 394 * @return the value mapped to UIPREFIX + key or key if no value is found. 395 */ 396 protected String getUIString(String key) { 397 return getUIString(key, getLocale()); 398 } 399 400 /** 401 * Returns a potentially localized value from the UIManager for the 402 * given locale. The given key 403 * is prefixed by this component's <code>UIPREFIX</code> before doing the 404 * lookup. Returns the key, if no value is found. 405 * 406 * @param key the bare key to look up in the UIManager. 407 * @param locale the locale use for lookup 408 * @return the value mapped to UIPREFIX + key in the given locale, 409 * or key if no value is found. 410 */ 411 protected String getUIString(String key, Locale locale) { 412 String text = UIManagerExt.getString(UIPREFIX + key, locale); 413 return text != null ? text : key; 414 } 415}