JAL-1159 Merging 2.8.1 patches and Anne Menards work from 2012 into development branch
[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.util.MessageManager;
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 (instance == null || (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()!=null && 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(MessageManager.getString("label.input_file_url"));
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             MessageManager.getString("label.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       synchronized(progressPanel) {
1514       progressPanel.remove(progbar);
1515       GridLayout gl = (GridLayout) progressPanel.getLayout();
1516       gl.setRows(gl.getRows() - 1);
1517       if (--totalProgressCount < 1)
1518       {
1519         this.getContentPane().remove(progressPanel);
1520         progressPanel = null;
1521       }
1522       }
1523     }
1524     validate();
1525   }
1526
1527   public void stopLoading()
1528   {
1529     fileLoadingCount--;
1530     if (fileLoadingCount < 1)
1531     {
1532       while (fileLoadingPanels.size()>0)
1533       {
1534         removeProgressPanel(fileLoadingPanels.remove(0));
1535       }
1536       fileLoadingPanels.clear();
1537       fileLoadingCount = 0;
1538     }
1539     validate();
1540   }
1541
1542   public static int getViewCount(String alignmentId)
1543   {
1544     AlignViewport[] aps = getViewports(alignmentId);
1545     return (aps == null) ? 0 : aps.length;
1546   }
1547
1548   /**
1549    * 
1550    * @param alignmentId
1551    * @return all AlignmentPanels concerning the alignmentId sequence set
1552    */
1553   public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1554   {
1555     int count = 0;
1556     if (Desktop.desktop == null)
1557     {
1558       // no frames created and in headless mode
1559       // TODO: verify that frames are recoverable when in headless mode
1560       return null;
1561     }
1562     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1563     ArrayList aps = new ArrayList();
1564     for (int t = 0; t < frames.length; t++)
1565     {
1566       if (frames[t] instanceof AlignFrame)
1567       {
1568         AlignFrame af = (AlignFrame) frames[t];
1569         for (int a = 0; a < af.alignPanels.size(); a++)
1570         {
1571           if (alignmentId.equals(((AlignmentPanel) af.alignPanels
1572                   .elementAt(a)).av.getSequenceSetId()))
1573           {
1574             aps.add(af.alignPanels.elementAt(a));
1575           }
1576         }
1577       }
1578     }
1579     if (aps.size() == 0)
1580     {
1581       return null;
1582     }
1583     AlignmentPanel[] vap = new AlignmentPanel[aps.size()];
1584     for (int t = 0; t < vap.length; t++)
1585     {
1586       vap[t] = (AlignmentPanel) aps.get(t);
1587     }
1588     return vap;
1589   }
1590
1591   /**
1592    * get all the viewports on an alignment.
1593    * 
1594    * @param sequenceSetId
1595    *          unique alignment id
1596    * @return all viewports on the alignment bound to sequenceSetId
1597    */
1598   public static AlignViewport[] getViewports(String sequenceSetId)
1599   {
1600     Vector viewp = new Vector();
1601     if (desktop != null)
1602     {
1603       javax.swing.JInternalFrame[] frames = instance.getAllFrames();
1604
1605       for (int t = 0; t < frames.length; t++)
1606       {
1607         if (frames[t] instanceof AlignFrame)
1608         {
1609           AlignFrame afr = ((AlignFrame) frames[t]);
1610           if (afr.getViewport().getSequenceSetId().equals(sequenceSetId))
1611           {
1612             if (afr.alignPanels != null)
1613             {
1614               for (int a = 0; a < afr.alignPanels.size(); a++)
1615               {
1616                 if (sequenceSetId.equals(((AlignmentPanel) afr.alignPanels
1617                         .elementAt(a)).av.getSequenceSetId()))
1618                 {
1619                   viewp.addElement(((AlignmentPanel) afr.alignPanels
1620                           .elementAt(a)).av);
1621                 }
1622               }
1623             }
1624             else
1625             {
1626               viewp.addElement(((AlignFrame) frames[t]).getViewport());
1627             }
1628           }
1629         }
1630       }
1631       if (viewp.size() > 0)
1632       {
1633         AlignViewport[] vp = new AlignViewport[viewp.size()];
1634         viewp.copyInto(vp);
1635         return vp;
1636       }
1637     }
1638     return null;
1639   }
1640
1641   public void explodeViews(AlignFrame af)
1642   {
1643     int size = af.alignPanels.size();
1644     if (size < 2)
1645     {
1646       return;
1647     }
1648
1649     for (int i = 0; i < size; i++)
1650     {
1651       AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(i);
1652       AlignFrame newaf = new AlignFrame(ap);
1653       if (ap.av.explodedPosition != null
1654               && !ap.av.explodedPosition.equals(af.getBounds()))
1655       {
1656         newaf.setBounds(ap.av.explodedPosition);
1657       }
1658
1659       ap.av.gatherViewsHere = false;
1660
1661       addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1662               AlignFrame.DEFAULT_HEIGHT);
1663     }
1664
1665     af.alignPanels.clear();
1666     af.closeMenuItem_actionPerformed(true);
1667
1668   }
1669
1670   public void gatherViews(AlignFrame source)
1671   {
1672     source.viewport.gatherViewsHere = true;
1673     source.viewport.explodedPosition = source.getBounds();
1674     JInternalFrame[] frames = desktop.getAllFrames();
1675     String viewId = source.viewport.getSequenceSetId();
1676
1677     for (int t = 0; t < frames.length; t++)
1678     {
1679       if (frames[t] instanceof AlignFrame && frames[t] != source)
1680       {
1681         AlignFrame af = (AlignFrame) frames[t];
1682         boolean gatherThis = false;
1683         for (int a = 0; a < af.alignPanels.size(); a++)
1684         {
1685           AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(a);
1686           if (viewId.equals(ap.av.getSequenceSetId()))
1687           {
1688             gatherThis = true;
1689             ap.av.gatherViewsHere = false;
1690             ap.av.explodedPosition = af.getBounds();
1691             source.addAlignmentPanel(ap, false);
1692           }
1693         }
1694
1695         if (gatherThis)
1696         {
1697           af.alignPanels.clear();
1698           af.closeMenuItem_actionPerformed(true);
1699         }
1700       }
1701     }
1702
1703   }
1704
1705   jalview.gui.VamsasApplication v_client = null;
1706
1707   public void vamsasImport_actionPerformed(ActionEvent e)
1708   {
1709     if (v_client == null)
1710     {
1711       // Load and try to start a session.
1712       JalviewFileChooser chooser = new JalviewFileChooser(
1713               jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
1714
1715       chooser.setFileView(new JalviewFileView());
1716       chooser.setDialogTitle("Open a saved VAMSAS session");
1717       chooser.setToolTipText("select a vamsas session to be opened as a new vamsas session.");
1718
1719       int value = chooser.showOpenDialog(this);
1720
1721       if (value == JalviewFileChooser.APPROVE_OPTION)
1722       {
1723         String fle = chooser.getSelectedFile().toString();
1724         if (!vamsasImport(chooser.getSelectedFile()))
1725         {
1726           JOptionPane.showInternalMessageDialog(Desktop.desktop,
1727                   MessageManager.formatMessage("label.couldnt_import_as_vamsas_session", new String[]{fle}),
1728                   MessageManager.getString("label.vamsas_document_import_failed"),
1729                   JOptionPane.ERROR_MESSAGE);
1730         }
1731       }
1732     }
1733     else
1734     {
1735       jalview.bin.Cache.log
1736               .error("Implementation error - load session from a running session is not supported.");
1737     }
1738   }
1739
1740   /**
1741    * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
1742    * 
1743    * @param file
1744    * @return true if import was a success and a session was started.
1745    */
1746   public boolean vamsasImport(URL url)
1747   {
1748     // TODO: create progress bar
1749     if (v_client != null)
1750     {
1751
1752       jalview.bin.Cache.log
1753               .error("Implementation error - load session from a running session is not supported.");
1754       return false;
1755     }
1756
1757     try
1758     {
1759       // copy the URL content to a temporary local file
1760       // TODO: be a bit cleverer here with nio (?!)
1761       File file = File.createTempFile("vdocfromurl", ".vdj");
1762       FileOutputStream fos = new FileOutputStream(file);
1763       BufferedInputStream bis = new BufferedInputStream(url.openStream());
1764       byte[] buffer = new byte[2048];
1765       int ln;
1766       while ((ln = bis.read(buffer)) > -1)
1767       {
1768         fos.write(buffer, 0, ln);
1769       }
1770       bis.close();
1771       fos.close();
1772       v_client = new jalview.gui.VamsasApplication(this, file,
1773               url.toExternalForm());
1774     } catch (Exception ex)
1775     {
1776       jalview.bin.Cache.log.error(
1777               "Failed to create new vamsas session from contents of URL "
1778                       + url, ex);
1779       return false;
1780     }
1781     setupVamsasConnectedGui();
1782     v_client.initial_update(); // TODO: thread ?
1783     return v_client.inSession();
1784   }
1785
1786   /**
1787    * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
1788    * 
1789    * @param file
1790    * @return true if import was a success and a session was started.
1791    */
1792   public boolean vamsasImport(File file)
1793   {
1794     if (v_client != null)
1795     {
1796
1797       jalview.bin.Cache.log
1798               .error("Implementation error - load session from a running session is not supported.");
1799       return false;
1800     }
1801
1802     setProgressBar("Importing VAMSAS session from " + file.getName(),
1803             file.hashCode());
1804     try
1805     {
1806       v_client = new jalview.gui.VamsasApplication(this, file, null);
1807     } catch (Exception ex)
1808     {
1809       setProgressBar("Importing VAMSAS session from " + file.getName(),
1810               file.hashCode());
1811       jalview.bin.Cache.log.error(
1812               "New vamsas session from existing session file failed:", ex);
1813       return false;
1814     }
1815     setupVamsasConnectedGui();
1816     v_client.initial_update(); // TODO: thread ?
1817     setProgressBar("Importing VAMSAS session from " + file.getName(),
1818             file.hashCode());
1819     return v_client.inSession();
1820   }
1821
1822   public boolean joinVamsasSession(String mysesid)
1823   {
1824     if (v_client != null)
1825     {
1826       throw new Error(
1827               "Trying to join a vamsas session when another is already connected.");
1828     }
1829     if (mysesid == null)
1830     {
1831       throw new Error("Invalid vamsas session id.");
1832     }
1833     v_client = new VamsasApplication(this, mysesid);
1834     setupVamsasConnectedGui();
1835     v_client.initial_update();
1836     return (v_client.inSession());
1837   }
1838
1839   public void vamsasStart_actionPerformed(ActionEvent e)
1840   {
1841     if (v_client == null)
1842     {
1843       // Start a session.
1844       // we just start a default session for moment.
1845       /*
1846        * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
1847        * getProperty("LAST_DIRECTORY"));
1848        * 
1849        * chooser.setFileView(new JalviewFileView());
1850        * chooser.setDialogTitle("Load Vamsas file");
1851        * chooser.setToolTipText("Import");
1852        * 
1853        * int value = chooser.showOpenDialog(this);
1854        * 
1855        * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
1856        * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
1857        */
1858       v_client = new VamsasApplication(this);
1859       setupVamsasConnectedGui();
1860       v_client.initial_update(); // TODO: thread ?
1861     }
1862     else
1863     {
1864       // store current data in session.
1865       v_client.push_update(); // TODO: thread
1866     }
1867   }
1868
1869   protected void setupVamsasConnectedGui()
1870   {
1871     vamsasStart.setText(MessageManager.getString("label.session_update"));
1872     vamsasSave.setVisible(true);
1873     vamsasStop.setVisible(true);
1874     vamsasImport.setVisible(false); // Document import to existing session is
1875     // not possible for vamsas-client-1.0.
1876   }
1877
1878   protected void setupVamsasDisconnectedGui()
1879   {
1880     vamsasSave.setVisible(false);
1881     vamsasStop.setVisible(false);
1882     vamsasImport.setVisible(true);
1883     vamsasStart.setText(MessageManager.getString("label.new_vamsas_session"));
1884   }
1885
1886   public void vamsasStop_actionPerformed(ActionEvent e)
1887   {
1888     if (v_client != null)
1889     {
1890       v_client.end_session();
1891       v_client = null;
1892       setupVamsasDisconnectedGui();
1893     }
1894   }
1895
1896   protected void buildVamsasStMenu()
1897   {
1898     if (v_client == null)
1899     {
1900       String[] sess = null;
1901       try
1902       {
1903         sess = VamsasApplication.getSessionList();
1904       } catch (Exception e)
1905       {
1906         jalview.bin.Cache.log.warn(
1907                 "Problem getting current sessions list.", e);
1908         sess = null;
1909       }
1910       if (sess != null)
1911       {
1912         jalview.bin.Cache.log.debug("Got current sessions list: "
1913                 + sess.length + " entries.");
1914         VamsasStMenu.removeAll();
1915         for (int i = 0; i < sess.length; i++)
1916         {
1917           JMenuItem sessit = new JMenuItem();
1918           sessit.setText(sess[i]);
1919           sessit.setToolTipText("Connect to session " + sess[i]);
1920           final Desktop dsktp = this;
1921           final String mysesid = sess[i];
1922           sessit.addActionListener(new ActionListener()
1923           {
1924
1925             public void actionPerformed(ActionEvent e)
1926             {
1927               if (dsktp.v_client == null)
1928               {
1929                 Thread rthr = new Thread(new Runnable()
1930                 {
1931
1932                   public void run()
1933                   {
1934                     dsktp.v_client = new VamsasApplication(dsktp, mysesid);
1935                     dsktp.setupVamsasConnectedGui();
1936                     dsktp.v_client.initial_update();
1937                   }
1938
1939                 });
1940                 rthr.start();
1941               }
1942             };
1943           });
1944           VamsasStMenu.add(sessit);
1945         }
1946         // don't show an empty menu.
1947         VamsasStMenu.setVisible(sess.length > 0);
1948
1949       }
1950       else
1951       {
1952         jalview.bin.Cache.log.debug("No current vamsas sessions.");
1953         VamsasStMenu.removeAll();
1954         VamsasStMenu.setVisible(false);
1955       }
1956     }
1957     else
1958     {
1959       // Not interested in the content. Just hide ourselves.
1960       VamsasStMenu.setVisible(false);
1961     }
1962   }
1963
1964   public void vamsasSave_actionPerformed(ActionEvent e)
1965   {
1966     if (v_client != null)
1967     {
1968       JalviewFileChooser chooser = new JalviewFileChooser(
1969               jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
1970               { "vdj" }, // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
1971               new String[]
1972               { "Vamsas Document" }, "Vamsas Document");
1973
1974       chooser.setFileView(new JalviewFileView());
1975       chooser.setDialogTitle("Save Vamsas Document Archive");
1976
1977       int value = chooser.showSaveDialog(this);
1978
1979       if (value == JalviewFileChooser.APPROVE_OPTION)
1980       {
1981         java.io.File choice = chooser.getSelectedFile();
1982         JPanel progpanel = addProgressPanel("Saving VAMSAS Document to "
1983                 + choice.getName());
1984         jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
1985         String warnmsg = null;
1986         String warnttl = null;
1987         try
1988         {
1989           v_client.vclient.storeDocument(choice);
1990         } catch (Error ex)
1991         {
1992           warnttl = "Serious Problem saving Vamsas Document";
1993           warnmsg = ex.toString();
1994           jalview.bin.Cache.log.error("Error Whilst saving document to "
1995                   + choice, ex);
1996
1997         } catch (Exception ex)
1998         {
1999           warnttl = "Problem saving Vamsas Document.";
2000           warnmsg = ex.toString();
2001           jalview.bin.Cache.log.warn("Exception Whilst saving document to "
2002                   + choice, ex);
2003
2004         }
2005         removeProgressPanel(progpanel);
2006         if (warnmsg != null)
2007         {
2008           JOptionPane.showInternalMessageDialog(Desktop.desktop,
2009
2010           warnmsg, warnttl, JOptionPane.ERROR_MESSAGE);
2011         }
2012       }
2013     }
2014   }
2015
2016   JPanel vamUpdate = null;
2017
2018   /**
2019    * hide vamsas user gui bits when a vamsas document event is being handled.
2020    * 
2021    * @param b
2022    *          true to hide gui, false to reveal gui
2023    */
2024   public void setVamsasUpdate(boolean b)
2025   {
2026     jalview.bin.Cache.log.debug("Setting gui for Vamsas update "
2027             + (b ? "in progress" : "finished"));
2028
2029     if (vamUpdate != null)
2030     {
2031       this.removeProgressPanel(vamUpdate);
2032     }
2033     if (b)
2034     {
2035       vamUpdate = this.addProgressPanel("Updating vamsas session");
2036     }
2037     vamsasStart.setVisible(!b);
2038     vamsasStop.setVisible(!b);
2039     vamsasSave.setVisible(!b);
2040   }
2041
2042   public JInternalFrame[] getAllFrames()
2043   {
2044     return desktop.getAllFrames();
2045   }
2046
2047   /**
2048    * Checks the given url to see if it gives a response indicating that the user
2049    * should be informed of a new questionnaire.
2050    * 
2051    * @param url
2052    */
2053   public void checkForQuestionnaire(String url)
2054   {
2055     UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2056     // javax.swing.SwingUtilities.invokeLater(jvq);
2057     new Thread(jvq).start();
2058   }
2059
2060   /**
2061    * Proxy class for JDesktopPane which optionally displays the current memory
2062    * usage and highlights the desktop area with a red bar if free memory runs
2063    * low.
2064    * 
2065    * @author AMW
2066    */
2067   public class MyDesktopPane extends JDesktopPane implements Runnable
2068   {
2069
2070     boolean showMemoryUsage = false;
2071
2072     Runtime runtime;
2073
2074     java.text.NumberFormat df;
2075
2076     float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2077             percentUsage;
2078
2079     public MyDesktopPane(boolean showMemoryUsage)
2080     {
2081       showMemoryUsage(showMemoryUsage);
2082     }
2083
2084     public void showMemoryUsage(boolean showMemoryUsage)
2085     {
2086       this.showMemoryUsage = showMemoryUsage;
2087       if (showMemoryUsage)
2088       {
2089         Thread worker = new Thread(this);
2090         worker.start();
2091       }
2092     }
2093
2094     public boolean isShowMemoryUsage()
2095     {
2096       return showMemoryUsage;
2097     }
2098
2099     public void run()
2100     {
2101       df = java.text.NumberFormat.getNumberInstance();
2102       df.setMaximumFractionDigits(2);
2103       runtime = Runtime.getRuntime();
2104
2105       while (showMemoryUsage)
2106       {
2107         try
2108         {
2109           maxMemory = runtime.maxMemory() / 1048576f;
2110           allocatedMemory = runtime.totalMemory() / 1048576f;
2111           freeMemory = runtime.freeMemory() / 1048576f;
2112           totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2113
2114           percentUsage = (totalFreeMemory / maxMemory) * 100;
2115
2116           // if (percentUsage < 20)
2117           {
2118             // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2119             // Color.red);
2120             // instance.set.setBorder(border1);
2121           }
2122           repaint();
2123           // sleep after showing usage
2124           Thread.sleep(3000);
2125         } catch (Exception ex)
2126         {
2127           ex.printStackTrace();
2128         }
2129       }
2130     }
2131
2132     public void paintComponent(Graphics g)
2133     {
2134       if (showMemoryUsage && g != null && df != null)
2135       {
2136         if (percentUsage < 20)
2137           g.setColor(Color.red);
2138         FontMetrics fm = g.getFontMetrics();
2139         if (fm != null)
2140         {
2141           g.drawString(
2142                           MessageManager.formatMessage("label.memory_stats", new String[]{df.format(totalFreeMemory),df.format(maxMemory),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(MessageManager.getString("label.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(MessageManager.getString("action.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     // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2441     if (true)
2442     {
2443     // todo: changesupport handlers need to be transferred
2444     if (discoverer == null)
2445     {
2446       discoverer = new jalview.ws.jws1.Discoverer();
2447       // register PCS handler for desktop.
2448       discoverer.addPropertyChangeListener(changeSupport);
2449     }
2450     // JAL-940 - disabled JWS1 service configuration - always start discoverer
2451     // until we phase out completely
2452       (t0 = new Thread(discoverer)).start();
2453     }
2454
2455     // ENFIN services are EOLed as of Jalview 2.8.1 release
2456     if (false)
2457     {
2458       try
2459       {
2460         if (Cache.getDefault("SHOW_ENFIN_SERVICES", true))
2461         {
2462           // EnfinEnvision web service menu entries are rebuild every time the
2463           // menu is shown, so no changeSupport events are needed.
2464           jalview.ws.EnfinEnvision2OneWay.getInstance();
2465           (t1 = new Thread(jalview.ws.EnfinEnvision2OneWay.getInstance()))
2466                   .start();
2467         }
2468       } catch (Exception e)
2469       {
2470         Cache.log
2471                 .info("Exception when trying to launch Envision2 workflow discovery.",
2472                         e);
2473         Cache.log.info(e.getStackTrace());
2474       }
2475     }
2476
2477     if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2478     {
2479       if (jalview.ws.jws2.Jws2Discoverer.getDiscoverer().isRunning())
2480       {
2481         jalview.ws.jws2.Jws2Discoverer.getDiscoverer().setAborted(true);
2482       }
2483       t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer().startDiscoverer(
2484               changeSupport);
2485
2486     }
2487     Thread t3 = null;
2488     {
2489       // TODO: do rest service discovery
2490     }
2491     if (blocking)
2492     {
2493       while (alive)
2494       {
2495         try
2496         {
2497           Thread.sleep(15);
2498         } catch (Exception e)
2499         {
2500         }
2501         alive = (t1 != null && t1.isAlive())
2502                 || (t2 != null && t2.isAlive())
2503                 || (t3 != null && t3.isAlive())
2504                 || (t0 != null && t0.isAlive());
2505       }
2506     }
2507   }
2508
2509   /**
2510    * called to check if the service discovery process completed successfully.
2511    * 
2512    * @param evt
2513    */
2514   protected void JalviewServicesChanged(PropertyChangeEvent evt)
2515   {
2516     if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2517     {
2518       final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2519               .getErrorMessages();
2520       if (ermsg != null)
2521       {
2522         if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2523         {
2524           if (serviceChangedDialog == null)
2525           {
2526             // only run if we aren't already displaying one of these.
2527             addDialogThread(serviceChangedDialog = new Runnable()
2528             {
2529               public void run()
2530               {
2531
2532                 /*
2533                  * JalviewDialog jd =new JalviewDialog() {
2534                  * 
2535                  * @Override protected void cancelPressed() { // TODO
2536                  * Auto-generated method stub
2537                  * 
2538                  * }@Override protected void okPressed() { // TODO
2539                  * Auto-generated method stub
2540                  * 
2541                  * }@Override protected void raiseClosed() { // TODO
2542                  * Auto-generated method stub
2543                  * 
2544                  * } }; jd.initDialogFrame(new
2545                  * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2546                  * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2547                  * + " or mis-configured HTTP proxy settings.<br/>" +
2548                  * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2549                  * +
2550                  * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2551                  * ), true, true, "Web Service Configuration Problem", 450,
2552                  * 400);
2553                  * 
2554                  * jd.waitForInput();
2555                  */
2556                 JOptionPane
2557                         .showConfirmDialog(
2558                                 Desktop.desktop,
2559                                 new JLabel(
2560                                         "<html><table width=\"450\"><tr><td>"
2561                                                 + ermsg
2562                                                 + "</td></tr></table>"
2563                                                 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2564                                                 + " or mis-configured HTTP proxy settings.</p>"
2565                                                 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2566                                                 + " Tools->Preferences dialog box to change them.</p></html>"),
2567                                 "Web Service Configuration Problem",
2568                                 JOptionPane.DEFAULT_OPTION,
2569                                 JOptionPane.ERROR_MESSAGE);
2570                 serviceChangedDialog = null;
2571
2572               }
2573             });
2574           }
2575         }
2576         else
2577         {
2578           Cache.log
2579                   .error("Errors reported by JABA discovery service. Check web services preferences.\n"
2580                           + ermsg);
2581         }
2582       }
2583     }
2584   }
2585
2586   private Runnable serviceChangedDialog = null;
2587
2588   /**
2589    * start a thread to open a URL in the configured browser. Pops up a warning
2590    * dialog to the user if there is an exception when calling out to the browser
2591    * to open the URL.
2592    * 
2593    * @param url
2594    */
2595   public static void showUrl(final String url)
2596   {
2597     showUrl(url, Desktop.instance);
2598   }
2599
2600   /**
2601    * Like showUrl but allows progress handler to be specified
2602    * 
2603    * @param url
2604    * @param progress
2605    *          (null) or object implementing IProgressIndicator
2606    */
2607   public static void showUrl(final String url,
2608           final IProgressIndicator progress)
2609   {
2610     new Thread(new Runnable()
2611     {
2612       public void run()
2613       {
2614         try
2615         {
2616           if (progress != null)
2617           {
2618             progress.setProgressBar("Opening " + url, this.hashCode());
2619           }
2620           jalview.util.BrowserLauncher.openURL(url);
2621         } catch (Exception ex)
2622         {
2623           JOptionPane
2624                   .showInternalMessageDialog(
2625                           Desktop.desktop,
2626                           "Unixers: Couldn't find default web browser."
2627                                   + "\nAdd the full path to your browser in Preferences.",
2628                           "Web browser not found",
2629                           JOptionPane.WARNING_MESSAGE);
2630
2631           ex.printStackTrace();
2632         }
2633         if (progress != null)
2634         {
2635           progress.setProgressBar(null, this.hashCode());
2636         }
2637       }
2638     }).start();
2639   }
2640
2641   public static WsParamSetManager wsparamManager = null;
2642
2643   public static ParamManager getUserParameterStore()
2644   {
2645     if (wsparamManager == null)
2646     {
2647       wsparamManager = new WsParamSetManager();
2648     }
2649     return wsparamManager;
2650   }
2651
2652   /**
2653    * static hyperlink handler proxy method for use by Jalview's internal windows
2654    * 
2655    * @param e
2656    */
2657   public static void hyperlinkUpdate(HyperlinkEvent e)
2658   {
2659     if (e.getEventType() == EventType.ACTIVATED)
2660     {
2661       String url = null;
2662       try
2663       {
2664         url = e.getURL().toString();
2665         Desktop.showUrl(url);
2666       } catch (Exception x)
2667       {
2668         if (url != null)
2669         {
2670           if (Cache.log != null)
2671           {
2672             Cache.log.error("Couldn't handle string " + url + " as a URL.");
2673           }
2674           else
2675           {
2676             System.err.println("Couldn't handle string " + url
2677                     + " as a URL.");
2678           }
2679         }
2680         // ignore any exceptions due to dud links.
2681       }
2682
2683     }
2684   }
2685
2686   /**
2687    * single thread that handles display of dialogs to user.
2688    */
2689   ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2690
2691   /**
2692    * flag indicating if dialogExecutor should try to acquire a permit
2693    */
2694   private volatile boolean dialogPause = true;
2695
2696   /**
2697    * pause the queue
2698    */
2699   private java.util.concurrent.Semaphore block = new Semaphore(0);
2700
2701   /**
2702    * add another dialog thread to the queue
2703    * 
2704    * @param prompter
2705    */
2706   public void addDialogThread(final Runnable prompter)
2707   {
2708     dialogExecutor.submit(new Runnable()
2709     {
2710       public void run()
2711       {
2712         if (dialogPause)
2713         {
2714           try
2715           {
2716             block.acquire();
2717           } catch (InterruptedException x)
2718           {
2719           }
2720           ;
2721         }
2722         if (instance == null)
2723         {
2724           return;
2725         }
2726         try
2727         {
2728           SwingUtilities.invokeAndWait(prompter);
2729         } catch (Exception q)
2730         {
2731           Cache.log.warn("Unexpected Exception in dialog thread.", q);
2732         }
2733       }
2734     });
2735   }
2736
2737   public void startDialogQueue()
2738   {
2739     // set the flag so we don't pause waiting for another permit and semaphore
2740     // the current task to begin
2741     dialogPause = false;
2742     block.release();
2743   }
2744 }