applied 2009 GPL license
[jalview.git] / src / jalview / gui / Desktop.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4.0.b2)
3  * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.gui;
20
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.lang.reflect.Constructor;
28 import java.util.*;
29
30 import javax.swing.*;
31 import javax.swing.event.MenuEvent;
32 import javax.swing.event.MenuListener;
33
34 /**
35  * DOCUMENT ME!
36  * 
37  * @author $author$
38  * @version $Revision$
39  */
40 public class Desktop extends jalview.jbgui.GDesktop implements
41         DropTargetListener, ClipboardOwner, IProgressIndicator
42 {
43   /** DOCUMENT ME!! */
44   public static Desktop instance;
45
46   // Need to decide if the Memory Usage is to be included in
47   // Next release or not.
48   public static MyDesktopPane desktop;
49
50   // public static JDesktopPane desktop;
51
52   static int openFrameCount = 0;
53
54   static final int xOffset = 30;
55
56   static final int yOffset = 30;
57
58   public static jalview.ws.Discoverer discoverer;
59
60   public static Object[] jalviewClipboard;
61
62   public static boolean internalCopy = false;
63
64   static int fileLoadingCount = 0;
65
66   /**
67    * Creates a new Desktop object.
68    */
69   public Desktop()
70   {
71     /**
72      * A note to implementors. It is ESSENTIAL that any activities that might
73      * block are spawned off as threads rather than waited for during this
74      * constructor.
75      */
76     instance = this;
77     doVamsasClientCheck();
78     doGroovyCheck();
79
80     setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
81     setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
82     boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
83             false);
84     desktop = new MyDesktopPane(selmemusage);
85     showMemusage.setSelected(selmemusage);
86     desktop.setBackground(Color.white);
87     getContentPane().setLayout(new BorderLayout());
88     getContentPane().add(desktop, BorderLayout.CENTER);
89     desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
90
91     // This line prevents Windows Look&Feel resizing all new windows to maximum
92     // if previous window was maximised
93     desktop.setDesktopManager(new DefaultDesktopManager());
94
95     Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
96     String x = jalview.bin.Cache.getProperty("SCREEN_X");
97     String y = jalview.bin.Cache.getProperty("SCREEN_Y");
98     String width = jalview.bin.Cache.getProperty("SCREEN_WIDTH");
99     String height = jalview.bin.Cache.getProperty("SCREEN_HEIGHT");
100
101     if ((x != null) && (y != null) && (width != null) && (height != null))
102     {
103       setBounds(Integer.parseInt(x), Integer.parseInt(y), Integer
104               .parseInt(width), Integer.parseInt(height));
105     }
106     else
107     {
108       setBounds((int) (screenSize.width - 900) / 2,
109               (int) (screenSize.height - 650) / 2, 900, 650);
110     }
111
112     this.addWindowListener(new WindowAdapter()
113     {
114       public void windowClosing(WindowEvent evt)
115       {
116         quit();
117       }
118     });
119
120     this.addMouseListener(new MouseAdapter()
121     {
122       public void mousePressed(MouseEvent evt)
123       {
124         if (SwingUtilities.isRightMouseButton(evt))
125         {
126           showPasteMenu(evt.getX(), evt.getY());
127         }
128       }
129     });
130
131     this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
132     // Spawn a thread that shows the splashscreen
133     new SplashScreen();
134
135     discoverer = new jalview.ws.Discoverer(); // Only gets started if gui is
136                                               // displayed.
137   }
138
139   private void doVamsasClientCheck()
140   {
141     if (jalview.bin.Cache.vamsasJarsPresent())
142     {
143       setupVamsasDisconnectedGui();
144       VamsasMenu.setVisible(true);
145       final Desktop us = this;
146       VamsasMenu.addMenuListener(new MenuListener()
147       {
148         // this listener remembers when the menu was first selected, and
149         // doesn't rebuild the session list until it has been cleared and
150         // reselected again.
151         boolean refresh = true;
152
153         public void menuCanceled(MenuEvent e)
154         {
155           refresh = true;
156         }
157
158         public void menuDeselected(MenuEvent e)
159         {
160           refresh = true;
161         }
162
163         public void menuSelected(MenuEvent e)
164         {
165           if (refresh)
166           {
167             us.buildVamsasStMenu();
168             refresh = false;
169           }
170         }
171       });
172       vamsasStart.setVisible(true);
173     }
174   }
175
176   void showPasteMenu(int x, int y)
177   {
178     JPopupMenu popup = new JPopupMenu();
179     JMenuItem item = new JMenuItem("Paste To New Window");
180     item.addActionListener(new ActionListener()
181     {
182       public void actionPerformed(ActionEvent evt)
183       {
184         paste();
185       }
186     });
187
188     popup.add(item);
189     popup.show(this, x, y);
190   }
191
192   public void paste()
193   {
194     try
195     {
196       Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
197       Transferable contents = c.getContents(this);
198
199       if (contents != null)
200       {
201         String file = (String) contents
202                 .getTransferData(DataFlavor.stringFlavor);
203
204         String format = new IdentifyFile().Identify(file,
205                 FormatAdapter.PASTE);
206
207         new FileLoader().LoadFile(file, FormatAdapter.PASTE, format);
208
209       }
210     } catch (Exception ex)
211     {
212       System.out
213               .println("Unable to paste alignment from system clipboard:\n"
214                       + ex);
215     }
216   }
217
218   /**
219    * DOCUMENT ME!
220    * 
221    * @param frame
222    *                DOCUMENT ME!
223    * @param title
224    *                DOCUMENT ME!
225    * @param w
226    *                DOCUMENT ME!
227    * @param h
228    *                DOCUMENT ME!
229    */
230   public static synchronized void addInternalFrame(
231           final JInternalFrame frame, String title, int w, int h)
232   {
233     addInternalFrame(frame, title, w, h, true);
234   }
235
236   /**
237    * DOCUMENT ME!
238    * 
239    * @param frame
240    *                DOCUMENT ME!
241    * @param title
242    *                DOCUMENT ME!
243    * @param w
244    *                DOCUMENT ME!
245    * @param h
246    *                DOCUMENT ME!
247    * @param resizable
248    *                DOCUMENT ME!
249    */
250   public static synchronized void addInternalFrame(
251           final JInternalFrame frame, String title, int w, int h,
252           boolean resizable)
253   {
254
255     // TODO: consider fixing method to update entries in the window submenu with
256     // the current window title
257
258     frame.setTitle(title);
259     if (frame.getWidth() < 1 || frame.getHeight() < 1)
260     {
261       frame.setSize(w, h);
262     }
263     // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
264     // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
265     // IF JALVIEW IS RUNNING HEADLESS
266     // ///////////////////////////////////////////////
267     if (System.getProperty("java.awt.headless") != null
268             && System.getProperty("java.awt.headless").equals("true"))
269     {
270       return;
271     }
272
273     openFrameCount++;
274
275     frame.setVisible(true);
276     frame.setClosable(true);
277     frame.setResizable(resizable);
278     frame.setMaximizable(resizable);
279     frame.setIconifiable(resizable);
280     frame.setFrameIcon(null);
281
282     if (frame.getX() < 1 && frame.getY() < 1)
283     {
284       frame.setLocation(xOffset * openFrameCount, yOffset
285               * ((openFrameCount - 1) % 10) + yOffset);
286     }
287
288     final JMenuItem menuItem = new JMenuItem(title);
289     frame
290             .addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
291             {
292               public void internalFrameActivated(
293                       javax.swing.event.InternalFrameEvent evt)
294               {
295                 JInternalFrame itf = desktop.getSelectedFrame();
296                 if (itf != null)
297                 {
298                   itf.requestFocus();
299                 }
300
301               }
302
303               public void internalFrameClosed(
304                       javax.swing.event.InternalFrameEvent evt)
305               {
306                 PaintRefresher.RemoveComponent(frame);
307                 openFrameCount--;
308                 windowMenu.remove(menuItem);
309                 JInternalFrame itf = desktop.getSelectedFrame();
310                 if (itf != null)
311                 {
312                   itf.requestFocus();
313                 }
314                 System.gc();
315               };
316             });
317
318     menuItem.addActionListener(new ActionListener()
319     {
320       public void actionPerformed(ActionEvent e)
321       {
322         try
323         {
324           frame.setSelected(true);
325           frame.setIcon(false);
326         } catch (java.beans.PropertyVetoException ex)
327         {
328
329         }
330       }
331     });
332
333     windowMenu.add(menuItem);
334
335     desktop.add(frame);
336     frame.toFront();
337     try
338     {
339       frame.setSelected(true);
340       frame.requestFocus();
341     } catch (java.beans.PropertyVetoException ve)
342     {
343     }
344   }
345
346   public void lostOwnership(Clipboard clipboard, Transferable contents)
347   {
348     if (!internalCopy)
349     {
350       Desktop.jalviewClipboard = null;
351     }
352
353     internalCopy = false;
354   }
355
356   public void dragEnter(DropTargetDragEvent evt)
357   {
358   }
359
360   public void dragExit(DropTargetEvent evt)
361   {
362   }
363
364   public void dragOver(DropTargetDragEvent evt)
365   {
366   }
367
368   public void dropActionChanged(DropTargetDragEvent evt)
369   {
370   }
371
372   /**
373    * DOCUMENT ME!
374    * 
375    * @param evt
376    *                DOCUMENT ME!
377    */
378   public void drop(DropTargetDropEvent evt)
379   {
380     Transferable t = evt.getTransferable();
381     java.util.List files = null;
382
383     try
384     {
385       DataFlavor uriListFlavor = new DataFlavor(
386               "text/uri-list;class=java.lang.String");
387       if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
388       {
389         // Works on Windows and MacOSX
390         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
391         files = (java.util.List) t
392                 .getTransferData(DataFlavor.javaFileListFlavor);
393       }
394       else if (t.isDataFlavorSupported(uriListFlavor))
395       {
396         // This is used by Unix drag system
397         evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
398         String data = (String) t.getTransferData(uriListFlavor);
399         files = new java.util.ArrayList(1);
400         for (java.util.StringTokenizer st = new java.util.StringTokenizer(
401                 data, "\r\n"); st.hasMoreTokens();)
402         {
403           String s = st.nextToken();
404           if (s.startsWith("#"))
405           {
406             // the line is a comment (as per the RFC 2483)
407             continue;
408           }
409
410           java.net.URI uri = new java.net.URI(s);
411           java.io.File file = new java.io.File(uri);
412           files.add(file);
413         }
414       }
415     } catch (Exception e)
416     {
417     }
418
419     if (files != null)
420     {
421       try
422       {
423         for (int i = 0; i < files.size(); i++)
424         {
425           String file = files.get(i).toString();
426           String protocol = FormatAdapter.FILE;
427           String format = null;
428
429           if (file.endsWith(".jar"))
430           {
431             format = "Jalview";
432
433           }
434           else
435           {
436             format = new IdentifyFile().Identify(file, protocol);
437           }
438
439           new FileLoader().LoadFile(file, protocol, format);
440
441         }
442       } catch (Exception ex)
443       {
444       }
445     }
446   }
447
448   /**
449    * DOCUMENT ME!
450    * 
451    * @param e
452    *                DOCUMENT ME!
453    */
454   public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
455   {
456     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
457             .getProperty("LAST_DIRECTORY"),
458             jalview.io.AppletFormatAdapter.READABLE_EXTENSIONS,
459             jalview.io.AppletFormatAdapter.READABLE_FNAMES,
460             jalview.bin.Cache.getProperty("DEFAULT_FILE_FORMAT"));
461
462     chooser.setFileView(new JalviewFileView());
463     chooser.setDialogTitle("Open local file");
464     chooser.setToolTipText("Open");
465
466     int value = chooser.showOpenDialog(this);
467
468     if (value == JalviewFileChooser.APPROVE_OPTION)
469     {
470       String choice = chooser.getSelectedFile().getPath();
471       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
472               .getSelectedFile().getParent());
473
474       String format = null;
475       if (chooser.getSelectedFormat().equals("Jalview"))
476       {
477         format = "Jalview";
478       }
479       else
480       {
481         format = new IdentifyFile().Identify(choice, FormatAdapter.FILE);
482       }
483
484       if (viewport != null)
485       {
486         new FileLoader().LoadFile(viewport, choice, FormatAdapter.FILE,
487                 format);
488       }
489       else
490       {
491         new FileLoader().LoadFile(choice, FormatAdapter.FILE, format);
492       }
493     }
494   }
495
496   /**
497    * DOCUMENT ME!
498    * 
499    * @param e
500    *                DOCUMENT ME!
501    */
502   public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
503   {
504     // This construct allows us to have a wider textfield
505     // for viewing
506     JLabel label = new JLabel("Enter URL of Input File");
507     final JComboBox history = new JComboBox();
508
509     JPanel panel = new JPanel(new GridLayout(2, 1));
510     panel.add(label);
511     panel.add(history);
512     history.setPreferredSize(new Dimension(400, 20));
513     history.setEditable(true);
514     history.addItem("http://www.");
515
516     String historyItems = jalview.bin.Cache.getProperty("RECENT_URL");
517
518     StringTokenizer st;
519
520     if (historyItems != null)
521     {
522       st = new StringTokenizer(historyItems, "\t");
523
524       while (st.hasMoreTokens())
525       {
526         history.addItem(st.nextElement());
527       }
528     }
529
530     int reply = JOptionPane.showInternalConfirmDialog(desktop, panel,
531             "Input Alignment From URL", JOptionPane.OK_CANCEL_OPTION);
532
533     if (reply != JOptionPane.OK_OPTION)
534     {
535       return;
536     }
537
538     String url = history.getSelectedItem().toString();
539
540     if (url.toLowerCase().endsWith(".jar"))
541     {
542       if (viewport != null)
543       {
544         new FileLoader().LoadFile(viewport, url, FormatAdapter.URL,
545                 "Jalview");
546       }
547       else
548       {
549         new FileLoader().LoadFile(url, FormatAdapter.URL, "Jalview");
550       }
551     }
552     else
553     {
554       String format = new IdentifyFile().Identify(url, FormatAdapter.URL);
555
556       if (format.equals("URL NOT FOUND"))
557       {
558         JOptionPane.showInternalMessageDialog(Desktop.desktop,
559                 "Couldn't locate " + url, "URL not found",
560                 JOptionPane.WARNING_MESSAGE);
561
562         return;
563       }
564
565       if (viewport != null)
566       {
567         new FileLoader().LoadFile(viewport, url, FormatAdapter.URL, format);
568       }
569       else
570       {
571         new FileLoader().LoadFile(url, FormatAdapter.URL, format);
572       }
573     }
574   }
575
576   /**
577    * DOCUMENT ME!
578    * 
579    * @param e
580    *                DOCUMENT ME!
581    */
582   public void inputTextboxMenuItem_actionPerformed(AlignViewport viewport)
583   {
584     CutAndPasteTransfer cap = new CutAndPasteTransfer();
585     cap.setForInput(viewport);
586     Desktop.addInternalFrame(cap, "Cut & Paste Alignment File", 600, 500);
587   }
588
589   /*
590    * Exit the program
591    */
592   public void quit()
593   {
594     jalview.bin.Cache.setProperty("SCREEN_X", getBounds().x + "");
595     jalview.bin.Cache.setProperty("SCREEN_Y", getBounds().y + "");
596     jalview.bin.Cache.setProperty("SCREEN_WIDTH", getWidth() + "");
597     jalview.bin.Cache.setProperty("SCREEN_HEIGHT", getHeight() + "");
598     System.exit(0);
599   }
600
601   /**
602    * DOCUMENT ME!
603    * 
604    * @param e
605    *                DOCUMENT ME!
606    */
607   public void aboutMenuItem_actionPerformed(ActionEvent e)
608   {
609     StringBuffer message = new StringBuffer("Jalview version "
610             + jalview.bin.Cache.getProperty("VERSION") + "; last updated: "
611             + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
612
613     if (!jalview.bin.Cache.getProperty("LATEST_VERSION").equals(
614             jalview.bin.Cache.getProperty("VERSION")))
615     {
616       message
617               .append("\n\n!! Jalview version "
618                       + jalview.bin.Cache.getProperty("LATEST_VERSION")
619                       + " is available for download from http://www.jalview.org !!\n");
620
621     }
622     // TODO: update this text for each release or centrally store it for lite
623     // and application
624     message
625             .append("\nAuthors:  Andrew Waterhouse, Jim Procter, Michele Clamp, James Cuff, Steve Searle,\n    David Martin & Geoff Barton."
626                     + "\nDevelopment managed by The Barton Group, University of Dundee, Scotland, UK.\n"
627                     + "\nFor help, see the FAQ at www.jalview.org and/or join the jalview-discuss@jalview.org mailing list\n"
628                     + "\nIf  you use Jalview, please cite:"
629                     + "\nWaterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
630                     + "\nJalview Version 2 - a multiple sequence alignment editor and analysis workbench"
631                     + "\nBioinformatics doi: 10.1093/bioinformatics/btp033");
632     JOptionPane.showInternalMessageDialog(Desktop.desktop,
633
634     message.toString(), "About Jalview", JOptionPane.INFORMATION_MESSAGE);
635   }
636
637   /**
638    * DOCUMENT ME!
639    * 
640    * @param e
641    *                DOCUMENT ME!
642    */
643   public void documentationMenuItem_actionPerformed(ActionEvent e)
644   {
645     try
646     {
647       ClassLoader cl = jalview.gui.Desktop.class.getClassLoader();
648       java.net.URL url = javax.help.HelpSet.findHelpSet(cl, "help/help");
649       javax.help.HelpSet hs = new javax.help.HelpSet(cl, url);
650
651       javax.help.HelpBroker hb = hs.createHelpBroker();
652       hb.setCurrentID("home");
653       hb.setDisplayed(true);
654     } catch (Exception ex)
655     {
656     }
657   }
658
659   public void closeAll_actionPerformed(ActionEvent e)
660   {
661     JInternalFrame[] frames = desktop.getAllFrames();
662     for (int i = 0; i < frames.length; i++)
663     {
664       try
665       {
666         frames[i].setClosed(true);
667       } catch (java.beans.PropertyVetoException ex)
668       {
669       }
670     }
671     System.out.println("ALL CLOSED");
672
673   }
674
675   public void raiseRelated_actionPerformed(ActionEvent e)
676   {
677     reorderAssociatedWindows(false, false);
678   }
679
680   public void minimizeAssociated_actionPerformed(ActionEvent e)
681   {
682     reorderAssociatedWindows(true, false);
683   }
684
685   void closeAssociatedWindows()
686   {
687     reorderAssociatedWindows(false, true);
688   }
689
690   /*
691    * (non-Javadoc)
692    * 
693    * @see jalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.ActionEvent)
694    */
695   protected void garbageCollect_actionPerformed(ActionEvent e)
696   {
697     // We simply collect the garbage
698     jalview.bin.Cache.log.debug("Collecting garbage...");
699     System.gc();
700     jalview.bin.Cache.log.debug("Finished garbage collection.");
701   }
702
703   /*
704    * (non-Javadoc)
705    * 
706    * @see jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent)
707    */
708   protected void showMemusage_actionPerformed(ActionEvent e)
709   {
710     desktop.showMemoryUsage(showMemusage.isSelected());
711   }
712
713   void reorderAssociatedWindows(boolean minimize, boolean close)
714   {
715     JInternalFrame[] frames = desktop.getAllFrames();
716     if (frames == null || frames.length < 1)
717     {
718       return;
719     }
720
721     AlignViewport source = null, target = null;
722     if (frames[0] instanceof AlignFrame)
723     {
724       source = ((AlignFrame) frames[0]).getCurrentView();
725     }
726     else if (frames[0] instanceof TreePanel)
727     {
728       source = ((TreePanel) frames[0]).getViewPort();
729     }
730     else if (frames[0] instanceof PCAPanel)
731     {
732       source = ((PCAPanel) frames[0]).av;
733     }
734     else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
735     {
736       source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
737     }
738
739     if (source != null)
740     {
741       for (int i = 0; i < frames.length; i++)
742       {
743         target = null;
744         if (frames[i] == null)
745         {
746           continue;
747         }
748         if (frames[i] instanceof AlignFrame)
749         {
750           target = ((AlignFrame) frames[i]).getCurrentView();
751         }
752         else if (frames[i] instanceof TreePanel)
753         {
754           target = ((TreePanel) frames[i]).getViewPort();
755         }
756         else if (frames[i] instanceof PCAPanel)
757         {
758           target = ((PCAPanel) frames[i]).av;
759         }
760         else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
761         {
762           target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
763         }
764
765         if (source == target)
766         {
767           try
768           {
769             if (close)
770             {
771               frames[i].setClosed(true);
772             }
773             else
774             {
775               frames[i].setIcon(minimize);
776               if (!minimize)
777               {
778                 frames[i].toFront();
779               }
780             }
781
782           } catch (java.beans.PropertyVetoException ex)
783           {
784           }
785         }
786       }
787     }
788   }
789
790   /**
791    * DOCUMENT ME!
792    * 
793    * @param e
794    *                DOCUMENT ME!
795    */
796   protected void preferences_actionPerformed(ActionEvent e)
797   {
798     new Preferences();
799   }
800
801   /**
802    * DOCUMENT ME!
803    * 
804    * @param e
805    *                DOCUMENT ME!
806    */
807   public void saveState_actionPerformed(ActionEvent e)
808   {
809     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
810             .getProperty("LAST_DIRECTORY"), new String[]
811     { "jar" }, new String[]
812     { "Jalview Project" }, "Jalview Project");
813
814     chooser.setFileView(new JalviewFileView());
815     chooser.setDialogTitle("Save State");
816
817     int value = chooser.showSaveDialog(this);
818
819     if (value == JalviewFileChooser.APPROVE_OPTION)
820     {
821       java.io.File choice = chooser.getSelectedFile();
822       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
823       new Jalview2XML().SaveState(choice);
824     }
825   }
826
827   /**
828    * DOCUMENT ME!
829    * 
830    * @param e
831    *                DOCUMENT ME!
832    */
833   public void loadState_actionPerformed(ActionEvent e)
834   {
835     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
836             .getProperty("LAST_DIRECTORY"), new String[]
837     { "jar" }, new String[]
838     { "Jalview Project" }, "Jalview Project");
839     chooser.setFileView(new JalviewFileView());
840     chooser.setDialogTitle("Restore state");
841
842     int value = chooser.showOpenDialog(this);
843
844     if (value == JalviewFileChooser.APPROVE_OPTION)
845     {
846       String choice = chooser.getSelectedFile().getAbsolutePath();
847       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
848               .getSelectedFile().getParent());
849       new Jalview2XML().LoadJalviewAlign(choice);
850     }
851   }
852
853   public void inputSequence_actionPerformed(ActionEvent e)
854   {
855     new SequenceFetcher(this);
856   }
857
858   JPanel progressPanel;
859
860   public void startLoading(final String fileName)
861   {
862     if (fileLoadingCount == 0)
863     {
864       addProgressPanel("Loading File: " + fileName + "   ");
865
866     }
867     fileLoadingCount++;
868   }
869
870   private JProgressBar addProgressPanel(String string)
871   {
872     if (progressPanel == null)
873     {
874       progressPanel = new JPanel(new BorderLayout());
875       totalProgressCount = 0;
876     }
877     JProgressBar progressBar = new JProgressBar();
878     progressBar.setIndeterminate(true);
879
880     progressPanel.add(new JLabel(string), BorderLayout.WEST);
881
882     progressPanel.add(progressBar, BorderLayout.CENTER);
883
884     instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
885     totalProgressCount++;
886     validate();
887     return progressBar;
888   }
889
890   int totalProgressCount = 0;
891
892   private void removeProgressPanel(JProgressBar progbar)
893   {
894     if (progressPanel != null)
895     {
896       progressPanel.remove(progbar);
897       if (--totalProgressCount < 1)
898       {
899         this.getContentPane().remove(progressPanel);
900         progressPanel = null;
901       }
902     }
903     validate();
904   }
905
906   public void stopLoading()
907   {
908     fileLoadingCount--;
909     if (fileLoadingCount < 1)
910     {
911       if (progressPanel != null)
912       {
913         this.getContentPane().remove(progressPanel);
914         progressPanel = null;
915       }
916       fileLoadingCount = 0;
917     }
918     validate();
919   }
920
921   public static int getViewCount(String viewId)
922   {
923     int count = 0;
924     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
925     for (int t = 0; t < frames.length; t++)
926     {
927       if (frames[t] instanceof AlignFrame)
928       {
929         AlignFrame af = (AlignFrame) frames[t];
930         for (int a = 0; a < af.alignPanels.size(); a++)
931         {
932           if (viewId
933                   .equals(((AlignmentPanel) af.alignPanels.elementAt(a)).av
934                           .getSequenceSetId()))
935           {
936             count++;
937           }
938         }
939       }
940     }
941
942     return count;
943   }
944
945   public void explodeViews(AlignFrame af)
946   {
947     int size = af.alignPanels.size();
948     if (size < 2)
949     {
950       return;
951     }
952
953     for (int i = 0; i < size; i++)
954     {
955       AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(i);
956       AlignFrame newaf = new AlignFrame(ap);
957       if (ap.av.explodedPosition != null
958               && !ap.av.explodedPosition.equals(af.getBounds()))
959       {
960         newaf.setBounds(ap.av.explodedPosition);
961       }
962
963       ap.av.gatherViewsHere = false;
964
965       addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
966               AlignFrame.DEFAULT_HEIGHT);
967     }
968
969     af.alignPanels.clear();
970     af.closeMenuItem_actionPerformed(true);
971
972   }
973
974   public void gatherViews(AlignFrame source)
975   {
976     source.viewport.gatherViewsHere = true;
977     source.viewport.explodedPosition = source.getBounds();
978     JInternalFrame[] frames = desktop.getAllFrames();
979     String viewId = source.viewport.sequenceSetID;
980
981     for (int t = 0; t < frames.length; t++)
982     {
983       if (frames[t] instanceof AlignFrame && frames[t] != source)
984       {
985         AlignFrame af = (AlignFrame) frames[t];
986         boolean gatherThis = false;
987         for (int a = 0; a < af.alignPanels.size(); a++)
988         {
989           AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(a);
990           if (viewId.equals(ap.av.getSequenceSetId()))
991           {
992             gatherThis = true;
993             ap.av.gatherViewsHere = false;
994             ap.av.explodedPosition = af.getBounds();
995             source.addAlignmentPanel(ap, false);
996           }
997         }
998
999         if (gatherThis)
1000         {
1001           af.alignPanels.clear();
1002           af.closeMenuItem_actionPerformed(true);
1003         }
1004       }
1005     }
1006
1007   }
1008
1009   jalview.gui.VamsasApplication v_client = null;
1010
1011   public void vamsasImport_actionPerformed(ActionEvent e)
1012   {
1013     if (v_client == null)
1014     {
1015       // Load and try to start a session.
1016       JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
1017               .getProperty("LAST_DIRECTORY"));
1018
1019       chooser.setFileView(new JalviewFileView());
1020       chooser.setDialogTitle("Open a saved VAMSAS session");
1021       chooser
1022               .setToolTipText("select a vamsas session to be opened as a new vamsas session.");
1023
1024       int value = chooser.showOpenDialog(this);
1025
1026       if (value == JalviewFileChooser.APPROVE_OPTION)
1027       {
1028         try
1029         {
1030           v_client = new jalview.gui.VamsasApplication(this, chooser
1031                   .getSelectedFile());
1032         } catch (Exception ex)
1033         {
1034           jalview.bin.Cache.log.error(
1035                   "New vamsas session from existing session file failed:",
1036                   ex);
1037           return;
1038         }
1039         setupVamsasConnectedGui();
1040         v_client.initial_update(); // TODO: thread ?
1041       }
1042     }
1043     else
1044     {
1045       jalview.bin.Cache.log
1046               .error("Implementation error - load session from a running session is not supported.");
1047     }
1048   }
1049
1050   public void vamsasStart_actionPerformed(ActionEvent e)
1051   {
1052     if (v_client == null)
1053     {
1054       // Start a session.
1055       // we just start a default session for moment.
1056       /*
1057        * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
1058        * getProperty("LAST_DIRECTORY"));
1059        * 
1060        * chooser.setFileView(new JalviewFileView());
1061        * chooser.setDialogTitle("Load Vamsas file");
1062        * chooser.setToolTipText("Import");
1063        * 
1064        * int value = chooser.showOpenDialog(this);
1065        * 
1066        * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
1067        * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
1068        * 
1069        */
1070       v_client = new VamsasApplication(this);
1071       setupVamsasConnectedGui();
1072       v_client.initial_update(); // TODO: thread ?
1073     }
1074     else
1075     {
1076       // store current data in session.
1077       v_client.push_update(); // TODO: thread
1078     }
1079   }
1080
1081   protected void setupVamsasConnectedGui()
1082   {
1083     vamsasStart.setText("Session Update");
1084     vamsasSave.setVisible(true);
1085     vamsasStop.setVisible(true);
1086     vamsasImport.setVisible(false); // Document import to existing session is
1087                                     // not possible for vamsas-client-1.0.
1088   }
1089
1090   protected void setupVamsasDisconnectedGui()
1091   {
1092     vamsasSave.setVisible(false);
1093     vamsasStop.setVisible(false);
1094     vamsasImport.setVisible(true);
1095     vamsasStart.setText("New Vamsas Session");
1096   }
1097
1098   public void vamsasStop_actionPerformed(ActionEvent e)
1099   {
1100     if (v_client != null)
1101     {
1102       v_client.end_session();
1103       v_client = null;
1104       setupVamsasDisconnectedGui();
1105     }
1106   }
1107
1108   protected void buildVamsasStMenu()
1109   {
1110     if (v_client == null)
1111     {
1112       String[] sess = null;
1113       try
1114       {
1115         sess = VamsasApplication.getSessionList();
1116       } catch (Exception e)
1117       {
1118         jalview.bin.Cache.log.warn(
1119                 "Problem getting current sessions list.", e);
1120         sess = null;
1121       }
1122       if (sess != null)
1123       {
1124         jalview.bin.Cache.log.debug("Got current sessions list: "
1125                 + sess.length + " entries.");
1126         VamsasStMenu.removeAll();
1127         for (int i = 0; i < sess.length; i++)
1128         {
1129           JMenuItem sessit = new JMenuItem();
1130           sessit.setText(sess[i]);
1131           sessit.setToolTipText("Connect to session " + sess[i]);
1132           final Desktop dsktp = this;
1133           final String mysesid = sess[i];
1134           sessit.addActionListener(new ActionListener()
1135           {
1136
1137             public void actionPerformed(ActionEvent e)
1138             {
1139               if (dsktp.v_client == null)
1140               {
1141                 Thread rthr = new Thread(new Runnable()
1142                 {
1143
1144                   public void run()
1145                   {
1146                     dsktp.v_client = new VamsasApplication(dsktp, mysesid);
1147                     dsktp.setupVamsasConnectedGui();
1148                     dsktp.v_client.initial_update();
1149                   }
1150
1151                 });
1152                 rthr.start();
1153               }
1154             };
1155           });
1156           VamsasStMenu.add(sessit);
1157         }
1158         // don't show an empty menu.
1159         VamsasStMenu.setVisible(sess.length > 0);
1160
1161       }
1162       else
1163       {
1164         jalview.bin.Cache.log.debug("No current vamsas sessions.");
1165         VamsasStMenu.removeAll();
1166         VamsasStMenu.setVisible(false);
1167       }
1168     }
1169     else
1170     {
1171       // Not interested in the content. Just hide ourselves.
1172       VamsasStMenu.setVisible(false);
1173     }
1174   }
1175
1176   public void vamsasSave_actionPerformed(ActionEvent e)
1177   {
1178     if (v_client != null)
1179     {
1180       JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
1181               .getProperty("LAST_DIRECTORY"), new String[]
1182       { "vdj" }, // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
1183               new String[]
1184               { "Vamsas Document" }, "Vamsas Document");
1185
1186       chooser.setFileView(new JalviewFileView());
1187       chooser.setDialogTitle("Save Vamsas Document Archive");
1188
1189       int value = chooser.showSaveDialog(this);
1190
1191       if (value == JalviewFileChooser.APPROVE_OPTION)
1192       {
1193         java.io.File choice = chooser.getSelectedFile();
1194         jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
1195         String warnmsg = null;
1196         String warnttl = null;
1197         try
1198         {
1199           v_client.vclient.storeDocument(choice);
1200         } catch (Error ex)
1201         {
1202           warnttl = "Serious Problem saving Vamsas Document";
1203           warnmsg = ex.toString();
1204           jalview.bin.Cache.log.error("Error Whilst saving document to "
1205                   + choice, ex);
1206
1207         } catch (Exception ex)
1208         {
1209           warnttl = "Problem saving Vamsas Document.";
1210           warnmsg = ex.toString();
1211           jalview.bin.Cache.log.warn("Exception Whilst saving document to "
1212                   + choice, ex);
1213
1214         }
1215         if (warnmsg != null)
1216         {
1217           JOptionPane.showInternalMessageDialog(Desktop.desktop,
1218
1219           warnmsg, warnttl, JOptionPane.ERROR_MESSAGE);
1220         }
1221       }
1222     }
1223   }
1224
1225   JProgressBar vamUpdate = null;
1226
1227   /**
1228    * hide vamsas user gui bits when a vamsas document event is being handled.
1229    * 
1230    * @param b
1231    *                true to hide gui, false to reveal gui
1232    */
1233   public void setVamsasUpdate(boolean b)
1234   {
1235     jalview.bin.Cache.log.debug("Setting gui for Vamsas update "
1236             + (b ? "in progress" : "finished"));
1237
1238     if (vamUpdate != null)
1239     {
1240       this.removeProgressPanel(vamUpdate);
1241     }
1242     if (b)
1243     {
1244       vamUpdate = this.addProgressPanel("Updating vamsas session");
1245     }
1246     vamsasStart.setVisible(!b);
1247     vamsasStop.setVisible(!b);
1248     vamsasSave.setVisible(!b);
1249   }
1250
1251   public JInternalFrame[] getAllFrames()
1252   {
1253     return desktop.getAllFrames();
1254   }
1255
1256   /**
1257    * Checks the given url to see if it gives a response indicating that the user
1258    * should be informed of a new questionnaire.
1259    * 
1260    * @param url
1261    */
1262   public void checkForQuestionnaire(String url)
1263   {
1264     UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
1265     // javax.swing.SwingUtilities.invokeLater(jvq);
1266     new Thread(jvq).start();
1267   }
1268
1269   /**
1270    * Proxy class for JDesktopPane which optionally displays the current memory
1271    * usage and highlights the desktop area with a red bar if free memory runs
1272    * low.
1273    * 
1274    * @author AMW
1275    */
1276   public class MyDesktopPane extends JDesktopPane implements Runnable
1277   {
1278
1279     boolean showMemoryUsage = false;
1280
1281     Runtime runtime;
1282
1283     java.text.NumberFormat df;
1284
1285     float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
1286             percentUsage;
1287
1288     public MyDesktopPane(boolean showMemoryUsage)
1289     {
1290       showMemoryUsage(showMemoryUsage);
1291     }
1292
1293     public void showMemoryUsage(boolean showMemoryUsage)
1294     {
1295       this.showMemoryUsage = showMemoryUsage;
1296       if (showMemoryUsage)
1297       {
1298         Thread worker = new Thread(this);
1299         worker.start();
1300       }
1301     }
1302
1303     public boolean isShowMemoryUsage()
1304     {
1305       return showMemoryUsage;
1306     }
1307
1308     public void run()
1309     {
1310       df = java.text.NumberFormat.getNumberInstance();
1311       df.setMaximumFractionDigits(2);
1312       runtime = Runtime.getRuntime();
1313
1314       while (showMemoryUsage)
1315       {
1316         try
1317         {
1318           Thread.sleep(3000);
1319           maxMemory = runtime.maxMemory() / 1048576f;
1320           allocatedMemory = runtime.totalMemory() / 1048576f;
1321           freeMemory = runtime.freeMemory() / 1048576f;
1322           totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
1323
1324           percentUsage = (totalFreeMemory / maxMemory) * 100;
1325
1326           // if (percentUsage < 20)
1327           {
1328             // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
1329             // Color.red);
1330             // instance.set.setBorder(border1);
1331           }
1332           repaint();
1333
1334         } catch (Exception ex)
1335         {
1336           ex.printStackTrace();
1337         }
1338       }
1339     }
1340
1341     public void paintComponent(Graphics g)
1342     {
1343       if (showMemoryUsage)
1344       {
1345         if (percentUsage < 20)
1346           g.setColor(Color.red);
1347
1348         g.drawString("Total Free Memory: " + df.format(totalFreeMemory)
1349                 + "MB; Max Memory: " + df.format(maxMemory) + "MB; "
1350                 + df.format(percentUsage) + "%", 10, getHeight()
1351                 - g.getFontMetrics().getHeight());
1352       }
1353     }
1354
1355   }
1356
1357   protected JMenuItem groovyShell;
1358
1359   public void doGroovyCheck()
1360   {
1361     if (jalview.bin.Cache.groovyJarsPresent())
1362     {
1363       groovyShell = new JMenuItem();
1364       groovyShell.setText("Groovy Console...");
1365       groovyShell.addActionListener(new ActionListener()
1366       {
1367         public void actionPerformed(ActionEvent e)
1368         {
1369           groovyShell_actionPerformed(e);
1370         }
1371       });
1372       toolsMenu.add(groovyShell);
1373       groovyShell.setVisible(true);
1374     }
1375   }
1376
1377   /**
1378    * Accessor method to quickly get all the AlignmentFrames loaded.
1379    */
1380   public static AlignFrame[] getAlignframes()
1381   {
1382     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1383
1384     if (frames == null)
1385     {
1386       return null;
1387     }
1388     Vector avp = new Vector();
1389     try
1390     {
1391       // REVERSE ORDER
1392       for (int i = frames.length - 1; i > -1; i--)
1393       {
1394         if (frames[i] instanceof AlignFrame)
1395         {
1396           AlignFrame af = (AlignFrame) frames[i];
1397           avp.addElement(af);
1398         }
1399       }
1400     } catch (Exception ex)
1401     {
1402       ex.printStackTrace();
1403     }
1404     if (avp.size() == 0)
1405     {
1406       return null;
1407     }
1408     AlignFrame afs[] = new AlignFrame[avp.size()];
1409     for (int i = 0, j = avp.size(); i < j; i++)
1410     {
1411       afs[i] = (AlignFrame) avp.elementAt(i);
1412     }
1413     avp.clear();
1414     return afs;
1415   }
1416
1417   /**
1418    * Add Groovy Support to Jalview
1419    */
1420   public void groovyShell_actionPerformed(ActionEvent e)
1421   {
1422     // use reflection to avoid creating compilation dependency.
1423     if (!jalview.bin.Cache.groovyJarsPresent())
1424     {
1425       throw new Error(
1426               "Implementation Error. Cannot create groovyShell without Groovy on the classpath!");
1427     }
1428     try
1429     {
1430       Class gcClass = Desktop.class.getClassLoader().loadClass(
1431               "groovy.ui.Console");
1432       Constructor gccons = gcClass.getConstructor(null);
1433       java.lang.reflect.Method setvar = gcClass.getMethod("setVariable",
1434               new Class[]
1435               { String.class, Object.class });
1436       java.lang.reflect.Method run = gcClass.getMethod("run", null);
1437       Object gc = gccons.newInstance(null);
1438       setvar.invoke(gc, new Object[]
1439       { "Jalview", this });
1440       run.invoke(gc, null);
1441     } catch (Exception ex)
1442     {
1443       jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
1444       JOptionPane
1445               .showInternalMessageDialog(
1446                       Desktop.desktop,
1447
1448                       "Couldn't create the groovy Shell. Check the error log for the details of what went wrong.",
1449                       "Jalview Groovy Support Failed",
1450                       JOptionPane.ERROR_MESSAGE);
1451     }
1452   }
1453
1454   /**
1455    * Progress bars managed by the IProgressIndicator method.
1456    */
1457   private Hashtable progressBars;
1458
1459   /*
1460    * (non-Javadoc)
1461    * 
1462    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
1463    */
1464   public void setProgressBar(String message, long id)
1465   {
1466     if (progressBars == null)
1467     {
1468       progressBars = new Hashtable();
1469     }
1470
1471     if (progressBars.get(new Long(id)) != null)
1472     {
1473       JProgressBar progressPanel = (JProgressBar) progressBars
1474               .remove(new Long(id));
1475       removeProgressPanel(progressPanel);
1476     }
1477     else
1478     {
1479       progressBars.put(new Long(id), addProgressPanel(message));
1480     }
1481   }
1482 }