View Javadoc

1   /*
2    *  XNap Commons
3    *
4    *  Copyright (C) 2005  Steffen Pingel
5    *
6    *  This library is free software; you can redistribute it and/or
7    *  modify it under the terms of the GNU Lesser General Public
8    *  License as published by the Free Software Foundation; either
9    *  version 2.1 of the License, or (at your option) any later version.
10   *
11   *  This library is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   *  Lesser General Public License for more details.
15   *
16   *  You should have received a copy of the GNU Lesser General Public
17   *  License along with this library; if not, write to the Free Software
18   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   */
20  package org.xnap.commons.gui.action;
21  
22  import java.beans.PropertyChangeEvent;
23  import java.beans.PropertyChangeListener;
24  import javax.swing.AbstractAction;
25  import javax.swing.AbstractButton;
26  import javax.swing.Action;
27  import javax.swing.Icon;
28  import javax.swing.JButton;
29  import javax.swing.JCheckBox;
30  import javax.swing.JCheckBoxMenuItem;
31  import javax.swing.JMenuItem;
32  import javax.swing.JToggleButton;
33  import javax.swing.SwingUtilities;
34  import org.xnap.commons.gui.ToolBarButton;
35  import org.xnap.commons.gui.ToolBarToggleButton;
36  import org.xnap.commons.gui.util.IconHelper;
37  import org.xnap.commons.i18n.I18n;
38  import org.xnap.commons.i18n.I18nFactory;
39  
40  /***
41   * Provides an abstract action. All actions should inherit this class,
42   * since it provides a few convenience methods. 
43   */
44  public abstract class AbstractXNapAction extends AbstractAction {
45  
46      public static final String ICON_FILENAME = "XNapIcon";
47  
48  	public AbstractXNapAction() 
49  	{
50  	}
51  
52  	/***
53  	 * Enables/disables the action in the swing thread by calling 
54  	 * {@link SwingUtilities#invokeLater(java.lang.Runnable)}.
55  	 * @param enabled parameter passed to {@link Action#setEnabled(boolean)}.
56  	 */
57  	public void setEnabledLater(final boolean enabled) 
58  	{
59  		Runnable runner = new Runnable() 
60  			{
61  				public void run()
62  				{
63  					setEnabled(enabled);
64  				}
65  			};
66  		SwingUtilities.invokeLater(runner);
67  	}
68  	
69  	/***
70  	 * Convenience method for translating which can be used by subclasses.
71  	 * <p>
72  	 * This works because subclasses are in their own package, so the right
73  	 * resource bundle can be chosen.
74  	 */
75  	protected String tr(String text)
76  	{
77  		I18n i18n = I18nFactory.getI18n(getClass());
78  		return i18n.tr(text);
79  	}
80  
81  	/***
82  	 * See {@link #tr(String)} and {@link I18n#tr(String, Object[])}.
83  	 */
84  	protected String tr(String text, Object ... objects)
85  	{
86  		I18n i18n = I18nFactory.getI18n(getClass());
87  		return i18n.tr(text, objects);
88  	}
89  	
90  	// TODO comment
91  	public static Icon getIcon(AbstractButton button, String filename)
92  	{
93  		if (button instanceof JCheckBox || button instanceof JCheckBoxMenuItem) {
94  			// icons look ugly with check boxes
95  			return null;
96  		}
97  		else if (button instanceof ToolBarButton || button instanceof ToolBarToggleButton) {
98  			return IconHelper.getToolBarIcon(filename);
99  		}
100 		else if (button instanceof JButton || button instanceof JToggleButton) {
101 			return IconHelper.getButtonIcon(filename);
102 		}
103 		else if (button instanceof JMenuItem) {
104 			return IconHelper.getMenuIcon(filename);
105 		}
106 		return null;
107 	}
108 	
109 	public static void initialize(AbstractButton button, Action action)
110 	{
111 		String filename = (String)action.getValue(AbstractXNapAction.ICON_FILENAME);
112 		// some components like JMenuItem objects require an empty icon for
113 		// alignment reasons if no icon is available, therefore pass some
114 		// crazy string that does not point to a valid icon filename
115 		button.setIcon(getIcon(button, (filename != null) ? filename : "does-not-exist"));
116 
117 		if (action instanceof ToggleAction) {
118 			button.setSelected(((ToggleAction)action).isSelected());
119 		}
120 		
121 		// FIXME will the garbage collecter ever get a chance to clean up 
122 		// button as long as action is around?
123 		// well, no, see source of javax.swing.AbstractActionPropertyChangeListener
124 		action.addPropertyChangeListener(new ActionPropertyListener(button));
125 	}
126 
127 	/***
128 	 * This class is static to allow the garbage collector to finalize the 
129 	 * component although the action is still existant. 
130 	 * 
131 	 * Does this make sense at all? The action still holds a reference to the
132 	 * button through the listener...
133 	 * 
134 	 * @author Steffen Pingel
135 	 */
136 	private static class ActionPropertyListener implements PropertyChangeListener {
137 
138 		private AbstractButton button;
139 
140 		public ActionPropertyListener(AbstractButton button)
141 		{
142 			this.button = button;
143 		}
144 
145 		public void propertyChange(PropertyChangeEvent e) 
146 		{
147 			if (e.getPropertyName().equals(ToggleAction.SELECTED)) {
148 				button.setSelected(((Boolean)e.getNewValue()).booleanValue());
149 			}
150 			else if (e.getPropertyName().equals(AbstractXNapAction.ICON_FILENAME)) {
151 				String filename = (String)e.getNewValue();
152 				button.setIcon(getIcon(button, filename));
153 			}
154 		}
155 	 
156 	}
157 	
158 }