Merge branch 'JAL-1445' into develop
[jalview.git] / src / jalview / gui / Desktop.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
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     Transferable t = evt.getTransferable();
811     java.util.List files = null;
812     java.util.List protocols = null;
813
814     try
815     {
816       DataFlavor uriListFlavor = new DataFlavor(
817               "text/uri-list;class=java.lang.String");
818       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
819       {
820         // Works on Windows and MacOSX
821         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
822         files = (java.util.List) t
823                 .getTransferData(DataFlavor.javaFileListFlavor);
824       }
825       else if (t.isDataFlavorSupported(uriListFlavor))
826       {
827         // This is used by Unix drag system
828         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
829         String data = (String) t.getTransferData(uriListFlavor);
830         files = new java.util.ArrayList(1);
831         protocols = new java.util.ArrayList(1);
832         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
833                 data, "\r\n"); st.hasMoreTokens();)
834         {
835           String s = st.nextToken();
836           if (s.startsWith("#"))
837           {
838             // the line is a comment (as per the RFC 2483)
839             continue;
840           }
841           java.net.URI uri = new java.net.URI(s);
842           if (uri.getScheme().toLowerCase().startsWith("http"))
843           {
844             protocols.add(FormatAdapter.URL);
845             files.add(uri.toString());
846           }
847           else
848           {
849             // otherwise preserve old behaviour: catch all for file objects
850             java.io.File file = new java.io.File(uri);
851             protocols.add(FormatAdapter.FILE);
852             files.add(file.toString());
853           }
854         }
855       }
856     } catch (Exception e)
857     {
858     }
859
860     if (files != null)
861     {
862       try
863       {
864         for (int i = 0; i < files.size(); i++)
865         {
866           String file = files.get(i).toString();
867           String protocol = (protocols == null) ? FormatAdapter.FILE
868                   : (String) protocols.get(i);
869           String format = null;
870
871           if (file.endsWith(".jar"))
872           {
873             format = "Jalview";
874
875           }
876           else
877           {
878             format = new IdentifyFile().Identify(file, protocol);
879           }
880
881           new FileLoader().LoadFile(file, protocol, format);
882
883         }
884       } catch (Exception ex)
885       {
886       }
887     }
888   }
889
890   /**
891    * DOCUMENT ME!
892    * 
893    * @param e
894    *          DOCUMENT ME!
895    */
896   public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
897   {
898     JalviewFileChooser chooser = new JalviewFileChooser(
899             jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
900             jalview.io.AppletFormatAdapter.READABLE_EXTENSIONS,
901             jalview.io.AppletFormatAdapter.READABLE_FNAMES,
902             jalview.bin.Cache.getProperty("DEFAULT_FILE_FORMAT"));
903
904     chooser.setFileView(new JalviewFileView());
905     chooser.setDialogTitle(MessageManager.getString("label.open_local_file"));
906     chooser.setToolTipText(MessageManager.getString("action.open"));
907
908     int value = chooser.showOpenDialog(this);
909
910     if (value == JalviewFileChooser.APPROVE_OPTION)
911     {
912       String choice = chooser.getSelectedFile().getPath();
913       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
914               .getSelectedFile().getParent());
915
916       String format = null;
917       if (chooser.getSelectedFormat()!=null && chooser.getSelectedFormat().equals("Jalview"))
918       {
919         format = "Jalview";
920       }
921       else
922       {
923         format = new IdentifyFile().Identify(choice, FormatAdapter.FILE);
924       }
925
926       if (viewport != null)
927       {
928         new FileLoader().LoadFile(viewport, choice, FormatAdapter.FILE,
929                 format);
930       }
931       else
932       {
933         new FileLoader().LoadFile(choice, FormatAdapter.FILE, format);
934       }
935     }
936   }
937
938   /**
939    * DOCUMENT ME!
940    * 
941    * @param e
942    *          DOCUMENT ME!
943    */
944   public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
945   {
946     // This construct allows us to have a wider textfield
947     // for viewing
948     JLabel label = new JLabel(MessageManager.getString("label.input_file_url"));
949     final JComboBox history = new JComboBox();
950
951     JPanel panel = new JPanel(new GridLayout(2, 1));
952     panel.add(label);
953     panel.add(history);
954     history.setPreferredSize(new Dimension(400, 20));
955     history.setEditable(true);
956     history.addItem("http://www.");
957
958     String historyItems = jalview.bin.Cache.getProperty("RECENT_URL");
959
960     StringTokenizer st;
961
962     if (historyItems != null)
963     {
964       st = new StringTokenizer(historyItems, "\t");
965
966       while (st.hasMoreTokens())
967       {
968         history.addItem(st.nextElement());
969       }
970     }
971
972     int reply = JOptionPane.showInternalConfirmDialog(desktop, panel,
973             MessageManager.getString("label.input_alignment_from_url"), JOptionPane.OK_CANCEL_OPTION);
974
975     if (reply != JOptionPane.OK_OPTION)
976     {
977       return;
978     }
979
980     String url = history.getSelectedItem().toString();
981
982     if (url.toLowerCase().endsWith(".jar"))
983     {
984       if (viewport != null)
985       {
986         new FileLoader().LoadFile(viewport, url, FormatAdapter.URL,
987                 "Jalview");
988       }
989       else
990       {
991         new FileLoader().LoadFile(url, FormatAdapter.URL, "Jalview");
992       }
993     }
994     else
995     {
996       String format = new IdentifyFile().Identify(url, FormatAdapter.URL);
997
998       if (format.equals("URL NOT FOUND"))
999       {
1000         JOptionPane.showInternalMessageDialog(Desktop.desktop,
1001                 "Couldn't locate " + url, "URL not found",
1002                 JOptionPane.WARNING_MESSAGE);
1003
1004         return;
1005       }
1006
1007       if (viewport != null)
1008       {
1009         new FileLoader().LoadFile(viewport, url, FormatAdapter.URL, format);
1010       }
1011       else
1012       {
1013         new FileLoader().LoadFile(url, FormatAdapter.URL, format);
1014       }
1015     }
1016   }
1017
1018   /**
1019    * DOCUMENT ME!
1020    * 
1021    * @param e
1022    *          DOCUMENT ME!
1023    */
1024   public void inputTextboxMenuItem_actionPerformed(AlignViewport viewport)
1025   {
1026     CutAndPasteTransfer cap = new CutAndPasteTransfer();
1027     cap.setForInput(viewport);
1028     Desktop.addInternalFrame(cap, "Cut & Paste Alignment File", 600, 500);
1029   }
1030
1031   /*
1032    * Exit the program
1033    */
1034   public void quit()
1035   {
1036     Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1037     jalview.bin.Cache
1038             .setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1039     jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height
1040             + "");
1041     storeLastKnownDimensions("", new Rectangle(getBounds().x,
1042             getBounds().y, getWidth(), getHeight()));
1043
1044     if (jconsole != null)
1045     {
1046       storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1047       jconsole.stopConsole();
1048     }
1049     if (jvnews != null)
1050     {
1051       storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1052
1053     }
1054     if (dialogExecutor != null)
1055     {
1056       dialogExecutor.shutdownNow();
1057     }
1058
1059     System.exit(0);
1060   }
1061
1062   private void storeLastKnownDimensions(String string, Rectangle jc)
1063   {
1064     jalview.bin.Cache.log.debug("Storing last known dimensions for "
1065             + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1066             + " height:" + jc.height);
1067
1068     jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
1069     jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1070     jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1071     jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1072   }
1073
1074   /**
1075    * DOCUMENT ME!
1076    * 
1077    * @param e
1078    *          DOCUMENT ME!
1079    */
1080   public void aboutMenuItem_actionPerformed(ActionEvent e)
1081   {
1082     // StringBuffer message = getAboutMessage(false);
1083     // JOptionPane.showInternalMessageDialog(Desktop.desktop,
1084     //
1085     // message.toString(), "About Jalview", JOptionPane.INFORMATION_MESSAGE);
1086     new Thread(new Runnable()
1087     {
1088       public void run()
1089       {
1090         new SplashScreen(true);
1091       }
1092     }).start();
1093   }
1094
1095   public StringBuffer getAboutMessage(boolean shortv)
1096   {
1097     StringBuffer message = new StringBuffer();
1098     message.append("<html>");
1099     if (shortv)
1100     {
1101       message.append("<h1><strong>Version: "
1102               + jalview.bin.Cache.getProperty("VERSION")
1103               + "</strong></h1><br>");
1104       message.append("<strong>Last Updated: <em>"
1105               + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1106               + "</em></strong>");
1107
1108     }
1109     else
1110     {
1111
1112       message.append("<strong>Version "
1113               + jalview.bin.Cache.getProperty("VERSION")
1114               + "; last updated: "
1115               + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1116     }
1117
1118     if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking").equals(
1119             "Checking"))
1120     {
1121       message.append("<br>...Checking latest version...</br>");
1122     }
1123     else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1124             .equals(jalview.bin.Cache.getProperty("VERSION")))
1125     {
1126       boolean red = false;
1127       if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1128               .indexOf("automated build") == -1)
1129       {
1130         red = true;
1131         // Displayed when code version and jnlp version do not match and code
1132         // version is not a development build
1133         message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1134       }
1135
1136       message.append("<br>!! Version "
1137               + jalview.bin.Cache.getDefault("LATEST_VERSION",
1138                       "..Checking..")
1139               + " is available for download from "
1140               + jalview.bin.Cache.getDefault("www.jalview.org",
1141                       "http://www.jalview.org") + " !!");
1142       if (red)
1143       {
1144         message.append("</div>");
1145       }
1146     }
1147     message.append("<br>Authors:  "
1148             + jalview.bin.Cache
1149                     .getDefault(
1150                             "AUTHORNAMES",
1151                             "Jim Procter, Andrew Waterhouse, Jan Engelhardt, Lauren Lui, Michele Clamp, James Cuff, Steve Searle, David Martin & Geoff Barton")
1152             + "<br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1153             + "<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"
1154             + "<br>If  you use Jalview, please cite:"
1155             + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1156             + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1157             + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1158             + "</html>");
1159     return message;
1160   }
1161
1162   /**
1163    * DOCUMENT ME!
1164    * 
1165    * @param e
1166    *          DOCUMENT ME!
1167    */
1168   public void documentationMenuItem_actionPerformed(ActionEvent e)
1169   {
1170     try
1171     {
1172       ClassLoader cl = jalview.gui.Desktop.class.getClassLoader();
1173       java.net.URL url = javax.help.HelpSet.findHelpSet(cl, "help/help");
1174       javax.help.HelpSet hs = new javax.help.HelpSet(cl, url);
1175
1176       javax.help.HelpBroker hb = hs.createHelpBroker();
1177       hb.setCurrentID("home");
1178       hb.setDisplayed(true);
1179     } catch (Exception ex)
1180     {
1181     }
1182   }
1183
1184   public void closeAll_actionPerformed(ActionEvent e)
1185   {
1186     JInternalFrame[] frames = desktop.getAllFrames();
1187     for (int i = 0; i < frames.length; i++)
1188     {
1189       try
1190       {
1191         frames[i].setClosed(true);
1192       } catch (java.beans.PropertyVetoException ex)
1193       {
1194       }
1195     }
1196     System.out.println("ALL CLOSED");
1197     if (v_client != null)
1198     {
1199       // TODO clear binding to vamsas document objects on close_all
1200
1201     }
1202   }
1203
1204   public void raiseRelated_actionPerformed(ActionEvent e)
1205   {
1206     reorderAssociatedWindows(false, false);
1207   }
1208
1209   public void minimizeAssociated_actionPerformed(ActionEvent e)
1210   {
1211     reorderAssociatedWindows(true, false);
1212   }
1213
1214   void closeAssociatedWindows()
1215   {
1216     reorderAssociatedWindows(false, true);
1217   }
1218
1219   /*
1220    * (non-Javadoc)
1221    * 
1222    * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1223    * ActionEvent)
1224    */
1225   protected void garbageCollect_actionPerformed(ActionEvent e)
1226   {
1227     // We simply collect the garbage
1228     jalview.bin.Cache.log.debug("Collecting garbage...");
1229     System.gc();
1230     jalview.bin.Cache.log.debug("Finished garbage collection.");
1231   }
1232
1233   /*
1234    * (non-Javadoc)
1235    * 
1236    * @see
1237    * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1238    * )
1239    */
1240   protected void showMemusage_actionPerformed(ActionEvent e)
1241   {
1242     desktop.showMemoryUsage(showMemusage.isSelected());
1243   }
1244
1245   /*
1246    * (non-Javadoc)
1247    * 
1248    * @see
1249    * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1250    * )
1251    */
1252   protected void showConsole_actionPerformed(ActionEvent e)
1253   {
1254     showConsole(showConsole.isSelected());
1255   }
1256
1257   Console jconsole = null;
1258
1259   /**
1260    * control whether the java console is visible or not
1261    * 
1262    * @param selected
1263    */
1264   void showConsole(boolean selected)
1265   {
1266     showConsole.setSelected(selected);
1267     // TODO: decide if we should update properties file
1268     Cache.setProperty("SHOW_JAVA_CONSOLE", Boolean.valueOf(selected)
1269             .toString());
1270     jconsole.setVisible(selected);
1271   }
1272
1273   void reorderAssociatedWindows(boolean minimize, boolean close)
1274   {
1275     JInternalFrame[] frames = desktop.getAllFrames();
1276     if (frames == null || frames.length < 1)
1277     {
1278       return;
1279     }
1280
1281     AlignViewport source = null, target = null;
1282     if (frames[0] instanceof AlignFrame)
1283     {
1284       source = ((AlignFrame) frames[0]).getCurrentView();
1285     }
1286     else if (frames[0] instanceof TreePanel)
1287     {
1288       source = ((TreePanel) frames[0]).getViewPort();
1289     }
1290     else if (frames[0] instanceof PCAPanel)
1291     {
1292       source = ((PCAPanel) frames[0]).av;
1293     }
1294     else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1295     {
1296       source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1297     }
1298
1299     if (source != null)
1300     {
1301       for (int i = 0; i < frames.length; i++)
1302       {
1303         target = null;
1304         if (frames[i] == null)
1305         {
1306           continue;
1307         }
1308         if (frames[i] instanceof AlignFrame)
1309         {
1310           target = ((AlignFrame) frames[i]).getCurrentView();
1311         }
1312         else if (frames[i] instanceof TreePanel)
1313         {
1314           target = ((TreePanel) frames[i]).getViewPort();
1315         }
1316         else if (frames[i] instanceof PCAPanel)
1317         {
1318           target = ((PCAPanel) frames[i]).av;
1319         }
1320         else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1321         {
1322           target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1323         }
1324
1325         if (source == target)
1326         {
1327           try
1328           {
1329             if (close)
1330             {
1331               frames[i].setClosed(true);
1332             }
1333             else
1334             {
1335               frames[i].setIcon(minimize);
1336               if (!minimize)
1337               {
1338                 frames[i].toFront();
1339               }
1340             }
1341
1342           } catch (java.beans.PropertyVetoException ex)
1343           {
1344           }
1345         }
1346       }
1347     }
1348   }
1349
1350   /**
1351    * DOCUMENT ME!
1352    * 
1353    * @param e
1354    *          DOCUMENT ME!
1355    */
1356   protected void preferences_actionPerformed(ActionEvent e)
1357   {
1358     new Preferences();
1359   }
1360
1361   /**
1362    * DOCUMENT ME!
1363    * 
1364    * @param e
1365    *          DOCUMENT ME!
1366    */
1367   public void saveState_actionPerformed(ActionEvent e)
1368   {
1369     JalviewFileChooser chooser = new JalviewFileChooser(
1370             jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
1371             { "jar" }, new String[]
1372             { "Jalview Project" }, "Jalview Project");
1373
1374     chooser.setFileView(new JalviewFileView());
1375     chooser.setDialogTitle("Save State");
1376
1377     int value = chooser.showSaveDialog(this);
1378
1379     if (value == JalviewFileChooser.APPROVE_OPTION)
1380     {
1381       final Desktop me = this;
1382       final java.io.File choice = chooser.getSelectedFile();
1383       new Thread(new Runnable()
1384       {
1385         public void run()
1386         {
1387
1388           setProgressBar("Saving jalview project " + choice.getName(),
1389                   choice.hashCode());
1390           jalview.bin.Cache.setProperty("LAST_DIRECTORY",
1391                   choice.getParent());
1392           // TODO catch and handle errors for savestate
1393           // TODO prevent user from messing with the Desktop whilst we're saving
1394           try
1395           {
1396             new Jalview2XML().SaveState(choice);
1397           } catch (OutOfMemoryError oom)
1398           {
1399             new OOMWarning("Whilst saving current state to "
1400                     + choice.getName(), oom);
1401           } catch (Exception ex)
1402           {
1403             Cache.log.error(
1404                     "Problems whilst trying to save to " + choice.getName(),
1405                     ex);
1406             JOptionPane.showMessageDialog(
1407                     me,
1408                     "Error whilst saving current state to "
1409                             + choice.getName(), "Couldn't save project",
1410                     JOptionPane.WARNING_MESSAGE);
1411           }
1412           setProgressBar(null, choice.hashCode());
1413         }
1414       }).start();
1415     }
1416   }
1417
1418   /**
1419    * DOCUMENT ME!
1420    * 
1421    * @param e
1422    *          DOCUMENT ME!
1423    */
1424   public void loadState_actionPerformed(ActionEvent e)
1425   {
1426     JalviewFileChooser chooser = new JalviewFileChooser(
1427             jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
1428             { "jar" }, new String[]
1429             { "Jalview Project" }, "Jalview Project");
1430     chooser.setFileView(new JalviewFileView());
1431     chooser.setDialogTitle("Restore state");
1432
1433     int value = chooser.showOpenDialog(this);
1434
1435     if (value == JalviewFileChooser.APPROVE_OPTION)
1436     {
1437       final String choice = chooser.getSelectedFile().getAbsolutePath();
1438       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
1439               .getSelectedFile().getParent());
1440       new Thread(new Runnable()
1441       {
1442         public void run()
1443         {
1444           setProgressBar("loading jalview project " + choice,
1445                   choice.hashCode());
1446           try
1447           {
1448             new Jalview2XML().LoadJalviewAlign(choice);
1449           } catch (OutOfMemoryError oom)
1450           {
1451             new OOMWarning("Whilst loading project from " + choice, oom);
1452           } catch (Exception ex)
1453           {
1454             Cache.log.error("Problems whilst loading project from "
1455                     + choice, ex);
1456             JOptionPane.showMessageDialog(Desktop.desktop,
1457                     "Error whilst loading project from " + choice,
1458                     "Couldn't load project", JOptionPane.WARNING_MESSAGE);
1459           }
1460           setProgressBar(null, choice.hashCode());
1461         }
1462       }).start();
1463     }
1464   }
1465
1466   public void inputSequence_actionPerformed(ActionEvent e)
1467   {
1468     new SequenceFetcher(this);
1469   }
1470
1471   JPanel progressPanel;
1472
1473   ArrayList<JPanel> fileLoadingPanels = new ArrayList<JPanel>();
1474
1475   public void startLoading(final String fileName)
1476   {
1477     if (fileLoadingCount == 0)
1478     {
1479       fileLoadingPanels.add(addProgressPanel("Loading File: " + fileName
1480               + "   "));
1481     }
1482     fileLoadingCount++;
1483   }
1484
1485   private JPanel addProgressPanel(String string)
1486   {
1487     if (progressPanel == null)
1488     {
1489       progressPanel = new JPanel(new GridLayout(1, 1));
1490       totalProgressCount = 0;
1491       instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1492     }
1493     JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1494     JProgressBar progressBar = new JProgressBar();
1495     progressBar.setIndeterminate(true);
1496
1497     thisprogress.add(new JLabel(string), BorderLayout.WEST);
1498
1499     thisprogress.add(progressBar, BorderLayout.CENTER);
1500     progressPanel.add(thisprogress);
1501     ((GridLayout) progressPanel.getLayout())
1502             .setRows(((GridLayout) progressPanel.getLayout()).getRows() + 1);
1503     ++totalProgressCount;
1504     instance.validate();
1505     return thisprogress;
1506   }
1507
1508   int totalProgressCount = 0;
1509
1510   private void removeProgressPanel(JPanel progbar)
1511   {
1512     if (progressPanel != null)
1513     {
1514       synchronized(progressPanel) {
1515       progressPanel.remove(progbar);
1516       GridLayout gl = (GridLayout) progressPanel.getLayout();
1517       gl.setRows(gl.getRows() - 1);
1518       if (--totalProgressCount < 1)
1519       {
1520         this.getContentPane().remove(progressPanel);
1521         progressPanel = null;
1522       }
1523       }
1524     }
1525     validate();
1526   }
1527
1528   public void stopLoading()
1529   {
1530     fileLoadingCount--;
1531     if (fileLoadingCount < 1)
1532     {
1533       while (fileLoadingPanels.size()>0)
1534       {
1535         removeProgressPanel(fileLoadingPanels.remove(0));
1536       }
1537       fileLoadingPanels.clear();
1538       fileLoadingCount = 0;
1539     }
1540     validate();
1541   }
1542
1543   public static int getViewCount(String alignmentId)
1544   {
1545     AlignViewport[] aps = getViewports(alignmentId);
1546     return (aps == null) ? 0 : aps.length;
1547   }
1548
1549   /**
1550    * 
1551    * @param alignmentId
1552    * @return all AlignmentPanels concerning the alignmentId sequence set
1553    */
1554   public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1555   {
1556     int count = 0;
1557     if (Desktop.desktop == null)
1558     {
1559       // no frames created and in headless mode
1560       // TODO: verify that frames are recoverable when in headless mode
1561       return null;
1562     }
1563     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1564     ArrayList aps = new ArrayList();
1565     for (int t = 0; t < frames.length; t++)
1566     {
1567       if (frames[t] instanceof AlignFrame)
1568       {
1569         AlignFrame af = (AlignFrame) frames[t];
1570         for (int a = 0; a < af.alignPanels.size(); a++)
1571         {
1572           if (alignmentId.equals(((AlignmentPanel) af.alignPanels
1573                   .elementAt(a)).av.getSequenceSetId()))
1574           {
1575             aps.add(af.alignPanels.elementAt(a));
1576           }
1577         }
1578       }
1579     }
1580     if (aps.size() == 0)
1581     {
1582       return null;
1583     }
1584     AlignmentPanel[] vap = new AlignmentPanel[aps.size()];
1585     for (int t = 0; t < vap.length; t++)
1586     {
1587       vap[t] = (AlignmentPanel) aps.get(t);
1588     }
1589     return vap;
1590   }
1591
1592   /**
1593    * get all the viewports on an alignment.
1594    * 
1595    * @param sequenceSetId
1596    *          unique alignment id
1597    * @return all viewports on the alignment bound to sequenceSetId
1598    */
1599   public static AlignViewport[] getViewports(String sequenceSetId)
1600   {
1601     Vector viewp = new Vector();
1602     if (desktop != null)
1603     {
1604       javax.swing.JInternalFrame[] frames = instance.getAllFrames();
1605
1606       for (int t = 0; t < frames.length; t++)
1607       {
1608         if (frames[t] instanceof AlignFrame)
1609         {
1610           AlignFrame afr = ((AlignFrame) frames[t]);
1611           if (afr.getViewport().getSequenceSetId().equals(sequenceSetId))
1612           {
1613             if (afr.alignPanels != null)
1614             {
1615               for (int a = 0; a < afr.alignPanels.size(); a++)
1616               {
1617                 if (sequenceSetId.equals(((AlignmentPanel) afr.alignPanels
1618                         .elementAt(a)).av.getSequenceSetId()))
1619                 {
1620                   viewp.addElement(((AlignmentPanel) afr.alignPanels
1621                           .elementAt(a)).av);
1622                 }
1623               }
1624             }
1625             else
1626             {
1627               viewp.addElement(((AlignFrame) frames[t]).getViewport());
1628             }
1629           }
1630         }
1631       }
1632       if (viewp.size() > 0)
1633       {
1634         AlignViewport[] vp = new AlignViewport[viewp.size()];
1635         viewp.copyInto(vp);
1636         return vp;
1637       }
1638     }
1639     return null;
1640   }
1641
1642   public void explodeViews(AlignFrame af)
1643   {
1644     int size = af.alignPanels.size();
1645     if (size < 2)
1646     {
1647       return;
1648     }
1649
1650     for (int i = 0; i < size; i++)
1651     {
1652       AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(i);
1653       AlignFrame newaf = new AlignFrame(ap);
1654       if (ap.av.explodedPosition != null
1655               && !ap.av.explodedPosition.equals(af.getBounds()))
1656       {
1657         newaf.setBounds(ap.av.explodedPosition);
1658       }
1659
1660       ap.av.gatherViewsHere = false;
1661
1662       addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1663               AlignFrame.DEFAULT_HEIGHT);
1664     }
1665
1666     af.alignPanels.clear();
1667     af.closeMenuItem_actionPerformed(true);
1668
1669   }
1670
1671   public void gatherViews(AlignFrame source)
1672   {
1673     source.viewport.gatherViewsHere = true;
1674     source.viewport.explodedPosition = source.getBounds();
1675     JInternalFrame[] frames = desktop.getAllFrames();
1676     String viewId = source.viewport.getSequenceSetId();
1677
1678     for (int t = 0; t < frames.length; t++)
1679     {
1680       if (frames[t] instanceof AlignFrame && frames[t] != source)
1681       {
1682         AlignFrame af = (AlignFrame) frames[t];
1683         boolean gatherThis = false;
1684         for (int a = 0; a < af.alignPanels.size(); a++)
1685         {
1686           AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(a);
1687           if (viewId.equals(ap.av.getSequenceSetId()))
1688           {
1689             gatherThis = true;
1690             ap.av.gatherViewsHere = false;
1691             ap.av.explodedPosition = af.getBounds();
1692             source.addAlignmentPanel(ap, false);
1693           }
1694         }
1695
1696         if (gatherThis)
1697         {
1698           af.alignPanels.clear();
1699           af.closeMenuItem_actionPerformed(true);
1700         }
1701       }
1702     }
1703
1704   }
1705
1706   jalview.gui.VamsasApplication v_client = null;
1707
1708   public void vamsasImport_actionPerformed(ActionEvent e)
1709   {
1710     if (v_client == null)
1711     {
1712       // Load and try to start a session.
1713       JalviewFileChooser chooser = new JalviewFileChooser(
1714               jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
1715
1716       chooser.setFileView(new JalviewFileView());
1717       chooser.setDialogTitle("Open a saved VAMSAS session");
1718       chooser.setToolTipText(MessageManager.getString("label.select_vamsas_session_opened_as_new_vamsas_session"));
1719
1720       int value = chooser.showOpenDialog(this);
1721
1722       if (value == JalviewFileChooser.APPROVE_OPTION)
1723       {
1724         String fle = chooser.getSelectedFile().toString();
1725         if (!vamsasImport(chooser.getSelectedFile()))
1726         {
1727           JOptionPane.showInternalMessageDialog(Desktop.desktop,
1728                   MessageManager.formatMessage("label.couldnt_import_as_vamsas_session", new String[]{fle}),
1729                   MessageManager.getString("label.vamsas_document_import_failed"),
1730                   JOptionPane.ERROR_MESSAGE);
1731         }
1732       }
1733     }
1734     else
1735     {
1736       jalview.bin.Cache.log
1737               .error("Implementation error - load session from a running session is not supported.");
1738     }
1739   }
1740
1741   /**
1742    * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
1743    * 
1744    * @param file
1745    * @return true if import was a success and a session was started.
1746    */
1747   public boolean vamsasImport(URL url)
1748   {
1749     // TODO: create progress bar
1750     if (v_client != null)
1751     {
1752
1753       jalview.bin.Cache.log
1754               .error("Implementation error - load session from a running session is not supported.");
1755       return false;
1756     }
1757
1758     try
1759     {
1760       // copy the URL content to a temporary local file
1761       // TODO: be a bit cleverer here with nio (?!)
1762       File file = File.createTempFile("vdocfromurl", ".vdj");
1763       FileOutputStream fos = new FileOutputStream(file);
1764       BufferedInputStream bis = new BufferedInputStream(url.openStream());
1765       byte[] buffer = new byte[2048];
1766       int ln;
1767       while ((ln = bis.read(buffer)) > -1)
1768       {
1769         fos.write(buffer, 0, ln);
1770       }
1771       bis.close();
1772       fos.close();
1773       v_client = new jalview.gui.VamsasApplication(this, file,
1774               url.toExternalForm());
1775     } catch (Exception ex)
1776     {
1777       jalview.bin.Cache.log.error(
1778               "Failed to create new vamsas session from contents of URL "
1779                       + url, ex);
1780       return false;
1781     }
1782     setupVamsasConnectedGui();
1783     v_client.initial_update(); // TODO: thread ?
1784     return v_client.inSession();
1785   }
1786
1787   /**
1788    * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
1789    * 
1790    * @param file
1791    * @return true if import was a success and a session was started.
1792    */
1793   public boolean vamsasImport(File file)
1794   {
1795     if (v_client != null)
1796     {
1797
1798       jalview.bin.Cache.log
1799               .error("Implementation error - load session from a running session is not supported.");
1800       return false;
1801     }
1802
1803     setProgressBar("Importing VAMSAS session from " + file.getName(),
1804             file.hashCode());
1805     try
1806     {
1807       v_client = new jalview.gui.VamsasApplication(this, file, null);
1808     } catch (Exception ex)
1809     {
1810       setProgressBar("Importing VAMSAS session from " + file.getName(),
1811               file.hashCode());
1812       jalview.bin.Cache.log.error(
1813               "New vamsas session from existing session file failed:", ex);
1814       return false;
1815     }
1816     setupVamsasConnectedGui();
1817     v_client.initial_update(); // TODO: thread ?
1818     setProgressBar("Importing VAMSAS session from " + file.getName(),
1819             file.hashCode());
1820     return v_client.inSession();
1821   }
1822
1823   public boolean joinVamsasSession(String mysesid)
1824   {
1825     if (v_client != null)
1826     {
1827       throw new Error(
1828               "Trying to join a vamsas session when another is already connected.");
1829     }
1830     if (mysesid == null)
1831     {
1832       throw new Error("Invalid vamsas session id.");
1833     }
1834     v_client = new VamsasApplication(this, mysesid);
1835     setupVamsasConnectedGui();
1836     v_client.initial_update();
1837     return (v_client.inSession());
1838   }
1839
1840   public void vamsasStart_actionPerformed(ActionEvent e)
1841   {
1842     if (v_client == null)
1843     {
1844       // Start a session.
1845       // we just start a default session for moment.
1846       /*
1847        * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
1848        * getProperty("LAST_DIRECTORY"));
1849        * 
1850        * chooser.setFileView(new JalviewFileView());
1851        * chooser.setDialogTitle("Load Vamsas file");
1852        * chooser.setToolTipText("Import");
1853        * 
1854        * int value = chooser.showOpenDialog(this);
1855        * 
1856        * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
1857        * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
1858        */
1859       v_client = new VamsasApplication(this);
1860       setupVamsasConnectedGui();
1861       v_client.initial_update(); // TODO: thread ?
1862     }
1863     else
1864     {
1865       // store current data in session.
1866       v_client.push_update(); // TODO: thread
1867     }
1868   }
1869
1870   protected void setupVamsasConnectedGui()
1871   {
1872     vamsasStart.setText(MessageManager.getString("label.session_update"));
1873     vamsasSave.setVisible(true);
1874     vamsasStop.setVisible(true);
1875     vamsasImport.setVisible(false); // Document import to existing session is
1876     // not possible for vamsas-client-1.0.
1877   }
1878
1879   protected void setupVamsasDisconnectedGui()
1880   {
1881     vamsasSave.setVisible(false);
1882     vamsasStop.setVisible(false);
1883     vamsasImport.setVisible(true);
1884     vamsasStart.setText(MessageManager.getString("label.new_vamsas_session"));
1885   }
1886
1887   public void vamsasStop_actionPerformed(ActionEvent e)
1888   {
1889     if (v_client != null)
1890     {
1891       v_client.end_session();
1892       v_client = null;
1893       setupVamsasDisconnectedGui();
1894     }
1895   }
1896
1897   protected void buildVamsasStMenu()
1898   {
1899     if (v_client == null)
1900     {
1901       String[] sess = null;
1902       try
1903       {
1904         sess = VamsasApplication.getSessionList();
1905       } catch (Exception e)
1906       {
1907         jalview.bin.Cache.log.warn(
1908                 "Problem getting current sessions list.", e);
1909         sess = null;
1910       }
1911       if (sess != null)
1912       {
1913         jalview.bin.Cache.log.debug("Got current sessions list: "
1914                 + sess.length + " entries.");
1915         VamsasStMenu.removeAll();
1916         for (int i = 0; i < sess.length; i++)
1917         {
1918           JMenuItem sessit = new JMenuItem();
1919           sessit.setText(sess[i]);
1920           sessit.setToolTipText(MessageManager.formatMessage("label.connect_to_session", new String[]{sess[i]}));
1921           final Desktop dsktp = this;
1922           final String mysesid = sess[i];
1923           sessit.addActionListener(new ActionListener()
1924           {
1925
1926             public void actionPerformed(ActionEvent e)
1927             {
1928               if (dsktp.v_client == null)
1929               {
1930                 Thread rthr = new Thread(new Runnable()
1931                 {
1932
1933                   public void run()
1934                   {
1935                     dsktp.v_client = new VamsasApplication(dsktp, mysesid);
1936                     dsktp.setupVamsasConnectedGui();
1937                     dsktp.v_client.initial_update();
1938                   }
1939
1940                 });
1941                 rthr.start();
1942               }
1943             };
1944           });
1945           VamsasStMenu.add(sessit);
1946         }
1947         // don't show an empty menu.
1948         VamsasStMenu.setVisible(sess.length > 0);
1949
1950       }
1951       else
1952       {
1953         jalview.bin.Cache.log.debug("No current vamsas sessions.");
1954         VamsasStMenu.removeAll();
1955         VamsasStMenu.setVisible(false);
1956       }
1957     }
1958     else
1959     {
1960       // Not interested in the content. Just hide ourselves.
1961       VamsasStMenu.setVisible(false);
1962     }
1963   }
1964
1965   public void vamsasSave_actionPerformed(ActionEvent e)
1966   {
1967     if (v_client != null)
1968     {
1969       JalviewFileChooser chooser = new JalviewFileChooser(
1970               jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
1971               { "vdj" }, // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
1972               new String[]
1973               { "Vamsas Document" }, "Vamsas Document");
1974
1975       chooser.setFileView(new JalviewFileView());
1976       chooser.setDialogTitle("Save Vamsas Document Archive");
1977
1978       int value = chooser.showSaveDialog(this);
1979
1980       if (value == JalviewFileChooser.APPROVE_OPTION)
1981       {
1982         java.io.File choice = chooser.getSelectedFile();
1983         JPanel progpanel = addProgressPanel("Saving VAMSAS Document to "
1984                 + choice.getName());
1985         jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
1986         String warnmsg = null;
1987         String warnttl = null;
1988         try
1989         {
1990           v_client.vclient.storeDocument(choice);
1991         } catch (Error ex)
1992         {
1993           warnttl = "Serious Problem saving Vamsas Document";
1994           warnmsg = ex.toString();
1995           jalview.bin.Cache.log.error("Error Whilst saving document to "
1996                   + choice, ex);
1997
1998         } catch (Exception ex)
1999         {
2000           warnttl = "Problem saving Vamsas Document.";
2001           warnmsg = ex.toString();
2002           jalview.bin.Cache.log.warn("Exception Whilst saving document to "
2003                   + choice, ex);
2004
2005         }
2006         removeProgressPanel(progpanel);
2007         if (warnmsg != null)
2008         {
2009           JOptionPane.showInternalMessageDialog(Desktop.desktop,
2010
2011           warnmsg, warnttl, JOptionPane.ERROR_MESSAGE);
2012         }
2013       }
2014     }
2015   }
2016
2017   JPanel vamUpdate = null;
2018
2019   /**
2020    * hide vamsas user gui bits when a vamsas document event is being handled.
2021    * 
2022    * @param b
2023    *          true to hide gui, false to reveal gui
2024    */
2025   public void setVamsasUpdate(boolean b)
2026   {
2027     jalview.bin.Cache.log.debug("Setting gui for Vamsas update "
2028             + (b ? "in progress" : "finished"));
2029
2030     if (vamUpdate != null)
2031     {
2032       this.removeProgressPanel(vamUpdate);
2033     }
2034     if (b)
2035     {
2036       vamUpdate = this.addProgressPanel("Updating vamsas session");
2037     }
2038     vamsasStart.setVisible(!b);
2039     vamsasStop.setVisible(!b);
2040     vamsasSave.setVisible(!b);
2041   }
2042
2043   public JInternalFrame[] getAllFrames()
2044   {
2045     return desktop.getAllFrames();
2046   }
2047
2048   /**
2049    * Checks the given url to see if it gives a response indicating that the user
2050    * should be informed of a new questionnaire.
2051    * 
2052    * @param url
2053    */
2054   public void checkForQuestionnaire(String url)
2055   {
2056     UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2057     // javax.swing.SwingUtilities.invokeLater(jvq);
2058     new Thread(jvq).start();
2059   }
2060
2061   /**
2062    * Proxy class for JDesktopPane which optionally displays the current memory
2063    * usage and highlights the desktop area with a red bar if free memory runs
2064    * low.
2065    * 
2066    * @author AMW
2067    */
2068   public class MyDesktopPane extends JDesktopPane implements Runnable
2069   {
2070
2071     boolean showMemoryUsage = false;
2072
2073     Runtime runtime;
2074
2075     java.text.NumberFormat df;
2076
2077     float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2078             percentUsage;
2079
2080     public MyDesktopPane(boolean showMemoryUsage)
2081     {
2082       showMemoryUsage(showMemoryUsage);
2083     }
2084
2085     public void showMemoryUsage(boolean showMemoryUsage)
2086     {
2087       this.showMemoryUsage = showMemoryUsage;
2088       if (showMemoryUsage)
2089       {
2090         Thread worker = new Thread(this);
2091         worker.start();
2092       }
2093     }
2094
2095     public boolean isShowMemoryUsage()
2096     {
2097       return showMemoryUsage;
2098     }
2099
2100     public void run()
2101     {
2102       df = java.text.NumberFormat.getNumberInstance();
2103       df.setMaximumFractionDigits(2);
2104       runtime = Runtime.getRuntime();
2105
2106       while (showMemoryUsage)
2107       {
2108         try
2109         {
2110           maxMemory = runtime.maxMemory() / 1048576f;
2111           allocatedMemory = runtime.totalMemory() / 1048576f;
2112           freeMemory = runtime.freeMemory() / 1048576f;
2113           totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2114
2115           percentUsage = (totalFreeMemory / maxMemory) * 100;
2116
2117           // if (percentUsage < 20)
2118           {
2119             // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2120             // Color.red);
2121             // instance.set.setBorder(border1);
2122           }
2123           repaint();
2124           // sleep after showing usage
2125           Thread.sleep(3000);
2126         } catch (Exception ex)
2127         {
2128           ex.printStackTrace();
2129         }
2130       }
2131     }
2132
2133     public void paintComponent(Graphics g)
2134     {
2135       if (showMemoryUsage && g != null && df != null)
2136       {
2137         if (percentUsage < 20)
2138           g.setColor(Color.red);
2139         FontMetrics fm = g.getFontMetrics();
2140         if (fm != null)
2141         {
2142           g.drawString(
2143                           MessageManager.formatMessage("label.memory_stats", new String[]{df.format(totalFreeMemory),df.format(maxMemory),df.format(percentUsage)}), 10,
2144                   getHeight() - fm.getHeight());
2145         }
2146       }
2147     }
2148   }
2149
2150   /**
2151    * fixes stacking order after a modal dialog to ensure windows that should be
2152    * on top actually are
2153    */
2154   public void relayerWindows()
2155   {
2156
2157   }
2158
2159   protected JMenuItem groovyShell;
2160
2161   public void doGroovyCheck()
2162   {
2163     if (jalview.bin.Cache.groovyJarsPresent())
2164     {
2165       groovyShell = new JMenuItem();
2166       groovyShell.setText(MessageManager.getString("label.groovy_console"));
2167       groovyShell.addActionListener(new ActionListener()
2168       {
2169         public void actionPerformed(ActionEvent e)
2170         {
2171           groovyShell_actionPerformed(e);
2172         }
2173       });
2174       toolsMenu.add(groovyShell);
2175       groovyShell.setVisible(true);
2176     }
2177   }
2178
2179   /**
2180    * Accessor method to quickly get all the AlignmentFrames loaded.
2181    */
2182   public static AlignFrame[] getAlignframes()
2183   {
2184     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2185
2186     if (frames == null)
2187     {
2188       return null;
2189     }
2190     Vector avp = new Vector();
2191     try
2192     {
2193       // REVERSE ORDER
2194       for (int i = frames.length - 1; i > -1; i--)
2195       {
2196         if (frames[i] instanceof AlignFrame)
2197         {
2198           AlignFrame af = (AlignFrame) frames[i];
2199           avp.addElement(af);
2200         }
2201       }
2202     } catch (Exception ex)
2203     {
2204       ex.printStackTrace();
2205     }
2206     if (avp.size() == 0)
2207     {
2208       return null;
2209     }
2210     AlignFrame afs[] = new AlignFrame[avp.size()];
2211     for (int i = 0, j = avp.size(); i < j; i++)
2212     {
2213       afs[i] = (AlignFrame) avp.elementAt(i);
2214     }
2215     avp.clear();
2216     return afs;
2217   }
2218
2219   public AppJmol[] getJmols()
2220   {
2221     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2222
2223     if (frames == null)
2224     {
2225       return null;
2226     }
2227     Vector avp = new Vector();
2228     try
2229     {
2230       // REVERSE ORDER
2231       for (int i = frames.length - 1; i > -1; i--)
2232       {
2233         if (frames[i] instanceof AppJmol)
2234         {
2235           AppJmol af = (AppJmol) frames[i];
2236           avp.addElement(af);
2237         }
2238       }
2239     } catch (Exception ex)
2240     {
2241       ex.printStackTrace();
2242     }
2243     if (avp.size() == 0)
2244     {
2245       return null;
2246     }
2247     AppJmol afs[] = new AppJmol[avp.size()];
2248     for (int i = 0, j = avp.size(); i < j; i++)
2249     {
2250       afs[i] = (AppJmol) avp.elementAt(i);
2251     }
2252     avp.clear();
2253     return afs;
2254   }
2255
2256   /**
2257    * Add Groovy Support to Jalview
2258    */
2259   public void groovyShell_actionPerformed(ActionEvent e)
2260   {
2261     // use reflection to avoid creating compilation dependency.
2262     if (!jalview.bin.Cache.groovyJarsPresent())
2263     {
2264       throw new Error(
2265               "Implementation Error. Cannot create groovyShell without Groovy on the classpath!");
2266     }
2267     try
2268     {
2269       Class gcClass = Desktop.class.getClassLoader().loadClass(
2270               "groovy.ui.Console");
2271       Constructor gccons = gcClass.getConstructor(null);
2272       java.lang.reflect.Method setvar = gcClass.getMethod("setVariable",
2273               new Class[]
2274               { String.class, Object.class });
2275       java.lang.reflect.Method run = gcClass.getMethod("run", null);
2276       Object gc = gccons.newInstance(null);
2277       setvar.invoke(gc, new Object[]
2278       { "Jalview", this });
2279       run.invoke(gc, null);
2280     } catch (Exception ex)
2281     {
2282       jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2283       JOptionPane
2284               .showInternalMessageDialog(
2285                       Desktop.desktop,
2286
2287                       "Couldn't create the groovy Shell. Check the error log for the details of what went wrong.",
2288                       "Jalview Groovy Support Failed",
2289                       JOptionPane.ERROR_MESSAGE);
2290     }
2291   }
2292
2293   /**
2294    * Progress bars managed by the IProgressIndicator method.
2295    */
2296   private Hashtable<Long, JPanel> progressBars;
2297
2298   private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2299
2300   /*
2301    * (non-Javadoc)
2302    * 
2303    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2304    */
2305   public void setProgressBar(String message, long id)
2306   {
2307     if (progressBars == null)
2308     {
2309       progressBars = new Hashtable<Long, JPanel>();
2310       progressBarHandlers = new Hashtable<Long, IProgressIndicatorHandler>();
2311     }
2312
2313     if (progressBars.get(new Long(id)) != null)
2314     {
2315       JPanel progressPanel = progressBars.remove(new Long(id));
2316       if (progressBarHandlers.contains(new Long(id)))
2317       {
2318         progressBarHandlers.remove(new Long(id));
2319       }
2320       removeProgressPanel(progressPanel);
2321     }
2322     else
2323     {
2324       progressBars.put(new Long(id), addProgressPanel(message));
2325     }
2326   }
2327
2328   /*
2329    * (non-Javadoc)
2330    * 
2331    * @see jalview.gui.IProgressIndicator#registerHandler(long,
2332    * jalview.gui.IProgressIndicatorHandler)
2333    */
2334   public void registerHandler(final long id,
2335           final IProgressIndicatorHandler handler)
2336   {
2337     if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
2338     {
2339       throw new Error(
2340               "call setProgressBar before registering the progress bar's handler.");
2341     }
2342     progressBarHandlers.put(new Long(id), handler);
2343     final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
2344     if (handler.canCancel())
2345     {
2346       JButton cancel = new JButton(MessageManager.getString("action.cancel"));
2347       final IProgressIndicator us = this;
2348       cancel.addActionListener(new ActionListener()
2349       {
2350
2351         public void actionPerformed(ActionEvent e)
2352         {
2353           handler.cancelActivity(id);
2354           us.setProgressBar(
2355                   "Cancelled "
2356                           + ((JLabel) progressPanel.getComponent(0))
2357                                   .getText(), id);
2358         }
2359       });
2360       progressPanel.add(cancel, BorderLayout.EAST);
2361     }
2362   }
2363
2364   /**
2365    * 
2366    * @return true if any progress bars are still active
2367    */
2368   @Override
2369   public boolean operationInProgress()
2370   {
2371     if (progressBars != null && progressBars.size() > 0)
2372     {
2373       return true;
2374     }
2375     return false;
2376   }
2377
2378   /**
2379    * This will return the first AlignFrame viewing AlignViewport av. It will
2380    * break if there are more than one AlignFrames viewing a particular av. This
2381    * 
2382    * @param av
2383    * @return alignFrame for av
2384    */
2385   public static AlignFrame getAlignFrameFor(AlignViewport av)
2386   {
2387     if (desktop != null)
2388     {
2389       AlignmentPanel[] aps = getAlignmentPanels(av.getSequenceSetId());
2390       for (int panel = 0; aps != null && panel < aps.length; panel++)
2391       {
2392         if (aps[panel] != null && aps[panel].av == av)
2393         {
2394           return aps[panel].alignFrame;
2395         }
2396       }
2397     }
2398     return null;
2399   }
2400
2401   public VamsasApplication getVamsasApplication()
2402   {
2403     return v_client;
2404
2405   }
2406
2407   /**
2408    * flag set if jalview GUI is being operated programmatically
2409    */
2410   private boolean inBatchMode = false;
2411
2412   /**
2413    * check if jalview GUI is being operated programmatically
2414    * 
2415    * @return inBatchMode
2416    */
2417   public boolean isInBatchMode()
2418   {
2419     return inBatchMode;
2420   }
2421
2422   /**
2423    * set flag if jalview GUI is being operated programmatically
2424    * 
2425    * @param inBatchMode
2426    */
2427   public void setInBatchMode(boolean inBatchMode)
2428   {
2429     this.inBatchMode = inBatchMode;
2430   }
2431
2432   public void startServiceDiscovery()
2433   {
2434     startServiceDiscovery(false);
2435   }
2436
2437   public void startServiceDiscovery(boolean blocking)
2438   {
2439     boolean alive = true;
2440     Thread t0 = null, t1 = null, t2 = null;
2441     // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2442     if (true)
2443     {
2444     // todo: changesupport handlers need to be transferred
2445     if (discoverer == null)
2446     {
2447       discoverer = new jalview.ws.jws1.Discoverer();
2448       // register PCS handler for desktop.
2449       discoverer.addPropertyChangeListener(changeSupport);
2450     }
2451     // JAL-940 - disabled JWS1 service configuration - always start discoverer
2452     // until we phase out completely
2453       (t0 = new Thread(discoverer)).start();
2454     }
2455
2456     // ENFIN services are EOLed as of Jalview 2.8.1 release
2457     if (false)
2458     {
2459       try
2460       {
2461         if (Cache.getDefault("SHOW_ENFIN_SERVICES", true))
2462         {
2463           // EnfinEnvision web service menu entries are rebuild every time the
2464           // menu is shown, so no changeSupport events are needed.
2465           jalview.ws.EnfinEnvision2OneWay.getInstance();
2466           (t1 = new Thread(jalview.ws.EnfinEnvision2OneWay.getInstance()))
2467                   .start();
2468         }
2469       } catch (Exception e)
2470       {
2471         Cache.log
2472                 .info("Exception when trying to launch Envision2 workflow discovery.",
2473                         e);
2474         Cache.log.info(e.getStackTrace());
2475       }
2476     }
2477
2478     if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2479     {
2480       if (jalview.ws.jws2.Jws2Discoverer.getDiscoverer().isRunning())
2481       {
2482         jalview.ws.jws2.Jws2Discoverer.getDiscoverer().setAborted(true);
2483       }
2484       t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer().startDiscoverer(
2485               changeSupport);
2486
2487     }
2488     Thread t3 = null;
2489     {
2490       // TODO: do rest service discovery
2491     }
2492     if (blocking)
2493     {
2494       while (alive)
2495       {
2496         try
2497         {
2498           Thread.sleep(15);
2499         } catch (Exception e)
2500         {
2501         }
2502         alive = (t1 != null && t1.isAlive())
2503                 || (t2 != null && t2.isAlive())
2504                 || (t3 != null && t3.isAlive())
2505                 || (t0 != null && t0.isAlive());
2506       }
2507     }
2508   }
2509
2510   /**
2511    * called to check if the service discovery process completed successfully.
2512    * 
2513    * @param evt
2514    */
2515   protected void JalviewServicesChanged(PropertyChangeEvent evt)
2516   {
2517     if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2518     {
2519       final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2520               .getErrorMessages();
2521       if (ermsg != null)
2522       {
2523         if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2524         {
2525           if (serviceChangedDialog == null)
2526           {
2527             // only run if we aren't already displaying one of these.
2528             addDialogThread(serviceChangedDialog = new Runnable()
2529             {
2530               public void run()
2531               {
2532
2533                 /*
2534                  * JalviewDialog jd =new JalviewDialog() {
2535                  * 
2536                  * @Override protected void cancelPressed() { // TODO
2537                  * Auto-generated method stub
2538                  * 
2539                  * }@Override protected void okPressed() { // TODO
2540                  * Auto-generated method stub
2541                  * 
2542                  * }@Override protected void raiseClosed() { // TODO
2543                  * Auto-generated method stub
2544                  * 
2545                  * } }; jd.initDialogFrame(new
2546                  * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2547                  * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2548                  * + " or mis-configured HTTP proxy settings.<br/>" +
2549                  * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2550                  * +
2551                  * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2552                  * ), true, true, "Web Service Configuration Problem", 450,
2553                  * 400);
2554                  * 
2555                  * jd.waitForInput();
2556                  */
2557                 JOptionPane
2558                         .showConfirmDialog(
2559                                 Desktop.desktop,
2560                                 new JLabel(
2561                                         "<html><table width=\"450\"><tr><td>"
2562                                                 + ermsg
2563                                                 + "</td></tr></table>"
2564                                                 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2565                                                 + " or mis-configured HTTP proxy settings.</p>"
2566                                                 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2567                                                 + " Tools->Preferences dialog box to change them.</p></html>"),
2568                                 "Web Service Configuration Problem",
2569                                 JOptionPane.DEFAULT_OPTION,
2570                                 JOptionPane.ERROR_MESSAGE);
2571                 serviceChangedDialog = null;
2572
2573               }
2574             });
2575           }
2576         }
2577         else
2578         {
2579           Cache.log
2580                   .error("Errors reported by JABA discovery service. Check web services preferences.\n"
2581                           + ermsg);
2582         }
2583       }
2584     }
2585   }
2586
2587   private Runnable serviceChangedDialog = null;
2588
2589   /**
2590    * start a thread to open a URL in the configured browser. Pops up a warning
2591    * dialog to the user if there is an exception when calling out to the browser
2592    * to open the URL.
2593    * 
2594    * @param url
2595    */
2596   public static void showUrl(final String url)
2597   {
2598     showUrl(url, Desktop.instance);
2599   }
2600
2601   /**
2602    * Like showUrl but allows progress handler to be specified
2603    * 
2604    * @param url
2605    * @param progress
2606    *          (null) or object implementing IProgressIndicator
2607    */
2608   public static void showUrl(final String url,
2609           final IProgressIndicator progress)
2610   {
2611     new Thread(new Runnable()
2612     {
2613       public void run()
2614       {
2615         try
2616         {
2617           if (progress != null)
2618           {
2619             progress.setProgressBar("Opening " + url, this.hashCode());
2620           }
2621           jalview.util.BrowserLauncher.openURL(url);
2622         } catch (Exception ex)
2623         {
2624           JOptionPane
2625                   .showInternalMessageDialog(
2626                           Desktop.desktop,
2627                           "Unixers: Couldn't find default web browser."
2628                                   + "\nAdd the full path to your browser in Preferences.",
2629                           "Web browser not found",
2630                           JOptionPane.WARNING_MESSAGE);
2631
2632           ex.printStackTrace();
2633         }
2634         if (progress != null)
2635         {
2636           progress.setProgressBar(null, this.hashCode());
2637         }
2638       }
2639     }).start();
2640   }
2641
2642   public static WsParamSetManager wsparamManager = null;
2643
2644   public static ParamManager getUserParameterStore()
2645   {
2646     if (wsparamManager == null)
2647     {
2648       wsparamManager = new WsParamSetManager();
2649     }
2650     return wsparamManager;
2651   }
2652
2653   /**
2654    * static hyperlink handler proxy method for use by Jalview's internal windows
2655    * 
2656    * @param e
2657    */
2658   public static void hyperlinkUpdate(HyperlinkEvent e)
2659   {
2660     if (e.getEventType() == EventType.ACTIVATED)
2661     {
2662       String url = null;
2663       try
2664       {
2665         url = e.getURL().toString();
2666         Desktop.showUrl(url);
2667       } catch (Exception x)
2668       {
2669         if (url != null)
2670         {
2671           if (Cache.log != null)
2672           {
2673             Cache.log.error("Couldn't handle string " + url + " as a URL.");
2674           }
2675           else
2676           {
2677             System.err.println("Couldn't handle string " + url
2678                     + " as a URL.");
2679           }
2680         }
2681         // ignore any exceptions due to dud links.
2682       }
2683
2684     }
2685   }
2686
2687   /**
2688    * single thread that handles display of dialogs to user.
2689    */
2690   ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2691
2692   /**
2693    * flag indicating if dialogExecutor should try to acquire a permit
2694    */
2695   private volatile boolean dialogPause = true;
2696
2697   /**
2698    * pause the queue
2699    */
2700   private java.util.concurrent.Semaphore block = new Semaphore(0);
2701
2702   /**
2703    * add another dialog thread to the queue
2704    * 
2705    * @param prompter
2706    */
2707   public void addDialogThread(final Runnable prompter)
2708   {
2709     dialogExecutor.submit(new Runnable()
2710     {
2711       public void run()
2712       {
2713         if (dialogPause)
2714         {
2715           try
2716           {
2717             block.acquire();
2718           } catch (InterruptedException x)
2719           {
2720           }
2721           ;
2722         }
2723         if (instance == null)
2724         {
2725           return;
2726         }
2727         try
2728         {
2729           SwingUtilities.invokeAndWait(prompter);
2730         } catch (Exception q)
2731         {
2732           Cache.log.warn("Unexpected Exception in dialog thread.", q);
2733         }
2734       }
2735     });
2736   }
2737
2738   public void startDialogQueue()
2739   {
2740     // set the flag so we don't pause waiting for another permit and semaphore
2741     // the current task to begin
2742     dialogPause = false;
2743     block.release();
2744   }
2745 }