1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.xnap.gui.component;
21
22 import java.awt.*;
23 import java.awt.Component;
24 import java.awt.FlowLayout;
25 import java.awt.event.*;
26 import java.awt.event.WindowAdapter;
27
28 import javax.help.BadIDException;
29 import javax.help.CSH;
30 import javax.help.HelpBroker;
31 import javax.help.HelpSet;
32 import javax.help.InvalidHelpSetContextException;
33 import javax.help.Map;
34 import javax.swing.AbstractAction;
35 import javax.swing.Action;
36 import javax.swing.BorderFactory;
37 import javax.swing.Box;
38 import javax.swing.JButton;
39 import javax.swing.JComponent;
40 import javax.swing.JDialog;
41 import javax.swing.JOptionPane;
42 import javax.swing.JPanel;
43 import javax.swing.JSeparator;
44 import javax.swing.border.EmptyBorder;
45
46 import org.apache.log4j.Logger;
47 import org.xnap.XNap;
48 import org.xnap.gui.action.*;
49 import org.xnap.gui.component.*;
50 import org.xnap.gui.util.GUIHelper;
51 import org.xnap.gui.util.HelpManager;
52 import org.xnap.util.SystemHelper;
53
54
55 /***
56 * This class provides a default implementation for a dialog. A dialog consists
57 * of two areas. The main area, located at the center of the dialog, contains
58 * the user interaction components. The button area, located at the south
59 * west of the dialog, is surrounded by an empty border and contains the
60 * buttons.
61 *
62 * <p>The most common buttons are provided with default actions.</p>
63 *
64 * <p>If you extend this class make sure <code>pack()</code> is called after
65 * all components have been added.</p>
66 */
67 public class DefaultDialog extends JDialog
68 {
69
70
71
72 /***
73 * The button type for no buttons.
74 */
75 public static int BUTTON_NONE = 0;
76
77 /***
78 * The button type for the okay button.
79 */
80 public static int BUTTON_OKAY = 1;
81
82 /***
83 * The button type for the apply button.
84 */
85 public static int BUTTON_APPLY = 2;
86
87 /***
88 * The button type for the cancel button.
89 */
90 public static int BUTTON_CANCEL = 4;
91
92 /***
93 * The button type for the close button.
94 */
95 public static int BUTTON_CLOSE = 8;
96
97 /***
98 * The button type for the help button.
99 */
100 public static int BUTTON_HELP = 16;
101
102 /***
103 * The button type for the context help button.
104 */
105 public static int BUTTON_CONTEXT_HELP = 32;
106
107
108
109 private static Logger logger = Logger.getLogger(DefaultDialog.class);
110
111 private ApplyAction acApply = new ApplyAction();
112 private CancelAction acCancel = new CancelAction();
113 private CloseAction acClose = new CloseAction();
114 private OkayAction acOkay = new OkayAction();
115 private HelpAction acHelp = new HelpAction();
116 private JPanel jpButtonsLeft;
117 private JPanel jpButtonsRight;
118 private JPanel jpMain;
119 private JPanel topPanel;
120
121 protected boolean isOkay = false;
122
123
124
125 /***
126 * Constructs a dialog. The escape key is by default bound to the cancel
127 * button.
128 *
129 * @param buttons a logical and combination of button type constants
130 * @param component the main component that is centered in the dialog
131 * @param exitOnEnter execute the okay action when the user presses enter
132 */
133 public DefaultDialog(int buttons, JComponent component,
134 boolean exitOnEnter)
135 {
136
137 topPanel = new JPanel(new BorderLayout());
138 topPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
139
140 jpMain = new JPanel();
141 topPanel.add(jpMain, BorderLayout.CENTER);
142 if (component != null) {
143 setMainComponent(component);
144 }
145
146 jpButtonsLeft = new JPanel(new FlowLayout(FlowLayout.LEFT));
147 if ((buttons & BUTTON_HELP) > 0) {
148 jpButtonsLeft.add(new JButton(acHelp));
149 }
150
151 if ((buttons & BUTTON_CONTEXT_HELP) > 0) {
152 jpButtonsLeft.add(new XNapButton(new ContextHelpAction()));
153 }
154
155 jpButtonsRight = new JPanel(new FlowLayout(FlowLayout.RIGHT));
156 if ((buttons & BUTTON_OKAY) > 0) {
157 JButton jbOkay = new JButton(acOkay);
158 if (exitOnEnter) {
159 GUIHelper.bindEnterKey(topPanel, jbOkay.getAction());
160 }
161 jpButtonsRight.add(jbOkay);
162 }
163 if ((buttons & BUTTON_CLOSE) > 0) {
164 jpButtonsRight.add(new JButton(acClose));
165 }
166 if ((buttons & BUTTON_APPLY) > 0) {
167 jpButtonsRight.add(new JButton(acApply));
168 }
169 if ((buttons & BUTTON_CANCEL) > 0) {
170 JButton jbCancel = new JButton(acCancel);
171 GUIHelper.bindEscapeKey(topPanel, jbCancel.getAction());
172 jpButtonsRight.add(jbCancel);
173 }
174
175
176
177
178 if (SystemHelper.isMacOSX) {
179 jpButtonsRight.add(Box.createHorizontalStrut(15));
180 }
181
182
183 JSeparator js = new JSeparator();
184
185 JPanel jpButtons = new JPanel(new BorderLayout());
186 jpButtons.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0));
187 jpButtons.add(js, BorderLayout.NORTH);
188 jpButtons.add(jpButtonsLeft, BorderLayout.WEST);
189 jpButtons.add(jpButtonsRight, BorderLayout.EAST);
190 topPanel.add(jpButtons, BorderLayout.SOUTH);
191
192 getContentPane().setLayout(new BorderLayout());
193 getContentPane().add(topPanel, BorderLayout.CENTER);
194
195 setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
196 addWindowListener(new CloseListener());
197
198 if (component != null) {
199 pack();
200 }
201 }
202
203 public DefaultDialog(int buttons, JComponent component)
204 {
205 this(buttons, component, true);
206 }
207
208 public DefaultDialog(int buttons, boolean exitOnEnter)
209 {
210 this(buttons, null, exitOnEnter);
211 }
212
213 public DefaultDialog(int buttons)
214 {
215 this(buttons, null);
216 }
217
218 /***
219 * Constructs a dialog with an okay and cancel button.
220 */
221 public DefaultDialog()
222 {
223 this(BUTTON_OKAY | BUTTON_CANCEL);
224 }
225
226
227
228 /***
229 * Called by ApplyAction and OkayAction when the dialog is closed.
230 *
231 * @return true, if apply was successful and close() should be
232 * invoked; false, otherwise
233 * @exception IllegalArgumentException
234 */
235 public boolean apply()
236 {
237 return true;
238 }
239
240 /***
241 * Disposes the dialog. Called by the actions to close the dialog. Sub
242 * classes can reimplement this.
243 *
244 * @see #isOkay()
245 */
246 public void close()
247 {
248 dispose();
249 }
250
251 /***
252 * Returns the button panel. Sub classes can add aditional buttons to this
253 * panel.
254 */
255 public JPanel getButtonPanel()
256 {
257 return jpButtonsRight;
258 }
259
260 /***
261 * Returns the cancel action.
262 */
263 public Action getCancelAction()
264 {
265 return acCancel;
266 }
267
268 /***
269 * Returns the cancel action.
270 */
271 public Action getOkayAction()
272 {
273 return acOkay;
274 }
275
276 /***
277 * Returns the close action.
278 */
279 public Action getCloseAction()
280 {
281 return acClose;
282 }
283
284 /***
285 * Sets the main component to <code>component</code>.
286 */
287 public void setMainComponent(Component component)
288 {
289 getMainPanel().setLayout(new BorderLayout());
290 getMainPanel().add(component, BorderLayout.CENTER);
291 }
292
293 /***
294 * Returns the main panel. Sub classes can add components to this panel.
295 */
296 public JPanel getMainPanel()
297 {
298 return jpMain;
299 }
300
301 /***
302 * Returns the top most panel.
303 */
304 public JPanel getTopPanel()
305 {
306 return topPanel;
307 }
308
309 /***
310 * Returns true, if the dialog was closed with the Okay button.
311 */
312 public boolean isOkay()
313 {
314 return isOkay;
315 }
316
317 /***
318 * Sets this dialogs position relative to <code>c</code> and makes it
319 * visible.
320 */
321 public void show(Component c)
322 {
323 if (!isVisible()) {
324 if (c != null) {
325 setLocationRelativeTo(c);
326 }
327 setVisible(true);
328 }
329 else {
330 toFront();
331 }
332 }
333
334 /***
335 * Handles the apply button. Invokes <code>apply()</code> when pressed.
336 *
337 * @see #apply()
338 */
339 private class ApplyAction extends AbstractAction {
340
341 public ApplyAction()
342 {
343 putValue(Action.NAME, XNap.tr("Apply"));
344 putValue(Action.SHORT_DESCRIPTION, XNap.tr("Applies changes."));
345 putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_A));
346 }
347
348 public void actionPerformed(ActionEvent event)
349 {
350 try {
351 apply();
352 }
353 catch (IllegalArgumentException e) {
354 JOptionPane.showMessageDialog
355 (DefaultDialog.this, e.getMessage(),
356 XNap.tr("Illegal Value"), JOptionPane.ERROR_MESSAGE);
357 }
358 }
359
360 }
361
362 /***
363 * Handles the Cancel button. Invokes <code>close()</code> when pressed.
364 *
365 * @see #close()
366 */
367 private class CancelAction extends AbstractAction {
368
369 public CancelAction()
370 {
371 putValue(Action.NAME, XNap.tr("Cancel"));
372 putValue(Action.SHORT_DESCRIPTION,
373 XNap.tr("Closes the dialog without saving changes."));
374 putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_C));
375 }
376
377 public void actionPerformed(ActionEvent event)
378 {
379 isOkay = false;
380 close();
381 }
382
383 }
384
385 /***
386 * Handle the Close button. Invokes <code>close()</code> when pressed.
387 *
388 * @see #close()
389 */
390 private class CloseAction extends AbstractAction {
391
392 public CloseAction()
393 {
394 putValue(Action.NAME, XNap.tr("Close"));
395 putValue(Action.SHORT_DESCRIPTION, XNap.tr("Closes the dialog."));
396 putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_S));
397 }
398
399 public void actionPerformed(ActionEvent event)
400 {
401 isOkay = false;
402 close();
403 }
404
405 }
406
407 /***
408 * Handles the Help button. Shows the help dialog registered for the
409 * <code>rootPane</code> of this dialog.
410 *
411 * It suffices to register a help id and a helpset for the dialog using
412 * {@link xnap.gui.util.HelpManager#enableHelpKeys(JComponent, String, HelpSet)} like this:
413 *
414 * <pre>
415 * HelpManager.enableHelpKeys(getRootPane(), "id-string", helpSet);
416 * </pre>
417 */
418 private class HelpAction extends AbstractAction
419 {
420
421 public HelpAction()
422 {
423 putValue(Action.NAME, XNap.tr("Help"));
424 putValue(Action.SHORT_DESCRIPTION,
425 XNap.tr("Shows the help dialog."));
426 putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_H));
427 if (HelpManager.getMainHelpSet() == null) {
428 this.setEnabled(false);
429 }
430 }
431
432 public void actionPerformed(ActionEvent event)
433 {
434 HelpBroker hb = HelpManager.getMainHelpBroker();
435 String id = CSH.getHelpIDString(getRootPane());
436 HelpSet hs = CSH.getHelpSet(getRootPane());
437
438 if (hs == null) {
439 hs = hb.getHelpSet();
440 }
441
442 try {
443 Map.ID mapId = Map.ID.create(id, hs);
444 if (mapId == null) {
445 mapId = hs.getHomeID();
446 }
447 hb.setCurrentID(mapId);
448 hb.setDisplayed(true);
449 }
450 catch (BadIDException be) {
451 logger.debug("bad id", be);
452 }
453 catch (InvalidHelpSetContextException e) {
454 logger.debug("helpset", e);
455 }
456 }
457
458 }
459
460 /***
461 * Handle the Okay button. Invokes <code>apply()</code> and
462 * <code>close()</code> when pressed.
463 *
464 * @see #apply()
465 * @see #close()
466 */
467 private class OkayAction extends AbstractAction {
468
469 public OkayAction()
470 {
471 putValue(Action.NAME, XNap.tr("OK"));
472 putValue(Action.SHORT_DESCRIPTION,
473 XNap.tr("Closes the dialog saving changes."));
474 putValue(Action.MNEMONIC_KEY, new Integer(KeyEvent.VK_O));
475 }
476
477 public void actionPerformed(ActionEvent event)
478 {
479 try {
480 if (!apply()) {
481 return;
482 }
483 }
484 catch (IllegalArgumentException e) {
485 JOptionPane.showMessageDialog
486 (DefaultDialog.this, e.getLocalizedMessage(),
487 XNap.tr("Illegal Value"), JOptionPane.ERROR_MESSAGE);
488 return;
489 }
490
491 isOkay = true;
492 close();
493 }
494
495 }
496
497 private class CloseListener extends WindowAdapter
498 {
499 public void windowClosing (java.awt.event.WindowEvent evt)
500 {
501 isOkay = false;
502 close();
503 }
504 }
505
506 }