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