Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.gui

File Desktop.java

 

Coverage histogram

../../img/srcFileCovDistChart4.png
44% of files have more coverage

Code metrics

372
903
142
3
3,367
2,387
395
0.44
6.36
47.33
2.78

Classes

Class Line # Actions
Desktop 133 852 363
0.3779113637.8%
Desktop.MyDesktopManager 230 25 19
0.220%
Desktop.MyDesktopPane 2196 26 13
0.2926829229.3%
 

Contributing tests

This file is covered by 143 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ 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 java.awt.BorderLayout;
24    import java.awt.Color;
25    import java.awt.Dimension;
26    import java.awt.FontMetrics;
27    import java.awt.Graphics;
28    import java.awt.GridLayout;
29    import java.awt.Point;
30    import java.awt.Rectangle;
31    import java.awt.Toolkit;
32    import java.awt.Window;
33    import java.awt.datatransfer.Clipboard;
34    import java.awt.datatransfer.ClipboardOwner;
35    import java.awt.datatransfer.DataFlavor;
36    import java.awt.datatransfer.Transferable;
37    import java.awt.dnd.DnDConstants;
38    import java.awt.dnd.DropTargetDragEvent;
39    import java.awt.dnd.DropTargetDropEvent;
40    import java.awt.dnd.DropTargetEvent;
41    import java.awt.dnd.DropTargetListener;
42    import java.awt.event.ActionEvent;
43    import java.awt.event.ActionListener;
44    import java.awt.event.InputEvent;
45    import java.awt.event.KeyEvent;
46    import java.awt.event.MouseAdapter;
47    import java.awt.event.MouseEvent;
48    import java.awt.event.WindowAdapter;
49    import java.awt.event.WindowEvent;
50    import java.beans.PropertyChangeEvent;
51    import java.beans.PropertyChangeListener;
52    import java.io.File;
53    import java.io.FileWriter;
54    import java.io.IOException;
55    import java.net.URL;
56    import java.util.ArrayList;
57    import java.util.HashMap;
58    import java.util.Hashtable;
59    import java.util.List;
60    import java.util.ListIterator;
61    import java.util.Vector;
62    import java.util.concurrent.ExecutorService;
63    import java.util.concurrent.Executors;
64    import java.util.concurrent.Semaphore;
65   
66    import javax.swing.AbstractAction;
67    import javax.swing.Action;
68    import javax.swing.ActionMap;
69    import javax.swing.Box;
70    import javax.swing.BoxLayout;
71    import javax.swing.DefaultDesktopManager;
72    import javax.swing.DesktopManager;
73    import javax.swing.InputMap;
74    import javax.swing.JButton;
75    import javax.swing.JCheckBox;
76    import javax.swing.JComboBox;
77    import javax.swing.JComponent;
78    import javax.swing.JDesktopPane;
79    import javax.swing.JInternalFrame;
80    import javax.swing.JLabel;
81    import javax.swing.JMenuItem;
82    import javax.swing.JPanel;
83    import javax.swing.JPopupMenu;
84    import javax.swing.JProgressBar;
85    import javax.swing.JTextField;
86    import javax.swing.KeyStroke;
87    import javax.swing.SwingUtilities;
88    import javax.swing.event.HyperlinkEvent;
89    import javax.swing.event.HyperlinkEvent.EventType;
90    import javax.swing.event.InternalFrameAdapter;
91    import javax.swing.event.InternalFrameEvent;
92   
93    import org.stackoverflowusers.file.WindowsShortcut;
94   
95    import jalview.api.AlignViewportI;
96    import jalview.api.AlignmentViewPanel;
97    import jalview.bin.Cache;
98    import jalview.bin.Jalview;
99    import jalview.gui.ImageExporter.ImageWriterI;
100    import jalview.io.BackupFiles;
101    import jalview.io.DataSourceType;
102    import jalview.io.FileFormat;
103    import jalview.io.FileFormatException;
104    import jalview.io.FileFormatI;
105    import jalview.io.FileFormats;
106    import jalview.io.FileLoader;
107    import jalview.io.FormatAdapter;
108    import jalview.io.IdentifyFile;
109    import jalview.io.JalviewFileChooser;
110    import jalview.io.JalviewFileView;
111    import jalview.jbgui.GSplitFrame;
112    import jalview.jbgui.GStructureViewer;
113    import jalview.project.Jalview2XML;
114    import jalview.structure.StructureSelectionManager;
115    import jalview.urls.IdOrgSettings;
116    import jalview.util.BrowserLauncher;
117    import jalview.util.ImageMaker.TYPE;
118    import jalview.util.MessageManager;
119    import jalview.util.Platform;
120    import jalview.util.ShortcutKeyMaskExWrapper;
121    import jalview.util.UrlConstants;
122    import jalview.viewmodel.AlignmentViewport;
123    import jalview.ws.params.ParamManager;
124    import jalview.ws.utils.UrlDownloadClient;
125   
126    /**
127    * Jalview Desktop
128    *
129    *
130    * @author $author$
131    * @version $Revision: 1.155 $
132    */
 
133    public class Desktop extends jalview.jbgui.GDesktop
134    implements DropTargetListener, ClipboardOwner, IProgressIndicator,
135    jalview.api.StructureSelectionManagerProvider
136    {
137    private static final String CITATION = "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
138    + "<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"
139    + "<br><br>If you use Jalview, please cite:"
140    + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
141    + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
142    + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033";
143   
144    private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)";
145   
146    private static int DEFAULT_MIN_WIDTH = 300;
147   
148    private static int DEFAULT_MIN_HEIGHT = 250;
149   
150    private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
151   
152    private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
153   
154    private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
155   
156    protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
157   
158    public static HashMap<String, FileWriter> savingFiles = new HashMap<String, FileWriter>();
159   
160    private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
161   
162    /**
163    * news reader - null if it was never started.
164    */
165    private BlogReader jvnews = null;
166   
167    private File projectFile;
168   
169    /**
170    * @param listener
171    * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
172    */
 
173  0 toggle public void addJalviewPropertyChangeListener(
174    PropertyChangeListener listener)
175    {
176  0 changeSupport.addJalviewPropertyChangeListener(listener);
177    }
178   
179    /**
180    * @param propertyName
181    * @param listener
182    * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
183    * java.beans.PropertyChangeListener)
184    */
 
185  205 toggle public void addJalviewPropertyChangeListener(String propertyName,
186    PropertyChangeListener listener)
187    {
188  205 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
189    }
190   
191    /**
192    * @param propertyName
193    * @param listener
194    * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
195    * java.beans.PropertyChangeListener)
196    */
 
197  107 toggle public void removeJalviewPropertyChangeListener(String propertyName,
198    PropertyChangeListener listener)
199    {
200  107 changeSupport.removeJalviewPropertyChangeListener(propertyName,
201    listener);
202    }
203   
204    /** Singleton Desktop instance */
205    public static Desktop instance;
206   
207    public static MyDesktopPane desktop;
208   
 
209  0 toggle public static MyDesktopPane getDesktop()
210    {
211    // BH 2018 could use currentThread() here as a reference to a
212    // Hashtable<Thread, MyDesktopPane> in JavaScript
213  0 return desktop;
214    }
215   
216    static int openFrameCount = 0;
217   
218    static final int xOffset = 30;
219   
220    static final int yOffset = 30;
221   
222    public static jalview.ws.jws1.Discoverer discoverer;
223   
224    public static Object[] jalviewClipboard;
225   
226    public static boolean internalCopy = false;
227   
228    static int fileLoadingCount = 0;
229   
 
230    class MyDesktopManager implements DesktopManager
231    {
232   
233    private DesktopManager delegate;
234   
 
235  18 toggle public MyDesktopManager(DesktopManager delegate)
236    {
237  18 this.delegate = delegate;
238    }
239   
 
240  300 toggle @Override
241    public void activateFrame(JInternalFrame f)
242    {
243  300 try
244    {
245  300 delegate.activateFrame(f);
246    } catch (NullPointerException npe)
247    {
248  0 Point p = getMousePosition();
249  0 instance.showPasteMenu(p.x, p.y);
250    }
251    }
252   
 
253  0 toggle @Override
254    public void beginDraggingFrame(JComponent f)
255    {
256  0 delegate.beginDraggingFrame(f);
257    }
258   
 
259  0 toggle @Override
260    public void beginResizingFrame(JComponent f, int direction)
261    {
262  0 delegate.beginResizingFrame(f, direction);
263    }
264   
 
265  156 toggle @Override
266    public void closeFrame(JInternalFrame f)
267    {
268  156 delegate.closeFrame(f);
269    }
270   
 
271  288 toggle @Override
272    public void deactivateFrame(JInternalFrame f)
273    {
274  288 delegate.deactivateFrame(f);
275    }
276   
 
277  0 toggle @Override
278    public void deiconifyFrame(JInternalFrame f)
279    {
280  0 delegate.deiconifyFrame(f);
281    }
282   
 
283  0 toggle @Override
284    public void dragFrame(JComponent f, int newX, int newY)
285    {
286  0 if (newY < 0)
287    {
288  0 newY = 0;
289    }
290  0 delegate.dragFrame(f, newX, newY);
291    }
292   
 
293  0 toggle @Override
294    public void endDraggingFrame(JComponent f)
295    {
296  0 delegate.endDraggingFrame(f);
297  0 desktop.repaint();
298    }
299   
 
300  0 toggle @Override
301    public void endResizingFrame(JComponent f)
302    {
303  0 delegate.endResizingFrame(f);
304  0 desktop.repaint();
305    }
306   
 
307  0 toggle @Override
308    public void iconifyFrame(JInternalFrame f)
309    {
310  0 delegate.iconifyFrame(f);
311    }
312   
 
313  0 toggle @Override
314    public void maximizeFrame(JInternalFrame f)
315    {
316  0 delegate.maximizeFrame(f);
317    }
318   
 
319  0 toggle @Override
320    public void minimizeFrame(JInternalFrame f)
321    {
322  0 delegate.minimizeFrame(f);
323    }
324   
 
325  0 toggle @Override
326    public void openFrame(JInternalFrame f)
327    {
328  0 delegate.openFrame(f);
329    }
330   
 
331  0 toggle @Override
332    public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
333    int newHeight)
334    {
335  0 if (newY < 0)
336    {
337  0 newY = 0;
338    }
339  0 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
340    }
341   
 
342  0 toggle @Override
343    public void setBoundsForFrame(JComponent f, int newX, int newY,
344    int newWidth, int newHeight)
345    {
346  0 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
347    }
348   
349    // All other methods, simply delegate
350   
351    }
352   
353    /**
354    * Creates a new Desktop object.
355    */
 
