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