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