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/help/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 reflection)
454 * 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() ? new AquaInternalFrameManager(
556 * desktop.getDesktopManager()) : desktop.getDesktopManager())));
559 Rectangle dims = getLastKnownDimensions("");
566 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
567 int xPos = Math.max(5, (screenSize.width - 900) / 2);
568 int yPos = Math.max(5, (screenSize.height - 650) / 2);
569 setBounds(xPos, yPos, 900, 650);
572 if (!Platform.isJS())
579 showNews.setVisible(false);
581 experimentalFeatures.setSelected(showExperimental());
583 getIdentifiersOrgData();
587 // Spawn a thread that shows the splashscreen
590 SwingUtilities.invokeLater(new Runnable()
595 new SplashScreen(true);
600 // Thread off a new instance of the file chooser - this reduces the time
601 // it takes to open it later on.
602 new Thread(new Runnable()
607 jalview.bin.Console.debug("Filechooser init thread started.");
608 String fileFormat = FileLoader.getUseDefaultFileFormat()
609 ? Cache.getProperty("DEFAULT_FILE_FORMAT")
611 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
613 jalview.bin.Console.debug("Filechooser init thread finished.");
616 // Add the service change listener
617 changeSupport.addJalviewPropertyChangeListener("services",
618 new PropertyChangeListener()
622 public void propertyChange(PropertyChangeEvent evt)
625 .debug("Firing service changed event for "
626 + evt.getNewValue());
627 JalviewServicesChanged(evt);
632 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
635 this.addMouseListener(ma = new MouseAdapter()
638 public void mousePressed(MouseEvent evt)
640 if (evt.isPopupTrigger()) // Mac
642 showPasteMenu(evt.getX(), evt.getY());
647 public void mouseReleased(MouseEvent evt)
649 if (evt.isPopupTrigger()) // Windows
651 showPasteMenu(evt.getX(), evt.getY());
655 desktop.addMouseListener(ma);
659 String ns = Jalview.getInstance().getJSNamespace();
663 String nsc = ns + (ns.length() > 0 ? ":" : "");
664 String splashId = nsc + "jalviewSplash";
666 * @j2sNative let splash = document.getElementById(splashId);
668 * if (splash != null) {
670 * splash.style.display = "none";
675 // used for jalviewjsTest
676 jalview.bin.Console.info("JALVIEWJS: CREATED DESKTOP");
682 * Answers true if user preferences to enable experimental features is True
687 public boolean showExperimental()
689 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
690 Boolean.FALSE.toString());
691 return Boolean.valueOf(experimental).booleanValue();
694 public void doConfigureStructurePrefs()
696 // configure services
697 StructureSelectionManager ssm = StructureSelectionManager
698 .getStructureSelectionManager(this);
699 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
701 ssm.setAddTempFacAnnot(
702 Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
703 ssm.setProcessSecondaryStructure(
704 Cache.getDefault(Preferences.STRUCT_FROM_PDB, true));
705 // JAL-3915 - RNAView is no longer an option so this has no effect
706 ssm.setSecStructServices(
707 Cache.getDefault(Preferences.USE_RNAVIEW, false));
711 ssm.setAddTempFacAnnot(false);
712 ssm.setProcessSecondaryStructure(false);
713 ssm.setSecStructServices(false);
717 public void checkForNews()
719 final Desktop me = this;
720 // Thread off the news reader, in case there are connection problems.
721 new Thread(new Runnable()
726 jalview.bin.Console.debug("Starting news thread.");
727 jvnews = new BlogReader(me);
728 showNews.setVisible(true);
729 jalview.bin.Console.debug("Completed news thread.");
734 public void getIdentifiersOrgData()
736 if (Cache.getProperty("NOIDENTIFIERSSERVICE") == null)
737 {// Thread off the identifiers fetcher
738 new Thread(new Runnable()
744 .debug("Downloading data from identifiers.org");
747 UrlDownloadClient.download(IdOrgSettings.getUrl(),
748 IdOrgSettings.getDownloadLocation());
749 } catch (IOException e)
752 .debug("Exception downloading identifiers.org data"
762 protected void showNews_actionPerformed(ActionEvent e)
764 showNews(showNews.isSelected());
767 void showNews(boolean visible)
769 jalview.bin.Console.debug((visible ? "Showing" : "Hiding") + " news.");
770 showNews.setSelected(visible);
771 if (visible && !jvnews.isVisible())
773 new Thread(new Runnable()
778 long progressId = IdUtils.newId(IdType.PROGRESS);
779 Desktop.instance.setProgressBar(
780 MessageManager.getString("status.refreshing_news"),
782 jvnews.refreshNews();
783 Desktop.instance.setProgressBar(null, progressId);
791 * recover the last known dimensions for a jalview window
794 * - empty string is desktop, all other windows have unique prefix
795 * @return null or last known dimensions scaled to current geometry (if last
796 * window geom was known)
798 Rectangle getLastKnownDimensions(String windowName)
800 // TODO: lock aspect ratio for scaling desktop Bug #0058199
801 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
802 String x = Cache.getProperty(windowName + "SCREEN_X");
803 String y = Cache.getProperty(windowName + "SCREEN_Y");
804 String width = Cache.getProperty(windowName + "SCREEN_WIDTH");
805 String height = Cache.getProperty(windowName + "SCREEN_HEIGHT");
806 if ((x != null) && (y != null) && (width != null) && (height != null))
808 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
809 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
810 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
812 // attempt #1 - try to cope with change in screen geometry - this
813 // version doesn't preserve original jv aspect ratio.
814 // take ratio of current screen size vs original screen size.
815 double sw = ((1f * screenSize.width) / (1f * Integer
816 .parseInt(Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
817 double sh = ((1f * screenSize.height) / (1f * Integer
818 .parseInt(Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
819 // rescale the bounds depending upon the current screen geometry.
820 ix = (int) (ix * sw);
821 iw = (int) (iw * sw);
822 iy = (int) (iy * sh);
823 ih = (int) (ih * sh);
824 if (ix >= screenSize.width)
826 jalview.bin.Console.debug(
827 "Window geometry location recall error: shifting horizontal to within screenbounds.");
828 ix = ix % screenSize.width;
830 if (iy >= screenSize.height)
832 jalview.bin.Console.debug(
833 "Window geometry location recall error: shifting vertical to within screenbounds.");
834 iy = iy % screenSize.height;
836 jalview.bin.Console.debug(
837 "Got last known dimensions for " + windowName + ": x:" + ix
838 + " y:" + iy + " width:" + iw + " height:" + ih);
840 // return dimensions for new instance
841 return new Rectangle(ix, iy, iw, ih);
846 void showPasteMenu(int x, int y)
848 JPopupMenu popup = new JPopupMenu();
849 JMenuItem item = new JMenuItem(
850 MessageManager.getString("label.paste_new_window"));
851 item.addActionListener(new ActionListener()
854 public void actionPerformed(ActionEvent evt)
861 popup.show(this, x, y);
866 // quick patch for JAL-4150 - needs some more work and test coverage
867 // TODO - unify below and AlignFrame.paste()
868 // TODO - write tests and fix AlignFrame.paste() which doesn't track if
869 // clipboard has come from a different alignment window than the one where
870 // paste has been called! JAL-4151
872 if (Desktop.jalviewClipboard != null)
874 // The clipboard was filled from within Jalview, we must use the
876 // And dataset from the copied alignment
877 SequenceI[] newseq = (SequenceI[]) Desktop.jalviewClipboard[0];
878 // be doubly sure that we create *new* sequence objects.
879 SequenceI[] sequences = new SequenceI[newseq.length];
880 for (int i = 0; i < newseq.length; i++)
882 sequences[i] = new Sequence(newseq[i]);
884 Alignment alignment = new Alignment(sequences);
885 // dataset is inherited
886 alignment.setDataset((Alignment) Desktop.jalviewClipboard[1]);
887 AlignFrame af = new AlignFrame(alignment, AlignFrame.DEFAULT_WIDTH,
888 AlignFrame.DEFAULT_HEIGHT);
889 String newtitle = new String("Copied sequences");
891 if (Desktop.jalviewClipboard[2] != null)
893 HiddenColumns hc = (HiddenColumns) Desktop.jalviewClipboard[2];
894 af.viewport.setHiddenColumns(hc);
897 Desktop.addInternalFrame(af, newtitle, AlignFrame.DEFAULT_WIDTH,
898 AlignFrame.DEFAULT_HEIGHT);
905 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
906 Transferable contents = c.getContents(this);
908 if (contents != null)
910 String file = (String) contents
911 .getTransferData(DataFlavor.stringFlavor);
913 FileFormatI format = new IdentifyFile().identify(file,
914 DataSourceType.PASTE);
916 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
919 } catch (Exception ex)
921 jalview.bin.Console.outPrintln(
922 "Unable to paste alignment from system clipboard:\n" + ex);
928 * Adds and opens the given frame to the desktop
939 public static synchronized void addInternalFrame(
940 final JInternalFrame frame, String title, int w, int h)
942 addInternalFrame(frame, title, true, w, h, true, false);
946 * Add an internal frame to the Jalview desktop
953 * When true, display frame immediately, otherwise, caller must call
954 * setVisible themselves.
960 public static synchronized void addInternalFrame(
961 final JInternalFrame frame, String title, boolean makeVisible,
964 addInternalFrame(frame, title, makeVisible, w, h, true, false);
968 * Add an internal frame to the Jalview desktop and make it visible
981 public static synchronized void addInternalFrame(
982 final JInternalFrame frame, String title, int w, int h,
985 addInternalFrame(frame, title, true, w, h, resizable, false);
989 * Add an internal frame to the Jalview desktop
996 * When true, display frame immediately, otherwise, caller must call
997 * setVisible themselves.
1004 * @param ignoreMinSize
1005 * Do not set the default minimum size for frame
1007 public static synchronized void addInternalFrame(
1008 final JInternalFrame frame, String title, boolean makeVisible,
1009 int w, int h, boolean resizable, boolean ignoreMinSize)
1012 // TODO: allow callers to determine X and Y position of frame (eg. via
1014 // TODO: consider fixing method to update entries in the window submenu with
1015 // the current window title
1017 frame.setTitle(title);
1018 if (frame.getWidth() < 1 || frame.getHeight() < 1)
1020 frame.setSize(w, h);
1022 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
1023 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
1024 // IF JALVIEW IS RUNNING HEADLESS
1025 // ///////////////////////////////////////////////
1026 if (instance == null || (System.getProperty("java.awt.headless") != null
1027 && System.getProperty("java.awt.headless").equals("true")))
1036 frame.setMinimumSize(
1037 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
1039 // Set default dimension for Alignment Frame window.
1040 // The Alignment Frame window could be added from a number of places,
1042 // I did this here in order not to miss out on any Alignment frame.
1043 if (frame instanceof AlignFrame)
1045 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
1046 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
1050 frame.setVisible(makeVisible);
1051 frame.setClosable(true);
1052 frame.setResizable(resizable);
1053 frame.setMaximizable(resizable);
1054 frame.setIconifiable(resizable);
1055 frame.setOpaque(Platform.isJS());
1057 if (frame.getX() < 1 && frame.getY() < 1)
1059 frame.setLocation(xOffset * openFrameCount,
1060 yOffset * ((openFrameCount - 1) % 10) + yOffset);
1064 * add an entry for the new frame in the Window menu (and remove it when the
1067 final JMenuItem menuItem = new JMenuItem(title);
1068 frame.addInternalFrameListener(new InternalFrameAdapter()
1071 public void internalFrameActivated(InternalFrameEvent evt)
1073 JInternalFrame itf = desktop.getSelectedFrame();
1076 if (itf instanceof AlignFrame)
1078 Jalview.getInstance().setCurrentAlignFrame((AlignFrame) itf);
1085 public void internalFrameClosed(InternalFrameEvent evt)
1087 PaintRefresher.RemoveComponent(frame);
1090 * defensive check to prevent frames being added half off the window
1092 if (openFrameCount > 0)
1098 * ensure no reference to alignFrame retained by menu item listener
1100 if (menuItem.getActionListeners().length > 0)
1102 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
1104 windowMenu.remove(menuItem);
1108 menuItem.addActionListener(new ActionListener()
1111 public void actionPerformed(ActionEvent e)
1115 frame.setSelected(true);
1116 frame.setIcon(false);
1117 } catch (java.beans.PropertyVetoException ex)
1124 setKeyBindings(frame);
1126 // Since the latest FlatLaf patch, we occasionally have problems showing
1127 // structureViewer frames...
1129 boolean shown = false;
1130 Exception last = null;
1137 } catch (IllegalArgumentException iaex)
1141 jalview.bin.Console.info("Squashed IllegalArgument Exception ("
1142 + tries + " left) for " + frame.getTitle(), iaex);
1146 } catch (InterruptedException iex)
1151 } while (!shown && tries > 0);
1154 jalview.bin.Console.error(
1155 "Serious Problem whilst showing window " + frame.getTitle(),
1159 windowMenu.add(menuItem);
1164 frame.setSelected(true);
1165 frame.requestFocus();
1166 } catch (java.beans.PropertyVetoException ve)
1168 } catch (java.lang.ClassCastException cex)
1170 jalview.bin.Console.warn(
1171 "Squashed a possible GUI implementation error. If you can recreate this, please look at https://issues.jalview.org/browse/JAL-869",
1177 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
1182 private static void setKeyBindings(JInternalFrame frame)
1184 @SuppressWarnings("serial")
1185 final Action closeAction = new AbstractAction()
1188 public void actionPerformed(ActionEvent e)
1195 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1197 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1198 InputEvent.CTRL_DOWN_MASK);
1199 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1200 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
1202 InputMap inputMap = frame
1203 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1204 String ctrlW = ctrlWKey.toString();
1205 inputMap.put(ctrlWKey, ctrlW);
1206 inputMap.put(cmdWKey, ctrlW);
1208 ActionMap actionMap = frame.getActionMap();
1209 actionMap.put(ctrlW, closeAction);
1213 public void lostOwnership(Clipboard clipboard, Transferable contents)
1217 Desktop.jalviewClipboard = null;
1220 internalCopy = false;
1224 public void dragEnter(DropTargetDragEvent evt)
1229 public void dragExit(DropTargetEvent evt)
1234 public void dragOver(DropTargetDragEvent evt)
1239 public void dropActionChanged(DropTargetDragEvent evt)
1250 public void drop(DropTargetDropEvent evt)
1252 boolean success = true;
1253 // JAL-1552 - acceptDrop required before getTransferable call for
1254 // Java's Transferable for native dnd
1255 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1256 Transferable t = evt.getTransferable();
1257 List<Object> files = new ArrayList<>();
1258 List<DataSourceType> protocols = new ArrayList<>();
1262 Desktop.transferFromDropTarget(files, protocols, evt, t);
1263 } catch (Exception e)
1265 e.printStackTrace();
1273 for (int i = 0; i < files.size(); i++)
1275 // BH 2018 File or String
1276 Object file = files.get(i);
1277 String fileName = file.toString();
1278 DataSourceType protocol = (protocols == null)
1279 ? DataSourceType.FILE
1281 FileFormatI format = null;
1283 if (fileName.endsWith(".jar"))
1285 format = FileFormat.Jalview;
1290 format = new IdentifyFile().identify(file, protocol);
1292 if (file instanceof File)
1294 Platform.cacheFileData((File) file);
1296 new FileLoader().LoadFile(null, file, protocol, format);
1299 } catch (Exception ex)
1304 evt.dropComplete(success); // need this to ensure input focus is properly
1305 // transfered to any new windows created
1315 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1317 String fileFormat = FileLoader.getUseDefaultFileFormat()
1318 ? Cache.getProperty("DEFAULT_FILE_FORMAT")
1320 JalviewFileChooser chooser = JalviewFileChooser.forRead(
1321 Cache.getProperty("LAST_DIRECTORY"), fileFormat,
1322 BackupFiles.getEnabled());
1324 chooser.setFileView(new JalviewFileView());
1325 chooser.setDialogTitle(
1326 MessageManager.getString("label.open_local_file"));
1327 chooser.setToolTipText(MessageManager.getString("action.open"));
1329 chooser.setResponseHandler(0, () -> {
1330 File selectedFile = chooser.getSelectedFile();
1331 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1333 FileFormatI format = chooser.getSelectedFormat();
1336 * Call IdentifyFile to verify the file contains what its extension implies.
1337 * Skip this step for dynamically added file formats, because IdentifyFile does
1338 * not know how to recognise them.
1340 if (FileFormats.getInstance().isIdentifiable(format))
1344 format = new IdentifyFile().identify(selectedFile,
1345 DataSourceType.FILE);
1346 } catch (FileFormatException e)
1348 // format = null; //??
1352 new FileLoader().LoadFile(viewport, selectedFile, DataSourceType.FILE,
1355 chooser.showOpenDialog(this);
1359 * Shows a dialog for input of a URL at which to retrieve alignment data
1364 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1366 // This construct allows us to have a wider textfield
1368 JLabel label = new JLabel(
1369 MessageManager.getString("label.input_file_url"));
1371 JPanel panel = new JPanel(new GridLayout(2, 1));
1375 * the URL to fetch is input in Java: an editable combobox with history JS:
1376 * (pending JAL-3038) a plain text field
1379 String urlBase = "https://www.";
1380 if (Platform.isJS())
1382 history = new JTextField(urlBase, 35);
1391 JComboBox<String> asCombo = new JComboBox<>();
1392 asCombo.setPreferredSize(new Dimension(400, 20));
1393 asCombo.setEditable(true);
1394 asCombo.addItem(urlBase);
1395 String historyItems = Cache.getProperty("RECENT_URL");
1396 if (historyItems != null)
1398 for (String token : historyItems.split("\\t"))
1400 asCombo.addItem(token);
1407 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1408 MessageManager.getString("action.cancel") };
1409 Runnable action = () -> {
1410 @SuppressWarnings("unchecked")
1411 String url = (history instanceof JTextField
1412 ? ((JTextField) history).getText()
1413 : ((JComboBox<String>) history).getEditor().getItem()
1414 .toString().trim());
1416 if (url.toLowerCase(Locale.ROOT).endsWith(".jar"))
1418 if (viewport != null)
1420 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1421 FileFormat.Jalview);
1425 new FileLoader().LoadFile(url, DataSourceType.URL,
1426 FileFormat.Jalview);
1431 FileFormatI format = null;
1434 format = new IdentifyFile().identify(url, DataSourceType.URL);
1435 } catch (FileFormatException e)
1437 // TODO revise error handling, distinguish between
1438 // URL not found and response not valid
1443 String msg = MessageManager.formatMessage("label.couldnt_locate",
1445 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1446 MessageManager.getString("label.url_not_found"),
1447 JvOptionPane.WARNING_MESSAGE);
1451 if (viewport != null)
1453 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1458 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1462 String dialogOption = MessageManager
1463 .getString("label.input_alignment_from_url");
1464 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1465 .showInternalDialog(panel, dialogOption,
1466 JvOptionPane.YES_NO_CANCEL_OPTION,
1467 JvOptionPane.PLAIN_MESSAGE, null, options,
1468 MessageManager.getString("action.ok"));
1472 * Opens the CutAndPaste window for the user to paste an alignment in to
1475 * - if not null, the pasted alignment is added to the current
1476 * alignment; if null, to a new alignment window
1479 public void inputTextboxMenuItem_actionPerformed(
1480 AlignmentViewPanel viewPanel)
1482 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1483 cap.setForInput(viewPanel);
1484 Desktop.addInternalFrame(cap,
1485 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1490 * Check with user and saving files before actually quitting
1492 public void desktopQuit()
1494 desktopQuit(true, false);
1498 * close everything, stash window geometries, and shut down all associated
1502 * - sets the dispose on close flag - JVM may terminate when set
1503 * @param terminateJvm
1504 * - quit with prejudice - stops the JVM.
1506 public void quitTheDesktop(boolean dispose, boolean terminateJvm)
1508 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1509 Cache.setProperty("SCREENGEOMETRY_WIDTH", screen.width + "");
1510 Cache.setProperty("SCREENGEOMETRY_HEIGHT", screen.height + "");
1511 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1512 getWidth(), getHeight()));
1514 if (jconsole != null)
1516 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1517 jconsole.stopConsole();
1522 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1525 // Frames should all close automatically. Keeping external
1526 // viewers open should already be decided by user.
1527 closeAll_actionPerformed(null);
1529 if (dialogExecutor != null)
1531 dialogExecutor.shutdownNow();
1534 if (groovyConsole != null)
1536 // suppress a possible repeat prompt to save script
1537 groovyConsole.setDirty(false);
1540 if (((Window) groovyConsole.getFrame()) != null
1541 && ((Window) groovyConsole.getFrame()).isVisible())
1543 // console is visible -- FIXME JAL-4327
1544 groovyConsole.exit();
1548 // console is not, so just let it dispose itself when we shutdown
1549 // we don't call groovyConsole.exit() because it calls the shutdown
1550 // handler with invokeAndWait() causing deadlock
1551 groovyConsole = null;
1557 // note that shutdown hook will not be run
1558 jalview.bin.Console.debug("Force Quit selected by user");
1559 Runtime.getRuntime().halt(0);
1562 jalview.bin.Console.debug("Quit selected by user");
1565 instance.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
1566 // instance.dispose();
1570 public QuitHandler.QResponse desktopQuit(boolean ui, boolean disposeFlag)
1572 final Runnable doDesktopQuit = () -> {
1574 // FIRST !! check for aborted quit
1575 if (QuitHandler.quitCancelled())
1578 .debug("Quit was cancelled - Desktop aborting quit");
1582 // Proceed with quitting
1583 quitTheDesktop(disposeFlag,
1584 QuitHandler.gotQuitResponse() == QResponse.FORCE_QUIT);
1589 return QuitHandler.getQuitResponse(ui, doDesktopQuit, doDesktopQuit,
1590 QuitHandler.defaultCancelQuit);
1594 * Exits the program and the JVM.
1596 * Don't call this directly
1598 * - use desktopQuit() above to tidy up first.
1600 * - use closeDesktop() to shutdown Jalview without shutting down the JVM
1606 // this will run the shutdownHook but QuitHandler.getQuitResponse() should
1607 // not run a second time if gotQuitResponse flag has been set (i.e. user
1608 // confirmed quit of some kind).
1609 Jalview.exit("Desktop exiting.", ExitCode.OK);
1612 private void storeLastKnownDimensions(String string, Rectangle jc)
1614 jalview.bin.Console.debug("Storing last known dimensions for " + string
1615 + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1616 + " height:" + jc.height);
1618 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1619 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1620 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1621 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1631 public void aboutMenuItem_actionPerformed(ActionEvent e)
1633 new Thread(new Runnable()
1638 new SplashScreen(false);
1644 * Returns the html text for the About screen, including any available version
1645 * number, build details, author details and citation reference, but without
1646 * the enclosing {@code html} tags
1650 public String getAboutMessage()
1652 StringBuilder message = new StringBuilder(1024);
1653 message.append("<div style=\"font-family: sans-serif;\">")
1654 .append("<h1><strong>Version: ")
1655 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1656 .append("<strong>Built: <em>")
1657 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1658 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1659 .append("</strong>");
1661 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1662 if (latestVersion.equals("Checking"))
1664 // JBP removed this message for 2.11: May be reinstated in future version
1665 // message.append("<br>...Checking latest version...</br>");
1667 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1669 boolean red = false;
1670 if (Cache.getProperty("VERSION").toLowerCase(Locale.ROOT)
1671 .indexOf("automated build") == -1)
1674 // Displayed when code version and jnlp version do not match and code
1675 // version is not a development build
1676 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1679 message.append("<br>!! Version ")
1680 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1681 .append(" is available for download from ")
1682 .append(Cache.getDefault("www.jalview.org",
1683 "https://www.jalview.org"))
1687 message.append("</div>");
1690 message.append("<br>Authors: ");
1691 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1692 message.append(CITATION);
1694 message.append("</div>");
1696 return message.toString();
1700 * Action on requesting Help documentation
1703 public void documentationMenuItem_actionPerformed()
1707 if (Platform.isJS())
1709 BrowserLauncher.openURL("https://www.jalview.org/help.html");
1718 Help.showHelpWindow();
1720 } catch (Exception ex)
1723 .errPrintln("Error opening help: " + ex.getMessage());
1728 public void closeAll_actionPerformed(ActionEvent e)
1730 // TODO show a progress bar while closing?
1731 JInternalFrame[] frames = desktop.getAllFrames();
1732 for (int i = 0; i < frames.length; i++)
1736 frames[i].setClosed(true);
1737 } catch (java.beans.PropertyVetoException ex)
1741 Jalview.getInstance().setCurrentAlignFrame(null);
1742 jalview.bin.Console.info("ALL CLOSED");
1745 * reset state of singleton objects as appropriate (clear down session state
1746 * when all windows are closed)
1748 StructureSelectionManager ssm = StructureSelectionManager
1749 .getStructureSelectionManager(this);
1756 public int structureViewersStillRunningCount()
1759 JInternalFrame[] frames = desktop.getAllFrames();
1760 for (int i = 0; i < frames.length; i++)
1762 if (frames[i] != null
1763 && frames[i] instanceof JalviewStructureDisplayI)
1765 if (((JalviewStructureDisplayI) frames[i]).stillRunning())
1773 public void raiseRelated_actionPerformed(ActionEvent e)
1775 reorderAssociatedWindows(false, false);
1779 public void minimizeAssociated_actionPerformed(ActionEvent e)
1781 reorderAssociatedWindows(true, false);
1784 void closeAssociatedWindows()
1786 reorderAssociatedWindows(false, true);
1792 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1796 protected void garbageCollect_actionPerformed(ActionEvent e)
1798 // We simply collect the garbage
1799 jalview.bin.Console.debug("Collecting garbage...");
1801 jalview.bin.Console.debug("Finished garbage collection.");
1807 * @see jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.
1811 protected void showMemusage_actionPerformed(ActionEvent e)
1813 desktop.showMemoryUsage(showMemusage.isSelected());
1820 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1824 protected void showConsole_actionPerformed(ActionEvent e)
1826 showConsole(showConsole.isSelected());
1829 Console jconsole = null;
1832 * control whether the java console is visible or not
1836 void showConsole(boolean selected)
1838 // TODO: decide if we should update properties file
1839 if (jconsole != null) // BH 2018
1841 showConsole.setSelected(selected);
1842 Cache.setProperty("SHOW_JAVA_CONSOLE",
1843 Boolean.valueOf(selected).toString());
1844 jconsole.setVisible(selected);
1848 void reorderAssociatedWindows(boolean minimize, boolean close)
1850 JInternalFrame[] frames = desktop.getAllFrames();
1851 if (frames == null || frames.length < 1)
1856 AlignmentViewport source = null, target = null;
1857 if (frames[0] instanceof AlignFrame)
1859 source = ((AlignFrame) frames[0]).getCurrentView();
1861 else if (frames[0] instanceof TreePanel)
1863 source = ((TreePanel) frames[0]).getViewPort();
1865 else if (frames[0] instanceof PCAPanel)
1867 source = ((PCAPanel) frames[0]).av;
1869 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1871 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1876 for (int i = 0; i < frames.length; i++)
1879 if (frames[i] == null)
1883 if (frames[i] instanceof AlignFrame)
1885 target = ((AlignFrame) frames[i]).getCurrentView();
1887 else if (frames[i] instanceof TreePanel)
1889 target = ((TreePanel) frames[i]).getViewPort();
1891 else if (frames[i] instanceof PCAPanel)
1893 target = ((PCAPanel) frames[i]).av;
1895 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1897 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1900 if (source == target)
1906 frames[i].setClosed(true);
1910 frames[i].setIcon(minimize);
1913 frames[i].toFront();
1917 } catch (java.beans.PropertyVetoException ex)
1932 protected void preferences_actionPerformed(ActionEvent e)
1934 Preferences.openPreferences();
1938 * Prompts the user to choose a file and then saves the Jalview state as a
1939 * Jalview project file
1942 public void saveState_actionPerformed()
1944 saveState_actionPerformed(false);
1947 public void saveState_actionPerformed(boolean saveAs)
1949 java.io.File projectFile = getProjectFile();
1950 // autoSave indicates we already have a file and don't need to ask
1951 boolean autoSave = projectFile != null && !saveAs
1952 && BackupFiles.getEnabled();
1954 // jalview.bin.Console.outPrintln("autoSave="+autoSave+",
1955 // projectFile='"+projectFile+"',
1956 // saveAs="+saveAs+", Backups
1957 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1959 boolean approveSave = false;
1962 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1965 chooser.setFileView(new JalviewFileView());
1966 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1968 int value = chooser.showSaveDialog(this);
1970 if (value == JalviewFileChooser.APPROVE_OPTION)
1972 projectFile = chooser.getSelectedFile();
1973 setProjectFile(projectFile);
1978 if (approveSave || autoSave)
1980 final Desktop me = this;
1981 final java.io.File chosenFile = projectFile;
1982 new Thread(new Runnable()
1987 // TODO: refactor to Jalview desktop session controller action.
1988 setProgressBar(MessageManager.formatMessage(
1989 "label.saving_jalview_project", new Object[]
1990 { chosenFile.getName() }),
1991 IdUtils.newId(IdType.PROGRESS, chosenFile));
1992 Cache.setProperty("LAST_DIRECTORY", chosenFile.getParent());
1993 // TODO catch and handle errors for savestate
1994 // TODO prevent user from messing with the Desktop whilst we're saving
1997 boolean doBackup = BackupFiles.getEnabled();
1998 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile)
2001 new Jalview2XML().saveState(
2002 doBackup ? backupfiles.getTempFile() : chosenFile);
2006 backupfiles.setWriteSuccess(true);
2007 backupfiles.rollBackupsAndRenameTempFile();
2009 } catch (OutOfMemoryError oom)
2011 new OOMWarning("Whilst saving current state to "
2012 + chosenFile.getName(), oom);
2013 } catch (Exception ex)
2015 jalview.bin.Console.error("Problems whilst trying to save to "
2016 + chosenFile.getName(), ex);
2017 JvOptionPane.showMessageDialog(me,
2018 MessageManager.formatMessage(
2019 "label.error_whilst_saving_current_state_to",
2021 { chosenFile.getName() }),
2022 MessageManager.getString("label.couldnt_save_project"),
2023 JvOptionPane.WARNING_MESSAGE);
2025 setProgressBar(null, IdUtils.newId(IdType.PROGRESS, chosenFile));
2032 public void saveAsState_actionPerformed(ActionEvent e)
2034 saveState_actionPerformed(true);
2037 protected void setProjectFile(File choice)
2039 this.projectFile = choice;
2042 public File getProjectFile()
2044 return this.projectFile;
2048 * Shows a file chooser dialog and tries to read in the selected file as a
2052 public void loadState_actionPerformed()
2054 final String[] suffix = new String[] { "jvp", "jar" };
2055 final String[] desc = new String[] { "Jalview Project",
2056 "Jalview Project (old)" };
2057 JalviewFileChooser chooser = new JalviewFileChooser(
2058 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
2059 "Jalview Project", true, BackupFiles.getEnabled()); // last two
2063 chooser.setFileView(new JalviewFileView());
2064 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
2065 chooser.setResponseHandler(0, () -> {
2066 File selectedFile = chooser.getSelectedFile();
2067 setProjectFile(selectedFile);
2068 String choice = selectedFile.getAbsolutePath();
2069 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
2070 new Thread(new Runnable()
2077 new Jalview2XML().loadJalviewAlign(selectedFile);
2078 } catch (OutOfMemoryError oom)
2080 new OOMWarning("Whilst loading project from " + choice, oom);
2081 } catch (Exception ex)
2083 jalview.bin.Console.error(
2084 "Problems whilst loading project from " + choice, ex);
2085 JvOptionPane.showMessageDialog(Desktop.desktop,
2086 MessageManager.formatMessage(
2087 "label.error_whilst_loading_project_from",
2090 MessageManager.getString("label.couldnt_load_project"),
2091 JvOptionPane.WARNING_MESSAGE);
2094 }, "Project Loader").start();
2097 chooser.showOpenDialog(this);
2101 public void inputSequence_actionPerformed(ActionEvent e)
2103 new SequenceFetcher(this);
2106 JPanel progressPanel;
2108 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
2110 public void startLoading(final Object fileName)
2112 if (fileLoadingCount == 0)
2114 fileLoadingPanels.add(addProgressPanel(MessageManager
2115 .formatMessage("label.loading_file", new Object[]
2121 private JPanel addProgressPanel(String string)
2123 if (progressPanel == null)
2125 progressPanel = new JPanel(new GridLayout(1, 1));
2126 totalProgressCount = 0;
2127 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
2129 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
2130 JProgressBar progressBar = new JProgressBar();
2131 progressBar.setIndeterminate(true);
2133 thisprogress.add(new JLabel(string), BorderLayout.WEST);
2135 thisprogress.add(progressBar, BorderLayout.CENTER);
2136 progressPanel.add(thisprogress);
2137 ((GridLayout) progressPanel.getLayout()).setRows(
2138 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
2139 ++totalProgressCount;
2140 instance.validate();
2141 return thisprogress;
2144 int totalProgressCount = 0;
2146 private void removeProgressPanel(JPanel progbar)
2148 if (progressPanel != null)
2150 synchronized (progressPanel)
2152 progressPanel.remove(progbar);
2153 GridLayout gl = (GridLayout) progressPanel.getLayout();
2154 gl.setRows(gl.getRows() - 1);
2155 if (--totalProgressCount < 1)
2157 this.getContentPane().remove(progressPanel);
2158 progressPanel = null;
2165 public void stopLoading()
2168 if (fileLoadingCount < 1)
2170 while (fileLoadingPanels.size() > 0)
2172 removeProgressPanel(fileLoadingPanels.remove(0));
2174 fileLoadingPanels.clear();
2175 fileLoadingCount = 0;
2180 public static int getViewCount(String alignmentId)
2182 AlignmentViewport[] aps = getViewports(alignmentId);
2183 return (aps == null) ? 0 : aps.length;
2188 * @param alignmentId
2189 * - if null, all sets are returned
2190 * @return all AlignmentPanels concerning the alignmentId sequence set
2192 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
2194 if (Desktop.desktop == null)
2196 // no frames created and in headless mode
2197 // TODO: verify that frames are recoverable when in headless mode
2200 List<AlignmentPanel> aps = new ArrayList<>();
2201 AlignFrame[] frames = Desktop.getDesktopAlignFrames();
2206 for (AlignFrame af : frames)
2208 for (AlignmentPanel ap : af.alignPanels)
2210 if (alignmentId == null
2211 || alignmentId.equals(ap.av.getSequenceSetId()))
2217 if (aps.size() == 0)
2221 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
2226 * get all the viewports on an alignment.
2228 * @param sequenceSetId
2229 * unique alignment id (may be null - all viewports returned in that
2231 * @return all viewports on the alignment bound to sequenceSetId
2233 public static AlignmentViewport[] getViewports(String sequenceSetId)
2235 List<AlignmentViewport> viewp = new ArrayList<>();
2236 if (desktop != null)
2238 AlignFrame[] frames = Desktop.getDesktopAlignFrames();
2240 for (AlignFrame afr : frames)
2242 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
2243 .equals(sequenceSetId))
2245 if (afr.alignPanels != null)
2247 for (AlignmentPanel ap : afr.alignPanels)
2249 if (sequenceSetId == null
2250 || sequenceSetId.equals(ap.av.getSequenceSetId()))
2258 viewp.add(afr.getViewport());
2262 if (viewp.size() > 0)
2264 return viewp.toArray(new AlignmentViewport[viewp.size()]);
2271 * Explode the views in the given frame into separate AlignFrame
2275 public static void explodeViews(AlignFrame af)
2277 int size = af.alignPanels.size();
2283 // FIXME: ideally should use UI interface API
2284 FeatureSettings viewFeatureSettings = (af.featureSettings != null
2285 && af.featureSettings.isOpen()) ? af.featureSettings : null;
2286 Rectangle fsBounds = af.getFeatureSettingsGeometry();
2287 for (int i = 0; i < size; i++)
2289 AlignmentPanel ap = af.alignPanels.get(i);
2291 AlignFrame newaf = new AlignFrame(ap);
2293 // transfer reference for existing feature settings to new alignFrame
2294 if (ap == af.alignPanel)
2296 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2298 newaf.featureSettings = viewFeatureSettings;
2300 newaf.setFeatureSettingsGeometry(fsBounds);
2304 * Restore the view's last exploded frame geometry if known. Multiple views from
2305 * one exploded frame share and restore the same (frame) position and size.
2307 Rectangle geometry = ap.av.getExplodedGeometry();
2308 if (geometry != null)
2310 newaf.setBounds(geometry);
2313 ap.av.setGatherViewsHere(false);
2315 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2316 AlignFrame.DEFAULT_HEIGHT);
2317 // and materialise a new feature settings dialog instance for the new
2319 // (closes the old as if 'OK' was pressed)
2320 if (ap == af.alignPanel && newaf.featureSettings != null
2321 && newaf.featureSettings.isOpen()
2322 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2324 newaf.showFeatureSettingsUI();
2328 af.featureSettings = null;
2329 af.alignPanels.clear();
2330 af.closeMenuItem_actionPerformed(true);
2335 * Gather expanded views (separate AlignFrame's) with the same sequence set
2336 * identifier back in to this frame as additional views, and close the
2337 * expanded views. Note the expanded frames may themselves have multiple
2338 * views. We take the lot.
2342 public void gatherViews(AlignFrame source)
2344 source.viewport.setGatherViewsHere(true);
2345 source.viewport.setExplodedGeometry(source.getBounds());
2346 JInternalFrame[] frames = desktop.getAllFrames();
2347 String viewId = source.viewport.getSequenceSetId();
2348 for (int t = 0; t < frames.length; t++)
2350 if (frames[t] instanceof AlignFrame && frames[t] != source)
2352 AlignFrame af = (AlignFrame) frames[t];
2353 boolean gatherThis = false;
2354 for (int a = 0; a < af.alignPanels.size(); a++)
2356 AlignmentPanel ap = af.alignPanels.get(a);
2357 if (viewId.equals(ap.av.getSequenceSetId()))
2360 ap.av.setGatherViewsHere(false);
2361 ap.av.setExplodedGeometry(af.getBounds());
2362 source.addAlignmentPanel(ap, false);
2368 if (af.featureSettings != null && af.featureSettings.isOpen())
2370 if (source.featureSettings == null)
2372 // preserve the feature settings geometry for this frame
2373 source.featureSettings = af.featureSettings;
2374 source.setFeatureSettingsGeometry(
2375 af.getFeatureSettingsGeometry());
2379 // close it and forget
2380 af.featureSettings.close();
2383 af.alignPanels.clear();
2384 af.closeMenuItem_actionPerformed(true);
2389 // refresh the feature setting UI for the source frame if it exists
2390 if (source.featureSettings != null && source.featureSettings.isOpen())
2392 source.showFeatureSettingsUI();
2397 public JInternalFrame[] getAllFrames()
2399 return desktop.getAllFrames();
2403 * Checks the given url to see if it gives a response indicating that the user
2404 * should be informed of a new questionnaire.
2408 public void checkForQuestionnaire(String url)
2410 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2411 // javax.swing.SwingUtilities.invokeLater(jvq);
2412 new Thread(jvq).start();
2415 public void checkURLLinks()
2417 // Thread off the URL link checker
2418 addDialogThread(new Runnable()
2423 if (Cache.getDefault("CHECKURLLINKS", true))
2425 // check what the actual links are - if it's just the default don't
2426 // bother with the warning
2427 List<String> links = Preferences.sequenceUrlLinks
2430 // only need to check links if there is one with a
2431 // SEQUENCE_ID which is not the default EMBL_EBI link
2432 ListIterator<String> li = links.listIterator();
2433 boolean check = false;
2434 List<JLabel> urls = new ArrayList<>();
2435 while (li.hasNext())
2437 String link = li.next();
2438 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2439 && !UrlConstants.isDefaultString(link))
2442 int barPos = link.indexOf("|");
2443 String urlMsg = barPos == -1 ? link
2444 : link.substring(0, barPos) + ": "
2445 + link.substring(barPos + 1);
2446 urls.add(new JLabel(urlMsg));
2454 // ask user to check in case URL links use old style tokens
2455 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2456 JPanel msgPanel = new JPanel();
2457 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2458 msgPanel.add(Box.createVerticalGlue());
2459 JLabel msg = new JLabel(MessageManager
2460 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2461 JLabel msg2 = new JLabel(MessageManager
2462 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2464 for (JLabel url : urls)
2470 final JCheckBox jcb = new JCheckBox(
2471 MessageManager.getString("label.do_not_display_again"));
2472 jcb.addActionListener(new ActionListener()
2475 public void actionPerformed(ActionEvent e)
2477 // update Cache settings for "don't show this again"
2478 boolean showWarningAgain = !jcb.isSelected();
2479 Cache.setProperty("CHECKURLLINKS",
2480 Boolean.valueOf(showWarningAgain).toString());
2485 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2487 .getString("label.SEQUENCE_ID_no_longer_used"),
2488 JvOptionPane.WARNING_MESSAGE);
2495 * Proxy class for JDesktopPane which optionally displays the current memory
2496 * usage and highlights the desktop area with a red bar if free memory runs
2501 public class MyDesktopPane extends JDesktopPane implements Runnable
2503 private static final float ONE_MB = 1048576f;
2505 boolean showMemoryUsage = false;
2509 java.text.NumberFormat df;
2511 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2514 public MyDesktopPane(boolean showMemoryUsage)
2516 showMemoryUsage(showMemoryUsage);
2519 public void showMemoryUsage(boolean showMemory)
2521 this.showMemoryUsage = showMemory;
2524 Thread worker = new Thread(this);
2530 public boolean isShowMemoryUsage()
2532 return showMemoryUsage;
2538 df = java.text.NumberFormat.getNumberInstance();
2539 df.setMaximumFractionDigits(2);
2540 runtime = Runtime.getRuntime();
2542 while (showMemoryUsage)
2546 maxMemory = runtime.maxMemory() / ONE_MB;
2547 allocatedMemory = runtime.totalMemory() / ONE_MB;
2548 freeMemory = runtime.freeMemory() / ONE_MB;
2549 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2551 percentUsage = (totalFreeMemory / maxMemory) * 100;
2553 // if (percentUsage < 20)
2555 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2557 // instance.set.setBorder(border1);
2560 // sleep after showing usage
2562 } catch (Exception ex)
2564 ex.printStackTrace();
2570 public void paintComponent(Graphics g)
2572 if (showMemoryUsage && g != null && df != null)
2574 if (percentUsage < 20)
2576 g.setColor(Color.red);
2578 FontMetrics fm = g.getFontMetrics();
2581 g.drawString(MessageManager.formatMessage("label.memory_stats",
2583 { df.format(totalFreeMemory), df.format(maxMemory),
2584 df.format(percentUsage) }),
2585 10, getHeight() - fm.getHeight());
2589 // output debug scale message. Important for jalview.bin.HiDPISettingTest2
2590 Desktop.debugScaleMessage(Desktop.getDesktop().getGraphics());
2595 * Accessor method to quickly get all the AlignmentFrames loaded.
2597 * @return an array of AlignFrame, or null if none found
2600 public AlignFrame[] getAlignFrames()
2602 if (desktop == null)
2607 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2613 List<AlignFrame> avp = new ArrayList<>();
2615 for (int i = frames.length - 1; i > -1; i--)
2617 if (frames[i] instanceof AlignFrame)
2619 avp.add((AlignFrame) frames[i]);
2621 else if (frames[i] instanceof SplitFrame)
2624 * Also check for a split frame containing an AlignFrame
2626 GSplitFrame sf = (GSplitFrame) frames[i];
2627 if (sf.getTopFrame() instanceof AlignFrame)
2629 avp.add((AlignFrame) sf.getTopFrame());
2631 if (sf.getBottomFrame() instanceof AlignFrame)
2633 avp.add((AlignFrame) sf.getBottomFrame());
2637 if (avp.size() == 0)
2641 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2648 public static AlignFrame[] getDesktopAlignFrames()
2650 if (Jalview.isHeadlessMode())
2652 // Desktop.desktop is null in headless mode
2653 return Jalview.getInstance().getAlignFrames();
2656 if (instance != null && desktop != null)
2658 return instance.getAlignFrames();
2665 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2669 public GStructureViewer[] getJmols()
2671 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2677 List<GStructureViewer> avp = new ArrayList<>();
2679 for (int i = frames.length - 1; i > -1; i--)
2681 if (frames[i] instanceof AppJmol)
2683 GStructureViewer af = (GStructureViewer) frames[i];
2687 if (avp.size() == 0)
2691 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2696 * Add Groovy Support to Jalview
2699 public void groovyShell_actionPerformed()
2703 openGroovyConsole();
2704 } catch (Exception ex)
2706 jalview.bin.Console.error("Groovy Console creation failed.", ex);
2707 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2709 MessageManager.getString("label.couldnt_create_groovy_shell"),
2710 MessageManager.getString("label.groovy_support_failed"),
2711 JvOptionPane.ERROR_MESSAGE);
2716 * Open the Groovy console
2718 void openGroovyConsole()
2720 if (groovyConsole == null)
2722 JalviewObjectI j = new JalviewObject(this);
2723 groovyConsole = new groovy.console.ui.Console();
2724 groovyConsole.setVariable(JalviewObjectI.jalviewObjectName, j);
2725 groovyConsole.setVariable(JalviewObjectI.currentAlFrameName,
2726 getCurrentAlignFrame());
2727 groovyConsole.run();
2730 * We allow only one console at a time, so that AlignFrame menu option
2731 * 'Calculate | Run Groovy script' is unambiguous. Disable 'Groovy Console', and
2732 * enable 'Run script', when the console is opened, and the reverse when it is
2735 Window window = (Window) groovyConsole.getFrame();
2736 window.addWindowListener(new WindowAdapter()
2739 public void windowClosed(WindowEvent e)
2742 * rebind CMD-Q from Groovy Console to Jalview Quit
2745 enableExecuteGroovy(false);
2751 * show Groovy console window (after close and reopen)
2753 ((Window) groovyConsole.getFrame()).setVisible(true);
2756 * if we got this far, enable 'Run Groovy' in AlignFrame menus and disable
2757 * opening a second console
2759 enableExecuteGroovy(true);
2763 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this
2764 * binding when opened
2766 protected void addQuitHandler()
2769 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
2771 .getKeyStroke(KeyEvent.VK_Q,
2772 jalview.util.ShortcutKeyMaskExWrapper
2773 .getMenuShortcutKeyMaskEx()),
2775 getRootPane().getActionMap().put("Quit", new AbstractAction()
2778 public void actionPerformed(ActionEvent e)
2786 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2789 * true if Groovy console is open
2791 public void enableExecuteGroovy(boolean enabled)
2794 * disable opening a second Groovy console (or re-enable when the console is
2797 groovyShell.setEnabled(!enabled);
2799 AlignFrame[] alignFrames = getDesktopAlignFrames();
2800 if (alignFrames != null)
2802 for (AlignFrame af : alignFrames)
2804 af.setGroovyEnabled(enabled);
2810 * Progress bars managed by the IProgressIndicator method.
2812 private Hashtable<Long, JPanel> progressBars;
2814 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2819 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2822 public void setProgressBar(String message, long id)
2824 if (progressBars == null)
2826 progressBars = new Hashtable<>();
2827 progressBarHandlers = new Hashtable<>();
2830 if (progressBars.get(Long.valueOf(id)) != null)
2832 JPanel panel = progressBars.remove(Long.valueOf(id));
2833 if (progressBarHandlers.contains(Long.valueOf(id)))
2835 progressBarHandlers.remove(Long.valueOf(id));
2837 removeProgressPanel(panel);
2841 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2848 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2849 * jalview.gui.IProgressIndicatorHandler)
2852 public void registerHandler(final long id,
2853 final IProgressIndicatorHandler handler)
2855 if (progressBarHandlers == null
2856 || !progressBars.containsKey(Long.valueOf(id)))
2858 throw new Error(MessageManager.getString(
2859 "error.call_setprogressbar_before_registering_handler"));
2861 progressBarHandlers.put(Long.valueOf(id), handler);
2862 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2863 if (handler.canCancel())
2865 JButton cancel = new JButton(
2866 MessageManager.getString("action.cancel"));
2867 final IProgressIndicator us = this;
2868 cancel.addActionListener(new ActionListener()
2872 public void actionPerformed(ActionEvent e)
2874 handler.cancelActivity(id);
2875 us.setProgressBar(MessageManager
2876 .formatMessage("label.cancelled_params", new Object[]
2877 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2881 progressPanel.add(cancel, BorderLayout.EAST);
2887 * @return true if any progress bars are still active
2890 public boolean operationInProgress()
2892 if (progressBars != null && progressBars.size() > 0)
2900 * This will return the first AlignFrame holding the given viewport instance.
2901 * It will break if there are more than one AlignFrames viewing a particular
2905 * @return alignFrame for viewport
2907 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2909 if (desktop != null)
2911 AlignmentPanel[] aps = getAlignmentPanels(
2912 viewport.getSequenceSetId());
2913 for (int panel = 0; aps != null && panel < aps.length; panel++)
2915 if (aps[panel] != null && aps[panel].av == viewport)
2917 return aps[panel].alignFrame;
2924 public VamsasApplication getVamsasApplication()
2926 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2932 * flag set if jalview GUI is being operated programmatically
2934 private boolean inBatchMode = false;
2937 * check if jalview GUI is being operated programmatically
2939 * @return inBatchMode
2941 public boolean isInBatchMode()
2947 * set flag if jalview GUI is being operated programmatically
2949 * @param inBatchMode
2951 public void setInBatchMode(boolean inBatchMode)
2953 this.inBatchMode = inBatchMode;
2957 * start service discovery and wait till it is done
2959 public void startServiceDiscovery()
2961 startServiceDiscovery(false);
2965 * start service discovery threads - blocking or non-blocking
2969 public void startServiceDiscovery(boolean blocking)
2971 startServiceDiscovery(blocking, false);
2975 * start service discovery threads
2978 * - false means call returns immediately
2979 * @param ignore_SHOW_JWS2_SERVICES_preference
2980 * - when true JABA services are discovered regardless of user's JWS2
2981 * discovery preference setting
2983 public void startServiceDiscovery(boolean blocking,
2984 boolean ignore_SHOW_JWS2_SERVICES_preference)
2986 boolean alive = true;
2987 Thread t0 = null, t1 = null, t2 = null;
2988 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2991 // todo: changesupport handlers need to be transferred
2992 if (discoverer == null)
2994 discoverer = new jalview.ws.jws1.Discoverer();
2995 // register PCS handler for desktop.
2996 discoverer.addPropertyChangeListener(changeSupport);
2998 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2999 // until we phase out completely
3000 (t0 = new Thread(discoverer)).start();
3003 if (ignore_SHOW_JWS2_SERVICES_preference
3004 || Cache.getDefault("SHOW_JWS2_SERVICES", true))
3006 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3007 .startDiscoverer(changeSupport);
3011 // TODO: do rest service discovery
3020 } catch (Exception e)
3023 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
3024 || (t3 != null && t3.isAlive())
3025 || (t0 != null && t0.isAlive());
3031 * called to check if the service discovery process completed successfully.
3035 protected void JalviewServicesChanged(PropertyChangeEvent evt)
3037 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
3039 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
3040 .getErrorMessages();
3043 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
3045 if (serviceChangedDialog == null)
3047 // only run if we aren't already displaying one of these.
3048 addDialogThread(serviceChangedDialog = new Runnable()
3055 * JalviewDialog jd =new JalviewDialog() {
3057 * @Override protected void cancelPressed() { // TODO Auto-generated method stub
3059 * }@Override protected void okPressed() { // TODO Auto-generated method stub
3061 * }@Override protected void raiseClosed() { // TODO Auto-generated method stub
3063 * } }; jd.initDialogFrame(new JLabel("<html><table width=\"450\"><tr><td>" +
3065 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
3066 * + " or mis-configured HTTP proxy settings.<br/>" +
3067 * "Check the <em>Connections</em> and <em>Web services</em> tab of the" +
3068 * " Tools->Preferences dialog box to change them.</td></tr></table></html>" ),
3069 * true, true, "Web Service Configuration Problem", 450, 400);
3071 * jd.waitForInput();
3073 JvOptionPane.showConfirmDialog(Desktop.desktop,
3074 new JLabel("<html><table width=\"450\"><tr><td>"
3075 + ermsg + "</td></tr></table>"
3076 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
3077 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
3078 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
3079 + " Tools->Preferences dialog box to change them.</p></html>"),
3080 "Web Service Configuration Problem",
3081 JvOptionPane.DEFAULT_OPTION,
3082 JvOptionPane.ERROR_MESSAGE);
3083 serviceChangedDialog = null;
3091 jalview.bin.Console.error(
3092 "Errors reported by JABA discovery service. Check web services preferences.\n"
3099 private Runnable serviceChangedDialog = null;
3102 * start a thread to open a URL in the configured browser. Pops up a warning
3103 * dialog to the user if there is an exception when calling out to the browser
3108 public static void showUrl(final String url)
3110 if (url != null && !url.trim().equals(""))
3112 jalview.bin.Console.info("Opening URL: " + url);
3113 showUrl(url, Desktop.instance);
3117 jalview.bin.Console.warn("Ignoring attempt to show an empty URL.");
3123 * Like showUrl but allows progress handler to be specified
3127 * (null) or object implementing IProgressIndicator
3129 public static void showUrl(final String url,
3130 final IProgressIndicator progress)
3132 new Thread(new Runnable()
3139 if (progress != null)
3141 progress.setProgressBar(MessageManager
3142 .formatMessage("status.opening_params", new Object[]
3143 { url }), IdUtils.newId(IdType.PROGRESS, this));
3145 jalview.util.BrowserLauncher.openURL(url);
3146 } catch (Exception ex)
3148 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
3150 .getString("label.web_browser_not_found_unix"),
3151 MessageManager.getString("label.web_browser_not_found"),
3152 JvOptionPane.WARNING_MESSAGE);
3154 ex.printStackTrace();
3156 if (progress != null)
3158 progress.setProgressBar(null,
3159 IdUtils.newId(IdType.PROGRESS, this));
3165 public static WsParamSetManager wsparamManager = null;
3167 public static ParamManager getUserParameterStore()
3169 if (wsparamManager == null)
3171 wsparamManager = new WsParamSetManager();
3173 return wsparamManager;
3177 * static hyperlink handler proxy method for use by Jalview's internal windows
3181 public static void hyperlinkUpdate(HyperlinkEvent e)
3183 if (e.getEventType() == EventType.ACTIVATED)
3188 url = e.getURL().toString();
3189 Desktop.showUrl(url);
3190 } catch (Exception x)
3195 .error("Couldn't handle string " + url + " as a URL.");
3197 // ignore any exceptions due to dud links.
3204 * single thread that handles display of dialogs to user.
3206 ExecutorService dialogExecutor = Executors.newFixedThreadPool(3);
3209 * flag indicating if dialogExecutor should try to acquire a permit
3211 private volatile boolean dialogPause = true;
3216 private Semaphore block = new Semaphore(0);
3218 private static groovy.console.ui.Console groovyConsole;
3221 * add another dialog thread to the queue
3225 public void addDialogThread(final Runnable prompter)
3227 dialogExecutor.submit(new Runnable()
3234 acquireDialogQueue();
3236 if (instance == null)
3242 SwingUtilities.invokeAndWait(prompter);
3243 } catch (Exception q)
3245 jalview.bin.Console.warn("Unexpected Exception in dialog thread.",
3252 private boolean dialogQueueStarted = false;
3254 public void startDialogQueue()
3256 if (dialogQueueStarted)
3260 // set the flag so we don't pause waiting for another permit and semaphore
3261 // the current task to begin
3262 releaseDialogQueue();
3263 dialogQueueStarted = true;
3266 public void acquireDialogQueue()
3272 } catch (InterruptedException e)
3274 jalview.bin.Console.debug("Interruption when acquiring DialogueQueue",
3279 public void releaseDialogQueue()
3286 dialogPause = false;
3290 * Outputs an image of the desktop to file in EPS format, after prompting the
3291 * user for choice of Text or Lineart character rendering (unless a preference
3292 * has been set). The file name is generated as
3295 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
3299 protected void snapShotWindow_actionPerformed(ActionEvent e)
3301 // currently the menu option to do this is not shown
3304 int width = getWidth();
3305 int height = getHeight();
3307 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
3308 ImageWriterI writer = new ImageWriterI()
3311 public void exportImage(Graphics g) throws Exception
3314 jalview.bin.Console.info("Successfully written snapshot to file "
3315 + of.getAbsolutePath());
3318 String title = "View of desktop";
3319 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
3323 exporter.doExport(of, this, width, height, title);
3324 } catch (ImageOutputException ioex)
3326 jalview.bin.Console.error(
3327 "Unexpected error whilst writing Jalview desktop snapshot as EPS",
3333 * Explode the views in the given SplitFrame into separate SplitFrame windows.
3334 * This respects (remembers) any previous 'exploded geometry' i.e. the size
3335 * and location last time the view was expanded (if any). However it does not
3336 * remember the split pane divider location - this is set to match the
3337 * 'exploding' frame.
3341 public void explodeViews(SplitFrame sf)
3343 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
3344 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
3345 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
3347 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
3349 int viewCount = topPanels.size();
3356 * Processing in reverse order works, forwards order leaves the first panels not
3357 * visible. I don't know why!
3359 for (int i = viewCount - 1; i >= 0; i--)
3362 * Make new top and bottom frames. These take over the respective AlignmentPanel
3363 * objects, including their AlignmentViewports, so the cdna/protein
3364 * relationships between the viewports is carried over to the new split frames.
3366 * explodedGeometry holds the (x, y) position of the previously exploded
3367 * SplitFrame, and the (width, height) of the AlignFrame component
3369 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3370 AlignFrame newTopFrame = new AlignFrame(topPanel);
3371 newTopFrame.setSize(oldTopFrame.getSize());
3372 newTopFrame.setVisible(true);
3373 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3374 .getExplodedGeometry();
3375 if (geometry != null)
3377 newTopFrame.setSize(geometry.getSize());
3380 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3381 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3382 newBottomFrame.setSize(oldBottomFrame.getSize());
3383 newBottomFrame.setVisible(true);
3384 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3385 .getExplodedGeometry();
3386 if (geometry != null)
3388 newBottomFrame.setSize(geometry.getSize());
3391 topPanel.av.setGatherViewsHere(false);
3392 bottomPanel.av.setGatherViewsHere(false);
3393 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3395 if (geometry != null)
3397 splitFrame.setLocation(geometry.getLocation());
3399 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3403 * Clear references to the panels (now relocated in the new SplitFrames) before
3404 * closing the old SplitFrame.
3407 bottomPanels.clear();
3412 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3413 * back into the given SplitFrame as additional views. Note that the gathered
3414 * frames may themselves have multiple views.
3418 public void gatherViews(GSplitFrame source)
3421 * special handling of explodedGeometry for a view within a SplitFrame: - it
3422 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3423 * height) of the AlignFrame component
3425 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3426 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3427 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3428 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3429 myBottomFrame.viewport
3430 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3431 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3432 myTopFrame.viewport.setGatherViewsHere(true);
3433 myBottomFrame.viewport.setGatherViewsHere(true);
3434 String topViewId = myTopFrame.viewport.getSequenceSetId();
3435 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3437 JInternalFrame[] frames = desktop.getAllFrames();
3438 for (JInternalFrame frame : frames)
3440 if (frame instanceof SplitFrame && frame != source)
3442 SplitFrame sf = (SplitFrame) frame;
3443 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3444 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3445 boolean gatherThis = false;
3446 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3448 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3449 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3450 if (topViewId.equals(topPanel.av.getSequenceSetId())
3451 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3454 topPanel.av.setGatherViewsHere(false);
3455 bottomPanel.av.setGatherViewsHere(false);
3456 topPanel.av.setExplodedGeometry(
3457 new Rectangle(sf.getLocation(), topFrame.getSize()));
3458 bottomPanel.av.setExplodedGeometry(
3459 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3460 myTopFrame.addAlignmentPanel(topPanel, false);
3461 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3467 topFrame.getAlignPanels().clear();
3468 bottomFrame.getAlignPanels().clear();
3475 * The dust settles...give focus to the tab we did this from.
3477 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3480 public static groovy.console.ui.Console getGroovyConsole()
3482 return groovyConsole;
3486 * handles the payload of a drag and drop event.
3488 * TODO refactor to desktop utilities class
3491 * - Data source strings extracted from the drop event
3493 * - protocol for each data source extracted from the drop event
3497 * - the payload from the drop event
3500 public static void transferFromDropTarget(List<Object> files,
3501 List<DataSourceType> protocols, DropTargetDropEvent evt,
3502 Transferable t) throws Exception
3505 DataFlavor uriListFlavor = new DataFlavor(
3506 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3509 urlFlavour = new DataFlavor(
3510 "application/x-java-url; class=java.net.URL");
3511 } catch (ClassNotFoundException cfe)
3513 jalview.bin.Console.debug("Couldn't instantiate the URL dataflavor.",
3517 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3522 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3523 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3524 // means url may be null.
3527 protocols.add(DataSourceType.URL);
3528 files.add(url.toString());
3529 jalview.bin.Console.debug("Drop handled as URL dataflavor "
3530 + files.get(files.size() - 1));
3535 if (Platform.isAMacAndNotJS())
3537 jalview.bin.Console.errPrintln(
3538 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3541 } catch (Throwable ex)
3543 jalview.bin.Console.debug("URL drop handler failed.", ex);
3546 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3548 // Works on Windows and MacOSX
3549 jalview.bin.Console.debug("Drop handled as javaFileListFlavor");
3550 for (Object file : (List) t
3551 .getTransferData(DataFlavor.javaFileListFlavor))
3554 protocols.add(DataSourceType.FILE);
3559 // Unix like behaviour
3560 boolean added = false;
3562 if (t.isDataFlavorSupported(uriListFlavor))
3564 jalview.bin.Console.debug("Drop handled as uriListFlavor");
3565 // This is used by Unix drag system
3566 data = (String) t.getTransferData(uriListFlavor);
3570 // fallback to text: workaround - on OSX where there's a JVM bug
3572 .debug("standard URIListFlavor failed. Trying text");
3573 // try text fallback
3574 DataFlavor textDf = new DataFlavor(
3575 "text/plain;class=java.lang.String");
3576 if (t.isDataFlavorSupported(textDf))
3578 data = (String) t.getTransferData(textDf);
3581 jalview.bin.Console.debug("Plain text drop content returned "
3582 + (data == null ? "Null - failed" : data));
3587 while (protocols.size() < files.size())
3589 jalview.bin.Console.debug("Adding missing FILE protocol for "
3590 + files.get(protocols.size()));
3591 protocols.add(DataSourceType.FILE);
3593 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3594 data, "\r\n"); st.hasMoreTokens();)
3597 String s = st.nextToken();
3598 if (s.startsWith("#"))
3600 // the line is a comment (as per the RFC 2483)
3603 java.net.URI uri = new java.net.URI(s);
3604 if (uri.getScheme().toLowerCase(Locale.ROOT).startsWith("http"))
3606 protocols.add(DataSourceType.URL);
3607 files.add(uri.toString());
3611 // otherwise preserve old behaviour: catch all for file objects
3612 java.io.File file = new java.io.File(uri);
3613 protocols.add(DataSourceType.FILE);
3614 files.add(file.toString());
3619 if (jalview.bin.Console.isDebugEnabled())
3621 if (data == null || !added)
3624 if (t.getTransferDataFlavors() != null
3625 && t.getTransferDataFlavors().length > 0)
3627 jalview.bin.Console.debug(
3628 "Couldn't resolve drop data. Here are the supported flavors:");
3629 for (DataFlavor fl : t.getTransferDataFlavors())
3631 jalview.bin.Console.debug(
3632 "Supported transfer dataflavor: " + fl.toString());
3633 Object df = t.getTransferData(fl);
3636 jalview.bin.Console.debug("Retrieves: " + df);
3640 jalview.bin.Console.debug("Retrieved nothing");
3647 .debug("Couldn't resolve dataflavor for drop: "
3653 if (Platform.isWindowsAndNotJS())
3656 .debug("Scanning dropped content for Windows Link Files");
3658 // resolve any .lnk files in the file drop
3659 for (int f = 0; f < files.size(); f++)
3661 String source = files.get(f).toString().toLowerCase(Locale.ROOT);
3662 if (protocols.get(f).equals(DataSourceType.FILE)
3663 && (source.endsWith(".lnk") || source.endsWith(".url")
3664 || source.endsWith(".site")))
3668 Object obj = files.get(f);
3669 File lf = (obj instanceof File ? (File) obj
3670 : new File((String) obj));
3671 // process link file to get a URL
3672 jalview.bin.Console.debug("Found potential link file: " + lf);
3673 WindowsShortcut wscfile = new WindowsShortcut(lf);
3674 String fullname = wscfile.getRealFilename();
3675 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3676 files.set(f, fullname);
3677 jalview.bin.Console.debug("Parsed real filename " + fullname
3678 + " to extract protocol: " + protocols.get(f));
3679 } catch (Exception ex)
3681 jalview.bin.Console.error(
3682 "Couldn't parse " + files.get(f) + " as a link file.",
3691 * Sets the Preferences property for experimental features to True or False
3692 * depending on the state of the controlling menu item
3695 protected void showExperimental_actionPerformed(boolean selected)
3697 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3701 * Answers a (possibly empty) list of any structure viewer frames (currently
3702 * for either Jmol or Chimera) which are currently open. This may optionally
3703 * be restricted to viewers of a specified class, or viewers linked to a
3704 * specified alignment panel.
3707 * if not null, only return viewers linked to this panel
3708 * @param structureViewerClass
3709 * if not null, only return viewers of this class
3712 public List<StructureViewerBase> getStructureViewers(
3713 AlignmentPanel apanel,
3714 Class<? extends StructureViewerBase> structureViewerClass)
3716 List<StructureViewerBase> result = new ArrayList<>();
3717 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3719 for (JInternalFrame frame : frames)
3721 if (frame instanceof StructureViewerBase)
3723 if (structureViewerClass == null
3724 || structureViewerClass.isInstance(frame))
3727 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3729 result.add((StructureViewerBase) frame);
3737 public static final String debugScaleMessage = "Desktop graphics transform scale=";
3739 private static boolean debugScaleMessageDone = false;
3741 public static void debugScaleMessage(Graphics g)
3743 if (debugScaleMessageDone)
3747 // output used by tests to check HiDPI scaling settings in action
3750 Graphics2D gg = (Graphics2D) g;
3753 AffineTransform t = gg.getTransform();
3754 double scaleX = t.getScaleX();
3755 double scaleY = t.getScaleY();
3756 jalview.bin.Console.debug(debugScaleMessage + scaleX + " (X)");
3757 jalview.bin.Console.debug(debugScaleMessage + scaleY + " (Y)");
3758 debugScaleMessageDone = true;
3762 jalview.bin.Console.debug("Desktop graphics null");
3764 } catch (Exception e)
3766 jalview.bin.Console.debug(Cache.getStackTraceString(e));
3771 * closes the current instance window, but leaves the JVM running. Bypasses
3772 * any shutdown prompts, but does not set window dispose on close in case JVM
3775 public static void closeDesktop()
3777 if (Desktop.instance != null)
3779 Desktop us = Desktop.instance;
3780 Desktop.instance.quitTheDesktop(false, false);
3781 // call dispose in a separate thread - try to avoid indirect deadlocks
3784 new Thread(new Runnable()
3789 ExecutorService dex = us.dialogExecutor;
3793 us.dialogExecutor = null;
3794 us.block.drainPermits();
3804 * checks if any progress bars are being displayed in any of the windows
3805 * managed by the desktop
3809 public boolean operationsAreInProgress()
3811 JInternalFrame[] frames = getAllFrames();
3812 for (JInternalFrame frame : frames)
3814 if (frame instanceof IProgressIndicator)
3816 if (((IProgressIndicator) frame).operationInProgress())
3822 return operationInProgress();
3826 * keep track of modal JvOptionPanes open as modal dialogs for AlignFrames.
3827 * The way the modal JInternalFrame is made means it cannot be a child of an
3828 * AlignFrame, so closing the AlignFrame might leave the modal open :(
3830 private static Map<AlignFrame, JInternalFrame> alignFrameModalMap = new HashMap<>();
3832 protected static void addModal(AlignFrame af, JInternalFrame jif)
3834 alignFrameModalMap.put(af, jif);
3837 protected static void closeModal(AlignFrame af)
3839 if (!alignFrameModalMap.containsKey(af))
3843 JInternalFrame jif = alignFrameModalMap.get(af);
3848 jif.setClosed(true);
3849 } catch (PropertyVetoException e)
3851 e.printStackTrace();
3854 alignFrameModalMap.remove(af);
3857 public void nonBlockingDialog(String title, String message, String button,
3858 int type, boolean scrollable, boolean modal)
3860 nonBlockingDialog(title, message, null, button, type, scrollable, false,
3864 public void nonBlockingDialog(String title, String message,
3865 String boxtext, String button, int type, boolean scrollable,
3866 boolean html, boolean modal, int timeout)
3868 nonBlockingDialog(32, 2, title, message, boxtext, button, type,
3869 scrollable, html, modal, timeout);
3872 public void nonBlockingDialog(int width, int height, String title,
3873 String message, String boxtext, String button, int type,
3874 boolean scrollable, boolean html, boolean modal, int timeout)
3878 type = JvOptionPane.WARNING_MESSAGE;
3880 JLabel jl = new JLabel(message);
3882 JTextComponent jtc = null;
3885 JTextPane jtp = new JTextPane();
3886 jtp.setContentType("text/html");
3887 jtp.setEditable(false);
3888 jtp.setAutoscrolls(true);
3889 jtp.setText(boxtext);
3895 JTextArea jta = new JTextArea(height, width);
3896 // jta.setLineWrap(true);
3897 jta.setEditable(false);
3898 jta.setWrapStyleWord(true);
3899 jta.setAutoscrolls(true);
3900 jta.setText(boxtext);
3905 JScrollPane jsp = scrollable
3906 ? new JScrollPane(jtc, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
3907 JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED)
3910 JvOptionPane jvp = JvOptionPane.newOptionDialog(this);
3912 JPanel jp = new JPanel();
3913 jp.setLayout(new BoxLayout(jp, BoxLayout.Y_AXIS));
3915 if (message != null)
3917 jl.setAlignmentX(Component.LEFT_ALIGNMENT);
3920 if (boxtext != null)
3924 jsp.setAlignmentX(Component.LEFT_ALIGNMENT);
3929 jtc.setAlignmentX(Component.LEFT_ALIGNMENT);
3934 jvp.setResponseHandler(JOptionPane.YES_OPTION, () -> {
3936 jvp.setTimeout(timeout);
3937 JButton jb = new JButton(button);
3938 jvp.showDialogOnTopAsync(this, jp, title, JOptionPane.YES_OPTION, type,
3940 { button }, button, modal, new JButton[] { jb }, false);
3944 public AlignFrame getCurrentAlignFrame()
3946 return Jalview.getInstance().getCurrentAlignFrame();