2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
23 import java.awt.BorderLayout;
24 import java.awt.Color;
25 import java.awt.Component;
26 import java.awt.Dimension;
27 import java.awt.FontMetrics;
28 import java.awt.Graphics;
29 import java.awt.Graphics2D;
30 import java.awt.GridLayout;
31 import java.awt.Point;
32 import java.awt.Rectangle;
33 import java.awt.Toolkit;
34 import java.awt.Window;
35 import java.awt.datatransfer.Clipboard;
36 import java.awt.datatransfer.ClipboardOwner;
37 import java.awt.datatransfer.DataFlavor;
38 import java.awt.datatransfer.Transferable;
39 import java.awt.dnd.DnDConstants;
40 import java.awt.dnd.DropTargetDragEvent;
41 import java.awt.dnd.DropTargetDropEvent;
42 import java.awt.dnd.DropTargetEvent;
43 import java.awt.dnd.DropTargetListener;
44 import java.awt.event.ActionEvent;
45 import java.awt.event.ActionListener;
46 import java.awt.event.InputEvent;
47 import java.awt.event.KeyEvent;
48 import java.awt.event.MouseAdapter;
49 import java.awt.event.MouseEvent;
50 import java.awt.event.WindowAdapter;
51 import java.awt.event.WindowEvent;
52 import java.awt.geom.AffineTransform;
53 import java.beans.PropertyChangeEvent;
54 import java.beans.PropertyChangeListener;
55 import java.beans.PropertyVetoException;
57 import java.io.FileWriter;
58 import java.io.IOException;
59 import java.lang.reflect.Field;
61 import java.util.ArrayList;
62 import java.util.Arrays;
63 import java.util.HashMap;
64 import java.util.Hashtable;
65 import java.util.List;
66 import java.util.ListIterator;
67 import java.util.Locale;
69 import java.util.Vector;
70 import java.util.concurrent.ExecutorService;
71 import java.util.concurrent.Executors;
72 import java.util.concurrent.Semaphore;
74 import javax.swing.AbstractAction;
75 import javax.swing.Action;
76 import javax.swing.ActionMap;
77 import javax.swing.Box;
78 import javax.swing.BoxLayout;
79 import javax.swing.DefaultDesktopManager;
80 import javax.swing.DesktopManager;
81 import javax.swing.InputMap;
82 import javax.swing.JButton;
83 import javax.swing.JCheckBox;
84 import javax.swing.JComboBox;
85 import javax.swing.JComponent;
86 import javax.swing.JDesktopPane;
87 import javax.swing.JFrame;
88 import javax.swing.JInternalFrame;
89 import javax.swing.JLabel;
90 import javax.swing.JMenuItem;
91 import javax.swing.JOptionPane;
92 import javax.swing.JPanel;
93 import javax.swing.JPopupMenu;
94 import javax.swing.JProgressBar;
95 import javax.swing.JScrollPane;
96 import javax.swing.JTextArea;
97 import javax.swing.JTextField;
98 import javax.swing.JTextPane;
99 import javax.swing.KeyStroke;
100 import javax.swing.SwingUtilities;
101 import javax.swing.WindowConstants;
102 import javax.swing.event.HyperlinkEvent;
103 import javax.swing.event.HyperlinkEvent.EventType;
104 import javax.swing.event.InternalFrameAdapter;
105 import javax.swing.event.InternalFrameEvent;
106 import javax.swing.text.JTextComponent;
108 import org.stackoverflowusers.file.WindowsShortcut;
110 import jalview.api.AlignViewportI;
111 import jalview.api.AlignmentViewPanel;
112 import jalview.api.structures.JalviewStructureDisplayI;
113 import jalview.bin.Cache;
114 import jalview.bin.Jalview;
115 import jalview.bin.Jalview.ExitCode;
116 import jalview.bin.argparser.Arg;
117 import jalview.bin.groovy.JalviewObject;
118 import jalview.bin.groovy.JalviewObjectI;
119 import jalview.datamodel.Alignment;
120 import jalview.datamodel.HiddenColumns;
121 import jalview.datamodel.Sequence;
122 import jalview.datamodel.SequenceI;
123 import jalview.gui.ImageExporter.ImageWriterI;
124 import jalview.gui.QuitHandler.QResponse;
125 import jalview.io.BackupFiles;
126 import jalview.io.DataSourceType;
127 import jalview.io.FileFormat;
128 import jalview.io.FileFormatException;
129 import jalview.io.FileFormatI;
130 import jalview.io.FileFormats;
131 import jalview.io.FileLoader;
132 import jalview.io.FormatAdapter;
133 import jalview.io.IdentifyFile;
134 import jalview.io.JalviewFileChooser;
135 import jalview.io.JalviewFileView;
136 import jalview.io.exceptions.ImageOutputException;
137 import jalview.jbgui.GSplitFrame;
138 import jalview.jbgui.GStructureViewer;
139 import jalview.project.Jalview2XML;
140 import jalview.structure.StructureSelectionManager;
141 import jalview.urls.IdOrgSettings;
142 import jalview.util.BrowserLauncher;
143 import jalview.util.ChannelProperties;
144 import jalview.util.IdUtils;
145 import jalview.util.IdUtils.IdType;
146 import jalview.util.ImageMaker.TYPE;
147 import jalview.util.LaunchUtils;
148 import jalview.util.MessageManager;
149 import jalview.util.Platform;
150 import jalview.util.ShortcutKeyMaskExWrapper;
151 import jalview.util.UrlConstants;
152 import jalview.viewmodel.AlignmentViewport;
153 import jalview.ws.params.ParamManager;
154 import jalview.ws.utils.UrlDownloadClient;
161 * @version $Revision: 1.155 $
163 public class Desktop extends jalview.jbgui.GDesktop
164 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
165 jalview.api.StructureSelectionManagerProvider, JalviewObjectI
167 private static final String CITATION;
170 URL bg_logo_url = ChannelProperties.getImageURL(
171 "bg_logo." + String.valueOf(SplashScreen.logoSize));
172 URL uod_logo_url = ChannelProperties.getImageURL(
173 "uod_banner." + String.valueOf(SplashScreen.logoSize));
174 boolean logo = (bg_logo_url != null || uod_logo_url != null);
175 StringBuilder sb = new StringBuilder();
177 "<br><br>Jalview is free software released under GPLv3.<br><br>Development is managed by The Barton Group, University of Dundee, Scotland, UK.");
182 sb.append(bg_logo_url == null ? ""
183 : "<img alt=\"Barton Group logo\" src=\""
184 + bg_logo_url.toString() + "\">");
185 sb.append(uod_logo_url == null ? ""
186 : " <img alt=\"University of Dundee shield\" src=\""
187 + uod_logo_url.toString() + "\">");
189 "<br><br>For help, see <a href=\"https://www.jalview.org/faq\">www.jalview.org/faq</a> and join <a href=\"https://discourse.jalview.org\">discourse.jalview.org</a>");
190 sb.append("<br><br>If you use Jalview, please cite:"
191 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
192 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
193 + "<br>Bioinformatics <a href=\"https://doi.org/10.1093/bioinformatics/btp033\">doi: 10.1093/bioinformatics/btp033</a>");
194 CITATION = sb.toString();
197 private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)";
199 private static int DEFAULT_MIN_WIDTH = 300;
201 private static int DEFAULT_MIN_HEIGHT = 250;
203 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
205 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
207 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
209 public static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
211 public static HashMap<String, FileWriter> savingFiles = new HashMap<String, FileWriter>();
213 private static int DRAG_MODE = JDesktopPane.OUTLINE_DRAG_MODE;
215 public static void setLiveDragMode(boolean b)
217 DRAG_MODE = b ? JDesktopPane.LIVE_DRAG_MODE
218 : JDesktopPane.OUTLINE_DRAG_MODE;
220 desktop.setDragMode(DRAG_MODE);
223 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
225 public static boolean nosplash = false;
228 * news reader - null if it was never started.
230 private BlogReader jvnews = null;
232 private File projectFile;
236 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
238 public void addJalviewPropertyChangeListener(
239 PropertyChangeListener listener)
241 changeSupport.addJalviewPropertyChangeListener(listener);
245 * @param propertyName
247 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
248 * java.beans.PropertyChangeListener)
250 public void addJalviewPropertyChangeListener(String propertyName,
251 PropertyChangeListener listener)
253 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
257 * @param propertyName
259 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
260 * java.beans.PropertyChangeListener)
262 public void removeJalviewPropertyChangeListener(String propertyName,
263 PropertyChangeListener listener)
265 changeSupport.removeJalviewPropertyChangeListener(propertyName,
269 /** Singleton Desktop instance */
270 public static Desktop instance;
272 public static MyDesktopPane desktop;
274 public static MyDesktopPane getDesktop()
276 // BH 2018 could use currentThread() here as a reference to a
277 // Hashtable<Thread, MyDesktopPane> in JavaScript
281 static int openFrameCount = 0;
283 static final int xOffset = 30;
285 static final int yOffset = 30;
287 public static jalview.ws.jws1.Discoverer discoverer;
289 public static Object[] jalviewClipboard;
291 public static boolean internalCopy = false;
293 static int fileLoadingCount = 0;
295 class MyDesktopManager implements DesktopManager
298 private DesktopManager delegate;
300 public MyDesktopManager(DesktopManager delegate)
302 this.delegate = delegate;
306 public void activateFrame(JInternalFrame f)
310 delegate.activateFrame(f);
311 } catch (NullPointerException npe)
313 Point p = getMousePosition();
314 instance.showPasteMenu(p.x, p.y);
319 public void beginDraggingFrame(JComponent f)
321 delegate.beginDraggingFrame(f);
325 public void beginResizingFrame(JComponent f, int direction)
327 delegate.beginResizingFrame(f, direction);
331 public void closeFrame(JInternalFrame f)
333 delegate.closeFrame(f);
337 public void deactivateFrame(JInternalFrame f)
339 delegate.deactivateFrame(f);
343 public void deiconifyFrame(JInternalFrame f)
345 delegate.deiconifyFrame(f);
349 public void dragFrame(JComponent f, int newX, int newY)
355 delegate.dragFrame(f, newX, newY);
359 public void endDraggingFrame(JComponent f)
361 delegate.endDraggingFrame(f);
366 public void endResizingFrame(JComponent f)
368 delegate.endResizingFrame(f);
373 public void iconifyFrame(JInternalFrame f)
375 delegate.iconifyFrame(f);
379 public void maximizeFrame(JInternalFrame f)
381 delegate.maximizeFrame(f);
385 public void minimizeFrame(JInternalFrame f)
387 delegate.minimizeFrame(f);
391 public void openFrame(JInternalFrame f)
393 delegate.openFrame(f);
397 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
404 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
408 public void setBoundsForFrame(JComponent f, int newX, int newY,
409 int newWidth, int newHeight)
411 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
414 // All other methods, simply delegate
419 * Creates a new Desktop object.
425 * A note to implementors. It is ESSENTIAL that any activities that might
426 * block are spawned off as threads rather than waited for during this
431 doConfigureStructurePrefs();
432 setTitle(ChannelProperties.getProperty("app_name") + " "
433 + Cache.getProperty("VERSION"));
436 * Set taskbar "grouped windows" name for linux desktops (works in GNOME and
437 * KDE). This uses sun.awt.X11.XToolkit.awtAppClassName which is not
438 * officially documented or guaranteed to exist, so we access it via
439 * reflection. There appear to be unfathomable criteria about what this
440 * string can contain, and it if doesn't meet those criteria then "java"
441 * (KDE) or "jalview-bin-Jalview" (GNOME) is used. "Jalview", "Jalview
442 * Develop" and "Jalview Test" seem okay, but "Jalview non-release" does
443 * not. The reflection access may generate a warning: WARNING: An illegal
444 * reflective access operation has occurred WARNING: Illegal reflective
445 * access by jalview.gui.Desktop () to field
446 * sun.awt.X11.XToolkit.awtAppClassName which I don't think can be avoided.
448 if (Platform.isLinux())
450 if (LaunchUtils.getJavaVersion() >= 11)
453 * Send this message to stderr as the warning that follows (due to
454 * reflection) also goes to stderr.
456 jalview.bin.Console.errPrintln(
457 "Linux platform only! You may have the following warning next: \"WARNING: An illegal reflective access operation has occurred\"\nThis is expected and cannot be avoided, sorry about that.");
459 final String awtAppClassName = "awtAppClassName";
462 Toolkit xToolkit = Toolkit.getDefaultToolkit();
463 Field[] declaredFields = xToolkit.getClass().getDeclaredFields();
464 Field awtAppClassNameField = null;
466 if (Arrays.stream(declaredFields)
467 .anyMatch(f -> f.getName().equals(awtAppClassName)))
469 awtAppClassNameField = xToolkit.getClass()
470 .getDeclaredField(awtAppClassName);
473 String title = ChannelProperties.getProperty("app_name");
474 if (awtAppClassNameField != null)
476 awtAppClassNameField.setAccessible(true);
477 awtAppClassNameField.set(xToolkit, title);
482 .debug("XToolkit: " + awtAppClassName + " not found");
484 } catch (Exception e)
486 jalview.bin.Console.debug("Error setting " + awtAppClassName);
487 jalview.bin.Console.trace(Cache.getStackTraceString(e));
491 setIconImages(ChannelProperties.getIconList());
493 // override quit handling when GUI OS close [X] button pressed
494 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
495 addWindowListener(new WindowAdapter()
498 public void windowClosing(WindowEvent ev)
500 QuitHandler.QResponse ret = desktopQuit(true, true); // ui, disposeFlag
504 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE", false);
506 boolean showjconsole = Cache.getArgCacheDefault(Arg.JAVACONSOLE,
507 "SHOW_JAVA_CONSOLE", false);
509 // start dialogue queue for single dialogues
512 if (!Platform.isJS())
519 Desktop.instance.acquireDialogQueue();
521 jconsole = new Console(this);
522 jconsole.setHeader(Cache.getVersionDetailsForConsole());
523 showConsole(showjconsole);
525 Desktop.instance.releaseDialogQueue();
528 desktop = new MyDesktopPane(selmemusage);
530 showMemusage.setSelected(selmemusage);
531 desktop.setBackground(Color.white);
533 getContentPane().setLayout(new BorderLayout());
534 // alternate config - have scrollbars - see notes in JAL-153
535 // JScrollPane sp = new JScrollPane();
536 // sp.getViewport().setView(desktop);
537 // getContentPane().add(sp, BorderLayout.CENTER);
539 // BH 2018 - just an experiment to try unclipped JInternalFrames.
542 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
545 getContentPane().add(desktop, BorderLayout.CENTER);
546 desktop.setDragMode(DRAG_MODE);
548 // This line prevents Windows Look&Feel resizing all new windows to maximum
549 // if previous window was maximised
550 desktop.setDesktopManager(new MyDesktopManager(
551 Platform.isJS() ? desktop.getDesktopManager()
552 : new DefaultDesktopManager()));
554 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
555 : Platform.isAMacAndNotJS()
556 ? new AquaInternalFrameManager(
557 desktop.getDesktopManager())
558 : desktop.getDesktopManager())));
561 Rectangle dims = getLastKnownDimensions("");
568 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
569 int xPos = Math.max(5, (screenSize.width - 900) / 2);
570 int yPos = Math.max(5, (screenSize.height - 650) / 2);
571 setBounds(xPos, yPos, 900, 650);
574 if (!Platform.isJS())
581 showNews.setVisible(false);
583 experimentalFeatures.setSelected(showExperimental());
585 getIdentifiersOrgData();
589 // Spawn a thread that shows the splashscreen
592 SwingUtilities.invokeLater(new Runnable()
597 new SplashScreen(true);
602 // Thread off a new instance of the file chooser - this reduces the time
603 // it takes to open it later on.
604 new Thread(new Runnable()
609 jalview.bin.Console.debug("Filechooser init thread started.");
610 String fileFormat = FileLoader.getUseDefaultFileFormat()
611 ? Cache.getProperty("DEFAULT_FILE_FORMAT")
613 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
615 jalview.bin.Console.debug("Filechooser init thread finished.");
618 // Add the service change listener
619 changeSupport.addJalviewPropertyChangeListener("services",
620 new PropertyChangeListener()
624 public void propertyChange(PropertyChangeEvent evt)
627 .debug("Firing service changed event for "
628 + evt.getNewValue());
629 JalviewServicesChanged(evt);
634 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
637 this.addMouseListener(ma = new MouseAdapter()
640 public void mousePressed(MouseEvent evt)
642 if (evt.isPopupTrigger()) // Mac
644 showPasteMenu(evt.getX(), evt.getY());
649 public void mouseReleased(MouseEvent evt)
651 if (evt.isPopupTrigger()) // Windows
653 showPasteMenu(evt.getX(), evt.getY());
657 desktop.addMouseListener(ma);
661 // used for jalviewjsTest
662 jalview.bin.Console.info("JALVIEWJS: CREATED DESKTOP");
668 * Answers true if user preferences to enable experimental features is True
673 public boolean showExperimental()
675 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
676 Boolean.FALSE.toString());
677 return Boolean.valueOf(experimental).booleanValue();
680 public void doConfigureStructurePrefs()
682 // configure services
683 StructureSelectionManager ssm = StructureSelectionManager
684 .getStructureSelectionManager(this);
685 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
687 ssm.setAddTempFacAnnot(
688 Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
689 ssm.setProcessSecondaryStructure(
690 Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
691 // JAL-3915 - RNAView is no longer an option so this has no effect
692 ssm.setSecStructServices(
693 Cache.getDefault(Preferences.USE_RNAVIEW, false));
697 ssm.setAddTempFacAnnot(false);
698 ssm.setProcessSecondaryStructure(false);
699 ssm.setSecStructServices(false);
703 public void checkForNews()
705 final Desktop me = this;
706 // Thread off the news reader, in case there are connection problems.
707 new Thread(new Runnable()
712 jalview.bin.Console.debug("Starting news thread.");
713 jvnews = new BlogReader(me);
714 showNews.setVisible(true);
715 jalview.bin.Console.debug("Completed news thread.");
720 public void getIdentifiersOrgData()
722 if (Cache.getProperty("NOIDENTIFIERSSERVICE") == null)
723 {// Thread off the identifiers fetcher
724 new Thread(new Runnable()
730 .debug("Downloading data from identifiers.org");
733 UrlDownloadClient.download(IdOrgSettings.getUrl(),
734 IdOrgSettings.getDownloadLocation());
735 } catch (IOException e)
738 .debug("Exception downloading identifiers.org data"
748 protected void showNews_actionPerformed(ActionEvent e)
750 showNews(showNews.isSelected());
753 void showNews(boolean visible)
755 jalview.bin.Console.debug((visible ? "Showing" : "Hiding") + " news.");
756 showNews.setSelected(visible);
757 if (visible && !jvnews.isVisible())
759 new Thread(new Runnable()
764 long progressId = IdUtils.newId(IdType.PROGRESS);
765 Desktop.instance.setProgressBar(
766 MessageManager.getString("status.refreshing_news"),
768 jvnews.refreshNews();
769 Desktop.instance.setProgressBar(null, progressId);
777 * recover the last known dimensions for a jalview window
780 * - empty string is desktop, all other windows have unique prefix
781 * @return null or last known dimensions scaled to current geometry (if last
782 * window geom was known)
784 Rectangle getLastKnownDimensions(String windowName)
786 // TODO: lock aspect ratio for scaling desktop Bug #0058199
787 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
788 String x = Cache.getProperty(windowName + "SCREEN_X");
789 String y = Cache.getProperty(windowName + "SCREEN_Y");
790 String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
791 String height = Cache.getProperty(windowName + "SCREEN_HEIGHT");
792 if ((x != null) && (y != null) && (width != null) && (height != null))
794 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
795 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
796 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
798 // attempt #1 - try to cope with change in screen geometry - this
799 // version doesn't preserve original jv aspect ratio.
800 // take ratio of current screen size vs original screen size.
801 double sw = ((1f * screenSize.width) / (1f * Integer
802 .parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
803 double sh = ((1f * screenSize.height) / (1f * Integer
804 .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
805 // rescale the bounds depending upon the current screen geometry.
806 ix = (int) (ix * sw);
807 iw = (int) (iw * sw);
808 iy = (int) (iy * sh);
809 ih = (int) (ih * sh);
810 if (ix >= screenSize.width)
812 jalview.bin.Console.debug(
813 "Window geometry location recall error: shifting horizontal to within screenbounds.");
814 ix = ix % screenSize.width;
816 if (iy >= screenSize.height)
818 jalview.bin.Console.debug(
819 "Window geometry location recall error: shifting vertical to within screenbounds.");
820 iy = iy % screenSize.height;
822 jalview.bin.Console.debug(
823 "Got last known dimensions for " + windowName + ": x:" + ix
824 + " y:" + iy + " width:" + iw + " height:" + ih);
826 // return dimensions for new instance
827 return new Rectangle(ix, iy, iw, ih);
832 void showPasteMenu(int x, int y)
834 JPopupMenu popup = new JPopupMenu();
835 JMenuItem item = new JMenuItem(
836 MessageManager.getString("label.paste_new_window"));
837 item.addActionListener(new ActionListener()
840 public void actionPerformed(ActionEvent evt)
847 popup.show(this, x, y);
852 // quick patch for JAL-4150 - needs some more work and test coverage
853 // TODO - unify below and AlignFrame.paste()
854 // TODO - write tests and fix AlignFrame.paste() which doesn't track if
855 // clipboard has come from a different alignment window than the one where
856 // paste has been called! JAL-4151
858 if (Desktop.jalviewClipboard != null)
860 // The clipboard was filled from within Jalview, we must use the
862 // And dataset from the copied alignment
863 SequenceI[] newseq = (SequenceI[]) Desktop.jalviewClipboard[0];
864 // be doubly sure that we create *new* sequence objects.
865 SequenceI[] sequences = new SequenceI[newseq.length];
866 for (int i = 0; i < newseq.length; i++)
868 sequences[i] = new Sequence(newseq[i]);
870 Alignment alignment = new Alignment(sequences);
871 // dataset is inherited
872 alignment.setDataset((Alignment) Desktop.jalviewClipboard[1]);
873 AlignFrame af = new AlignFrame(alignment, AlignFrame.DEFAULT_WIDTH,
874 AlignFrame.DEFAULT_HEIGHT);
875 String newtitle = new String("Copied sequences");
877 if (Desktop.jalviewClipboard[2] != null)
879 HiddenColumns hc = (HiddenColumns) Desktop.jalviewClipboard[2];
880 af.viewport.setHiddenColumns(hc);
883 Desktop.addInternalFrame(af, newtitle, AlignFrame.DEFAULT_WIDTH,
884 AlignFrame.DEFAULT_HEIGHT);
891 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
892 Transferable contents = c.getContents(this);
894 if (contents != null)
896 String file = (String) contents
897 .getTransferData(DataFlavor.stringFlavor);
899 FileFormatI format = new IdentifyFile().identify(file,
900 DataSourceType.PASTE);
902 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
905 } catch (Exception ex)
907 jalview.bin.Console.outPrintln(
908 "Unable to paste alignment from system clipboard:\n" + ex);
914 * Adds and opens the given frame to the desktop
925 public static synchronized void addInternalFrame(
926 final JInternalFrame frame, String title, int w, int h)
928 addInternalFrame(frame, title, true, w, h, true, false);
932 * Add an internal frame to the Jalview desktop
939 * When true, display frame immediately, otherwise, caller must call
940 * setVisible themselves.
946 public static synchronized void addInternalFrame(
947 final JInternalFrame frame, String title, boolean makeVisible,
950 addInternalFrame(frame, title, makeVisible, w, h, true, false);
954 * Add an internal frame to the Jalview desktop and make it visible
967 public static synchronized void addInternalFrame(
968 final JInternalFrame frame, String title, int w, int h,
971 addInternalFrame(frame, title, true, w, h, resizable, false);
975 * Add an internal frame to the Jalview desktop
982 * When true, display frame immediately, otherwise, caller must call
983 * setVisible themselves.
990 * @param ignoreMinSize
991 * Do not set the default minimum size for frame
993 public static synchronized void addInternalFrame(
994 final JInternalFrame frame, String title, boolean makeVisible,
995 int w, int h, boolean resizable, boolean ignoreMinSize)
998 // TODO: allow callers to determine X and Y position of frame (eg. via
1000 // TODO: consider fixing method to update entries in the window submenu with
1001 // the current window title
1003 frame.setTitle(title);
1004 if (frame.getWidth() < 1 || frame.getHeight() < 1)
1006 frame.setSize(w, h);
1008 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
1009 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
1010 // IF JALVIEW IS RUNNING HEADLESS
1011 // ///////////////////////////////////////////////
1012 if (instance == null || (System.getProperty("java.awt.headless") != null
1013 && System.getProperty("java.awt.headless").equals("true")))
1022 frame.setMinimumSize(
1023 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
1025 // Set default dimension for Alignment Frame window.
1026 // The Alignment Frame window could be added from a number of places,
1028 // I did this here in order not to miss out on any Alignment frame.
1029 if (frame instanceof AlignFrame)
1031 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
1032 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
1036 frame.setVisible(makeVisible);
1037 frame.setClosable(true);
1038 frame.setResizable(resizable);
1039 frame.setMaximizable(resizable);
1040 frame.setIconifiable(resizable);
1041 frame.setOpaque(Platform.isJS());
1043 if (frame.getX() < 1 && frame.getY() < 1)
1045 frame.setLocation(xOffset * openFrameCount,
1046 yOffset * ((openFrameCount - 1) % 10) + yOffset);
1050 * add an entry for the new frame in the Window menu (and remove it when the
1053 final JMenuItem menuItem = new JMenuItem(title);
1054 frame.addInternalFrameListener(new InternalFrameAdapter()
1057 public void internalFrameActivated(InternalFrameEvent evt)
1059 JInternalFrame itf = desktop.getSelectedFrame();
1062 if (itf instanceof AlignFrame)
1064 Jalview.getInstance().setCurrentAlignFrame((AlignFrame) itf);
1071 public void internalFrameClosed(InternalFrameEvent evt)
1073 PaintRefresher.RemoveComponent(frame);
1076 * defensive check to prevent frames being added half off the window
1078 if (openFrameCount > 0)
1084 * ensure no reference to alignFrame retained by menu item listener
1086 if (menuItem.getActionListeners().length > 0)
1088 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
1090 windowMenu.remove(menuItem);
1094 menuItem.addActionListener(new ActionListener()
1097 public void actionPerformed(ActionEvent e)
1101 frame.setSelected(true);
1102 frame.setIcon(false);
1103 } catch (java.beans.PropertyVetoException ex)
1110 setKeyBindings(frame);
1112 // Since the latest FlatLaf patch, we occasionally have problems showing
1113 // structureViewer frames...
1115 boolean shown = false;
1116 Exception last = null;
1123 } catch (IllegalArgumentException iaex)
1127 jalview.bin.Console.info("Squashed IllegalArgument Exception ("
1128 + tries + " left) for " + frame.getTitle(), iaex);
1132 } catch (InterruptedException iex)
1137 } while (!shown && tries > 0);
1140 jalview.bin.Console.error(
1141 "Serious Problem whilst showing window " + frame.getTitle(),
1145 windowMenu.add(menuItem);
1150 frame.setSelected(true);
1151 frame.requestFocus();
1152 } catch (java.beans.PropertyVetoException ve)
1154 } catch (java.lang.ClassCastException cex)
1156 jalview.bin.Console.warn(
1157 "Squashed a possible GUI implementation error. If you can recreate this, please look at https://issues.jalview.org/browse/JAL-869",
1163 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
1168 private static void setKeyBindings(JInternalFrame frame)
1170 @SuppressWarnings("serial")
1171 final Action closeAction = new AbstractAction()
1174 public void actionPerformed(ActionEvent e)
1181 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1183 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1184 InputEvent.CTRL_DOWN_MASK);
1185 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1186 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
1188 InputMap inputMap = frame
1189 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1190 String ctrlW = ctrlWKey.toString();
1191 inputMap.put(ctrlWKey, ctrlW);
1192 inputMap.put(cmdWKey, ctrlW);
1194 ActionMap actionMap = frame.getActionMap();
1195 actionMap.put(ctrlW, closeAction);
1199 public void lostOwnership(Clipboard clipboard, Transferable contents)
1203 Desktop.jalviewClipboard = null;
1206 internalCopy = false;
1210 public void dragEnter(DropTargetDragEvent evt)
1215 public void dragExit(DropTargetEvent evt)
1220 public void dragOver(DropTargetDragEvent evt)
1225 public void dropActionChanged(DropTargetDragEvent evt)
1236 public void drop(DropTargetDropEvent evt)
1238 boolean success = true;
1239 // JAL-1552 - acceptDrop required before getTransferable call for
1240 // Java's Transferable for native dnd
1241 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1242 Transferable t = evt.getTransferable();
1243 List<Object> files = new ArrayList<>();
1244 List<DataSourceType> protocols = new ArrayList<>();
1248 Desktop.transferFromDropTarget(files, protocols, evt, t);
1249 } catch (Exception e)
1251 e.printStackTrace();
1259 for (int i = 0; i < files.size(); i++)
1261 // BH 2018 File or String
1262 Object file = files.get(i);
1263 String fileName = file.toString();
1264 DataSourceType protocol = (protocols == null)
1265 ? DataSourceType.FILE
1267 FileFormatI format = null;
1269 if (fileName.endsWith(".jar"))
1271 format = FileFormat.Jalview;
1276 format = new IdentifyFile().identify(file, protocol);
1278 if (file instanceof File)
1280 Platform.cacheFileData((File) file);
1282 new FileLoader().LoadFile(null, file, protocol, format);
1285 } catch (Exception ex)
1290 evt.dropComplete(success); // need this to ensure input focus is properly
1291 // transfered to any new windows created
1301 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1303 String fileFormat = FileLoader.getUseDefaultFileFormat()
1304 ? Cache.getProperty("DEFAULT_FILE_FORMAT")
1306 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1307 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1308 BackupFiles.getEnabled());
1310 chooser.setFileView(new JalviewFileView());
1311 chooser.setDialogTitle(
1312 MessageManager.getString("label.open_local_file"));
1313 chooser.setToolTipText(MessageManager.getString("action.open"));
1315 chooser.setResponseHandler(0, () -> {
1316 File selectedFile = chooser.getSelectedFile();
1317 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1319 FileFormatI format = chooser.getSelectedFormat();
1322 * Call IdentifyFile to verify the file contains what its extension implies.
1323 * Skip this step for dynamically added file formats, because IdentifyFile does
1324 * not know how to recognise them.
1326 if (FileFormats.getInstance().isIdentifiable(format))
1330 format = new IdentifyFile().identify(selectedFile,
1331 DataSourceType.FILE);
1332 } catch (FileFormatException e)
1334 // format = null; //??
1338 new FileLoader().LoadFile(viewport, selectedFile, DataSourceType.FILE,
1341 chooser.showOpenDialog(this);
1345 * Shows a dialog for input of a URL at which to retrieve alignment data
1350 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1352 // This construct allows us to have a wider textfield
1354 JLabel label = new JLabel(
1355 MessageManager.getString("label.input_file_url"));
1357 JPanel panel = new JPanel(new GridLayout(2, 1));
1361 * the URL to fetch is input in Java: an editable combobox with history JS:
1362 * (pending JAL-3038) a plain text field
1365 String urlBase = "https://www.";
1366 if (Platform.isJS())
1368 history = new JTextField(urlBase, 35);
1377 JComboBox<String> asCombo = new JComboBox<>();
1378 asCombo.setPreferredSize(new Dimension(400, 20));
1379 asCombo.setEditable(true);
1380 asCombo.addItem(urlBase);
1381 String historyItems = Cache.getProperty("RECENT_URL");
1382 if (historyItems != null)
1384 for (String token : historyItems.split("\\t"))
1386 asCombo.addItem(token);
1393 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1394 MessageManager.getString("action.cancel") };
1395 Runnable action = () -> {
1396 @SuppressWarnings("unchecked")
1397 String url = (history instanceof JTextField
1398 ? ((JTextField) history).getText()
1399 : ((JComboBox<String>) history).getEditor().getItem()
1400 .toString().trim());
1402 if (url.toLowerCase(Locale.ROOT).endsWith(".jar"))
1404 if (viewport != null)
1406 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1407 FileFormat.Jalview);
1411 new FileLoader().LoadFile(url, DataSourceType.URL,
1412 FileFormat.Jalview);
1417 FileFormatI format = null;
1420 format = new IdentifyFile().identify(url, DataSourceType.URL);
1421 } catch (FileFormatException e)
1423 // TODO revise error handling, distinguish between
1424 // URL not found and response not valid
1429 String msg = MessageManager.formatMessage("label.couldnt_locate",
1431 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1432 MessageManager.getString("label.url_not_found"),
1433 JvOptionPane.WARNING_MESSAGE);
1437 if (viewport != null)
1439 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1444 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1448 String dialogOption = MessageManager
1449 .getString("label.input_alignment_from_url");
1450 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1451 .showInternalDialog(panel, dialogOption,
1452 JvOptionPane.YES_NO_CANCEL_OPTION,
1453 JvOptionPane.PLAIN_MESSAGE, null, options,
1454 MessageManager.getString("action.ok"));
1458 * Opens the CutAndPaste window for the user to paste an alignment in to
1461 * - if not null, the pasted alignment is added to the current
1462 * alignment; if null, to a new alignment window
1465 public void inputTextboxMenuItem_actionPerformed(
1466 AlignmentViewPanel viewPanel)
1468 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1469 cap.setForInput(viewPanel);
1470 Desktop.addInternalFrame(cap,
1471 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1476 * Check with user and saving files before actually quitting
1478 public void desktopQuit()
1480 desktopQuit(true, false);
1484 * close everything, stash window geometries, and shut down all associated
1488 * - sets the dispose on close flag - JVM may terminate when set
1489 * @param terminateJvm
1490 * - quit with prejudice - stops the JVM.
1492 public void quitTheDesktop(boolean dispose, boolean terminateJvm)
1494 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1495 Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1496 Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
1497 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1498 getWidth(), getHeight()));
1500 if (jconsole != null)
1502 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1503 jconsole.stopConsole();
1508 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1511 // Frames should all close automatically. Keeping external
1512 // viewers open should already be decided by user.
1513 closeAll_actionPerformed(null);
1515 if (dialogExecutor != null)
1517 dialogExecutor.shutdownNow();
1520 if (groovyConsole != null)
1522 // suppress a possible repeat prompt to save script
1523 groovyConsole.setDirty(false);
1524 groovyConsole.exit();
1529 // note that shutdown hook will not be run
1530 jalview.bin.Console.debug("Force Quit selected by user");
1531 Runtime.getRuntime().halt(0);
1534 jalview.bin.Console.debug("Quit selected by user");
1537 instance.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
1538 // instance.dispose();
1542 public QuitHandler.QResponse desktopQuit(boolean ui, boolean disposeFlag)
1544 final Runnable doDesktopQuit = () -> {
1546 // FIRST !! check for aborted quit
1547 if (QuitHandler.quitCancelled())
1550 .debug("Quit was cancelled - Desktop aborting quit");
1554 // Proceed with quitting
1555 quitTheDesktop(disposeFlag,
1556 QuitHandler.gotQuitResponse() == QResponse.FORCE_QUIT);
1561 return QuitHandler.getQuitResponse(ui, doDesktopQuit, doDesktopQuit,
1562 QuitHandler.defaultCancelQuit);
1566 * Exits the program and the JVM.
1568 * Don't call this directly
1570 * - use desktopQuit() above to tidy up first.
1572 * - use closeDesktop() to shutdown Jalview without shutting down the JVM
1578 // this will run the shutdownHook but QuitHandler.getQuitResponse() should
1579 // not run a second time if gotQuitResponse flag has been set (i.e. user
1580 // confirmed quit of some kind).
1581 Jalview.exit("Desktop exiting.", ExitCode.OK);
1584 private void storeLastKnownDimensions(String string, Rectangle jc)
1586 jalview.bin.Console.debug("Storing last known dimensions for " + string
1587 + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1588 + " height:" + jc.height);
1590 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1591 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1592 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1593 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1603 public void aboutMenuItem_actionPerformed(ActionEvent e)
1605 new Thread(new Runnable()
1610 new SplashScreen(false);
1616 * Returns the html text for the About screen, including any available version
1617 * number, build details, author details and citation reference, but without
1618 * the enclosing {@code html} tags
1622 public String getAboutMessage()
1624 StringBuilder message = new StringBuilder(1024);
1625 message.append("<div style=\"font-family: sans-serif;\">")
1626 .append("<h1><strong>Version: ")
1627 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1628 .append("<strong>Built: <em>")
1629 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1630 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1631 .append("</strong>");
1633 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1634 if (latestVersion.equals("Checking"))
1636 // JBP removed this message for 2.11: May be reinstated in future version
1637 // message.append("<br>...Checking latest version...</br>");
1639 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1641 boolean red = false;
1642 if (Cache.getProperty("VERSION").toLowerCase(Locale.ROOT)
1643 .indexOf("automated build") == -1)
1646 // Displayed when code version and jnlp version do not match and code
1647 // version is not a development build
1648 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1651 message.append("<br>!! Version ")
1652 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1653 .append(" is available for download from ")
1654 .append(Cache.getDefault("www.jalview.org",
1655 "https://www.jalview.org"))
1659 message.append("</div>");
1662 message.append("<br>Authors: ");
1663 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1664 message.append(CITATION);
1666 message.append("</div>");
1668 return message.toString();
1672 * Action on requesting Help documentation
1675 public void documentationMenuItem_actionPerformed()
1679 if (Platform.isJS())
1681 BrowserLauncher.openURL("https://www.jalview.org/help.html");
1690 Help.showHelpWindow();
1692 } catch (Exception ex)
1695 .errPrintln("Error opening help: " + ex.getMessage());
1700 public void closeAll_actionPerformed(ActionEvent e)
1702 // TODO show a progress bar while closing?
1703 JInternalFrame[] frames = desktop.getAllFrames();
1704 for (int i = 0; i < frames.length; i++)
1708 frames[i].setClosed(true);
1709 } catch (java.beans.PropertyVetoException ex)
1713 Jalview.getInstance().setCurrentAlignFrame(null);
1714 jalview.bin.Console.info("ALL CLOSED");
1717 * reset state of singleton objects as appropriate (clear down session state
1718 * when all windows are closed)
1720 StructureSelectionManager ssm = StructureSelectionManager
1721 .getStructureSelectionManager(this);
1728 public int structureViewersStillRunningCount()
1731 JInternalFrame[] frames = desktop.getAllFrames();
1732 for (int i = 0; i < frames.length; i++)
1734 if (frames[i] != null
1735 && frames[i] instanceof JalviewStructureDisplayI)
1737 if (((JalviewStructureDisplayI) frames[i]).stillRunning())
1745 public void raiseRelated_actionPerformed(ActionEvent e)
1747 reorderAssociatedWindows(false, false);
1751 public void minimizeAssociated_actionPerformed(ActionEvent e)
1753 reorderAssociatedWindows(true, false);
1756 void closeAssociatedWindows()
1758 reorderAssociatedWindows(false, true);
1764 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1768 protected void garbageCollect_actionPerformed(ActionEvent e)
1770 // We simply collect the garbage
1771 jalview.bin.Console.debug("Collecting garbage...");
1773 jalview.bin.Console.debug("Finished garbage collection.");
1779 * @see jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.
1783 protected void showMemusage_actionPerformed(ActionEvent e)
1785 desktop.showMemoryUsage(showMemusage.isSelected());
1792 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1796 protected void showConsole_actionPerformed(ActionEvent e)
1798 showConsole(showConsole.isSelected());
1801 Console jconsole = null;
1804 * control whether the java console is visible or not
1808 void showConsole(boolean selected)
1810 // TODO: decide if we should update properties file
1811 if (jconsole != null) // BH 2018
1813 showConsole.setSelected(selected);
1814 Cache.setProperty("SHOW_JAVA_CONSOLE",
1815 Boolean.valueOf(selected).toString());
1816 jconsole.setVisible(selected);
1820 void reorderAssociatedWindows(boolean minimize, boolean close)
1822 JInternalFrame[] frames = desktop.getAllFrames();
1823 if (frames == null || frames.length < 1)
1828 AlignmentViewport source = null, target = null;
1829 if (frames[0] instanceof AlignFrame)
1831 source = ((AlignFrame) frames[0]).getCurrentView();
1833 else if (frames[0] instanceof TreePanel)
1835 source = ((TreePanel) frames[0]).getViewPort();
1837 else if (frames[0] instanceof PCAPanel)
1839 source = ((PCAPanel) frames[0]).av;
1841 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1843 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1848 for (int i = 0; i < frames.length; i++)
1851 if (frames[i] == null)
1855 if (frames[i] instanceof AlignFrame)
1857 target = ((AlignFrame) frames[i]).getCurrentView();
1859 else if (frames[i] instanceof TreePanel)
1861 target = ((TreePanel) frames[i]).getViewPort();
1863 else if (frames[i] instanceof PCAPanel)
1865 target = ((PCAPanel) frames[i]).av;
1867 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1869 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1872 if (source == target)
1878 frames[i].setClosed(true);
1882 frames[i].setIcon(minimize);
1885 frames[i].toFront();
1889 } catch (java.beans.PropertyVetoException ex)
1904 protected void preferences_actionPerformed(ActionEvent e)
1906 Preferences.openPreferences();
1910 * Prompts the user to choose a file and then saves the Jalview state as a
1911 * Jalview project file
1914 public void saveState_actionPerformed()
1916 saveState_actionPerformed(false);
1919 public void saveState_actionPerformed(boolean saveAs)
1921 java.io.File projectFile = getProjectFile();
1922 // autoSave indicates we already have a file and don't need to ask
1923 boolean autoSave = projectFile != null && !saveAs
1924 && BackupFiles.getEnabled();
1926 // jalview.bin.Console.outPrintln("autoSave="+autoSave+",
1927 // projectFile='"+projectFile+"',
1928 // saveAs="+saveAs+", Backups
1929 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1931 boolean approveSave = false;
1934 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1937 chooser.setFileView(new JalviewFileView());
1938 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1940 int value = chooser.showSaveDialog(this);
1942 if (value == JalviewFileChooser.APPROVE_OPTION)
1944 projectFile = chooser.getSelectedFile();
1945 setProjectFile(projectFile);
1950 if (approveSave || autoSave)
1952 final Desktop me = this;
1953 final java.io.File chosenFile = projectFile;
1954 new Thread(new Runnable()
1959 // TODO: refactor to Jalview desktop session controller action.
1960 setProgressBar(MessageManager.formatMessage(
1961 "label.saving_jalview_project", new Object[]
1962 { chosenFile.getName() }),
1963 IdUtils.newId(IdType.PROGRESS, chosenFile));
1964 Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
1965 // TODO catch and handle errors for savestate
1966 // TODO prevent user from messing with the Desktop whilst we're saving
1969 boolean doBackup = BackupFiles.getEnabled();
1970 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
1973 new Jalview2XML().saveState(
1974 doBackup ? backupfiles.getTempFile() : chosenFile);
1978 backupfiles.setWriteSuccess(true);
1979 backupfiles.rollBackupsAndRenameTempFile();
1981 } catch (OutOfMemoryError oom)
1983 new OOMWarning("Whilst saving current state to "
1984 + chosenFile.getName(), oom);
1985 } catch (Exception ex)
1987 jalview.bin.Console.error("Problems whilst trying to save to "
1988 + chosenFile.getName(), ex);
1989 JvOptionPane.showMessageDialog(me,
1990 MessageManager.formatMessage(
1991 "label.error_whilst_saving_current_state_to",
1993 { chosenFile.getName() }),
1994 MessageManager.getString("label.couldnt_save_project"),
1995 JvOptionPane.WARNING_MESSAGE);
1997 setProgressBar(null, IdUtils.newId(IdType.PROGRESS, chosenFile));
2004 public void saveAsState_actionPerformed(ActionEvent e)
2006 saveState_actionPerformed(true);
2009 protected void setProjectFile(File choice)
2011 this.projectFile = choice;
2014 public File getProjectFile()
2016 return this.projectFile;
2020 * Shows a file chooser dialog and tries to read in the selected file as a
2024 public void loadState_actionPerformed()
2026 final String[] suffix = new String[] { "jvp", "jar" };
2027 final String[] desc = new String[] { "Jalview Project",
2028 "Jalview Project (old)" };
2029 JalviewFileChooser chooser = new JalviewFileChooser(
2030 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
2031 "Jalview Project", true, BackupFiles.getEnabled()); // last two
2035 chooser.setFileView(new JalviewFileView());
2036 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
2037 chooser.setResponseHandler(0, () -> {
2038 File selectedFile = chooser.getSelectedFile();
2039 setProjectFile(selectedFile);
2040 String choice = selectedFile.getAbsolutePath();
2041 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
2042 new Thread(new Runnable()
2049 new Jalview2XML().loadJalviewAlign(selectedFile);
2050 } catch (OutOfMemoryError oom)
2052 new OOMWarning("Whilst loading project from " + choice, oom);
2053 } catch (Exception ex)
2055 jalview.bin.Console.error(
2056 "Problems whilst loading project from " + choice, ex);
2057 JvOptionPane.showMessageDialog(Desktop.desktop,
2058 MessageManager.formatMessage(
2059 "label.error_whilst_loading_project_from",
2062 MessageManager.getString("label.couldnt_load_project"),
2063 JvOptionPane.WARNING_MESSAGE);
2066 }, "Project Loader").start();
2069 chooser.showOpenDialog(this);
2073 public void inputSequence_actionPerformed(ActionEvent e)
2075 new SequenceFetcher(this);
2078 JPanel progressPanel;
2080 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
2082 public void startLoading(final Object fileName)
2084 if (fileLoadingCount == 0)
2086 fileLoadingPanels.add(addProgressPanel(MessageManager
2087 .formatMessage("label.loading_file", new Object[]
2093 private JPanel addProgressPanel(String string)
2095 if (progressPanel == null)
2097 progressPanel = new JPanel(new GridLayout(1, 1));
2098 totalProgressCount = 0;
2099 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
2101 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
2102 JProgressBar progressBar = new JProgressBar();
2103 progressBar.setIndeterminate(true);
2105 thisprogress.add(new JLabel(string), BorderLayout.WEST);
2107 thisprogress.add(progressBar, BorderLayout.CENTER);
2108 progressPanel.add(thisprogress);
2109 ((GridLayout) progressPanel.getLayout()).setRows(
2110 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
2111 ++totalProgressCount;
2112 instance.validate();
2113 return thisprogress;
2116 int totalProgressCount = 0;
2118 private void removeProgressPanel(JPanel progbar)
2120 if (progressPanel != null)
2122 synchronized (progressPanel)
2124 progressPanel.remove(progbar);
2125 GridLayout gl = (GridLayout) progressPanel.getLayout();
2126 gl.setRows(gl.getRows() - 1);
2127 if (--totalProgressCount < 1)
2129 this.getContentPane().remove(progressPanel);
2130 progressPanel = null;
2137 public void stopLoading()
2140 if (fileLoadingCount < 1)
2142 while (fileLoadingPanels.size() > 0)
2144 removeProgressPanel(fileLoadingPanels.remove(0));
2146 fileLoadingPanels.clear();
2147 fileLoadingCount = 0;
2152 public static int getViewCount(String alignmentId)
2154 AlignmentViewport[] aps = getViewports(alignmentId);
2155 return (aps == null) ? 0 : aps.length;
2160 * @param alignmentId
2161 * - if null, all sets are returned
2162 * @return all AlignmentPanels concerning the alignmentId sequence set
2164 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
2166 if (Desktop.desktop == null)
2168 // no frames created and in headless mode
2169 // TODO: verify that frames are recoverable when in headless mode
2172 List<AlignmentPanel> aps = new ArrayList<>();
2173 AlignFrame[] frames = Desktop.getDesktopAlignFrames();
2178 for (AlignFrame af : frames)
2180 for (AlignmentPanel ap : af.alignPanels)
2182 if (alignmentId == null
2183 || alignmentId.equals(ap.av.getSequenceSetId()))
2189 if (aps.size() == 0)
2193 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2198 * get all the viewports on an alignment.
2200 * @param sequenceSetId
2201 * unique alignment id (may be null - all viewports returned in that
2203 * @return all viewports on the alignment bound to sequenceSetId
2205 public static AlignmentViewport[] getViewports(String sequenceSetId)
2207 List<AlignmentViewport> viewp = new ArrayList<>();
2208 if (desktop != null)
2210 AlignFrame[] frames = Desktop.getDesktopAlignFrames();
2212 for (AlignFrame afr : frames)
2214 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2215 .equals(sequenceSetId))
2217 if (afr.alignPanels != null)
2219 for (AlignmentPanel ap : afr.alignPanels)
2221 if (sequenceSetId == null
2222 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2230 viewp.add(afr.getViewport());
2234 if (viewp.size() > 0)
2236 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2243 * Explode the views in the given frame into separate AlignFrame
2247 public static void explodeViews(AlignFrame af)
2249 int size = af.alignPanels.size();
2255 // FIXME: ideally should use UI interface API
2256 FeatureSettings viewFeatureSettings = (af.featureSettings != null
2257 && af.featureSettings.isOpen()) ? af.featureSettings : null;
2258 Rectangle fsBounds = af.getFeatureSettingsGeometry();
2259 for (int i = 0; i < size; i++)
2261 AlignmentPanel ap = af.alignPanels.get(i);
2263 AlignFrame newaf = new AlignFrame(ap);
2265 // transfer reference for existing feature settings to new alignFrame
2266 if (ap == af.alignPanel)
2268 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2270 newaf.featureSettings = viewFeatureSettings;
2272 newaf.setFeatureSettingsGeometry(fsBounds);
2276 * Restore the view's last exploded frame geometry if known. Multiple views from
2277 * one exploded frame share and restore the same (frame) position and size.
2279 Rectangle geometry = ap.av.getExplodedGeometry();
2280 if (geometry != null)
2282 newaf.setBounds(geometry);
2285 ap.av.setGatherViewsHere(false);
2287 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2288 AlignFrame.DEFAULT_HEIGHT);
2289 // and materialise a new feature settings dialog instance for the new
2291 // (closes the old as if 'OK' was pressed)
2292 if (ap == af.alignPanel && newaf.featureSettings != null
2293 && newaf.featureSettings.isOpen()
2294 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2296 newaf.showFeatureSettingsUI();
2300 af.featureSettings = null;
2301 af.alignPanels.clear();
2302 af.closeMenuItem_actionPerformed(true);
2307 * Gather expanded views (separate AlignFrame's) with the same sequence set
2308 * identifier back in to this frame as additional views, and close the
2309 * expanded views. Note the expanded frames may themselves have multiple
2310 * views. We take the lot.
2314 public void gatherViews(AlignFrame source)
2316 source.viewport.setGatherViewsHere(true);
2317 source.viewport.setExplodedGeometry(source.getBounds());
2318 JInternalFrame[] frames = desktop.getAllFrames();
2319 String viewId = source.viewport.getSequenceSetId();
2320 for (int t = 0; t < frames.length; t++)
2322 if (frames[t] instanceof AlignFrame && frames[t] != source)
2324 AlignFrame af = (AlignFrame) frames[t];
2325 boolean gatherThis = false;
2326 for (int a = 0; a < af.alignPanels.size(); a++)
2328 AlignmentPanel ap = af.alignPanels.get(a);
2329 if (viewId.equals(ap.av.getSequenceSetId()))
2332 ap.av.setGatherViewsHere(false);
2333 ap.av.setExplodedGeometry(af.getBounds());
2334 source.addAlignmentPanel(ap, false);
2340 if (af.featureSettings != null && af.featureSettings.isOpen())
2342 if (source.featureSettings == null)
2344 // preserve the feature settings geometry for this frame
2345 source.featureSettings = af.featureSettings;
2346 source.setFeatureSettingsGeometry(
2347 af.getFeatureSettingsGeometry());
2351 // close it and forget
2352 af.featureSettings.close();
2355 af.alignPanels.clear();
2356 af.closeMenuItem_actionPerformed(true);
2361 // refresh the feature setting UI for the source frame if it exists
2362 if (source.featureSettings != null && source.featureSettings.isOpen())
2364 source.showFeatureSettingsUI();
2369 public JInternalFrame[] getAllFrames()
2371 return desktop.getAllFrames();
2375 * Checks the given url to see if it gives a response indicating that the user
2376 * should be informed of a new questionnaire.
2380 public void checkForQuestionnaire(String url)
2382 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2383 // javax.swing.SwingUtilities.invokeLater(jvq);
2384 new Thread(jvq).start();
2387 public void checkURLLinks()
2389 // Thread off the URL link checker
2390 addDialogThread(new Runnable()
2395 if (Cache.getDefault("CHECKURLLINKS", true))
2397 // check what the actual links are - if it's just the default don't
2398 // bother with the warning
2399 List<String> links = Preferences.sequenceUrlLinks
2402 // only need to check links if there is one with a
2403 // SEQUENCE_ID which is not the default EMBL_EBI link
2404 ListIterator<String> li = links.listIterator();
2405 boolean check = false;
2406 List<JLabel> urls = new ArrayList<>();
2407 while (li.hasNext())
2409 String link = li.next();
2410 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2411 && !UrlConstants.isDefaultString(link))
2414 int barPos = link.indexOf("|");
2415 String urlMsg = barPos == -1 ? link
2416 : link.substring(0, barPos) + ": "
2417 + link.substring(barPos + 1);
2418 urls.add(new JLabel(urlMsg));
2426 // ask user to check in case URL links use old style tokens
2427 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2428 JPanel msgPanel = new JPanel();
2429 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2430 msgPanel.add(Box.createVerticalGlue());
2431 JLabel msg = new JLabel(MessageManager
2432 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2433 JLabel msg2 = new JLabel(MessageManager
2434 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2436 for (JLabel url : urls)
2442 final JCheckBox jcb = new JCheckBox(
2443 MessageManager.getString("label.do_not_display_again"));
2444 jcb.addActionListener(new ActionListener()
2447 public void actionPerformed(ActionEvent e)
2449 // update Cache settings for "don't show this again"
2450 boolean showWarningAgain = !jcb.isSelected();
2451 Cache.setProperty("CHECKURLLINKS",
2452 Boolean.valueOf(showWarningAgain).toString());
2457 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2459 .getString("label.SEQUENCE_ID_no_longer_used"),
2460 JvOptionPane.WARNING_MESSAGE);
2467 * Proxy class for JDesktopPane which optionally displays the current memory
2468 * usage and highlights the desktop area with a red bar if free memory runs
2473 public class MyDesktopPane extends JDesktopPane implements Runnable
2475 private static final float ONE_MB = 1048576f;
2477 boolean showMemoryUsage = false;
2481 java.text.NumberFormat df;
2483 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2486 public MyDesktopPane(boolean showMemoryUsage)
2488 showMemoryUsage(showMemoryUsage);
2491 public void showMemoryUsage(boolean showMemory)
2493 this.showMemoryUsage = showMemory;
2496 Thread worker = new Thread(this);
2502 public boolean isShowMemoryUsage()
2504 return showMemoryUsage;
2510 df = java.text.NumberFormat.getNumberInstance();
2511 df.setMaximumFractionDigits(2);
2512 runtime = Runtime.getRuntime();
2514 while (showMemoryUsage)
2518 maxMemory = runtime.maxMemory() / ONE_MB;
2519 allocatedMemory = runtime.totalMemory() / ONE_MB;
2520 freeMemory = runtime.freeMemory() / ONE_MB;
2521 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2523 percentUsage = (totalFreeMemory / maxMemory) * 100;
2525 // if (percentUsage < 20)
2527 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2529 // instance.set.setBorder(border1);
2532 // sleep after showing usage
2534 } catch (Exception ex)
2536 ex.printStackTrace();
2542 public void paintComponent(Graphics g)
2544 if (showMemoryUsage && g != null && df != null)
2546 if (percentUsage < 20)
2548 g.setColor(Color.red);
2550 FontMetrics fm = g.getFontMetrics();
2553 g.drawString(MessageManager.formatMessage("label.memory_stats",
2555 { df.format(totalFreeMemory), df.format(maxMemory),
2556 df.format(percentUsage) }),
2557 10, getHeight() - fm.getHeight());
2561 // output debug scale message. Important for jalview.bin.HiDPISettingTest2
2562 Desktop.debugScaleMessage(Desktop.getDesktop().getGraphics());
2567 * Accessor method to quickly get all the AlignmentFrames loaded.
2569 * @return an array of AlignFrame, or null if none found
2572 public AlignFrame[] getAlignFrames()
2574 if (desktop == null)
2579 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2585 List<AlignFrame> avp = new ArrayList<>();
2587 for (int i = frames.length - 1; i > -1; i--)
2589 if (frames[i] instanceof AlignFrame)
2591 avp.add((AlignFrame) frames[i]);
2593 else if (frames[i] instanceof SplitFrame)
2596 * Also check for a split frame containing an AlignFrame
2598 GSplitFrame sf = (GSplitFrame) frames[i];
2599 if (sf.getTopFrame() instanceof AlignFrame)
2601 avp.add((AlignFrame) sf.getTopFrame());
2603 if (sf.getBottomFrame() instanceof AlignFrame)
2605 avp.add((AlignFrame) sf.getBottomFrame());
2609 if (avp.size() == 0)
2613 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2620 public static AlignFrame[] getDesktopAlignFrames()
2622 if (Jalview.isHeadlessMode())
2624 // Desktop.desktop is null in headless mode
2625 return Jalview.getInstance().getAlignFrames();
2628 if (instance != null && desktop != null)
2630 return instance.getAlignFrames();
2637 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2641 public GStructureViewer[] getJmols()
2643 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2649 List<GStructureViewer> avp = new ArrayList<>();
2651 for (int i = frames.length - 1; i > -1; i--)
2653 if (frames[i] instanceof AppJmol)
2655 GStructureViewer af = (GStructureViewer) frames[i];
2659 if (avp.size() == 0)
2663 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2668 * Add Groovy Support to Jalview
2671 public void groovyShell_actionPerformed()
2675 openGroovyConsole();
2676 } catch (Exception ex)
2678 jalview.bin.Console.error("Groovy Console creation failed.", ex);
2679 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2681 MessageManager.getString("label.couldnt_create_groovy_shell"),
2682 MessageManager.getString("label.groovy_support_failed"),
2683 JvOptionPane.ERROR_MESSAGE);
2688 * Open the Groovy console
2690 void openGroovyConsole()
2692 if (groovyConsole == null)
2694 JalviewObjectI j = new JalviewObject(this);
2695 groovyConsole = new groovy.console.ui.Console();
2696 groovyConsole.setVariable(JalviewObjectI.jalviewObjectName, j);
2697 groovyConsole.setVariable(JalviewObjectI.currentAlFrameName,
2698 getCurrentAlignFrame());
2699 groovyConsole.run();
2702 * We allow only one console at a time, so that AlignFrame menu option
2703 * 'Calculate | Run Groovy script' is unambiguous. Disable 'Groovy Console', and
2704 * enable 'Run script', when the console is opened, and the reverse when it is
2707 Window window = (Window) groovyConsole.getFrame();
2708 window.addWindowListener(new WindowAdapter()
2711 public void windowClosed(WindowEvent e)
2714 * rebind CMD-Q from Groovy Console to Jalview Quit
2717 enableExecuteGroovy(false);
2723 * show Groovy console window (after close and reopen)
2725 ((Window) groovyConsole.getFrame()).setVisible(true);
2728 * if we got this far, enable 'Run Groovy' in AlignFrame menus and disable
2729 * opening a second console
2731 enableExecuteGroovy(true);
2735 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2736 * binding when opened
2738 protected void addQuitHandler()
2741 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
2743 .getKeyStroke(KeyEvent.VK_Q,
2744 jalview.util.ShortcutKeyMaskExWrapper
2745 .getMenuShortcutKeyMaskEx()),
2747 getRootPane().getActionMap().put("Quit", new AbstractAction()
2750 public void actionPerformed(ActionEvent e)
2758 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2761 * true if Groovy console is open
2763 public void enableExecuteGroovy(boolean enabled)
2766 * disable opening a second Groovy console (or re-enable when the console is
2769 groovyShell.setEnabled(!enabled);
2771 AlignFrame[] alignFrames = getDesktopAlignFrames();
2772 if (alignFrames != null)
2774 for (AlignFrame af : alignFrames)
2776 af.setGroovyEnabled(enabled);
2782 * Progress bars managed by the IProgressIndicator method.
2784 private Hashtable<Long, JPanel> progressBars;
2786 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2791 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2794 public void setProgressBar(String message, long id)
2796 if (progressBars == null)
2798 progressBars = new Hashtable<>();
2799 progressBarHandlers = new Hashtable<>();
2802 if (progressBars.get(Long.valueOf(id)) != null)
2804 JPanel panel = progressBars.remove(Long.valueOf(id));
2805 if (progressBarHandlers.contains(Long.valueOf(id)))
2807 progressBarHandlers.remove(Long.valueOf(id));
2809 removeProgressPanel(panel);
2813 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2820 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2821 * jalview.gui.IProgressIndicatorHandler)
2824 public void registerHandler(final long id,
2825 final IProgressIndicatorHandler handler)
2827 if (progressBarHandlers == null
2828 || !progressBars.containsKey(Long.valueOf(id)))
2830 throw new Error(MessageManager.getString(
2831 "error.call_setprogressbar_before_registering_handler"));
2833 progressBarHandlers.put(Long.valueOf(id), handler);
2834 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2835 if (handler.canCancel())
2837 JButton cancel = new JButton(
2838 MessageManager.getString("action.cancel"));
2839 final IProgressIndicator us = this;
2840 cancel.addActionListener(new ActionListener()
2844 public void actionPerformed(ActionEvent e)
2846 handler.cancelActivity(id);
2847 us.setProgressBar(MessageManager
2848 .formatMessage("label.cancelled_params", new Object[]
2849 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2853 progressPanel.add(cancel, BorderLayout.EAST);
2859 * @return true if any progress bars are still active
2862 public boolean operationInProgress()
2864 if (progressBars != null && progressBars.size() > 0)
2872 * This will return the first AlignFrame holding the given viewport instance.
2873 * It will break if there are more than one AlignFrames viewing a particular
2877 * @return alignFrame for viewport
2879 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2881 if (desktop != null)
2883 AlignmentPanel[] aps = getAlignmentPanels(
2884 viewport.getSequenceSetId());
2885 for (int panel = 0; aps != null && panel < aps.length; panel++)
2887 if (aps[panel] != null && aps[panel].av == viewport)
2889 return aps[panel].alignFrame;
2896 public VamsasApplication getVamsasApplication()
2898 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2904 * flag set if jalview GUI is being operated programmatically
2906 private boolean inBatchMode = false;
2909 * check if jalview GUI is being operated programmatically
2911 * @return inBatchMode
2913 public boolean isInBatchMode()
2919 * set flag if jalview GUI is being operated programmatically
2921 * @param inBatchMode
2923 public void setInBatchMode(boolean inBatchMode)
2925 this.inBatchMode = inBatchMode;
2929 * start service discovery and wait till it is done
2931 public void startServiceDiscovery()
2933 startServiceDiscovery(false);
2937 * start service discovery threads - blocking or non-blocking
2941 public void startServiceDiscovery(boolean blocking)
2943 startServiceDiscovery(blocking, false);
2947 * start service discovery threads
2950 * - false means call returns immediately
2951 * @param ignore_SHOW_JWS2_SERVICES_preference
2952 * - when true JABA services are discovered regardless of user's JWS2
2953 * discovery preference setting
2955 public void startServiceDiscovery(boolean blocking,
2956 boolean ignore_SHOW_JWS2_SERVICES_preference)
2958 boolean alive = true;
2959 Thread t0 = null, t1 = null, t2 = null;
2960 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2963 // todo: changesupport handlers need to be transferred
2964 if (discoverer == null)
2966 discoverer = new jalview.ws.jws1.Discoverer();
2967 // register PCS handler for desktop.
2968 discoverer.addPropertyChangeListener(changeSupport);
2970 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2971 // until we phase out completely
2972 (t0 = new Thread(discoverer)).start();
2975 if (ignore_SHOW_JWS2_SERVICES_preference
2976 || Cache.getDefault("SHOW_JWS2_SERVICES", true))
2978 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2979 .startDiscoverer(changeSupport);
2983 // TODO: do rest service discovery
2992 } catch (Exception e)
2995 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2996 || (t3 != null && t3.isAlive())
2997 || (t0 != null && t0.isAlive());
3003 * called to check if the service discovery process completed successfully.
3007 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3009 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3011 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3012 .getErrorMessages();
3015 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3017 if (serviceChangedDialog == null)
3019 // only run if we aren't already displaying one of these.
3020 addDialogThread(serviceChangedDialog = new Runnable()
3027 * JalviewDialog jd =new JalviewDialog() {
3029 * @Override protected void cancelPressed() { // TODO Auto-generated method stub
3031 * }@Override protected void okPressed() { // TODO Auto-generated method stub
3033 * }@Override protected void raiseClosed() { // TODO Auto-generated method stub
3035 * } }; jd.initDialogFrame(new JLabel("<html><table width=\"450\"><tr><td>" +
3037 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3038 * + " or mis-configured HTTP proxy settings.<br/>" +
3039 * "Check the <em>Connections</em> and <em>Web services</em> tab of the" +
3040 * " Tools->Preferences dialog box to change them.</td></tr></table></html>" ),
3041 * true, true, "Web Service Configuration Problem", 450, 400);
3043 * jd.waitForInput();
3045 JvOptionPane.showConfirmDialog(Desktop.desktop,
3046 new JLabel("<html><table width=\"450\"><tr><td>"
3047 + ermsg + "</td></tr></table>"
3048 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3049 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3050 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3051 + " Tools->Preferences dialog box to change them.</p></html>"),
3052 "Web Service Configuration Problem",
3053 JvOptionPane.DEFAULT_OPTION,
3054 JvOptionPane.ERROR_MESSAGE);
3055 serviceChangedDialog = null;
3063 jalview.bin.Console.error(
3064 "Errors reported by JABA discovery service. Check web services preferences.\n"
3071 private Runnable serviceChangedDialog = null;
3074 * start a thread to open a URL in the configured browser. Pops up a warning
3075 * dialog to the user if there is an exception when calling out to the browser
3080 public static void showUrl(final String url)
3082 if (url != null && !url.trim().equals(""))
3084 jalview.bin.Console.info("Opening URL: " + url);
3085 showUrl(url, Desktop.instance);
3089 jalview.bin.Console.warn("Ignoring attempt to show an empty URL.");
3095 * Like showUrl but allows progress handler to be specified
3099 * (null) or object implementing IProgressIndicator
3101 public static void showUrl(final String url,
3102 final IProgressIndicator progress)
3104 new Thread(new Runnable()
3111 if (progress != null)
3113 progress.setProgressBar(MessageManager
3114 .formatMessage("status.opening_params", new Object[]
3115 { url }), IdUtils.newId(IdType.PROGRESS, this));
3117 jalview.util.BrowserLauncher.openURL(url);
3118 } catch (Exception ex)
3120 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
3122 .getString("label.web_browser_not_found_unix"),
3123 MessageManager.getString("label.web_browser_not_found"),
3124 JvOptionPane.WARNING_MESSAGE);
3126 ex.printStackTrace();
3128 if (progress != null)
3130 progress.setProgressBar(null,
3131 IdUtils.newId(IdType.PROGRESS, this));
3137 public static WsParamSetManager wsparamManager = null;
3139 public static ParamManager getUserParameterStore()
3141 if (wsparamManager == null)
3143 wsparamManager = new WsParamSetManager();
3145 return wsparamManager;
3149 * static hyperlink handler proxy method for use by Jalview's internal windows
3153 public static void hyperlinkUpdate(HyperlinkEvent e)
3155 if (e.getEventType() == EventType.ACTIVATED)
3160 url = e.getURL().toString();
3161 Desktop.showUrl(url);
3162 } catch (Exception x)
3167 .error("Couldn't handle string " + url + " as a URL.");
3169 // ignore any exceptions due to dud links.
3176 * single thread that handles display of dialogs to user.
3178 ExecutorService dialogExecutor = Executors.newFixedThreadPool(3);
3181 * flag indicating if dialogExecutor should try to acquire a permit
3183 private volatile boolean dialogPause = true;
3188 private Semaphore block = new Semaphore(0);
3190 private static groovy.console.ui.Console groovyConsole;
3193 * add another dialog thread to the queue
3197 public void addDialogThread(final Runnable prompter)
3199 dialogExecutor.submit(new Runnable()
3206 acquireDialogQueue();
3208 if (instance == null)
3214 SwingUtilities.invokeAndWait(prompter);
3215 } catch (Exception q)
3217 jalview.bin.Console.warn("Unexpected Exception in dialog thread.",
3224 private boolean dialogQueueStarted = false;
3226 public void startDialogQueue()
3228 if (dialogQueueStarted)
3232 // set the flag so we don't pause waiting for another permit and semaphore
3233 // the current task to begin
3234 releaseDialogQueue();
3235 dialogQueueStarted = true;
3238 public void acquireDialogQueue()
3244 } catch (InterruptedException e)
3246 jalview.bin.Console.debug("Interruption when acquiring DialogueQueue",
3251 public void releaseDialogQueue()
3258 dialogPause = false;
3262 * Outputs an image of the desktop to file in EPS format, after prompting the
3263 * user for choice of Text or Lineart character rendering (unless a preference
3264 * has been set). The file name is generated as
3267 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3271 protected void snapShotWindow_actionPerformed(ActionEvent e)
3273 // currently the menu option to do this is not shown
3276 int width = getWidth();
3277 int height = getHeight();
3279 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3280 ImageWriterI writer = new ImageWriterI()
3283 public void exportImage(Graphics g) throws Exception
3286 jalview.bin.Console.info("Successfully written snapshot to file "
3287 + of.getAbsolutePath());
3290 String title = "View of desktop";
3291 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3295 exporter.doExport(of, this, width, height, title);
3296 } catch (ImageOutputException ioex)
3298 jalview.bin.Console.error(
3299 "Unexpected error whilst writing Jalview desktop snapshot as EPS",
3305 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3306 * This respects (remembers) any previous 'exploded geometry' i.e. the size
3307 * and location last time the view was expanded (if any). However it does not
3308 * remember the split pane divider location - this is set to match the
3309 * 'exploding' frame.
3313 public void explodeViews(SplitFrame sf)
3315 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3316 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3317 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3319 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3321 int viewCount = topPanels.size();
3328 * Processing in reverse order works, forwards order leaves the first panels not
3329 * visible. I don't know why!
3331 for (int i = viewCount - 1; i >= 0; i--)
3334 * Make new top and bottom frames. These take over the respective AlignmentPanel
3335 * objects, including their AlignmentViewports, so the cdna/protein
3336 * relationships between the viewports is carried over to the new split frames.
3338 * explodedGeometry holds the (x, y) position of the previously exploded
3339 * SplitFrame, and the (width, height) of the AlignFrame component
3341 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3342 AlignFrame newTopFrame = new AlignFrame(topPanel);
3343 newTopFrame.setSize(oldTopFrame.getSize());
3344 newTopFrame.setVisible(true);
3345 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3346 .getExplodedGeometry();
3347 if (geometry != null)
3349 newTopFrame.setSize(geometry.getSize());
3352 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3353 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3354 newBottomFrame.setSize(oldBottomFrame.getSize());
3355 newBottomFrame.setVisible(true);
3356 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3357 .getExplodedGeometry();
3358 if (geometry != null)
3360 newBottomFrame.setSize(geometry.getSize());
3363 topPanel.av.setGatherViewsHere(false);
3364 bottomPanel.av.setGatherViewsHere(false);
3365 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3367 if (geometry != null)
3369 splitFrame.setLocation(geometry.getLocation());
3371 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3375 * Clear references to the panels (now relocated in the new SplitFrames) before
3376 * closing the old SplitFrame.
3379 bottomPanels.clear();
3384 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3385 * back into the given SplitFrame as additional views. Note that the gathered
3386 * frames may themselves have multiple views.
3390 public void gatherViews(GSplitFrame source)
3393 * special handling of explodedGeometry for a view within a SplitFrame: - it
3394 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3395 * height) of the AlignFrame component
3397 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3398 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3399 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3400 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3401 myBottomFrame.viewport
3402 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3403 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3404 myTopFrame.viewport.setGatherViewsHere(true);
3405 myBottomFrame.viewport.setGatherViewsHere(true);
3406 String topViewId = myTopFrame.viewport.getSequenceSetId();
3407 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3409 JInternalFrame[] frames = desktop.getAllFrames();
3410 for (JInternalFrame frame : frames)
3412 if (frame instanceof SplitFrame && frame != source)
3414 SplitFrame sf = (SplitFrame) frame;
3415 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3416 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3417 boolean gatherThis = false;
3418 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3420 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3421 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3422 if (topViewId.equals(topPanel.av.getSequenceSetId())
3423 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3426 topPanel.av.setGatherViewsHere(false);
3427 bottomPanel.av.setGatherViewsHere(false);
3428 topPanel.av.setExplodedGeometry(
3429 new Rectangle(sf.getLocation(), topFrame.getSize()));
3430 bottomPanel.av.setExplodedGeometry(
3431 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3432 myTopFrame.addAlignmentPanel(topPanel, false);
3433 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3439 topFrame.getAlignPanels().clear();
3440 bottomFrame.getAlignPanels().clear();
3447 * The dust settles...give focus to the tab we did this from.
3449 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3452 public static groovy.console.ui.Console getGroovyConsole()
3454 return groovyConsole;
3458 * handles the payload of a drag and drop event.
3460 * TODO refactor to desktop utilities class
3463 * - Data source strings extracted from the drop event
3465 * - protocol for each data source extracted from the drop event
3469 * - the payload from the drop event
3472 public static void transferFromDropTarget(List<Object> files,
3473 List<DataSourceType> protocols, DropTargetDropEvent evt,
3474 Transferable t) throws Exception
3477 DataFlavor uriListFlavor = new DataFlavor(
3478 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3481 urlFlavour = new DataFlavor(
3482 "application/x-java-url; class=java.net.URL");
3483 } catch (ClassNotFoundException cfe)
3485 jalview.bin.Console.debug("Couldn't instantiate the URL dataflavor.",
3489 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3494 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3495 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3496 // means url may be null.
3499 protocols.add(DataSourceType.URL);
3500 files.add(url.toString());
3501 jalview.bin.Console.debug("Drop handled as URL dataflavor "
3502 + files.get(files.size() - 1));
3507 if (Platform.isAMacAndNotJS())
3509 jalview.bin.Console.errPrintln(
3510 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3513 } catch (Throwable ex)
3515 jalview.bin.Console.debug("URL drop handler failed.", ex);
3518 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3520 // Works on Windows and MacOSX
3521 jalview.bin.Console.debug("Drop handled as javaFileListFlavor");
3522 for (Object file : (List) t
3523 .getTransferData(DataFlavor.javaFileListFlavor))
3526 protocols.add(DataSourceType.FILE);
3531 // Unix like behaviour
3532 boolean added = false;
3534 if (t.isDataFlavorSupported(uriListFlavor))
3536 jalview.bin.Console.debug("Drop handled as uriListFlavor");
3537 // This is used by Unix drag system
3538 data = (String) t.getTransferData(uriListFlavor);
3542 // fallback to text: workaround - on OSX where there's a JVM bug
3544 .debug("standard URIListFlavor failed. Trying text");
3545 // try text fallback
3546 DataFlavor textDf = new DataFlavor(
3547 "text/plain;class=java.lang.String");
3548 if (t.isDataFlavorSupported(textDf))
3550 data = (String) t.getTransferData(textDf);
3553 jalview.bin.Console.debug("Plain text drop content returned "
3554 + (data == null ? "Null - failed" : data));
3559 while (protocols.size() < files.size())
3561 jalview.bin.Console.debug("Adding missing FILE protocol for "
3562 + files.get(protocols.size()));
3563 protocols.add(DataSourceType.FILE);
3565 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3566 data, "\r\n"); st.hasMoreTokens();)
3569 String s = st.nextToken();
3570 if (s.startsWith("#"))
3572 // the line is a comment (as per the RFC 2483)
3575 java.net.URI uri = new java.net.URI(s);
3576 if (uri.getScheme().toLowerCase(Locale.ROOT).startsWith("http"))
3578 protocols.add(DataSourceType.URL);
3579 files.add(uri.toString());
3583 // otherwise preserve old behaviour: catch all for file objects
3584 java.io.File file = new java.io.File(uri);
3585 protocols.add(DataSourceType.FILE);
3586 files.add(file.toString());
3591 if (jalview.bin.Console.isDebugEnabled())
3593 if (data == null || !added)
3596 if (t.getTransferDataFlavors() != null
3597 && t.getTransferDataFlavors().length > 0)
3599 jalview.bin.Console.debug(
3600 "Couldn't resolve drop data. Here are the supported flavors:");
3601 for (DataFlavor fl : t.getTransferDataFlavors())
3603 jalview.bin.Console.debug(
3604 "Supported transfer dataflavor: " + fl.toString());
3605 Object df = t.getTransferData(fl);
3608 jalview.bin.Console.debug("Retrieves: " + df);
3612 jalview.bin.Console.debug("Retrieved nothing");
3619 .debug("Couldn't resolve dataflavor for drop: "
3625 if (Platform.isWindowsAndNotJS())
3628 .debug("Scanning dropped content for Windows Link Files");
3630 // resolve any .lnk files in the file drop
3631 for (int f = 0; f < files.size(); f++)
3633 String source = files.get(f).toString().toLowerCase(Locale.ROOT);
3634 if (protocols.get(f).equals(DataSourceType.FILE)
3635 && (source.endsWith(".lnk") || source.endsWith(".url")
3636 || source.endsWith(".site")))
3640 Object obj = files.get(f);
3641 File lf = (obj instanceof File ? (File) obj
3642 : new File((String) obj));
3643 // process link file to get a URL
3644 jalview.bin.Console.debug("Found potential link file: " + lf);
3645 WindowsShortcut wscfile = new WindowsShortcut(lf);
3646 String fullname = wscfile.getRealFilename();
3647 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3648 files.set(f, fullname);
3649 jalview.bin.Console.debug("Parsed real filename " + fullname
3650 + " to extract protocol: " + protocols.get(f));
3651 } catch (Exception ex)
3653 jalview.bin.Console.error(
3654 "Couldn't parse " + files.get(f) + " as a link file.",
3663 * Sets the Preferences property for experimental features to True or False
3664 * depending on the state of the controlling menu item
3667 protected void showExperimental_actionPerformed(boolean selected)
3669 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3673 * Answers a (possibly empty) list of any structure viewer frames (currently
3674 * for either Jmol or Chimera) which are currently open. This may optionally
3675 * be restricted to viewers of a specified class, or viewers linked to a
3676 * specified alignment panel.
3679 * if not null, only return viewers linked to this panel
3680 * @param structureViewerClass
3681 * if not null, only return viewers of this class
3684 public List<StructureViewerBase> getStructureViewers(
3685 AlignmentPanel apanel,
3686 Class<? extends StructureViewerBase> structureViewerClass)
3688 List<StructureViewerBase> result = new ArrayList<>();
3689 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3691 for (JInternalFrame frame : frames)
3693 if (frame instanceof StructureViewerBase)
3695 if (structureViewerClass == null
3696 || structureViewerClass.isInstance(frame))
3699 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3701 result.add((StructureViewerBase) frame);
3709 public static final String debugScaleMessage = "Desktop graphics transform scale=";
3711 private static boolean debugScaleMessageDone = false;
3713 public static void debugScaleMessage(Graphics g)
3715 if (debugScaleMessageDone)
3719 // output used by tests to check HiDPI scaling settings in action
3722 Graphics2D gg = (Graphics2D) g;
3725 AffineTransform t = gg.getTransform();
3726 double scaleX = t.getScaleX();
3727 double scaleY = t.getScaleY();
3728 jalview.bin.Console.debug(debugScaleMessage + scaleX + " (X)");
3729 jalview.bin.Console.debug(debugScaleMessage + scaleY + " (Y)");
3730 debugScaleMessageDone = true;
3734 jalview.bin.Console.debug("Desktop graphics null");
3736 } catch (Exception e)
3738 jalview.bin.Console.debug(Cache.getStackTraceString(e));
3743 * closes the current instance window, but leaves the JVM running. Bypasses
3744 * any shutdown prompts, but does not set window dispose on close in case JVM
3747 public static void closeDesktop()
3749 if (Desktop.instance != null)
3751 Desktop us = Desktop.instance;
3752 Desktop.instance.quitTheDesktop(false, false);
3753 // call dispose in a separate thread - try to avoid indirect deadlocks
3756 new Thread(new Runnable()
3761 ExecutorService dex = us.dialogExecutor;
3765 us.dialogExecutor = null;
3766 us.block.drainPermits();
3776 * checks if any progress bars are being displayed in any of the windows
3777 * managed by the desktop
3781 public boolean operationsAreInProgress()
3783 JInternalFrame[] frames = getAllFrames();
3784 for (JInternalFrame frame : frames)
3786 if (frame instanceof IProgressIndicator)
3788 if (((IProgressIndicator) frame).operationInProgress())
3794 return operationInProgress();
3798 * keep track of modal JvOptionPanes open as modal dialogs for AlignFrames.
3799 * The way the modal JInternalFrame is made means it cannot be a child of an
3800 * AlignFrame, so closing the AlignFrame might leave the modal open :(
3802 private static Map<AlignFrame, JInternalFrame> alignFrameModalMap = new HashMap<>();
3804 protected static void addModal(AlignFrame af, JInternalFrame jif)
3806 alignFrameModalMap.put(af, jif);
3809 protected static void closeModal(AlignFrame af)
3811 if (!alignFrameModalMap.containsKey(af))
3815 JInternalFrame jif = alignFrameModalMap.get(af);
3820 jif.setClosed(true);
3821 } catch (PropertyVetoException e)
3823 e.printStackTrace();
3826 alignFrameModalMap.remove(af);
3829 public void nonBlockingDialog(String title, String message, String button,
3830 int type, boolean scrollable, boolean modal)
3832 nonBlockingDialog(title, message, null, button, type, scrollable, false,
3836 public void nonBlockingDialog(String title, String message,
3837 String boxtext, String button, int type, boolean scrollable,
3838 boolean html, boolean modal, int timeout)
3840 nonBlockingDialog(32, 2, title, message, boxtext, button, type,
3841 scrollable, html, modal, timeout);
3844 public void nonBlockingDialog(int width, int height, String title,
3845 String message, String boxtext, String button, int type,
3846 boolean scrollable, boolean html, boolean modal, int timeout)
3850 type = JvOptionPane.WARNING_MESSAGE;
3852 JLabel jl = new JLabel(message);
3854 JTextComponent jtc = null;
3857 JTextPane jtp = new JTextPane();
3858 jtp.setContentType("text/html");
3859 jtp.setEditable(false);
3860 jtp.setAutoscrolls(true);
3861 jtp.setText(boxtext);
3867 JTextArea jta = new JTextArea(height, width);
3868 // jta.setLineWrap(true);
3869 jta.setEditable(false);
3870 jta.setWrapStyleWord(true);
3871 jta.setAutoscrolls(true);
3872 jta.setText(boxtext);
3877 JScrollPane jsp = scrollable
3878 ? new JScrollPane(jtc, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
3879 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED)
3882 JvOptionPane jvp = JvOptionPane.newOptionDialog(this);
3884 JPanel jp = new JPanel();
3885 jp.setLayout(new BoxLayout(jp, BoxLayout.Y_AXIS));
3887 if (message != null)
3889 jl.setAlignmentX(Component.LEFT_ALIGNMENT);
3892 if (boxtext != null)
3896 jsp.setAlignmentX(Component.LEFT_ALIGNMENT);
3901 jtc.setAlignmentX(Component.LEFT_ALIGNMENT);
3906 jvp.setResponseHandler(JOptionPane.YES_OPTION, () -> {
3908 jvp.setTimeout(timeout);
3909 JButton jb = new JButton(button);
3910 jvp.showDialogOnTopAsync(this, jp, title, JOptionPane.YES_OPTION, type,
3912 { button }, button, modal, new JButton[] { jb }, false);
3916 public AlignFrame getCurrentAlignFrame()
3918 return Jalview.getInstance().getCurrentAlignFrame();