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