356  18 toggle public Desktop()
357    {
358  18 super();
359    /**
360    * A note to implementors. It is ESSENTIAL that any activities that might
361    * block are spawned off as threads rather than waited for during this
362    * constructor.
363    */
364  18 instance = this;
365   
366  18 doConfigureStructurePrefs();
367  18 setTitle("Jalview " + Cache.getProperty("VERSION"));
368    /*
369    if (!Platform.isAMac())
370    {
371    // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
372    }
373    else
374    {
375    this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
376    }
377    */
378   
379  18 try
380    {
381  18 APQHandlers.setAPQHandlers(this);
382    } catch (Throwable t)
383    {
384  0 System.out.println("Error setting APQHandlers: " + t.toString());
385    // t.printStackTrace();
386    }
387   
388  18 addWindowListener(new WindowAdapter()
389    {
390   
 
391  0 toggle @Override
392    public void windowClosing(WindowEvent ev)
393    {
394  0 quit();
395    }
396    });
397   
398  18 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE",
399    false);
400   
401  18 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE",
402    false);
403  18 desktop = new MyDesktopPane(selmemusage);
404   
405  18 showMemusage.setSelected(selmemusage);
406  18 desktop.setBackground(Color.white);
407   
408  18 getContentPane().setLayout(new BorderLayout());
409    // alternate config - have scrollbars - see notes in JAL-153
410    // JScrollPane sp = new JScrollPane();
411    // sp.getViewport().setView(desktop);
412    // getContentPane().add(sp, BorderLayout.CENTER);
413   
414    // BH 2018 - just an experiment to try unclipped JInternalFrames.
415  18 if (Platform.isJS())
416    {
417  0 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
418    }
419   
420  18 getContentPane().add(desktop, BorderLayout.CENTER);
421  18 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
422   
423    // This line prevents Windows Look&Feel resizing all new windows to maximum
424    // if previous window was maximised
425  18 desktop.setDesktopManager(new MyDesktopManager(
426  18 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
427  18 : Platform.isAMacAndNotJS()
428    ? new AquaInternalFrameManager(
429    desktop.getDesktopManager())
430    : desktop.getDesktopManager())));
431   
432  18 Rectangle dims = getLastKnownDimensions("");
433  18 if (dims != null)
434    {
435  17 setBounds(dims);
436    }
437    else
438    {
439  1 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
440  1 int xPos = Math.max(5, (screenSize.width - 900) / 2);
441  1 int yPos = Math.max(5, (screenSize.height - 650) / 2);
442  1 setBounds(xPos, yPos, 900, 650);
443    }
444   
445  18 if (!Platform.isJS())
446    /**
447    * Java only
448    *
449    * @j2sIgnore
450    */
451    {
452  18 jconsole = new Console(this, showjconsole);
453  18 jconsole.setHeader(Cache.getVersionDetailsForConsole());
454  18 showConsole(showjconsole);
455   
456  18 showNews.setVisible(false);
457   
458  18 experimentalFeatures.setSelected(showExperimental());
459   
460  18 getIdentifiersOrgData();
461   
462  18 checkURLLinks();
463   
464    // Spawn a thread that shows the splashscreen
465   
466  18 SwingUtilities.invokeLater(new Runnable()
467    {
 
468  18 toggle @Override
469    public void run()
470    {
471  18 new SplashScreen(true);
472    }
473    });
474   
475    // Thread off a new instance of the file chooser - this reduces the time
476    // it
477    // takes to open it later on.
478  18 new Thread(new Runnable()
479    {
 
480  18 toggle @Override
481    public void run()
482    {
483  18 Cache.log.debug("Filechooser init thread started.");
484  18 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
485  18 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
486    fileFormat);
487  18 Cache.log.debug("Filechooser init thread finished.");
488    }
489    }).start();
490    // Add the service change listener
491  18 changeSupport.addJalviewPropertyChangeListener("services",
492    new PropertyChangeListener()
493    {
494   
 
495  299 toggle @Override
496    public void propertyChange(PropertyChangeEvent evt)
497    {
498  299 Cache.log.debug("Firing service changed event for "
499    + evt.getNewValue());
500  299 JalviewServicesChanged(evt);
501    }
502    });
503    }
504   
505  18 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
506   
507  18 this.addWindowListener(new WindowAdapter()
508    {
 
509  0 toggle @Override
510    public void windowClosing(WindowEvent evt)
511    {
512  0 quit();
513    }
514    });
515   
516  18 MouseAdapter ma;
517  18 this.addMouseListener(ma = new MouseAdapter()
518    {
 
519  0 toggle @Override
520    public void mousePressed(MouseEvent evt)
521    {
522  0 if (evt.isPopupTrigger()) // Mac
523    {
524  0 showPasteMenu(evt.getX(), evt.getY());
525    }
526    }
527   
 
528  0 toggle @Override
529    public void mouseReleased(MouseEvent evt)
530    {
531  0 if (evt.isPopupTrigger()) // Windows
532    {
533  0 showPasteMenu(evt.getX(), evt.getY());
534    }
535    }
536    });
537  18 desktop.addMouseListener(ma);
538   
539    }
540   
541    /**
542    * Answers true if user preferences to enable experimental features is True
543    * (on), else false
544    *
545    * @return
546    */
 
547  18 toggle public boolean showExperimental()
548    {
549  18 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
550    Boolean.FALSE.toString());
551  18 return Boolean.valueOf(experimental).booleanValue();
552    }
553   
 
554  18 toggle public void doConfigureStructurePrefs()
555    {
556    // configure services
557  18 StructureSelectionManager ssm = StructureSelectionManager
558    .getStructureSelectionManager(this);
559  18 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
560    {
561  18 ssm.setAddTempFacAnnot(Cache
562    .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
563  18 ssm.setProcessSecondaryStructure(Cache
564    .getDefault(Preferences.STRUCT_FROM_PDB, true));
565  18 ssm.setSecStructServices(
566    Cache.getDefault(Preferences.USE_RNAVIEW, true));
567    }
568    else
569    {
570  0 ssm.setAddTempFacAnnot(false);
571  0 ssm.setProcessSecondaryStructure(false);
572  0 ssm.setSecStructServices(false);
573    }
574    }
575   
 
576  6 toggle public void checkForNews()
577    {
578  6 final Desktop me = this;
579    // Thread off the news reader, in case there are connection problems.
580  6 new Thread(new Runnable()
581    {
 
582  6 toggle @Override
583    public void run()
584    {
585  6 Cache.log.debug("Starting news thread.");
586  6 jvnews = new BlogReader(me);
587  6 showNews.setVisible(true);
588  6 Cache.log.debug("Completed news thread.");
589    }
590    }).start();
591    }
592   
 
593  18 toggle public void getIdentifiersOrgData()
594    {
595    // Thread off the identifiers fetcher
596  18 new Thread(new Runnable()
597    {
 
598  18 toggle @Override
599    public void run()
600    {
601  18 Cache.log.debug("Downloading data from identifiers.org");
602  18 try
603    {
604  18 UrlDownloadClient.download(IdOrgSettings.getUrl(),
605    IdOrgSettings.getDownloadLocation());
606    } catch (IOException e)
607    {
608  0 Cache.log.debug("Exception downloading identifiers.org data"
609    + e.getMessage());
610    }
611    }
612    }).start();
613   
614    }
615   
 
616  0 toggle @Override
617    protected void showNews_actionPerformed(ActionEvent e)
618    {
619  0 showNews(showNews.isSelected());
620    }
621   
 
622  0 toggle void showNews(boolean visible)
623    {
624  0 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
625  0 showNews.setSelected(visible);
626  0 if (visible && !jvnews.isVisible())
627    {
628  0 new Thread(new Runnable()
629    {
 
630  0 toggle @Override
631    public void run()
632    {
633  0 long now = System.currentTimeMillis();
634  0 Desktop.instance.setProgressBar(
635    MessageManager.getString("status.refreshing_news"), now);
636  0 jvnews.refreshNews();
637  0 Desktop.instance.setProgressBar(null, now);
638  0 jvnews.showNews();
639    }
640    }).start();
641    }
642    }
643   
644    /**
645    * recover the last known dimensions for a jalview window
646    *
647    * @param windowName
648    * - empty string is desktop, all other windows have unique prefix
649    * @return null or last known dimensions scaled to current geometry (if last
650    * window geom was known)
651    */
 
652  36 toggle Rectangle getLastKnownDimensions(String windowName)
653    {
654    // TODO: lock aspect ratio for scaling desktop Bug #0058199
655  36 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
656  36 String x = Cache.getProperty(windowName + "SCREEN_X");
657  36 String y = Cache.getProperty(windowName + "SCREEN_Y");
658  36 String width = Cache
659    .getProperty(windowName + "SCREEN_WIDTH");
660  36 String height = Cache
661    .getProperty(windowName + "SCREEN_HEIGHT");
662  36 if ((x != null) && (y != null) && (width != null) && (height != null))
663    {
664  34 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
665    iw = Integer.parseInt(width), ih = Integer.parseInt(height);
666  34 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
667    {
668    // attempt #1 - try to cope with change in screen geometry - this
669    // version doesn't preserve original jv aspect ratio.
670    // take ratio of current screen size vs original screen size.
671  34 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
672    Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
673  34 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
674    Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
675    // rescale the bounds depending upon the current screen geometry.
676  34 ix = (int) (ix * sw);
677  34 iw = (int) (iw * sw);
678  34 iy = (int) (iy * sh);
679  34 ih = (int) (ih * sh);
680  34 while (ix >= screenSize.width)
681    {
682  0 Cache.log.debug(
683    "Window geometry location recall error: shifting horizontal to within screenbounds.");
684  0 ix -= screenSize.width;
685    }
686  34 while (iy >= screenSize.height)
687    {
688  0 Cache.log.debug(
689    "Window geometry location recall error: shifting vertical to within screenbounds.");
690  0 iy -= screenSize.height;
691    }
692  34 Cache.log.debug(
693    "Got last known dimensions for " + windowName + ": x:" + ix
694    + " y:" + iy + " width:" + iw + " height:" + ih);
695    }
696    // return dimensions for new instance
697  34 return new Rectangle(ix, iy, iw, ih);
698    }
699  2 return null;
700    }
701   
 
702  0 toggle void showPasteMenu(int x, int y)
703    {
704  0 JPopupMenu popup = new JPopupMenu();
705  0 JMenuItem item = new JMenuItem(
706    MessageManager.getString("label.paste_new_window"));
707  0 item.addActionListener(new ActionListener()
708    {
 
709  0 toggle @Override
710    public void actionPerformed(ActionEvent evt)
711    {
712  0 paste();
713    }
714    });
715   
716  0 popup.add(item);
717  0 popup.show(this, x, y);
718    }
719   
 
720  0 toggle public void paste()
721    {
722  0 try
723    {
724  0 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
725  0 Transferable contents = c.getContents(this);
726   
727  0 if (contents != null)
728    {
729  0 String file = (String) contents
730    .getTransferData(DataFlavor.stringFlavor);
731   
732  0 FileFormatI format = new IdentifyFile().identify(file,
733    DataSourceType.PASTE);
734   
735  0 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
736   
737    }
738    } catch (Exception ex)
739    {
740  0 System.out.println(
741    "Unable to paste alignment from system clipboard:\n" + ex);
742    }
743    }
744   
745    /**
746    * Adds and opens the given frame to the desktop
747    *
748    * @param frame
749    * Frame to show
750    * @param title
751    * Visible Title
752    * @param w
753    * width
754    * @param h
755    * height
756    */
 
757  169 toggle public static synchronized void addInternalFrame(
758    final JInternalFrame frame, String title, int w, int h)
759    {
760  169 addInternalFrame(frame, title, true, w, h, true, false);
761    }
762   
763    /**
764    * Add an internal frame to the Jalview desktop
765    *
766    * @param frame
767    * Frame to show
768    * @param title
769    * Visible Title
770    * @param makeVisible
771    * When true, display frame immediately, otherwise, caller must call
772    * setVisible themselves.
773    * @param w
774    * width
775    * @param h
776    * height
777    */
 
778  0 toggle public static synchronized void addInternalFrame(
779    final JInternalFrame frame, String title, boolean makeVisible,
780    int w, int h)
781    {
782  0 addInternalFrame(frame, title, makeVisible, w, h, true, false);
783    }
784   
785    /**
786    * Add an internal frame to the Jalview desktop and make it visible
787    *
788    * @param frame
789    * Frame to show
790    * @param title
791    * Visible Title
792    * @param w
793    * width
794    * @param h
795    * height
796    * @param resizable
797    * Allow resize
798    */
 
799  17 toggle public static synchronized void addInternalFrame(
800    final JInternalFrame frame, String title, int w, int h,
801    boolean resizable)
802    {
803  17 addInternalFrame(frame, title, true, w, h, resizable, false);
804    }
805   
806    /**
807    * Add an internal frame to the Jalview desktop
808    *
809    * @param frame
810    * Frame to show
811    * @param title
812    * Visible Title
813    * @param makeVisible
814    * When true, display frame immediately, otherwise, caller must call
815    * setVisible themselves.
816    * @param w
817    * width
818    * @param h
819    * height
820    * @param resizable
821    * Allow resize
822    * @param ignoreMinSize
823    * Do not set the default minimum size for frame
824    */
 
825  202 toggle public static synchronized void addInternalFrame(
826    final JInternalFrame frame, String title, boolean makeVisible,
827    int w, int h, boolean resizable, boolean ignoreMinSize)
828    {
829   
830    // TODO: allow callers to determine X and Y position of frame (eg. via
831    // bounds object).
832    // TODO: consider fixing method to update entries in the window submenu with
833    // the current window title
834   
835  202 frame.setTitle(title);
836  202 if (frame.getWidth() < 1 || frame.getHeight() < 1)
837    {
838  38 frame.setSize(w, h);
839    }
840    // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
841    // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
842    // IF JALVIEW IS RUNNING HEADLESS
843    // ///////////////////////////////////////////////
844  202 if (instance == null || (System.getProperty("java.awt.headless") != null
845    && System.getProperty("java.awt.headless").equals("true")))
846    {
847  8 return;
848    }
849   
850  194 openFrameCount++;
851   
852  194 if (!ignoreMinSize)
853    {
854  178 frame.setMinimumSize(
855    new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
856   
857    // Set default dimension for Alignment Frame window.
858    // The Alignment Frame window could be added from a number of places,
859    // hence,
860    // I did this here in order not to miss out on any Alignment frame.
861  178 if (frame instanceof AlignFrame)
862    {
863  138 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
864    ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
865    }
866    }
867   
868  194 frame.setVisible(makeVisible);
869  194 frame.setClosable(true);
870  194 frame.setResizable(resizable);
871  194 frame.setMaximizable(resizable);
872  194 frame.setIconifiable(resizable);
873  194 frame.setOpaque(Platform.isJS());
874   
875  194 if (frame.getX() < 1 && frame.getY() < 1)
876    {
877  105 frame.setLocation(xOffset * openFrameCount,
878    yOffset * ((openFrameCount - 1) % 10) + yOffset);
879    }
880   
881    /*
882    * add an entry for the new frame in the Window menu
883    * (and remove it when the frame is closed)
884    */
885  194 final JMenuItem menuItem = new JMenuItem(title);
886  194 frame.addInternalFrameListener(new InternalFrameAdapter()
887    {
 
888  277 toggle @Override
889    public void internalFrameActivated(InternalFrameEvent evt)
890    {
891  277 JInternalFrame itf = desktop.getSelectedFrame();
892  277 if (itf != null)
893    {
894  277 if (itf instanceof AlignFrame)
895    {
896  197 Jalview.setCurrentAlignFrame((AlignFrame) itf);
897    }
898  277 itf.requestFocus();
899    }
900    }
901   
 
902  144 toggle @Override
903    public void internalFrameClosed(InternalFrameEvent evt)
904    {
905  144 PaintRefresher.RemoveComponent(frame);
906   
907    /*
908    * defensive check to prevent frames being
909    * added half off the window
910    */
911  144 if (openFrameCount > 0)
912    {
913  139 openFrameCount--;
914    }
915   
916    /*
917    * ensure no reference to alignFrame retained by menu item listener
918    */
919  144 if (menuItem.getActionListeners().length > 0)
920    {
921  138 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
922    }
923  144 windowMenu.remove(menuItem);
924    }
925    });
926   
927  194 menuItem.addActionListener(new ActionListener()
928    {
 
929  0 toggle @Override
930    public void actionPerformed(ActionEvent e)
931    {
932  0 try
933    {
934  0 frame.setSelected(true);
935  0 frame.setIcon(false);
936    } catch (java.beans.PropertyVetoException ex)
937    {
938    // System.err.println(ex.toString());
939    }
940    }
941    });
942   
943  194 setKeyBindings(frame);
944   
945  194 desktop.add(frame);
946   
947  194 windowMenu.add(menuItem);
948   
949  194 frame.toFront();
950  194 try
951    {
952  194 frame.setSelected(true);
953  194 frame.requestFocus();
954    } catch (java.beans.PropertyVetoException ve)
955    {
956    } catch (java.lang.ClassCastException cex)
957    {
958  0 Cache.log.warn(
959    "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
960    cex);
961    }
962    }
963   
964    /**
965    * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
966    * window
967    *
968    * @param frame
969    */
 
970  194 toggle private static void setKeyBindings(JInternalFrame frame)
971    {
972  194 @SuppressWarnings("serial")
973    final Action closeAction = new AbstractAction()
974    {
 
975  0 toggle @Override
976    public void actionPerformed(ActionEvent e)
977    {
978  0 frame.dispose();
979    }
980    };
981   
982    /*
983    * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
984    */
985  194 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
986    InputEvent.CTRL_DOWN_MASK);
987  194 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
988    ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
989   
990  194 InputMap inputMap = frame
991    .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
992  194 String ctrlW = ctrlWKey.toString();
993  194 inputMap.put(ctrlWKey, ctrlW);
994  194 inputMap.put(cmdWKey, ctrlW);
995   
996  194 ActionMap actionMap = frame.getActionMap();
997  194 actionMap.put(ctrlW, closeAction);
998    }
999   
 
1000  0 toggle @Override
1001    public void lostOwnership(Clipboard clipboard, Transferable contents)
1002    {
1003  0 if (!internalCopy)
1004    {
1005  0 Desktop.jalviewClipboard = null;
1006    }
1007   
1008  0 internalCopy = false;
1009    }
1010   
 
1011  0 toggle @Override
1012    public void dragEnter(DropTargetDragEvent evt)
1013    {
1014    }
1015   
 
1016  0 toggle @Override
1017    public void dragExit(DropTargetEvent evt)
1018    {
1019    }
1020   
 
1021  0 toggle @Override
1022    public void dragOver(DropTargetDragEvent evt)
1023    {
1024    }
1025   
 
1026  0 toggle @Override
1027    public void dropActionChanged(DropTargetDragEvent evt)
1028    {
1029    }
1030   
1031    /**
1032    * DOCUMENT ME!
1033    *
1034    * @param evt
1035    * DOCUMENT ME!
1036    */
 
1037  0 toggle @Override
1038    public void drop(DropTargetDropEvent evt)
1039    {
1040  0 boolean success = true;
1041    // JAL-1552 - acceptDrop required before getTransferable call for
1042    // Java's Transferable for native dnd
1043  0 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1044  0 Transferable t = evt.getTransferable();
1045  0 List<Object> files = new ArrayList<>();
1046  0 List<DataSourceType> protocols = new ArrayList<>();
1047   
1048  0 try
1049    {
1050  0 Desktop.transferFromDropTarget(files, protocols, evt, t);
1051    } catch (Exception e)
1052    {
1053  0 e.printStackTrace();
1054  0 success = false;
1055    }
1056   
1057  0 if (files != null)
1058    {
1059  0 try
1060    {
1061  0 for (int i = 0; i < files.size(); i++)
1062    {
1063    // BH 2018 File or String
1064  0 Object file = files.get(i);
1065  0 String fileName = file.toString();
1066  0 DataSourceType protocol = (protocols == null)
1067    ? DataSourceType.FILE
1068    : protocols.get(i);
1069  0 FileFormatI format = null;
1070   
1071  0 if (fileName.endsWith(".jar"))
1072    {
1073  0 format = FileFormat.Jalview;
1074   
1075    }
1076    else
1077    {
1078  0 format = new IdentifyFile().identify(file, protocol);
1079    }
1080  0 if (file instanceof File)
1081    {
1082  0 Platform.cacheFileData((File) file);
1083    }
1084  0 new FileLoader().LoadFile(null, file, protocol, format);
1085   
1086    }
1087    } catch (Exception ex)
1088    {
1089  0 success = false;
1090    }
1091    }
1092  0 evt.dropComplete(success); // need this to ensure input focus is properly
1093    // transfered to any new windows created
1094    }
1095   
1096    /**
1097    * DOCUMENT ME!
1098    *
1099    * @param e
1100    * DOCUMENT ME!
1101    */
 
1102  0 toggle @Override
1103    public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1104    {
1105  0 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1106  0 JalviewFileChooser chooser = JalviewFileChooser
1107    .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, BackupFiles.getEnabled());
1108   
1109  0 chooser.setFileView(new JalviewFileView());
1110  0 chooser.setDialogTitle(
1111    MessageManager.getString("label.open_local_file"));
1112  0 chooser.setToolTipText(MessageManager.getString("action.open"));
1113   
1114  0 chooser.setResponseHandler(0, new Runnable()
1115    {
 
1116  0 toggle @Override
1117    public void run()
1118    {
1119  0 File selectedFile = chooser.getSelectedFile();
1120  0 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1121   
1122  0 FileFormatI format = chooser.getSelectedFormat();
1123   
1124    /*
1125    * Call IdentifyFile to verify the file contains what its extension implies.
1126    * Skip this step for dynamically added file formats, because
1127    * IdentifyFile does not know how to recognise them.
1128    */
1129  0 if (FileFormats.getInstance().isIdentifiable(format))
1130    {
1131  0 try
1132    {
1133  0 format = new IdentifyFile().identify(selectedFile,
1134    DataSourceType.FILE);
1135    } catch (FileFormatException e)
1136    {
1137    // format = null; //??
1138    }
1139    }
1140   
1141  0 new FileLoader().LoadFile(viewport, selectedFile,
1142    DataSourceType.FILE, format);
1143    }
1144    });
1145  0 chooser.showOpenDialog(this);
1146    }
1147   
1148    /**
1149    * Shows a dialog for input of a URL at which to retrieve alignment data
1150    *
1151    * @param viewport
1152    */
 
