PageRenderTime 97ms CodeModel.GetById 33ms app.highlight 48ms RepoModel.GetById 1ms app.codeStats 0ms

/jEdit/tags/jedit-4-0-pre3/org/gjt/sp/jedit/Macros.java

#
Java | 639 lines | 367 code | 82 blank | 190 comment | 54 complexity | d082eb66337e4847a02e663421753267 MD5 | raw file
  1/*
  2 * Macros.java - Macro manager
  3 * :tabSize=8:indentSize=8:noTabs=false:
  4 * :folding=explicit:collapseFolds=1:
  5 *
  6 * Copyright (C) 1999, 2000, 2001 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
 25//{{{ Imports
 26import javax.swing.JOptionPane;
 27import java.awt.event.ActionEvent;
 28import java.awt.event.ActionListener;
 29import java.awt.*;
 30import java.io.*;
 31import java.util.*;
 32import org.gjt.sp.jedit.browser.*;
 33import org.gjt.sp.jedit.gui.*;
 34import org.gjt.sp.jedit.io.VFSManager;
 35import org.gjt.sp.jedit.msg.*;
 36import org.gjt.sp.util.Log;
 37//}}}
 38
 39/**
 40 * This class records and runs macros.
 41 *
 42 * @author Slava Pestov
 43 * @version $Id: Macros.java 3928 2001-12-01 05:48:48Z spestov $
 44 */
 45public class Macros
 46{
 47	//{{{ message() method
 48	/**
 49	 * Utility method that can be used to display a message dialog in a macro.
 50	 * @param comp The component to show the dialog on behalf of, this
 51	 * will usually be a view instance
 52	 * @param message The message
 53	 * @since jEdit 2.7pre2
 54	 */
 55	public static void message(Component comp, String message)
 56	{
 57		GUIUtilities.hideSplashScreen();
 58
 59		JOptionPane.showMessageDialog(comp,message,
 60			jEdit.getProperty("macro-message.title"),
 61			JOptionPane.INFORMATION_MESSAGE);
 62	} //}}}
 63
 64	//{{{ error() method
 65	/**
 66	 * Utility method that can be used to display an error dialog in a macro.
 67	 * @param comp The component to show the dialog on behalf of, this
 68	 * will usually be a view instance
 69	 * @param message The message
 70	 * @since jEdit 2.7pre2
 71	 */
 72	public static void error(Component comp, String message)
 73	{
 74		GUIUtilities.hideSplashScreen();
 75
 76		JOptionPane.showMessageDialog(comp,message,
 77			jEdit.getProperty("macro-message.title"),
 78			JOptionPane.ERROR_MESSAGE);
 79	} //}}}
 80
 81	//{{{ input() method
 82	/**
 83	 * Utility method that can be used to prompt for input in a macro.
 84	 * @param comp The component to show the dialog on behalf of, this
 85	 * will usually be a view instance
 86	 * @param prompt The prompt string
 87	 * @since jEdit 2.7pre2
 88	 */
 89	public static String input(Component comp, String prompt)
 90	{
 91		GUIUtilities.hideSplashScreen();
 92
 93		return input(comp,prompt,null);
 94	} //}}}
 95
 96	//{{{ input() method
 97	/**
 98	 * Utility method that can be used to prompt for input in a macro.
 99	 * @param comp The component to show the dialog on behalf of, this
100	 * will usually be a view instance
101	 * @param prompt The prompt string
102	 * @since jEdit 3.1final
103	 */
104	public static String input(Component comp, String prompt, String defaultValue)
105	{
106		GUIUtilities.hideSplashScreen();
107
108		return (String)JOptionPane.showInputDialog(comp,prompt,
109			jEdit.getProperty("macro-input.title"),
110			JOptionPane.QUESTION_MESSAGE,null,null,defaultValue);
111	} //}}}
112
113	//{{{ confirm() method
114	/**
115	 * Utility method that can be used to ask for confirmation in a macro.
116	 * @param comp The component to show the dialog on behalf of, this
117	 * will usually be a view instance
118	 * @param prompt The prompt string
119	 * @param buttons The buttons to display - for example,
120	 * JOptionPane.YES_NO_CANCEL_OPTION
121	 * @since jEdit 4.0pre2
122	 */
123	public static int confirm(Component comp, String prompt, int buttons)
124	{
125		GUIUtilities.hideSplashScreen();
126
127		return JOptionPane.showConfirmDialog(comp,prompt,
128			jEdit.getProperty("macro-confirm.title"),buttons,
129			JOptionPane.QUESTION_MESSAGE);
130	} //}}}
131
132	//{{{ confirm() method
133	/**
134	 * Utility method that can be used to ask for confirmation in a macro.
135	 * @param comp The component to show the dialog on behalf of, this
136	 * will usually be a view instance
137	 * @param prompt The prompt string
138	 * @param buttons The buttons to display - for example,
139	 * JOptionPane.YES_NO_CANCEL_OPTION
140	 * @param type The dialog type - for example,
141	 * JOptionPane.WARNING_MESSAGE
142	 */
143	public static int confirm(Component comp, String prompt, int buttons, int type)
144	{
145		GUIUtilities.hideSplashScreen();
146
147		return JOptionPane.showConfirmDialog(comp,prompt,
148			jEdit.getProperty("macro-confirm.title"),buttons,type);
149	} //}}}
150
151	//{{{ browseSystemMacros() method
152	/**
153	 * Opens the system macro directory in a VFS browser.
154	 * @param view The view
155	 * @since jEdit 2.7pre2
156	 */
157	public static void browseSystemMacros(View view)
158	{
159		if(systemMacroPath == null)
160		{
161			GUIUtilities.error(view,"no-webstart",null);
162			return;
163		}
164
165		VFSBrowser.browseDirectory(view,systemMacroPath);
166	} //}}}
167
168	//{{{ browseUserMacros() method
169	/**
170	 * Opens the user macro directory in a VFS browser.
171	 * @param view The view
172	 * @since jEdit 2.7pre2
173	 */
174	public static void browseUserMacros(View view)
175	{
176		if(userMacroPath == null)
177		{
178			GUIUtilities.error(view,"no-settings",null);
179			return;
180		}
181
182		VFSBrowser.browseDirectory(view,userMacroPath);
183	} //}}}
184
185	//{{{ loadMacros() method
186	/**
187	 * Rebuilds the macros list, and sends a MacrosChanged message
188	 * (views update their Macros menu upon receiving it)
189	 * @since jEdit 2.2pre4
190	 */
191	public static void loadMacros()
192	{
193		macroActionSet.removeAllActions();
194		macroHierarchy.removeAllElements();
195		macroHash.clear();
196
197		if(jEdit.getJEditHome() != null)
198		{
199			systemMacroPath = MiscUtilities.constructPath(
200				jEdit.getJEditHome(),"macros");
201			loadMacros(macroHierarchy,"",new File(systemMacroPath));
202		}
203
204		String settings = jEdit.getSettingsDirectory();
205
206		if(settings != null)
207		{
208			userMacroPath = MiscUtilities.constructPath(
209				settings,"macros");
210			loadMacros(macroHierarchy,"",new File(userMacroPath));
211		}
212
213		EditBus.send(new MacrosChanged(null));
214	} //}}}
215
216	//{{{ getMacroHierarchy() method
217	/**
218	 * Returns a vector hierarchy with all known macros in it.
219	 * Each element of this vector is either a macro name string,
220	 * or another vector. If it is a vector, the first element is a
221	 * string label, the rest are again, either macro name strings
222	 * or vectors.
223	 * @since jEdit 2.6pre1
224	 */
225	public static Vector getMacroHierarchy()
226	{
227		return macroHierarchy;
228	} //}}}
229
230	//{{{ getMacroActionSet() method
231	/**
232	 * Returns an action set with all known macros in it.
233	 * @since jEdit 4.0pre1
234	 */
235	public static ActionSet getMacroActionSet()
236	{
237		return macroActionSet;
238	} //}}}
239
240	//{{{ getMacro() method
241	/**
242	 * Returns the macro with the specified name.
243	 * @param macro The macro's name
244	 * @since jEdit 2.6pre1
245	 */
246	public static Macro getMacro(String macro)
247	{
248		return (Macro)macroHash.get(macro);
249	} //}}}
250
251	//{{{ Macro class
252	/**
253	 * Encapsulates the macro's label, name and path.
254	 * @since jEdit 2.2pre4
255	 */
256	public static class Macro extends EditAction
257	{
258		//{{{ Macro constructor
259		public Macro(String name, String path)
260		{
261			// stupid name for backwards compatibility
262			super("play-macro@" + name);
263			this.path = path;
264
265			int index = name.lastIndexOf('/');
266			label = name.substring(index + 1)
267				.replace('_',' ');
268		} //}}}
269
270		//{{{ getLabel() method
271		public String getLabel()
272		{
273			return label;
274		} //}}}
275
276		//{{{ invoke() method
277		public void invoke(View view)
278		{
279			lastMacro = path;
280			Buffer buffer = view.getBuffer();
281
282			try
283			{
284				buffer.beginCompoundEdit();
285
286				BeanShell.runScript(view,path,
287					true,false);
288			}
289			finally
290			{
291				buffer.endCompoundEdit();
292			}
293		} //}}}
294
295		//{{{ getCode() method
296		public String getCode()
297		{
298			return "Macros.getMacro(\""
299				+ getName().substring("play-macro@".length())
300				+ "\").invoke(view);";
301		} //}}}
302
303		//{{{ Private members
304		private String path;
305		private String label;
306		//}}}
307	} //}}}
308
309	//{{{ recordTemporaryMacro() method
310	/**
311	 * Starts recording a temporary macro.
312	 * @param view The view
313	 * @since jEdit 2.7pre2
314	 */
315	public static void recordTemporaryMacro(View view)
316	{
317		String settings = jEdit.getSettingsDirectory();
318
319		if(settings == null)
320		{
321			GUIUtilities.error(view,"no-settings",new String[0]);
322			return;
323		}
324		if(view.getMacroRecorder() != null)
325		{
326			GUIUtilities.error(view,"already-recording",new String[0]);
327			return;
328		}
329
330		Buffer buffer = jEdit.openFile(null,settings + File.separator
331			+ "macros","Temporary_Macro.bsh",true,null);
332
333		if(buffer == null)
334			return;
335
336		buffer.remove(0,buffer.getLength());
337		buffer.insert(0,jEdit.getProperty("macro.temp.header"));
338
339		recordMacro(view,buffer,true);
340	} //}}}
341
342	//{{{ recordMacro() method
343	/**
344	 * Starts recording a macro.
345	 * @param view The view
346	 * @since jEdit 2.7pre2
347	 */
348	public static void recordMacro(View view)
349	{
350		String settings = jEdit.getSettingsDirectory();
351
352		if(settings == null)
353		{
354			GUIUtilities.error(view,"no-settings",new String[0]);
355			return;
356		}
357
358		if(view.getMacroRecorder() != null)
359		{
360			GUIUtilities.error(view,"already-recording",new String[0]);
361			return;
362		}
363
364		String name = GUIUtilities.input(view,"record",null);
365		if(name == null)
366			return;
367
368		name = name.replace(' ','_');
369
370		Buffer buffer = jEdit.openFile(null,null,
371			MiscUtilities.constructPath(settings,"macros",
372			name + ".bsh"),true,null);
373
374		if(buffer == null)
375			return;
376
377		buffer.remove(0,buffer.getLength());
378		buffer.insert(0,jEdit.getProperty("macro.header"));
379
380		recordMacro(view,buffer,false);
381	} //}}}
382
383	//{{{ stopRecording() method
384	/**
385	 * Stops a recording currently in progress.
386	 * @param view The view
387	 * @since jEdit 2.7pre2
388	 */
389	public static void stopRecording(View view)
390	{
391		InputHandler inputHandler = view.getInputHandler();
392		Recorder recorder = view.getMacroRecorder();
393
394		if(recorder == null)
395			GUIUtilities.error(view,"macro-not-recording",null);
396		else
397		{
398			view.setMacroRecorder(null);
399			if(!recorder.temporary)
400				view.setBuffer(recorder.buffer);
401			recorder.dispose();
402		}
403	} //}}}
404
405	//{{{ runTemporaryMacro() method
406	/**
407	 * Runs the temporary macro.
408	 * @param view The view
409	 * @since jEdit 2.7pre2
410	 */
411	public static void runTemporaryMacro(View view)
412	{
413		String settings = jEdit.getSettingsDirectory();
414
415		if(settings == null)
416		{
417			GUIUtilities.error(view,"no-settings",new String[0]);
418			return;
419		}
420
421		lastMacro = MiscUtilities.constructPath(
422			jEdit.getSettingsDirectory(),"macros",
423			"Temporary_Macro.bsh");
424
425		Buffer buffer = view.getBuffer();
426
427		try
428		{
429			buffer.beginCompoundEdit();
430			BeanShell.runScript(view,lastMacro,true,false);
431		}
432		finally
433		{
434			buffer.endCompoundEdit();
435		}
436	} //}}}
437
438	//{{{ runLastMacro() method
439	/**
440	 * Runs the most recently run or recorded macro.
441	 * @param view The view
442	 * @since jEdit 2.7pre2
443	 */
444	public static void runLastMacro(View view)
445	{
446		if(lastMacro == null)
447			view.getToolkit().beep();
448		else
449			BeanShell.runScript(view,lastMacro,true,false);
450	} //}}}
451
452	//{{{ Private members
453
454	//{{{ Static variables
455	private static String systemMacroPath;
456	private static String userMacroPath;
457
458	private static ActionSet macroActionSet;
459	private static Vector macroHierarchy;
460	private static Hashtable macroHash;
461	private static String lastMacro;
462	//}}}
463
464	//{{{ Class initializer
465	static
466	{
467		macroActionSet = new ActionSet(jEdit.getProperty("action-set.macros"));
468		jEdit.addActionSet(macroActionSet);
469		macroHierarchy = new Vector();
470		macroHash = new Hashtable();
471	} //}}}
472
473	//{{{ loadMacros() method
474	private static void loadMacros(Vector vector, String path, File directory)
475	{
476		String[] macroFiles = directory.list();
477		if(macroFiles == null)
478			return;
479
480		MiscUtilities.quicksort(macroFiles,new MiscUtilities.StringICaseCompare());
481
482		for(int i = 0; i < macroFiles.length; i++)
483		{
484			String fileName = macroFiles[i];
485			File file = new File(directory,fileName);
486			if(fileName.toLowerCase().endsWith(".bsh"))
487			{
488				String label = fileName.substring(0,fileName.length() - 4);
489				String name = path + label;
490				Macro newMacro = new Macro(name,file.getPath());
491				vector.addElement(newMacro);
492				macroActionSet.addAction(newMacro);
493				macroHash.put(name,newMacro);
494			}
495			else if(file.isDirectory())
496			{
497				Vector submenu = new Vector();
498				submenu.addElement(fileName.replace('_',' '));
499				loadMacros(submenu,path + fileName + '/',file);
500				if(submenu.size() != 1)
501					vector.addElement(submenu);
502			}
503		}
504	} //}}}
505
506	//{{{ recordMacro() method
507	/**
508	 * Starts recording a macro.
509	 * @param view The view
510	 * @param buffer The buffer to record to
511	 * @param temporary True if this is a temporary macro
512	 * @since jEdit 3.0pre5
513	 */
514	private static void recordMacro(View view, Buffer buffer, boolean temporary)
515	{
516		lastMacro = buffer.getPath();
517
518		view.setMacroRecorder(new Recorder(view,buffer,temporary));
519
520		// setting the message to 'null' causes the status bar to check
521		// if a recording is in progress
522		view.getStatus().setMessage(null);
523	} //}}}
524
525	//}}}
526
527	//{{{ Recorder class
528	public static class Recorder implements EBComponent
529	{
530		View view;
531		Buffer buffer;
532		boolean temporary;
533
534		boolean lastWasInput;
535
536		//{{{ Recorder constructor
537		public Recorder(View view, Buffer buffer, boolean temporary)
538		{
539			this.view = view;
540			this.buffer = buffer;
541			this.temporary = temporary;
542			EditBus.addToBus(this);
543		} //}}}
544
545		//{{{ record() method
546		public void record(String code)
547		{
548			if(lastWasInput)
549			{
550				lastWasInput = false;
551				append("\");");
552			}
553
554			append("\n");
555			append(code);
556		} //}}}
557
558		//{{{ record() method
559		public void record(int repeat, String code)
560		{
561			if(repeat == 1)
562				record(code);
563			else
564			{
565				record("for(int i = 1; i <= " + repeat + "; i++)\n"
566					+ "{\n"
567					+ code + "\n"
568					+ "}");
569			}
570		} //}}}
571
572		//{{{ record() method
573		public void record(int repeat, char ch)
574		{
575			// record \n and \t on lines specially so that auto indent
576			// can take place
577			if(ch == '\n')
578				record(repeat,"textArea.userInput(\'\\n\');");
579			else if(ch == '\t')
580				record(repeat,"textArea.userInput(\'\\t\');");
581			else
582			{
583				StringBuffer buf = new StringBuffer();
584				for(int i = 0; i < repeat; i++)
585					buf.append(ch);
586				String charStr = MiscUtilities.charsToEscapes(buf.toString());
587
588				if(lastWasInput)
589					append(charStr);
590				else
591				{
592					append("\ntextArea.setSelectedText(\"" + charStr);
593					lastWasInput = true;
594				}
595			}
596		} //}}}
597
598		//{{{ handleMessage() method
599		public void handleMessage(EBMessage msg)
600		{
601			if(msg instanceof BufferUpdate)
602			{
603				BufferUpdate bmsg = (BufferUpdate)msg;
604				if(bmsg.getWhat() == BufferUpdate.CLOSED)
605				{
606					if(bmsg.getBuffer() == buffer)
607						stopRecording(view);
608				}
609			}
610		} //}}}
611
612		//{{{ append() method
613		private void append(String str)
614		{
615			buffer.insert(buffer.getLength(),str);
616		} //}}}
617
618		//{{{ dispose() method
619		private void dispose()
620		{
621			if(lastWasInput)
622			{
623				lastWasInput = false;
624				append("\");");
625			}
626
627			for(int i = 0; i < buffer.getLineCount(); i++)
628			{
629				buffer.indentLine(i,true,true);
630			}
631
632			EditBus.removeFromBus(this);
633
634			// setting the message to 'null' causes the status bar to
635			// check if a recording is in progress
636			view.getStatus().setMessage(null);
637		} //}}}
638	} //}}}
639}