package ij; import ij.plugin.Converter; import ij.plugin.frame.Recorder; import ij.plugin.frame.Editor; 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 { public static boolean checkForDuplicateName; 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 Hashtable tempImageTable = new Hashtable(); 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); tempImageTable.remove(Thread.currentThread()); 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 ) { if (!Prefs.keepUndoBuffers) imp.trimProcessor(); imp.saveRoi(); } } Undo.reset(); currentWindow = win; Menus.updateMenus(); if (Recorder.record && !IJ.isMacro()) Recorder.record("selectWindow", win.getImagePlus().getTitle()); } /** 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 a reference to the active image or null if there isn't one. */ public static ImagePlus getCurrentImage() { ImagePlus img = (ImagePlus)tempImageTable.get(Thread.currentThread()); //String str = (img==null)?" null":""; if (img==null) img = getActiveImage(); //if (img!=null) IJ.log("getCurrentImage: "+img.getTitle()+" "+Thread.currentThread().hashCode()+str); return img; } /** Makes the specified image temporarily the active image for this thread. Call again with a null argument to revert to the previous active image. */ public static void setTempCurrentImage(ImagePlus img) { //IJ.log("setTempImage: "+(img!=null?img.getTitle():"null ")+Thread.currentThread().hashCode()); if (img==null) tempImageTable.remove(Thread.currentThread()); else tempImageTable.put(Thread.currentThread(), img); } /** Sets a temporary image for the specified thread. */ public static void setTempCurrentImage(Thread thread, ImagePlus img) { if (thread==null) throw new RuntimeException("thread==null"); if (img==null) tempImageTable.remove(thread); else tempImageTable.put(thread, img); } /** Returns the active ImagePlus. */ private static ImagePlus getActiveImage() { 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(); return count; } /** Returns the number of open images. */ public static int getImageCount() { int count = imageList.size(); count += Interpreter.getBatchModeImageCount(); if (count==0 && getCurrentImage()!=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; } } imp2 = getCurrentImage(); if (imp==null &&imp2!=null && imp2.getID()==imageID) return imp2; 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("-"); len = name2.length(); if (lastDash!=-1&&len-lastDash<4&&lastDash<len-1&&Character.isDigit(name2.charAt(lastDash+1))&&name2.charAt(lastDash+1)!='0') 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); setTempCurrentImage(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) { frontWindow = win; //IJ.log("Set window: "+(win!=null?win.getTitle():"null")); } /** Closes all windows. Stops and returns false if an image or Editor "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); } Frame[] nonImages = getNonImageWindows(); for (int i=0; i<nonImages.length; i++) { Frame frame = nonImages[i]; if (frame!=null && (frame instanceof Editor)) { ((Editor)frame).close(); if (((Editor)frame).fileChanged()) return false; IJ.wait(100); } } ImageJ ij = IJ.getInstance(); if (ij!=null && ij.quitting() && IJ.getApplet()==null) return true; for (int i=0; i<nonImages.length; i++) { Frame frame = nonImages[i]; if ((frame instanceof PlugInFrame) && !(frame instanceof Editor)) ((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 = null; 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); if (win==null) return; ImagePlus imp = win.getImagePlus(); if (imp!=null) IJ.selectWindow(imp.getID()); } /** Returns the temporary current image for this thread, or null. */ public static ImagePlus getTempCurrentImage() { return (ImagePlus)tempImageTable.get(Thread.currentThread()); } /** 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)) { toFront(win); ((CheckboxMenuItem)item).setState(false); if (Recorder.record && !IJ.isMacro()) 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); toFront(win); 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); } 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) { imp2.setTitle(imp2.getTitle()); // update "(G)" flag (global calibration) 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(" "); } } public static void toFront(Frame frame) { if (frame==null) return; if (frame.getState()==Frame.ICONIFIED) frame.setState(Frame.NORMAL); frame.toFront(); } }