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