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

Quick Search    Search Deep

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


1   /*
2    * Macros.java - Macro manager
3    * :tabSize=8:indentSize=8:noTabs=false:
4    * :folding=explicit:collapseFolds=1:
5    *
6    * Copyright (C) 1999, 2003 Slava Pestov
7    * Portions copyright (C) 2002 mike dillon
8    *
9    * This program is free software; you can redistribute it and/or
10   * modify it under the terms of the GNU General Public License
11   * as published by the Free Software Foundation; either version 2
12   * of the License, or any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Public License for more details.
18   *
19   * You should have received a copy of the GNU General Public License
20   * along with this program; if not, write to the Free Software
21   * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22   */
23  
24  package org.gjt.sp.jedit;
25  
26  //{{{ Imports
27  import gnu.regexp.RE;
28  import javax.swing.*;
29  import java.awt.*;
30  import java.io.*;
31  import java.util.*;
32  import org.gjt.sp.jedit.msg.*;
33  import org.gjt.sp.util.Log;
34  //}}}
35  
36  /**
37   * This class records and runs macros.<p>
38   *
39   * It also contains a few methods useful for displaying output messages
40   * or obtaining input from a macro:
41   *
42   * <ul>
43   * <li>{@link #confirm(Component,String,int)}</li>
44   * <li>{@link #confirm(Component,String,int,int)}</li>
45   * <li>{@link #error(Component,String)}</li>
46   * <li>{@link #input(Component,String)}</li>
47   * <li>{@link #input(Component,String,String)}</li>
48   * <li>{@link #message(Component,String)}</li>
49   * </ul>
50   *
51   * Note that plugins should not use the above methods. Call
52   * the methods in the {@link GUIUtilities} class instead.
53   *
54   * @author Slava Pestov
55   * @version $Id: Macros.java,v 1.37 2003/08/20 20:23:53 spestov Exp $
56   */
57  public class Macros
58  {
59    //{{{ showRunScriptDialog() method
60    /**
61     * Prompts for one or more files to run as macros
62     * @param view The view
63     * @since jEdit 4.0pre7
64     */
65    public static void showRunScriptDialog(View view)
66    {
67      String[] paths = GUIUtilities.showVFSFileDialog(view,
68        null,JFileChooser.OPEN_DIALOG,true);
69      if(paths != null)
70      {
71        Buffer buffer = view.getBuffer();
72        try
73        {
74          buffer.beginCompoundEdit();
75  
76  file_loop:      for(int i = 0; i < paths.length; i++)
77            runScript(view,paths[i],false);
78        }
79        finally
80        {
81          buffer.endCompoundEdit();
82        }
83      }
84    } //}}}
85  
86    //{{{ runScript() method
87    /**
88     * Runs the specified script.
89     * Unlike the {@link BeanShell#runScript(View,String,Reader,boolean)}
90     * method, this method can run scripts supported
91     * by any registered macro handler.
92     * @param view The view
93     * @param path The VFS path of the script
94     * @param ignoreUnknown If true, then unknown file types will be
95     * ignored; otherwise, a warning message will be printed and they will
96     * be evaluated as BeanShell scripts.
97     *
98     * @since jEdit 4.1pre2
99     */
100   public static void runScript(View view, String path, boolean ignoreUnknown)
101   {
102     Handler handler = getHandlerForPathName(path);
103     if(handler != null)
104     {
105       try
106       {
107         Macro newMacro = handler.createMacro(
108           MiscUtilities.getFileName(path), path);
109         newMacro.invoke(view);
110       }
111       catch (Exception e)
112       {
113         Log.log(Log.ERROR, Macros.class, e);
114         return;
115       }
116       return;
117     }
118 
119     // only executed if above loop falls
120     // through, ie there is no handler for
121     // this file
122     if(ignoreUnknown)
123     {
124       Log.log(Log.NOTICE,Macros.class,path +
125         ": Cannot find a suitable macro handler");
126     }
127     else
128     {
129       Log.log(Log.ERROR,Macros.class,path +
130         ": Cannot find a suitable macro handler, "
131         + "assuming BeanShell");
132       getHandler("beanshell").createMacro(
133         path,path).invoke(view);
134     }
135   } //}}}
136 
137   //{{{ message() method
138   /**
139    * Utility method that can be used to display a message dialog in a macro.
140    * @param comp The component to show the dialog on behalf of, this
141    * will usually be a view instance
142    * @param message The message
143    * @since jEdit 2.7pre2
144    */
145   public static void message(Component comp, String message)
146   {
147     GUIUtilities.hideSplashScreen();
148 
149     JOptionPane.showMessageDialog(comp,message,
150       jEdit.getProperty("macro-message.title"),
151       JOptionPane.INFORMATION_MESSAGE);
152   } //}}}
153 
154   //{{{ error() method
155   /**
156    * Utility method that can be used to display an error dialog in a macro.
157    * @param comp The component to show the dialog on behalf of, this
158    * will usually be a view instance
159    * @param message The message
160    * @since jEdit 2.7pre2
161    */
162   public static void error(Component comp, String message)
163   {
164     GUIUtilities.hideSplashScreen();
165 
166     JOptionPane.showMessageDialog(comp,message,
167       jEdit.getProperty("macro-message.title"),
168       JOptionPane.ERROR_MESSAGE);
169   } //}}}
170 
171   //{{{ input() method
172   /**
173    * Utility method that can be used to prompt for input in a macro.
174    * @param comp The component to show the dialog on behalf of, this
175    * will usually be a view instance
176    * @param prompt The prompt string
177    * @since jEdit 2.7pre2
178    */
179   public static String input(Component comp, String prompt)
180   {
181     GUIUtilities.hideSplashScreen();
182 
183     return input(comp,prompt,null);
184   } //}}}
185 
186   //{{{ input() method
187   /**
188    * Utility method that can be used to prompt for input in a macro.
189    * @param comp The component to show the dialog on behalf of, this
190    * will usually be a view instance
191    * @param prompt The prompt string
192    * @since jEdit 3.1final
193    */
194   public static String input(Component comp, String prompt, String defaultValue)
195   {
196     GUIUtilities.hideSplashScreen();
197 
198     return (String)JOptionPane.showInputDialog(comp,prompt,
199       jEdit.getProperty("macro-input.title"),
200       JOptionPane.QUESTION_MESSAGE,null,null,defaultValue);
201   } //}}}
202 
203   //{{{ confirm() method
204   /**
205    * Utility method that can be used to ask for confirmation in a macro.
206    * @param comp The component to show the dialog on behalf of, this
207    * will usually be a view instance
208    * @param prompt The prompt string
209    * @param buttons The buttons to display - for example,
210    * JOptionPane.YES_NO_CANCEL_OPTION
211    * @since jEdit 4.0pre2
212    */
213   public static int confirm(Component comp, String prompt, int buttons)
214   {
215     GUIUtilities.hideSplashScreen();
216 
217     return JOptionPane.showConfirmDialog(comp,prompt,
218       jEdit.getProperty("macro-confirm.title"),buttons,
219       JOptionPane.QUESTION_MESSAGE);
220   } //}}}
221 
222   //{{{ confirm() method
223   /**
224    * Utility method that can be used to ask for confirmation in a macro.
225    * @param comp The component to show the dialog on behalf of, this
226    * will usually be a view instance
227    * @param prompt The prompt string
228    * @param buttons The buttons to display - for example,
229    * JOptionPane.YES_NO_CANCEL_OPTION
230    * @param type The dialog type - for example,
231    * JOptionPane.WARNING_MESSAGE
232    */
233   public static int confirm(Component comp, String prompt, int buttons, int type)
234   {
235     GUIUtilities.hideSplashScreen();
236 
237     return JOptionPane.showConfirmDialog(comp,prompt,
238       jEdit.getProperty("macro-confirm.title"),buttons,type);
239   } //}}}
240 
241   //{{{ loadMacros() method
242   /**
243    * Rebuilds the macros list, and sends a MacrosChanged message
244    * (views update their Macros menu upon receiving it)
245    * @since jEdit 2.2pre4
246    */
247   public static void loadMacros()
248   {
249     macroActionSet.removeAllActions();
250     macroHierarchy.removeAllElements();
251     macroHash.clear();
252 
253     if(jEdit.getJEditHome() != null)
254     {
255       systemMacroPath = MiscUtilities.constructPath(
256         jEdit.getJEditHome(),"macros");
257       loadMacros(macroHierarchy,"",new File(systemMacroPath));
258     }
259 
260     String settings = jEdit.getSettingsDirectory();
261 
262     if(settings != null)
263     {
264       userMacroPath = MiscUtilities.constructPath(
265         settings,"macros");
266       loadMacros(macroHierarchy,"",new File(userMacroPath));
267     }
268 
269     EditBus.send(new DynamicMenuChanged("macros"));
270   } //}}}
271 
272   //{{{ registerHandler() method
273   /**
274    * Adds a macro handler to the handlers list
275    * @since jEdit 4.0pre6
276    */
277   public static void registerHandler(Handler handler)
278   {
279     if (getHandler(handler.getName()) != null)
280     {
281       Log.log(Log.ERROR, Macros.class, "Cannot register more than one macro handler with the same name");
282       return;
283     }
284 
285     Log.log(Log.DEBUG,Macros.class,"Registered " + handler.getName()
286       + " macro handler");
287     macroHandlers.add(handler);
288   } //}}}
289 
290   //{{{ getHandlers() method
291   /**
292    * Returns an array containing the list of registered macro handlers
293    * @since jEdit 4.0pre6
294    */
295   public static Handler[] getHandlers()
296   {
297     Handler[] handlers = new Handler[macroHandlers.size()];
298     return (Handler[])macroHandlers.toArray(handlers);
299   } //}}}
300 
301   //{{{ getHandlerForFileName() method
302   /**
303    * Returns the macro handler suitable for running the specified file
304    * name, or null if there is no suitable handler.
305    * @since jEdit 4.1pre3
306    */
307   public static Handler getHandlerForPathName(String pathName)
308   {
309     for (int i = 0; i < macroHandlers.size(); i++)
310     {
311       Handler handler = (Handler)macroHandlers.get(i);
312       if (handler.accept(pathName))
313         return handler;
314     }
315 
316     return null;
317   } //}}}
318 
319   //{{{ getHandler() method
320   /**
321    * Returns the macro handler with the specified name, or null if
322    * there is no registered handler with that name.
323    * @since jEdit 4.0pre6
324    */
325   public static Handler getHandler(String name)
326   {
327     Handler handler = null;
328     for (int i = 0; i < macroHandlers.size(); i++)
329     {
330       handler = (Handler)macroHandlers.get(i);
331       if (handler.getName().equals(name)) return handler;
332     }
333 
334     return null;
335   }
336   //}}}
337 
338   //{{{ getMacroHierarchy() method
339   /**
340    * Returns a vector hierarchy with all known macros in it.
341    * Each element of this vector is either a macro name string,
342    * or another vector. If it is a vector, the first element is a
343    * string label, the rest are again, either macro name strings
344    * or vectors.
345    * @since jEdit 2.6pre1
346    */
347   public static Vector getMacroHierarchy()
348   {
349     return macroHierarchy;
350   } //}}}
351 
352   //{{{ getMacroActionSet() method
353   /**
354    * Returns an action set with all known macros in it.
355    * @since jEdit 4.0pre1
356    */
357   public static ActionSet getMacroActionSet()
358   {
359     return macroActionSet;
360   } //}}}
361 
362   //{{{ getMacro() method
363   /**
364    * Returns the macro with the specified name.
365    * @param macro The macro's name
366    * @since jEdit 2.6pre1
367    */
368   public static Macro getMacro(String macro)
369   {
370     return (Macro)macroHash.get(macro);
371   } //}}}
372 
373   //{{{ Macro class
374   /**
375    * Encapsulates the macro's label, name and path.
376    * @since jEdit 2.2pre4
377    */
378   public static class Macro extends EditAction
379   {
380     //{{{ Macro constructor
381     public Macro(Handler handler, String name, String label, String path)
382     {
383       // in case macro file name has a space in it.
384       // spaces break the view.toolBar property, for instance,
385       // since it uses spaces to delimit action names.
386       super(name.replace(' ','_'));
387       this.handler = handler;
388       this.label = label;
389       this.path = path;
390 
391       jEdit.setTemporaryProperty(getName() + ".label",label);
392       jEdit.setTemporaryProperty(getName() + ".mouse-over",
393         handler.getLabel() + " - " + path);
394     } //}}}
395 
396     //{{{ getHandler() method
397     public Handler getHandler()
398     {
399       return handler;
400     }
401     //}}}
402 
403     //{{{ getPath() method
404     public String getPath()
405     {
406       return path;
407     } //}}}
408 
409     //{{{ invoke() method
410     public void invoke(View view)
411     {
412       if(view == null)
413         handler.runMacro(null,this);
414       else
415       {
416         Buffer buffer = view.getBuffer();
417 
418         try
419         {
420           buffer.beginCompoundEdit();
421           handler.runMacro(view,this);
422         }
423         finally
424         {
425           buffer.endCompoundEdit();
426         }
427       }
428     } //}}}
429 
430     //{{{ getCode() method
431     public String getCode()
432     {
433       return "Macros.getMacro(\"" + getName() + "\").invoke(view);";
434     } //}}}
435 
436     //{{{ macroNameToLabel() method
437     public static String macroNameToLabel(String macroName)
438     {
439       int index = macroName.lastIndexOf('/');
440       return macroName.substring(index + 1).replace('_', ' ');
441     }
442     //}}}
443 
444     //{{{ Private members
445     private Handler handler;
446     private String path;
447     private String label;
448     //}}}
449   } //}}}
450 
451   //{{{ recordTemporaryMacro() method
452   /**
453    * Starts recording a temporary macro.
454    * @param view The view
455    * @since jEdit 2.7pre2
456    */
457   public static void recordTemporaryMacro(View view)
458   {
459     String settings = jEdit.getSettingsDirectory();
460 
461     if(settings == null)
462     {
463       GUIUtilities.error(view,"no-settings",new String[0]);
464       return;
465     }
466     if(view.getMacroRecorder() != null)
467     {
468       GUIUtilities.error(view,"already-recording",new String[0]);
469       return;
470     }
471 
472     Buffer buffer = jEdit.openFile(null,settings + File.separator
473       + "macros","Temporary_Macro.bsh",true,null);
474 
475     if(buffer == null)
476       return;
477 
478     buffer.remove(0,buffer.getLength());
479     buffer.insert(0,jEdit.getProperty("macro.temp.header"));
480 
481     recordMacro(view,buffer,true);
482   } //}}}
483 
484   //{{{ recordMacro() method
485   /**
486    * Starts recording a macro.
487    * @param view The view
488    * @since jEdit 2.7pre2
489    */
490   public static void recordMacro(View view)
491   {
492     String settings = jEdit.getSettingsDirectory();
493 
494     if(settings == null)
495     {
496       GUIUtilities.error(view,"no-settings",new String[0]);
497       return;
498     }
499 
500     if(view.getMacroRecorder() != null)
501     {
502       GUIUtilities.error(view,"already-recording",new String[0]);
503       return;
504     }
505 
506     String name = GUIUtilities.input(view,"record",null);
507     if(name == null)
508       return;
509 
510     name = name.replace(' ','_');
511 
512     Buffer buffer = jEdit.openFile(null,null,
513       MiscUtilities.constructPath(settings,"macros",
514       name + ".bsh"),true,null);
515 
516     if(buffer == null)
517       return;
518 
519     buffer.remove(0,buffer.getLength());
520     buffer.insert(0,jEdit.getProperty("macro.header"));
521 
522     recordMacro(view,buffer,false);
523   } //}}}
524 
525   //{{{ stopRecording() method
526   /**
527    * Stops a recording currently in progress.
528    * @param view The view
529    * @since jEdit 2.7pre2
530    */
531   public static void stopRecording(View view)
532   {
533     Recorder recorder = view.getMacroRecorder();
534 
535     if(recorder == null)
536       GUIUtilities.error(view,"macro-not-recording",null);
537     else
538     {
539       view.setMacroRecorder(null);
540       if(!recorder.temporary)
541         view.setBuffer(recorder.buffer);
542       recorder.dispose();
543     }
544   } //}}}
545 
546   //{{{ runTemporaryMacro() method
547   /**
548    * Runs the temporary macro.
549    * @param view The view
550    * @since jEdit 2.7pre2
551    */
552   public static void runTemporaryMacro(View view)
553   {
554     String settings = jEdit.getSettingsDirectory();
555 
556     if(settings == null)
557     {
558       GUIUtilities.error(view,"no-settings",null);
559       return;
560     }
561 
562     String path = MiscUtilities.constructPath(
563       jEdit.getSettingsDirectory(),"macros",
564       "Temporary_Macro.bsh");
565 
566     if(jEdit.getBuffer(path) == null)
567     {
568       GUIUtilities.error(view,"no-temp-macro",null);
569       return;
570     }
571 
572     Handler handler = getHandler("beanshell");
573     Macro temp = handler.createMacro(path,path);
574 
575     Buffer buffer = view.getBuffer();
576 
577     try
578     {
579       buffer.beginCompoundEdit();
580       temp.invoke(view);
581     }
582     finally
583     {
584       /* I already wrote a comment expaining this in
585        * Macro.invoke(). */
586       if(buffer.insideCompoundEdit())
587         buffer.endCompoundEdit();
588     }
589   } //}}}
590 
591   //{{{ Private members
592 
593   //{{{ Static variables
594   private static String systemMacroPath;
595   private static String userMacroPath;
596 
597   private static ArrayList macroHandlers;
598 
599   private static ActionSet macroActionSet;
600   private static Vector macroHierarchy;
601   private static Hashtable macroHash;
602   //}}}
603 
604   //{{{ Class initializer
605   static
606   {
607     macroHandlers = new ArrayList();
608     registerHandler(new BeanShellHandler());
609     macroActionSet = new ActionSet(jEdit.getProperty("action-set.macros"));
610     jEdit.addActionSet(macroActionSet);
611     macroHierarchy = new Vector();
612     macroHash = new Hashtable();
613   } //}}}
614 
615   //{{{ loadMacros() method
616   private static void loadMacros(Vector vector, String path, File directory)
617   {
618     File[] macroFiles = directory.listFiles();
619     if(macroFiles == null || macroFiles.length == 0)
620       return;
621 
622     for(int i = 0; i < macroFiles.length; i++)
623     {
624       File file = macroFiles[i];
625       String fileName = file.getName();
626       if(file.isHidden())
627       {
628         /* do nothing! */
629         continue;
630       }
631       else if(file.isDirectory())
632       {
633         String submenuName = fileName.replace('_',' ');
634         Vector submenu = null;
635         //{{{ try to merge with an existing menu first
636         for(int j = 0; j < vector.size(); j++)
637         {
638           Object obj = vector.get(j);
639           if(obj instanceof Vector)
640           {
641             Vector vec = (Vector)obj;
642             if(((String)vec.get(0)).equals(submenuName))
643             {
644               submenu = vec;
645               break;
646             }
647           }
648         } //}}}
649         if(submenu == null)
650         {
651           submenu = new Vector();
652           submenu.addElement(submenuName);
653           vector.addElement(submenu);
654         }
655 
656         loadMacros(submenu,path + fileName + '/',file);
657       }
658       else
659       {
660         Handler handler = getHandlerForPathName(file.getPath());
661 
662         if(handler == null)
663           continue;
664 
665         try
666         {
667           Macro newMacro = handler.createMacro(
668             path + fileName, file.getPath());
669           vector.addElement(newMacro.getName());
670           macroActionSet.addAction(newMacro);
671           macroHash.put(newMacro.getName(),newMacro);
672         }
673         catch (Exception e)
674         {
675           Log.log(Log.ERROR, Macros.class, e);
676           macroHandlers.remove(handler);
677         }
678       }
679     }
680   } //}}}
681 
682   //{{{ recordMacro() method
683   /**
684    * Starts recording a macro.
685    * @param view The view
686    * @param buffer The buffer to record to
687    * @param temporary True if this is a temporary macro
688    * @since jEdit 3.0pre5
689    */
690   private static void recordMacro(View view, Buffer buffer, boolean temporary)
691   {
692     Handler handler = getHandler("beanshell");
693     String path = buffer.getPath();
694 
695     view.setMacroRecorder(new Recorder(view,buffer,temporary));
696 
697     // setting the message to 'null' causes the status bar to check
698     // if a recording is in progress
699     view.getStatus().setMessage(null);
700   } //}}}
701 
702   //}}}
703 
704   //{{{ Recorder class
705   /**
706    * Handles macro recording.
707    */
708   public static class Recorder implements EBComponent
709   {
710     View view;
711     Buffer buffer;
712     boolean temporary;
713 
714     boolean lastWasInput;
715     boolean lastWasOverwrite;
716     int overwriteCount;
717 
718     //{{{ Recorder constructor
719     public Recorder(View view, Buffer buffer, boolean temporary)
720     {
721       this.view = view;
722       this.buffer = buffer;
723       this.temporary = temporary;
724       EditBus.addToBus(this);
725     } //}}}
726 
727     //{{{ record() method
728     public void record(String code)
729     {
730       flushInput();
731 
732       append("\n");
733       append(code);
734     } //}}}
735 
736     //{{{ record() method
737     public void record(int repeat, String code)
738     {
739       if(repeat == 1)
740         record(code);
741       else
742       {
743         record("for(int i = 1; i <= " + repeat + "; i++)\n"
744           + "{\n"
745           + code + "\n"
746           + "}");
747       }
748     } //}}}
749 
750     //{{{ recordInput() method
751     /**
752      * @since jEdit 4.2pre5
753      */
754     public void recordInput(int repeat, char ch, boolean overwrite)
755     {
756       // record \n and \t on lines specially so that auto indent
757       // can take place
758       if(ch == '\n')
759         record(repeat,"textArea.userInput(\'\\n\');");
760       else if(ch == '\t')
761         record(repeat,"textArea.userInput(\'\\t\');");
762       else
763       {
764         StringBuffer buf = new StringBuffer();
765         for(int i = 0; i < repeat; i++)
766           buf.append(ch);
767         recordInput(buf.toString(),overwrite);
768       }
769     } //}}}
770 
771     //{{{ recordInput() method
772     /**
773      * @since jEdit 4.2pre5
774      */
775     public void recordInput(String str, boolean overwrite)
776     {
777       String charStr = MiscUtilities.charsToEscapes(str);
778 
779       if(overwrite)
780       {
781         if(lastWasOverwrite)
782         {
783           overwriteCount++;
784           append(charStr);
785         }
786         else
787         {
788           flushInput();
789           overwriteCount = 1;
790           lastWasOverwrite = true;
791           append("\ntextArea.setSelectedText(\"" + charStr);
792         }
793       }
794       else
795       {
796         if(lastWasInput)
797           append(charStr);
798         else
799         {
800           flushInput();
801           lastWasInput = true;
802           append("\ntextArea.setSelectedText(\"" + charStr);
803         }
804       }
805     } //}}}
806 
807     //{{{ handleMessage() method
808     public void handleMessage(EBMessage msg)
809     {
810       if(msg instanceof BufferUpdate)
811       {
812         BufferUpdate bmsg = (BufferUpdate)msg;
813         if(bmsg.getWhat() == BufferUpdate.CLOSED)
814         {
815           if(bmsg.getBuffer() == buffer)
816             stopRecording(view);
817         }
818       }
819     } //}}}
820 
821     //{{{ append() method
822     private void append(String str)
823     {
824       buffer.insert(buffer.getLength(),str);
825     } //}}}
826 
827     //{{{ dispose() method
828     private void dispose()
829     {
830       flushInput();
831 
832       for(int i = 0; i < buffer.getLineCount(); i++)
833       {
834         buffer.indentLine(i,true);
835       }
836 
837       EditBus.removeFromBus(this);
838 
839       // setting the message to 'null' causes the status bar to
840       // check if a recording is in progress
841       view.getStatus().setMessage(null);
842     } //}}}
843 
844     //{{{ flushInput() method
845     /**
846      * We try to merge consecutive inputs. This helper method is
847      * called when something other than input is to be recorded.
848      */
849     private void flushInput()
850     {
851       if(lastWasInput)
852       {
853         lastWasInput = false;
854         append("\");");
855       }
856 
857       if(lastWasOverwrite)
858       {
859         lastWasOverwrite = false;
860         append("\");\n");
861         append("offset = buffer.getLineEndOffset("
862           + "textArea.getCaretLine()) - 1;\n");
863         append("buffer.remove(textArea.getCaretPosition(),"
864           + "Math.min(" + overwriteCount
865           + ",offset - "
866           + "textArea.getCaretPosition()));");
867       }
868     } //}}}
869   } //}}}
870 
871   //{{{ Handler class
872   /**
873    * Encapsulates creating and invoking macros in arbitrary scripting languages
874    * @since jEdit 4.0pre6
875    */
876   public static abstract class Handler
877   {
878     //{{{ getName() method
879     public String getName()
880     {
881       return name;
882     } //}}}
883 
884     //{{{ getLabel() method
885     public String getLabel()
886     {
887       return label;
888     } //}}}
889 
890     //{{{ accept() method
891     public boolean accept(String path)
892     {
893       return filter.isMatch(MiscUtilities.getFileName(path));
894     } //}}}
895 
896     //{{{ createMacro() method
897     public abstract Macro createMacro(String macroName, String path);
898     //}}}
899 
900     //{{{ runMacro() method
901     /**
902      * Runs the specified macro.
903      * @param view The view - may be null.
904      * @param macro The macro.
905      */
906     public abstract void runMacro(View view, Macro macro);
907     //}}}
908 
909     //{{{ runMacro() method
910     /**
911      * Runs the specified macro. This method is optional; it is
912      * called if the specified macro is a startup script. The
913      * default behavior is to simply call {@link #runMacro(View,Macros.Macro)}.
914      *
915      * @param view The view - may be null.
916      * @param macro The macro.
917      * @param ownNamespace  A hint indicating whenever functions and
918      * variables defined in the script are to be self-contained, or
919      * made available to other scripts. The macro handler may ignore
920      * this parameter.
921      * @since jEdit 4.1pre3
922      */
923     public void runMacro(View view, Macro macro, boolean ownNamespace)
924     {
925       runMacro(view,macro);
926     } //}}}
927 
928     //{{{ Handler constructor
929     protected Handler(String name)
930     {
931       this.name = name;
932       label = jEdit.getProperty("macro-handler."
933         + name + ".label", name);
934       try
935       {
936         filter = new RE(MiscUtilities.globToRE(
937           jEdit.getProperty(
938           "macro-handler." + name + ".glob")));
939       }
940       catch (Exception e)
941       {
942         throw new InternalError("Missing or invalid glob for handler " + name);
943       }
944     } //}}}
945 
946     //{{{ Private members
947     private String name;
948     private String label;
949     private RE filter;
950     //}}}
951   } //}}}
952 
953   //{{{ BeanShellHandler class
954   static class BeanShellHandler extends Handler
955   {
956     //{{{ BeanShellHandler constructor
957     BeanShellHandler()
958     {
959       super("beanshell");
960     } //}}}
961 
962     //{{{ createMacro() method
963     public Macro createMacro(String macroName, String path)
964     {
965       // Remove '.bsh'
966       macroName = macroName.substring(0, macroName.length() - 4);
967 
968       return new Macro(this, macroName,
969         Macro.macroNameToLabel(macroName), path);
970     } //}}}
971 
972     //{{{ runMacro() method
973     public void runMacro(View view, Macro macro)
974     {
975       BeanShell.runScript(view,macro.getPath(),null,true);
976     } //}}}
977 
978     //{{{ runMacro() method
979     public void runMacro(View view, Macro macro, boolean ownNamespace)
980     {
981       BeanShell.runScript(view,macro.getPath(),null,ownNamespace);
982     } //}}}
983   } //}}}
984 }