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