org.wings.SAbstractButton Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2000,2005 wingS development team.
*
* This file is part of wingS (http://wingsframework.org).
*
* wingS is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1
* of the License, or (at your option) any later version.
*
* Please see COPYING for the complete licence.
*/
package org.wings;
import org.wings.plaf.ButtonCG;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Objects;
/**
* Base class for components with button functionality, ie. the need to handle ActionListener notification.
*
* @author Armin Haaf
*/
public abstract class SAbstractButton
extends SAbstractIconTextCompound
implements LowLevelEventListener {
public static final String SUBMIT_BUTTON = "submit";
public static final String RESET_BUTTON = "reset";
public static final String IMAGE_BUTTON = "image";
public static final String CHECKBOX = "checkbox";
public static final String RADIOBUTTON = "radio";
private String type = SUBMIT_BUTTON;
private SButtonGroup buttonGroup;
protected String actionCommand;
private String eventTarget;
private Action action;
private PropertyChangeListener actionPropertyChangeListener;
private String mnemonic;
/** @see LowLevelEventListener#isEpochCheckEnabled() */
private boolean epochCheckEnabled = true;
private boolean wordWrap = false;
/**
* Create a button with given text.
*
* @param text the button text
*/
public SAbstractButton(String text) {
super(text);
}
public SAbstractButton(Action action) {
setAction(action);
}
/**
* Creates a new Button with the given Text and the given Type.
*
* @param text the button text
* @param type the button type
* @see #setType
*/
public SAbstractButton(String text, String type) {
this(text);
setType(type);
}
/**
* Creates a new submit button
*/
public SAbstractButton() {
}
/**
* Sets the action command for this button.
*
* @param ac the action command for this button
*/
public void setActionCommand(String ac) {
String oldVal = this.actionCommand;
actionCommand = ac;
propertyChangeSupport.firePropertyChange("actionCommand", oldVal, this.actionCommand);
}
/**
* Returns the action command for this button.
*
* @return the action command for this button
*/
public final String getActionCommand() {
return actionCommand;
}
/**
* Return the Button group where this button lies in
*
* @return Button Group or null if not in a group
*/
public final SButtonGroup getGroup() {
return buttonGroup;
}
@Override
protected void setParentFrame(SFrame f) {
if (f == getParentFrame())
return;
if (buttonGroup != null && getSession().getDispatcher() != null) {
getSession().getDispatcher().removeLowLevelEventListener(this, buttonGroup.getComponentId());
} // end of if ()
super.setParentFrame(f);
if (buttonGroup != null && f != null && getSession().getDispatcher() != null) {
getSession().getDispatcher().addLowLevelEventListener(this, buttonGroup.getComponentId());
} // end of if ()
}
/**
* Add this button to a button group. This influences the event-prefix
* this button reports to the request dispatcher: it will change to
* the button group's prefix.
*/
protected void setGroup(SButtonGroup g) {
if (isDifferent(buttonGroup, g)) {
SButtonGroup oldVal = this.buttonGroup;
// Do no longer react on events from old button group
if (buttonGroup != null && getSession().getDispatcher() != null) {
getSession().getDispatcher().removeLowLevelEventListener(this, buttonGroup.getComponentId());
} // end of if ()
buttonGroup = g;
// Button Group changed but button already registered via parent frame?
if (buttonGroup != null && getParentFrame() != null && getSession().getDispatcher() != null) {
getSession().getDispatcher().addLowLevelEventListener(this, buttonGroup.getComponentId());
} // end of if ()
reload();
propertyChangeSupport.firePropertyChange("group", oldVal, this.buttonGroup);
}
}
/**
* Adds an ActionListener to the button.
*
* @param listener the ActionListener to be added
*/
public void addActionListener(ActionListener listener) {
addEventListener(ActionListener.class, listener);
}
/**
* Removes the supplied Listener from the listener list
*/
public void removeActionListener(ActionListener listener) {
removeEventListener(ActionListener.class, listener);
}
/**
* Returns an array of all the ActionListener
s added
* to this AbstractButton with addActionListener().
*
* @return all of the ActionListener
s added or an empty
* array if no listeners have been added
*/
public ActionListener[] getActionListeners() {
return (ActionListener[]) (getListeners(ActionListener.class));
}
/**
* Fire an ActionEvent at each registered listener.
*
* @param event supplied ActionEvent
*/
protected void fireActionPerformed(ActionEvent event) {
// Guaranteed to return a non-null array
Object[] listeners = getListenerList();
ActionEvent e = null;
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ActionListener.class) {
if (e == null) {
String actionCommand = event.getActionCommand();
if (actionCommand == null) {
actionCommand = this.actionCommand;
}
e = new ActionEvent(SAbstractButton.this,
ActionEvent.ACTION_PERFORMED,
actionCommand,
event.getWhen(),
event.getModifiers());
}
((ActionListener) listeners[i + 1]).actionPerformed(e);
}
}
}
/**
* Sets the button type. Use one of the following types:
*
* - {@link #SUBMIT_BUTTON}
*
- {@link #RESET_BUTTON}
*
- {@link #CHECKBOX}
*
- {@link #RADIOBUTTON}
*
*/
public void setType(String t) {
if (isDifferent(type, t)) {
String oldVal = this.type;
type = t;
reload();
propertyChangeSupport.firePropertyChange("type", oldVal, this.type);
}
}
/**
* Delifers the Button Type
*
* @return Button Type
*/
public final String getType() {
return type;
}
/**
* Simulates an click on the Button
*/
public void doClick() {
setSelected(!isSelected());
fireActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, actionCommand));
}
/**
* Sets the state of the button. *
*
* @param b true if the button is selected, otherwise false
*/
@Override
public void setSelected(boolean b) {
if (isSelected() != b) {
if (buttonGroup != null) {
buttonGroup.setSelected(this, b);
}
super.setSelected(b);
}
}
@Override
public void processLowLevelEvent(String action, String... values) {
processKeyEvents(values);
if (action.endsWith("_keystroke"))
return;
delayEvents(true);
boolean requestSelection = isSelected();
int eventCount = 0;
if (buttonGroup != null) {
// button group prefix is shared, so maybe more than one value is
// delivered in a form
for (String value : values) {
// with button group the value has a special encoding...
// this is because in a form the name of a parameter for
// buttons in a buttongroup must be the same...
// illegal format
if (value.length() < 3) {
continue;
}
// no uid DIVIDER
// value.charAt(value.length()-2)!=UID_DIVIDER ) { break; }
// not for me
if (!value.startsWith(super.getLowLevelEventId())) {
continue;
}
// last character is indicator, if button should be
// selected or not
switch (value.charAt(value.length() - 1)) {
case '1':
requestSelection = true;
++eventCount;
break;
case '0':
requestSelection = false;
++eventCount;
break;
}
}
} else {
for (String value : values) {
requestSelection = parseSelectionToggle(values[0]);
++eventCount;
}
}
/*
* Checkboxes in HTML-forms write two form components:
* one hidden input, containing the deselect-command (value='0'),
* and one .
* This is, because browsers send the checkbox-variable
* only if it is set, not if it is not set.
* So, if we get two events with the same name, then the checkbox
* actually got selected; if we only got one event (from the hidden
* input), then it was a deselect-event (select event value = '1',
* deselect value = '0').
* This is just in case, the browser sends the both events
* in the wrong order (select and then deselect).
*/
if (eventCount == 2) {
requestSelection = true;
}
if (isSelected() != requestSelection) {
if (buttonGroup != null) {
buttonGroup.setDelayEvents(true);
setSelected(requestSelection);
buttonGroup.setDelayEvents(false);
} else {
setSelected(requestSelection);
}
SForm.addArmedComponent(this);
}
delayEvents(false);
}
@Override
public void fireIntermediateEvents() {
super.fireIntermediateEvents();
if (buttonGroup != null) {
buttonGroup.fireDelayedIntermediateEvents();
}
}
@Override
public void fireFinalEvents() {
super.fireFinalEvents();
fireActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED, actionCommand));
if (buttonGroup != null)
buttonGroup.fireDelayedFinalEvents();
}
public final String getEventTarget() {
return eventTarget;
}
public void setEventTarget(String target) {
if (isDifferent(eventTarget, target)) {
String oldVal = this.eventTarget;
eventTarget = target;
reload();
propertyChangeSupport.firePropertyChange("eventTarget", oldVal, this.eventTarget);
}
}
protected boolean parseSelectionToggle(String toggleParameter) {
if ("1".equals(toggleParameter))
return true;
if ("0".equals(toggleParameter))
return false;
// don't change...
return isSelected();
}
public String getToggleSelectionParameter() {
return isSelected() ? getDeselectionParameter() : getSelectionParameter();
}
public String getSelectionParameter() {
return "1";
}
public String getDeselectionParameter() {
return "0";
}
/**
* Sets the action for the ActionEvent source.
* the new action code will replace the old one but not the one bound to the actionListener
*
* @param a the Action for the AbstractButton,
*/
public void setAction(Action a) {
Action oldValue = action;
if (!Objects.equals(action, a)) {
action = a;
if (oldValue != null) {
removeActionListener(oldValue);
oldValue.removePropertyChangeListener(actionPropertyChangeListener);
actionPropertyChangeListener = null;
}
configurePropertiesFromAction(action);
if (action != null) {
// Don't add if it is already a listener
if (!isListener(ActionListener.class, action)) {
addActionListener(action);
}
// Reverse linkage:
actionPropertyChangeListener = createActionPropertyChangeListener(action);
action.addPropertyChangeListener(actionPropertyChangeListener);
}
reload();
}
propertyChangeSupport.firePropertyChange("action", oldValue, this.action);
}
private boolean isListener(Class c, ActionListener a) {
boolean isListener = false;
Object[] listeners = getListenerList();
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == c && listeners[i + 1] == a) {
isListener = true;
}
}
return isListener;
}
/**
* Returns the action for this ActionEvent source, or null
* if no Action
is set.
*
* @return the Action
for this ActionEvent
* source, or null
*/
public Action getAction() {
return action;
}
public void setCG(ButtonCG cg) {
super.setCG(cg);
}
protected void configurePropertiesFromAction(Action a) {
// uncomment if compiled against < jdk 1.3
// setActionCommand((a != null
// ? (String)a.getValue(Action.ACTION_COMMAND_KEY)
// : null));
setText((a != null ? (String) a.getValue(Action.NAME) : null));
setIcon((a != null ? (SIcon) a.getValue(Action.SMALL_ICON) : null));
setEnabled((a == null || a.isEnabled()));
setToolTipText((a != null ? (String) a.getValue(Action.SHORT_DESCRIPTION) : null));
}
protected PropertyChangeListener createActionPropertyChangeListener(Action a) {
return new ButtonActionPropertyChangeListener(this, a);
}
private static class ButtonActionPropertyChangeListener
extends AbstractActionPropertyChangeListener {
ButtonActionPropertyChangeListener(SAbstractButton b, Action a) {
super(b, a);
}
@Override
public void propertyChange(PropertyChangeEvent e) {
String propertyName = e.getPropertyName();
SAbstractButton button = (SAbstractButton) getTarget();
if (button == null) {
Action action = (Action) e.getSource();
action.removePropertyChangeListener(this);
} else {
if (e.getPropertyName().equals(Action.NAME)) {
String text = (String) e.getNewValue();
button.setText(text);
} else if (e.getPropertyName().equals(Action.SHORT_DESCRIPTION)) {
String text = (String) e.getNewValue();
button.setToolTipText(text);
} else if (propertyName.equals("enabled")) {
Boolean enabled = (Boolean) e.getNewValue();
button.setEnabled(enabled);
} else if (e.getPropertyName().equals(Action.SMALL_ICON)) {
SIcon icon = (SIcon) e.getNewValue();
button.setIcon(icon);
}
// uncomment if compiled against jdk < 1.3
/* else if (e.getPropertyName().equals(Action.ACTION_COMMAND_KEY)) {
String actionCommand = (String)e.getNewValue();
button.setActionCommand(actionCommand);
}*/
}
}
}
public void setMnemonic(String mnemonic) {
String oldVal = this.mnemonic;
reloadIfChange(this.mnemonic, mnemonic);
this.mnemonic = mnemonic;
propertyChangeSupport.firePropertyChange("mnemonic", oldVal, this.mnemonic);
}
public String getMnemonic() {
return mnemonic;
}
/** @see LowLevelEventListener#isEpochCheckEnabled() */
@Override
public boolean isEpochCheckEnabled() {
return epochCheckEnabled;
}
/** @see LowLevelEventListener#isEpochCheckEnabled() */
public void setEpochCheckEnabled(boolean epochCheckEnabled) {
boolean oldVal = this.epochCheckEnabled;
this.epochCheckEnabled = epochCheckEnabled;
propertyChangeSupport.firePropertyChange("epochCheckEnabled", oldVal, this.epochCheckEnabled);
}
/**
* Determiens if the label text word wrap inside the browser. Defaults to false
(Swing).
* @return false
if the label should not word wrap an be in line as in Swing.
*/
public boolean isWordWrap() {
return wordWrap;
}
/**
* Defines if the label is allowed to wrap.
* @param wordWrap Set to true
if you want labels to allow to break into more lines than passed.
*/
public void setWordWrap(boolean wordWrap) {
if (this.wordWrap != wordWrap) {
boolean oldVal = this.wordWrap;
this.wordWrap = wordWrap;
reload();
propertyChangeSupport.firePropertyChange("wordWrap", oldVal, this.wordWrap);
}
}
}