001: /*
002: * Copyright 2000,2005 wingS development team.
003: *
004: * This file is part of wingS (http://wingsframework.org).
005: *
006: * wingS is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU Lesser General Public License
008: * as published by the Free Software Foundation; either version 2.1
009: * of the License, or (at your option) any later version.
010: *
011: * Please see COPYING for the complete licence.
012: */
013: package org.wings;
014:
015: import org.wings.plaf.ButtonCG;
016:
017: import javax.swing.*;
018: import java.awt.event.ActionEvent;
019: import java.awt.event.ActionListener;
020: import java.beans.PropertyChangeEvent;
021: import java.beans.PropertyChangeListener;
022:
023: /**
024: * Base class for components with button functionality, ie. the need to handle ActionListener notification.
025: *
026: * @author <a href="mailto:armin.haaf@mercatis.de">Armin Haaf</a>
027: */
028: public abstract class SAbstractButton extends SAbstractIconTextCompound
029: implements LowLevelEventListener {
030: public static final String SUBMIT_BUTTON = "submit";
031: public static final String RESET_BUTTON = "reset";
032: public static final String IMAGE_BUTTON = "image";
033: public static final String CHECKBOX = "checkbox";
034: public static final String RADIOBUTTON = "radio";
035:
036: private String type = SUBMIT_BUTTON;
037:
038: private SButtonGroup buttonGroup;
039:
040: protected String actionCommand;
041:
042: private String eventTarget;
043:
044: private Action action;
045:
046: private PropertyChangeListener actionPropertyChangeListener;
047:
048: private String mnemonic;
049:
050: /** @see LowLevelEventListener#isEpochCheckEnabled() */
051: private boolean epochCheckEnabled = true;
052:
053: private boolean wordWrap = false;
054:
055: /**
056: * Create a button with given text.
057: *
058: * @param text the button text
059: */
060: public SAbstractButton(String text) {
061: super (text);
062: }
063:
064: public SAbstractButton(Action action) {
065: setAction(action);
066: }
067:
068: /**
069: * Creates a new Button with the given Text and the given Type.
070: *
071: * @param text the button text
072: * @param type the button type
073: * @see #setType
074: */
075: public SAbstractButton(String text, String type) {
076: this (text);
077: setType(type);
078: }
079:
080: /**
081: * Creates a new submit button
082: */
083: public SAbstractButton() {
084: }
085:
086: /**
087: * Sets the action command for this button.
088: *
089: * @param ac the action command for this button
090: */
091: public void setActionCommand(String ac) {
092: actionCommand = ac;
093: }
094:
095: /**
096: * Returns the action command for this button.
097: *
098: * @return the action command for this button
099: */
100: public final String getActionCommand() {
101: return actionCommand;
102: }
103:
104: /**
105: * Return the Button group where this button lies in
106: *
107: * @return Button Group or null if not in a group
108: */
109: public final SButtonGroup getGroup() {
110: return buttonGroup;
111: }
112:
113: protected void setParentFrame(SFrame f) {
114: if (f == getParentFrame())
115: return;
116:
117: if (buttonGroup != null && getSession().getDispatcher() != null) {
118: getSession().getDispatcher().removeLowLevelEventListener(
119: this , buttonGroup.getComponentId());
120: } // end of if ()
121:
122: super .setParentFrame(f);
123:
124: if (buttonGroup != null && f != null
125: && getSession().getDispatcher() != null) {
126: getSession().getDispatcher().addLowLevelEventListener(this ,
127: buttonGroup.getComponentId());
128: } // end of if ()
129: }
130:
131: /**
132: * Add this button to a button group. This influences the event-prefix
133: * this button reports to the request dispatcher: it will change to
134: * the button group's prefix.
135: */
136: protected void setGroup(SButtonGroup g) {
137: if (isDifferent(buttonGroup, g)) {
138: // Do no longer react on events from old button group
139: if (buttonGroup != null
140: && getSession().getDispatcher() != null) {
141: getSession().getDispatcher()
142: .removeLowLevelEventListener(this ,
143: buttonGroup.getComponentId());
144: } // end of if ()
145: buttonGroup = g;
146: // Button Group changed but button already registered via parent frame?
147: if (buttonGroup != null && getParentFrame() != null
148: && getSession().getDispatcher() != null) {
149: getSession().getDispatcher().addLowLevelEventListener(
150: this , buttonGroup.getComponentId());
151: } // end of if ()
152: reload();
153: }
154: }
155:
156: /**
157: * Adds an ActionListener to the button.
158: *
159: * @param listener the ActionListener to be added
160: */
161: public void addActionListener(ActionListener listener) {
162: addEventListener(ActionListener.class, listener);
163: }
164:
165: /**
166: * Removes the supplied Listener from the listener list
167: */
168: public void removeActionListener(ActionListener listener) {
169: removeEventListener(ActionListener.class, listener);
170: }
171:
172: /**
173: * Returns an array of all the <code>ActionListener</code>s added
174: * to this AbstractButton with addActionListener().
175: *
176: * @return all of the <code>ActionListener</code>s added or an empty
177: * array if no listeners have been added
178: */
179: public ActionListener[] getActionListeners() {
180: return (ActionListener[]) (getListeners(ActionListener.class));
181: }
182:
183: /**
184: * Fire an ActionEvent at each registered listener.
185: *
186: * @param event supplied ActionEvent
187: */
188: protected void fireActionPerformed(ActionEvent event) {
189: // Guaranteed to return a non-null array
190: Object[] listeners = getListenerList();
191: ActionEvent e = null;
192: // Process the listeners last to first, notifying
193: // those that are interested in this event
194: for (int i = listeners.length - 2; i >= 0; i -= 2) {
195: if (listeners[i] == ActionListener.class) {
196: if (e == null) {
197: String actionCommand = event.getActionCommand();
198: if (actionCommand == null) {
199: actionCommand = getActionCommand();
200: }
201: e = new ActionEvent(SAbstractButton.this ,
202: ActionEvent.ACTION_PERFORMED,
203: actionCommand, event.getWhen(), event
204: .getModifiers());
205: }
206: ((ActionListener) listeners[i + 1]).actionPerformed(e);
207: }
208: }
209: }
210:
211: /**
212: * Sets the button type. Use one of the following types:
213: * <UL>
214: * <LI> {@link #SUBMIT_BUTTON}
215: * <LI> {@link #RESET_BUTTON}
216: * <LI> {@link #CHECKBOX}
217: * <LI> {@link #RADIOBUTTON}
218: * </UL>
219: */
220: public void setType(String t) {
221: if (isDifferent(type, t)) {
222: type = t;
223: reload();
224: }
225: }
226:
227: /**
228: * Delifers the Button Type
229: *
230: * @return Button Type
231: */
232: public final String getType() {
233: return type;
234: }
235:
236: /**
237: * Simulates an click on the Button
238: */
239: public void doClick() {
240: setSelected(!isSelected());
241:
242: fireActionPerformed(new ActionEvent(this ,
243: ActionEvent.ACTION_PERFORMED, getActionCommand()));
244: }
245:
246: /**
247: * Sets the state of the button. *
248: *
249: * @param b true if the button is selected, otherwise false
250: */
251: public void setSelected(boolean b) {
252: if (isSelected() != b) {
253: if (buttonGroup != null) {
254: buttonGroup.setSelected(this , b);
255: }
256: super .setSelected(b);
257: }
258: }
259:
260: public void processLowLevelEvent(String action, String[] values) {
261: processKeyEvents(values);
262: if (action.endsWith("_keystroke"))
263: return;
264:
265: delayEvents(true);
266:
267: boolean requestSelection = isSelected();
268:
269: int eventCount = 0;
270:
271: if (buttonGroup != null) {
272: // button group prefix is shared, so maybe more than one value is
273: // delivered in a form
274: for (int i = 0; i < values.length; i++) {
275:
276: // with button group the value has a special encoding...
277: // this is because in a form the name of a parameter for
278: // buttons in a buttongroup must be the same...
279: String value = values[i];
280:
281: // illegal format
282: if (value.length() < 3) {
283: continue;
284: }
285:
286: // no uid DIVIDER
287: // value.charAt(value.length()-2)!=UID_DIVIDER ) { break; }
288:
289: // not for me
290: if (!value.startsWith(super .getLowLevelEventId())) {
291: continue;
292: }
293:
294: // last character is indicator, if button should be
295: // selected or not
296: switch (value.charAt(value.length() - 1)) {
297: case '1':
298: requestSelection = true;
299: ++eventCount;
300: break;
301: case '0':
302: requestSelection = false;
303: ++eventCount;
304: break;
305: }
306: }
307: } else {
308: for (int i = 0; i < values.length; i++) {
309: requestSelection = parseSelectionToggle(values[0]);
310: ++eventCount;
311: }
312: }
313:
314: /*
315: * Checkboxes in HTML-forms write two form components:
316: * one hidden input, containing the deselect-command (value='0'),
317: * and one <input type="checkbox".. value="1">.
318: * This is, because browsers send the checkbox-variable
319: * only if it is set, not if it is not set.
320: * So, if we get two events with the same name, then the checkbox
321: * actually got selected; if we only got one event (from the hidden
322: * input), then it was a deselect-event (select event value = '1',
323: * deselect value = '0').
324: * This is just in case, the browser sends the both events
325: * in the wrong order (select and then deselect).
326: */
327: if (eventCount == 2) {
328: requestSelection = true;
329: }
330:
331: if (isSelected() != requestSelection) {
332: if (buttonGroup != null) {
333: buttonGroup.setDelayEvents(true);
334: setSelected(requestSelection);
335: buttonGroup.setDelayEvents(false);
336: } else {
337: setSelected(requestSelection);
338: }
339:
340: SForm.addArmedComponent(this );
341: }
342:
343: delayEvents(false);
344: }
345:
346: public void fireIntermediateEvents() {
347: super .fireIntermediateEvents();
348: if (buttonGroup != null) {
349: buttonGroup.fireDelayedIntermediateEvents();
350: }
351: }
352:
353: public void fireFinalEvents() {
354: super .fireFinalEvents();
355: fireActionPerformed(new ActionEvent(this ,
356: ActionEvent.ACTION_PERFORMED, getActionCommand()));
357: if (buttonGroup != null)
358: buttonGroup.fireDelayedFinalEvents();
359: }
360:
361: public final String getEventTarget() {
362: return eventTarget;
363: }
364:
365: public void setEventTarget(String target) {
366: if (isDifferent(eventTarget, target)) {
367: eventTarget = target;
368: reload();
369: }
370: }
371:
372: protected boolean parseSelectionToggle(String toggleParameter) {
373: if ("1".equals(toggleParameter))
374: return true;
375: else if ("0".equals(toggleParameter))
376: return false;
377:
378: // don't change...
379: return isSelected();
380: }
381:
382: public String getToggleSelectionParameter() {
383: return isSelected() ? getDeselectionParameter()
384: : getSelectionParameter();
385: }
386:
387: public String getSelectionParameter() {
388: return "1";
389: }
390:
391: public String getDeselectionParameter() {
392: return "0";
393: }
394:
395: /**
396: * Sets the action for the ActionEvent source.
397: * the new action code will replace the old one but not the one bound to the actionListener
398: *
399: * @param a the Action for the AbstractButton,
400: */
401:
402: public void setAction(Action a) {
403: Action oldValue = getAction();
404: if (action == null || !action.equals(a)) {
405: action = a;
406: if (oldValue != null) {
407: removeActionListener(oldValue);
408: oldValue
409: .removePropertyChangeListener(actionPropertyChangeListener);
410: actionPropertyChangeListener = null;
411: }
412: configurePropertiesFromAction(action);
413: if (action != null) {
414: // Don't add if it is already a listener
415: if (!isListener(ActionListener.class, action)) {
416: addActionListener(action);
417: }
418: // Reverse linkage:
419: actionPropertyChangeListener = createActionPropertyChangeListener(action);
420: action
421: .addPropertyChangeListener(actionPropertyChangeListener);
422: }
423: reload();
424: }
425: }
426:
427: private boolean isListener(Class c, ActionListener a) {
428: boolean isListener = false;
429: Object[] listeners = getListenerList();
430: for (int i = listeners.length - 2; i >= 0; i -= 2) {
431: if (listeners[i] == c && listeners[i + 1] == a) {
432: isListener = true;
433: }
434: }
435: return isListener;
436: }
437:
438: /**
439: * Returns the action for this ActionEvent source, or <code>null</code>
440: * if no <code>Action</code> is set.
441: *
442: * @return the <code>Action</code> for this <code>ActionEvent</code>
443: * source, or <code>null</code>
444: */
445: public Action getAction() {
446: return action;
447: }
448:
449: public void setCG(ButtonCG cg) {
450: super .setCG(cg);
451: }
452:
453: protected void configurePropertiesFromAction(Action a) {
454: // uncomment if compiled against < jdk 1.3
455: // setActionCommand((a != null
456: // ? (String)a.getValue(Action.ACTION_COMMAND_KEY)
457: // : null));
458: setText((a != null ? (String) a.getValue(Action.NAME) : null));
459: setIcon((a != null ? (SIcon) a.getValue(Action.SMALL_ICON)
460: : null));
461: setEnabled((a != null ? a.isEnabled() : true));
462: setToolTipText((a != null ? (String) a
463: .getValue(Action.SHORT_DESCRIPTION) : null));
464: }
465:
466: protected PropertyChangeListener createActionPropertyChangeListener(
467: Action a) {
468: return new ButtonActionPropertyChangeListener(this , a);
469: }
470:
471: private static class ButtonActionPropertyChangeListener extends
472: AbstractActionPropertyChangeListener {
473: ButtonActionPropertyChangeListener(SAbstractButton b, Action a) {
474: super (b, a);
475: }
476:
477: public void propertyChange(PropertyChangeEvent e) {
478: String propertyName = e.getPropertyName();
479: SAbstractButton button = (SAbstractButton) getTarget();
480: if (button == null) {
481: Action action = (Action) e.getSource();
482: action.removePropertyChangeListener(this );
483: } else {
484: if (e.getPropertyName().equals(Action.NAME)) {
485: String text = (String) e.getNewValue();
486: button.setText(text);
487: } else if (e.getPropertyName().equals(
488: Action.SHORT_DESCRIPTION)) {
489: String text = (String) e.getNewValue();
490: button.setToolTipText(text);
491: } else if (propertyName.equals("enabled")) {
492: Boolean enabled = (Boolean) e.getNewValue();
493: button.setEnabled(enabled.booleanValue());
494: } else if (e.getPropertyName()
495: .equals(Action.SMALL_ICON)) {
496: SIcon icon = (SIcon) e.getNewValue();
497: button.setIcon(icon);
498: }
499: // uncomment if compiled against jdk < 1.3
500: /* else if (e.getPropertyName().equals(Action.ACTION_COMMAND_KEY)) {
501: String actionCommand = (String)e.getNewValue();
502: button.setActionCommand(actionCommand);
503: }*/
504: }
505: }
506: }
507:
508: public void setMnemonic(String mnemonic) {
509: reloadIfChange(this .mnemonic, mnemonic);
510: this .mnemonic = mnemonic;
511: }
512:
513: public String getMnemonic() {
514: return mnemonic;
515: }
516:
517: /** @see LowLevelEventListener#isEpochCheckEnabled() */
518: public boolean isEpochCheckEnabled() {
519: return epochCheckEnabled;
520: }
521:
522: /** @see LowLevelEventListener#isEpochCheckEnabled() */
523: public void setEpochCheckEnabled(boolean epochCheckEnabled) {
524: this .epochCheckEnabled = epochCheckEnabled;
525: }
526:
527: /**
528: * Determiens if the label text word wrap inside the browser. Defaults to <code>false</code> (Swing).
529: * @return <code>false</code> if the label should not word wrap an be in line as in Swing.
530: */
531: public boolean isWordWrap() {
532: return wordWrap;
533: }
534:
535: /**
536: * Defines if the label is allowed to wrap.
537: * @param wordWrap Set to <code>true</code> if you want labels to allow to break into more lines than passed.
538: */
539: public void setWordWrap(boolean wordWrap) {
540: if (this.wordWrap != wordWrap) {
541: this.wordWrap = wordWrap;
542: reload();
543: }
544: }
545: }
|