new window discovery and alignment view navigation methods
[jalview.git] / src / jalview / gui / Desktop.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3  * Copyright (C) 2008 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                     + "\n\"Clamp, M., Cuff, J., Searle, S. M. and Barton, G. J. (2004), The Jalview Java Alignment Editor\""
630                     + "\nBioinformatics,  2004 20;426-7.");
631
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     if (v_client!=null)
673     {
674       // TODO clear binding to vamsas document objects on close_all
675       
676     }
677   }
678
679   public void raiseRelated_actionPerformed(ActionEvent e)
680   {
681     reorderAssociatedWindows(false, false);
682   }
683
684   public void minimizeAssociated_actionPerformed(ActionEvent e)
685   {
686     reorderAssociatedWindows(true, false);
687   }
688
689   void closeAssociatedWindows()
690   {
691     reorderAssociatedWindows(false, true);
692   }
693
694   /*
695    * (non-Javadoc)
696    * 
697    * @see jalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.ActionEvent)
698    */
699   protected void garbageCollect_actionPerformed(ActionEvent e)
700   {
701     // We simply collect the garbage
702     jalview.bin.Cache.log.debug("Collecting garbage...");
703     System.gc();
704     jalview.bin.Cache.log.debug("Finished garbage collection.");
705   }
706
707   /*
708    * (non-Javadoc)
709    * 
710    * @see jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent)
711    */
712   protected void showMemusage_actionPerformed(ActionEvent e)
713   {
714     desktop.showMemoryUsage(showMemusage.isSelected());
715   }
716
717   void reorderAssociatedWindows(boolean minimize, boolean close)
718   {
719     JInternalFrame[] frames = desktop.getAllFrames();
720     if (frames == null || frames.length < 1)
721     {
722       return;
723     }
724
725     AlignViewport source = null, target = null;
726     if (frames[0] instanceof AlignFrame)
727     {
728       source = ((AlignFrame) frames[0]).getCurrentView();
729     }
730     else if (frames[0] instanceof TreePanel)
731     {
732       source = ((TreePanel) frames[0]).getViewPort();
733     }
734     else if (frames[0] instanceof PCAPanel)
735     {
736       source = ((PCAPanel) frames[0]).av;
737     }
738     else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
739     {
740       source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
741     }
742
743     if (source != null)
744     {
745       for (int i = 0; i < frames.length; i++)
746       {
747         target = null;
748         if (frames[i] == null)
749         {
750           continue;
751         }
752         if (frames[i] instanceof AlignFrame)
753         {
754           target = ((AlignFrame) frames[i]).getCurrentView();
755         }
756         else if (frames[i] instanceof TreePanel)
757         {
758           target = ((TreePanel) frames[i]).getViewPort();
759         }
760         else if (frames[i] instanceof PCAPanel)
761         {
762           target = ((PCAPanel) frames[i]).av;
763         }
764         else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
765         {
766           target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
767         }
768
769         if (source == target)
770         {
771           try
772           {
773             if (close)
774             {
775               frames[i].setClosed(true);
776             }
777             else
778             {
779               frames[i].setIcon(minimize);
780               if (!minimize)
781               {
782                 frames[i].toFront();
783               }
784             }
785
786           } catch (java.beans.PropertyVetoException ex)
787           {
788           }
789         }
790       }
791     }
792   }
793
794   /**
795    * DOCUMENT ME!
796    * 
797    * @param e
798    *                DOCUMENT ME!
799    */
800   protected void preferences_actionPerformed(ActionEvent e)
801   {
802     new Preferences();
803   }
804
805   /**
806    * DOCUMENT ME!
807    * 
808    * @param e
809    *                DOCUMENT ME!
810    */
811   public void saveState_actionPerformed(ActionEvent e)
812   {
813     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
814             .getProperty("LAST_DIRECTORY"), new String[]
815     { "jar" }, new String[]
816     { "Jalview Project" }, "Jalview Project");
817
818     chooser.setFileView(new JalviewFileView());
819     chooser.setDialogTitle("Save State");
820
821     int value = chooser.showSaveDialog(this);
822
823     if (value == JalviewFileChooser.APPROVE_OPTION)
824     {
825       java.io.File choice = chooser.getSelectedFile();
826       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
827       new Jalview2XML().SaveState(choice);
828     }
829   }
830
831   /**
832    * DOCUMENT ME!
833    * 
834    * @param e
835    *                DOCUMENT ME!
836    */
837   public void loadState_actionPerformed(ActionEvent e)
838   {
839     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
840             .getProperty("LAST_DIRECTORY"), new String[]
841     { "jar" }, new String[]
842     { "Jalview Project" }, "Jalview Project");
843     chooser.setFileView(new JalviewFileView());
844     chooser.setDialogTitle("Restore state");
845
846     int value = chooser.showOpenDialog(this);
847
848     if (value == JalviewFileChooser.APPROVE_OPTION)
849     {
850       String choice = chooser.getSelectedFile().getAbsolutePath();
851       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
852               .getSelectedFile().getParent());
853       new Jalview2XML().LoadJalviewAlign(choice);
854     }
855   }
856
857   public void inputSequence_actionPerformed(ActionEvent e)
858   {
859     new SequenceFetcher(this);
860   }
861
862   JPanel progressPanel;
863
864   public void startLoading(final String fileName)
865   {
866     if (fileLoadingCount == 0)
867     {
868       addProgressPanel("Loading File: " + fileName + "   ");
869
870     }
871     fileLoadingCount++;
872   }
873
874   private JProgressBar addProgressPanel(String string)
875   {
876     if (progressPanel == null)
877     {
878       progressPanel = new JPanel(new BorderLayout());
879       totalProgressCount = 0;
880     }
881     JProgressBar progressBar = new JProgressBar();
882     progressBar.setIndeterminate(true);
883
884     progressPanel.add(new JLabel(string), BorderLayout.WEST);
885
886     progressPanel.add(progressBar, BorderLayout.CENTER);
887
888     instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
889     totalProgressCount++;
890     validate();
891     return progressBar;
892   }
893
894   int totalProgressCount = 0;
895
896   private void removeProgressPanel(JProgressBar progbar)
897   {
898     if (progressPanel != null)
899     {
900       progressPanel.remove(progbar);
901       if (--totalProgressCount < 1)
902       {
903         this.getContentPane().remove(progressPanel);
904         progressPanel = null;
905       }
906     }
907     validate();
908   }
909
910   public void stopLoading()
911   {
912     fileLoadingCount--;
913     if (fileLoadingCount < 1)
914     {
915       if (progressPanel != null)
916       {
917         this.getContentPane().remove(progressPanel);
918         progressPanel = null;
919       }
920       fileLoadingCount = 0;
921     }
922     validate();
923   }
924
925   public static int getViewCount(String viewId)
926   {
927     AlignViewport[] aps = getViewports(viewId);
928     return (aps==null) ? 0 : aps.length;
929   }
930   /**
931    * 
932    * @param viewId
933    * @return all AlignmentPanels concerning the viewId sequence set
934    */
935   public static AlignmentPanel[] getAlignmentPanels(String viewId)
936   {
937     int count = 0;
938     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
939     ArrayList aps = new ArrayList();
940     for (int t = 0; t < frames.length; t++)
941     {
942       if (frames[t] instanceof AlignFrame)
943       {
944         AlignFrame af = (AlignFrame) frames[t];
945         for (int a = 0; a < af.alignPanels.size(); a++)
946         {
947           if (viewId
948                   .equals(((AlignmentPanel) af.alignPanels.elementAt(a)).av
949                           .getSequenceSetId()))
950           {
951             aps.add(af.alignPanels.elementAt(a));
952           }
953         }
954       }
955     }
956     if (aps.size()==0)
957     {
958       return null;
959     }
960     AlignmentPanel[] vap = new AlignmentPanel[aps.size()];
961     for (int t=0;t<vap.length;t++)
962     {
963       vap[t] = (AlignmentPanel) aps.get(t);
964     }
965     return vap;
966   }
967   /**
968    * get all the viewports on an alignment.
969    * @param sequenceSetId unique alignment id
970    * @return all viewports on the alignment bound to sequenceSetId
971    */
972   public static AlignViewport[] getViewports(String sequenceSetId)
973   {
974     Vector viewp = new Vector();
975     if (desktop != null)
976     {
977       javax.swing.JInternalFrame[] frames = instance.getAllFrames();
978   
979       for (int t = 0; t < frames.length; t++)
980       {
981         if (frames[t] instanceof AlignFrame)
982         {
983           AlignFrame afr = ((AlignFrame) frames[t]);
984           if (afr.getViewport().getSequenceSetId()
985                   .equals(sequenceSetId))
986           {
987             if (afr.alignPanels!=null)
988             {
989               for (int a = 0; a < afr.alignPanels.size(); a++)
990               {
991                 if (sequenceSetId
992                         .equals(((AlignmentPanel) afr.alignPanels.elementAt(a)).av
993                                 .getSequenceSetId()))
994                 {
995                   viewp.addElement(((AlignmentPanel)afr.alignPanels.elementAt(a)).av);
996                 }
997               }
998             } else {
999               viewp.addElement(((AlignFrame) frames[t]).getViewport());
1000             }
1001           }
1002         }
1003       }
1004       if (viewp.size() > 0)
1005       {
1006         AlignViewport[] vp = new AlignViewport[viewp.size()];
1007         viewp.copyInto(vp);
1008         return vp;
1009       }
1010     }
1011     return null;
1012   }
1013
1014   public void explodeViews(AlignFrame af)
1015   {
1016     int size = af.alignPanels.size();
1017     if (size < 2)
1018     {
1019       return;
1020     }
1021
1022     for (int i = 0; i < size; i++)
1023     {
1024       AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(i);
1025       AlignFrame newaf = new AlignFrame(ap);
1026       if (ap.av.explodedPosition != null
1027               && !ap.av.explodedPosition.equals(af.getBounds()))
1028       {
1029         newaf.setBounds(ap.av.explodedPosition);
1030       }
1031
1032       ap.av.gatherViewsHere = false;
1033
1034       addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1035               AlignFrame.DEFAULT_HEIGHT);
1036     }
1037
1038     af.alignPanels.clear();
1039     af.closeMenuItem_actionPerformed(true);
1040
1041   }
1042
1043   public void gatherViews(AlignFrame source)
1044   {
1045     source.viewport.gatherViewsHere = true;
1046     source.viewport.explodedPosition = source.getBounds();
1047     JInternalFrame[] frames = desktop.getAllFrames();
1048     String viewId = source.viewport.sequenceSetID;
1049
1050     for (int t = 0; t < frames.length; t++)
1051     {
1052       if (frames[t] instanceof AlignFrame && frames[t] != source)
1053       {
1054         AlignFrame af = (AlignFrame) frames[t];
1055         boolean gatherThis = false;
1056         for (int a = 0; a < af.alignPanels.size(); a++)
1057         {
1058           AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(a);
1059           if (viewId.equals(ap.av.getSequenceSetId()))
1060           {
1061             gatherThis = true;
1062             ap.av.gatherViewsHere = false;
1063             ap.av.explodedPosition = af.getBounds();
1064             source.addAlignmentPanel(ap, false);
1065           }
1066         }
1067
1068         if (gatherThis)
1069         {
1070           af.alignPanels.clear();
1071           af.closeMenuItem_actionPerformed(true);
1072         }
1073       }
1074     }
1075
1076   }
1077
1078   jalview.gui.VamsasApplication v_client = null;
1079
1080   public void vamsasImport_actionPerformed(ActionEvent e)
1081   {
1082     if (v_client == null)
1083     {
1084       // Load and try to start a session.
1085       JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
1086               .getProperty("LAST_DIRECTORY"));
1087
1088       chooser.setFileView(new JalviewFileView());
1089       chooser.setDialogTitle("Open a saved VAMSAS session");
1090       chooser
1091               .setToolTipText("select a vamsas session to be opened as a new vamsas session.");
1092
1093       int value = chooser.showOpenDialog(this);
1094
1095       if (value == JalviewFileChooser.APPROVE_OPTION)
1096       {
1097         try
1098         {
1099           v_client = new jalview.gui.VamsasApplication(this, chooser
1100                   .getSelectedFile());
1101         } catch (Exception ex)
1102         {
1103           jalview.bin.Cache.log.error(
1104                   "New vamsas session from existing session file failed:",
1105                   ex);
1106           return;
1107         }
1108         setupVamsasConnectedGui();
1109         v_client.initial_update(); // TODO: thread ?
1110       }
1111     }
1112     else
1113     {
1114       jalview.bin.Cache.log
1115               .error("Implementation error - load session from a running session is not supported.");
1116     }
1117   }
1118
1119   public void vamsasStart_actionPerformed(ActionEvent e)
1120   {
1121     if (v_client == null)
1122     {
1123       // Start a session.
1124       // we just start a default session for moment.
1125       /*
1126        * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
1127        * getProperty("LAST_DIRECTORY"));
1128        * 
1129        * chooser.setFileView(new JalviewFileView());
1130        * chooser.setDialogTitle("Load Vamsas file");
1131        * chooser.setToolTipText("Import");
1132        * 
1133        * int value = chooser.showOpenDialog(this);
1134        * 
1135        * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
1136        * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
1137        * 
1138        */
1139       v_client = new VamsasApplication(this);
1140       setupVamsasConnectedGui();
1141       v_client.initial_update(); // TODO: thread ?
1142     }
1143     else
1144     {
1145       // store current data in session.
1146       v_client.push_update(); // TODO: thread
1147     }
1148   }
1149
1150   protected void setupVamsasConnectedGui()
1151   {
1152     vamsasStart.setText("Session Update");
1153     vamsasSave.setVisible(true);
1154     vamsasStop.setVisible(true);
1155     vamsasImport.setVisible(false); // Document import to existing session is
1156                                     // not possible for vamsas-client-1.0.
1157   }
1158
1159   protected void setupVamsasDisconnectedGui()
1160   {
1161     vamsasSave.setVisible(false);
1162     vamsasStop.setVisible(false);
1163     vamsasImport.setVisible(true);
1164     vamsasStart.setText("New Vamsas Session");
1165   }
1166
1167   public void vamsasStop_actionPerformed(ActionEvent e)
1168   {
1169     if (v_client != null)
1170     {
1171       v_client.end_session();
1172       v_client = null;
1173       setupVamsasDisconnectedGui();
1174     }
1175   }
1176
1177   protected void buildVamsasStMenu()
1178   {
1179     if (v_client == null)
1180     {
1181       String[] sess = null;
1182       try
1183       {
1184         sess = VamsasApplication.getSessionList();
1185       } catch (Exception e)
1186       {
1187         jalview.bin.Cache.log.warn(
1188                 "Problem getting current sessions list.", e);
1189         sess = null;
1190       }
1191       if (sess != null)
1192       {
1193         jalview.bin.Cache.log.debug("Got current sessions list: "
1194                 + sess.length + " entries.");
1195         VamsasStMenu.removeAll();
1196         for (int i = 0; i < sess.length; i++)
1197         {
1198           JMenuItem sessit = new JMenuItem();
1199           sessit.setText(sess[i]);
1200           sessit.setToolTipText("Connect to session " + sess[i]);
1201           final Desktop dsktp = this;
1202           final String mysesid = sess[i];
1203           sessit.addActionListener(new ActionListener()
1204           {
1205
1206             public void actionPerformed(ActionEvent e)
1207             {
1208               if (dsktp.v_client == null)
1209               {
1210                 Thread rthr = new Thread(new Runnable()
1211                 {
1212
1213                   public void run()
1214                   {
1215                     dsktp.v_client = new VamsasApplication(dsktp, mysesid);
1216                     dsktp.setupVamsasConnectedGui();
1217                     dsktp.v_client.initial_update();
1218                   }
1219
1220                 });
1221                 rthr.start();
1222               }
1223             };
1224           });
1225           VamsasStMenu.add(sessit);
1226         }
1227         // don't show an empty menu.
1228         VamsasStMenu.setVisible(sess.length > 0);
1229
1230       }
1231       else
1232       {
1233         jalview.bin.Cache.log.debug("No current vamsas sessions.");
1234         VamsasStMenu.removeAll();
1235         VamsasStMenu.setVisible(false);
1236       }
1237     }
1238     else
1239     {
1240       // Not interested in the content. Just hide ourselves.
1241       VamsasStMenu.setVisible(false);
1242     }
1243   }
1244
1245   public void vamsasSave_actionPerformed(ActionEvent e)
1246   {
1247     if (v_client != null)
1248     {
1249       JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
1250               .getProperty("LAST_DIRECTORY"), new String[]
1251       { "vdj" }, // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
1252               new String[]
1253               { "Vamsas Document" }, "Vamsas Document");
1254
1255       chooser.setFileView(new JalviewFileView());
1256       chooser.setDialogTitle("Save Vamsas Document Archive");
1257
1258       int value = chooser.showSaveDialog(this);
1259
1260       if (value == JalviewFileChooser.APPROVE_OPTION)
1261       {
1262         java.io.File choice = chooser.getSelectedFile();
1263         jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
1264         String warnmsg = null;
1265         String warnttl = null;
1266         try
1267         {
1268           v_client.vclient.storeDocument(choice);
1269         } catch (Error ex)
1270         {
1271           warnttl = "Serious Problem saving Vamsas Document";
1272           warnmsg = ex.toString();
1273           jalview.bin.Cache.log.error("Error Whilst saving document to "
1274                   + choice, ex);
1275
1276         } catch (Exception ex)
1277         {
1278           warnttl = "Problem saving Vamsas Document.";
1279           warnmsg = ex.toString();
1280           jalview.bin.Cache.log.warn("Exception Whilst saving document to "
1281                   + choice, ex);
1282
1283         }
1284         if (warnmsg != null)
1285         {
1286           JOptionPane.showInternalMessageDialog(Desktop.desktop,
1287
1288           warnmsg, warnttl, JOptionPane.ERROR_MESSAGE);
1289         }
1290       }
1291     }
1292   }
1293
1294   JProgressBar vamUpdate = null;
1295
1296   /**
1297    * hide vamsas user gui bits when a vamsas document event is being handled.
1298    * 
1299    * @param b
1300    *                true to hide gui, false to reveal gui
1301    */
1302   public void setVamsasUpdate(boolean b)
1303   {
1304     jalview.bin.Cache.log.debug("Setting gui for Vamsas update "
1305             + (b ? "in progress" : "finished"));
1306
1307     if (vamUpdate != null)
1308     {
1309       this.removeProgressPanel(vamUpdate);
1310     }
1311     if (b)
1312     {
1313       vamUpdate = this.addProgressPanel("Updating vamsas session");
1314     }
1315     vamsasStart.setVisible(!b);
1316     vamsasStop.setVisible(!b);
1317     vamsasSave.setVisible(!b);
1318   }
1319
1320   public JInternalFrame[] getAllFrames()
1321   {
1322     return desktop.getAllFrames();
1323   }
1324
1325   /**
1326    * Checks the given url to see if it gives a response indicating that the user
1327    * should be informed of a new questionnaire.
1328    * 
1329    * @param url
1330    */
1331   public void checkForQuestionnaire(String url)
1332   {
1333     UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
1334     // javax.swing.SwingUtilities.invokeLater(jvq);
1335     new Thread(jvq).start();
1336   }
1337
1338   /**
1339    * Proxy class for JDesktopPane which optionally displays the current memory
1340    * usage and highlights the desktop area with a red bar if free memory runs
1341    * low.
1342    * 
1343    * @author AMW
1344    */
1345   public class MyDesktopPane extends JDesktopPane implements Runnable
1346   {
1347
1348     boolean showMemoryUsage = false;
1349
1350     Runtime runtime;
1351
1352     java.text.NumberFormat df;
1353
1354     float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
1355             percentUsage;
1356
1357     public MyDesktopPane(boolean showMemoryUsage)
1358     {
1359       showMemoryUsage(showMemoryUsage);
1360     }
1361
1362     public void showMemoryUsage(boolean showMemoryUsage)
1363     {
1364       this.showMemoryUsage = showMemoryUsage;
1365       if (showMemoryUsage)
1366       {
1367         Thread worker = new Thread(this);
1368         worker.start();
1369       }
1370     }
1371
1372     public boolean isShowMemoryUsage()
1373     {
1374       return showMemoryUsage;
1375     }
1376
1377     public void run()
1378     {
1379       df = java.text.NumberFormat.getNumberInstance();
1380       df.setMaximumFractionDigits(2);
1381       runtime = Runtime.getRuntime();
1382
1383       while (showMemoryUsage)
1384       {
1385         try
1386         {
1387           Thread.sleep(3000);
1388           maxMemory = runtime.maxMemory() / 1048576f;
1389           allocatedMemory = runtime.totalMemory() / 1048576f;
1390           freeMemory = runtime.freeMemory() / 1048576f;
1391           totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
1392
1393           percentUsage = (totalFreeMemory / maxMemory) * 100;
1394
1395           // if (percentUsage < 20)
1396           {
1397             // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
1398             // Color.red);
1399             // instance.set.setBorder(border1);
1400           }
1401           repaint();
1402
1403         } catch (Exception ex)
1404         {
1405           ex.printStackTrace();
1406         }
1407       }
1408     }
1409
1410     public void paintComponent(Graphics g)
1411     {
1412       if (showMemoryUsage)
1413       {
1414         if (percentUsage < 20)
1415           g.setColor(Color.red);
1416
1417         g.drawString("Total Free Memory: " + df.format(totalFreeMemory)
1418                 + "MB; Max Memory: " + df.format(maxMemory) + "MB; "
1419                 + df.format(percentUsage) + "%", 10, getHeight()
1420                 - g.getFontMetrics().getHeight());
1421       }
1422     }
1423
1424   }
1425
1426   protected JMenuItem groovyShell;
1427
1428   public void doGroovyCheck()
1429   {
1430     if (jalview.bin.Cache.groovyJarsPresent())
1431     {
1432       groovyShell = new JMenuItem();
1433       groovyShell.setText("Groovy Console...");
1434       groovyShell.addActionListener(new ActionListener()
1435       {
1436         public void actionPerformed(ActionEvent e)
1437         {
1438           groovyShell_actionPerformed(e);
1439         }
1440       });
1441       toolsMenu.add(groovyShell);
1442       groovyShell.setVisible(true);
1443     }
1444   }
1445
1446   /**
1447    * Accessor method to quickly get all the AlignmentFrames loaded.
1448    */
1449   public static AlignFrame[] getAlignframes()
1450   {
1451     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1452
1453     if (frames == null)
1454     {
1455       return null;
1456     }
1457     Vector avp = new Vector();
1458     try
1459     {
1460       // REVERSE ORDER
1461       for (int i = frames.length - 1; i > -1; i--)
1462       {
1463         if (frames[i] instanceof AlignFrame)
1464         {
1465           AlignFrame af = (AlignFrame) frames[i];
1466           avp.addElement(af);
1467         }
1468       }
1469     } catch (Exception ex)
1470     {
1471       ex.printStackTrace();
1472     }
1473     if (avp.size() == 0)
1474     {
1475       return null;
1476     }
1477     AlignFrame afs[] = new AlignFrame[avp.size()];
1478     for (int i = 0, j = avp.size(); i < j; i++)
1479     {
1480       afs[i] = (AlignFrame) avp.elementAt(i);
1481     }
1482     avp.clear();
1483     return afs;
1484   }
1485
1486   /**
1487    * Add Groovy Support to Jalview
1488    */
1489   public void groovyShell_actionPerformed(ActionEvent e)
1490   {
1491     // use reflection to avoid creating compilation dependency.
1492     if (!jalview.bin.Cache.groovyJarsPresent())
1493     {
1494       throw new Error(
1495               "Implementation Error. Cannot create groovyShell without Groovy on the classpath!");
1496     }
1497     try
1498     {
1499       Class gcClass = Desktop.class.getClassLoader().loadClass(
1500               "groovy.ui.Console");
1501       Constructor gccons = gcClass.getConstructor(null);
1502       java.lang.reflect.Method setvar = gcClass.getMethod("setVariable",
1503               new Class[]
1504               { String.class, Object.class });
1505       java.lang.reflect.Method run = gcClass.getMethod("run", null);
1506       Object gc = gccons.newInstance(null);
1507       setvar.invoke(gc, new Object[]
1508       { "Jalview", this });
1509       run.invoke(gc, null);
1510     } catch (Exception ex)
1511     {
1512       jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
1513       JOptionPane
1514               .showInternalMessageDialog(
1515                       Desktop.desktop,
1516
1517                       "Couldn't create the groovy Shell. Check the error log for the details of what went wrong.",
1518                       "Jalview Groovy Support Failed",
1519                       JOptionPane.ERROR_MESSAGE);
1520     }
1521   }
1522
1523   /**
1524    * Progress bars managed by the IProgressIndicator method.
1525    */
1526   private Hashtable progressBars;
1527
1528   /*
1529    * (non-Javadoc)
1530    * 
1531    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
1532    */
1533   public void setProgressBar(String message, long id)
1534   {
1535     if (progressBars == null)
1536     {
1537       progressBars = new Hashtable();
1538     }
1539
1540     if (progressBars.get(new Long(id)) != null)
1541     {
1542       JProgressBar progressPanel = (JProgressBar) progressBars
1543               .remove(new Long(id));
1544       removeProgressPanel(progressPanel);
1545     }
1546     else
1547     {
1548       progressBars.put(new Long(id), addProgressPanel(message));
1549     }
1550   }
1551
1552   /**
1553    * This will return the first AlignFrame viewing AlignViewport av. It will
1554    * break if there are more than one AlignFrames viewing a particular av. This
1555    * 
1556    * @param av
1557    * @return alignFrame for av
1558    */
1559   public static AlignFrame getAlignFrameFor(AlignViewport av)
1560   {
1561     if (desktop != null)
1562     {
1563       AlignmentPanel[] aps = getAlignmentPanels(av.getSequenceSetId());
1564       for (int panel=0;aps!=null && panel<aps.length;panel++)
1565         { if (aps[panel]!=null && aps[panel].av==av) {
1566           return aps[panel].alignFrame;
1567         }
1568         }
1569     }
1570     return null;
1571   }
1572 }