View Javadoc

1   /*
2    *  XNap - A P2P framework and client.
3    *
4    *  See the file AUTHORS for copyright information.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation.
9    *
10   *  This program is distributed in the hope that it will be useful,
11   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   *  GNU General Public License for more details.
14   *
15   *  You should have received a copy of the GNU General Public License
16   *  along with this program; if not, write to the Free Software
17   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   */
19  
20  package org.xnap.gui.action;
21  
22  import java.awt.event.ActionEvent;
23  import java.io.File;
24  import java.io.IOException;
25  import java.util.Arrays;
26  import java.util.Collection;
27  import java.util.Hashtable;
28  import java.util.Iterator;
29  import java.util.LinkedList;
30  
31  import javax.swing.Action;
32  import javax.swing.JComponent;
33  import javax.swing.JPopupMenu;
34  import javax.swing.text.DefaultEditorKit;
35  
36  import org.xnap.XNap;
37  import org.xnap.action.ToggleAction;
38  import org.xnap.gui.StatusBar;
39  import org.xnap.gui.component.XNapCheckBoxMenuItem;
40  import org.xnap.gui.component.XNapMenuItem;
41  import org.xnap.gui.menu.AbstractDynamicMenu;
42  import org.xnap.gui.util.IconHelper;
43  import org.xnap.util.launcher.Player;
44  import org.xnap.util.launcher.PlayerManager;
45  
46  /***
47   * This class provides a set of static actions. 
48   */
49  public class ActionHelper
50  {
51      
52      //--- Constant(s) ---
53  
54      /***
55       * Default clipboard copy action that operates on any 
56       * <code>JTextComponent</code>.
57       */
58      public static Action copyAction;
59      static {
60  		copyAction = new DefaultEditorKit.CopyAction();
61  		copyAction.putValue(Action.NAME, XNap.tr("Copy"));
62  		copyAction.putValue(Action.SHORT_DESCRIPTION, 
63  							XNap.tr("Copies selected text to clipboard."));
64  		copyAction.putValue(IconHelper.XNAP_ICON, "editcopy.png");
65      }
66  
67      /***
68       * Default clipboard cut action that operates on any 
69       * <code>JTextComponent</code>.
70       */
71      public static Action cutAction;
72      static {
73  		cutAction = new DefaultEditorKit.CutAction();
74  		cutAction.putValue(Action.NAME, XNap.tr("Cut"));
75  		cutAction.putValue(Action.SHORT_DESCRIPTION, 
76  						   XNap.tr("Moves selected text to clipboard."));
77  		cutAction.putValue(IconHelper.XNAP_ICON, "editcut.png");
78      }
79  
80      /***
81       * Default clipboard paste action that operates on any 
82       * <code>JTextComponent</code>.
83       */
84      public static Action pasteAction;
85      static {
86  		pasteAction = new DefaultEditorKit.PasteAction();
87  		pasteAction.putValue(Action.NAME, XNap.tr("Paste"));
88  		pasteAction.putValue(Action.SHORT_DESCRIPTION, 
89  							 XNap.tr("Pastes clipboard contents."));
90  		pasteAction.putValue(IconHelper.XNAP_ICON, "editpaste.png");
91      }
92  
93  	public static Action pluginPreferencesDialogAction;
94  	static {
95  		pluginPreferencesDialogAction = new PluginPreferencesDialogAction();
96  		pluginPreferencesDialogAction.setEnabled(false);
97  	}
98      
99      //--- Data field(s) ---
100     
101     //--- Constructor(s) ---
102 
103     //--- Method(s) ---
104 
105 	//      public static boolean shouldEnable(IUser[] users, Class actionClass) 
106 	//      {
107 	//  	boolean isSupported = false;
108 
109 	//  	for (int i = 0; i < users.length; i++) {
110 	//  	    isSupported |= users[i].isActionSupported(actionClass);
111 	//  	}
112 
113 	//  	return isSupported;
114 	//      }
115 
116     /***
117      * Uses the ActionExtractor to extract the common actions from all objects
118      * in the array and adds them to the AbstractDynamicMenu as temporaries.
119 	 *
120      * @return -1, if nothing was added; the number of actions that were added
121      * to <code>jm</code>, otherwise
122 	 */
123     public static int addCommonActions(AbstractDynamicMenu jm, 
124 									   Object[] object, ActionExtractor ae,
125 									   int startIndex)
126     {
127 		Collection actions = getCommonActions(object, ae);
128 		if (actions != null) {
129 			int index = startIndex;
130 			for (Iterator i = actions.iterator(); i.hasNext();) {
131 				Action action = (Action)i.next();
132 				jm.addTemporary(createMenuItem(action), index);
133 				index++;
134 			}
135 			return index - startIndex;
136 		}
137 		return -1;
138     }
139 
140     public static int addCommonActions(AbstractDynamicMenu jm, 
141 									   Object[] object, ActionExtractor ae)
142 	{
143 		return addCommonActions(jm, object, ae, 0);
144 	}
145 
146     /***
147      * Returns a collection of common actions in <code>objects</code>.
148      */
149     public static Collection getCommonActions(Object[] items, 
150 											  ActionExtractor ae)
151     {
152 		if (items != null) {
153 			if (items.length == 1) {
154 				Action[] actions = ae.getActions(items[0]);
155 				if (actions != null) {
156 					return Arrays.asList(actions);
157 				}
158 			}
159 			else {
160 				// maps Action items to ActionContainer items
161 				Hashtable containerByAction = new Hashtable();
162 		    
163 				// create action container
164 				Action[] refActions = ae.getActions(items[0]);
165 				if (refActions != null) {
166 					for (int i = 0; i < refActions.length; i++) {
167 						if (refActions[i] != null) {
168 							containerByAction.put
169 								(refActions[i], 
170 								 new ActionContainer(refActions[i]));
171 						}
172 					}
173 				}
174 		
175 				for (int i = 1; (i < items.length 
176 								 && containerByAction.size() > 0); i++) {
177 					Action[] actions = ae.getActions(items[i]);
178 					if (actions != null && actions.length > 0) {
179 						retainAll(containerByAction, actions);
180 					}
181 					else {
182 						containerByAction.clear();
183 					}
184 				}
185 		
186 				if (containerByAction.size() > 0) {
187 					// restore order of refActions
188 					LinkedList sorted = new LinkedList();
189 					for (int i = 0; i < refActions.length; i++) {
190 						if (refActions[i] != null
191 							&& containerByAction.containsKey(refActions[i])) {
192 
193 							sorted.add(containerByAction.get(refActions[i]));
194 						}
195 					}
196 					return sorted;
197 				}
198 			}
199 		}
200 	
201 		return null;
202     }
203 
204 	/***
205 	 * MenuItem factory.
206 	 */
207 	public static JComponent createMenuItem(Action action) 
208 	{
209 		if (action == null) {
210 			return new JPopupMenu.Separator();
211 		}
212 		else if (action instanceof ToggleAction) {
213 			return new XNapCheckBoxMenuItem((ToggleAction)action);
214 		}
215 		else if (action instanceof SubmenuAction) {
216 			return ((SubmenuAction)action).createMenu();
217 		}
218 		else {
219 			return new XNapMenuItem(action);
220 		}
221 	}
222 	    
223     /***
224      * This is O(n^2);
225      */
226     private static void retainAll(Hashtable containerByAction,
227 								  Action[] actions)
228     {
229 		for (Iterator it = containerByAction.keySet().iterator(); 
230 			 it.hasNext();) {
231 			Object key = it.next();
232 			boolean contained = false;
233 			// searches array for action
234 			for (int i = 0; i < actions.length && !contained; i++) {
235 				if (actions[i] != null
236 					&& key.equals(actions[i])
237 					&& actions[i].equals(key)) {
238 
239 					ActionContainer ac 
240 						= (ActionContainer)containerByAction.get(key);
241 					ac.add(actions[i]);
242 					contained = true;
243 				}
244 			}
245 			if (!contained) {
246 				// action was not found
247 				it.remove();
248 			}
249 		}
250     }
251     
252     public interface ActionExtractor {
253 	
254 		Action[] getActions(Object o);
255 	
256     }
257     
258     /***
259      * Used by the Play...Actions for enqueueing files.
260      *
261      * A thread is used if when array size surpasses 5 elements.
262      *
263      * @param files array of files to be enqueued
264      * @param playFirst play first file
265      */
266     public static void enqueueFiles(final File[] files,
267 									final boolean playFirst)
268     {
269 		if (files.length < 5) {
270 			enqueue(files, playFirst);   
271 		}
272 		else {
273 			Thread t = new Thread("EnqueueThread")
274 				{
275 					public void run()
276 					{
277 						enqueue(files, playFirst);
278 					}
279 				};
280 			t.start();
281 		}
282     }
283 
284     private static void enqueue(File[] files, boolean playFirst)
285     {
286 		Player player = PlayerManager.getInstance().getDefaultPlayer();
287 	
288 		if (player == null) {
289 			StatusBar.setText(XNap.tr("Could not launch player"));
290 			return;
291 		}
292 	
293 		for (int i = 0; i < files.length; i++) {
294 			try {
295 				if (files[i] != null && files[i].length() > 0  
296 					&& player.canPlay(files[i])) {
297 					if (playFirst) {
298 						player.open(files[i]);
299 						playFirst = false;
300 					}
301 					else {
302 						player.enqueue(files[i]);
303 					}
304 				}
305 			}
306 			catch (IOException e) {
307 			}
308 		}
309     }
310     
311     public static boolean perform(Action[] actions, ActionEvent event, Class requiredInterface)
312 	{
313 		if (actions != null) {
314 			for (int i = 0; i < actions.length; i++) {
315 				if (actions[i] != null 
316 					&& actions[i].isEnabled()
317 					&& requiredInterface.isInstance(actions[i])) {
318 
319 					actions[i].actionPerformed(event);
320 					return true;
321 				}
322 			}
323 		}
324 		return false;
325     }
326     
327 }
328