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