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