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 int DEFAULT_MIN_WIDTH = 300;
139 private static int DEFAULT_MIN_HEIGHT = 250;
141 private static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
143 private static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
145 private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
147 protected static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
149 public static HashMap<String, FileWriter> savingFiles = new HashMap<>();
151 private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
154 * news reader - null if it was never started.
156 private BlogReader jvnews = null;
158 private File projectFile;
162 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
164 public void addJalviewPropertyChangeListener(
165 PropertyChangeListener listener)
167 changeSupport.addJalviewPropertyChangeListener(listener);
171 * @param propertyName
173 * @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
174 * java.beans.PropertyChangeListener)
176 public void addJalviewPropertyChangeListener(String propertyName,
177 PropertyChangeListener listener)
179 changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
183 * @param propertyName
185 * @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
186 * java.beans.PropertyChangeListener)
188 public void removeJalviewPropertyChangeListener(String propertyName,
189 PropertyChangeListener listener)
191 changeSupport.removeJalviewPropertyChangeListener(propertyName,
195 /** Singleton Desktop instance */
196 public static Desktop instance;
198 public static MyDesktopPane desktop;
200 public static MyDesktopPane getDesktop()
202 // BH 2018 could use currentThread() here as a reference to a
203 // Hashtable<Thread, MyDesktopPane> in JavaScript
207 static int openFrameCount = 0;
209 static final int xOffset = 30;
211 static final int yOffset = 30;
213 public static jalview.ws.jws1.Discoverer discoverer;
215 public static Object[] jalviewClipboard;
217 public static boolean internalCopy = false;
219 static int fileLoadingCount = 0;
221 class MyDesktopManager implements DesktopManager
224 private DesktopManager delegate;
226 public MyDesktopManager(DesktopManager delegate)
228 this.delegate = delegate;
232 public void activateFrame(JInternalFrame f)
236 delegate.activateFrame(f);
237 } catch (NullPointerException npe)
239 Point p = getMousePosition();
240 instance.showPasteMenu(p.x, p.y);
245 public void beginDraggingFrame(JComponent f)
247 delegate.beginDraggingFrame(f);
251 public void beginResizingFrame(JComponent f, int direction)
253 delegate.beginResizingFrame(f, direction);
257 public void closeFrame(JInternalFrame f)
259 delegate.closeFrame(f);
263 public void deactivateFrame(JInternalFrame f)
265 delegate.deactivateFrame(f);
269 public void deiconifyFrame(JInternalFrame f)
271 delegate.deiconifyFrame(f);
275 public void dragFrame(JComponent f, int newX, int newY)
281 delegate.dragFrame(f, newX, newY);
285 public void endDraggingFrame(JComponent f)
287 delegate.endDraggingFrame(f);
292 public void endResizingFrame(JComponent f)
294 delegate.endResizingFrame(f);
299 public void iconifyFrame(JInternalFrame f)
301 delegate.iconifyFrame(f);
305 public void maximizeFrame(JInternalFrame f)
307 delegate.maximizeFrame(f);
311 public void minimizeFrame(JInternalFrame f)
313 delegate.minimizeFrame(f);
317 public void openFrame(JInternalFrame f)
319 delegate.openFrame(f);
323 public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
330 delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
334 public void setBoundsForFrame(JComponent f, int newX, int newY,
335 int newWidth, int newHeight)
337 delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
340 // All other methods, simply delegate
345 * Creates a new Desktop object.
351 * A note to implementors. It is ESSENTIAL that any activities that might
352 * block are spawned off as threads rather than waited for during this
357 doConfigureStructurePrefs();
358 setTitle("Jalview " + Cache.getProperty("VERSION"));
360 if (!Platform.isAMac())
362 // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
366 this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
370 if (!Platform.isJS())
374 APQHandlers.setAPQHandlers(this);
375 } catch (Throwable t)
377 System.out.println("Error setting APQHandlers: " + t.toString());
378 // t.printStackTrace();
382 addWindowListener(new WindowAdapter()
386 public void windowClosing(WindowEvent ev)
392 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE",
395 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE",
397 desktop = new MyDesktopPane(selmemusage);
399 showMemusage.setSelected(selmemusage);
400 desktop.setBackground(Color.white);
402 getContentPane().setLayout(new BorderLayout());
403 // alternate config - have scrollbars - see notes in JAL-153
404 // JScrollPane sp = new JScrollPane();
405 // sp.getViewport().setView(desktop);
406 // getContentPane().add(sp, BorderLayout.CENTER);
408 // BH 2018 - just an experiment to try unclipped JInternalFrames.
411 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
414 getContentPane().add(desktop, BorderLayout.CENTER);
415 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
417 // This line prevents Windows Look&Feel resizing all new windows to maximum
418 // if previous window was maximised
419 desktop.setDesktopManager(new MyDesktopManager(
420 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
421 : Platform.isAMacAndNotJS()
422 ? new AquaInternalFrameManager(
423 desktop.getDesktopManager())
424 : desktop.getDesktopManager())));
426 Rectangle dims = getLastKnownDimensions("");
433 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
434 int xPos = Math.max(5, (screenSize.width - 900) / 2);
435 int yPos = Math.max(5, (screenSize.height - 650) / 2);
436 setBounds(xPos, yPos, 900, 650);
439 if (!Platform.isJS())
447 jconsole = new Console(this, showjconsole);
448 // add essential build information
449 jconsole.setHeader("Jalview Version: "
450 + Cache.getProperty("VERSION") + "\n"
451 + "Jalview Installation: "
452 + Cache.getDefault("INSTALLATION", "unknown")
453 + "\n" + "Build Date: "
454 + Cache.getDefault("BUILD_DATE", "unknown") + "\n"
455 + "Java version: " + System.getProperty("java.version") + "\n"
456 + System.getProperty("os.arch") + " "
457 + System.getProperty("os.name") + " "
458 + System.getProperty("os.version"));
460 showConsole(showjconsole);
462 showNews.setVisible(false);
464 experimentalFeatures.setSelected(showExperimental());
466 getIdentifiersOrgData();
470 // Spawn a thread that shows the splashscreen
472 SwingUtilities.invokeLater(new Runnable()
481 // Thread off a new instance of the file chooser - this reduces the time
483 // takes to open it later on.
484 new Thread(new Runnable()
489 Cache.log.debug("Filechooser init thread started.");
490 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
491 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
493 Cache.log.debug("Filechooser init thread finished.");
496 // Add the service change listener
497 changeSupport.addJalviewPropertyChangeListener("services",
498 new PropertyChangeListener()
502 public void propertyChange(PropertyChangeEvent evt)
504 Cache.log.debug("Firing service changed event for "
505 + evt.getNewValue());
506 JalviewServicesChanged(evt);
511 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
513 this.addWindowListener(new WindowAdapter()
516 public void windowClosing(WindowEvent evt)
523 this.addMouseListener(ma = new MouseAdapter()
526 public void mousePressed(MouseEvent evt)
528 if (evt.isPopupTrigger()) // Mac
530 showPasteMenu(evt.getX(), evt.getY());
535 public void mouseReleased(MouseEvent evt)
537 if (evt.isPopupTrigger()) // Windows
539 showPasteMenu(evt.getX(), evt.getY());
543 desktop.addMouseListener(ma);
548 * Answers true if user preferences to enable experimental features is True
553 public boolean showExperimental()
555 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
556 Boolean.FALSE.toString());
557 return Boolean.valueOf(experimental).booleanValue();
560 public void doConfigureStructurePrefs()
562 // configure services
563 StructureSelectionManager ssm = StructureSelectionManager
564 .getStructureSelectionManager(this);
565 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
567 ssm.setAddTempFacAnnot(Cache
568 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
569 ssm.setProcessSecondaryStructure(Cache
570 .getDefault(Preferences.STRUCT_FROM_PDB, true));
571 ssm.setSecStructServices(
572 Cache.getDefault(Preferences.USE_RNAVIEW, true));
576 ssm.setAddTempFacAnnot(false);
577 ssm.setProcessSecondaryStructure(false);
578 ssm.setSecStructServices(false);
582 public void checkForNews()
584 final Desktop me = this;
585 // Thread off the news reader, in case there are connection problems.
586 new Thread(new Runnable()
591 Cache.log.debug("Starting news thread.");
592 jvnews = new BlogReader(me);
593 showNews.setVisible(true);
594 Cache.log.debug("Completed news thread.");
599 public void getIdentifiersOrgData()
601 // Thread off the identifiers fetcher
602 new Thread(new Runnable()
607 Cache.log.debug("Downloading data from identifiers.org");
610 UrlDownloadClient.download(IdOrgSettings.getUrl(),
611 IdOrgSettings.getDownloadLocation());
612 } catch (IOException e)
614 Cache.log.debug("Exception downloading identifiers.org data"
623 protected void showNews_actionPerformed(ActionEvent e)
625 showNews(showNews.isSelected());
628 void showNews(boolean visible)
630 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
631 showNews.setSelected(visible);
632 if (visible && !jvnews.isVisible())
634 new Thread(new Runnable()
639 long now = System.currentTimeMillis();
640 Desktop.instance.setProgressBar(
641 MessageManager.getString("status.refreshing_news"), now);
642 jvnews.refreshNews();
643 Desktop.instance.setProgressBar(null, now);
651 * recover the last known dimensions for a jalview window
654 * - empty string is desktop, all other windows have unique prefix
655 * @return null or last known dimensions scaled to current geometry (if last
656 * window geom was known)
658 Rectangle getLastKnownDimensions(String windowName)
660 // TODO: lock aspect ratio for scaling desktop Bug #0058199
661 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
662 String x = Cache.getProperty(windowName + "SCREEN_X");
663 String y = Cache.getProperty(windowName + "SCREEN_Y");
665 .getProperty(windowName + "SCREEN_WIDTH");
666 String height = Cache
667 .getProperty(windowName + "SCREEN_HEIGHT");
668 if ((x != null) && (y != null) && (width != null) && (height != null))
670 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
671 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
672 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
674 // attempt #1 - try to cope with change in screen geometry - this
675 // version doesn't preserve original jv aspect ratio.
676 // take ratio of current screen size vs original screen size.
677 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
678 Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
679 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
680 Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
681 // rescale the bounds depending upon the current screen geometry.
682 ix = (int) (ix * sw);
683 iw = (int) (iw * sw);
684 iy = (int) (iy * sh);
685 ih = (int) (ih * sh);
686 while (ix >= screenSize.width)
689 "Window geometry location recall error: shifting horizontal to within screenbounds.");
690 ix -= screenSize.width;
692 while (iy >= screenSize.height)
695 "Window geometry location recall error: shifting vertical to within screenbounds.");
696 iy -= screenSize.height;
699 "Got last known dimensions for " + windowName + ": x:" + ix
700 + " y:" + iy + " width:" + iw + " height:" + ih);
702 // return dimensions for new instance
703 return new Rectangle(ix, iy, iw, ih);
708 void showPasteMenu(int x, int y)
710 JPopupMenu popup = new JPopupMenu();
711 JMenuItem item = new JMenuItem(
712 MessageManager.getString("label.paste_new_window"));
713 item.addActionListener(new ActionListener()
716 public void actionPerformed(ActionEvent evt)
723 popup.show(this, x, y);
730 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
731 Transferable contents = c.getContents(this);
733 if (contents != null)
735 String file = (String) contents
736 .getTransferData(DataFlavor.stringFlavor);
738 FileFormatI format = new IdentifyFile().identify(file,
739 DataSourceType.PASTE);
741 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
744 } catch (Exception ex)
747 "Unable to paste alignment from system clipboard:\n" + ex);
752 * Adds and opens the given frame to the desktop
763 public static synchronized void addInternalFrame(
764 final JInternalFrame frame, String title, int w, int h)
766 addInternalFrame(frame, title, true, w, h, true, false);
770 * Add an internal frame to the Jalview desktop
777 * When true, display frame immediately, otherwise, caller must call
778 * setVisible themselves.
784 public static synchronized void addInternalFrame(
785 final JInternalFrame frame, String title, boolean makeVisible,
788 addInternalFrame(frame, title, makeVisible, w, h, true, false);
792 * Add an internal frame to the Jalview desktop and make it visible
805 public static synchronized void addInternalFrame(
806 final JInternalFrame frame, String title, int w, int h,
809 addInternalFrame(frame, title, true, w, h, resizable, false);
813 * Add an internal frame to the Jalview desktop
820 * When true, display frame immediately, otherwise, caller must call
821 * setVisible themselves.
828 * @param ignoreMinSize
829 * Do not set the default minimum size for frame
831 public static synchronized void addInternalFrame(
832 final JInternalFrame frame, String title, boolean makeVisible,
833 int w, int h, boolean resizable, boolean ignoreMinSize)
836 // TODO: allow callers to determine X and Y position of frame (eg. via
838 // TODO: consider fixing method to update entries in the window submenu with
839 // the current window title
841 frame.setTitle(title);
842 if (frame.getWidth() < 1 || frame.getHeight() < 1)
846 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
847 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
848 // IF JALVIEW IS RUNNING HEADLESS
849 // ///////////////////////////////////////////////
850 if (instance == null || (System.getProperty("java.awt.headless") != null
851 && System.getProperty("java.awt.headless").equals("true")))
860 frame.setMinimumSize(
861 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
863 // Set default dimension for Alignment Frame window.
864 // The Alignment Frame window could be added from a number of places,
866 // I did this here in order not to miss out on any Alignment frame.
867 if (frame instanceof AlignFrame)
869 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
870 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
874 frame.setVisible(makeVisible);
875 frame.setClosable(true);
876 frame.setResizable(resizable);
877 frame.setMaximizable(resizable);
878 frame.setIconifiable(resizable);
879 frame.setOpaque(Platform.isJS());
881 if (frame.getX() < 1 && frame.getY() < 1)
883 frame.setLocation(xOffset * openFrameCount,
884 yOffset * ((openFrameCount - 1) % 10) + yOffset);
888 * add an entry for the new frame in the Window menu
889 * (and remove it when the frame is closed)
891 final JMenuItem menuItem = new JMenuItem(title);
892 frame.addInternalFrameListener(new InternalFrameAdapter()
895 public void internalFrameActivated(InternalFrameEvent evt)
897 JInternalFrame itf = desktop.getSelectedFrame();
900 if (itf instanceof AlignFrame)
902 Jalview.setCurrentAlignFrame((AlignFrame) itf);
909 public void internalFrameClosed(InternalFrameEvent evt)
911 PaintRefresher.RemoveComponent(frame);
914 * defensive check to prevent frames being
915 * added half off the window
917 if (openFrameCount > 0)
923 * ensure no reference to alignFrame retained by menu item listener
925 if (menuItem.getActionListeners().length > 0)
927 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
929 windowMenu.remove(menuItem);
933 menuItem.addActionListener(new ActionListener()
936 public void actionPerformed(ActionEvent e)
940 frame.setSelected(true);
941 frame.setIcon(false);
942 } catch (java.beans.PropertyVetoException ex)
944 // System.err.println(ex.toString());
949 setKeyBindings(frame);
953 windowMenu.add(menuItem);
958 frame.setSelected(true);
959 frame.requestFocus();
960 } catch (java.beans.PropertyVetoException ve)
962 } catch (java.lang.ClassCastException cex)
965 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
971 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
976 private static void setKeyBindings(JInternalFrame frame)
978 @SuppressWarnings("serial")
979 final Action closeAction = new AbstractAction()
982 public void actionPerformed(ActionEvent e)
989 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
991 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
992 InputEvent.CTRL_DOWN_MASK);
993 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
994 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
996 InputMap inputMap = frame
997 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
998 String ctrlW = ctrlWKey.toString();
999 inputMap.put(ctrlWKey, ctrlW);
1000 inputMap.put(cmdWKey, ctrlW);
1002 ActionMap actionMap = frame.getActionMap();
1003 actionMap.put(ctrlW, closeAction);
1007 public void lostOwnership(Clipboard clipboard, Transferable contents)
1011 Desktop.jalviewClipboard = null;
1014 internalCopy = false;
1018 public void dragEnter(DropTargetDragEvent evt)
1023 public void dragExit(DropTargetEvent evt)
1028 public void dragOver(DropTargetDragEvent evt)
1033 public void dropActionChanged(DropTargetDragEvent evt)
1044 public void drop(DropTargetDropEvent evt)
1046 boolean success = true;
1047 // JAL-1552 - acceptDrop required before getTransferable call for
1048 // Java's Transferable for native dnd
1049 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1050 Transferable t = evt.getTransferable();
1051 List<Object> files = new ArrayList<>();
1052 List<DataSourceType> protocols = new ArrayList<>();
1056 Desktop.transferFromDropTarget(files, protocols, evt, t);
1057 } catch (Exception e)
1059 e.printStackTrace();
1067 for (int i = 0; i < files.size(); i++)
1069 // BH 2018 File or String
1070 Object file = files.get(i);
1071 String fileName = file.toString();
1072 DataSourceType protocol = (protocols == null)
1073 ? DataSourceType.FILE
1075 FileFormatI format = null;
1077 if (fileName.endsWith(".jar"))
1079 format = FileFormat.Jalview;
1084 format = new IdentifyFile().identify(file, protocol);
1086 if (file instanceof File)
1088 Platform.cacheFileData((File) file);
1090 new FileLoader().LoadFile(null, file, protocol, format);
1093 } catch (Exception ex)
1098 evt.dropComplete(success); // need this to ensure input focus is properly
1099 // transfered to any new windows created
1109 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1111 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1112 JalviewFileChooser chooser = JalviewFileChooser
1113 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, BackupFiles.getEnabled());
1115 chooser.setFileView(new JalviewFileView());
1116 chooser.setDialogTitle(
1117 MessageManager.getString("label.open_local_file"));
1118 chooser.setToolTipText(MessageManager.getString("action.open"));
1120 chooser.setResponseHandler(0, new Runnable()
1125 File selectedFile = chooser.getSelectedFile();
1126 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1128 FileFormatI format = chooser.getSelectedFormat();
1131 * Call IdentifyFile to verify the file contains what its extension implies.
1132 * Skip this step for dynamically added file formats, because
1133 * IdentifyFile does not know how to recognise them.
1135 if (FileFormats.getInstance().isIdentifiable(format))
1139 format = new IdentifyFile().identify(selectedFile,
1140 DataSourceType.FILE);
1141 } catch (FileFormatException e)
1143 // format = null; //??
1147 new FileLoader().LoadFile(viewport, selectedFile,
1148 DataSourceType.FILE, format);
1151 chooser.showOpenDialog(this);
1155 * Shows a dialog for input of a URL at which to retrieve alignment data
1160 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1162 // This construct allows us to have a wider textfield
1164 JLabel label = new JLabel(
1165 MessageManager.getString("label.input_file_url"));
1167 JPanel panel = new JPanel(new GridLayout(2, 1));
1171 * the URL to fetch is
1172 * Java: an editable combobox with history
1173 * JS: (pending JAL-3038) a plain text field
1176 String urlBase = "http://www.";
1177 if (Platform.isJS())
1179 history = new JTextField(urlBase, 35);
1188 JComboBox<String> asCombo = new JComboBox<>();
1189 asCombo.setPreferredSize(new Dimension(400, 20));
1190 asCombo.setEditable(true);
1191 asCombo.addItem(urlBase);
1192 String historyItems = Cache.getProperty("RECENT_URL");
1193 if (historyItems != null)
1195 for (String token : historyItems.split("\\t"))
1197 asCombo.addItem(token);
1204 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1205 MessageManager.getString("action.cancel") };
1206 Runnable action = new Runnable()
1211 @SuppressWarnings("unchecked")
1212 String url = (history instanceof JTextField
1213 ? ((JTextField) history).getText()
1214 : ((JComboBox<String>) history).getSelectedItem()
1217 if (url.toLowerCase().endsWith(".jar"))
1219 if (viewport != null)
1221 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1222 FileFormat.Jalview);
1226 new FileLoader().LoadFile(url, DataSourceType.URL,
1227 FileFormat.Jalview);
1232 FileFormatI format = null;
1235 format = new IdentifyFile().identify(url, DataSourceType.URL);
1236 } catch (FileFormatException e)
1238 // TODO revise error handling, distinguish between
1239 // URL not found and response not valid
1244 String msg = MessageManager
1245 .formatMessage("label.couldnt_locate", url);
1246 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1247 MessageManager.getString("label.url_not_found"),
1248 JvOptionPane.WARNING_MESSAGE);
1253 if (viewport != null)
1255 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1260 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1265 String dialogOption = MessageManager
1266 .getString("label.input_alignment_from_url");
1267 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1268 .showInternalDialog(panel, dialogOption,
1269 JvOptionPane.YES_NO_CANCEL_OPTION,
1270 JvOptionPane.PLAIN_MESSAGE, null, options,
1271 MessageManager.getString("action.ok"));
1275 * Opens the CutAndPaste window for the user to paste an alignment in to
1278 * - if not null, the pasted alignment is added to the current
1279 * alignment; if null, to a new alignment window
1282 public void inputTextboxMenuItem_actionPerformed(
1283 AlignmentViewPanel viewPanel)
1285 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1286 cap.setForInput(viewPanel);
1287 Desktop.addInternalFrame(cap,
1288 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1298 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1299 Cache.setProperty("SCREENGEOMETRY_WIDTH",
1301 Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1302 screen.height + "");
1303 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1304 getWidth(), getHeight()));
1306 if (jconsole != null)
1308 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1309 jconsole.stopConsole();
1313 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1316 if (dialogExecutor != null)
1318 dialogExecutor.shutdownNow();
1320 closeAll_actionPerformed(null);
1322 if (groovyConsole != null)
1324 // suppress a possible repeat prompt to save script
1325 groovyConsole.setDirty(false);
1326 groovyConsole.exit();
1331 private void storeLastKnownDimensions(String string, Rectangle jc)
1333 Cache.log.debug("Storing last known dimensions for "
1334 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1335 + " height:" + jc.height);
1337 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1338 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1339 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1340 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1350 public void aboutMenuItem_actionPerformed(ActionEvent e)
1352 // StringBuffer message = getAboutMessage(false);
1353 // JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1355 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1356 new Thread(new Runnable()
1361 new SplashScreen(true);
1366 public StringBuffer getAboutMessage(boolean shortv)
1368 StringBuffer message = new StringBuffer();
1369 message.append("<html>");
1372 message.append("<h1><strong>Version: "
1373 + Cache.getProperty("VERSION")
1374 + "</strong></h1>");
1375 message.append("<strong>Built: <em>"
1376 + Cache.getDefault("BUILD_DATE", "unknown") + "</em> from "
1377 + Cache.getBuildDetailsForSplash()
1384 message.append("<strong>Version "
1385 + Cache.getProperty("VERSION")
1386 + "; last updated: "
1387 + Cache.getDefault("BUILD_DATE", "unknown"));
1390 if (Cache.getDefault("LATEST_VERSION", "Checking")
1391 .equals("Checking"))
1393 // JBP removed this message for 2.11: May be reinstated in future version
1394 // message.append("<br>...Checking latest version...</br>");
1396 else if (!Cache.getDefault("LATEST_VERSION", "Checking")
1397 .equals(Cache.getProperty("VERSION")))
1399 boolean red = false;
1400 if (Cache.getProperty("VERSION").toLowerCase()
1401 .indexOf("automated build") == -1)
1404 // Displayed when code version and jnlp version do not match and code
1405 // version is not a development build
1406 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1409 message.append("<br>!! Version "
1410 + Cache.getDefault("LATEST_VERSION",
1412 + " is available for download from "
1413 + Cache.getDefault("www.jalview.org",
1414 "http://www.jalview.org")
1418 message.append("</div>");
1421 message.append("<br>Authors: " + Cache.getDefault(
1423 "The Jalview Authors (See AUTHORS file for current list)")
1424 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1425 + "<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"
1426 + "<br><br>If you use Jalview, please cite:"
1427 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1428 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1429 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1435 * Action on requesting Help documentation
1438 public void documentationMenuItem_actionPerformed()
1442 if (Platform.isJS())
1444 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1453 Help.showHelpWindow();
1455 } catch (Exception ex)
1457 System.err.println("Error opening help: " + ex.getMessage());
1462 public void closeAll_actionPerformed(ActionEvent e)
1464 // TODO show a progress bar while closing?
1465 JInternalFrame[] frames = desktop.getAllFrames();
1466 for (int i = 0; i < frames.length; i++)
1470 frames[i].setClosed(true);
1471 } catch (java.beans.PropertyVetoException ex)
1475 Jalview.setCurrentAlignFrame(null);
1476 System.out.println("ALL CLOSED");
1479 * reset state of singleton objects as appropriate (clear down session state
1480 * when all windows are closed)
1482 StructureSelectionManager ssm = StructureSelectionManager
1483 .getStructureSelectionManager(this);
1491 public void raiseRelated_actionPerformed(ActionEvent e)
1493 reorderAssociatedWindows(false, false);
1497 public void minimizeAssociated_actionPerformed(ActionEvent e)
1499 reorderAssociatedWindows(true, false);
1502 void closeAssociatedWindows()
1504 reorderAssociatedWindows(false, true);
1510 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1514 protected void garbageCollect_actionPerformed(ActionEvent e)
1516 // We simply collect the garbage
1517 Cache.log.debug("Collecting garbage...");
1519 Cache.log.debug("Finished garbage collection.");
1526 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1530 protected void showMemusage_actionPerformed(ActionEvent e)
1532 desktop.showMemoryUsage(showMemusage.isSelected());
1539 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1543 protected void showConsole_actionPerformed(ActionEvent e)
1545 showConsole(showConsole.isSelected());
1548 Console jconsole = null;
1551 * control whether the java console is visible or not
1555 void showConsole(boolean selected)
1557 // TODO: decide if we should update properties file
1558 if (jconsole != null) // BH 2018
1560 showConsole.setSelected(selected);
1561 Cache.setProperty("SHOW_JAVA_CONSOLE",
1562 Boolean.valueOf(selected).toString());
1563 jconsole.setVisible(selected);
1567 void reorderAssociatedWindows(boolean minimize, boolean close)
1569 JInternalFrame[] frames = desktop.getAllFrames();
1570 if (frames == null || frames.length < 1)
1575 AlignmentViewport source = null, target = null;
1576 if (frames[0] instanceof AlignFrame)
1578 source = ((AlignFrame) frames[0]).getCurrentView();
1580 else if (frames[0] instanceof TreePanel)
1582 source = ((TreePanel) frames[0]).getViewPort();
1584 else if (frames[0] instanceof PCAPanel)
1586 source = ((PCAPanel) frames[0]).av;
1588 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1590 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1595 for (int i = 0; i < frames.length; i++)
1598 if (frames[i] == null)
1602 if (frames[i] instanceof AlignFrame)
1604 target = ((AlignFrame) frames[i]).getCurrentView();
1606 else if (frames[i] instanceof TreePanel)
1608 target = ((TreePanel) frames[i]).getViewPort();
1610 else if (frames[i] instanceof PCAPanel)
1612 target = ((PCAPanel) frames[i]).av;
1614 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1616 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1619 if (source == target)
1625 frames[i].setClosed(true);
1629 frames[i].setIcon(minimize);
1632 frames[i].toFront();
1636 } catch (java.beans.PropertyVetoException ex)
1651 protected void preferences_actionPerformed(ActionEvent e)
1657 * Prompts the user to choose a file and then saves the Jalview state as a
1658 * Jalview project file
1661 public void saveState_actionPerformed()
1663 saveState_actionPerformed(false);
1666 public void saveState_actionPerformed(boolean saveAs)
1668 java.io.File projectFile = getProjectFile();
1669 // autoSave indicates we already have a file and don't need to ask
1670 boolean autoSave = projectFile != null && !saveAs
1671 && BackupFiles.getEnabled();
1673 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1674 // saveAs="+saveAs+", Backups
1675 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1677 boolean approveSave = false;
1680 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1683 chooser.setFileView(new JalviewFileView());
1684 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1686 int value = chooser.showSaveDialog(this);
1688 if (value == JalviewFileChooser.APPROVE_OPTION)
1690 projectFile = chooser.getSelectedFile();
1691 setProjectFile(projectFile);
1696 if (approveSave || autoSave)
1698 final Desktop me = this;
1699 final java.io.File chosenFile = projectFile;
1700 new Thread(new Runnable()
1705 // TODO: refactor to Jalview desktop session controller action.
1706 setProgressBar(MessageManager.formatMessage(
1707 "label.saving_jalview_project", new Object[]
1708 { chosenFile.getName() }), chosenFile.hashCode());
1709 Cache.setProperty("LAST_DIRECTORY",
1710 chosenFile.getParent());
1711 // TODO catch and handle errors for savestate
1712 // TODO prevent user from messing with the Desktop whilst we're saving
1715 boolean doBackup = BackupFiles.getEnabled();
1716 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1718 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1722 backupfiles.setWriteSuccess(true);
1723 backupfiles.rollBackupsAndRenameTempFile();
1725 } catch (OutOfMemoryError oom)
1727 new OOMWarning("Whilst saving current state to "
1728 + chosenFile.getName(), oom);
1729 } catch (Exception ex)
1731 Cache.log.error("Problems whilst trying to save to "
1732 + chosenFile.getName(), ex);
1733 JvOptionPane.showMessageDialog(me,
1734 MessageManager.formatMessage(
1735 "label.error_whilst_saving_current_state_to",
1737 { chosenFile.getName() }),
1738 MessageManager.getString("label.couldnt_save_project"),
1739 JvOptionPane.WARNING_MESSAGE);
1741 setProgressBar(null, chosenFile.hashCode());
1748 public void saveAsState_actionPerformed(ActionEvent e)
1750 saveState_actionPerformed(true);
1753 private void setProjectFile(File choice)
1755 this.projectFile = choice;
1758 public File getProjectFile()
1760 return this.projectFile;
1764 * Shows a file chooser dialog and tries to read in the selected file as a
1768 public void loadState_actionPerformed()
1770 final String[] suffix = new String[] { "jvp", "jar" };
1771 final String[] desc = new String[] { "Jalview Project",
1772 "Jalview Project (old)" };
1773 JalviewFileChooser chooser = new JalviewFileChooser(
1774 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1775 "Jalview Project", true, BackupFiles.getEnabled()); // last two booleans: allFiles,
1777 chooser.setFileView(new JalviewFileView());
1778 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1779 chooser.setResponseHandler(0, new Runnable()
1784 File selectedFile = chooser.getSelectedFile();
1785 setProjectFile(selectedFile);
1786 String choice = selectedFile.getAbsolutePath();
1787 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1788 new Thread(new Runnable()
1795 new Jalview2XML().loadJalviewAlign(choice);
1796 } catch (OutOfMemoryError oom)
1798 new OOMWarning("Whilst loading project from " + choice, oom);
1799 } catch (Exception ex)
1802 "Problems whilst loading project from " + choice, ex);
1803 JvOptionPane.showMessageDialog(Desktop.desktop,
1804 MessageManager.formatMessage(
1805 "label.error_whilst_loading_project_from",
1808 MessageManager.getString("label.couldnt_load_project"),
1809 JvOptionPane.WARNING_MESSAGE);
1816 chooser.showOpenDialog(this);
1820 public void inputSequence_actionPerformed(ActionEvent e)
1822 new SequenceFetcher(this);
1825 JPanel progressPanel;
1827 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1829 public void startLoading(final Object fileName)
1831 if (fileLoadingCount == 0)
1833 fileLoadingPanels.add(addProgressPanel(MessageManager
1834 .formatMessage("label.loading_file", new Object[]
1840 private JPanel addProgressPanel(String string)
1842 if (progressPanel == null)
1844 progressPanel = new JPanel(new GridLayout(1, 1));
1845 totalProgressCount = 0;
1846 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1848 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1849 JProgressBar progressBar = new JProgressBar();
1850 progressBar.setIndeterminate(true);
1852 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1854 thisprogress.add(progressBar, BorderLayout.CENTER);
1855 progressPanel.add(thisprogress);
1856 ((GridLayout) progressPanel.getLayout()).setRows(
1857 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1858 ++totalProgressCount;
1859 instance.validate();
1860 return thisprogress;
1863 int totalProgressCount = 0;
1865 private void removeProgressPanel(JPanel progbar)
1867 if (progressPanel != null)
1869 synchronized (progressPanel)
1871 progressPanel.remove(progbar);
1872 GridLayout gl = (GridLayout) progressPanel.getLayout();
1873 gl.setRows(gl.getRows() - 1);
1874 if (--totalProgressCount < 1)
1876 this.getContentPane().remove(progressPanel);
1877 progressPanel = null;
1884 public void stopLoading()
1887 if (fileLoadingCount < 1)
1889 while (fileLoadingPanels.size() > 0)
1891 removeProgressPanel(fileLoadingPanels.remove(0));
1893 fileLoadingPanels.clear();
1894 fileLoadingCount = 0;
1899 public static int getViewCount(String alignmentId)
1901 AlignmentViewport[] aps = getViewports(alignmentId);
1902 return (aps == null) ? 0 : aps.length;
1907 * @param alignmentId
1908 * - if null, all sets are returned
1909 * @return all AlignmentPanels concerning the alignmentId sequence set
1911 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1913 if (Desktop.desktop == null)
1915 // no frames created and in headless mode
1916 // TODO: verify that frames are recoverable when in headless mode
1919 List<AlignmentPanel> aps = new ArrayList<>();
1920 AlignFrame[] frames = getAlignFrames();
1925 for (AlignFrame af : frames)
1927 for (AlignmentPanel ap : af.alignPanels)
1929 if (alignmentId == null
1930 || alignmentId.equals(ap.av.getSequenceSetId()))
1936 if (aps.size() == 0)
1940 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1945 * get all the viewports on an alignment.
1947 * @param sequenceSetId
1948 * unique alignment id (may be null - all viewports returned in that
1950 * @return all viewports on the alignment bound to sequenceSetId
1952 public static AlignmentViewport[] getViewports(String sequenceSetId)
1954 List<AlignmentViewport> viewp = new ArrayList<>();
1955 if (desktop != null)
1957 AlignFrame[] frames = Desktop.getAlignFrames();
1959 for (AlignFrame afr : frames)
1961 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1962 .equals(sequenceSetId))
1964 if (afr.alignPanels != null)
1966 for (AlignmentPanel ap : afr.alignPanels)
1968 if (sequenceSetId == null
1969 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1977 viewp.add(afr.getViewport());
1981 if (viewp.size() > 0)
1983 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1990 * Explode the views in the given frame into separate AlignFrame
1994 public static void explodeViews(AlignFrame af)
1996 int size = af.alignPanels.size();
2002 for (int i = 0; i < size; i++)
2004 AlignmentPanel ap = af.alignPanels.get(i);
2005 AlignFrame newaf = new AlignFrame(ap);
2008 * Restore the view's last exploded frame geometry if known. Multiple
2009 * views from one exploded frame share and restore the same (frame)
2010 * position and size.
2012 Rectangle geometry = ap.av.getExplodedGeometry();
2013 if (geometry != null)
2015 newaf.setBounds(geometry);
2018 ap.av.setGatherViewsHere(false);
2020 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2021 AlignFrame.DEFAULT_HEIGHT);
2024 af.alignPanels.clear();
2025 af.closeMenuItem_actionPerformed(true);
2030 * Gather expanded views (separate AlignFrame's) with the same sequence set
2031 * identifier back in to this frame as additional views, and close the expanded
2032 * views. Note the expanded frames may themselves have multiple views. We take
2037 public void gatherViews(AlignFrame source)
2039 source.viewport.setGatherViewsHere(true);
2040 source.viewport.setExplodedGeometry(source.getBounds());
2041 JInternalFrame[] frames = desktop.getAllFrames();
2042 String viewId = source.viewport.getSequenceSetId();
2044 for (int t = 0; t < frames.length; t++)
2046 if (frames[t] instanceof AlignFrame && frames[t] != source)
2048 AlignFrame af = (AlignFrame) frames[t];
2049 boolean gatherThis = false;
2050 for (int a = 0; a < af.alignPanels.size(); a++)
2052 AlignmentPanel ap = af.alignPanels.get(a);
2053 if (viewId.equals(ap.av.getSequenceSetId()))
2056 ap.av.setGatherViewsHere(false);
2057 ap.av.setExplodedGeometry(af.getBounds());
2058 source.addAlignmentPanel(ap, false);
2064 af.alignPanels.clear();
2065 af.closeMenuItem_actionPerformed(true);
2071 public JInternalFrame[] getAllFrames()
2073 return desktop.getAllFrames();
2077 * Checks the given url to see if it gives a response indicating that the user
2078 * should be informed of a new questionnaire.
2082 public void checkForQuestionnaire(String url)
2084 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2085 // javax.swing.SwingUtilities.invokeLater(jvq);
2086 new Thread(jvq).start();
2089 public void checkURLLinks()
2091 // Thread off the URL link checker
2092 addDialogThread(new Runnable()
2097 if (Cache.getDefault("CHECKURLLINKS", true))
2099 // check what the actual links are - if it's just the default don't
2100 // bother with the warning
2101 List<String> links = Preferences.sequenceUrlLinks
2104 // only need to check links if there is one with a
2105 // SEQUENCE_ID which is not the default EMBL_EBI link
2106 ListIterator<String> li = links.listIterator();
2107 boolean check = false;
2108 List<JLabel> urls = new ArrayList<>();
2109 while (li.hasNext())
2111 String link = li.next();
2112 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2113 && !UrlConstants.isDefaultString(link))
2116 int barPos = link.indexOf("|");
2117 String urlMsg = barPos == -1 ? link
2118 : link.substring(0, barPos) + ": "
2119 + link.substring(barPos + 1);
2120 urls.add(new JLabel(urlMsg));
2128 // ask user to check in case URL links use old style tokens
2129 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2130 JPanel msgPanel = new JPanel();
2131 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2132 msgPanel.add(Box.createVerticalGlue());
2133 JLabel msg = new JLabel(MessageManager
2134 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2135 JLabel msg2 = new JLabel(MessageManager
2136 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2138 for (JLabel url : urls)
2144 final JCheckBox jcb = new JCheckBox(
2145 MessageManager.getString("label.do_not_display_again"));
2146 jcb.addActionListener(new ActionListener()
2149 public void actionPerformed(ActionEvent e)
2151 // update Cache settings for "don't show this again"
2152 boolean showWarningAgain = !jcb.isSelected();
2153 Cache.setProperty("CHECKURLLINKS",
2154 Boolean.valueOf(showWarningAgain).toString());
2159 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2161 .getString("label.SEQUENCE_ID_no_longer_used"),
2162 JvOptionPane.WARNING_MESSAGE);
2169 * Proxy class for JDesktopPane which optionally displays the current memory
2170 * usage and highlights the desktop area with a red bar if free memory runs low.
2174 public class MyDesktopPane extends JDesktopPane
2177 private static final float ONE_MB = 1048576f;
2179 boolean showMemoryUsage = false;
2183 java.text.NumberFormat df;
2185 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2188 public MyDesktopPane(boolean showMemoryUsage)
2190 showMemoryUsage(showMemoryUsage);
2193 public void showMemoryUsage(boolean showMemory)
2195 this.showMemoryUsage = showMemory;
2198 Thread worker = new Thread(this);
2204 public boolean isShowMemoryUsage()
2206 return showMemoryUsage;
2212 df = java.text.NumberFormat.getNumberInstance();
2213 df.setMaximumFractionDigits(2);
2214 runtime = Runtime.getRuntime();
2216 while (showMemoryUsage)
2220 maxMemory = runtime.maxMemory() / ONE_MB;
2221 allocatedMemory = runtime.totalMemory() / ONE_MB;
2222 freeMemory = runtime.freeMemory() / ONE_MB;
2223 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2225 percentUsage = (totalFreeMemory / maxMemory) * 100;
2227 // if (percentUsage < 20)
2229 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2231 // instance.set.setBorder(border1);
2234 // sleep after showing usage
2236 } catch (Exception ex)
2238 ex.printStackTrace();
2244 public void paintComponent(Graphics g)
2246 if (showMemoryUsage && g != null && df != null)
2248 if (percentUsage < 20)
2250 g.setColor(Color.red);
2252 FontMetrics fm = g.getFontMetrics();
2255 g.drawString(MessageManager.formatMessage("label.memory_stats",
2257 { df.format(totalFreeMemory), df.format(maxMemory),
2258 df.format(percentUsage) }),
2259 10, getHeight() - fm.getHeight());
2266 * Accessor method to quickly get all the AlignmentFrames loaded.
2268 * @return an array of AlignFrame, or null if none found
2270 public static AlignFrame[] getAlignFrames()
2272 if (Jalview.isHeadlessMode())
2274 // Desktop.desktop is null in headless mode
2275 return new AlignFrame[] { Jalview.currentAlignFrame };
2278 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2284 List<AlignFrame> avp = new ArrayList<>();
2286 for (int i = frames.length - 1; i > -1; i--)
2288 if (frames[i] instanceof AlignFrame)
2290 avp.add((AlignFrame) frames[i]);
2292 else if (frames[i] instanceof SplitFrame)
2295 * Also check for a split frame containing an AlignFrame
2297 GSplitFrame sf = (GSplitFrame) frames[i];
2298 if (sf.getTopFrame() instanceof AlignFrame)
2300 avp.add((AlignFrame) sf.getTopFrame());
2302 if (sf.getBottomFrame() instanceof AlignFrame)
2304 avp.add((AlignFrame) sf.getBottomFrame());
2308 if (avp.size() == 0)
2312 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2317 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2321 public GStructureViewer[] getJmols()
2323 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2329 List<GStructureViewer> avp = new ArrayList<>();
2331 for (int i = frames.length - 1; i > -1; i--)
2333 if (frames[i] instanceof AppJmol)
2335 GStructureViewer af = (GStructureViewer) frames[i];
2339 if (avp.size() == 0)
2343 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2348 * Add Groovy Support to Jalview
2351 public void groovyShell_actionPerformed()
2355 openGroovyConsole();
2356 } catch (Exception ex)
2358 Cache.log.error("Groovy Shell Creation failed.", ex);
2359 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2361 MessageManager.getString("label.couldnt_create_groovy_shell"),
2362 MessageManager.getString("label.groovy_support_failed"),
2363 JvOptionPane.ERROR_MESSAGE);
2368 * Open the Groovy console
2370 void openGroovyConsole()
2372 if (groovyConsole == null)
2374 groovyConsole = new groovy.ui.Console();
2375 groovyConsole.setVariable("Jalview", this);
2376 groovyConsole.run();
2379 * We allow only one console at a time, so that AlignFrame menu option
2380 * 'Calculate | Run Groovy script' is unambiguous.
2381 * Disable 'Groovy Console', and enable 'Run script', when the console is
2382 * opened, and the reverse when it is closed
2384 Window window = (Window) groovyConsole.getFrame();
2385 window.addWindowListener(new WindowAdapter()
2388 public void windowClosed(WindowEvent e)
2391 * rebind CMD-Q from Groovy Console to Jalview Quit
2394 enableExecuteGroovy(false);
2400 * show Groovy console window (after close and reopen)
2402 ((Window) groovyConsole.getFrame()).setVisible(true);
2405 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2406 * and disable opening a second console
2408 enableExecuteGroovy(true);
2412 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2415 protected void addQuitHandler()
2417 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2418 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2419 jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
2421 getRootPane().getActionMap().put("Quit", new AbstractAction()
2424 public void actionPerformed(ActionEvent e)
2432 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2435 * true if Groovy console is open
2437 public void enableExecuteGroovy(boolean enabled)
2440 * disable opening a second Groovy console
2441 * (or re-enable when the console is closed)
2443 groovyShell.setEnabled(!enabled);
2445 AlignFrame[] alignFrames = getAlignFrames();
2446 if (alignFrames != null)
2448 for (AlignFrame af : alignFrames)
2450 af.setGroovyEnabled(enabled);
2456 * Progress bars managed by the IProgressIndicator method.
2458 private Hashtable<Long, JPanel> progressBars;
2460 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2465 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2468 public void setProgressBar(String message, long id)
2470 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2472 if (progressBars == null)
2474 progressBars = new Hashtable<>();
2475 progressBarHandlers = new Hashtable<>();
2478 if (progressBars.get(Long.valueOf(id)) != null)
2480 JPanel panel = progressBars.remove(Long.valueOf(id));
2481 if (progressBarHandlers.contains(Long.valueOf(id)))
2483 progressBarHandlers.remove(Long.valueOf(id));
2485 removeProgressPanel(panel);
2489 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2496 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2497 * jalview.gui.IProgressIndicatorHandler)
2500 public void registerHandler(final long id,
2501 final IProgressIndicatorHandler handler)
2503 if (progressBarHandlers == null
2504 || !progressBars.containsKey(Long.valueOf(id)))
2506 throw new Error(MessageManager.getString(
2507 "error.call_setprogressbar_before_registering_handler"));
2509 progressBarHandlers.put(Long.valueOf(id), handler);
2510 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2511 if (handler.canCancel())
2513 JButton cancel = new JButton(
2514 MessageManager.getString("action.cancel"));
2515 final IProgressIndicator us = this;
2516 cancel.addActionListener(new ActionListener()
2520 public void actionPerformed(ActionEvent e)
2522 handler.cancelActivity(id);
2523 us.setProgressBar(MessageManager
2524 .formatMessage("label.cancelled_params", new Object[]
2525 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2529 progressPanel.add(cancel, BorderLayout.EAST);
2535 * @return true if any progress bars are still active
2538 public boolean operationInProgress()
2540 if (progressBars != null && progressBars.size() > 0)
2548 * This will return the first AlignFrame holding the given viewport instance. It
2549 * will break if there are more than one AlignFrames viewing a particular av.
2552 * @return alignFrame for viewport
2554 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2556 if (desktop != null)
2558 AlignmentPanel[] aps = getAlignmentPanels(
2559 viewport.getSequenceSetId());
2560 for (int panel = 0; aps != null && panel < aps.length; panel++)
2562 if (aps[panel] != null && aps[panel].av == viewport)
2564 return aps[panel].alignFrame;
2571 public VamsasApplication getVamsasApplication()
2573 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2579 * flag set if jalview GUI is being operated programmatically
2581 private boolean inBatchMode = false;
2584 * check if jalview GUI is being operated programmatically
2586 * @return inBatchMode
2588 public boolean isInBatchMode()
2594 * set flag if jalview GUI is being operated programmatically
2596 * @param inBatchMode
2598 public void setInBatchMode(boolean inBatchMode)
2600 this.inBatchMode = inBatchMode;
2603 public void startServiceDiscovery()
2605 startServiceDiscovery(false);
2608 public void startServiceDiscovery(boolean blocking)
2610 boolean alive = true;
2611 Thread t0 = null, t1 = null, t2 = null;
2612 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2615 // todo: changesupport handlers need to be transferred
2616 if (discoverer == null)
2618 discoverer = new jalview.ws.jws1.Discoverer();
2619 // register PCS handler for desktop.
2620 discoverer.addPropertyChangeListener(changeSupport);
2622 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2623 // until we phase out completely
2624 (t0 = new Thread(discoverer)).start();
2627 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2629 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2630 .startDiscoverer(changeSupport);
2634 // TODO: do rest service discovery
2643 } catch (Exception e)
2646 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2647 || (t3 != null && t3.isAlive())
2648 || (t0 != null && t0.isAlive());
2654 * called to check if the service discovery process completed successfully.
2658 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2660 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2662 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2663 .getErrorMessages();
2666 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2668 if (serviceChangedDialog == null)
2670 // only run if we aren't already displaying one of these.
2671 addDialogThread(serviceChangedDialog = new Runnable()
2678 * JalviewDialog jd =new JalviewDialog() {
2680 * @Override protected void cancelPressed() { // TODO
2681 * Auto-generated method stub
2683 * }@Override protected void okPressed() { // TODO
2684 * Auto-generated method stub
2686 * }@Override protected void raiseClosed() { // TODO
2687 * Auto-generated method stub
2689 * } }; jd.initDialogFrame(new
2690 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2691 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2692 * + " or mis-configured HTTP proxy settings.<br/>" +
2693 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2695 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2696 * ), true, true, "Web Service Configuration Problem", 450,
2699 * jd.waitForInput();
2701 JvOptionPane.showConfirmDialog(Desktop.desktop,
2702 new JLabel("<html><table width=\"450\"><tr><td>"
2703 + ermsg + "</td></tr></table>"
2704 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2705 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2706 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2707 + " Tools->Preferences dialog box to change them.</p></html>"),
2708 "Web Service Configuration Problem",
2709 JvOptionPane.DEFAULT_OPTION,
2710 JvOptionPane.ERROR_MESSAGE);
2711 serviceChangedDialog = null;
2720 "Errors reported by JABA discovery service. Check web services preferences.\n"
2727 private Runnable serviceChangedDialog = null;
2730 * start a thread to open a URL in the configured browser. Pops up a warning
2731 * dialog to the user if there is an exception when calling out to the browser
2736 public static void showUrl(final String url)
2738 showUrl(url, Desktop.instance);
2742 * Like showUrl but allows progress handler to be specified
2746 * (null) or object implementing IProgressIndicator
2748 public static void showUrl(final String url,
2749 final IProgressIndicator progress)
2751 new Thread(new Runnable()
2758 if (progress != null)
2760 progress.setProgressBar(MessageManager
2761 .formatMessage("status.opening_params", new Object[]
2762 { url }), this.hashCode());
2764 jalview.util.BrowserLauncher.openURL(url);
2765 } catch (Exception ex)
2767 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2769 .getString("label.web_browser_not_found_unix"),
2770 MessageManager.getString("label.web_browser_not_found"),
2771 JvOptionPane.WARNING_MESSAGE);
2773 ex.printStackTrace();
2775 if (progress != null)
2777 progress.setProgressBar(null, this.hashCode());
2783 public static WsParamSetManager wsparamManager = null;
2785 public static ParamManager getUserParameterStore()
2787 if (wsparamManager == null)
2789 wsparamManager = new WsParamSetManager();
2791 return wsparamManager;
2795 * static hyperlink handler proxy method for use by Jalview's internal windows
2799 public static void hyperlinkUpdate(HyperlinkEvent e)
2801 if (e.getEventType() == EventType.ACTIVATED)
2806 url = e.getURL().toString();
2807 Desktop.showUrl(url);
2808 } catch (Exception x)
2812 if (Cache.log != null)
2814 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2819 "Couldn't handle string " + url + " as a URL.");
2822 // ignore any exceptions due to dud links.
2829 * single thread that handles display of dialogs to user.
2831 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2834 * flag indicating if dialogExecutor should try to acquire a permit
2836 private volatile boolean dialogPause = true;
2841 private java.util.concurrent.Semaphore block = new Semaphore(0);
2843 private static groovy.ui.Console groovyConsole;
2846 * add another dialog thread to the queue
2850 public void addDialogThread(final Runnable prompter)
2852 dialogExecutor.submit(new Runnable()
2862 } catch (InterruptedException x)
2866 if (instance == null)
2872 SwingUtilities.invokeAndWait(prompter);
2873 } catch (Exception q)
2875 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2881 public void startDialogQueue()
2883 // set the flag so we don't pause waiting for another permit and semaphore
2884 // the current task to begin
2885 dialogPause = false;
2890 * Outputs an image of the desktop to file in EPS format, after prompting the
2891 * user for choice of Text or Lineart character rendering (unless a preference
2892 * has been set). The file name is generated as
2895 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2899 protected void snapShotWindow_actionPerformed(ActionEvent e)
2901 // currently the menu option to do this is not shown
2904 int width = getWidth();
2905 int height = getHeight();
2907 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2908 ImageWriterI writer = new ImageWriterI()
2911 public void exportImage(Graphics g) throws Exception
2914 Cache.log.info("Successfully written snapshot to file "
2915 + of.getAbsolutePath());
2918 String title = "View of desktop";
2919 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2921 exporter.doExport(of, this, width, height, title);
2925 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2926 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
2927 * location last time the view was expanded (if any). However it does not
2928 * remember the split pane divider location - this is set to match the
2929 * 'exploding' frame.
2933 public void explodeViews(SplitFrame sf)
2935 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2936 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2937 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2939 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2941 int viewCount = topPanels.size();
2948 * Processing in reverse order works, forwards order leaves the first panels
2949 * not visible. I don't know why!
2951 for (int i = viewCount - 1; i >= 0; i--)
2954 * Make new top and bottom frames. These take over the respective
2955 * AlignmentPanel objects, including their AlignmentViewports, so the
2956 * cdna/protein relationships between the viewports is carried over to the
2959 * explodedGeometry holds the (x, y) position of the previously exploded
2960 * SplitFrame, and the (width, height) of the AlignFrame component
2962 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
2963 AlignFrame newTopFrame = new AlignFrame(topPanel);
2964 newTopFrame.setSize(oldTopFrame.getSize());
2965 newTopFrame.setVisible(true);
2966 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
2967 .getExplodedGeometry();
2968 if (geometry != null)
2970 newTopFrame.setSize(geometry.getSize());
2973 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
2974 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
2975 newBottomFrame.setSize(oldBottomFrame.getSize());
2976 newBottomFrame.setVisible(true);
2977 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
2978 .getExplodedGeometry();
2979 if (geometry != null)
2981 newBottomFrame.setSize(geometry.getSize());
2984 topPanel.av.setGatherViewsHere(false);
2985 bottomPanel.av.setGatherViewsHere(false);
2986 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
2988 if (geometry != null)
2990 splitFrame.setLocation(geometry.getLocation());
2992 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
2996 * Clear references to the panels (now relocated in the new SplitFrames)
2997 * before closing the old SplitFrame.
3000 bottomPanels.clear();
3005 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3006 * back into the given SplitFrame as additional views. Note that the gathered
3007 * frames may themselves have multiple views.
3011 public void gatherViews(GSplitFrame source)
3014 * special handling of explodedGeometry for a view within a SplitFrame: - it
3015 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3016 * height) of the AlignFrame component
3018 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3019 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3020 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3021 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3022 myBottomFrame.viewport
3023 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3024 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3025 myTopFrame.viewport.setGatherViewsHere(true);
3026 myBottomFrame.viewport.setGatherViewsHere(true);
3027 String topViewId = myTopFrame.viewport.getSequenceSetId();
3028 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3030 JInternalFrame[] frames = desktop.getAllFrames();
3031 for (JInternalFrame frame : frames)
3033 if (frame instanceof SplitFrame && frame != source)
3035 SplitFrame sf = (SplitFrame) frame;
3036 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3037 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3038 boolean gatherThis = false;
3039 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3041 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3042 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3043 if (topViewId.equals(topPanel.av.getSequenceSetId())
3044 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3047 topPanel.av.setGatherViewsHere(false);
3048 bottomPanel.av.setGatherViewsHere(false);
3049 topPanel.av.setExplodedGeometry(
3050 new Rectangle(sf.getLocation(), topFrame.getSize()));
3051 bottomPanel.av.setExplodedGeometry(
3052 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3053 myTopFrame.addAlignmentPanel(topPanel, false);
3054 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3060 topFrame.getAlignPanels().clear();
3061 bottomFrame.getAlignPanels().clear();
3068 * The dust settles...give focus to the tab we did this from.
3070 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3073 public static groovy.ui.Console getGroovyConsole()
3075 return groovyConsole;
3079 * handles the payload of a drag and drop event.
3081 * TODO refactor to desktop utilities class
3084 * - Data source strings extracted from the drop event
3086 * - protocol for each data source extracted from the drop event
3090 * - the payload from the drop event
3093 public static void transferFromDropTarget(List<Object> files,
3094 List<DataSourceType> protocols, DropTargetDropEvent evt,
3095 Transferable t) throws Exception
3098 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3100 // DataFlavor[] flavors = t.getTransferDataFlavors();
3101 // for (int i = 0; i < flavors.length; i++) {
3102 // if (flavors[i].isFlavorJavaFileListType()) {
3103 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3104 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3105 // for (int j = 0; j < list.size(); j++) {
3106 // File file = (File) list.get(j);
3107 // byte[] data = getDroppedFileBytes(file);
3108 // fileName.setText(file.getName() + " - " + data.length + " " +
3109 // evt.getLocation());
3110 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3111 // target.setText(new String(data));
3113 // dtde.dropComplete(true);
3118 DataFlavor uriListFlavor = new DataFlavor(
3119 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3122 urlFlavour = new DataFlavor(
3123 "application/x-java-url; class=java.net.URL");
3124 } catch (ClassNotFoundException cfe)
3126 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3129 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3134 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3135 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3136 // means url may be null.
3139 protocols.add(DataSourceType.URL);
3140 files.add(url.toString());
3141 Cache.log.debug("Drop handled as URL dataflavor "
3142 + files.get(files.size() - 1));
3147 if (Platform.isAMacAndNotJS())
3150 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3153 } catch (Throwable ex)
3155 Cache.log.debug("URL drop handler failed.", ex);
3158 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3160 // Works on Windows and MacOSX
3161 Cache.log.debug("Drop handled as javaFileListFlavor");
3162 for (Object file : (List) t
3163 .getTransferData(DataFlavor.javaFileListFlavor))
3166 protocols.add(DataSourceType.FILE);
3171 // Unix like behaviour
3172 boolean added = false;
3174 if (t.isDataFlavorSupported(uriListFlavor))
3176 Cache.log.debug("Drop handled as uriListFlavor");
3177 // This is used by Unix drag system
3178 data = (String) t.getTransferData(uriListFlavor);
3182 // fallback to text: workaround - on OSX where there's a JVM bug
3183 Cache.log.debug("standard URIListFlavor failed. Trying text");
3184 // try text fallback
3185 DataFlavor textDf = new DataFlavor(
3186 "text/plain;class=java.lang.String");
3187 if (t.isDataFlavorSupported(textDf))
3189 data = (String) t.getTransferData(textDf);
3192 Cache.log.debug("Plain text drop content returned "
3193 + (data == null ? "Null - failed" : data));
3198 while (protocols.size() < files.size())
3200 Cache.log.debug("Adding missing FILE protocol for "
3201 + files.get(protocols.size()));
3202 protocols.add(DataSourceType.FILE);
3204 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3205 data, "\r\n"); st.hasMoreTokens();)
3208 String s = st.nextToken();
3209 if (s.startsWith("#"))
3211 // the line is a comment (as per the RFC 2483)
3214 java.net.URI uri = new java.net.URI(s);
3215 if (uri.getScheme().toLowerCase().startsWith("http"))
3217 protocols.add(DataSourceType.URL);
3218 files.add(uri.toString());
3222 // otherwise preserve old behaviour: catch all for file objects
3223 java.io.File file = new java.io.File(uri);
3224 protocols.add(DataSourceType.FILE);
3225 files.add(file.toString());
3230 if (Cache.log.isDebugEnabled())
3232 if (data == null || !added)
3235 if (t.getTransferDataFlavors() != null
3236 && t.getTransferDataFlavors().length > 0)
3239 "Couldn't resolve drop data. Here are the supported flavors:");
3240 for (DataFlavor fl : t.getTransferDataFlavors())
3243 "Supported transfer dataflavor: " + fl.toString());
3244 Object df = t.getTransferData(fl);
3247 Cache.log.debug("Retrieves: " + df);
3251 Cache.log.debug("Retrieved nothing");
3257 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3263 if (Platform.isWindowsAndNotJS())
3265 Cache.log.debug("Scanning dropped content for Windows Link Files");
3267 // resolve any .lnk files in the file drop
3268 for (int f = 0; f < files.size(); f++)
3270 String source = files.get(f).toString().toLowerCase();
3271 if (protocols.get(f).equals(DataSourceType.FILE)
3272 && (source.endsWith(".lnk") || source.endsWith(".url")
3273 || source.endsWith(".site")))
3277 Object obj = files.get(f);
3278 File lf = (obj instanceof File ? (File) obj
3279 : new File((String) obj));
3280 // process link file to get a URL
3281 Cache.log.debug("Found potential link file: " + lf);
3282 WindowsShortcut wscfile = new WindowsShortcut(lf);
3283 String fullname = wscfile.getRealFilename();
3284 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3285 files.set(f, fullname);
3286 Cache.log.debug("Parsed real filename " + fullname
3287 + " to extract protocol: " + protocols.get(f));
3288 } catch (Exception ex)
3291 "Couldn't parse " + files.get(f) + " as a link file.",
3300 * Sets the Preferences property for experimental features to True or False
3301 * depending on the state of the controlling menu item
3304 protected void showExperimental_actionPerformed(boolean selected)
3306 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3310 * Answers a (possibly empty) list of any structure viewer frames (currently for
3311 * either Jmol or Chimera) which are currently open. This may optionally be
3312 * restricted to viewers of a specified class, or viewers linked to a specified
3316 * if not null, only return viewers linked to this panel
3317 * @param structureViewerClass
3318 * if not null, only return viewers of this class
3321 public List<StructureViewerBase> getStructureViewers(
3322 AlignmentPanel apanel,
3323 Class<? extends StructureViewerBase> structureViewerClass)
3325 List<StructureViewerBase> result = new ArrayList<>();
3326 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3328 for (JInternalFrame frame : frames)
3330 if (frame instanceof StructureViewerBase)
3332 if (structureViewerClass == null
3333 || structureViewerClass.isInstance(frame))
3336 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3338 result.add((StructureViewerBase) frame);