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