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