| WindowManager |

    package ij;
    import ij.plugin.Converter;
    import ij.plugin.frame.Recorder;
    import ij.macro.Interpreter;
    import ij.text.TextWindow;
    import ij.plugin.frame.PlugInFrame;
    import java.awt.*;
    import java.util.*;
    import ij.gui.*;

    /** This class consists of static methods used to manage ImageJ's windows. */
    public class WindowManager {

        private static Vector imageList = new Vector();      // list of image windows
        private static Vector nonImageList = new Vector();   // list of non-image windows
        private static ImageWindow currentWindow;            // active image window
        private static Frame frontWindow;
        private static ImagePlus tempCurrentImage;
        public static boolean checkForDuplicateName;

        private WindowManager() {
        }

        /** Makes the image contained in the specified window the active image. */
        public static void setCurrentWindow(ImageWindow win) {
            if (win==null || win.isClosed() || win.getImagePlus()==null) // deadlock-"wait to lock"
                return;
            //IJ.log("setCurrentWindow: "+win.getImagePlus().getTitle()+" ("+(currentWindow!=null?currentWindow.getImagePlus().getTitle():"null") + ")");
            setWindow(win);
            tempCurrentImage = null;
            if (win==currentWindow || imageList.size()==0)
                return;
            if (currentWindow!=null) {
                // free up pixel buffers used by current window
                ImagePlus imp = currentWindow.getImagePlus();
                if (imp!=null ) {
                    imp.trimProcessor();
                    imp.saveRoi();
                }
            }
            Undo.reset();
            currentWindow = win;
            Menus.updateMenus();
        }

        /** Returns the active ImageWindow. */
        public static ImageWindow getCurrentWindow() {
            //if (IJ.debugMode) IJ.write("ImageWindow.getCurrentWindow");
            return currentWindow;
        }

        static int getCurrentIndex() {
            return imageList.indexOf(currentWindow);
        }

        /** Returns the active ImagePlus. */
        public static ImagePlus getCurrentImage() {
            //IJ.log("getCurrentImage: "+tempCurrentImage+"  "+currentWindow);
            if (tempCurrentImage!=null)
                return tempCurrentImage;
            else if (currentWindow!=null)
                return currentWindow.getImagePlus();
            else if (frontWindow!=null && (frontWindow instanceof ImageWindow))
                return ((ImageWindow)frontWindow).getImagePlus();
            else    if (imageList.size()>0) {
                ImageWindow win = (ImageWindow)imageList.elementAt(imageList.size()-1);
                return win.getImagePlus();
            } else
                return Interpreter.getLastBatchModeImage();
        }

        /** Returns the number of open image windows. */
        public static int getWindowCount() {
            int count = imageList.size();
            if (count==0 && tempCurrentImage!=null)
                count = 1;
            return count;
        }

        /** Returns the number of open images. */
        public static int getImageCount() {
            int count = imageList.size();
            count += Interpreter.getBatchModeImageCount();
            if (count==0 && tempCurrentImage!=null)
                count = 1;
            return count;
        }

        /** Returns the front most window or null. */
        public static Frame getFrontWindow() {
            return frontWindow;
        }

        /** Returns a list of the IDs of open images. Returns
            null if no windows are open. */
        public synchronized static int[] getIDList() {
            int nWindows = imageList.size();
            int[] batchModeImages = Interpreter.getBatchModeImageIDs();
            int nBatchImages = batchModeImages.length;
            if ((nWindows+nBatchImages)==0)
                return null;
            int[] list = new int[nWindows+nBatchImages];
            for (int i=0; i<nBatchImages; i++)
                list[i] = batchModeImages[i];
            int index = 0;
            for (int i=nBatchImages; i<nBatchImages+nWindows; i++) {
                ImageWindow win = (ImageWindow)imageList.elementAt(index++);
                list[i] = win.getImagePlus().getID();
            }
            return list;
        }

        /** Returns an array containing a list of the non-image windows. */
        public synchronized static Frame[] getNonImageWindows() {
            Frame[] list = new Frame[nonImageList.size()];
            nonImageList.copyInto((Frame[])list);
            return list;
        }

        /** For IDs less than zero, returns the ImagePlus with the specified ID.
            Returns null if no open window has a matching ID or no images are open.
            For IDs greater than zero, returns the Nth ImagePlus. Returns null if
            the ID is zero. */
        public synchronized static ImagePlus getImage(int imageID) {
            //if (IJ.debugMode) IJ.write("ImageWindow.getImage");
            if (imageID>0)
                imageID = getNthImageID(imageID);
            if (imageID==0 || getImageCount()==0)
                return null;
            ImagePlus imp2 = Interpreter.getBatchModeImage(imageID);
            if (imp2!=null)
                return imp2;
            ImagePlus imp = null;
            for (int i=0; i<imageList.size(); i++) {
                ImageWindow win = (ImageWindow)imageList.elementAt(i);
                imp2 = win.getImagePlus();
                if (imageID==imp2.getID()) {
                    imp = imp2;
                    break;
                }
            }
            if (imp==null && tempCurrentImage!=null && tempCurrentImage.getID()==imageID)
                return tempCurrentImage;
            return imp;
        }

        /** Returns the ID of the Nth open image. Returns zero if n<=0
            or n greater than the number of open image windows. */
        public synchronized static int getNthImageID(int n) {
                if (n<=0) return 0;
                if (Interpreter.isBatchMode()) {
                    int[] list = getIDList();
                    if (n>list.length)
                        return 0;
                    else
                        return list[n-1];
                } else {
                    if (n>imageList.size()) return 0;
                    ImageWindow win = (ImageWindow)imageList.elementAt(n-1);
                    if (win!=null)
                        return win.getImagePlus().getID();
                    else
                        return 0;
                }
        }

        /** Returns the first image that has the specified title or null if it is not found. */
        public synchronized static ImagePlus getImage(String title) {
            int[] wList = getIDList();
            if (wList==null) return null;
            for (int i=0; i<wList.length; i++) {
                ImagePlus imp = getImage(wList[i]);
                if (imp!=null && imp.getTitle().equals(title))
                    return imp;
            }
            return null;
        }

        /** Adds the specified window to the Window menu. */
        public synchronized static void addWindow(Frame win) {
            //IJ.write("addWindow: "+win.getTitle());
            if (win==null)
                return;
            else if (win instanceof ImageWindow)
                addImageWindow((ImageWindow)win);
            else {
                Menus.insertWindowMenuItem(win);
                nonImageList.addElement(win);
            }
        }

        private static void addImageWindow(ImageWindow win) {
            ImagePlus imp = win.getImagePlus();
            if (imp==null) return;
            checkForDuplicateName(imp);
            imageList.addElement(win);
            Menus.addWindowMenuItem(imp);
            setCurrentWindow(win);
        }

        static void checkForDuplicateName(ImagePlus imp) {
            if (checkForDuplicateName) {
                String name = imp.getTitle();
                if (isDuplicateName(name))
                    imp.setTitle(getUniqueName(name));
            }
            checkForDuplicateName = false;
        }

        static boolean isDuplicateName(String name) {
            int n = imageList.size();
            for (int i=0; i<n; i++) {
                ImageWindow win = (ImageWindow)imageList.elementAt(i);
                String name2 = win.getImagePlus().getTitle();
                if (name.equals(name2))
                    return true;
            }
            return false;
        }

        /** Returns a unique name by adding, before the extension,  -1, -2, etc. as needed. */
        public static String getUniqueName(String name) {
            String name2 = name;
            String extension = "";
            int len = name2.length();
            int lastDot = name2.lastIndexOf(".");
            if (lastDot!=-1 && len-lastDot<6 && lastDot!=len-1) {
                extension = name2.substring(lastDot, len);
                name2 = name2.substring(0, lastDot);
            }
            int lastDash = name2.lastIndexOf("-");
            if (lastDash!=-1 && name2.length()-lastDash<4)
                name2 = name2.substring(0, lastDash);
            for (int i=1; i<=99; i++) {
                String name3 = name2+"-"+ i + extension;
                //IJ.log(i+" "+name3);
                if (!isDuplicateName(name3))
                    return name3;
            }
            return name;
        }

        /** If 'name' is not unique, adds -1, -2, etc. as needed to make it unique. */
        public static String makeUniqueName(String name) {
            return isDuplicateName(name)?getUniqueName(name):name;
        }

        /** Removes the specified window from the Window menu. */
        public synchronized static void removeWindow(Frame win) {
            //IJ.write("removeWindow: "+win.getTitle());
            if (win instanceof ImageWindow)
                removeImageWindow((ImageWindow)win);
            else {
                int index = nonImageList.indexOf(win);
                ImageJ ij = IJ.getInstance();
                if (index>=0) {
                    //if (ij!=null && !ij.quitting())
                    Menus.removeWindowMenuItem(index);
                    nonImageList.removeElement(win);
                }
            }
            setWindow(null);
        }

        private static void removeImageWindow(ImageWindow win) {
            int index = imageList.indexOf(win);
            if (index==-1)
                return;  // not on the window list
            if (imageList.size()>1) {
                int newIndex = index-1;
                if (newIndex<0)
                    newIndex = imageList.size()-1;
                setCurrentWindow((ImageWindow)imageList.elementAt(newIndex));
            } else
                currentWindow = null;
            imageList.removeElementAt(index);
            tempCurrentImage = null;
            int nonImageCount = nonImageList.size();
            if (nonImageCount>0)
                nonImageCount++;
            Menus.removeWindowMenuItem(nonImageCount+index);
            Menus.updateMenus();
            Undo.reset();
        }

        /** The specified frame becomes the front window, the one returnd by getFrontWindow(). */
        public static void setWindow(Frame win) {
            /*
            if (Recorder.record && win!=null && win!=frontWindow) {
                String title = win.getTitle();
                IJ.log("Set window: "+title+"  "+(getFrame(title)!=null?"not null":"null"));
                if (getFrame(title)!=null && !title.equals("Recorder"))
                    Recorder.record("selectWindow", title);
            }
            */
            frontWindow = win;
            //IJ.log("Set window: "+(win!=null?win.getTitle():"null"));
        }

        /** Closes all windows. Stops and returns false if any image "save changes" dialog is canceled. */
        public synchronized static boolean closeAllWindows() {
            while (imageList.size()>0) {
                if (!((ImageWindow)imageList.elementAt(0)).close())
                    return false;
                IJ.wait(100);
            }
            ImageJ ij = IJ.getInstance();
            if (ij!=null && ij.quitting() && IJ.getApplet()==null)
                return true;
            Frame[] list = getNonImageWindows();
            for (int i=0; i<list.length; i++) {
                Frame frame = list[i];
                if (frame instanceof PlugInFrame)
                    ((PlugInFrame)frame).close();
                else if (frame instanceof TextWindow)
                    ((TextWindow)frame).close();
                else {
                    frame.setVisible(false);
                    frame.dispose();
                }
            }
            return true;
        }

        /** Activates the next image window on the window list. */
        public static void putBehind() {
            if (IJ.debugMode) IJ.log("putBehind");
            if(imageList.size()<1 || currentWindow==null)
                return;
            int index = imageList.indexOf(currentWindow);
            ImageWindow win;
            int count = 0;
            do {
                index--;
                if (index<0) index = imageList.size()-1;
                win = (ImageWindow)imageList.elementAt(index);
                if (++count==imageList.size()) return;
            } while (win instanceof HistogramWindow || win instanceof PlotWindow);
            setCurrentWindow(win);
            win.toFront();
            Menus.updateMenus();
        }

        /** Makes the specified image temporarily the active image.
            Allows use of IJ.run() commands on images that
            are not displayed in a window. Call again with a null
            argument to revert to the previous active image. */
        public static void setTempCurrentImage(ImagePlus imp) {
            tempCurrentImage = imp;
        }

        /** Returns <code>tempCurrentImage</code>, which may be null. */
        public static ImagePlus getTempCurrentImage() {
            return tempCurrentImage;
        }

        /** Returns the frame with the specified title or null if a frame with that
            title is not found. */
        public static Frame getFrame(String title) {
            for (int i=0; i<nonImageList.size(); i++) {
                Frame frame = (Frame)nonImageList.elementAt(i);
                if (title.equals(frame.getTitle()))
                    return frame;
            }
            int[] wList = getIDList();
            int len = wList!=null?wList.length:0;
            for (int i=0; i<len; i++) {
                ImagePlus imp = getImage(wList[i]);
                if (imp!=null) {
                    if (imp.getTitle().equals(title))
                        return imp.getWindow();
                }
            }
            return null;
        }

        /** Activates a window selected from the Window menu. */
        synchronized static void activateWindow(String menuItemLabel, MenuItem item) {
            //IJ.write("activateWindow: "+menuItemLabel+" "+item);
            for (int i=0; i<nonImageList.size(); i++) {
                Frame win = (Frame)nonImageList.elementAt(i);
                String title = win.getTitle();
                if (menuItemLabel.equals(title)) {
                    win.toFront();
                    ((CheckboxMenuItem)item).setState(false);
                    if (Recorder.record)
                        Recorder.record("selectWindow", title);
                    return;
                }
            }
            int lastSpace = menuItemLabel.lastIndexOf(' ');
            if (lastSpace>0) // remove image size (e.g., " 90K")
                menuItemLabel = menuItemLabel.substring(0, lastSpace);
            for (int i=0; i<imageList.size(); i++) {
                ImageWindow win = (ImageWindow)imageList.elementAt(i);
                String title = win.getImagePlus().getTitle();
                if (menuItemLabel.equals(title)) {
                    setCurrentWindow(win);
                    win.toFront();
                    int index = imageList.indexOf(win);
                    int n = Menus.window.getItemCount();
                    int start = Menus.WINDOW_MENU_ITEMS+Menus.windowMenuItems2;
                    for (int j=start; j<n; j++) {
                        MenuItem mi = Menus.window.getItem(j);
                        ((CheckboxMenuItem)mi).setState((j-start)==index);
                    }
                    if (Recorder.record)
                        Recorder.record("selectWindow", title);
                    break;
                }
            }
        }

        /** Repaints all open image windows. */
        public synchronized static void repaintImageWindows() {
            int[] list = getIDList();
            if (list==null) return;
            for (int i=0; i<list.length; i++) {
                ImagePlus imp2 = getImage(list[i]);
                if (imp2!=null) {
                    ImageWindow win = imp2.getWindow();
                    if (win!=null) win.repaint();
                }
            }
        }

        static void showList() {
            if (IJ.debugMode) {
                for (int i=0; i<imageList.size(); i++) {
                    ImageWindow win = (ImageWindow)imageList.elementAt(i);
                    ImagePlus imp = win.getImagePlus();
                    IJ.log(i + " " + imp.getTitle() + (win==currentWindow?"*":""));
                }
                IJ.log(" ");
            }
        }

    }

| WindowManager |