(JAL-957) change splash screen and about to HTML text, and author list now stored...
[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 = getAboutMessage(false);
1050     JOptionPane.showInternalMessageDialog(Desktop.desktop,
1051
1052     message.toString(), "About Jalview", JOptionPane.INFORMATION_MESSAGE);
1053   }
1054   public StringBuffer getAboutMessage(boolean shortv)
1055   {
1056     StringBuffer message=new StringBuffer();
1057     message.append("<html>");
1058     if (shortv)
1059     { 
1060       message.append("<h1><strong>Jalview "
1061               + jalview.bin.Cache.getProperty("VERSION") + "</strong></h1><br>");
1062       message.append("<strong>Last Updated: <em>"+jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")+"</em></strong>");
1063       
1064     } else {
1065       
1066     message.append("<strong>Jalview version "
1067             + jalview.bin.Cache.getProperty("VERSION") + "; last updated: "
1068             + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1069     }
1070     
1071     if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking").equals(
1072             "Checking"))
1073     {
1074       message.append("<br>...Checking latest version...</br>");
1075     }
1076     else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1077             .equals(jalview.bin.Cache.getProperty("VERSION")))
1078     {
1079       boolean red=false;
1080       if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1081               .indexOf("automated build") == -1)
1082       {
1083         red=true;
1084         // Displayed when code version and jnlp version do not match and code
1085         // version is not a development build
1086         message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1087       }
1088       
1089       message.append("<br>!! Jalview version "
1090                       + jalview.bin.Cache.getDefault("LATEST_VERSION",
1091                               "..Checking..")
1092                       + " is available for download from "+jalview.bin.Cache.getDefault("www.jalview.org","http://www.jalview.org")+" !!");
1093       if (red) {
1094         message.append("</div>");
1095       }
1096     }    
1097     message.append("<br>Authors:  "+jalview.bin.Cache.getDefault("AUTHORNAMES","Jim Procter, Andrew Waterhouse, Jan Engelhardt, Lauren Lui, Michele Clamp, James Cuff, Steve Searle, David Martin & Geoff Barton")
1098             + "<br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1099             + "<br>For help, see the FAQ at www.jalview.org and/or join the jalview-discuss@jalview.org mailing list"
1100             + "<br>If  you use Jalview, please cite:"
1101             + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1102             + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1103             + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1104             + "</html>");
1105     return message;
1106   }
1107
1108   /**
1109    * DOCUMENT ME!
1110    * 
1111    * @param e
1112    *          DOCUMENT ME!
1113    */
1114   public void documentationMenuItem_actionPerformed(ActionEvent e)
1115   {
1116     try
1117     {
1118       ClassLoader cl = jalview.gui.Desktop.class.getClassLoader();
1119       java.net.URL url = javax.help.HelpSet.findHelpSet(cl, "help/help");
1120       javax.help.HelpSet hs = new javax.help.HelpSet(cl, url);
1121
1122       javax.help.HelpBroker hb = hs.createHelpBroker();
1123       hb.setCurrentID("home");
1124       hb.setDisplayed(true);
1125     } catch (Exception ex)
1126     {
1127     }
1128   }
1129
1130   public void closeAll_actionPerformed(ActionEvent e)
1131   {
1132     JInternalFrame[] frames = desktop.getAllFrames();
1133     for (int i = 0; i < frames.length; i++)
1134     {
1135       try
1136       {
1137         frames[i].setClosed(true);
1138       } catch (java.beans.PropertyVetoException ex)
1139       {
1140       }
1141     }
1142     System.out.println("ALL CLOSED");
1143     if (v_client != null)
1144     {
1145       // TODO clear binding to vamsas document objects on close_all
1146
1147     }
1148   }
1149
1150   public void raiseRelated_actionPerformed(ActionEvent e)
1151   {
1152     reorderAssociatedWindows(false, false);
1153   }
1154
1155   public void minimizeAssociated_actionPerformed(ActionEvent e)
1156   {
1157     reorderAssociatedWindows(true, false);
1158   }
1159
1160   void closeAssociatedWindows()
1161   {
1162     reorderAssociatedWindows(false, true);
1163   }
1164
1165   /*
1166    * (non-Javadoc)
1167    * 
1168    * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1169    * ActionEvent)
1170    */
1171   protected void garbageCollect_actionPerformed(ActionEvent e)
1172   {
1173     // We simply collect the garbage
1174     jalview.bin.Cache.log.debug("Collecting garbage...");
1175     System.gc();
1176     jalview.bin.Cache.log.debug("Finished garbage collection.");
1177   }
1178
1179   /*
1180    * (non-Javadoc)
1181    * 
1182    * @see
1183    * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1184    * )
1185    */
1186   protected void showMemusage_actionPerformed(ActionEvent e)
1187   {
1188     desktop.showMemoryUsage(showMemusage.isSelected());
1189   }
1190
1191   /*
1192    * (non-Javadoc)
1193    * 
1194    * @see
1195    * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1196    * )
1197    */
1198   protected void showConsole_actionPerformed(ActionEvent e)
1199   {
1200     showConsole(showConsole.isSelected());
1201   }
1202
1203   Console jconsole = null;
1204
1205   /**
1206    * control whether the java console is visible or not
1207    * 
1208    * @param selected
1209    */
1210   void showConsole(boolean selected)
1211   {
1212     showConsole.setSelected(selected);
1213     // TODO: decide if we should update properties file
1214     Cache.setProperty("SHOW_JAVA_CONSOLE", Boolean.valueOf(selected)
1215             .toString());
1216     jconsole.setVisible(selected);
1217   }
1218
1219   void reorderAssociatedWindows(boolean minimize, boolean close)
1220   {
1221     JInternalFrame[] frames = desktop.getAllFrames();
1222     if (frames == null || frames.length < 1)
1223     {
1224       return;
1225     }
1226
1227     AlignViewport source = null, target = null;
1228     if (frames[0] instanceof AlignFrame)
1229     {
1230       source = ((AlignFrame) frames[0]).getCurrentView();
1231     }
1232     else if (frames[0] instanceof TreePanel)
1233     {
1234       source = ((TreePanel) frames[0]).getViewPort();
1235     }
1236     else if (frames[0] instanceof PCAPanel)
1237     {
1238       source = ((PCAPanel) frames[0]).av;
1239     }
1240     else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1241     {
1242       source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1243     }
1244
1245     if (source != null)
1246     {
1247       for (int i = 0; i < frames.length; i++)
1248       {
1249         target = null;
1250         if (frames[i] == null)
1251         {
1252           continue;
1253         }
1254         if (frames[i] instanceof AlignFrame)
1255         {
1256           target = ((AlignFrame) frames[i]).getCurrentView();
1257         }
1258         else if (frames[i] instanceof TreePanel)
1259         {
1260           target = ((TreePanel) frames[i]).getViewPort();
1261         }
1262         else if (frames[i] instanceof PCAPanel)
1263         {
1264           target = ((PCAPanel) frames[i]).av;
1265         }
1266         else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1267         {
1268           target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1269         }
1270
1271         if (source == target)
1272         {
1273           try
1274           {
1275             if (close)
1276             {
1277               frames[i].setClosed(true);
1278             }
1279             else
1280             {
1281               frames[i].setIcon(minimize);
1282               if (!minimize)
1283               {
1284                 frames[i].toFront();
1285               }
1286             }
1287
1288           } catch (java.beans.PropertyVetoException ex)
1289           {
1290           }
1291         }
1292       }
1293     }
1294   }
1295
1296   /**
1297    * DOCUMENT ME!
1298    * 
1299    * @param e
1300    *          DOCUMENT ME!
1301    */
1302   protected void preferences_actionPerformed(ActionEvent e)
1303   {
1304     new Preferences();
1305   }
1306
1307   /**
1308    * DOCUMENT ME!
1309    * 
1310    * @param e
1311    *          DOCUMENT ME!
1312    */
1313   public void saveState_actionPerformed(ActionEvent e)
1314   {
1315     JalviewFileChooser chooser = new JalviewFileChooser(
1316             jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
1317             { "jar" }, new String[]
1318             { "Jalview Project" }, "Jalview Project");
1319
1320     chooser.setFileView(new JalviewFileView());
1321     chooser.setDialogTitle("Save State");
1322
1323     int value = chooser.showSaveDialog(this);
1324
1325     if (value == JalviewFileChooser.APPROVE_OPTION)
1326     {
1327       final Desktop me = this;
1328       final java.io.File choice = chooser.getSelectedFile();
1329       new Thread(new Runnable()
1330       {
1331         public void run()
1332         {
1333
1334       setProgressBar("Saving jalview project " + choice.getName(),
1335               choice.hashCode());
1336       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
1337       // TODO catch and handle errors for savestate
1338       // TODO prevent user from messing with the Desktop whilst we're saving
1339       try
1340       {
1341         new Jalview2XML().SaveState(choice);
1342       } catch (OutOfMemoryError oom)
1343       {
1344         new OOMWarning(
1345                 "Whilst saving current state to " + choice.getName(), oom);
1346       } catch (Exception ex)
1347       {
1348         Cache.log
1349                 .error("Problems whilst trying to save to "
1350                         + choice.getName(), ex);
1351         JOptionPane.showMessageDialog(me,
1352                 "Error whilst saving current state to " + choice.getName(),
1353                 "Couldn't save project", JOptionPane.WARNING_MESSAGE);
1354       }
1355       setProgressBar(null, choice.hashCode());
1356         }
1357       }).start();
1358     }
1359   }
1360
1361   /**
1362    * DOCUMENT ME!
1363    * 
1364    * @param e
1365    *          DOCUMENT ME!
1366    */
1367   public void loadState_actionPerformed(ActionEvent e)
1368   {
1369     JalviewFileChooser chooser = new JalviewFileChooser(
1370             jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
1371             { "jar" }, new String[]
1372             { "Jalview Project" }, "Jalview Project");
1373     chooser.setFileView(new JalviewFileView());
1374     chooser.setDialogTitle("Restore state");
1375
1376     int value = chooser.showOpenDialog(this);
1377
1378     if (value == JalviewFileChooser.APPROVE_OPTION)
1379     {
1380       final String choice = chooser.getSelectedFile().getAbsolutePath();
1381       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
1382               .getSelectedFile().getParent());
1383       new Thread(new Runnable()
1384       {
1385         public void run()
1386         {
1387           setProgressBar("loading jalview project " + choice,
1388                   choice.hashCode());
1389           try
1390           {
1391             new Jalview2XML().LoadJalviewAlign(choice);
1392           } catch (OutOfMemoryError oom)
1393           {
1394             new OOMWarning("Whilst loading project from " + choice, oom);
1395           } catch (Exception ex)
1396           {
1397             Cache.log.error("Problems whilst loading project from "
1398                     + choice, ex);
1399             JOptionPane.showMessageDialog(Desktop.desktop,
1400                     "Error whilst loading project from " + choice,
1401                     "Couldn't load project", JOptionPane.WARNING_MESSAGE);
1402           }
1403           setProgressBar(null, choice.hashCode());
1404         }
1405       }).start();
1406     }
1407   }
1408
1409   public void inputSequence_actionPerformed(ActionEvent e)
1410   {
1411     new SequenceFetcher(this);
1412   }
1413
1414   JPanel progressPanel;
1415   ArrayList<JPanel> fileLoadingPanels=new ArrayList<JPanel>();
1416   public void startLoading(final String fileName)
1417   {
1418     if (fileLoadingCount == 0)
1419     {
1420       fileLoadingPanels.add(addProgressPanel("Loading File: " + fileName + "   "));
1421     }
1422     fileLoadingCount++;
1423   }
1424
1425   private JPanel addProgressPanel(String string)
1426   {
1427     if (progressPanel == null)
1428     {
1429       progressPanel = new JPanel(new GridLayout(1,1));
1430       totalProgressCount = 0;
1431       instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1432     }
1433     JPanel thisprogress=new JPanel(new BorderLayout(10,5));
1434     JProgressBar progressBar = new JProgressBar();
1435     progressBar.setIndeterminate(true);
1436
1437     thisprogress.add(new JLabel(string), BorderLayout.WEST);
1438
1439     thisprogress.add(progressBar, BorderLayout.CENTER);
1440     progressPanel.add(thisprogress);
1441     ((GridLayout)progressPanel.getLayout()).setRows(((GridLayout)progressPanel.getLayout()).getRows()+1);
1442     ++totalProgressCount;
1443     instance.validate();
1444     return thisprogress;
1445   }
1446
1447   int totalProgressCount = 0;
1448
1449   private void removeProgressPanel(JPanel progbar)
1450   {
1451     if (progressPanel != null)
1452     {
1453       progressPanel.remove(progbar);
1454       GridLayout gl = (GridLayout) progressPanel.getLayout();
1455       gl.setRows(gl.getRows()-1);
1456       if (--totalProgressCount < 1)
1457       {
1458         this.getContentPane().remove(progressPanel);
1459         progressPanel = null;
1460       }
1461     }
1462     validate();
1463   }
1464
1465   public void stopLoading()
1466   {
1467     fileLoadingCount--;
1468     if (fileLoadingCount < 1)
1469     {
1470       for (JPanel flp : fileLoadingPanels)
1471       {
1472         removeProgressPanel(flp);
1473       }
1474       fileLoadingPanels.clear();
1475       fileLoadingCount = 0;
1476     }
1477     validate();
1478   }
1479
1480   public static int getViewCount(String alignmentId)
1481   {
1482     AlignViewport[] aps = getViewports(alignmentId);
1483     return (aps == null) ? 0 : aps.length;
1484   }
1485
1486   /**
1487    * 
1488    * @param alignmentId
1489    * @return all AlignmentPanels concerning the alignmentId sequence set
1490    */
1491   public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1492   {
1493     int count = 0;
1494     if (Desktop.desktop == null)
1495     {
1496       // no frames created and in headless mode
1497       // TODO: verify that frames are recoverable when in headless mode
1498       return null;
1499     }
1500     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1501     ArrayList aps = new ArrayList();
1502     for (int t = 0; t < frames.length; t++)
1503     {
1504       if (frames[t] instanceof AlignFrame)
1505       {
1506         AlignFrame af = (AlignFrame) frames[t];
1507         for (int a = 0; a < af.alignPanels.size(); a++)
1508         {
1509           if (alignmentId
1510                   .equals(((AlignmentPanel) af.alignPanels.elementAt(a)).av
1511                           .getSequenceSetId()))
1512           {
1513             aps.add(af.alignPanels.elementAt(a));
1514           }
1515         }
1516       }
1517     }
1518     if (aps.size() == 0)
1519     {
1520       return null;
1521     }
1522     AlignmentPanel[] vap = new AlignmentPanel[aps.size()];
1523     for (int t = 0; t < vap.length; t++)
1524     {
1525       vap[t] = (AlignmentPanel) aps.get(t);
1526     }
1527     return vap;
1528   }
1529
1530   /**
1531    * get all the viewports on an alignment.
1532    * 
1533    * @param sequenceSetId
1534    *          unique alignment id
1535    * @return all viewports on the alignment bound to sequenceSetId
1536    */
1537   public static AlignViewport[] getViewports(String sequenceSetId)
1538   {
1539     Vector viewp = new Vector();
1540     if (desktop != null)
1541     {
1542       javax.swing.JInternalFrame[] frames = instance.getAllFrames();
1543
1544       for (int t = 0; t < frames.length; t++)
1545       {
1546         if (frames[t] instanceof AlignFrame)
1547         {
1548           AlignFrame afr = ((AlignFrame) frames[t]);
1549           if (afr.getViewport().getSequenceSetId().equals(sequenceSetId))
1550           {
1551             if (afr.alignPanels != null)
1552             {
1553               for (int a = 0; a < afr.alignPanels.size(); a++)
1554               {
1555                 if (sequenceSetId.equals(((AlignmentPanel) afr.alignPanels
1556                         .elementAt(a)).av.getSequenceSetId()))
1557                 {
1558                   viewp.addElement(((AlignmentPanel) afr.alignPanels
1559                           .elementAt(a)).av);
1560                 }
1561               }
1562             }
1563             else
1564             {
1565               viewp.addElement(((AlignFrame) frames[t]).getViewport());
1566             }
1567           }
1568         }
1569       }
1570       if (viewp.size() > 0)
1571       {
1572         AlignViewport[] vp = new AlignViewport[viewp.size()];
1573         viewp.copyInto(vp);
1574         return vp;
1575       }
1576     }
1577     return null;
1578   }
1579
1580   public void explodeViews(AlignFrame af)
1581   {
1582     int size = af.alignPanels.size();
1583     if (size < 2)
1584     {
1585       return;
1586     }
1587
1588     for (int i = 0; i < size; i++)
1589     {
1590       AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(i);
1591       AlignFrame newaf = new AlignFrame(ap);
1592       if (ap.av.explodedPosition != null
1593               && !ap.av.explodedPosition.equals(af.getBounds()))
1594       {
1595         newaf.setBounds(ap.av.explodedPosition);
1596       }
1597
1598       ap.av.gatherViewsHere = false;
1599
1600       addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1601               AlignFrame.DEFAULT_HEIGHT);
1602     }
1603
1604     af.alignPanels.clear();
1605     af.closeMenuItem_actionPerformed(true);
1606
1607   }
1608
1609   public void gatherViews(AlignFrame source)
1610   {
1611     source.viewport.gatherViewsHere = true;
1612     source.viewport.explodedPosition = source.getBounds();
1613     JInternalFrame[] frames = desktop.getAllFrames();
1614     String viewId = source.viewport.sequenceSetID;
1615
1616     for (int t = 0; t < frames.length; t++)
1617     {
1618       if (frames[t] instanceof AlignFrame && frames[t] != source)
1619       {
1620         AlignFrame af = (AlignFrame) frames[t];
1621         boolean gatherThis = false;
1622         for (int a = 0; a < af.alignPanels.size(); a++)
1623         {
1624           AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(a);
1625           if (viewId.equals(ap.av.getSequenceSetId()))
1626           {
1627             gatherThis = true;
1628             ap.av.gatherViewsHere = false;
1629             ap.av.explodedPosition = af.getBounds();
1630             source.addAlignmentPanel(ap, false);
1631           }
1632         }
1633
1634         if (gatherThis)
1635         {
1636           af.alignPanels.clear();
1637           af.closeMenuItem_actionPerformed(true);
1638         }
1639       }
1640     }
1641
1642   }
1643
1644   jalview.gui.VamsasApplication v_client = null;
1645
1646   public void vamsasImport_actionPerformed(ActionEvent e)
1647   {
1648     if (v_client == null)
1649     {
1650       // Load and try to start a session.
1651       JalviewFileChooser chooser = new JalviewFileChooser(
1652               jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
1653
1654       chooser.setFileView(new JalviewFileView());
1655       chooser.setDialogTitle("Open a saved VAMSAS session");
1656       chooser.setToolTipText("select a vamsas session to be opened as a new vamsas session.");
1657
1658       int value = chooser.showOpenDialog(this);
1659
1660       if (value == JalviewFileChooser.APPROVE_OPTION)
1661       {
1662         String fle = chooser.getSelectedFile().toString();
1663         if (!vamsasImport(chooser.getSelectedFile()))
1664         {
1665           JOptionPane.showInternalMessageDialog(Desktop.desktop,
1666                   "Couldn't import '" + fle + "' as a new vamsas session.",
1667                   "Vamsas Document Import Failed",
1668                   JOptionPane.ERROR_MESSAGE);
1669         }
1670       }
1671     }
1672     else
1673     {
1674       jalview.bin.Cache.log
1675               .error("Implementation error - load session from a running session is not supported.");
1676     }
1677   }
1678
1679   /**
1680    * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
1681    * 
1682    * @param file
1683    * @return true if import was a success and a session was started.
1684    */
1685   public boolean vamsasImport(URL url)
1686   {
1687     // TODO: create progress bar
1688     if (v_client != null)
1689     {
1690
1691       jalview.bin.Cache.log
1692               .error("Implementation error - load session from a running session is not supported.");
1693       return false;
1694     }
1695
1696     try
1697     {
1698       // copy the URL content to a temporary local file
1699       // TODO: be a bit cleverer here with nio (?!)
1700       File file = File.createTempFile("vdocfromurl", ".vdj");
1701       FileOutputStream fos = new FileOutputStream(file);
1702       BufferedInputStream bis = new BufferedInputStream(url.openStream());
1703       byte[] buffer = new byte[2048];
1704       int ln;
1705       while ((ln = bis.read(buffer)) > -1)
1706       {
1707         fos.write(buffer, 0, ln);
1708       }
1709       bis.close();
1710       fos.close();
1711       v_client = new jalview.gui.VamsasApplication(this, file,
1712               url.toExternalForm());
1713     } catch (Exception ex)
1714     {
1715       jalview.bin.Cache.log.error(
1716               "Failed to create new vamsas session from contents of URL "
1717                       + url, ex);
1718       return false;
1719     }
1720     setupVamsasConnectedGui();
1721     v_client.initial_update(); // TODO: thread ?
1722     return v_client.inSession();
1723   }
1724
1725   /**
1726    * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
1727    * 
1728    * @param file
1729    * @return true if import was a success and a session was started.
1730    */
1731   public boolean vamsasImport(File file)
1732   {
1733     if (v_client != null)
1734     {
1735
1736       jalview.bin.Cache.log
1737               .error("Implementation error - load session from a running session is not supported.");
1738       return false;
1739     }
1740
1741     setProgressBar("Importing VAMSAS session from " + file.getName(),
1742             file.hashCode());
1743     try
1744     {
1745       v_client = new jalview.gui.VamsasApplication(this, file, null);
1746     } catch (Exception ex)
1747     {
1748       setProgressBar("Importing VAMSAS session from " + file.getName(),
1749               file.hashCode());
1750       jalview.bin.Cache.log.error(
1751               "New vamsas session from existing session file failed:", ex);
1752       return false;
1753     }
1754     setupVamsasConnectedGui();
1755     v_client.initial_update(); // TODO: thread ?
1756     setProgressBar("Importing VAMSAS session from " + file.getName(),
1757             file.hashCode());
1758     return v_client.inSession();
1759   }
1760
1761   public boolean joinVamsasSession(String mysesid)
1762   {
1763     if (v_client != null)
1764     {
1765       throw new Error(
1766               "Trying to join a vamsas session when another is already connected.");
1767     }
1768     if (mysesid == null)
1769     {
1770       throw new Error("Invalid vamsas session id.");
1771     }
1772     v_client = new VamsasApplication(this, mysesid);
1773     setupVamsasConnectedGui();
1774     v_client.initial_update();
1775     return (v_client.inSession());
1776   }
1777
1778   public void vamsasStart_actionPerformed(ActionEvent e)
1779   {
1780     if (v_client == null)
1781     {
1782       // Start a session.
1783       // we just start a default session for moment.
1784       /*
1785        * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
1786        * getProperty("LAST_DIRECTORY"));
1787        * 
1788        * chooser.setFileView(new JalviewFileView());
1789        * chooser.setDialogTitle("Load Vamsas file");
1790        * chooser.setToolTipText("Import");
1791        * 
1792        * int value = chooser.showOpenDialog(this);
1793        * 
1794        * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
1795        * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
1796        */
1797       v_client = new VamsasApplication(this);
1798       setupVamsasConnectedGui();
1799       v_client.initial_update(); // TODO: thread ?
1800     }
1801     else
1802     {
1803       // store current data in session.
1804       v_client.push_update(); // TODO: thread
1805     }
1806   }
1807
1808   protected void setupVamsasConnectedGui()
1809   {
1810     vamsasStart.setText("Session Update");
1811     vamsasSave.setVisible(true);
1812     vamsasStop.setVisible(true);
1813     vamsasImport.setVisible(false); // Document import to existing session is
1814     // not possible for vamsas-client-1.0.
1815   }
1816
1817   protected void setupVamsasDisconnectedGui()
1818   {
1819     vamsasSave.setVisible(false);
1820     vamsasStop.setVisible(false);
1821     vamsasImport.setVisible(true);
1822     vamsasStart.setText("New Vamsas Session");
1823   }
1824
1825   public void vamsasStop_actionPerformed(ActionEvent e)
1826   {
1827     if (v_client != null)
1828     {
1829       v_client.end_session();
1830       v_client = null;
1831       setupVamsasDisconnectedGui();
1832     }
1833   }
1834
1835   protected void buildVamsasStMenu()
1836   {
1837     if (v_client == null)
1838     {
1839       String[] sess = null;
1840       try
1841       {
1842         sess = VamsasApplication.getSessionList();
1843       } catch (Exception e)
1844       {
1845         jalview.bin.Cache.log.warn(
1846                 "Problem getting current sessions list.", e);
1847         sess = null;
1848       }
1849       if (sess != null)
1850       {
1851         jalview.bin.Cache.log.debug("Got current sessions list: "
1852                 + sess.length + " entries.");
1853         VamsasStMenu.removeAll();
1854         for (int i = 0; i < sess.length; i++)
1855         {
1856           JMenuItem sessit = new JMenuItem();
1857           sessit.setText(sess[i]);
1858           sessit.setToolTipText("Connect to session " + sess[i]);
1859           final Desktop dsktp = this;
1860           final String mysesid = sess[i];
1861           sessit.addActionListener(new ActionListener()
1862           {
1863
1864             public void actionPerformed(ActionEvent e)
1865             {
1866               if (dsktp.v_client == null)
1867               {
1868                 Thread rthr = new Thread(new Runnable()
1869                 {
1870
1871                   public void run()
1872                   {
1873                     dsktp.v_client = new VamsasApplication(dsktp, mysesid);
1874                     dsktp.setupVamsasConnectedGui();
1875                     dsktp.v_client.initial_update();
1876                   }
1877
1878                 });
1879                 rthr.start();
1880               }
1881             };
1882           });
1883           VamsasStMenu.add(sessit);
1884         }
1885         // don't show an empty menu.
1886         VamsasStMenu.setVisible(sess.length > 0);
1887
1888       }
1889       else
1890       {
1891         jalview.bin.Cache.log.debug("No current vamsas sessions.");
1892         VamsasStMenu.removeAll();
1893         VamsasStMenu.setVisible(false);
1894       }
1895     }
1896     else
1897     {
1898       // Not interested in the content. Just hide ourselves.
1899       VamsasStMenu.setVisible(false);
1900     }
1901   }
1902
1903   public void vamsasSave_actionPerformed(ActionEvent e)
1904   {
1905     if (v_client != null)
1906     {
1907       JalviewFileChooser chooser = new JalviewFileChooser(
1908               jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
1909               { "vdj" }, // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
1910               new String[]
1911               { "Vamsas Document" }, "Vamsas Document");
1912
1913       chooser.setFileView(new JalviewFileView());
1914       chooser.setDialogTitle("Save Vamsas Document Archive");
1915
1916       int value = chooser.showSaveDialog(this);
1917
1918       if (value == JalviewFileChooser.APPROVE_OPTION)
1919       {
1920         java.io.File choice = chooser.getSelectedFile();
1921         JPanel progpanel = addProgressPanel("Saving VAMSAS Document to "
1922                 + choice.getName());
1923         jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
1924         String warnmsg = null;
1925         String warnttl = null;
1926         try
1927         {
1928           v_client.vclient.storeDocument(choice);
1929         } catch (Error ex)
1930         {
1931           warnttl = "Serious Problem saving Vamsas Document";
1932           warnmsg = ex.toString();
1933           jalview.bin.Cache.log.error("Error Whilst saving document to "
1934                   + choice, ex);
1935
1936         } catch (Exception ex)
1937         {
1938           warnttl = "Problem saving Vamsas Document.";
1939           warnmsg = ex.toString();
1940           jalview.bin.Cache.log.warn("Exception Whilst saving document to "
1941                   + choice, ex);
1942
1943         }
1944         removeProgressPanel(progpanel);
1945         if (warnmsg != null)
1946         {
1947           JOptionPane.showInternalMessageDialog(Desktop.desktop,
1948
1949           warnmsg, warnttl, JOptionPane.ERROR_MESSAGE);
1950         }
1951       }
1952     }
1953   }
1954
1955   JPanel vamUpdate = null;
1956
1957   /**
1958    * hide vamsas user gui bits when a vamsas document event is being handled.
1959    * 
1960    * @param b
1961    *          true to hide gui, false to reveal gui
1962    */
1963   public void setVamsasUpdate(boolean b)
1964   {
1965     jalview.bin.Cache.log.debug("Setting gui for Vamsas update "
1966             + (b ? "in progress" : "finished"));
1967
1968     if (vamUpdate != null)
1969     {
1970       this.removeProgressPanel(vamUpdate);
1971     }
1972     if (b)
1973     {
1974       vamUpdate = this.addProgressPanel("Updating vamsas session");
1975     }
1976     vamsasStart.setVisible(!b);
1977     vamsasStop.setVisible(!b);
1978     vamsasSave.setVisible(!b);
1979   }
1980
1981   public JInternalFrame[] getAllFrames()
1982   {
1983     return desktop.getAllFrames();
1984   }
1985
1986   /**
1987    * Checks the given url to see if it gives a response indicating that the user
1988    * should be informed of a new questionnaire.
1989    * 
1990    * @param url
1991    */
1992   public void checkForQuestionnaire(String url)
1993   {
1994     UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
1995     // javax.swing.SwingUtilities.invokeLater(jvq);
1996     new Thread(jvq).start();
1997   }
1998
1999   /**
2000    * Proxy class for JDesktopPane which optionally displays the current memory
2001    * usage and highlights the desktop area with a red bar if free memory runs
2002    * low.
2003    * 
2004    * @author AMW
2005    */
2006   public class MyDesktopPane extends JDesktopPane implements Runnable
2007   {
2008
2009     boolean showMemoryUsage = false;
2010
2011     Runtime runtime;
2012
2013     java.text.NumberFormat df;
2014
2015     float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2016             percentUsage;
2017
2018     public MyDesktopPane(boolean showMemoryUsage)
2019     {
2020       showMemoryUsage(showMemoryUsage);
2021     }
2022
2023     public void showMemoryUsage(boolean showMemoryUsage)
2024     {
2025       this.showMemoryUsage = showMemoryUsage;
2026       if (showMemoryUsage)
2027       {
2028         Thread worker = new Thread(this);
2029         worker.start();
2030       }
2031     }
2032
2033     public boolean isShowMemoryUsage()
2034     {
2035       return showMemoryUsage;
2036     }
2037
2038     public void run()
2039     {
2040       df = java.text.NumberFormat.getNumberInstance();
2041       df.setMaximumFractionDigits(2);
2042       runtime = Runtime.getRuntime();
2043
2044       while (showMemoryUsage)
2045       {
2046         try
2047         {
2048           maxMemory = runtime.maxMemory() / 1048576f;
2049           allocatedMemory = runtime.totalMemory() / 1048576f;
2050           freeMemory = runtime.freeMemory() / 1048576f;
2051           totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2052
2053           percentUsage = (totalFreeMemory / maxMemory) * 100;
2054
2055           // if (percentUsage < 20)
2056           {
2057             // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2058             // Color.red);
2059             // instance.set.setBorder(border1);
2060           }
2061           repaint();
2062           // sleep after showing usage
2063           Thread.sleep(3000);
2064         } catch (Exception ex)
2065         {
2066           ex.printStackTrace();
2067         }
2068       }
2069     }
2070
2071     public void paintComponent(Graphics g)
2072     {
2073       if (showMemoryUsage && g != null && df != null)
2074       {
2075         if (percentUsage < 20)
2076           g.setColor(Color.red);
2077         FontMetrics fm = g.getFontMetrics();
2078         if (fm != null)
2079         {
2080           g.drawString(
2081                   "Total Free Memory: " + df.format(totalFreeMemory)
2082                           + "MB; Max Memory: " + df.format(maxMemory)
2083                           + "MB; " + df.format(percentUsage) + "%", 10,
2084                   getHeight() - fm.getHeight());
2085         }
2086       }
2087     }
2088
2089   }
2090
2091   /**
2092    * fixes stacking order after a modal dialog to ensure windows that should be on top actually are
2093    */
2094   public void relayerWindows()
2095   {
2096     
2097   }
2098
2099   protected JMenuItem groovyShell;
2100
2101   public void doGroovyCheck()
2102   {
2103     if (jalview.bin.Cache.groovyJarsPresent())
2104     {
2105       groovyShell = new JMenuItem();
2106       groovyShell.setText("Groovy Console...");
2107       groovyShell.addActionListener(new ActionListener()
2108       {
2109         public void actionPerformed(ActionEvent e)
2110         {
2111           groovyShell_actionPerformed(e);
2112         }
2113       });
2114       toolsMenu.add(groovyShell);
2115       groovyShell.setVisible(true);
2116     }
2117   }
2118
2119   /**
2120    * Accessor method to quickly get all the AlignmentFrames loaded.
2121    */
2122   public static AlignFrame[] getAlignframes()
2123   {
2124     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2125
2126     if (frames == null)
2127     {
2128       return null;
2129     }
2130     Vector avp = new Vector();
2131     try
2132     {
2133       // REVERSE ORDER
2134       for (int i = frames.length - 1; i > -1; i--)
2135       {
2136         if (frames[i] instanceof AlignFrame)
2137         {
2138           AlignFrame af = (AlignFrame) frames[i];
2139           avp.addElement(af);
2140         }
2141       }
2142     } catch (Exception ex)
2143     {
2144       ex.printStackTrace();
2145     }
2146     if (avp.size() == 0)
2147     {
2148       return null;
2149     }
2150     AlignFrame afs[] = new AlignFrame[avp.size()];
2151     for (int i = 0, j = avp.size(); i < j; i++)
2152     {
2153       afs[i] = (AlignFrame) avp.elementAt(i);
2154     }
2155     avp.clear();
2156     return afs;
2157   }
2158   public AppJmol[] getJmols()
2159   {
2160     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2161
2162     if (frames == null)
2163     {
2164       return null;
2165     }
2166     Vector avp = new Vector();
2167     try
2168     {
2169       // REVERSE ORDER
2170       for (int i = frames.length - 1; i > -1; i--)
2171       {
2172         if (frames[i] instanceof AppJmol)
2173         {
2174           AppJmol af = (AppJmol) frames[i];
2175           avp.addElement(af);
2176         }
2177       }
2178     } catch (Exception ex)
2179     {
2180       ex.printStackTrace();
2181     }
2182     if (avp.size() == 0)
2183     {
2184       return null;
2185     }
2186     AppJmol afs[] = new AppJmol[avp.size()];
2187     for (int i = 0, j = avp.size(); i < j; i++)
2188     {
2189       afs[i] = (AppJmol) avp.elementAt(i);
2190     }
2191     avp.clear();
2192     return afs;
2193   }
2194
2195   /**
2196    * Add Groovy Support to Jalview
2197    */
2198   public void groovyShell_actionPerformed(ActionEvent e)
2199   {
2200     // use reflection to avoid creating compilation dependency.
2201     if (!jalview.bin.Cache.groovyJarsPresent())
2202     {
2203       throw new Error(
2204               "Implementation Error. Cannot create groovyShell without Groovy on the classpath!");
2205     }
2206     try
2207     {
2208       Class gcClass = Desktop.class.getClassLoader().loadClass(
2209               "groovy.ui.Console");
2210       Constructor gccons = gcClass.getConstructor(null);
2211       java.lang.reflect.Method setvar = gcClass.getMethod("setVariable",
2212               new Class[]
2213               { String.class, Object.class });
2214       java.lang.reflect.Method run = gcClass.getMethod("run", null);
2215       Object gc = gccons.newInstance(null);
2216       setvar.invoke(gc, new Object[]
2217       { "Jalview", this });
2218       run.invoke(gc, null);
2219     } catch (Exception ex)
2220     {
2221       jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2222       JOptionPane
2223               .showInternalMessageDialog(
2224                       Desktop.desktop,
2225
2226                       "Couldn't create the groovy Shell. Check the error log for the details of what went wrong.",
2227                       "Jalview Groovy Support Failed",
2228                       JOptionPane.ERROR_MESSAGE);
2229     }
2230   }
2231
2232   /**
2233    * Progress bars managed by the IProgressIndicator method.
2234    */
2235   private Hashtable<Long,JPanel> progressBars;
2236   private Hashtable<Long,IProgressIndicatorHandler> progressBarHandlers;
2237
2238   /*
2239    * (non-Javadoc)
2240    * 
2241    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2242    */
2243   public void setProgressBar(String message, long id)
2244   {
2245     if (progressBars == null)
2246     {
2247       progressBars = new Hashtable<Long,JPanel>();
2248       progressBarHandlers = new Hashtable<Long,IProgressIndicatorHandler>();
2249     }
2250
2251     if (progressBars.get(new Long(id)) != null)
2252     {
2253       JPanel progressPanel = progressBars
2254               .remove(new Long(id));
2255       if (progressBarHandlers.contains(new Long(id)))
2256       {
2257         progressBarHandlers.remove(new Long(id));
2258       }
2259       removeProgressPanel(progressPanel);
2260     }
2261     else
2262     {
2263       progressBars.put(new Long(id), addProgressPanel(message));
2264     }
2265   }
2266
2267   /*
2268    * (non-Javadoc)
2269    * 
2270    * @see jalview.gui.IProgressIndicator#registerHandler(long,
2271    * jalview.gui.IProgressIndicatorHandler)
2272    */
2273   public void registerHandler(final long id,
2274           final IProgressIndicatorHandler handler)
2275   {
2276     if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
2277     {
2278       throw new Error(
2279               "call setProgressBar before registering the progress bar's handler.");
2280     }
2281     progressBarHandlers.put(new Long(id), handler);
2282     final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
2283     if (handler.canCancel())
2284     {
2285       JButton cancel = new JButton("Cancel");
2286       final IProgressIndicator us = this;
2287       cancel.addActionListener(new ActionListener()
2288       {
2289
2290         public void actionPerformed(ActionEvent e)
2291         {
2292           handler.cancelActivity(id);
2293           us.setProgressBar(
2294                   "Cancelled "
2295                           + ((JLabel) progressPanel.getComponent(0))
2296                                   .getText(), id);
2297         }
2298       });
2299       progressPanel.add(cancel, BorderLayout.EAST);
2300     }
2301   }
2302
2303   /**
2304    * This will return the first AlignFrame viewing AlignViewport av. It will
2305    * break if there are more than one AlignFrames viewing a particular av. This
2306    * 
2307    * @param av
2308    * @return alignFrame for av
2309    */
2310   public static AlignFrame getAlignFrameFor(AlignViewport av)
2311   {
2312     if (desktop != null)
2313     {
2314       AlignmentPanel[] aps = getAlignmentPanels(av.getSequenceSetId());
2315       for (int panel = 0; aps != null && panel < aps.length; panel++)
2316       {
2317         if (aps[panel] != null && aps[panel].av == av)
2318         {
2319           return aps[panel].alignFrame;
2320         }
2321       }
2322     }
2323     return null;
2324   }
2325
2326   public VamsasApplication getVamsasApplication()
2327   {
2328     return v_client;
2329
2330   }
2331
2332   /**
2333    * flag set if jalview GUI is being operated programmatically
2334    */
2335   private boolean inBatchMode = false;
2336
2337   /**
2338    * check if jalview GUI is being operated programmatically
2339    * 
2340    * @return inBatchMode
2341    */
2342   public boolean isInBatchMode()
2343   {
2344     return inBatchMode;
2345   }
2346
2347   /**
2348    * set flag if jalview GUI is being operated programmatically
2349    * 
2350    * @param inBatchMode
2351    */
2352   public void setInBatchMode(boolean inBatchMode)
2353   {
2354     this.inBatchMode = inBatchMode;
2355   }
2356
2357   public void startServiceDiscovery()
2358   {
2359     startServiceDiscovery(false);
2360   }
2361
2362   public void startServiceDiscovery(boolean blocking)
2363   {
2364     boolean alive = true;
2365     Thread t0 = null, t1 = null, t2 = null;
2366
2367     // todo: changesupport handlers need to be transferred
2368     if (discoverer == null)
2369     {
2370       discoverer = new jalview.ws.jws1.Discoverer();
2371       // register PCS handler for desktop.
2372       discoverer.addPropertyChangeListener(changeSupport);
2373     }
2374     // JAL-940 - disabled JWS1 service configuration - always start discoverer until we phase out completely
2375     if (true)
2376     {
2377       (t0 = new Thread(discoverer)).start();
2378     }
2379
2380     try
2381     {
2382       if (Cache.getDefault("SHOW_ENFIN_SERVICES", true))
2383       {
2384         // EnfinEnvision web service menu entries are rebuild every time the
2385         // menu is shown, so no changeSupport events are needed.
2386         jalview.ws.EnfinEnvision2OneWay.getInstance();
2387         (t1 = new Thread(jalview.ws.EnfinEnvision2OneWay.getInstance()))
2388                 .start();
2389       }
2390     } catch (Exception e)
2391     {
2392       Cache.log
2393               .info("Exception when trying to launch Envision2 workflow discovery.",
2394                       e);
2395       Cache.log.info(e.getStackTrace());
2396     }
2397     if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2398     {
2399       if (jalview.ws.jws2.Jws2Discoverer.getDiscoverer().isRunning())
2400       {
2401         jalview.ws.jws2.Jws2Discoverer.getDiscoverer().setAborted(true);
2402       }
2403       t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer().startDiscoverer(
2404               changeSupport);
2405       
2406     }
2407     Thread t3=null;
2408     {
2409       // TODO: do rest service discovery
2410     }
2411     if (blocking)
2412     {
2413       while (alive)
2414       {
2415         try
2416         {
2417           Thread.sleep(15);
2418         } catch (Exception e)
2419         {
2420         }
2421         alive = (t1 != null && t1.isAlive())
2422                 || (t2 != null && t2.isAlive())
2423                 || (t3 != null && t3.isAlive())
2424                 || (t0 != null && t0.isAlive());
2425       }
2426     }
2427   }
2428
2429   /**
2430    * called to check if the service discovery process completed successfully.
2431    * 
2432    * @param evt
2433    */
2434   protected void JalviewServicesChanged(PropertyChangeEvent evt)
2435   {
2436     if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2437     {
2438       final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2439               .getErrorMessages();
2440       if (ermsg != null)
2441       {
2442         if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2443         {
2444         if (serviceChangedDialog == null)
2445         {
2446           // only run if we aren't already displaying one of these.
2447           javax.swing.SwingUtilities
2448                   .invokeLater(serviceChangedDialog = new Runnable()
2449                   {
2450                     public void run()
2451                     {
2452
2453                       JOptionPane
2454                               .showInternalMessageDialog(
2455                                       Desktop.desktop,
2456                                       ermsg
2457                                                 + "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",
2458                                       "Preferences Problem",
2459                                       JOptionPane.WARNING_MESSAGE);
2460                       serviceChangedDialog = null;
2461
2462                     }
2463                   });
2464         }
2465       }
2466         else
2467         {
2468           Cache.log
2469                   .error("Errors reported by JABA discovery service. Check web services preferences.\n"
2470                           + ermsg);
2471         }
2472       }
2473     }
2474   }
2475
2476   private Runnable serviceChangedDialog = null;
2477
2478   /**
2479    * start a thread to open a URL in the configured browser. Pops up a warning
2480    * dialog to the user if there is an exception when calling out to the browser
2481    * to open the URL.
2482    * 
2483    * @param url
2484    */
2485   public static void showUrl(final String url)
2486   {
2487     showUrl(url, Desktop.instance);
2488   }
2489   /**
2490    * Like showUrl but allows progress handler to be specified
2491    * @param url
2492    * @param progress (null) or object implementing IProgressIndicator
2493    */
2494   public static void showUrl(final String url, final IProgressIndicator progress)
2495   {
2496     new Thread(new Runnable()
2497     {
2498       public void run()
2499       {
2500         try
2501         {
2502           if (progress!=null) {
2503             progress.setProgressBar("Opening "+url, this.hashCode());
2504           }
2505           jalview.util.BrowserLauncher.openURL(url);
2506         } catch (Exception ex)
2507         {
2508           JOptionPane
2509                   .showInternalMessageDialog(
2510                           Desktop.desktop,
2511                           "Unixers: Couldn't find default web browser."
2512                                   + "\nAdd the full path to your browser in Preferences.",
2513                           "Web browser not found",
2514                           JOptionPane.WARNING_MESSAGE);
2515
2516           ex.printStackTrace();
2517         }
2518         if (progress!=null) {
2519           progress.setProgressBar(null, this.hashCode());
2520         }
2521       }
2522     }).start();
2523   }
2524
2525   public static WsParamSetManager wsparamManager = null;
2526
2527   public static ParamManager getUserParameterStore()
2528   {
2529     if (wsparamManager == null)
2530     {
2531       wsparamManager = new WsParamSetManager();
2532     }
2533     return wsparamManager;
2534   }
2535
2536
2537 }