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