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.bin.Cache;
26 import jalview.bin.Jalview;
27 import jalview.gui.ImageExporter.ImageWriterI;
28 import jalview.io.BackupFiles;
29 import jalview.io.DataSourceType;
30 import jalview.io.FileFormat;
31 import jalview.io.FileFormatException;
32 import jalview.io.FileFormatI;
33 import jalview.io.FileFormats;
34 import jalview.io.FileLoader;
35 import jalview.io.FormatAdapter;
36 import jalview.io.IdentifyFile;
37 import jalview.io.JalviewFileChooser;
38 import jalview.io.JalviewFileView;
39 import jalview.jbgui.GSplitFrame;
40 import jalview.jbgui.GStructureViewer;
41 import jalview.project.Jalview2XML;
42 import jalview.structure.StructureSelectionManager;
43 import jalview.urls.IdOrgSettings;
44 import jalview.util.BrowserLauncher;
45 import jalview.util.ImageMaker.TYPE;
46 import jalview.util.MessageManager;
47 import jalview.util.Platform;
48 import jalview.util.ShortcutKeyMaskExWrapper;
49 import jalview.util.UrlConstants;
50 import jalview.viewmodel.AlignmentViewport;
51 import jalview.ws.params.ParamManager;
52 import jalview.ws.utils.UrlDownloadClient;
54 import java.awt.BorderLayout;
55 import java.awt.Color;
56 import java.awt.Dimension;
57 import java.awt.FontMetrics;
58 import java.awt.Graphics;
59 import java.awt.GridLayout;
60 import java.awt.Point;
61 import java.awt.Rectangle;
62 import java.awt.Toolkit;
63 import java.awt.Window;
64 import java.awt.datatransfer.Clipboard;
65 import java.awt.datatransfer.ClipboardOwner;
66 import java.awt.datatransfer.DataFlavor;
67 import java.awt.datatransfer.Transferable;
68 import java.awt.dnd.DnDConstants;
69 import java.awt.dnd.DropTargetDragEvent;
70 import java.awt.dnd.DropTargetDropEvent;
71 import java.awt.dnd.DropTargetEvent;
72 import java.awt.dnd.DropTargetListener;
73 import java.awt.event.ActionEvent;
74 import java.awt.event.ActionListener;
75 import java.awt.event.InputEvent;
76 import java.awt.event.KeyEvent;
77 import java.awt.event.MouseAdapter;
78 import java.awt.event.MouseEvent;
79 import java.awt.event.WindowAdapter;
80 import java.awt.event.WindowEvent;
81 import java.beans.PropertyChangeEvent;
82 import java.beans.PropertyChangeListener;
84 import java.io.FileWriter;
85 import java.io.IOException;
87 import java.util.ArrayList;
88 import java.util.HashMap;
89 import java.util.Hashtable;
90 import java.util.List;
91 import java.util.ListIterator;
92 import java.util.Vector;
93 import java.util.concurrent.ExecutorService;
94 import java.util.concurrent.Executors;
95 import java.util.concurrent.Semaphore;
97 import javax.swing.AbstractAction;
98 import javax.swing.Action;
99 import javax.swing.ActionMap;
100 import javax.swing.Box;
101 import javax.swing.BoxLayout;
102 import javax.swing.DefaultDesktopManager;
103 import javax.swing.DesktopManager;
104 import javax.swing.InputMap;
105 import javax.swing.JButton;
106 import javax.swing.JCheckBox;
107 import javax.swing.JComboBox;
108 import javax.swing.JComponent;
109 import javax.swing.JDesktopPane;
110 import javax.swing.JInternalFrame;
111 import javax.swing.JLabel;
112 import javax.swing.JMenuItem;
113 import javax.swing.JPanel;
114 import javax.swing.JPopupMenu;
115 import javax.swing.JProgressBar;
116 import javax.swing.JTextField;
117 import javax.swing.KeyStroke;
118 import javax.swing.SwingUtilities;
119 import javax.swing.event.HyperlinkEvent;
120 import javax.swing.event.HyperlinkEvent.EventType;
121 import javax.swing.event.InternalFrameAdapter;
122 import javax.swing.event.InternalFrameEvent;
124 import org.stackoverflowusers.file.WindowsShortcut;
131 * @version $Revision: 1.155 $
133 public class Desktop extends jalview.jbgui.GDesktop
134 implements DropTargetListener, ClipboardOwner, IProgressIndicator,
135 jalview.api.StructureSelectionManagerProvider
137 private static final String CITATION = "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
138 + "<br><br>For help, see the FAQ at <a href=\"http://www.jalview.org/faq\">www.jalview.org/faq</a> and/or join the jalview-discuss@jalview.org mailing list"
139 + "<br><br>If you use Jalview, please cite:"
140 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
141 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
142 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033";
144 private static final String DEFAULT_AUTHORS = "The Jalview Authors (See AUTHORS file for current list)";
146 private static int DEFAULT_MIN_WIDTH = 300;
148 private static int DEFAULT_MIN_HEIGHT = 250;
150 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
152 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
154 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
156 protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
158 public static HashMap<String, FileWriter> savingFiles = new HashMap<>();
160 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
163 * news reader - null if it was never started.
165 private BlogReader jvnews = null;
167 private File projectFile;
171 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
173 public void addJalviewPropertyChangeListener(
174 PropertyChangeListener listener)
176 changeSupport.addJalviewPropertyChangeListener(listener);
180 * @param propertyName
182 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
183 * java.beans.PropertyChangeListener)
185 public void addJalviewPropertyChangeListener(String propertyName,
186 PropertyChangeListener listener)
188 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
192 * @param propertyName
194 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
195 * java.beans.PropertyChangeListener)
197 public void removeJalviewPropertyChangeListener(String propertyName,
198 PropertyChangeListener listener)
200 changeSupport.removeJalviewPropertyChangeListener(propertyName,
204 /** Singleton Desktop instance */
205 public static Desktop instance;
207 public static MyDesktopPane desktop;
209 public static MyDesktopPane getDesktop()
211 // BH 2018 could use currentThread() here as a reference to a
212 // Hashtable<Thread, MyDesktopPane> in JavaScript
216 static int openFrameCount = 0;
218 static final int xOffset = 30;
220 static final int yOffset = 30;
222 public static jalview.ws.jws1.Discoverer discoverer;
224 public static Object[] jalviewClipboard;
226 public static boolean internalCopy = false;
228 static int fileLoadingCount = 0;
230 class MyDesktopManager implements DesktopManager
233 private DesktopManager delegate;
235 public MyDesktopManager(DesktopManager delegate)
237 this.delegate = delegate;
241 public void activateFrame(JInternalFrame f)
245 delegate.activateFrame(f);
246 } catch (NullPointerException npe)
248 Point p = getMousePosition();
249 instance.showPasteMenu(p.x, p.y);
254 public void beginDraggingFrame(JComponent f)
256 delegate.beginDraggingFrame(f);
260 public void beginResizingFrame(JComponent f, int direction)
262 delegate.beginResizingFrame(f, direction);
266 public void closeFrame(JInternalFrame f)
268 delegate.closeFrame(f);
272 public void deactivateFrame(JInternalFrame f)
274 delegate.deactivateFrame(f);
278 public void deiconifyFrame(JInternalFrame f)
280 delegate.deiconifyFrame(f);
284 public void dragFrame(JComponent f, int newX, int newY)
290 delegate.dragFrame(f, newX, newY);
294 public void endDraggingFrame(JComponent f)
296 delegate.endDraggingFrame(f);
301 public void endResizingFrame(JComponent f)
303 delegate.endResizingFrame(f);
308 public void iconifyFrame(JInternalFrame f)
310 delegate.iconifyFrame(f);
314 public void maximizeFrame(JInternalFrame f)
316 delegate.maximizeFrame(f);
320 public void minimizeFrame(JInternalFrame f)
322 delegate.minimizeFrame(f);
326 public void openFrame(JInternalFrame f)
328 delegate.openFrame(f);
332 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
339 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
343 public void setBoundsForFrame(JComponent f, int newX, int newY,
344 int newWidth, int newHeight)
346 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
349 // All other methods, simply delegate
354 * Creates a new Desktop object.
360 * A note to implementors. It is ESSENTIAL that any activities that might
361 * block are spawned off as threads rather than waited for during this
366 doConfigureStructurePrefs();
367 setTitle("Jalview " + Cache.getProperty("VERSION"));
369 if (!Platform.isAMac())
371 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
375 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
381 APQHandlers.setAPQHandlers(this);
382 } catch (Throwable t)
384 System.out.println("Error setting APQHandlers: " + t.toString());
385 // t.printStackTrace();
388 addWindowListener(new WindowAdapter()
392 public void windowClosing(WindowEvent ev)
398 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE",
401 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE",
403 desktop = new MyDesktopPane(selmemusage);
405 showMemusage.setSelected(selmemusage);
406 desktop.setBackground(Color.white);
408 getContentPane().setLayout(new BorderLayout());
409 // alternate config - have scrollbars - see notes in JAL-153
410 // JScrollPane sp = new JScrollPane();
411 // sp.getViewport().setView(desktop);
412 // getContentPane().add(sp, BorderLayout.CENTER);
414 // BH 2018 - just an experiment to try unclipped JInternalFrames.
417 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
420 getContentPane().add(desktop, BorderLayout.CENTER);
421 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
423 // This line prevents Windows Look&Feel resizing all new windows to maximum
424 // if previous window was maximised
425 desktop.setDesktopManager(new MyDesktopManager(
426 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
427 : Platform.isAMacAndNotJS()
428 ? new AquaInternalFrameManager(
429 desktop.getDesktopManager())
430 : desktop.getDesktopManager())));
432 Rectangle dims = getLastKnownDimensions("");
439 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
440 int xPos = Math.max(5, (screenSize.width - 900) / 2);
441 int yPos = Math.max(5, (screenSize.height - 650) / 2);
442 setBounds(xPos, yPos, 900, 650);
445 if (!Platform.isJS())
452 jconsole = new Console(this, showjconsole);
453 jconsole.setHeader(Cache.getVersionDetailsForConsole());
454 showConsole(showjconsole);
456 showNews.setVisible(false);
458 experimentalFeatures.setSelected(showExperimental());
460 getIdentifiersOrgData();
464 // Spawn a thread that shows the splashscreen
466 SwingUtilities.invokeLater(new Runnable()
471 new SplashScreen(true);
475 // Thread off a new instance of the file chooser - this reduces the time
477 // takes to open it later on.
478 new Thread(new Runnable()
483 Cache.log.debug("Filechooser init thread started.");
484 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
485 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
487 Cache.log.debug("Filechooser init thread finished.");
490 // Add the service change listener
491 changeSupport.addJalviewPropertyChangeListener("services",
492 new PropertyChangeListener()
496 public void propertyChange(PropertyChangeEvent evt)
498 Cache.log.debug("Firing service changed event for "
499 + evt.getNewValue());
500 JalviewServicesChanged(evt);
505 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
507 this.addWindowListener(new WindowAdapter()
510 public void windowClosing(WindowEvent evt)
517 this.addMouseListener(ma = new MouseAdapter()
520 public void mousePressed(MouseEvent evt)
522 if (evt.isPopupTrigger()) // Mac
524 showPasteMenu(evt.getX(), evt.getY());
529 public void mouseReleased(MouseEvent evt)
531 if (evt.isPopupTrigger()) // Windows
533 showPasteMenu(evt.getX(), evt.getY());
537 desktop.addMouseListener(ma);
542 * Answers true if user preferences to enable experimental features is True
547 public boolean showExperimental()
549 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
550 Boolean.FALSE.toString());
551 return Boolean.valueOf(experimental).booleanValue();
554 public void doConfigureStructurePrefs()
556 // configure services
557 StructureSelectionManager ssm = StructureSelectionManager
558 .getStructureSelectionManager(this);
559 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
561 ssm.setAddTempFacAnnot(Cache
562 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
563 ssm.setProcessSecondaryStructure(Cache
564 .getDefault(Preferences.STRUCT_FROM_PDB, true));
565 ssm.setSecStructServices(
566 Cache.getDefault(Preferences.USE_RNAVIEW, true));
570 ssm.setAddTempFacAnnot(false);
571 ssm.setProcessSecondaryStructure(false);
572 ssm.setSecStructServices(false);
576 public void checkForNews()
578 final Desktop me = this;
579 // Thread off the news reader, in case there are connection problems.
580 new Thread(new Runnable()
585 Cache.log.debug("Starting news thread.");
586 jvnews = new BlogReader(me);
587 showNews.setVisible(true);
588 Cache.log.debug("Completed news thread.");
593 public void getIdentifiersOrgData()
595 // Thread off the identifiers fetcher
596 new Thread(new Runnable()
601 Cache.log.debug("Downloading data from identifiers.org");
604 UrlDownloadClient.download(IdOrgSettings.getUrl(),
605 IdOrgSettings.getDownloadLocation());
606 } catch (IOException e)
608 Cache.log.debug("Exception downloading identifiers.org data"
617 protected void showNews_actionPerformed(ActionEvent e)
619 showNews(showNews.isSelected());
622 void showNews(boolean visible)
624 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
625 showNews.setSelected(visible);
626 if (visible && !jvnews.isVisible())
628 new Thread(new Runnable()
633 long now = System.currentTimeMillis();
634 Desktop.instance.setProgressBar(
635 MessageManager.getString("status.refreshing_news"), now);
636 jvnews.refreshNews();
637 Desktop.instance.setProgressBar(null, now);
645 * recover the last known dimensions for a jalview window
648 * - empty string is desktop, all other windows have unique prefix
649 * @return null or last known dimensions scaled to current geometry (if last
650 * window geom was known)
652 Rectangle getLastKnownDimensions(String windowName)
654 // TODO: lock aspect ratio for scaling desktop Bug #0058199
655 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
656 String x = Cache.getProperty(windowName + "SCREEN_X");
657 String y = Cache.getProperty(windowName + "SCREEN_Y");
659 .getProperty(windowName + "SCREEN_WIDTH");
660 String height = Cache
661 .getProperty(windowName + "SCREEN_HEIGHT");
662 if ((x != null) && (y != null) && (width != null) && (height != null))
664 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
665 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
666 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
668 // attempt #1 - try to cope with change in screen geometry - this
669 // version doesn't preserve original jv aspect ratio.
670 // take ratio of current screen size vs original screen size.
671 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
672 Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
673 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
674 Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
675 // rescale the bounds depending upon the current screen geometry.
676 ix = (int) (ix * sw);
677 iw = (int) (iw * sw);
678 iy = (int) (iy * sh);
679 ih = (int) (ih * sh);
680 while (ix >= screenSize.width)
683 "Window geometry location recall error: shifting horizontal to within screenbounds.");
684 ix -= screenSize.width;
686 while (iy >= screenSize.height)
689 "Window geometry location recall error: shifting vertical to within screenbounds.");
690 iy -= screenSize.height;
693 "Got last known dimensions for " + windowName + ": x:" + ix
694 + " y:" + iy + " width:" + iw + " height:" + ih);
696 // return dimensions for new instance
697 return new Rectangle(ix, iy, iw, ih);
702 void showPasteMenu(int x, int y)
704 JPopupMenu popup = new JPopupMenu();
705 JMenuItem item = new JMenuItem(
706 MessageManager.getString("label.paste_new_window"));
707 item.addActionListener(new ActionListener()
710 public void actionPerformed(ActionEvent evt)
717 popup.show(this, x, y);
724 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
725 Transferable contents = c.getContents(this);
727 if (contents != null)
729 String file = (String) contents
730 .getTransferData(DataFlavor.stringFlavor);
732 FileFormatI format = new IdentifyFile().identify(file,
733 DataSourceType.PASTE);
735 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
738 } catch (Exception ex)
741 "Unable to paste alignment from system clipboard:\n" + ex);
746 * Adds and opens the given frame to the desktop
757 public static synchronized void addInternalFrame(
758 final JInternalFrame frame, String title, int w, int h)
760 addInternalFrame(frame, title, true, w, h, true, false);
764 * Add an internal frame to the Jalview desktop
771 * When true, display frame immediately, otherwise, caller must call
772 * setVisible themselves.
778 public static synchronized void addInternalFrame(
779 final JInternalFrame frame, String title, boolean makeVisible,
782 addInternalFrame(frame, title, makeVisible, w, h, true, false);
786 * Add an internal frame to the Jalview desktop and make it visible
799 public static synchronized void addInternalFrame(
800 final JInternalFrame frame, String title, int w, int h,
803 addInternalFrame(frame, title, true, w, h, resizable, false);
807 * Add an internal frame to the Jalview desktop
814 * When true, display frame immediately, otherwise, caller must call
815 * setVisible themselves.
822 * @param ignoreMinSize
823 * Do not set the default minimum size for frame
825 public static synchronized void addInternalFrame(
826 final JInternalFrame frame, String title, boolean makeVisible,
827 int w, int h, boolean resizable, boolean ignoreMinSize)
830 // TODO: allow callers to determine X and Y position of frame (eg. via
832 // TODO: consider fixing method to update entries in the window submenu with
833 // the current window title
835 frame.setTitle(title);
836 if (frame.getWidth() < 1 || frame.getHeight() < 1)
840 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
841 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
842 // IF JALVIEW IS RUNNING HEADLESS
843 // ///////////////////////////////////////////////
844 if (instance == null || (System.getProperty("java.awt.headless") != null
845 && System.getProperty("java.awt.headless").equals("true")))
854 frame.setMinimumSize(
855 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
857 // Set default dimension for Alignment Frame window.
858 // The Alignment Frame window could be added from a number of places,
860 // I did this here in order not to miss out on any Alignment frame.
861 if (frame instanceof AlignFrame)
863 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
864 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
868 frame.setVisible(makeVisible);
869 frame.setClosable(true);
870 frame.setResizable(resizable);
871 frame.setMaximizable(resizable);
872 frame.setIconifiable(resizable);
873 frame.setOpaque(Platform.isJS());
875 if (frame.getX() < 1 && frame.getY() < 1)
877 frame.setLocation(xOffset * openFrameCount,
878 yOffset * ((openFrameCount - 1) % 10) + yOffset);
882 * add an entry for the new frame in the Window menu
883 * (and remove it when the frame is closed)
885 final JMenuItem menuItem = new JMenuItem(title);
886 frame.addInternalFrameListener(new InternalFrameAdapter()
889 public void internalFrameActivated(InternalFrameEvent evt)
891 JInternalFrame itf = desktop.getSelectedFrame();
894 if (itf instanceof AlignFrame)
896 Jalview.setCurrentAlignFrame((AlignFrame) itf);
903 public void internalFrameClosed(InternalFrameEvent evt)
905 PaintRefresher.RemoveComponent(frame);
908 * defensive check to prevent frames being
909 * added half off the window
911 if (openFrameCount > 0)
917 * ensure no reference to alignFrame retained by menu item listener
919 if (menuItem.getActionListeners().length > 0)
921 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
923 windowMenu.remove(menuItem);
927 menuItem.addActionListener(new ActionListener()
930 public void actionPerformed(ActionEvent e)
934 frame.setSelected(true);
935 frame.setIcon(false);
936 } catch (java.beans.PropertyVetoException ex)
938 // System.err.println(ex.toString());
943 setKeyBindings(frame);
947 windowMenu.add(menuItem);
952 frame.setSelected(true);
953 frame.requestFocus();
954 } catch (java.beans.PropertyVetoException ve)
956 } catch (java.lang.ClassCastException cex)
959 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
965 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
970 private static void setKeyBindings(JInternalFrame frame)
972 @SuppressWarnings("serial")
973 final Action closeAction = new AbstractAction()
976 public void actionPerformed(ActionEvent e)
983 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
985 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
986 InputEvent.CTRL_DOWN_MASK);
987 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
988 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
990 InputMap inputMap = frame
991 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
992 String ctrlW = ctrlWKey.toString();
993 inputMap.put(ctrlWKey, ctrlW);
994 inputMap.put(cmdWKey, ctrlW);
996 ActionMap actionMap = frame.getActionMap();
997 actionMap.put(ctrlW, closeAction);
1001 public void lostOwnership(Clipboard clipboard, Transferable contents)
1005 Desktop.jalviewClipboard = null;
1008 internalCopy = false;
1012 public void dragEnter(DropTargetDragEvent evt)
1017 public void dragExit(DropTargetEvent evt)
1022 public void dragOver(DropTargetDragEvent evt)
1027 public void dropActionChanged(DropTargetDragEvent evt)
1038 public void drop(DropTargetDropEvent evt)
1040 boolean success = true;
1041 // JAL-1552 - acceptDrop required before getTransferable call for
1042 // Java's Transferable for native dnd
1043 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1044 Transferable t = evt.getTransferable();
1045 List<Object> files = new ArrayList<>();
1046 List<DataSourceType> protocols = new ArrayList<>();
1050 Desktop.transferFromDropTarget(files, protocols, evt, t);
1051 } catch (Exception e)
1053 e.printStackTrace();
1061 for (int i = 0; i < files.size(); i++)
1063 // BH 2018 File or String
1064 Object file = files.get(i);
1065 String fileName = file.toString();
1066 DataSourceType protocol = (protocols == null)
1067 ? DataSourceType.FILE
1069 FileFormatI format = null;
1071 if (fileName.endsWith(".jar"))
1073 format = FileFormat.Jalview;
1078 format = new IdentifyFile().identify(file, protocol);
1080 if (file instanceof File)
1082 Platform.cacheFileData((File) file);
1084 new FileLoader().LoadFile(null, file, protocol, format);
1087 } catch (Exception ex)
1092 evt.dropComplete(success); // need this to ensure input focus is properly
1093 // transfered to any new windows created
1103 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1105 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1106 JalviewFileChooser chooser = JalviewFileChooser
1107 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, BackupFiles.getEnabled());
1109 chooser.setFileView(new JalviewFileView());
1110 chooser.setDialogTitle(
1111 MessageManager.getString("label.open_local_file"));
1112 chooser.setToolTipText(MessageManager.getString("action.open"));
1114 chooser.setResponseHandler(0, new Runnable()
1119 File selectedFile = chooser.getSelectedFile();
1120 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1122 FileFormatI format = chooser.getSelectedFormat();
1125 * Call IdentifyFile to verify the file contains what its extension implies.
1126 * Skip this step for dynamically added file formats, because
1127 * IdentifyFile does not know how to recognise them.
1129 if (FileFormats.getInstance().isIdentifiable(format))
1133 format = new IdentifyFile().identify(selectedFile,
1134 DataSourceType.FILE);
1135 } catch (FileFormatException e)
1137 // format = null; //??
1141 new FileLoader().LoadFile(viewport, selectedFile,
1142 DataSourceType.FILE, format);
1145 chooser.showOpenDialog(this);
1149 * Shows a dialog for input of a URL at which to retrieve alignment data
1154 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1156 // This construct allows us to have a wider textfield
1158 JLabel label = new JLabel(
1159 MessageManager.getString("label.input_file_url"));
1161 JPanel panel = new JPanel(new GridLayout(2, 1));
1165 * the URL to fetch is
1166 * Java: an editable combobox with history
1167 * JS: (pending JAL-3038) a plain text field
1170 String urlBase = "http://www.";
1171 if (Platform.isJS())
1173 history = new JTextField(urlBase, 35);
1182 JComboBox<String> asCombo = new JComboBox<>();
1183 asCombo.setPreferredSize(new Dimension(400, 20));
1184 asCombo.setEditable(true);
1185 asCombo.addItem(urlBase);
1186 String historyItems = Cache.getProperty("RECENT_URL");
1187 if (historyItems != null)
1189 for (String token : historyItems.split("\\t"))
1191 asCombo.addItem(token);
1198 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1199 MessageManager.getString("action.cancel") };
1200 Runnable action = new Runnable()
1205 @SuppressWarnings("unchecked")
1206 String url = (history instanceof JTextField
1207 ? ((JTextField) history).getText()
1208 : ((JComboBox<String>) history).getSelectedItem()
1211 if (url.toLowerCase().endsWith(".jar"))
1213 if (viewport != null)
1215 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1216 FileFormat.Jalview);
1220 new FileLoader().LoadFile(url, DataSourceType.URL,
1221 FileFormat.Jalview);
1226 FileFormatI format = null;
1229 format = new IdentifyFile().identify(url, DataSourceType.URL);
1230 } catch (FileFormatException e)
1232 // TODO revise error handling, distinguish between
1233 // URL not found and response not valid
1238 String msg = MessageManager
1239 .formatMessage("label.couldnt_locate", url);
1240 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1241 MessageManager.getString("label.url_not_found"),
1242 JvOptionPane.WARNING_MESSAGE);
1247 if (viewport != null)
1249 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1254 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1259 String dialogOption = MessageManager
1260 .getString("label.input_alignment_from_url");
1261 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1262 .showInternalDialog(panel, dialogOption,
1263 JvOptionPane.YES_NO_CANCEL_OPTION,
1264 JvOptionPane.PLAIN_MESSAGE, null, options,
1265 MessageManager.getString("action.ok"));
1269 * Opens the CutAndPaste window for the user to paste an alignment in to
1272 * - if not null, the pasted alignment is added to the current
1273 * alignment; if null, to a new alignment window
1276 public void inputTextboxMenuItem_actionPerformed(
1277 AlignmentViewPanel viewPanel)
1279 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1280 cap.setForInput(viewPanel);
1281 Desktop.addInternalFrame(cap,
1282 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1292 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1293 Cache.setProperty("SCREENGEOMETRY_WIDTH",
1295 Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1296 screen.height + "");
1297 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1298 getWidth(), getHeight()));
1300 if (jconsole != null)
1302 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1303 jconsole.stopConsole();
1307 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1310 if (dialogExecutor != null)
1312 dialogExecutor.shutdownNow();
1314 closeAll_actionPerformed(null);
1316 if (groovyConsole != null)
1318 // suppress a possible repeat prompt to save script
1319 groovyConsole.setDirty(false);
1320 groovyConsole.exit();
1325 private void storeLastKnownDimensions(String string, Rectangle jc)
1327 Cache.log.debug("Storing last known dimensions for "
1328 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1329 + " height:" + jc.height);
1331 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1332 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1333 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1334 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1344 public void aboutMenuItem_actionPerformed(ActionEvent e)
1346 new Thread(new Runnable()
1351 new SplashScreen(false);
1357 * Returns the html text for the About screen, including any available version
1358 * number, build details, author details and citation reference, but without
1359 * the enclosing {@code html} tags
1363 public String getAboutMessage()
1365 StringBuilder message = new StringBuilder(1024);
1366 message.append("<h1><strong>Version: ")
1367 .append(Cache.getProperty("VERSION")).append("</strong></h1>")
1368 .append("<strong>Built: <em>")
1369 .append(Cache.getDefault("BUILD_DATE", "unknown"))
1370 .append("</em> from ").append(Cache.getBuildDetailsForSplash())
1371 .append("</strong>");
1373 String latestVersion = Cache.getDefault("LATEST_VERSION", "Checking");
1374 if (latestVersion.equals("Checking"))
1376 // JBP removed this message for 2.11: May be reinstated in future version
1377 // message.append("<br>...Checking latest version...</br>");
1379 else if (!latestVersion.equals(Cache.getProperty("VERSION")))
1381 boolean red = false;
1382 if (Cache.getProperty("VERSION").toLowerCase()
1383 .indexOf("automated build") == -1)
1386 // Displayed when code version and jnlp version do not match and code
1387 // version is not a development build
1388 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1391 message.append("<br>!! Version ")
1392 .append(Cache.getDefault("LATEST_VERSION", "..Checking.."))
1393 .append(" is available for download from ")
1394 .append(Cache.getDefault("www.jalview.org",
1395 "http://www.jalview.org"))
1399 message.append("</div>");
1402 message.append("<br>Authors: ");
1403 message.append(Cache.getDefault("AUTHORFNAMES", DEFAULT_AUTHORS));
1404 message.append(CITATION);
1406 return message.toString();
1410 * Action on requesting Help documentation
1413 public void documentationMenuItem_actionPerformed()
1417 if (Platform.isJS())
1419 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1428 Help.showHelpWindow();
1430 } catch (Exception ex)
1432 System.err.println("Error opening help: " + ex.getMessage());
1437 public void closeAll_actionPerformed(ActionEvent e)
1439 // TODO show a progress bar while closing?
1440 JInternalFrame[] frames = desktop.getAllFrames();
1441 for (int i = 0; i < frames.length; i++)
1445 frames[i].setClosed(true);
1446 } catch (java.beans.PropertyVetoException ex)
1450 Jalview.setCurrentAlignFrame(null);
1451 System.out.println("ALL CLOSED");
1454 * reset state of singleton objects as appropriate (clear down session state
1455 * when all windows are closed)
1457 StructureSelectionManager ssm = StructureSelectionManager
1458 .getStructureSelectionManager(this);
1466 public void raiseRelated_actionPerformed(ActionEvent e)
1468 reorderAssociatedWindows(false, false);
1472 public void minimizeAssociated_actionPerformed(ActionEvent e)
1474 reorderAssociatedWindows(true, false);
1477 void closeAssociatedWindows()
1479 reorderAssociatedWindows(false, true);
1485 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1489 protected void garbageCollect_actionPerformed(ActionEvent e)
1491 // We simply collect the garbage
1492 Cache.log.debug("Collecting garbage...");
1494 Cache.log.debug("Finished garbage collection.");
1501 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1505 protected void showMemusage_actionPerformed(ActionEvent e)
1507 desktop.showMemoryUsage(showMemusage.isSelected());
1514 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1518 protected void showConsole_actionPerformed(ActionEvent e)
1520 showConsole(showConsole.isSelected());
1523 Console jconsole = null;
1526 * control whether the java console is visible or not
1530 void showConsole(boolean selected)
1532 // TODO: decide if we should update properties file
1533 if (jconsole != null) // BH 2018
1535 showConsole.setSelected(selected);
1536 Cache.setProperty("SHOW_JAVA_CONSOLE",
1537 Boolean.valueOf(selected).toString());
1538 jconsole.setVisible(selected);
1542 void reorderAssociatedWindows(boolean minimize, boolean close)
1544 JInternalFrame[] frames = desktop.getAllFrames();
1545 if (frames == null || frames.length < 1)
1550 AlignmentViewport source = null, target = null;
1551 if (frames[0] instanceof AlignFrame)
1553 source = ((AlignFrame) frames[0]).getCurrentView();
1555 else if (frames[0] instanceof TreePanel)
1557 source = ((TreePanel) frames[0]).getViewPort();
1559 else if (frames[0] instanceof PCAPanel)
1561 source = ((PCAPanel) frames[0]).av;
1563 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1565 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1570 for (int i = 0; i < frames.length; i++)
1573 if (frames[i] == null)
1577 if (frames[i] instanceof AlignFrame)
1579 target = ((AlignFrame) frames[i]).getCurrentView();
1581 else if (frames[i] instanceof TreePanel)
1583 target = ((TreePanel) frames[i]).getViewPort();
1585 else if (frames[i] instanceof PCAPanel)
1587 target = ((PCAPanel) frames[i]).av;
1589 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1591 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1594 if (source == target)
1600 frames[i].setClosed(true);
1604 frames[i].setIcon(minimize);
1607 frames[i].toFront();
1611 } catch (java.beans.PropertyVetoException ex)
1626 protected void preferences_actionPerformed(ActionEvent e)
1632 * Prompts the user to choose a file and then saves the Jalview state as a
1633 * Jalview project file
1636 public void saveState_actionPerformed()
1638 saveState_actionPerformed(false);
1641 public void saveState_actionPerformed(boolean saveAs)
1643 java.io.File projectFile = getProjectFile();
1644 // autoSave indicates we already have a file and don't need to ask
1645 boolean autoSave = projectFile != null && !saveAs
1646 && BackupFiles.getEnabled();
1648 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1649 // saveAs="+saveAs+", Backups
1650 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1652 boolean approveSave = false;
1655 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1658 chooser.setFileView(new JalviewFileView());
1659 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1661 int value = chooser.showSaveDialog(this);
1663 if (value == JalviewFileChooser.APPROVE_OPTION)
1665 projectFile = chooser.getSelectedFile();
1666 setProjectFile(projectFile);
1671 if (approveSave || autoSave)
1673 final Desktop me = this;
1674 final java.io.File chosenFile = projectFile;
1675 new Thread(new Runnable()
1680 // TODO: refactor to Jalview desktop session controller action.
1681 setProgressBar(MessageManager.formatMessage(
1682 "label.saving_jalview_project", new Object[]
1683 { chosenFile.getName() }), chosenFile.hashCode());
1684 Cache.setProperty("LAST_DIRECTORY",
1685 chosenFile.getParent());
1686 // TODO catch and handle errors for savestate
1687 // TODO prevent user from messing with the Desktop whilst we're saving
1690 boolean doBackup = BackupFiles.getEnabled();
1691 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1693 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1697 backupfiles.setWriteSuccess(true);
1698 backupfiles.rollBackupsAndRenameTempFile();
1700 } catch (OutOfMemoryError oom)
1702 new OOMWarning("Whilst saving current state to "
1703 + chosenFile.getName(), oom);
1704 } catch (Exception ex)
1706 Cache.log.error("Problems whilst trying to save to "
1707 + chosenFile.getName(), ex);
1708 JvOptionPane.showMessageDialog(me,
1709 MessageManager.formatMessage(
1710 "label.error_whilst_saving_current_state_to",
1712 { chosenFile.getName() }),
1713 MessageManager.getString("label.couldnt_save_project"),
1714 JvOptionPane.WARNING_MESSAGE);
1716 setProgressBar(null, chosenFile.hashCode());
1723 public void saveAsState_actionPerformed(ActionEvent e)
1725 saveState_actionPerformed(true);
1728 private void setProjectFile(File choice)
1730 this.projectFile = choice;
1733 public File getProjectFile()
1735 return this.projectFile;
1739 * Shows a file chooser dialog and tries to read in the selected file as a
1743 public void loadState_actionPerformed()
1745 final String[] suffix = new String[] { "jvp", "jar" };
1746 final String[] desc = new String[] { "Jalview Project",
1747 "Jalview Project (old)" };
1748 JalviewFileChooser chooser = new JalviewFileChooser(
1749 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1750 "Jalview Project", true, BackupFiles.getEnabled()); // last two booleans: allFiles,
1752 chooser.setFileView(new JalviewFileView());
1753 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1754 chooser.setResponseHandler(0, new Runnable()
1759 File selectedFile = chooser.getSelectedFile();
1760 setProjectFile(selectedFile);
1761 String choice = selectedFile.getAbsolutePath();
1762 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1763 new Thread(new Runnable()
1770 new Jalview2XML().loadJalviewAlign(choice);
1771 } catch (OutOfMemoryError oom)
1773 new OOMWarning("Whilst loading project from " + choice, oom);
1774 } catch (Exception ex)
1777 "Problems whilst loading project from " + choice, ex);
1778 JvOptionPane.showMessageDialog(Desktop.desktop,
1779 MessageManager.formatMessage(
1780 "label.error_whilst_loading_project_from",
1783 MessageManager.getString("label.couldnt_load_project"),
1784 JvOptionPane.WARNING_MESSAGE);
1791 chooser.showOpenDialog(this);
1795 public void inputSequence_actionPerformed(ActionEvent e)
1797 new SequenceFetcher(this);
1800 JPanel progressPanel;
1802 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1804 public void startLoading(final Object fileName)
1806 if (fileLoadingCount == 0)
1808 fileLoadingPanels.add(addProgressPanel(MessageManager
1809 .formatMessage("label.loading_file", new Object[]
1815 private JPanel addProgressPanel(String string)
1817 if (progressPanel == null)
1819 progressPanel = new JPanel(new GridLayout(1, 1));
1820 totalProgressCount = 0;
1821 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1823 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1824 JProgressBar progressBar = new JProgressBar();
1825 progressBar.setIndeterminate(true);
1827 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1829 thisprogress.add(progressBar, BorderLayout.CENTER);
1830 progressPanel.add(thisprogress);
1831 ((GridLayout) progressPanel.getLayout()).setRows(
1832 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1833 ++totalProgressCount;
1834 instance.validate();
1835 return thisprogress;
1838 int totalProgressCount = 0;
1840 private void removeProgressPanel(JPanel progbar)
1842 if (progressPanel != null)
1844 synchronized (progressPanel)
1846 progressPanel.remove(progbar);
1847 GridLayout gl = (GridLayout) progressPanel.getLayout();
1848 gl.setRows(gl.getRows() - 1);
1849 if (--totalProgressCount < 1)
1851 this.getContentPane().remove(progressPanel);
1852 progressPanel = null;
1859 public void stopLoading()
1862 if (fileLoadingCount < 1)
1864 while (fileLoadingPanels.size() > 0)
1866 removeProgressPanel(fileLoadingPanels.remove(0));
1868 fileLoadingPanels.clear();
1869 fileLoadingCount = 0;
1874 public static int getViewCount(String alignmentId)
1876 AlignmentViewport[] aps = getViewports(alignmentId);
1877 return (aps == null) ? 0 : aps.length;
1882 * @param alignmentId
1883 * - if null, all sets are returned
1884 * @return all AlignmentPanels concerning the alignmentId sequence set
1886 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1888 if (Desktop.desktop == null)
1890 // no frames created and in headless mode
1891 // TODO: verify that frames are recoverable when in headless mode
1894 List<AlignmentPanel> aps = new ArrayList<>();
1895 AlignFrame[] frames = getAlignFrames();
1900 for (AlignFrame af : frames)
1902 for (AlignmentPanel ap : af.alignPanels)
1904 if (alignmentId == null
1905 || alignmentId.equals(ap.av.getSequenceSetId()))
1911 if (aps.size() == 0)
1915 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1920 * get all the viewports on an alignment.
1922 * @param sequenceSetId
1923 * unique alignment id (may be null - all viewports returned in that
1925 * @return all viewports on the alignment bound to sequenceSetId
1927 public static AlignmentViewport[] getViewports(String sequenceSetId)
1929 List<AlignmentViewport> viewp = new ArrayList<>();
1930 if (desktop != null)
1932 AlignFrame[] frames = Desktop.getAlignFrames();
1934 for (AlignFrame afr : frames)
1936 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1937 .equals(sequenceSetId))
1939 if (afr.alignPanels != null)
1941 for (AlignmentPanel ap : afr.alignPanels)
1943 if (sequenceSetId == null
1944 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1952 viewp.add(afr.getViewport());
1956 if (viewp.size() > 0)
1958 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1965 * Explode the views in the given frame into separate AlignFrame
1969 public static void explodeViews(AlignFrame af)
1971 int size = af.alignPanels.size();
1977 for (int i = 0; i < size; i++)
1979 AlignmentPanel ap = af.alignPanels.get(i);
1980 AlignFrame newaf = new AlignFrame(ap);
1983 * Restore the view's last exploded frame geometry if known. Multiple
1984 * views from one exploded frame share and restore the same (frame)
1985 * position and size.
1987 Rectangle geometry = ap.av.getExplodedGeometry();
1988 if (geometry != null)
1990 newaf.setBounds(geometry);
1993 ap.av.setGatherViewsHere(false);
1995 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
1996 AlignFrame.DEFAULT_HEIGHT);
1999 af.alignPanels.clear();
2000 af.closeMenuItem_actionPerformed(true);
2005 * Gather expanded views (separate AlignFrame's) with the same sequence set
2006 * identifier back in to this frame as additional views, and close the expanded
2007 * views. Note the expanded frames may themselves have multiple views. We take
2012 public void gatherViews(AlignFrame source)
2014 source.viewport.setGatherViewsHere(true);
2015 source.viewport.setExplodedGeometry(source.getBounds());
2016 JInternalFrame[] frames = desktop.getAllFrames();
2017 String viewId = source.viewport.getSequenceSetId();
2019 for (int t = 0; t < frames.length; t++)
2021 if (frames[t] instanceof AlignFrame && frames[t] != source)
2023 AlignFrame af = (AlignFrame) frames[t];
2024 boolean gatherThis = false;
2025 for (int a = 0; a < af.alignPanels.size(); a++)
2027 AlignmentPanel ap = af.alignPanels.get(a);
2028 if (viewId.equals(ap.av.getSequenceSetId()))
2031 ap.av.setGatherViewsHere(false);
2032 ap.av.setExplodedGeometry(af.getBounds());
2033 source.addAlignmentPanel(ap, false);
2039 af.alignPanels.clear();
2040 af.closeMenuItem_actionPerformed(true);
2046 public JInternalFrame[] getAllFrames()
2048 return desktop.getAllFrames();
2052 * Checks the given url to see if it gives a response indicating that the user
2053 * should be informed of a new questionnaire.
2057 public void checkForQuestionnaire(String url)
2059 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2060 // javax.swing.SwingUtilities.invokeLater(jvq);
2061 new Thread(jvq).start();
2064 public void checkURLLinks()
2066 // Thread off the URL link checker
2067 addDialogThread(new Runnable()
2072 if (Cache.getDefault("CHECKURLLINKS", true))
2074 // check what the actual links are - if it's just the default don't
2075 // bother with the warning
2076 List<String> links = Preferences.sequenceUrlLinks
2079 // only need to check links if there is one with a
2080 // SEQUENCE_ID which is not the default EMBL_EBI link
2081 ListIterator<String> li = links.listIterator();
2082 boolean check = false;
2083 List<JLabel> urls = new ArrayList<>();
2084 while (li.hasNext())
2086 String link = li.next();
2087 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2088 && !UrlConstants.isDefaultString(link))
2091 int barPos = link.indexOf("|");
2092 String urlMsg = barPos == -1 ? link
2093 : link.substring(0, barPos) + ": "
2094 + link.substring(barPos + 1);
2095 urls.add(new JLabel(urlMsg));
2103 // ask user to check in case URL links use old style tokens
2104 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2105 JPanel msgPanel = new JPanel();
2106 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2107 msgPanel.add(Box.createVerticalGlue());
2108 JLabel msg = new JLabel(MessageManager
2109 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2110 JLabel msg2 = new JLabel(MessageManager
2111 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2113 for (JLabel url : urls)
2119 final JCheckBox jcb = new JCheckBox(
2120 MessageManager.getString("label.do_not_display_again"));
2121 jcb.addActionListener(new ActionListener()
2124 public void actionPerformed(ActionEvent e)
2126 // update Cache settings for "don't show this again"
2127 boolean showWarningAgain = !jcb.isSelected();
2128 Cache.setProperty("CHECKURLLINKS",
2129 Boolean.valueOf(showWarningAgain).toString());
2134 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2136 .getString("label.SEQUENCE_ID_no_longer_used"),
2137 JvOptionPane.WARNING_MESSAGE);
2144 * Proxy class for JDesktopPane which optionally displays the current memory
2145 * usage and highlights the desktop area with a red bar if free memory runs low.
2149 public class MyDesktopPane extends JDesktopPane
2152 private static final float ONE_MB = 1048576f;
2154 boolean showMemoryUsage = false;
2158 java.text.NumberFormat df;
2160 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2163 public MyDesktopPane(boolean showMemoryUsage)
2165 showMemoryUsage(showMemoryUsage);
2168 public void showMemoryUsage(boolean showMemory)
2170 this.showMemoryUsage = showMemory;
2173 Thread worker = new Thread(this);
2179 public boolean isShowMemoryUsage()
2181 return showMemoryUsage;
2187 df = java.text.NumberFormat.getNumberInstance();
2188 df.setMaximumFractionDigits(2);
2189 runtime = Runtime.getRuntime();
2191 while (showMemoryUsage)
2195 maxMemory = runtime.maxMemory() / ONE_MB;
2196 allocatedMemory = runtime.totalMemory() / ONE_MB;
2197 freeMemory = runtime.freeMemory() / ONE_MB;
2198 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2200 percentUsage = (totalFreeMemory / maxMemory) * 100;
2202 // if (percentUsage < 20)
2204 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2206 // instance.set.setBorder(border1);
2209 // sleep after showing usage
2211 } catch (Exception ex)
2213 ex.printStackTrace();
2219 public void paintComponent(Graphics g)
2221 if (showMemoryUsage && g != null && df != null)
2223 if (percentUsage < 20)
2225 g.setColor(Color.red);
2227 FontMetrics fm = g.getFontMetrics();
2230 g.drawString(MessageManager.formatMessage("label.memory_stats",
2232 { df.format(totalFreeMemory), df.format(maxMemory),
2233 df.format(percentUsage) }),
2234 10, getHeight() - fm.getHeight());
2241 * Accessor method to quickly get all the AlignmentFrames loaded.
2243 * @return an array of AlignFrame, or null if none found
2245 public static AlignFrame[] getAlignFrames()
2247 if (Jalview.isHeadlessMode())
2249 // Desktop.desktop is null in headless mode
2250 return new AlignFrame[] { Jalview.currentAlignFrame };
2253 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2259 List<AlignFrame> avp = new ArrayList<>();
2261 for (int i = frames.length - 1; i > -1; i--)
2263 if (frames[i] instanceof AlignFrame)
2265 avp.add((AlignFrame) frames[i]);
2267 else if (frames[i] instanceof SplitFrame)
2270 * Also check for a split frame containing an AlignFrame
2272 GSplitFrame sf = (GSplitFrame) frames[i];
2273 if (sf.getTopFrame() instanceof AlignFrame)
2275 avp.add((AlignFrame) sf.getTopFrame());
2277 if (sf.getBottomFrame() instanceof AlignFrame)
2279 avp.add((AlignFrame) sf.getBottomFrame());
2283 if (avp.size() == 0)
2287 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2292 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2296 public GStructureViewer[] getJmols()
2298 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2304 List<GStructureViewer> avp = new ArrayList<>();
2306 for (int i = frames.length - 1; i > -1; i--)
2308 if (frames[i] instanceof AppJmol)
2310 GStructureViewer af = (GStructureViewer) frames[i];
2314 if (avp.size() == 0)
2318 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2323 * Add Groovy Support to Jalview
2326 public void groovyShell_actionPerformed()
2330 openGroovyConsole();
2331 } catch (Exception ex)
2333 Cache.log.error("Groovy Shell Creation failed.", ex);
2334 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2336 MessageManager.getString("label.couldnt_create_groovy_shell"),
2337 MessageManager.getString("label.groovy_support_failed"),
2338 JvOptionPane.ERROR_MESSAGE);
2343 * Open the Groovy console
2345 void openGroovyConsole()
2347 if (groovyConsole == null)
2349 groovyConsole = new groovy.ui.Console();
2350 groovyConsole.setVariable("Jalview", this);
2351 groovyConsole.run();
2354 * We allow only one console at a time, so that AlignFrame menu option
2355 * 'Calculate | Run Groovy script' is unambiguous.
2356 * Disable 'Groovy Console', and enable 'Run script', when the console is
2357 * opened, and the reverse when it is closed
2359 Window window = (Window) groovyConsole.getFrame();
2360 window.addWindowListener(new WindowAdapter()
2363 public void windowClosed(WindowEvent e)
2366 * rebind CMD-Q from Groovy Console to Jalview Quit
2369 enableExecuteGroovy(false);
2375 * show Groovy console window (after close and reopen)
2377 ((Window) groovyConsole.getFrame()).setVisible(true);
2380 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2381 * and disable opening a second console
2383 enableExecuteGroovy(true);
2387 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2390 protected void addQuitHandler()
2392 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2393 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2394 jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
2396 getRootPane().getActionMap().put("Quit", new AbstractAction()
2399 public void actionPerformed(ActionEvent e)
2407 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2410 * true if Groovy console is open
2412 public void enableExecuteGroovy(boolean enabled)
2415 * disable opening a second Groovy console
2416 * (or re-enable when the console is closed)
2418 groovyShell.setEnabled(!enabled);
2420 AlignFrame[] alignFrames = getAlignFrames();
2421 if (alignFrames != null)
2423 for (AlignFrame af : alignFrames)
2425 af.setGroovyEnabled(enabled);
2431 * Progress bars managed by the IProgressIndicator method.
2433 private Hashtable<Long, JPanel> progressBars;
2435 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2440 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2443 public void setProgressBar(String message, long id)
2445 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2447 if (progressBars == null)
2449 progressBars = new Hashtable<>();
2450 progressBarHandlers = new Hashtable<>();
2453 if (progressBars.get(Long.valueOf(id)) != null)
2455 JPanel panel = progressBars.remove(Long.valueOf(id));
2456 if (progressBarHandlers.contains(Long.valueOf(id)))
2458 progressBarHandlers.remove(Long.valueOf(id));
2460 removeProgressPanel(panel);
2464 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2471 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2472 * jalview.gui.IProgressIndicatorHandler)
2475 public void registerHandler(final long id,
2476 final IProgressIndicatorHandler handler)
2478 if (progressBarHandlers == null
2479 || !progressBars.containsKey(Long.valueOf(id)))
2481 throw new Error(MessageManager.getString(
2482 "error.call_setprogressbar_before_registering_handler"));
2484 progressBarHandlers.put(Long.valueOf(id), handler);
2485 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2486 if (handler.canCancel())
2488 JButton cancel = new JButton(
2489 MessageManager.getString("action.cancel"));
2490 final IProgressIndicator us = this;
2491 cancel.addActionListener(new ActionListener()
2495 public void actionPerformed(ActionEvent e)
2497 handler.cancelActivity(id);
2498 us.setProgressBar(MessageManager
2499 .formatMessage("label.cancelled_params", new Object[]
2500 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2504 progressPanel.add(cancel, BorderLayout.EAST);
2510 * @return true if any progress bars are still active
2513 public boolean operationInProgress()
2515 if (progressBars != null && progressBars.size() > 0)
2523 * This will return the first AlignFrame holding the given viewport instance. It
2524 * will break if there are more than one AlignFrames viewing a particular av.
2527 * @return alignFrame for viewport
2529 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2531 if (desktop != null)
2533 AlignmentPanel[] aps = getAlignmentPanels(
2534 viewport.getSequenceSetId());
2535 for (int panel = 0; aps != null && panel < aps.length; panel++)
2537 if (aps[panel] != null && aps[panel].av == viewport)
2539 return aps[panel].alignFrame;
2546 public VamsasApplication getVamsasApplication()
2548 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2554 * flag set if jalview GUI is being operated programmatically
2556 private boolean inBatchMode = false;
2559 * check if jalview GUI is being operated programmatically
2561 * @return inBatchMode
2563 public boolean isInBatchMode()
2569 * set flag if jalview GUI is being operated programmatically
2571 * @param inBatchMode
2573 public void setInBatchMode(boolean inBatchMode)
2575 this.inBatchMode = inBatchMode;
2578 public void startServiceDiscovery()
2580 startServiceDiscovery(false);
2583 public void startServiceDiscovery(boolean blocking)
2585 boolean alive = true;
2586 Thread t0 = null, t1 = null, t2 = null;
2587 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2590 // todo: changesupport handlers need to be transferred
2591 if (discoverer == null)
2593 discoverer = new jalview.ws.jws1.Discoverer();
2594 // register PCS handler for desktop.
2595 discoverer.addPropertyChangeListener(changeSupport);
2597 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2598 // until we phase out completely
2599 (t0 = new Thread(discoverer)).start();
2602 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2604 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2605 .startDiscoverer(changeSupport);
2609 // TODO: do rest service discovery
2618 } catch (Exception e)
2621 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2622 || (t3 != null && t3.isAlive())
2623 || (t0 != null && t0.isAlive());
2629 * called to check if the service discovery process completed successfully.
2633 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2635 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2637 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2638 .getErrorMessages();
2641 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2643 if (serviceChangedDialog == null)
2645 // only run if we aren't already displaying one of these.
2646 addDialogThread(serviceChangedDialog = new Runnable()
2653 * JalviewDialog jd =new JalviewDialog() {
2655 * @Override protected void cancelPressed() { // TODO
2656 * Auto-generated method stub
2658 * }@Override protected void okPressed() { // TODO
2659 * Auto-generated method stub
2661 * }@Override protected void raiseClosed() { // TODO
2662 * Auto-generated method stub
2664 * } }; jd.initDialogFrame(new
2665 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2666 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2667 * + " or mis-configured HTTP proxy settings.<br/>" +
2668 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2670 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2671 * ), true, true, "Web Service Configuration Problem", 450,
2674 * jd.waitForInput();
2676 JvOptionPane.showConfirmDialog(Desktop.desktop,
2677 new JLabel("<html><table width=\"450\"><tr><td>"
2678 + ermsg + "</td></tr></table>"
2679 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2680 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2681 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2682 + " Tools->Preferences dialog box to change them.</p></html>"),
2683 "Web Service Configuration Problem",
2684 JvOptionPane.DEFAULT_OPTION,
2685 JvOptionPane.ERROR_MESSAGE);
2686 serviceChangedDialog = null;
2695 "Errors reported by JABA discovery service. Check web services preferences.\n"
2702 private Runnable serviceChangedDialog = null;
2705 * start a thread to open a URL in the configured browser. Pops up a warning
2706 * dialog to the user if there is an exception when calling out to the browser
2711 public static void showUrl(final String url)
2713 showUrl(url, Desktop.instance);
2717 * Like showUrl but allows progress handler to be specified
2721 * (null) or object implementing IProgressIndicator
2723 public static void showUrl(final String url,
2724 final IProgressIndicator progress)
2726 new Thread(new Runnable()
2733 if (progress != null)
2735 progress.setProgressBar(MessageManager
2736 .formatMessage("status.opening_params", new Object[]
2737 { url }), this.hashCode());
2739 jalview.util.BrowserLauncher.openURL(url);
2740 } catch (Exception ex)
2742 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2744 .getString("label.web_browser_not_found_unix"),
2745 MessageManager.getString("label.web_browser_not_found"),
2746 JvOptionPane.WARNING_MESSAGE);
2748 ex.printStackTrace();
2750 if (progress != null)
2752 progress.setProgressBar(null, this.hashCode());
2758 public static WsParamSetManager wsparamManager = null;
2760 public static ParamManager getUserParameterStore()
2762 if (wsparamManager == null)
2764 wsparamManager = new WsParamSetManager();
2766 return wsparamManager;
2770 * static hyperlink handler proxy method for use by Jalview's internal windows
2774 public static void hyperlinkUpdate(HyperlinkEvent e)
2776 if (e.getEventType() == EventType.ACTIVATED)
2781 url = e.getURL().toString();
2782 Desktop.showUrl(url);
2783 } catch (Exception x)
2787 if (Cache.log != null)
2789 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2794 "Couldn't handle string " + url + " as a URL.");
2797 // ignore any exceptions due to dud links.
2804 * single thread that handles display of dialogs to user.
2806 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2809 * flag indicating if dialogExecutor should try to acquire a permit
2811 private volatile boolean dialogPause = true;
2816 private java.util.concurrent.Semaphore block = new Semaphore(0);
2818 private static groovy.ui.Console groovyConsole;
2821 * add another dialog thread to the queue
2825 public void addDialogThread(final Runnable prompter)
2827 dialogExecutor.submit(new Runnable()
2837 } catch (InterruptedException x)
2841 if (instance == null)
2847 SwingUtilities.invokeAndWait(prompter);
2848 } catch (Exception q)
2850 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2856 public void startDialogQueue()
2858 // set the flag so we don't pause waiting for another permit and semaphore
2859 // the current task to begin
2860 dialogPause = false;
2865 * Outputs an image of the desktop to file in EPS format, after prompting the
2866 * user for choice of Text or Lineart character rendering (unless a preference
2867 * has been set). The file name is generated as
2870 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2874 protected void snapShotWindow_actionPerformed(ActionEvent e)
2876 // currently the menu option to do this is not shown
2879 int width = getWidth();
2880 int height = getHeight();
2882 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2883 ImageWriterI writer = new ImageWriterI()
2886 public void exportImage(Graphics g) throws Exception
2889 Cache.log.info("Successfully written snapshot to file "
2890 + of.getAbsolutePath());
2893 String title = "View of desktop";
2894 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2896 exporter.doExport(of, this, width, height, title);
2900 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2901 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
2902 * location last time the view was expanded (if any). However it does not
2903 * remember the split pane divider location - this is set to match the
2904 * 'exploding' frame.
2908 public void explodeViews(SplitFrame sf)
2910 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2911 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2912 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2914 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2916 int viewCount = topPanels.size();
2923 * Processing in reverse order works, forwards order leaves the first panels
2924 * not visible. I don't know why!
2926 for (int i = viewCount - 1; i >= 0; i--)
2929 * Make new top and bottom frames. These take over the respective
2930 * AlignmentPanel objects, including their AlignmentViewports, so the
2931 * cdna/protein relationships between the viewports is carried over to the
2934 * explodedGeometry holds the (x, y) position of the previously exploded
2935 * SplitFrame, and the (width, height) of the AlignFrame component
2937 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
2938 AlignFrame newTopFrame = new AlignFrame(topPanel);
2939 newTopFrame.setSize(oldTopFrame.getSize());
2940 newTopFrame.setVisible(true);
2941 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
2942 .getExplodedGeometry();
2943 if (geometry != null)
2945 newTopFrame.setSize(geometry.getSize());
2948 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
2949 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
2950 newBottomFrame.setSize(oldBottomFrame.getSize());
2951 newBottomFrame.setVisible(true);
2952 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
2953 .getExplodedGeometry();
2954 if (geometry != null)
2956 newBottomFrame.setSize(geometry.getSize());
2959 topPanel.av.setGatherViewsHere(false);
2960 bottomPanel.av.setGatherViewsHere(false);
2961 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
2963 if (geometry != null)
2965 splitFrame.setLocation(geometry.getLocation());
2967 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
2971 * Clear references to the panels (now relocated in the new SplitFrames)
2972 * before closing the old SplitFrame.
2975 bottomPanels.clear();
2980 * Gather expanded split frames, sharing the same pairs of sequence set ids,
2981 * back into the given SplitFrame as additional views. Note that the gathered
2982 * frames may themselves have multiple views.
2986 public void gatherViews(GSplitFrame source)
2989 * special handling of explodedGeometry for a view within a SplitFrame: - it
2990 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
2991 * height) of the AlignFrame component
2993 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
2994 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
2995 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
2996 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
2997 myBottomFrame.viewport
2998 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
2999 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3000 myTopFrame.viewport.setGatherViewsHere(true);
3001 myBottomFrame.viewport.setGatherViewsHere(true);
3002 String topViewId = myTopFrame.viewport.getSequenceSetId();
3003 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3005 JInternalFrame[] frames = desktop.getAllFrames();
3006 for (JInternalFrame frame : frames)
3008 if (frame instanceof SplitFrame && frame != source)
3010 SplitFrame sf = (SplitFrame) frame;
3011 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3012 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3013 boolean gatherThis = false;
3014 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3016 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3017 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3018 if (topViewId.equals(topPanel.av.getSequenceSetId())
3019 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3022 topPanel.av.setGatherViewsHere(false);
3023 bottomPanel.av.setGatherViewsHere(false);
3024 topPanel.av.setExplodedGeometry(
3025 new Rectangle(sf.getLocation(), topFrame.getSize()));
3026 bottomPanel.av.setExplodedGeometry(
3027 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3028 myTopFrame.addAlignmentPanel(topPanel, false);
3029 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3035 topFrame.getAlignPanels().clear();
3036 bottomFrame.getAlignPanels().clear();
3043 * The dust settles...give focus to the tab we did this from.
3045 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3048 public static groovy.ui.Console getGroovyConsole()
3050 return groovyConsole;
3054 * handles the payload of a drag and drop event.
3056 * TODO refactor to desktop utilities class
3059 * - Data source strings extracted from the drop event
3061 * - protocol for each data source extracted from the drop event
3065 * - the payload from the drop event
3068 public static void transferFromDropTarget(List<Object> files,
3069 List<DataSourceType> protocols, DropTargetDropEvent evt,
3070 Transferable t) throws Exception
3073 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3075 // DataFlavor[] flavors = t.getTransferDataFlavors();
3076 // for (int i = 0; i < flavors.length; i++) {
3077 // if (flavors[i].isFlavorJavaFileListType()) {
3078 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3079 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3080 // for (int j = 0; j < list.size(); j++) {
3081 // File file = (File) list.get(j);
3082 // byte[] data = getDroppedFileBytes(file);
3083 // fileName.setText(file.getName() + " - " + data.length + " " +
3084 // evt.getLocation());
3085 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3086 // target.setText(new String(data));
3088 // dtde.dropComplete(true);
3093 DataFlavor uriListFlavor = new DataFlavor(
3094 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3097 urlFlavour = new DataFlavor(
3098 "application/x-java-url; class=java.net.URL");
3099 } catch (ClassNotFoundException cfe)
3101 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3104 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3109 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3110 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3111 // means url may be null.
3114 protocols.add(DataSourceType.URL);
3115 files.add(url.toString());
3116 Cache.log.debug("Drop handled as URL dataflavor "
3117 + files.get(files.size() - 1));
3122 if (Platform.isAMacAndNotJS())
3125 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3128 } catch (Throwable ex)
3130 Cache.log.debug("URL drop handler failed.", ex);
3133 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3135 // Works on Windows and MacOSX
3136 Cache.log.debug("Drop handled as javaFileListFlavor");
3137 for (Object file : (List) t
3138 .getTransferData(DataFlavor.javaFileListFlavor))
3141 protocols.add(DataSourceType.FILE);
3146 // Unix like behaviour
3147 boolean added = false;
3149 if (t.isDataFlavorSupported(uriListFlavor))
3151 Cache.log.debug("Drop handled as uriListFlavor");
3152 // This is used by Unix drag system
3153 data = (String) t.getTransferData(uriListFlavor);
3157 // fallback to text: workaround - on OSX where there's a JVM bug
3158 Cache.log.debug("standard URIListFlavor failed. Trying text");
3159 // try text fallback
3160 DataFlavor textDf = new DataFlavor(
3161 "text/plain;class=java.lang.String");
3162 if (t.isDataFlavorSupported(textDf))
3164 data = (String) t.getTransferData(textDf);
3167 Cache.log.debug("Plain text drop content returned "
3168 + (data == null ? "Null - failed" : data));
3173 while (protocols.size() < files.size())
3175 Cache.log.debug("Adding missing FILE protocol for "
3176 + files.get(protocols.size()));
3177 protocols.add(DataSourceType.FILE);
3179 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3180 data, "\r\n"); st.hasMoreTokens();)
3183 String s = st.nextToken();
3184 if (s.startsWith("#"))
3186 // the line is a comment (as per the RFC 2483)
3189 java.net.URI uri = new java.net.URI(s);
3190 if (uri.getScheme().toLowerCase().startsWith("http"))
3192 protocols.add(DataSourceType.URL);
3193 files.add(uri.toString());
3197 // otherwise preserve old behaviour: catch all for file objects
3198 java.io.File file = new java.io.File(uri);
3199 protocols.add(DataSourceType.FILE);
3200 files.add(file.toString());
3205 if (Cache.log.isDebugEnabled())
3207 if (data == null || !added)
3210 if (t.getTransferDataFlavors() != null
3211 && t.getTransferDataFlavors().length > 0)
3214 "Couldn't resolve drop data. Here are the supported flavors:");
3215 for (DataFlavor fl : t.getTransferDataFlavors())
3218 "Supported transfer dataflavor: " + fl.toString());
3219 Object df = t.getTransferData(fl);
3222 Cache.log.debug("Retrieves: " + df);
3226 Cache.log.debug("Retrieved nothing");
3232 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3238 if (Platform.isWindowsAndNotJS())
3240 Cache.log.debug("Scanning dropped content for Windows Link Files");
3242 // resolve any .lnk files in the file drop
3243 for (int f = 0; f < files.size(); f++)
3245 String source = files.get(f).toString().toLowerCase();
3246 if (protocols.get(f).equals(DataSourceType.FILE)
3247 && (source.endsWith(".lnk") || source.endsWith(".url")
3248 || source.endsWith(".site")))
3252 Object obj = files.get(f);
3253 File lf = (obj instanceof File ? (File) obj
3254 : new File((String) obj));
3255 // process link file to get a URL
3256 Cache.log.debug("Found potential link file: " + lf);
3257 WindowsShortcut wscfile = new WindowsShortcut(lf);
3258 String fullname = wscfile.getRealFilename();
3259 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3260 files.set(f, fullname);
3261 Cache.log.debug("Parsed real filename " + fullname
3262 + " to extract protocol: " + protocols.get(f));
3263 } catch (Exception ex)
3266 "Couldn't parse " + files.get(f) + " as a link file.",
3275 * Sets the Preferences property for experimental features to True or False
3276 * depending on the state of the controlling menu item
3279 protected void showExperimental_actionPerformed(boolean selected)
3281 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3285 * Answers a (possibly empty) list of any structure viewer frames (currently for
3286 * either Jmol or Chimera) which are currently open. This may optionally be
3287 * restricted to viewers of a specified class, or viewers linked to a specified
3291 * if not null, only return viewers linked to this panel
3292 * @param structureViewerClass
3293 * if not null, only return viewers of this class
3296 public List<StructureViewerBase> getStructureViewers(
3297 AlignmentPanel apanel,
3298 Class<? extends StructureViewerBase> structureViewerClass)
3300 List<StructureViewerBase> result = new ArrayList<>();
3301 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3303 for (JInternalFrame frame : frames)
3305 if (frame instanceof StructureViewerBase)
3307 if (structureViewerClass == null
3308 || structureViewerClass.isInstance(frame))
3311 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3313 result.add((StructureViewerBase) frame);