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 jalview.api.AlignViewportI;
24 import jalview.api.AlignmentViewPanel;
25 import jalview.api.StructureSelectionManagerProvider;
26 import jalview.bin.ApplicationSingletonProvider;
27 import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
28 import jalview.bin.Cache;
29 import jalview.bin.Jalview;
30 import jalview.gui.ImageExporter.ImageWriterI;
31 import jalview.io.BackupFiles;
32 import jalview.io.DataSourceType;
33 import jalview.io.FileFormat;
34 import jalview.io.FileFormatException;
35 import jalview.io.FileFormatI;
36 import jalview.io.FileFormats;
37 import jalview.io.FileLoader;
38 import jalview.io.FormatAdapter;
39 import jalview.io.IdentifyFile;
40 import jalview.io.JalviewFileChooser;
41 import jalview.io.JalviewFileView;
42 import jalview.jbgui.GSplitFrame;
43 import jalview.jbgui.GStructureViewer;
44 import jalview.project.Jalview2XML;
45 import jalview.structure.StructureSelectionManager;
46 import jalview.urls.IdOrgSettings;
47 import jalview.util.BrowserLauncher;
48 import jalview.util.ImageMaker.TYPE;
49 import jalview.util.MessageManager;
50 import jalview.util.Platform;
51 import jalview.util.ShortcutKeyMaskExWrapper;
52 import jalview.util.UrlConstants;
53 import jalview.viewmodel.AlignmentViewport;
54 import jalview.ws.params.ParamManager;
55 import jalview.ws.utils.UrlDownloadClient;
57 import java.awt.BorderLayout;
58 import java.awt.Color;
59 import java.awt.Dimension;
60 import java.awt.FontMetrics;
61 import java.awt.Graphics;
62 import java.awt.GridLayout;
63 import java.awt.Point;
64 import java.awt.Rectangle;
65 import java.awt.Toolkit;
66 import java.awt.Window;
67 import java.awt.datatransfer.Clipboard;
68 import java.awt.datatransfer.ClipboardOwner;
69 import java.awt.datatransfer.DataFlavor;
70 import java.awt.datatransfer.Transferable;
71 import java.awt.dnd.DnDConstants;
72 import java.awt.dnd.DropTargetDragEvent;
73 import java.awt.dnd.DropTargetDropEvent;
74 import java.awt.dnd.DropTargetEvent;
75 import java.awt.dnd.DropTargetListener;
76 import java.awt.event.ActionEvent;
77 import java.awt.event.ActionListener;
78 import java.awt.event.InputEvent;
79 import java.awt.event.KeyEvent;
80 import java.awt.event.MouseAdapter;
81 import java.awt.event.MouseEvent;
82 import java.awt.event.WindowAdapter;
83 import java.awt.event.WindowEvent;
84 import java.beans.PropertyChangeEvent;
85 import java.beans.PropertyChangeListener;
87 import java.io.FileWriter;
88 import java.io.IOException;
89 import java.lang.reflect.Method;
91 import java.util.ArrayList;
92 import java.util.HashMap;
93 import java.util.Hashtable;
94 import java.util.List;
95 import java.util.ListIterator;
96 import java.util.Vector;
97 import java.util.concurrent.ExecutorService;
98 import java.util.concurrent.Executors;
99 import java.util.concurrent.Semaphore;
101 import javax.swing.AbstractAction;
102 import javax.swing.Action;
103 import javax.swing.ActionMap;
104 import javax.swing.Box;
105 import javax.swing.BoxLayout;
106 import javax.swing.DefaultDesktopManager;
107 import javax.swing.DesktopManager;
108 import javax.swing.InputMap;
109 import javax.swing.JButton;
110 import javax.swing.JCheckBox;
111 import javax.swing.JComboBox;
112 import javax.swing.JComponent;
113 import javax.swing.JDesktopPane;
114 import javax.swing.JInternalFrame;
115 import javax.swing.JLabel;
116 import javax.swing.JMenuItem;
117 import javax.swing.JPanel;
118 import javax.swing.JPopupMenu;
119 import javax.swing.JProgressBar;
120 import javax.swing.JTextField;
121 import javax.swing.KeyStroke;
122 import javax.swing.SwingUtilities;
123 import javax.swing.event.HyperlinkEvent;
124 import javax.swing.event.HyperlinkEvent.EventType;
125 import javax.swing.event.InternalFrameAdapter;
126 import javax.swing.event.InternalFrameEvent;
128 import org.stackoverflowusers.file.WindowsShortcut;
135 * @version $Revision: 1.155 $
137 public class Desktop extends jalview.jbgui.GDesktop
138 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
139 StructureSelectionManagerProvider, ApplicationSingletonI
141 private static final String CITATION = "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
142 + "<br><br>For help, see the FAQ at <a href=\"http://www.jalview.org/faq\">www.jalview.org/faq</a> and/or join the jalview-discuss@jalview.org mailing list"
143 + "<br><br>If you use Jalview, please cite:"
144 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
145 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
146 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033";
148 private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)";
150 private final static int DEFAULT_MIN_WIDTH = 300;
152 private final static int DEFAULT_MIN_HEIGHT = 250;
154 private final static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
156 private final static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
158 private final static String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
160 protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
162 public static HashMap<String, FileWriter> savingFiles = new HashMap<>();
164 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
167 * news reader - null if it was never started.
169 private BlogReader jvnews = null;
171 private File projectFile;
175 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
177 public void addJalviewPropertyChangeListener(
178 PropertyChangeListener listener)
180 changeSupport.addJalviewPropertyChangeListener(listener);
184 * @param propertyName
186 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
187 * java.beans.PropertyChangeListener)
189 public void addJalviewPropertyChangeListener(String propertyName,
190 PropertyChangeListener listener)
192 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
196 * @param propertyName
198 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
199 * java.beans.PropertyChangeListener)
201 public void removeJalviewPropertyChangeListener(String propertyName,
202 PropertyChangeListener listener)
204 changeSupport.removeJalviewPropertyChangeListener(propertyName,
208 public static StructureSelectionManager getStructureSelectionManager()
210 return StructureSelectionManager
211 .getStructureSelectionManager(getInstance());
214 static int openFrameCount = 0;
216 static final int xOffset = 30;
218 static final int yOffset = 30;
221 public jalview.ws.jws1.Discoverer discoverer;
224 public Object[] jalviewClipboard;
227 public boolean internalCopy = false;
229 private static int fileLoadingCount = 0;
231 public JInternalFrame conservationSlider;
233 public JInternalFrame PIDSlider;
235 class MyDesktopManager implements DesktopManager
238 private DesktopManager delegate;
240 public MyDesktopManager(DesktopManager delegate)
242 this.delegate = delegate;
246 public void activateFrame(JInternalFrame f)
250 delegate.activateFrame(f);
251 } catch (NullPointerException npe)
253 Point p = getMousePosition();
254 showPasteMenu(p.x, p.y);
259 public void beginDraggingFrame(JComponent f)
261 delegate.beginDraggingFrame(f);
265 public void beginResizingFrame(JComponent f, int direction)
267 delegate.beginResizingFrame(f, direction);
271 public void closeFrame(JInternalFrame f)
273 delegate.closeFrame(f);
277 public void deactivateFrame(JInternalFrame f)
279 delegate.deactivateFrame(f);
283 public void deiconifyFrame(JInternalFrame f)
285 delegate.deiconifyFrame(f);
289 public void dragFrame(JComponent f, int newX, int newY)
295 delegate.dragFrame(f, newX, newY);
299 public void endDraggingFrame(JComponent f)
301 delegate.endDraggingFrame(f);
302 desktopPane.repaint();
306 public void endResizingFrame(JComponent f)
308 delegate.endResizingFrame(f);
309 desktopPane.repaint();
313 public void iconifyFrame(JInternalFrame f)
315 delegate.iconifyFrame(f);
319 public void maximizeFrame(JInternalFrame f)
321 delegate.maximizeFrame(f);
325 public void minimizeFrame(JInternalFrame f)
327 delegate.minimizeFrame(f);
331 public void openFrame(JInternalFrame f)
333 delegate.openFrame(f);
337 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
344 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
348 public void setBoundsForFrame(JComponent f, int newX, int newY,
349 int newWidth, int newHeight)
351 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
354 // All other methods, simply delegate
358 * Private constructor enforces singleton pattern. It is called by reflection
359 * from ApplicationSingletonProvider.getInstance().
367 * A note to implementors. It is ESSENTIAL that any activities that might
368 * block are spawned off as threads rather than waited for during this
372 doConfigureStructurePrefs();
373 setTitle("Jalview " + Cache.getProperty("VERSION"));
377 if (Platform.getJavaVersion() >= 11)
379 // BH use reflection so that this code can be in both the Java8 and
381 Class<?> j11APQHandlers = Class
382 .forName("jalview.gui.APQHandlers");
383 Method meth = j11APQHandlers.getMethod("setAPQHandlers",
386 meth.invoke(j11APQHandlers.newInstance(), this);
388 } catch (Throwable t)
391 "Desktop Error setting APQHandlers: " + t.toString());
394 addWindowListener(new WindowAdapter()
398 public void windowClosing(WindowEvent ev)
404 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE",
407 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE",
409 desktopPane = new MyDesktopPane(selmemusage);
411 showMemusage.setSelected(selmemusage);
412 desktopPane.setBackground(Color.white);
414 getContentPane().setLayout(new BorderLayout());
415 // alternate config - have scrollbars - see notes in JAL-153
416 // JScrollPane sp = new JScrollPane();
417 // sp.getViewport().setView(desktop);
418 // getContentPane().add(sp, BorderLayout.CENTER);
420 // BH 2018 - just an experiment to try unclipped JInternalFrames.
423 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
426 getContentPane().add(desktopPane, BorderLayout.CENTER);
427 desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
429 // This line prevents Windows Look&Feel resizing all new windows to
431 // if previous window was maximised
432 desktopPane.setDesktopManager(new MyDesktopManager(
433 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
434 : Platform.isAMacAndNotJS()
435 ? new AquaInternalFrameManager(
436 desktopPane.getDesktopManager())
437 : desktopPane.getDesktopManager())));
439 Rectangle dims = getLastKnownDimensions("");
446 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
447 int xPos = Math.max(5, (screenSize.width - 900) / 2);
448 int yPos = Math.max(5, (screenSize.height - 650) / 2);
449 setBounds(xPos, yPos, 900, 650);
452 // Note that this next syntax, checking for Platform.isJS and also
453 // escaping the code using @j2sIgnore, serves two purposes. It gives
454 // us an easily findable tag, Platform.isJS(), to places in the code where
455 // there is something different about the SwingJS implementation. Second,
456 // it deletes the unneeded Java-only code form the JavaScript version
457 // completely (@j2sIgnore), since it will never be used there.
459 if (!Platform.isJS() && !Jalview.isSynchronous())
467 jconsole = new Console(this, showjconsole);
468 jconsole.setHeader(Cache.getVersionDetailsForConsole());
469 showConsole(showjconsole);
471 showNews.setVisible(false);
473 experimentalFeatures.setSelected(showExperimental());
475 getIdentifiersOrgData();
479 // Spawn a thread that shows the splashscreen
481 SwingUtilities.invokeLater(new Runnable()
486 new SplashScreen(true);
490 // Thread off a new instance of the file chooser - this reduces the time
492 // takes to open it later on.
493 new Thread(new Runnable()
498 Cache.log.debug("Filechooser init thread started.");
499 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
500 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
502 Cache.log.debug("Filechooser init thread finished.");
505 // Add the service change listener
506 changeSupport.addJalviewPropertyChangeListener("services",
507 new PropertyChangeListener()
511 public void propertyChange(PropertyChangeEvent evt)
513 Cache.log.debug("Firing service changed event for "
514 + evt.getNewValue());
515 JalviewServicesChanged(evt);
522 if (!Jalview.isSynchronous())
524 this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
526 this.addWindowListener(new WindowAdapter()
529 public void windowClosing(WindowEvent evt)
536 this.addMouseListener(ma = new MouseAdapter()
539 public void mousePressed(MouseEvent evt)
541 if (evt.isPopupTrigger()) // Mac
543 showPasteMenu(evt.getX(), evt.getY());
548 public void mouseReleased(MouseEvent evt)
550 if (evt.isPopupTrigger()) // Windows
552 showPasteMenu(evt.getX(), evt.getY());
556 desktopPane.addMouseListener(ma);
558 } catch (Throwable t)
565 * Answers true if user preferences to enable experimental features is True
570 public boolean showExperimental()
572 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
573 Boolean.FALSE.toString());
574 return Boolean.valueOf(experimental).booleanValue();
577 public void doConfigureStructurePrefs()
579 // configure services
580 StructureSelectionManager ssm = StructureSelectionManager
581 .getStructureSelectionManager(this);
582 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
584 ssm.setAddTempFacAnnot(Cache
585 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
586 ssm.setProcessSecondaryStructure(Cache
587 .getDefault(Preferences.STRUCT_FROM_PDB, true));
588 ssm.setSecStructServices(
589 Cache.getDefault(Preferences.USE_RNAVIEW, true));
593 ssm.setAddTempFacAnnot(false);
594 ssm.setProcessSecondaryStructure(false);
595 ssm.setSecStructServices(false);
599 public void checkForNews()
601 final Desktop me = this;
602 // Thread off the news reader, in case there are connection problems.
603 new Thread(new Runnable()
608 Cache.log.debug("Starting news thread.");
609 jvnews = new BlogReader(me);
610 showNews.setVisible(true);
611 Cache.log.debug("Completed news thread.");
616 public void getIdentifiersOrgData()
618 // Thread off the identifiers fetcher
619 new Thread(new Runnable()
624 Cache.log.debug("Downloading data from identifiers.org");
627 UrlDownloadClient.download(IdOrgSettings.getUrl(),
628 IdOrgSettings.getDownloadLocation());
629 } catch (IOException e)
631 Cache.log.debug("Exception downloading identifiers.org data"
640 protected void showNews_actionPerformed(ActionEvent e)
642 showNews(showNews.isSelected());
645 protected void showNews(boolean visible)
647 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
648 showNews.setSelected(visible);
649 if (visible && !jvnews.isVisible())
651 new Thread(new Runnable()
656 long now = System.currentTimeMillis();
658 MessageManager.getString("status.refreshing_news"), now);
659 jvnews.refreshNews();
660 setProgressBar(null, now);
668 * recover the last known dimensions for a jalview window
671 * - empty string is desktop, all other windows have unique prefix
672 * @return null or last known dimensions scaled to current geometry (if last
673 * window geom was known)
675 Rectangle getLastKnownDimensions(String windowName)
677 // TODO: lock aspect ratio for scaling desktop Bug #0058199
678 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
679 String x = Cache.getProperty(windowName + "SCREEN_X");
680 String y = Cache.getProperty(windowName + "SCREEN_Y");
682 .getProperty(windowName + "SCREEN_WIDTH");
683 String height = Cache
684 .getProperty(windowName + "SCREEN_HEIGHT");
685 if ((x != null) && (y != null) && (width != null) && (height != null))
687 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
688 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
689 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
691 // attempt #1 - try to cope with change in screen geometry - this
692 // version doesn't preserve original jv aspect ratio.
693 // take ratio of current screen size vs original screen size.
694 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
695 Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
696 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
697 Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
698 // rescale the bounds depending upon the current screen geometry.
699 ix = (int) (ix * sw);
700 iw = (int) (iw * sw);
701 iy = (int) (iy * sh);
702 ih = (int) (ih * sh);
703 while (ix >= screenSize.width)
706 "Window geometry location recall error: shifting horizontal to within screenbounds.");
707 ix -= screenSize.width;
709 while (iy >= screenSize.height)
712 "Window geometry location recall error: shifting vertical to within screenbounds.");
713 iy -= screenSize.height;
716 "Got last known dimensions for " + windowName + ": x:" + ix
717 + " y:" + iy + " width:" + iw + " height:" + ih);
719 // return dimensions for new instance
720 return new Rectangle(ix, iy, iw, ih);
725 protected void showPasteMenu(int x, int y)
727 JPopupMenu popup = new JPopupMenu();
728 JMenuItem item = new JMenuItem(
729 MessageManager.getString("label.paste_new_window"));
730 item.addActionListener(new ActionListener()
733 public void actionPerformed(ActionEvent evt)
740 popup.show(this, x, y);
747 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
748 Transferable contents = c.getContents(this);
750 if (contents != null)
752 String file = (String) contents
753 .getTransferData(DataFlavor.stringFlavor);
755 FileFormatI format = new IdentifyFile().identify(file,
756 DataSourceType.PASTE);
758 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
761 } catch (Exception ex)
764 "Unable to paste alignment from system clipboard:\n" + ex);
769 * Adds and opens the given frame to the desktop
780 public static synchronized void addInternalFrame(
781 final JInternalFrame frame, String title, int w, int h)
783 addInternalFrame(frame, title, true, w, h, true, false);
787 * Add an internal frame to the Jalview desktop
794 * When true, display frame immediately, otherwise, caller must call
795 * setVisible themselves.
801 public static synchronized void addInternalFrame(
802 final JInternalFrame frame, String title, boolean makeVisible,
805 addInternalFrame(frame, title, makeVisible, w, h, true, false);
809 * Add an internal frame to the Jalview desktop and make it visible
822 public static synchronized void addInternalFrame(
823 final JInternalFrame frame, String title, int w, int h,
826 addInternalFrame(frame, title, true, w, h, resizable, false);
830 * Add an internal frame to the Jalview desktop
837 * When true, display frame immediately, otherwise, caller must call
838 * setVisible themselves.
845 * @param ignoreMinSize
846 * Do not set the default minimum size for frame
848 public static synchronized void addInternalFrame(
849 final JInternalFrame frame, String title, boolean makeVisible,
850 int w, int h, boolean resizable, boolean ignoreMinSize)
854 // TODO: allow callers to determine X and Y position of frame (eg. via
856 // TODO: consider fixing method to update entries in the window submenu with
857 // the current window title
859 frame.setTitle(title);
861 if (w > 0 && (frame.getWidth() < 1 || frame.getHeight() < 1))
865 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
866 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
867 // IF JALVIEW IS RUNNING HEADLESS
868 // ///////////////////////////////////////////////
869 if (Jalview.isHeadlessMode())
878 frame.setMinimumSize(
879 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
881 // Set default dimension for Alignment Frame window.
882 // The Alignment Frame window could be added from a number of places,
884 // I did this here in order not to miss out on any Alignment frame.
885 if (frame instanceof AlignFrame)
887 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
888 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
892 frame.setVisible(makeVisible);
893 frame.setClosable(true);
894 frame.setResizable(resizable);
895 frame.setMaximizable(resizable);
896 frame.setIconifiable(resizable);
897 frame.setOpaque(Platform.isJS());
899 boolean isEmbedded = (Platform.getDimIfEmbedded(frame, -1, -1) != null);
900 if (!isEmbedded && frame.getX() < 1 && frame.getY() < 1)
902 frame.setLocation(xOffset * openFrameCount,
903 yOffset * ((openFrameCount - 1) % 10) + yOffset);
907 * add an entry for the new frame in the Window menu
908 * (and remove it when the frame is closed)
910 JMenuItem menuItem = new JMenuItem(title);
911 frame.addInternalFrameListener(new InternalFrameAdapter()
914 public void internalFrameActivated(InternalFrameEvent evt)
916 JInternalFrame itf = getDesktopPane().getSelectedFrame();
919 if (itf instanceof AlignFrame)
921 Jalview.setCurrentAlignFrame((AlignFrame) itf);
928 public void internalFrameClosed(InternalFrameEvent evt)
930 PaintRefresher.RemoveComponent(frame);
933 * defensive check to prevent frames being
934 * added half off the window
936 if (openFrameCount > 0)
942 * ensure no reference to alignFrame retained by menu item listener
944 if (menuItem.getActionListeners().length > 0)
946 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
948 Desktop.getInstance().windowMenu.remove(menuItem);
952 menuItem.addActionListener(new ActionListener()
955 public void actionPerformed(ActionEvent e)
959 frame.setSelected(true);
960 frame.setIcon(false);
961 } catch (java.beans.PropertyVetoException ex)
963 // System.err.println(ex.toString());
968 setKeyBindings(frame);
970 getDesktopPane().add(frame);
972 Desktop.getInstance().windowMenu.add(menuItem);
977 frame.setSelected(true);
978 frame.requestFocus();
979 } catch (java.beans.PropertyVetoException ve)
981 } catch (java.lang.ClassCastException cex)
984 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
990 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
995 private static void setKeyBindings(JInternalFrame frame)
997 @SuppressWarnings("serial")
998 final Action closeAction = new AbstractAction()
1001 public void actionPerformed(ActionEvent e)
1008 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
1010 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1011 InputEvent.CTRL_DOWN_MASK);
1012 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
1013 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
1015 InputMap inputMap = frame
1016 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1017 String ctrlW = ctrlWKey.toString();
1018 inputMap.put(ctrlWKey, ctrlW);
1019 inputMap.put(cmdWKey, ctrlW);
1021 ActionMap actionMap = frame.getActionMap();
1022 actionMap.put(ctrlW, closeAction);
1026 public void lostOwnership(Clipboard clipboard, Transferable contents)
1030 Desktop.getInstance().jalviewClipboard = null;
1033 internalCopy = false;
1037 public void dragEnter(DropTargetDragEvent evt)
1042 public void dragExit(DropTargetEvent evt)
1047 public void dragOver(DropTargetDragEvent evt)
1052 public void dropActionChanged(DropTargetDragEvent evt)
1063 public void drop(DropTargetDropEvent evt)
1065 boolean success = true;
1066 // JAL-1552 - acceptDrop required before getTransferable call for
1067 // Java's Transferable for native dnd
1068 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1069 Transferable t = evt.getTransferable();
1070 List<Object> files = new ArrayList<>();
1071 List<DataSourceType> protocols = new ArrayList<>();
1075 Desktop.transferFromDropTarget(files, protocols, evt, t);
1076 } catch (Exception e)
1078 e.printStackTrace();
1086 for (int i = 0; i < files.size(); i++)
1088 // BH 2018 File or String
1089 Object file = files.get(i);
1090 String fileName = file.toString();
1091 DataSourceType protocol = (protocols == null)
1092 ? DataSourceType.FILE
1094 FileFormatI format = null;
1096 if (fileName.endsWith(".jar"))
1098 format = FileFormat.Jalview;
1103 format = new IdentifyFile().identify(file, protocol);
1105 if (file instanceof File)
1107 Platform.cacheFileData((File) file);
1108 new FileLoader().loadFile(null, (File) file, protocol, format);
1112 new FileLoader().LoadFile((String) file, protocol, format);
1116 } catch (Exception ex)
1121 evt.dropComplete(success); // need this to ensure input focus is properly
1122 // transfered to any new windows created
1132 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1134 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1135 JalviewFileChooser chooser = JalviewFileChooser
1136 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, BackupFiles.getEnabled());
1138 chooser.setFileView(new JalviewFileView());
1139 chooser.setDialogTitle(
1140 MessageManager.getString("label.open_local_file"));
1141 chooser.setToolTipText(MessageManager.getString("action.open"));
1143 chooser.setResponseHandler(0, new Runnable()
1148 File selectedFile = chooser.getSelectedFile();
1149 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1151 FileFormatI format = chooser.getSelectedFormat();
1154 * Call IdentifyFile to verify the file contains what its extension implies.
1155 * Skip this step for dynamically added file formats, because
1156 * IdentifyFile does not know how to recognise them.
1158 if (FileFormats.getInstance().isIdentifiable(format))
1162 format = new IdentifyFile().identify(selectedFile,
1163 DataSourceType.FILE);
1164 } catch (FileFormatException e)
1166 // format = null; //??
1170 new FileLoader().LoadFile(viewport, selectedFile,
1171 DataSourceType.FILE, format);
1174 chooser.showOpenDialog(this);
1178 * Shows a dialog for input of a URL at which to retrieve alignment data
1183 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1185 // This construct allows us to have a wider textfield
1187 JLabel label = new JLabel(
1188 MessageManager.getString("label.input_file_url"));
1190 JPanel panel = new JPanel(new GridLayout(2, 1));
1194 * the URL to fetch is
1195 * Java: an editable combobox with history
1196 * JS: (pending JAL-3038) a plain text field
1199 String urlBase = "http://www.";
1200 if (Platform.isJS())
1202 history = new JTextField(urlBase, 35);
1211 JComboBox<String> asCombo = new JComboBox<>();
1212 asCombo.setPreferredSize(new Dimension(400, 20));
1213 asCombo.setEditable(true);
1214 asCombo.addItem(urlBase);
1215 String historyItems = Cache.getProperty("RECENT_URL");
1216 if (historyItems != null)
1218 for (String token : historyItems.split("\\t"))
1220 asCombo.addItem(token);
1227 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1228 MessageManager.getString("action.cancel") };
1229 Runnable action = new Runnable()
1234 @SuppressWarnings("unchecked")
1235 String url = (history instanceof JTextField
1236 ? ((JTextField) history).getText()
1237 : ((JComboBox<String>) history).getSelectedItem()
1240 if (url.toLowerCase().endsWith(".jar"))
1242 if (viewport != null)
1244 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1245 FileFormat.Jalview);
1249 new FileLoader().LoadFile(url, DataSourceType.URL,
1250 FileFormat.Jalview);
1255 FileFormatI format = null;
1258 format = new IdentifyFile().identify(url, DataSourceType.URL);
1259 } catch (FileFormatException e)
1261 // TODO revise error handling, distinguish between
1262 // URL not found and response not valid
1267 String msg = MessageManager
1268 .formatMessage("label.couldnt_locate", url);
1269 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
1271 MessageManager.getString("label.url_not_found"),
1272 JvOptionPane.WARNING_MESSAGE);
1277 if (viewport != null)
1279 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1284 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1289 String dialogOption = MessageManager
1290 .getString("label.input_alignment_from_url");
1291 JvOptionPane.newOptionDialog(getDesktopPane())
1292 .setResponseHandler(0, action)
1293 .showInternalDialog(panel, dialogOption,
1294 JvOptionPane.YES_NO_CANCEL_OPTION,
1295 JvOptionPane.PLAIN_MESSAGE, null, options,
1296 MessageManager.getString("action.ok"));
1300 * Opens the CutAndPaste window for the user to paste an alignment in to
1303 * - if not null, the pasted alignment is added to the current
1304 * alignment; if null, to a new alignment window
1307 public void inputTextboxMenuItem_actionPerformed(
1308 AlignmentViewPanel viewPanel)
1310 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1311 cap.setForInput(viewPanel);
1312 Desktop.addInternalFrame(cap,
1313 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1323 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1324 Cache.setProperty("SCREENGEOMETRY_WIDTH",
1326 Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1327 screen.height + "");
1328 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1329 getWidth(), getHeight()));
1331 if (jconsole != null)
1333 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1334 jconsole.stopConsole();
1338 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1341 if (dialogExecutor != null)
1343 dialogExecutor.shutdownNow();
1345 closeAll_actionPerformed(null);
1347 if (groovyConsole != null)
1349 // suppress a possible repeat prompt to save script
1350 groovyConsole.setDirty(false);
1351 groovyConsole.exit();
1356 private void storeLastKnownDimensions(String string, Rectangle jc)
1358 Cache.log.debug("Storing last known dimensions for "
1359 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1360 + " height:" + jc.height);
1362 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1363 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1364 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1365 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1375 public void aboutMenuItem_actionPerformed(ActionEvent e)
1377 new Thread(new Runnable()
1382 // BH! true meaning "interactive" here (applet branch); was false in
1383 // develop version??
1384 new SplashScreen(true);
1390 * Returns the html text for the About screen, including any available version
1391 * number, build details, author details and citation reference, but without
1392 * the enclosing {@code html} tags
1396 public String getAboutMessage()
1398 StringBuilder message = new StringBuilder(1024);
1399 message.append("<h1><strong>Version: ")
1400 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1401 .append("<strong>Built: <em>")
1402 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1403 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1404 .append("</strong>");
1406 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1407 if (latestVersion.equals("Checking"))
1409 // JBP removed this message for 2.11: May be reinstated in future version
1410 // message.append("<br>...Checking latest version...</br>");
1412 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1414 boolean red = false;
1415 if (Cache.getProperty("VERSION").toLowerCase()
1416 .indexOf("automated build") == -1)
1419 // Displayed when code version and jnlp version do not match and code
1420 // version is not a development build
1421 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1424 message.append("<br>!! Version ")
1425 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1426 .append(" is available for download from ")
1427 .append(Cache.getDefault("www.jalview.org",
1428 "http://www.jalview.org"))
1432 message.append("</div>");
1435 message.append("<br>Authors: ");
1436 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1437 message.append(CITATION);
1439 return message.toString();
1443 * Action on requesting Help documentation
1446 public void documentationMenuItem_actionPerformed()
1450 if (Platform.isJS())
1452 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1461 Help.showHelpWindow();
1463 } catch (Exception ex)
1465 System.err.println("Error opening help: " + ex.getMessage());
1470 public void closeAll_actionPerformed(ActionEvent e)
1472 if (desktopPane == null)
1476 // TODO show a progress bar while closing?
1477 JInternalFrame[] frames = desktopPane.getAllFrames();
1478 for (int i = 0; i < frames.length; i++)
1482 frames[i].setClosed(true);
1483 } catch (java.beans.PropertyVetoException ex)
1487 Jalview.setCurrentAlignFrame(null);
1488 System.out.println("ALL CLOSED");
1491 * reset state of singleton objects as appropriate (clear down session state
1492 * when all windows are closed)
1494 getStructureSelectionManager().resetAll();
1498 public void raiseRelated_actionPerformed(ActionEvent e)
1500 reorderAssociatedWindows(false, false);
1504 public void minimizeAssociated_actionPerformed(ActionEvent e)
1506 reorderAssociatedWindows(true, false);
1509 void closeAssociatedWindows()
1511 reorderAssociatedWindows(false, true);
1517 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1521 protected void garbageCollect_actionPerformed(ActionEvent e)
1523 // We simply collect the garbage
1524 Cache.log.debug("Collecting garbage...");
1526 Cache.log.debug("Finished garbage collection.");
1533 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1537 protected void showMemusage_actionPerformed(ActionEvent e)
1539 getDesktopPane().showMemoryUsage(showMemusage.isSelected());
1546 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1550 protected void showConsole_actionPerformed(ActionEvent e)
1552 showConsole(showConsole.isSelected());
1555 Console jconsole = null;
1558 * control whether the java console is visible or not
1562 void showConsole(boolean selected)
1564 // TODO: decide if we should update properties file
1565 if (jconsole != null) // BH 2018
1567 showConsole.setSelected(selected);
1568 Cache.setProperty("SHOW_JAVA_CONSOLE",
1569 Boolean.valueOf(selected).toString());
1570 jconsole.setVisible(selected);
1574 void reorderAssociatedWindows(boolean minimize, boolean close)
1576 JInternalFrame[] frames = getDesktopPane().getAllFrames();
1577 if (frames == null || frames.length < 1)
1582 AlignmentViewport source = null, target = null;
1583 if (frames[0] instanceof AlignFrame)
1585 source = ((AlignFrame) frames[0]).getCurrentView();
1587 else if (frames[0] instanceof TreePanel)
1589 source = ((TreePanel) frames[0]).getViewPort();
1591 else if (frames[0] instanceof PCAPanel)
1593 source = ((PCAPanel) frames[0]).av;
1595 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1597 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1602 for (int i = 0; i < frames.length; i++)
1605 if (frames[i] == null)
1609 if (frames[i] instanceof AlignFrame)
1611 target = ((AlignFrame) frames[i]).getCurrentView();
1613 else if (frames[i] instanceof TreePanel)
1615 target = ((TreePanel) frames[i]).getViewPort();
1617 else if (frames[i] instanceof PCAPanel)
1619 target = ((PCAPanel) frames[i]).av;
1621 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1623 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1626 if (source == target)
1632 frames[i].setClosed(true);
1636 frames[i].setIcon(minimize);
1639 frames[i].toFront();
1643 } catch (java.beans.PropertyVetoException ex)
1658 protected void preferences_actionPerformed(ActionEvent e)
1664 * Prompts the user to choose a file and then saves the Jalview state as a
1665 * Jalview project file
1668 public void saveState_actionPerformed()
1670 saveState_actionPerformed(false);
1673 public void saveState_actionPerformed(boolean saveAs)
1675 java.io.File projectFile = getProjectFile();
1676 // autoSave indicates we already have a file and don't need to ask
1677 boolean autoSave = projectFile != null && !saveAs
1678 && BackupFiles.getEnabled();
1680 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1681 // saveAs="+saveAs+", Backups
1682 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1684 boolean approveSave = false;
1687 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1690 chooser.setFileView(new JalviewFileView());
1691 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1693 int value = chooser.showSaveDialog(this);
1695 if (value == JalviewFileChooser.APPROVE_OPTION)
1697 projectFile = chooser.getSelectedFile();
1698 setProjectFile(projectFile);
1703 if (approveSave || autoSave)
1705 final Desktop me = this;
1706 final java.io.File chosenFile = projectFile;
1707 new Thread(new Runnable()
1712 // TODO: refactor to Jalview desktop session controller action.
1713 setProgressBar(MessageManager.formatMessage(
1714 "label.saving_jalview_project", new Object[]
1715 { chosenFile.getName() }), chosenFile.hashCode());
1716 Cache.setProperty("LAST_DIRECTORY",
1717 chosenFile.getParent());
1718 // TODO catch and handle errors for savestate
1719 // TODO prevent user from messing with the Desktop whilst we're saving
1722 boolean doBackup = BackupFiles.getEnabled();
1723 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1725 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1729 backupfiles.setWriteSuccess(true);
1730 backupfiles.rollBackupsAndRenameTempFile();
1732 } catch (OutOfMemoryError oom)
1734 new OOMWarning("Whilst saving current state to "
1735 + chosenFile.getName(), oom);
1736 } catch (Exception ex)
1738 Cache.log.error("Problems whilst trying to save to "
1739 + chosenFile.getName(), ex);
1740 JvOptionPane.showMessageDialog(me,
1741 MessageManager.formatMessage(
1742 "label.error_whilst_saving_current_state_to",
1744 { chosenFile.getName() }),
1745 MessageManager.getString("label.couldnt_save_project"),
1746 JvOptionPane.WARNING_MESSAGE);
1748 setProgressBar(null, chosenFile.hashCode());
1755 public void saveAsState_actionPerformed(ActionEvent e)
1757 saveState_actionPerformed(true);
1760 protected void setProjectFile(File choice)
1762 this.projectFile = choice;
1765 public File getProjectFile()
1767 return this.projectFile;
1771 * Shows a file chooser dialog and tries to read in the selected file as a
1775 public void loadState_actionPerformed()
1777 final String[] suffix = new String[] { "jvp", "jar" };
1778 final String[] desc = new String[] { "Jalview Project",
1779 "Jalview Project (old)" };
1780 JalviewFileChooser chooser = new JalviewFileChooser(
1781 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1782 "Jalview Project", true, BackupFiles.getEnabled()); // last two booleans: allFiles,
1784 chooser.setFileView(new JalviewFileView());
1785 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1786 chooser.setResponseHandler(0, new Runnable()
1791 File selectedFile = chooser.getSelectedFile();
1792 setProjectFile(selectedFile);
1793 String choice = selectedFile.getAbsolutePath();
1794 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1795 new Thread(new Runnable()
1802 // BH was String "choice" here but needs to be File object
1803 new Jalview2XML().loadJalviewAlign(selectedFile);
1804 } catch (OutOfMemoryError oom)
1806 new OOMWarning("Whilst loading project from " + choice, oom);
1807 } catch (Exception ex)
1810 "Problems whilst loading project from " + choice, ex);
1811 JvOptionPane.showMessageDialog(Desktop.getDesktopPane(),
1812 MessageManager.formatMessage(
1813 "label.error_whilst_loading_project_from",
1817 .getString("label.couldnt_load_project"),
1818 JvOptionPane.WARNING_MESSAGE);
1825 chooser.showOpenDialog(this);
1829 public void inputSequence_actionPerformed(ActionEvent e)
1831 new SequenceFetcher(this);
1834 JPanel progressPanel;
1836 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1838 public void startLoading(final Object fileName)
1840 if (fileLoadingCount == 0)
1842 fileLoadingPanels.add(addProgressPanel(MessageManager
1843 .formatMessage("label.loading_file", new Object[]
1849 private JPanel addProgressPanel(String string)
1851 if (progressPanel == null)
1853 progressPanel = new JPanel(new GridLayout(1, 1));
1854 totalProgressCount = 0;
1855 getContentPane().add(progressPanel, BorderLayout.SOUTH);
1857 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1858 JProgressBar progressBar = new JProgressBar();
1859 progressBar.setIndeterminate(true);
1861 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1863 thisprogress.add(progressBar, BorderLayout.CENTER);
1864 progressPanel.add(thisprogress);
1865 ((GridLayout) progressPanel.getLayout()).setRows(
1866 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1867 ++totalProgressCount;
1869 return thisprogress;
1872 int totalProgressCount = 0;
1874 private void removeProgressPanel(JPanel progbar)
1876 if (progressPanel != null)
1878 synchronized (progressPanel)
1880 progressPanel.remove(progbar);
1881 GridLayout gl = (GridLayout) progressPanel.getLayout();
1882 gl.setRows(gl.getRows() - 1);
1883 if (--totalProgressCount < 1)
1885 this.getContentPane().remove(progressPanel);
1886 progressPanel = null;
1893 public void stopLoading()
1896 if (fileLoadingCount < 1)
1898 while (fileLoadingPanels.size() > 0)
1900 removeProgressPanel(fileLoadingPanels.remove(0));
1902 fileLoadingPanels.clear();
1903 fileLoadingCount = 0;
1908 public static int getViewCount(String alignmentId)
1910 AlignmentViewport[] aps = getViewports(alignmentId);
1911 return (aps == null) ? 0 : aps.length;
1916 * @param alignmentId
1917 * - if null, all sets are returned
1918 * @return all AlignmentPanels concerning the alignmentId sequence set
1920 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1922 if (Desktop.getDesktopPane() == null)
1924 // no frames created and in headless mode
1925 // TODO: verify that frames are recoverable when in headless mode
1928 List<AlignmentPanel> aps = new ArrayList<>();
1929 AlignFrame[] frames = getAlignFrames();
1934 for (AlignFrame af : frames)
1936 for (AlignmentPanel ap : af.alignPanels)
1938 if (alignmentId == null
1939 || alignmentId.equals(ap.av.getSequenceSetId()))
1945 if (aps.size() == 0)
1949 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1954 * get all the viewports on an alignment.
1956 * @param sequenceSetId
1957 * unique alignment id (may be null - all viewports returned in that
1959 * @return all viewports on the alignment bound to sequenceSetId
1961 public static AlignmentViewport[] getViewports(String sequenceSetId)
1963 List<AlignmentViewport> viewp = new ArrayList<>();
1964 if (getDesktopPane() != null)
1966 AlignFrame[] frames = Desktop.getAlignFrames();
1968 for (AlignFrame afr : frames)
1970 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1971 .equals(sequenceSetId))
1973 if (afr.alignPanels != null)
1975 for (AlignmentPanel ap : afr.alignPanels)
1977 if (sequenceSetId == null
1978 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1986 viewp.add(afr.getViewport());
1990 if (viewp.size() > 0)
1992 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1999 * Explode the views in the given frame into separate AlignFrame
2003 public static void explodeViews(AlignFrame af)
2005 int size = af.alignPanels.size();
2011 // BH! not in applet branch
2012 // FIXME: ideally should use UI interface API
2013 FeatureSettings viewFeatureSettings = (af.featureSettings != null
2014 && af.featureSettings.isOpen())
2015 ? af.featureSettings
2017 Rectangle fsBounds = af.getFeatureSettingsGeometry();
2018 for (int i = 0; i < size; i++)
2020 AlignmentPanel ap = af.alignPanels.get(i);
2021 AlignFrame newaf = new AlignFrame(ap);
2023 // BH! not in applet branch
2024 // transfer reference for existing feature settings to new alignFrame
2025 if (ap == af.alignPanel)
2027 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2029 newaf.featureSettings = viewFeatureSettings;
2031 newaf.setFeatureSettingsGeometry(fsBounds);
2035 * Restore the view's last exploded frame geometry if known. Multiple
2036 * views from one exploded frame share and restore the same (frame)
2037 * position and size.
2039 Rectangle geometry = ap.av.getExplodedGeometry();
2040 if (geometry != null)
2042 newaf.setBounds(geometry);
2045 ap.av.setGatherViewsHere(false);
2047 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2048 AlignFrame.DEFAULT_HEIGHT);
2049 // BH! not in applet branch
2050 // and materialise a new feature settings dialog instance for the new alignframe
2051 // (closes the old as if 'OK' was pressed)
2052 if (ap == af.alignPanel && newaf.featureSettings != null
2053 && newaf.featureSettings.isOpen()
2054 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2056 newaf.showFeatureSettingsUI();
2060 // BH! not in applet branch
2061 af.featureSettings = null;
2062 af.alignPanels.clear();
2063 af.closeMenuItem_actionPerformed(true);
2068 * Gather expanded views (separate AlignFrame's) with the same sequence set
2069 * identifier back in to this frame as additional views, and close the expanded
2070 * views. Note the expanded frames may themselves have multiple views. We take
2075 public void gatherViews(AlignFrame source)
2077 source.viewport.setGatherViewsHere(true);
2078 source.viewport.setExplodedGeometry(source.getBounds());
2079 JInternalFrame[] frames = getAllFrames();
2080 String viewId = source.viewport.getSequenceSetId();
2082 for (int t = 0; t < frames.length; t++)
2084 if (frames[t] instanceof AlignFrame && frames[t] != source)
2086 AlignFrame af = (AlignFrame) frames[t];
2087 boolean gatherThis = false;
2088 for (int a = 0; a < af.alignPanels.size(); a++)
2090 AlignmentPanel ap = af.alignPanels.get(a);
2091 if (viewId.equals(ap.av.getSequenceSetId()))
2094 ap.av.setGatherViewsHere(false);
2095 ap.av.setExplodedGeometry(af.getBounds());
2096 source.addAlignmentPanel(ap, false);
2102 if (af.featureSettings != null && af.featureSettings.isOpen())
2104 if (source.featureSettings == null)
2106 // preserve the feature settings geometry for this frame
2107 source.featureSettings = af.featureSettings;
2108 source.setFeatureSettingsGeometry(
2109 af.getFeatureSettingsGeometry());
2113 // close it and forget
2114 af.featureSettings.close();
2117 af.alignPanels.clear();
2118 af.closeMenuItem_actionPerformed(true);
2122 // refresh the feature setting UI for the source frame if it exists
2123 if (source.featureSettings != null
2124 && source.featureSettings.isOpen())
2126 source.showFeatureSettingsUI();
2130 public JInternalFrame[] getAllFrames()
2132 return desktopPane.getAllFrames();
2136 * Checks the given url to see if it gives a response indicating that the user
2137 * should be informed of a new questionnaire.
2141 public void checkForQuestionnaire(String url)
2143 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2144 // javax.swing.SwingUtilities.invokeLater(jvq);
2145 new Thread(jvq).start();
2148 public void checkURLLinks()
2150 // Thread off the URL link checker
2151 addDialogThread(new Runnable()
2156 if (Cache.getDefault("CHECKURLLINKS", true))
2158 // check what the actual links are - if it's just the default don't
2159 // bother with the warning
2160 List<String> links = Preferences.sequenceUrlLinks
2163 // only need to check links if there is one with a
2164 // SEQUENCE_ID which is not the default EMBL_EBI link
2165 ListIterator<String> li = links.listIterator();
2166 boolean check = false;
2167 List<JLabel> urls = new ArrayList<>();
2168 while (li.hasNext())
2170 String link = li.next();
2171 if (link.contains(UrlConstants.SEQUENCE_ID)
2172 && !UrlConstants.isDefaultString(link))
2175 int barPos = link.indexOf("|");
2176 String urlMsg = barPos == -1 ? link
2177 : link.substring(0, barPos) + ": "
2178 + link.substring(barPos + 1);
2179 urls.add(new JLabel(urlMsg));
2187 // ask user to check in case URL links use old style tokens
2188 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2189 JPanel msgPanel = new JPanel();
2190 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2191 msgPanel.add(Box.createVerticalGlue());
2192 JLabel msg = new JLabel(MessageManager
2193 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2194 JLabel msg2 = new JLabel(MessageManager
2195 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2197 for (JLabel url : urls)
2203 final JCheckBox jcb = new JCheckBox(
2204 MessageManager.getString("label.do_not_display_again"));
2205 jcb.addActionListener(new ActionListener()
2208 public void actionPerformed(ActionEvent e)
2210 // update Cache settings for "don't show this again"
2211 boolean showWarningAgain = !jcb.isSelected();
2212 Cache.setProperty("CHECKURLLINKS",
2213 Boolean.valueOf(showWarningAgain).toString());
2218 JvOptionPane.showMessageDialog(desktopPane, msgPanel,
2220 .getString("label.SEQUENCE_ID_no_longer_used"),
2221 JvOptionPane.WARNING_MESSAGE);
2228 * Proxy class for JDesktopPane which optionally displays the current memory
2229 * usage and highlights the desktop area with a red bar if free memory runs low.
2233 public class MyDesktopPane extends JDesktopPane
2236 private static final float ONE_MB = 1048576f;
2238 boolean showMemoryUsage = false;
2242 java.text.NumberFormat df;
2244 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2247 public MyDesktopPane(boolean showMemoryUsage)
2249 showMemoryUsage(showMemoryUsage);
2252 public void showMemoryUsage(boolean showMemory)
2254 this.showMemoryUsage = showMemory;
2257 Thread worker = new Thread(this);
2263 public boolean isShowMemoryUsage()
2265 return showMemoryUsage;
2271 df = java.text.NumberFormat.getNumberInstance();
2272 df.setMaximumFractionDigits(2);
2273 runtime = Runtime.getRuntime();
2275 while (showMemoryUsage)
2279 maxMemory = runtime.maxMemory() / ONE_MB;
2280 allocatedMemory = runtime.totalMemory() / ONE_MB;
2281 freeMemory = runtime.freeMemory() / ONE_MB;
2282 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2284 percentUsage = (totalFreeMemory / maxMemory) * 100;
2286 // if (percentUsage < 20)
2288 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2290 // instance.set.setBorder(border1);
2293 // sleep after showing usage
2295 } catch (Exception ex)
2297 ex.printStackTrace();
2303 public void paintComponent(Graphics g)
2305 if (showMemoryUsage && g != null && df != null)
2307 if (percentUsage < 20)
2309 g.setColor(Color.red);
2311 FontMetrics fm = g.getFontMetrics();
2314 g.drawString(MessageManager.formatMessage("label.memory_stats",
2316 { df.format(totalFreeMemory), df.format(maxMemory),
2317 df.format(percentUsage) }),
2318 10, getHeight() - fm.getHeight());
2325 * Accessor method to quickly get all the AlignmentFrames loaded.
2327 * @return an array of AlignFrame, or null if none found
2329 public static AlignFrame[] getAlignFrames()
2331 if (Jalview.isHeadlessMode())
2333 // Desktop.getDesktopPane() is null in headless mode
2334 return new AlignFrame[] { Jalview.getCurrentAlignFrame() };
2337 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2343 List<AlignFrame> avp = new ArrayList<>();
2345 for (int i = frames.length - 1; i > -1; i--)
2347 if (frames[i] instanceof AlignFrame)
2349 avp.add((AlignFrame) frames[i]);
2351 else if (frames[i] instanceof SplitFrame)
2354 * Also check for a split frame containing an AlignFrame
2356 GSplitFrame sf = (GSplitFrame) frames[i];
2357 if (sf.getTopFrame() instanceof AlignFrame)
2359 avp.add((AlignFrame) sf.getTopFrame());
2361 if (sf.getBottomFrame() instanceof AlignFrame)
2363 avp.add((AlignFrame) sf.getBottomFrame());
2367 if (avp.size() == 0)
2371 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2376 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2380 public GStructureViewer[] getJmols()
2382 JInternalFrame[] frames = Desktop.getDesktopPane().getAllFrames();
2388 List<GStructureViewer> avp = new ArrayList<>();
2390 for (int i = frames.length - 1; i > -1; i--)
2392 if (frames[i] instanceof AppJmol)
2394 GStructureViewer af = (GStructureViewer) frames[i];
2398 if (avp.size() == 0)
2402 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2407 * Add Groovy Support to Jalview
2410 public void groovyShell_actionPerformed()
2414 openGroovyConsole();
2415 } catch (Exception ex)
2417 Cache.log.error("Groovy Shell Creation failed.", ex);
2418 JvOptionPane.showInternalMessageDialog(desktopPane,
2420 MessageManager.getString("label.couldnt_create_groovy_shell"),
2421 MessageManager.getString("label.groovy_support_failed"),
2422 JvOptionPane.ERROR_MESSAGE);
2427 * Open the Groovy console
2429 private void openGroovyConsole()
2431 if (groovyConsole == null)
2433 groovyConsole = new groovy.ui.Console();
2434 groovyConsole.setVariable("Jalview", this);
2435 groovyConsole.run();
2438 * We allow only one console at a time, so that AlignFrame menu option
2439 * 'Calculate | Run Groovy script' is unambiguous.
2440 * Disable 'Groovy Console', and enable 'Run script', when the console is
2441 * opened, and the reverse when it is closed
2443 Window window = (Window) groovyConsole.getFrame();
2444 window.addWindowListener(new WindowAdapter()
2447 public void windowClosed(WindowEvent e)
2450 * rebind CMD-Q from Groovy Console to Jalview Quit
2453 enableExecuteGroovy(false);
2459 * show Groovy console window (after close and reopen)
2461 ((Window) groovyConsole.getFrame()).setVisible(true);
2464 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2465 * and disable opening a second console
2467 enableExecuteGroovy(true);
2471 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2474 protected void addQuitHandler()
2476 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2477 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2478 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
2480 getRootPane().getActionMap().put("Quit", new AbstractAction()
2483 public void actionPerformed(ActionEvent e)
2491 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2494 * true if Groovy console is open
2496 public void enableExecuteGroovy(boolean enabled)
2499 * disable opening a second Groovy console
2500 * (or re-enable when the console is closed)
2502 groovyShell.setEnabled(!enabled);
2504 AlignFrame[] alignFrames = getAlignFrames();
2505 if (alignFrames != null)
2507 for (AlignFrame af : alignFrames)
2509 af.setGroovyEnabled(enabled);
2515 * Progress bars managed by the IProgressIndicator method.
2517 private Hashtable<Long, JPanel> progressBars;
2519 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2524 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2527 public void setProgressBar(String message, long id)
2529 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2531 if (progressBars == null)
2533 progressBars = new Hashtable<>();
2534 progressBarHandlers = new Hashtable<>();
2537 if (progressBars.get(Long.valueOf(id)) != null)
2539 JPanel panel = progressBars.remove(Long.valueOf(id));
2540 if (progressBarHandlers.contains(Long.valueOf(id)))
2542 progressBarHandlers.remove(Long.valueOf(id));
2544 removeProgressPanel(panel);
2548 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2555 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2556 * jalview.gui.IProgressIndicatorHandler)
2559 public void registerHandler(final long id,
2560 final IProgressIndicatorHandler handler)
2562 if (progressBarHandlers == null
2563 || !progressBars.containsKey(Long.valueOf(id)))
2565 throw new Error(MessageManager.getString(
2566 "error.call_setprogressbar_before_registering_handler"));
2568 progressBarHandlers.put(Long.valueOf(id), handler);
2569 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2570 if (handler.canCancel())
2572 JButton cancel = new JButton(
2573 MessageManager.getString("action.cancel"));
2574 final IProgressIndicator us = this;
2575 cancel.addActionListener(new ActionListener()
2579 public void actionPerformed(ActionEvent e)
2581 handler.cancelActivity(id);
2582 us.setProgressBar(MessageManager
2583 .formatMessage("label.cancelled_params", new Object[]
2584 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2588 progressPanel.add(cancel, BorderLayout.EAST);
2594 * @return true if any progress bars are still active
2597 public boolean operationInProgress()
2599 if (progressBars != null && progressBars.size() > 0)
2607 * This will return the first AlignFrame holding the given viewport instance. It
2608 * will break if there are more than one AlignFrames viewing a particular av.
2611 * @return alignFrame for viewport
2613 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2615 if (getDesktopPane() != null)
2617 AlignmentPanel[] aps = getAlignmentPanels(
2618 viewport.getSequenceSetId());
2619 for (int panel = 0; aps != null && panel < aps.length; panel++)
2621 if (aps[panel] != null && aps[panel].av == viewport)
2623 return aps[panel].alignFrame;
2631 * flag set if jalview GUI is being operated programmatically
2633 private boolean inBatchMode = false;
2636 * check if jalview GUI is being operated programmatically
2638 * @return inBatchMode
2640 public boolean isInBatchMode()
2646 * set flag if jalview GUI is being operated programmatically
2648 * @param inBatchMode
2650 public void setInBatchMode(boolean inBatchMode)
2652 this.inBatchMode = inBatchMode;
2655 public void startServiceDiscovery()
2657 startServiceDiscovery(false);
2660 public void startServiceDiscovery(boolean blocking)
2662 boolean alive = true;
2663 Thread t0 = null, t1 = null, t2 = null;
2664 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2667 // todo: changesupport handlers need to be transferred
2668 if (discoverer == null)
2670 discoverer = jalview.ws.jws1.Discoverer.getInstance();
2671 // register PCS handler for getDesktopPane().
2672 discoverer.addPropertyChangeListener(changeSupport);
2674 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2675 // until we phase out completely
2676 (t0 = new Thread(discoverer)).start();
2679 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2681 t2 = jalview.ws.jws2.Jws2Discoverer.getInstance()
2682 .startDiscoverer(changeSupport);
2686 // TODO: do rest service discovery
2695 } catch (Exception e)
2698 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2699 || (t3 != null && t3.isAlive())
2700 || (t0 != null && t0.isAlive());
2706 * called to check if the service discovery process completed successfully.
2710 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2712 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2714 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getInstance()
2715 .getErrorMessages();
2718 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2720 if (serviceChangedDialog == null)
2722 // only run if we aren't already displaying one of these.
2723 addDialogThread(serviceChangedDialog = new Runnable()
2730 * JalviewDialog jd =new JalviewDialog() {
2732 * @Override protected void cancelPressed() { // TODO
2733 * Auto-generated method stub
2735 * }@Override protected void okPressed() { // TODO
2736 * Auto-generated method stub
2738 * }@Override protected void raiseClosed() { // TODO
2739 * Auto-generated method stub
2741 * } }; jd.initDialogFrame(new
2742 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2743 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2744 * + " or mis-configured HTTP proxy settings.<br/>" +
2745 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2747 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2748 * ), true, true, "Web Service Configuration Problem", 450,
2751 * jd.waitForInput();
2753 JvOptionPane.showConfirmDialog(Desktop.getDesktopPane(),
2754 new JLabel("<html><table width=\"450\"><tr><td>"
2755 + ermsg + "</td></tr></table>"
2756 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2757 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2758 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2759 + " Tools->Preferences dialog box to change them.</p></html>"),
2760 "Web Service Configuration Problem",
2761 JvOptionPane.DEFAULT_OPTION,
2762 JvOptionPane.ERROR_MESSAGE);
2763 serviceChangedDialog = null;
2772 "Errors reported by JABA discovery service. Check web services preferences.\n"
2779 Runnable serviceChangedDialog = null;
2782 * start a thread to open a URL in the configured browser. Pops up a warning
2783 * dialog to the user if there is an exception when calling out to the browser
2788 public static void showUrl(final String url)
2790 showUrl(url, Desktop.getInstance());
2794 * Like showUrl but allows progress handler to be specified
2798 * (null) or object implementing IProgressIndicator
2800 public static void showUrl(final String url,
2801 final IProgressIndicator progress)
2803 new Thread(new Runnable()
2810 if (progress != null && !Platform.isJS())
2812 progress.setProgressBar(MessageManager
2813 .formatMessage("status.opening_params", new Object[]
2814 { url }), this.hashCode());
2816 Platform.openURL(url);
2817 } catch (Exception ex)
2819 JvOptionPane.showInternalMessageDialog(Desktop.getDesktopPane(),
2821 .getString("label.web_browser_not_found_unix"),
2822 MessageManager.getString("label.web_browser_not_found"),
2823 JvOptionPane.WARNING_MESSAGE);
2825 ex.printStackTrace();
2827 if (progress != null && !Platform.isJS())
2829 progress.setProgressBar(null, this.hashCode());
2835 private WsParamSetManager wsparamManager = null;
2837 public static ParamManager getUserParameterStore()
2839 Desktop d = Desktop.getInstance();
2840 if (d.wsparamManager == null)
2842 d.wsparamManager = new WsParamSetManager();
2844 return d.wsparamManager;
2848 * static hyperlink handler proxy method for use by Jalview's internal windows
2852 public static void hyperlinkUpdate(HyperlinkEvent e)
2854 if (e.getEventType() == EventType.ACTIVATED)
2859 url = e.getURL().toString();
2860 Desktop.showUrl(url);
2861 } catch (Exception x)
2865 if (Cache.log != null)
2867 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2872 "Couldn't handle string " + url + " as a URL.");
2875 // ignore any exceptions due to dud links.
2882 * single thread that handles display of dialogs to user.
2884 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2887 * flag indicating if dialogExecutor should try to acquire a permit
2889 private volatile boolean dialogPause = true;
2894 private java.util.concurrent.Semaphore block = new Semaphore(0);
2897 private groovy.ui.Console groovyConsole;
2899 public StructureViewer lastTargetedView;
2902 * add another dialog thread to the queue
2906 public void addDialogThread(final Runnable prompter)
2908 dialogExecutor.submit(new Runnable()
2918 } catch (InterruptedException x)
2922 // BH! Q: do we mean System.headless ? or "nogui/nodisplay" headless?
2923 if (Jalview.isHeadlessMode())
2929 SwingUtilities.invokeAndWait(prompter);
2930 } catch (Exception q)
2932 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2938 public void startDialogQueue()
2940 // set the flag so we don't pause waiting for another permit and semaphore
2941 // the current task to begin
2942 dialogPause = false;
2947 * Outputs an image of the desktop to file in EPS format, after prompting the
2948 * user for choice of Text or Lineart character rendering (unless a preference
2949 * has been set). The file name is generated as
2952 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2956 protected void snapShotWindow_actionPerformed(ActionEvent e)
2958 // currently the menu option to do this is not shown
2961 int width = getWidth();
2962 int height = getHeight();
2964 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2965 ImageWriterI writer = new ImageWriterI()
2968 public void exportImage(Graphics g) throws Exception
2971 Cache.log.info("Successfully written snapshot to file "
2972 + of.getAbsolutePath());
2975 String title = "View of desktop";
2976 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2978 exporter.doExport(of, this, width, height, title);
2982 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2983 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
2984 * location last time the view was expanded (if any). However it does not
2985 * remember the split pane divider location - this is set to match the
2986 * 'exploding' frame.
2990 public void explodeViews(SplitFrame sf)
2992 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2993 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2994 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2996 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2998 int viewCount = topPanels.size();
3005 * Processing in reverse order works, forwards order leaves the first panels
3006 * not visible. I don't know why!
3008 for (int i = viewCount - 1; i >= 0; i--)
3011 * Make new top and bottom frames. These take over the respective
3012 * AlignmentPanel objects, including their AlignmentViewports, so the
3013 * cdna/protein relationships between the viewports is carried over to the
3016 * explodedGeometry holds the (x, y) position of the previously exploded
3017 * SplitFrame, and the (width, height) of the AlignFrame component
3019 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3020 AlignFrame newTopFrame = new AlignFrame(topPanel);
3021 newTopFrame.setSize(oldTopFrame.getSize());
3022 newTopFrame.setVisible(true);
3023 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3024 .getExplodedGeometry();
3025 if (geometry != null)
3027 newTopFrame.setSize(geometry.getSize());
3030 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3031 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3032 newBottomFrame.setSize(oldBottomFrame.getSize());
3033 newBottomFrame.setVisible(true);
3034 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3035 .getExplodedGeometry();
3036 if (geometry != null)
3038 newBottomFrame.setSize(geometry.getSize());
3041 topPanel.av.setGatherViewsHere(false);
3042 bottomPanel.av.setGatherViewsHere(false);
3043 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3045 if (geometry != null)
3047 splitFrame.setLocation(geometry.getLocation());
3049 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3053 * Clear references to the panels (now relocated in the new SplitFrames)
3054 * before closing the old SplitFrame.
3057 bottomPanels.clear();
3062 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3063 * back into the given SplitFrame as additional views. Note that the gathered
3064 * frames may themselves have multiple views.
3068 public void gatherViews(GSplitFrame source)
3071 * special handling of explodedGeometry for a view within a SplitFrame: - it
3072 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3073 * height) of the AlignFrame component
3075 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3076 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3077 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3078 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3079 myBottomFrame.viewport
3080 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3081 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3082 myTopFrame.viewport.setGatherViewsHere(true);
3083 myBottomFrame.viewport.setGatherViewsHere(true);
3084 String topViewId = myTopFrame.viewport.getSequenceSetId();
3085 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3087 JInternalFrame[] frames = desktopPane.getAllFrames();
3088 for (JInternalFrame frame : frames)
3090 if (frame instanceof SplitFrame && frame != source)
3092 SplitFrame sf = (SplitFrame) frame;
3093 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3094 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3095 boolean gatherThis = false;
3096 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3098 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3099 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3100 if (topViewId.equals(topPanel.av.getSequenceSetId())
3101 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3104 topPanel.av.setGatherViewsHere(false);
3105 bottomPanel.av.setGatherViewsHere(false);
3106 topPanel.av.setExplodedGeometry(
3107 new Rectangle(sf.getLocation(), topFrame.getSize()));
3108 bottomPanel.av.setExplodedGeometry(
3109 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3110 myTopFrame.addAlignmentPanel(topPanel, false);
3111 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3117 topFrame.getAlignPanels().clear();
3118 bottomFrame.getAlignPanels().clear();
3125 * The dust settles...give focus to the tab we did this from.
3127 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3130 public static groovy.ui.Console getGroovyConsole()
3132 Desktop desktop = Desktop.getInstance();
3133 return desktop == null ? null : desktop.groovyConsole;
3137 * handles the payload of a drag and drop event.
3139 * TODO refactor to desktop utilities class
3142 * - Data source strings extracted from the drop event
3144 * - protocol for each data source extracted from the drop event
3148 * - the payload from the drop event
3151 @SuppressWarnings("unchecked")
3152 public static void transferFromDropTarget(List<Object> files,
3153 List<DataSourceType> protocols, DropTargetDropEvent evt,
3154 Transferable t) throws Exception
3157 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3159 // DataFlavor[] flavors = t.getTransferDataFlavors();
3160 // for (int i = 0; i < flavors.length; i++) {
3161 // if (flavors[i].isFlavorJavaFileListType()) {
3162 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3163 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3164 // for (int j = 0; j < list.size(); j++) {
3165 // File file = (File) list.get(j);
3166 // byte[] data = getDroppedFileBytes(file);
3167 // fileName.setText(file.getName() + " - " + data.length + " " +
3168 // evt.getLocation());
3169 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3170 // target.setText(new String(data));
3172 // dtde.dropComplete(true);
3177 DataFlavor uriListFlavor = new DataFlavor(
3178 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3181 urlFlavour = new DataFlavor(
3182 "application/x-java-url; class=java.net.URL");
3183 } catch (ClassNotFoundException cfe)
3185 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3188 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3193 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3194 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3195 // means url may be null.
3198 protocols.add(DataSourceType.URL);
3199 files.add(url.toString());
3200 Cache.log.debug("Drop handled as URL dataflavor "
3201 + files.get(files.size() - 1));
3206 if (Platform.isAMacAndNotJS())
3209 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3212 } catch (Throwable ex)
3214 Cache.log.debug("URL drop handler failed.", ex);
3217 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3219 // Works on Windows and MacOSX
3220 Cache.log.debug("Drop handled as javaFileListFlavor");
3221 for (Object file : (List<Object>) t
3222 .getTransferData(DataFlavor.javaFileListFlavor))
3225 protocols.add(DataSourceType.FILE);
3230 // Unix like behaviour
3231 boolean added = false;
3233 if (t.isDataFlavorSupported(uriListFlavor))
3235 Cache.log.debug("Drop handled as uriListFlavor");
3236 // This is used by Unix drag system
3237 data = (String) t.getTransferData(uriListFlavor);
3241 // fallback to text: workaround - on OSX where there's a JVM bug
3242 Cache.log.debug("standard URIListFlavor failed. Trying text");
3243 // try text fallback
3244 DataFlavor textDf = new DataFlavor(
3245 "text/plain;class=java.lang.String");
3246 if (t.isDataFlavorSupported(textDf))
3248 data = (String) t.getTransferData(textDf);
3251 Cache.log.debug("Plain text drop content returned "
3252 + (data == null ? "Null - failed" : data));
3257 while (protocols.size() < files.size())
3259 Cache.log.debug("Adding missing FILE protocol for "
3260 + files.get(protocols.size()));
3261 protocols.add(DataSourceType.FILE);
3263 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3264 data, "\r\n"); st.hasMoreTokens();)
3267 String s = st.nextToken();
3268 if (s.startsWith("#"))
3270 // the line is a comment (as per the RFC 2483)
3273 java.net.URI uri = new java.net.URI(s);
3274 if (uri.getScheme().toLowerCase().startsWith("http"))
3276 protocols.add(DataSourceType.URL);
3277 files.add(uri.toString());
3281 // otherwise preserve old behaviour: catch all for file objects
3282 java.io.File file = new java.io.File(uri);
3283 protocols.add(DataSourceType.FILE);
3284 files.add(file.toString());
3289 if (Cache.log.isDebugEnabled())
3291 if (data == null || !added)
3294 if (t.getTransferDataFlavors() != null
3295 && t.getTransferDataFlavors().length > 0)
3298 "Couldn't resolve drop data. Here are the supported flavors:");
3299 for (DataFlavor fl : t.getTransferDataFlavors())
3302 "Supported transfer dataflavor: " + fl.toString());
3303 Object df = t.getTransferData(fl);
3306 Cache.log.debug("Retrieves: " + df);
3310 Cache.log.debug("Retrieved nothing");
3316 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3322 if (Platform.isWindowsAndNotJS())
3324 Cache.log.debug("Scanning dropped content for Windows Link Files");
3326 // resolve any .lnk files in the file drop
3327 for (int f = 0; f < files.size(); f++)
3329 String source = files.get(f).toString().toLowerCase();
3330 if (protocols.get(f).equals(DataSourceType.FILE)
3331 && (source.endsWith(".lnk") || source.endsWith(".url")
3332 || source.endsWith(".site")))
3336 Object obj = files.get(f);
3337 File lf = (obj instanceof File ? (File) obj
3338 : new File((String) obj));
3339 // process link file to get a URL
3340 Cache.log.debug("Found potential link file: " + lf);
3341 WindowsShortcut wscfile = new WindowsShortcut(lf);
3342 String fullname = wscfile.getRealFilename();
3343 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3344 files.set(f, fullname);
3345 Cache.log.debug("Parsed real filename " + fullname
3346 + " to extract protocol: " + protocols.get(f));
3347 } catch (Exception ex)
3350 "Couldn't parse " + files.get(f) + " as a link file.",
3359 * Sets the Preferences property for experimental features to True or False
3360 * depending on the state of the controlling menu item
3363 protected void showExperimental_actionPerformed(boolean selected)
3365 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3369 * Answers a (possibly empty) list of any structure viewer frames (currently for
3370 * either Jmol or Chimera) which are currently open. This may optionally be
3371 * restricted to viewers of a specified class, or viewers linked to a specified
3375 * if not null, only return viewers linked to this panel
3376 * @param structureViewerClass
3377 * if not null, only return viewers of this class
3380 public List<StructureViewerBase> getStructureViewers(
3381 AlignmentPanel apanel,
3382 Class<? extends StructureViewerBase> structureViewerClass)
3384 List<StructureViewerBase> result = new ArrayList<>();
3385 JInternalFrame[] frames = getAllFrames();
3387 for (JInternalFrame frame : frames)
3389 if (frame instanceof StructureViewerBase)
3391 if (structureViewerClass == null
3392 || structureViewerClass.isInstance(frame))
3395 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3397 result.add((StructureViewerBase) frame);
3407 public MyDesktopPane desktopPane;
3410 * Get the instance of the JDesktopPane from the application-local Desktop
3413 * The key here is that the Java application can have multiple static
3414 * instances of the desktop JFrame because those instances are sandboxed, but
3415 * the SwingJS JFrames will be in the same VM-like space. So we need
3416 * application singletons, at least for JavaScript.
3420 public static MyDesktopPane getDesktopPane()
3422 Desktop desktop = Desktop.getInstance();
3423 return desktop == null ? null : desktop.desktopPane;
3427 * Answers an 'application scope' singleton instance of this class. Separate
3428 * SwingJS 'applets' running in the same browser page will each have a
3429 * distinct instance of Desktop.
3433 public static Desktop getInstance()
3435 return Jalview.isHeadlessMode() ? null
3436 : (Desktop) ApplicationSingletonProvider
3437 .getInstance(Desktop.class);