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