001 /* 002 * $Id: BoundAction.java,v 1.4 2005/10/26 11:44:31 kleopatra Exp $ 003 * 004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle, 005 * Santa Clara, California 95054, U.S.A. All rights reserved. 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this library; if not, write to the Free Software 019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 020 */ 021 022 package org.jdesktop.swingx.action; 023 024 import java.awt.event.ActionEvent; 025 import java.awt.event.ActionListener; 026 import java.awt.event.ItemEvent; 027 import java.awt.event.ItemListener; 028 import java.beans.EventHandler; 029 import java.beans.Statement; 030 import java.util.EventListener; 031 import java.util.logging.Level; 032 import java.util.logging.Logger; 033 034 import javax.swing.Icon; 035 import javax.swing.event.EventListenerList; 036 037 /** 038 * A class that represents the many type of actions that this framework supports. 039 * <p> 040 * The command invocation of this action may be delegated to another action or item state 041 * listener. If there isn't an explicit binding then the command is forwarded to 042 * the TargetManager. 043 * 044 * @author Mark Davidson 045 */ 046 public class BoundAction extends AbstractActionExt { 047 private static final Logger LOG = Logger.getLogger(BoundAction.class 048 .getName()); 049 // Holds the listeners 050 private EventListenerList listeners; 051 052 public BoundAction() { 053 this("BoundAction"); 054 } 055 056 public BoundAction(String name) { 057 super(name); 058 } 059 060 /** 061 * @param name display name of the action 062 * @param command the value of the action command key 063 */ 064 public BoundAction(String name, String command) { 065 super(name, command); 066 } 067 068 public BoundAction(String name, Icon icon) { 069 super(name, icon); 070 } 071 072 /** 073 * @param name display name of the action 074 * @param command the value of the action command key 075 * @param icon icon to display 076 */ 077 public BoundAction(String name, String command, Icon icon) { 078 super(name, command, icon); 079 } 080 081 /** 082 * The callback string will be called to register the action callback. 083 * Note the toggle property must be set if this is a state action before 084 * this method is called. 085 * For example, 086 * <pre> 087 * <exec>com.sun.foo.FubarHandler#handleBar</exec> 088 * </pre> 089 * will register 090 * <pre> 091 * registerCallback(com.sun.foo.FubarHandler(), "handleBar"); 092 * </pre> 093 */ 094 public void setCallback(String callback) { 095 String[] elems = callback.split("#", 2); 096 if (elems.length == 2) { 097 try { 098 Class clz = Class.forName(elems[0]); 099 100 // May throw a security exception in an Applet 101 // context. 102 Object obj = clz.newInstance(); 103 104 registerCallback(obj, elems[1]); 105 } catch (Exception ex) { 106 LOG.fine("ERROR: setCallback(" + callback 107 + ") - " + ex.getMessage()); 108 } 109 } 110 } 111 112 /** 113 * Registers a callback method when the Action corresponding to 114 * the action id is invoked. When a Component that was constructed from the 115 * Action identified by the action id invokes actionPerformed then the method 116 * named will be invoked on the handler Object. 117 * <p> 118 * If the Action represented by the action id is a StateChangeAction, then 119 * the method passed should take an int as an argument. The value of 120 * getStateChange() on the ItemEvent object will be passed as the parameter. 121 * 122 * @param handler the object which will be perform the action 123 * @param method the name of the method on the handler which will be called. 124 */ 125 public void registerCallback(Object handler, String method) { 126 if (isStateAction()) { 127 // Create a handler for toogle type actions. 128 addItemListener(new BooleanInvocationHandler(handler, method)); 129 } else { 130 // Create a new ActionListener using the dynamic proxy api. 131 addActionListener((ActionListener)EventHandler.create(ActionListener.class, 132 handler, method)); 133 } 134 } 135 136 /** 137 * The callback for the toggle/state changed action that invokes a method 138 * with a boolean argument on a target. 139 * 140 * TODO: should reimplement this class as something that can be persistable. 141 */ 142 private class BooleanInvocationHandler implements ItemListener { 143 144 private Statement falseStatement; 145 private Statement trueStatement; 146 147 public BooleanInvocationHandler(Object target, String methodName) { 148 // Create the true and false statements. 149 falseStatement = new Statement(target, methodName, 150 new Object[] { Boolean.FALSE }); 151 152 trueStatement = new Statement(target, methodName, 153 new Object[] { Boolean.TRUE }); 154 } 155 156 public void itemStateChanged(ItemEvent evt) { 157 Statement statement = (evt.getStateChange() == ItemEvent.DESELECTED) ? falseStatement 158 : trueStatement; 159 160 try { 161 statement.execute(); 162 } catch (Exception ex) { 163 LOG.log(Level.FINE, 164 "Couldn't execute boolean method via Statement " 165 + statement, ex); 166 } 167 } 168 } 169 170 // Listener registration... 171 172 private void addListener(Class clz, EventListener listener) { 173 if (listeners == null) { 174 listeners = new EventListenerList(); 175 } 176 listeners.add(clz, listener); 177 } 178 179 private void removeListener(Class clz, EventListener listener) { 180 if (listeners != null) { 181 listeners.remove(clz, listener); 182 } 183 } 184 185 private EventListener[] getListeners(Class clz) { 186 if (listeners == null) { 187 return null; 188 } 189 return listeners.getListeners(clz); 190 } 191 192 /** 193 * Add an action listener which will be invoked when this action is invoked. 194 */ 195 public void addActionListener(ActionListener listener) { 196 addListener(ActionListener.class, listener); 197 } 198 199 public void removeActionListener(ActionListener listener) { 200 removeListener(ActionListener.class, listener); 201 } 202 203 public ActionListener[] getActionListeners() { 204 return (ActionListener[])getListeners(ActionListener.class); 205 } 206 207 /** 208 * Add an item listener which will be invoked for toggle actions. 209 */ 210 public void addItemListener(ItemListener listener) { 211 addListener(ItemListener.class, listener); 212 } 213 214 public void removeItemListener(ItemListener listener) { 215 removeListener(ItemListener.class, listener); 216 } 217 218 public ItemListener[] getItemListeners() { 219 return (ItemListener[])getListeners(ItemListener.class); 220 } 221 222 // Callbacks... 223 224 /** 225 * Callback for command actions. 226 */ 227 public void actionPerformed(ActionEvent evt) { 228 ActionListener[] alist = getActionListeners(); 229 if (alist != null) { 230 for (int i = 0 ; i < alist.length; i++) { 231 alist[i].actionPerformed(evt); 232 } 233 } 234 } 235 236 /** 237 * Callback for toggle actions. 238 */ 239 public void itemStateChanged(ItemEvent evt) { 240 // Update all objects that share this item 241 boolean newValue; 242 boolean oldValue = isSelected(); 243 244 if (evt.getStateChange() == ItemEvent.SELECTED) { 245 newValue = true; 246 } else { 247 newValue = false; 248 } 249 250 if (oldValue != newValue) { 251 setSelected(newValue); 252 253 // Forward the event to the delgate for handling. 254 ItemListener[] ilist = getItemListeners(); 255 if (ilist != null) { 256 for (int i = 0; i < ilist.length; i++) { 257 ilist[i].itemStateChanged(evt); 258 } 259 } 260 } 261 } 262 263 }