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