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