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