001: /*
002: * @(#)ButtonPanel.java
003: *
004: * Copyright 2002 - 2003 JIDE Software. All rights reserved.
005: */
006: package com.jidesoft.dialog;
007:
008: import com.jidesoft.plaf.LookAndFeelFactory;
009: import com.jidesoft.plaf.UIDefaultsLookup;
010:
011: import javax.swing.*;
012: import java.awt.*;
013:
014: /**
015: * <code>ButtonPanel</code> can help to layout buttons easily
016: * in any dialogs.
017: * <p/>
018: * For more detail, please refer to JIDE Dialogs Developer Guide.
019: */
020: public class ButtonPanel extends JPanel implements ButtonListener,
021: ButtonNames {
022:
023: /**
024: * This option will make all buttons have the same size.
025: * If all buttons have the same size, the GUI will certainly look better.
026: */
027: public static final int SAME_SIZE = 0;
028:
029: /**
030: * This option will make all buttons no less than a certain size.
031: * The size is different on different platforms.
032: * We need this option because sometimes one button has a very long text.
033: * If all buttons have the same size, it will make the button panel extremely long.
034: * Even though they have the same size but will look out of balance.
035: * This option is not available if the buttons are arranged vertically.
036: */
037: public static final int NO_LESS_THAN = 1;
038:
039: /**
040: * Client property key. If this client property is set to Boolean.TRUE, the button panel
041: * will always use the component's preferred width instead of using minButtonWidth.
042: */
043: public final static String KEEP_PREFERRED_WIDTH = "keepPreferredWidth";
044:
045: /**
046: * The button will produce an affirmative action. Typical affirmative buttons are
047: * OK, Save, Print, Replace etc.
048: * This constant is used as contraint parameter in {@link #addButton(javax.swing.AbstractButton,Object)} method.
049: */
050: public static final String AFFIRMATIVE_BUTTON = "AFFIRMATIVE";
051:
052: /**
053: * The button will produce a cancel action. Typical cancel button is Cancel.
054: * This constant is used as contraint parameter in {@link #addButton(javax.swing.AbstractButton,Object)} method.
055: */
056: public static final String CANCEL_BUTTON = "CANCEL";
057:
058: /**
059: * The button will open some help windows.
060: * This constant is used as contraint parameter in {@link #addButton(javax.swing.AbstractButton,Object)} method.
061: */
062: public static final String HELP_BUTTON = "HELP";
063:
064: /**
065: * The button will produce an alternative action different neither an affirmative
066: * or cancel action. Typical alaternative button is Don't Save comparing with Save as affirmative action
067: * and Cancel as cancel action.
068: * This constant is used as contraint parameter in {@link #addButton(javax.swing.AbstractButton,Object)} method.
069: */
070: public static final String OTHER_BUTTON = "ALTERNATIVE";
071:
072: private String _defaultOrder = UIDefaultsLookup
073: .getString("ButtonPanel.order");
074:
075: private String _defaultOppositeOrder = UIDefaultsLookup
076: .getString("ButtonPanel.oppositeOrder");
077:
078: private int _defaultButtonGap = UIDefaultsLookup
079: .getInt("ButtonPanel.buttonGap");
080:
081: private int _defaultGroupGap = UIDefaultsLookup
082: .getInt("ButtonPanel.groupGap");
083:
084: private int _defaultButtonWidth = UIDefaultsLookup
085: .getInt("ButtonPanel.minButtonWidth");
086:
087: private int _alignment;
088:
089: private ButtonPanelLayout _layout;
090:
091: /**
092: * Constructs a new <code>ButtonPanel</code> with right alignment.
093: */
094: public ButtonPanel() {
095: this (SwingConstants.RIGHT);
096: }
097:
098: /**
099: * Constructs a new <code>ButtonPanel</code> with the specified alignment.
100: *
101: * @param alignment the alignment. The supported alignment are {@link SwingConstants#RIGHT},
102: * {@link SwingConstants#LEFT}, {@link SwingConstants#CENTER}, {@link SwingConstants#TOP} or
103: * {@link SwingConstants#BOTTOM}.
104: */
105: public ButtonPanel(int alignment) {
106: this (alignment, SAME_SIZE);
107: }
108:
109: /**
110: * Constructs a new <code>ButtonPanel</code> with default horizontal
111: * spacing and the given alignment.
112: *
113: * @param alignment the alignment of the buttons. It can be one of <code>SwingConstants.LEFT</code> or
114: * <code>SwingConstants.RIGHT</code> or <code>SwingConstants.TOP</code> or
115: * <code>SwingConstants.BOTTOM</code> or <code>SwingConstants.CENTER</code>.
116: * @param sizeContraint size contraint of the button.
117: * It can be either <code>SAME_SIZE</code> or <code>NO_LESS_THAN</code>
118: */
119:
120: public ButtonPanel(int alignment, int sizeContraint) {
121: _alignment = alignment;
122:
123: if (alignment != SwingConstants.LEFT
124: && alignment != SwingConstants.RIGHT
125: && alignment != SwingConstants.TOP
126: && alignment != SwingConstants.BOTTOM
127: && alignment != SwingConstants.CENTER) {
128: throw new IllegalArgumentException("Invalid alignment");
129: }
130:
131: int axis = (_alignment == SwingConstants.CENTER
132: || _alignment == SwingConstants.LEFT || _alignment == SwingConstants.RIGHT) ? ButtonPanelLayout.X_AXIS
133: : ButtonPanelLayout.Y_AXIS;
134:
135: _layout = new ButtonPanelLayout(this , axis, _alignment,
136: sizeContraint, _defaultOrder, _defaultOppositeOrder,
137: _defaultButtonGap, _defaultGroupGap);
138: setLayout(_layout);
139:
140: // Set forwardTraversalKeys = new HashSet();//getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS);
141: // forwardTraversalKeys.add(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0));
142: // forwardTraversalKeys.add(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0));
143: // setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, forwardTraversalKeys);
144: //
145: // Set backwardTraversalKeys = new HashSet();//getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS);
146: // backwardTraversalKeys.add(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0));
147: // backwardTraversalKeys.add(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0));
148: // setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, backwardTraversalKeys);
149: //
150: // setFocusCycleRoot(true);
151: }
152:
153: @Override
154: public void updateUI() {
155: if (UIDefaultsLookup.get("ButtonPanel.buttonGap") == null
156: && UIDefaultsLookup.get("ButtonPanel.order") == null
157: && UIDefaultsLookup.get("ButtonPanel.groupGap") == null) {
158: LookAndFeelFactory.installJideExtension();
159: }
160: super .updateUI();
161: reinstallDefaults();
162: }
163:
164: protected void reinstallDefaults() {
165: if (_layout != null) {
166: if (_defaultButtonGap == _layout.getButtonGap()) {
167: _defaultButtonGap = UIDefaultsLookup
168: .getInt("ButtonPanel.buttonGap");
169: _layout.setButtonGap(_defaultButtonGap);
170: }
171: if (_defaultGroupGap == _layout.getGroupGap()) {
172: _defaultGroupGap = UIDefaultsLookup
173: .getInt("ButtonPanel.groupGap");
174: _layout.setGroupGap(_defaultGroupGap);
175: }
176: if (_defaultOrder.equals(_layout.getButtonOrder())) {
177: _defaultOrder = UIDefaultsLookup
178: .getString("ButtonPanel.order");
179: _layout.setButtonOrder(_defaultOrder);
180: }
181: if (_defaultOppositeOrder.equals(_layout
182: .getOppositeButtonOrder())) {
183: _defaultOppositeOrder = UIDefaultsLookup
184: .getString("ButtonPanel.oppositeOrder");
185: _layout.setOppositeButtonOrder(_defaultOppositeOrder);
186: }
187: if (_defaultButtonWidth == _layout.getMinButtonWidth()) {
188: _defaultButtonWidth = UIDefaultsLookup
189: .getInt("ButtonPanel.minButtonWidth");
190: _layout.setMinButtonWidth(_defaultButtonWidth);
191: }
192: }
193: }
194:
195: /**
196: * Sets the alignment. If the alignment is one of SwingConstants.CENTER, SwingConstants.LEFT, and SwingConstants.RIGHT,
197: * the buttons will be laid out horizontally. If the alignment is SwingConstants.TOP or SwingConstants.BOTTOM, the buttons will
198: * be laid out vertically.
199: *
200: * @param alignment the alignment. The supported alignment are {@link SwingConstants#RIGHT},
201: * {@link SwingConstants#LEFT}, {@link SwingConstants#CENTER}, {@link SwingConstants#TOP} or
202: * {@link SwingConstants#BOTTOM}.
203: */
204: public void setAlignment(int alignment) {
205: _alignment = alignment;
206: int axis = (_alignment == SwingConstants.CENTER
207: || _alignment == SwingConstants.LEFT || _alignment == SwingConstants.RIGHT) ? ButtonPanelLayout.X_AXIS
208: : ButtonPanelLayout.Y_AXIS;
209: _layout.setAlignment(_alignment);
210: _layout.setAxis(axis);
211: _layout.layoutContainer(this );
212: }
213:
214: /**
215: * Gets the alignment of the ButtonPanel.
216: *
217: * @return the alignment of the ButtonPanel.
218: */
219: public int getAlignment() {
220: return _alignment;
221: }
222:
223: /**
224: * Adds button to ButonPanel as AFFIRMATIVE_BUTTON.
225: *
226: * @param button a button
227: */
228: public void addButton(AbstractButton button) {
229: addButton(button, AFFIRMATIVE_BUTTON);
230: }
231:
232: /**
233: * Adds button to ButonPanel with specified type.
234: *
235: * @param button a button.
236: * @param index the position in the button panel's list at which to insert the component; -1
237: * means insert at the end component
238: */
239: public void addButton(AbstractButton button, int index) {
240: addButton(button, AFFIRMATIVE_BUTTON, index);
241: }
242:
243: /**
244: * Adds button to ButonPanel with specified constraint. The valid constraints
245: * are {@link #AFFIRMATIVE_BUTTON}, {@link #CANCEL_BUTTON},{@link #OTHER_BUTTON} and {@link #HELP_BUTTON}.
246: * The main purpose of the constraints is to determine how the buttons are laid out on different platforms according to the OS convension.
247: * For example, on Windows, AFFIRMATIVE_BUTTON appears on the right hand side of CANCEL_BUTTON. On Mac OSX, AFFIRMATIVE_BUTTON will
248: * appear on the left hand side of CANCEL_BUTTON.
249: *
250: * @param button a button.
251: * @param constraint one of constraints.
252: */
253: public void addButton(AbstractButton button, Object constraint) {
254: addButton(button, constraint, -1);
255: }
256:
257: /**
258: * Adds button to ButonPanel with specified type.
259: *
260: * @param button a button.
261: * @param constraint String of one of types.
262: * @param index the position in the button panel's list at which to insert the component; -1
263: * means insert at the end component
264: */
265: public void addButton(AbstractButton button, Object constraint,
266: int index) {
267: add(button, constraint, index);
268: }
269:
270: @Override
271: protected void addImpl(Component comp, Object constraints, int index) {
272: // TODO: if index is not 0, it could be a problem
273: if (constraints == null) {
274: constraints = AFFIRMATIVE_BUTTON;
275: }
276: super .addImpl(comp, constraints, index);
277: }
278:
279: /**
280: * Removes the button. It's the same as {@link #remove(java.awt.Component)}.
281: *
282: * @param button a button
283: */
284: public void removeButton(AbstractButton button) {
285: remove(button);
286: }
287:
288: /**
289: * Gets the button order.
290: *
291: * @return the button order.
292: */
293: public String getButtonOrder() {
294: return _layout.getButtonOrder();
295: }
296:
297: /**
298: * Sets the button order.
299: *
300: * @param buttonOrder the new button order.
301: */
302: public void setButtonOrder(String buttonOrder) {
303: _layout.setButtonOrder(buttonOrder);
304: }
305:
306: /**
307: * Gets the opposite button order.
308: *
309: * @return the opposite button order.
310: */
311: public String getOppositeButtonOrder() {
312: return _layout.getOppositeButtonOrder();
313: }
314:
315: /**
316: * Sets the opposite button order.
317: *
318: * @param oppositeButtonOrder the new opposite button order.
319: */
320: public void setOppositeButtonOrder(String oppositeButtonOrder) {
321: _layout.setOppositeButtonOrder(oppositeButtonOrder);
322: }
323:
324: /**
325: * Gets the size contraint.
326: *
327: * @return the size contraint.
328: */
329: public int getSizeContraint() {
330: return _layout.getSizeConstraint();
331: }
332:
333: /**
334: * Sets the size contraint. Valid values are {@link #NO_LESS_THAN} and {@link #SAME_SIZE}.
335: * The size constraint will apply to all components except if the component client property
336: * {@link ButtonPanel#KEEP_PREFERRED_WIDTH} is set to Boolean.TRUE.
337: *
338: * @param sizeContraint the size contraint.
339: */
340: public void setSizeContraint(int sizeContraint) {
341: _layout.setSizeConstraint(sizeContraint);
342: }
343:
344: /**
345: * Gets the gap between two button groups.
346: *
347: * @return the gap between two button groups.
348: */
349: public int getGroupGap() {
350: return _layout.getGroupGap();
351: }
352:
353: /**
354: * Sets the gap between two button groups.
355: *
356: * @param groupGap the gap between button groups.
357: */
358: public void setGroupGap(int groupGap) {
359: _layout.setGroupGap(groupGap);
360: }
361:
362: /**
363: * Gets the gap between two buttons in the same group.
364: *
365: * @return the gap between two buttons in the same group.
366: */
367: public int getButtonGap() {
368: return _layout.getButtonGap();
369: }
370:
371: /**
372: * Sets the gap between two buttons in the same group.
373: *
374: * @param buttonGap the gap between buttons.
375: */
376: public void setButtonGap(int buttonGap) {
377: _layout.setButtonGap(buttonGap);
378: }
379:
380: /**
381: * Gets the minimium button width.
382: *
383: * @return the minimium button width.
384: */
385: public int getMinButtonWidth() {
386: return _layout.getMinButtonWidth();
387: }
388:
389: /**
390: * Sets the minimium button width.
391: *
392: * @param minButtonWidth the minimium button width.
393: */
394: public void setMinButtonWidth(int minButtonWidth) {
395: _layout.setMinButtonWidth(minButtonWidth);
396: }
397:
398: public void buttonEventFired(ButtonEvent e) {
399: for (int i = 0; i < getComponentCount(); i++) {
400: final Component component = getComponent(i);
401: if (e.getButtonName().equals(component.getName())) {
402: switch (e.getID()) {
403: case ButtonEvent.ENABLE_BUTTON:
404: component.setVisible(true);
405: if (component instanceof JButton
406: && ((JButton) component).getAction() != null) {
407: ((JButton) component).getAction().setEnabled(
408: true);
409: }
410: component.setEnabled(true);
411: break;
412: case ButtonEvent.DISABLE_BUTTON:
413: component.setEnabled(false);
414: if (component instanceof JButton
415: && ((JButton) component).getAction() != null) {
416: ((JButton) component).getAction().setEnabled(
417: false);
418: }
419: component.setVisible(true);
420: break;
421: case ButtonEvent.SHOW_BUTTON:
422: component.setVisible(true);
423: break;
424: case ButtonEvent.HIDE_BUTTON:
425: component.setVisible(false);
426: break;
427: case ButtonEvent.CHANGE_BUTTON_TEXT:
428: if (component instanceof AbstractButton) {
429: ((AbstractButton) component).setText(e
430: .getUserObject());
431: }
432: break;
433: case ButtonEvent.CHANGE_BUTTON_MNEMONIC:
434: if (component instanceof AbstractButton) {
435: ((AbstractButton) component).setMnemonic(e
436: .getUserObject().charAt(0));
437: }
438: break;
439: case ButtonEvent.CHANGE_BUTTON_TOOLTIP:
440: if (component instanceof AbstractButton) {
441: ((AbstractButton) component).setToolTipText(e
442: .getUserObject());
443: }
444: break;
445: case ButtonEvent.CHANGE_BUTTON_FOCUS:
446: Runnable runnable = new Runnable() {
447: public void run() {
448: component.requestFocus();
449: }
450: };
451: SwingUtilities.invokeLater(runnable);
452: break;
453: case ButtonEvent.SET_DEFAULT_BUTTON:
454: if (component instanceof JButton) {
455: if (getRootPane() != null) {
456: getRootPane().setDefaultButton(
457: ((JButton) component));
458: } else {
459: _defaultButton = (JButton) component;
460: _addNotify = true;
461: }
462: }
463: break;
464: }
465: break;
466: }
467: }
468: }
469:
470: private boolean _addNotify = false;
471: private JButton _defaultButton;
472:
473: @Override
474: public void addNotify() {
475: super .addNotify();
476: if (_addNotify) {
477: JRootPane pane = getRootPane();
478: if (_defaultButton != null && pane != null) {
479: pane.setDefaultButton(_defaultButton);
480: _addNotify = false;
481: _defaultButton = null;
482: }
483: }
484: }
485:
486: /**
487: * Gets the button with the name. In order to use this method, you have to set a name to the
488: * button using {@link AbstractButton#setName(String)} method. Please note, the name is not the same as
489: * the contraint in the second parameter of {@link #add(java.awt.Component,Object)}.
490: *
491: * @param name the button name.
492: * @return the button which has the name. null if there is no button with that name.
493: * @throws IllegalArgumentException if the name is null or empty.
494: */
495: public Component getButtonByName(String name) {
496: if (name == null || name.trim().length() == 0) {
497: throw new IllegalArgumentException(
498: "name cannot be null or empty");
499: }
500: for (int i = 0; i < getComponentCount(); i++) {
501: Component component = getComponent(i);
502: if (name.equals(component.getName())) {
503: return component;
504: }
505: }
506: return null;
507: }
508: }
|