Merge branch 'Release_2_8_1_Branch_i18n' into try_r20b1_merge
[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().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       progressPanel.remove(progbar);
1515       GridLayout gl = (GridLayout) progressPanel.getLayout();
1516       gl.setRows(gl.getRows() - 1);
1517       if (--totalProgressCount < 1)
1518       {
1519         this.getContentPane().remove(progressPanel);
1520         progressPanel = null;
1521       }
1522     }
1523     validate();
1524   }
1525
1526   public void stopLoading()
1527   {
1528     fileLoadingCount--;
1529     if (fileLoadingCount < 1)
1530     {
1531       for (JPanel flp : fileLoadingPanels)
1532       {
1533         removeProgressPanel(flp);
1534       }
1535       fileLoadingPanels.clear();
1536       fileLoadingCount = 0;
1537     }
1538     validate();
1539   }
1540
1541   public static int getViewCount(String alignmentId)
1542   {
1543     AlignViewport[] aps = getViewports(alignmentId);
1544     return (aps == null) ? 0 : aps.length;
1545   }
1546
1547   /**
1548    * 
1549    * @param alignmentId
1550    * @return all AlignmentPanels concerning the alignmentId sequence set
1551    */
1552   public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1553   {
1554     int count = 0;
1555     if (Desktop.desktop == null)
1556     {
1557       // no frames created and in headless mode
1558       // TODO: verify that frames are recoverable when in headless mode
1559       return null;
1560     }
1561     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1562     ArrayList aps = new ArrayList();
1563     for (int t = 0; t < frames.length; t++)
1564     {
1565       if (frames[t] instanceof AlignFrame)
1566       {
1567         AlignFrame af = (AlignFrame) frames[t];
1568         for (int a = 0; a < af.alignPanels.size(); a++)
1569         {
1570           if (alignmentId.equals(((AlignmentPanel) af.alignPanels
1571                   .elementAt(a)).av.getSequenceSetId()))
1572           {
1573             aps.add(af.alignPanels.elementAt(a));
1574           }
1575         }
1576       }
1577     }
1578     if (aps.size() == 0)
1579     {
1580       return null;
1581     }
1582     AlignmentPanel[] vap = new AlignmentPanel[aps.size()];
1583     for (int t = 0; t < vap.length; t++)
1584     {
1585       vap[t] = (AlignmentPanel) aps.get(t);
1586     }
1587     return vap;
1588   }
1589
1590   /**
1591    * get all the viewports on an alignment.
1592    * 
1593    * @param sequenceSetId
1594    *          unique alignment id
1595    * @return all viewports on the alignment bound to sequenceSetId
1596    */
1597   public static AlignViewport[] getViewports(String sequenceSetId)
1598   {
1599     Vector viewp = new Vector();
1600     if (desktop != null)
1601     {
1602       javax.swing.JInternalFrame[] frames = instance.getAllFrames();
1603
1604       for (int t = 0; t < frames.length; t++)
1605       {
1606         if (frames[t] instanceof AlignFrame)
1607         {
1608           AlignFrame afr = ((AlignFrame) frames[t]);
1609           if (afr.getViewport().getSequenceSetId().equals(sequenceSetId))
1610           {
1611             if (afr.alignPanels != null)
1612             {
1613               for (int a = 0; a < afr.alignPanels.size(); a++)
1614               {
1615                 if (sequenceSetId.equals(((AlignmentPanel) afr.alignPanels
1616                         .elementAt(a)).av.getSequenceSetId()))
1617                 {
1618                   viewp.addElement(((AlignmentPanel) afr.alignPanels
1619                           .elementAt(a)).av);
1620                 }
1621               }
1622             }
1623             else
1624             {
1625               viewp.addElement(((AlignFrame) frames[t]).getViewport());
1626             }
1627           }
1628         }
1629       }
1630       if (viewp.size() > 0)
1631       {
1632         AlignViewport[] vp = new AlignViewport[viewp.size()];
1633         viewp.copyInto(vp);
1634         return vp;
1635       }
1636     }
1637     return null;
1638   }
1639
1640   public void explodeViews(AlignFrame af)
1641   {
1642     int size = af.alignPanels.size();
1643     if (size < 2)
1644     {
1645       return;
1646     }
1647
1648     for (int i = 0; i < size; i++)
1649     {
1650       AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(i);
1651       AlignFrame newaf = new AlignFrame(ap);
1652       if (ap.av.explodedPosition != null
1653               && !ap.av.explodedPosition.equals(af.getBounds()))
1654       {
1655         newaf.setBounds(ap.av.explodedPosition);
1656       }
1657
1658       ap.av.gatherViewsHere = false;
1659
1660       addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1661               AlignFrame.DEFAULT_HEIGHT);
1662     }
1663
1664     af.alignPanels.clear();
1665     af.closeMenuItem_actionPerformed(true);
1666
1667   }
1668
1669   public void gatherViews(AlignFrame source)
1670   {
1671     source.viewport.gatherViewsHere = true;
1672     source.viewport.explodedPosition = source.getBounds();
1673     JInternalFrame[] frames = desktop.getAllFrames();
1674     String viewId = source.viewport.getSequenceSetId();
1675
1676     for (int t = 0; t < frames.length; t++)
1677     {
1678       if (frames[t] instanceof AlignFrame && frames[t] != source)
1679       {
1680         AlignFrame af = (AlignFrame) frames[t];
1681         boolean gatherThis = false;
1682         for (int a = 0; a < af.alignPanels.size(); a++)
1683         {
1684           AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(a);
1685           if (viewId.equals(ap.av.getSequenceSetId()))
1686           {
1687             gatherThis = true;
1688             ap.av.gatherViewsHere = false;
1689             ap.av.explodedPosition = af.getBounds();
1690             source.addAlignmentPanel(ap, false);
1691           }
1692         }
1693
1694         if (gatherThis)
1695         {
1696           af.alignPanels.clear();
1697           af.closeMenuItem_actionPerformed(true);
1698         }
1699       }
1700     }
1701
1702   }
1703
1704   jalview.gui.VamsasApplication v_client = null;
1705
1706   public void vamsasImport_actionPerformed(ActionEvent e)
1707   {
1708     if (v_client == null)
1709     {
1710       // Load and try to start a session.
1711       JalviewFileChooser chooser = new JalviewFileChooser(
1712               jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
1713
1714       chooser.setFileView(new JalviewFileView());
1715       chooser.setDialogTitle("Open a saved VAMSAS session");
1716       chooser.setToolTipText(MessageManager.getString("label.select_vamsas_session_opened_as_new_vamsas_session"));
1717
1718       int value = chooser.showOpenDialog(this);
1719
1720       if (value == JalviewFileChooser.APPROVE_OPTION)
1721       {
1722         String fle = chooser.getSelectedFile().toString();
1723         if (!vamsasImport(chooser.getSelectedFile()))
1724         {
1725           JOptionPane.showInternalMessageDialog(Desktop.desktop,
1726                   MessageManager.formatMessage("label.couldnt_import_as_vamsas_session", new String[]{fle}),
1727                   MessageManager.getString("label.vamsas_document_import_failed"),
1728                   JOptionPane.ERROR_MESSAGE);
1729         }
1730       }
1731     }
1732     else
1733     {
1734       jalview.bin.Cache.log
1735               .error("Implementation error - load session from a running session is not supported.");
1736     }
1737   }
1738
1739   /**
1740    * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
1741    * 
1742    * @param file
1743    * @return true if import was a success and a session was started.
1744    */
1745   public boolean vamsasImport(URL url)
1746   {
1747     // TODO: create progress bar
1748     if (v_client != null)
1749     {
1750
1751       jalview.bin.Cache.log
1752               .error("Implementation error - load session from a running session is not supported.");
1753       return false;
1754     }
1755
1756     try
1757     {
1758       // copy the URL content to a temporary local file
1759       // TODO: be a bit cleverer here with nio (?!)
1760       File file = File.createTempFile("vdocfromurl", ".vdj");
1761       FileOutputStream fos = new FileOutputStream(file);
1762       BufferedInputStream bis = new BufferedInputStream(url.openStream());
1763       byte[] buffer = new byte[2048];
1764       int ln;
1765       while ((ln = bis.read(buffer)) > -1)
1766       {
1767         fos.write(buffer, 0, ln);
1768       }
1769       bis.close();
1770       fos.close();
1771       v_client = new jalview.gui.VamsasApplication(this, file,
1772               url.toExternalForm());
1773     } catch (Exception ex)
1774     {
1775       jalview.bin.Cache.log.error(
1776               "Failed to create new vamsas session from contents of URL "
1777                       + url, ex);
1778       return false;
1779     }
1780     setupVamsasConnectedGui();
1781     v_client.initial_update(); // TODO: thread ?
1782     return v_client.inSession();
1783   }
1784
1785   /**
1786    * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
1787    * 
1788    * @param file
1789    * @return true if import was a success and a session was started.
1790    */
1791   public boolean vamsasImport(File file)
1792   {
1793     if (v_client != null)
1794     {
1795
1796       jalview.bin.Cache.log
1797               .error("Implementation error - load session from a running session is not supported.");
1798       return false;
1799     }
1800
1801     setProgressBar("Importing VAMSAS session from " + file.getName(),
1802             file.hashCode());
1803     try
1804     {
1805       v_client = new jalview.gui.VamsasApplication(this, file, null);
1806     } catch (Exception ex)
1807     {
1808       setProgressBar("Importing VAMSAS session from " + file.getName(),
1809               file.hashCode());
1810       jalview.bin.Cache.log.error(
1811               "New vamsas session from existing session file failed:", ex);
1812       return false;
1813     }
1814     setupVamsasConnectedGui();
1815     v_client.initial_update(); // TODO: thread ?
1816     setProgressBar("Importing VAMSAS session from " + file.getName(),
1817             file.hashCode());
1818     return v_client.inSession();
1819   }
1820
1821   public boolean joinVamsasSession(String mysesid)
1822   {
1823     if (v_client != null)
1824     {
1825       throw new Error(
1826               "Trying to join a vamsas session when another is already connected.");
1827     }
1828     if (mysesid == null)
1829     {
1830       throw new Error("Invalid vamsas session id.");
1831     }
1832     v_client = new VamsasApplication(this, mysesid);
1833     setupVamsasConnectedGui();
1834     v_client.initial_update();
1835     return (v_client.inSession());
1836   }
1837
1838   public void vamsasStart_actionPerformed(ActionEvent e)
1839   {
1840     if (v_client == null)
1841     {
1842       // Start a session.
1843       // we just start a default session for moment.
1844       /*
1845        * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
1846        * getProperty("LAST_DIRECTORY"));
1847        * 
1848        * chooser.setFileView(new JalviewFileView());
1849        * chooser.setDialogTitle("Load Vamsas file");
1850        * chooser.setToolTipText("Import");
1851        * 
1852        * int value = chooser.showOpenDialog(this);
1853        * 
1854        * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
1855        * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
1856        */
1857       v_client = new VamsasApplication(this);
1858       setupVamsasConnectedGui();
1859       v_client.initial_update(); // TODO: thread ?
1860     }
1861     else
1862     {
1863       // store current data in session.
1864       v_client.push_update(); // TODO: thread
1865     }
1866   }
1867
1868   protected void setupVamsasConnectedGui()
1869   {
1870     vamsasStart.setText(MessageManager.getString("label.session_update"));
1871     vamsasSave.setVisible(true);
1872     vamsasStop.setVisible(true);
1873     vamsasImport.setVisible(false); // Document import to existing session is
1874     // not possible for vamsas-client-1.0.
1875   }
1876
1877   protected void setupVamsasDisconnectedGui()
1878   {
1879     vamsasSave.setVisible(false);
1880     vamsasStop.setVisible(false);
1881     vamsasImport.setVisible(true);
1882     vamsasStart.setText(MessageManager.getString("label.new_vamsas_session"));
1883   }
1884
1885   public void vamsasStop_actionPerformed(ActionEvent e)
1886   {
1887     if (v_client != null)
1888     {
1889       v_client.end_session();
1890       v_client = null;
1891       setupVamsasDisconnectedGui();
1892     }
1893   }
1894
1895   protected void buildVamsasStMenu()
1896   {
1897     if (v_client == null)
1898     {
1899       String[] sess = null;
1900       try
1901       {
1902         sess = VamsasApplication.getSessionList();
1903       } catch (Exception e)
1904       {
1905         jalview.bin.Cache.log.warn(
1906                 "Problem getting current sessions list.", e);
1907         sess = null;
1908       }
1909       if (sess != null)
1910       {
1911         jalview.bin.Cache.log.debug("Got current sessions list: "
1912                 + sess.length + " entries.");
1913         VamsasStMenu.removeAll();
1914         for (int i = 0; i < sess.length; i++)
1915         {
1916           JMenuItem sessit = new JMenuItem();
1917           sessit.setText(sess[i]);
1918           sessit.setToolTipText(MessageManager.formatMessage("label.connect_to_session", new String[]{sess[i]}));
1919           final Desktop dsktp = this;
1920           final String mysesid = sess[i];
1921           sessit.addActionListener(new ActionListener()
1922           {
1923
1924             public void actionPerformed(ActionEvent e)
1925             {
1926               if (dsktp.v_client == null)
1927               {
1928                 Thread rthr = new Thread(new Runnable()
1929                 {
1930
1931                   public void run()
1932                   {
1933                     dsktp.v_client = new VamsasApplication(dsktp, mysesid);
1934                     dsktp.setupVamsasConnectedGui();
1935                     dsktp.v_client.initial_update();
1936                   }
1937
1938                 });
1939                 rthr.start();
1940               }
1941             };
1942           });
1943           VamsasStMenu.add(sessit);
1944         }
1945         // don't show an empty menu.
1946         VamsasStMenu.setVisible(sess.length > 0);
1947
1948       }
1949       else
1950       {
1951         jalview.bin.Cache.log.debug("No current vamsas sessions.");
1952         VamsasStMenu.removeAll();
1953         VamsasStMenu.setVisible(false);
1954       }
1955     }
1956     else
1957     {
1958       // Not interested in the content. Just hide ourselves.
1959       VamsasStMenu.setVisible(false);
1960     }
1961   }
1962
1963   public void vamsasSave_actionPerformed(ActionEvent e)
1964   {
1965     if (v_client != null)
1966     {
1967       JalviewFileChooser chooser = new JalviewFileChooser(
1968               jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
1969               { "vdj" }, // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
1970               new String[]
1971               { "Vamsas Document" }, "Vamsas Document");
1972
1973       chooser.setFileView(new JalviewFileView());
1974       chooser.setDialogTitle("Save Vamsas Document Archive");
1975
1976       int value = chooser.showSaveDialog(this);
1977
1978       if (value == JalviewFileChooser.APPROVE_OPTION)
1979       {
1980         java.io.File choice = chooser.getSelectedFile();
1981         JPanel progpanel = addProgressPanel("Saving VAMSAS Document to "
1982                 + choice.getName());
1983         jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
1984         String warnmsg = null;
1985         String warnttl = null;
1986         try
1987         {
1988           v_client.vclient.storeDocument(choice);
1989         } catch (Error ex)
1990         {
1991           warnttl = "Serious Problem saving Vamsas Document";
1992           warnmsg = ex.toString();
1993           jalview.bin.Cache.log.error("Error Whilst saving document to "
1994                   + choice, ex);
1995
1996         } catch (Exception ex)
1997         {
1998           warnttl = "Problem saving Vamsas Document.";
1999           warnmsg = ex.toString();
2000           jalview.bin.Cache.log.warn("Exception Whilst saving document to "
2001                   + choice, ex);
2002
2003         }
2004         removeProgressPanel(progpanel);
2005         if (warnmsg != null)
2006         {
2007           JOptionPane.showInternalMessageDialog(Desktop.desktop,
2008
2009           warnmsg, warnttl, JOptionPane.ERROR_MESSAGE);
2010         }
2011       }
2012     }
2013   }
2014
2015   JPanel vamUpdate = null;
2016
2017   /**
2018    * hide vamsas user gui bits when a vamsas document event is being handled.
2019    * 
2020    * @param b
2021    *          true to hide gui, false to reveal gui
2022    */
2023   public void setVamsasUpdate(boolean b)
2024   {
2025     jalview.bin.Cache.log.debug("Setting gui for Vamsas update "
2026             + (b ? "in progress" : "finished"));
2027
2028     if (vamUpdate != null)
2029     {
2030       this.removeProgressPanel(vamUpdate);
2031     }
2032     if (b)
2033     {
2034       vamUpdate = this.addProgressPanel("Updating vamsas session");
2035     }
2036     vamsasStart.setVisible(!b);
2037     vamsasStop.setVisible(!b);
2038     vamsasSave.setVisible(!b);
2039   }
2040
2041   public JInternalFrame[] getAllFrames()
2042   {
2043     return desktop.getAllFrames();
2044   }
2045
2046   /**
2047    * Checks the given url to see if it gives a response indicating that the user
2048    * should be informed of a new questionnaire.
2049    * 
2050    * @param url
2051    */
2052   public void checkForQuestionnaire(String url)
2053   {
2054     UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2055     // javax.swing.SwingUtilities.invokeLater(jvq);
2056     new Thread(jvq).start();
2057   }
2058
2059   /**
2060    * Proxy class for JDesktopPane which optionally displays the current memory
2061    * usage and highlights the desktop area with a red bar if free memory runs
2062    * low.
2063    * 
2064    * @author AMW
2065    */
2066   public class MyDesktopPane extends JDesktopPane implements Runnable
2067   {
2068
2069     boolean showMemoryUsage = false;
2070
2071     Runtime runtime;
2072
2073     java.text.NumberFormat df;
2074
2075     float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2076             percentUsage;
2077
2078     public MyDesktopPane(boolean showMemoryUsage)
2079     {
2080       showMemoryUsage(showMemoryUsage);
2081     }
2082
2083     public void showMemoryUsage(boolean showMemoryUsage)
2084     {
2085       this.showMemoryUsage = showMemoryUsage;
2086       if (showMemoryUsage)
2087       {
2088         Thread worker = new Thread(this);
2089         worker.start();
2090       }
2091     }
2092
2093     public boolean isShowMemoryUsage()
2094     {
2095       return showMemoryUsage;
2096     }
2097
2098     public void run()
2099     {
2100       df = java.text.NumberFormat.getNumberInstance();
2101       df.setMaximumFractionDigits(2);
2102       runtime = Runtime.getRuntime();
2103
2104       while (showMemoryUsage)
2105       {
2106         try
2107         {
2108           maxMemory = runtime.maxMemory() / 1048576f;
2109           allocatedMemory = runtime.totalMemory() / 1048576f;
2110           freeMemory = runtime.freeMemory() / 1048576f;
2111           totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2112
2113           percentUsage = (totalFreeMemory / maxMemory) * 100;
2114
2115           // if (percentUsage < 20)
2116           {
2117             // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2118             // Color.red);
2119             // instance.set.setBorder(border1);
2120           }
2121           repaint();
2122           // sleep after showing usage
2123           Thread.sleep(3000);
2124         } catch (Exception ex)
2125         {
2126           ex.printStackTrace();
2127         }
2128       }
2129     }
2130
2131     public void paintComponent(Graphics g)
2132     {
2133       if (showMemoryUsage && g != null && df != null)
2134       {
2135         if (percentUsage < 20)
2136           g.setColor(Color.red);
2137         FontMetrics fm = g.getFontMetrics();
2138         if (fm != null)
2139         {
2140           g.drawString(
2141                           MessageManager.formatMessage("label.memory_stats", new String[]{df.format(totalFreeMemory),df.format(maxMemory),df.format(percentUsage)}), 10,
2142                   getHeight() - fm.getHeight());
2143         }
2144       }
2145     }
2146   }
2147
2148   /**
2149    * fixes stacking order after a modal dialog to ensure windows that should be
2150    * on top actually are
2151    */
2152   public void relayerWindows()
2153   {
2154
2155   }
2156
2157   protected JMenuItem groovyShell;
2158
2159   public void doGroovyCheck()
2160   {
2161     if (jalview.bin.Cache.groovyJarsPresent())
2162     {
2163       groovyShell = new JMenuItem();
2164       groovyShell.setText(MessageManager.getString("label.groovy_console"));
2165       groovyShell.addActionListener(new ActionListener()
2166       {
2167         public void actionPerformed(ActionEvent e)
2168         {
2169           groovyShell_actionPerformed(e);
2170         }
2171       });
2172       toolsMenu.add(groovyShell);
2173       groovyShell.setVisible(true);
2174     }
2175   }
2176
2177   /**
2178    * Accessor method to quickly get all the AlignmentFrames loaded.
2179    */
2180   public static AlignFrame[] getAlignframes()
2181   {
2182     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2183
2184     if (frames == null)
2185     {
2186       return null;
2187     }
2188     Vector avp = new Vector();
2189     try
2190     {
2191       // REVERSE ORDER
2192       for (int i = frames.length - 1; i > -1; i--)
2193       {
2194         if (frames[i] instanceof AlignFrame)
2195         {
2196           AlignFrame af = (AlignFrame) frames[i];
2197           avp.addElement(af);
2198         }
2199       }
2200     } catch (Exception ex)
2201     {
2202       ex.printStackTrace();
2203     }
2204     if (avp.size() == 0)
2205     {
2206       return null;
2207     }
2208     AlignFrame afs[] = new AlignFrame[avp.size()];
2209     for (int i = 0, j = avp.size(); i < j; i++)
2210     {
2211       afs[i] = (AlignFrame) avp.elementAt(i);
2212     }
2213     avp.clear();
2214     return afs;
2215   }
2216
2217   public AppJmol[] getJmols()
2218   {
2219     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2220
2221     if (frames == null)
2222     {
2223       return null;
2224     }
2225     Vector avp = new Vector();
2226     try
2227     {
2228       // REVERSE ORDER
2229       for (int i = frames.length - 1; i > -1; i--)
2230       {
2231         if (frames[i] instanceof AppJmol)
2232         {
2233           AppJmol af = (AppJmol) frames[i];
2234           avp.addElement(af);
2235         }
2236       }
2237     } catch (Exception ex)
2238     {
2239       ex.printStackTrace();
2240     }
2241     if (avp.size() == 0)
2242     {
2243       return null;
2244     }
2245     AppJmol afs[] = new AppJmol[avp.size()];
2246     for (int i = 0, j = avp.size(); i < j; i++)
2247     {
2248       afs[i] = (AppJmol) avp.elementAt(i);
2249     }
2250     avp.clear();
2251     return afs;
2252   }
2253
2254   /**
2255    * Add Groovy Support to Jalview
2256    */
2257   public void groovyShell_actionPerformed(ActionEvent e)
2258   {
2259     // use reflection to avoid creating compilation dependency.
2260     if (!jalview.bin.Cache.groovyJarsPresent())
2261     {
2262       throw new Error(
2263               "Implementation Error. Cannot create groovyShell without Groovy on the classpath!");
2264     }
2265     try
2266     {
2267       Class gcClass = Desktop.class.getClassLoader().loadClass(
2268               "groovy.ui.Console");
2269       Constructor gccons = gcClass.getConstructor(null);
2270       java.lang.reflect.Method setvar = gcClass.getMethod("setVariable",
2271               new Class[]
2272               { String.class, Object.class });
2273       java.lang.reflect.Method run = gcClass.getMethod("run", null);
2274       Object gc = gccons.newInstance(null);
2275       setvar.invoke(gc, new Object[]
2276       { "Jalview", this });
2277       run.invoke(gc, null);
2278     } catch (Exception ex)
2279     {
2280       jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2281       JOptionPane
2282               .showInternalMessageDialog(
2283                       Desktop.desktop,
2284
2285                       "Couldn't create the groovy Shell. Check the error log for the details of what went wrong.",
2286                       "Jalview Groovy Support Failed",
2287                       JOptionPane.ERROR_MESSAGE);
2288     }
2289   }
2290
2291   /**
2292    * Progress bars managed by the IProgressIndicator method.
2293    */
2294   private Hashtable<Long, JPanel> progressBars;
2295
2296   private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2297
2298   /*
2299    * (non-Javadoc)
2300    * 
2301    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2302    */
2303   public void setProgressBar(String message, long id)
2304   {
2305     if (progressBars == null)
2306     {
2307       progressBars = new Hashtable<Long, JPanel>();
2308       progressBarHandlers = new Hashtable<Long, IProgressIndicatorHandler>();
2309     }
2310
2311     if (progressBars.get(new Long(id)) != null)
2312     {
2313       JPanel progressPanel = progressBars.remove(new Long(id));
2314       if (progressBarHandlers.contains(new Long(id)))
2315       {
2316         progressBarHandlers.remove(new Long(id));
2317       }
2318       removeProgressPanel(progressPanel);
2319     }
2320     else
2321     {
2322       progressBars.put(new Long(id), addProgressPanel(message));
2323     }
2324   }
2325
2326   /*
2327    * (non-Javadoc)
2328    * 
2329    * @see jalview.gui.IProgressIndicator#registerHandler(long,
2330    * jalview.gui.IProgressIndicatorHandler)
2331    */
2332   public void registerHandler(final long id,
2333           final IProgressIndicatorHandler handler)
2334   {
2335     if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
2336     {
2337       throw new Error(
2338               "call setProgressBar before registering the progress bar's handler.");
2339     }
2340     progressBarHandlers.put(new Long(id), handler);
2341     final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
2342     if (handler.canCancel())
2343     {
2344       JButton cancel = new JButton(MessageManager.getString("action.cancel"));
2345       final IProgressIndicator us = this;
2346       cancel.addActionListener(new ActionListener()
2347       {
2348
2349         public void actionPerformed(ActionEvent e)
2350         {
2351           handler.cancelActivity(id);
2352           us.setProgressBar(
2353                   "Cancelled "
2354                           + ((JLabel) progressPanel.getComponent(0))
2355                                   .getText(), id);
2356         }
2357       });
2358       progressPanel.add(cancel, BorderLayout.EAST);
2359     }
2360   }
2361
2362   /**
2363    * 
2364    * @return true if any progress bars are still active
2365    */
2366   @Override
2367   public boolean operationInProgress()
2368   {
2369     if (progressBars != null && progressBars.size() > 0)
2370     {
2371       return true;
2372     }
2373     return false;
2374   }
2375
2376   /**
2377    * This will return the first AlignFrame viewing AlignViewport av. It will
2378    * break if there are more than one AlignFrames viewing a particular av. This
2379    * 
2380    * @param av
2381    * @return alignFrame for av
2382    */
2383   public static AlignFrame getAlignFrameFor(AlignViewport av)
2384   {
2385     if (desktop != null)
2386     {
2387       AlignmentPanel[] aps = getAlignmentPanels(av.getSequenceSetId());
2388       for (int panel = 0; aps != null && panel < aps.length; panel++)
2389       {
2390         if (aps[panel] != null && aps[panel].av == av)
2391         {
2392           return aps[panel].alignFrame;
2393         }
2394       }
2395     }
2396     return null;
2397   }
2398
2399   public VamsasApplication getVamsasApplication()
2400   {
2401     return v_client;
2402
2403   }
2404
2405   /**
2406    * flag set if jalview GUI is being operated programmatically
2407    */
2408   private boolean inBatchMode = false;
2409
2410   /**
2411    * check if jalview GUI is being operated programmatically
2412    * 
2413    * @return inBatchMode
2414    */
2415   public boolean isInBatchMode()
2416   {
2417     return inBatchMode;
2418   }
2419
2420   /**
2421    * set flag if jalview GUI is being operated programmatically
2422    * 
2423    * @param inBatchMode
2424    */
2425   public void setInBatchMode(boolean inBatchMode)
2426   {
2427     this.inBatchMode = inBatchMode;
2428   }
2429
2430   public void startServiceDiscovery()
2431   {
2432     startServiceDiscovery(false);
2433   }
2434
2435   public void startServiceDiscovery(boolean blocking)
2436   {
2437     boolean alive = true;
2438     Thread t0 = null, t1 = null, t2 = null;
2439     // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2440     if (true)
2441     {
2442     // todo: changesupport handlers need to be transferred
2443     if (discoverer == null)
2444     {
2445       discoverer = new jalview.ws.jws1.Discoverer();
2446       // register PCS handler for desktop.
2447       discoverer.addPropertyChangeListener(changeSupport);
2448     }
2449     // JAL-940 - disabled JWS1 service configuration - always start discoverer
2450     // until we phase out completely
2451       (t0 = new Thread(discoverer)).start();
2452     }
2453
2454     // ENFIN services are EOLed as of Jalview 2.8.1 release
2455     if (false)
2456     {
2457       try
2458       {
2459         if (Cache.getDefault("SHOW_ENFIN_SERVICES", true))
2460         {
2461           // EnfinEnvision web service menu entries are rebuild every time the
2462           // menu is shown, so no changeSupport events are needed.
2463           jalview.ws.EnfinEnvision2OneWay.getInstance();
2464           (t1 = new Thread(jalview.ws.EnfinEnvision2OneWay.getInstance()))
2465                   .start();
2466         }
2467       } catch (Exception e)
2468       {
2469         Cache.log
2470                 .info("Exception when trying to launch Envision2 workflow discovery.",
2471                         e);
2472         Cache.log.info(e.getStackTrace());
2473       }
2474     }
2475
2476     if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2477     {
2478       if (jalview.ws.jws2.Jws2Discoverer.getDiscoverer().isRunning())
2479       {
2480         jalview.ws.jws2.Jws2Discoverer.getDiscoverer().setAborted(true);
2481       }
2482       t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer().startDiscoverer(
2483               changeSupport);
2484
2485     }
2486     Thread t3 = null;
2487     {
2488       // TODO: do rest service discovery
2489     }
2490     if (blocking)
2491     {
2492       while (alive)
2493       {
2494         try
2495         {
2496           Thread.sleep(15);
2497         } catch (Exception e)
2498         {
2499         }
2500         alive = (t1 != null && t1.isAlive())
2501                 || (t2 != null && t2.isAlive())
2502                 || (t3 != null && t3.isAlive())
2503                 || (t0 != null && t0.isAlive());
2504       }
2505     }
2506   }
2507
2508   /**
2509    * called to check if the service discovery process completed successfully.
2510    * 
2511    * @param evt
2512    */
2513   protected void JalviewServicesChanged(PropertyChangeEvent evt)
2514   {
2515     if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2516     {
2517       final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2518               .getErrorMessages();
2519       if (ermsg != null)
2520       {
2521         if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2522         {
2523           if (serviceChangedDialog == null)
2524           {
2525             // only run if we aren't already displaying one of these.
2526             addDialogThread(serviceChangedDialog = new Runnable()
2527             {
2528               public void run()
2529               {
2530
2531                 /*
2532                  * JalviewDialog jd =new JalviewDialog() {
2533                  * 
2534                  * @Override protected void cancelPressed() { // TODO
2535                  * Auto-generated method stub
2536                  * 
2537                  * }@Override protected void okPressed() { // TODO
2538                  * Auto-generated method stub
2539                  * 
2540                  * }@Override protected void raiseClosed() { // TODO
2541                  * Auto-generated method stub
2542                  * 
2543                  * } }; jd.initDialogFrame(new
2544                  * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2545                  * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2546                  * + " or mis-configured HTTP proxy settings.<br/>" +
2547                  * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2548                  * +
2549                  * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2550                  * ), true, true, "Web Service Configuration Problem", 450,
2551                  * 400);
2552                  * 
2553                  * jd.waitForInput();
2554                  */
2555                 JOptionPane
2556                         .showConfirmDialog(
2557                                 Desktop.desktop,
2558                                 new JLabel(
2559                                         "<html><table width=\"450\"><tr><td>"
2560                                                 + ermsg
2561                                                 + "</td></tr></table>"
2562                                                 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2563                                                 + " or mis-configured HTTP proxy settings.</p>"
2564                                                 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2565                                                 + " Tools->Preferences dialog box to change them.</p></html>"),
2566                                 "Web Service Configuration Problem",
2567                                 JOptionPane.DEFAULT_OPTION,
2568                                 JOptionPane.ERROR_MESSAGE);
2569                 serviceChangedDialog = null;
2570
2571               }
2572             });
2573           }
2574         }
2575         else
2576         {
2577           Cache.log
2578                   .error("Errors reported by JABA discovery service. Check web services preferences.\n"
2579                           + ermsg);
2580         }
2581       }
2582     }
2583   }
2584
2585   private Runnable serviceChangedDialog = null;
2586
2587   /**
2588    * start a thread to open a URL in the configured browser. Pops up a warning
2589    * dialog to the user if there is an exception when calling out to the browser
2590    * to open the URL.
2591    * 
2592    * @param url
2593    */
2594   public static void showUrl(final String url)
2595   {
2596     showUrl(url, Desktop.instance);
2597   }
2598
2599   /**
2600    * Like showUrl but allows progress handler to be specified
2601    * 
2602    * @param url
2603    * @param progress
2604    *          (null) or object implementing IProgressIndicator
2605    */
2606   public static void showUrl(final String url,
2607           final IProgressIndicator progress)
2608   {
2609     new Thread(new Runnable()
2610     {
2611       public void run()
2612       {
2613         try
2614         {
2615           if (progress != null)
2616           {
2617             progress.setProgressBar("Opening " + url, this.hashCode());
2618           }
2619           jalview.util.BrowserLauncher.openURL(url);
2620         } catch (Exception ex)
2621         {
2622           JOptionPane
2623                   .showInternalMessageDialog(
2624                           Desktop.desktop,
2625                           "Unixers: Couldn't find default web browser."
2626                                   + "\nAdd the full path to your browser in Preferences.",
2627                           "Web browser not found",
2628                           JOptionPane.WARNING_MESSAGE);
2629
2630           ex.printStackTrace();
2631         }
2632         if (progress != null)
2633         {
2634           progress.setProgressBar(null, this.hashCode());
2635         }
2636       }
2637     }).start();
2638   }
2639
2640   public static WsParamSetManager wsparamManager = null;
2641
2642   public static ParamManager getUserParameterStore()
2643   {
2644     if (wsparamManager == null)
2645     {
2646       wsparamManager = new WsParamSetManager();
2647     }
2648     return wsparamManager;
2649   }
2650
2651   /**
2652    * static hyperlink handler proxy method for use by Jalview's internal windows
2653    * 
2654    * @param e
2655    */
2656   public static void hyperlinkUpdate(HyperlinkEvent e)
2657   {
2658     if (e.getEventType() == EventType.ACTIVATED)
2659     {
2660       String url = null;
2661       try
2662       {
2663         url = e.getURL().toString();
2664         Desktop.showUrl(url);
2665       } catch (Exception x)
2666       {
2667         if (url != null)
2668         {
2669           if (Cache.log != null)
2670           {
2671             Cache.log.error("Couldn't handle string " + url + " as a URL.");
2672           }
2673           else
2674           {
2675             System.err.println("Couldn't handle string " + url
2676                     + " as a URL.");
2677           }
2678         }
2679         // ignore any exceptions due to dud links.
2680       }
2681
2682     }
2683   }
2684
2685   /**
2686    * single thread that handles display of dialogs to user.
2687    */
2688   ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2689
2690   /**
2691    * flag indicating if dialogExecutor should try to acquire a permit
2692    */
2693   private volatile boolean dialogPause = true;
2694
2695   /**
2696    * pause the queue
2697    */
2698   private java.util.concurrent.Semaphore block = new Semaphore(0);
2699
2700   /**
2701    * add another dialog thread to the queue
2702    * 
2703    * @param prompter
2704    */
2705   public void addDialogThread(final Runnable prompter)
2706   {
2707     dialogExecutor.submit(new Runnable()
2708     {
2709       public void run()
2710       {
2711         if (dialogPause)
2712         {
2713           try
2714           {
2715             block.acquire();
2716           } catch (InterruptedException x)
2717           {
2718           }
2719           ;
2720         }
2721         if (instance == null)
2722         {
2723           return;
2724         }
2725         try
2726         {
2727           SwingUtilities.invokeAndWait(prompter);
2728         } catch (Exception q)
2729         {
2730           Cache.log.warn("Unexpected Exception in dialog thread.", q);
2731         }
2732       }
2733     });
2734   }
2735
2736   public void startDialogQueue()
2737   {
2738     // set the flag so we don't pause waiting for another permit and semaphore
2739     // the current task to begin
2740     dialogPause = false;
2741     block.release();
2742   }
2743 }