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