Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/gjt/sp/jedit/ActionSet.java


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  
23  package org.gjt.sp.jedit;
24  
25  import com.microstar.xml.*;
26  import java.io.*;
27  import java.net.URL;
28  import java.util.*;
29  import org.gjt.sp.jedit.gui.InputHandler;
30  import 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,v 1.25 2003/11/16 00:04:07 spestov Exp $
141  * @since jEdit 4.0pre1
142  */
143 public class ActionSet
144 {
145   //{{{ ActionSet constructor
146   /**
147    * Creates a new action set.
148    * @param plugin The plugin
149    * @param cachedActionNames The list of cached action names
150    * @param cachedActionToggleFlags The list of cached action toggle flags
151    * @param uri The actions.xml URI
152    * @since jEdit 4.2pre2
153    */
154   public ActionSet(PluginJAR plugin, String[] cachedActionNames,
155     boolean[] cachedActionToggleFlags, URL uri)
156   {
157     this();
158     this.plugin = plugin;
159     this.uri = uri;
160     if(cachedActionNames != null)
161     {
162       for(int i = 0; i < cachedActionNames.length; i++)
163       {
164         actions.put(cachedActionNames[i],placeholder);
165         jEdit.setTemporaryProperty(cachedActionNames[i]
166           + ".toggle",cachedActionToggleFlags[i]
167           ? "true" : "false");
168       }
169     }
170     loaded = false;
171   } //}}}
172 
173   //{{{ ActionSet constructor
174   /**
175    * Creates a new action set.
176    * @since jEdit 4.0pre1
177    */
178   public ActionSet()
179   {
180     actions = new Hashtable();
181     loaded = true;
182   } //}}}
183 
184   //{{{ ActionSet constructor
185   /**
186    * Creates a new action set.
187    * @param label The label, shown in the shortcuts option pane
188    * @since jEdit 4.0pre1
189    */
190   public ActionSet(String label)
191   {
192     this();
193     this.label = label;
194   } //}}}
195 
196   //{{{ getLabel() method
197   /**
198    * Return the action source label.
199    * @since jEdit 4.0pre1
200    */
201   public String getLabel()
202   {
203     return label;
204   } //}}}
205 
206   //{{{ setLabel() method
207   /**
208    * Sets the action source label.
209    * @param label The label
210    * @since jEdit 4.0pre1
211    */
212   public void setLabel(String label)
213   {
214     this.label = label;
215   } //}}}
216 
217   //{{{ addAction() method
218   /**
219    * Adds an action to the action set.
220    * @param action The action
221    * @since jEdit 4.0pre1
222    */
223   public void addAction(EditAction action)
224   {
225     actions.put(action.getName(),action);
226     if(context != null)
227       context.actionHash.put(action.getName(),this);
228   } //}}}
229 
230   //{{{ removeAction() method
231   /**
232    * Removes an action from the action set.
233    * @param name The action name
234    * @since jEdit 4.0pre1
235    */
236   public void removeAction(String name)
237   {
238     actions.remove(name);
239     if(context != null)
240       context.actionHash.remove(name);
241   } //}}}
242 
243   //{{{ removeAllActions() method
244   /**
245    * Removes all actions from the action set.
246    * @since jEdit 4.0pre1
247    */
248   public void removeAllActions()
249   {
250     if(context != null)
251     {
252       String[] actions = getActionNames();
253       for(int i = 0; i < actions.length; i++)
254       {
255         context.actionHash.remove(actions[i]);
256       }
257     }
258     this.actions.clear();
259   } //}}}
260 
261   //{{{ getAction() method
262   /**
263    * Returns an action with the specified name.<p>
264    *
265    * <b>Deferred loading:</b> this will load the action set if necessary.
266    *
267    * @param name The action name
268    * @since jEdit 4.0pre1
269    */
270   public EditAction getAction(String name)
271   {
272     Object obj = actions.get(name);
273     if(obj == placeholder)
274     {
275       load();
276       obj = actions.get(name);
277       if(obj == placeholder)
278       {
279         Log.log(Log.WARNING,this,"Outdated cache");
280         obj = null;
281       }
282     }
283 
284     return (EditAction)obj;
285   } //}}}
286 
287   //{{{ getActionCount() method
288   /**
289    * Returns the number of actions in the set.
290    * @since jEdit 4.0pre1
291    */
292   public int getActionCount()
293   {
294     return actions.size();
295   } //}}}
296 
297   //{{{ getActionNames() method
298   /**
299    * Returns an array of all action names in this action set.
300    * @since jEdit 4.2pre1
301    */
302   public String[] getActionNames()
303   {
304     String[] retVal = new String[actions.size()];
305     Enumeration enum = actions.keys();
306     int i = 0;
307     while(enum.hasMoreElements())
308     {
309       retVal[i++] = (String)enum.nextElement();
310     }
311     return retVal;
312   } //}}}
313 
314   //{{{ getCacheableActionNames() method
315   /**
316    * Returns an array of all action names in this action set that should
317    * be cached; namely, <code>BeanShellAction</code>s.
318    * @since jEdit 4.2pre1
319    */
320   public String[] getCacheableActionNames()
321   {
322     LinkedList retVal = new LinkedList();
323     Enumeration enum = actions.elements();
324     int i = 0;
325     while(enum.hasMoreElements())
326     {
327       Object obj = enum.nextElement();
328       if(obj == placeholder)
329       {
330         // ??? this should only be called with
331         // fully loaded action set
332         Log.log(Log.WARNING,this,"Action set not up "
333           + "to date");
334       }
335       else if(obj instanceof BeanShellAction)
336         retVal.add(((BeanShellAction)obj).getName());
337     }
338     return (String[])retVal.toArray(new String[retVal.size()]);
339   } //}}}
340 
341   //{{{ getActions() method
342   /**
343    * Returns an array of all actions in this action set.<p>
344    *
345    * <b>Deferred loading:</b> this will load the action set if necessary.
346    *
347    * @since jEdit 4.0pre1
348    */
349   public EditAction[] getActions()
350   {
351     load();
352 
353     EditAction[] retVal = new EditAction[actions.size()];
354     Enumeration enum = actions.elements();
355     int i = 0;
356     while(enum.hasMoreElements())
357     {
358       retVal[i++] = (EditAction)enum.nextElement();
359     }
360     return retVal;
361   } //}}}
362 
363   //{{{ contains() method
364   /**
365    * Returns if this action set contains the specified action.
366    * @param action The action
367    * @since jEdit 4.2pre1
368    */
369   public boolean contains(String action)
370   {
371     return actions.containsKey(action);
372   } //}}}
373 
374   //{{{ size() method
375   /**
376    * Returns the number of actions in this action set.
377    * @since jEdit 4.2pre2
378    */
379   public int size()
380   {
381     return actions.size();
382   } //}}}
383 
384   //{{{ toString() method
385   public String toString()
386   {
387     return label;
388   } //}}}
389 
390   //{{{ initKeyBindings() method
391   /**
392    * Initializes the action set's key bindings.
393    * jEdit calls this method for all registered action sets when the
394    * user changes key bindings in the <b>Global Options</b> dialog box.<p>
395    *
396    * Note if your plugin adds a custom action set to jEdit's collection,
397    * it must also call this method on the action set after adding it.
398    *
399    * @since jEdit 4.2pre1
400    */
401   public void initKeyBindings()
402   {
403     InputHandler inputHandler = jEdit.getInputHandler();
404 
405     Iterator iter = actions.entrySet().iterator();
406     while(iter.hasNext())
407     {
408       Map.Entry entry = (Map.Entry)iter.next();
409       String name = (String)entry.getKey();
410 
411       String shortcut1 = jEdit.getProperty(name + ".shortcut");
412       if(shortcut1 != null)
413         inputHandler.addKeyBinding(shortcut1,name);
414 
415       String shortcut2 = jEdit.getProperty(name + ".shortcut2");
416       if(shortcut2 != null)
417         inputHandler.addKeyBinding(shortcut2,name);
418     }
419   } //}}}
420 
421   //{{{ load() method
422   /**
423    * Forces the action set to be loaded. Plugins and macros should not
424    * call this method.
425    * @since jEdit 4.2pre1
426    */
427   public void load()
428   {
429     if(loaded)
430       return;
431 
432     loaded = true;
433     //actions.clear();
434 
435     Reader stream = null;
436 
437     try
438     {
439       Log.log(Log.DEBUG,this,"Loading actions from " + uri);
440 
441       ActionListHandler ah = new ActionListHandler(uri.toString(),this);
442       stream = new BufferedReader(new InputStreamReader(
443         uri.openStream()));
444       XmlParser parser = new XmlParser();
445       parser.setHandler(ah);
446       parser.parse(null, null, stream);
447     }
448     catch(XmlException xe)
449     {
450       int line = xe.getLine();
451       String message = xe.getMessage();
452       Log.log(Log.ERROR,this,uri + ":" + line
453         + ": " + message);
454     }
455     catch(Exception e)
456     {
457       Log.log(Log.ERROR,uri,e);
458     }
459     finally
460     {
461       try
462       {
463         if(stream != null)
464           stream.close();
465       }
466       catch(IOException io)
467       {
468         Log.log(Log.ERROR,this,io);
469       }
470     }
471   } //}}}
472 
473   //{{{ Package-private members
474   ActionContext context;
475 
476   //{{{ getActionNames() method
477   void getActionNames(ArrayList vec)
478   {
479     Enumeration enum = actions.keys();
480     while(enum.hasMoreElements())
481       vec.add(enum.nextElement());
482   } //}}}
483 
484   //}}}
485 
486   //{{{ Private members
487   private String label;
488   private Hashtable actions;
489   private PluginJAR plugin;
490   private URL uri;
491   private boolean loaded;
492 
493   private static final Object placeholder = new Object();
494 
495   //}}}
496 }