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