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