1153  0 toggle @Override
1154    public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1155    {
1156    // This construct allows us to have a wider textfield
1157    // for viewing
1158  0 JLabel label = new JLabel(
1159    MessageManager.getString("label.input_file_url"));
1160   
1161  0 JPanel panel = new JPanel(new GridLayout(2, 1));
1162  0 panel.add(label);
1163   
1164    /*
1165    * the URL to fetch is
1166    * Java: an editable combobox with history
1167    * JS: (pending JAL-3038) a plain text field
1168    */
1169  0 JComponent history;
1170  0 String urlBase = "http://www.";
1171  0 if (Platform.isJS())
1172    {
1173  0 history = new JTextField(urlBase, 35);
1174    }
1175    else
1176    /**
1177    * Java only
1178    *
1179    * @j2sIgnore
1180    */
1181    {
1182  0 JComboBox<String> asCombo = new JComboBox<>();
1183  0 asCombo.setPreferredSize(new Dimension(400, 20));
1184  0 asCombo.setEditable(true);
1185  0 asCombo.addItem(urlBase);
1186  0 String historyItems = Cache.getProperty("RECENT_URL");
1187  0 if (historyItems != null)
1188    {
1189  0 for (String token : historyItems.split("\\t"))
1190    {
1191  0 asCombo.addItem(token);
1192    }
1193    }
1194  0 history = asCombo;
1195    }
1196  0 panel.add(history);
1197   
1198  0 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1199    MessageManager.getString("action.cancel") };
1200  0 Runnable action = new Runnable()
1201    {
 
1202  0 toggle @Override
1203    public void run()
1204    {
1205  0 @SuppressWarnings("unchecked")
1206  0 String url = (history instanceof JTextField
1207    ? ((JTextField) history).getText()
1208    : ((JComboBox<String>) history).getSelectedItem()
1209    .toString());
1210   
1211  0 if (url.toLowerCase().endsWith(".jar"))
1212    {
1213  0 if (viewport != null)
1214    {
1215  0 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1216    FileFormat.Jalview);
1217    }
1218    else
1219    {
1220  0 new FileLoader().LoadFile(url, DataSourceType.URL,
1221    FileFormat.Jalview);
1222    }
1223    }
1224    else
1225    {
1226  0 FileFormatI format = null;
1227  0 try
1228    {
1229  0 format = new IdentifyFile().identify(url, DataSourceType.URL);
1230    } catch (FileFormatException e)
1231    {
1232    // TODO revise error handling, distinguish between
1233    // URL not found and response not valid
1234    }
1235   
1236  0 if (format == null)
1237    {
1238  0 String msg = MessageManager
1239    .formatMessage("label.couldnt_locate", url);
1240  0 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1241    MessageManager.getString("label.url_not_found"),
1242    JvOptionPane.WARNING_MESSAGE);
1243   
1244  0 return;
1245    }
1246   
1247  0 if (viewport != null)
1248    {
1249  0 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1250    format);
1251    }
1252    else
1253    {
1254  0 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1255    }
1256    }
1257    }
1258    };
1259  0 String dialogOption = MessageManager
1260    .getString("label.input_alignment_from_url");
1261  0 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1262    .showInternalDialog(panel, dialogOption,
1263    JvOptionPane.YES_NO_CANCEL_OPTION,
1264    JvOptionPane.PLAIN_MESSAGE, null, options,
1265    MessageManager.getString("action.ok"));
1266    }
1267   
1268    /**
1269    * Opens the CutAndPaste window for the user to paste an alignment in to
1270    *
1271    * @param viewPanel
1272    * - if not null, the pasted alignment is added to the current
1273    * alignment; if null, to a new alignment window
1274    */
 
1275  0 toggle @Override
1276    public void inputTextboxMenuItem_actionPerformed(
1277    AlignmentViewPanel viewPanel)
1278    {
1279  0 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1280  0 cap.setForInput(viewPanel);
1281  0 Desktop.addInternalFrame(cap,
1282    MessageManager.getString("label.cut_paste_alignmen_file"), true,
1283    600, 500);
1284    }
1285   
1286    /*
1287    * Exit the program
1288    */
 
