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