hack to ensure that if window geometry was not stored correctly, windows are not...
[jalview.git] / src / jalview / gui / Desktop.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.5)
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       JProgressBar progpanel = addProgressPanel("Saving jalview project "
1189               + choice.getName());
1190       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
1191       // TODO catch and handle errors for savestate
1192       new Jalview2XML().SaveState(choice);
1193       removeProgressPanel(progpanel);
1194
1195     }
1196   }
1197
1198   /**
1199    * DOCUMENT ME!
1200    * 
1201    * @param e
1202    *          DOCUMENT ME!
1203    */
1204   public void loadState_actionPerformed(ActionEvent e)
1205   {
1206     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
1207             .getProperty("LAST_DIRECTORY"), new String[]
1208     { "jar" }, new String[]
1209     { "Jalview Project" }, "Jalview Project");
1210     chooser.setFileView(new JalviewFileView());
1211     chooser.setDialogTitle("Restore state");
1212
1213     int value = chooser.showOpenDialog(this);
1214
1215     if (value == JalviewFileChooser.APPROVE_OPTION)
1216     {
1217       String choice = chooser.getSelectedFile().getAbsolutePath();
1218       setProgressBar("loading jalview project "
1219               + chooser.getSelectedFile().getName(), choice.hashCode());
1220       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
1221               .getSelectedFile().getParent());
1222       new Jalview2XML().LoadJalviewAlign(choice);
1223       setProgressBar(null, choice.hashCode());
1224     }
1225   }
1226
1227   public void inputSequence_actionPerformed(ActionEvent e)
1228   {
1229     new SequenceFetcher(this);
1230   }
1231
1232   JPanel progressPanel;
1233
1234   public void startLoading(final String fileName)
1235   {
1236     if (fileLoadingCount == 0)
1237     {
1238       addProgressPanel("Loading File: " + fileName + "   ");
1239
1240     }
1241     fileLoadingCount++;
1242   }
1243
1244   private JProgressBar addProgressPanel(String string)
1245   {
1246     if (progressPanel == null)
1247     {
1248       progressPanel = new JPanel(new BorderLayout());
1249       totalProgressCount = 0;
1250     }
1251     JProgressBar progressBar = new JProgressBar();
1252     progressBar.setIndeterminate(true);
1253
1254     progressPanel.add(new JLabel(string), BorderLayout.WEST);
1255
1256     progressPanel.add(progressBar, BorderLayout.CENTER);
1257
1258     instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1259     totalProgressCount++;
1260     validate();
1261     return progressBar;
1262   }
1263
1264   int totalProgressCount = 0;
1265
1266   private void removeProgressPanel(JProgressBar progbar)
1267   {
1268     if (progressPanel != null)
1269     {
1270       progressPanel.remove(progbar);
1271       if (--totalProgressCount < 1)
1272       {
1273         this.getContentPane().remove(progressPanel);
1274         progressPanel = null;
1275       }
1276     }
1277     validate();
1278   }
1279
1280   public void stopLoading()
1281   {
1282     fileLoadingCount--;
1283     if (fileLoadingCount < 1)
1284     {
1285       if (progressPanel != null)
1286       {
1287         this.getContentPane().remove(progressPanel);
1288         progressPanel = null;
1289       }
1290       fileLoadingCount = 0;
1291     }
1292     validate();
1293   }
1294
1295   public static int getViewCount(String viewId)
1296   {
1297     AlignViewport[] aps = getViewports(viewId);
1298     return (aps == null) ? 0 : aps.length;
1299   }
1300
1301   /**
1302    * 
1303    * @param viewId
1304    * @return all AlignmentPanels concerning the viewId sequence set
1305    */
1306   public static AlignmentPanel[] getAlignmentPanels(String viewId)
1307   {
1308     int count = 0;
1309     if (Desktop.desktop == null)
1310     {
1311       // no frames created and in headless mode
1312       // TODO: verify that frames are recoverable when in headless mode
1313       return null;
1314     }
1315     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1316     ArrayList aps = new ArrayList();
1317     for (int t = 0; t < frames.length; t++)
1318     {
1319       if (frames[t] instanceof AlignFrame)
1320       {
1321         AlignFrame af = (AlignFrame) frames[t];
1322         for (int a = 0; a < af.alignPanels.size(); a++)
1323         {
1324           if (viewId
1325                   .equals(((AlignmentPanel) af.alignPanels.elementAt(a)).av
1326                           .getSequenceSetId()))
1327           {
1328             aps.add(af.alignPanels.elementAt(a));
1329           }
1330         }
1331       }
1332     }
1333     if (aps.size() == 0)
1334     {
1335       return null;
1336     }
1337     AlignmentPanel[] vap = new AlignmentPanel[aps.size()];
1338     for (int t = 0; t < vap.length; t++)
1339     {
1340       vap[t] = (AlignmentPanel) aps.get(t);
1341     }
1342     return vap;
1343   }
1344
1345   /**
1346    * get all the viewports on an alignment.
1347    * 
1348    * @param sequenceSetId
1349    *          unique alignment id
1350    * @return all viewports on the alignment bound to sequenceSetId
1351    */
1352   public static AlignViewport[] getViewports(String sequenceSetId)
1353   {
1354     Vector viewp = new Vector();
1355     if (desktop != null)
1356     {
1357       javax.swing.JInternalFrame[] frames = instance.getAllFrames();
1358
1359       for (int t = 0; t < frames.length; t++)
1360       {
1361         if (frames[t] instanceof AlignFrame)
1362         {
1363           AlignFrame afr = ((AlignFrame) frames[t]);
1364           if (afr.getViewport().getSequenceSetId().equals(sequenceSetId))
1365           {
1366             if (afr.alignPanels != null)
1367             {
1368               for (int a = 0; a < afr.alignPanels.size(); a++)
1369               {
1370                 if (sequenceSetId.equals(((AlignmentPanel) afr.alignPanels
1371                         .elementAt(a)).av.getSequenceSetId()))
1372                 {
1373                   viewp.addElement(((AlignmentPanel) afr.alignPanels
1374                           .elementAt(a)).av);
1375                 }
1376               }
1377             }
1378             else
1379             {
1380               viewp.addElement(((AlignFrame) frames[t]).getViewport());
1381             }
1382           }
1383         }
1384       }
1385       if (viewp.size() > 0)
1386       {
1387         AlignViewport[] vp = new AlignViewport[viewp.size()];
1388         viewp.copyInto(vp);
1389         return vp;
1390       }
1391     }
1392     return null;
1393   }
1394
1395   public void explodeViews(AlignFrame af)
1396   {
1397     int size = af.alignPanels.size();
1398     if (size < 2)
1399     {
1400       return;
1401     }
1402
1403     for (int i = 0; i < size; i++)
1404     {
1405       AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(i);
1406       AlignFrame newaf = new AlignFrame(ap);
1407       if (ap.av.explodedPosition != null
1408               && !ap.av.explodedPosition.equals(af.getBounds()))
1409       {
1410         newaf.setBounds(ap.av.explodedPosition);
1411       }
1412
1413       ap.av.gatherViewsHere = false;
1414
1415       addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1416               AlignFrame.DEFAULT_HEIGHT);
1417     }
1418
1419     af.alignPanels.clear();
1420     af.closeMenuItem_actionPerformed(true);
1421
1422   }
1423
1424   public void gatherViews(AlignFrame source)
1425   {
1426     source.viewport.gatherViewsHere = true;
1427     source.viewport.explodedPosition = source.getBounds();
1428     JInternalFrame[] frames = desktop.getAllFrames();
1429     String viewId = source.viewport.sequenceSetID;
1430
1431     for (int t = 0; t < frames.length; t++)
1432     {
1433       if (frames[t] instanceof AlignFrame && frames[t] != source)
1434       {
1435         AlignFrame af = (AlignFrame) frames[t];
1436         boolean gatherThis = false;
1437         for (int a = 0; a < af.alignPanels.size(); a++)
1438         {
1439           AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(a);
1440           if (viewId.equals(ap.av.getSequenceSetId()))
1441           {
1442             gatherThis = true;
1443             ap.av.gatherViewsHere = false;
1444             ap.av.explodedPosition = af.getBounds();
1445             source.addAlignmentPanel(ap, false);
1446           }
1447         }
1448
1449         if (gatherThis)
1450         {
1451           af.alignPanels.clear();
1452           af.closeMenuItem_actionPerformed(true);
1453         }
1454       }
1455     }
1456
1457   }
1458
1459   jalview.gui.VamsasApplication v_client = null;
1460
1461   public void vamsasImport_actionPerformed(ActionEvent e)
1462   {
1463     if (v_client == null)
1464     {
1465       // Load and try to start a session.
1466       JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
1467               .getProperty("LAST_DIRECTORY"));
1468
1469       chooser.setFileView(new JalviewFileView());
1470       chooser.setDialogTitle("Open a saved VAMSAS session");
1471       chooser
1472               .setToolTipText("select a vamsas session to be opened as a new vamsas session.");
1473
1474       int value = chooser.showOpenDialog(this);
1475
1476       if (value == JalviewFileChooser.APPROVE_OPTION)
1477       {
1478         String fle = chooser.getSelectedFile().toString();
1479         if (!vamsasImport(chooser.getSelectedFile()))
1480         {
1481           JOptionPane.showInternalMessageDialog(Desktop.desktop,
1482                   "Couldn't import '" + fle + "' as a new vamsas session.",
1483                   "Vamsas Document Import Failed",
1484                   JOptionPane.ERROR_MESSAGE);
1485         }
1486       }
1487     }
1488     else
1489     {
1490       jalview.bin.Cache.log
1491               .error("Implementation error - load session from a running session is not supported.");
1492     }
1493   }
1494
1495   /**
1496    * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
1497    * 
1498    * @param file
1499    * @return true if import was a success and a session was started.
1500    */
1501   public boolean vamsasImport(URL url)
1502   {
1503     // TODO: create progress bar
1504     if (v_client != null)
1505     {
1506
1507       jalview.bin.Cache.log
1508               .error("Implementation error - load session from a running session is not supported.");
1509       return false;
1510     }
1511
1512     try
1513     {
1514       // copy the URL content to a temporary local file
1515       // TODO: be a bit cleverer here with nio (?!)
1516       File file = File.createTempFile("vdocfromurl", ".vdj");
1517       FileOutputStream fos = new FileOutputStream(file);
1518       BufferedInputStream bis = new BufferedInputStream(url.openStream());
1519       byte[] buffer = new byte[2048];
1520       int ln;
1521       while ((ln = bis.read(buffer)) > -1)
1522       {
1523         fos.write(buffer, 0, ln);
1524       }
1525       bis.close();
1526       fos.close();
1527       v_client = new jalview.gui.VamsasApplication(this, file, url
1528               .toExternalForm());
1529     } catch (Exception ex)
1530     {
1531       jalview.bin.Cache.log.error(
1532               "Failed to create new vamsas session from contents of URL "
1533                       + url, ex);
1534       return false;
1535     }
1536     setupVamsasConnectedGui();
1537     v_client.initial_update(); // TODO: thread ?
1538     return v_client.inSession();
1539   }
1540
1541   /**
1542    * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
1543    * 
1544    * @param file
1545    * @return true if import was a success and a session was started.
1546    */
1547   public boolean vamsasImport(File file)
1548   {
1549     if (v_client != null)
1550     {
1551
1552       jalview.bin.Cache.log
1553               .error("Implementation error - load session from a running session is not supported.");
1554       return false;
1555     }
1556
1557     setProgressBar("Importing VAMSAS session from " + file.getName(), file
1558             .hashCode());
1559     try
1560     {
1561       v_client = new jalview.gui.VamsasApplication(this, file, null);
1562     } catch (Exception ex)
1563     {
1564       setProgressBar("Importing VAMSAS session from " + file.getName(),
1565               file.hashCode());
1566       jalview.bin.Cache.log.error(
1567               "New vamsas session from existing session file failed:", ex);
1568       return false;
1569     }
1570     setupVamsasConnectedGui();
1571     v_client.initial_update(); // TODO: thread ?
1572     setProgressBar("Importing VAMSAS session from " + file.getName(), file
1573             .hashCode());
1574     return v_client.inSession();
1575   }
1576
1577   public boolean joinVamsasSession(String mysesid)
1578   {
1579     if (v_client != null)
1580     {
1581       throw new Error(
1582               "Trying to join a vamsas session when another is already connected.");
1583     }
1584     if (mysesid == null)
1585     {
1586       throw new Error("Invalid vamsas session id.");
1587     }
1588     v_client = new VamsasApplication(this, mysesid);
1589     setupVamsasConnectedGui();
1590     v_client.initial_update();
1591     return (v_client.inSession());
1592   }
1593
1594   public void vamsasStart_actionPerformed(ActionEvent e)
1595   {
1596     if (v_client == null)
1597     {
1598       // Start a session.
1599       // we just start a default session for moment.
1600       /*
1601        * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
1602        * getProperty("LAST_DIRECTORY"));
1603        * 
1604        * chooser.setFileView(new JalviewFileView());
1605        * chooser.setDialogTitle("Load Vamsas file");
1606        * chooser.setToolTipText("Import");
1607        * 
1608        * int value = chooser.showOpenDialog(this);
1609        * 
1610        * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
1611        * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
1612        */
1613       v_client = new VamsasApplication(this);
1614       setupVamsasConnectedGui();
1615       v_client.initial_update(); // TODO: thread ?
1616     }
1617     else
1618     {
1619       // store current data in session.
1620       v_client.push_update(); // TODO: thread
1621     }
1622   }
1623
1624   protected void setupVamsasConnectedGui()
1625   {
1626     vamsasStart.setText("Session Update");
1627     vamsasSave.setVisible(true);
1628     vamsasStop.setVisible(true);
1629     vamsasImport.setVisible(false); // Document import to existing session is
1630     // not possible for vamsas-client-1.0.
1631   }
1632
1633   protected void setupVamsasDisconnectedGui()
1634   {
1635     vamsasSave.setVisible(false);
1636     vamsasStop.setVisible(false);
1637     vamsasImport.setVisible(true);
1638     vamsasStart.setText("New Vamsas Session");
1639   }
1640
1641   public void vamsasStop_actionPerformed(ActionEvent e)
1642   {
1643     if (v_client != null)
1644     {
1645       v_client.end_session();
1646       v_client = null;
1647       setupVamsasDisconnectedGui();
1648     }
1649   }
1650
1651   protected void buildVamsasStMenu()
1652   {
1653     if (v_client == null)
1654     {
1655       String[] sess = null;
1656       try
1657       {
1658         sess = VamsasApplication.getSessionList();
1659       } catch (Exception e)
1660       {
1661         jalview.bin.Cache.log.warn(
1662                 "Problem getting current sessions list.", e);
1663         sess = null;
1664       }
1665       if (sess != null)
1666       {
1667         jalview.bin.Cache.log.debug("Got current sessions list: "
1668                 + sess.length + " entries.");
1669         VamsasStMenu.removeAll();
1670         for (int i = 0; i < sess.length; i++)
1671         {
1672           JMenuItem sessit = new JMenuItem();
1673           sessit.setText(sess[i]);
1674           sessit.setToolTipText("Connect to session " + sess[i]);
1675           final Desktop dsktp = this;
1676           final String mysesid = sess[i];
1677           sessit.addActionListener(new ActionListener()
1678           {
1679
1680             public void actionPerformed(ActionEvent e)
1681             {
1682               if (dsktp.v_client == null)
1683               {
1684                 Thread rthr = new Thread(new Runnable()
1685                 {
1686
1687                   public void run()
1688                   {
1689                     dsktp.v_client = new VamsasApplication(dsktp, mysesid);
1690                     dsktp.setupVamsasConnectedGui();
1691                     dsktp.v_client.initial_update();
1692                   }
1693
1694                 });
1695                 rthr.start();
1696               }
1697             };
1698           });
1699           VamsasStMenu.add(sessit);
1700         }
1701         // don't show an empty menu.
1702         VamsasStMenu.setVisible(sess.length > 0);
1703
1704       }
1705       else
1706       {
1707         jalview.bin.Cache.log.debug("No current vamsas sessions.");
1708         VamsasStMenu.removeAll();
1709         VamsasStMenu.setVisible(false);
1710       }
1711     }
1712     else
1713     {
1714       // Not interested in the content. Just hide ourselves.
1715       VamsasStMenu.setVisible(false);
1716     }
1717   }
1718
1719   public void vamsasSave_actionPerformed(ActionEvent e)
1720   {
1721     if (v_client != null)
1722     {
1723       JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
1724               .getProperty("LAST_DIRECTORY"), new String[]
1725       { "vdj" }, // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
1726               new String[]
1727               { "Vamsas Document" }, "Vamsas Document");
1728
1729       chooser.setFileView(new JalviewFileView());
1730       chooser.setDialogTitle("Save Vamsas Document Archive");
1731
1732       int value = chooser.showSaveDialog(this);
1733
1734       if (value == JalviewFileChooser.APPROVE_OPTION)
1735       {
1736         java.io.File choice = chooser.getSelectedFile();
1737         JProgressBar progpanel = addProgressPanel("Saving VAMSAS Document to "
1738                 + choice.getName());
1739         jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
1740         String warnmsg = null;
1741         String warnttl = null;
1742         try
1743         {
1744           v_client.vclient.storeDocument(choice);
1745         } catch (Error ex)
1746         {
1747           warnttl = "Serious Problem saving Vamsas Document";
1748           warnmsg = ex.toString();
1749           jalview.bin.Cache.log.error("Error Whilst saving document to "
1750                   + choice, ex);
1751
1752         } catch (Exception ex)
1753         {
1754           warnttl = "Problem saving Vamsas Document.";
1755           warnmsg = ex.toString();
1756           jalview.bin.Cache.log.warn("Exception Whilst saving document to "
1757                   + choice, ex);
1758
1759         }
1760         removeProgressPanel(progpanel);
1761         if (warnmsg != null)
1762         {
1763           JOptionPane.showInternalMessageDialog(Desktop.desktop,
1764
1765           warnmsg, warnttl, JOptionPane.ERROR_MESSAGE);
1766         }
1767       }
1768     }
1769   }
1770
1771   JProgressBar vamUpdate = null;
1772
1773   /**
1774    * hide vamsas user gui bits when a vamsas document event is being handled.
1775    * 
1776    * @param b
1777    *          true to hide gui, false to reveal gui
1778    */
1779   public void setVamsasUpdate(boolean b)
1780   {
1781     jalview.bin.Cache.log.debug("Setting gui for Vamsas update "
1782             + (b ? "in progress" : "finished"));
1783
1784     if (vamUpdate != null)
1785     {
1786       this.removeProgressPanel(vamUpdate);
1787     }
1788     if (b)
1789     {
1790       vamUpdate = this.addProgressPanel("Updating vamsas session");
1791     }
1792     vamsasStart.setVisible(!b);
1793     vamsasStop.setVisible(!b);
1794     vamsasSave.setVisible(!b);
1795   }
1796
1797   public JInternalFrame[] getAllFrames()
1798   {
1799     return desktop.getAllFrames();
1800   }
1801
1802   /**
1803    * Checks the given url to see if it gives a response indicating that the user
1804    * should be informed of a new questionnaire.
1805    * 
1806    * @param url
1807    */
1808   public void checkForQuestionnaire(String url)
1809   {
1810     UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
1811     // javax.swing.SwingUtilities.invokeLater(jvq);
1812     new Thread(jvq).start();
1813   }
1814
1815   /**
1816    * Proxy class for JDesktopPane which optionally displays the current memory
1817    * usage and highlights the desktop area with a red bar if free memory runs
1818    * low.
1819    * 
1820    * @author AMW
1821    */
1822   public class MyDesktopPane extends JDesktopPane implements Runnable
1823   {
1824
1825     boolean showMemoryUsage = false;
1826
1827     Runtime runtime;
1828
1829     java.text.NumberFormat df;
1830
1831     float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
1832             percentUsage;
1833
1834     public MyDesktopPane(boolean showMemoryUsage)
1835     {
1836       showMemoryUsage(showMemoryUsage);
1837     }
1838
1839     public void showMemoryUsage(boolean showMemoryUsage)
1840     {
1841       this.showMemoryUsage = showMemoryUsage;
1842       if (showMemoryUsage)
1843       {
1844         Thread worker = new Thread(this);
1845         worker.start();
1846       }
1847     }
1848
1849     public boolean isShowMemoryUsage()
1850     {
1851       return showMemoryUsage;
1852     }
1853
1854     public void run()
1855     {
1856       df = java.text.NumberFormat.getNumberInstance();
1857       df.setMaximumFractionDigits(2);
1858       runtime = Runtime.getRuntime();
1859
1860       while (showMemoryUsage)
1861       {
1862         try
1863         {
1864           maxMemory = runtime.maxMemory() / 1048576f;
1865           allocatedMemory = runtime.totalMemory() / 1048576f;
1866           freeMemory = runtime.freeMemory() / 1048576f;
1867           totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
1868
1869           percentUsage = (totalFreeMemory / maxMemory) * 100;
1870
1871           // if (percentUsage < 20)
1872           {
1873             // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
1874             // Color.red);
1875             // instance.set.setBorder(border1);
1876           }
1877           repaint();
1878           // sleep after showing usage
1879           Thread.sleep(3000);
1880         } catch (Exception ex)
1881         {
1882           ex.printStackTrace();
1883         }
1884       }
1885     }
1886
1887     public void paintComponent(Graphics g)
1888     {
1889       if (showMemoryUsage && g != null && df != null)
1890       {
1891         if (percentUsage < 20)
1892           g.setColor(Color.red);
1893         FontMetrics fm = g.getFontMetrics();
1894         if (fm != null)
1895         {
1896           g.drawString("Total Free Memory: " + df.format(totalFreeMemory)
1897                   + "MB; Max Memory: " + df.format(maxMemory) + "MB; "
1898                   + df.format(percentUsage) + "%", 10, getHeight()
1899                   - fm.getHeight());
1900         }
1901       }
1902     }
1903
1904   }
1905
1906   protected JMenuItem groovyShell;
1907
1908   public void doGroovyCheck()
1909   {
1910     if (jalview.bin.Cache.groovyJarsPresent())
1911     {
1912       groovyShell = new JMenuItem();
1913       groovyShell.setText("Groovy Console...");
1914       groovyShell.addActionListener(new ActionListener()
1915       {
1916         public void actionPerformed(ActionEvent e)
1917         {
1918           groovyShell_actionPerformed(e);
1919         }
1920       });
1921       toolsMenu.add(groovyShell);
1922       groovyShell.setVisible(true);
1923     }
1924   }
1925
1926   /**
1927    * Accessor method to quickly get all the AlignmentFrames loaded.
1928    */
1929   public static AlignFrame[] getAlignframes()
1930   {
1931     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1932
1933     if (frames == null)
1934     {
1935       return null;
1936     }
1937     Vector avp = new Vector();
1938     try
1939     {
1940       // REVERSE ORDER
1941       for (int i = frames.length - 1; i > -1; i--)
1942       {
1943         if (frames[i] instanceof AlignFrame)
1944         {
1945           AlignFrame af = (AlignFrame) frames[i];
1946           avp.addElement(af);
1947         }
1948       }
1949     } catch (Exception ex)
1950     {
1951       ex.printStackTrace();
1952     }
1953     if (avp.size() == 0)
1954     {
1955       return null;
1956     }
1957     AlignFrame afs[] = new AlignFrame[avp.size()];
1958     for (int i = 0, j = avp.size(); i < j; i++)
1959     {
1960       afs[i] = (AlignFrame) avp.elementAt(i);
1961     }
1962     avp.clear();
1963     return afs;
1964   }
1965
1966   /**
1967    * Add Groovy Support to Jalview
1968    */
1969   public void groovyShell_actionPerformed(ActionEvent e)
1970   {
1971     // use reflection to avoid creating compilation dependency.
1972     if (!jalview.bin.Cache.groovyJarsPresent())
1973     {
1974       throw new Error(
1975               "Implementation Error. Cannot create groovyShell without Groovy on the classpath!");
1976     }
1977     try
1978     {
1979       Class gcClass = Desktop.class.getClassLoader().loadClass(
1980               "groovy.ui.Console");
1981       Constructor gccons = gcClass.getConstructor(null);
1982       java.lang.reflect.Method setvar = gcClass.getMethod("setVariable",
1983               new Class[]
1984               { String.class, Object.class });
1985       java.lang.reflect.Method run = gcClass.getMethod("run", null);
1986       Object gc = gccons.newInstance(null);
1987       setvar.invoke(gc, new Object[]
1988       { "Jalview", this });
1989       run.invoke(gc, null);
1990     } catch (Exception ex)
1991     {
1992       jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
1993       JOptionPane
1994               .showInternalMessageDialog(
1995                       Desktop.desktop,
1996
1997                       "Couldn't create the groovy Shell. Check the error log for the details of what went wrong.",
1998                       "Jalview Groovy Support Failed",
1999                       JOptionPane.ERROR_MESSAGE);
2000     }
2001   }
2002
2003   /**
2004    * Progress bars managed by the IProgressIndicator method.
2005    */
2006   private Hashtable progressBars, progressBarHandlers;
2007
2008   /*
2009    * (non-Javadoc)
2010    * 
2011    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2012    */
2013   public void setProgressBar(String message, long id)
2014   {
2015     if (progressBars == null)
2016     {
2017       progressBars = new Hashtable();
2018       progressBarHandlers = new Hashtable();
2019     }
2020
2021     if (progressBars.get(new Long(id)) != null)
2022     {
2023       JProgressBar progressPanel = (JProgressBar) progressBars
2024               .remove(new Long(id));
2025       if (progressBarHandlers.contains(new Long(id)))
2026       {
2027         progressBarHandlers.remove(new Long(id));
2028       }
2029       removeProgressPanel(progressPanel);
2030     }
2031     else
2032     {
2033       progressBars.put(new Long(id), addProgressPanel(message));
2034     }
2035   }
2036
2037   /*
2038    * (non-Javadoc)
2039    * 
2040    * @see jalview.gui.IProgressIndicator#registerHandler(long,
2041    * jalview.gui.IProgressIndicatorHandler)
2042    */
2043   public void registerHandler(final long id,
2044           final IProgressIndicatorHandler handler)
2045   {
2046     if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
2047     {
2048       throw new Error(
2049               "call setProgressBar before registering the progress bar's handler.");
2050     }
2051     progressBarHandlers.put(new Long(id), handler);
2052     final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
2053     if (handler.canCancel())
2054     {
2055       JButton cancel = new JButton("Cancel");
2056       final IProgressIndicator us = this;
2057       cancel.addActionListener(new ActionListener()
2058       {
2059
2060         public void actionPerformed(ActionEvent e)
2061         {
2062           handler.cancelActivity(id);
2063           us.setProgressBar("Cancelled "
2064                   + ((JLabel) progressPanel.getComponent(0)).getText(), id);
2065         }
2066       });
2067       progressPanel.add(cancel, BorderLayout.EAST);
2068     }
2069   }
2070
2071   /**
2072    * This will return the first AlignFrame viewing AlignViewport av. It will
2073    * break if there are more than one AlignFrames viewing a particular av. This
2074    * 
2075    * @param av
2076    * @return alignFrame for av
2077    */
2078   public static AlignFrame getAlignFrameFor(AlignViewport av)
2079   {
2080     if (desktop != null)
2081     {
2082       AlignmentPanel[] aps = getAlignmentPanels(av.getSequenceSetId());
2083       for (int panel = 0; aps != null && panel < aps.length; panel++)
2084       {
2085         if (aps[panel] != null && aps[panel].av == av)
2086         {
2087           return aps[panel].alignFrame;
2088         }
2089       }
2090     }
2091     return null;
2092   }
2093
2094   public VamsasApplication getVamsasApplication()
2095   {
2096     return v_client;
2097
2098   }
2099   /**
2100    * flag set if jalview GUI is being operated programmatically
2101    */
2102   private boolean inBatchMode=false;
2103
2104   /**
2105    * check if jalview GUI is being operated programmatically
2106    * @return inBatchMode
2107    */
2108   public boolean isInBatchMode()
2109   {
2110     return inBatchMode;
2111   }
2112
2113   /**
2114    * set flag if jalview GUI is being operated programmatically
2115    * @param inBatchMode 
2116    */
2117   public void setInBatchMode(boolean inBatchMode)
2118   {
2119     this.inBatchMode = inBatchMode;
2120   }
2121
2122
2123   public void startServiceDiscovery()
2124   {
2125     startServiceDiscovery(false);
2126   }
2127
2128   public void startServiceDiscovery(boolean blocking)
2129   {
2130     boolean alive = true;
2131     Thread t0 = null, t1 = null, t2 = null;
2132
2133     // todo: changesupport handlers need to be transferred
2134     if (discoverer == null)
2135     {
2136       discoverer = new jalview.ws.jws1.Discoverer();
2137       // register PCS handler for desktop.
2138       discoverer.addPropertyChangeListener(changeSupport);
2139     }
2140     if (Cache.getDefault("SHOW_JWS1_SERVICES", true))
2141     {
2142       (t0 = new Thread(discoverer)).start();
2143     }
2144
2145     try
2146     {
2147       if (Cache.getDefault("SHOW_ENFIN_SERVICES", true))
2148       {
2149         // EnfinEnvision web service menu entries are rebuild every time the
2150         // menu is shown, so no changeSupport events are needed.
2151         jalview.ws.EnfinEnvision2OneWay.getInstance();
2152         (t1 = new Thread(jalview.ws.EnfinEnvision2OneWay.getInstance()))
2153                 .start();
2154       }
2155     } catch (Exception e)
2156     {
2157       Cache.log
2158               .info(
2159                       "Exception when trying to launch Envision2 workflow discovery.",
2160                       e);
2161       Cache.log.info(e.getStackTrace());
2162     }
2163     if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2164     {
2165       jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2166               .addPropertyChangeListener(changeSupport);
2167       (t2 = new Thread(jalview.ws.jws2.Jws2Discoverer.getDiscoverer()))
2168               .start();
2169     }
2170     if (blocking)
2171     {
2172       while (alive)
2173       {
2174         try
2175         {
2176           Thread.sleep(15);
2177         } catch (Exception e)
2178         {
2179         }
2180         alive = (t1 != null && t1.isAlive())
2181                 || (t2 != null && t2.isAlive())
2182                 || (t0 != null && t0.isAlive());
2183       }
2184     }
2185   }
2186
2187   /**
2188    * 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.
2189    * @param url
2190    */
2191   public static void showUrl(final String url)
2192   {
2193     new Thread(new Runnable() {
2194       public void run() {
2195         try {
2196           jalview.util.BrowserLauncher.openURL(url);
2197         } catch (Exception ex)
2198         {    
2199           JOptionPane
2200                   .showInternalMessageDialog(
2201                           Desktop.desktop,
2202                           "Unixers: Couldn't find default web browser."
2203                                   + "\nAdd the full path to your browser in Preferences.",
2204                           "Web browser not found", JOptionPane.WARNING_MESSAGE);
2205
2206           ex.printStackTrace();
2207         }
2208       }
2209     }).start();    
2210   }
2211
2212   public static WsParamSetManager wsparamManager = null;
2213   public static ParamManager getUserParameterStore()
2214   {
2215     if (wsparamManager==null)
2216     {
2217       wsparamManager = new WsParamSetManager();
2218     }
2219     return wsparamManager;
2220   }
2221
2222 }