1289  0 toggle @Override
1290    public void quit()
1291    {
1292  0 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1293  0 Cache.setProperty("SCREENGEOMETRY_WIDTH",
1294    screen.width + "");
1295  0 Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1296    screen.height + "");
1297  0 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1298    getWidth(), getHeight()));
1299   
1300  0 if (jconsole != null)
1301    {
1302  0 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1303  0 jconsole.stopConsole();
1304    }
1305  0 if (jvnews != null)
1306    {
1307  0 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1308   
1309    }
1310  0 if (dialogExecutor != null)
1311    {
1312  0 dialogExecutor.shutdownNow();
1313    }
1314  0 closeAll_actionPerformed(null);
1315   
1316  0 if (groovyConsole != null)
1317    {
1318    // suppress a possible repeat prompt to save script
1319  0 groovyConsole.setDirty(false);
1320  0 groovyConsole.exit();
1321    }
1322  0 System.exit(0);
1323    }
1324   
 
1325  0 toggle private void storeLastKnownDimensions(String string, Rectangle jc)
1326    {
1327  0 Cache.log.debug("Storing last known dimensions for "
1328    + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1329    + " height:" + jc.height);
1330   
1331  0 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1332  0 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1333  0 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1334  0 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1335    }
1336   
1337    /**
1338    * DOCUMENT ME!
1339    *
1340    * @param e
1341    * DOCUMENT ME!
1342    */
 
1343  0 toggle @Override
1344    public void aboutMenuItem_actionPerformed(ActionEvent e)
1345    {
1346  0 new Thread(new Runnable()
1347    {
 
1348  0 toggle @Override
1349    public void run()
1350    {
1351  0 new SplashScreen(false);
1352    }
1353    }).start();
1354    }
1355   
1356    /**
1357    * Returns the html text for the About screen, including any available version
1358    * number, build details, author details and citation reference, but without
1359    * the enclosing {@code html} tags
1360    *
1361    * @return
1362    */
 
1363  188 toggle public String getAboutMessage()
1364    {
1365  188 StringBuilder message = new StringBuilder(1024);
1366  188 message.append("<h1><strong>Version: ")
1367    .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1368    .append("<strong>Built: <em>")
1369    .append(Cache.getDefault("BUILD_DATE", "unknown"))
1370    .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1371    .append("</strong>");
1372   
1373  188 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1374  188 if (latestVersion.equals("Checking"))
1375    {
1376    // JBP removed this message for 2.11: May be reinstated in future version
1377    // message.append("<br>...Checking latest version...</br>");
1378    }
1379  12 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1380    {
1381  12 boolean red = false;
1382  12 if (Cache.getProperty("VERSION").toLowerCase()
1383    .indexOf("automated build") == -1)
1384    {
1385  12 red = true;
1386    // Displayed when code version and jnlp version do not match and code
1387    // version is not a development build
1388  12 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1389    }
1390   
1391  12 message.append("<br>!! Version ")
1392    .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1393    .append(" is available for download from ")
1394    .append(Cache.getDefault("www.jalview.org",
1395    "http://www.jalview.org"))
1396    .append(" !!");
1397  12 if (red)
1398    {
1399  12 message.append("</div>");
1400    }
1401    }
1402  188 message.append("<br>Authors: ");
1403  188 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1404  188 message.append(CITATION);
1405   
1406  188 return message.toString();
1407    }
1408   
1409    /**
1410    * Action on requesting Help documentation
1411    */
 
1412  0 toggle @Override
1413    public void documentationMenuItem_actionPerformed()
1414    {
1415  0 try
1416    {
1417  0 if (Platform.isJS())
1418    {
1419  0 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1420    }
1421    else
1422    /**
1423    * Java only
1424    *
1425    * @j2sIgnore
1426    */
1427    {
1428  0 Help.showHelpWindow();
1429    }
1430    } catch (Exception ex)
1431    {
1432  0 System.err.println("Error opening help: " + ex.getMessage());
1433    }
1434    }
1435   
 
1436  36 toggle @Override
1437    public void closeAll_actionPerformed(ActionEvent e)
1438    {
1439    // TODO show a progress bar while closing?
1440  36 JInternalFrame[] frames = desktop.getAllFrames();
1441  114 for (int i = 0; i < frames.length; i++)
1442    {
1443  78 try
1444    {
1445  78 frames[i].setClosed(true);
1446    } catch (java.beans.PropertyVetoException ex)
1447    {
1448    }
1449    }
1450  36 Jalview.setCurrentAlignFrame(null);
1451  36 System.out.println("ALL CLOSED");
1452   
1453    /*
1454    * reset state of singleton objects as appropriate (clear down session state
1455    * when all windows are closed)
1456    */
1457  36 StructureSelectionManager ssm = StructureSelectionManager
1458    .getStructureSelectionManager(this);
1459  36 if (ssm != null)
1460    {
1461  36 ssm.resetAll();
1462    }
1463    }
1464   
 
1465  0 toggle @Override
1466    public void raiseRelated_actionPerformed(ActionEvent e)
1467    {
1468  0 reorderAssociatedWindows(false, false);
1469    }
1470   
 
1471  0 toggle @Override
1472    public void minimizeAssociated_actionPerformed(ActionEvent e)
1473    {
1474  0 reorderAssociatedWindows(true, false);
1475    }
1476   
 
1477  0 toggle void closeAssociatedWindows()
1478    {
1479  0 reorderAssociatedWindows(false, true);
1480    }
1481   
1482    /*
1483    * (non-Javadoc)
1484    *
1485    * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1486    * ActionEvent)
1487    */
 
1488  0 toggle @Override
1489    protected void garbageCollect_actionPerformed(ActionEvent e)
1490    {
1491    // We simply collect the garbage
1492  0 Cache.log.debug("Collecting garbage...");
1493  0 System.gc();
1494  0 Cache.log.debug("Finished garbage collection.");
1495    }
1496   
1497    /*
1498    * (non-Javadoc)
1499    *
1500    * @see
1501    * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1502    * )
1503    */
 
1504  0 toggle @Override
1505    protected void showMemusage_actionPerformed(ActionEvent e)
1506    {
1507  0 desktop.showMemoryUsage(showMemusage.isSelected());
1508    }
1509   
1510    /*
1511    * (non-Javadoc)
1512    *
1513    * @see
1514    * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1515    * )
1516    */
 
1517  0 toggle @Override
1518    protected void showConsole_actionPerformed(ActionEvent e)
1519    {
1520  0 showConsole(showConsole.isSelected());
1521    }
1522   
1523    Console jconsole = null;
1524   
1525    /**
1526    * control whether the java console is visible or not
1527    *
1528    * @param selected
1529    */
 
1530  18 toggle void showConsole(boolean selected)
1531    {
1532    // TODO: decide if we should update properties file
1533  18 if (jconsole != null) // BH 2018
1534    {
1535  18 showConsole.setSelected(selected);
1536  18 Cache.setProperty("SHOW_JAVA_CONSOLE",
1537    Boolean.valueOf(selected).toString());
1538  18 jconsole.setVisible(selected);
1539    }
1540    }
1541   
 
1542  0 toggle void reorderAssociatedWindows(boolean minimize, boolean close)
1543    {
1544  0 JInternalFrame[] frames = desktop.getAllFrames();
1545  0 if (frames == null || frames.length < 1)
1546    {
1547  0 return;
1548    }
1549   
1550  0 AlignmentViewport source = null, target = null;
1551  0 if (frames[0] instanceof AlignFrame)
1552    {
1553  0 source = ((AlignFrame) frames[0]).getCurrentView();
1554    }
1555  0 else if (frames[0] instanceof TreePanel)
1556    {
1557  0 source = ((TreePanel) frames[0]).getViewPort();
1558    }
1559  0 else if (frames[0] instanceof PCAPanel)
1560    {
1561  0 source = ((PCAPanel) frames[0]).av;
1562    }
1563  0 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1564    {
1565  0 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1566    }
1567   
1568  0 if (source != null)
1569    {
1570  0 for (int i = 0; i < frames.length; i++)
1571    {
1572  0 target = null;
1573  0 if (frames[i] == null)
1574    {
1575  0 continue;
1576    }
1577  0 if (frames[i] instanceof AlignFrame)
1578    {
1579  0 target = ((AlignFrame) frames[i]).getCurrentView();
1580    }
1581  0 else if (frames[i] instanceof TreePanel)
1582    {
1583  0 target = ((TreePanel) frames[i]).getViewPort();
1584    }
1585  0 else if (frames[i] instanceof PCAPanel)
1586    {
1587  0 target = ((PCAPanel) frames[i]).av;
1588    }
1589  0 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1590    {
1591  0 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1592    }
1593   
1594  0 if (source == target)
1595    {
1596  0 try
1597    {
1598  0 if (close)
1599    {
1600  0 frames[i].setClosed(true);
1601    }
1602    else
1603    {
1604  0 frames[i].setIcon(minimize);
1605  0 if (!minimize)
1606    {
1607  0 frames[i].toFront();
1608    }
1609    }
1610   
1611    } catch (java.beans.PropertyVetoException ex)
1612    {
1613    }
1614    }
1615    }
1616    }
1617    }
1618   
1619    /**
1620    * DOCUMENT ME!
1621    *
1622    * @param e
1623    * DOCUMENT ME!
1624    */
 
1625  0 toggle @Override
1626    protected void preferences_actionPerformed(ActionEvent e)
1627    {
1628  0 new Preferences();
1629    }
1630   
1631    /**
1632    * Prompts the user to choose a file and then saves the Jalview state as a
1633    * Jalview project file
1634    */
 
1635  0 toggle @Override
1636    public void saveState_actionPerformed()
1637    {
1638  0 saveState_actionPerformed(false);
1639    }
1640   
 
1641  0 toggle public void saveState_actionPerformed(boolean saveAs)
1642    {
1643  0 java.io.File projectFile = getProjectFile();
1644    // autoSave indicates we already have a file and don't need to ask
1645  0 boolean autoSave = projectFile != null && !saveAs
1646    && BackupFiles.getEnabled();
1647   
1648    // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1649    // saveAs="+saveAs+", Backups
1650    // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1651   
1652  0 boolean approveSave = false;
1653  0 if (!autoSave)
1654    {
1655  0 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1656    "Jalview Project");
1657   
1658  0 chooser.setFileView(new JalviewFileView());
1659  0 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1660   
1661  0 int value = chooser.showSaveDialog(this);
1662   
1663  0 if (value == JalviewFileChooser.APPROVE_OPTION)
1664    {
1665  0 projectFile = chooser.getSelectedFile();
1666  0 setProjectFile(projectFile);
1667  0 approveSave = true;
1668    }
1669    }
1670   
1671  0 if (approveSave || autoSave)
1672    {
1673  0 final Desktop me = this;
1674  0 final java.io.File chosenFile = projectFile;
1675  0 new Thread(new Runnable()
1676    {
 
1677  0 toggle @Override
1678    public void run()
1679    {
1680    // TODO: refactor to Jalview desktop session controller action.
1681  0 setProgressBar(MessageManager.formatMessage(
1682    "label.saving_jalview_project", new Object[]
1683    { chosenFile.getName() }), chosenFile.hashCode());
1684  0 Cache.setProperty("LAST_DIRECTORY",
1685    chosenFile.getParent());
1686    // TODO catch and handle errors for savestate
1687    // TODO prevent user from messing with the Desktop whilst we're saving
1688  0 try
1689    {
1690  0 boolean doBackup = BackupFiles.getEnabled();
1691  0 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1692   
1693  0 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1694   
1695  0 if (doBackup)
1696    {
1697  0 backupfiles.setWriteSuccess(true);
1698  0 backupfiles.rollBackupsAndRenameTempFile();
1699    }
1700    } catch (OutOfMemoryError oom)
1701    {
1702  0 new OOMWarning("Whilst saving current state to "
1703    + chosenFile.getName(), oom);
1704    } catch (Exception ex)
1705    {
1706  0 Cache.log.error("Problems whilst trying to save to "
1707    + chosenFile.getName(), ex);
1708  0 JvOptionPane.showMessageDialog(me,
1709    MessageManager.formatMessage(
1710    "label.error_whilst_saving_current_state_to",
1711    new Object[]
1712    { chosenFile.getName() }),
1713    MessageManager.getString("label.couldnt_save_project"),
1714    JvOptionPane.WARNING_MESSAGE);
1715    }
1716  0 setProgressBar(null, chosenFile.hashCode());
1717    }
1718    }).start();
1719    }
1720    }
1721   
 
1722  0 toggle @Override
1723    public void saveAsState_actionPerformed(ActionEvent e)
1724    {
1725  0 saveState_actionPerformed(true);
1726    }
1727   
 
1728  0 toggle private void setProjectFile(File choice)
1729    {
1730  0 this.projectFile = choice;
1731    }
1732   
 
1733  0 toggle public File getProjectFile()
1734    {
1735  0 return this.projectFile;
1736    }
1737   
1738    /**
1739    * Shows a file chooser dialog and tries to read in the selected file as a
1740    * Jalview project
1741    */
 
