| Toolbar |

    package ij.gui;
    import java.awt.*;
    import java.awt.event.KeyEvent;
    import java.awt.event.*;
    import java.io.File;
    import java.util.Hashtable;
    import ij.*;
    import ij.plugin.frame.Recorder;
    import ij.plugin.frame.Editor;
    import ij.plugin.MacroInstaller;
    import ij.macro.Program;

    /** The ImageJ toolbar. */
    public class Toolbar extends Canvas implements MouseListener, MouseMotionListener, ItemListener, ActionListener {

        public static final int RECTANGLE = 0;
        public static final int OVAL = 1;
        public static final int POLYGON = 2;
        public static final int FREEROI = 3;
        public static final int LINE = 4;
        public static final int POLYLINE = 5;
        public static final int FREELINE = 6;
        public static final int POINT = 7, CROSSHAIR = 7;
        public static final int WAND = 8;
        public static final int TEXT = 9;
        public static final int SPARE1 = 10;
        public static final int MAGNIFIER = 11;
        public static final int HAND = 12;
        public static final int DROPPER = 13;
        public static final int ANGLE = 14;
        public static final int SPARE2 = 15;
        public static final int SPARE3 = 16;
        public static final int SPARE4 = 17;
        public static final int SPARE5 = 18;
        public static final int SPARE6 = 19;
        public static final int SPARE7 = 20;
        public static final int SPARE8 = 21;
        public static final int SPARE9 = 22;

        public static final int DOUBLE_CLICK_THRESHOLD = 650;

        private static final int NUM_TOOLS = 23;
        private static final int NUM_BUTTONS = 21;
        private static final int SIZE = 24;
        private static final int OFFSET = 4;
        private static final String BRUSH_SIZE = "toolbar.brush.size";

        private Dimension ps = new Dimension(SIZE*NUM_BUTTONS, SIZE);
        private boolean[] down;
        private static int current;
        private int previous;
        private int x,y;
        private int xOffset, yOffset;
        private long mouseDownTime;
        private Graphics g;
        private static Toolbar instance;
        private int mpPrevious = RECTANGLE;
        private String[] names = new String[NUM_TOOLS];
        private String[] icons = new String[NUM_TOOLS];
        private PopupMenu[] menus = new PopupMenu[NUM_TOOLS];
        private int pc;
        private String icon;
        private MacroInstaller macroInstaller;
        private int startupTime;
        private PopupMenu ovalPopup, linePopup, switchPopup;
        private CheckboxMenuItem ovalItem, brushItem;
        private CheckboxMenuItem straightLineItem, polyLineItem, freeLineItem;
        private String currentSet = "Startup Macros";

        private static Color foregroundColor = Prefs.getColor(Prefs.FCOLOR,Color.black);
        private static Color backgroundColor = Prefs.getColor(Prefs.BCOLOR,Color.white);
        private static boolean brushEnabled;
        private static int brushSize = (int)Prefs.get(BRUSH_SIZE, 15);
        private int lineType = LINE;

        private Color gray = ImageJ.backgroundColor;
        private Color brighter = gray.brighter();
        //private Color darker = gray.darker();
        //private Color evenDarker = darker.darker();
        private Color darker = new Color(175, 175, 175);
        private Color evenDarker = new Color(110, 110, 110);
        private Color triangleColor = new Color(150, 0, 0);
        private Color toolColor = new Color(0, 25, 45);

        public Toolbar() {
            down = new boolean[NUM_TOOLS];
            resetButtons();
            down[0] = true;
            setForeground(foregroundColor);
            setBackground(gray);
            //setBackground(Color.red);
            addMouseListener(this);
            addMouseMotionListener(this);
            instance = this;
            names[NUM_TOOLS-1] = "Switch to alternate macro tool sets";
            icons[NUM_TOOLS-1] = "C900T1c12>T7c12>"; // ">>"
            addPopupMenus();
            if (IJ.isMacOSX() || IJ.isVista()) Prefs.antialiasedTools = true;
        }

        void addPopupMenus() {
            ovalPopup = new PopupMenu();
            if (Menus.getFontSize()!=0)
                ovalPopup.setFont(Menus.getFont());
            ovalItem = new CheckboxMenuItem("Elliptical Selection Tool", !brushEnabled);
            ovalItem.addItemListener(this);
            ovalPopup.add(ovalItem);
            brushItem = new CheckboxMenuItem("Selection Brush Tool", brushEnabled);
            brushItem.addItemListener(this);
            ovalPopup.add(brushItem);
            add(ovalPopup);

            linePopup = new PopupMenu();
            if (Menus.getFontSize()!=0)
                linePopup.setFont(Menus.getFont());
            straightLineItem = new CheckboxMenuItem("Straight Lines", lineType==LINE);
            straightLineItem.addItemListener(this);
            linePopup.add(straightLineItem);
            polyLineItem = new CheckboxMenuItem("Segmented Lines", lineType==POLYLINE);
            polyLineItem.addItemListener(this);
            linePopup.add(polyLineItem);
            freeLineItem = new CheckboxMenuItem("Freehand Lines", lineType==FREELINE);
            freeLineItem.addItemListener(this);
            linePopup.add(freeLineItem);
            add(linePopup);

            switchPopup = new PopupMenu();
            if (Menus.getFontSize()!=0)
                switchPopup.setFont(Menus.getFont());
            add(switchPopup);
        }

        /** Returns the ID of the current tool (Toolbar.RECTANGLE,
            Toolbar.OVAL, etc.). */
        public static int getToolId() {
            return current;
        }

        /** Returns the ID of the tool whose name (the description displayed in the status bar)
            starts with the specified string, or -1 if the tool is not found. */
        public int getToolId(String name) {
            int tool =  -1;
            for (int i=0; i<=SPARE9; i++) {
                if (names[i]!=null && names[i].startsWith(name)) {
                    tool = i;
                    break;
                }
            }
            return tool;
        }

        /** Returns a reference to the ImageJ toolbar. */
        public static Toolbar getInstance() {
            return instance;
        }

        private void drawButtons(Graphics g) {
            if (Prefs.antialiasedTools) {
                Graphics2D g2d = (Graphics2D)g;
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
            }
            for (int i=0; i<LINE; i++)
                drawButton(g, i);
            drawButton(g, lineType);
            for (int i=POINT; i<NUM_TOOLS; i++)
                drawButton(g, i);
        }

        private void fill3DRect(Graphics g, int x, int y, int width, int height, boolean raised) {
            if (null==g) return;
            if (raised)
                g.setColor(gray);
            else
                g.setColor(darker);
            g.fillRect(x+1, y+1, width-2, height-2);
            g.setColor(raised ? brighter : evenDarker);
            g.drawLine(x, y, x, y + height - 1);
            g.drawLine(x + 1, y, x + width - 2, y);
            g.setColor(raised ? evenDarker : brighter);
            g.drawLine(x + 1, y + height - 1, x + width - 1, y + height - 1);
            g.drawLine(x + width - 1, y, x + width - 1, y + height - 2);
        }

        private void drawButton(Graphics g, int tool) {
            if (g==null) return;
            int index = toolIndex(tool);
            fill3DRect(g, index * SIZE + 1, 1, SIZE, SIZE-1, !down[tool]);
            g.setColor(toolColor);
            int x = index * SIZE + OFFSET;
            int y = OFFSET;
            if (down[tool]) { x++; y++;}
            this.g = g;
            if (tool>=SPARE1 && tool<=SPARE9 && icons[tool]!=null) {
                drawIcon(g, tool, x, y);
                return;
            }
            switch (tool) {
                case RECTANGLE:
                    g.drawRect(x+1, y+2, 14, 11);
                    return;
                case OVAL:
                    xOffset = x; yOffset = y;
                    if (brushEnabled) {
                        m(9,2); d(13,2); d(13,2); d(15,5); d(15,8);
                        d(13,10); d(10,10); d(8,13); d(4,13);
                        d(2,11);  d(2,7); d(4,5); d(7,5); d(9,2);
                        drawTriangle(13,14);
                    } else {
                        g.drawOval(x+1, y+2, 14, 11);
                        drawTriangle(13,14);
                    }
                    return;
                case POLYGON:
                    xOffset = x+1; yOffset = y+3;
                    m(4,0); d(14,0); d(14,1); d(10,5); d(10,6);
                    d(13,9); d(13,10); d(0,10); d(0,4); d(4,0);
                    return;
                case FREEROI:
                    xOffset = x+1; yOffset = y+3;
                    m(3,0); d(5,0); d(7,2); d(9,2); d(11,0); d(13,0); d(14,1); d(15,2);
                    d(15,4); d(14,5); d(14,6); d(12,8); d(11,8); d(10,9); d(9,9); d(8,10);
                    d(5,10); d(3,8); d(2,8); d(1,7); d(1,6); d(0,5); d(0,2); d(1,1); d(2,1);
                    return;
                case LINE:
                    xOffset = x; yOffset = y;
                    m(0,10); d(16,4);
                    drawTriangle(11,13);
                    return;
                case POLYLINE:
                    xOffset = x; yOffset = y;
                    m(14,6); d(11,3); d(1,3); d(1,4); d(6,9); d(2,13);
                    drawTriangle(11,13);
                    return;
                case FREELINE:
                    xOffset = x; yOffset = y;
                    m(16,4); d(14,6); d(12,6); d(9,3); d(8,3); d(6,7); d(2,11); d(1,11);
                    drawTriangle(11,13);
                    return;
                case POINT:
                    xOffset = x; yOffset = y;
                    m(1,8); d(6,8); d(6,6); d(10,6); d(10,10); d(6,10); d(6,9);
                    m(8,1); d(8,5); m(11,8); d(15,8); m(8,11); d(8,15);
                    m(8,8); d(8,8);
                    g.setColor(Roi.getColor());
                    g.fillRect(x+7, y+7, 3, 3);
                    return;
                case WAND:
                    xOffset = x+2; yOffset = y+2;
                    dot(4,0);  m(2,0); d(3,1); d(4,2);  m(0,0); d(1,1);
                    m(0,2); d(1,3); d(2,4);  dot(0,4); m(3,3); d(12,12);
                    return;
                case TEXT:
                    xOffset = x+2; yOffset = y+1;
                    m(0,13); d(3,13);
                    m(1,12); d(7,0); d(12,13);
                    m(11,13); d(14,13);
                    m(3,8); d(10,8);
                    return;
                case MAGNIFIER:
                    xOffset = x+2; yOffset = y+2;
                    m(3,0); d(3,0); d(5,0); d(8,3); d(8,5); d(7,6); d(7,7);
                    d(6,7); d(5,8); d(3,8); d(0,5); d(0,3); d(3,0);
                    m(8,8); d(9,8); d(13,12); d(13,13); d(12,13); d(8,9); d(8,8);
                    return;
                case HAND:
                    xOffset = x+1; yOffset = y+1;
                    m(5,14); d(2,11); d(2,10); d(0,8); d(0,7); d(1,6); d(2,6); d(4,8);
                    d(4,6); d(3,5); d(3,4); d(2,3); d(2,2); d(3,1); d(4,1); d(5,2); d(5,3);
                    m(6,5); d(6,1); d(7,0); d(8,0); d(9,1); d(9,5);
                    m(9,1); d(11,1); d(12,2); d(12,6);
                    m(13,4); d(14,3); d(15,4); d(15,7); d(14,8);
                    d(14,10); d(13,11); d(13,12); d(12,13); d(12,14);
                    return;
                case DROPPER:
                    xOffset = x; yOffset = y;
                    g.setColor(foregroundColor);
                    //m(0,0); d(17,0); d(17,17); d(0,17); d(0,0);
                    m(12,2); d(14,2);
                    m(11,3); d(15,3);
                    m(11,4); d(15,4);
                    m(8,5); d(15,5);
                    m(9,6); d(14,6);
                    m(10,7); d(12,7); d(12,9);
                    m(8,7); d(2,13); d(2,15); d(4,15); d(11,8);
                    g.setColor(backgroundColor);
                    m(0,0); d(16,0); d(16,16); d(0,16); d(0,0);
                    return;
                case ANGLE:
                    xOffset = x+1; yOffset = y+2;
                    m(0,11); d(11,0); m(0,11); d(15,11);
                    m(10,11); d(10,8); m(9,7); d(9,6); dot(8,5);
                    return;
            }
        }

        void drawTriangle(int x, int y) {
            g.setColor(triangleColor);
            xOffset+=x; yOffset+=y;
            m(0,0); d(4,0); m(1,1); d(3,1); dot(2,2);
        }

        void drawIcon(Graphics g, int tool, int x, int y) {
            if (null==g) return;
            icon = icons[tool];
            this.icon = icon;
            int length = icon.length();
            int x1, y1, x2, y2;
            pc = 0;
            while (true) {
                char command = icon.charAt(pc++);
                if (pc>=length) break;
                switch (command) {
                    case 'B': x+=v(); y+=v(); break;  // reset base
                    case 'R': g.drawRect(x+v(), y+v(), v(), v()); break;  // rectangle
                    case 'F': g.fillRect(x+v(), y+v(), v(), v()); break;  // filled rectangle
                    case 'O': g.drawOval(x+v(), y+v(), v(), v()); break;  // oval
                    case 'o': g.fillOval(x+v(), y+v(), v(), v()); break;  // filled oval
                    case 'C': g.setColor(new Color(v()*16,v()*16,v()*16)); break; // set color
                    case 'L': g.drawLine(x+v(), y+v(), x+v(), y+v()); break; // line
                    case 'D': g.fillRect(x+v(), y+v(), 1, 1); break; // dot
                    case 'P': // polyline
                        x1=x+v(); y1=y+v();
                        while (true) {
                            x2=v(); if (x2==0) break;
                            y2=v(); if (y2==0) break;
                            x2+=x; y2+=y;
                            g.drawLine(x1, y1, x2, y2);
                            x1=x2; y1=y2;
                        }
                        break;
                    case 'T': // text (one character)
                        x2 = x+v();
                        y2 = y+v();
                        int size = v()*10+v();
                        char[] c = new char[1];
                        c[0] = pc<icon.length()?icon.charAt(pc++):'e';
                        g.setFont(new Font("SansSerif", Font.BOLD, size));
                        g.drawString(new String(c), x2, y2);
                        break;
                    default: break;
                }
                if (pc>=length) break;
            }
            if (menus[tool]!=null && menus[tool].getItemCount()>0) {
                xOffset = x; yOffset = y;
                drawTriangle(13, 14);
            }
        }

        int v() {
            if (pc>=icon.length()) return 0;
            char c = icon.charAt(pc++);
            //IJ.log("v: "+pc+" "+c+" "+toInt(c));
            switch (c) {
                case '0': return 0;
                case '1': return 1;
                case '2': return 2;
                case '3': return 3;
                case '4': return 4;
                case '5': return 5;
                case '6': return 6;
                case '7': return 7;
                case '8': return 8;
                case '9': return 9;
                case 'a': return 10;
                case 'b': return 11;
                case 'c': return 12;
                case 'd': return 13;
                case 'e': return 14;
                case 'f': return 15;
                default: return 0;
            }
        }

        private void showMessage(int tool) {
            if (tool>=SPARE1 && tool<=SPARE9 && names[tool]!=null) {
                String name = names[tool];
                int index = name.indexOf("Action Tool");
                if (index!=-1)
                    name = name.substring(0, index);
                else {
                    index = name.indexOf("Menu Tool");
                    if (index!=-1)
                        name = name.substring(0, index+4);
                }
                IJ.showStatus(name);
                return;
            }
            switch (tool) {
                case RECTANGLE:
                    IJ.showStatus("Rectangular selections");
                    return;
                case OVAL:
                    if (brushEnabled)
                        IJ.showStatus("Elliptical or *brush* selections");
                    else
                        IJ.showStatus("*Elliptical* or brush selections");
                    return;
                case POLYGON:
                    IJ.showStatus("Polygon selections");
                    return;
                case FREEROI:
                    IJ.showStatus("Freehand selections");
                    return;
                case LINE:
                    IJ.showStatus("Straight line selections (right click for other types)");
                    return;
                case POLYLINE:
                    IJ.showStatus("Segmented line selections");
                    return;
                case FREELINE:
                    IJ.showStatus("Freehand line selections");
                    return;
                case POINT:
                    IJ.showStatus("Point selections (shift click for multiple points)");
                    return;
                case WAND:
                    IJ.showStatus("Wand (tracing) tool");
                    return;
                case TEXT:
                    IJ.showStatus("Text tool");
                    return;
                case MAGNIFIER:
                    IJ.showStatus("Magnifying glass (or use \"+\" and \"-\" keys)");
                    return;
                case HAND:
                    IJ.showStatus("Scrolling tool (or press space bar and drag)");
                    return;
                case DROPPER:
                    IJ.showStatus("Color picker (" + foregroundColor.getRed() + ","
                    + foregroundColor.getGreen() + "," + foregroundColor.getBlue() + ")");
                    return;
                case ANGLE:
                    IJ.showStatus("Angle tool");
                    return;
                default:
                    IJ.showStatus("ImageJ "+IJ.getVersion()+" / Java "+System.getProperty("java.version"));
                    return;
            }
        }

        private void m(int x, int y) {
            this.x = xOffset+x;
            this.y = yOffset+y;
        }

        private void d(int x, int y) {
            x += xOffset;
            y += yOffset;
            g.drawLine(this.x, this.y, x, y);
            this.x = x;
            this.y = y;
        }

        private void dot(int x, int y) {
            g.fillRect(x+xOffset, y+yOffset, 1, 1);
        }

        private void resetButtons() {
            for (int i=0; i<NUM_TOOLS; i++)
                down[i] = false;
        }

        public void paint(Graphics g) {
            if (null==g) return;
            drawButtons(g);
        }

        public void setTool(int tool) {
            if (tool==current || tool<0 || tool>=NUM_TOOLS)
                return;
            if (tool==SPARE1||(tool>=SPARE2&&tool<=SPARE9)) {
                if (names[tool]==null)
                    names[tool] = "Spare tool"; // enable tool
                if (names[tool].indexOf("Action Tool")!=-1)
                    return;
            }
            if (isLine(tool)) lineType = tool;
            setTool2(tool);
        }

        private void setTool2(int tool) {
            if (tool==current || !isValidTool(tool)) return;
            current = tool;
            down[current] = true;
            down[previous] = false;
            Graphics g = this.getGraphics();
            if (Prefs.antialiasedTools) {
                Graphics2D g2d = (Graphics2D)g;
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            }
            drawButton(g, previous);
            drawButton(g, current);
            if (null==g) return;
            g.dispose();
            showMessage(current);
            previous = current;
            if (Recorder.record)
                Recorder.record("setTool", current);
            if (IJ.isMacOSX())
                repaint();
        }

        boolean isValidTool(int tool) {
            if (tool<0 || tool>=NUM_TOOLS)
                return false;
            if ((tool==SPARE1||(tool>=SPARE2&&tool<=SPARE9)) && names[tool]==null)
                return false;
            return true;
        }

        /** Obsolete. Use getForegroundColor(). */
        public Color getColor() {
            return foregroundColor;
        }

        /** Obsolete. Use setForegroundColor(). */
        public void setColor(Color c) {
            if (c!=null) {
                foregroundColor = c;
                drawButton(this.getGraphics(), DROPPER);
            }
        }

        public static Color getForegroundColor() {
            return foregroundColor;
        }

        public static void setForegroundColor(Color c) {
            if (c!=null) {
                foregroundColor = c;
                repaintTool(DROPPER);
            }
        }

        public static Color getBackgroundColor() {
            return backgroundColor;
        }

        public static void setBackgroundColor(Color c) {
            if (c!=null) {
                backgroundColor = c;
                repaintTool(DROPPER);
            }
        }

        public static int getBrushSize() {
            if (brushEnabled)
                return brushSize;
            else
                return 0;
        }

        static void repaintTool(int tool) {
            if (IJ.getInstance()!=null) {
                Toolbar tb = getInstance();
                Graphics g = tb.getGraphics();
                if (Prefs.antialiasedTools)
                    ((Graphics2D)g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                tb.drawButton(g, tool);
                if (g!=null) g.dispose();
            }
            //Toolbar tb = getInstance();
            //tb.repaint(tool * SIZE , 0, SIZE, SIZE);
        }

        // Returns the toolbar position index of the specified tool
        int toolIndex(int tool) {
            switch (tool) {
                case RECTANGLE: return 0;
                case OVAL: return 1;
                case POLYGON: return 2;
                case FREEROI: return 3;
                case LINE: return 4;
                case POLYLINE: return 4;
                case FREELINE: return 4;
                case POINT: return 6;
                case WAND: return 7;
                case TEXT: return 8;
                case MAGNIFIER: return 9;
                case HAND: return 10;
                case DROPPER: return 11;
                case ANGLE: return 5;
                case SPARE1: return 12;
                default: return tool - 2;
            }
        }

        // Returns the tool corresponding to the specified tool position index
        int toolID(int index) {
            switch (index) {
                case 0: return RECTANGLE;
                case 1: return OVAL;
                case 2: return POLYGON;
                case 3: return FREEROI;
                case 4: return lineType;
                case 5: return ANGLE;
                case 6: return POINT;
                case 7: return WAND;
                case 8: return TEXT;
                case 9: return MAGNIFIER;
                case 10: return HAND;
                case 11: return DROPPER;
                case 12: return SPARE1;
                default: return index + 2;
            }
        }

        public void mousePressed(MouseEvent e) {
            int x = e.getX();
            int newTool = 0;
            for (int i=0; i<NUM_BUTTONS; i++) {
                if (x>i*SIZE && x<i*SIZE+SIZE)
                    newTool = toolID(i);
            }
            if (newTool==SPARE9) {
                showSwitchPopupMenu(e);
                return;
            }
            if (!isValidTool(newTool)) return;
            if (menus[newTool]!=null && menus[newTool].getItemCount()>0) {
                menus[newTool].show(e.getComponent(), e.getX(), e.getY());
                return;
            }
            boolean doubleClick = newTool==current && (System.currentTimeMillis()-mouseDownTime)<=DOUBLE_CLICK_THRESHOLD;
            mouseDownTime = System.currentTimeMillis();
            if (!doubleClick) {
                mpPrevious = current;
                if (isMacroTool(newTool)) {
                    String name = names[newTool];
                    if (name.indexOf("Unused Tool")!=-1)
                        return;
                    if (name.indexOf("Action Tool")!=-1) {
                        drawTool(newTool, true);
                        IJ.wait(50);
                        drawTool(newTool, false);
                        runMacroTool(newTool);
                        return;
                    } else {
                        name = name.endsWith(" ")?name:name+" ";
                        macroInstaller.runMacroTool(name+"Selected");
                    }
                }
                setTool2(newTool);
                if (current==OVAL && (e.isPopupTrigger() || e.isMetaDown())) {
                    ovalItem.setState(!brushEnabled);
                    brushItem.setState(brushEnabled);
                    if (IJ.isMacOSX()) IJ.wait(10);
                    ovalPopup.show(e.getComponent(),x,y);
                    mouseDownTime = 0L;
                }
                if (isLine(current) && (e.isPopupTrigger() || e.isMetaDown())) {
                    straightLineItem.setState(lineType==LINE);
                    polyLineItem.setState(lineType==POLYLINE);
                    freeLineItem.setState(lineType==FREELINE);
                    if (IJ.isMacOSX()) IJ.wait(10);
                    linePopup.show(e.getComponent(),x,y);
                    mouseDownTime = 0L;
                }
            } else {
                if (isMacroTool(current)) {
                    String name = names[current].endsWith(" ")?names[current]:names[current]+" ";
                    macroInstaller.runMacroTool(name+"Options");
                    return;
                }
                ImagePlus imp = WindowManager.getCurrentImage();
                switch (current) {
                    case OVAL:
                        showBrushDialog();
                        break;
                    case MAGNIFIER:
                        if (imp!=null) {
                            ImageCanvas ic = imp.getCanvas();
                            if (ic!=null) ic.unzoom();
                        }
                        break;
                    case LINE: case POLYLINE: case FREELINE:
                        IJ.runPlugIn("ij.plugin.frame.LineWidthAdjuster", "");
                        break;
                    case POINT:
                        IJ.doCommand("Point Tool...");
                        break;
                    case TEXT:
                        IJ.doCommand("Fonts...");
                        break;
                    case DROPPER:
                        IJ.doCommand("Color Picker...");
                        setTool2(mpPrevious);
                        break;
                    default:
                }
            }
        }

        void showSwitchPopupMenu(MouseEvent e) {
            String path = IJ.getDirectory("macros")+"toolsets/";
            if (path==null) {
                return;
            }
            boolean applet = IJ.getApplet()!=null;
            File f = new File(path);
            String[] list;
            if (!applet && f.exists() && f.isDirectory()) {
                list = f.list();
                if (list==null) return;
            } else
                list = new String[0];
            boolean stackTools = false;
            for (int i=0; i<list.length; i++) {
                if (list[i].equals("Stack Tools.txt")) {
                    stackTools = true;
                    break;
                }
            }
            switchPopup.removeAll();
            path = IJ.getDirectory("macros") + "StartupMacros.txt";
            f = new File(path);
            if (!applet && f.exists())
                addItem("Startup Macros");
            else
                addItem("StartupMacros*");
            if (!stackTools) addItem("Stack Tools*");
            for (int i=0; i<list.length; i++) {
                String name = list[i];
                if (name.endsWith(".txt")) {
                    name = name.substring(0, name.length()-4);
                    addItem(name);
                } else if (name.endsWith(".ijm")) {
                    name = name.substring(0, name.length()-4) + " ";
                    addItem(name);
                }
            }
            addItem("Help...");
            add(ovalPopup);
            if (IJ.isMacOSX()) IJ.wait(10);
            switchPopup.show(e.getComponent(), e.getX(), e.getY());
        }

        void addItem(String name) {
            CheckboxMenuItem item = new CheckboxMenuItem(name, name.equals(currentSet));
            item.addItemListener(this);
            switchPopup.add(item);
        }

        void drawTool(int tool, boolean drawDown) {
            down[tool] = drawDown;
            Graphics g = this.getGraphics();
            if (!drawDown && Prefs.antialiasedTools && IJ.isJava2()) {
                Graphics2D g2d = (Graphics2D)g;
                g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            }
            drawButton(g, tool);
            if (null==g) return;
            g.dispose();
        }

        boolean isLine(int tool) {
            return tool==LINE || tool==POLYLINE || tool==FREELINE;
        }

        public void restorePreviousTool() {
            setTool2(mpPrevious);
        }

        boolean isMacroTool(int tool) {
            return tool>=SPARE1 && tool<=SPARE9 && names[tool]!=null && macroInstaller!=null;
        }

        public void mouseReleased(MouseEvent e) {}
        public void mouseExited(MouseEvent e) {}
        public void mouseClicked(MouseEvent e) {}
        public void mouseEntered(MouseEvent e) {}
        public void mouseDragged(MouseEvent e) {}

        public void itemStateChanged(ItemEvent e) {
            CheckboxMenuItem item = (CheckboxMenuItem)e.getSource();
            if (item==ovalItem || item==brushItem) {
                brushEnabled = item==brushItem;
                repaintTool(OVAL);
                showMessage(OVAL);
            } else if (item==straightLineItem) {
                lineType = LINE;
                setTool2(LINE);
                showMessage(LINE);
            } else if (item==polyLineItem) {
                lineType = POLYLINE;
                setTool2(POLYLINE);
                showMessage(POLYLINE);
            } else if (item==freeLineItem) {
                lineType = FREELINE;
                setTool2(FREELINE);
                showMessage(FREELINE);
            } else {
                String label = item.getActionCommand();
                if (!label.equals("Help...")) currentSet = label;
                String path;
                if (label.equals("Help...")) {
                    IJ.showMessage("Tool Switcher",
                        "Use this drop down menu to switch to macro tool\n"+
                        "sets located in the ImageJ/macros/toolsets folder,\n"+
                        "or to revert to the ImageJ/macros/StartupMacros\n"+
                        "set. The default tool sets, which have names\n"+
                        "ending in '*', are loaded from ij.jar.\n"+
                        " \n"+
                        "Hold the shift key down while selecting a tool\n"+
                        "set to view its source code.\n"+
                        " \n"+
                        "Several example tool sets are available at\n"+
                        "<http://rsb.info.nih.gov/ij/macros/toolsets/>."
                        );
                    return;
                } else if (label.endsWith("*")) {
                    // load from ij.jar
                    MacroInstaller mi = new MacroInstaller();
                    label = label.substring(0, label.length()-1) + ".txt";
                    path = "/macros/"+label;
                    if (IJ.shiftKeyDown()) {
                        String macros = mi.openFromIJJar(path);
                        Editor ed = new Editor();
                        ed.setSize(350, 300);
                        ed.create(label, macros);
                        IJ.setKeyUp(KeyEvent.VK_SHIFT);
                    } else
                        mi.installFromIJJar(path);
                } else {
                    // load from ImageJ/macros/toolsets
                    if (label.equals("Startup Macros"))
                        path = IJ.getDirectory("macros")+"StartupMacros.txt";
                    else if (label.endsWith(" "))
                        path = IJ.getDirectory("macros")+"toolsets/"+label.substring(0, label.length()-1)+".ijm";
                    else
                        path = IJ.getDirectory("macros")+"toolsets/"+label+".txt";
                    try {
                        if (IJ.shiftKeyDown()) {
                            IJ.open(path);
                            IJ.setKeyUp(KeyEvent.VK_SHIFT);
                        } else
                            new MacroInstaller().run(path);
                    }
                    catch(Exception ex) {}
                }
            }
        }

        public void actionPerformed(ActionEvent e) {
            MenuItem item = (MenuItem)e.getSource();
            String cmd = e.getActionCommand();
            PopupMenu popup = (PopupMenu)item.getParent();
            int tool = -1;
            for (int i=SPARE1; i<NUM_TOOLS; i++) {
                if (popup==menus[i]) {
                    tool = i;
                    break;
                }
            }
            if (tool==-1) return;
            if (macroInstaller!=null)
                macroInstaller.runMenuTool(names[tool], cmd);
        }

        public Dimension getPreferredSize(){
            return ps;
        }

        public Dimension getMinimumSize(){
            return ps;
        }

        public void mouseMoved(MouseEvent e) {
            int x = e.getX();
            x=toolID(x/SIZE);
            showMessage(x);
        }

        /** Adds a tool to the toolbar. The 'toolTip' string is displayed in the status bar
             when the mouse is over the tool icon. The 'toolTip' string may include icon
            (http://rsb.info.nih.gov/ij/developer/macro/macros.html#tools).
            Returns the tool ID, or -1 if all tools are in use. */
        public int addTool(String toolTip) {
            int index = toolTip.indexOf('-');
            boolean hasIcon = index>=0 && (toolTip.length()-index)>4;
            int tool =-1;
            if (names[SPARE1]==null)
                tool = SPARE1;
            if (tool==-1) {
                for (int i=SPARE2; i<=SPARE8; i++) {
                    if (names[i]==null) {
                        tool = i;
                        break;
                    }
                }
            }
            if (tool==-1) return -1;
            if (hasIcon) {
                icons[tool] = toolTip.substring(index+1);
                if (index>0 && toolTip.charAt(index-1)==' ')
                    names[tool] = toolTip.substring(0, index-1);
                else
                    names[tool] = toolTip.substring(0, index);
            } else {
                if (toolTip.endsWith("-"))
                    toolTip = toolTip.substring(0, toolTip.length()-1);
                else if (toolTip.endsWith("- "))
                    toolTip = toolTip.substring(0, toolTip.length()-2);
                names[tool] = toolTip;
            }
            if (tool==current && (names[tool].indexOf("Action Tool")!=-1||names[tool].indexOf("Unused Tool")!=-1))
                setTool(RECTANGLE);
            if (names[tool].endsWith(" Menu Tool"))
                installMenu(tool);
            return tool;
        }

        void installMenu(int tool) {
            Program pgm = macroInstaller.getProgram();
            Hashtable h = pgm.getMenus();
            if (h==null) return;
            String[] commands = (String[])h.get(names[tool]);
            if (commands==null) return;
            if (menus[tool]==null) {
                menus[tool] = new PopupMenu("");
                if (Menus.getFontSize()!=0)
                    menus[tool].setFont(Menus.getFont());
                add(menus[tool] );
            } else
                menus[tool].removeAll();
            for (int i=0; i<commands.length; i++) {
                if (commands[i].equals("-"))
                    menus[tool].addSeparator();
                else {
                    MenuItem mi = new MenuItem(commands[i]);
                    mi.addActionListener(this);
                    menus[tool].add(mi);
                }
            }
            if (tool==current) setTool(RECTANGLE);
        }

        /** Used by the MacroInstaller class to install macro tools. */
        public void addMacroTool(String name, MacroInstaller macroInstaller, int id) {
            if (id==0) {
                for (int i=SPARE1; i<NUM_TOOLS-1; i++) {
                    names[i] = null;
                    icons[i] = null;
                    if (menus[i]!=null) menus[i].removeAll();
                }
            }
            this.macroInstaller = macroInstaller;
            addTool(name);
        }

        void runMacroTool(int id) {
            if (macroInstaller!=null)
                macroInstaller.runMacroTool(names[id]);
        }

        void showBrushDialog() {
            GenericDialog gd = new GenericDialog("Selection Brush");
            gd.addCheckbox("Enable Selection Brush", brushEnabled);
            gd.addNumericField("           Size:", brushSize, 0, 4, "pixels");
            gd.showDialog();
            if (gd.wasCanceled()) return;
            brushEnabled = gd.getNextBoolean();
            brushSize = (int)gd.getNextNumber();
            repaintTool(OVAL);
            ImagePlus img = WindowManager.getCurrentImage();
            Roi roi = img!=null?img.getRoi():null;
            if (roi!=null && roi.getType()==Roi.OVAL && brushEnabled) img.killRoi();
            Prefs.set(BRUSH_SIZE, brushSize);
        }

    }

| Toolbar |