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