Jalview 2.6 source licence
[jalview.git] / src / jalview / gui / Desktop.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6)
3  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
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  */
18 package jalview.gui;
19
20 import jalview.bin.Cache;
21 import jalview.io.*;
22 import jalview.ws.params.ParamManager;
23
24 import java.awt.*;
25 import java.awt.datatransfer.*;
26 import java.awt.dnd.*;
27 import java.awt.event.*;
28 import java.beans.PropertyChangeEvent;
29 import java.beans.PropertyChangeListener;
30 import java.io.BufferedInputStream;
31 import java.io.BufferedOutputStream;
32 import java.io.File;
33 import java.io.FileOutputStream;
34 import java.io.InputStream;
35 import java.lang.reflect.Constructor;
36 import java.net.URL;
37 import java.net.URLConnection;
38 import java.nio.channels.ReadableByteChannel;
39 import java.util.*;
40
41 import javax.swing.*;
42 import javax.swing.event.MenuEvent;
43 import javax.swing.event.MenuListener;
44
45 /**
46  * Jalview Desktop
47  * 
48  * 
49  * @author $author$
50  * @version $Revision$
51  */
52 public class Desktop extends jalview.jbgui.GDesktop implements
53         DropTargetListener, ClipboardOwner, IProgressIndicator
54 {
55
56   private class JalviewChangeSupport implements PropertyChangeListener
57   {
58     public void propertyChange(PropertyChangeEvent evt)
59     {
60       // Handle change events - most are simply routed to other sources
61       changeSupport.firePropertyChange(evt);
62     }
63
64     /**
65      * change listeners are notified of changes to resources so they can update
66      * their state. E.g. - the 'services' property notifies when the available
67      * set of web service endpoints have changed.
68      */
69     private java.beans.PropertyChangeSupport changeSupport = new java.beans.PropertyChangeSupport(
70             this);
71
72     /**
73      * @param propertyName
74      * @param listener
75      * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(java.lang.String,
76      *      java.beans.PropertyChangeListener)
77      */
78     public void addJalviewPropertyChangeListener(String propertyName,
79             PropertyChangeListener listener)
80     {
81       changeSupport.addPropertyChangeListener(propertyName, listener);
82     }
83
84     /**
85      * @param listener
86      * @see java.beans.PropertyChangeSupport#addPropertyChangeListener(
87      *      java.beans.PropertyChangeListener)
88      */
89     public void addJalviewPropertyChangeListener(
90             PropertyChangeListener listener)
91     {
92       changeSupport.addPropertyChangeListener(listener);
93     }
94
95     /*
96      * @param propertyName
97      * 
98      * @param oldValue
99      * 
100      * @param newValue
101      * 
102      * @see
103      * java.beans.PropertyChangeSupport#firePropertyChange(java.lang.String,
104      * java.lang.Object, java.lang.Object) public void firePropertyChange(String
105      * propertyName, Object oldValue, Object newValue) {
106      * changeSupport.firePropertyChange(propertyName, oldValue, newValue); }
107      */
108
109     /**
110      * @param propertyName
111      * @param listener
112      * @see java.beans.PropertyChangeSupport#removePropertyChangeListener(java.lang.String,
113      *      java.beans.PropertyChangeListener)
114      */
115     public void removeJalviewPropertyChangeListener(String propertyName,
116             PropertyChangeListener listener)
117     {
118       changeSupport.removePropertyChangeListener(propertyName, listener);
119     }
120
121   }
122
123   private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
124
125   /**
126    * @param listener
127    * @see jalview.gui.Desktop.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
128    */
129   public void addJalviewPropertyChangeListener(
130           PropertyChangeListener listener)
131   {
132     changeSupport.addJalviewPropertyChangeListener(listener);
133   }
134
135   /**
136    * @param propertyName
137    * @param listener
138    * @see jalview.gui.Desktop.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
139    *      java.beans.PropertyChangeListener)
140    */
141   public void addJalviewPropertyChangeListener(String propertyName,
142           PropertyChangeListener listener)
143   {
144     changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
145   }
146
147   /**
148    * @param propertyName
149    * @param listener
150    * @see jalview.gui.Desktop.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
151    *      java.beans.PropertyChangeListener)
152    */
153   public void removeJalviewPropertyChangeListener(String propertyName,
154           PropertyChangeListener listener)
155   {
156     changeSupport.removeJalviewPropertyChangeListener(propertyName,
157             listener);
158   }
159
160   /** Singleton Desktop instance */
161   public static Desktop instance;
162
163   public static MyDesktopPane desktop;
164
165   static int openFrameCount = 0;
166
167   static final int xOffset = 30;
168
169   static final int yOffset = 30;
170
171   public static jalview.ws.jws1.Discoverer discoverer;
172
173   public static Object[] jalviewClipboard;
174
175   public static boolean internalCopy = false;
176
177   static int fileLoadingCount = 0;
178
179   class MyDesktopManager implements DesktopManager
180   {
181
182     private DesktopManager delegate;
183
184     public MyDesktopManager(DesktopManager delegate)
185     {
186       this.delegate = delegate;
187     }
188
189     public void activateFrame(JInternalFrame f)
190     {
191       try
192       {
193         delegate.activateFrame(f);
194       } catch (NullPointerException npe)
195       {
196         Point p = getMousePosition();
197         instance.showPasteMenu(p.x, p.y);
198       }
199     }
200
201     public void beginDraggingFrame(JComponent f)
202     {
203       delegate.beginDraggingFrame(f);
204     }
205
206     public void beginResizingFrame(JComponent f, int direction)
207     {
208       delegate.beginResizingFrame(f, direction);
209     }
210
211     public void closeFrame(JInternalFrame f)
212     {
213       delegate.closeFrame(f);
214     }
215
216     public void deactivateFrame(JInternalFrame f)
217     {
218       delegate.deactivateFrame(f);
219     }
220
221     public void deiconifyFrame(JInternalFrame f)
222     {
223       delegate.deiconifyFrame(f);
224     }
225
226     public void dragFrame(JComponent f, int newX, int newY)
227     {
228       delegate.dragFrame(f, newX, newY);
229     }
230
231     public void endDraggingFrame(JComponent f)
232     {
233       delegate.endDraggingFrame(f);
234     }
235
236     public void endResizingFrame(JComponent f)
237     {
238       delegate.endResizingFrame(f);
239     }
240
241     public void iconifyFrame(JInternalFrame f)
242     {
243       delegate.iconifyFrame(f);
244     }
245
246     public void maximizeFrame(JInternalFrame f)
247     {
248       delegate.maximizeFrame(f);
249     }
250
251     public void minimizeFrame(JInternalFrame f)
252     {
253       delegate.minimizeFrame(f);
254     }
255
256     public void openFrame(JInternalFrame f)
257     {
258       delegate.openFrame(f);
259     }
260
261     public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
262             int newHeight)
263     {
264       delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
265     }
266
267     public void setBoundsForFrame(JComponent f, int newX, int newY,
268             int newWidth, int newHeight)
269     {
270       delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
271     }
272
273     // All other methods, simply delegate
274
275   }
276
277   /**
278    * Creates a new Desktop object.
279    */
280   public Desktop()
281   {
282     /**
283      * A note to implementors. It is ESSENTIAL that any activities that might
284      * block are spawned off as threads rather than waited for during this
285      * constructor.
286      */
287     instance = this;
288     doVamsasClientCheck();
289     doGroovyCheck();
290
291     setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
292     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
293     boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
294             false);
295     boolean showjconsole = jalview.bin.Cache.getDefault(
296             "SHOW_JAVA_CONSOLE", false);
297     desktop = new MyDesktopPane(selmemusage);
298     showMemusage.setSelected(selmemusage);
299     desktop.setBackground(Color.white);
300     getContentPane().setLayout(new BorderLayout());
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     Rectangle dims = getLastKnownDimensions("");
309     if (dims != null)
310     {
311       setBounds(dims);
312     }
313     else
314     {
315       Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
316       setBounds((int) (screenSize.width - 900) / 2,
317               (int) (screenSize.height - 650) / 2, 900, 650);
318     }
319     jconsole = new Console(this, showjconsole);
320     // add essential build information
321     jconsole.setHeader("Jalview Desktop "
322             + jalview.bin.Cache.getProperty("VERSION") + "\n"
323             + "Build Date: "
324             + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
325             + "Java version: " + System.getProperty("java.version") + "\n"
326             + System.getProperty("os.arch") + " "
327             + System.getProperty("os.name") + " "
328             + System.getProperty("os.version"));
329
330     showConsole(showjconsole);
331
332     this.addWindowListener(new WindowAdapter()
333     {
334       public void windowClosing(WindowEvent evt)
335       {
336         quit();
337       }
338     });
339
340     this.addMouseListener(new MouseAdapter()
341     {
342       public void mousePressed(MouseEvent evt)
343       {
344         if (SwingUtilities.isRightMouseButton(evt))
345         {
346           showPasteMenu(evt.getX(), evt.getY());
347         }
348       }
349     });
350
351     this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
352     // Spawn a thread that shows the splashscreen
353     SwingUtilities.invokeLater(new Runnable()
354     {
355       public void run()
356       {
357         new SplashScreen();
358       }
359     });
360
361     // displayed.
362     // Thread off a new instance of the file chooser - this reduces the time it
363     // takes to open it later on.
364     new Thread(new Runnable()
365     {
366       public void run()
367       {
368         Cache.log.debug("Filechooser init thread started.");
369         JalviewFileChooser chooser = new JalviewFileChooser(
370                 jalview.bin.Cache.getProperty("LAST_DIRECTORY"),
371                 jalview.io.AppletFormatAdapter.READABLE_EXTENSIONS,
372                 jalview.io.AppletFormatAdapter.READABLE_FNAMES,
373                 jalview.bin.Cache.getProperty("DEFAULT_FILE_FORMAT"));
374         Cache.log.debug("Filechooser init thread finished.");
375       }
376     }).start();
377   }
378
379   /**
380    * recover the last known dimensions for a jalview window
381    * 
382    * @param windowName
383    *          - empty string is desktop, all other windows have unique prefix
384    * @return null or last known dimensions scaled to current geometry (if last
385    *         window geom was known)
386    */
387   Rectangle getLastKnownDimensions(String windowName)
388   {
389     // TODO: lock aspect ratio for scaling desktop Bug #0058199
390     Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
391     String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
392     String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
393     String width = jalview.bin.Cache.getProperty(windowName
394             + "SCREEN_WIDTH");
395     String height = jalview.bin.Cache.getProperty(windowName
396             + "SCREEN_HEIGHT");
397     if ((x != null) && (y != null) && (width != null) && (height != null))
398     {
399       int ix = Integer.parseInt(x), iy = Integer.parseInt(y), iw = Integer
400               .parseInt(width), ih = Integer.parseInt(height);
401       if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
402       {
403         // attempt #1 - try to cope with change in screen geometry - this
404         // version doesn't preserve original jv aspect ratio.
405         // take ratio of current screen size vs original screen size.
406         double sw = ((1f * screenSize.width) / (1f * Integer
407                 .parseInt(jalview.bin.Cache
408                         .getProperty("SCREENGEOMETRY_WIDTH"))));
409         double sh = ((1f * screenSize.height) / (1f * Integer
410                 .parseInt(jalview.bin.Cache
411                         .getProperty("SCREENGEOMETRY_HEIGHT"))));
412         // rescale the bounds depending upon the current screen geometry.
413         ix = (int) (ix * sw);
414         iw = (int) (iw * sw);
415         iy = (int) (iy * sh);
416         ih = (int) (ih * sh);
417         while (ix>=screenSize.width) {
418           jalview.bin.Cache.log.debug("Window geometry location recall error: shifting horizontal to within screenbounds.");
419           ix-=screenSize.width;
420         }
421         while (iy>=screenSize.height) {
422           jalview.bin.Cache.log.debug("Window geometry location recall error: shifting vertical to within screenbounds.");
423                     iy-=screenSize.height;
424         }
425         jalview.bin.Cache.log.debug("Got last known dimensions for "
426                 + windowName + ": x:" + ix + " y:" + iy + " width:" + iw
427                 + " height:" + ih);
428       }
429       // return dimensions for new instance
430       return new Rectangle(ix, iy, iw, ih);
431     }
432     return null;
433   }
434
435   private void doVamsasClientCheck()
436   {
437     if (jalview.bin.Cache.vamsasJarsPresent())
438     {
439       setupVamsasDisconnectedGui();
440       VamsasMenu.setVisible(true);
441       final Desktop us = this;
442       VamsasMenu.addMenuListener(new MenuListener()
443       {
444         // this listener remembers when the menu was first selected, and
445         // doesn't rebuild the session list until it has been cleared and
446         // reselected again.
447         boolean refresh = true;
448
449         public void menuCanceled(MenuEvent e)
450         {
451           refresh = true;
452         }
453
454         public void menuDeselected(MenuEvent e)
455         {
456           refresh = true;
457         }
458
459         public void menuSelected(MenuEvent e)
460         {
461           if (refresh)
462           {
463             us.buildVamsasStMenu();
464             refresh = false;
465           }
466         }
467       });
468       vamsasStart.setVisible(true);
469     }
470   }
471
472   void showPasteMenu(int x, int y)
473   {
474     JPopupMenu popup = new JPopupMenu();
475     JMenuItem item = new JMenuItem("Paste To New Window");
476     item.addActionListener(new ActionListener()
477     {
478       public void actionPerformed(ActionEvent evt)
479       {
480         paste();
481       }
482     });
483
484     popup.add(item);
485     popup.show(this, x, y);
486   }
487
488   public void paste()
489   {
490     try
491     {
492       Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
493       Transferable contents = c.getContents(this);
494
495       if (contents != null)
496       {
497         String file = (String) contents
498                 .getTransferData(DataFlavor.stringFlavor);
499
500         String format = new IdentifyFile().Identify(file,
501                 FormatAdapter.PASTE);
502
503         new FileLoader().LoadFile(file, FormatAdapter.PASTE, format);
504
505       }
506     } catch (Exception ex)
507     {
508       System.out
509               .println("Unable to paste alignment from system clipboard:\n"
510                       + ex);
511     }
512   }
513
514   /**
515    * Adds and opens the given frame to the desktop
516    * 
517    * @param frame
518    *          DOCUMENT ME!
519    * @param title
520    *          DOCUMENT ME!
521    * @param w
522    *          DOCUMENT ME!
523    * @param h
524    *          DOCUMENT ME!
525    */
526   public static synchronized void addInternalFrame(
527           final JInternalFrame frame, String title, int w, int h)
528   {
529     addInternalFrame(frame, title, w, h, true);
530   }
531
532   /**
533    * DOCUMENT ME!
534    * 
535    * @param frame
536    *          DOCUMENT ME!
537    * @param title
538    *          DOCUMENT ME!
539    * @param w
540    *          DOCUMENT ME!
541    * @param h
542    *          DOCUMENT ME!
543    * @param resizable
544    *          DOCUMENT ME!
545    */
546   public static synchronized void addInternalFrame(
547           final JInternalFrame frame, String title, int w, int h,
548           boolean resizable)
549   {
550
551     // TODO: allow callers to determine X and Y position of frame (eg. via
552     // bounds object).
553     // TODO: consider fixing method to update entries in the window submenu with
554     // the current window title
555
556     frame.setTitle(title);
557     if (frame.getWidth() < 1 || frame.getHeight() < 1)
558     {
559       frame.setSize(w, h);
560     }
561     // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
562     // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
563     // IF JALVIEW IS RUNNING HEADLESS
564     // ///////////////////////////////////////////////
565     if (System.getProperty("java.awt.headless") != null
566             && System.getProperty("java.awt.headless").equals("true"))
567     {
568       return;
569     }
570
571     openFrameCount++;
572
573     frame.setVisible(true);
574     frame.setClosable(true);
575     frame.setResizable(resizable);
576     frame.setMaximizable(resizable);
577     frame.setIconifiable(resizable);
578     frame.setFrameIcon(null);
579
580     if (frame.getX() < 1 && frame.getY() < 1)
581     {
582       frame.setLocation(xOffset * openFrameCount, yOffset
583               * ((openFrameCount - 1) % 10) + yOffset);
584     }
585
586     final JMenuItem menuItem = new JMenuItem(title);
587     frame
588             .addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
589             {
590               public void internalFrameActivated(
591                       javax.swing.event.InternalFrameEvent evt)
592               {
593                 JInternalFrame itf = desktop.getSelectedFrame();
594                 if (itf != null)
595                 {
596                   itf.requestFocus();
597                 }
598
599               }
600
601               public void internalFrameClosed(
602                       javax.swing.event.InternalFrameEvent evt)
603               {
604                 PaintRefresher.RemoveComponent(frame);
605                 openFrameCount--;
606                 windowMenu.remove(menuItem);
607                 JInternalFrame itf = desktop.getSelectedFrame();
608                 if (itf != null)
609                 {
610                   itf.requestFocus();
611                 }
612                 System.gc();
613               };
614             });
615
616     menuItem.addActionListener(new ActionListener()
617     {
618       public void actionPerformed(ActionEvent e)
619       {
620         try
621         {
622           frame.setSelected(true);
623           frame.setIcon(false);
624         } catch (java.beans.PropertyVetoException ex)
625         {
626
627         }
628       }
629     });
630
631     windowMenu.add(menuItem);
632
633     desktop.add(frame);
634     frame.toFront();
635     try
636     {
637       frame.setSelected(true);
638       frame.requestFocus();
639     } catch (java.beans.PropertyVetoException ve)
640     {
641     }
642   }
643
644   public void lostOwnership(Clipboard clipboard, Transferable contents)
645   {
646     if (!internalCopy)
647     {
648       Desktop.jalviewClipboard = null;
649     }
650
651     internalCopy = false;
652   }
653
654   public void dragEnter(DropTargetDragEvent evt)
655   {
656   }
657
658   public void dragExit(DropTargetEvent evt)
659   {
660   }
661
662   public void dragOver(DropTargetDragEvent evt)
663   {
664   }
665
666   public void dropActionChanged(DropTargetDragEvent evt)
667   {
668   }
669
670   /**
671    * DOCUMENT ME!
672    * 
673    * @param evt
674    *          DOCUMENT ME!
675    */
676   public void drop(DropTargetDropEvent evt)
677   {
678     Transferable t = evt.getTransferable();
679     java.util.List files = null;
680     java.util.List protocols = null;
681
682     try
683     {
684       DataFlavor uriListFlavor = new DataFlavor(
685               "text/uri-list;class=java.lang.String");
686       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
687       {
688         // Works on Windows and MacOSX
689         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
690         files = (java.util.List) t
691                 .getTransferData(DataFlavor.javaFileListFlavor);
692       }
693       else if (t.isDataFlavorSupported(uriListFlavor))
694       {
695         // This is used by Unix drag system
696         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
697         String data = (String) t.getTransferData(uriListFlavor);
698         files = new java.util.ArrayList(1);
699         protocols = new java.util.ArrayList(1);
700         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
701                 data, "\r\n"); st.hasMoreTokens();)
702         {
703           String s = st.nextToken();
704           if (s.startsWith("#"))
705           {
706             // the line is a comment (as per the RFC 2483)
707             continue;
708           }
709           java.net.URI uri = new java.net.URI(s);
710           if (uri.getScheme().toLowerCase().startsWith("http"))
711           {
712             protocols.add(FormatAdapter.URL);
713             files.add(uri.toString());
714           }
715           else
716           {
717             // otherwise preserve old behaviour: catch all for file objects
718             java.io.File file = new java.io.File(uri);
719             protocols.add(FormatAdapter.FILE);
720             files.add(file.toString());
721           }
722         }
723       }
724     } catch (Exception e)
725     {
726     }
727
728     if (files != null)
729     {
730       try
731       {
732         for (int i = 0; i < files.size(); i++)
733         {
734           String file = files.get(i).toString();
735           String protocol = (protocols == null) ? FormatAdapter.FILE
736                   : (String) protocols.get(i);
737           String format = null;
738
739           if (file.endsWith(".jar"))
740           {
741             format = "Jalview";
742
743           }
744           else
745           {
746             format = new IdentifyFile().Identify(file, protocol);
747           }
748
749           new FileLoader().LoadFile(file, protocol, format);
750
751         }
752       } catch (Exception ex)
753       {
754       }
755     }
756   }
757
758   /**
759    * DOCUMENT ME!
760    * 
761    * @param e
762    *          DOCUMENT ME!
763    */
764   public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
765   {
766     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
767             .getProperty("LAST_DIRECTORY"),
768             jalview.io.AppletFormatAdapter.READABLE_EXTENSIONS,
769             jalview.io.AppletFormatAdapter.READABLE_FNAMES,
770             jalview.bin.Cache.getProperty("DEFAULT_FILE_FORMAT"));
771
772     chooser.setFileView(new JalviewFileView());
773     chooser.setDialogTitle("Open local file");
774     chooser.setToolTipText("Open");
775
776     int value = chooser.showOpenDialog(this);
777
778     if (value == JalviewFileChooser.APPROVE_OPTION)
779     {
780       String choice = chooser.getSelectedFile().getPath();
781       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
782               .getSelectedFile().getParent());
783
784       String format = null;
785       if (chooser.getSelectedFormat().equals("Jalview"))
786       {
787         format = "Jalview";
788       }
789       else
790       {
791         format = new IdentifyFile().Identify(choice, FormatAdapter.FILE);
792       }
793
794       if (viewport != null)
795       {
796         new FileLoader().LoadFile(viewport, choice, FormatAdapter.FILE,
797                 format);
798       }
799       else
800       {
801         new FileLoader().LoadFile(choice, FormatAdapter.FILE, format);
802       }
803     }
804   }
805
806   /**
807    * DOCUMENT ME!
808    * 
809    * @param e
810    *          DOCUMENT ME!
811    */
812   public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
813   {
814     // This construct allows us to have a wider textfield
815     // for viewing
816     JLabel label = new JLabel("Enter URL of Input File");
817     final JComboBox history = new JComboBox();
818
819     JPanel panel = new JPanel(new GridLayout(2, 1));
820     panel.add(label);
821     panel.add(history);
822     history.setPreferredSize(new Dimension(400, 20));
823     history.setEditable(true);
824     history.addItem("http://www.");
825
826     String historyItems = jalview.bin.Cache.getProperty("RECENT_URL");
827
828     StringTokenizer st;
829
830     if (historyItems != null)
831     {
832       st = new StringTokenizer(historyItems, "\t");
833
834       while (st.hasMoreTokens())
835       {
836         history.addItem(st.nextElement());
837       }
838     }
839
840     int reply = JOptionPane.showInternalConfirmDialog(desktop, panel,
841             "Input Alignment From URL", JOptionPane.OK_CANCEL_OPTION);
842
843     if (reply != JOptionPane.OK_OPTION)
844     {
845       return;
846     }
847
848     String url = history.getSelectedItem().toString();
849
850     if (url.toLowerCase().endsWith(".jar"))
851     {
852       if (viewport != null)
853       {
854         new FileLoader().LoadFile(viewport, url, FormatAdapter.URL,
855                 "Jalview");
856       }
857       else
858       {
859         new FileLoader().LoadFile(url, FormatAdapter.URL, "Jalview");
860       }
861     }
862     else
863     {
864       String format = new IdentifyFile().Identify(url, FormatAdapter.URL);
865
866       if (format.equals("URL NOT FOUND"))
867       {
868         JOptionPane.showInternalMessageDialog(Desktop.desktop,
869                 "Couldn't locate " + url, "URL not found",
870                 JOptionPane.WARNING_MESSAGE);
871
872         return;
873       }
874
875       if (viewport != null)
876       {
877         new FileLoader().LoadFile(viewport, url, FormatAdapter.URL, format);
878       }
879       else
880       {
881         new FileLoader().LoadFile(url, FormatAdapter.URL, format);
882       }
883     }
884   }
885
886   /**
887    * DOCUMENT ME!
888    * 
889    * @param e
890    *          DOCUMENT ME!
891    */
892   public void inputTextboxMenuItem_actionPerformed(AlignViewport viewport)
893   {
894     CutAndPasteTransfer cap = new CutAndPasteTransfer();
895     cap.setForInput(viewport);
896     Desktop.addInternalFrame(cap, "Cut & Paste Alignment File", 600, 500);
897   }
898
899   /*
900    * Exit the program
901    */
902   public void quit()
903   {
904     Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
905     jalview.bin.Cache
906             .setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
907     jalview.bin.Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height
908             + "");
909     storeLastKnownDimensions("", new Rectangle(getBounds().x,
910             getBounds().y, getWidth(), getHeight()));
911
912     if (jconsole != null)
913     {
914       storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
915       jconsole.stopConsole();
916     }
917     System.exit(0);
918   }
919
920   private void storeLastKnownDimensions(String string, Rectangle jc)
921   {
922     jalview.bin.Cache.log.debug("Storing last known dimensions for "
923             + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
924             + " height:" + jc.height);
925
926     jalview.bin.Cache.setProperty(string + "SCREEN_X", jc.x + "");
927     jalview.bin.Cache.setProperty(string + "SCREEN_Y", jc.y + "");
928     jalview.bin.Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
929     jalview.bin.Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
930   }
931
932   /**
933    * DOCUMENT ME!
934    * 
935    * @param e
936    *          DOCUMENT ME!
937    */
938   public void aboutMenuItem_actionPerformed(ActionEvent e)
939   {
940     StringBuffer message = new StringBuffer("Jalview version "
941             + jalview.bin.Cache.getProperty("VERSION") + "; last updated: "
942             + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
943
944     if (!jalview.bin.Cache.getProperty("LATEST_VERSION").equals(
945             jalview.bin.Cache.getProperty("VERSION")))
946     {
947       message
948               .append("\n\n!! Jalview version "
949                       + jalview.bin.Cache.getProperty("LATEST_VERSION")
950                       + " is available for download from http://www.jalview.org !!\n");
951
952     }
953     // TODO: update this text for each release or centrally store it for lite
954     // and application
955     message
956             .append("\nAuthors:  Andrew Waterhouse, Jim Procter, Michele Clamp, James Cuff, Steve Searle,\n    David Martin & Geoff Barton."
957                     + "\nDevelopment managed by The Barton Group, University of Dundee, Scotland, UK.\n"
958                     + "\nFor help, see the FAQ at www.jalview.org and/or join the jalview-discuss@jalview.org mailing list\n"
959                     + "\nIf  you use Jalview, please cite:"
960                     + "\nWaterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
961                     + "\nJalview Version 2 - a multiple sequence alignment editor and analysis workbench"
962                     + "\nBioinformatics doi: 10.1093/bioinformatics/btp033");
963     JOptionPane.showInternalMessageDialog(Desktop.desktop,
964
965     message.toString(), "About Jalview", JOptionPane.INFORMATION_MESSAGE);
966   }
967
968   /**
969    * DOCUMENT ME!
970    * 
971    * @param e
972    *          DOCUMENT ME!
973    */
974   public void documentationMenuItem_actionPerformed(ActionEvent e)
975   {
976     try
977     {
978       ClassLoader cl = jalview.gui.Desktop.class.getClassLoader();
979       java.net.URL url = javax.help.HelpSet.findHelpSet(cl, "help/help");
980       javax.help.HelpSet hs = new javax.help.HelpSet(cl, url);
981
982       javax.help.HelpBroker hb = hs.createHelpBroker();
983       hb.setCurrentID("home");
984       hb.setDisplayed(true);
985     } catch (Exception ex)
986     {
987     }
988   }
989
990   public void closeAll_actionPerformed(ActionEvent e)
991   {
992     JInternalFrame[] frames = desktop.getAllFrames();
993     for (int i = 0; i < frames.length; i++)
994     {
995       try
996       {
997         frames[i].setClosed(true);
998       } catch (java.beans.PropertyVetoException ex)
999       {
1000       }
1001     }
1002     System.out.println("ALL CLOSED");
1003     if (v_client != null)
1004     {
1005       // TODO clear binding to vamsas document objects on close_all
1006
1007     }
1008   }
1009
1010   public void raiseRelated_actionPerformed(ActionEvent e)
1011   {
1012     reorderAssociatedWindows(false, false);
1013   }
1014
1015   public void minimizeAssociated_actionPerformed(ActionEvent e)
1016   {
1017     reorderAssociatedWindows(true, false);
1018   }
1019
1020   void closeAssociatedWindows()
1021   {
1022     reorderAssociatedWindows(false, true);
1023   }
1024
1025   /*
1026    * (non-Javadoc)
1027    * 
1028    * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1029    * ActionEvent)
1030    */
1031   protected void garbageCollect_actionPerformed(ActionEvent e)
1032   {
1033     // We simply collect the garbage
1034     jalview.bin.Cache.log.debug("Collecting garbage...");
1035     System.gc();
1036     jalview.bin.Cache.log.debug("Finished garbage collection.");
1037   }
1038
1039   /*
1040    * (non-Javadoc)
1041    * 
1042    * @see
1043    * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1044    * )
1045    */
1046   protected void showMemusage_actionPerformed(ActionEvent e)
1047   {
1048     desktop.showMemoryUsage(showMemusage.isSelected());
1049   }
1050
1051   /*
1052    * (non-Javadoc)
1053    * 
1054    * @see
1055    * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1056    * )
1057    */
1058   protected void showConsole_actionPerformed(ActionEvent e)
1059   {
1060     showConsole(showConsole.isSelected());
1061   }
1062
1063   Console jconsole = null;
1064
1065   /**
1066    * control whether the java console is visible or not
1067    * 
1068    * @param selected
1069    */
1070   void showConsole(boolean selected)
1071   {
1072     showConsole.setSelected(selected);
1073     // TODO: decide if we should update properties file
1074     Cache.setProperty("SHOW_JAVA_CONSOLE", Boolean.valueOf(selected)
1075             .toString());
1076     jconsole.setVisible(selected);
1077   }
1078
1079   void reorderAssociatedWindows(boolean minimize, boolean close)
1080   {
1081     JInternalFrame[] frames = desktop.getAllFrames();
1082     if (frames == null || frames.length < 1)
1083     {
1084       return;
1085     }
1086
1087     AlignViewport source = null, target = null;
1088     if (frames[0] instanceof AlignFrame)
1089     {
1090       source = ((AlignFrame) frames[0]).getCurrentView();
1091     }
1092     else if (frames[0] instanceof TreePanel)
1093     {
1094       source = ((TreePanel) frames[0]).getViewPort();
1095     }
1096     else if (frames[0] instanceof PCAPanel)
1097     {
1098       source = ((PCAPanel) frames[0]).av;
1099     }
1100     else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1101     {
1102       source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1103     }
1104
1105     if (source != null)
1106     {
1107       for (int i = 0; i < frames.length; i++)
1108       {
1109         target = null;
1110         if (frames[i] == null)
1111         {
1112           continue;
1113         }
1114         if (frames[i] instanceof AlignFrame)
1115         {
1116           target = ((AlignFrame) frames[i]).getCurrentView();
1117         }
1118         else if (frames[i] instanceof TreePanel)
1119         {
1120           target = ((TreePanel) frames[i]).getViewPort();
1121         }
1122         else if (frames[i] instanceof PCAPanel)
1123         {
1124           target = ((PCAPanel) frames[i]).av;
1125         }
1126         else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1127         {
1128           target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1129         }
1130
1131         if (source == target)
1132         {
1133           try
1134           {
1135             if (close)
1136             {
1137               frames[i].setClosed(true);
1138             }
1139             else
1140             {
1141               frames[i].setIcon(minimize);
1142               if (!minimize)
1143               {
1144                 frames[i].toFront();
1145               }
1146             }
1147
1148           } catch (java.beans.PropertyVetoException ex)
1149           {
1150           }
1151         }
1152       }
1153     }
1154   }
1155
1156   /**
1157    * DOCUMENT ME!
1158    * 
1159    * @param e
1160    *          DOCUMENT ME!
1161    */
1162   protected void preferences_actionPerformed(ActionEvent e)
1163   {
1164     new Preferences();
1165   }
1166
1167   /**
1168    * DOCUMENT ME!
1169    * 
1170    * @param e
1171    *          DOCUMENT ME!
1172    */
1173   public void saveState_actionPerformed(ActionEvent e)
1174   {
1175     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
1176             .getProperty("LAST_DIRECTORY"), new String[]
1177     { "jar" }, new String[]
1178     { "Jalview Project" }, "Jalview Project");
1179
1180     chooser.setFileView(new JalviewFileView());
1181     chooser.setDialogTitle("Save State");
1182
1183     int value = chooser.showSaveDialog(this);
1184
1185     if (value == JalviewFileChooser.APPROVE_OPTION)
1186     {
1187       java.io.File choice = chooser.getSelectedFile();
1188       setProgressBar("Saving jalview project "
1189               + choice.getName(), choice.hashCode());
1190       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
1191       // TODO catch and handle errors for savestate
1192       try {
1193         new Jalview2XML().SaveState(choice);
1194       } catch (OutOfMemoryError oom)
1195       {
1196         new OOMWarning("Whilst saving current state to "+choice.getName(), oom);
1197       }
1198       catch (Exception ex)
1199       {
1200         Cache.log.error("Problems whilst trying to save to "+choice.getName(),ex);
1201         JOptionPane.showMessageDialog(this, "Error whilst saving current state to "+choice.getName(), "Couldn't save project", JOptionPane.WARNING_MESSAGE);
1202       }
1203       setProgressBar(null, choice.hashCode());
1204       
1205     }
1206   }
1207
1208   /**
1209    * DOCUMENT ME!
1210    * 
1211    * @param e
1212    *          DOCUMENT ME!
1213    */
1214   public void loadState_actionPerformed(ActionEvent e)
1215   {
1216     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
1217             .getProperty("LAST_DIRECTORY"), new String[]
1218     { "jar" }, new String[]
1219     { "Jalview Project" }, "Jalview Project");
1220     chooser.setFileView(new JalviewFileView());
1221     chooser.setDialogTitle("Restore state");
1222
1223     int value = chooser.showOpenDialog(this);
1224
1225     if (value == JalviewFileChooser.APPROVE_OPTION)
1226     {
1227       final String choice = chooser.getSelectedFile().getAbsolutePath();
1228       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
1229               .getSelectedFile().getParent());
1230       new Thread(new Runnable() {
1231         public void run() {
1232           setProgressBar("loading jalview project "
1233               + choice, choice.hashCode());
1234           try {
1235       new Jalview2XML().LoadJalviewAlign(choice); } 
1236       catch (OutOfMemoryError oom)
1237       {
1238         new OOMWarning("Whilst loading project from "+choice, oom);
1239       }
1240       catch (Exception ex)
1241       {
1242         Cache.log.error("Problems whilst loading project from "+choice,ex);
1243         JOptionPane.showMessageDialog(Desktop.desktop, "Error whilst loading project from "+choice, "Couldn't load project", JOptionPane.WARNING_MESSAGE);
1244       }
1245       setProgressBar(null, choice.hashCode());
1246         }
1247       }).start();
1248     }
1249   }
1250
1251   public void inputSequence_actionPerformed(ActionEvent e)
1252   {
1253     new SequenceFetcher(this);
1254   }
1255
1256   JPanel progressPanel;
1257
1258   public void startLoading(final String fileName)
1259   {
1260     if (fileLoadingCount == 0)
1261     {
1262       addProgressPanel("Loading File: " + fileName + "   ");
1263
1264     }
1265     fileLoadingCount++;
1266   }
1267
1268   private JProgressBar addProgressPanel(String string)
1269   {
1270     if (progressPanel == null)
1271     {
1272       progressPanel = new JPanel(new BorderLayout());
1273       totalProgressCount = 0;
1274     }
1275     JProgressBar progressBar = new JProgressBar();
1276     progressBar.setIndeterminate(true);
1277
1278     progressPanel.add(new JLabel(string), BorderLayout.WEST);
1279
1280     progressPanel.add(progressBar, BorderLayout.CENTER);
1281
1282     instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1283     totalProgressCount++;
1284     validate();
1285     return progressBar;
1286   }
1287
1288   int totalProgressCount = 0;
1289
1290   private void removeProgressPanel(JProgressBar progbar)
1291   {
1292     if (progressPanel != null)
1293     {
1294       progressPanel.remove(progbar);
1295       if (--totalProgressCount < 1)
1296       {
1297         this.getContentPane().remove(progressPanel);
1298         progressPanel = null;
1299       }
1300     }
1301     validate();
1302   }
1303
1304   public void stopLoading()
1305   {
1306     fileLoadingCount--;
1307     if (fileLoadingCount < 1)
1308     {
1309       if (progressPanel != null)
1310       {
1311         this.getContentPane().remove(progressPanel);
1312         progressPanel = null;
1313       }
1314       fileLoadingCount = 0;
1315     }
1316     validate();
1317   }
1318
1319   public static int getViewCount(String viewId)
1320   {
1321     AlignViewport[] aps = getViewports(viewId);
1322     return (aps == null) ? 0 : aps.length;
1323   }
1324
1325   /**
1326    * 
1327    * @param viewId
1328    * @return all AlignmentPanels concerning the viewId sequence set
1329    */
1330   public static AlignmentPanel[] getAlignmentPanels(String viewId)
1331   {
1332     int count = 0;
1333     if (Desktop.desktop == null)
1334     {
1335       // no frames created and in headless mode
1336       // TODO: verify that frames are recoverable when in headless mode
1337       return null;
1338     }
1339     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1340     ArrayList aps = new ArrayList();
1341     for (int t = 0; t < frames.length; t++)
1342     {
1343       if (frames[t] instanceof AlignFrame)
1344       {
1345         AlignFrame af = (AlignFrame) frames[t];
1346         for (int a = 0; a < af.alignPanels.size(); a++)
1347         {
1348           if (viewId
1349                   .equals(((AlignmentPanel) af.alignPanels.elementAt(a)).av
1350                           .getSequenceSetId()))
1351           {
1352             aps.add(af.alignPanels.elementAt(a));
1353           }
1354         }
1355       }
1356     }
1357     if (aps.size() == 0)
1358     {
1359       return null;
1360     }
1361     AlignmentPanel[] vap = new AlignmentPanel[aps.size()];
1362     for (int t = 0; t < vap.length; t++)
1363     {
1364       vap[t] = (AlignmentPanel) aps.get(t);
1365     }
1366     return vap;
1367   }
1368
1369   /**
1370    * get all the viewports on an alignment.
1371    * 
1372    * @param sequenceSetId
1373    *          unique alignment id
1374    * @return all viewports on the alignment bound to sequenceSetId
1375    */
1376   public static AlignViewport[] getViewports(String sequenceSetId)
1377   {
1378     Vector viewp = new Vector();
1379     if (desktop != null)
1380     {
1381       javax.swing.JInternalFrame[] frames = instance.getAllFrames();
1382
1383       for (int t = 0; t < frames.length; t++)
1384       {
1385         if (frames[t] instanceof AlignFrame)
1386         {
1387           AlignFrame afr = ((AlignFrame) frames[t]);
1388           if (afr.getViewport().getSequenceSetId().equals(sequenceSetId))
1389           {
1390             if (afr.alignPanels != null)
1391             {
1392               for (int a = 0; a < afr.alignPanels.size(); a++)
1393               {
1394                 if (sequenceSetId.equals(((AlignmentPanel) afr.alignPanels
1395                         .elementAt(a)).av.getSequenceSetId()))
1396                 {
1397                   viewp.addElement(((AlignmentPanel) afr.alignPanels
1398                           .elementAt(a)).av);
1399                 }
1400               }
1401             }
1402             else
1403             {
1404               viewp.addElement(((AlignFrame) frames[t]).getViewport());
1405             }
1406           }
1407         }
1408       }
1409       if (viewp.size() > 0)
1410       {
1411         AlignViewport[] vp = new AlignViewport[viewp.size()];
1412         viewp.copyInto(vp);
1413         return vp;
1414       }
1415     }
1416     return null;
1417   }
1418
1419   public void explodeViews(AlignFrame af)
1420   {
1421     int size = af.alignPanels.size();
1422     if (size < 2)
1423     {
1424       return;
1425     }
1426
1427     for (int i = 0; i < size; i++)
1428     {
1429       AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(i);
1430       AlignFrame newaf = new AlignFrame(ap);
1431       if (ap.av.explodedPosition != null
1432               && !ap.av.explodedPosition.equals(af.getBounds()))
1433       {
1434         newaf.setBounds(ap.av.explodedPosition);
1435       }
1436
1437       ap.av.gatherViewsHere = false;
1438
1439       addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1440               AlignFrame.DEFAULT_HEIGHT);
1441     }
1442
1443     af.alignPanels.clear();
1444     af.closeMenuItem_actionPerformed(true);
1445
1446   }
1447
1448   public void gatherViews(AlignFrame source)
1449   {
1450     source.viewport.gatherViewsHere = true;
1451     source.viewport.explodedPosition = source.getBounds();
1452     JInternalFrame[] frames = desktop.getAllFrames();
1453     String viewId = source.viewport.sequenceSetID;
1454
1455     for (int t = 0; t < frames.length; t++)
1456     {
1457       if (frames[t] instanceof AlignFrame && frames[t] != source)
1458       {
1459         AlignFrame af = (AlignFrame) frames[t];
1460         boolean gatherThis = false;
1461         for (int a = 0; a < af.alignPanels.size(); a++)
1462         {
1463           AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(a);
1464           if (viewId.equals(ap.av.getSequenceSetId()))
1465           {
1466             gatherThis = true;
1467             ap.av.gatherViewsHere = false;
1468             ap.av.explodedPosition = af.getBounds();
1469             source.addAlignmentPanel(ap, false);
1470           }
1471         }
1472
1473         if (gatherThis)
1474         {
1475           af.alignPanels.clear();
1476           af.closeMenuItem_actionPerformed(true);
1477         }
1478       }
1479     }
1480
1481   }
1482
1483   jalview.gui.VamsasApplication v_client = null;
1484
1485   public void vamsasImport_actionPerformed(ActionEvent e)
1486   {
1487     if (v_client == null)
1488     {
1489       // Load and try to start a session.
1490       JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
1491               .getProperty("LAST_DIRECTORY"));
1492
1493       chooser.setFileView(new JalviewFileView());
1494       chooser.setDialogTitle("Open a saved VAMSAS session");
1495       chooser
1496               .setToolTipText("select a vamsas session to be opened as a new vamsas session.");
1497
1498       int value = chooser.showOpenDialog(this);
1499
1500       if (value == JalviewFileChooser.APPROVE_OPTION)
1501       {
1502         String fle = chooser.getSelectedFile().toString();
1503         if (!vamsasImport(chooser.getSelectedFile()))
1504         {
1505           JOptionPane.showInternalMessageDialog(Desktop.desktop,
1506                   "Couldn't import '" + fle + "' as a new vamsas session.",
1507                   "Vamsas Document Import Failed",
1508                   JOptionPane.ERROR_MESSAGE);
1509         }
1510       }
1511     }
1512     else
1513     {
1514       jalview.bin.Cache.log
1515               .error("Implementation error - load session from a running session is not supported.");
1516     }
1517   }
1518
1519   /**
1520    * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
1521    * 
1522    * @param file
1523    * @return true if import was a success and a session was started.
1524    */
1525   public boolean vamsasImport(URL url)
1526   {
1527     // TODO: create progress bar
1528     if (v_client != null)
1529     {
1530
1531       jalview.bin.Cache.log
1532               .error("Implementation error - load session from a running session is not supported.");
1533       return false;
1534     }
1535
1536     try
1537     {
1538       // copy the URL content to a temporary local file
1539       // TODO: be a bit cleverer here with nio (?!)
1540       File file = File.createTempFile("vdocfromurl", ".vdj");
1541       FileOutputStream fos = new FileOutputStream(file);
1542       BufferedInputStream bis = new BufferedInputStream(url.openStream());
1543       byte[] buffer = new byte[2048];
1544       int ln;
1545       while ((ln = bis.read(buffer)) > -1)
1546       {
1547         fos.write(buffer, 0, ln);
1548       }
1549       bis.close();
1550       fos.close();
1551       v_client = new jalview.gui.VamsasApplication(this, file, url
1552               .toExternalForm());
1553     } catch (Exception ex)
1554     {
1555       jalview.bin.Cache.log.error(
1556               "Failed to create new vamsas session from contents of URL "
1557                       + url, ex);
1558       return false;
1559     }
1560     setupVamsasConnectedGui();
1561     v_client.initial_update(); // TODO: thread ?
1562     return v_client.inSession();
1563   }
1564
1565   /**
1566    * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
1567    * 
1568    * @param file
1569    * @return true if import was a success and a session was started.
1570    */
1571   public boolean vamsasImport(File file)
1572   {
1573     if (v_client != null)
1574     {
1575
1576       jalview.bin.Cache.log
1577               .error("Implementation error - load session from a running session is not supported.");
1578       return false;
1579     }
1580
1581     setProgressBar("Importing VAMSAS session from " + file.getName(), file
1582             .hashCode());
1583     try
1584     {
1585       v_client = new jalview.gui.VamsasApplication(this, file, null);
1586     } catch (Exception ex)
1587     {
1588       setProgressBar("Importing VAMSAS session from " + file.getName(),
1589               file.hashCode());
1590       jalview.bin.Cache.log.error(
1591               "New vamsas session from existing session file failed:", ex);
1592       return false;
1593     }
1594     setupVamsasConnectedGui();
1595     v_client.initial_update(); // TODO: thread ?
1596     setProgressBar("Importing VAMSAS session from " + file.getName(), file
1597             .hashCode());
1598     return v_client.inSession();
1599   }
1600
1601   public boolean joinVamsasSession(String mysesid)
1602   {
1603     if (v_client != null)
1604     {
1605       throw new Error(
1606               "Trying to join a vamsas session when another is already connected.");
1607     }
1608     if (mysesid == null)
1609     {
1610       throw new Error("Invalid vamsas session id.");
1611     }
1612     v_client = new VamsasApplication(this, mysesid);
1613     setupVamsasConnectedGui();
1614     v_client.initial_update();
1615     return (v_client.inSession());
1616   }
1617
1618   public void vamsasStart_actionPerformed(ActionEvent e)
1619   {
1620     if (v_client == null)
1621     {
1622       // Start a session.
1623       // we just start a default session for moment.
1624       /*
1625        * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
1626        * getProperty("LAST_DIRECTORY"));
1627        * 
1628        * chooser.setFileView(new JalviewFileView());
1629        * chooser.setDialogTitle("Load Vamsas file");
1630        * chooser.setToolTipText("Import");
1631        * 
1632        * int value = chooser.showOpenDialog(this);
1633        * 
1634        * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
1635        * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
1636        */
1637       v_client = new VamsasApplication(this);
1638       setupVamsasConnectedGui();
1639       v_client.initial_update(); // TODO: thread ?
1640     }
1641     else
1642     {
1643       // store current data in session.
1644       v_client.push_update(); // TODO: thread
1645     }
1646   }
1647
1648   protected void setupVamsasConnectedGui()
1649   {
1650     vamsasStart.setText("Session Update");
1651     vamsasSave.setVisible(true);
1652     vamsasStop.setVisible(true);
1653     vamsasImport.setVisible(false); // Document import to existing session is
1654     // not possible for vamsas-client-1.0.
1655   }
1656
1657   protected void setupVamsasDisconnectedGui()
1658   {
1659     vamsasSave.setVisible(false);
1660     vamsasStop.setVisible(false);
1661     vamsasImport.setVisible(true);
1662     vamsasStart.setText("New Vamsas Session");
1663   }
1664
1665   public void vamsasStop_actionPerformed(ActionEvent e)
1666   {
1667     if (v_client != null)
1668     {
1669       v_client.end_session();
1670       v_client = null;
1671       setupVamsasDisconnectedGui();
1672     }
1673   }
1674
1675   protected void buildVamsasStMenu()
1676   {
1677     if (v_client == null)
1678     {
1679       String[] sess = null;
1680       try
1681       {
1682         sess = VamsasApplication.getSessionList();
1683       } catch (Exception e)
1684       {
1685         jalview.bin.Cache.log.warn(
1686                 "Problem getting current sessions list.", e);
1687         sess = null;
1688       }
1689       if (sess != null)
1690       {
1691         jalview.bin.Cache.log.debug("Got current sessions list: "
1692                 + sess.length + " entries.");
1693         VamsasStMenu.removeAll();
1694         for (int i = 0; i < sess.length; i++)
1695         {
1696           JMenuItem sessit = new JMenuItem();
1697           sessit.setText(sess[i]);
1698           sessit.setToolTipText("Connect to session " + sess[i]);
1699           final Desktop dsktp = this;
1700           final String mysesid = sess[i];
1701           sessit.addActionListener(new ActionListener()
1702           {
1703
1704             public void actionPerformed(ActionEvent e)
1705             {
1706               if (dsktp.v_client == null)
1707               {
1708                 Thread rthr = new Thread(new Runnable()
1709                 {
1710
1711                   public void run()
1712                   {
1713                     dsktp.v_client = new VamsasApplication(dsktp, mysesid);
1714                     dsktp.setupVamsasConnectedGui();
1715                     dsktp.v_client.initial_update();
1716                   }
1717
1718                 });
1719                 rthr.start();
1720               }
1721             };
1722           });
1723           VamsasStMenu.add(sessit);
1724         }
1725         // don't show an empty menu.
1726         VamsasStMenu.setVisible(sess.length > 0);
1727
1728       }
1729       else
1730       {
1731         jalview.bin.Cache.log.debug("No current vamsas sessions.");
1732         VamsasStMenu.removeAll();
1733         VamsasStMenu.setVisible(false);
1734       }
1735     }
1736     else
1737     {
1738       // Not interested in the content. Just hide ourselves.
1739       VamsasStMenu.setVisible(false);
1740     }
1741   }
1742
1743   public void vamsasSave_actionPerformed(ActionEvent e)
1744   {
1745     if (v_client != null)
1746     {
1747       JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
1748               .getProperty("LAST_DIRECTORY"), new String[]
1749       { "vdj" }, // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
1750               new String[]
1751               { "Vamsas Document" }, "Vamsas Document");
1752
1753       chooser.setFileView(new JalviewFileView());
1754       chooser.setDialogTitle("Save Vamsas Document Archive");
1755
1756       int value = chooser.showSaveDialog(this);
1757
1758       if (value == JalviewFileChooser.APPROVE_OPTION)
1759       {
1760         java.io.File choice = chooser.getSelectedFile();
1761         JProgressBar progpanel = addProgressPanel("Saving VAMSAS Document to "
1762                 + choice.getName());
1763         jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
1764         String warnmsg = null;
1765         String warnttl = null;
1766         try
1767         {
1768           v_client.vclient.storeDocument(choice);
1769         } catch (Error ex)
1770         {
1771           warnttl = "Serious Problem saving Vamsas Document";
1772           warnmsg = ex.toString();
1773           jalview.bin.Cache.log.error("Error Whilst saving document to "
1774                   + choice, ex);
1775
1776         } catch (Exception ex)
1777         {
1778           warnttl = "Problem saving Vamsas Document.";
1779           warnmsg = ex.toString();
1780           jalview.bin.Cache.log.warn("Exception Whilst saving document to "
1781                   + choice, ex);
1782
1783         }
1784         removeProgressPanel(progpanel);
1785         if (warnmsg != null)
1786         {
1787           JOptionPane.showInternalMessageDialog(Desktop.desktop,
1788
1789           warnmsg, warnttl, JOptionPane.ERROR_MESSAGE);
1790         }
1791       }
1792     }
1793   }
1794
1795   JProgressBar vamUpdate = null;
1796
1797   /**
1798    * hide vamsas user gui bits when a vamsas document event is being handled.
1799    * 
1800    * @param b
1801    *          true to hide gui, false to reveal gui
1802    */
1803   public void setVamsasUpdate(boolean b)
1804   {
1805     jalview.bin.Cache.log.debug("Setting gui for Vamsas update "
1806             + (b ? "in progress" : "finished"));
1807
1808     if (vamUpdate != null)
1809     {
1810       this.removeProgressPanel(vamUpdate);
1811     }
1812     if (b)
1813     {
1814       vamUpdate = this.addProgressPanel("Updating vamsas session");
1815     }
1816     vamsasStart.setVisible(!b);
1817     vamsasStop.setVisible(!b);
1818     vamsasSave.setVisible(!b);
1819   }
1820
1821   public JInternalFrame[] getAllFrames()
1822   {
1823     return desktop.getAllFrames();
1824   }
1825
1826   /**
1827    * Checks the given url to see if it gives a response indicating that the user
1828    * should be informed of a new questionnaire.
1829    * 
1830    * @param url
1831    */
1832   public void checkForQuestionnaire(String url)
1833   {
1834     UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
1835     // javax.swing.SwingUtilities.invokeLater(jvq);
1836     new Thread(jvq).start();
1837   }
1838
1839   /**
1840    * Proxy class for JDesktopPane which optionally displays the current memory
1841    * usage and highlights the desktop area with a red bar if free memory runs
1842    * low.
1843    * 
1844    * @author AMW
1845    */
1846   public class MyDesktopPane extends JDesktopPane implements Runnable
1847   {
1848
1849     boolean showMemoryUsage = false;
1850
1851     Runtime runtime;
1852
1853     java.text.NumberFormat df;
1854
1855     float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
1856             percentUsage;
1857
1858     public MyDesktopPane(boolean showMemoryUsage)
1859     {
1860       showMemoryUsage(showMemoryUsage);
1861     }
1862
1863     public void showMemoryUsage(boolean showMemoryUsage)
1864     {
1865       this.showMemoryUsage = showMemoryUsage;
1866       if (showMemoryUsage)
1867       {
1868         Thread worker = new Thread(this);
1869         worker.start();
1870       }
1871     }
1872
1873     public boolean isShowMemoryUsage()
1874     {
1875       return showMemoryUsage;
1876     }
1877
1878     public void run()
1879     {
1880       df = java.text.NumberFormat.getNumberInstance();
1881       df.setMaximumFractionDigits(2);
1882       runtime = Runtime.getRuntime();
1883
1884       while (showMemoryUsage)
1885       {
1886         try
1887         {
1888           maxMemory = runtime.maxMemory() / 1048576f;
1889           allocatedMemory = runtime.totalMemory() / 1048576f;
1890           freeMemory = runtime.freeMemory() / 1048576f;
1891           totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
1892
1893           percentUsage = (totalFreeMemory / maxMemory) * 100;
1894
1895           // if (percentUsage < 20)
1896           {
1897             // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
1898             // Color.red);
1899             // instance.set.setBorder(border1);
1900           }
1901           repaint();
1902           // sleep after showing usage
1903           Thread.sleep(3000);
1904         } catch (Exception ex)
1905         {
1906           ex.printStackTrace();
1907         }
1908       }
1909     }
1910
1911     public void paintComponent(Graphics g)
1912     {
1913       if (showMemoryUsage && g != null && df != null)
1914       {
1915         if (percentUsage < 20)
1916           g.setColor(Color.red);
1917         FontMetrics fm = g.getFontMetrics();
1918         if (fm != null)
1919         {
1920           g.drawString("Total Free Memory: " + df.format(totalFreeMemory)
1921                   + "MB; Max Memory: " + df.format(maxMemory) + "MB; "
1922                   + df.format(percentUsage) + "%", 10, getHeight()
1923                   - fm.getHeight());
1924         }
1925       }
1926     }
1927
1928   }
1929
1930   protected JMenuItem groovyShell;
1931
1932   public void doGroovyCheck()
1933   {
1934     if (jalview.bin.Cache.groovyJarsPresent())
1935     {
1936       groovyShell = new JMenuItem();
1937       groovyShell.setText("Groovy Console...");
1938       groovyShell.addActionListener(new ActionListener()
1939       {
1940         public void actionPerformed(ActionEvent e)
1941         {
1942           groovyShell_actionPerformed(e);
1943         }
1944       });
1945       toolsMenu.add(groovyShell);
1946       groovyShell.setVisible(true);
1947     }
1948   }
1949
1950   /**
1951    * Accessor method to quickly get all the AlignmentFrames loaded.
1952    */
1953   public static AlignFrame[] getAlignframes()
1954   {
1955     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1956
1957     if (frames == null)
1958     {
1959       return null;
1960     }
1961     Vector avp = new Vector();
1962     try
1963     {
1964       // REVERSE ORDER
1965       for (int i = frames.length - 1; i > -1; i--)
1966       {
1967         if (frames[i] instanceof AlignFrame)
1968         {
1969           AlignFrame af = (AlignFrame) frames[i];
1970           avp.addElement(af);
1971         }
1972       }
1973     } catch (Exception ex)
1974     {
1975       ex.printStackTrace();
1976     }
1977     if (avp.size() == 0)
1978     {
1979       return null;
1980     }
1981     AlignFrame afs[] = new AlignFrame[avp.size()];
1982     for (int i = 0, j = avp.size(); i < j; i++)
1983     {
1984       afs[i] = (AlignFrame) avp.elementAt(i);
1985     }
1986     avp.clear();
1987     return afs;
1988   }
1989
1990   /**
1991    * Add Groovy Support to Jalview
1992    */
1993   public void groovyShell_actionPerformed(ActionEvent e)
1994   {
1995     // use reflection to avoid creating compilation dependency.
1996     if (!jalview.bin.Cache.groovyJarsPresent())
1997     {
1998       throw new Error(
1999               "Implementation Error. Cannot create groovyShell without Groovy on the classpath!");
2000     }
2001     try
2002     {
2003       Class gcClass = Desktop.class.getClassLoader().loadClass(
2004               "groovy.ui.Console");
2005       Constructor gccons = gcClass.getConstructor(null);
2006       java.lang.reflect.Method setvar = gcClass.getMethod("setVariable",
2007               new Class[]
2008               { String.class, Object.class });
2009       java.lang.reflect.Method run = gcClass.getMethod("run", null);
2010       Object gc = gccons.newInstance(null);
2011       setvar.invoke(gc, new Object[]
2012       { "Jalview", this });
2013       run.invoke(gc, null);
2014     } catch (Exception ex)
2015     {
2016       jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2017       JOptionPane
2018               .showInternalMessageDialog(
2019                       Desktop.desktop,
2020
2021                       "Couldn't create the groovy Shell. Check the error log for the details of what went wrong.",
2022                       "Jalview Groovy Support Failed",
2023                       JOptionPane.ERROR_MESSAGE);
2024     }
2025   }
2026
2027   /**
2028    * Progress bars managed by the IProgressIndicator method.
2029    */
2030   private Hashtable progressBars, progressBarHandlers;
2031
2032   /*
2033    * (non-Javadoc)
2034    * 
2035    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2036    */
2037   public void setProgressBar(String message, long id)
2038   {
2039     if (progressBars == null)
2040     {
2041       progressBars = new Hashtable();
2042       progressBarHandlers = new Hashtable();
2043     }
2044
2045     if (progressBars.get(new Long(id)) != null)
2046     {
2047       JProgressBar progressPanel = (JProgressBar) progressBars
2048               .remove(new Long(id));
2049       if (progressBarHandlers.contains(new Long(id)))
2050       {
2051         progressBarHandlers.remove(new Long(id));
2052       }
2053       removeProgressPanel(progressPanel);
2054     }
2055     else
2056     {
2057       progressBars.put(new Long(id), addProgressPanel(message));
2058     }
2059   }
2060
2061   /*
2062    * (non-Javadoc)
2063    * 
2064    * @see jalview.gui.IProgressIndicator#registerHandler(long,
2065    * jalview.gui.IProgressIndicatorHandler)
2066    */
2067   public void registerHandler(final long id,
2068           final IProgressIndicatorHandler handler)
2069   {
2070     if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
2071     {
2072       throw new Error(
2073               "call setProgressBar before registering the progress bar's handler.");
2074     }
2075     progressBarHandlers.put(new Long(id), handler);
2076     final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
2077     if (handler.canCancel())
2078     {
2079       JButton cancel = new JButton("Cancel");
2080       final IProgressIndicator us = this;
2081       cancel.addActionListener(new ActionListener()
2082       {
2083
2084         public void actionPerformed(ActionEvent e)
2085         {
2086           handler.cancelActivity(id);
2087           us.setProgressBar("Cancelled "
2088                   + ((JLabel) progressPanel.getComponent(0)).getText(), id);
2089         }
2090       });
2091       progressPanel.add(cancel, BorderLayout.EAST);
2092     }
2093   }
2094
2095   /**
2096    * This will return the first AlignFrame viewing AlignViewport av. It will
2097    * break if there are more than one AlignFrames viewing a particular av. This
2098    * 
2099    * @param av
2100    * @return alignFrame for av
2101    */
2102   public static AlignFrame getAlignFrameFor(AlignViewport av)
2103   {
2104     if (desktop != null)
2105     {
2106       AlignmentPanel[] aps = getAlignmentPanels(av.getSequenceSetId());
2107       for (int panel = 0; aps != null && panel < aps.length; panel++)
2108       {
2109         if (aps[panel] != null && aps[panel].av == av)
2110         {
2111           return aps[panel].alignFrame;
2112         }
2113       }
2114     }
2115     return null;
2116   }
2117
2118   public VamsasApplication getVamsasApplication()
2119   {
2120     return v_client;
2121
2122   }
2123   /**
2124    * flag set if jalview GUI is being operated programmatically
2125    */
2126   private boolean inBatchMode=false;
2127
2128   /**
2129    * check if jalview GUI is being operated programmatically
2130    * @return inBatchMode
2131    */
2132   public boolean isInBatchMode()
2133   {
2134     return inBatchMode;
2135   }
2136
2137   /**
2138    * set flag if jalview GUI is being operated programmatically
2139    * @param inBatchMode 
2140    */
2141   public void setInBatchMode(boolean inBatchMode)
2142   {
2143     this.inBatchMode = inBatchMode;
2144   }
2145
2146
2147   public void startServiceDiscovery()
2148   {
2149     startServiceDiscovery(false);
2150   }
2151
2152   public void startServiceDiscovery(boolean blocking)
2153   {
2154     boolean alive = true;
2155     Thread t0 = null, t1 = null, t2 = null;
2156
2157     // todo: changesupport handlers need to be transferred
2158     if (discoverer == null)
2159     {
2160       discoverer = new jalview.ws.jws1.Discoverer();
2161       // register PCS handler for desktop.
2162       discoverer.addPropertyChangeListener(changeSupport);
2163     }
2164     if (Cache.getDefault("SHOW_JWS1_SERVICES", true))
2165     {
2166       (t0 = new Thread(discoverer)).start();
2167     }
2168
2169     try
2170     {
2171       if (Cache.getDefault("SHOW_ENFIN_SERVICES", true))
2172       {
2173         // EnfinEnvision web service menu entries are rebuild every time the
2174         // menu is shown, so no changeSupport events are needed.
2175         jalview.ws.EnfinEnvision2OneWay.getInstance();
2176         (t1 = new Thread(jalview.ws.EnfinEnvision2OneWay.getInstance()))
2177                 .start();
2178       }
2179     } catch (Exception e)
2180     {
2181       Cache.log
2182               .info(
2183                       "Exception when trying to launch Envision2 workflow discovery.",
2184                       e);
2185       Cache.log.info(e.getStackTrace());
2186     }
2187     if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2188     {
2189       jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2190               .addPropertyChangeListener(changeSupport);
2191       (t2 = new Thread(jalview.ws.jws2.Jws2Discoverer.getDiscoverer()))
2192               .start();
2193     }
2194     if (blocking)
2195     {
2196       while (alive)
2197       {
2198         try
2199         {
2200           Thread.sleep(15);
2201         } catch (Exception e)
2202         {
2203         }
2204         alive = (t1 != null && t1.isAlive())
2205                 || (t2 != null && t2.isAlive())
2206                 || (t0 != null && t0.isAlive());
2207       }
2208     }
2209   }
2210
2211   /**
2212    * start a thread to open a URL in the configured browser. Pops up a warning dialog to the user if there is an exception when calling out to the browser to open the URL.
2213    * @param url
2214    */
2215   public static void showUrl(final String url)
2216   {
2217     new Thread(new Runnable() {
2218       public void run() {
2219         try {
2220           jalview.util.BrowserLauncher.openURL(url);
2221         } catch (Exception ex)
2222         {    
2223           JOptionPane
2224                   .showInternalMessageDialog(
2225                           Desktop.desktop,
2226                           "Unixers: Couldn't find default web browser."
2227                                   + "\nAdd the full path to your browser in Preferences.",
2228                           "Web browser not found", JOptionPane.WARNING_MESSAGE);
2229
2230           ex.printStackTrace();
2231         }
2232       }
2233     }).start();    
2234   }
2235
2236   public static WsParamSetManager wsparamManager = null;
2237   public static ParamManager getUserParameterStore()
2238   {
2239     if (wsparamManager==null)
2240     {
2241       wsparamManager = new WsParamSetManager();
2242     }
2243     return wsparamManager;
2244   }
2245
2246 }