PageRenderTime 569ms CodeModel.GetById 102ms app.highlight 20ms RepoModel.GetById 443ms app.codeStats 0ms

/jEdit/tags/jedit-4-3-pre5/org/gjt/sp/jedit/ActionSet.java

#
Java | 493 lines | 201 code | 36 blank | 256 comment | 25 complexity | c03b2c7bc93198af81dddbf7aaabbfd6 MD5 | raw file
  1/*
  2 * ActionSet.java - A set of actions
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 2001, 2003 Slava Pestov
  7 *
  8 * This program is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU General Public License
 10 * as published by the Free Software Foundation; either version 2
 11 * of the License, or any later version.
 12 *
 13 * This program is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 16 * GNU General Public License for more details.
 17 *
 18 * You should have received a copy of the GNU General Public License
 19 * along with this program; if not, write to the Free Software
 20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 21 */
 22
 23package org.gjt.sp.jedit;
 24
 25import java.io.*;
 26import java.net.URL;
 27import java.util.*;
 28
 29import org.gjt.sp.jedit.gui.InputHandler;
 30import org.gjt.sp.util.Log;
 31
 32/**
 33 * A set of actions, either loaded from an XML file, or constructed at runtime
 34 * by a plugin.<p>
 35 *
 36 * <h3>Action sets loaded from XML files</h3>
 37 *
 38 * Action sets are read from these files inside the plugin JAR:
 39 * <ul>
 40 * <li><code>actions.xml</code> - actions made available for use in jEdit views,
 41 * including the view's <b>Plugins</b> menu, the tool bar, etc.</li>
 42 * <li><code>browser.actions.xml</code> - actions for the file system browser's
 43 * <b>Plugins</b> menu.</li>
 44 * </ul>
 45 *
 46 * An action definition file has the following form:
 47 *
 48 * <pre>&lt;?xml version="1.0"?&gt;
 49 *&lt;!DOCTYPE ACTIONS SYSTEM "actions.dtd"&gt;
 50 *&lt;ACTIONS&gt;
 51 *    &lt;ACTION NAME="some-action"&gt;
 52 *        &lt;CODE&gt;
 53 *            // BeanShell code evaluated when the action is invoked
 54 *        &lt;/CODE&gt;
 55 *    &lt;/ACTION&gt;
 56 *    &lt;ACTION NAME="some-toggle-action"&gt;
 57 *        &lt;CODE&gt;
 58 *            // BeanShell code evaluated when the action is invoked
 59 *        &lt;/CODE&gt;
 60 *        &lt;IS_SELECTED&gt;
 61 *            // BeanShell code that should evaluate to true or false
 62 *        &lt;/IS_SELECTED&gt;
 63 *    &lt;/ACTION&gt;
 64 *&lt;/ACTIONS&gt;</pre>
 65 *
 66 * The following elements are valid:
 67 *
 68 * <ul>
 69 * <li>
 70 * <code>ACTIONS</code> is the top-level element and refers
 71 * to the set of actions used by the plugin.
 72 * </li>
 73 * <li>
 74 * An <code>ACTION</code> contains the data for a particular action.
 75 * It has three attributes: a required <code>NAME</code>;
 76 * an optional <code>NO_REPEAT</code>, which is a flag
 77 * indicating whether the action should not be repeated with the
 78 * <b>C+ENTER</b> command; and an optional
 79 * <code>NO_RECORD</code> which is a a flag indicating whether the
 80 * action should be recorded if it is invoked while the user is recording a
 81 * macro. The two flag attributes
 82 * can have two possible values, "TRUE" or
 83 * "FALSE". In both cases, "FALSE" is the
 84 * default if the attribute is not specified.
 85 * </li>
 86 * <li>
 87 * An <code>ACTION</code> can have two child elements
 88 * within it: a required <code>CODE</code> element which
 89 * specifies the
 90 * BeanShell code that will be executed when the action is invoked,
 91 * and an optional <code>IS_SELECTED</code> element, used for
 92 * checkbox
 93 * menu items.  The <code>IS_SELECTED</code> element contains
 94 * BeanShell code that returns a boolean flag that will
 95 * determine the state of the checkbox.
 96 * </li>
 97 * </ul>
 98 *
 99 * Each action must have a property <code><i>name</i>.label</code> containing
100 * the action's menu item label.
101 *
102 * <h3>View actions</h3>
103 *
104 * Actions defined in <code>actions.xml</code> can be added to the view's
105 * <b>Plugins</b> menu; see {@link EditPlugin}.
106 * The action code may use any standard predefined
107 * BeanShell variable; see {@link BeanShell}.
108 *
109 * <h3>File system browser actions</h3>
110 *
111 * Actions defined in <code>actions.xml</code> can be added to the file
112 * system browser's <b>Plugins</b> menu; see {@link EditPlugin}.
113 * The action code may use any standard predefined
114 * BeanShell variable, in addition to a variable <code>browser</code> which
115 * contains a reference to the current
116 * {@link org.gjt.sp.jedit.browser.VFSBrowser} instance.<p>
117 *
118 * File system browser actions should not define
119 * <code>&lt;IS_SELECTED&gt;</code> blocks.
120 *
121 * <h3>Custom action sets</h3>
122 *
123 * Call {@link jEdit#addActionSet(ActionSet)} to add a custom action set to
124 * jEdit's action context. You must also call {@link #initKeyBindings()} for new
125 * action sets. Don't forget to call {@link jEdit#removeActionSet(ActionSet)}
126 * before your plugin is unloaded, too.
127 *
128 * @see jEdit#getActionContext()
129 * @see org.gjt.sp.jedit.browser.VFSBrowser#getActionContext()
130 * @see ActionContext#getActionNames()
131 * @see ActionContext#getAction(String)
132 * @see jEdit#addActionSet(ActionSet)
133 * @see jEdit#removeActionSet(ActionSet)
134 * @see PluginJAR#getActionSet()
135 * @see BeanShell
136 * @see View
137 *
138 * @author Slava Pestov
139 * @author John Gellene (API documentation)
140 * @version $Id: ActionSet.java 5443 2006-06-18 18:51:40Z vanza $
141 * @since jEdit 4.0pre1
142 */
143public class ActionSet
144{
145	//{{{ ActionSet constructor
146	/**
147	 * Creates a new action set.
148	 * @since jEdit 4.0pre1
149	 */
150	public ActionSet()
151	{
152		actions = new Hashtable();
153		loaded = true;
154		label = "<no label set; plugin bug>";
155	} //}}}
156
157	//{{{ ActionSet constructor
158	/**
159	 * Creates a new action set.
160	 * @param plugin The plugin
161	 * @param cachedActionNames The list of cached action names
162	 * @param cachedActionToggleFlags The list of cached action toggle flags
163	 * @param uri The actions.xml URI
164	 * @since jEdit 4.2pre2
165	 */
166	public ActionSet(PluginJAR plugin, String[] cachedActionNames,
167		boolean[] cachedActionToggleFlags, URL uri)
168	{
169		this();
170		this.plugin = plugin;
171		this.uri = uri;
172		if(cachedActionNames != null)
173		{
174			for(int i = 0; i < cachedActionNames.length; i++)
175			{
176				actions.put(cachedActionNames[i],placeholder);
177				jEdit.setTemporaryProperty(cachedActionNames[i]
178					+ ".toggle",cachedActionToggleFlags[i]
179					? "true" : "false");
180			}
181		}
182		loaded = false;
183	} //}}}
184
185	//{{{ ActionSet constructor
186	/**
187	 * Creates a new action set.
188	 * @param label The label, shown in the shortcuts option pane
189	 * @since jEdit 4.0pre1
190	 */
191	public ActionSet(String label)
192	{
193		this();
194		setLabel(label);
195	} //}}}
196
197	//{{{ getLabel() method
198	/**
199	 * Return the action source label.
200	 * @since jEdit 4.0pre1
201	 */
202	public String getLabel()
203	{
204		return label;
205	} //}}}
206
207	//{{{ setLabel() method
208	/**
209	 * Sets the action source label.
210	 * @param label The label
211	 * @since jEdit 4.0pre1
212	 */
213	public void setLabel(String label)
214	{
215		if(label == null)
216			throw new NullPointerException();
217		this.label = label;
218	} //}}}
219
220	//{{{ getPluginJAR() method
221	/**
222	 * Return the plugin this action set was loaded from, or null.
223	 * @since jEdit 4.2pre13
224	 */
225	public PluginJAR getPluginJAR()
226	{
227		return plugin;
228	} //}}}
229
230	//{{{ addAction() method
231	/**
232	 * Adds an action to the action set.
233	 * @param action The action
234	 * @since jEdit 4.0pre1
235	 */
236	public void addAction(EditAction action)
237	{
238		actions.put(action.getName(),action);
239		if(context != null)
240		{
241			context.actionNames = null;
242			context.actionHash.put(action.getName(),this);
243		}
244	} //}}}
245
246	//{{{ removeAction() method
247	/**
248	 * Removes an action from the action set.
249	 * @param name The action name
250	 * @since jEdit 4.0pre1
251	 */
252	public void removeAction(String name)
253	{
254		actions.remove(name);
255		if(context != null)
256		{
257			context.actionNames = null;
258			context.actionHash.remove(name);
259		}
260	} //}}}
261
262	//{{{ removeAllActions() method
263	/**
264	 * Removes all actions from the action set.
265	 * @since jEdit 4.0pre1
266	 */
267	public void removeAllActions()
268	{
269		if(context != null)
270		{
271			context.actionNames = null;
272			String[] actions = getActionNames();
273			for(int i = 0; i < actions.length; i++)
274			{
275				context.actionHash.remove(actions[i]);
276			}
277		}
278		this.actions.clear();
279	} //}}}
280
281	//{{{ getAction() method
282	/**
283	 * Returns an action with the specified name.<p>
284	 *
285	 * <b>Deferred loading:</b> this will load the action set if necessary.
286	 *
287	 * @param name The action name
288	 * @since jEdit 4.0pre1
289	 */
290	public EditAction getAction(String name)
291	{
292		Object obj = actions.get(name);
293		if(obj == placeholder)
294		{
295			load();
296			obj = actions.get(name);
297			if(obj == placeholder)
298			{
299				Log.log(Log.WARNING,this,"Outdated cache");
300				obj = null;
301			}
302		}
303
304		return (EditAction)obj;
305	} //}}}
306
307	//{{{ getActionCount() method
308	/**
309	 * Returns the number of actions in the set.
310	 * @since jEdit 4.0pre1
311	 */
312	public int getActionCount()
313	{
314		return actions.size();
315	} //}}}
316
317	//{{{ getActionNames() method
318	/**
319	 * Returns an array of all action names in this action set.
320	 * @since jEdit 4.2pre1
321	 */
322	public String[] getActionNames()
323	{
324		String[] retVal = new String[actions.size()];
325		Enumeration e = actions.keys();
326		int i = 0;
327		while(e.hasMoreElements())
328		{
329			retVal[i++] = (String)e.nextElement();
330		}
331		return retVal;
332	} //}}}
333
334	//{{{ getCacheableActionNames() method
335	/**
336	 * Returns an array of all action names in this action set that should
337	 * be cached; namely, <code>BeanShellAction</code>s.
338	 * @since jEdit 4.2pre1
339	 */
340	public String[] getCacheableActionNames()
341	{
342		LinkedList retVal = new LinkedList();
343		Enumeration e = actions.elements();
344		while(e.hasMoreElements())
345		{
346			Object obj = e.nextElement();
347			if(obj == placeholder)
348			{
349				// ??? this should only be called with
350				// fully loaded action set
351				Log.log(Log.WARNING,this,"Action set not up "
352					+ "to date");
353			}
354			else if(obj instanceof BeanShellAction)
355				retVal.add(((BeanShellAction)obj).getName());
356		}
357		return (String[])retVal.toArray(new String[retVal.size()]);
358	} //}}}
359
360	//{{{ getActions() method
361	/**
362	 * Returns an array of all actions in this action set.<p>
363	 *
364	 * <b>Deferred loading:</b> this will load the action set if necessary.
365	 *
366	 * @since jEdit 4.0pre1
367	 */
368	public EditAction[] getActions()
369	{
370		load();
371
372		EditAction[] retVal = new EditAction[actions.size()];
373		Enumeration e = actions.elements();
374		int i = 0;
375		while(e.hasMoreElements())
376		{
377			retVal[i++] = (EditAction)e.nextElement();
378		}
379		return retVal;
380	} //}}}
381
382	//{{{ contains() method
383	/**
384	 * Returns if this action set contains the specified action.
385	 * @param action The action
386	 * @since jEdit 4.2pre1
387	 */
388	public boolean contains(String action)
389	{
390		boolean retval = actions.containsKey(action);
391		return retval;
392//		return actions.containsKey(action);
393	} //}}}
394
395	//{{{ size() method
396	/**
397	 * Returns the number of actions in this action set.
398	 * @since jEdit 4.2pre2
399	 */
400	public int size()
401	{
402		return actions.size();
403	} //}}}
404
405	//{{{ toString() method
406	public String toString()
407	{
408		return label;
409	} //}}}
410
411	//{{{ initKeyBindings() method
412	/**
413	 * Initializes the action set's key bindings.
414	 * jEdit calls this method for all registered action sets when the
415	 * user changes key bindings in the <b>Global Options</b> dialog box.<p>
416	 *
417	 * Note if your plugin adds a custom action set to jEdit's collection,
418	 * it must also call this method on the action set after adding it.
419	 *
420	 * @since jEdit 4.2pre1
421	 */
422	public void initKeyBindings()
423	{
424		InputHandler inputHandler = jEdit.getInputHandler();
425
426		Iterator iter = actions.entrySet().iterator();
427		while(iter.hasNext())
428		{
429			Map.Entry entry = (Map.Entry)iter.next();
430			String name = (String)entry.getKey();
431
432			String shortcut1 = jEdit.getProperty(name + ".shortcut");
433			if(shortcut1 != null)
434				inputHandler.addKeyBinding(shortcut1,name);
435
436			String shortcut2 = jEdit.getProperty(name + ".shortcut2");
437			if(shortcut2 != null)
438				inputHandler.addKeyBinding(shortcut2,name);
439		}
440	} //}}}
441
442	//{{{ load() method
443	/**
444	 * Forces the action set to be loaded. Plugins and macros should not
445	 * call this method.
446	 * @since jEdit 4.2pre1
447	 */
448	public void load()
449	{
450		if(loaded)
451			return;
452
453		loaded = true;
454		//actions.clear();
455
456		Reader stream = null;
457
458		try
459		{
460			Log.log(Log.DEBUG,this,"Loading actions from " + uri);
461			ActionListHandler ah = new ActionListHandler(uri.toString(),this);
462			MiscUtilities.parseXML(uri.openStream(), ah);
463		}
464		catch(IOException e)
465		{
466			Log.log(Log.ERROR,uri,e);
467		}
468	} //}}}
469
470	//{{{ Package-private members
471	ActionContext context;
472
473	//{{{ getActionNames() method
474	void getActionNames(List vec)
475	{
476		Enumeration e = actions.keys();
477		while(e.hasMoreElements())
478			vec.add(e.nextElement());
479	} //}}}
480
481	//}}}
482
483	//{{{ Private members
484	private String label;
485	private Hashtable actions;
486	private PluginJAR plugin;
487	private URL uri;
488	private boolean loaded;
489
490	private static final Object placeholder = new Object();
491
492	//}}}
493}