1742  0 toggle @Override
1743    public void loadState_actionPerformed()
1744    {
1745  0 final String[] suffix = new String[] { "jvp", "jar" };
1746  0 final String[] desc = new String[] { "Jalview Project",
1747    "Jalview Project (old)" };
1748  0 JalviewFileChooser chooser = new JalviewFileChooser(
1749    Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1750    "Jalview Project", true, BackupFiles.getEnabled()); // last two booleans: allFiles,
1751    // allowBackupFiles
1752  0 chooser.setFileView(new JalviewFileView());
1753  0 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1754  0 chooser.setResponseHandler(0, new Runnable()
1755    {
 
1756  0 toggle @Override
1757    public void run()
1758    {
1759  0 File selectedFile = chooser.getSelectedFile();
1760  0 setProjectFile(selectedFile);
1761  0 String choice = selectedFile.getAbsolutePath();
1762  0 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1763  0 new Thread(new Runnable()
1764    {
 
1765  0 toggle @Override
1766    public void run()
1767    {
1768  0 try
1769    {
1770  0 new Jalview2XML().loadJalviewAlign(selectedFile);
1771    } catch (OutOfMemoryError oom)
1772    {
1773  0 new OOMWarning("Whilst loading project from " + choice, oom);
1774    } catch (Exception ex)
1775    {
1776  0 Cache.log.error(
1777    "Problems whilst loading project from " + choice, ex);
1778  0 JvOptionPane.showMessageDialog(Desktop.desktop,
1779    MessageManager.formatMessage(
1780    "label.error_whilst_loading_project_from",
1781    new Object[]
1782    { choice }),
1783    MessageManager.getString("label.couldnt_load_project"),
1784    JvOptionPane.WARNING_MESSAGE);
1785    }
1786    }
1787    }, "Project Loader").start();
1788    }
1789    });
1790   
1791  0 chooser.showOpenDialog(this);
1792    }
1793   
 
1794  0 toggle @Override
1795    public void inputSequence_actionPerformed(ActionEvent e)
1796    {
1797  0 new SequenceFetcher(this);
1798    }
1799   
1800    JPanel progressPanel;
1801   
1802    ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1803   
 
1804  99 toggle public void startLoading(final Object fileName)
1805    {
1806  99 if (fileLoadingCount == 0)
1807    {
1808  99 fileLoadingPanels.add(addProgressPanel(MessageManager
1809    .formatMessage("label.loading_file", new Object[]
1810    { fileName })));
1811    }
1812  99 fileLoadingCount++;
1813    }
1814   
 
1815  100 toggle private JPanel addProgressPanel(String string)
1816    {
1817  100 if (progressPanel == null)
1818    {
1819  99 progressPanel = new JPanel(new GridLayout(1, 1));
1820  99 totalProgressCount = 0;
1821  99 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1822    }
1823  100 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1824  100 JProgressBar progressBar = new JProgressBar();
1825  100 progressBar.setIndeterminate(true);
1826   
1827  100 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1828   
1829  100 thisprogress.add(progressBar, BorderLayout.CENTER);
1830  100 progressPanel.add(thisprogress);
1831  100 ((GridLayout) progressPanel.getLayout()).setRows(
1832    ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1833  100 ++totalProgressCount;
1834  100 instance.validate();
1835  100 return thisprogress;
1836    }
1837   
1838    int totalProgressCount = 0;
1839   
 
1840  100 toggle private void removeProgressPanel(JPanel progbar)
1841    {
1842  100 if (progressPanel != null)
1843    {
1844  100 synchronized (progressPanel)
1845    {
1846  100 progressPanel.remove(progbar);
1847  100 GridLayout gl = (GridLayout) progressPanel.getLayout();
1848  100 gl.setRows(gl.getRows() - 1);
1849  100 if (--totalProgressCount < 1)
1850    {
1851  99 this.getContentPane().remove(progressPanel);
1852  99 progressPanel = null;
1853    }
1854    }
1855    }
1856  100 validate();
1857    }
1858   
 
1859  120 toggle public void stopLoading()
1860    {
1861  120 fileLoadingCount--;
1862  120 if (fileLoadingCount < 1)
1863    {
1864  219 while (fileLoadingPanels.size() > 0)
1865    {
1866  99 removeProgressPanel(fileLoadingPanels.remove(0));
1867    }
1868  120 fileLoadingPanels.clear();
1869  120 fileLoadingCount = 0;
1870    }
1871  120 validate();
1872    }
1873   
 
1874  6 toggle public static int getViewCount(String alignmentId)
1875    {
1876  6 AlignmentViewport[] aps = getViewports(alignmentId);
1877  6 return (aps == null) ? 0 : aps.length;
1878    }
1879   
1880    /**
1881    *
1882    * @param alignmentId
1883    * - if null, all sets are returned
1884    * @return all AlignmentPanels concerning the alignmentId sequence set
1885    */
 
1886  78 toggle public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1887    {
1888  78 if (Desktop.desktop == null)
1889    {
1890    // no frames created and in headless mode
1891    // TODO: verify that frames are recoverable when in headless mode
1892  0 return null;
1893    }
1894  78 List<AlignmentPanel> aps = new ArrayList<>();
1895  78 AlignFrame[] frames = getAlignFrames();
1896  78 if (frames == null)
1897    {
1898  13 return null;
1899    }
1900  65 for (AlignFrame af : frames)
1901    {
1902  181 for (AlignmentPanel ap : af.alignPanels)
1903    {
1904  303 if (alignmentId == null
1905    || alignmentId.equals(ap.av.getSequenceSetId()))
1906    {
1907  136 aps.add(ap);
1908    }
1909    }
1910    }
1911  65 if (aps.size() == 0)
1912    {
1913  10 return null;
1914    }
1915  55 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1916  55 return vap;
1917    }
1918   
1919    /**
1920    * get all the viewports on an alignment.
1921    *
1922    * @param sequenceSetId
1923    * unique alignment id (may be null - all viewports returned in that
1924    * case)
1925    * @return all viewports on the alignment bound to sequenceSetId
1926    */
 
1927  6 toggle public static AlignmentViewport[] getViewports(String sequenceSetId)
1928    {
1929  6 List<AlignmentViewport> viewp = new ArrayList<>();
1930  6 if (desktop != null)
1931    {
1932  6 AlignFrame[] frames = Desktop.getAlignFrames();
1933   
1934  6 for (AlignFrame afr : frames)
1935    {
1936  6 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1937    .equals(sequenceSetId))
1938    {
1939  6 if (afr.alignPanels != null)
1940    {
1941  6 for (AlignmentPanel ap : afr.alignPanels)
1942    {
1943  7 if (sequenceSetId == null
1944    || sequenceSetId.equals(ap.av.getSequenceSetId()))
1945    {
1946  7 viewp.add(ap.av);
1947    }
1948    }
1949    }
1950    else
1951    {
1952  0 viewp.add(afr.getViewport());
1953    }
1954    }
1955    }
1956  6 if (viewp.size() > 0)
1957    {
1958  6 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1959    }
1960    }
1961  0 return null;
1962    }
1963   
1964    /**
1965    * Explode the views in the given frame into separate AlignFrame
1966    *
1967    * @param af
1968    */
 
1969  1 toggle public static void explodeViews(AlignFrame af)
1970    {
1971  1 int size = af.alignPanels.size();
1972  1 if (size < 2)
1973    {
1974  0 return;
1975    }
1976   
1977    // FIXME: ideally should use UI interface API
1978  1 FeatureSettings viewFeatureSettings = (af.featureSettings != null
1979    && af.featureSettings.isOpen())
1980    ? af.featureSettings
1981    : null;
1982  1 Rectangle fsBounds = af.getFeatureSettingsGeometry();
1983  6 for (int i = 0; i < size; i++)
1984    {
1985  5 AlignmentPanel ap = af.alignPanels.get(i);
1986   
1987  5 AlignFrame newaf = new AlignFrame(ap);
1988   
1989    // transfer reference for existing feature settings to new alignFrame
1990  5 if (ap == af.alignPanel)
1991    {
1992  1 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
1993    {
1994  0 newaf.featureSettings = viewFeatureSettings;
1995    }
1996  1 newaf.setFeatureSettingsGeometry(fsBounds);
1997    }
1998   
1999    /*
2000    * Restore the view's last exploded frame geometry if known. Multiple
2001    * views from one exploded frame share and restore the same (frame)
2002    * position and size.
2003    */
2004  5 Rectangle geometry = ap.av.getExplodedGeometry();
2005  5 if (geometry != null)
2006    {
2007  5 newaf.setBounds(geometry);
2008    }
2009   
2010  5 ap.av.setGatherViewsHere(false);
2011   
2012  5 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2013    AlignFrame.DEFAULT_HEIGHT);
2014    // and materialise a new feature settings dialog instance for the new alignframe
2015    // (closes the old as if 'OK' was pressed)
2016  5 if (ap == af.alignPanel && newaf.featureSettings != null
2017    && newaf.featureSettings.isOpen()
2018    && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2019    {
2020  0 newaf.showFeatureSettingsUI();
2021    }
2022    }
2023   
2024  1 af.featureSettings = null;
2025  1 af.alignPanels.clear();
2026  1 af.closeMenuItem_actionPerformed(true);
2027   
2028    }
2029   
2030    /**
2031    * Gather expanded views (separate AlignFrame's) with the same sequence set
2032    * identifier back in to this frame as additional views, and close the expanded
2033    * views. Note the expanded frames may themselves have multiple views. We take
2034    * the lot.
2035    *
2036    * @param source
2037    */
 
2038  12 toggle public void gatherViews(AlignFrame source)
2039    {
2040  12 source.viewport.setGatherViewsHere(true);
2041  12 source.viewport.setExplodedGeometry(source.getBounds());
2042  12 JInternalFrame[] frames = desktop.getAllFrames();
2043  12 String viewId = source.viewport.getSequenceSetId();
2044  98 for (int t = 0; t < frames.length; t++)
2045    {
2046  86 if (frames[t] instanceof AlignFrame && frames[t] != source)
2047    {
2048  52 AlignFrame af = (AlignFrame) frames[t];
2049  52 boolean gatherThis = false;
2050  128 for (int a = 0; a < af.alignPanels.size(); a++)
2051    {
2052  76 AlignmentPanel ap = af.alignPanels.get(a);
2053  76 if (viewId.equals(ap.av.getSequenceSetId()))
2054    {
2055  37 gatherThis = true;
2056  37 ap.av.setGatherViewsHere(false);
2057  37 ap.av.setExplodedGeometry(af.getBounds());
2058  37 source.addAlignmentPanel(ap, false);
2059    }
2060    }
2061   
2062  52 if (gatherThis)
2063    {
2064  37 if (af.featureSettings != null && af.featureSettings.isOpen())
2065    {
2066  0 if (source.featureSettings == null)
2067    {
2068    // preserve the feature settings geometry for this frame
2069  0 source.featureSettings = af.featureSettings;
2070  0 source.setFeatureSettingsGeometry(
2071    af.getFeatureSettingsGeometry());
2072    }
2073    else
2074    {
2075    // close it and forget
2076  0 af.featureSettings.close();
2077    }
2078    }
2079  37 af.alignPanels.clear();
2080  37 af.closeMenuItem_actionPerformed(true);
2081    }
2082    }
2083    }
2084   
2085    // refresh the feature setting UI for the source frame if it exists
2086  12 if (source.featureSettings != null
2087    && source.featureSettings.isOpen())
2088    {
2089  0 source.showFeatureSettingsUI();
2090    }
2091    }
2092   
 
2093  2 toggle public JInternalFrame[] getAllFrames()
2094    {
2095  2 return desktop.getAllFrames();
2096    }
2097   
2098    /**
2099    * Checks the given url to see if it gives a response indicating that the user
2100    * should be informed of a new questionnaire.
2101    *
2102    * @param url
2103    */
 
2104  0 toggle public void checkForQuestionnaire(String url)
2105    {
2106  0 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2107    // javax.swing.SwingUtilities.invokeLater(jvq);
2108  0 new Thread(jvq).start();
2109    }
2110   
 
