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