001 /* 002 * $Id: BoundAction.java 3132 2008-12-05 14:34:58Z kschaefe $ 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 javax.swing.*; 025 import javax.swing.event.EventListenerList; 026 import java.awt.event.ActionEvent; 027 import java.awt.event.ActionListener; 028 import java.awt.event.ItemEvent; 029 import java.awt.event.ItemListener; 030 import java.beans.EventHandler; 031 import java.beans.Statement; 032 import java.util.EventListener; 033 import java.util.logging.Level; 034 import java.util.logging.Logger; 035 036 /** 037 * A class that represents the many type of actions that this framework supports. 038 * <p> 039 * The command invocation of this action may be delegated to another action or item state 040 * listener. If there isn't an explicit binding then the command is forwarded to 041 * the TargetManager. 042 * 043 * @author Mark Davidson 044 */ 045 public class BoundAction extends AbstractActionExt { 046 private static final Logger LOG = Logger.getLogger(BoundAction.class 047 .getName()); 048 // Holds the listeners 049 private EventListenerList listeners; 050 051 public BoundAction() { 052 this("BoundAction"); 053 } 054 055 public BoundAction(String name) { 056 super(name); 057 } 058 059 /** 060 * @param name display name of the action 061 * @param command the value of the action command key 062 */ 063 public BoundAction(String name, String command) { 064 super(name, command); 065 } 066 067 public BoundAction(String name, Icon icon) { 068 super(name, icon); 069 } 070 071 /** 072 * @param name display name of the action 073 * @param command the value of the action command key 074 * @param icon icon to display 075 */ 076 public BoundAction(String name, String command, Icon icon) { 077 super(name, command, icon); 078 } 079 080 /** 081 * The callback string will be called to register the action callback. 082 * Note the toggle property must be set if this is a state action before 083 * this method is called. 084 * For example, 085 * <pre> 086 * <exec>com.sun.foo.FubarHandler#handleBar</exec> 087 * </pre> 088 * will register 089 * <pre> 090 * registerCallback(com.sun.foo.FubarHandler(), "handleBar"); 091 * </pre> 092 */ 093 public void setCallback(String callback) { 094 String[] elems = callback.split("#", 2); 095 if (elems.length == 2) { 096 try { 097 Class<?> clz = Class.forName(elems[0]); 098 099 // May throw a security exception in an Applet 100 // context. 101 Object obj = clz.newInstance(); 102 103 registerCallback(obj, elems[1]); 104 } catch (Exception ex) { 105 LOG.fine("ERROR: setCallback(" + callback 106 + ") - " + ex.getMessage()); 107 } 108 } 109 } 110 111 /** 112 * Registers a callback method when the Action corresponding to 113 * the action id is invoked. When a Component that was constructed from the 114 * Action identified by the action id invokes actionPerformed then the method 115 * named will be invoked on the handler Object. 116 * <p> 117 * If the Action represented by the action id is a StateChangeAction, then 118 * the method passed should take an int as an argument. The value of 119 * getStateChange() on the ItemEvent object will be passed as the parameter. 120 * 121 * @param handler the object which will be perform the action 122 * @param method the name of the method on the handler which will be called. 123 */ 124 public void registerCallback(Object handler, String method) { 125 if (isStateAction()) { 126 // Create a handler for toogle type actions. 127 addItemListener(new BooleanInvocationHandler(handler, method)); 128 } else { 129 // Create a new ActionListener using the dynamic proxy api. 130 addActionListener((ActionListener)EventHandler.create(ActionListener.class, 131 handler, method)); 132 } 133 } 134 135 /** 136 * The callback for the toggle/state changed action that invokes a method 137 * with a boolean argument on a target. 138 * 139 * TODO: should reimplement this class as something that can be persistable. 140 */ 141 private class BooleanInvocationHandler implements ItemListener { 142 143 private Statement falseStatement; 144 private Statement trueStatement; 145 146 public BooleanInvocationHandler(Object target, String methodName) { 147 // Create the true and false statements. 148 falseStatement = new Statement(target, methodName, 149 new Object[] { Boolean.FALSE }); 150 151 trueStatement = new Statement(target, methodName, 152 new Object[] { Boolean.TRUE }); 153 } 154 155 public void itemStateChanged(ItemEvent evt) { 156 Statement statement = (evt.getStateChange() == ItemEvent.DESELECTED) ? falseStatement 157 : trueStatement; 158 159 try { 160 statement.execute(); 161 } catch (Exception ex) { 162 LOG.log(Level.FINE, 163 "Couldn't execute boolean method via Statement " 164 + statement, ex); 165 } 166 } 167 } 168 169 // Listener registration... 170 171 private <T extends EventListener> void addListener(Class<T> clz, T listener) { 172 if (listeners == null) { 173 listeners = new EventListenerList(); 174 } 175 listeners.add(clz, listener); 176 } 177 178 private <T extends EventListener> void removeListener(Class<T> clz, T listener) { 179 if (listeners != null) { 180 listeners.remove(clz, listener); 181 } 182 } 183 184 private EventListener[] getListeners(Class<? extends EventListener> clz) { 185 if (listeners == null) { 186 return null; 187 } 188 return listeners.getListeners(clz); 189 } 190 191 /** 192 * Add an action listener which will be invoked when this action is invoked. 193 */ 194 public void addActionListener(ActionListener listener) { 195 addListener(ActionListener.class, listener); 196 } 197 198 public void removeActionListener(ActionListener listener) { 199 removeListener(ActionListener.class, listener); 200 } 201 202 public ActionListener[] getActionListeners() { 203 return (ActionListener[])getListeners(ActionListener.class); 204 } 205 206 /** 207 * Add an item listener which will be invoked for toggle actions. 208 */ 209 public void addItemListener(ItemListener listener) { 210 addListener(ItemListener.class, listener); 211 } 212 213 public void removeItemListener(ItemListener listener) { 214 removeListener(ItemListener.class, listener); 215 } 216 217 public ItemListener[] getItemListeners() { 218 return (ItemListener[])getListeners(ItemListener.class); 219 } 220 221 // Callbacks... 222 223 /** 224 * Callback for command actions. 225 */ 226 public void actionPerformed(ActionEvent evt) { 227 ActionListener[] alist = getActionListeners(); 228 if (alist != null) { 229 for (int i = 0 ; i < alist.length; i++) { 230 alist[i].actionPerformed(evt); 231 } 232 } 233 } 234 235 /** 236 * Callback for toggle actions. 237 */ 238 public void itemStateChanged(ItemEvent evt) { 239 // Update all objects that share this item 240 boolean newValue; 241 boolean oldValue = isSelected(); 242 243 newValue = evt.getStateChange() == ItemEvent.SELECTED; 244 245 if (oldValue != newValue) { 246 setSelected(newValue); 247 248 // Forward the event to the delgate for handling. 249 ItemListener[] ilist = getItemListeners(); 250 if (ilist != null) { 251 for (int i = 0; i < ilist.length; i++) { 252 ilist[i].itemStateChanged(evt); 253 } 254 } 255 } 256 } 257 258 }