2111  18 toggle public void checkURLLinks()
2112    {
2113    // Thread off the URL link checker
2114  18 addDialogThread(new Runnable()
2115    {
 
2116  6 toggle @Override
2117    public void run()
2118    {
2119  6 if (Cache.getDefault("CHECKURLLINKS", true))
2120    {
2121    // check what the actual links are - if it's just the default don't
2122    // bother with the warning
2123  6 List<String> links = Preferences.sequenceUrlLinks
2124    .getLinksForMenu();
2125   
2126    // only need to check links if there is one with a
2127    // SEQUENCE_ID which is not the default EMBL_EBI link
2128  6 ListIterator<String> li = links.listIterator();
2129  6 boolean check = false;
2130  6 List<JLabel> urls = new ArrayList<>();
2131  24 while (li.hasNext())
2132    {
2133  18 String link = li.next();
2134  18 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2135    && !UrlConstants.isDefaultString(link))
2136    {
2137  0 check = true;
2138  0 int barPos = link.indexOf("|");
2139  0 String urlMsg = barPos == -1 ? link
2140    : link.substring(0, barPos) + ": "
2141    + link.substring(barPos + 1);
2142  0 urls.add(new JLabel(urlMsg));
2143    }
2144    }
2145  6 if (!check)
2146    {
2147  6 return;
2148    }
2149   
2150    // ask user to check in case URL links use old style tokens
2151    // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2152  0 JPanel msgPanel = new JPanel();
2153  0 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2154  0 msgPanel.add(Box.createVerticalGlue());
2155  0 JLabel msg = new JLabel(MessageManager
2156    .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2157  0 JLabel msg2 = new JLabel(MessageManager
2158    .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2159  0 msgPanel.add(msg);
2160  0 for (JLabel url : urls)
2161    {
2162  0 msgPanel.add(url);
2163    }
2164  0 msgPanel.add(msg2);
2165   
2166  0 final JCheckBox jcb = new JCheckBox(
2167    MessageManager.getString("label.do_not_display_again"));
2168  0 jcb.addActionListener(new ActionListener()
2169    {
 
2170  0 toggle @Override
2171    public void actionPerformed(ActionEvent e)
2172    {
2173    // update Cache settings for "don't show this again"
2174  0 boolean showWarningAgain = !jcb.isSelected();
2175  0 Cache.setProperty("CHECKURLLINKS",
2176    Boolean.valueOf(showWarningAgain).toString());
2177    }
2178    });
2179  0 msgPanel.add(jcb);
2180   
2181  0 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2182    MessageManager
2183    .getString("label.SEQUENCE_ID_no_longer_used"),
2184    JvOptionPane.WARNING_MESSAGE);
2185    }
2186    }
2187    });
2188    }
2189   
2190    /**
2191    * Proxy class for JDesktopPane which optionally displays the current memory
2192    * usage and highlights the desktop area with a red bar if free memory runs low.
2193    *
2194    * @author AMW
2195    */
 
2196    public class MyDesktopPane extends JDesktopPane
2197    implements Runnable
2198    {
2199    private static final float ONE_MB = 1048576f;
2200   
2201    boolean showMemoryUsage = false;
2202   
2203    Runtime runtime;
2204   
2205    java.text.NumberFormat df;
2206   
2207    float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2208    percentUsage;
2209   
 
2210  18 toggle public MyDesktopPane(boolean showMemoryUsage)
2211    {
2212  18 showMemoryUsage(showMemoryUsage);
2213    }
2214   
 
2215  18 toggle public void showMemoryUsage(boolean showMemory)
2216    {
2217  18 this.showMemoryUsage = showMemory;
2218  18 if (showMemory)
2219    {
2220  0 Thread worker = new Thread(this);
2221  0 worker.start();
2222    }
2223  18 repaint();
2224    }
2225   
 
2226  198 toggle public boolean isShowMemoryUsage()
2227    {
2228  198 return showMemoryUsage;
2229    }
2230   
 
2231  0 toggle @Override
2232    public void run()
2233    {
2234  0 df = java.text.NumberFormat.getNumberInstance();
2235  0 df.setMaximumFractionDigits(2);
2236  0 runtime = Runtime.getRuntime();
2237   
2238  0 while (showMemoryUsage)
2239    {
2240  0 try
2241    {
2242  0 maxMemory = runtime.maxMemory() / ONE_MB;
2243  0 allocatedMemory = runtime.totalMemory() / ONE_MB;
2244  0 freeMemory = runtime.freeMemory() / ONE_MB;
2245  0 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2246   
2247  0 percentUsage = (totalFreeMemory / maxMemory) * 100;
2248   
2249    // if (percentUsage < 20)
2250    {
2251    // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2252    // Color.red);
2253    // instance.set.setBorder(border1);
2254    }
2255  0 repaint();
2256    // sleep after showing usage
2257  0 Thread.sleep(3000);
2258    } catch (Exception ex)
2259    {
2260  0 ex.printStackTrace();
2261    }
2262    }
2263    }
2264   
 
2265  423 toggle @Override
2266    public void paintComponent(Graphics g)
2267    {
2268  423 if (showMemoryUsage && g != null && df != null)
2269    {
2270  0 if (percentUsage < 20)
2271    {
2272  0 g.setColor(Color.red);
2273    }
2274  0 FontMetrics fm = g.getFontMetrics();
2275  0 if (fm != null)
2276    {
2277  0 g.drawString(MessageManager.formatMessage("label.memory_stats",
2278    new Object[]
2279    { df.format(totalFreeMemory), df.format(maxMemory),
2280    df.format(percentUsage) }),
2281    10, getHeight() - fm.getHeight());
2282    }
2283    }
2284    }
2285    }
2286   
2287    /**
2288    * Accessor method to quickly get all the AlignmentFrames loaded.
2289    *
2290    * @return an array of AlignFrame, or null if none found
2291    */
 
2292  172 toggle public static AlignFrame[] getAlignFrames()
2293    {
2294  172 if (Jalview.isHeadlessMode())
2295    {
2296    // Desktop.desktop is null in headless mode
2297  0 return new AlignFrame[] { Jalview.currentAlignFrame };
2298    }
2299   
2300  172 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2301   
2302  172 if (frames == null)
2303    {
2304  0 return null;
2305    }
2306  172 List<AlignFrame> avp = new ArrayList<>();
2307    // REVERSE ORDER
2308  618 for (int i = frames.length - 1; i > -1; i--)
2309    {
2310  446 if (frames[i] instanceof AlignFrame)
2311    {
2312  280 avp.add((AlignFrame) frames[i]);
2313    }
2314  166 else if (frames[i] instanceof SplitFrame)
2315    {
2316    /*
2317    * Also check for a split frame containing an AlignFrame
2318    */
2319  26 GSplitFrame sf = (GSplitFrame) frames[i];
2320  26 if (sf.getTopFrame() instanceof AlignFrame)
2321    {
2322  26 avp.add((AlignFrame) sf.getTopFrame());
2323    }
2324  26 if (sf.getBottomFrame() instanceof AlignFrame)
2325    {
2326  26 avp.add((AlignFrame) sf.getBottomFrame());
2327    }
2328    }
2329    }
2330  172 if (avp.size() == 0)
2331    {
2332  30 return null;
2333    }
2334  142 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2335  142 return afs;
2336    }
2337   
2338    /**
2339    * Returns an array of any AppJmol frames in the Desktop (or null if none).
2340    *
2341    * @return
2342    */
 
2343  0 toggle public GStructureViewer[] getJmols()
2344    {
2345  0 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2346   
2347  0 if (frames == null)
2348    {
2349  0 return null;
2350    }
2351  0 List<GStructureViewer> avp = new ArrayList<>();
2352    // REVERSE ORDER
2353  0 for (int i = frames.length - 1; i > -1; i--)
2354    {
2355  0 if (frames[i] instanceof AppJmol)
2356    {
2357  0 GStructureViewer af = (GStructureViewer) frames[i];
2358  0 avp.add(af);
2359    }
2360    }
2361  0 if (avp.size() == 0)
2362    {
2363  0 return null;
2364    }
2365  0 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2366  0 return afs;
2367    }
2368   
2369    /**
2370    * Add Groovy Support to Jalview
2371    */
 
2372  0 toggle @Override
2373    public void groovyShell_actionPerformed()
2374    {
2375  0 try
2376    {
2377  0 openGroovyConsole();
2378    } catch (Exception ex)
2379    {
2380  0 Cache.log.error("Groovy Shell Creation failed.", ex);
2381  0 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2382   
2383    MessageManager.getString("label.couldnt_create_groovy_shell"),
2384    MessageManager.getString("label.groovy_support_failed"),
2385    JvOptionPane.ERROR_MESSAGE);
2386    }
2387    }
2388   
2389    /**
2390    * Open the Groovy console
2391    */
 
2392  0 toggle void openGroovyConsole()
2393    {
2394  0 if (groovyConsole == null)
2395    {
2396  0 groovyConsole = new groovy.ui.Console();
2397  0 groovyConsole.setVariable("Jalview", this);
2398  0 groovyConsole.run();
2399   
2400    /*
2401    * We allow only one console at a time, so that AlignFrame menu option
2402    * 'Calculate | Run Groovy script' is unambiguous.
2403    * Disable 'Groovy Console', and enable 'Run script', when the console is
2404    * opened, and the reverse when it is closed
2405    */
2406  0 Window window = (Window) groovyConsole.getFrame();
2407  0 window.addWindowListener(new WindowAdapter()
2408    {
 
2409  0 toggle @Override
2410    public void windowClosed(WindowEvent e)
2411    {
2412    /*
2413    * rebind CMD-Q from Groovy Console to Jalview Quit
2414    */
2415  0 addQuitHandler();
2416  0 enableExecuteGroovy(false);
2417    }
2418    });
2419    }
2420   
2421    /*
2422    * show Groovy console window (after close and reopen)
2423    */
2424  0 ((Window) groovyConsole.getFrame()).setVisible(true);
2425   
2426    /*
2427    * if we got this far, enable 'Run Groovy' in AlignFrame menus
2428    * and disable opening a second console
2429    */
2430  0 enableExecuteGroovy(true);
2431    }
2432   
2433    /**
2434    * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2435    * when opened
2436    */
 
2437  0 toggle protected void addQuitHandler()
2438    {
2439  0 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2440    .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2441    jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
2442    "Quit");
2443  0 getRootPane().getActionMap().put("Quit", new AbstractAction()
2444    {
 
2445  0 toggle @Override
2446    public void actionPerformed(ActionEvent e)
2447    {
2448  0 quit();
2449    }
2450    });
2451    }
2452   
2453    /**
2454    * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2455    *
2456    * @param enabled
2457    * true if Groovy console is open
2458    */
 
2459  0 toggle public void enableExecuteGroovy(boolean enabled)
2460    {
2461    /*
2462    * disable opening a second Groovy console
2463    * (or re-enable when the console is closed)
2464    */
2465  0 groovyShell.setEnabled(!enabled);
2466   
2467  0 AlignFrame[] alignFrames = getAlignFrames();
2468  0 if (alignFrames != null)
2469    {
2470  0 for (AlignFrame af : alignFrames)
2471    {
2472  0 af.setGroovyEnabled(enabled);
2473    }
2474    }
2475    }
2476   
2477    /**
2478    * Progress bars managed by the IProgressIndicator method.
2479    */
2480    private Hashtable<Long, JPanel> progressBars;
2481   
2482    private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2483   
2484    /*
2485    * (non-Javadoc)
2486    *
2487    * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2488    */
 
2489  2 toggle @Override
2490    public void setProgressBar(String message, long id)
2491    {
2492    // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2493   
2494  2 if (progressBars == null)
2495    {
2496  1 progressBars = new Hashtable<>();
2497  1 progressBarHandlers = new Hashtable<>();
2498    }
2499   
2500  2 if (progressBars.get(Long.valueOf(id)) != null)
2501    {
2502  1 JPanel panel = progressBars.remove(Long.valueOf(id));
2503  1 if (progressBarHandlers.contains(Long.valueOf(id)))
2504    {
2505  0 progressBarHandlers.remove(Long.valueOf(id));
2506    }
2507  1 removeProgressPanel(panel);
2508    }
2509    else
2510    {
2511  1 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2512    }
2513    }
2514   
2515    /*
2516    * (non-Javadoc)
2517    *
2518    * @see jalview.gui.IProgressIndicator#registerHandler(long,
2519    * jalview.gui.IProgressIndicatorHandler)
2520    */
 
