JAL-1130 made SplashScreen able to be a dialog that supports opening of hyperlinks...
[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     new Thread(new Runnable() { public void run() {new SplashScreen(true);}}).start();
1070   }
1071
1072   public StringBuffer getAboutMessage(boolean shortv)
1073   {
1074     StringBuffer message = new StringBuffer();
1075     message.append("<html>");
1076     if (shortv)
1077     {
1078       message.append("<h1><strong>Version: "
1079               + jalview.bin.Cache.getProperty("VERSION")
1080               + "</strong></h1><br>");
1081       message.append("<strong>Last Updated: <em>"
1082               + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown")
1083               + "</em></strong>");
1084
1085     }
1086     else
1087     {
1088
1089       message.append("<strong>Version "
1090               + jalview.bin.Cache.getProperty("VERSION")
1091               + "; last updated: "
1092               + jalview.bin.Cache.getDefault("BUILD_DATE", "unknown"));
1093     }
1094
1095     if (jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking").equals(
1096             "Checking"))
1097     {
1098       message.append("<br>...Checking latest version...</br>");
1099     }
1100     else if (!jalview.bin.Cache.getDefault("LATEST_VERSION", "Checking")
1101             .equals(jalview.bin.Cache.getProperty("VERSION")))
1102     {
1103       boolean red = false;
1104       if (jalview.bin.Cache.getProperty("VERSION").toLowerCase()
1105               .indexOf("automated build") == -1)
1106       {
1107         red = true;
1108         // Displayed when code version and jnlp version do not match and code
1109         // version is not a development build
1110         message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1111       }
1112
1113       message.append("<br>!! Version "
1114               + jalview.bin.Cache.getDefault("LATEST_VERSION",
1115                       "..Checking..")
1116               + " is available for download from "
1117               + jalview.bin.Cache.getDefault("www.jalview.org",
1118                       "http://www.jalview.org") + " !!");
1119       if (red)
1120       {
1121         message.append("</div>");
1122       }
1123     }
1124     message.append("<br>Authors:  "
1125             + jalview.bin.Cache
1126                     .getDefault(
1127                             "AUTHORNAMES",
1128                             "Jim Procter, Andrew Waterhouse, Jan Engelhardt, Lauren Lui, Michele Clamp, James Cuff, Steve Searle, David Martin & Geoff Barton")
1129             + "<br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1130             + "<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"
1131             + "<br>If  you use Jalview, please cite:"
1132             + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1133             + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1134             + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1135             + "</html>");
1136     return message;
1137   }
1138
1139   /**
1140    * DOCUMENT ME!
1141    * 
1142    * @param e
1143    *          DOCUMENT ME!
1144    */
1145   public void documentationMenuItem_actionPerformed(ActionEvent e)
1146   {
1147     try
1148     {
1149       ClassLoader cl = jalview.gui.Desktop.class.getClassLoader();
1150       java.net.URL url = javax.help.HelpSet.findHelpSet(cl, "help/help");
1151       javax.help.HelpSet hs = new javax.help.HelpSet(cl, url);
1152
1153       javax.help.HelpBroker hb = hs.createHelpBroker();
1154       hb.setCurrentID("home");
1155       hb.setDisplayed(true);
1156     } catch (Exception ex)
1157     {
1158     }
1159   }
1160
1161   public void closeAll_actionPerformed(ActionEvent e)
1162   {
1163     JInternalFrame[] frames = desktop.getAllFrames();
1164     for (int i = 0; i < frames.length; i++)
1165     {
1166       try
1167       {
1168         frames[i].setClosed(true);
1169       } catch (java.beans.PropertyVetoException ex)
1170       {
1171       }
1172     }
1173     System.out.println("ALL CLOSED");
1174     if (v_client != null)
1175     {
1176       // TODO clear binding to vamsas document objects on close_all
1177
1178     }
1179   }
1180
1181   public void raiseRelated_actionPerformed(ActionEvent e)
1182   {
1183     reorderAssociatedWindows(false, false);
1184   }
1185
1186   public void minimizeAssociated_actionPerformed(ActionEvent e)
1187   {
1188     reorderAssociatedWindows(true, false);
1189   }
1190
1191   void closeAssociatedWindows()
1192   {
1193     reorderAssociatedWindows(false, true);
1194   }
1195
1196   /*
1197    * (non-Javadoc)
1198    * 
1199    * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1200    * ActionEvent)
1201    */
1202   protected void garbageCollect_actionPerformed(ActionEvent e)
1203   {
1204     // We simply collect the garbage
1205     jalview.bin.Cache.log.debug("Collecting garbage...");
1206     System.gc();
1207     jalview.bin.Cache.log.debug("Finished garbage collection.");
1208   }
1209
1210   /*
1211    * (non-Javadoc)
1212    * 
1213    * @see
1214    * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1215    * )
1216    */
1217   protected void showMemusage_actionPerformed(ActionEvent e)
1218   {
1219     desktop.showMemoryUsage(showMemusage.isSelected());
1220   }
1221
1222   /*
1223    * (non-Javadoc)
1224    * 
1225    * @see
1226    * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1227    * )
1228    */
1229   protected void showConsole_actionPerformed(ActionEvent e)
1230   {
1231     showConsole(showConsole.isSelected());
1232   }
1233
1234   Console jconsole = null;
1235
1236   /**
1237    * control whether the java console is visible or not
1238    * 
1239    * @param selected
1240    */
1241   void showConsole(boolean selected)
1242   {
1243     showConsole.setSelected(selected);
1244     // TODO: decide if we should update properties file
1245     Cache.setProperty("SHOW_JAVA_CONSOLE", Boolean.valueOf(selected)
1246             .toString());
1247     jconsole.setVisible(selected);
1248   }
1249
1250   void reorderAssociatedWindows(boolean minimize, boolean close)
1251   {
1252     JInternalFrame[] frames = desktop.getAllFrames();
1253     if (frames == null || frames.length < 1)
1254     {
1255       return;
1256     }
1257
1258     AlignViewport source = null, target = null;
1259     if (frames[0] instanceof AlignFrame)
1260     {
1261       source = ((AlignFrame) frames[0]).getCurrentView();
1262     }
1263     else if (frames[0] instanceof TreePanel)
1264     {
1265       source = ((TreePanel) frames[0]).getViewPort();
1266     }
1267     else if (frames[0] instanceof PCAPanel)
1268     {
1269       source = ((PCAPanel) frames[0]).av;
1270     }
1271     else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1272     {
1273       source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1274     }
1275
1276     if (source != null)
1277     {
1278       for (int i = 0; i < frames.length; i++)
1279       {
1280         target = null;
1281         if (frames[i] == null)
1282         {
1283           continue;
1284         }
1285         if (frames[i] instanceof AlignFrame)
1286         {
1287           target = ((AlignFrame) frames[i]).getCurrentView();
1288         }
1289         else if (frames[i] instanceof TreePanel)
1290         {
1291           target = ((TreePanel) frames[i]).getViewPort();
1292         }
1293         else if (frames[i] instanceof PCAPanel)
1294         {
1295           target = ((PCAPanel) frames[i]).av;
1296         }
1297         else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1298         {
1299           target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1300         }
1301
1302         if (source == target)
1303         {
1304           try
1305           {
1306             if (close)
1307             {
1308               frames[i].setClosed(true);
1309             }
1310             else
1311             {
1312               frames[i].setIcon(minimize);
1313               if (!minimize)
1314               {
1315                 frames[i].toFront();
1316               }
1317             }
1318
1319           } catch (java.beans.PropertyVetoException ex)
1320           {
1321           }
1322         }
1323       }
1324     }
1325   }
1326
1327   /**
1328    * DOCUMENT ME!
1329    * 
1330    * @param e
1331    *          DOCUMENT ME!
1332    */
1333   protected void preferences_actionPerformed(ActionEvent e)
1334   {
1335     new Preferences();
1336   }
1337
1338   /**
1339    * DOCUMENT ME!
1340    * 
1341    * @param e
1342    *          DOCUMENT ME!
1343    */
1344   public void saveState_actionPerformed(ActionEvent e)
1345   {
1346     JalviewFileChooser chooser = new JalviewFileChooser(
1347             jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
1348             { "jar" }, new String[]
1349             { "Jalview Project" }, "Jalview Project");
1350
1351     chooser.setFileView(new JalviewFileView());
1352     chooser.setDialogTitle("Save State");
1353
1354     int value = chooser.showSaveDialog(this);
1355
1356     if (value == JalviewFileChooser.APPROVE_OPTION)
1357     {
1358       final Desktop me = this;
1359       final java.io.File choice = chooser.getSelectedFile();
1360       new Thread(new Runnable()
1361       {
1362         public void run()
1363         {
1364
1365       setProgressBar("Saving jalview project " + choice.getName(),
1366               choice.hashCode());
1367       jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
1368       // TODO catch and handle errors for savestate
1369       // TODO prevent user from messing with the Desktop whilst we're saving
1370       try
1371       {
1372         new Jalview2XML().SaveState(choice);
1373       } catch (OutOfMemoryError oom)
1374       {
1375         new OOMWarning(
1376                 "Whilst saving current state to " + choice.getName(), oom);
1377       } catch (Exception ex)
1378       {
1379         Cache.log
1380                 .error("Problems whilst trying to save to "
1381                         + choice.getName(), ex);
1382         JOptionPane.showMessageDialog(me,
1383                 "Error whilst saving current state to " + choice.getName(),
1384                 "Couldn't save project", JOptionPane.WARNING_MESSAGE);
1385       }
1386       setProgressBar(null, choice.hashCode());
1387         }
1388       }).start();
1389     }
1390   }
1391
1392   /**
1393    * DOCUMENT ME!
1394    * 
1395    * @param e
1396    *          DOCUMENT ME!
1397    */
1398   public void loadState_actionPerformed(ActionEvent e)
1399   {
1400     JalviewFileChooser chooser = new JalviewFileChooser(
1401             jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
1402             { "jar" }, new String[]
1403             { "Jalview Project" }, "Jalview Project");
1404     chooser.setFileView(new JalviewFileView());
1405     chooser.setDialogTitle("Restore state");
1406
1407     int value = chooser.showOpenDialog(this);
1408
1409     if (value == JalviewFileChooser.APPROVE_OPTION)
1410     {
1411       final String choice = chooser.getSelectedFile().getAbsolutePath();
1412       jalview.bin.Cache.setProperty("LAST_DIRECTORY", chooser
1413               .getSelectedFile().getParent());
1414       new Thread(new Runnable()
1415       {
1416         public void run()
1417         {
1418           setProgressBar("loading jalview project " + choice,
1419                   choice.hashCode());
1420           try
1421           {
1422             new Jalview2XML().LoadJalviewAlign(choice);
1423           } catch (OutOfMemoryError oom)
1424           {
1425             new OOMWarning("Whilst loading project from " + choice, oom);
1426           } catch (Exception ex)
1427           {
1428             Cache.log.error("Problems whilst loading project from "
1429                     + choice, ex);
1430             JOptionPane.showMessageDialog(Desktop.desktop,
1431                     "Error whilst loading project from " + choice,
1432                     "Couldn't load project", JOptionPane.WARNING_MESSAGE);
1433           }
1434           setProgressBar(null, choice.hashCode());
1435         }
1436       }).start();
1437     }
1438   }
1439
1440   public void inputSequence_actionPerformed(ActionEvent e)
1441   {
1442     new SequenceFetcher(this);
1443   }
1444
1445   JPanel progressPanel;
1446   ArrayList<JPanel> fileLoadingPanels=new ArrayList<JPanel>();
1447   public void startLoading(final String fileName)
1448   {
1449     if (fileLoadingCount == 0)
1450     {
1451       fileLoadingPanels.add(addProgressPanel("Loading File: " + fileName + "   "));
1452     }
1453     fileLoadingCount++;
1454   }
1455
1456   private JPanel addProgressPanel(String string)
1457   {
1458     if (progressPanel == null)
1459     {
1460       progressPanel = new JPanel(new GridLayout(1,1));
1461       totalProgressCount = 0;
1462       instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1463     }
1464     JPanel thisprogress=new JPanel(new BorderLayout(10,5));
1465     JProgressBar progressBar = new JProgressBar();
1466     progressBar.setIndeterminate(true);
1467
1468     thisprogress.add(new JLabel(string), BorderLayout.WEST);
1469
1470     thisprogress.add(progressBar, BorderLayout.CENTER);
1471     progressPanel.add(thisprogress);
1472     ((GridLayout)progressPanel.getLayout()).setRows(((GridLayout)progressPanel.getLayout()).getRows()+1);
1473     ++totalProgressCount;
1474     instance.validate();
1475     return thisprogress;
1476   }
1477
1478   int totalProgressCount = 0;
1479
1480   private void removeProgressPanel(JPanel progbar)
1481   {
1482     if (progressPanel != null)
1483     {
1484       progressPanel.remove(progbar);
1485       GridLayout gl = (GridLayout) progressPanel.getLayout();
1486       gl.setRows(gl.getRows()-1);
1487       if (--totalProgressCount < 1)
1488       {
1489         this.getContentPane().remove(progressPanel);
1490         progressPanel = null;
1491       }
1492     }
1493     validate();
1494   }
1495
1496   public void stopLoading()
1497   {
1498     fileLoadingCount--;
1499     if (fileLoadingCount < 1)
1500     {
1501       for (JPanel flp : fileLoadingPanels)
1502       {
1503         removeProgressPanel(flp);
1504       }
1505       fileLoadingPanels.clear();
1506       fileLoadingCount = 0;
1507     }
1508     validate();
1509   }
1510
1511   public static int getViewCount(String alignmentId)
1512   {
1513     AlignViewport[] aps = getViewports(alignmentId);
1514     return (aps == null) ? 0 : aps.length;
1515   }
1516
1517   /**
1518    * 
1519    * @param alignmentId
1520    * @return all AlignmentPanels concerning the alignmentId sequence set
1521    */
1522   public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1523   {
1524     int count = 0;
1525     if (Desktop.desktop == null)
1526     {
1527       // no frames created and in headless mode
1528       // TODO: verify that frames are recoverable when in headless mode
1529       return null;
1530     }
1531     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
1532     ArrayList aps = new ArrayList();
1533     for (int t = 0; t < frames.length; t++)
1534     {
1535       if (frames[t] instanceof AlignFrame)
1536       {
1537         AlignFrame af = (AlignFrame) frames[t];
1538         for (int a = 0; a < af.alignPanels.size(); a++)
1539         {
1540           if (alignmentId
1541                   .equals(((AlignmentPanel) af.alignPanels.elementAt(a)).av
1542                           .getSequenceSetId()))
1543           {
1544             aps.add(af.alignPanels.elementAt(a));
1545           }
1546         }
1547       }
1548     }
1549     if (aps.size() == 0)
1550     {
1551       return null;
1552     }
1553     AlignmentPanel[] vap = new AlignmentPanel[aps.size()];
1554     for (int t = 0; t < vap.length; t++)
1555     {
1556       vap[t] = (AlignmentPanel) aps.get(t);
1557     }
1558     return vap;
1559   }
1560
1561   /**
1562    * get all the viewports on an alignment.
1563    * 
1564    * @param sequenceSetId
1565    *          unique alignment id
1566    * @return all viewports on the alignment bound to sequenceSetId
1567    */
1568   public static AlignViewport[] getViewports(String sequenceSetId)
1569   {
1570     Vector viewp = new Vector();
1571     if (desktop != null)
1572     {
1573       javax.swing.JInternalFrame[] frames = instance.getAllFrames();
1574
1575       for (int t = 0; t < frames.length; t++)
1576       {
1577         if (frames[t] instanceof AlignFrame)
1578         {
1579           AlignFrame afr = ((AlignFrame) frames[t]);
1580           if (afr.getViewport().getSequenceSetId().equals(sequenceSetId))
1581           {
1582             if (afr.alignPanels != null)
1583             {
1584               for (int a = 0; a < afr.alignPanels.size(); a++)
1585               {
1586                 if (sequenceSetId.equals(((AlignmentPanel) afr.alignPanels
1587                         .elementAt(a)).av.getSequenceSetId()))
1588                 {
1589                   viewp.addElement(((AlignmentPanel) afr.alignPanels
1590                           .elementAt(a)).av);
1591                 }
1592               }
1593             }
1594             else
1595             {
1596               viewp.addElement(((AlignFrame) frames[t]).getViewport());
1597             }
1598           }
1599         }
1600       }
1601       if (viewp.size() > 0)
1602       {
1603         AlignViewport[] vp = new AlignViewport[viewp.size()];
1604         viewp.copyInto(vp);
1605         return vp;
1606       }
1607     }
1608     return null;
1609   }
1610
1611   public void explodeViews(AlignFrame af)
1612   {
1613     int size = af.alignPanels.size();
1614     if (size < 2)
1615     {
1616       return;
1617     }
1618
1619     for (int i = 0; i < size; i++)
1620     {
1621       AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(i);
1622       AlignFrame newaf = new AlignFrame(ap);
1623       if (ap.av.explodedPosition != null
1624               && !ap.av.explodedPosition.equals(af.getBounds()))
1625       {
1626         newaf.setBounds(ap.av.explodedPosition);
1627       }
1628
1629       ap.av.gatherViewsHere = false;
1630
1631       addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1632               AlignFrame.DEFAULT_HEIGHT);
1633     }
1634
1635     af.alignPanels.clear();
1636     af.closeMenuItem_actionPerformed(true);
1637
1638   }
1639
1640   public void gatherViews(AlignFrame source)
1641   {
1642     source.viewport.gatherViewsHere = true;
1643     source.viewport.explodedPosition = source.getBounds();
1644     JInternalFrame[] frames = desktop.getAllFrames();
1645     String viewId = source.viewport.getSequenceSetId();
1646
1647     for (int t = 0; t < frames.length; t++)
1648     {
1649       if (frames[t] instanceof AlignFrame && frames[t] != source)
1650       {
1651         AlignFrame af = (AlignFrame) frames[t];
1652         boolean gatherThis = false;
1653         for (int a = 0; a < af.alignPanels.size(); a++)
1654         {
1655           AlignmentPanel ap = (AlignmentPanel) af.alignPanels.elementAt(a);
1656           if (viewId.equals(ap.av.getSequenceSetId()))
1657           {
1658             gatherThis = true;
1659             ap.av.gatherViewsHere = false;
1660             ap.av.explodedPosition = af.getBounds();
1661             source.addAlignmentPanel(ap, false);
1662           }
1663         }
1664
1665         if (gatherThis)
1666         {
1667           af.alignPanels.clear();
1668           af.closeMenuItem_actionPerformed(true);
1669         }
1670       }
1671     }
1672
1673   }
1674
1675   jalview.gui.VamsasApplication v_client = null;
1676
1677   public void vamsasImport_actionPerformed(ActionEvent e)
1678   {
1679     if (v_client == null)
1680     {
1681       // Load and try to start a session.
1682       JalviewFileChooser chooser = new JalviewFileChooser(
1683               jalview.bin.Cache.getProperty("LAST_DIRECTORY"));
1684
1685       chooser.setFileView(new JalviewFileView());
1686       chooser.setDialogTitle("Open a saved VAMSAS session");
1687       chooser.setToolTipText("select a vamsas session to be opened as a new vamsas session.");
1688
1689       int value = chooser.showOpenDialog(this);
1690
1691       if (value == JalviewFileChooser.APPROVE_OPTION)
1692       {
1693         String fle = chooser.getSelectedFile().toString();
1694         if (!vamsasImport(chooser.getSelectedFile()))
1695         {
1696           JOptionPane.showInternalMessageDialog(Desktop.desktop,
1697                   "Couldn't import '" + fle + "' as a new vamsas session.",
1698                   "Vamsas Document Import Failed",
1699                   JOptionPane.ERROR_MESSAGE);
1700         }
1701       }
1702     }
1703     else
1704     {
1705       jalview.bin.Cache.log
1706               .error("Implementation error - load session from a running session is not supported.");
1707     }
1708   }
1709
1710   /**
1711    * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
1712    * 
1713    * @param file
1714    * @return true if import was a success and a session was started.
1715    */
1716   public boolean vamsasImport(URL url)
1717   {
1718     // TODO: create progress bar
1719     if (v_client != null)
1720     {
1721
1722       jalview.bin.Cache.log
1723               .error("Implementation error - load session from a running session is not supported.");
1724       return false;
1725     }
1726
1727     try
1728     {
1729       // copy the URL content to a temporary local file
1730       // TODO: be a bit cleverer here with nio (?!)
1731       File file = File.createTempFile("vdocfromurl", ".vdj");
1732       FileOutputStream fos = new FileOutputStream(file);
1733       BufferedInputStream bis = new BufferedInputStream(url.openStream());
1734       byte[] buffer = new byte[2048];
1735       int ln;
1736       while ((ln = bis.read(buffer)) > -1)
1737       {
1738         fos.write(buffer, 0, ln);
1739       }
1740       bis.close();
1741       fos.close();
1742       v_client = new jalview.gui.VamsasApplication(this, file,
1743               url.toExternalForm());
1744     } catch (Exception ex)
1745     {
1746       jalview.bin.Cache.log.error(
1747               "Failed to create new vamsas session from contents of URL "
1748                       + url, ex);
1749       return false;
1750     }
1751     setupVamsasConnectedGui();
1752     v_client.initial_update(); // TODO: thread ?
1753     return v_client.inSession();
1754   }
1755
1756   /**
1757    * import file into a new vamsas session (uses jalview.gui.VamsasApplication)
1758    * 
1759    * @param file
1760    * @return true if import was a success and a session was started.
1761    */
1762   public boolean vamsasImport(File file)
1763   {
1764     if (v_client != null)
1765     {
1766
1767       jalview.bin.Cache.log
1768               .error("Implementation error - load session from a running session is not supported.");
1769       return false;
1770     }
1771
1772     setProgressBar("Importing VAMSAS session from " + file.getName(),
1773             file.hashCode());
1774     try
1775     {
1776       v_client = new jalview.gui.VamsasApplication(this, file, null);
1777     } catch (Exception ex)
1778     {
1779       setProgressBar("Importing VAMSAS session from " + file.getName(),
1780               file.hashCode());
1781       jalview.bin.Cache.log.error(
1782               "New vamsas session from existing session file failed:", ex);
1783       return false;
1784     }
1785     setupVamsasConnectedGui();
1786     v_client.initial_update(); // TODO: thread ?
1787     setProgressBar("Importing VAMSAS session from " + file.getName(),
1788             file.hashCode());
1789     return v_client.inSession();
1790   }
1791
1792   public boolean joinVamsasSession(String mysesid)
1793   {
1794     if (v_client != null)
1795     {
1796       throw new Error(
1797               "Trying to join a vamsas session when another is already connected.");
1798     }
1799     if (mysesid == null)
1800     {
1801       throw new Error("Invalid vamsas session id.");
1802     }
1803     v_client = new VamsasApplication(this, mysesid);
1804     setupVamsasConnectedGui();
1805     v_client.initial_update();
1806     return (v_client.inSession());
1807   }
1808
1809   public void vamsasStart_actionPerformed(ActionEvent e)
1810   {
1811     if (v_client == null)
1812     {
1813       // Start a session.
1814       // we just start a default session for moment.
1815       /*
1816        * JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
1817        * getProperty("LAST_DIRECTORY"));
1818        * 
1819        * chooser.setFileView(new JalviewFileView());
1820        * chooser.setDialogTitle("Load Vamsas file");
1821        * chooser.setToolTipText("Import");
1822        * 
1823        * int value = chooser.showOpenDialog(this);
1824        * 
1825        * if (value == JalviewFileChooser.APPROVE_OPTION) { v_client = new
1826        * jalview.gui.VamsasApplication(this, chooser.getSelectedFile());
1827        */
1828       v_client = new VamsasApplication(this);
1829       setupVamsasConnectedGui();
1830       v_client.initial_update(); // TODO: thread ?
1831     }
1832     else
1833     {
1834       // store current data in session.
1835       v_client.push_update(); // TODO: thread
1836     }
1837   }
1838
1839   protected void setupVamsasConnectedGui()
1840   {
1841     vamsasStart.setText("Session Update");
1842     vamsasSave.setVisible(true);
1843     vamsasStop.setVisible(true);
1844     vamsasImport.setVisible(false); // Document import to existing session is
1845     // not possible for vamsas-client-1.0.
1846   }
1847
1848   protected void setupVamsasDisconnectedGui()
1849   {
1850     vamsasSave.setVisible(false);
1851     vamsasStop.setVisible(false);
1852     vamsasImport.setVisible(true);
1853     vamsasStart.setText("New Vamsas Session");
1854   }
1855
1856   public void vamsasStop_actionPerformed(ActionEvent e)
1857   {
1858     if (v_client != null)
1859     {
1860       v_client.end_session();
1861       v_client = null;
1862       setupVamsasDisconnectedGui();
1863     }
1864   }
1865
1866   protected void buildVamsasStMenu()
1867   {
1868     if (v_client == null)
1869     {
1870       String[] sess = null;
1871       try
1872       {
1873         sess = VamsasApplication.getSessionList();
1874       } catch (Exception e)
1875       {
1876         jalview.bin.Cache.log.warn(
1877                 "Problem getting current sessions list.", e);
1878         sess = null;
1879       }
1880       if (sess != null)
1881       {
1882         jalview.bin.Cache.log.debug("Got current sessions list: "
1883                 + sess.length + " entries.");
1884         VamsasStMenu.removeAll();
1885         for (int i = 0; i < sess.length; i++)
1886         {
1887           JMenuItem sessit = new JMenuItem();
1888           sessit.setText(sess[i]);
1889           sessit.setToolTipText("Connect to session " + sess[i]);
1890           final Desktop dsktp = this;
1891           final String mysesid = sess[i];
1892           sessit.addActionListener(new ActionListener()
1893           {
1894
1895             public void actionPerformed(ActionEvent e)
1896             {
1897               if (dsktp.v_client == null)
1898               {
1899                 Thread rthr = new Thread(new Runnable()
1900                 {
1901
1902                   public void run()
1903                   {
1904                     dsktp.v_client = new VamsasApplication(dsktp, mysesid);
1905                     dsktp.setupVamsasConnectedGui();
1906                     dsktp.v_client.initial_update();
1907                   }
1908
1909                 });
1910                 rthr.start();
1911               }
1912             };
1913           });
1914           VamsasStMenu.add(sessit);
1915         }
1916         // don't show an empty menu.
1917         VamsasStMenu.setVisible(sess.length > 0);
1918
1919       }
1920       else
1921       {
1922         jalview.bin.Cache.log.debug("No current vamsas sessions.");
1923         VamsasStMenu.removeAll();
1924         VamsasStMenu.setVisible(false);
1925       }
1926     }
1927     else
1928     {
1929       // Not interested in the content. Just hide ourselves.
1930       VamsasStMenu.setVisible(false);
1931     }
1932   }
1933
1934   public void vamsasSave_actionPerformed(ActionEvent e)
1935   {
1936     if (v_client != null)
1937     {
1938       JalviewFileChooser chooser = new JalviewFileChooser(
1939               jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
1940               { "vdj" }, // TODO: VAMSAS DOCUMENT EXTENSION is VDJ
1941               new String[]
1942               { "Vamsas Document" }, "Vamsas Document");
1943
1944       chooser.setFileView(new JalviewFileView());
1945       chooser.setDialogTitle("Save Vamsas Document Archive");
1946
1947       int value = chooser.showSaveDialog(this);
1948
1949       if (value == JalviewFileChooser.APPROVE_OPTION)
1950       {
1951         java.io.File choice = chooser.getSelectedFile();
1952         JPanel progpanel = addProgressPanel("Saving VAMSAS Document to "
1953                 + choice.getName());
1954         jalview.bin.Cache.setProperty("LAST_DIRECTORY", choice.getParent());
1955         String warnmsg = null;
1956         String warnttl = null;
1957         try
1958         {
1959           v_client.vclient.storeDocument(choice);
1960         } catch (Error ex)
1961         {
1962           warnttl = "Serious Problem saving Vamsas Document";
1963           warnmsg = ex.toString();
1964           jalview.bin.Cache.log.error("Error Whilst saving document to "
1965                   + choice, ex);
1966
1967         } catch (Exception ex)
1968         {
1969           warnttl = "Problem saving Vamsas Document.";
1970           warnmsg = ex.toString();
1971           jalview.bin.Cache.log.warn("Exception Whilst saving document to "
1972                   + choice, ex);
1973
1974         }
1975         removeProgressPanel(progpanel);
1976         if (warnmsg != null)
1977         {
1978           JOptionPane.showInternalMessageDialog(Desktop.desktop,
1979
1980           warnmsg, warnttl, JOptionPane.ERROR_MESSAGE);
1981         }
1982       }
1983     }
1984   }
1985
1986   JPanel vamUpdate = null;
1987
1988   /**
1989    * hide vamsas user gui bits when a vamsas document event is being handled.
1990    * 
1991    * @param b
1992    *          true to hide gui, false to reveal gui
1993    */
1994   public void setVamsasUpdate(boolean b)
1995   {
1996     jalview.bin.Cache.log.debug("Setting gui for Vamsas update "
1997             + (b ? "in progress" : "finished"));
1998
1999     if (vamUpdate != null)
2000     {
2001       this.removeProgressPanel(vamUpdate);
2002     }
2003     if (b)
2004     {
2005       vamUpdate = this.addProgressPanel("Updating vamsas session");
2006     }
2007     vamsasStart.setVisible(!b);
2008     vamsasStop.setVisible(!b);
2009     vamsasSave.setVisible(!b);
2010   }
2011
2012   public JInternalFrame[] getAllFrames()
2013   {
2014     return desktop.getAllFrames();
2015   }
2016
2017   /**
2018    * Checks the given url to see if it gives a response indicating that the user
2019    * should be informed of a new questionnaire.
2020    * 
2021    * @param url
2022    */
2023   public void checkForQuestionnaire(String url)
2024   {
2025     UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2026     // javax.swing.SwingUtilities.invokeLater(jvq);
2027     new Thread(jvq).start();
2028   }
2029
2030   /**
2031    * Proxy class for JDesktopPane which optionally displays the current memory
2032    * usage and highlights the desktop area with a red bar if free memory runs
2033    * low.
2034    * 
2035    * @author AMW
2036    */
2037   public class MyDesktopPane extends JDesktopPane implements Runnable
2038   {
2039
2040     boolean showMemoryUsage = false;
2041
2042     Runtime runtime;
2043
2044     java.text.NumberFormat df;
2045
2046     float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2047             percentUsage;
2048
2049     public MyDesktopPane(boolean showMemoryUsage)
2050     {
2051       showMemoryUsage(showMemoryUsage);
2052     }
2053
2054     public void showMemoryUsage(boolean showMemoryUsage)
2055     {
2056       this.showMemoryUsage = showMemoryUsage;
2057       if (showMemoryUsage)
2058       {
2059         Thread worker = new Thread(this);
2060         worker.start();
2061       }
2062     }
2063
2064     public boolean isShowMemoryUsage()
2065     {
2066       return showMemoryUsage;
2067     }
2068
2069     public void run()
2070     {
2071       df = java.text.NumberFormat.getNumberInstance();
2072       df.setMaximumFractionDigits(2);
2073       runtime = Runtime.getRuntime();
2074
2075       while (showMemoryUsage)
2076       {
2077         try
2078         {
2079           maxMemory = runtime.maxMemory() / 1048576f;
2080           allocatedMemory = runtime.totalMemory() / 1048576f;
2081           freeMemory = runtime.freeMemory() / 1048576f;
2082           totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2083
2084           percentUsage = (totalFreeMemory / maxMemory) * 100;
2085
2086           // if (percentUsage < 20)
2087           {
2088             // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2089             // Color.red);
2090             // instance.set.setBorder(border1);
2091           }
2092           repaint();
2093           // sleep after showing usage
2094           Thread.sleep(3000);
2095         } catch (Exception ex)
2096         {
2097           ex.printStackTrace();
2098         }
2099       }
2100     }
2101
2102     public void paintComponent(Graphics g)
2103     {
2104       if (showMemoryUsage && g != null && df != null)
2105       {
2106         if (percentUsage < 20)
2107           g.setColor(Color.red);
2108         FontMetrics fm = g.getFontMetrics();
2109         if (fm != null)
2110         {
2111           g.drawString(
2112                   "Total Free Memory: " + df.format(totalFreeMemory)
2113                           + "MB; Max Memory: " + df.format(maxMemory)
2114                           + "MB; " + df.format(percentUsage) + "%", 10,
2115                   getHeight() - fm.getHeight());
2116         }
2117       }
2118     }
2119   }
2120
2121   /**
2122    * fixes stacking order after a modal dialog to ensure windows that should be on top actually are
2123    */
2124   public void relayerWindows()
2125   {
2126     
2127   }
2128
2129   protected JMenuItem groovyShell;
2130
2131   public void doGroovyCheck()
2132   {
2133     if (jalview.bin.Cache.groovyJarsPresent())
2134     {
2135       groovyShell = new JMenuItem();
2136       groovyShell.setText("Groovy Console...");
2137       groovyShell.addActionListener(new ActionListener()
2138       {
2139         public void actionPerformed(ActionEvent e)
2140         {
2141           groovyShell_actionPerformed(e);
2142         }
2143       });
2144       toolsMenu.add(groovyShell);
2145       groovyShell.setVisible(true);
2146     }
2147   }
2148
2149   /**
2150    * Accessor method to quickly get all the AlignmentFrames loaded.
2151    */
2152   public static AlignFrame[] getAlignframes()
2153   {
2154     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2155
2156     if (frames == null)
2157     {
2158       return null;
2159     }
2160     Vector avp = new Vector();
2161     try
2162     {
2163       // REVERSE ORDER
2164       for (int i = frames.length - 1; i > -1; i--)
2165       {
2166         if (frames[i] instanceof AlignFrame)
2167         {
2168           AlignFrame af = (AlignFrame) frames[i];
2169           avp.addElement(af);
2170         }
2171       }
2172     } catch (Exception ex)
2173     {
2174       ex.printStackTrace();
2175     }
2176     if (avp.size() == 0)
2177     {
2178       return null;
2179     }
2180     AlignFrame afs[] = new AlignFrame[avp.size()];
2181     for (int i = 0, j = avp.size(); i < j; i++)
2182     {
2183       afs[i] = (AlignFrame) avp.elementAt(i);
2184     }
2185     avp.clear();
2186     return afs;
2187   }
2188   public AppJmol[] getJmols()
2189   {
2190     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2191
2192     if (frames == null)
2193     {
2194       return null;
2195     }
2196     Vector avp = new Vector();
2197     try
2198     {
2199       // REVERSE ORDER
2200       for (int i = frames.length - 1; i > -1; i--)
2201       {
2202         if (frames[i] instanceof AppJmol)
2203         {
2204           AppJmol af = (AppJmol) frames[i];
2205           avp.addElement(af);
2206         }
2207       }
2208     } catch (Exception ex)
2209     {
2210       ex.printStackTrace();
2211     }
2212     if (avp.size() == 0)
2213     {
2214       return null;
2215     }
2216     AppJmol afs[] = new AppJmol[avp.size()];
2217     for (int i = 0, j = avp.size(); i < j; i++)
2218     {
2219       afs[i] = (AppJmol) avp.elementAt(i);
2220     }
2221     avp.clear();
2222     return afs;
2223   }
2224
2225   /**
2226    * Add Groovy Support to Jalview
2227    */
2228   public void groovyShell_actionPerformed(ActionEvent e)
2229   {
2230     // use reflection to avoid creating compilation dependency.
2231     if (!jalview.bin.Cache.groovyJarsPresent())
2232     {
2233       throw new Error(
2234               "Implementation Error. Cannot create groovyShell without Groovy on the classpath!");
2235     }
2236     try
2237     {
2238       Class gcClass = Desktop.class.getClassLoader().loadClass(
2239               "groovy.ui.Console");
2240       Constructor gccons = gcClass.getConstructor(null);
2241       java.lang.reflect.Method setvar = gcClass.getMethod("setVariable",
2242               new Class[]
2243               { String.class, Object.class });
2244       java.lang.reflect.Method run = gcClass.getMethod("run", null);
2245       Object gc = gccons.newInstance(null);
2246       setvar.invoke(gc, new Object[]
2247       { "Jalview", this });
2248       run.invoke(gc, null);
2249     } catch (Exception ex)
2250     {
2251       jalview.bin.Cache.log.error("Groovy Shell Creation failed.", ex);
2252       JOptionPane
2253               .showInternalMessageDialog(
2254                       Desktop.desktop,
2255
2256                       "Couldn't create the groovy Shell. Check the error log for the details of what went wrong.",
2257                       "Jalview Groovy Support Failed",
2258                       JOptionPane.ERROR_MESSAGE);
2259     }
2260   }
2261
2262   /**
2263    * Progress bars managed by the IProgressIndicator method.
2264    */
2265   private Hashtable<Long,JPanel> progressBars;
2266   private Hashtable<Long,IProgressIndicatorHandler> progressBarHandlers;
2267
2268   /*
2269    * (non-Javadoc)
2270    * 
2271    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2272    */
2273   public void setProgressBar(String message, long id)
2274   {
2275     if (progressBars == null)
2276     {
2277       progressBars = new Hashtable<Long,JPanel>();
2278       progressBarHandlers = new Hashtable<Long,IProgressIndicatorHandler>();
2279     }
2280
2281     if (progressBars.get(new Long(id)) != null)
2282     {
2283       JPanel progressPanel = progressBars
2284               .remove(new Long(id));
2285       if (progressBarHandlers.contains(new Long(id)))
2286       {
2287         progressBarHandlers.remove(new Long(id));
2288       }
2289       removeProgressPanel(progressPanel);
2290     }
2291     else
2292     {
2293       progressBars.put(new Long(id), addProgressPanel(message));
2294     }
2295   }
2296
2297   /*
2298    * (non-Javadoc)
2299    * 
2300    * @see jalview.gui.IProgressIndicator#registerHandler(long,
2301    * jalview.gui.IProgressIndicatorHandler)
2302    */
2303   public void registerHandler(final long id,
2304           final IProgressIndicatorHandler handler)
2305   {
2306     if (progressBarHandlers == null || !progressBars.contains(new Long(id)))
2307     {
2308       throw new Error(
2309               "call setProgressBar before registering the progress bar's handler.");
2310     }
2311     progressBarHandlers.put(new Long(id), handler);
2312     final JPanel progressPanel = (JPanel) progressBars.get(new Long(id));
2313     if (handler.canCancel())
2314     {
2315       JButton cancel = new JButton("Cancel");
2316       final IProgressIndicator us = this;
2317       cancel.addActionListener(new ActionListener()
2318       {
2319
2320         public void actionPerformed(ActionEvent e)
2321         {
2322           handler.cancelActivity(id);
2323           us.setProgressBar(
2324                   "Cancelled "
2325                           + ((JLabel) progressPanel.getComponent(0))
2326                                   .getText(), id);
2327         }
2328       });
2329       progressPanel.add(cancel, BorderLayout.EAST);
2330     }
2331   }
2332
2333   /**
2334    * 
2335    * @return true if any progress bars are still active
2336    */
2337   @Override
2338   public boolean operationInProgress()
2339   {
2340     if (progressBars != null && progressBars.size() > 0)
2341     {
2342       return true;
2343     }
2344     return false;
2345   }
2346
2347   /**
2348    * This will return the first AlignFrame viewing AlignViewport av. It will
2349    * break if there are more than one AlignFrames viewing a particular av. This
2350    * 
2351    * @param av
2352    * @return alignFrame for av
2353    */
2354   public static AlignFrame getAlignFrameFor(AlignViewport av)
2355   {
2356     if (desktop != null)
2357     {
2358       AlignmentPanel[] aps = getAlignmentPanels(av.getSequenceSetId());
2359       for (int panel = 0; aps != null && panel < aps.length; panel++)
2360       {
2361         if (aps[panel] != null && aps[panel].av == av)
2362         {
2363           return aps[panel].alignFrame;
2364         }
2365       }
2366     }
2367     return null;
2368   }
2369
2370   public VamsasApplication getVamsasApplication()
2371   {
2372     return v_client;
2373
2374   }
2375
2376   /**
2377    * flag set if jalview GUI is being operated programmatically
2378    */
2379   private boolean inBatchMode = false;
2380
2381   /**
2382    * check if jalview GUI is being operated programmatically
2383    * 
2384    * @return inBatchMode
2385    */
2386   public boolean isInBatchMode()
2387   {
2388     return inBatchMode;
2389   }
2390
2391   /**
2392    * set flag if jalview GUI is being operated programmatically
2393    * 
2394    * @param inBatchMode
2395    */
2396   public void setInBatchMode(boolean inBatchMode)
2397   {
2398     this.inBatchMode = inBatchMode;
2399   }
2400
2401   public void startServiceDiscovery()
2402   {
2403     startServiceDiscovery(false);
2404   }
2405
2406   public void startServiceDiscovery(boolean blocking)
2407   {
2408     boolean alive = true;
2409     Thread t0 = null, t1 = null, t2 = null;
2410
2411     // todo: changesupport handlers need to be transferred
2412     if (discoverer == null)
2413     {
2414       discoverer = new jalview.ws.jws1.Discoverer();
2415       // register PCS handler for desktop.
2416       discoverer.addPropertyChangeListener(changeSupport);
2417     }
2418     // JAL-940 - disabled JWS1 service configuration - always start discoverer until we phase out completely
2419     if (true)
2420     {
2421       (t0 = new Thread(discoverer)).start();
2422     }
2423
2424     try
2425     {
2426       if (Cache.getDefault("SHOW_ENFIN_SERVICES", true))
2427       {
2428         // EnfinEnvision web service menu entries are rebuild every time the
2429         // menu is shown, so no changeSupport events are needed.
2430         jalview.ws.EnfinEnvision2OneWay.getInstance();
2431         (t1 = new Thread(jalview.ws.EnfinEnvision2OneWay.getInstance()))
2432                 .start();
2433       }
2434     } catch (Exception e)
2435     {
2436       Cache.log
2437               .info("Exception when trying to launch Envision2 workflow discovery.",
2438                       e);
2439       Cache.log.info(e.getStackTrace());
2440     }
2441     if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2442     {
2443       if (jalview.ws.jws2.Jws2Discoverer.getDiscoverer().isRunning())
2444       {
2445         jalview.ws.jws2.Jws2Discoverer.getDiscoverer().setAborted(true);
2446       }
2447       t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer().startDiscoverer(
2448               changeSupport);
2449       
2450     }
2451     Thread t3=null;
2452     {
2453       // TODO: do rest service discovery
2454     }
2455     if (blocking)
2456     {
2457       while (alive)
2458       {
2459         try
2460         {
2461           Thread.sleep(15);
2462         } catch (Exception e)
2463         {
2464         }
2465         alive = (t1 != null && t1.isAlive())
2466                 || (t2 != null && t2.isAlive())
2467                 || (t3 != null && t3.isAlive())
2468                 || (t0 != null && t0.isAlive());
2469       }
2470     }
2471   }
2472
2473   /**
2474    * called to check if the service discovery process completed successfully.
2475    * 
2476    * @param evt
2477    */
2478   protected void JalviewServicesChanged(PropertyChangeEvent evt)
2479   {
2480     if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2481     {
2482       final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2483               .getErrorMessages();
2484       if (ermsg != null)
2485       {
2486         if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2487         {
2488         if (serviceChangedDialog == null)
2489         {
2490           // only run if we aren't already displaying one of these.
2491           javax.swing.SwingUtilities
2492                   .invokeLater(serviceChangedDialog = new Runnable()
2493                   {
2494                     public void run()
2495                     {
2496
2497                       JOptionPane
2498                               .showInternalMessageDialog(
2499                                       Desktop.desktop,
2500                                       ermsg
2501                                                 + "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",
2502                                       "Preferences Problem",
2503                                       JOptionPane.WARNING_MESSAGE);
2504                       serviceChangedDialog = null;
2505
2506                     }
2507                   });
2508         }
2509       }
2510         else
2511         {
2512           Cache.log
2513                   .error("Errors reported by JABA discovery service. Check web services preferences.\n"
2514                           + ermsg);
2515         }
2516       }
2517     }
2518   }
2519
2520   private Runnable serviceChangedDialog = null;
2521
2522   /**
2523    * start a thread to open a URL in the configured browser. Pops up a warning
2524    * dialog to the user if there is an exception when calling out to the browser
2525    * to open the URL.
2526    * 
2527    * @param url
2528    */
2529   public static void showUrl(final String url)
2530   {
2531     showUrl(url, Desktop.instance);
2532   }
2533   /**
2534    * Like showUrl but allows progress handler to be specified
2535    * @param url
2536    * @param progress (null) or object implementing IProgressIndicator
2537    */
2538   public static void showUrl(final String url, final IProgressIndicator progress)
2539   {
2540     new Thread(new Runnable()
2541     {
2542       public void run()
2543       {
2544         try
2545         {
2546           if (progress!=null) {
2547             progress.setProgressBar("Opening "+url, this.hashCode());
2548           }
2549           jalview.util.BrowserLauncher.openURL(url);
2550         } catch (Exception ex)
2551         {
2552           JOptionPane
2553                   .showInternalMessageDialog(
2554                           Desktop.desktop,
2555                           "Unixers: Couldn't find default web browser."
2556                                   + "\nAdd the full path to your browser in Preferences.",
2557                           "Web browser not found",
2558                           JOptionPane.WARNING_MESSAGE);
2559
2560           ex.printStackTrace();
2561         }
2562         if (progress!=null) {
2563           progress.setProgressBar(null, this.hashCode());
2564         }
2565       }
2566     }).start();
2567   }
2568
2569   public static WsParamSetManager wsparamManager = null;
2570
2571   public static ParamManager getUserParameterStore()
2572   {
2573     if (wsparamManager == null)
2574     {
2575       wsparamManager = new WsParamSetManager();
2576     }
2577     return wsparamManager;
2578   }
2579
2580   /**
2581    * static hyperlink handler proxy method for use by Jalview's internal windows 
2582    * @param e
2583    */
2584   public static void hyperlinkUpdate(HyperlinkEvent e)
2585   {
2586     if (e.getEventType() == EventType.ACTIVATED)
2587     {
2588       String url=null;
2589       try
2590       {
2591         url = e.getURL().toString();
2592         Desktop.showUrl(url);
2593       } catch (Exception x)
2594       {
2595         if (url!=null) { 
2596           if (Cache.log!=null) { 
2597             Cache.log.error("Couldn't handle string "+url+" as a URL.");
2598           } else {
2599             System.err.println("Couldn't handle string "+url+" as a URL.");
2600           }
2601         }
2602         // ignore any exceptions due to dud links.
2603       }
2604
2605     }    
2606   }
2607
2608
2609 }