8ed8c9d40670da7e77430485375e9ed4453e913f
[imageja.git] / ij / IJ.java
1 package ij;
2 import ij.gui.*;
3 import ij.process.*;
4 import ij.text.*;
5 import ij.io.*;
6 import ij.plugin.*;
7 import ij.plugin.filter.*;
8 import ij.util.Tools;
9 import ij.plugin.frame.Recorder;
10 import ij.macro.Interpreter;
11 import ij.measure.Calibration;
12 import java.awt.event.*;
13 import java.text.*;
14 import java.util.*;     
15 import java.awt.*;      
16 import java.applet.Applet;
17 import java.io.*;
18 import java.lang.reflect.*;
19
20
21 /** This class consists of static utility methods. */
22 public class IJ {
23         public static final int ALL_KEYS = 0x32;
24         
25         public static boolean debugMode;
26         public static boolean hideProcessStackDialog;
27             
28     public static final char micronSymbol = '\u00B5';
29     public static final char angstromSymbol = '\u00C5';
30     public static final char degreeSymbol = '\u00B0';
31
32         private static ImageJ ij;
33         private static java.applet.Applet applet;
34         private static ProgressBar progressBar;
35         private static TextPanel textPanel;
36         private static String osname, osarch;
37         private static boolean isMac, isWin, isJava2, isJava14, isJava15, isJava16, isLinux, isVista, is64Bit;
38         private static boolean altDown, spaceDown, shiftDown;
39         private static boolean macroRunning;
40         private static Thread previousThread;
41         private static TextPanel logPanel;
42         private static boolean notVerified = true;              
43         private static ClassLoader classLoader;
44         private static boolean memMessageDisplayed;
45         private static long maxMemory;
46         private static boolean escapePressed;
47         private static boolean redirectErrorMessages;
48         private static boolean suppressPluginNotFoundError;
49         private static Dimension screenSize;
50         private static Hashtable commandTable;
51
52                         
53         static {
54                 osname = System.getProperty("os.name");
55                 isWin = osname.startsWith("Windows");
56                 isMac = !isWin && osname.startsWith("Mac");
57                 isLinux = osname.startsWith("Linux");
58                 isVista = isWin && osname.indexOf("Vista")!=-1;
59                 String version = System.getProperty("java.version").substring(0,3);
60                 if (version.compareTo("2.9")<=0) {  // JVM on Sharp Zaurus PDA claims to be "3.1"!
61                         isJava2 = version.compareTo("1.1")>0;
62                         isJava14 = version.compareTo("1.3")>0;
63                         isJava15 = version.compareTo("1.4")>0;
64                         isJava16 = version.compareTo("1.5")>0;
65                 }
66         }
67                         
68         static void init(ImageJ imagej, Applet theApplet) {
69                 if (theApplet == null)
70                         System.setSecurityManager(null);
71                 ij = imagej;
72                 applet = theApplet;
73                 progressBar = ij.getProgressBar();
74         }
75
76         /**Returns a reference to the "ImageJ" frame.*/
77         public static ImageJ getInstance() {
78                 return ij;
79         }
80         
81         /** Runs the macro contained in the string <code>macro</code>.
82                 Returns any string value returned by the macro, or null. 
83                 The equivalent macro function is eval(). */
84         public static String runMacro(String macro) {
85                 return runMacro(macro, "");
86         }
87
88         /** Runs the macro contained in the string <code>macro</code>.
89                 The optional string argument can be retrieved in the
90                 called macro using the getArgument() macro function. 
91                 Returns any string value returned by the macro, or null. */
92         public static String runMacro(String macro, String arg) {
93                 Macro_Runner mr = new Macro_Runner();
94                 return mr.runMacro(macro, arg);
95         }
96
97         /** Runs the specified macro file. The file is assumed to be in the macros 
98                 folder unless <code>name</code> is a full path. ".txt"  is
99         added if <code>name</code> does not have an extension.
100                 The optional string argument (<code>arg</code>) can be retrieved in the called 
101                 macro using the getArgument() macro function. 
102                 Returns any string value returned by the macro or null. 
103                 The equivalent macro function is runMacro(). */
104         public static String runMacroFile(String name, String arg) {
105                 if (ij==null && Menus.getCommands()==null)
106                         init();
107                 Macro_Runner mr = new Macro_Runner();
108                 return mr.runMacroFile(name, arg);
109         }
110
111         /** Runs the specified macro file. */
112         public static String runMacroFile(String name) {
113                 return runMacroFile(name, null);
114         }
115
116         /** Runs the specified plugin using the specified image. */
117         public static Object runPlugIn(ImagePlus imp, String className, String arg) {
118                 WindowManager.setTempCurrentImage(imp);
119                 Object o = runPlugIn("", className, arg);
120                 WindowManager.setTempCurrentImage(null);
121                 return o;
122         }
123
124         /** Runs the specified plugin and returns a reference to it. */
125         public static Object runPlugIn(String className, String arg) {
126                 return runPlugIn("", className, arg);
127         }
128         
129         /** Runs the specified plugin and returns a reference to it. */
130         static Object runPlugIn(String commandName, String className, String arg) {
131                 if (IJ.debugMode)
132                         IJ.log("runPlugin: "+className+" "+arg);
133                 // Use custom classloader if this is a user plugin
134                 // and we are not running as an applet
135                 if (!className.startsWith("ij") && applet==null) {
136                         boolean createNewClassLoader = altKeyDown();
137                         return runUserPlugIn(commandName, className, arg, createNewClassLoader);
138                 }
139                 Object thePlugIn=null;
140                 try {
141                         Class c = Class.forName(className);
142                         thePlugIn = c.newInstance();
143                         if (thePlugIn instanceof PlugIn)
144                                 ((PlugIn)thePlugIn).run(arg);
145                         else
146                                 new PlugInFilterRunner(thePlugIn, commandName, arg);
147                 }
148                 catch (ClassNotFoundException e) {
149                         if (IJ.getApplet()==null)
150                                 log("Plugin or class not found: \"" + className + "\"\n(" + e+")");
151                 }
152                 catch (InstantiationException e) {log("Unable to load plugin (ins)");}
153                 catch (IllegalAccessException e) {log("Unable to load plugin, possibly \nbecause it is not public.");}
154                 redirectErrorMessages = false;
155                 return thePlugIn;
156         }
157         
158         static Object runUserPlugIn(String commandName, String className, String arg, boolean createNewLoader) {
159                 if (applet!=null) return null;
160                 if (notVerified) {
161                         // check for duplicate classes in the plugins folder
162                         IJ.runPlugIn("ij.plugin.ClassChecker", "");
163                         notVerified = false;
164                 }
165                 if (createNewLoader) classLoader = null;
166                 ClassLoader loader = getClassLoader();
167                 Object thePlugIn = null;
168                 try { 
169                         thePlugIn = (loader.loadClass(className)).newInstance(); 
170                         if (thePlugIn instanceof PlugIn)
171                                 ((PlugIn)thePlugIn).run(arg);
172                         else if (thePlugIn instanceof PlugInFilter)
173                                 new PlugInFilterRunner(thePlugIn, commandName, arg);
174                 }
175                 catch (ClassNotFoundException e) {
176                         if (className.indexOf('_')!=-1 && !suppressPluginNotFoundError)
177                                 error("Plugin or class not found: \"" + className + "\"\n(" + e+")");
178                 }
179                 catch (NoClassDefFoundError e) {
180                         int dotIndex = className.indexOf('.');
181                         String cause = e.getMessage();
182                         int parenIndex = cause.indexOf('(');
183                         if (parenIndex >= 1)
184                                 cause = cause.substring(0, parenIndex - 1);
185                         boolean correctClass = cause.endsWith(dotIndex < 0 ?
186                                 className : className.substring(dotIndex + 1));
187                         if (!correctClass && !suppressPluginNotFoundError)
188                                 error("Plugin " + className +
189                                         " did not find required class: " +
190                                         e.getMessage());
191                         if (correctClass && dotIndex >= 0)
192                                 return runUserPlugIn(commandName, className.substring(dotIndex + 1), arg, createNewLoader);
193                         if (className.indexOf('_')!=-1 && !suppressPluginNotFoundError)
194                                 error("Plugin or class not found: \"" + className + "\"\n(" + e+")");
195                 }
196                 catch (InstantiationException e) {error("Unable to load plugin (ins)");}
197                 catch (IllegalAccessException e) {error("Unable to load plugin, possibly \nbecause it is not public.");}
198                 redirectErrorMessages = false;
199                 suppressPluginNotFoundError = false;
200                 return thePlugIn;
201         } 
202
203         static void wrongType(int capabilities, String cmd) {
204                 String s = "\""+cmd+"\" requires an image of type:\n \n";
205                 if ((capabilities&PlugInFilter.DOES_8G)!=0) s +=  "    8-bit grayscale\n";
206                 if ((capabilities&PlugInFilter.DOES_8C)!=0) s +=  "    8-bit color\n";
207                 if ((capabilities&PlugInFilter.DOES_16)!=0) s +=  "    16-bit grayscale\n";
208                 if ((capabilities&PlugInFilter.DOES_32)!=0) s +=  "    32-bit (float) grayscale\n";
209                 if ((capabilities&PlugInFilter.DOES_RGB)!=0) s += "    RGB color\n";
210                 error(s);
211         }
212         
213     /** Starts executing a menu command in a separete thread and returns immediately. */
214         public static void doCommand(String command) {
215                 if (ij!=null)
216                         ij.doCommand(command);
217         }
218         
219     /** Runs an ImageJ command. Does not return until 
220         the command has finished executing. To avoid "image locked",
221         errors, plugins that call this method should implement
222         the PlugIn interface instead of PlugInFilter. */
223         public static void run(String command) {
224                 run(command, null);
225         }
226         
227     /** Runs an ImageJ command, with options that are passed to the
228                 GenericDialog and OpenDialog classes. Does not return until
229                 the command has finished executing. */
230         public static void run(String command, String options) {
231                 //IJ.log("run1: "+command+" "+Thread.currentThread().hashCode());
232                 if (ij==null && Menus.getCommands()==null)
233                         init();
234                 Macro.abort = false;
235                 Macro.setOptions(options);
236                 Thread thread = Thread.currentThread();
237                 if (previousThread==null || thread!=previousThread) {
238                         String name = thread.getName();
239                         if (!name.startsWith("Run$_"))
240                                 thread.setName("Run$_"+name);
241                 }
242                 command = convert(command);
243                 previousThread = thread;
244                 macroRunning = true;
245                 Executer e = new Executer(command);
246                 e.run();
247                 macroRunning = false;
248                 Macro.setOptions(null);
249                 testAbort();
250                 //IJ.log("run2: "+command+" "+Thread.currentThread().hashCode());
251         }
252         
253         /** Converts commands that have been renamed so 
254                 macros using the old names continue to work. */
255         private static String convert(String command) {
256                 if (commandTable==null) {
257                         commandTable = new Hashtable(23); // initial capacity should be increased as needed
258                         commandTable.put("New...", "Image...");
259                         commandTable.put("Threshold", "Make Binary");
260                         commandTable.put("Display...", "Appearance...");
261                         commandTable.put("Start Animation", "Start Animation [\\]");
262                         commandTable.put("Convert Images to Stack", "Images to Stack");
263                         commandTable.put("Convert Stack to Images", "Stack to Images");
264                         commandTable.put("Convert Stack to RGB", "Stack to RGB");
265                         commandTable.put("Convert to Composite", "Make Composite");
266                         commandTable.put("New HyperStack...", "New Hyperstack...");
267                         commandTable.put("Stack to HyperStack...", "Stack to Hyperstack...");
268                         commandTable.put("HyperStack to Stack", "Hyperstack to Stack");
269                         commandTable.put("RGB Split", "Split Channels");
270                         commandTable.put("RGB Merge...", "Merge Channels...");
271                         commandTable.put("Channels...", "Channels Tool...");
272                 }
273                 String command2 = (String)commandTable.get(command);
274                 if (command2!=null)
275                         return command2;
276                 else
277                         return command;
278         }
279
280     /** Runs an ImageJ command using the specified image and options. */
281         public static void run(ImagePlus imp, String command, String options) {
282                 if (imp!=null) WindowManager.setTempCurrentImage(imp);
283                 run(command, options);
284                 if (imp!=null) WindowManager.setTempCurrentImage(null);
285         }
286
287         static void init() {
288                 Menus m = new Menus(null, null);
289                 Prefs.load(m, null);
290                 m.addMenuBar();
291         }
292
293         private static void testAbort() {
294                 if (Macro.abort)
295                         abort();
296         }
297
298         /** Returns true if the run(), open() or newImage() method is executing. */
299         public static boolean macroRunning() {
300                 return macroRunning;
301         }
302
303         /** Returns true if a macro is running, or if the run(), open() 
304                 or newImage() method is executing. */
305         public static boolean isMacro() {
306                 return macroRunning || Interpreter.getInstance()!=null;
307         }
308
309         /**Returns the Applet that created this ImageJ or null if running as an application.*/
310         public static java.applet.Applet getApplet() {
311                 return applet;
312         }
313         
314         /**Displays a message in the ImageJ status bar.*/
315         public static void showStatus(String s) {
316                 if (ij!=null) ij.showStatus(s);
317                 ImagePlus imp = WindowManager.getCurrentImage();
318                 ImageCanvas ic = imp!=null?imp.getCanvas():null;
319                 if (ic!=null)
320                         ic.setShowCursorStatus(s.length()==0?true:false);
321         }
322
323         /** Displays a line of text in the "Results" window. Writes to
324                 System.out.println if the "ImageJ" frame is not present. */
325         public static void write(String s) {
326                 if (textPanel==null && ij!=null)
327                         showResults();
328                 if (textPanel!=null)
329                                 textPanel.append(s);
330                 else
331                         System.out.println(s);
332         }
333
334         private static void showResults() {
335                 TextWindow resultsWindow = new TextWindow("Results", "", 300, 200);
336                 textPanel = resultsWindow.getTextPanel();
337                 textPanel.setResultsTable(Analyzer.getResultsTable());
338                 if (ij!=null)
339                         textPanel.addKeyListener(ij);
340         }
341
342         public static synchronized void log(String s) {
343                 if (s==null) return;
344                 if (logPanel==null && ij!=null) {
345                         TextWindow logWindow = new TextWindow("Log", "", 350, 250);
346                         logPanel = logWindow.getTextPanel();
347                         logPanel.setFont(new Font("SansSerif", Font.PLAIN, 16));
348                 }
349                 if (logPanel!=null) {
350                         if (s.startsWith("\\"))
351                                 handleLogCommand(s);
352                         else
353                                 logPanel.append(s);
354                 } else
355                         System.out.println(s);
356         }
357
358         static void handleLogCommand(String s) {
359                 if (s.equals("\\Closed"))
360                         logPanel = null;
361                 else if (s.startsWith("\\Update:")) {
362                         int n = logPanel.getLineCount();
363                         String s2 = s.substring(8, s.length());
364                         if (n==0)
365                                 logPanel.append(s2);
366                         else
367                                 logPanel.setLine(n-1, s2);
368                 } else if (s.startsWith("\\Update")) {
369                         int cindex = s.indexOf(":");
370                         if (cindex==-1)
371                                 {logPanel.append(s); return;}
372                         String nstr = s.substring(7, cindex);
373                         int line = (int)Tools.parseDouble(nstr, -1);
374                         if (line<0 || line>25)
375                                 {logPanel.append(s); return;}
376                         int count = logPanel.getLineCount();
377                         while (line>=count) {
378                                 log("");
379                                 count++;
380                         }
381                         String s2 = s.substring(cindex+1, s.length());
382                         logPanel.setLine(line, s2);
383                 } else if (s.equals("\\Clear"))
384                         logPanel.clear();
385                 else
386                         logPanel.append(s);
387         }
388         
389         /** Clears the "Results" window and sets the column headings to
390                 those in the tab-delimited 'headings' String. Writes to
391                 System.out.println if the "ImageJ" frame is not present.*/
392         public static void setColumnHeadings(String headings) {
393                 if (textPanel==null && ij!=null)
394                         showResults();
395                 if (textPanel!=null)
396                         textPanel.setColumnHeadings(headings);
397                 else
398                         System.out.println(headings);
399         }
400
401         /** Returns true if the "Results" window is open. */
402         public static boolean isResultsWindow() {
403                 return textPanel!=null;
404         }
405         
406         /** Returns a reference to the "Results" window TextPanel.
407                 Opens the "Results" window if it is currently not open. */
408         public static TextPanel getTextPanel() {
409                 if (textPanel==null)
410                         showResults();
411                 return textPanel;
412         }
413         
414         /** TextWindow calls this method with a null argument when the "Results" window is closed. */
415         public static void setTextPanel(TextPanel tp) {
416                 textPanel = tp;
417         }
418     
419     /**Displays a "no images are open" dialog box.*/
420         public static void noImage() {
421                 error("No Image", "There are no images open.");
422         }
423
424         /** Displays an "out of memory" message to the "Log" window. */
425         public static void outOfMemory(String name) {
426                 Undo.reset();
427                 System.gc();
428                 String tot = Runtime.getRuntime().totalMemory()/1048576L+"MB";
429                 if (!memMessageDisplayed)
430                         log(">>>>>>>>>>>>>>>>>>>>>>>>>>>");
431                 log("<Out of memory>");
432                 if (!memMessageDisplayed) {
433                         log("<All available memory ("+tot+") has been>");
434                         log("<used. Instructions for making more>");
435                         log("<available can be found in the \"Memory\" >");
436                         log("<sections of the installation notes at>");
437                         log("<http://rsb.info.nih.gov/ij/docs/install/>");
438                         log(">>>>>>>>>>>>>>>>>>>>>>>>>>>");
439                         memMessageDisplayed = true;
440                 }
441                 Macro.abort();
442         }
443
444         /**     Updates the progress bar, where 0<=progress<=1.0. The progress bar is 
445         not shown in BatchMode and erased if progress>=1.0. The progress bar is
446     updated only if more than 90 ms have passes since the last call. Does nothing 
447         if the ImageJ window is not present. */
448         public static void showProgress(double progress) {
449                 if (progressBar!=null) progressBar.show(progress, false);
450         }
451
452         /**     Updates the progress bar, where the length of the bar is set to
453     (<code>currentValue+1)/finalValue</code> of the maximum bar length.
454     The bar is erased if <code>currentValue&gt;=finalValue</code>. 
455     The bar is updated only if more than 90 ms have passed since the last call.
456     Does nothing if the ImageJ window is not present. */
457     public static void showProgress(int currentIndex, int finalIndex) {
458                 if (progressBar!=null) progressBar.show(currentIndex, finalIndex);
459         }
460
461         /** Displays a message in a dialog box titled "Message".
462                 Writes the Java console if ImageJ is not present. */
463         public static void showMessage(String msg) {
464                 showMessage("Message", msg);
465         }
466
467         /**     Displays a message in a dialog box with the specified title.
468                 Writes the Java console if ImageJ is not present. */
469         public static void showMessage(String title, String msg) {
470                 if (redirectErrorMessages) {
471                         IJ.log(title + ": " + msg);
472                         redirectErrorMessages = false;
473                         return;
474                 }
475                 if (ij!=null) {
476                         if (msg!=null && msg.startsWith("<html>"))
477                                 new HTMLDialog(title, msg);
478                         else
479                                 new MessageDialog(ij, title, msg);
480                 } else
481                         System.out.println(msg);
482         }
483
484         /** Displays a message in a dialog box titled "ImageJ". If a 
485                 macro is running, it is aborted. Writes to the Java console
486                 if the ImageJ window is not present.*/
487         public static void error(String msg) {
488                 showMessage("ImageJA", msg);
489                 showMessage("ImageJ", msg);
490                 if (Thread.currentThread().getName().endsWith("JavaScript"))
491                         throw new RuntimeException(Macro.MACRO_CANCELED);
492                 else
493                         Macro.abort();
494         }
495         
496         /**     Displays a message in a dialog box with the specified title.
497                 If a macro is running, it is aborted. Writes to the Java  
498                 console if ImageJ is not present. */
499         public static synchronized void error(String title, String msg) {
500                 showMessage(title, msg);
501                 Macro.abort();
502         }
503
504         /** Displays a message in a dialog box with the specified title.
505                 Returns false if the user pressed "Cancel". */
506         public static boolean showMessageWithCancel(String title, String msg) {
507                 GenericDialog gd = new GenericDialog(title);
508                 gd.addMessage(msg);
509                 gd.showDialog();
510                 return !gd.wasCanceled();
511         }
512
513         public static final int CANCELED = Integer.MIN_VALUE;
514
515         /** Allows the user to enter a number in a dialog box. Returns the      
516             value IJ.CANCELED (-2,147,483,648) if the user cancels the dialog box. 
517             Returns 'defaultValue' if the user enters an invalid number. */
518         public static double getNumber(String prompt, double defaultValue) {
519                 GenericDialog gd = new GenericDialog("");
520                 int decimalPlaces = (int)defaultValue==defaultValue?0:2;
521                 gd.addNumericField(prompt, defaultValue, decimalPlaces);
522                 gd.showDialog();
523                 if (gd.wasCanceled())
524                         return CANCELED;
525                 double v = gd.getNextNumber();
526                 if (gd.invalidNumber())
527                         return defaultValue;
528                 else
529                         return v;
530         }
531
532         /** Allows the user to enter a string in a dialog box. Returns
533             "" if the user cancels the dialog box. */
534         public static String getString(String prompt, String defaultString) {
535                 GenericDialog gd = new GenericDialog("");
536                 gd.addStringField(prompt, defaultString, 20);
537                 gd.showDialog();
538                 if (gd.wasCanceled())
539                         return "";
540                 return gd.getNextString();
541         }
542
543         /**Delays 'msecs' milliseconds.*/
544         public static void wait(int msecs) {
545                 try {Thread.sleep(msecs);}
546                 catch (InterruptedException e) { }
547         }
548         
549         /** Emits an audio beep. */
550         public static void beep() {
551                 java.awt.Toolkit.getDefaultToolkit().beep();
552         }
553         
554         /**     Runs the garbage collector and returns a string something
555                 like "64K of 256MB (25%)" that shows how much of 
556                 the  available memory is in use. This is the string
557                 displayed when the user clicks in the status bar. */
558         public static String freeMemory() {
559                 System.gc();
560                 long inUse = currentMemory();
561                 String inUseStr = inUse<10000*1024?inUse/1024L+"K":inUse/1048576L+"MB";
562                 String maxStr="";
563                 long max = maxMemory();
564                 if (max>0L) {
565                         double percent = inUse*100/max;
566                         maxStr = " of "+max/1048576L+"MB ("+(percent<1.0?"<1":d2s(percent,0)) + "%)";
567                 }
568                 return  inUseStr + maxStr;
569         }
570         
571         /** Returns the amount of memory currently being used by ImageJ. */
572         public static long currentMemory() {
573                 long freeMem = Runtime.getRuntime().freeMemory();
574                 long totMem = Runtime.getRuntime().totalMemory();
575                 return totMem-freeMem;
576         }
577
578         /** Returns the maximum amount of memory available to ImageJ or
579                 zero if ImageJ is unable to determine this limit. */
580         public static long maxMemory() {
581                 if (maxMemory==0L) {
582                         Memory mem = new Memory();
583                         maxMemory = mem.getMemorySetting();
584                         if (maxMemory==0L) maxMemory = mem.maxMemory();
585                 }
586                 return maxMemory;
587         }
588         
589         public static void showTime(ImagePlus imp, long start, String str) {
590                 showTime(imp, start, str, 1);
591         }
592         
593         public static void showTime(ImagePlus imp, long start, String str, int nslices) {
594                 if (Interpreter.isBatchMode()) return;
595             long elapsedTime = System.currentTimeMillis() - start;
596                 double seconds = elapsedTime / 1000.0;
597                 long pixels = imp.getWidth() * imp.getHeight();
598                 int rate = (int)((double)pixels*nslices/seconds);
599                 String str2;
600                 if (rate>1000000000)
601                         str2 = "";
602                 else if (rate<1000000)
603                         str2 = ", "+rate+" pixels/second";
604                 else
605                         str2 = ", "+d2s(rate/1000000.0,1)+" million pixels/second";
606                 showStatus(str+seconds+" seconds"+str2);
607         }
608
609         /** Converts a number to a formatted string using
610                 2 digits to the right of the decimal point. */
611         public static String d2s(double n) {
612                 return d2s(n, 2);
613         }
614         
615         private static DecimalFormat[] df;
616
617         /** Converts a number to a rounded formatted string.
618                 The 'decimalPlaces' argument specifies the number of
619                 digits to the right of the decimal point (0-9). */
620         public static String d2s(double n, int decimalPlaces) {
621                 if (Double.isNaN(n))
622                         return "NaN";
623                 if (n==Float.MAX_VALUE) // divide by 0 in FloatProcessor
624                         return "3.4e38";
625                 double np = n;
626                 if (n<0.0) np = -n;
627                 if ((np<0.001 && np!=0.0 && np<1.0/Math.pow(10,decimalPlaces)) || np>999999999999d)
628                         return Float.toString((float)n); // use scientific notation
629                 if (df==null) {
630                         DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US);
631                         df = new DecimalFormat[10];
632                         df[0] = new DecimalFormat("0", dfs);
633                         df[1] = new DecimalFormat("0.0", dfs);
634                         df[2] = new DecimalFormat("0.00", dfs);
635                         df[3] = new DecimalFormat("0.000", dfs);
636                         df[4] = new DecimalFormat("0.0000", dfs);
637                         df[5] = new DecimalFormat("0.00000", dfs);
638                         df[6] = new DecimalFormat("0.000000", dfs);
639                         df[7] = new DecimalFormat("0.0000000", dfs);
640                         df[8] = new DecimalFormat("0.00000000", dfs);
641                         df[9] = new DecimalFormat("0.000000000", dfs);
642                 }
643                 if (decimalPlaces<0) decimalPlaces = 0;
644                 if (decimalPlaces>9) decimalPlaces = 9;
645                 return df[decimalPlaces].format(n);
646         }
647
648         /** Adds the specified class to a Vector to keep it from being garbage
649         collected, which would cause the classes static fields to be reset. 
650         Probably not needed with Java 1.2 or later. */
651         public static void register(Class c) {
652                 if (ij!=null) ij.register(c);
653         }
654         
655         /** Returns true if the space bar is down. */
656         public static boolean spaceBarDown() {
657                 return spaceDown;
658         }
659
660         /** Returns true if the alt key is down. */
661         public static boolean altKeyDown() {
662                 return altDown;
663         }
664
665         /** Returns true if the shift key is down. */
666         public static boolean shiftKeyDown() {
667                 return shiftDown;
668         }
669
670         public static void setKeyDown(int key) {
671                 //IJ.showStatus("setKeyDown: "+key);
672                 switch (key) {
673                         case KeyEvent.VK_ALT:
674                                 altDown=true;
675                                 break;
676                         case KeyEvent.VK_SHIFT:
677                                 shiftDown=true;
678                                 if (debugMode) beep();
679                                 break;
680                         case KeyEvent.VK_SPACE: {
681                                 spaceDown=true;
682                                 ImageWindow win = WindowManager.getCurrentWindow();
683                                 if (win!=null) win.getCanvas().setCursor(-1,-1,-1, -1);
684                                 break;
685                         }
686                         case KeyEvent.VK_ESCAPE: {
687                                 escapePressed = true;
688                                 break;
689                         }
690                 }
691         }
692         
693         public static void setKeyUp(int key) {
694                 //IJ.showStatus("setKeyUp: "+key);
695                 switch (key) {
696                         case KeyEvent.VK_ALT: altDown=false; break;
697                         case KeyEvent.VK_SHIFT: shiftDown=false; if (debugMode) beep(); break;
698                         case KeyEvent.VK_SPACE: {
699                                 spaceDown=false;
700                                 ImageWindow win = WindowManager.getCurrentWindow();
701                                 if (win!=null) win.getCanvas().setCursor(-1,-1,-1,-1);
702                                 break;
703                         }
704                         case ALL_KEYS: altDown=shiftDown=spaceDown=false; break;
705                 }
706         }
707         
708         public static void setInputEvent(InputEvent e) {
709                 altDown = e.isAltDown();
710                 shiftDown = e.isShiftDown();
711         }
712
713         /** Returns true if this machine is a Macintosh. */
714         public static boolean isMacintosh() {
715                 return isMac;
716         }
717         
718         /** Returns true if this machine is a Macintosh running OS X. */
719         public static boolean isMacOSX() {
720                 return isMacintosh();
721         }
722
723         /** Returns true if this machine is running Windows. */
724         public static boolean isWindows() {
725                 return isWin;
726         }
727         
728         /** Always returns true. */
729         public static boolean isJava2() {
730                 return isJava2;
731         }
732         
733         /** Returns true if ImageJ is running on a Java 1.4 or greater JVM. */
734         public static boolean isJava14() {
735                 return isJava14;
736         }
737
738         /** Returns true if ImageJ is running on a Java 1.5 or greater JVM. */
739         public static boolean isJava15() {
740                 return isJava15;
741         }
742
743         /** Returns true if ImageJ is running on a Java 1.6 or greater JVM. */
744         public static boolean isJava16() {
745                 return isJava16;
746         }
747
748         /** Returns true if ImageJ is running on Linux. */
749         public static boolean isLinux() {
750                 return isLinux;
751         }
752
753         /** Returns true if ImageJ is running on Windows Vista. */
754         public static boolean isVista() {
755                 return isVista;
756         }
757         
758         /** Returns true if ImageJ is running a 64-bit version of Java. */
759         public static boolean is64Bit() {
760                 if (osarch==null)
761                         osarch = System.getProperty("os.arch");
762                 return osarch!=null && osarch.indexOf("64")!=-1;
763         }
764
765         /** Displays an error message and returns false if the
766                 ImageJ version is less than the one specified. */
767         public static boolean versionLessThan(String version) {
768                 boolean lessThan = ImageJ.VERSION.compareTo(version)<0;
769                 if (lessThan)
770                         error("This plugin or macro requires ImageJA "+version+" or later.");
771                 return lessThan;
772         }
773         
774         /** Displays a "Process all images?" dialog. Returns
775                 'flags'+PlugInFilter.DOES_STACKS if the user selects "Yes",
776                 'flags' if the user selects "No" and PlugInFilter.DONE
777                 if the user selects "Cancel".
778         */
779         public static int setupDialog(ImagePlus imp, int flags) {
780                 if (imp==null || (ij!=null&&ij.hotkey) || hideProcessStackDialog)
781                         return flags;
782                 int stackSize = imp.getStackSize();
783                 if (stackSize>1) {
784                         if (imp.isComposite() && ((CompositeImage)imp).getMode()==CompositeImage.COMPOSITE)
785                                 return flags+PlugInFilter.DOES_STACKS;
786                         String macroOptions = Macro.getOptions();
787                         if (macroOptions!=null) {
788                                 if (macroOptions.indexOf("stack ")>=0)
789                                         return flags+PlugInFilter.DOES_STACKS;
790                                 else
791                                         return flags;
792                         }
793                         YesNoCancelDialog d = new YesNoCancelDialog(getInstance(),
794                                 "Process Stack?", "Process all "+stackSize+" images?  There is\n"
795                                 +"no Undo if you select \"Yes\".");
796                         if (d.cancelPressed())
797                                 return PlugInFilter.DONE;
798                         else if (d.yesPressed()) {
799                         if (imp.getStack().isVirtual()) {
800                                 error("Custom code is required to process virtual stacks.");
801                                         return PlugInFilter.DONE;
802                         }
803                                 if (Recorder.record)
804                                         Recorder.recordOption("stack");
805                                 return flags+PlugInFilter.DOES_STACKS;
806                         }
807                         if (Recorder.record)
808                                 Recorder.recordOption("slice");
809                 }
810                 return flags;
811         }
812         
813         /** Creates a rectangular selection. Removes any existing 
814                 selection if width or height are less than 1. */
815         public static void makeRectangle(int x, int y, int width, int height) {
816                 if (width<=0 || height<0)
817                         getImage().killRoi();
818                 else {
819                         ImagePlus img = getImage();
820                         img.setRoi(x, y, width, height);
821                 }
822         }
823         
824         /** Creates an elliptical selection. Removes any existing 
825                 selection if width or height are less than 1. */
826         public static void makeOval(int x, int y, int width, int height) {
827                 if (width<=0 || height<0)
828                         getImage().killRoi();
829                 else {
830                         ImagePlus img = getImage();
831                         img.setRoi(new OvalRoi(x, y, width, height));
832                 }
833         }
834         
835         /** Creates a straight line selection. */
836         public static void makeLine(int x1, int y1, int x2, int y2) {
837                 getImage().setRoi(new Line(x1, y1, x2, y2));
838         }
839         
840         /** Creates a straight line selection using double coordinates. */
841         public static void makeLine(double x1, double y1, double x2, double y2) {
842                 getImage().setRoi(new Line(x1, y1, x2, y2));
843         }
844
845         /** Sets the minimum and maximum displayed pixel values. */
846         public static void setMinAndMax(double min, double max) {
847                 ImagePlus img = getImage();
848                 if (img.getBitDepth()==16) {
849                         Calibration cal = img.getCalibration();
850                         min = cal.getRawValue(min); 
851                         max = cal.getRawValue(max); 
852                 }
853                 img.setDisplayRange(min, max);
854                 img.updateAndDraw();
855         }
856
857         /** Resets the minimum and maximum displayed pixel values
858                 to be the same as the min and max pixel values. */
859         public static void resetMinAndMax() {
860                 ImagePlus img = getImage();
861                 img.resetDisplayRange();
862                 img.updateAndDraw();
863         }
864
865         /** Sets the lower and upper threshold levels and displays the image 
866                 using red to highlight thresholded pixels. May not work correctly on
867                 16 and 32 bit images unless the display range has been reset using IJ.resetMinAndMax().
868         */
869         public static void setThreshold(double lowerThreshold, double upperThresold) {
870                 setThreshold(lowerThreshold, upperThresold, null);
871         }
872         
873         /** Sets the lower and upper threshold levels and displays the image using
874                 the specified <code>displayMode</code> ("Red", "Black & White", "Over/Under" or "No Update"). */
875         public static void setThreshold(double lowerThreshold, double upperThreshold, String displayMode) {
876                 int mode = ImageProcessor.RED_LUT;
877                 if (displayMode!=null) {
878                         displayMode = displayMode.toLowerCase(Locale.US);
879                         if (displayMode.indexOf("black")!=-1)
880                                 mode = ImageProcessor.BLACK_AND_WHITE_LUT;
881                         else if (displayMode.indexOf("over")!=-1)
882                                 mode = ImageProcessor.OVER_UNDER_LUT;
883                         else if (displayMode.indexOf("no")!=-1)
884                                 mode = ImageProcessor.NO_LUT_UPDATE;
885                 }
886                 ImagePlus img = getImage();
887                 if (img.getBitDepth()==16) {
888                         Calibration cal = img.getCalibration();
889                         lowerThreshold = cal.getRawValue(lowerThreshold); 
890                         upperThreshold = cal.getRawValue(upperThreshold); 
891                 }
892                 img.getProcessor().setThreshold(lowerThreshold, upperThreshold, mode);
893                 if (mode != ImageProcessor.NO_LUT_UPDATE) {
894                         img.getProcessor().setLutAnimation(true);
895                         img.updateAndDraw();
896                 }
897         }
898
899         /** Disables thresholding. */
900         public static void resetThreshold() {
901                 ImagePlus img = getImage();
902                 ImageProcessor ip = img.getProcessor();
903                 ip.resetThreshold();
904                 ip.setLutAnimation(true);
905                 img.updateAndDraw();
906         }
907         
908         /** For IDs less than zero, activates the image with the specified ID.
909                 For IDs greater than zero, activates the Nth image. */
910         public static void selectWindow(int id) {
911                 if (id>0)
912                         id = WindowManager.getNthImageID(id);
913                 ImagePlus imp = WindowManager.getImage(id);
914                 if (imp==null)
915                         error("Macro Error", "Image "+id+" not found or no images are open.");
916                 if (Interpreter.isBatchMode()) {
917                         ImagePlus imp2 = WindowManager.getCurrentImage();
918                         if (imp2!=null && imp2!=imp) imp2.saveRoi();
919             WindowManager.setTempCurrentImage(imp);
920             WindowManager.setWindow(null);
921                 } else {
922                         ImageWindow win = imp.getWindow();
923                         win.toFront();
924                         WindowManager.setWindow(win);
925                         long start = System.currentTimeMillis();
926                         // timeout after 2 seconds unless current thread is event dispatch thread
927                         String thread = Thread.currentThread().getName();
928                         int timeout = thread!=null&&thread.indexOf("EventQueue")!=-1?0:2000;
929                         while (true) {
930                                 wait(10);
931                                 imp = WindowManager.getCurrentImage();
932                                 if (imp!=null && imp.getID()==id)
933                                         return; // specified image is now active
934                                 if ((System.currentTimeMillis()-start)>timeout) {
935                                         WindowManager.setCurrentWindow(win);
936                                         return;
937                                 }
938                         }
939                 }
940         }
941
942         /** Activates the window with the specified title. */
943         public static void selectWindow(String title) {
944                 long start = System.currentTimeMillis();
945                 while (System.currentTimeMillis()-start<3000) { // 3 sec timeout
946                         Frame frame = WindowManager.getFrame(title);
947                         if (frame!=null && !(frame instanceof ImageWindow)) {
948                                 selectWindow(frame);
949                                 return;
950                         }
951                         int[] wList = WindowManager.getIDList();
952                         int len = wList!=null?wList.length:0;
953                         for (int i=0; i<len; i++) {
954                                 ImagePlus imp = WindowManager.getImage(wList[i]);
955                                 if (imp!=null) {
956                                         if (imp.getTitle().equals(title)) {
957                                                 selectWindow(imp.getID());
958                                                 return;
959                                         }
960                                 }
961                         }
962                         wait(10);
963                 }
964                 error("Macro Error", "No window with the title \""+title+"\" found.");
965         }
966         
967         static void selectWindow(Frame frame) {
968                 frame.toFront();
969                 long start = System.currentTimeMillis();
970                 while (true) {
971                         wait(10);
972                         if (WindowManager.getFrontWindow()==frame)
973                                 return; // specified window is now in front
974                         if ((System.currentTimeMillis()-start)>1000) {
975                                 WindowManager.setWindow(frame);
976                                 return;   // 1 second timeout
977                         }
978                 }
979         }
980         
981         /** Sets the foreground color. */
982         public static void setForegroundColor(int red, int green, int blue) {
983                 setColor(red, green, blue, true);
984         }
985
986         /** Sets the background color. */
987         public static void setBackgroundColor(int red, int green, int blue) {
988                 setColor(red, green, blue, false);
989         }
990         
991         static void setColor(int red, int green, int blue, boolean foreground) {
992             if (red<0) red=0; if (green<0) green=0; if (blue<0) blue=0; 
993             if (red>255) red=255; if (green>255) green=255; if (blue>255) blue=255;  
994                 Color c = new Color(red, green, blue);
995                 if (foreground) {
996                         Toolbar.setForegroundColor(c);
997                         ImagePlus img = WindowManager.getCurrentImage();
998                         if (img!=null)
999                                 img.getProcessor().setColor(c);
1000                 } else
1001                         Toolbar.setBackgroundColor(c);
1002         }
1003
1004         /** Switches to the specified tool, where id = Toolbar.RECTANGLE (0),
1005                 Toolbar.OVAL (1), etc. */
1006         public static void setTool(int id) {
1007                 Toolbar.getInstance().setTool(id);
1008         }
1009
1010         /** Switches to the specified tool, where 'name' is "rect", "elliptical", 
1011                 "brush", etc. Returns 'false' if the name is not recognized. */
1012         public static boolean setTool(String name) {
1013                 return Toolbar.getInstance().setTool(name);
1014         }
1015
1016         /** Equivalent to clicking on the current image at (x,y) with the
1017                 wand tool. Returns the number of points in the resulting ROI. */
1018         public static int doWand(int x, int y) {
1019                 ImagePlus img = getImage();
1020                 ImageProcessor ip = img.getProcessor();
1021                 if ((img.getType()==ImagePlus.GRAY32) && Double.isNaN(ip.getPixelValue(x,y)))
1022                         return 0;
1023                 Wand w = new Wand(ip);
1024                 double t1 = ip.getMinThreshold();
1025                 if (t1==ImageProcessor.NO_THRESHOLD)
1026                         w.autoOutline(x, y);
1027                 else
1028                         w.autoOutline(x, y, t1, ip.getMaxThreshold());
1029                 if (w.npoints>0) {
1030                         Roi previousRoi = img.getRoi();
1031                         Roi roi = new PolygonRoi(w.xpoints, w.ypoints, w.npoints, Roi.TRACED_ROI);
1032                         img.killRoi();
1033                         img.setRoi(roi);
1034                         // add/subtract this ROI to the previous one if the shift/alt key is down
1035                         if (previousRoi!=null)
1036                                 roi.update(shiftKeyDown(), altKeyDown());
1037                 }
1038                 return w.npoints;
1039         }
1040         
1041         /** Sets the transfer mode used by the <i>Edit/Paste</i> command, where mode is "Copy", "Blend", "Average", "Difference", 
1042                 "Transparent", "Transparent2", "AND", "OR", "XOR", "Add", "Subtract", "Multiply", or "Divide". */
1043         public static void setPasteMode(String mode) {
1044                 mode = mode.toLowerCase(Locale.US);
1045                 int m = Blitter.COPY;
1046                 if (mode.startsWith("ble") || mode.startsWith("ave"))
1047                         m = Blitter.AVERAGE;
1048                 else if (mode.startsWith("diff"))
1049                         m = Blitter.DIFFERENCE;
1050                 else if (mode.indexOf("zero")!=-1)
1051                         m = Blitter.COPY_ZERO_TRANSPARENT;
1052                 else if (mode.startsWith("tran"))
1053                         m = Blitter.COPY_TRANSPARENT;
1054                 else if (mode.startsWith("and"))
1055                         m = Blitter.AND;
1056                 else if (mode.startsWith("or"))
1057                         m = Blitter.OR;
1058                 else if (mode.startsWith("xor"))
1059                         m = Blitter.XOR;
1060                 else if (mode.startsWith("sub"))
1061                         m = Blitter.SUBTRACT;
1062                 else if (mode.startsWith("add"))
1063                         m = Blitter.ADD;
1064                 else if (mode.startsWith("div"))
1065                         m = Blitter.DIVIDE;
1066                 else if (mode.startsWith("mul"))
1067                         m = Blitter.MULTIPLY;
1068                 else if (mode.startsWith("min"))
1069                         m = Blitter.MIN;
1070                 else if (mode.startsWith("max"))
1071                         m = Blitter.MAX;
1072                 Roi.setPasteMode(m);
1073         }
1074
1075         /** Returns a reference to the active image. Displays an error
1076                 message and aborts the macro if no images are open. */
1077         public static ImagePlus getImage() {  //ts
1078                 ImagePlus img = WindowManager.getCurrentImage();
1079                 if (img==null) {
1080                         IJ.noImage();
1081                         abort();
1082                 }
1083                 return img;
1084         }
1085         
1086         /** Switches to the specified stack slice, where 1<='slice'<=stack-size. */
1087         public static void setSlice(int slice) {
1088                 getImage().setSlice(slice);
1089         }
1090
1091         /** Returns the ImageJ version number as a string. */
1092         public static String getVersion() {
1093                 return ImageJ.VERSION;
1094         }
1095         
1096         /** Returns the path to the home ("user.home"), startup (ImageJ directory), plugins, macros, 
1097                 temp, current or image directory if <code>title</code> is "home", "startup", 
1098                 "plugins", "macros", "temp", "current" or "image", otherwise, displays a dialog 
1099                 and returns the path to the directory selected by the user. 
1100                 Returns null if the specified directory is not found or the user
1101                 cancels the dialog box. Also aborts the macro if the user cancels
1102                 the dialog box.*/
1103         public static String getDirectory(String title) {
1104                 if (title.equals("plugins"))
1105                         return Menus.getPlugInsPath();
1106                 else if (title.equals("macros"))
1107                         return Menus.getMacrosPath();
1108                 else if (title.equals("luts"))
1109                         return Prefs.getHomeDir()+File.separator+"luts"+File.separator;
1110                 else if (title.equals("home"))
1111                         return System.getProperty("user.home") + File.separator;
1112                 else if (title.equals("startup")||title.equals("imagej"))
1113                         return Prefs.getHomeDir() + File.separator;
1114                 else if (title.equals("current"))
1115                         return OpenDialog.getDefaultDirectory();
1116                 else if (title.equals("temp")) {
1117                         String dir = System.getProperty("java.io.tmpdir");
1118                         if (dir!=null && !dir.endsWith(File.separator)) dir += File.separator;
1119                         return dir;
1120                 } else if (title.equals("image")) {
1121                         ImagePlus imp = WindowManager.getCurrentImage();
1122                 FileInfo fi = imp!=null?imp.getOriginalFileInfo():null;
1123                         if (fi!=null && fi.directory!=null)
1124                                 return fi.directory;
1125                         else
1126                                 return null;
1127                 } else {
1128                         DirectoryChooser dc = new DirectoryChooser(title);
1129                         String dir = dc.getDirectory();
1130                         if (dir==null)
1131                                 Macro.abort();
1132                         else if (Recorder.record)
1133                                 Recorder.record("getDirectory", dir);
1134                         return dir;
1135                 }
1136         }
1137         
1138         /** Displays a file open dialog box and then opens the tiff, dicom, 
1139                 fits, pgm, jpeg, bmp, gif, lut, roi, or text file selected by 
1140                 the user. Displays an error message if the selected file is not
1141                 in one of the supported formats, or if it is not found. */
1142         public static void open() {
1143                 open(null);
1144         }
1145
1146         /** Opens and displays a tiff, dicom, fits, pgm, jpeg, bmp, gif, lut, 
1147                 roi, or text file. Displays an error message if the specified file
1148                 is not in one of the supported formats, or if it is not found. */
1149         public static void open(String path) {
1150                 if (ij==null && Menus.getCommands()==null) init();
1151                 Opener o = new Opener();
1152                 macroRunning = true;
1153                 if (path==null || path.equals(""))              
1154                         o.open();
1155                 else
1156                         o.open(path);
1157                 macroRunning = false;
1158         }
1159                 
1160         /** Open the specified file as a tiff, bmp, dicom, fits, pgm, gif 
1161                 or jpeg image and returns an ImagePlus object if successful.
1162                 Calls HandleExtraFileTypes plugin if the file type is not recognised.
1163                 Note that 'path' can also be a URL. */
1164         public static ImagePlus openImage(String path) {
1165                 return (new Opener()).openImage(path);
1166         }
1167
1168         /** Saves the current image, lookup table, selection or text window to the specified file path. 
1169                 The path must end in ".tif", ".jpg", ".gif", ".zip", ".raw", ".avi", ".bmp", ".fits", ".pgm", ".png", ".lut", ".roi" or ".txt".  */
1170         public static void save(String path) {
1171                 save(null, path);
1172         }
1173
1174         /** Saves the specified image, lookup table or selection to the specified file path. 
1175                 The path must end in ".tif", ".jpg", ".gif", ".zip", ".raw", ".avi", ".bmp", ".fits", ".pgm", ".png", ".lut", ".roi" or ".txt".  */
1176         public static void save(ImagePlus imp, String path) {
1177                 int dotLoc = path.lastIndexOf('.');
1178                 if (dotLoc!=-1)
1179                         saveAs(imp, path.substring(dotLoc+1), path);
1180                 else
1181                         error("The save() macro function requires a file name extension.\n \n"+path);
1182         }
1183
1184         /* Saves the active image, lookup table, selection, measurement results, selection XY 
1185                 coordinates or text window to the specified file path. The format argument must be "tiff", 
1186                 "jpeg", "gif", "zip", "raw", "avi", "bmp", "fits", "pgm", "png", "text image", "lut", "selection", "measurements", 
1187                 "xy Coordinates" or "text".  If <code>path</code> is null or an emply string, a file
1188                 save dialog is displayed. */
1189         public static void saveAs(String format, String path) {
1190                 saveAs(null, format, path);
1191         }
1192
1193         /* Saves the specified image. The format argument must be "tiff",  
1194                 "jpeg", "gif", "zip", "raw", "avi", "bmp", "fits", "pgm", "png", 
1195                 "text image", "lut", "selection" or "xy Coordinates". */
1196         public static void saveAs(ImagePlus imp, String format, String path) {
1197                 if (format==null) return;
1198                 if (path!=null && path.length()==0) path = null;
1199                 format = format.toLowerCase(Locale.US);
1200                 if (format.indexOf("tif")!=-1) {
1201                         path = updateExtension(path, ".tif");
1202                         format = "Tiff...";
1203                 } else if (format.indexOf("jpeg")!=-1  || format.indexOf("jpg")!=-1) {
1204                         path = updateExtension(path, ".jpg");
1205                         format = "Jpeg...";
1206                 } else if (format.indexOf("gif")!=-1) {
1207                         path = updateExtension(path, ".gif");
1208                         format = "Gif...";
1209                 } else if (format.indexOf("text image")!=-1) {
1210                         path = updateExtension(path, ".txt");
1211                         format = "Text Image...";
1212                 } else if (format.indexOf("text")!=-1 || format.indexOf("txt")!=-1) {
1213                         if (path!=null && !path.endsWith(".xls"))
1214                                 path = updateExtension(path, ".txt");
1215                         format = "Text...";
1216                 } else if (format.indexOf("zip")!=-1) {
1217                         path = updateExtension(path, ".zip");
1218                         format = "ZIP...";
1219                 } else if (format.indexOf("raw")!=-1) {
1220                         path = updateExtension(path, ".raw");
1221                         format = "Raw Data...";
1222                 } else if (format.indexOf("avi")!=-1) {
1223                         path = updateExtension(path, ".avi");
1224                         format = "AVI... ";
1225                 } else if (format.indexOf("bmp")!=-1) {
1226                         path = updateExtension(path, ".bmp");
1227                         format = "BMP...";
1228                 } else if (format.indexOf("fits")!=-1) {
1229                         path = updateExtension(path, ".fits");
1230                         format = "FITS...";
1231                 } else if (format.indexOf("png")!=-1) {
1232                         path = updateExtension(path, ".png");
1233                         format = "PNG...";
1234                 } else if (format.indexOf("pgm")!=-1) {
1235                         path = updateExtension(path, ".pgm");
1236                         format = "PGM...";
1237                 } else if (format.indexOf("lut")!=-1) {
1238                         path = updateExtension(path, ".lut");
1239                         format = "LUT...";
1240                 } else if (format.indexOf("measurements")!=-1) {
1241                         format = "Measurements...";
1242                 } else if (format.indexOf("selection")!=-1 || format.indexOf("roi")!=-1) {
1243                         path = updateExtension(path, ".roi");
1244                         format = "Selection...";
1245                 } else if (format.indexOf("xy")!=-1 || format.indexOf("coordinates")!=-1) {
1246                         path = updateExtension(path, ".txt");
1247                         format = "XY Coordinates...";
1248                 } else
1249                         error("Unrecognized format: "+format);
1250                 if (path==null)
1251                         run(format);
1252                 else
1253                         run(imp, format, "save=["+path+"]");
1254         }
1255
1256         static String updateExtension(String path, String extension) {
1257                 if (path==null) return null;
1258                 int dotIndex = path.lastIndexOf(".");
1259                 if (dotIndex>=0)
1260                         path = path.substring(0, dotIndex) + extension;
1261                 else
1262                         path += extension;
1263                 return path;
1264         }
1265
1266          /** Creates a new imagePlus. <code>Type</code> should contain "8-bit", "16-bit", "32-bit" or "RGB". 
1267                  In addition, it can contain "white", "black" or "ramp" (the default is "white"). <code>Width</code> 
1268                 and <code>height</code> specify the width and height of the image in pixels.  
1269                 <code>Depth</code> specifies the number of stack slices. */
1270          public static ImagePlus createImage(String title, String type, int width, int height, int depth) {
1271                 type = type.toLowerCase(Locale.US);
1272                 int bitDepth = 8;
1273                 if (type.indexOf("16")!=-1) bitDepth = 16;
1274                 if (type.indexOf("24")!=-1||type.indexOf("rgb")!=-1) bitDepth = 24;
1275                 if (type.indexOf("32")!=-1) bitDepth = 32;
1276                 int options = NewImage.FILL_WHITE;
1277                 if (bitDepth==16 || bitDepth==32)
1278                         options = NewImage.FILL_BLACK;
1279                 if (type.indexOf("white")!=-1)
1280                         options = NewImage.FILL_WHITE;
1281                 else if (type.indexOf("black")!=-1)
1282                         options = NewImage.FILL_BLACK;
1283                 else if (type.indexOf("ramp")!=-1)
1284                         options = NewImage.FILL_RAMP;
1285                 options += NewImage.CHECK_AVAILABLE_MEMORY;
1286                 return NewImage.createImage(title, width, height, depth, bitDepth, options);
1287         }
1288
1289          /** Opens a new image. <code>Type</code> should contain "8-bit", "16-bit", "32-bit" or "RGB". 
1290                 In addition, it can contain "white", "black" or "ramp" (the default is "white"). <code>Width</code> 
1291                 and <code>height</code> specify the width and height of the image in pixels.  
1292                 <code>Depth</code> specifies the number of stack slices. */
1293         public static void newImage(String title, String type, int width, int height, int depth) {
1294                 ImagePlus imp = createImage(title, type, width, height, depth);
1295                 if (imp!=null) {
1296                         macroRunning = true;
1297                         imp.show();
1298                         macroRunning = false;
1299                 }
1300         }
1301
1302         /** Returns true if the <code>Esc</code> key was pressed since the
1303                 last ImageJ command started to execute or since resetEscape() was called. */
1304         public static boolean escapePressed() {
1305                 return escapePressed;
1306         }
1307
1308         /** This method sets the <code>Esc</code> key to the "up" position.
1309                 The Executer class calls this method when it runs 
1310                 an ImageJ command in a separate thread. */
1311         public static void resetEscape() {
1312                 escapePressed = false;
1313         }
1314         
1315         /** Causes IJ.error() and IJ.showMessage() output to be temporarily redirected to the "Log" window. */
1316         public static void redirectErrorMessages() {
1317                 redirectErrorMessages = true;
1318         }
1319         
1320         /** Returns the state of the  'redirectErrorMessages' flag. The File/Import/Image Sequence command sets this flag.*/
1321         public static boolean redirectingErrorMessages() {
1322                 return redirectErrorMessages;
1323         }
1324
1325         /** Temporarily suppress "plugin not found" errors. */
1326         public static void suppressPluginNotFoundError() {
1327                 suppressPluginNotFoundError = true;
1328         }
1329
1330         /** Returns an instance of the class loader ImageJ uses to run plugins. */
1331         public static ClassLoader getClassLoader() {
1332                 if (classLoader==null) {
1333                         String pluginsDir = Menus.getPlugInsPath();
1334                         if (pluginsDir==null) {
1335                                 String home = System.getProperty("plugins.dir");
1336                                 if (home!=null) {
1337                                         if (!home.endsWith(Prefs.separator)) home+=Prefs.separator;
1338                                         pluginsDir = home+"plugins"+Prefs.separator;
1339                                         if (!(new File(pluginsDir)).isDirectory()) pluginsDir = home;
1340                                 }
1341                         }
1342                         if (pluginsDir==null)
1343                                 return IJ.class.getClassLoader();
1344                         else {
1345                                 if (Menus.jnlp)
1346                                         classLoader = new PluginClassLoader(pluginsDir, true);
1347                                 else
1348                                         classLoader = new PluginClassLoader(pluginsDir);
1349                         }
1350                 }
1351                 return classLoader;
1352         }
1353         
1354         /** Returns the size, in pixels, of the primary display. */
1355         public static Dimension getScreenSize() {
1356                 if (screenSize==null) {
1357                         if (isWindows()) { // GraphicsEnvironment.getConfigurations is *very* slow on Windows
1358                                 screenSize = Toolkit.getDefaultToolkit().getScreenSize();
1359                                 return screenSize;
1360                         }
1361                         if (GraphicsEnvironment.isHeadless())
1362                                 screenSize = new Dimension(0, 0);
1363                         else {
1364                                 // Can't use Toolkit.getScreenSize() on Linux because it returns 
1365                                 // size of all displays rather than just the primary display.
1366                                 GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
1367                                 GraphicsDevice[] gd = ge.getScreenDevices();
1368                                 GraphicsConfiguration[] gc = gd[0].getConfigurations();
1369                                 Rectangle bounds = gc[0].getBounds();
1370                                 if (bounds.x==0&&bounds.y==0)
1371                                         screenSize = new Dimension(bounds.width, bounds.height);
1372                                 else
1373                                         screenSize = Toolkit.getDefaultToolkit().getScreenSize();
1374                         }
1375                 }
1376                 return screenSize;
1377         }
1378         
1379         static void abort() {
1380                 if (ij!=null || Interpreter.isBatchMode())
1381                         throw new RuntimeException(Macro.MACRO_CANCELED);
1382         }
1383     
1384         
1385 }