| MacroRunner |

    package ij.macro;
    import ij.*;
    import ij.text.*;
    import ij.util.*;
    import java.io.*;

    /** This class runs macros in a separate thread. */
    public class MacroRunner implements Runnable {

        private String macro;
        private Program pgm;
        private int address;
        private String name;
        private Thread thread;
        private String argument;

        /** Create a MacrRunner. */
        public MacroRunner() {
        }

        /** Create a new object that interprets macro source in a separate thread. */
        public MacroRunner(String macro) {
            this.macro = macro;
            thread = new Thread(this, "Macro$");
            thread.setPriority(Math.max(thread.getPriority()-2, Thread.MIN_PRIORITY));
            thread.start();
        }

        /** Create a new object that interprets macro source in a
            separate thread, and also passing a string argument. */
        public MacroRunner(String macro, String argument) {
            this.macro = macro;
            this.argument = argument;
            thread = new Thread(this, "Macro$");
            thread.setPriority(Math.max(thread.getPriority()-2, Thread.MIN_PRIORITY));
            thread.start();
        }

        /** Create a new object that interprets a macro file using a separate thread. */
        public MacroRunner(File file) {
            int size = (int)file.length();
            if (size<=0)
                return;
            try {
                StringBuffer sb = new StringBuffer(5000);
                BufferedReader r = new BufferedReader(new FileReader(file));
                while (true) {
                    String s=r.readLine();
                    if (s==null)
                        break;
                    else
                        sb.append(s+"\n");
                }
                r.close();
                macro = new String(sb);
            }
            catch (Exception e) {
                IJ.error(e.getMessage());
                return;
            }
            thread = new Thread(this, "Macro$");
            thread.setPriority(Math.max(thread.getPriority()-2, Thread.MIN_PRIORITY));
            thread.start();
        }

        /** Create a new object that runs a tokenized macro in a separate thread. */
        public MacroRunner(Program pgm, int address, String name) {
            this(pgm, address, name, null);
        }

        /** Create a new object that runs a tokenized macro in a separate thread,
            passing a string argument. */
        public MacroRunner(Program pgm, int address, String name, String argument) {
            this.pgm = pgm;
            this.address = address;
            this.name = name;
            this.argument = argument;
            thread = new Thread(this, name+"_Macro$");
            thread.setPriority(Math.max(thread.getPriority()-2, Thread.MIN_PRIORITY));
            thread.start();
        }

        /** Runs tokenized macro on current thread if pgm.queueCommands is true. */
        public void runShortcut(Program pgm, int address, String name) {
            this.pgm = pgm;
            this.address = address;
            this.name = name;
            if (pgm.queueCommands)
                run();
            else {
                thread = new Thread(this, name+"_Macro$");
                thread.setPriority(Math.max(thread.getPriority()-2, Thread.MIN_PRIORITY));
                thread.start();
            }
        }

        public void run() {
            try {
                Interpreter interp = new Interpreter();
                interp.argument = argument;
                if (pgm==null)
                    interp.run(macro);
                else
                    interp.runMacro(pgm, address, name);
            } catch(Throwable e) {
                Interpreter.abort();
                IJ.showStatus("");
                IJ.showProgress(1.0);
                ImagePlus imp = WindowManager.getCurrentImage();
                if (imp!=null) imp.unlock();
                String msg = e.getMessage();
                if (e instanceof RuntimeException && msg!=null && e.getMessage().equals(Macro.MACRO_CANCELED))
                    return;
                CharArrayWriter caw = new CharArrayWriter();
                PrintWriter pw = new PrintWriter(caw);
                e.printStackTrace(pw);
                String s = caw.toString();
                if (IJ.isMacintosh())
                    s = Tools.fixNewLines(s);
                //Don't show exceptions resulting from window being closed
                if (!(s.indexOf("NullPointerException")>=0 && s.indexOf("ij.process")>=0))
                    new TextWindow("Exception", s, 350, 250);
            }
        }

    }

| MacroRunner |