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