2521  0 toggle @Override
2522    public void registerHandler(final long id,
2523    final IProgressIndicatorHandler handler)
2524    {
2525  0 if (progressBarHandlers == null
2526    || !progressBars.containsKey(Long.valueOf(id)))
2527    {
2528  0 throw new Error(MessageManager.getString(
2529    "error.call_setprogressbar_before_registering_handler"));
2530    }
2531  0 progressBarHandlers.put(Long.valueOf(id), handler);
2532  0 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2533  0 if (handler.canCancel())
2534    {
2535  0 JButton cancel = new JButton(
2536    MessageManager.getString("action.cancel"));
2537  0 final IProgressIndicator us = this;
2538  0 cancel.addActionListener(new ActionListener()
2539    {
2540   
 
2541  0 toggle @Override
2542    public void actionPerformed(ActionEvent e)
2543    {
2544  0 handler.cancelActivity(id);
2545  0 us.setProgressBar(MessageManager
2546    .formatMessage("label.cancelled_params", new Object[]
2547    { ((JLabel) progressPanel.getComponent(0)).getText() }),
2548    id);
2549    }
2550    });
2551  0 progressPanel.add(cancel, BorderLayout.EAST);
2552    }
2553    }
2554   
2555    /**
2556    *
2557    * @return true if any progress bars are still active
2558    */
 
2559  0 toggle @Override
2560    public boolean operationInProgress()
2561    {
2562  0 if (progressBars != null && progressBars.size() > 0)
2563    {
2564  0 return true;
2565    }
2566  0 return false;
2567    }
2568   
2569    /**
2570    * This will return the first AlignFrame holding the given viewport instance. It
2571    * will break if there are more than one AlignFrames viewing a particular av.
2572    *
2573    * @param viewport
2574    * @return alignFrame for viewport
2575    */
 
2576  1 toggle public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2577    {
2578  1 if (desktop != null)
2579    {
2580  1 AlignmentPanel[] aps = getAlignmentPanels(
2581    viewport.getSequenceSetId());
2582  1 for (int panel = 0; aps != null && panel < aps.length; panel++)
2583    {
2584  1 if (aps[panel] != null && aps[panel].av == viewport)
2585    {
2586  1 return aps[panel].alignFrame;
2587    }
2588    }
2589    }
2590  0 return null;
2591    }
2592   
 
2593  0 toggle public VamsasApplication getVamsasApplication()
2594    {
2595    // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2596  0 return null;
2597   
2598    }
2599   
2600    /**
2601    * flag set if jalview GUI is being operated programmatically
2602    */
2603    private boolean inBatchMode = false;
2604   
2605    /**
2606    * check if jalview GUI is being operated programmatically
2607    *
2608    * @return inBatchMode
2609    */
 
2610  0 toggle public boolean isInBatchMode()
2611    {
2612  0 return inBatchMode;
2613    }
2614   
2615    /**
2616    * set flag if jalview GUI is being operated programmatically
2617    *
2618    * @param inBatchMode
2619    */
 
2620  36 toggle public void setInBatchMode(boolean inBatchMode)
2621    {
2622  36 this.inBatchMode = inBatchMode;
2623    }
2624   
 
2625  18 toggle public void startServiceDiscovery()
2626    {
2627  18 startServiceDiscovery(false);
2628    }
2629   
 
2630  18 toggle public void startServiceDiscovery(boolean blocking)
2631    {
2632  18 boolean alive = true;
2633  18 Thread t0 = null, t1 = null, t2 = null;
2634    // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2635  18 if (true)
2636    {
2637    // todo: changesupport handlers need to be transferred
2638  18 if (discoverer == null)
2639    {
2640  2 discoverer = new jalview.ws.jws1.Discoverer();
2641    // register PCS handler for desktop.
2642  2 discoverer.addPropertyChangeListener(changeSupport);
2643    }
2644    // JAL-940 - disabled JWS1 service configuration - always start discoverer
2645    // until we phase out completely
2646  18 (t0 = new Thread(discoverer)).start();
2647    }
2648   
2649  18 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2650    {
2651  18 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2652    .startDiscoverer(changeSupport);
2653    }
2654  18 Thread t3 = null;
2655    {
2656    // TODO: do rest service discovery
2657    }
2658  18 if (blocking)
2659    {
2660  0 while (alive)
2661    {
2662  0 try
2663    {
2664  0 Thread.sleep(15);
2665    } catch (Exception e)
2666    {
2667    }
2668  0 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2669    || (t3 != null && t3.isAlive())
2670    || (t0 != null && t0.isAlive());
2671    }
2672    }
2673    }
2674   
2675    /**
2676    * called to check if the service discovery process completed successfully.
2677    *
2678    * @param evt
2679    */
 
2680  299 toggle protected void JalviewServicesChanged(PropertyChangeEvent evt)
2681    {
2682  299 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2683    {
2684  299 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2685    .getErrorMessages();
2686  299 if (ermsg != null)
2687    {
2688  0 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2689    {
2690  0 if (serviceChangedDialog == null)
2691    {
2692    // only run if we aren't already displaying one of these.
2693  0 addDialogThread(serviceChangedDialog = new Runnable()
2694    {
 
2695  0 toggle @Override
2696    public void run()
2697    {
2698   
2699    /*
2700    * JalviewDialog jd =new JalviewDialog() {
2701    *
2702    * @Override protected void cancelPressed() { // TODO
2703    * Auto-generated method stub
2704    *
2705    * }@Override protected void okPressed() { // TODO
2706    * Auto-generated method stub
2707    *
2708    * }@Override protected void raiseClosed() { // TODO
2709    * Auto-generated method stub
2710    *
2711    * } }; jd.initDialogFrame(new
2712    * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2713    * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2714    * + " or mis-configured HTTP proxy settings.<br/>" +
2715    * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2716    * +
2717    * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2718    * ), true, true, "Web Service Configuration Problem", 450,
2719    * 400);
2720    *
2721    * jd.waitForInput();
2722    */
2723  0 JvOptionPane.showConfirmDialog(Desktop.desktop,
2724    new JLabel("<html><table width=\"450\"><tr><td>"
2725    + ermsg + "</td></tr></table>"
2726    + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2727    + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2728    + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2729    + " Tools->Preferences dialog box to change them.</p></html>"),
2730    "Web Service Configuration Problem",
2731    JvOptionPane.DEFAULT_OPTION,
2732    JvOptionPane.ERROR_MESSAGE);
2733  0 serviceChangedDialog = null;
2734   
2735    }
2736    });
2737    }
2738    }
2739    else
2740    {
2741  0 Cache.log.error(
2742    "Errors reported by JABA discovery service. Check web services preferences.\n"
2743    + ermsg);
2744    }
2745    }
2746    }
2747    }
2748   
2749    private Runnable serviceChangedDialog = null;
2750   
2751    /**
2752    * start a thread to open a URL in the configured browser. Pops up a warning
2753    * dialog to the user if there is an exception when calling out to the browser
2754    * to open the URL.
2755    *
2756    * @param url
2757    */
 
2758  0 toggle public static void showUrl(final String url)
2759    {
2760  0 showUrl(url, Desktop.instance);
2761    }
2762   
2763    /**
2764    * Like showUrl but allows progress handler to be specified
2765    *
2766    * @param url
2767    * @param progress
2768    * (null) or object implementing IProgressIndicator
2769    */
 
2770  0 toggle public static void showUrl(final String url,
2771    final IProgressIndicator progress)
2772    {
2773  0 new Thread(new Runnable()
2774    {
 
2775  0 toggle @Override
2776    public void run()
2777    {
2778  0 try
2779    {
2780  0 if (progress != null)
2781    {
2782  0 progress.setProgressBar(MessageManager
2783    .formatMessage("status.opening_params", new Object[]
2784    { url }), this.hashCode());
2785    }
2786  0 jalview.util.BrowserLauncher.openURL(url);
2787    } catch (Exception ex)
2788    {
2789  0 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2790    MessageManager
2791    .getString("label.web_browser_not_found_unix"),
2792    MessageManager.getString("label.web_browser_not_found"),
2793    JvOptionPane.WARNING_MESSAGE);
2794   
2795  0 ex.printStackTrace();
2796    }
2797  0 if (progress != null)
2798    {
2799  0 progress.setProgressBar(null, this.hashCode());
2800    }
2801    }
2802    }).start();
2803    }
2804   
2805    public static WsParamSetManager wsparamManager = null;
2806   
 
2807  191 toggle public static ParamManager getUserParameterStore()
2808    {
2809  191 if (wsparamManager == null)
2810    {
2811  2 wsparamManager = new WsParamSetManager();
2812    }
2813  191 return wsparamManager;
2814    }
2815   
2816    /**
2817    * static hyperlink handler proxy method for use by Jalview's internal windows
2818    *
2819    * @param e
2820    */
 
2821  0 toggle public static void hyperlinkUpdate(HyperlinkEvent e)
2822    {
2823  0 if (e.getEventType() == EventType.ACTIVATED)
2824    {
2825  0 String url = null;
2826  0 try
2827    {
2828  0 url = e.getURL().toString();
2829  0 Desktop.showUrl(url);
2830    } catch (Exception x)
2831    {
2832  0 if (url != null)
2833    {
2834  0 if (Cache.log != null)
2835    {
2836  0 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2837    }
2838    else
2839    {
2840  0 System.err.println(
2841    "Couldn't handle string " + url + " as a URL.");
2842    }
2843    }
2844    // ignore any exceptions due to dud links.
2845    }
2846   
2847    }
2848    }
2849   
2850    /**
2851    * single thread that handles display of dialogs to user.
2852    */
2853    ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2854   
2855    /**
2856    * flag indicating if dialogExecutor should try to acquire a permit
2857    */
2858    private volatile boolean dialogPause = true;
2859   
2860    /**
2861    * pause the queue
2862    */
2863    private java.util.concurrent.Semaphore block = new Semaphore(0);
2864   
2865    private static groovy.ui.Console groovyConsole;
2866   
2867    /**
2868    * add another dialog thread to the queue
2869    *
2870    * @param prompter
2871    */
 
2872  35 toggle public void addDialogThread(final Runnable prompter)
2873    {
2874  35 dialogExecutor.submit(new Runnable()
2875    {
 
2876  23 toggle @Override
2877    public void run()
2878    {
2879  23 if (dialogPause)
2880    {
2881  18 try
2882    {
2883  18 block.acquire();
2884    } catch (InterruptedException x)
2885    {
2886    }
2887    }
2888  11 if (instance == null)
2889    {
2890  0 return;
2891    }
2892  11 try
2893    {
2894  11 SwingUtilities.invokeAndWait(prompter);
2895    } catch (Exception q)
2896    {
2897  0 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2898    }
2899    }
2900    });
2901    }
2902   
 
2903  16 toggle public void startDialogQueue()
2904    {
2905    // set the flag so we don't pause waiting for another permit and semaphore
2906    // the current task to begin
2907  16 dialogPause = false;
2908  16 block.release();
2909    }
2910   
2911    /**
2912    * Outputs an image of the desktop to file in EPS format, after prompting the
2913    * user for choice of Text or Lineart character rendering (unless a preference
2914    * has been set). The file name is generated as
2915    *
2916    * <pre>
2917    * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2918    * </pre>
2919    */
 
2920  0 toggle @Override
2921    protected void snapShotWindow_actionPerformed(ActionEvent e)
2922    {
2923    // currently the menu option to do this is not shown
2924  0 invalidate();
2925   
2926  0 int width = getWidth();
2927  0 int height = getHeight();
2928  0 File of = new File(
2929    "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2930  0 ImageWriterI writer = new ImageWriterI()
2931    {
 
2932  0 toggle @Override
2933    public void exportImage(Graphics g) throws Exception
2934    {
2935  0 paintAll(g);
2936  0 Cache.log.info("Successfully written snapshot to file "
2937    + of.getAbsolutePath());
2938    }
2939    };
2940  0 String title = "View of desktop";
2941  0 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2942    title);
2943  0 exporter.doExport(of, this, width, height, title);
2944    }
2945   
2946    /**
2947    * Explode the views in the given SplitFrame into separate SplitFrame windows.
2948    * This respects (remembers) any previous 'exploded geometry' i.e. the size and
2949    * location last time the view was expanded (if any). However it does not
2950    * remember the split pane divider location - this is set to match the
2951    * 'exploding' frame.
2952    *
2953    * @param sf
2954    */
 
