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.Dimension;
26 import java.awt.FontMetrics;
27 import java.awt.Graphics;
28 import java.awt.GridLayout;
29 import java.awt.Point;
30 import java.awt.Rectangle;
31 import java.awt.Toolkit;
32 import java.awt.Window;
33 import java.awt.datatransfer.Clipboard;
34 import java.awt.datatransfer.ClipboardOwner;
35 import java.awt.datatransfer.DataFlavor;
36 import java.awt.datatransfer.Transferable;
37 import java.awt.dnd.DnDConstants;
38 import java.awt.dnd.DropTargetDragEvent;
39 import java.awt.dnd.DropTargetDropEvent;
40 import java.awt.dnd.DropTargetEvent;
41 import java.awt.dnd.DropTargetListener;
42 import java.awt.event.ActionEvent;
43 import java.awt.event.ActionListener;
44 import java.awt.event.InputEvent;
45 import java.awt.event.KeyEvent;
46 import java.awt.event.MouseAdapter;
47 import java.awt.event.MouseEvent;
48 import java.awt.event.WindowAdapter;
49 import java.awt.event.WindowEvent;
50 import java.beans.PropertyChangeEvent;
51 import java.beans.PropertyChangeListener;
53 import java.io.FileWriter;
54 import java.io.IOException;
56 import java.util.ArrayList;
57 import java.util.HashMap;
58 import java.util.Hashtable;
59 import java.util.List;
60 import java.util.ListIterator;
61 import java.util.Vector;
62 import java.util.concurrent.ExecutionException;
63 import java.util.concurrent.ExecutorService;
64 import java.util.concurrent.Executors;
65 import java.util.concurrent.Future;
66 import java.util.concurrent.FutureTask;
67 import java.util.concurrent.Semaphore;
69 import javax.swing.AbstractAction;
70 import javax.swing.Action;
71 import javax.swing.ActionMap;
72 import javax.swing.Box;
73 import javax.swing.BoxLayout;
74 import javax.swing.DefaultDesktopManager;
75 import javax.swing.DesktopManager;
76 import javax.swing.InputMap;
77 import javax.swing.JButton;
78 import javax.swing.JCheckBox;
79 import javax.swing.JComboBox;
80 import javax.swing.JComponent;
81 import javax.swing.JDesktopPane;
82 import javax.swing.JInternalFrame;
83 import javax.swing.JLabel;
84 import javax.swing.JMenuItem;
85 import javax.swing.JPanel;
86 import javax.swing.JPopupMenu;
87 import javax.swing.JProgressBar;
88 import javax.swing.JTextField;
89 import javax.swing.KeyStroke;
90 import javax.swing.SwingUtilities;
91 import javax.swing.event.HyperlinkEvent;
92 import javax.swing.event.HyperlinkEvent.EventType;
93 import javax.swing.event.InternalFrameAdapter;
94 import javax.swing.event.InternalFrameEvent;
96 import org.stackoverflowusers.file.WindowsShortcut;
98 import jalview.api.AlignViewportI;
99 import jalview.api.AlignmentViewPanel;
100 import jalview.bin.Cache;
101 import jalview.bin.Jalview;
102 import jalview.gui.ImageExporter.ImageWriterI;
103 import jalview.io.BackupFiles;
104 import jalview.io.DataSourceType;
105 import jalview.io.FileFormat;
106 import jalview.io.FileFormatException;
107 import jalview.io.FileFormatI;
108 import jalview.io.FileFormats;
109 import jalview.io.FileLoader;
110 import jalview.io.FormatAdapter;
111 import jalview.io.IdentifyFile;
112 import jalview.io.JalviewFileChooser;
113 import jalview.io.JalviewFileView;
114 import jalview.jbgui.GSplitFrame;
115 import jalview.jbgui.GStructureViewer;
116 import jalview.project.Jalview2XML;
117 import jalview.structure.StructureSelectionManager;
118 import jalview.urls.IdOrgSettings;
119 import jalview.util.BrowserLauncher;
120 import jalview.util.ImageMaker.TYPE;
121 import jalview.util.MessageManager;
122 import jalview.util.Platform;
123 import jalview.util.ShortcutKeyMaskExWrapper;
124 import jalview.util.UrlConstants;
125 import jalview.viewmodel.AlignmentViewport;
126 import jalview.ws.WSDiscovererI;
127 import jalview.ws.params.ParamManager;
128 import jalview.ws.utils.UrlDownloadClient;
135 * @version $Revision: 1.155 $
137 public class Desktop extends jalview.jbgui.GDesktop
138 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
139 jalview.api.StructureSelectionManagerProvider
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 static int DEFAULT_MIN_WIDTH = 300;
152 private static int DEFAULT_MIN_HEIGHT = 250;
154 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
156 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
158 private static final 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<String, FileWriter>();
164 @SuppressWarnings("deprecation")
165 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
168 * news reader - null if it was never started.
170 private BlogReader jvnews = null;
172 private File projectFile;
176 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
179 public void addJalviewPropertyChangeListener(
180 PropertyChangeListener listener)
182 changeSupport.addJalviewPropertyChangeListener(listener);
186 * @param propertyName
188 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
189 * java.beans.PropertyChangeListener)
192 public void addJalviewPropertyChangeListener(String propertyName,
193 PropertyChangeListener listener)
195 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
199 * @param propertyName
201 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
202 * java.beans.PropertyChangeListener)
205 public void removeJalviewPropertyChangeListener(String propertyName,
206 PropertyChangeListener listener)
208 changeSupport.removeJalviewPropertyChangeListener(propertyName,
212 /** Singleton Desktop instance */
213 public static Desktop instance;
215 public static MyDesktopPane desktop;
217 public static MyDesktopPane getDesktop()
219 // BH 2018 could use currentThread() here as a reference to a
220 // Hashtable<Thread, MyDesktopPane> in JavaScript
224 static int openFrameCount = 0;
226 static final int xOffset = 30;
228 static final int yOffset = 30;
230 public static jalview.ws.jws1.Discoverer discoverer;
232 public static Object[] jalviewClipboard;
234 public static boolean internalCopy = false;
236 static int fileLoadingCount = 0;
238 class MyDesktopManager implements DesktopManager
241 private DesktopManager delegate;
243 public MyDesktopManager(DesktopManager delegate)
245 this.delegate = delegate;
249 public void activateFrame(JInternalFrame f)
253 delegate.activateFrame(f);
254 } catch (NullPointerException npe)
256 Point p = getMousePosition();
257 instance.showPasteMenu(p.x, p.y);
262 public void beginDraggingFrame(JComponent f)
264 delegate.beginDraggingFrame(f);
268 public void beginResizingFrame(JComponent f, int direction)
270 delegate.beginResizingFrame(f, direction);
274 public void closeFrame(JInternalFrame f)
276 delegate.closeFrame(f);
280 public void deactivateFrame(JInternalFrame f)
282 delegate.deactivateFrame(f);
286 public void deiconifyFrame(JInternalFrame f)
288 delegate.deiconifyFrame(f);
292 public void dragFrame(JComponent f, int newX, int newY)
298 delegate.dragFrame(f, newX, newY);
302 public void endDraggingFrame(JComponent f)
304 delegate.endDraggingFrame(f);
309 public void endResizingFrame(JComponent f)
311 delegate.endResizingFrame(f);
316 public void iconifyFrame(JInternalFrame f)
318 delegate.iconifyFrame(f);
322 public void maximizeFrame(JInternalFrame f)
324 delegate.maximizeFrame(f);
328 public void minimizeFrame(JInternalFrame f)
330 delegate.minimizeFrame(f);
334 public void openFrame(JInternalFrame f)
336 delegate.openFrame(f);
340 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
347 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
351 public void setBoundsForFrame(JComponent f, int newX, int newY,
352 int newWidth, int newHeight)
354 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
357 // All other methods, simply delegate
362 * Creates a new Desktop object.
368 * A note to implementors. It is ESSENTIAL that any activities that might
369 * block are spawned off as threads rather than waited for during this
374 doConfigureStructurePrefs();
375 setTitle("Jalview " + Cache.getProperty("VERSION"));
377 if (!Platform.isAMac())
379 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
383 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
389 APQHandlers.setAPQHandlers(this);
390 } catch (Throwable t)
392 System.out.println("Error setting APQHandlers: " + t.toString());
393 // t.printStackTrace();
396 addWindowListener(new WindowAdapter()
400 public void windowClosing(WindowEvent ev)
406 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE",
409 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE",
411 desktop = new MyDesktopPane(selmemusage);
413 showMemusage.setSelected(selmemusage);
414 desktop.setBackground(Color.white);
416 getContentPane().setLayout(new BorderLayout());
417 // alternate config - have scrollbars - see notes in JAL-153
418 // JScrollPane sp = new JScrollPane();
419 // sp.getViewport().setView(desktop);
420 // getContentPane().add(sp, BorderLayout.CENTER);
422 // BH 2018 - just an experiment to try unclipped JInternalFrames.
425 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
428 getContentPane().add(desktop, BorderLayout.CENTER);
429 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
431 // This line prevents Windows Look&Feel resizing all new windows to maximum
432 // if previous window was maximised
433 desktop.setDesktopManager(new MyDesktopManager(
434 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
435 : Platform.isAMacAndNotJS()
436 ? new AquaInternalFrameManager(
437 desktop.getDesktopManager())
438 : desktop.getDesktopManager())));
440 Rectangle dims = getLastKnownDimensions("");
447 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
448 int xPos = Math.max(5, (screenSize.width - 900) / 2);
449 int yPos = Math.max(5, (screenSize.height - 650) / 2);
450 setBounds(xPos, yPos, 900, 650);
453 if (!Platform.isJS())
460 jconsole = new Console(this, showjconsole);
461 jconsole.setHeader(Cache.getVersionDetailsForConsole());
462 showConsole(showjconsole);
464 showNews.setVisible(false);
466 experimentalFeatures.setSelected(showExperimental());
468 getIdentifiersOrgData();
472 // Spawn a thread that shows the splashscreen
474 SwingUtilities.invokeLater(new Runnable()
479 new SplashScreen(true);
483 // Thread off a new instance of the file chooser - this reduces the time
485 // takes to open it later on.
486 new Thread(new Runnable()
491 Cache.log.debug("Filechooser init thread started.");
492 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
493 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
495 Cache.log.debug("Filechooser init thread finished.");
498 // Add the service change listener
499 changeSupport.addJalviewPropertyChangeListener("services",
500 new PropertyChangeListener()
504 public void propertyChange(PropertyChangeEvent evt)
506 Cache.log.debug("Firing service changed event for "
507 + evt.getNewValue());
508 JalviewServicesChanged(evt);
513 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
515 this.addWindowListener(new WindowAdapter()
518 public void windowClosing(WindowEvent evt)
525 this.addMouseListener(ma = new MouseAdapter()
528 public void mousePressed(MouseEvent evt)
530 if (evt.isPopupTrigger()) // Mac
532 showPasteMenu(evt.getX(), evt.getY());
537 public void mouseReleased(MouseEvent evt)
539 if (evt.isPopupTrigger()) // Windows
541 showPasteMenu(evt.getX(), evt.getY());
545 desktop.addMouseListener(ma);
550 * Answers true if user preferences to enable experimental features is True
555 public boolean showExperimental()
557 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
558 Boolean.FALSE.toString());
559 return Boolean.valueOf(experimental).booleanValue();
562 public void doConfigureStructurePrefs()
564 // configure services
565 StructureSelectionManager ssm = StructureSelectionManager
566 .getStructureSelectionManager(this);
567 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
569 ssm.setAddTempFacAnnot(Cache
570 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
571 ssm.setProcessSecondaryStructure(Cache
572 .getDefault(Preferences.STRUCT_FROM_PDB, true));
573 ssm.setSecStructServices(
574 Cache.getDefault(Preferences.USE_RNAVIEW, true));
578 ssm.setAddTempFacAnnot(false);
579 ssm.setProcessSecondaryStructure(false);
580 ssm.setSecStructServices(false);
584 public void checkForNews()
586 final Desktop me = this;
587 // Thread off the news reader, in case there are connection problems.
588 new Thread(new Runnable()
593 Cache.log.debug("Starting news thread.");
594 jvnews = new BlogReader(me);
595 showNews.setVisible(true);
596 Cache.log.debug("Completed news thread.");
601 public void getIdentifiersOrgData()
603 // Thread off the identifiers fetcher
604 new Thread(new Runnable()
609 Cache.log.debug("Downloading data from identifiers.org");
612 UrlDownloadClient.download(IdOrgSettings.getUrl(),
613 IdOrgSettings.getDownloadLocation());
614 } catch (IOException e)
616 Cache.log.debug("Exception downloading identifiers.org data"
625 protected void showNews_actionPerformed(ActionEvent e)
627 showNews(showNews.isSelected());
630 void showNews(boolean visible)
632 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
633 showNews.setSelected(visible);
634 if (visible && !jvnews.isVisible())
636 new Thread(new Runnable()
641 long now = System.currentTimeMillis();
642 Desktop.instance.setProgressBar(
643 MessageManager.getString("status.refreshing_news"), now);
644 jvnews.refreshNews();
645 Desktop.instance.setProgressBar(null, now);
653 * recover the last known dimensions for a jalview window
656 * - empty string is desktop, all other windows have unique prefix
657 * @return null or last known dimensions scaled to current geometry (if last
658 * window geom was known)
660 Rectangle getLastKnownDimensions(String windowName)
662 // TODO: lock aspect ratio for scaling desktop Bug #0058199
663 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
664 String x = Cache.getProperty(windowName + "SCREEN_X");
665 String y = Cache.getProperty(windowName + "SCREEN_Y");
667 .getProperty(windowName + "SCREEN_WIDTH");
668 String height = Cache
669 .getProperty(windowName + "SCREEN_HEIGHT");
670 if ((x != null) && (y != null) && (width != null) && (height != null))
672 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
673 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
674 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
676 // attempt #1 - try to cope with change in screen geometry - this
677 // version doesn't preserve original jv aspect ratio.
678 // take ratio of current screen size vs original screen size.
679 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
680 Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
681 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
682 Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
683 // rescale the bounds depending upon the current screen geometry.
684 ix = (int) (ix * sw);
685 iw = (int) (iw * sw);
686 iy = (int) (iy * sh);
687 ih = (int) (ih * sh);
688 while (ix >= screenSize.width)
691 "Window geometry location recall error: shifting horizontal to within screenbounds.");
692 ix -= screenSize.width;
694 while (iy >= screenSize.height)
697 "Window geometry location recall error: shifting vertical to within screenbounds.");
698 iy -= screenSize.height;
701 "Got last known dimensions for " + windowName + ": x:" + ix
702 + " y:" + iy + " width:" + iw + " height:" + ih);
704 // return dimensions for new instance
705 return new Rectangle(ix, iy, iw, ih);
710 void showPasteMenu(int x, int y)
712 JPopupMenu popup = new JPopupMenu();
713 JMenuItem item = new JMenuItem(
714 MessageManager.getString("label.paste_new_window"));
715 item.addActionListener(new ActionListener()
718 public void actionPerformed(ActionEvent evt)
725 popup.show(this, x, y);
732 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
733 Transferable contents = c.getContents(this);
735 if (contents != null)
737 String file = (String) contents
738 .getTransferData(DataFlavor.stringFlavor);
740 FileFormatI format = new IdentifyFile().identify(file,
741 DataSourceType.PASTE);
743 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
746 } catch (Exception ex)
749 "Unable to paste alignment from system clipboard:\n" + ex);
754 * Adds and opens the given frame to the desktop
765 public static synchronized void addInternalFrame(
766 final JInternalFrame frame, String title, int w, int h)
768 addInternalFrame(frame, title, true, w, h, true, false);
772 * Add an internal frame to the Jalview desktop
779 * When true, display frame immediately, otherwise, caller must call
780 * setVisible themselves.
786 public static synchronized void addInternalFrame(
787 final JInternalFrame frame, String title, boolean makeVisible,
790 addInternalFrame(frame, title, makeVisible, w, h, true, false);
794 * Add an internal frame to the Jalview desktop and make it visible
807 public static synchronized void addInternalFrame(
808 final JInternalFrame frame, String title, int w, int h,
811 addInternalFrame(frame, title, true, w, h, resizable, false);
815 * Add an internal frame to the Jalview desktop
822 * When true, display frame immediately, otherwise, caller must call
823 * setVisible themselves.
830 * @param ignoreMinSize
831 * Do not set the default minimum size for frame
833 public static synchronized void addInternalFrame(
834 final JInternalFrame frame, String title, boolean makeVisible,
835 int w, int h, boolean resizable, boolean ignoreMinSize)
838 // TODO: allow callers to determine X and Y position of frame (eg. via
840 // TODO: consider fixing method to update entries in the window submenu with
841 // the current window title
843 frame.setTitle(title);
844 if (frame.getWidth() < 1 || frame.getHeight() < 1)
848 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
849 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
850 // IF JALVIEW IS RUNNING HEADLESS
851 // ///////////////////////////////////////////////
852 if (instance == null || (System.getProperty("java.awt.headless") != null
853 && System.getProperty("java.awt.headless").equals("true")))
862 frame.setMinimumSize(
863 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
865 // Set default dimension for Alignment Frame window.
866 // The Alignment Frame window could be added from a number of places,
868 // I did this here in order not to miss out on any Alignment frame.
869 if (frame instanceof AlignFrame)
871 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
872 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
876 frame.setVisible(makeVisible);
877 frame.setClosable(true);
878 frame.setResizable(resizable);
879 frame.setMaximizable(resizable);
880 frame.setIconifiable(resizable);
881 frame.setOpaque(Platform.isJS());
883 if (frame.getX() < 1 && frame.getY() < 1)
885 frame.setLocation(xOffset * openFrameCount,
886 yOffset * ((openFrameCount - 1) % 10) + yOffset);
890 * add an entry for the new frame in the Window menu
891 * (and remove it when the frame is closed)
893 final JMenuItem menuItem = new JMenuItem(title);
894 frame.addInternalFrameListener(new InternalFrameAdapter()
897 public void internalFrameActivated(InternalFrameEvent evt)
899 JInternalFrame itf = desktop.getSelectedFrame();
902 if (itf instanceof AlignFrame)
904 Jalview.setCurrentAlignFrame((AlignFrame) itf);
911 public void internalFrameClosed(InternalFrameEvent evt)
913 PaintRefresher.RemoveComponent(frame);
916 * defensive check to prevent frames being
917 * added half off the window
919 if (openFrameCount > 0)
925 * ensure no reference to alignFrame retained by menu item listener
927 if (menuItem.getActionListeners().length > 0)
929 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
931 windowMenu.remove(menuItem);
935 menuItem.addActionListener(new ActionListener()
938 public void actionPerformed(ActionEvent e)
942 frame.setSelected(true);
943 frame.setIcon(false);
944 } catch (java.beans.PropertyVetoException ex)
946 // System.err.println(ex.toString());
951 setKeyBindings(frame);
955 windowMenu.add(menuItem);
960 frame.setSelected(true);
961 frame.requestFocus();
962 } catch (java.beans.PropertyVetoException ve)
964 } catch (java.lang.ClassCastException cex)
967 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
973 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
978 private static void setKeyBindings(JInternalFrame frame)
980 @SuppressWarnings("serial")
981 final Action closeAction = new AbstractAction()
984 public void actionPerformed(ActionEvent e)
991 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
993 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
994 InputEvent.CTRL_DOWN_MASK);
995 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
996 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
998 InputMap inputMap = frame
999 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1000 String ctrlW = ctrlWKey.toString();
1001 inputMap.put(ctrlWKey, ctrlW);
1002 inputMap.put(cmdWKey, ctrlW);
1004 ActionMap actionMap = frame.getActionMap();
1005 actionMap.put(ctrlW, closeAction);
1009 public void lostOwnership(Clipboard clipboard, Transferable contents)
1013 Desktop.jalviewClipboard = null;
1016 internalCopy = false;
1020 public void dragEnter(DropTargetDragEvent evt)
1025 public void dragExit(DropTargetEvent evt)
1030 public void dragOver(DropTargetDragEvent evt)
1035 public void dropActionChanged(DropTargetDragEvent evt)
1046 public void drop(DropTargetDropEvent evt)
1048 boolean success = true;
1049 // JAL-1552 - acceptDrop required before getTransferable call for
1050 // Java's Transferable for native dnd
1051 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1052 Transferable t = evt.getTransferable();
1053 List<Object> files = new ArrayList<>();
1054 List<DataSourceType> protocols = new ArrayList<>();
1058 Desktop.transferFromDropTarget(files, protocols, evt, t);
1059 } catch (Exception e)
1061 e.printStackTrace();
1069 for (int i = 0; i < files.size(); i++)
1071 // BH 2018 File or String
1072 Object file = files.get(i);
1073 String fileName = file.toString();
1074 DataSourceType protocol = (protocols == null)
1075 ? DataSourceType.FILE
1077 FileFormatI format = null;
1079 if (fileName.endsWith(".jar"))
1081 format = FileFormat.Jalview;
1086 format = new IdentifyFile().identify(file, protocol);
1088 if (file instanceof File)
1090 Platform.cacheFileData((File) file);
1092 new FileLoader().LoadFile(null, file, protocol, format);
1095 } catch (Exception ex)
1100 evt.dropComplete(success); // need this to ensure input focus is properly
1101 // transfered to any new windows created
1111 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1113 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1114 JalviewFileChooser chooser = JalviewFileChooser
1115 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, BackupFiles.getEnabled());
1117 chooser.setFileView(new JalviewFileView());
1118 chooser.setDialogTitle(
1119 MessageManager.getString("label.open_local_file"));
1120 chooser.setToolTipText(MessageManager.getString("action.open"));
1122 chooser.setResponseHandler(0, new Runnable()
1127 File selectedFile = chooser.getSelectedFile();
1128 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1130 FileFormatI format = chooser.getSelectedFormat();
1133 * Call IdentifyFile to verify the file contains what its extension implies.
1134 * Skip this step for dynamically added file formats, because
1135 * IdentifyFile does not know how to recognise them.
1137 if (FileFormats.getInstance().isIdentifiable(format))
1141 format = new IdentifyFile().identify(selectedFile,
1142 DataSourceType.FILE);
1143 } catch (FileFormatException e)
1145 // format = null; //??
1149 new FileLoader().LoadFile(viewport, selectedFile,
1150 DataSourceType.FILE, format);
1153 chooser.showOpenDialog(this);
1157 * Shows a dialog for input of a URL at which to retrieve alignment data
1162 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1164 // This construct allows us to have a wider textfield
1166 JLabel label = new JLabel(
1167 MessageManager.getString("label.input_file_url"));
1169 JPanel panel = new JPanel(new GridLayout(2, 1));
1173 * the URL to fetch is
1174 * Java: an editable combobox with history
1175 * JS: (pending JAL-3038) a plain text field
1178 String urlBase = "http://www.";
1179 if (Platform.isJS())
1181 history = new JTextField(urlBase, 35);
1190 JComboBox<String> asCombo = new JComboBox<>();
1191 asCombo.setPreferredSize(new Dimension(400, 20));
1192 asCombo.setEditable(true);
1193 asCombo.addItem(urlBase);
1194 String historyItems = Cache.getProperty("RECENT_URL");
1195 if (historyItems != null)
1197 for (String token : historyItems.split("\\t"))
1199 asCombo.addItem(token);
1206 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1207 MessageManager.getString("action.cancel") };
1208 Runnable action = new Runnable()
1213 @SuppressWarnings("unchecked")
1214 String url = (history instanceof JTextField
1215 ? ((JTextField) history).getText()
1216 : ((JComboBox<String>) history).getSelectedItem()
1219 if (url.toLowerCase().endsWith(".jar"))
1221 if (viewport != null)
1223 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1224 FileFormat.Jalview);
1228 new FileLoader().LoadFile(url, DataSourceType.URL,
1229 FileFormat.Jalview);
1234 FileFormatI format = null;
1237 format = new IdentifyFile().identify(url, DataSourceType.URL);
1238 } catch (FileFormatException e)
1240 // TODO revise error handling, distinguish between
1241 // URL not found and response not valid
1246 String msg = MessageManager
1247 .formatMessage("label.couldnt_locate", url);
1248 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1249 MessageManager.getString("label.url_not_found"),
1250 JvOptionPane.WARNING_MESSAGE);
1255 if (viewport != null)
1257 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1262 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1267 String dialogOption = MessageManager
1268 .getString("label.input_alignment_from_url");
1269 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1270 .showInternalDialog(panel, dialogOption,
1271 JvOptionPane.YES_NO_CANCEL_OPTION,
1272 JvOptionPane.PLAIN_MESSAGE, null, options,
1273 MessageManager.getString("action.ok"));
1277 * Opens the CutAndPaste window for the user to paste an alignment in to
1280 * - if not null, the pasted alignment is added to the current
1281 * alignment; if null, to a new alignment window
1284 public void inputTextboxMenuItem_actionPerformed(
1285 AlignmentViewPanel viewPanel)
1287 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1288 cap.setForInput(viewPanel);
1289 Desktop.addInternalFrame(cap,
1290 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1300 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1301 Cache.setProperty("SCREENGEOMETRY_WIDTH",
1303 Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1304 screen.height + "");
1305 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1306 getWidth(), getHeight()));
1308 if (jconsole != null)
1310 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1311 jconsole.stopConsole();
1315 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1318 if (dialogExecutor != null)
1320 dialogExecutor.shutdownNow();
1322 closeAll_actionPerformed(null);
1324 if (groovyConsole != null)
1326 // suppress a possible repeat prompt to save script
1327 groovyConsole.setDirty(false);
1328 groovyConsole.exit();
1333 private void storeLastKnownDimensions(String string, Rectangle jc)
1335 Cache.log.debug("Storing last known dimensions for "
1336 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1337 + " height:" + jc.height);
1339 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1340 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1341 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1342 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1352 public void aboutMenuItem_actionPerformed(ActionEvent e)
1354 new Thread(new Runnable()
1359 new SplashScreen(false);
1365 * Returns the html text for the About screen, including any available version
1366 * number, build details, author details and citation reference, but without
1367 * the enclosing {@code html} tags
1371 public String getAboutMessage()
1373 StringBuilder message = new StringBuilder(1024);
1374 message.append("<h1><strong>Version: ")
1375 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1376 .append("<strong>Built: <em>")
1377 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1378 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1379 .append("</strong>");
1381 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1382 if (latestVersion.equals("Checking"))
1384 // JBP removed this message for 2.11: May be reinstated in future version
1385 // message.append("<br>...Checking latest version...</br>");
1387 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1389 boolean red = false;
1390 if (Cache.getProperty("VERSION").toLowerCase()
1391 .indexOf("automated build") == -1)
1394 // Displayed when code version and jnlp version do not match and code
1395 // version is not a development build
1396 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1399 message.append("<br>!! Version ")
1400 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1401 .append(" is available for download from ")
1402 .append(Cache.getDefault("www.jalview.org",
1403 "http://www.jalview.org"))
1407 message.append("</div>");
1410 message.append("<br>Authors: ");
1411 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1412 message.append(CITATION);
1414 return message.toString();
1418 * Action on requesting Help documentation
1421 public void documentationMenuItem_actionPerformed()
1425 if (Platform.isJS())
1427 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1436 Help.showHelpWindow();
1438 } catch (Exception ex)
1440 System.err.println("Error opening help: " + ex.getMessage());
1445 public void closeAll_actionPerformed(ActionEvent e)
1447 // TODO show a progress bar while closing?
1448 JInternalFrame[] frames = desktop.getAllFrames();
1449 for (int i = 0; i < frames.length; i++)
1453 frames[i].setClosed(true);
1454 } catch (java.beans.PropertyVetoException ex)
1458 Jalview.setCurrentAlignFrame(null);
1459 System.out.println("ALL CLOSED");
1462 * reset state of singleton objects as appropriate (clear down session state
1463 * when all windows are closed)
1465 StructureSelectionManager ssm = StructureSelectionManager
1466 .getStructureSelectionManager(this);
1474 public void raiseRelated_actionPerformed(ActionEvent e)
1476 reorderAssociatedWindows(false, false);
1480 public void minimizeAssociated_actionPerformed(ActionEvent e)
1482 reorderAssociatedWindows(true, false);
1485 void closeAssociatedWindows()
1487 reorderAssociatedWindows(false, true);
1493 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1497 protected void garbageCollect_actionPerformed(ActionEvent e)
1499 // We simply collect the garbage
1500 Cache.log.debug("Collecting garbage...");
1502 Cache.log.debug("Finished garbage collection.");
1509 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1513 protected void showMemusage_actionPerformed(ActionEvent e)
1515 desktop.showMemoryUsage(showMemusage.isSelected());
1522 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1526 protected void showConsole_actionPerformed(ActionEvent e)
1528 showConsole(showConsole.isSelected());
1531 Console jconsole = null;
1534 * control whether the java console is visible or not
1538 void showConsole(boolean selected)
1540 // TODO: decide if we should update properties file
1541 if (jconsole != null) // BH 2018
1543 showConsole.setSelected(selected);
1544 Cache.setProperty("SHOW_JAVA_CONSOLE",
1545 Boolean.valueOf(selected).toString());
1546 jconsole.setVisible(selected);
1550 void reorderAssociatedWindows(boolean minimize, boolean close)
1552 JInternalFrame[] frames = desktop.getAllFrames();
1553 if (frames == null || frames.length < 1)
1558 AlignViewportI source = null;
1559 AlignViewportI target = null;
1560 if (frames[0] instanceof AlignFrame)
1562 source = ((AlignFrame) frames[0]).getCurrentView();
1564 else if (frames[0] instanceof TreePanel)
1566 source = ((TreePanel) frames[0]).getViewPort();
1568 else if (frames[0] instanceof PCAPanel)
1570 source = ((PCAPanel) frames[0]).av;
1572 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1574 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1579 for (int i = 0; i < frames.length; i++)
1582 if (frames[i] == null)
1586 if (frames[i] instanceof AlignFrame)
1588 target = ((AlignFrame) frames[i]).getCurrentView();
1590 else if (frames[i] instanceof TreePanel)
1592 target = ((TreePanel) frames[i]).getViewPort();
1594 else if (frames[i] instanceof PCAPanel)
1596 target = ((PCAPanel) frames[i]).av;
1598 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1600 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1603 if (source == target)
1609 frames[i].setClosed(true);
1613 frames[i].setIcon(minimize);
1616 frames[i].toFront();
1620 } catch (java.beans.PropertyVetoException ex)
1635 protected void preferences_actionPerformed(ActionEvent e)
1641 * Prompts the user to choose a file and then saves the Jalview state as a
1642 * Jalview project file
1645 public void saveState_actionPerformed()
1647 saveState_actionPerformed(false);
1650 public void saveState_actionPerformed(boolean saveAs)
1652 java.io.File projectFile = getProjectFile();
1653 // autoSave indicates we already have a file and don't need to ask
1654 boolean autoSave = projectFile != null && !saveAs
1655 && BackupFiles.getEnabled();
1657 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1658 // saveAs="+saveAs+", Backups
1659 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1661 boolean approveSave = false;
1664 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1667 chooser.setFileView(new JalviewFileView());
1668 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1670 int value = chooser.showSaveDialog(this);
1672 if (value == JalviewFileChooser.APPROVE_OPTION)
1674 projectFile = chooser.getSelectedFile();
1675 setProjectFile(projectFile);
1680 if (approveSave || autoSave)
1682 final Desktop me = this;
1683 final java.io.File chosenFile = projectFile;
1684 new Thread(new Runnable()
1689 // TODO: refactor to Jalview desktop session controller action.
1690 setProgressBar(MessageManager.formatMessage(
1691 "label.saving_jalview_project", new Object[]
1692 { chosenFile.getName() }), chosenFile.hashCode());
1693 Cache.setProperty("LAST_DIRECTORY",
1694 chosenFile.getParent());
1695 // TODO catch and handle errors for savestate
1696 // TODO prevent user from messing with the Desktop whilst we're saving
1699 boolean doBackup = BackupFiles.getEnabled();
1700 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1702 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1706 backupfiles.setWriteSuccess(true);
1707 backupfiles.rollBackupsAndRenameTempFile();
1709 } catch (OutOfMemoryError oom)
1711 new OOMWarning("Whilst saving current state to "
1712 + chosenFile.getName(), oom);
1713 } catch (Exception ex)
1715 Cache.log.error("Problems whilst trying to save to "
1716 + chosenFile.getName(), ex);
1717 JvOptionPane.showMessageDialog(me,
1718 MessageManager.formatMessage(
1719 "label.error_whilst_saving_current_state_to",
1721 { chosenFile.getName() }),
1722 MessageManager.getString("label.couldnt_save_project"),
1723 JvOptionPane.WARNING_MESSAGE);
1725 setProgressBar(null, chosenFile.hashCode());
1732 public void saveAsState_actionPerformed(ActionEvent e)
1734 saveState_actionPerformed(true);
1737 private void setProjectFile(File choice)
1739 this.projectFile = choice;
1742 public File getProjectFile()
1744 return this.projectFile;
1748 * Shows a file chooser dialog and tries to read in the selected file as a
1752 public void loadState_actionPerformed()
1754 final String[] suffix = new String[] { "jvp", "jar" };
1755 final String[] desc = new String[] { "Jalview Project",
1756 "Jalview Project (old)" };
1757 JalviewFileChooser chooser = new JalviewFileChooser(
1758 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1759 "Jalview Project", true, BackupFiles.getEnabled()); // last two booleans: allFiles,
1761 chooser.setFileView(new JalviewFileView());
1762 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1763 chooser.setResponseHandler(0, new Runnable()
1768 File selectedFile = chooser.getSelectedFile();
1769 setProjectFile(selectedFile);
1770 String choice = selectedFile.getAbsolutePath();
1771 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1772 new Thread(new Runnable()
1779 new Jalview2XML().loadJalviewAlign(selectedFile);
1780 } catch (OutOfMemoryError oom)
1782 new OOMWarning("Whilst loading project from " + choice, oom);
1783 } catch (Exception ex)
1786 "Problems whilst loading project from " + choice, ex);
1787 JvOptionPane.showMessageDialog(Desktop.desktop,
1788 MessageManager.formatMessage(
1789 "label.error_whilst_loading_project_from",
1792 MessageManager.getString("label.couldnt_load_project"),
1793 JvOptionPane.WARNING_MESSAGE);
1800 chooser.showOpenDialog(this);
1804 public void inputSequence_actionPerformed(ActionEvent e)
1806 new SequenceFetcher(this);
1809 JPanel progressPanel;
1811 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1813 public void startLoading(final Object fileName)
1815 if (fileLoadingCount == 0)
1817 fileLoadingPanels.add(addProgressPanel(MessageManager
1818 .formatMessage("label.loading_file", new Object[]
1824 private JPanel addProgressPanel(String string)
1826 if (progressPanel == null)
1828 progressPanel = new JPanel(new GridLayout(1, 1));
1829 totalProgressCount = 0;
1830 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1832 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1833 JProgressBar progressBar = new JProgressBar();
1834 progressBar.setIndeterminate(true);
1836 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1838 thisprogress.add(progressBar, BorderLayout.CENTER);
1839 progressPanel.add(thisprogress);
1840 ((GridLayout) progressPanel.getLayout()).setRows(
1841 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1842 ++totalProgressCount;
1843 instance.validate();
1844 return thisprogress;
1847 int totalProgressCount = 0;
1849 private void removeProgressPanel(JPanel progbar)
1851 if (progressPanel != null)
1853 synchronized (progressPanel)
1855 progressPanel.remove(progbar);
1856 GridLayout gl = (GridLayout) progressPanel.getLayout();
1857 gl.setRows(gl.getRows() - 1);
1858 if (--totalProgressCount < 1)
1860 this.getContentPane().remove(progressPanel);
1861 progressPanel = null;
1868 public void stopLoading()
1871 if (fileLoadingCount < 1)
1873 while (fileLoadingPanels.size() > 0)
1875 removeProgressPanel(fileLoadingPanels.remove(0));
1877 fileLoadingPanels.clear();
1878 fileLoadingCount = 0;
1883 public static int getViewCount(String alignmentId)
1885 AlignmentViewport[] aps = getViewports(alignmentId);
1886 return (aps == null) ? 0 : aps.length;
1891 * @param alignmentId
1892 * - if null, all sets are returned
1893 * @return all AlignmentPanels concerning the alignmentId sequence set
1895 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1897 if (Desktop.desktop == null)
1899 // no frames created and in headless mode
1900 // TODO: verify that frames are recoverable when in headless mode
1903 List<AlignmentPanel> aps = new ArrayList<>();
1904 AlignFrame[] frames = getAlignFrames();
1909 for (AlignFrame af : frames)
1911 for (AlignmentPanel ap : af.alignPanels)
1913 if (alignmentId == null
1914 || alignmentId.equals(ap.av.getSequenceSetId()))
1920 if (aps.size() == 0)
1924 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1929 * get all the viewports on an alignment.
1931 * @param sequenceSetId
1932 * unique alignment id (may be null - all viewports returned in that
1934 * @return all viewports on the alignment bound to sequenceSetId
1936 public static AlignmentViewport[] getViewports(String sequenceSetId)
1938 List<AlignmentViewport> viewp = new ArrayList<>();
1939 if (desktop != null)
1941 AlignFrame[] frames = Desktop.getAlignFrames();
1943 for (AlignFrame afr : frames)
1945 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1946 .equals(sequenceSetId))
1948 if (afr.alignPanels != null)
1950 for (AlignmentPanel ap : afr.alignPanels)
1952 if (sequenceSetId == null
1953 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1961 viewp.add(afr.getViewport());
1965 if (viewp.size() > 0)
1967 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1974 * Explode the views in the given frame into separate AlignFrame
1978 public static void explodeViews(AlignFrame af)
1980 int size = af.alignPanels.size();
1986 // FIXME: ideally should use UI interface API
1987 FeatureSettings viewFeatureSettings = (af.featureSettings != null
1988 && af.featureSettings.isOpen())
1989 ? af.featureSettings
1991 Rectangle fsBounds = af.getFeatureSettingsGeometry();
1992 for (int i = 0; i < size; i++)
1994 AlignmentPanel ap = af.alignPanels.get(i);
1996 AlignFrame newaf = new AlignFrame(ap);
1998 // transfer reference for existing feature settings to new alignFrame
1999 if (ap == af.alignPanel)
2001 if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
2003 newaf.featureSettings = viewFeatureSettings;
2005 newaf.setFeatureSettingsGeometry(fsBounds);
2009 * Restore the view's last exploded frame geometry if known. Multiple
2010 * views from one exploded frame share and restore the same (frame)
2011 * position and size.
2013 Rectangle geometry = ap.av.getExplodedGeometry();
2014 if (geometry != null)
2016 newaf.setBounds(geometry);
2019 ap.av.setGatherViewsHere(false);
2021 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2022 AlignFrame.DEFAULT_HEIGHT);
2023 // and materialise a new feature settings dialog instance for the new alignframe
2024 // (closes the old as if 'OK' was pressed)
2025 if (ap == af.alignPanel && newaf.featureSettings != null
2026 && newaf.featureSettings.isOpen()
2027 && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
2029 newaf.showFeatureSettingsUI();
2033 af.featureSettings = null;
2034 af.alignPanels.clear();
2035 af.closeMenuItem_actionPerformed(true);
2040 * Gather expanded views (separate AlignFrame's) with the same sequence set
2041 * identifier back in to this frame as additional views, and close the expanded
2042 * views. Note the expanded frames may themselves have multiple views. We take
2047 public void gatherViews(AlignFrame source)
2049 source.viewport.setGatherViewsHere(true);
2050 source.viewport.setExplodedGeometry(source.getBounds());
2051 JInternalFrame[] frames = desktop.getAllFrames();
2052 String viewId = source.viewport.getSequenceSetId();
2053 for (int t = 0; t < frames.length; t++)
2055 if (frames[t] instanceof AlignFrame && frames[t] != source)
2057 AlignFrame af = (AlignFrame) frames[t];
2058 boolean gatherThis = false;
2059 for (int a = 0; a < af.alignPanels.size(); a++)
2061 AlignmentPanel ap = af.alignPanels.get(a);
2062 if (viewId.equals(ap.av.getSequenceSetId()))
2065 ap.av.setGatherViewsHere(false);
2066 ap.av.setExplodedGeometry(af.getBounds());
2067 source.addAlignmentPanel(ap, false);
2073 if (af.featureSettings != null && af.featureSettings.isOpen())
2075 if (source.featureSettings == null)
2077 // preserve the feature settings geometry for this frame
2078 source.featureSettings = af.featureSettings;
2079 source.setFeatureSettingsGeometry(
2080 af.getFeatureSettingsGeometry());
2084 // close it and forget
2085 af.featureSettings.close();
2088 af.alignPanels.clear();
2089 af.closeMenuItem_actionPerformed(true);
2094 // refresh the feature setting UI for the source frame if it exists
2095 if (source.featureSettings != null
2096 && source.featureSettings.isOpen())
2098 source.showFeatureSettingsUI();
2102 public JInternalFrame[] getAllFrames()
2104 return desktop.getAllFrames();
2108 * Checks the given url to see if it gives a response indicating that the user
2109 * should be informed of a new questionnaire.
2113 public void checkForQuestionnaire(String url)
2115 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2116 // javax.swing.SwingUtilities.invokeLater(jvq);
2117 new Thread(jvq).start();
2120 public void checkURLLinks()
2122 // Thread off the URL link checker
2123 addDialogThread(new Runnable()
2128 if (Cache.getDefault("CHECKURLLINKS", true))
2130 // check what the actual links are - if it's just the default don't
2131 // bother with the warning
2132 List<String> links = Preferences.sequenceUrlLinks
2135 // only need to check links if there is one with a
2136 // SEQUENCE_ID which is not the default EMBL_EBI link
2137 ListIterator<String> li = links.listIterator();
2138 boolean check = false;
2139 List<JLabel> urls = new ArrayList<>();
2140 while (li.hasNext())
2142 String link = li.next();
2143 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2144 && !UrlConstants.isDefaultString(link))
2147 int barPos = link.indexOf("|");
2148 String urlMsg = barPos == -1 ? link
2149 : link.substring(0, barPos) + ": "
2150 + link.substring(barPos + 1);
2151 urls.add(new JLabel(urlMsg));
2159 // ask user to check in case URL links use old style tokens
2160 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2161 JPanel msgPanel = new JPanel();
2162 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2163 msgPanel.add(Box.createVerticalGlue());
2164 JLabel msg = new JLabel(MessageManager
2165 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2166 JLabel msg2 = new JLabel(MessageManager
2167 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2169 for (JLabel url : urls)
2175 final JCheckBox jcb = new JCheckBox(
2176 MessageManager.getString("label.do_not_display_again"));
2177 jcb.addActionListener(new ActionListener()
2180 public void actionPerformed(ActionEvent e)
2182 // update Cache settings for "don't show this again"
2183 boolean showWarningAgain = !jcb.isSelected();
2184 Cache.setProperty("CHECKURLLINKS",
2185 Boolean.valueOf(showWarningAgain).toString());
2190 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2192 .getString("label.SEQUENCE_ID_no_longer_used"),
2193 JvOptionPane.WARNING_MESSAGE);
2200 * Proxy class for JDesktopPane which optionally displays the current memory
2201 * usage and highlights the desktop area with a red bar if free memory runs low.
2205 public class MyDesktopPane extends JDesktopPane
2208 private static final float ONE_MB = 1048576f;
2210 boolean showMemoryUsage = false;
2214 java.text.NumberFormat df;
2216 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2219 public MyDesktopPane(boolean showMemoryUsage)
2221 showMemoryUsage(showMemoryUsage);
2224 public void showMemoryUsage(boolean showMemory)
2226 this.showMemoryUsage = showMemory;
2229 Thread worker = new Thread(this);
2235 public boolean isShowMemoryUsage()
2237 return showMemoryUsage;
2243 df = java.text.NumberFormat.getNumberInstance();
2244 df.setMaximumFractionDigits(2);
2245 runtime = Runtime.getRuntime();
2247 while (showMemoryUsage)
2251 maxMemory = runtime.maxMemory() / ONE_MB;
2252 allocatedMemory = runtime.totalMemory() / ONE_MB;
2253 freeMemory = runtime.freeMemory() / ONE_MB;
2254 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2256 percentUsage = (totalFreeMemory / maxMemory) * 100;
2258 // if (percentUsage < 20)
2260 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2262 // instance.set.setBorder(border1);
2265 // sleep after showing usage
2267 } catch (Exception ex)
2269 ex.printStackTrace();
2275 public void paintComponent(Graphics g)
2277 if (showMemoryUsage && g != null && df != null)
2279 if (percentUsage < 20)
2281 g.setColor(Color.red);
2283 FontMetrics fm = g.getFontMetrics();
2286 g.drawString(MessageManager.formatMessage("label.memory_stats",
2288 { df.format(totalFreeMemory), df.format(maxMemory),
2289 df.format(percentUsage) }),
2290 10, getHeight() - fm.getHeight());
2297 * Accessor method to quickly get all the AlignmentFrames loaded.
2299 * @return an array of AlignFrame, or null if none found
2301 public static AlignFrame[] getAlignFrames()
2303 if (Jalview.isHeadlessMode())
2305 // Desktop.desktop is null in headless mode
2306 return new AlignFrame[] { Jalview.currentAlignFrame };
2309 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2315 List<AlignFrame> avp = new ArrayList<>();
2317 for (int i = frames.length - 1; i > -1; i--)
2319 if (frames[i] instanceof AlignFrame)
2321 avp.add((AlignFrame) frames[i]);
2323 else if (frames[i] instanceof SplitFrame)
2326 * Also check for a split frame containing an AlignFrame
2328 GSplitFrame sf = (GSplitFrame) frames[i];
2329 if (sf.getTopFrame() instanceof AlignFrame)
2331 avp.add((AlignFrame) sf.getTopFrame());
2333 if (sf.getBottomFrame() instanceof AlignFrame)
2335 avp.add((AlignFrame) sf.getBottomFrame());
2339 if (avp.size() == 0)
2343 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2348 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2352 public GStructureViewer[] getJmols()
2354 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2360 List<GStructureViewer> avp = new ArrayList<>();
2362 for (int i = frames.length - 1; i > -1; i--)
2364 if (frames[i] instanceof AppJmol)
2366 GStructureViewer af = (GStructureViewer) frames[i];
2370 if (avp.size() == 0)
2374 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2379 * Add Groovy Support to Jalview
2382 public void groovyShell_actionPerformed()
2386 openGroovyConsole();
2387 } catch (Exception ex)
2389 Cache.log.error("Groovy Shell Creation failed.", ex);
2390 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2392 MessageManager.getString("label.couldnt_create_groovy_shell"),
2393 MessageManager.getString("label.groovy_support_failed"),
2394 JvOptionPane.ERROR_MESSAGE);
2399 * Open the Groovy console
2401 void openGroovyConsole()
2403 if (groovyConsole == null)
2405 groovyConsole = new groovy.ui.Console();
2406 groovyConsole.setVariable("Jalview", this);
2407 groovyConsole.run();
2410 * We allow only one console at a time, so that AlignFrame menu option
2411 * 'Calculate | Run Groovy script' is unambiguous.
2412 * Disable 'Groovy Console', and enable 'Run script', when the console is
2413 * opened, and the reverse when it is closed
2415 Window window = (Window) groovyConsole.getFrame();
2416 window.addWindowListener(new WindowAdapter()
2419 public void windowClosed(WindowEvent e)
2422 * rebind CMD-Q from Groovy Console to Jalview Quit
2425 enableExecuteGroovy(false);
2431 * show Groovy console window (after close and reopen)
2433 ((Window) groovyConsole.getFrame()).setVisible(true);
2436 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2437 * and disable opening a second console
2439 enableExecuteGroovy(true);
2443 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2446 protected void addQuitHandler()
2448 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2449 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2450 jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
2452 getRootPane().getActionMap().put("Quit", new AbstractAction()
2455 public void actionPerformed(ActionEvent e)
2463 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2466 * true if Groovy console is open
2468 public void enableExecuteGroovy(boolean enabled)
2471 * disable opening a second Groovy console
2472 * (or re-enable when the console is closed)
2474 groovyShell.setEnabled(!enabled);
2476 AlignFrame[] alignFrames = getAlignFrames();
2477 if (alignFrames != null)
2479 for (AlignFrame af : alignFrames)
2481 af.setGroovyEnabled(enabled);
2487 * Progress bars managed by the IProgressIndicator method.
2489 private Hashtable<Long, JPanel> progressBars;
2491 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2496 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2499 public void setProgressBar(String message, long id)
2501 // Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2503 if (progressBars == null)
2505 progressBars = new Hashtable<>();
2506 progressBarHandlers = new Hashtable<>();
2509 if (progressBars.get(Long.valueOf(id)) != null)
2511 JPanel panel = progressBars.remove(Long.valueOf(id));
2512 if (progressBarHandlers.contains(Long.valueOf(id)))
2514 progressBarHandlers.remove(Long.valueOf(id));
2516 removeProgressPanel(panel);
2520 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2525 public void removeProgressBar(long id)
2528 throw new UnsupportedOperationException("not implemented");
2534 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2535 * jalview.gui.IProgressIndicatorHandler)
2538 public void registerHandler(final long id,
2539 final IProgressIndicatorHandler handler)
2541 if (progressBarHandlers == null
2542 || !progressBars.containsKey(Long.valueOf(id)))
2544 throw new Error(MessageManager.getString(
2545 "error.call_setprogressbar_before_registering_handler"));
2547 progressBarHandlers.put(Long.valueOf(id), handler);
2548 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2549 if (handler.canCancel())
2551 JButton cancel = new JButton(
2552 MessageManager.getString("action.cancel"));
2553 final IProgressIndicator us = this;
2554 cancel.addActionListener(new ActionListener()
2558 public void actionPerformed(ActionEvent e)
2560 handler.cancelActivity(id);
2561 us.setProgressBar(MessageManager
2562 .formatMessage("label.cancelled_params", new Object[]
2563 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2567 progressPanel.add(cancel, BorderLayout.EAST);
2573 * @return true if any progress bars are still active
2576 public boolean operationInProgress()
2578 if (progressBars != null && progressBars.size() > 0)
2586 * This will return the first AlignFrame holding the given viewport instance. It
2587 * will break if there are more than one AlignFrames viewing a particular av.
2590 * @return alignFrame for viewport
2592 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2594 if (desktop != null)
2596 AlignmentPanel[] aps = getAlignmentPanels(
2597 viewport.getSequenceSetId());
2598 for (int panel = 0; aps != null && panel < aps.length; panel++)
2600 if (aps[panel] != null && aps[panel].av == viewport)
2602 return aps[panel].alignFrame;
2609 public VamsasApplication getVamsasApplication()
2611 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2617 * flag set if jalview GUI is being operated programmatically
2619 private boolean inBatchMode = false;
2622 * check if jalview GUI is being operated programmatically
2624 * @return inBatchMode
2626 public boolean isInBatchMode()
2632 * set flag if jalview GUI is being operated programmatically
2634 * @param inBatchMode
2636 public void setInBatchMode(boolean inBatchMode)
2638 this.inBatchMode = inBatchMode;
2641 public void startServiceDiscovery()
2643 startServiceDiscovery(false);
2646 public void startServiceDiscovery(boolean blocking)
2648 System.out.println("Starting service discovery");
2649 var tasks = new ArrayList<Future<?>>();
2650 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2652 System.out.println("loading services");
2656 // todo: changesupport handlers need to be transferred
2657 if (discoverer == null)
2659 discoverer = new jalview.ws.jws1.Discoverer();
2660 // register PCS handler for desktop.
2661 discoverer.addPropertyChangeListener(changeSupport);
2663 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2664 // until we phase out completely
2665 var f = new FutureTask<Void>(discoverer, null);
2666 new Thread(f).start();
2670 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2672 tasks.add(jalview.ws.jws2.Jws2Discoverer.getDiscoverer().startDiscoverer());
2674 if (Cache.getDefault("SHOW_SLIVKA_SERVICES", true))
2676 tasks.add(jalview.ws.slivkaws.SlivkaWSDiscoverer.getInstance().startDiscoverer());
2680 for (Future<?> task : tasks) {
2683 // block until all discovery tasks are done
2685 } catch (Exception e)
2687 e.printStackTrace();
2694 * called to check if the service discovery process completed successfully.
2698 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2700 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2702 final WSDiscovererI discoverer = jalview.ws.jws2.Jws2Discoverer
2704 final String ermsg = discoverer.getErrorMessages();
2707 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2709 if (serviceChangedDialog == null)
2711 // only run if we aren't already displaying one of these.
2712 addDialogThread(serviceChangedDialog = new Runnable()
2719 * JalviewDialog jd =new JalviewDialog() {
2721 * @Override protected void cancelPressed() { // TODO
2722 * Auto-generated method stub
2724 * }@Override protected void okPressed() { // TODO
2725 * Auto-generated method stub
2727 * }@Override protected void raiseClosed() { // TODO
2728 * Auto-generated method stub
2730 * } }; jd.initDialogFrame(new
2731 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2732 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2733 * + " or mis-configured HTTP proxy settings.<br/>" +
2734 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2736 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2737 * ), true, true, "Web Service Configuration Problem", 450,
2740 * jd.waitForInput();
2742 JvOptionPane.showConfirmDialog(Desktop.desktop,
2743 new JLabel("<html><table width=\"450\"><tr><td>"
2744 + ermsg + "</td></tr></table>"
2745 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2746 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2747 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2748 + " Tools->Preferences dialog box to change them.</p></html>"),
2749 "Web Service Configuration Problem",
2750 JvOptionPane.DEFAULT_OPTION,
2751 JvOptionPane.ERROR_MESSAGE);
2752 serviceChangedDialog = null;
2761 "Errors reported by JABA discovery service. Check web services preferences.\n"
2768 private Runnable serviceChangedDialog = null;
2771 * start a thread to open a URL in the configured browser. Pops up a warning
2772 * dialog to the user if there is an exception when calling out to the browser
2777 public static void showUrl(final String url)
2779 showUrl(url, Desktop.instance);
2783 * Like showUrl but allows progress handler to be specified
2787 * (null) or object implementing IProgressIndicator
2789 public static void showUrl(final String url,
2790 final IProgressIndicator progress)
2792 new Thread(new Runnable()
2799 if (progress != null)
2801 progress.setProgressBar(MessageManager
2802 .formatMessage("status.opening_params", new Object[]
2803 { url }), this.hashCode());
2805 jalview.util.BrowserLauncher.openURL(url);
2806 } catch (Exception ex)
2808 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2810 .getString("label.web_browser_not_found_unix"),
2811 MessageManager.getString("label.web_browser_not_found"),
2812 JvOptionPane.WARNING_MESSAGE);
2814 ex.printStackTrace();
2816 if (progress != null)
2818 progress.setProgressBar(null, this.hashCode());
2824 public static WsParamSetManager wsparamManager = null;
2826 public static ParamManager getUserParameterStore()
2828 if (wsparamManager == null)
2830 wsparamManager = new WsParamSetManager();
2832 return wsparamManager;
2836 * static hyperlink handler proxy method for use by Jalview's internal windows
2840 public static void hyperlinkUpdate(HyperlinkEvent e)
2842 if (e.getEventType() == EventType.ACTIVATED)
2847 url = e.getURL().toString();
2848 Desktop.showUrl(url);
2849 } catch (Exception x)
2853 if (Cache.log != null)
2855 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2860 "Couldn't handle string " + url + " as a URL.");
2863 // ignore any exceptions due to dud links.
2870 * single thread that handles display of dialogs to user.
2872 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2875 * flag indicating if dialogExecutor should try to acquire a permit
2877 private volatile boolean dialogPause = true;
2882 private java.util.concurrent.Semaphore block = new Semaphore(0);
2884 private static groovy.ui.Console groovyConsole;
2887 * add another dialog thread to the queue
2891 public void addDialogThread(final Runnable prompter)
2893 dialogExecutor.submit(new Runnable()
2903 } catch (InterruptedException x)
2907 if (instance == null)
2913 SwingUtilities.invokeAndWait(prompter);
2914 } catch (Exception q)
2916 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2922 public void startDialogQueue()
2924 // set the flag so we don't pause waiting for another permit and semaphore
2925 // the current task to begin
2926 dialogPause = false;
2931 * Outputs an image of the desktop to file in EPS format, after prompting the
2932 * user for choice of Text or Lineart character rendering (unless a preference
2933 * has been set). The file name is generated as
2936 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2940 protected void snapShotWindow_actionPerformed(ActionEvent e)
2942 // currently the menu option to do this is not shown
2945 int width = getWidth();
2946 int height = getHeight();
2948 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2949 ImageWriterI writer = new ImageWriterI()
2952 public void exportImage(Graphics g) throws Exception
2955 Cache.log.info("Successfully written snapshot to file "
2956 + of.getAbsolutePath());
2959 String title = "View of desktop";
2960 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2962 exporter.doExport(of, this, width, height, title);
2966 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2967 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
2968 * location last time the view was expanded (if any). However it does not
2969 * remember the split pane divider location - this is set to match the
2970 * 'exploding' frame.
2974 public void explodeViews(SplitFrame sf)
2976 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2977 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2978 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2980 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2982 int viewCount = topPanels.size();
2989 * Processing in reverse order works, forwards order leaves the first panels
2990 * not visible. I don't know why!
2992 for (int i = viewCount - 1; i >= 0; i--)
2995 * Make new top and bottom frames. These take over the respective
2996 * AlignmentPanel objects, including their AlignmentViewports, so the
2997 * cdna/protein relationships between the viewports is carried over to the
3000 * explodedGeometry holds the (x, y) position of the previously exploded
3001 * SplitFrame, and the (width, height) of the AlignFrame component
3003 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
3004 AlignFrame newTopFrame = new AlignFrame(topPanel);
3005 newTopFrame.setSize(oldTopFrame.getSize());
3006 newTopFrame.setVisible(true);
3007 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
3008 .getExplodedGeometry();
3009 if (geometry != null)
3011 newTopFrame.setSize(geometry.getSize());
3014 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
3015 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
3016 newBottomFrame.setSize(oldBottomFrame.getSize());
3017 newBottomFrame.setVisible(true);
3018 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
3019 .getExplodedGeometry();
3020 if (geometry != null)
3022 newBottomFrame.setSize(geometry.getSize());
3025 topPanel.av.setGatherViewsHere(false);
3026 bottomPanel.av.setGatherViewsHere(false);
3027 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
3029 if (geometry != null)
3031 splitFrame.setLocation(geometry.getLocation());
3033 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
3037 * Clear references to the panels (now relocated in the new SplitFrames)
3038 * before closing the old SplitFrame.
3041 bottomPanels.clear();
3046 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3047 * back into the given SplitFrame as additional views. Note that the gathered
3048 * frames may themselves have multiple views.
3052 public void gatherViews(GSplitFrame source)
3055 * special handling of explodedGeometry for a view within a SplitFrame: - it
3056 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3057 * height) of the AlignFrame component
3059 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3060 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3061 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3062 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3063 myBottomFrame.viewport
3064 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3065 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3066 myTopFrame.viewport.setGatherViewsHere(true);
3067 myBottomFrame.viewport.setGatherViewsHere(true);
3068 String topViewId = myTopFrame.viewport.getSequenceSetId();
3069 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3071 JInternalFrame[] frames = desktop.getAllFrames();
3072 for (JInternalFrame frame : frames)
3074 if (frame instanceof SplitFrame && frame != source)
3076 SplitFrame sf = (SplitFrame) frame;
3077 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3078 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3079 boolean gatherThis = false;
3080 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3082 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3083 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3084 if (topViewId.equals(topPanel.av.getSequenceSetId())
3085 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3088 topPanel.av.setGatherViewsHere(false);
3089 bottomPanel.av.setGatherViewsHere(false);
3090 topPanel.av.setExplodedGeometry(
3091 new Rectangle(sf.getLocation(), topFrame.getSize()));
3092 bottomPanel.av.setExplodedGeometry(
3093 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3094 myTopFrame.addAlignmentPanel(topPanel, false);
3095 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3101 topFrame.getAlignPanels().clear();
3102 bottomFrame.getAlignPanels().clear();
3109 * The dust settles...give focus to the tab we did this from.
3111 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3114 public static groovy.ui.Console getGroovyConsole()
3116 return groovyConsole;
3120 * handles the payload of a drag and drop event.
3122 * TODO refactor to desktop utilities class
3125 * - Data source strings extracted from the drop event
3127 * - protocol for each data source extracted from the drop event
3131 * - the payload from the drop event
3134 public static void transferFromDropTarget(List<Object> files,
3135 List<DataSourceType> protocols, DropTargetDropEvent evt,
3136 Transferable t) throws Exception
3139 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3141 // DataFlavor[] flavors = t.getTransferDataFlavors();
3142 // for (int i = 0; i < flavors.length; i++) {
3143 // if (flavors[i].isFlavorJavaFileListType()) {
3144 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3145 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3146 // for (int j = 0; j < list.size(); j++) {
3147 // File file = (File) list.get(j);
3148 // byte[] data = getDroppedFileBytes(file);
3149 // fileName.setText(file.getName() + " - " + data.length + " " +
3150 // evt.getLocation());
3151 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3152 // target.setText(new String(data));
3154 // dtde.dropComplete(true);
3159 DataFlavor uriListFlavor = new DataFlavor(
3160 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3163 urlFlavour = new DataFlavor(
3164 "application/x-java-url; class=java.net.URL");
3165 } catch (ClassNotFoundException cfe)
3167 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3170 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3175 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3176 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3177 // means url may be null.
3180 protocols.add(DataSourceType.URL);
3181 files.add(url.toString());
3182 Cache.log.debug("Drop handled as URL dataflavor "
3183 + files.get(files.size() - 1));
3188 if (Platform.isAMacAndNotJS())
3191 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3194 } catch (Throwable ex)
3196 Cache.log.debug("URL drop handler failed.", ex);
3199 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3201 // Works on Windows and MacOSX
3202 Cache.log.debug("Drop handled as javaFileListFlavor");
3203 for (Object file : (List) t
3204 .getTransferData(DataFlavor.javaFileListFlavor))
3207 protocols.add(DataSourceType.FILE);
3212 // Unix like behaviour
3213 boolean added = false;
3215 if (t.isDataFlavorSupported(uriListFlavor))
3217 Cache.log.debug("Drop handled as uriListFlavor");
3218 // This is used by Unix drag system
3219 data = (String) t.getTransferData(uriListFlavor);
3223 // fallback to text: workaround - on OSX where there's a JVM bug
3224 Cache.log.debug("standard URIListFlavor failed. Trying text");
3225 // try text fallback
3226 DataFlavor textDf = new DataFlavor(
3227 "text/plain;class=java.lang.String");
3228 if (t.isDataFlavorSupported(textDf))
3230 data = (String) t.getTransferData(textDf);
3233 Cache.log.debug("Plain text drop content returned "
3234 + (data == null ? "Null - failed" : data));
3239 while (protocols.size() < files.size())
3241 Cache.log.debug("Adding missing FILE protocol for "
3242 + files.get(protocols.size()));
3243 protocols.add(DataSourceType.FILE);
3245 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3246 data, "\r\n"); st.hasMoreTokens();)
3249 String s = st.nextToken();
3250 if (s.startsWith("#"))
3252 // the line is a comment (as per the RFC 2483)
3255 java.net.URI uri = new java.net.URI(s);
3256 if (uri.getScheme().toLowerCase().startsWith("http"))
3258 protocols.add(DataSourceType.URL);
3259 files.add(uri.toString());
3263 // otherwise preserve old behaviour: catch all for file objects
3264 java.io.File file = new java.io.File(uri);
3265 protocols.add(DataSourceType.FILE);
3266 files.add(file.toString());
3271 if (Cache.log.isDebugEnabled())
3273 if (data == null || !added)
3276 if (t.getTransferDataFlavors() != null
3277 && t.getTransferDataFlavors().length > 0)
3280 "Couldn't resolve drop data. Here are the supported flavors:");
3281 for (DataFlavor fl : t.getTransferDataFlavors())
3284 "Supported transfer dataflavor: " + fl.toString());
3285 Object df = t.getTransferData(fl);
3288 Cache.log.debug("Retrieves: " + df);
3292 Cache.log.debug("Retrieved nothing");
3298 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3304 if (Platform.isWindowsAndNotJS())
3306 Cache.log.debug("Scanning dropped content for Windows Link Files");
3308 // resolve any .lnk files in the file drop
3309 for (int f = 0; f < files.size(); f++)
3311 String source = files.get(f).toString().toLowerCase();
3312 if (protocols.get(f).equals(DataSourceType.FILE)
3313 && (source.endsWith(".lnk") || source.endsWith(".url")
3314 || source.endsWith(".site")))
3318 Object obj = files.get(f);
3319 File lf = (obj instanceof File ? (File) obj
3320 : new File((String) obj));
3321 // process link file to get a URL
3322 Cache.log.debug("Found potential link file: " + lf);
3323 WindowsShortcut wscfile = new WindowsShortcut(lf);
3324 String fullname = wscfile.getRealFilename();
3325 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3326 files.set(f, fullname);
3327 Cache.log.debug("Parsed real filename " + fullname
3328 + " to extract protocol: " + protocols.get(f));
3329 } catch (Exception ex)
3332 "Couldn't parse " + files.get(f) + " as a link file.",
3341 * Sets the Preferences property for experimental features to True or False
3342 * depending on the state of the controlling menu item
3345 protected void showExperimental_actionPerformed(boolean selected)
3347 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3351 * Answers a (possibly empty) list of any structure viewer frames (currently for
3352 * either Jmol or Chimera) which are currently open. This may optionally be
3353 * restricted to viewers of a specified class, or viewers linked to a specified
3357 * if not null, only return viewers linked to this panel
3358 * @param structureViewerClass
3359 * if not null, only return viewers of this class
3362 public List<StructureViewerBase> getStructureViewers(
3363 AlignmentPanel apanel,
3364 Class<? extends StructureViewerBase> structureViewerClass)
3366 List<StructureViewerBase> result = new ArrayList<>();
3367 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3369 for (JInternalFrame frame : frames)
3371 if (frame instanceof StructureViewerBase)
3373 if (structureViewerClass == null
3374 || structureViewerClass.isInstance(frame))
3377 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3379 result.add((StructureViewerBase) frame);