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