2955  0 toggle public void explodeViews(SplitFrame sf)
2956    {
2957  0 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2958  0 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2959  0 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2960    .getAlignPanels();
2961  0 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2962    .getAlignPanels();
2963  0 int viewCount = topPanels.size();
2964  0 if (viewCount < 2)
2965    {
2966  0 return;
2967    }
2968   
2969    /*
2970    * Processing in reverse order works, forwards order leaves the first panels
2971    * not visible. I don't know why!
2972    */
2973  0 for (int i = viewCount - 1; i >= 0; i--)
2974    {
2975    /*
2976    * Make new top and bottom frames. These take over the respective
2977    * AlignmentPanel objects, including their AlignmentViewports, so the
2978    * cdna/protein relationships between the viewports is carried over to the
2979    * new split frames.
2980    *
2981    * explodedGeometry holds the (x, y) position of the previously exploded
2982    * SplitFrame, and the (width, height) of the AlignFrame component
2983    */
2984  0 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
2985  0 AlignFrame newTopFrame = new AlignFrame(topPanel);
2986  0 newTopFrame.setSize(oldTopFrame.getSize());
2987  0 newTopFrame.setVisible(true);
2988  0 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
2989    .getExplodedGeometry();
2990  0 if (geometry != null)
2991    {
2992  0 newTopFrame.setSize(geometry.getSize());
2993    }
2994   
2995  0 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
2996  0 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
2997  0 newBottomFrame.setSize(oldBottomFrame.getSize());
2998  0 newBottomFrame.setVisible(true);
2999  0 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3000    .getExplodedGeometry();
3001  0 if (geometry != null)
3002    {
3003  0 newBottomFrame.setSize(geometry.getSize());
3004    }
3005   
3006  0 topPanel.av.setGatherViewsHere(false);
3007  0 bottomPanel.av.setGatherViewsHere(false);
3008  0 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3009    newBottomFrame);
3010  0 if (geometry != null)
3011    {
3012  0 splitFrame.setLocation(geometry.getLocation());
3013    }
3014  0 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3015    }
3016   
3017    /*
3018    * Clear references to the panels (now relocated in the new SplitFrames)
3019    * before closing the old SplitFrame.
3020    */
3021  0 topPanels.clear();
3022  0 bottomPanels.clear();
3023  0 sf.close();
3024    }
3025   
3026    /**
3027    * Gather expanded split frames, sharing the same pairs of sequence set ids,
3028    * back into the given SplitFrame as additional views. Note that the gathered
3029    * frames may themselves have multiple views.
3030    *
3031    * @param source
3032    */
 
3033  0 toggle public void gatherViews(GSplitFrame source)
3034    {
3035    /*
3036    * special handling of explodedGeometry for a view within a SplitFrame: - it
3037    * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3038    * height) of the AlignFrame component
3039    */
3040  0 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3041  0 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3042  0 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3043    source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3044  0 myBottomFrame.viewport
3045    .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3046    myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3047  0 myTopFrame.viewport.setGatherViewsHere(true);
3048  0 myBottomFrame.viewport.setGatherViewsHere(true);
3049  0 String topViewId = myTopFrame.viewport.getSequenceSetId();
3050  0 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3051   
3052  0 JInternalFrame[] frames = desktop.getAllFrames();
3053  0 for (JInternalFrame frame : frames)
3054    {
3055  0 if (frame instanceof SplitFrame && frame != source)
3056    {
3057  0 SplitFrame sf = (SplitFrame) frame;
3058  0 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3059  0 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3060  0 boolean gatherThis = false;
3061  0 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3062    {
3063  0 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3064  0 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3065  0 if (topViewId.equals(topPanel.av.getSequenceSetId())
3066    && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3067    {
3068  0 gatherThis = true;
3069  0 topPanel.av.setGatherViewsHere(false);
3070  0 bottomPanel.av.setGatherViewsHere(false);
3071  0 topPanel.av.setExplodedGeometry(
3072    new Rectangle(sf.getLocation(), topFrame.getSize()));
3073  0 bottomPanel.av.setExplodedGeometry(
3074    new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3075  0 myTopFrame.addAlignmentPanel(topPanel, false);
3076  0 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3077    }
3078    }
3079   
3080  0 if (gatherThis)
3081    {
3082  0 topFrame.getAlignPanels().clear();
3083  0 bottomFrame.getAlignPanels().clear();
3084  0 sf.close();
3085    }
3086    }
3087    }
3088   
3089    /*
3090    * The dust settles...give focus to the tab we did this from.
3091    */
3092  0 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3093    }
3094   
 
3095  421 toggle public static groovy.ui.Console getGroovyConsole()
3096    {
3097  421 return groovyConsole;
3098    }
3099   
3100    /**
3101    * handles the payload of a drag and drop event.
3102    *
3103    * TODO refactor to desktop utilities class
3104    *
3105    * @param files
3106    * - Data source strings extracted from the drop event
3107    * @param protocols
3108    * - protocol for each data source extracted from the drop event
3109    * @param evt
3110    * - the drop event
3111    * @param t
3112    * - the payload from the drop event
3113    * @throws Exception
3114    */
 
3115  0 toggle public static void transferFromDropTarget(List<Object> files,
3116    List<DataSourceType> protocols, DropTargetDropEvent evt,
3117    Transferable t) throws Exception
3118    {
3119   
3120    // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3121   
3122    // DataFlavor[] flavors = t.getTransferDataFlavors();
3123    // for (int i = 0; i < flavors.length; i++) {
3124    // if (flavors[i].isFlavorJavaFileListType()) {
3125    // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3126    // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3127    // for (int j = 0; j < list.size(); j++) {
3128    // File file = (File) list.get(j);
3129    // byte[] data = getDroppedFileBytes(file);
3130    // fileName.setText(file.getName() + " - " + data.length + " " +
3131    // evt.getLocation());
3132    // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3133    // target.setText(new String(data));
3134    // }
3135    // dtde.dropComplete(true);
3136    // return;
3137    // }
3138    //
3139   
3140  0 DataFlavor uriListFlavor = new DataFlavor(
3141    "text/uri-list;class=java.lang.String"), urlFlavour = null;
3142  0 try
3143    {
3144  0 urlFlavour = new DataFlavor(
3145    "application/x-java-url; class=java.net.URL");
3146    } catch (ClassNotFoundException cfe)
3147    {
3148  0 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3149    }
3150   
3151  0 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3152    {
3153   
3154  0 try
3155    {
3156  0 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3157    // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3158    // means url may be null.
3159  0 if (url != null)
3160    {
3161  0 protocols.add(DataSourceType.URL);
3162  0 files.add(url.toString());
3163  0 Cache.log.debug("Drop handled as URL dataflavor "
3164    + files.get(files.size() - 1));
3165  0 return;
3166    }
3167    else
3168    {
3169  0 if (Platform.isAMacAndNotJS())
3170    {
3171  0 System.err.println(
3172    "Please ignore plist error - occurs due to problem with java 8 on OSX");
3173    }
3174    }
3175    } catch (Throwable ex)
3176    {
3177  0 Cache.log.debug("URL drop handler failed.", ex);
3178    }
3179    }
3180  0 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3181    {
3182    // Works on Windows and MacOSX
3183  0 Cache.log.debug("Drop handled as javaFileListFlavor");
3184  0 for (Object file : (List) t
3185    .getTransferData(DataFlavor.javaFileListFlavor))
3186    {
3187  0 files.add(file);
3188  0 protocols.add(DataSourceType.FILE);
3189    }
3190    }
3191    else
3192    {
3193    // Unix like behaviour
3194  0 boolean added = false;
3195  0 String data = null;
3196  0 if (t.isDataFlavorSupported(uriListFlavor))
3197    {
3198  0 Cache.log.debug("Drop handled as uriListFlavor");
3199    // This is used by Unix drag system
3200  0 data = (String) t.getTransferData(uriListFlavor);
3201    }
3202  0 if (data == null)
3203    {
3204    // fallback to text: workaround - on OSX where there's a JVM bug
3205  0 Cache.log.debug("standard URIListFlavor failed. Trying text");
3206    // try text fallback
3207  0 DataFlavor textDf = new DataFlavor(
3208    "text/plain;class=java.lang.String");
3209  0 if (t.isDataFlavorSupported(textDf))
3210    {
3211  0 data = (String) t.getTransferData(textDf);
3212    }
3213   
3214  0 Cache.log.debug("Plain text drop content returned "
3215  0 + (data == null ? "Null - failed" : data));
3216   
3217    }
3218  0 if (data != null)
3219    {
3220  0 while (protocols.size() < files.size())
3221    {
3222  0 Cache.log.debug("Adding missing FILE protocol for "
3223    + files.get(protocols.size()));
3224  0 protocols.add(DataSourceType.FILE);
3225    }
3226  0 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3227  0 data, "\r\n"); st.hasMoreTokens();)
3228    {
3229  0 added = true;
3230  0 String s = st.nextToken();
3231  0 if (s.startsWith("#"))
3232    {
3233    // the line is a comment (as per the RFC 2483)
3234  0 continue;
3235    }
3236  0 java.net.URI uri = new java.net.URI(s);
3237  0 if (uri.getScheme().toLowerCase().startsWith("http"))
3238    {
3239  0 protocols.add(DataSourceType.URL);
3240  0 files.add(uri.toString());
3241    }
3242    else
3243    {
3244    // otherwise preserve old behaviour: catch all for file objects
3245  0 java.io.File file = new java.io.File(uri);
3246  0 protocols.add(DataSourceType.FILE);
3247  0 files.add(file.toString());
3248    }
3249    }
3250    }
3251   
3252  0 if (Cache.log.isDebugEnabled())
3253    {
3254  0 if (data == null || !added)
3255    {
3256   
3257  0 if (t.getTransferDataFlavors() != null
3258    && t.getTransferDataFlavors().length > 0)
3259    {
3260  0 Cache.log.debug(
3261    "Couldn't resolve drop data. Here are the supported flavors:");
3262  0 for (DataFlavor fl : t.getTransferDataFlavors())
3263    {
3264  0 Cache.log.debug(
3265    "Supported transfer dataflavor: " + fl.toString());
3266  0 Object df = t.getTransferData(fl);
3267  0 if (df != null)
3268    {
3269  0 Cache.log.debug("Retrieves: " + df);
3270    }
3271    else
3272    {
3273  0 Cache.log.debug("Retrieved nothing");
3274    }
3275    }
3276    }
3277    else
3278    {
3279  0 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3280    + t.toString());
3281    }
3282    }
3283    }
3284    }
3285  0 if (Platform.isWindowsAndNotJS())
3286    {
3287  0 Cache.log.debug("Scanning dropped content for Windows Link Files");
3288   
3289    // resolve any .lnk files in the file drop
3290  0 for (int f = 0; f < files.size(); f++)
3291    {
3292  0 String source = files.get(f).toString().toLowerCase();
3293  0 if (protocols.get(f).equals(DataSourceType.FILE)
3294    && (source.endsWith(".lnk") || source.endsWith(".url")
3295    || source.endsWith(".site")))
3296    {
3297  0 try
3298    {
3299  0 Object obj = files.get(f);
3300  0 File lf = (obj instanceof File ? (File) obj
3301    : new File((String) obj));
3302    // process link file to get a URL
3303  0 Cache.log.debug("Found potential link file: " + lf);
3304  0 WindowsShortcut wscfile = new WindowsShortcut(lf);
3305  0 String fullname = wscfile.getRealFilename();
3306  0 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3307  0 files.set(f, fullname);
3308  0 Cache.log.debug("Parsed real filename " + fullname
3309    + " to extract protocol: " + protocols.get(f));
3310    } catch (Exception ex)
3311    {
3312  0 Cache.log.error(
3313    "Couldn't parse " + files.get(f) + " as a link file.",
3314    ex);
3315    }
3316    }
3317    }
3318    }
3319    }
3320   
3321    /**
3322    * Sets the Preferences property for experimental features to True or False
3323    * depending on the state of the controlling menu item
3324    */
 
3325  0 toggle @Override
3326    protected void showExperimental_actionPerformed(boolean selected)
3327    {
3328  0 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3329    }
3330   
3331    /**
3332    * Answers a (possibly empty) list of any structure viewer frames (currently for
3333    * either Jmol or Chimera) which are currently open. This may optionally be
3334    * restricted to viewers of a specified class, or viewers linked to a specified
3335    * alignment panel.
3336    *
3337    * @param apanel
3338    * if not null, only return viewers linked to this panel
3339    * @param structureViewerClass
3340    * if not null, only return viewers of this class
3341    * @return
3342    */
 
3343  1 toggle public List<StructureViewerBase> getStructureViewers(
3344    AlignmentPanel apanel,
3345    Class<? extends StructureViewerBase> structureViewerClass)
3346    {
3347  1 List<StructureViewerBase> result = new ArrayList<>();
3348  1 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3349   
3350  1 for (JInternalFrame frame : frames)
3351    {
3352  2 if (frame instanceof StructureViewerBase)
3353    {
3354  0 if (structureViewerClass == null
3355    || structureViewerClass.isInstance(frame))
3356    {
3357  0 if (apanel == null
3358    || ((StructureViewerBase) frame).isLinkedWith(apanel))
3359    {
3360  0 result.add((StructureViewerBase) frame);
3361    }
3362    }
3363    }
3364    }
3365  1 return result;
3366    }
3367    }