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