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);
372 APQHandlers.setAPQHandlers(this);
373 } catch (Throwable t)
375 System.out.println("Error setting APQHandlers: " + t.toString());
376 // t.printStackTrace();
379 addWindowListener(new WindowAdapter()
383 public void windowClosing(WindowEvent ev)
389 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE",
392 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE",
394 desktop = new MyDesktopPane(selmemusage);
396 showMemusage.setSelected(selmemusage);
397 desktop.setBackground(Color.white);
399 getContentPane().setLayout(new BorderLayout());
400 // alternate config - have scrollbars - see notes in JAL-153
401 // JScrollPane sp = new JScrollPane();
402 // sp.getViewport().setView(desktop);
403 // getContentPane().add(sp, BorderLayout.CENTER);
405 // BH 2018 - just an experiment to try unclipped JInternalFrames.
408 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
411 getContentPane().add(desktop, BorderLayout.CENTER);
412 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
414 // This line prevents Windows Look&Feel resizing all new windows to maximum
415 // if previous window was maximised
416 desktop.setDesktopManager(new MyDesktopManager(
417 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
418 : Platform.isAMacAndNotJS()
419 ? new AquaInternalFrameManager(
420 desktop.getDesktopManager())
421 : desktop.getDesktopManager())));
423 Rectangle dims = getLastKnownDimensions("");
430 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
431 int xPos = Math.max(5, (screenSize.width - 900) / 2);
432 int yPos = Math.max(5, (screenSize.height - 650) / 2);
433 setBounds(xPos, yPos, 900, 650);
436 if (!Platform.isJS())
444 jconsole = new Console(this, showjconsole);
445 // add essential build information
446 jconsole.setHeader("Jalview Version: "
447 + Cache.getProperty("VERSION") + "\n"
448 + "Jalview Installation: "
449 + Cache.getDefault("INSTALLATION", "unknown")
450 + "\n" + "Build Date: "
451 + Cache.getDefault("BUILD_DATE", "unknown") + "\n"
452 + "Java version: " + System.getProperty("java.version") + "\n"
453 + System.getProperty("os.arch") + " "
454 + System.getProperty("os.name") + " "
455 + System.getProperty("os.version"));
457 showConsole(showjconsole);
459 showNews.setVisible(false);
461 experimentalFeatures.setSelected(showExperimental());
463 getIdentifiersOrgData();
467 // Spawn a thread that shows the splashscreen
469 SwingUtilities.invokeLater(new Runnable()
478 // Thread off a new instance of the file chooser - this reduces the time
480 // takes to open it later on.
481 new Thread(new Runnable()
486 Cache.log.debug("Filechooser init thread started.");
487 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
488 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
490 Cache.log.debug("Filechooser init thread finished.");
493 // Add the service change listener
494 changeSupport.addJalviewPropertyChangeListener("services",
495 new PropertyChangeListener()
499 public void propertyChange(PropertyChangeEvent evt)
501 Cache.log.debug("Firing service changed event for "
502 + evt.getNewValue());
503 JalviewServicesChanged(evt);
508 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
510 this.addWindowListener(new WindowAdapter()
513 public void windowClosing(WindowEvent evt)
520 this.addMouseListener(ma = new MouseAdapter()
523 public void mousePressed(MouseEvent evt)
525 if (evt.isPopupTrigger()) // Mac
527 showPasteMenu(evt.getX(), evt.getY());
532 public void mouseReleased(MouseEvent evt)
534 if (evt.isPopupTrigger()) // Windows
536 showPasteMenu(evt.getX(), evt.getY());
540 desktop.addMouseListener(ma);
545 * Answers true if user preferences to enable experimental features is True
550 public boolean showExperimental()
552 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
553 Boolean.FALSE.toString());
554 return Boolean.valueOf(experimental).booleanValue();
557 public void doConfigureStructurePrefs()
559 // configure services
560 StructureSelectionManager ssm = StructureSelectionManager
561 .getStructureSelectionManager(this);
562 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
564 ssm.setAddTempFacAnnot(Cache
565 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
566 ssm.setProcessSecondaryStructure(Cache
567 .getDefault(Preferences.STRUCT_FROM_PDB, true));
568 ssm.setSecStructServices(
569 Cache.getDefault(Preferences.USE_RNAVIEW, true));
573 ssm.setAddTempFacAnnot(false);
574 ssm.setProcessSecondaryStructure(false);
575 ssm.setSecStructServices(false);
579 public void checkForNews()
581 final Desktop me = this;
582 // Thread off the news reader, in case there are connection problems.
583 new Thread(new Runnable()
588 Cache.log.debug("Starting news thread.");
589 jvnews = new BlogReader(me);
590 showNews.setVisible(true);
591 Cache.log.debug("Completed news thread.");
596 public void getIdentifiersOrgData()
598 // Thread off the identifiers fetcher
599 new Thread(new Runnable()
604 Cache.log.debug("Downloading data from identifiers.org");
607 UrlDownloadClient.download(IdOrgSettings.getUrl(),
608 IdOrgSettings.getDownloadLocation());
609 } catch (IOException e)
611 Cache.log.debug("Exception downloading identifiers.org data"
620 protected void showNews_actionPerformed(ActionEvent e)
622 showNews(showNews.isSelected());
625 void showNews(boolean visible)
627 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
628 showNews.setSelected(visible);
629 if (visible && !jvnews.isVisible())
631 new Thread(new Runnable()
636 long now = System.currentTimeMillis();
637 Desktop.instance.setProgressBar(
638 MessageManager.getString("status.refreshing_news"), now);
639 jvnews.refreshNews();
640 Desktop.instance.setProgressBar(null, now);
648 * recover the last known dimensions for a jalview window
651 * - empty string is desktop, all other windows have unique prefix
652 * @return null or last known dimensions scaled to current geometry (if last
653 * window geom was known)
655 Rectangle getLastKnownDimensions(String windowName)
657 // TODO: lock aspect ratio for scaling desktop Bug #0058199
658 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
659 String x = Cache.getProperty(windowName + "SCREEN_X");
660 String y = Cache.getProperty(windowName + "SCREEN_Y");
662 .getProperty(windowName + "SCREEN_WIDTH");
663 String height = Cache
664 .getProperty(windowName + "SCREEN_HEIGHT");
665 if ((x != null) && (y != null) && (width != null) && (height != null))
667 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
668 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
669 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
671 // attempt #1 - try to cope with change in screen geometry - this
672 // version doesn't preserve original jv aspect ratio.
673 // take ratio of current screen size vs original screen size.
674 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
675 Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
676 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
677 Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
678 // rescale the bounds depending upon the current screen geometry.
679 ix = (int) (ix * sw);
680 iw = (int) (iw * sw);
681 iy = (int) (iy * sh);
682 ih = (int) (ih * sh);
683 while (ix >= screenSize.width)
686 "Window geometry location recall error: shifting horizontal to within screenbounds.");
687 ix -= screenSize.width;
689 while (iy >= screenSize.height)
692 "Window geometry location recall error: shifting vertical to within screenbounds.");
693 iy -= screenSize.height;
696 "Got last known dimensions for " + windowName + ": x:" + ix
697 + " y:" + iy + " width:" + iw + " height:" + ih);
699 // return dimensions for new instance
700 return new Rectangle(ix, iy, iw, ih);
705 void showPasteMenu(int x, int y)
707 JPopupMenu popup = new JPopupMenu();
708 JMenuItem item = new JMenuItem(
709 MessageManager.getString("label.paste_new_window"));
710 item.addActionListener(new ActionListener()
713 public void actionPerformed(ActionEvent evt)
720 popup.show(this, x, y);
727 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
728 Transferable contents = c.getContents(this);
730 if (contents != null)
732 String file = (String) contents
733 .getTransferData(DataFlavor.stringFlavor);
735 FileFormatI format = new IdentifyFile().identify(file,
736 DataSourceType.PASTE);
738 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
741 } catch (Exception ex)
744 "Unable to paste alignment from system clipboard:\n" + ex);
749 * Adds and opens the given frame to the desktop
760 public static synchronized void addInternalFrame(
761 final JInternalFrame frame, String title, int w, int h)
763 addInternalFrame(frame, title, true, w, h, true, false);
767 * Add an internal frame to the Jalview desktop
774 * When true, display frame immediately, otherwise, caller must call
775 * setVisible themselves.
781 public static synchronized void addInternalFrame(
782 final JInternalFrame frame, String title, boolean makeVisible,
785 addInternalFrame(frame, title, makeVisible, w, h, true, false);
789 * Add an internal frame to the Jalview desktop and make it visible
802 public static synchronized void addInternalFrame(
803 final JInternalFrame frame, String title, int w, int h,
806 addInternalFrame(frame, title, true, w, h, resizable, false);
810 * Add an internal frame to the Jalview desktop
817 * When true, display frame immediately, otherwise, caller must call
818 * setVisible themselves.
825 * @param ignoreMinSize
826 * Do not set the default minimum size for frame
828 public static synchronized void addInternalFrame(
829 final JInternalFrame frame, String title, boolean makeVisible,
830 int w, int h, boolean resizable, boolean ignoreMinSize)
833 // TODO: allow callers to determine X and Y position of frame (eg. via
835 // TODO: consider fixing method to update entries in the window submenu with
836 // the current window title
838 frame.setTitle(title);
839 if (frame.getWidth() < 1 || frame.getHeight() < 1)
843 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
844 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
845 // IF JALVIEW IS RUNNING HEADLESS
846 // ///////////////////////////////////////////////
847 if (instance == null || (System.getProperty("java.awt.headless") != null
848 && System.getProperty("java.awt.headless").equals("true")))
857 frame.setMinimumSize(
858 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
860 // Set default dimension for Alignment Frame window.
861 // The Alignment Frame window could be added from a number of places,
863 // I did this here in order not to miss out on any Alignment frame.
864 if (frame instanceof AlignFrame)
866 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
867 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
871 frame.setVisible(makeVisible);
872 frame.setClosable(true);
873 frame.setResizable(resizable);
874 frame.setMaximizable(resizable);
875 frame.setIconifiable(resizable);
876 frame.setOpaque(Platform.isJS());
878 if (frame.getX() < 1 && frame.getY() < 1)
880 frame.setLocation(xOffset * openFrameCount,
881 yOffset * ((openFrameCount - 1) % 10) + yOffset);
885 * add an entry for the new frame in the Window menu
886 * (and remove it when the frame is closed)
888 final JMenuItem menuItem = new JMenuItem(title);
889 frame.addInternalFrameListener(new InternalFrameAdapter()
892 public void internalFrameActivated(InternalFrameEvent evt)
894 JInternalFrame itf = desktop.getSelectedFrame();
897 if (itf instanceof AlignFrame)
899 Jalview.setCurrentAlignFrame((AlignFrame) itf);
906 public void internalFrameClosed(InternalFrameEvent evt)
908 PaintRefresher.RemoveComponent(frame);
911 * defensive check to prevent frames being
912 * added half off the window
914 if (openFrameCount > 0)
920 * ensure no reference to alignFrame retained by menu item listener
922 if (menuItem.getActionListeners().length > 0)
924 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
926 windowMenu.remove(menuItem);
930 menuItem.addActionListener(new ActionListener()
933 public void actionPerformed(ActionEvent e)
937 frame.setSelected(true);
938 frame.setIcon(false);
939 } catch (java.beans.PropertyVetoException ex)
941 // System.err.println(ex.toString());
946 setKeyBindings(frame);
950 windowMenu.add(menuItem);
955 frame.setSelected(true);
956 frame.requestFocus();
957 } catch (java.beans.PropertyVetoException ve)
959 } catch (java.lang.ClassCastException cex)
962 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
968 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
973 private static void setKeyBindings(JInternalFrame frame)
975 @SuppressWarnings("serial")
976 final Action closeAction = new AbstractAction()
979 public void actionPerformed(ActionEvent e)
986 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
988 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
989 InputEvent.CTRL_DOWN_MASK);
990 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
991 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
993 InputMap inputMap = frame
994 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
995 String ctrlW = ctrlWKey.toString();
996 inputMap.put(ctrlWKey, ctrlW);
997 inputMap.put(cmdWKey, ctrlW);
999 ActionMap actionMap = frame.getActionMap();
1000 actionMap.put(ctrlW, closeAction);
1004 public void lostOwnership(Clipboard clipboard, Transferable contents)
1008 Desktop.jalviewClipboard = null;
1011 internalCopy = false;
1015 public void dragEnter(DropTargetDragEvent evt)
1020 public void dragExit(DropTargetEvent evt)
1025 public void dragOver(DropTargetDragEvent evt)
1030 public void dropActionChanged(DropTargetDragEvent evt)
1041 public void drop(DropTargetDropEvent evt)
1043 boolean success = true;
1044 // JAL-1552 - acceptDrop required before getTransferable call for
1045 // Java's Transferable for native dnd
1046 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1047 Transferable t = evt.getTransferable();
1048 List<Object> files = new ArrayList<>();
1049 List<DataSourceType> protocols = new ArrayList<>();
1053 Desktop.transferFromDropTarget(files, protocols, evt, t);
1054 } catch (Exception e)
1056 e.printStackTrace();
1064 for (int i = 0; i < files.size(); i++)
1066 // BH 2018 File or String
1067 Object file = files.get(i);
1068 String fileName = file.toString();
1069 DataSourceType protocol = (protocols == null)
1070 ? DataSourceType.FILE
1072 FileFormatI format = null;
1074 if (fileName.endsWith(".jar"))
1076 format = FileFormat.Jalview;
1081 format = new IdentifyFile().identify(file, protocol);
1083 if (file instanceof File)
1085 Platform.cacheFileData((File) file);
1087 new FileLoader().LoadFile(null, file, protocol, format);
1090 } catch (Exception ex)
1095 evt.dropComplete(success); // need this to ensure input focus is properly
1096 // transfered to any new windows created
1106 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1108 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1109 JalviewFileChooser chooser = JalviewFileChooser
1110 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, BackupFiles.getEnabled());
1112 chooser.setFileView(new JalviewFileView());
1113 chooser.setDialogTitle(
1114 MessageManager.getString("label.open_local_file"));
1115 chooser.setToolTipText(MessageManager.getString("action.open"));
1117 chooser.setResponseHandler(0, new Runnable()
1122 File selectedFile = chooser.getSelectedFile();
1123 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1125 FileFormatI format = chooser.getSelectedFormat();
1128 * Call IdentifyFile to verify the file contains what its extension implies.
1129 * Skip this step for dynamically added file formats, because
1130 * IdentifyFile does not know how to recognise them.
1132 if (FileFormats.getInstance().isIdentifiable(format))
1136 format = new IdentifyFile().identify(selectedFile,
1137 DataSourceType.FILE);
1138 } catch (FileFormatException e)
1140 // format = null; //??
1144 new FileLoader().LoadFile(viewport, selectedFile,
1145 DataSourceType.FILE, format);
1148 chooser.showOpenDialog(this);
1152 * Shows a dialog for input of a URL at which to retrieve alignment data
1157 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1159 // This construct allows us to have a wider textfield
1161 JLabel label = new JLabel(
1162 MessageManager.getString("label.input_file_url"));
1164 JPanel panel = new JPanel(new GridLayout(2, 1));
1168 * the URL to fetch is
1169 * Java: an editable combobox with history
1170 * JS: (pending JAL-3038) a plain text field
1173 String urlBase = "http://www.";
1174 if (Platform.isJS())
1176 history = new JTextField(urlBase, 35);
1185 JComboBox<String> asCombo = new JComboBox<>();
1186 asCombo.setPreferredSize(new Dimension(400, 20));
1187 asCombo.setEditable(true);
1188 asCombo.addItem(urlBase);
1189 String historyItems = Cache.getProperty("RECENT_URL");
1190 if (historyItems != null)
1192 for (String token : historyItems.split("\\t"))
1194 asCombo.addItem(token);
1201 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1202 MessageManager.getString("action.cancel") };
1203 Runnable action = new Runnable()
1208 @SuppressWarnings("unchecked")
1209 String url = (history instanceof JTextField
1210 ? ((JTextField) history).getText()
1211 : ((JComboBox<String>) history).getSelectedItem()
1214 if (url.toLowerCase().endsWith(".jar"))
1216 if (viewport != null)
1218 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1219 FileFormat.Jalview);
1223 new FileLoader().LoadFile(url, DataSourceType.URL,
1224 FileFormat.Jalview);
1229 FileFormatI format = null;
1232 format = new IdentifyFile().identify(url, DataSourceType.URL);
1233 } catch (FileFormatException e)
1235 // TODO revise error handling, distinguish between
1236 // URL not found and response not valid
1241 String msg = MessageManager
1242 .formatMessage("label.couldnt_locate", url);
1243 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1244 MessageManager.getString("label.url_not_found"),
1245 JvOptionPane.WARNING_MESSAGE);
1250 if (viewport != null)
1252 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1257 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1262 String dialogOption = MessageManager
1263 .getString("label.input_alignment_from_url");
1264 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1265 .showInternalDialog(panel, dialogOption,
1266 JvOptionPane.YES_NO_CANCEL_OPTION,
1267 JvOptionPane.PLAIN_MESSAGE, null, options,
1268 MessageManager.getString("action.ok"));
1272 * Opens the CutAndPaste window for the user to paste an alignment in to
1275 * - if not null, the pasted alignment is added to the current
1276 * alignment; if null, to a new alignment window
1279 public void inputTextboxMenuItem_actionPerformed(
1280 AlignmentViewPanel viewPanel)
1282 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1283 cap.setForInput(viewPanel);
1284 Desktop.addInternalFrame(cap,
1285 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1295 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1296 Cache.setProperty("SCREENGEOMETRY_WIDTH",
1298 Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1299 screen.height + "");
1300 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1301 getWidth(), getHeight()));
1303 if (jconsole != null)
1305 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1306 jconsole.stopConsole();
1310 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1313 if (dialogExecutor != null)
1315 dialogExecutor.shutdownNow();
1317 closeAll_actionPerformed(null);
1319 if (groovyConsole != null)
1321 // suppress a possible repeat prompt to save script
1322 groovyConsole.setDirty(false);
1323 groovyConsole.exit();
1328 private void storeLastKnownDimensions(String string, Rectangle jc)
1330 Cache.log.debug("Storing last known dimensions for "
1331 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1332 + " height:" + jc.height);
1334 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1335 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1336 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1337 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1347 public void aboutMenuItem_actionPerformed(ActionEvent e)
1349 // StringBuffer message = getAboutMessage(false);
1350 // JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1352 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1353 new Thread(new Runnable()
1358 new SplashScreen(true);
1363 public StringBuffer getAboutMessage(boolean shortv)
1365 StringBuffer message = new StringBuffer();
1366 message.append("<html>");
1369 message.append("<h1><strong>Version: "
1370 + Cache.getProperty("VERSION")
1371 + "</strong></h1>");
1372 message.append("<strong>Built: <em>"
1373 + Cache.getDefault("BUILD_DATE", "unknown") + "</em> from "
1374 + Cache.getBuildDetailsForSplash()
1381 message.append("<strong>Version "
1382 + Cache.getProperty("VERSION")
1383 + "; last updated: "
1384 + Cache.getDefault("BUILD_DATE", "unknown"));
1387 if (Cache.getDefault("LATEST_VERSION", "Checking")
1388 .equals("Checking"))
1390 // JBP removed this message for 2.11: May be reinstated in future version
1391 // message.append("<br>...Checking latest version...</br>");
1393 else if (!Cache.getDefault("LATEST_VERSION", "Checking")
1394 .equals(Cache.getProperty("VERSION")))
1396 boolean red = false;
1397 if (Cache.getProperty("VERSION").toLowerCase()
1398 .indexOf("automated build") == -1)
1401 // Displayed when code version and jnlp version do not match and code
1402 // version is not a development build
1403 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1406 message.append("<br>!! Version "
1407 + Cache.getDefault("LATEST_VERSION",
1409 + " is available for download from "
1410 + Cache.getDefault("www.jalview.org",
1411 "http://www.jalview.org")
1415 message.append("</div>");
1418 message.append("<br>Authors: " + Cache.getDefault(
1420 "The Jalview Authors (See AUTHORS file for current list)")
1421 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1422 + "<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"
1423 + "<br><br>If you use Jalview, please cite:"
1424 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1425 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1426 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1432 * Action on requesting Help documentation
1435 public void documentationMenuItem_actionPerformed()
1439 if (Platform.isJS())
1441 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1450 Help.showHelpWindow();
1452 } catch (Exception ex)
1454 System.err.println("Error opening help: " + ex.getMessage());
1459 public void closeAll_actionPerformed(ActionEvent e)
1461 // TODO show a progress bar while closing?
1462 JInternalFrame[] frames = desktop.getAllFrames();
1463 for (int i = 0; i < frames.length; i++)
1467 frames[i].setClosed(true);
1468 } catch (java.beans.PropertyVetoException ex)
1472 Jalview.setCurrentAlignFrame(null);
1473 System.out.println("ALL CLOSED");
1476 * reset state of singleton objects as appropriate (clear down session state
1477 * when all windows are closed)
1479 StructureSelectionManager ssm = StructureSelectionManager
1480 .getStructureSelectionManager(this);
1488 public void raiseRelated_actionPerformed(ActionEvent e)
1490 reorderAssociatedWindows(false, false);
1494 public void minimizeAssociated_actionPerformed(ActionEvent e)
1496 reorderAssociatedWindows(true, false);
1499 void closeAssociatedWindows()
1501 reorderAssociatedWindows(false, true);
1507 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1511 protected void garbageCollect_actionPerformed(ActionEvent e)
1513 // We simply collect the garbage
1514 Cache.log.debug("Collecting garbage...");
1516 Cache.log.debug("Finished garbage collection.");
1523 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1527 protected void showMemusage_actionPerformed(ActionEvent e)
1529 desktop.showMemoryUsage(showMemusage.isSelected());
1536 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1540 protected void showConsole_actionPerformed(ActionEvent e)
1542 showConsole(showConsole.isSelected());
1545 Console jconsole = null;
1548 * control whether the java console is visible or not
1552 void showConsole(boolean selected)
1554 // TODO: decide if we should update properties file
1555 if (jconsole != null) // BH 2018
1557 showConsole.setSelected(selected);
1558 Cache.setProperty("SHOW_JAVA_CONSOLE",
1559 Boolean.valueOf(selected).toString());
1560 jconsole.setVisible(selected);
1564 void reorderAssociatedWindows(boolean minimize, boolean close)
1566 JInternalFrame[] frames = desktop.getAllFrames();
1567 if (frames == null || frames.length < 1)
1572 AlignmentViewport source = null, target = null;
1573 if (frames[0] instanceof AlignFrame)
1575 source = ((AlignFrame) frames[0]).getCurrentView();
1577 else if (frames[0] instanceof TreePanel)
1579 source = ((TreePanel) frames[0]).getViewPort();
1581 else if (frames[0] instanceof PCAPanel)
1583 source = ((PCAPanel) frames[0]).av;
1585 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1587 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1592 for (int i = 0; i < frames.length; i++)
1595 if (frames[i] == null)
1599 if (frames[i] instanceof AlignFrame)
1601 target = ((AlignFrame) frames[i]).getCurrentView();
1603 else if (frames[i] instanceof TreePanel)
1605 target = ((TreePanel) frames[i]).getViewPort();
1607 else if (frames[i] instanceof PCAPanel)
1609 target = ((PCAPanel) frames[i]).av;
1611 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1613 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1616 if (source == target)
1622 frames[i].setClosed(true);
1626 frames[i].setIcon(minimize);
1629 frames[i].toFront();
1633 } catch (java.beans.PropertyVetoException ex)
1648 protected void preferences_actionPerformed(ActionEvent e)
1654 * Prompts the user to choose a file and then saves the Jalview state as a
1655 * Jalview project file
1658 public void saveState_actionPerformed()
1660 saveState_actionPerformed(false);
1663 public void saveState_actionPerformed(boolean saveAs)
1665 java.io.File projectFile = getProjectFile();
1666 // autoSave indicates we already have a file and don't need to ask
1667 boolean autoSave = projectFile != null && !saveAs
1668 && BackupFiles.getEnabled();
1670 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1671 // saveAs="+saveAs+", Backups
1672 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1674 boolean approveSave = false;
1677 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1680 chooser.setFileView(new JalviewFileView());
1681 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1683 int value = chooser.showSaveDialog(this);
1685 if (value == JalviewFileChooser.APPROVE_OPTION)
1687 projectFile = chooser.getSelectedFile();
1688 setProjectFile(projectFile);
1693 if (approveSave || autoSave)
1695 final Desktop me = this;
1696 final java.io.File chosenFile = projectFile;
1697 new Thread(new Runnable()
1702 // TODO: refactor to Jalview desktop session controller action.
1703 setProgressBar(MessageManager.formatMessage(
1704 "label.saving_jalview_project", new Object[]
1705 { chosenFile.getName() }), chosenFile.hashCode());
1706 Cache.setProperty("LAST_DIRECTORY",
1707 chosenFile.getParent());
1708 // TODO catch and handle errors for savestate
1709 // TODO prevent user from messing with the Desktop whilst we're saving
1712 boolean doBackup = BackupFiles.getEnabled();
1713 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1715 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1719 backupfiles.setWriteSuccess(true);
1720 backupfiles.rollBackupsAndRenameTempFile();
1722 } catch (OutOfMemoryError oom)
1724 new OOMWarning("Whilst saving current state to "
1725 + chosenFile.getName(), oom);
1726 } catch (Exception ex)
1728 Cache.log.error("Problems whilst trying to save to "
1729 + chosenFile.getName(), ex);
1730 JvOptionPane.showMessageDialog(me,
1731 MessageManager.formatMessage(
1732 "label.error_whilst_saving_current_state_to",
1734 { chosenFile.getName() }),
1735 MessageManager.getString("label.couldnt_save_project"),
1736 JvOptionPane.WARNING_MESSAGE);
1738 setProgressBar(null, chosenFile.hashCode());
1745 public void saveAsState_actionPerformed(ActionEvent e)
1747 saveState_actionPerformed(true);
1750 private void setProjectFile(File choice)
1752 this.projectFile = choice;
1755 public File getProjectFile()
1757 return this.projectFile;
1761 * Shows a file chooser dialog and tries to read in the selected file as a
1765 public void loadState_actionPerformed()
1767 final String[] suffix = new String[] { "jvp", "jar" };
1768 final String[] desc = new String[] { "Jalview Project",
1769 "Jalview Project (old)" };
1770 JalviewFileChooser chooser = new JalviewFileChooser(
1771 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1772 "Jalview Project", true, BackupFiles.getEnabled()); // last two booleans: allFiles,
1774 chooser.setFileView(new JalviewFileView());
1775 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1776 chooser.setResponseHandler(0, new Runnable()
1781 File selectedFile = chooser.getSelectedFile();
1782 setProjectFile(selectedFile);
1783 String choice = selectedFile.getAbsolutePath();
1784 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1785 new Thread(new Runnable()
1792 new Jalview2XML().loadJalviewAlign(choice);
1793 } catch (OutOfMemoryError oom)
1795 new OOMWarning("Whilst loading project from " + choice, oom);
1796 } catch (Exception ex)
1799 "Problems whilst loading project from " + choice, ex);
1800 JvOptionPane.showMessageDialog(Desktop.desktop,
1801 MessageManager.formatMessage(
1802 "label.error_whilst_loading_project_from",
1805 MessageManager.getString("label.couldnt_load_project"),
1806 JvOptionPane.WARNING_MESSAGE);
1813 chooser.showOpenDialog(this);
1817 public void inputSequence_actionPerformed(ActionEvent e)
1819 new SequenceFetcher(this);
1822 JPanel progressPanel;
1824 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1826 public void startLoading(final Object fileName)
1828 if (fileLoadingCount == 0)
1830 fileLoadingPanels.add(addProgressPanel(MessageManager
1831 .formatMessage("label.loading_file", new Object[]
1837 private JPanel addProgressPanel(String string)
1839 if (progressPanel == null)
1841 progressPanel = new JPanel(new GridLayout(1, 1));
1842 totalProgressCount = 0;
1843 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1845 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1846 JProgressBar progressBar = new JProgressBar();
1847 progressBar.setIndeterminate(true);
1849 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1851 thisprogress.add(progressBar, BorderLayout.CENTER);
1852 progressPanel.add(thisprogress);
1853 ((GridLayout) progressPanel.getLayout()).setRows(
1854 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1855 ++totalProgressCount;
1856 instance.validate();
1857 return thisprogress;
1860 int totalProgressCount = 0;
1862 private void removeProgressPanel(JPanel progbar)
1864 if (progressPanel != null)
1866 synchronized (progressPanel)
1868 progressPanel.remove(progbar);
1869 GridLayout gl = (GridLayout) progressPanel.getLayout();
1870 gl.setRows(gl.getRows() - 1);
1871 if (--totalProgressCount < 1)
1873 this.getContentPane().remove(progressPanel);
1874 progressPanel = null;
1881 public void stopLoading()
1884 if (fileLoadingCount < 1)
1886 while (fileLoadingPanels.size() > 0)
1888 removeProgressPanel(fileLoadingPanels.remove(0));
1890 fileLoadingPanels.clear();
1891 fileLoadingCount = 0;
1896 public static int getViewCount(String alignmentId)
1898 AlignmentViewport[] aps = getViewports(alignmentId);
1899 return (aps == null) ? 0 : aps.length;
1904 * @param alignmentId
1905 * - if null, all sets are returned
1906 * @return all AlignmentPanels concerning the alignmentId sequence set
1908 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1910 if (Desktop.desktop == null)
1912 // no frames created and in headless mode
1913 // TODO: verify that frames are recoverable when in headless mode
1916 List<AlignmentPanel> aps = new ArrayList<>();
1917 AlignFrame[] frames = getAlignFrames();
1922 for (AlignFrame af : frames)
1924 for (AlignmentPanel ap : af.alignPanels)
1926 if (alignmentId == null
1927 || alignmentId.equals(ap.av.getSequenceSetId()))
1933 if (aps.size() == 0)
1937 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1942 * get all the viewports on an alignment.
1944 * @param sequenceSetId
1945 * unique alignment id (may be null - all viewports returned in that
1947 * @return all viewports on the alignment bound to sequenceSetId
1949 public static AlignmentViewport[] getViewports(String sequenceSetId)
1951 List<AlignmentViewport> viewp = new ArrayList<>();
1952 if (desktop != null)
1954 AlignFrame[] frames = Desktop.getAlignFrames();
1956 for (AlignFrame afr : frames)
1958 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1959 .equals(sequenceSetId))
1961 if (afr.alignPanels != null)
1963 for (AlignmentPanel ap : afr.alignPanels)
1965 if (sequenceSetId == null
1966 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1974 viewp.add(afr.getViewport());
1978 if (viewp.size() > 0)
1980 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1987 * Explode the views in the given frame into separate AlignFrame
1991 public static void explodeViews(AlignFrame af)
1993 int size = af.alignPanels.size();
1999 for (int i = 0; i < size; i++)
2001 AlignmentPanel ap = af.alignPanels.get(i);
2002 AlignFrame newaf = new AlignFrame(ap);
2005 * Restore the view's last exploded frame geometry if known. Multiple
2006 * views from one exploded frame share and restore the same (frame)
2007 * position and size.
2009 Rectangle geometry = ap.av.getExplodedGeometry();
2010 if (geometry != null)
2012 newaf.setBounds(geometry);
2015 ap.av.setGatherViewsHere(false);
2017 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2018 AlignFrame.DEFAULT_HEIGHT);
2021 af.alignPanels.clear();
2022 af.closeMenuItem_actionPerformed(true);
2027 * Gather expanded views (separate AlignFrame's) with the same sequence set
2028 * identifier back in to this frame as additional views, and close the expanded
2029 * views. Note the expanded frames may themselves have multiple views. We take
2034 public void gatherViews(AlignFrame source)
2036 source.viewport.setGatherViewsHere(true);
2037 source.viewport.setExplodedGeometry(source.getBounds());
2038 JInternalFrame[] frames = desktop.getAllFrames();
2039 String viewId = source.viewport.getSequenceSetId();
2041 for (int t = 0; t < frames.length; t++)
2043 if (frames[t] instanceof AlignFrame && frames[t] != source)
2045 AlignFrame af = (AlignFrame) frames[t];
2046 boolean gatherThis = false;
2047 for (int a = 0; a < af.alignPanels.size(); a++)
2049 AlignmentPanel ap = af.alignPanels.get(a);
2050 if (viewId.equals(ap.av.getSequenceSetId()))
2053 ap.av.setGatherViewsHere(false);
2054 ap.av.setExplodedGeometry(af.getBounds());
2055 source.addAlignmentPanel(ap, false);
2061 af.alignPanels.clear();
2062 af.closeMenuItem_actionPerformed(true);
2068 public JInternalFrame[] getAllFrames()
2070 return desktop.getAllFrames();
2074 * Checks the given url to see if it gives a response indicating that the user
2075 * should be informed of a new questionnaire.
2079 public void checkForQuestionnaire(String url)
2081 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2082 // javax.swing.SwingUtilities.invokeLater(jvq);
2083 new Thread(jvq).start();
2086 public void checkURLLinks()
2088 // Thread off the URL link checker
2089 addDialogThread(new Runnable()
2094 if (Cache.getDefault("CHECKURLLINKS", true))
2096 // check what the actual links are - if it's just the default don't
2097 // bother with the warning
2098 List<String> links = Preferences.sequenceUrlLinks
2101 // only need to check links if there is one with a
2102 // SEQUENCE_ID which is not the default EMBL_EBI link
2103 ListIterator<String> li = links.listIterator();
2104 boolean check = false;
2105 List<JLabel> urls = new ArrayList<>();
2106 while (li.hasNext())
2108 String link = li.next();
2109 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2110 && !UrlConstants.isDefaultString(link))
2113 int barPos = link.indexOf("|");
2114 String urlMsg = barPos == -1 ? link
2115 : link.substring(0, barPos) + ": "
2116 + link.substring(barPos + 1);
2117 urls.add(new JLabel(urlMsg));
2125 // ask user to check in case URL links use old style tokens
2126 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2127 JPanel msgPanel = new JPanel();
2128 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2129 msgPanel.add(Box.createVerticalGlue());
2130 JLabel msg = new JLabel(MessageManager
2131 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2132 JLabel msg2 = new JLabel(MessageManager
2133 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2135 for (JLabel url : urls)
2141 final JCheckBox jcb = new JCheckBox(
2142 MessageManager.getString("label.do_not_display_again"));
2143 jcb.addActionListener(new ActionListener()
2146 public void actionPerformed(ActionEvent e)
2148 // update Cache settings for "don't show this again"
2149 boolean showWarningAgain = !jcb.isSelected();
2150 Cache.setProperty("CHECKURLLINKS",
2151 Boolean.valueOf(showWarningAgain).toString());
2156 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2158 .getString("label.SEQUENCE_ID_no_longer_used"),
2159 JvOptionPane.WARNING_MESSAGE);
2166 * Proxy class for JDesktopPane which optionally displays the current memory
2167 * usage and highlights the desktop area with a red bar if free memory runs low.
2171 public class MyDesktopPane extends JDesktopPane
2174 private static final float ONE_MB = 1048576f;
2176 boolean showMemoryUsage = false;
2180 java.text.NumberFormat df;
2182 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2185 public MyDesktopPane(boolean showMemoryUsage)
2187 showMemoryUsage(showMemoryUsage);
2190 public void showMemoryUsage(boolean showMemory)
2192 this.showMemoryUsage = showMemory;
2195 Thread worker = new Thread(this);
2201 public boolean isShowMemoryUsage()
2203 return showMemoryUsage;
2209 df = java.text.NumberFormat.getNumberInstance();
2210 df.setMaximumFractionDigits(2);
2211 runtime = Runtime.getRuntime();
2213 while (showMemoryUsage)
2217 maxMemory = runtime.maxMemory() / ONE_MB;
2218 allocatedMemory = runtime.totalMemory() / ONE_MB;
2219 freeMemory = runtime.freeMemory() / ONE_MB;
2220 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2222 percentUsage = (totalFreeMemory / maxMemory) * 100;
2224 // if (percentUsage < 20)
2226 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2228 // instance.set.setBorder(border1);
2231 // sleep after showing usage
2233 } catch (Exception ex)
2235 ex.printStackTrace();
2241 public void paintComponent(Graphics g)
2243 if (showMemoryUsage && g != null && df != null)
2245 if (percentUsage < 20)
2247 g.setColor(Color.red);
2249 FontMetrics fm = g.getFontMetrics();
2252 g.drawString(MessageManager.formatMessage("label.memory_stats",
2254 { df.format(totalFreeMemory), df.format(maxMemory),
2255 df.format(percentUsage) }),
2256 10, getHeight() - fm.getHeight());
2263 * Accessor method to quickly get all the AlignmentFrames loaded.
2265 * @return an array of AlignFrame, or null if none found
2267 public static AlignFrame[] getAlignFrames()
2269 if (Jalview.isHeadlessMode())
2271 // Desktop.desktop is null in headless mode
2272 return new AlignFrame[] { Jalview.currentAlignFrame };
2275 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2281 List<AlignFrame> avp = new ArrayList<>();
2283 for (int i = frames.length - 1; i > -1; i--)
2285 if (frames[i] instanceof AlignFrame)
2287 avp.add((AlignFrame) frames[i]);
2289 else if (frames[i] instanceof SplitFrame)
2292 * Also check for a split frame containing an AlignFrame
2294 GSplitFrame sf = (GSplitFrame) frames[i];
2295 if (sf.getTopFrame() instanceof AlignFrame)
2297 avp.add((AlignFrame) sf.getTopFrame());
2299 if (sf.getBottomFrame() instanceof AlignFrame)
2301 avp.add((AlignFrame) sf.getBottomFrame());
2305 if (avp.size() == 0)
2309 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2314 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2318 public GStructureViewer[] getJmols()
2320 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2326 List<GStructureViewer> avp = new ArrayList<>();
2328 for (int i = frames.length - 1; i > -1; i--)
2330 if (frames[i] instanceof AppJmol)
2332 GStructureViewer af = (GStructureViewer) frames[i];
2336 if (avp.size() == 0)
2340 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2345 * Add Groovy Support to Jalview
2348 public void groovyShell_actionPerformed()
2352 openGroovyConsole();
2353 } catch (Exception ex)
2355 Cache.log.error("Groovy Shell Creation failed.", ex);
2356 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2358 MessageManager.getString("label.couldnt_create_groovy_shell"),
2359 MessageManager.getString("label.groovy_support_failed"),
2360 JvOptionPane.ERROR_MESSAGE);
2365 * Open the Groovy console
2367 void openGroovyConsole()
2369 if (groovyConsole == null)
2371 groovyConsole = new groovy.ui.Console();
2372 groovyConsole.setVariable("Jalview", this);
2373 groovyConsole.run();
2376 * We allow only one console at a time, so that AlignFrame menu option
2377 * 'Calculate | Run Groovy script' is unambiguous.
2378 * Disable 'Groovy Console', and enable 'Run script', when the console is
2379 * opened, and the reverse when it is closed
2381 Window window = (Window) groovyConsole.getFrame();
2382 window.addWindowListener(new WindowAdapter()
2385 public void windowClosed(WindowEvent e)
2388 * rebind CMD-Q from Groovy Console to Jalview Quit
2391 enableExecuteGroovy(false);
2397 * show Groovy console window (after close and reopen)
2399 ((Window) groovyConsole.getFrame()).setVisible(true);
2402 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2403 * and disable opening a second console
2405 enableExecuteGroovy(true);
2409 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2412 protected void addQuitHandler()
2414 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2415 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2416 jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
2418 getRootPane().getActionMap().put("Quit", new AbstractAction()
2421 public void actionPerformed(ActionEvent e)
2429 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2432 * true if Groovy console is open
2434 public void enableExecuteGroovy(boolean enabled)
2437 * disable opening a second Groovy console
2438 * (or re-enable when the console is closed)
2440 groovyShell.setEnabled(!enabled);
2442 AlignFrame[] alignFrames = getAlignFrames();
2443 if (alignFrames != null)
2445 for (AlignFrame af : alignFrames)
2447 af.setGroovyEnabled(enabled);
2453 * Progress bars managed by the IProgressIndicator method.
2455 private Hashtable<Long, JPanel> progressBars;
2457 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2462 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2465 public void setProgressBar(String message, long id)
2467 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2469 if (progressBars == null)
2471 progressBars = new Hashtable<>();
2472 progressBarHandlers = new Hashtable<>();
2475 if (progressBars.get(Long.valueOf(id)) != null)
2477 JPanel panel = progressBars.remove(Long.valueOf(id));
2478 if (progressBarHandlers.contains(Long.valueOf(id)))
2480 progressBarHandlers.remove(Long.valueOf(id));
2482 removeProgressPanel(panel);
2486 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2493 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2494 * jalview.gui.IProgressIndicatorHandler)
2497 public void registerHandler(final long id,
2498 final IProgressIndicatorHandler handler)
2500 if (progressBarHandlers == null
2501 || !progressBars.containsKey(Long.valueOf(id)))
2503 throw new Error(MessageManager.getString(
2504 "error.call_setprogressbar_before_registering_handler"));
2506 progressBarHandlers.put(Long.valueOf(id), handler);
2507 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2508 if (handler.canCancel())
2510 JButton cancel = new JButton(
2511 MessageManager.getString("action.cancel"));
2512 final IProgressIndicator us = this;
2513 cancel.addActionListener(new ActionListener()
2517 public void actionPerformed(ActionEvent e)
2519 handler.cancelActivity(id);
2520 us.setProgressBar(MessageManager
2521 .formatMessage("label.cancelled_params", new Object[]
2522 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2526 progressPanel.add(cancel, BorderLayout.EAST);
2532 * @return true if any progress bars are still active
2535 public boolean operationInProgress()
2537 if (progressBars != null && progressBars.size() > 0)
2545 * This will return the first AlignFrame holding the given viewport instance. It
2546 * will break if there are more than one AlignFrames viewing a particular av.
2549 * @return alignFrame for viewport
2551 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2553 if (desktop != null)
2555 AlignmentPanel[] aps = getAlignmentPanels(
2556 viewport.getSequenceSetId());
2557 for (int panel = 0; aps != null && panel < aps.length; panel++)
2559 if (aps[panel] != null && aps[panel].av == viewport)
2561 return aps[panel].alignFrame;
2568 public VamsasApplication getVamsasApplication()
2570 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2576 * flag set if jalview GUI is being operated programmatically
2578 private boolean inBatchMode = false;
2581 * check if jalview GUI is being operated programmatically
2583 * @return inBatchMode
2585 public boolean isInBatchMode()
2591 * set flag if jalview GUI is being operated programmatically
2593 * @param inBatchMode
2595 public void setInBatchMode(boolean inBatchMode)
2597 this.inBatchMode = inBatchMode;
2600 public void startServiceDiscovery()
2602 startServiceDiscovery(false);
2605 public void startServiceDiscovery(boolean blocking)
2607 boolean alive = true;
2608 Thread t0 = null, t1 = null, t2 = null;
2609 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2612 // todo: changesupport handlers need to be transferred
2613 if (discoverer == null)
2615 discoverer = new jalview.ws.jws1.Discoverer();
2616 // register PCS handler for desktop.
2617 discoverer.addPropertyChangeListener(changeSupport);
2619 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2620 // until we phase out completely
2621 (t0 = new Thread(discoverer)).start();
2624 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2626 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2627 .startDiscoverer(changeSupport);
2631 // TODO: do rest service discovery
2640 } catch (Exception e)
2643 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2644 || (t3 != null && t3.isAlive())
2645 || (t0 != null && t0.isAlive());
2651 * called to check if the service discovery process completed successfully.
2655 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2657 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2659 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2660 .getErrorMessages();
2663 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2665 if (serviceChangedDialog == null)
2667 // only run if we aren't already displaying one of these.
2668 addDialogThread(serviceChangedDialog = new Runnable()
2675 * JalviewDialog jd =new JalviewDialog() {
2677 * @Override protected void cancelPressed() { // TODO
2678 * Auto-generated method stub
2680 * }@Override protected void okPressed() { // TODO
2681 * Auto-generated method stub
2683 * }@Override protected void raiseClosed() { // TODO
2684 * Auto-generated method stub
2686 * } }; jd.initDialogFrame(new
2687 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2688 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2689 * + " or mis-configured HTTP proxy settings.<br/>" +
2690 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2692 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2693 * ), true, true, "Web Service Configuration Problem", 450,
2696 * jd.waitForInput();
2698 JvOptionPane.showConfirmDialog(Desktop.desktop,
2699 new JLabel("<html><table width=\"450\"><tr><td>"
2700 + ermsg + "</td></tr></table>"
2701 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2702 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2703 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2704 + " Tools->Preferences dialog box to change them.</p></html>"),
2705 "Web Service Configuration Problem",
2706 JvOptionPane.DEFAULT_OPTION,
2707 JvOptionPane.ERROR_MESSAGE);
2708 serviceChangedDialog = null;
2717 "Errors reported by JABA discovery service. Check web services preferences.\n"
2724 private Runnable serviceChangedDialog = null;
2727 * start a thread to open a URL in the configured browser. Pops up a warning
2728 * dialog to the user if there is an exception when calling out to the browser
2733 public static void showUrl(final String url)
2735 showUrl(url, Desktop.instance);
2739 * Like showUrl but allows progress handler to be specified
2743 * (null) or object implementing IProgressIndicator
2745 public static void showUrl(final String url,
2746 final IProgressIndicator progress)
2748 new Thread(new Runnable()
2755 if (progress != null)
2757 progress.setProgressBar(MessageManager
2758 .formatMessage("status.opening_params", new Object[]
2759 { url }), this.hashCode());
2761 jalview.util.BrowserLauncher.openURL(url);
2762 } catch (Exception ex)
2764 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2766 .getString("label.web_browser_not_found_unix"),
2767 MessageManager.getString("label.web_browser_not_found"),
2768 JvOptionPane.WARNING_MESSAGE);
2770 ex.printStackTrace();
2772 if (progress != null)
2774 progress.setProgressBar(null, this.hashCode());
2780 public static WsParamSetManager wsparamManager = null;
2782 public static ParamManager getUserParameterStore()
2784 if (wsparamManager == null)
2786 wsparamManager = new WsParamSetManager();
2788 return wsparamManager;
2792 * static hyperlink handler proxy method for use by Jalview's internal windows
2796 public static void hyperlinkUpdate(HyperlinkEvent e)
2798 if (e.getEventType() == EventType.ACTIVATED)
2803 url = e.getURL().toString();
2804 Desktop.showUrl(url);
2805 } catch (Exception x)
2809 if (Cache.log != null)
2811 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2816 "Couldn't handle string " + url + " as a URL.");
2819 // ignore any exceptions due to dud links.
2826 * single thread that handles display of dialogs to user.
2828 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2831 * flag indicating if dialogExecutor should try to acquire a permit
2833 private volatile boolean dialogPause = true;
2838 private java.util.concurrent.Semaphore block = new Semaphore(0);
2840 private static groovy.ui.Console groovyConsole;
2843 * add another dialog thread to the queue
2847 public void addDialogThread(final Runnable prompter)
2849 dialogExecutor.submit(new Runnable()
2859 } catch (InterruptedException x)
2863 if (instance == null)
2869 SwingUtilities.invokeAndWait(prompter);
2870 } catch (Exception q)
2872 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2878 public void startDialogQueue()
2880 // set the flag so we don't pause waiting for another permit and semaphore
2881 // the current task to begin
2882 dialogPause = false;
2887 * Outputs an image of the desktop to file in EPS format, after prompting the
2888 * user for choice of Text or Lineart character rendering (unless a preference
2889 * has been set). The file name is generated as
2892 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2896 protected void snapShotWindow_actionPerformed(ActionEvent e)
2898 // currently the menu option to do this is not shown
2901 int width = getWidth();
2902 int height = getHeight();
2904 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2905 ImageWriterI writer = new ImageWriterI()
2908 public void exportImage(Graphics g) throws Exception
2911 Cache.log.info("Successfully written snapshot to file "
2912 + of.getAbsolutePath());
2915 String title = "View of desktop";
2916 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2918 exporter.doExport(of, this, width, height, title);
2922 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2923 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
2924 * location last time the view was expanded (if any). However it does not
2925 * remember the split pane divider location - this is set to match the
2926 * 'exploding' frame.
2930 public void explodeViews(SplitFrame sf)
2932 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2933 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2934 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2936 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2938 int viewCount = topPanels.size();
2945 * Processing in reverse order works, forwards order leaves the first panels
2946 * not visible. I don't know why!
2948 for (int i = viewCount - 1; i >= 0; i--)
2951 * Make new top and bottom frames. These take over the respective
2952 * AlignmentPanel objects, including their AlignmentViewports, so the
2953 * cdna/protein relationships between the viewports is carried over to the
2956 * explodedGeometry holds the (x, y) position of the previously exploded
2957 * SplitFrame, and the (width, height) of the AlignFrame component
2959 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
2960 AlignFrame newTopFrame = new AlignFrame(topPanel);
2961 newTopFrame.setSize(oldTopFrame.getSize());
2962 newTopFrame.setVisible(true);
2963 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
2964 .getExplodedGeometry();
2965 if (geometry != null)
2967 newTopFrame.setSize(geometry.getSize());
2970 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
2971 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
2972 newBottomFrame.setSize(oldBottomFrame.getSize());
2973 newBottomFrame.setVisible(true);
2974 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
2975 .getExplodedGeometry();
2976 if (geometry != null)
2978 newBottomFrame.setSize(geometry.getSize());
2981 topPanel.av.setGatherViewsHere(false);
2982 bottomPanel.av.setGatherViewsHere(false);
2983 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
2985 if (geometry != null)
2987 splitFrame.setLocation(geometry.getLocation());
2989 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
2993 * Clear references to the panels (now relocated in the new SplitFrames)
2994 * before closing the old SplitFrame.
2997 bottomPanels.clear();
3002 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3003 * back into the given SplitFrame as additional views. Note that the gathered
3004 * frames may themselves have multiple views.
3008 public void gatherViews(GSplitFrame source)
3011 * special handling of explodedGeometry for a view within a SplitFrame: - it
3012 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3013 * height) of the AlignFrame component
3015 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3016 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3017 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3018 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3019 myBottomFrame.viewport
3020 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3021 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3022 myTopFrame.viewport.setGatherViewsHere(true);
3023 myBottomFrame.viewport.setGatherViewsHere(true);
3024 String topViewId = myTopFrame.viewport.getSequenceSetId();
3025 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3027 JInternalFrame[] frames = desktop.getAllFrames();
3028 for (JInternalFrame frame : frames)
3030 if (frame instanceof SplitFrame && frame != source)
3032 SplitFrame sf = (SplitFrame) frame;
3033 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3034 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3035 boolean gatherThis = false;
3036 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3038 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3039 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3040 if (topViewId.equals(topPanel.av.getSequenceSetId())
3041 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3044 topPanel.av.setGatherViewsHere(false);
3045 bottomPanel.av.setGatherViewsHere(false);
3046 topPanel.av.setExplodedGeometry(
3047 new Rectangle(sf.getLocation(), topFrame.getSize()));
3048 bottomPanel.av.setExplodedGeometry(
3049 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3050 myTopFrame.addAlignmentPanel(topPanel, false);
3051 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3057 topFrame.getAlignPanels().clear();
3058 bottomFrame.getAlignPanels().clear();
3065 * The dust settles...give focus to the tab we did this from.
3067 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3070 public static groovy.ui.Console getGroovyConsole()
3072 return groovyConsole;
3076 * handles the payload of a drag and drop event.
3078 * TODO refactor to desktop utilities class
3081 * - Data source strings extracted from the drop event
3083 * - protocol for each data source extracted from the drop event
3087 * - the payload from the drop event
3090 public static void transferFromDropTarget(List<Object> files,
3091 List<DataSourceType> protocols, DropTargetDropEvent evt,
3092 Transferable t) throws Exception
3095 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3097 // DataFlavor[] flavors = t.getTransferDataFlavors();
3098 // for (int i = 0; i < flavors.length; i++) {
3099 // if (flavors[i].isFlavorJavaFileListType()) {
3100 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3101 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3102 // for (int j = 0; j < list.size(); j++) {
3103 // File file = (File) list.get(j);
3104 // byte[] data = getDroppedFileBytes(file);
3105 // fileName.setText(file.getName() + " - " + data.length + " " +
3106 // evt.getLocation());
3107 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3108 // target.setText(new String(data));
3110 // dtde.dropComplete(true);
3115 DataFlavor uriListFlavor = new DataFlavor(
3116 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3119 urlFlavour = new DataFlavor(
3120 "application/x-java-url; class=java.net.URL");
3121 } catch (ClassNotFoundException cfe)
3123 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3126 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3131 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3132 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3133 // means url may be null.
3136 protocols.add(DataSourceType.URL);
3137 files.add(url.toString());
3138 Cache.log.debug("Drop handled as URL dataflavor "
3139 + files.get(files.size() - 1));
3144 if (Platform.isAMacAndNotJS())
3147 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3150 } catch (Throwable ex)
3152 Cache.log.debug("URL drop handler failed.", ex);
3155 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3157 // Works on Windows and MacOSX
3158 Cache.log.debug("Drop handled as javaFileListFlavor");
3159 for (Object file : (List) t
3160 .getTransferData(DataFlavor.javaFileListFlavor))
3163 protocols.add(DataSourceType.FILE);
3168 // Unix like behaviour
3169 boolean added = false;
3171 if (t.isDataFlavorSupported(uriListFlavor))
3173 Cache.log.debug("Drop handled as uriListFlavor");
3174 // This is used by Unix drag system
3175 data = (String) t.getTransferData(uriListFlavor);
3179 // fallback to text: workaround - on OSX where there's a JVM bug
3180 Cache.log.debug("standard URIListFlavor failed. Trying text");
3181 // try text fallback
3182 DataFlavor textDf = new DataFlavor(
3183 "text/plain;class=java.lang.String");
3184 if (t.isDataFlavorSupported(textDf))
3186 data = (String) t.getTransferData(textDf);
3189 Cache.log.debug("Plain text drop content returned "
3190 + (data == null ? "Null - failed" : data));
3195 while (protocols.size() < files.size())
3197 Cache.log.debug("Adding missing FILE protocol for "
3198 + files.get(protocols.size()));
3199 protocols.add(DataSourceType.FILE);
3201 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3202 data, "\r\n"); st.hasMoreTokens();)
3205 String s = st.nextToken();
3206 if (s.startsWith("#"))
3208 // the line is a comment (as per the RFC 2483)
3211 java.net.URI uri = new java.net.URI(s);
3212 if (uri.getScheme().toLowerCase().startsWith("http"))
3214 protocols.add(DataSourceType.URL);
3215 files.add(uri.toString());
3219 // otherwise preserve old behaviour: catch all for file objects
3220 java.io.File file = new java.io.File(uri);
3221 protocols.add(DataSourceType.FILE);
3222 files.add(file.toString());
3227 if (Cache.log.isDebugEnabled())
3229 if (data == null || !added)
3232 if (t.getTransferDataFlavors() != null
3233 && t.getTransferDataFlavors().length > 0)
3236 "Couldn't resolve drop data. Here are the supported flavors:");
3237 for (DataFlavor fl : t.getTransferDataFlavors())
3240 "Supported transfer dataflavor: " + fl.toString());
3241 Object df = t.getTransferData(fl);
3244 Cache.log.debug("Retrieves: " + df);
3248 Cache.log.debug("Retrieved nothing");
3254 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3260 if (Platform.isWindowsAndNotJS())
3262 Cache.log.debug("Scanning dropped content for Windows Link Files");
3264 // resolve any .lnk files in the file drop
3265 for (int f = 0; f < files.size(); f++)
3267 String source = files.get(f).toString().toLowerCase();
3268 if (protocols.get(f).equals(DataSourceType.FILE)
3269 && (source.endsWith(".lnk") || source.endsWith(".url")
3270 || source.endsWith(".site")))
3274 Object obj = files.get(f);
3275 File lf = (obj instanceof File ? (File) obj
3276 : new File((String) obj));
3277 // process link file to get a URL
3278 Cache.log.debug("Found potential link file: " + lf);
3279 WindowsShortcut wscfile = new WindowsShortcut(lf);
3280 String fullname = wscfile.getRealFilename();
3281 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3282 files.set(f, fullname);
3283 Cache.log.debug("Parsed real filename " + fullname
3284 + " to extract protocol: " + protocols.get(f));
3285 } catch (Exception ex)
3288 "Couldn't parse " + files.get(f) + " as a link file.",
3297 * Sets the Preferences property for experimental features to True or False
3298 * depending on the state of the controlling menu item
3301 protected void showExperimental_actionPerformed(boolean selected)
3303 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3307 * Answers a (possibly empty) list of any structure viewer frames (currently for
3308 * either Jmol or Chimera) which are currently open. This may optionally be
3309 * restricted to viewers of a specified class, or viewers linked to a specified
3313 * if not null, only return viewers linked to this panel
3314 * @param structureViewerClass
3315 * if not null, only return viewers of this class
3318 public List<StructureViewerBase> getStructureViewers(
3319 AlignmentPanel apanel,
3320 Class<? extends StructureViewerBase> structureViewerClass)
3322 List<StructureViewerBase> result = new ArrayList<>();
3323 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3325 for (JInternalFrame frame : frames)
3327 if (frame instanceof StructureViewerBase)
3329 if (structureViewerClass == null
3330 || structureViewerClass.isInstance(frame))
3333 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3335 result.add((StructureViewerBase) frame);