6e226d79a09cd3aa5381a4d90f599edfd8299679
[imageja.git] / ij / WindowManager.java
1 package ij;
2 import ij.plugin.Converter;
3 import ij.plugin.frame.Recorder;
4 import ij.macro.Interpreter;
5 import ij.text.TextWindow;
6 import ij.plugin.frame.PlugInFrame;
7 import java.awt.*;
8 import java.util.*;
9 import ij.gui.*;
10
11 /** This class consists of static methods used to manage ImageJ's windows. */
12 public class WindowManager {
13
14         public static boolean checkForDuplicateName;
15         private static Vector imageList = new Vector();          // list of image windows
16         private static Vector nonImageList = new Vector();       // list of non-image windows
17         private static ImageWindow currentWindow;                        // active image window
18         private static Frame frontWindow;
19         private static Hashtable tempImageTable = new Hashtable();
20         
21         private WindowManager() {
22         }
23
24         /** Makes the image contained in the specified window the active image. */
25         public static void setCurrentWindow(ImageWindow win) {
26                 setCurrentWindow(win,false);
27         }
28
29         /** Makes the specified image active. */
30         public static void setCurrentWindow(ImageWindow win,boolean suppressRecording) {
31                 if (win==null || win.isClosed() || win.getImagePlus()==null) // deadlock-"wait to lock"
32                         return;
33                 //IJ.log("setCurrentWindow: "+win.getImagePlus().getTitle()+" ("+(currentWindow!=null?currentWindow.getImagePlus().getTitle():"null") + ")");
34                 setWindow(win);
35                 tempImageTable.remove(Thread.currentThread());
36                 if (win==currentWindow || imageList.size()==0)
37                         return;
38                 if (currentWindow!=null) {
39                         // free up pixel buffers used by current window
40                         ImagePlus imp = currentWindow.getImagePlus();
41                         if (imp!=null ) {
42                                 imp.trimProcessor();
43                                 imp.saveRoi();
44                         }
45                 }
46                 Undo.reset();
47                 currentWindow = win;
48                 if (!suppressRecording && Recorder.record)
49                         Recorder.record("selectWindow", win.getTitle());
50                 Menus.updateMenus();
51         }
52         
53         /** Returns the active ImageWindow. */
54         public static ImageWindow getCurrentWindow() {
55                 //if (IJ.debugMode) IJ.write("ImageWindow.getCurrentWindow");
56                 return currentWindow;
57         }
58
59         static int getCurrentIndex() {
60                 return imageList.indexOf(currentWindow);
61         }
62
63         /** Returns a reference to the active image or null if there isn't one. */
64         public static ImagePlus getCurrentImage() {
65                 ImagePlus img = (ImagePlus)tempImageTable.get(Thread.currentThread());
66                 //String str = (img==null)?" null":"";
67                 if (img==null)
68                         img = getActiveImage();
69                 //if (img!=null) IJ.log("getCurrentImage: "+img.getTitle()+" "+Thread.currentThread().hashCode()+str);
70                 return img;
71         }
72
73         /** Makes the specified image temporarily the active 
74                 image for this thread. Call again with a null
75                 argument to revert to the previous active image. */
76         public static void setTempCurrentImage(ImagePlus img) {
77                 //IJ.log("setTempImage: "+(img!=null?img.getTitle():"null ")+Thread.currentThread().hashCode());
78                 if (img==null)
79                         tempImageTable.remove(Thread.currentThread());
80                 else
81                         tempImageTable.put(Thread.currentThread(), img);
82         }
83         
84         /** Sets a temporary image for the specified thread. */
85         public static void setTempCurrentImage(Thread thread, ImagePlus img) {
86                 if (thread==null)
87                         throw new RuntimeException("thread==null");
88                 if (img==null)
89                         tempImageTable.remove(thread);
90                 else
91                         tempImageTable.put(thread, img);
92         }
93
94         /** Returns the active ImagePlus. */
95         private static ImagePlus getActiveImage() {
96                 if (currentWindow!=null)
97                         return currentWindow.getImagePlus();
98                 else if (frontWindow!=null && (frontWindow instanceof ImageWindow))
99                         return ((ImageWindow)frontWindow).getImagePlus();
100                 else    if (imageList.size()>0) {       
101                         ImageWindow win = (ImageWindow)imageList.elementAt(imageList.size()-1);
102                         return win.getImagePlus();
103                 } else
104                         return Interpreter.getLastBatchModeImage(); 
105         }
106
107         /** Returns the number of open image windows. */
108         public static int getWindowCount() {
109                 int count = imageList.size();
110                 return count;
111         }
112
113         /** Returns the number of open images. */
114         public static int getImageCount() {
115                 int count = imageList.size();
116                 count += Interpreter.getBatchModeImageCount();
117                 if (count==0 && getCurrentImage()!=null)
118                         count = 1;
119                 return count;
120         }
121
122         /** Returns the front most window or null. */
123         public static Frame getFrontWindow() {
124                 return frontWindow;
125         }
126
127         /** Returns a list of the IDs of open images. Returns
128                 null if no windows are open. */
129         public synchronized static int[] getIDList() {
130                 int nWindows = imageList.size();
131                 int[] batchModeImages = Interpreter.getBatchModeImageIDs();
132                 int nBatchImages = batchModeImages.length;
133                 if ((nWindows+nBatchImages)==0)
134                         return null;
135                 int[] list = new int[nWindows+nBatchImages];
136                 for (int i=0; i<nBatchImages; i++)
137                         list[i] = batchModeImages[i];
138                 int index = 0;
139                 for (int i=nBatchImages; i<nBatchImages+nWindows; i++) {
140                         ImageWindow win = (ImageWindow)imageList.elementAt(index++);
141                         list[i] = win.getImagePlus().getID();
142                 }
143                 return list;
144         }
145
146         /** Returns an array containing a list of the non-image windows. */
147         public synchronized static Frame[] getNonImageWindows() {
148                 Frame[] list = new Frame[nonImageList.size()];
149                 nonImageList.copyInto((Frame[])list);
150                 return list;
151         }
152
153         /** For IDs less than zero, returns the ImagePlus with the specified ID. 
154                 Returns null if no open window has a matching ID or no images are open. 
155                 For IDs greater than zero, returns the Nth ImagePlus. Returns null if 
156                 the ID is zero. */
157         public synchronized static ImagePlus getImage(int imageID) {
158                 //if (IJ.debugMode) IJ.write("ImageWindow.getImage");
159                 if (imageID>0)
160                         imageID = getNthImageID(imageID);
161                 if (imageID==0 || getImageCount()==0)
162                         return null;
163                 ImagePlus imp2 = Interpreter.getBatchModeImage(imageID);
164                 if (imp2!=null)
165                         return imp2;
166                 ImagePlus imp = null;
167                 for (int i=0; i<imageList.size(); i++) {
168                         ImageWindow win = (ImageWindow)imageList.elementAt(i);
169                         imp2 = win.getImagePlus();
170                         if (imageID==imp2.getID()) {
171                                 imp = imp2;
172                                 break;
173                         }
174                 }
175                 imp2 = getCurrentImage();
176                 if (imp==null &&imp2!=null && imp2.getID()==imageID)
177                         return imp2;
178                 return imp;
179         }
180         
181         /** Returns the ID of the Nth open image. Returns zero if n<=0 
182                 or n greater than the number of open image windows. */
183         public synchronized static int getNthImageID(int n) {
184                         if (n<=0) return 0;
185             if (Interpreter.isBatchMode()) {
186                 int[] list = getIDList();
187                 if (n>list.length)
188                         return 0;
189                 else
190                         return list[n-1];
191             } else {
192                 if (n>imageList.size()) return 0;
193                 ImageWindow win = (ImageWindow)imageList.elementAt(n-1);
194                 if (win!=null)
195                     return win.getImagePlus().getID();
196                 else
197                     return 0;
198             }
199         }
200
201         
202         /** Returns the first image that has the specified title or null if it is not found. */
203         public synchronized static ImagePlus getImage(String title) {
204                 int[] wList = getIDList();
205                 if (wList==null) return null;
206                 for (int i=0; i<wList.length; i++) {
207                         ImagePlus imp = getImage(wList[i]);
208                         if (imp!=null && imp.getTitle().equals(title))
209                                 return imp;
210                 }
211                 return null;
212         }
213
214         /** Adds the specified window to the Window menu. */
215         public synchronized static void addWindow(Frame win) {
216                 //IJ.write("addWindow: "+win.getTitle());
217                 if (win==null)
218                         return;
219                 else if (win instanceof ImageWindow)
220                         addImageWindow((ImageWindow)win);
221                 else {
222                         Menus.insertWindowMenuItem(win);
223                         nonImageList.addElement(win);
224                 }
225     }
226
227         private static void addImageWindow(ImageWindow win) {
228                 ImagePlus imp = win.getImagePlus();
229                 if (imp==null) return;
230                 checkForDuplicateName(imp);
231                 imageList.addElement(win);
232                 Menus.addWindowMenuItem(imp);
233                 setCurrentWindow(win,true);
234         }
235
236         static void checkForDuplicateName(ImagePlus imp) {
237                 if (checkForDuplicateName) {
238                         String name = imp.getTitle();
239                         if (isDuplicateName(name))
240                                 imp.setTitle(getUniqueName(name));
241                 } 
242                 checkForDuplicateName = false;
243         }
244
245         static boolean isDuplicateName(String name) {
246                 int n = imageList.size();
247                 for (int i=0; i<n; i++) {
248                         ImageWindow win = (ImageWindow)imageList.elementAt(i);
249                         String name2 = win.getImagePlus().getTitle();
250                         if (name.equals(name2))
251                                 return true;
252                 }
253                 return false;
254         }
255
256         /** Returns a unique name by adding, before the extension,  -1, -2, etc. as needed. */
257         public static String getUniqueName(String name) {
258         String name2 = name;
259         String extension = "";
260         int len = name2.length();
261         int lastDot = name2.lastIndexOf(".");
262         if (lastDot!=-1 && len-lastDot<6 && lastDot!=len-1) {
263             extension = name2.substring(lastDot, len);
264             name2 = name2.substring(0, lastDot);
265         }
266         int lastDash = name2.lastIndexOf("-");
267         if (lastDash!=-1 && name2.length()-lastDash<4)
268             name2 = name2.substring(0, lastDash);
269         for (int i=1; i<=99; i++) {
270             String name3 = name2+"-"+ i + extension;
271             //IJ.log(i+" "+name3);
272             if (!isDuplicateName(name3))
273                 return name3;
274         }
275         return name;
276         }
277
278         /** If 'name' is not unique, adds -1, -2, etc. as needed to make it unique. */
279         public static String makeUniqueName(String name) {
280         return isDuplicateName(name)?getUniqueName(name):name;
281     }
282
283         /** Removes the specified window from the Window menu. */
284         public synchronized static void removeWindow(Frame win) {
285                 //IJ.write("removeWindow: "+win.getTitle());
286                 if (win instanceof ImageWindow)
287                         removeImageWindow((ImageWindow)win);
288                 else {
289                         int index = nonImageList.indexOf(win);
290                         ImageJ ij = IJ.getInstance();
291                         if (index>=0) {
292                                 //if (ij!=null && !ij.quitting())
293                                 Menus.removeWindowMenuItem(index);
294                                 nonImageList.removeElement(win);
295                         }
296                 }
297                 setWindow(null);
298         }
299
300         private static void removeImageWindow(ImageWindow win) {
301                 int index = imageList.indexOf(win);
302                 if (index==-1)
303                         return;  // not on the window list
304                 if (imageList.size()>1) {
305                         int newIndex = index-1;
306                         if (newIndex<0)
307                                 newIndex = imageList.size()-1;
308                         setCurrentWindow((ImageWindow)imageList.elementAt(newIndex));
309                 } else
310                         currentWindow = null;
311                 imageList.removeElementAt(index);
312                 setTempCurrentImage(null);  //???
313                 int nonImageCount = nonImageList.size();
314                 if (nonImageCount>0)
315                         nonImageCount++;
316                 Menus.removeWindowMenuItem(nonImageCount+index);
317                 Menus.updateMenus();
318                 Undo.reset();
319         }
320
321         /** The specified frame becomes the front window, the one returnd by getFrontWindow(). */
322         public static void setWindow(Frame win) {
323                 /*
324                 if (Recorder.record && win!=null && win!=frontWindow) {
325                         String title = win.getTitle();
326                         IJ.log("Set window: "+title+"  "+(getFrame(title)!=null?"not null":"null"));
327                         if (getFrame(title)!=null && !title.equals("Recorder"))
328                                 Recorder.record("selectWindow", title);
329                 }
330                 */
331                 frontWindow = win;
332                 //IJ.log("Set window: "+(win!=null?win.getTitle():"null"));
333                 if (IJ.getApplet() != null && win instanceof ImageWindow) {
334                         currentWindow = (ImageWindow)win;
335                         tempImageTable.remove(Thread.currentThread());
336                         ImagePlus current = currentWindow.getImagePlus();
337                         if (current != null)
338                                 current.setActivated();
339                 }
340     }
341
342         /** Closes all windows. Stops and returns false if any image "save changes" dialog is canceled. */
343         public synchronized static boolean closeAllWindows() {
344                 while (imageList.size()>0) {
345                         if (!((ImageWindow)imageList.elementAt(0)).close())
346                                 return false;
347                         IJ.wait(100);
348                 }
349                 ImageJ ij = IJ.getInstance();
350                 if (ij!=null && ij.quitting() && IJ.getApplet()==null)
351                         return true;
352                 Frame[] list = getNonImageWindows();
353                 for (int i=0; i<list.length; i++) {
354                         Frame frame = list[i];
355                         if (frame instanceof PlugInFrame)
356                                 ((PlugInFrame)frame).close();
357                         else if (frame instanceof TextWindow)
358                                 ((TextWindow)frame).close();
359                         else {
360                                 frame.setVisible(false);
361                                 frame.dispose();
362                         }
363                 }
364                 return true;
365     }
366     
367         /** Activates the next image window on the window list. */
368         public static void putBehind() {
369                 if (IJ.debugMode) IJ.log("putBehind");
370                 if(imageList.size()<1 || currentWindow==null)
371                         return;
372                 int index = imageList.indexOf(currentWindow);
373                 ImageWindow win;
374                 int count = 0;
375                 do {
376                         index--;
377                         if (index<0) index = imageList.size()-1;
378                         win = (ImageWindow)imageList.elementAt(index);
379                         if (++count==imageList.size()) return;
380                 } while (win instanceof HistogramWindow || win instanceof PlotWindow);
381                 setCurrentWindow(win);
382                 win.toFront();
383                 Menus.updateMenus();
384     }
385
386         /** Returns the temporary current image for this thread, or null. */
387         public static ImagePlus getTempCurrentImage() {
388                 return (ImagePlus)tempImageTable.get(Thread.currentThread()); 
389         }
390
391     /** Returns the frame with the specified title or null if a frame with that 
392         title is not found. */
393     public static Frame getFrame(String title) {
394                 for (int i=0; i<nonImageList.size(); i++) {
395                         Frame frame = (Frame)nonImageList.elementAt(i);
396                         if (title.equals(frame.getTitle()))
397                                 return frame;
398                 }
399                 int[] wList = getIDList();
400                 int len = wList!=null?wList.length:0;
401                 for (int i=0; i<len; i++) {
402                         ImagePlus imp = getImage(wList[i]);
403                         if (imp!=null) {
404                                 if (imp.getTitle().equals(title))
405                                         return imp.getWindow();
406                         }
407                 }
408                 return null;
409     }
410
411         /** Activates a window selected from the Window menu. */
412         synchronized static void activateWindow(String menuItemLabel, MenuItem item) {
413                 //IJ.write("activateWindow: "+menuItemLabel+" "+item);
414                 for (int i=0; i<nonImageList.size(); i++) {
415                         Frame win = (Frame)nonImageList.elementAt(i);
416                         String title = win.getTitle();
417                         if (menuItemLabel.equals(title)) {
418                                 win.toFront();
419                                 ((CheckboxMenuItem)item).setState(false);
420                                 if (Recorder.record)
421                                         Recorder.record("selectWindow", title);
422                                 return;
423                         }
424                 }
425                 int lastSpace = menuItemLabel.lastIndexOf(' ');
426                 if (lastSpace>0) // remove image size (e.g., " 90K")
427                         menuItemLabel = menuItemLabel.substring(0, lastSpace);
428                 for (int i=0; i<imageList.size(); i++) {
429                         ImageWindow win = (ImageWindow)imageList.elementAt(i);
430                         String title = win.getImagePlus().getTitle();
431                         if (menuItemLabel.equals(title)) {
432                                 setCurrentWindow(win);
433                                 win.toFront();
434                                 int index = imageList.indexOf(win);
435                                 int n = Menus.window.getItemCount();
436                                 int start = Menus.WINDOW_MENU_ITEMS+Menus.windowMenuItems2;
437                                 for (int j=start; j<n; j++) {
438                                         MenuItem mi = Menus.window.getItem(j);
439                                         ((CheckboxMenuItem)mi).setState((j-start)==index);                                              
440                                 }
441                                 break;
442                         }
443                 }
444     }
445     
446     /** Repaints all open image windows. */
447     public synchronized static void repaintImageWindows() {
448                 int[] list = getIDList();
449                 if (list==null) return;
450                 for (int i=0; i<list.length; i++) {
451                         ImagePlus imp2 = getImage(list[i]);
452                         if (imp2!=null) {
453                                 imp2.setTitle(imp2.getTitle()); // update "(G)" flag (global calibration)
454                                 ImageWindow win = imp2.getWindow();
455                                 if (win!=null) win.repaint();
456                         }
457                 }
458         }
459     
460         static void showList() {
461                 if (IJ.debugMode) {
462                         for (int i=0; i<imageList.size(); i++) {
463                                 ImageWindow win = (ImageWindow)imageList.elementAt(i);
464                                 ImagePlus imp = win.getImagePlus();
465                                 IJ.log(i + " " + imp.getTitle() + (win==currentWindow?"*":""));
466                         }
467                         IJ.log(" ");
468                 }
469     }
470     
471 }