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