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