001 /* JAPI - (Yet anothr (hopefully) useful) Java API
002 *
003 * Copyright (C) 2004-2006 Christian Hujer
004 *
005 * This program is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU General Public License as
007 * published by the Free Software Foundation; either version 2 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful, but
011 * WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
013 * General Public License for more details.
014 *
015 * You should have received a copy of the GNU General Public License
016 * along with this program; if not, write to the Free Software
017 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
018 * 02111-1307, USA.
019 */
020
021 package net.sf.japi.swing;
022
023 import java.awt.event.ActionEvent;
024 import java.lang.ref.WeakReference;
025 import java.lang.reflect.InvocationTargetException;
026 import java.lang.reflect.Method;
027 import java.util.ArrayList;
028 import java.util.Iterator;
029 import java.util.List;
030 import javax.swing.AbstractAction;
031 import javax.swing.AbstractButton;
032 import javax.swing.JCheckBox;
033 import javax.swing.JCheckBoxMenuItem;
034
035 /** The ToggleAction works similar as an ReflectionAction.
036 * But it keeps track of the components.
037 * Be sure to use its factory methodsA
038 * @author <a href="mailto:Christian.Hujer@itcqis.com">Christian Hujer</a>
039 */
040 public final class ToggleAction extends AbstractAction {
041
042 /** The key used for storing the target object to invoke the methods on.
043 * Value Type: {@link java.lang.Object}.
044 */
045 public static final String REFLECTION_TARGET = "ReflectionTarget";
046
047 /** The key used for storing the target object's boolean property name to find the methods
048 * Value Type: {@link java.lang.String}.
049 */
050 public static final String REFLECTION_PROPERTY_NAME = "ReflectionPropertyName";
051
052 /** Serial Version. */
053 private static final long serialVersionUID = 1L;
054
055 /** The selected state.
056 * @serial include
057 */
058 private boolean selected;
059
060 /** The buttons created. */
061 private final transient List<WeakReference<AbstractButton>> buttons = new ArrayList<WeakReference<AbstractButton>>();
062
063 /** Returns the state of the action.
064 * @return selected state of this action
065 */
066 public boolean isSelected() {
067 return selected;
068 }
069
070 /** {@inheritDoc} */
071 public void actionPerformed(final ActionEvent e) {
072 if (!isEnabled()) {
073 return;
074 }
075 final Object instance = getValue(REFLECTION_TARGET);
076 String property = (String) getValue(REFLECTION_PROPERTY_NAME);
077 property = Character.toUpperCase(property.charAt(0)) + property.substring(1);
078 final String getterName = "is" + property;
079 final String setterName = "set" + property;
080 try {
081 final Method getter = instance.getClass().getMethod(getterName);
082 final Method setter = instance.getClass().getMethod(setterName, boolean.class);
083 setter.invoke(instance, !(Boolean) getter.invoke(instance));
084 setSelected((Boolean) getter.invoke(instance));
085 } catch (final NoSuchMethodException ex) {
086 assert false : ex;
087 } catch (final IllegalAccessException ex) {
088 assert false : ex;
089 } catch (final InvocationTargetException ex) {
090 throw new RuntimeException(ex.getCause());
091 }
092 }
093
094 /** {@inheritDoc} */
095 @Override protected Object clone() throws CloneNotSupportedException {
096 return super.clone();
097 }
098
099 /** Create a JCheckBox for this action.
100 * @return JCheckBox for this action
101 */
102 public JCheckBox createCheckBox() {
103 final JCheckBox ret = new JCheckBox(this);
104 buttons.add(new WeakReference<AbstractButton>(ret));
105 return ret;
106 }
107
108 /** Create a JCheckBoxMenuItem.
109 * @return JCheckBoxMenuItem for this action
110 */
111 public JCheckBoxMenuItem createCheckBoxMenuItem() {
112 final JCheckBoxMenuItem ret = new JCheckBoxMenuItem(this);
113 buttons.add(new WeakReference<AbstractButton>(ret));
114 return ret;
115 }
116
117 /** {@inheritDoc}
118 * This implementation checks the type of <var>newValue</var> if the <var>key</var> is {@link #REFLECTION_TARGET} or {@link
119 * #REFLECTION_PROPERTY_NAME}, so you'll know of errors quite soon.
120 * @throws IllegalArgumentException if <var>newValue</var> is of the wrong type
121 */
122 @Override public void putValue(final String key, final Object newValue) throws IllegalArgumentException {
123 if (REFLECTION_PROPERTY_NAME.equals(key)) {
124 if (newValue != null && !(newValue instanceof String)) {
125 throw new IllegalArgumentException("Value for key REFLECTION_PROPERTY_NAME must be of type " + String.class.getName() + " but was " + newValue.getClass().getName());
126 }
127 }
128 super.putValue(key, newValue);
129 }
130
131 /** Update the selected state.
132 * @param selected new selected state
133 */
134 public void setSelected(final boolean selected) {
135 this.selected = selected;
136 for (final Iterator<WeakReference<AbstractButton>> it = buttons.iterator(); it.hasNext();) {
137 final WeakReference<AbstractButton> ref = it.next();
138 final AbstractButton button = ref.get();
139 if (button == null) {
140 it.remove();
141 } else {
142 button.setSelected(selected);
143 }
144 }
145 }
146
147 } // class ToggleAction