772a5d7ca5f8bbd4429ff4083a1762daa69c1158
[jalview.git] / src / jalview / gui / Desktop.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.bin.Cache;
24 import jalview.io.FileLoader;
25 import jalview.io.FormatAdapter;
26 import jalview.io.IdentifyFile;
27 import jalview.io.JalviewFileChooser;
28 import jalview.io.JalviewFileView;
29 import jalview.util.ImageMaker;
30 import jalview.util.MessageManager;
31 import jalview.ws.params.ParamManager;
32
33 import java.awt.BorderLayout;
34 import java.awt.Color;
35 import java.awt.Dimension;
36 import java.awt.FontMetrics;
37 import java.awt.Graphics;
38 import java.awt.GridLayout;
39 import java.awt.Point;
40 import java.awt.Rectangle;
41 import java.awt.Toolkit;
42 import java.awt.datatransfer.Clipboard;
43 import java.awt.datatransfer.ClipboardOwner;
44 import java.awt.datatransfer.DataFlavor;
45 import java.awt.datatransfer.Transferable;
46 import java.awt.dnd.DnDConstants;
47 import java.awt.dnd.DropTargetDragEvent;
48 import java.awt.dnd.DropTargetDropEvent;
49 import java.awt.dnd.DropTargetEvent;
50 import java.awt.dnd.DropTargetListener;
51 import java.awt.event.ActionEvent;
52 import java.awt.event.ActionListener;
53 import java.awt.event.FocusEvent;
54 import java.awt.event.FocusListener;
55 import java.awt.event.MouseAdapter;
56 import java.awt.event.MouseEvent;
57 import java.awt.event.MouseListener;
58 import java.awt.event.WindowAdapter;
59 import java.awt.event.WindowEvent;
60 import java.beans.PropertyChangeEvent;
61 import java.beans.PropertyChangeListener;
62 import java.beans.PropertyVetoException;
63 import java.io.BufferedInputStream;
64 import java.io.File;
65 import java.io.FileOutputStream;
66 import java.lang.reflect.Constructor;
67 import java.net.URL;
68 import java.util.ArrayList;
69 import java.util.Hashtable;
70 import java.util.StringTokenizer;
71 import java.util.Vector;
72 import java.util.concurrent.ExecutorService;
73 import java.util.concurrent.Executors;
74 import java.util.concurrent.Semaphore;
75
76 import javax.swing.DefaultDesktopManager;
77 import javax.swing.DesktopManager;
78 import javax.swing.JButton;
79 import javax.swing.JComboBox;
80 import javax.swing.JComponent;
81 import javax.swing.JDesktopPane;
82 import javax.swing.JFrame;
83 import javax.swing.JInternalFrame;
84 import javax.swing.JLabel;
85 import javax.swing.JMenuItem;
86 import javax.swing.JOptionPane;
87 import javax.swing.JPanel;
88 import javax.swing.JPopupMenu;
89 import javax.swing.JProgressBar;
90 import javax.swing.SwingUtilities;
91 import javax.swing.event.HyperlinkEvent;
92 import javax.swing.event.MenuEvent;
93 import javax.swing.event.MenuListener;
94 import javax.swing.event.HyperlinkEvent.EventType;
95
96 /**
97  * Jalview Desktop
98  * 
99  * 
100  * @author $author$
101  * @version $Revision: 1.155 $
102  */
103 public class Desktop extends jalview.jbgui.GDesktop implements
104         DropTargetListener, ClipboardOwner, IProgressIndicator,
105         jalview.api.StructureSelectionManagerProvider
106 {
107
108   private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
109
110   /**
111    * news reader - null if it was never started.
112    */
113   private BlogReader jvnews = null;
114
115   /**
116    * @param listener
117    * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
118    */
119   public void addJalviewPropertyChangeListener(
120           PropertyChangeListener listener)
121   {
122     changeSupport.addJalviewPropertyChangeListener(listener);
123   }
124
125   /**
126    * @param propertyName
127    * @param listener
128    * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
129    *      java.beans.PropertyChangeListener)
130    */
131   public void addJalviewPropertyChangeListener(String propertyName,
132           PropertyChangeListener listener)
133   {
134     changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
135   }
136
137   /**
138    * @param propertyName
139    * @param listener
140    * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
141    *      java.beans.PropertyChangeListener)
142    */
143   public void removeJalviewPropertyChangeListener(String propertyName,
144           PropertyChangeListener listener)
145   {
146     changeSupport.removeJalviewPropertyChangeListener(propertyName,
147             listener);
148   }
149
150   /** Singleton Desktop instance */
151   public static Desktop instance;
152
153   public static MyDesktopPane desktop;
154
155   static int openFrameCount = 0;
156
157   static final int xOffset = 30;
158
159   static final int yOffset = 30;
160
161   public static jalview.ws.jws1.Discoverer discoverer;
162
163   public static Object[] jalviewClipboard;
164
165   public static boolean internalCopy = false;
166
167   static int fileLoadingCount = 0;
168
169   class MyDesktopManager implements DesktopManager
170   {
171
172     private DesktopManager delegate;
173
174     public MyDesktopManager(DesktopManager delegate)
175     {
176       this.delegate = delegate;
177     }
178
179     public void activateFrame(JInternalFrame f)
180     {
181       try
182       {
183         delegate.activateFrame(f);
184       } catch (NullPointerException npe)
185       {
186         Point p = getMousePosition();
187         instance.showPasteMenu(p.x, p.y);
188       }
189     }
190
191     public void beginDraggingFrame(JComponent f)
192     {
193       delegate.beginDraggingFrame(f);
194     }
195
196     public void beginResizingFrame(JComponent f, int direction)
197     {
198       delegate.beginResizingFrame(f, direction);
199     }
200
201     public void closeFrame(JInternalFrame f)
202     {
203       delegate.closeFrame(f);
204     }
205
206     public void deactivateFrame(JInternalFrame f)
207     {
208       delegate.deactivateFrame(f);
209     }
210
211     public void deiconifyFrame(JInternalFrame f)
212     {
213       delegate.deiconifyFrame(f);
214     }
215
216     public void dragFrame(JComponent f, int newX, int newY)
217     {
218       if (newY < 0)
219       {
220         newY = 0;
221       }
222       delegate.dragFrame(f, newX, newY);
223     }
224
225     public void endDraggingFrame(JComponent f)
226     {
227       delegate.endDraggingFrame(f);
228     }
229
230     public void endResizingFrame(JComponent f)
231     {
232       delegate.endResizingFrame(f);
233     }
234
235     public void iconifyFrame(JInternalFrame f)
236     {
237       delegate.iconifyFrame(f);
238     }
239
240     public void maximizeFrame(JInternalFrame f)
241     {
242       delegate.maximizeFrame(f);
243     }
244
245     public void minimizeFrame(JInternalFrame f)
246     {
247       delegate.minimizeFrame(f);
248     }
249
250     public void openFrame(JInternalFrame f)
251     {
252       delegate.openFrame(f);
253     }
254
255     public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
256             int newHeight)
257     {
258       Rectangle b = desktop.getBounds();
259       if (newY < 0)
260       {
261         newY = 0;
262       }
263       delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
264     }
265
266     public void setBoundsForFrame(JComponent f, int newX, int newY,
267             int newWidth, int newHeight)
268     {
269       delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
270     }
271
272     // All other methods, simply delegate
273
274   }
275
276   /**
277    * Creates a new Desktop object.
278    */
279   public Desktop()
280   {
281     /**
282      * A note to implementors. It is ESSENTIAL that any activities that might
283      * block are spawned off as threads rather than waited for during this
284      * constructor.
285      */
286     instance = this;
287     doVamsasClientCheck();
288     doGroovyCheck();
289
290     setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
291     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
292     boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
293             false);
294     boolean showjconsole = jalview.bin.Cache.getDefault(
295             "SHOW_JAVA_CONSOLE", false);
296     desktop = new MyDesktopPane(selmemusage);
297     showMemusage.setSelected(selmemusage);
298     desktop.setBackground(Color.white);
299     getContentPane().setLayout(new BorderLayout());
300     // alternate config - have scrollbars - see notes in JAL-153
301     // JScrollPane sp = new JScrollPane();
302     // sp.getViewport().setView(desktop);
303     // getContentPane().add(sp, BorderLayout.CENTER);
304     getContentPane().add(desktop, BorderLayout.CENTER);
305     desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
306
307     // This line prevents Windows Look&Feel resizing all new windows to maximum
308     // if previous window was maximised
309     desktop.setDesktopManager(new MyDesktopManager(
310             new DefaultDesktopManager()));
311
312     Rectangle dims = getLastKnownDimensions("");
313     if (dims != null)
314     {
315       setBounds(dims);
316     }
317     else
318     {
319       Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
320       setBounds((int) (screenSize.width - 900) / 2,
321               (int) (screenSize.height - 650) / 2, 900, 650);
322     }
323     jconsole = new Console(this, showjconsole);
324     // add essential build information
325     jconsole.setHeader("Jalview Desktop "
326             + jalview.bin.Cache.getProperty("VERSION") + "\n"
327             + "Build Date: "
328             + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
329             + "Java version: " + System.getProperty("java.version") + "\n"
330             + System.getProperty("os.arch") + " "
331             + System.getProperty("os.name") + " "
332             + System.getProperty("os.version"));
333
334     showConsole(showjconsole);
335
336     showNews.setVisible(false);
337
338     this.addWindowListener(new WindowAdapter()
339     {
340       public void windowClosing(WindowEvent evt)
341       {
342         quit();
343       }
344     });
345
346     MouseAdapter ma;
347     this.addMouseListener(ma = new MouseAdapter()
348     {
349       public void mousePressed(MouseEvent evt)
350       {
351         if (SwingUtilities.isRightMouseButton(evt))
352         {
353           showPasteMenu(evt.getX(), evt.getY());
354         }
355       }
356     });
357     desktop.addMouseListener(ma);
358
359     this.addFocusListener(new FocusListener()
360     {
361
362       @Override
363       public void focusLost(FocusEvent e)
364       {
365         // TODO Auto-generated method stub
366
367       }
368
369       @Override
370       public void focusGained(FocusEvent e)
371       {
372         Cache.log.debug("Relaying windows after focus gain");
373         // make sure that we sort windows properly after we gain focus
374         instance.relayerWindows();
375       }
376     });
377     this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
378     // Spawn a thread that shows the splashscreen
379     SwingUtilities.invokeLater(new Runnable()
380     {
381       public void run()
382       {
383         new SplashScreen();
384       }
385     });
386
387     // displayed.
388     // Thread off a new instance of the file chooser - this reduces the time it
389     // takes to open it later on.
390     new Thread(new Runnable()
391     {
392       public void run()
393       {
394         Cache.log.debug("Filechooser init thread started.");
395         JalviewFileChooser chooser = new JalviewFileChooser(
396                 jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
397                 jalview.io.AppletFormatAdapter.READABLE_EXTENSIONS,
398                 jalview.io.AppletFormatAdapter.READABLE_FNAMES,
399                 jalview.bin.Cache.getProperty("DEFAULT_FILE_FORMAT"));
400         Cache.log.debug("Filechooser init thread finished.");
401       }
402     }).start();
403     // Add the service change listener
404     changeSupport.addJalviewPropertyChangeListener("services",
405             new PropertyChangeListener()
406             {
407
408               @Override
409               public void propertyChange(PropertyChangeEvent evt)
410               {
411                 Cache.log.debug("Firing service changed event for "
412                         + evt.getNewValue());
413                 JalviewServicesChanged(evt);
414               }
415
416             });
417   }
418
419   public void checkForNews()
420   {
421     final Desktop me = this;
422     // Thread off the news reader, in case there are connection problems.
423     addDialogThread(new Runnable()
424     {
425       @Override
426       public void run()
427       {
428         Cache.log.debug("Starting news thread.");
429
430         jvnews = new BlogReader(me);
431         showNews.setVisible(true);
432         Cache.log.debug("Completed news thread.");
433       }
434     });
435   }
436
437   protected void showNews_actionPerformed(ActionEvent e)
438   {
439     showNews(showNews.isSelected());
440   }
441
442   void showNews(boolean visible)
443   {
444     {
445       Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
446       showNews.setSelected(visible);
447       if (visible && !jvnews.isVisible())
448       {
449         new Thread(new Runnable()
450         {
451           @Override
452           public void run()
453           {
454             long instance = System.currentTimeMillis();
455             Desktop.instance.setProgressBar("Refreshing news", instance);
456             jvnews.refreshNews();
457             Desktop.instance.setProgressBar(null, instance);
458             jvnews.showNews();
459           }
460         }).start();
461       }
462     }
463   }
464
465   /**
466    * recover the last known dimensions for a jalview window
467    * 
468    * @param windowName
469    *          - empty string is desktop, all other windows have unique prefix
470    * @return null or last known dimensions scaled to current geometry (if last
471    *         window geom was known)
472    */
473   Rectangle getLastKnownDimensions(String windowName)
474   {
475     // TODO: lock aspect ratio for scaling desktop Bug #0058199
476     Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
477     String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
478     String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
479     String width = jalview.bin.Cache.getProperty(windowName
480             + "SCREEN_WIDTH");
481     String height = jalview.bin.Cache.getProperty(windowName
482             + "SCREEN_HEIGHT");
483     if ((x != null) && (y != null) && (width != null) && (height != null))
484     {
485       int ix = Integer.parseInt(x), iy = Integer.parseInt(y), iw = Integer
486               .parseInt(width), ih = Integer.parseInt(height);
487       if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
488       {
489         // attempt #1 - try to cope with change in screen geometry - this
490         // version doesn't preserve original jv aspect ratio.
491         // take ratio of current screen size vs original screen size.
492         double sw = ((1f * screenSize.width) / (1f * Integer
493                 .parseInt(jalview.bin.Cache
494                         .getProperty("SCREENGEOMETRY_WIDTH"))));
495         double sh = ((1f * screenSize.height) / (1f * Integer
496                 .parseInt(jalview.bin.Cache
497                         .getProperty("SCREENGEOMETRY_HEIGHT"))));
498         // rescale the bounds depending upon the current screen geometry.
499         ix = (int) (ix * sw);
500         iw = (int) (iw * sw);
501         iy = (int) (iy * sh);
502         ih = (int) (ih * sh);
503         while (ix >= screenSize.width)
504         {
505           jalview.bin.Cache.log
506                   .debug("Window geometry location recall error: shifting horizontal to within screenbounds.");
507           ix -= screenSize.width;
508         }
509         while (iy >= screenSize.height)
510         {
511           jalview.bin.Cache.log
512                   .debug("Window geometry location recall error: shifting vertical to within screenbounds.");
513           iy -= screenSize.height;
514         }
515         jalview.bin.Cache.log.debug("Got last known dimensions for "
516                 + windowName + ": x:" + ix + " y:" + iy + " width:" + iw
517                 + " height:" + ih);
518       }
519       // return dimensions for new instance
520       return new Rectangle(ix, iy, iw, ih);
521     }
522     return null;
523   }
524
525   private void doVamsasClientCheck()
526   {
527     if (jalview.bin.Cache.vamsasJarsPresent())
528     {
529       setupVamsasDisconnectedGui();
530       VamsasMenu.setVisible(true);
531       final Desktop us = this;
532       VamsasMenu.addMenuListener(new MenuListener()
533       {
534         // this listener remembers when the menu was first selected, and
535         // doesn't rebuild the session list until it has been cleared and
536         // reselected again.
537         boolean refresh = true;
538
539         public void menuCanceled(MenuEvent e)
540         {
541           refresh = true;
542         }
543
544         public void menuDeselected(MenuEvent e)
545         {
546           refresh = true;
547         }
548
549         public void menuSelected(MenuEvent e)
550         {
551           if (refresh)
552           {
553             us.buildVamsasStMenu();
554             refresh = false;
555           }
556         }
557       });
558       vamsasStart.setVisible(true);
559     }
560   }
561
562   void showPasteMenu(int x, int y)
563   {
564     JPopupMenu popup = new JPopupMenu();
565     JMenuItem item = new JMenuItem(
566             MessageManager.getString("label.paste_new_window"));
567     item.addActionListener(new ActionListener()
568     {
569       public void actionPerformed(ActionEvent evt)
570       {
571         paste();
572       }
573     });
574
575     popup.add(item);
576     popup.show(this, x, y);
577   }
578
579   public void paste()
580   {
581     try
582     {
583       Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
584       Transferable contents = c.getContents(this);
585
586       if (contents != null)
587       {
588         String file = (String) contents
589                 .getTransferData(DataFlavor.stringFlavor);
590
591         String format = new IdentifyFile().Identify(file,
592                 FormatAdapter.PASTE);
593
594         new FileLoader().LoadFile(file, FormatAdapter.PASTE, format);
595
596       }
597     } catch (Exception ex)
598     {
599       System.out
600               .println("Unable to paste alignment from system clipboard:\n"
601                       + ex);
602     }
603   }
604
605   /**
606    * Adds and opens the given frame to the desktop
607    * 
608    * @param frame
609    *          DOCUMENT ME!
610    * @param title
611    *          DOCUMENT ME!
612    * @param w
613    *          DOCUMENT ME!
614    * @param h
615    *          DOCUMENT ME!
616    */
617   public static synchronized void addInternalFrame(
618           final JInternalFrame frame, String title, int w, int h)
619   {
620     addInternalFrame(frame, title, w, h, true);
621   }
622
623   /**
624    * DOCUMENT ME!
625    * 
626    * @param frame
627    *          DOCUMENT ME!
628    * @param title
629    *          DOCUMENT ME!
630    * @param w
631    *          DOCUMENT ME!
632    * @param h
633    *          DOCUMENT ME!
634    * @param resizable
635    *          DOCUMENT ME!
636    */
637   public static synchronized void addInternalFrame(
638           final JInternalFrame frame, String title, int w, int h,
639           boolean resizable)
640   {
641
642     // TODO: allow callers to determine X and Y position of frame (eg. via
643     // bounds object).
644     // TODO: consider fixing method to update entries in the window submenu with
645     // the current window title
646
647     frame.setTitle(title);
648     if (frame.getWidth() < 1 || frame.getHeight() < 1)
649     {
650       frame.setSize(w, h);
651     }
652     // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
653     // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
654     // IF JALVIEW IS RUNNING HEADLESS
655     // ///////////////////////////////////////////////
656     if (instance == null
657             || (System.getProperty("java.awt.headless") != null && System
658                     .getProperty("java.awt.headless").equals("true")))
659     {
660       return;
661     }
662
663     openFrameCount++;
664
665     frame.setVisible(true);
666     frame.setClosable(true);
667     frame.setResizable(resizable);
668     frame.setMaximizable(resizable);
669     frame.setIconifiable(resizable);
670     frame.setFrameIcon(null);
671
672     if (frame.getX() < 1 && frame.getY() < 1)
673     {
674       frame.setLocation(xOffset * openFrameCount, yOffset
675               * ((openFrameCount - 1) % 10) + yOffset);
676     }
677
678     final JMenuItem menuItem = new JMenuItem(title);
679     frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
680     {
681       public void internalFrameActivated(
682               javax.swing.event.InternalFrameEvent evt)
683       {
684         JInternalFrame itf = desktop.getSelectedFrame();
685         if (itf != null)
686         {
687           itf.requestFocus();
688         }
689
690       }
691
692       public void internalFrameClosed(
693               javax.swing.event.InternalFrameEvent evt)
694       {
695         PaintRefresher.RemoveComponent(frame);
696         openFrameCount--;
697         windowMenu.remove(menuItem);
698         JInternalFrame itf = desktop.getSelectedFrame();
699         if (itf != null)
700         {
701           itf.requestFocus();
702         }
703         System.gc();
704       };
705     });
706
707     menuItem.addActionListener(new ActionListener()
708     {
709       public void actionPerformed(ActionEvent e)
710       {
711         try
712         {
713           frame.setSelected(true);
714           frame.setIcon(false);
715         } catch (java.beans.PropertyVetoException ex)
716         {
717
718         }
719       }
720     });
721     menuItem.addMouseListener(new MouseListener()
722     {
723
724       @Override
725       public void mouseReleased(MouseEvent e)
726       {
727       }
728
729       @Override
730       public void mousePressed(MouseEvent e)
731       {
732       }
733
734       @Override
735       public void mouseExited(MouseEvent e)
736       {
737         try
738         {
739           frame.setSelected(false);
740         } catch (PropertyVetoException e1)
741         {
742         }
743       }
744
745       @Override
746       public void mouseEntered(MouseEvent e)
747       {
748         try
749         {
750           frame.setSelected(true);
751         } catch (PropertyVetoException e1)
752         {
753         }
754       }
755
756       @Override
757       public void mouseClicked(MouseEvent e)
758       {
759
760       }
761     });
762
763     windowMenu.add(menuItem);
764
765     desktop.add(frame);
766     frame.toFront();
767     try
768     {
769       frame.setSelected(true);
770       frame.requestFocus();
771     } catch (java.beans.PropertyVetoException ve)
772     {
773     } catch (java.lang.ClassCastException cex)
774     {
775       Cache.log
776               .warn("Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
777                       cex);
778     }
779   }
780
781   public void lostOwnership(Clipboard clipboard, Transferable contents)
782   {
783     if (!internalCopy)
784     {
785       Desktop.jalviewClipboard = null;
786     }
787
788     internalCopy = false;
789   }
790
791   public void dragEnter(DropTargetDragEvent evt)
792   {
793   }
794
795   public void dragExit(DropTargetEvent evt)
796   {
797   }
798
799   public void dragOver(DropTargetDragEvent evt)
800   {
801   }
802
803   public void dropActionChanged(DropTargetDragEvent evt)
804   {
805   }
806
807   /**
808    * DOCUMENT ME!
809    * 
810    * @param evt
811    *          DOCUMENT ME!
812    */
813   public void drop(DropTargetDropEvent evt)
814   {
815     boolean success = true;
816     Transferable t = evt.getTransferable();
817     java.util.List files = null;
818     java.util.List protocols = null;
819
820     try
821     {
822       DataFlavor uriListFlavor = new DataFlavor(
823               "text/uri-list;class=java.lang.String");
824       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
825       {
826         // Works on Windows and MacOSX
827         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
828         files = (java.util.List) t
829                 .getTransferData(DataFlavor.javaFileListFlavor);
830       }
831       else if (t.isDataFlavorSupported(uriListFlavor))
832       {
833         // This is used by Unix drag system
834         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
835         String data = (String) t.getTransferData(uriListFlavor);
836         files = new java.util.ArrayList(1);
837         protocols = new java.util.ArrayList(1);
838         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
839                 data, "\r\n"); st.hasMoreTokens();)
840         {
841           String s = st.nextToken();
842           if (s.startsWith("#"))
843           {
844             // the line is a comment (as per the RFC 2483)
845             continue;
846           }
847           java.net.URI uri = new java.net.URI(s);
848           if (uri.getScheme().toLowerCase().startsWith("http"))
849           {
850             protocols.add(FormatAdapter.URL);
851             files.add(uri.toString());
852           }
853           else
854           {
855             // otherwise preserve old behaviour: catch all for file objects
856             java.io.File file = new java.io.File(uri);
857             protocols.add(FormatAdapter.FILE);
858             files.add(file.toString());
859           }
860         }
861       }
862     } catch (Exception e)
863     {
864       success = false;
865     }
866
867     if (files != null)
868     {
869       try
870       {
871         for (int i = 0; i < files.size(); i++)
872         {
873           String file = files.get(i).toString();
874           String protocol = (protocols == null) ? FormatAdapter.FILE
875                   : (String) protocols.get(i);
876           String format = null;
877
878           if (file.endsWith(".jar"))
879           {
880             format = "Jalview";
881
882           }
883           else
884           {
885             format = new IdentifyFile().Identify(file, protocol);
886           }
887
888           new FileLoader().LoadFile(file, protocol, format);
889
890         }
891       } catch (Exception ex)
892       {
893         success = false;
894       }
895     }
896     evt.dropComplete(success); // need this to ensure input focus is properly
897                                // transfered to any new windows created
898   }
899
900   /**
901    * DOCUMENT ME!
902    * 
903    * @param e
904    *          DOCUMENT ME!
905    */
906   public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
907   {
908     JalviewFileChooser chooser = new JalviewFileChooser(
909             jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
910             jalview.io.AppletFormatAdapter.READABLE_EXTENSIONS,
911             jalview.io.AppletFormatAdapter.READABLE_FNAMES,
912             jalview.bin.Cache.getProperty("DEFAULT_FILE_FORMAT"));
913
914     chooser.setFileView(new JalviewFileView());
915     chooser.setDialogTitle(MessageManager
916             .getString("label.open_local_file"));
917     chooser.setToolTipText(MessageManager.getString("action.open"));
918
919     int value = chooser.showOpenDialog(this);
920
921     if (value == JalviewFileChooser.APPROVE_OPTION)
922     {
923       String choice = chooser.getSelectedFile().getPath();
924       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
925               .getSelectedFile().getParent());
926
927       String format = null;
928       if (chooser.getSelectedFormat() != null
929               && chooser.getSelectedFormat().equals("Jalview"))
930       {
931         format = "Jalview";
932       }
933       else
934       {
935         format = new IdentifyFile().Identify(choice, FormatAdapter.FILE);
936       }
937
938       if (viewport != null)
939       {
940         new FileLoader().LoadFile(viewport, choice, FormatAdapter.FILE,
941                 format);
942       }
943       else
944       {
945         new FileLoader().LoadFile(choice, FormatAdapter.FILE, format);
946       }
947     }
948   }
949
950   /**
951    * DOCUMENT ME!
952    * 
953    * @param e
954    *          DOCUMENT ME!
955    */
956   public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
957   {
958     // This construct allows us to have a wider textfield
959     // for viewing
960     JLabel label = new JLabel(
961             MessageManager.getString("label.input_file_url"));
962     final JComboBox history = new JComboBox();
963
964     JPanel panel = new JPanel(new GridLayout(2, 1));
965     panel.add(label);
966     panel.add(history);
967     history.setPreferredSize(new Dimension(400, 20));
968     history.setEditable(true);
969     history.addItem("http://www.");
970
971     String historyItems = jalview.bin.Cache.getProperty("RECENT_URL");
972
973     StringTokenizer st;
974
975     if (historyItems != null)
976     {
977       st = new StringTokenizer(historyItems, "\t");
978
979       while (st.hasMoreTokens())
980       {
981         history.addItem(st.nextElement());
982       }
983     }
984
985     int reply = JOptionPane.showInternalConfirmDialog(desktop, panel,
986             MessageManager.getString("label.input_alignment_from_url"),
987             JOptionPane.OK_CANCEL_OPTION);
988
989     if (reply != JOptionPane.OK_OPTION)
990     {
991       return;
992     }
993
994     String url = history.getSelectedItem().toString();
995
996     if (url.toLowerCase().endsWith(".jar"))
997     {
998       if (viewport != null)
999       {
1000         new FileLoader().LoadFile(viewport, url, FormatAdapter.URL,
1001                 "Jalview");
1002       }
1003       else
1004       {
1005         new FileLoader().LoadFile(url, FormatAdapter.URL, "Jalview");
1006       }
1007     }
1008     else
1009     {
1010       String format = new IdentifyFile().Identify(url, FormatAdapter.URL);
1011
1012       if (format.equals("URL NOT FOUND"))
1013       {
1014         JOptionPane.showInternalMessageDialog(Desktop.desktop,
1015                 "Couldn't locate " + url, "URL not found",
1016                 JOptionPane.WARNING_MESSAGE);
1017
1018         return;
1019       }
1020
1021       if (viewport != null)
1022       {
1023         new FileLoader().LoadFile(viewport, url, FormatAdapter.URL, format);
1024       }
1025       else
1026       {
1027         new FileLoader().LoadFile(url, FormatAdapter.URL, format);
1028       }
1029     }
1030   }
1031
1032   /**
1033    * DOCUMENT ME!
1034    * 
1035    * @param e
1036    *          DOCUMENT ME!
1037    */
1038   public void inputTextboxMenuItem_actionPerformed(AlignViewport viewport)
1039   {
1040     CutAndPasteTransfer cap = new CutAndPasteTransfer();
1041     cap.setForInput(viewport);
1042     Desktop.addInternalFrame(cap, "Cut & Paste Alignment File", 600, 500);
1043   }
1044
1045   /*
1046    * Exit the program
1047    */
1048   public void quit()
1049   {
1050     Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1051     jalview.bin.Cache
1052             .setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1053     jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height
1054             + "");
1055     storeLastKnownDimensions("", new Rectangle(getBounds().x,
1056             getBounds().y, getWidth(), getHeight()));
1057
1058     if (jconsole != null)
1059     {
1060       storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1061       jconsole.stopConsole();
1062     }
1063     if (jvnews != null)
1064     {
1065       storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1066
1067     }
1068     if (dialogExecutor != null)
1069     {
1070       dialogExecutor.shutdownNow();
1071     }
1072
1073     System.exit(0);
1074   }
1075
1076   private void storeLastKnownDimensions(String string, Rectangle jc)
1077   {
1078     jalview.bin.Cache.log.debug("Storing last known dimensions for "
1079             + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1080             + " height:" + jc.height);
1081
1082     jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1083     jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1084     jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1085     jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1086   }
1087
1088   /**
1089    * DOCUMENT ME!
1090    * 
1091    * @param e
1092    *          DOCUMENT ME!
1093    */
1094   public void aboutMenuItem_actionPerformed(ActionEvent e)
1095   {
1096     // StringBuffer message = getAboutMessage(false);
1097     // JOptionPane.showInternalMessageDialog(Desktop.desktop,
1098     //
1099     // message.toString(), "About Jalview", JOptionPane.INFORMATION_MESSAGE);
1100     new Thread(new Runnable()
1101     {
1102       public void run()
1103       {
1104         new SplashScreen(true);
1105       }
1106     }).start();
1107   }
1108
1109   public StringBuffer getAboutMessage(boolean shortv)
1110   {
1111     StringBuffer message = new StringBuffer();
1112     message.append("<html>");
1113     if (shortv)
1114     {
1115       message.append("<h1><strong>Version: "
1116               + jalview.bin.Cache.getProperty("VERSION")
1117               + "</strong></h1>");
1118       message.append("<strong>Last Updated: <em>"
1119               + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1120               + "</em></strong>");
1121
1122     }
1123     else
1124     {
1125
1126       message.append("<strong>Version "
1127               + jalview.bin.Cache.getProperty("VERSION")
1128               + "; last updated: "
1129               + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1130     }
1131
1132     if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking").equals(
1133             "Checking"))
1134     {
1135       message.append("<br>...Checking latest version...</br>");
1136     }
1137     else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1138             .equals(jalview.bin.Cache.getProperty("VERSION")))
1139     {
1140       boolean red = false;
1141       if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1142               .indexOf("automated build") == -1)
1143       {
1144         red = true;
1145         // Displayed when code version and jnlp version do not match and code
1146         // version is not a development build
1147         message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1148       }
1149
1150       message.append("<br>!! Version "
1151               + jalview.bin.Cache.getDefault("LATEST_VERSION",
1152                       "..Checking..")
1153               + " is available for download from "
1154               + jalview.bin.Cache.getDefault("www.jalview.org",
1155                       "http://www.jalview.org") + " !!");
1156       if (red)
1157       {
1158         message.append("</div>");
1159       }
1160     }
1161     message.append("<br>Authors:  "
1162             + jalview.bin.Cache
1163                     .getDefault(
1164                             "AUTHORFNAMES",
1165                             "The Jalview Authors (See AUTHORS file for current list)")
1166             + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1167             + "<br><br>For help, see the FAQ at <a href=\"http://www.jalview.org/faq\">www.jalview.org/faq</a> and/or join the jalview-discuss@jalview.org mailing list"
1168             + "<br><br>If  you use Jalview, please cite:"
1169             + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1170             + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1171             + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1172             + "</html>");
1173     return message;
1174   }
1175
1176   /**
1177    * DOCUMENT ME!
1178    * 
1179    * @param e
1180    *          DOCUMENT ME!
1181    */
1182   public void documentationMenuItem_actionPerformed(ActionEvent e)
1183   {
1184     try
1185     {
1186       ClassLoader cl = jalview.gui.Desktop.class.getClassLoader();
1187       java.net.URL url = javax.help.HelpSet.findHelpSet(cl, "help/help");
1188       javax.help.HelpSet hs = new javax.help.HelpSet(cl, url);
1189
1190       javax.help.HelpBroker hb = hs.createHelpBroker();
1191       hb.setCurrentID("home");
1192       hb.setDisplayed(true);
1193     } catch (Exception ex)
1194     {
1195     }
1196   }
1197
1198   public void closeAll_actionPerformed(ActionEvent e)
1199   {
1200     JInternalFrame[] frames = desktop.getAllFrames();
1201     for (int i = 0; i < frames.length; i++)
1202     {
1203       try
1204       {
1205         frames[i].setClosed(true);
1206       } catch (java.beans.PropertyVetoException ex)
1207       {
1208       }
1209     }
1210     System.out.println("ALL CLOSED");
1211     if (v_client != null)
1212     {
1213       // TODO clear binding to vamsas document objects on close_all
1214
1215     }
1216   }
1217
1218   public void raiseRelated_actionPerformed(ActionEvent e)
1219   {
1220     reorderAssociatedWindows(false, false);
1221   }
1222
1223   public void minimizeAssociated_actionPerformed(ActionEvent e)
1224   {
1225     reorderAssociatedWindows(true, false);
1226   }
1227
1228   void closeAssociatedWindows()
1229   {
1230     reorderAssociatedWindows(false, true);
1231   }
1232
1233   /*
1234    * (non-Javadoc)
1235    * 
1236    * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1237    * ActionEvent)
1238    */
1239   protected void garbageCollect_actionPerformed(ActionEvent e)
1240   {
1241     // We simply collect the garbage
1242     jalview.bin.Cache.log.debug("Collecting garbage...");
1243     System.gc();
1244     jalview.bin.Cache.log.debug("Finished garbage collection.");
1245   }
1246
1247   /*
1248    * (non-Javadoc)
1249    * 
1250    * @see
1251    * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1252    * )
1253    */
1254   protected void showMemusage_actionPerformed(ActionEvent e)
1255   {
1256     desktop.showMemoryUsage(showMemusage.isSelected());
1257   }
1258
1259   /*
1260    * (non-Javadoc)
1261    * 
1262    * @see
1263    * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1264    * )
1265    */
1266   protected void showConsole_actionPerformed(ActionEvent e)
1267   {
1268     showConsole(showConsole.isSelected());
1269   }
1270
1271   Console jconsole = null;
1272
1273   /**
1274    * control whether the java console is visible or not
1275    * 
1276    * @param selected
1277    */
1278   void showConsole(boolean selected)
1279   {
1280     showConsole.setSelected(selected);
1281     // TODO: decide if we should update properties file
1282     Cache.setProperty("SHOW_JAVA_CONSOLE", Boolean.valueOf(selected)
1283             .toString());
1284     jconsole.setVisible(selected);
1285   }
1286
1287   void reorderAssociatedWindows(boolean minimize, boolean close)
1288   {
1289     JInternalFrame[] frames = desktop.getAllFrames();
1290     if (frames == null || frames.length < 1)
1291     {
1292       return;
1293     }
1294
1295     AlignViewport source = null, target = null;
1296     if (frames[0] instanceof AlignFrame)
1297     {
1298       source = ((AlignFrame) frames[0]).getCurrentView();
1299     }
1300     else if (frames[0] instanceof TreePanel)
1301     {
1302       source = ((TreePanel) frames[0]).getViewPort();
1303     }
1304     else if (frames[0] instanceof PCAPanel)
1305     {
1306       source = ((PCAPanel) frames[0]).av;
1307     }
1308     else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1309     {
1310       source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1311     }
1312
1313     if (source != null)
1314     {
1315       for (int i = 0; i < frames.length; i++)
1316       {
1317         target = null;
1318         if (frames[i] == null)
1319         {
1320           continue;
1321         }
1322         if (frames[i] instanceof AlignFrame)
1323         {
1324           target = ((AlignFrame) frames[i]).getCurrentView();
1325         }
1326         else if (frames[i] instanceof TreePanel)
1327         {
1328           target = ((TreePanel) frames[i]).getViewPort();
1329         }
1330         else if (frames[i] instanceof PCAPanel)
1331         {
1332           target = ((PCAPanel) frames[i]).av;
1333         }
1334         else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1335         {
1336           target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1337         }
1338
1339         if (source == target)
1340         {
1341           try
1342           {
1343             if (close)
1344             {
1345               frames[i].setClosed(true);
1346             }
1347             else
1348             {
1349               frames[i].setIcon(minimize);
1350               if (!minimize)
1351               {
1352                 frames[i].toFront();
1353               }
1354             }
1355
1356           } catch (java.beans.PropertyVetoException ex)
1357           {
1358           }
1359         }
1360       }
1361     }
1362   }
1363
1364   /**
1365    * DOCUMENT ME!
1366    * 
1367    * @param e
1368    *          DOCUMENT ME!
1369    */
1370   protected void preferences_actionPerformed(ActionEvent e)
1371   {
1372     new Preferences();
1373   }
1374
1375   /**
1376    * DOCUMENT ME!
1377    * 
1378    * @param e
1379    *          DOCUMENT ME!
1380    */
1381   public void saveState_actionPerformed(ActionEvent e)
1382   {
1383     JalviewFileChooser chooser = new JalviewFileChooser(
1384             jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
1385             { "jvp" }, new String[]
1386             { "Jalview Project" }, "Jalview Project");
1387
1388     chooser.setFileView(new JalviewFileView());
1389     chooser.setDialogTitle("Save State");
1390
1391     int value = chooser.showSaveDialog(this);
1392
1393     if (value == JalviewFileChooser.APPROVE_OPTION)
1394     {
1395       final Desktop me = this;
1396       final java.io.File choice = chooser.getSelectedFile();
1397       new Thread(new Runnable()
1398       {
1399         public void run()
1400         {
1401
1402           setProgressBar("Saving jalview project " + choice.getName(),
1403                   choice.hashCode());
1404           jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1405                   choice.getParent());
1406           // TODO catch and handle errors for savestate
1407           // TODO prevent user from messing with the Desktop whilst we're saving
1408           try
1409           {
1410             new Jalview2XML().SaveState(choice);
1411           } catch (OutOfMemoryError oom)
1412           {
1413             new OOMWarning("Whilst saving current state to "
1414                     + choice.getName(), oom);
1415           } catch (Exception ex)
1416           {
1417             Cache.log.error(
1418                     "Problems whilst trying to save to " + choice.getName(),
1419                     ex);
1420             JOptionPane.showMessageDialog(
1421                     me,
1422                     MessageManager.formatMessage("label.error_whilst_saving_current_state_to", new String[]{ choice.getName()}),
1423                     MessageManager.getString("label.couldnt_save_project"),
1424                     JOptionPane.WARNING_MESSAGE);
1425           }
1426           setProgressBar(null, choice.hashCode());
1427         }
1428       }).start();
1429     }
1430   }
1431
1432   /**
1433    * DOCUMENT ME!
1434    * 
1435    * @param e
1436    *          DOCUMENT ME!
1437    */
1438   public void loadState_actionPerformed(ActionEvent e)
1439   {
1440     JalviewFileChooser chooser = new JalviewFileChooser(
1441             jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
1442             { "jvp", "jar" }, new String[]
1443             { "Jalview Project", "Jalview Project (old)" },
1444             "Jalview Project");
1445     chooser.setFileView(new JalviewFileView());
1446     chooser.setDialogTitle("Restore state");
1447
1448     int value = chooser.showOpenDialog(this);
1449
1450     if (value == JalviewFileChooser.APPROVE_OPTION)
1451     {
1452       final String choice = chooser.getSelectedFile().getAbsolutePath();
1453       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
1454               .getSelectedFile().getParent());
1455       new Thread(new Runnable()
1456       {
1457         public void run()
1458         {
1459           setProgressBar("loading jalview project " + choice,
1460                   choice.hashCode());
1461           try
1462           {
1463             new Jalview2XML().LoadJalviewAlign(choice);
1464           } catch (OutOfMemoryError oom)
1465           {
1466             new OOMWarning("Whilst loading project from " + choice, oom);
1467           } catch (Exception ex)
1468           {
1469             Cache.log.error("Problems whilst loading project from "
1470                     + choice, ex);
1471             JOptionPane.showMessageDialog(Desktop.desktop,
1472                         MessageManager.formatMessage("label.error_whilst_loading_project_from", new String[]{choice}),
1473                     MessageManager.getString("label.couldnt_load_project"), JOptionPane.WARNING_MESSAGE);
1474           }
1475           setProgressBar(null, choice.hashCode());
1476         }
1477       }).start();
1478     }
1479   }
1480
1481   public void inputSequence_actionPerformed(ActionEvent e)
1482   {
1483     new SequenceFetcher(this);
1484   }
1485
1486   JPanel progressPanel;
1487
1488   ArrayList<JPanel> fileLoadingPanels = new ArrayList<JPanel>();
1489
1490   public void startLoading(final String fileName)
1491   {
1492     if (fileLoadingCount == 0)
1493     {
1494       fileLoadingPanels.add(addProgressPanel("Loading File: " + fileName
1495               + "   "));
1496     }
1497     fileLoadingCount++;
1498   }
1499
1500   private JPanel addProgressPanel(String string)
1501   {
1502     if (progressPanel == null)
1503     {
1504       progressPanel = new JPanel(new GridLayout(1, 1));
1505       totalProgressCount = 0;
1506       instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1507     }
1508     JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1509     JProgressBar progressBar = new JProgressBar();
1510     progressBar.setIndeterminate(true);
1511
1512     thisprogress.add(new JLabel(string), BorderLayout.WEST);
1513
1514     thisprogress.add(progressBar, BorderLayout.CENTER);
1515     progressPanel.add(thisprogress);
1516     ((GridLayout) progressPanel.getLayout())
1517             .setRows(((GridLayout) progressPanel.getLayout()).getRows() + 1);
1518     ++totalProgressCount;
1519     instance.validate();
1520     return thisprogress;
1521   }
1522
1523   int totalProgressCount = 0;
1524
1525   private void removeProgressPanel(JPanel progbar)
1526   {
1527     if (progressPanel != null)
1528     {
1529       synchronized (progressPanel)
1530       {
1531         progressPanel.remove(progbar);
1532         GridLayout gl = (GridLayout) progressPanel.getLayout();
1533         gl.setRows(gl.getRows() - 1);
1534         if (--totalProgressCount < 1)
1535         {
1536           this.getContentPane().remove(progressPanel);
1537           progressPanel = null;
1538         }
1539       }
1540     }
1541     validate();
1542   }
1543
1544   public void stopLoading()
1545   {
1546     fileLoadingCount--;
1547     if (fileLoadingCount < 1)
1548     {
1549       while (fileLoadingPanels.size() > 0)
1550       {
1551         removeProgressPanel(fileLoadingPanels.remove(0));
1552       }
1553       fileLoadingPanels.clear();
1554       fileLoadingCount = 0;
1555     }
1556     validate();
1557   }
1558
1559   public static int getViewCount(String alignmentId)
1560   {
1561     AlignViewport[] aps = getViewports(alignmentId);
1562     return (aps == null) ? 0 : aps.length;
1563   }
1564
1565   /**
1566    * 
1567    * @param alignmentId
1568    * @return all AlignmentPanels concerning the alignmentId sequence set
1569    */
1570   public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1571   {
1572     int count = 0;
1573     if (Desktop.desktop == null)
1574     {
1575       // no frames created and in headless mode
1576       // TODO: verify that frames are recoverable when in headless mode
1577       return null;
1578     }
1579     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1580     ArrayList aps = new ArrayList();
1581     for (int t = 0; t < frames.length; t++)
1582     {
1583       if (frames[t] instanceof AlignFrame)
1584       {
1585         AlignFrame af = (AlignFrame) frames[t];
1586         for (int a = 0; a < af.alignPanels.size(); a++)
1587         {
1588           if (alignmentId.equals(((AlignmentPanel) af.alignPanels
1589                   .elementAt(a)).av.getSequenceSetId()))
1590           {
1591             aps.add(af.alignPanels.elementAt(a));
1592           }
1593         }
1594       }
1595     }
1596     if (aps.size() == 0)
1597     {
1598       return null;
1599     }
1600     AlignmentPanel[] vap = new AlignmentPanel[aps.size()];
1601     for (int t = 0; t < vap.length; t++)
1602     {
1603       vap[t] = (AlignmentPanel) aps.get(t);
1604     }
1605     return vap;
1606   }
1607
1608   /**
1609    * get all the viewports on an alignment.
1610    * 
1611    * @param sequenceSetId
1612    *          unique alignment id
1613    * @return all viewports on the alignment bound to sequenceSetId
1614    */
1615   public static AlignViewport[] getViewports(String sequenceSetId)
1616   {
1617     Vector viewp = new Vector();
1618     if (desktop != null)
1619     {
1620       javax.swing.JInternalFrame[] frames = instance.getAllFrames();
1621
1622       for (int t = 0; t < frames.length; t++)
1623       {
1624         if (frames[t] instanceof AlignFrame)
1625         {
1626           AlignFrame afr = ((AlignFrame) frames[t]);
1627           if (afr.getViewport().getSequenceSetId().equals(sequenceSetId))
1628           {
1629             if (afr.alignPanels != null)
1630             {
1631               for (int a = 0; a < afr.alignPanels.size(); a++)
1632               {
1633                 if (sequenceSetId.equals(((AlignmentPanel) afr.alignPanels
1634                         .elementAt(a)).av.getSequenceSetId()))
1635                 {
1636                   viewp.addElement(((AlignmentPanel) afr.alignPanels
1637                           .elementAt(a)).av);
1638                 }
1639               }
1640             }
1641             else
1642             {
1643               viewp.addElement(((AlignFrame) frames[t]).getViewport());
1644             }
1645           }
1646         }
1647       }
1648       if (viewp.size() > 0)
1649       {
1650         AlignViewport[] vp = new AlignViewport[viewp.size()];
1651         viewp.copyInto(vp);
1652         return vp;
1653       }
1654     }
1655     return null;
1656   }
1657
1658   public void explodeViews(AlignFrame af)
1659   {
1660     int size = af.alignPanels.size();
1661     if (size < 2)
1662     {
1663       return;
1664     }
1665
1666     for (int i = 0; i < size; i++)
1667     {
1668       AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(i);
1669       AlignFrame newaf = new AlignFrame(ap);
1670       if (ap.av.explodedPosition != null
1671               && !ap.av.explodedPosition.equals(af.getBounds()))
1672       {
1673         newaf.setBounds(ap.av.explodedPosition);
1674       }
1675
1676       ap.av.gatherViewsHere = false;
1677
1678       addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1679               AlignFrame.DEFAULT_HEIGHT);
1680     }
1681
1682     af.alignPanels.clear();
1683     af.closeMenuItem_actionPerformed(true);
1684
1685   }
1686
1687   public void gatherViews(AlignFrame source)
1688   {
1689     source.viewport.gatherViewsHere = true;
1690     source.viewport.explodedPosition = source.getBounds();
1691     JInternalFrame[] frames = desktop.getAllFrames();
1692     String viewId = source.viewport.getSequenceSetId();
1693
1694     for (int t = 0; t < frames.length; t++)
1695     {
1696       if (frames[t] instanceof AlignFrame && frames[t] != source)
1697       {
1698         AlignFrame af = (AlignFrame) frames[t];
1699         boolean gatherThis = false;
1700         for (int a = 0; a < af.alignPanels.size(); a++)
1701         {
1702           AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(a);
1703           if (viewId.equals(ap.av.getSequenceSetId()))
1704           {
1705             gatherThis = true;
1706             ap.av.gatherViewsHere = false;
1707             ap.av.explodedPosition = af.getBounds();
1708             source.addAlignmentPanel(ap, false);
1709           }
1710         }
1711
1712         if (gatherThis)
1713         {
1714           af.alignPanels.clear();
1715           af.closeMenuItem_actionPerformed(true);
1716         }
1717       }
1718     }
1719
1720   }
1721
1722   jalview.gui.VamsasApplication v_client = null;
1723
1724   public void vamsasImport_actionPerformed(ActionEvent e)
1725   {
1726     if (v_client == null)
1727     {
1728       // Load and try to start a session.
1729       JalviewFileChooser chooser = new JalviewFileChooser(
1730               jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
1731
1732       chooser.setFileView(new JalviewFileView());
1733       chooser.setDialogTitle("Open a saved VAMSAS session");
1734       chooser.setToolTipText(MessageManager
1735               .getString("label.select_vamsas_session_opened_as_new_vamsas_session"));
1736
1737       int value = chooser.showOpenDialog(this);
1738
1739       if (value == JalviewFileChooser.APPROVE_OPTION)
1740       {
1741         String fle = chooser.getSelectedFile().toString();
1742         if (!vamsasImport(chooser.getSelectedFile()))
1743         {
1744           JOptionPane
1745                   .showInternalMessageDialog(
1746                           Desktop.desktop,
1747                           MessageManager.formatMessage(
1748                                   "label.couldnt_import_as_vamsas_session",
1749                                   new String[]
1750                                   { fle }),
1751                           MessageManager
1752                                   .getString("label.vamsas_document_import_failed"),
1753                           JOptionPane.ERROR_MESSAGE);
1754         }
1755       }
1756     }
1757     else
1758     {
1759       jalview.bin.Cache.log
1760               .error("Implementation error - load session from a running session is not supported.");
1761     }
1762   }
1763
1764   /**
1765    * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
1766    * 
1767    * @param file
1768    * @return true if import was a success and a session was started.
1769    */
1770   public boolean vamsasImport(URL url)
1771   {
1772     // TODO: create progress bar
1773     if (v_client != null)
1774     {
1775
1776       jalview.bin.Cache.log
1777               .error("Implementation error - load session from a running session is not supported.");
1778       return false;
1779     }
1780
1781     try
1782     {
1783       // copy the URL content to a temporary local file
1784       // TODO: be a bit cleverer here with nio (?!)
1785       File file = File.createTempFile("vdocfromurl", ".vdj");
1786       FileOutputStream fos = new FileOutputStream(file);
1787       BufferedInputStream bis = new BufferedInputStream(url.openStream());
1788       byte[] buffer = new byte[2048];
1789       int ln;
1790       while ((ln = bis.read(buffer)) > -1)
1791       {
1792         fos.write(buffer, 0, ln);
1793       }
1794       bis.close();
1795       fos.close();
1796       v_client = new jalview.gui.VamsasApplication(this, file,
1797               url.toExternalForm());
1798     } catch (Exception ex)
1799     {
1800       jalview.bin.Cache.log.error(
1801               "Failed to create new vamsas session from contents of URL "
1802                       + url, ex);
1803       return false;
1804     }
1805     setupVamsasConnectedGui();
1806     v_client.initial_update(); // TODO: thread ?
1807     return v_client.inSession();
1808   }
1809
1810   /**
1811    * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
1812    * 
1813    * @param file
1814    * @return true if import was a success and a session was started.
1815    */
1816   public boolean vamsasImport(File file)
1817   {
1818     if (v_client != null)
1819     {
1820
1821       jalview.bin.Cache.log
1822               .error("Implementation error - load session from a running session is not supported.");
1823       return false;
1824     }
1825
1826     setProgressBar("Importing VAMSAS session from " + file.getName(),
1827             file.hashCode());
1828     try
1829     {
1830       v_client = new jalview.gui.VamsasApplication(this, file, null);
1831     } catch (Exception ex)
1832     {
1833       setProgressBar("Importing VAMSAS session from " + file.getName(),
1834               file.hashCode());
1835       jalview.bin.Cache.log.error(
1836               "New vamsas session from existing session file failed:", ex);
1837       return false;
1838     }
1839     setupVamsasConnectedGui();
1840     v_client.initial_update(); // TODO: thread ?
1841     setProgressBar("Importing VAMSAS session from " + file.getName(),
1842             file.hashCode());
1843     return v_client.inSession();
1844   }
1845
1846   public boolean joinVamsasSession(String mysesid)
1847   {
1848     if (v_client != null)
1849     {
1850       throw new Error(
1851               "Trying to join a vamsas session when another is already connected.");
1852     }
1853     if (mysesid == null)
1854     {
1855       throw new Error("Invalid vamsas session id.");
1856     }
1857     v_client = new VamsasApplication(this, mysesid);
1858     setupVamsasConnectedGui();
1859     v_client.initial_update();
1860     return (v_client.inSession());
1861   }
1862
1863   public void vamsasStart_actionPerformed(ActionEvent e)
1864   {
1865     if (v_client == null)
1866     {
1867       // Start a session.
1868       // we just start a default session for moment.
1869       /*
1870        * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
1871        * getProperty("LAST_DIRECTORY"));
1872        * 
1873        * chooser.setFileView(new JalviewFileView());
1874        * chooser.setDialogTitle("Load Vamsas file");
1875        * chooser.setToolTipText("Import");
1876        * 
1877        * int value = chooser.showOpenDialog(this);
1878        * 
1879        * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
1880        * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
1881        */
1882       v_client = new VamsasApplication(this);
1883       setupVamsasConnectedGui();
1884       v_client.initial_update(); // TODO: thread ?
1885     }
1886     else
1887     {
1888       // store current data in session.
1889       v_client.push_update(); // TODO: thread
1890     }
1891   }
1892
1893   protected void setupVamsasConnectedGui()
1894   {
1895     vamsasStart.setText(MessageManager.getString("label.session_update"));
1896     vamsasSave.setVisible(true);
1897     vamsasStop.setVisible(true);
1898     vamsasImport.setVisible(false); // Document import to existing session is
1899     // not possible for vamsas-client-1.0.
1900   }
1901
1902   protected void setupVamsasDisconnectedGui()
1903   {
1904     vamsasSave.setVisible(false);
1905     vamsasStop.setVisible(false);
1906     vamsasImport.setVisible(true);
1907     vamsasStart.setText(MessageManager
1908             .getString("label.new_vamsas_session"));
1909   }
1910
1911   public void vamsasStop_actionPerformed(ActionEvent e)
1912   {
1913     if (v_client != null)
1914     {
1915       v_client.end_session();
1916       v_client = null;
1917       setupVamsasDisconnectedGui();
1918     }
1919   }
1920
1921   protected void buildVamsasStMenu()
1922   {
1923     if (v_client == null)
1924     {
1925       String[] sess = null;
1926       try
1927       {
1928         sess = VamsasApplication.getSessionList();
1929       } catch (Exception e)
1930       {
1931         jalview.bin.Cache.log.warn(
1932                 "Problem getting current sessions list.", e);
1933         sess = null;
1934       }
1935       if (sess != null)
1936       {
1937         jalview.bin.Cache.log.debug("Got current sessions list: "
1938                 + sess.length + " entries.");
1939         VamsasStMenu.removeAll();
1940         for (int i = 0; i < sess.length; i++)
1941         {
1942           JMenuItem sessit = new JMenuItem();
1943           sessit.setText(sess[i]);
1944           sessit.setToolTipText(MessageManager.formatMessage(
1945                   "label.connect_to_session", new String[]
1946                   { sess[i] }));
1947           final Desktop dsktp = this;
1948           final String mysesid = sess[i];
1949           sessit.addActionListener(new ActionListener()
1950           {
1951
1952             public void actionPerformed(ActionEvent e)
1953             {
1954               if (dsktp.v_client == null)
1955               {
1956                 Thread rthr = new Thread(new Runnable()
1957                 {
1958
1959                   public void run()
1960                   {
1961                     dsktp.v_client = new VamsasApplication(dsktp, mysesid);
1962                     dsktp.setupVamsasConnectedGui();
1963                     dsktp.v_client.initial_update();
1964                   }
1965
1966                 });
1967                 rthr.start();
1968               }
1969             };
1970           });
1971           VamsasStMenu.add(sessit);
1972         }
1973         // don't show an empty menu.
1974         VamsasStMenu.setVisible(sess.length > 0);
1975
1976       }
1977       else
1978       {
1979         jalview.bin.Cache.log.debug("No current vamsas sessions.");
1980         VamsasStMenu.removeAll();
1981         VamsasStMenu.setVisible(false);
1982       }
1983     }
1984     else
1985     {
1986       // Not interested in the content. Just hide ourselves.
1987       VamsasStMenu.setVisible(false);
1988     }
1989   }
1990
1991   public void vamsasSave_actionPerformed(ActionEvent e)
1992   {
1993     if (v_client != null)
1994     {
1995       JalviewFileChooser chooser = new JalviewFileChooser(
1996               jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
1997               { "vdj" }, // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
1998               new String[]
1999               { "Vamsas Document" }, "Vamsas Document");
2000
2001       chooser.setFileView(new JalviewFileView());
2002       chooser.setDialogTitle("Save Vamsas Document Archive");
2003
2004       int value = chooser.showSaveDialog(this);
2005
2006       if (value == JalviewFileChooser.APPROVE_OPTION)
2007       {
2008         java.io.File choice = chooser.getSelectedFile();
2009         JPanel progpanel = addProgressPanel("Saving VAMSAS Document to "
2010                 + choice.getName());
2011         jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
2012         String warnmsg = null;
2013         String warnttl = null;
2014         try
2015         {
2016           v_client.vclient.storeDocument(choice);
2017         } catch (Error ex)
2018         {
2019           warnttl = "Serious Problem saving Vamsas Document";
2020           warnmsg = ex.toString();
2021           jalview.bin.Cache.log.error("Error Whilst saving document to "
2022                   + choice, ex);
2023
2024         } catch (Exception ex)
2025         {
2026           warnttl = "Problem saving Vamsas Document.";
2027           warnmsg = ex.toString();
2028           jalview.bin.Cache.log.warn("Exception Whilst saving document to "
2029                   + choice, ex);
2030
2031         }
2032         removeProgressPanel(progpanel);
2033         if (warnmsg != null)
2034         {
2035           JOptionPane.showInternalMessageDialog(Desktop.desktop,
2036
2037           warnmsg, warnttl, JOptionPane.ERROR_MESSAGE);
2038         }
2039       }
2040     }
2041   }
2042
2043   JPanel vamUpdate = null;
2044
2045   /**
2046    * hide vamsas user gui bits when a vamsas document event is being handled.
2047    * 
2048    * @param b
2049    *          true to hide gui, false to reveal gui
2050    */
2051   public void setVamsasUpdate(boolean b)
2052   {
2053     jalview.bin.Cache.log.debug("Setting gui for Vamsas update "
2054             + (b ? "in progress" : "finished"));
2055
2056     if (vamUpdate != null)
2057     {
2058       this.removeProgressPanel(vamUpdate);
2059     }
2060     if (b)
2061     {
2062       vamUpdate = this.addProgressPanel("Updating vamsas session");
2063     }
2064     vamsasStart.setVisible(!b);
2065     vamsasStop.setVisible(!b);
2066     vamsasSave.setVisible(!b);
2067   }
2068
2069   public JInternalFrame[] getAllFrames()
2070   {
2071     return desktop.getAllFrames();
2072   }
2073
2074   /**
2075    * Checks the given url to see if it gives a response indicating that the user
2076    * should be informed of a new questionnaire.
2077    * 
2078    * @param url
2079    */
2080   public void checkForQuestionnaire(String url)
2081   {
2082     UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2083     // javax.swing.SwingUtilities.invokeLater(jvq);
2084     new Thread(jvq).start();
2085   }
2086
2087   /**
2088    * Proxy class for JDesktopPane which optionally displays the current memory
2089    * usage and highlights the desktop area with a red bar if free memory runs
2090    * low.
2091    * 
2092    * @author AMW
2093    */
2094   public class MyDesktopPane extends JDesktopPane implements Runnable
2095   {
2096
2097     boolean showMemoryUsage = false;
2098
2099     Runtime runtime;
2100
2101     java.text.NumberFormat df;
2102
2103     float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2104             percentUsage;
2105
2106     public MyDesktopPane(boolean showMemoryUsage)
2107     {
2108       showMemoryUsage(showMemoryUsage);
2109     }
2110
2111     public void showMemoryUsage(boolean showMemoryUsage)
2112     {
2113       this.showMemoryUsage = showMemoryUsage;
2114       if (showMemoryUsage)
2115       {
2116         Thread worker = new Thread(this);
2117         worker.start();
2118       }
2119     }
2120
2121     public boolean isShowMemoryUsage()
2122     {
2123       return showMemoryUsage;
2124     }
2125
2126     public void run()
2127     {
2128       df = java.text.NumberFormat.getNumberInstance();
2129       df.setMaximumFractionDigits(2);
2130       runtime = Runtime.getRuntime();
2131
2132       while (showMemoryUsage)
2133       {
2134         try
2135         {
2136           maxMemory = runtime.maxMemory() / 1048576f;
2137           allocatedMemory = runtime.totalMemory() / 1048576f;
2138           freeMemory = runtime.freeMemory() / 1048576f;
2139           totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2140
2141           percentUsage = (totalFreeMemory / maxMemory) * 100;
2142
2143           // if (percentUsage < 20)
2144           {
2145             // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2146             // Color.red);
2147             // instance.set.setBorder(border1);
2148           }
2149           repaint();
2150           // sleep after showing usage
2151           Thread.sleep(3000);
2152         } catch (Exception ex)
2153         {
2154           ex.printStackTrace();
2155         }
2156       }
2157     }
2158
2159     public void paintComponent(Graphics g)
2160     {
2161       if (showMemoryUsage && g != null && df != null)
2162       {
2163         if (percentUsage < 20)
2164           g.setColor(Color.red);
2165         FontMetrics fm = g.getFontMetrics();
2166         if (fm != null)
2167         {
2168           g.drawString(MessageManager.formatMessage(
2169                   "label.memory_stats",
2170                   new String[]
2171                   { df.format(totalFreeMemory), df.format(maxMemory),
2172                       df.format(percentUsage) }), 10,
2173                   getHeight() - fm.getHeight());
2174         }
2175       }
2176     }
2177   }
2178
2179   /**
2180    * fixes stacking order after a modal dialog to ensure windows that should be
2181    * on top actually are
2182    */
2183   public void relayerWindows()
2184   {
2185
2186   }
2187
2188   protected JMenuItem groovyShell;
2189
2190   public void doGroovyCheck()
2191   {
2192     if (jalview.bin.Cache.groovyJarsPresent())
2193     {
2194       groovyShell = new JMenuItem();
2195       groovyShell.setText(MessageManager.getString("label.groovy_console"));
2196       groovyShell.addActionListener(new ActionListener()
2197       {
2198         public void actionPerformed(ActionEvent e)
2199         {
2200           groovyShell_actionPerformed(e);
2201         }
2202       });
2203       toolsMenu.add(groovyShell);
2204       groovyShell.setVisible(true);
2205     }
2206   }
2207
2208   /**
2209    * Accessor method to quickly get all the AlignmentFrames loaded.
2210    */
2211   public static AlignFrame[] getAlignframes()
2212   {
2213     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2214
2215     if (frames == null)
2216     {
2217       return null;
2218     }
2219     Vector avp = new Vector();
2220     try
2221     {
2222       // REVERSE ORDER
2223       for (int i = frames.length - 1; i > -1; i--)
2224       {
2225         if (frames[i] instanceof AlignFrame)
2226         {
2227           AlignFrame af = (AlignFrame) frames[i];
2228           avp.addElement(af);
2229         }
2230       }
2231     } catch (Exception ex)
2232     {
2233       ex.printStackTrace();
2234     }
2235     if (avp.size() == 0)
2236     {
2237       return null;
2238     }
2239     AlignFrame afs[] = new AlignFrame[avp.size()];
2240     for (int i = 0, j = avp.size(); i < j; i++)
2241     {
2242       afs[i] = (AlignFrame) avp.elementAt(i);
2243     }
2244     avp.clear();
2245     return afs;
2246   }
2247
2248   public AppJmol[] getJmols()
2249   {
2250     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2251
2252     if (frames == null)
2253     {
2254       return null;
2255     }
2256     Vector avp = new Vector();
2257     try
2258     {
2259       // REVERSE ORDER
2260       for (int i = frames.length - 1; i > -1; i--)
2261       {
2262         if (frames[i] instanceof AppJmol)
2263         {
2264           AppJmol af = (AppJmol) frames[i];
2265           avp.addElement(af);
2266         }
2267       }
2268     } catch (Exception ex)
2269     {
2270       ex.printStackTrace();
2271     }
2272     if (avp.size() == 0)
2273     {
2274       return null;
2275     }
2276     AppJmol afs[] = new AppJmol[avp.size()];
2277     for (int i = 0, j = avp.size(); i < j; i++)
2278     {
2279       afs[i] = (AppJmol) avp.elementAt(i);
2280     }
2281     avp.clear();
2282     return afs;
2283   }
2284
2285   /**
2286    * Add Groovy Support to Jalview
2287    */
2288   public void groovyShell_actionPerformed(ActionEvent e)
2289   {
2290     // use reflection to avoid creating compilation dependency.
2291     if (!jalview.bin.Cache.groovyJarsPresent())
2292     {
2293       throw new Error(
2294               "Implementation Error. Cannot create groovyShell without Groovy on the classpath!");
2295     }
2296     try
2297     {
2298       Class gcClass = Desktop.class.getClassLoader().loadClass(
2299               "groovy.ui.Console");
2300       Constructor gccons = gcClass.getConstructor(null);
2301       java.lang.reflect.Method setvar = gcClass.getMethod("setVariable",
2302               new Class[]
2303               { String.class, Object.class });
2304       java.lang.reflect.Method run = gcClass.getMethod("run", null);
2305       Object gc = gccons.newInstance(null);
2306       setvar.invoke(gc, new Object[]
2307       { "Jalview", this });
2308       run.invoke(gc, null);
2309     } catch (Exception ex)
2310     {
2311       jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2312       JOptionPane
2313               .showInternalMessageDialog(
2314                       Desktop.desktop,
2315
2316                       "Couldn't create the groovy Shell. Check the error log for the details of what went wrong.",
2317                       "Jalview Groovy Support Failed",
2318                       JOptionPane.ERROR_MESSAGE);
2319     }
2320   }
2321
2322   /**
2323    * Progress bars managed by the IProgressIndicator method.
2324    */
2325   private Hashtable<Long, JPanel> progressBars;
2326
2327   private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2328
2329   /*
2330    * (non-Javadoc)
2331    * 
2332    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2333    */
2334   public void setProgressBar(String message, long id)
2335   {
2336     if (progressBars == null)
2337     {
2338       progressBars = new Hashtable<Long, JPanel>();
2339       progressBarHandlers = new Hashtable<Long, IProgressIndicatorHandler>();
2340     }
2341
2342     if (progressBars.get(new Long(id)) != null)
2343     {
2344       JPanel progressPanel = progressBars.remove(new Long(id));
2345       if (progressBarHandlers.contains(new Long(id)))
2346       {
2347         progressBarHandlers.remove(new Long(id));
2348       }
2349       removeProgressPanel(progressPanel);
2350     }
2351     else
2352     {
2353       progressBars.put(new Long(id), addProgressPanel(message));
2354     }
2355   }
2356
2357   /*
2358    * (non-Javadoc)
2359    * 
2360    * @see jalview.gui.IProgressIndicator#registerHandler(long,
2361    * jalview.gui.IProgressIndicatorHandler)
2362    */
2363   public void registerHandler(final long id,
2364           final IProgressIndicatorHandler handler)
2365   {
2366     if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
2367     {
2368       throw new Error(
2369               "call setProgressBar before registering the progress bar's handler.");
2370     }
2371     progressBarHandlers.put(new Long(id), handler);
2372     final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
2373     if (handler.canCancel())
2374     {
2375       JButton cancel = new JButton(
2376               MessageManager.getString("action.cancel"));
2377       final IProgressIndicator us = this;
2378       cancel.addActionListener(new ActionListener()
2379       {
2380
2381         public void actionPerformed(ActionEvent e)
2382         {
2383           handler.cancelActivity(id);
2384           us.setProgressBar(
2385                   "Cancelled "
2386                           + ((JLabel) progressPanel.getComponent(0))
2387                                   .getText(), id);
2388         }
2389       });
2390       progressPanel.add(cancel, BorderLayout.EAST);
2391     }
2392   }
2393
2394   /**
2395    * 
2396    * @return true if any progress bars are still active
2397    */
2398   @Override
2399   public boolean operationInProgress()
2400   {
2401     if (progressBars != null && progressBars.size() > 0)
2402     {
2403       return true;
2404     }
2405     return false;
2406   }
2407
2408   /**
2409    * This will return the first AlignFrame viewing AlignViewport av. It will
2410    * break if there are more than one AlignFrames viewing a particular av. This
2411    * 
2412    * @param av
2413    * @return alignFrame for av
2414    */
2415   public static AlignFrame getAlignFrameFor(AlignViewport av)
2416   {
2417     if (desktop != null)
2418     {
2419       AlignmentPanel[] aps = getAlignmentPanels(av.getSequenceSetId());
2420       for (int panel = 0; aps != null && panel < aps.length; panel++)
2421       {
2422         if (aps[panel] != null && aps[panel].av == av)
2423         {
2424           return aps[panel].alignFrame;
2425         }
2426       }
2427     }
2428     return null;
2429   }
2430
2431   public VamsasApplication getVamsasApplication()
2432   {
2433     return v_client;
2434
2435   }
2436
2437   /**
2438    * flag set if jalview GUI is being operated programmatically
2439    */
2440   private boolean inBatchMode = false;
2441
2442   /**
2443    * check if jalview GUI is being operated programmatically
2444    * 
2445    * @return inBatchMode
2446    */
2447   public boolean isInBatchMode()
2448   {
2449     return inBatchMode;
2450   }
2451
2452   /**
2453    * set flag if jalview GUI is being operated programmatically
2454    * 
2455    * @param inBatchMode
2456    */
2457   public void setInBatchMode(boolean inBatchMode)
2458   {
2459     this.inBatchMode = inBatchMode;
2460   }
2461
2462   public void startServiceDiscovery()
2463   {
2464     startServiceDiscovery(false);
2465   }
2466
2467   public void startServiceDiscovery(boolean blocking)
2468   {
2469     boolean alive = true;
2470     Thread t0 = null, t1 = null, t2 = null;
2471     // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2472     if (true)
2473     {
2474       // todo: changesupport handlers need to be transferred
2475       if (discoverer == null)
2476       {
2477         discoverer = new jalview.ws.jws1.Discoverer();
2478         // register PCS handler for desktop.
2479         discoverer.addPropertyChangeListener(changeSupport);
2480       }
2481       // JAL-940 - disabled JWS1 service configuration - always start discoverer
2482       // until we phase out completely
2483       (t0 = new Thread(discoverer)).start();
2484     }
2485
2486     // ENFIN services are EOLed as of Jalview 2.8.1 release
2487     if (false)
2488     {
2489       try
2490       {
2491         if (Cache.getDefault("SHOW_ENFIN_SERVICES", true))
2492         {
2493           // EnfinEnvision web service menu entries are rebuild every time the
2494           // menu is shown, so no changeSupport events are needed.
2495           jalview.ws.EnfinEnvision2OneWay.getInstance();
2496           (t1 = new Thread(jalview.ws.EnfinEnvision2OneWay.getInstance()))
2497                   .start();
2498         }
2499       } catch (Exception e)
2500       {
2501         Cache.log
2502                 .info("Exception when trying to launch Envision2 workflow discovery.",
2503                         e);
2504         Cache.log.info(e.getStackTrace());
2505       }
2506     }
2507
2508     if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2509     {
2510       if (jalview.ws.jws2.Jws2Discoverer.getDiscoverer().isRunning())
2511       {
2512         jalview.ws.jws2.Jws2Discoverer.getDiscoverer().setAborted(true);
2513       }
2514       t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer().startDiscoverer(
2515               changeSupport);
2516
2517     }
2518     Thread t3 = null;
2519     {
2520       // TODO: do rest service discovery
2521     }
2522     if (blocking)
2523     {
2524       while (alive)
2525       {
2526         try
2527         {
2528           Thread.sleep(15);
2529         } catch (Exception e)
2530         {
2531         }
2532         alive = (t1 != null && t1.isAlive())
2533                 || (t2 != null && t2.isAlive())
2534                 || (t3 != null && t3.isAlive())
2535                 || (t0 != null && t0.isAlive());
2536       }
2537     }
2538   }
2539
2540   /**
2541    * called to check if the service discovery process completed successfully.
2542    * 
2543    * @param evt
2544    */
2545   protected void JalviewServicesChanged(PropertyChangeEvent evt)
2546   {
2547     if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2548     {
2549       final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2550               .getErrorMessages();
2551       if (ermsg != null)
2552       {
2553         if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2554         {
2555           if (serviceChangedDialog == null)
2556           {
2557             // only run if we aren't already displaying one of these.
2558             addDialogThread(serviceChangedDialog = new Runnable()
2559             {
2560               public void run()
2561               {
2562
2563                 /*
2564                  * JalviewDialog jd =new JalviewDialog() {
2565                  * 
2566                  * @Override protected void cancelPressed() { // TODO
2567                  * Auto-generated method stub
2568                  * 
2569                  * }@Override protected void okPressed() { // TODO
2570                  * Auto-generated method stub
2571                  * 
2572                  * }@Override protected void raiseClosed() { // TODO
2573                  * Auto-generated method stub
2574                  * 
2575                  * } }; jd.initDialogFrame(new
2576                  * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2577                  * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2578                  * + " or mis-configured HTTP proxy settings.<br/>" +
2579                  * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2580                  * +
2581                  * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2582                  * ), true, true, "Web Service Configuration Problem", 450,
2583                  * 400);
2584                  * 
2585                  * jd.waitForInput();
2586                  */
2587                 JOptionPane
2588                         .showConfirmDialog(
2589                                 Desktop.desktop,
2590                                 new JLabel(
2591                                         "<html><table width=\"450\"><tr><td>"
2592                                                 + ermsg
2593                                                 + "</td></tr></table>"
2594                                                 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2595                                                 + " or mis-configured HTTP proxy settings.</p>"
2596                                                 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2597                                                 + " Tools->Preferences dialog box to change them.</p></html>"),
2598                                 "Web Service Configuration Problem",
2599                                 JOptionPane.DEFAULT_OPTION,
2600                                 JOptionPane.ERROR_MESSAGE);
2601                 serviceChangedDialog = null;
2602
2603               }
2604             });
2605           }
2606         }
2607         else
2608         {
2609           Cache.log
2610                   .error("Errors reported by JABA discovery service. Check web services preferences.\n"
2611                           + ermsg);
2612         }
2613       }
2614     }
2615   }
2616
2617   private Runnable serviceChangedDialog = null;
2618
2619   /**
2620    * start a thread to open a URL in the configured browser. Pops up a warning
2621    * dialog to the user if there is an exception when calling out to the browser
2622    * to open the URL.
2623    * 
2624    * @param url
2625    */
2626   public static void showUrl(final String url)
2627   {
2628     showUrl(url, Desktop.instance);
2629   }
2630
2631   /**
2632    * Like showUrl but allows progress handler to be specified
2633    * 
2634    * @param url
2635    * @param progress
2636    *          (null) or object implementing IProgressIndicator
2637    */
2638   public static void showUrl(final String url,
2639           final IProgressIndicator progress)
2640   {
2641     new Thread(new Runnable()
2642     {
2643       public void run()
2644       {
2645         try
2646         {
2647           if (progress != null)
2648           {
2649             progress.setProgressBar("Opening " + url, this.hashCode());
2650           }
2651           jalview.util.BrowserLauncher.openURL(url);
2652         } catch (Exception ex)
2653         {
2654           JOptionPane
2655                   .showInternalMessageDialog(
2656                           Desktop.desktop,
2657                           "Unixers: Couldn't find default web browser."
2658                                   + "\nAdd the full path to your browser in Preferences.",
2659                           "Web browser not found",
2660                           JOptionPane.WARNING_MESSAGE);
2661
2662           ex.printStackTrace();
2663         }
2664         if (progress != null)
2665         {
2666           progress.setProgressBar(null, this.hashCode());
2667         }
2668       }
2669     }).start();
2670   }
2671
2672   public static WsParamSetManager wsparamManager = null;
2673
2674   public static ParamManager getUserParameterStore()
2675   {
2676     if (wsparamManager == null)
2677     {
2678       wsparamManager = new WsParamSetManager();
2679     }
2680     return wsparamManager;
2681   }
2682
2683   /**
2684    * static hyperlink handler proxy method for use by Jalview's internal windows
2685    * 
2686    * @param e
2687    */
2688   public static void hyperlinkUpdate(HyperlinkEvent e)
2689   {
2690     if (e.getEventType() == EventType.ACTIVATED)
2691     {
2692       String url = null;
2693       try
2694       {
2695         url = e.getURL().toString();
2696         Desktop.showUrl(url);
2697       } catch (Exception x)
2698       {
2699         if (url != null)
2700         {
2701           if (Cache.log != null)
2702           {
2703             Cache.log.error("Couldn't handle string " + url + " as a URL.");
2704           }
2705           else
2706           {
2707             System.err.println("Couldn't handle string " + url
2708                     + " as a URL.");
2709           }
2710         }
2711         // ignore any exceptions due to dud links.
2712       }
2713
2714     }
2715   }
2716
2717   /**
2718    * single thread that handles display of dialogs to user.
2719    */
2720   ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2721
2722   /**
2723    * flag indicating if dialogExecutor should try to acquire a permit
2724    */
2725   private volatile boolean dialogPause = true;
2726
2727   /**
2728    * pause the queue
2729    */
2730   private java.util.concurrent.Semaphore block = new Semaphore(0);
2731
2732   /**
2733    * add another dialog thread to the queue
2734    * 
2735    * @param prompter
2736    */
2737   public void addDialogThread(final Runnable prompter)
2738   {
2739     dialogExecutor.submit(new Runnable()
2740     {
2741       public void run()
2742       {
2743         if (dialogPause)
2744         {
2745           try
2746           {
2747             block.acquire();
2748           } catch (InterruptedException x)
2749           {
2750           }
2751           ;
2752         }
2753         if (instance == null)
2754         {
2755           return;
2756         }
2757         try
2758         {
2759           SwingUtilities.invokeAndWait(prompter);
2760         } catch (Exception q)
2761         {
2762           Cache.log.warn("Unexpected Exception in dialog thread.", q);
2763         }
2764       }
2765     });
2766   }
2767
2768   public void startDialogQueue()
2769   {
2770     // set the flag so we don't pause waiting for another permit and semaphore
2771     // the current task to begin
2772     dialogPause = false;
2773     block.release();
2774   }
2775   @Override
2776   protected void snapShotWindow_actionPerformed(ActionEvent e)
2777   {
2778     invalidate();
2779     File of;
2780     ImageMaker im = new jalview.util.ImageMaker(this, ImageMaker.EPS, "View of Desktop", getWidth(), getHeight(), of=new File("Jalview_snapshot"+System.currentTimeMillis()+".eps"), "View of desktop");
2781     try {
2782       paintAll(im.getGraphics());
2783       im.writeImage();
2784     } catch (Exception q)
2785     {
2786       Cache.log.error("Couldn't write snapshot to "+of.getAbsolutePath(),q);
2787       return;
2788     }
2789     Cache.log.info("Successfully written snapshot to file "+of.getAbsolutePath());
2790   }
2791 }