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 (Exception e)
375 System.out.println("Cannot set APQHandlers");
376 // e.printStackTrace();
377 } catch (Throwable t)
379 System.out.println("Cannot set APQHandlers");
380 // t.printStackTrace();
384 addWindowListener(new WindowAdapter()
388 public void windowClosing(WindowEvent ev)
394 boolean selmemusage = Cache.getDefault("SHOW_MEMUSAGE",
397 boolean showjconsole = Cache.getDefault("SHOW_JAVA_CONSOLE",
399 desktop = new MyDesktopPane(selmemusage);
401 showMemusage.setSelected(selmemusage);
402 desktop.setBackground(Color.white);
404 getContentPane().setLayout(new BorderLayout());
405 // alternate config - have scrollbars - see notes in JAL-153
406 // JScrollPane sp = new JScrollPane();
407 // sp.getViewport().setView(desktop);
408 // getContentPane().add(sp, BorderLayout.CENTER);
410 // BH 2018 - just an experiment to try unclipped JInternalFrames.
413 getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
416 getContentPane().add(desktop, BorderLayout.CENTER);
417 desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
419 // This line prevents Windows Look&Feel resizing all new windows to maximum
420 // if previous window was maximised
421 desktop.setDesktopManager(new MyDesktopManager(
422 (Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
423 : Platform.isAMacAndNotJS()
424 ? new AquaInternalFrameManager(
425 desktop.getDesktopManager())
426 : desktop.getDesktopManager())));
428 Rectangle dims = getLastKnownDimensions("");
435 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
436 int xPos = Math.max(5, (screenSize.width - 900) / 2);
437 int yPos = Math.max(5, (screenSize.height - 650) / 2);
438 setBounds(xPos, yPos, 900, 650);
441 if (!Platform.isJS())
449 jconsole = new Console(this, showjconsole);
450 // add essential build information
451 jconsole.setHeader("Jalview Version: "
452 + Cache.getProperty("VERSION") + "\n"
453 + "Jalview Installation: "
454 + Cache.getDefault("INSTALLATION", "unknown")
455 + "\n" + "Build Date: "
456 + Cache.getDefault("BUILD_DATE", "unknown") + "\n"
457 + "Java version: " + System.getProperty("java.version") + "\n"
458 + System.getProperty("os.arch") + " "
459 + System.getProperty("os.name") + " "
460 + System.getProperty("os.version"));
462 showConsole(showjconsole);
464 showNews.setVisible(false);
466 experimentalFeatures.setSelected(showExperimental());
468 getIdentifiersOrgData();
472 // Spawn a thread that shows the splashscreen
474 SwingUtilities.invokeLater(new Runnable()
483 // Thread off a new instance of the file chooser - this reduces the time
485 // takes to open it later on.
486 new Thread(new Runnable()
491 Cache.log.debug("Filechooser init thread started.");
492 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
493 JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
495 Cache.log.debug("Filechooser init thread finished.");
498 // Add the service change listener
499 changeSupport.addJalviewPropertyChangeListener("services",
500 new PropertyChangeListener()
504 public void propertyChange(PropertyChangeEvent evt)
506 Cache.log.debug("Firing service changed event for "
507 + evt.getNewValue());
508 JalviewServicesChanged(evt);
513 this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
515 this.addWindowListener(new WindowAdapter()
518 public void windowClosing(WindowEvent evt)
525 this.addMouseListener(ma = new MouseAdapter()
528 public void mousePressed(MouseEvent evt)
530 if (evt.isPopupTrigger()) // Mac
532 showPasteMenu(evt.getX(), evt.getY());
537 public void mouseReleased(MouseEvent evt)
539 if (evt.isPopupTrigger()) // Windows
541 showPasteMenu(evt.getX(), evt.getY());
545 desktop.addMouseListener(ma);
550 * Answers true if user preferences to enable experimental features is True
555 public boolean showExperimental()
557 String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
558 Boolean.FALSE.toString());
559 return Boolean.valueOf(experimental).booleanValue();
562 public void doConfigureStructurePrefs()
564 // configure services
565 StructureSelectionManager ssm = StructureSelectionManager
566 .getStructureSelectionManager(this);
567 if (Cache.getDefault(Preferences.ADD_SS_ANN, true))
569 ssm.setAddTempFacAnnot(Cache
570 .getDefault(Preferences.ADD_TEMPFACT_ANN, true));
571 ssm.setProcessSecondaryStructure(Cache
572 .getDefault(Preferences.STRUCT_FROM_PDB, true));
573 ssm.setSecStructServices(
574 Cache.getDefault(Preferences.USE_RNAVIEW, true));
578 ssm.setAddTempFacAnnot(false);
579 ssm.setProcessSecondaryStructure(false);
580 ssm.setSecStructServices(false);
584 public void checkForNews()
586 final Desktop me = this;
587 // Thread off the news reader, in case there are connection problems.
588 new Thread(new Runnable()
593 Cache.log.debug("Starting news thread.");
594 jvnews = new BlogReader(me);
595 showNews.setVisible(true);
596 Cache.log.debug("Completed news thread.");
601 public void getIdentifiersOrgData()
603 // Thread off the identifiers fetcher
604 new Thread(new Runnable()
609 Cache.log.debug("Downloading data from identifiers.org");
612 UrlDownloadClient.download(IdOrgSettings.getUrl(),
613 IdOrgSettings.getDownloadLocation());
614 } catch (IOException e)
616 Cache.log.debug("Exception downloading identifiers.org data"
625 protected void showNews_actionPerformed(ActionEvent e)
627 showNews(showNews.isSelected());
630 void showNews(boolean visible)
632 Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
633 showNews.setSelected(visible);
634 if (visible && !jvnews.isVisible())
636 new Thread(new Runnable()
641 long now = System.currentTimeMillis();
642 Desktop.instance.setProgressBar(
643 MessageManager.getString("status.refreshing_news"), now);
644 jvnews.refreshNews();
645 Desktop.instance.setProgressBar(null, now);
653 * recover the last known dimensions for a jalview window
656 * - empty string is desktop, all other windows have unique prefix
657 * @return null or last known dimensions scaled to current geometry (if last
658 * window geom was known)
660 Rectangle getLastKnownDimensions(String windowName)
662 // TODO: lock aspect ratio for scaling desktop Bug #0058199
663 Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
664 String x = Cache.getProperty(windowName + "SCREEN_X");
665 String y = Cache.getProperty(windowName + "SCREEN_Y");
667 .getProperty(windowName + "SCREEN_WIDTH");
668 String height = Cache
669 .getProperty(windowName + "SCREEN_HEIGHT");
670 if ((x != null) && (y != null) && (width != null) && (height != null))
672 int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
673 iw = Integer.parseInt(width), ih = Integer.parseInt(height);
674 if (Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
676 // attempt #1 - try to cope with change in screen geometry - this
677 // version doesn't preserve original jv aspect ratio.
678 // take ratio of current screen size vs original screen size.
679 double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
680 Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
681 double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
682 Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
683 // rescale the bounds depending upon the current screen geometry.
684 ix = (int) (ix * sw);
685 iw = (int) (iw * sw);
686 iy = (int) (iy * sh);
687 ih = (int) (ih * sh);
688 while (ix >= screenSize.width)
691 "Window geometry location recall error: shifting horizontal to within screenbounds.");
692 ix -= screenSize.width;
694 while (iy >= screenSize.height)
697 "Window geometry location recall error: shifting vertical to within screenbounds.");
698 iy -= screenSize.height;
701 "Got last known dimensions for " + windowName + ": x:" + ix
702 + " y:" + iy + " width:" + iw + " height:" + ih);
704 // return dimensions for new instance
705 return new Rectangle(ix, iy, iw, ih);
710 void showPasteMenu(int x, int y)
712 JPopupMenu popup = new JPopupMenu();
713 JMenuItem item = new JMenuItem(
714 MessageManager.getString("label.paste_new_window"));
715 item.addActionListener(new ActionListener()
718 public void actionPerformed(ActionEvent evt)
725 popup.show(this, x, y);
732 Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
733 Transferable contents = c.getContents(this);
735 if (contents != null)
737 String file = (String) contents
738 .getTransferData(DataFlavor.stringFlavor);
740 FileFormatI format = new IdentifyFile().identify(file,
741 DataSourceType.PASTE);
743 new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
746 } catch (Exception ex)
749 "Unable to paste alignment from system clipboard:\n" + ex);
754 * Adds and opens the given frame to the desktop
765 public static synchronized void addInternalFrame(
766 final JInternalFrame frame, String title, int w, int h)
768 addInternalFrame(frame, title, true, w, h, true, false);
772 * Add an internal frame to the Jalview desktop
779 * When true, display frame immediately, otherwise, caller must call
780 * setVisible themselves.
786 public static synchronized void addInternalFrame(
787 final JInternalFrame frame, String title, boolean makeVisible,
790 addInternalFrame(frame, title, makeVisible, w, h, true, false);
794 * Add an internal frame to the Jalview desktop and make it visible
807 public static synchronized void addInternalFrame(
808 final JInternalFrame frame, String title, int w, int h,
811 addInternalFrame(frame, title, true, w, h, resizable, false);
815 * Add an internal frame to the Jalview desktop
822 * When true, display frame immediately, otherwise, caller must call
823 * setVisible themselves.
830 * @param ignoreMinSize
831 * Do not set the default minimum size for frame
833 public static synchronized void addInternalFrame(
834 final JInternalFrame frame, String title, boolean makeVisible,
835 int w, int h, boolean resizable, boolean ignoreMinSize)
838 // TODO: allow callers to determine X and Y position of frame (eg. via
840 // TODO: consider fixing method to update entries in the window submenu with
841 // the current window title
843 frame.setTitle(title);
844 if (frame.getWidth() < 1 || frame.getHeight() < 1)
848 // THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
849 // A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
850 // IF JALVIEW IS RUNNING HEADLESS
851 // ///////////////////////////////////////////////
852 if (instance == null || (System.getProperty("java.awt.headless") != null
853 && System.getProperty("java.awt.headless").equals("true")))
862 frame.setMinimumSize(
863 new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
865 // Set default dimension for Alignment Frame window.
866 // The Alignment Frame window could be added from a number of places,
868 // I did this here in order not to miss out on any Alignment frame.
869 if (frame instanceof AlignFrame)
871 frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
872 ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
876 frame.setVisible(makeVisible);
877 frame.setClosable(true);
878 frame.setResizable(resizable);
879 frame.setMaximizable(resizable);
880 frame.setIconifiable(resizable);
881 frame.setOpaque(Platform.isJS());
883 if (frame.getX() < 1 && frame.getY() < 1)
885 frame.setLocation(xOffset * openFrameCount,
886 yOffset * ((openFrameCount - 1) % 10) + yOffset);
890 * add an entry for the new frame in the Window menu
891 * (and remove it when the frame is closed)
893 final JMenuItem menuItem = new JMenuItem(title);
894 frame.addInternalFrameListener(new InternalFrameAdapter()
897 public void internalFrameActivated(InternalFrameEvent evt)
899 JInternalFrame itf = desktop.getSelectedFrame();
902 if (itf instanceof AlignFrame)
904 Jalview.setCurrentAlignFrame((AlignFrame) itf);
911 public void internalFrameClosed(InternalFrameEvent evt)
913 PaintRefresher.RemoveComponent(frame);
916 * defensive check to prevent frames being
917 * added half off the window
919 if (openFrameCount > 0)
925 * ensure no reference to alignFrame retained by menu item listener
927 if (menuItem.getActionListeners().length > 0)
929 menuItem.removeActionListener(menuItem.getActionListeners()[0]);
931 windowMenu.remove(menuItem);
935 menuItem.addActionListener(new ActionListener()
938 public void actionPerformed(ActionEvent e)
942 frame.setSelected(true);
943 frame.setIcon(false);
944 } catch (java.beans.PropertyVetoException ex)
946 // System.err.println(ex.toString());
951 setKeyBindings(frame);
955 windowMenu.add(menuItem);
960 frame.setSelected(true);
961 frame.requestFocus();
962 } catch (java.beans.PropertyVetoException ve)
964 } catch (java.lang.ClassCastException cex)
967 "Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
973 * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close the
978 private static void setKeyBindings(JInternalFrame frame)
980 @SuppressWarnings("serial")
981 final Action closeAction = new AbstractAction()
984 public void actionPerformed(ActionEvent e)
991 * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
993 KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
994 InputEvent.CTRL_DOWN_MASK);
995 KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
996 ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx());
998 InputMap inputMap = frame
999 .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
1000 String ctrlW = ctrlWKey.toString();
1001 inputMap.put(ctrlWKey, ctrlW);
1002 inputMap.put(cmdWKey, ctrlW);
1004 ActionMap actionMap = frame.getActionMap();
1005 actionMap.put(ctrlW, closeAction);
1009 public void lostOwnership(Clipboard clipboard, Transferable contents)
1013 Desktop.jalviewClipboard = null;
1016 internalCopy = false;
1020 public void dragEnter(DropTargetDragEvent evt)
1025 public void dragExit(DropTargetEvent evt)
1030 public void dragOver(DropTargetDragEvent evt)
1035 public void dropActionChanged(DropTargetDragEvent evt)
1046 public void drop(DropTargetDropEvent evt)
1048 boolean success = true;
1049 // JAL-1552 - acceptDrop required before getTransferable call for
1050 // Java's Transferable for native dnd
1051 evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
1052 Transferable t = evt.getTransferable();
1053 List<Object> files = new ArrayList<>();
1054 List<DataSourceType> protocols = new ArrayList<>();
1058 Desktop.transferFromDropTarget(files, protocols, evt, t);
1059 } catch (Exception e)
1061 e.printStackTrace();
1069 for (int i = 0; i < files.size(); i++)
1071 // BH 2018 File or String
1072 Object file = files.get(i);
1073 String fileName = file.toString();
1074 DataSourceType protocol = (protocols == null)
1075 ? DataSourceType.FILE
1077 FileFormatI format = null;
1079 if (fileName.endsWith(".jar"))
1081 format = FileFormat.Jalview;
1086 format = new IdentifyFile().identify(file, protocol);
1088 if (file instanceof File)
1090 Platform.cacheFileData((File) file);
1092 new FileLoader().LoadFile(null, file, protocol, format);
1095 } catch (Exception ex)
1100 evt.dropComplete(success); // need this to ensure input focus is properly
1101 // transfered to any new windows created
1111 public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport)
1113 String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
1114 JalviewFileChooser chooser = JalviewFileChooser
1115 .forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat, BackupFiles.getEnabled());
1117 chooser.setFileView(new JalviewFileView());
1118 chooser.setDialogTitle(
1119 MessageManager.getString("label.open_local_file"));
1120 chooser.setToolTipText(MessageManager.getString("action.open"));
1122 chooser.setResponseHandler(0, new Runnable()
1127 File selectedFile = chooser.getSelectedFile();
1128 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1130 FileFormatI format = chooser.getSelectedFormat();
1133 * Call IdentifyFile to verify the file contains what its extension implies.
1134 * Skip this step for dynamically added file formats, because
1135 * IdentifyFile does not know how to recognise them.
1137 if (FileFormats.getInstance().isIdentifiable(format))
1141 format = new IdentifyFile().identify(selectedFile,
1142 DataSourceType.FILE);
1143 } catch (FileFormatException e)
1145 // format = null; //??
1149 new FileLoader().LoadFile(viewport, selectedFile,
1150 DataSourceType.FILE, format);
1153 chooser.showOpenDialog(this);
1157 * Shows a dialog for input of a URL at which to retrieve alignment data
1162 public void inputURLMenuItem_actionPerformed(AlignViewport viewport)
1164 // This construct allows us to have a wider textfield
1166 JLabel label = new JLabel(
1167 MessageManager.getString("label.input_file_url"));
1169 JPanel panel = new JPanel(new GridLayout(2, 1));
1173 * the URL to fetch is
1174 * Java: an editable combobox with history
1175 * JS: (pending JAL-3038) a plain text field
1178 String urlBase = "http://www.";
1179 if (Platform.isJS())
1181 history = new JTextField(urlBase, 35);
1190 JComboBox<String> asCombo = new JComboBox<>();
1191 asCombo.setPreferredSize(new Dimension(400, 20));
1192 asCombo.setEditable(true);
1193 asCombo.addItem(urlBase);
1194 String historyItems = Cache.getProperty("RECENT_URL");
1195 if (historyItems != null)
1197 for (String token : historyItems.split("\\t"))
1199 asCombo.addItem(token);
1206 Object[] options = new Object[] { MessageManager.getString("action.ok"),
1207 MessageManager.getString("action.cancel") };
1208 Runnable action = new Runnable()
1213 @SuppressWarnings("unchecked")
1214 String url = (history instanceof JTextField
1215 ? ((JTextField) history).getText()
1216 : ((JComboBox<String>) history).getSelectedItem()
1219 if (url.toLowerCase().endsWith(".jar"))
1221 if (viewport != null)
1223 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1224 FileFormat.Jalview);
1228 new FileLoader().LoadFile(url, DataSourceType.URL,
1229 FileFormat.Jalview);
1234 FileFormatI format = null;
1237 format = new IdentifyFile().identify(url, DataSourceType.URL);
1238 } catch (FileFormatException e)
1240 // TODO revise error handling, distinguish between
1241 // URL not found and response not valid
1246 String msg = MessageManager
1247 .formatMessage("label.couldnt_locate", url);
1248 JvOptionPane.showInternalMessageDialog(Desktop.desktop, msg,
1249 MessageManager.getString("label.url_not_found"),
1250 JvOptionPane.WARNING_MESSAGE);
1255 if (viewport != null)
1257 new FileLoader().LoadFile(viewport, url, DataSourceType.URL,
1262 new FileLoader().LoadFile(url, DataSourceType.URL, format);
1267 String dialogOption = MessageManager
1268 .getString("label.input_alignment_from_url");
1269 JvOptionPane.newOptionDialog(desktop).setResponseHandler(0, action)
1270 .showInternalDialog(panel, dialogOption,
1271 JvOptionPane.YES_NO_CANCEL_OPTION,
1272 JvOptionPane.PLAIN_MESSAGE, null, options,
1273 MessageManager.getString("action.ok"));
1277 * Opens the CutAndPaste window for the user to paste an alignment in to
1280 * - if not null, the pasted alignment is added to the current
1281 * alignment; if null, to a new alignment window
1284 public void inputTextboxMenuItem_actionPerformed(
1285 AlignmentViewPanel viewPanel)
1287 CutAndPasteTransfer cap = new CutAndPasteTransfer();
1288 cap.setForInput(viewPanel);
1289 Desktop.addInternalFrame(cap,
1290 MessageManager.getString("label.cut_paste_alignmen_file"), true,
1300 Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
1301 Cache.setProperty("SCREENGEOMETRY_WIDTH",
1303 Cache.setProperty("SCREENGEOMETRY_HEIGHT",
1304 screen.height + "");
1305 storeLastKnownDimensions("", new Rectangle(getBounds().x, getBounds().y,
1306 getWidth(), getHeight()));
1308 if (jconsole != null)
1310 storeLastKnownDimensions("JAVA_CONSOLE_", jconsole.getBounds());
1311 jconsole.stopConsole();
1315 storeLastKnownDimensions("JALVIEW_RSS_WINDOW_", jvnews.getBounds());
1318 if (dialogExecutor != null)
1320 dialogExecutor.shutdownNow();
1322 closeAll_actionPerformed(null);
1324 if (groovyConsole != null)
1326 // suppress a possible repeat prompt to save script
1327 groovyConsole.setDirty(false);
1328 groovyConsole.exit();
1333 private void storeLastKnownDimensions(String string, Rectangle jc)
1335 Cache.log.debug("Storing last known dimensions for "
1336 + string + ": x:" + jc.x + " y:" + jc.y + " width:" + jc.width
1337 + " height:" + jc.height);
1339 Cache.setProperty(string + "SCREEN_X", jc.x + "");
1340 Cache.setProperty(string + "SCREEN_Y", jc.y + "");
1341 Cache.setProperty(string + "SCREEN_WIDTH", jc.width + "");
1342 Cache.setProperty(string + "SCREEN_HEIGHT", jc.height + "");
1352 public void aboutMenuItem_actionPerformed(ActionEvent e)
1354 // StringBuffer message = getAboutMessage(false);
1355 // JvOptionPane.showInternalMessageDialog(Desktop.desktop,
1357 // message.toString(), "About Jalview", JvOptionPane.INFORMATION_MESSAGE);
1358 new Thread(new Runnable()
1363 new SplashScreen(true);
1368 public StringBuffer getAboutMessage(boolean shortv)
1370 StringBuffer message = new StringBuffer();
1371 message.append("<html>");
1374 message.append("<h1><strong>Version: "
1375 + Cache.getProperty("VERSION")
1376 + "</strong></h1>");
1377 message.append("<strong>Built: <em>"
1378 + Cache.getDefault("BUILD_DATE", "unknown") + "</em> from "
1379 + Cache.getBuildDetailsForSplash()
1386 message.append("<strong>Version "
1387 + Cache.getProperty("VERSION")
1388 + "; last updated: "
1389 + Cache.getDefault("BUILD_DATE", "unknown"));
1392 if (Cache.getDefault("LATEST_VERSION", "Checking")
1393 .equals("Checking"))
1395 // JBP removed this message for 2.11: May be reinstated in future version
1396 // message.append("<br>...Checking latest version...</br>");
1398 else if (!Cache.getDefault("LATEST_VERSION", "Checking")
1399 .equals(Cache.getProperty("VERSION")))
1401 boolean red = false;
1402 if (Cache.getProperty("VERSION").toLowerCase()
1403 .indexOf("automated build") == -1)
1406 // Displayed when code version and jnlp version do not match and code
1407 // version is not a development build
1408 message.append("<div style=\"color: #FF0000;font-style: bold;\">");
1411 message.append("<br>!! Version "
1412 + Cache.getDefault("LATEST_VERSION",
1414 + " is available for download from "
1415 + Cache.getDefault("www.jalview.org",
1416 "http://www.jalview.org")
1420 message.append("</div>");
1423 message.append("<br>Authors: " + Cache.getDefault(
1425 "The Jalview Authors (See AUTHORS file for current list)")
1426 + "<br><br>Development managed by The Barton Group, University of Dundee, Scotland, UK.<br>"
1427 + "<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"
1428 + "<br><br>If you use Jalview, please cite:"
1429 + "<br>Waterhouse, A.M., Procter, J.B., Martin, D.M.A, Clamp, M. and Barton, G. J. (2009)"
1430 + "<br>Jalview Version 2 - a multiple sequence alignment editor and analysis workbench"
1431 + "<br>Bioinformatics doi: 10.1093/bioinformatics/btp033"
1437 * Action on requesting Help documentation
1440 public void documentationMenuItem_actionPerformed()
1444 if (Platform.isJS())
1446 BrowserLauncher.openURL("http://www.jalview.org/help.html");
1455 Help.showHelpWindow();
1457 } catch (Exception ex)
1459 System.err.println("Error opening help: " + ex.getMessage());
1464 public void closeAll_actionPerformed(ActionEvent e)
1466 // TODO show a progress bar while closing?
1467 JInternalFrame[] frames = desktop.getAllFrames();
1468 for (int i = 0; i < frames.length; i++)
1472 frames[i].setClosed(true);
1473 } catch (java.beans.PropertyVetoException ex)
1477 Jalview.setCurrentAlignFrame(null);
1478 System.out.println("ALL CLOSED");
1481 * reset state of singleton objects as appropriate (clear down session state
1482 * when all windows are closed)
1484 StructureSelectionManager ssm = StructureSelectionManager
1485 .getStructureSelectionManager(this);
1493 public void raiseRelated_actionPerformed(ActionEvent e)
1495 reorderAssociatedWindows(false, false);
1499 public void minimizeAssociated_actionPerformed(ActionEvent e)
1501 reorderAssociatedWindows(true, false);
1504 void closeAssociatedWindows()
1506 reorderAssociatedWindows(false, true);
1512 * @seejalview.jbgui.GDesktop#garbageCollect_actionPerformed(java.awt.event.
1516 protected void garbageCollect_actionPerformed(ActionEvent e)
1518 // We simply collect the garbage
1519 Cache.log.debug("Collecting garbage...");
1521 Cache.log.debug("Finished garbage collection.");
1528 * jalview.jbgui.GDesktop#showMemusage_actionPerformed(java.awt.event.ActionEvent
1532 protected void showMemusage_actionPerformed(ActionEvent e)
1534 desktop.showMemoryUsage(showMemusage.isSelected());
1541 * jalview.jbgui.GDesktop#showConsole_actionPerformed(java.awt.event.ActionEvent
1545 protected void showConsole_actionPerformed(ActionEvent e)
1547 showConsole(showConsole.isSelected());
1550 Console jconsole = null;
1553 * control whether the java console is visible or not
1557 void showConsole(boolean selected)
1559 // TODO: decide if we should update properties file
1560 if (jconsole != null) // BH 2018
1562 showConsole.setSelected(selected);
1563 Cache.setProperty("SHOW_JAVA_CONSOLE",
1564 Boolean.valueOf(selected).toString());
1565 jconsole.setVisible(selected);
1569 void reorderAssociatedWindows(boolean minimize, boolean close)
1571 JInternalFrame[] frames = desktop.getAllFrames();
1572 if (frames == null || frames.length < 1)
1577 AlignmentViewport source = null, target = null;
1578 if (frames[0] instanceof AlignFrame)
1580 source = ((AlignFrame) frames[0]).getCurrentView();
1582 else if (frames[0] instanceof TreePanel)
1584 source = ((TreePanel) frames[0]).getViewPort();
1586 else if (frames[0] instanceof PCAPanel)
1588 source = ((PCAPanel) frames[0]).av;
1590 else if (frames[0].getContentPane() instanceof PairwiseAlignPanel)
1592 source = ((PairwiseAlignPanel) frames[0].getContentPane()).av;
1597 for (int i = 0; i < frames.length; i++)
1600 if (frames[i] == null)
1604 if (frames[i] instanceof AlignFrame)
1606 target = ((AlignFrame) frames[i]).getCurrentView();
1608 else if (frames[i] instanceof TreePanel)
1610 target = ((TreePanel) frames[i]).getViewPort();
1612 else if (frames[i] instanceof PCAPanel)
1614 target = ((PCAPanel) frames[i]).av;
1616 else if (frames[i].getContentPane() instanceof PairwiseAlignPanel)
1618 target = ((PairwiseAlignPanel) frames[i].getContentPane()).av;
1621 if (source == target)
1627 frames[i].setClosed(true);
1631 frames[i].setIcon(minimize);
1634 frames[i].toFront();
1638 } catch (java.beans.PropertyVetoException ex)
1653 protected void preferences_actionPerformed(ActionEvent e)
1659 * Prompts the user to choose a file and then saves the Jalview state as a
1660 * Jalview project file
1663 public void saveState_actionPerformed()
1665 saveState_actionPerformed(false);
1668 public void saveState_actionPerformed(boolean saveAs)
1670 java.io.File projectFile = getProjectFile();
1671 // autoSave indicates we already have a file and don't need to ask
1672 boolean autoSave = projectFile != null && !saveAs
1673 && BackupFiles.getEnabled();
1675 // System.out.println("autoSave="+autoSave+", projectFile='"+projectFile+"',
1676 // saveAs="+saveAs+", Backups
1677 // "+(BackupFiles.getEnabled()?"enabled":"disabled"));
1679 boolean approveSave = false;
1682 JalviewFileChooser chooser = new JalviewFileChooser("jvp",
1685 chooser.setFileView(new JalviewFileView());
1686 chooser.setDialogTitle(MessageManager.getString("label.save_state"));
1688 int value = chooser.showSaveDialog(this);
1690 if (value == JalviewFileChooser.APPROVE_OPTION)
1692 projectFile = chooser.getSelectedFile();
1693 setProjectFile(projectFile);
1698 if (approveSave || autoSave)
1700 final Desktop me = this;
1701 final java.io.File chosenFile = projectFile;
1702 new Thread(new Runnable()
1707 // TODO: refactor to Jalview desktop session controller action.
1708 setProgressBar(MessageManager.formatMessage(
1709 "label.saving_jalview_project", new Object[]
1710 { chosenFile.getName() }), chosenFile.hashCode());
1711 Cache.setProperty("LAST_DIRECTORY",
1712 chosenFile.getParent());
1713 // TODO catch and handle errors for savestate
1714 // TODO prevent user from messing with the Desktop whilst we're saving
1717 boolean doBackup = BackupFiles.getEnabled();
1718 BackupFiles backupfiles = doBackup ? new BackupFiles(chosenFile) : null;
1720 new Jalview2XML().saveState(doBackup ? backupfiles.getTempFile() : chosenFile);
1724 backupfiles.setWriteSuccess(true);
1725 backupfiles.rollBackupsAndRenameTempFile();
1727 } catch (OutOfMemoryError oom)
1729 new OOMWarning("Whilst saving current state to "
1730 + chosenFile.getName(), oom);
1731 } catch (Exception ex)
1733 Cache.log.error("Problems whilst trying to save to "
1734 + chosenFile.getName(), ex);
1735 JvOptionPane.showMessageDialog(me,
1736 MessageManager.formatMessage(
1737 "label.error_whilst_saving_current_state_to",
1739 { chosenFile.getName() }),
1740 MessageManager.getString("label.couldnt_save_project"),
1741 JvOptionPane.WARNING_MESSAGE);
1743 setProgressBar(null, chosenFile.hashCode());
1750 public void saveAsState_actionPerformed(ActionEvent e)
1752 saveState_actionPerformed(true);
1755 private void setProjectFile(File choice)
1757 this.projectFile = choice;
1760 public File getProjectFile()
1762 return this.projectFile;
1766 * Shows a file chooser dialog and tries to read in the selected file as a
1770 public void loadState_actionPerformed()
1772 final String[] suffix = new String[] { "jvp", "jar" };
1773 final String[] desc = new String[] { "Jalview Project",
1774 "Jalview Project (old)" };
1775 JalviewFileChooser chooser = new JalviewFileChooser(
1776 Cache.getProperty("LAST_DIRECTORY"), suffix, desc,
1777 "Jalview Project", true, BackupFiles.getEnabled()); // last two booleans: allFiles,
1779 chooser.setFileView(new JalviewFileView());
1780 chooser.setDialogTitle(MessageManager.getString("label.restore_state"));
1781 chooser.setResponseHandler(0, new Runnable()
1786 File selectedFile = chooser.getSelectedFile();
1787 setProjectFile(selectedFile);
1788 String choice = selectedFile.getAbsolutePath();
1789 Cache.setProperty("LAST_DIRECTORY", selectedFile.getParent());
1790 new Thread(new Runnable()
1797 new Jalview2XML().loadJalviewAlign(choice);
1798 } catch (OutOfMemoryError oom)
1800 new OOMWarning("Whilst loading project from " + choice, oom);
1801 } catch (Exception ex)
1804 "Problems whilst loading project from " + choice, ex);
1805 JvOptionPane.showMessageDialog(Desktop.desktop,
1806 MessageManager.formatMessage(
1807 "label.error_whilst_loading_project_from",
1810 MessageManager.getString("label.couldnt_load_project"),
1811 JvOptionPane.WARNING_MESSAGE);
1818 chooser.showOpenDialog(this);
1822 public void inputSequence_actionPerformed(ActionEvent e)
1824 new SequenceFetcher(this);
1827 JPanel progressPanel;
1829 ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
1831 public void startLoading(final Object fileName)
1833 if (fileLoadingCount == 0)
1835 fileLoadingPanels.add(addProgressPanel(MessageManager
1836 .formatMessage("label.loading_file", new Object[]
1842 private JPanel addProgressPanel(String string)
1844 if (progressPanel == null)
1846 progressPanel = new JPanel(new GridLayout(1, 1));
1847 totalProgressCount = 0;
1848 instance.getContentPane().add(progressPanel, BorderLayout.SOUTH);
1850 JPanel thisprogress = new JPanel(new BorderLayout(10, 5));
1851 JProgressBar progressBar = new JProgressBar();
1852 progressBar.setIndeterminate(true);
1854 thisprogress.add(new JLabel(string), BorderLayout.WEST);
1856 thisprogress.add(progressBar, BorderLayout.CENTER);
1857 progressPanel.add(thisprogress);
1858 ((GridLayout) progressPanel.getLayout()).setRows(
1859 ((GridLayout) progressPanel.getLayout()).getRows() + 1);
1860 ++totalProgressCount;
1861 instance.validate();
1862 return thisprogress;
1865 int totalProgressCount = 0;
1867 private void removeProgressPanel(JPanel progbar)
1869 if (progressPanel != null)
1871 synchronized (progressPanel)
1873 progressPanel.remove(progbar);
1874 GridLayout gl = (GridLayout) progressPanel.getLayout();
1875 gl.setRows(gl.getRows() - 1);
1876 if (--totalProgressCount < 1)
1878 this.getContentPane().remove(progressPanel);
1879 progressPanel = null;
1886 public void stopLoading()
1889 if (fileLoadingCount < 1)
1891 while (fileLoadingPanels.size() > 0)
1893 removeProgressPanel(fileLoadingPanels.remove(0));
1895 fileLoadingPanels.clear();
1896 fileLoadingCount = 0;
1901 public static int getViewCount(String alignmentId)
1903 AlignmentViewport[] aps = getViewports(alignmentId);
1904 return (aps == null) ? 0 : aps.length;
1909 * @param alignmentId
1910 * - if null, all sets are returned
1911 * @return all AlignmentPanels concerning the alignmentId sequence set
1913 public static AlignmentPanel[] getAlignmentPanels(String alignmentId)
1915 if (Desktop.desktop == null)
1917 // no frames created and in headless mode
1918 // TODO: verify that frames are recoverable when in headless mode
1921 List<AlignmentPanel> aps = new ArrayList<>();
1922 AlignFrame[] frames = getAlignFrames();
1927 for (AlignFrame af : frames)
1929 for (AlignmentPanel ap : af.alignPanels)
1931 if (alignmentId == null
1932 || alignmentId.equals(ap.av.getSequenceSetId()))
1938 if (aps.size() == 0)
1942 AlignmentPanel[] vap = aps.toArray(new AlignmentPanel[aps.size()]);
1947 * get all the viewports on an alignment.
1949 * @param sequenceSetId
1950 * unique alignment id (may be null - all viewports returned in that
1952 * @return all viewports on the alignment bound to sequenceSetId
1954 public static AlignmentViewport[] getViewports(String sequenceSetId)
1956 List<AlignmentViewport> viewp = new ArrayList<>();
1957 if (desktop != null)
1959 AlignFrame[] frames = Desktop.getAlignFrames();
1961 for (AlignFrame afr : frames)
1963 if (sequenceSetId == null || afr.getViewport().getSequenceSetId()
1964 .equals(sequenceSetId))
1966 if (afr.alignPanels != null)
1968 for (AlignmentPanel ap : afr.alignPanels)
1970 if (sequenceSetId == null
1971 || sequenceSetId.equals(ap.av.getSequenceSetId()))
1979 viewp.add(afr.getViewport());
1983 if (viewp.size() > 0)
1985 return viewp.toArray(new AlignmentViewport[viewp.size()]);
1992 * Explode the views in the given frame into separate AlignFrame
1996 public static void explodeViews(AlignFrame af)
1998 int size = af.alignPanels.size();
2004 for (int i = 0; i < size; i++)
2006 AlignmentPanel ap = af.alignPanels.get(i);
2007 AlignFrame newaf = new AlignFrame(ap);
2010 * Restore the view's last exploded frame geometry if known. Multiple
2011 * views from one exploded frame share and restore the same (frame)
2012 * position and size.
2014 Rectangle geometry = ap.av.getExplodedGeometry();
2015 if (geometry != null)
2017 newaf.setBounds(geometry);
2020 ap.av.setGatherViewsHere(false);
2022 addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
2023 AlignFrame.DEFAULT_HEIGHT);
2026 af.alignPanels.clear();
2027 af.closeMenuItem_actionPerformed(true);
2032 * Gather expanded views (separate AlignFrame's) with the same sequence set
2033 * identifier back in to this frame as additional views, and close the expanded
2034 * views. Note the expanded frames may themselves have multiple views. We take
2039 public void gatherViews(AlignFrame source)
2041 source.viewport.setGatherViewsHere(true);
2042 source.viewport.setExplodedGeometry(source.getBounds());
2043 JInternalFrame[] frames = desktop.getAllFrames();
2044 String viewId = source.viewport.getSequenceSetId();
2046 for (int t = 0; t < frames.length; t++)
2048 if (frames[t] instanceof AlignFrame && frames[t] != source)
2050 AlignFrame af = (AlignFrame) frames[t];
2051 boolean gatherThis = false;
2052 for (int a = 0; a < af.alignPanels.size(); a++)
2054 AlignmentPanel ap = af.alignPanels.get(a);
2055 if (viewId.equals(ap.av.getSequenceSetId()))
2058 ap.av.setGatherViewsHere(false);
2059 ap.av.setExplodedGeometry(af.getBounds());
2060 source.addAlignmentPanel(ap, false);
2066 af.alignPanels.clear();
2067 af.closeMenuItem_actionPerformed(true);
2073 public JInternalFrame[] getAllFrames()
2075 return desktop.getAllFrames();
2079 * Checks the given url to see if it gives a response indicating that the user
2080 * should be informed of a new questionnaire.
2084 public void checkForQuestionnaire(String url)
2086 UserQuestionnaireCheck jvq = new UserQuestionnaireCheck(url);
2087 // javax.swing.SwingUtilities.invokeLater(jvq);
2088 new Thread(jvq).start();
2091 public void checkURLLinks()
2093 // Thread off the URL link checker
2094 addDialogThread(new Runnable()
2099 if (Cache.getDefault("CHECKURLLINKS", true))
2101 // check what the actual links are - if it's just the default don't
2102 // bother with the warning
2103 List<String> links = Preferences.sequenceUrlLinks
2106 // only need to check links if there is one with a
2107 // SEQUENCE_ID which is not the default EMBL_EBI link
2108 ListIterator<String> li = links.listIterator();
2109 boolean check = false;
2110 List<JLabel> urls = new ArrayList<>();
2111 while (li.hasNext())
2113 String link = li.next();
2114 if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
2115 && !UrlConstants.isDefaultString(link))
2118 int barPos = link.indexOf("|");
2119 String urlMsg = barPos == -1 ? link
2120 : link.substring(0, barPos) + ": "
2121 + link.substring(barPos + 1);
2122 urls.add(new JLabel(urlMsg));
2130 // ask user to check in case URL links use old style tokens
2131 // ($SEQUENCE_ID$ for sequence id _or_ accession id)
2132 JPanel msgPanel = new JPanel();
2133 msgPanel.setLayout(new BoxLayout(msgPanel, BoxLayout.PAGE_AXIS));
2134 msgPanel.add(Box.createVerticalGlue());
2135 JLabel msg = new JLabel(MessageManager
2136 .getString("label.SEQUENCE_ID_for_DB_ACCESSION1"));
2137 JLabel msg2 = new JLabel(MessageManager
2138 .getString("label.SEQUENCE_ID_for_DB_ACCESSION2"));
2140 for (JLabel url : urls)
2146 final JCheckBox jcb = new JCheckBox(
2147 MessageManager.getString("label.do_not_display_again"));
2148 jcb.addActionListener(new ActionListener()
2151 public void actionPerformed(ActionEvent e)
2153 // update Cache settings for "don't show this again"
2154 boolean showWarningAgain = !jcb.isSelected();
2155 Cache.setProperty("CHECKURLLINKS",
2156 Boolean.valueOf(showWarningAgain).toString());
2161 JvOptionPane.showMessageDialog(Desktop.desktop, msgPanel,
2163 .getString("label.SEQUENCE_ID_no_longer_used"),
2164 JvOptionPane.WARNING_MESSAGE);
2171 * Proxy class for JDesktopPane which optionally displays the current memory
2172 * usage and highlights the desktop area with a red bar if free memory runs low.
2176 public class MyDesktopPane extends JDesktopPane
2179 private static final float ONE_MB = 1048576f;
2181 boolean showMemoryUsage = false;
2185 java.text.NumberFormat df;
2187 float maxMemory, allocatedMemory, freeMemory, totalFreeMemory,
2190 public MyDesktopPane(boolean showMemoryUsage)
2192 showMemoryUsage(showMemoryUsage);
2195 public void showMemoryUsage(boolean showMemory)
2197 this.showMemoryUsage = showMemory;
2200 Thread worker = new Thread(this);
2206 public boolean isShowMemoryUsage()
2208 return showMemoryUsage;
2214 df = java.text.NumberFormat.getNumberInstance();
2215 df.setMaximumFractionDigits(2);
2216 runtime = Runtime.getRuntime();
2218 while (showMemoryUsage)
2222 maxMemory = runtime.maxMemory() / ONE_MB;
2223 allocatedMemory = runtime.totalMemory() / ONE_MB;
2224 freeMemory = runtime.freeMemory() / ONE_MB;
2225 totalFreeMemory = freeMemory + (maxMemory - allocatedMemory);
2227 percentUsage = (totalFreeMemory / maxMemory) * 100;
2229 // if (percentUsage < 20)
2231 // border1 = BorderFactory.createMatteBorder(12, 12, 12, 12,
2233 // instance.set.setBorder(border1);
2236 // sleep after showing usage
2238 } catch (Exception ex)
2240 ex.printStackTrace();
2246 public void paintComponent(Graphics g)
2248 if (showMemoryUsage && g != null && df != null)
2250 if (percentUsage < 20)
2252 g.setColor(Color.red);
2254 FontMetrics fm = g.getFontMetrics();
2257 g.drawString(MessageManager.formatMessage("label.memory_stats",
2259 { df.format(totalFreeMemory), df.format(maxMemory),
2260 df.format(percentUsage) }),
2261 10, getHeight() - fm.getHeight());
2268 * Accessor method to quickly get all the AlignmentFrames loaded.
2270 * @return an array of AlignFrame, or null if none found
2272 public static AlignFrame[] getAlignFrames()
2274 if (Jalview.isHeadlessMode())
2276 // Desktop.desktop is null in headless mode
2277 return new AlignFrame[] { Jalview.currentAlignFrame };
2280 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2286 List<AlignFrame> avp = new ArrayList<>();
2288 for (int i = frames.length - 1; i > -1; i--)
2290 if (frames[i] instanceof AlignFrame)
2292 avp.add((AlignFrame) frames[i]);
2294 else if (frames[i] instanceof SplitFrame)
2297 * Also check for a split frame containing an AlignFrame
2299 GSplitFrame sf = (GSplitFrame) frames[i];
2300 if (sf.getTopFrame() instanceof AlignFrame)
2302 avp.add((AlignFrame) sf.getTopFrame());
2304 if (sf.getBottomFrame() instanceof AlignFrame)
2306 avp.add((AlignFrame) sf.getBottomFrame());
2310 if (avp.size() == 0)
2314 AlignFrame afs[] = avp.toArray(new AlignFrame[avp.size()]);
2319 * Returns an array of any AppJmol frames in the Desktop (or null if none).
2323 public GStructureViewer[] getJmols()
2325 JInternalFrame[] frames = Desktop.desktop.getAllFrames();
2331 List<GStructureViewer> avp = new ArrayList<>();
2333 for (int i = frames.length - 1; i > -1; i--)
2335 if (frames[i] instanceof AppJmol)
2337 GStructureViewer af = (GStructureViewer) frames[i];
2341 if (avp.size() == 0)
2345 GStructureViewer afs[] = avp.toArray(new GStructureViewer[avp.size()]);
2350 * Add Groovy Support to Jalview
2353 public void groovyShell_actionPerformed()
2357 openGroovyConsole();
2358 } catch (Exception ex)
2360 Cache.log.error("Groovy Shell Creation failed.", ex);
2361 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2363 MessageManager.getString("label.couldnt_create_groovy_shell"),
2364 MessageManager.getString("label.groovy_support_failed"),
2365 JvOptionPane.ERROR_MESSAGE);
2370 * Open the Groovy console
2372 void openGroovyConsole()
2374 if (groovyConsole == null)
2376 groovyConsole = new groovy.ui.Console();
2377 groovyConsole.setVariable("Jalview", this);
2378 groovyConsole.run();
2381 * We allow only one console at a time, so that AlignFrame menu option
2382 * 'Calculate | Run Groovy script' is unambiguous.
2383 * Disable 'Groovy Console', and enable 'Run script', when the console is
2384 * opened, and the reverse when it is closed
2386 Window window = (Window) groovyConsole.getFrame();
2387 window.addWindowListener(new WindowAdapter()
2390 public void windowClosed(WindowEvent e)
2393 * rebind CMD-Q from Groovy Console to Jalview Quit
2396 enableExecuteGroovy(false);
2402 * show Groovy console window (after close and reopen)
2404 ((Window) groovyConsole.getFrame()).setVisible(true);
2407 * if we got this far, enable 'Run Groovy' in AlignFrame menus
2408 * and disable opening a second console
2410 enableExecuteGroovy(true);
2414 * Bind Ctrl/Cmd-Q to Quit - for reset as Groovy Console takes over this binding
2417 protected void addQuitHandler()
2419 getRootPane().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
2420 .put(KeyStroke.getKeyStroke(KeyEvent.VK_Q,
2421 jalview.util.ShortcutKeyMaskExWrapper.getMenuShortcutKeyMaskEx()),
2423 getRootPane().getActionMap().put("Quit", new AbstractAction()
2426 public void actionPerformed(ActionEvent e)
2434 * Enable or disable 'Run Groovy script' in AlignFrame calculate menus
2437 * true if Groovy console is open
2439 public void enableExecuteGroovy(boolean enabled)
2442 * disable opening a second Groovy console
2443 * (or re-enable when the console is closed)
2445 groovyShell.setEnabled(!enabled);
2447 AlignFrame[] alignFrames = getAlignFrames();
2448 if (alignFrames != null)
2450 for (AlignFrame af : alignFrames)
2452 af.setGroovyEnabled(enabled);
2458 * Progress bars managed by the IProgressIndicator method.
2460 private Hashtable<Long, JPanel> progressBars;
2462 private Hashtable<Long, IProgressIndicatorHandler> progressBarHandlers;
2467 * @see jalview.gui.IProgressIndicator#setProgressBar(java.lang.String, long)
2470 public void setProgressBar(String message, long id)
2472 Platform.timeCheck("Desktop " + message, Platform.TIME_MARK);
2474 if (progressBars == null)
2476 progressBars = new Hashtable<>();
2477 progressBarHandlers = new Hashtable<>();
2480 if (progressBars.get(Long.valueOf(id)) != null)
2482 JPanel panel = progressBars.remove(Long.valueOf(id));
2483 if (progressBarHandlers.contains(Long.valueOf(id)))
2485 progressBarHandlers.remove(Long.valueOf(id));
2487 removeProgressPanel(panel);
2491 progressBars.put(Long.valueOf(id), addProgressPanel(message));
2498 * @see jalview.gui.IProgressIndicator#registerHandler(long,
2499 * jalview.gui.IProgressIndicatorHandler)
2502 public void registerHandler(final long id,
2503 final IProgressIndicatorHandler handler)
2505 if (progressBarHandlers == null
2506 || !progressBars.containsKey(Long.valueOf(id)))
2508 throw new Error(MessageManager.getString(
2509 "error.call_setprogressbar_before_registering_handler"));
2511 progressBarHandlers.put(Long.valueOf(id), handler);
2512 final JPanel progressPanel = progressBars.get(Long.valueOf(id));
2513 if (handler.canCancel())
2515 JButton cancel = new JButton(
2516 MessageManager.getString("action.cancel"));
2517 final IProgressIndicator us = this;
2518 cancel.addActionListener(new ActionListener()
2522 public void actionPerformed(ActionEvent e)
2524 handler.cancelActivity(id);
2525 us.setProgressBar(MessageManager
2526 .formatMessage("label.cancelled_params", new Object[]
2527 { ((JLabel) progressPanel.getComponent(0)).getText() }),
2531 progressPanel.add(cancel, BorderLayout.EAST);
2537 * @return true if any progress bars are still active
2540 public boolean operationInProgress()
2542 if (progressBars != null && progressBars.size() > 0)
2550 * This will return the first AlignFrame holding the given viewport instance. It
2551 * will break if there are more than one AlignFrames viewing a particular av.
2554 * @return alignFrame for viewport
2556 public static AlignFrame getAlignFrameFor(AlignViewportI viewport)
2558 if (desktop != null)
2560 AlignmentPanel[] aps = getAlignmentPanels(
2561 viewport.getSequenceSetId());
2562 for (int panel = 0; aps != null && panel < aps.length; panel++)
2564 if (aps[panel] != null && aps[panel].av == viewport)
2566 return aps[panel].alignFrame;
2573 public VamsasApplication getVamsasApplication()
2575 // TODO: JAL-3311 remove remaining code from Jalview relating to VAMSAS
2581 * flag set if jalview GUI is being operated programmatically
2583 private boolean inBatchMode = false;
2586 * check if jalview GUI is being operated programmatically
2588 * @return inBatchMode
2590 public boolean isInBatchMode()
2596 * set flag if jalview GUI is being operated programmatically
2598 * @param inBatchMode
2600 public void setInBatchMode(boolean inBatchMode)
2602 this.inBatchMode = inBatchMode;
2605 public void startServiceDiscovery()
2607 startServiceDiscovery(false);
2610 public void startServiceDiscovery(boolean blocking)
2612 boolean alive = true;
2613 Thread t0 = null, t1 = null, t2 = null;
2614 // JAL-940 - JALVIEW 1 services are now being EOLed as of JABA 2.1 release
2617 // todo: changesupport handlers need to be transferred
2618 if (discoverer == null)
2620 discoverer = new jalview.ws.jws1.Discoverer();
2621 // register PCS handler for desktop.
2622 discoverer.addPropertyChangeListener(changeSupport);
2624 // JAL-940 - disabled JWS1 service configuration - always start discoverer
2625 // until we phase out completely
2626 (t0 = new Thread(discoverer)).start();
2629 if (Cache.getDefault("SHOW_JWS2_SERVICES", true))
2631 t2 = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2632 .startDiscoverer(changeSupport);
2636 // TODO: do rest service discovery
2645 } catch (Exception e)
2648 alive = (t1 != null && t1.isAlive()) || (t2 != null && t2.isAlive())
2649 || (t3 != null && t3.isAlive())
2650 || (t0 != null && t0.isAlive());
2656 * called to check if the service discovery process completed successfully.
2660 protected void JalviewServicesChanged(PropertyChangeEvent evt)
2662 if (evt.getNewValue() == null || evt.getNewValue() instanceof Vector)
2664 final String ermsg = jalview.ws.jws2.Jws2Discoverer.getDiscoverer()
2665 .getErrorMessages();
2668 if (Cache.getDefault("SHOW_WSDISCOVERY_ERRORS", true))
2670 if (serviceChangedDialog == null)
2672 // only run if we aren't already displaying one of these.
2673 addDialogThread(serviceChangedDialog = new Runnable()
2680 * JalviewDialog jd =new JalviewDialog() {
2682 * @Override protected void cancelPressed() { // TODO
2683 * Auto-generated method stub
2685 * }@Override protected void okPressed() { // TODO
2686 * Auto-generated method stub
2688 * }@Override protected void raiseClosed() { // TODO
2689 * Auto-generated method stub
2691 * } }; jd.initDialogFrame(new
2692 * JLabel("<html><table width=\"450\"><tr><td>" + ermsg +
2693 * "<br/>It may be that you have invalid JABA URLs in your web service preferences,"
2694 * + " or mis-configured HTTP proxy settings.<br/>" +
2695 * "Check the <em>Connections</em> and <em>Web services</em> tab of the"
2697 * " Tools->Preferences dialog box to change them.</td></tr></table></html>"
2698 * ), true, true, "Web Service Configuration Problem", 450,
2701 * jd.waitForInput();
2703 JvOptionPane.showConfirmDialog(Desktop.desktop,
2704 new JLabel("<html><table width=\"450\"><tr><td>"
2705 + ermsg + "</td></tr></table>"
2706 + "<p>It may be that you have invalid JABA URLs<br/>in your web service preferences,"
2707 + "<br>or as a command-line argument, or mis-configured HTTP proxy settings.</p>"
2708 + "<p>Check the <em>Connections</em> and <em>Web services</em> tab<br/>of the"
2709 + " Tools->Preferences dialog box to change them.</p></html>"),
2710 "Web Service Configuration Problem",
2711 JvOptionPane.DEFAULT_OPTION,
2712 JvOptionPane.ERROR_MESSAGE);
2713 serviceChangedDialog = null;
2722 "Errors reported by JABA discovery service. Check web services preferences.\n"
2729 private Runnable serviceChangedDialog = null;
2732 * start a thread to open a URL in the configured browser. Pops up a warning
2733 * dialog to the user if there is an exception when calling out to the browser
2738 public static void showUrl(final String url)
2740 showUrl(url, Desktop.instance);
2744 * Like showUrl but allows progress handler to be specified
2748 * (null) or object implementing IProgressIndicator
2750 public static void showUrl(final String url,
2751 final IProgressIndicator progress)
2753 new Thread(new Runnable()
2760 if (progress != null)
2762 progress.setProgressBar(MessageManager
2763 .formatMessage("status.opening_params", new Object[]
2764 { url }), this.hashCode());
2766 jalview.util.BrowserLauncher.openURL(url);
2767 } catch (Exception ex)
2769 JvOptionPane.showInternalMessageDialog(Desktop.desktop,
2771 .getString("label.web_browser_not_found_unix"),
2772 MessageManager.getString("label.web_browser_not_found"),
2773 JvOptionPane.WARNING_MESSAGE);
2775 ex.printStackTrace();
2777 if (progress != null)
2779 progress.setProgressBar(null, this.hashCode());
2785 public static WsParamSetManager wsparamManager = null;
2787 public static ParamManager getUserParameterStore()
2789 if (wsparamManager == null)
2791 wsparamManager = new WsParamSetManager();
2793 return wsparamManager;
2797 * static hyperlink handler proxy method for use by Jalview's internal windows
2801 public static void hyperlinkUpdate(HyperlinkEvent e)
2803 if (e.getEventType() == EventType.ACTIVATED)
2808 url = e.getURL().toString();
2809 Desktop.showUrl(url);
2810 } catch (Exception x)
2814 if (Cache.log != null)
2816 Cache.log.error("Couldn't handle string " + url + " as a URL.");
2821 "Couldn't handle string " + url + " as a URL.");
2824 // ignore any exceptions due to dud links.
2831 * single thread that handles display of dialogs to user.
2833 ExecutorService dialogExecutor = Executors.newSingleThreadExecutor();
2836 * flag indicating if dialogExecutor should try to acquire a permit
2838 private volatile boolean dialogPause = true;
2843 private java.util.concurrent.Semaphore block = new Semaphore(0);
2845 private static groovy.ui.Console groovyConsole;
2848 * add another dialog thread to the queue
2852 public void addDialogThread(final Runnable prompter)
2854 dialogExecutor.submit(new Runnable()
2864 } catch (InterruptedException x)
2868 if (instance == null)
2874 SwingUtilities.invokeAndWait(prompter);
2875 } catch (Exception q)
2877 Cache.log.warn("Unexpected Exception in dialog thread.", q);
2883 public void startDialogQueue()
2885 // set the flag so we don't pause waiting for another permit and semaphore
2886 // the current task to begin
2887 dialogPause = false;
2892 * Outputs an image of the desktop to file in EPS format, after prompting the
2893 * user for choice of Text or Lineart character rendering (unless a preference
2894 * has been set). The file name is generated as
2897 * Jalview_snapshot_nnnnn.eps where nnnnn is the current timestamp in milliseconds
2901 protected void snapShotWindow_actionPerformed(ActionEvent e)
2903 // currently the menu option to do this is not shown
2906 int width = getWidth();
2907 int height = getHeight();
2909 "Jalview_snapshot_" + System.currentTimeMillis() + ".eps");
2910 ImageWriterI writer = new ImageWriterI()
2913 public void exportImage(Graphics g) throws Exception
2916 Cache.log.info("Successfully written snapshot to file "
2917 + of.getAbsolutePath());
2920 String title = "View of desktop";
2921 ImageExporter exporter = new ImageExporter(writer, null, TYPE.EPS,
2923 exporter.doExport(of, this, width, height, title);
2927 * Explode the views in the given SplitFrame into separate SplitFrame windows.
2928 * This respects (remembers) any previous 'exploded geometry' i.e. the size and
2929 * location last time the view was expanded (if any). However it does not
2930 * remember the split pane divider location - this is set to match the
2931 * 'exploding' frame.
2935 public void explodeViews(SplitFrame sf)
2937 AlignFrame oldTopFrame = (AlignFrame) sf.getTopFrame();
2938 AlignFrame oldBottomFrame = (AlignFrame) sf.getBottomFrame();
2939 List<? extends AlignmentViewPanel> topPanels = oldTopFrame
2941 List<? extends AlignmentViewPanel> bottomPanels = oldBottomFrame
2943 int viewCount = topPanels.size();
2950 * Processing in reverse order works, forwards order leaves the first panels
2951 * not visible. I don't know why!
2953 for (int i = viewCount - 1; i >= 0; i--)
2956 * Make new top and bottom frames. These take over the respective
2957 * AlignmentPanel objects, including their AlignmentViewports, so the
2958 * cdna/protein relationships between the viewports is carried over to the
2961 * explodedGeometry holds the (x, y) position of the previously exploded
2962 * SplitFrame, and the (width, height) of the AlignFrame component
2964 AlignmentPanel topPanel = (AlignmentPanel) topPanels.get(i);
2965 AlignFrame newTopFrame = new AlignFrame(topPanel);
2966 newTopFrame.setSize(oldTopFrame.getSize());
2967 newTopFrame.setVisible(true);
2968 Rectangle geometry = ((AlignViewport) topPanel.getAlignViewport())
2969 .getExplodedGeometry();
2970 if (geometry != null)
2972 newTopFrame.setSize(geometry.getSize());
2975 AlignmentPanel bottomPanel = (AlignmentPanel) bottomPanels.get(i);
2976 AlignFrame newBottomFrame = new AlignFrame(bottomPanel);
2977 newBottomFrame.setSize(oldBottomFrame.getSize());
2978 newBottomFrame.setVisible(true);
2979 geometry = ((AlignViewport) bottomPanel.getAlignViewport())
2980 .getExplodedGeometry();
2981 if (geometry != null)
2983 newBottomFrame.setSize(geometry.getSize());
2986 topPanel.av.setGatherViewsHere(false);
2987 bottomPanel.av.setGatherViewsHere(false);
2988 JInternalFrame splitFrame = new SplitFrame(newTopFrame,
2990 if (geometry != null)
2992 splitFrame.setLocation(geometry.getLocation());
2994 Desktop.addInternalFrame(splitFrame, sf.getTitle(), -1, -1);
2998 * Clear references to the panels (now relocated in the new SplitFrames)
2999 * before closing the old SplitFrame.
3002 bottomPanels.clear();
3007 * Gather expanded split frames, sharing the same pairs of sequence set ids,
3008 * back into the given SplitFrame as additional views. Note that the gathered
3009 * frames may themselves have multiple views.
3013 public void gatherViews(GSplitFrame source)
3016 * special handling of explodedGeometry for a view within a SplitFrame: - it
3017 * holds the (x, y) position of the enclosing SplitFrame, and the (width,
3018 * height) of the AlignFrame component
3020 AlignFrame myTopFrame = (AlignFrame) source.getTopFrame();
3021 AlignFrame myBottomFrame = (AlignFrame) source.getBottomFrame();
3022 myTopFrame.viewport.setExplodedGeometry(new Rectangle(source.getX(),
3023 source.getY(), myTopFrame.getWidth(), myTopFrame.getHeight()));
3024 myBottomFrame.viewport
3025 .setExplodedGeometry(new Rectangle(source.getX(), source.getY(),
3026 myBottomFrame.getWidth(), myBottomFrame.getHeight()));
3027 myTopFrame.viewport.setGatherViewsHere(true);
3028 myBottomFrame.viewport.setGatherViewsHere(true);
3029 String topViewId = myTopFrame.viewport.getSequenceSetId();
3030 String bottomViewId = myBottomFrame.viewport.getSequenceSetId();
3032 JInternalFrame[] frames = desktop.getAllFrames();
3033 for (JInternalFrame frame : frames)
3035 if (frame instanceof SplitFrame && frame != source)
3037 SplitFrame sf = (SplitFrame) frame;
3038 AlignFrame topFrame = (AlignFrame) sf.getTopFrame();
3039 AlignFrame bottomFrame = (AlignFrame) sf.getBottomFrame();
3040 boolean gatherThis = false;
3041 for (int a = 0; a < topFrame.alignPanels.size(); a++)
3043 AlignmentPanel topPanel = topFrame.alignPanels.get(a);
3044 AlignmentPanel bottomPanel = bottomFrame.alignPanels.get(a);
3045 if (topViewId.equals(topPanel.av.getSequenceSetId())
3046 && bottomViewId.equals(bottomPanel.av.getSequenceSetId()))
3049 topPanel.av.setGatherViewsHere(false);
3050 bottomPanel.av.setGatherViewsHere(false);
3051 topPanel.av.setExplodedGeometry(
3052 new Rectangle(sf.getLocation(), topFrame.getSize()));
3053 bottomPanel.av.setExplodedGeometry(
3054 new Rectangle(sf.getLocation(), bottomFrame.getSize()));
3055 myTopFrame.addAlignmentPanel(topPanel, false);
3056 myBottomFrame.addAlignmentPanel(bottomPanel, false);
3062 topFrame.getAlignPanels().clear();
3063 bottomFrame.getAlignPanels().clear();
3070 * The dust settles...give focus to the tab we did this from.
3072 myTopFrame.setDisplayedView(myTopFrame.alignPanel);
3075 public static groovy.ui.Console getGroovyConsole()
3077 return groovyConsole;
3081 * handles the payload of a drag and drop event.
3083 * TODO refactor to desktop utilities class
3086 * - Data source strings extracted from the drop event
3088 * - protocol for each data source extracted from the drop event
3092 * - the payload from the drop event
3095 public static void transferFromDropTarget(List<Object> files,
3096 List<DataSourceType> protocols, DropTargetDropEvent evt,
3097 Transferable t) throws Exception
3100 // BH 2018 changed List<String> to List<Object> to allow for File from SwingJS
3102 // DataFlavor[] flavors = t.getTransferDataFlavors();
3103 // for (int i = 0; i < flavors.length; i++) {
3104 // if (flavors[i].isFlavorJavaFileListType()) {
3105 // evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
3106 // List<File> list = (List<File>) t.getTransferData(flavors[i]);
3107 // for (int j = 0; j < list.size(); j++) {
3108 // File file = (File) list.get(j);
3109 // byte[] data = getDroppedFileBytes(file);
3110 // fileName.setText(file.getName() + " - " + data.length + " " +
3111 // evt.getLocation());
3112 // JTextArea target = (JTextArea) ((DropTarget) evt.getSource()).getComponent();
3113 // target.setText(new String(data));
3115 // dtde.dropComplete(true);
3120 DataFlavor uriListFlavor = new DataFlavor(
3121 "text/uri-list;class=java.lang.String"), urlFlavour = null;
3124 urlFlavour = new DataFlavor(
3125 "application/x-java-url; class=java.net.URL");
3126 } catch (ClassNotFoundException cfe)
3128 Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
3131 if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
3136 java.net.URL url = (URL) t.getTransferData(urlFlavour);
3137 // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
3138 // means url may be null.
3141 protocols.add(DataSourceType.URL);
3142 files.add(url.toString());
3143 Cache.log.debug("Drop handled as URL dataflavor "
3144 + files.get(files.size() - 1));
3149 if (Platform.isAMacAndNotJS())
3152 "Please ignore plist error - occurs due to problem with java 8 on OSX");
3155 } catch (Throwable ex)
3157 Cache.log.debug("URL drop handler failed.", ex);
3160 if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
3162 // Works on Windows and MacOSX
3163 Cache.log.debug("Drop handled as javaFileListFlavor");
3164 for (Object file : (List) t
3165 .getTransferData(DataFlavor.javaFileListFlavor))
3168 protocols.add(DataSourceType.FILE);
3173 // Unix like behaviour
3174 boolean added = false;
3176 if (t.isDataFlavorSupported(uriListFlavor))
3178 Cache.log.debug("Drop handled as uriListFlavor");
3179 // This is used by Unix drag system
3180 data = (String) t.getTransferData(uriListFlavor);
3184 // fallback to text: workaround - on OSX where there's a JVM bug
3185 Cache.log.debug("standard URIListFlavor failed. Trying text");
3186 // try text fallback
3187 DataFlavor textDf = new DataFlavor(
3188 "text/plain;class=java.lang.String");
3189 if (t.isDataFlavorSupported(textDf))
3191 data = (String) t.getTransferData(textDf);
3194 Cache.log.debug("Plain text drop content returned "
3195 + (data == null ? "Null - failed" : data));
3200 while (protocols.size() < files.size())
3202 Cache.log.debug("Adding missing FILE protocol for "
3203 + files.get(protocols.size()));
3204 protocols.add(DataSourceType.FILE);
3206 for (java.util.StringTokenizer st = new java.util.StringTokenizer(
3207 data, "\r\n"); st.hasMoreTokens();)
3210 String s = st.nextToken();
3211 if (s.startsWith("#"))
3213 // the line is a comment (as per the RFC 2483)
3216 java.net.URI uri = new java.net.URI(s);
3217 if (uri.getScheme().toLowerCase().startsWith("http"))
3219 protocols.add(DataSourceType.URL);
3220 files.add(uri.toString());
3224 // otherwise preserve old behaviour: catch all for file objects
3225 java.io.File file = new java.io.File(uri);
3226 protocols.add(DataSourceType.FILE);
3227 files.add(file.toString());
3232 if (Cache.log.isDebugEnabled())
3234 if (data == null || !added)
3237 if (t.getTransferDataFlavors() != null
3238 && t.getTransferDataFlavors().length > 0)
3241 "Couldn't resolve drop data. Here are the supported flavors:");
3242 for (DataFlavor fl : t.getTransferDataFlavors())
3245 "Supported transfer dataflavor: " + fl.toString());
3246 Object df = t.getTransferData(fl);
3249 Cache.log.debug("Retrieves: " + df);
3253 Cache.log.debug("Retrieved nothing");
3259 Cache.log.debug("Couldn't resolve dataflavor for drop: "
3265 if (Platform.isWindowsAndNotJS())
3267 Cache.log.debug("Scanning dropped content for Windows Link Files");
3269 // resolve any .lnk files in the file drop
3270 for (int f = 0; f < files.size(); f++)
3272 String source = files.get(f).toString().toLowerCase();
3273 if (protocols.get(f).equals(DataSourceType.FILE)
3274 && (source.endsWith(".lnk") || source.endsWith(".url")
3275 || source.endsWith(".site")))
3279 Object obj = files.get(f);
3280 File lf = (obj instanceof File ? (File) obj
3281 : new File((String) obj));
3282 // process link file to get a URL
3283 Cache.log.debug("Found potential link file: " + lf);
3284 WindowsShortcut wscfile = new WindowsShortcut(lf);
3285 String fullname = wscfile.getRealFilename();
3286 protocols.set(f, FormatAdapter.checkProtocol(fullname));
3287 files.set(f, fullname);
3288 Cache.log.debug("Parsed real filename " + fullname
3289 + " to extract protocol: " + protocols.get(f));
3290 } catch (Exception ex)
3293 "Couldn't parse " + files.get(f) + " as a link file.",
3302 * Sets the Preferences property for experimental features to True or False
3303 * depending on the state of the controlling menu item
3306 protected void showExperimental_actionPerformed(boolean selected)
3308 Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
3312 * Answers a (possibly empty) list of any structure viewer frames (currently for
3313 * either Jmol or Chimera) which are currently open. This may optionally be
3314 * restricted to viewers of a specified class, or viewers linked to a specified
3318 * if not null, only return viewers linked to this panel
3319 * @param structureViewerClass
3320 * if not null, only return viewers of this class
3323 public List<StructureViewerBase> getStructureViewers(
3324 AlignmentPanel apanel,
3325 Class<? extends StructureViewerBase> structureViewerClass)
3327 List<StructureViewerBase> result = new ArrayList<>();
3328 JInternalFrame[] frames = Desktop.instance.getAllFrames();
3330 for (JInternalFrame frame : frames)
3332 if (frame instanceof StructureViewerBase)
3334 if (structureViewerClass == null
3335 || structureViewerClass.isInstance(frame))
3338 || ((StructureViewerBase) frame).isLinkedWith(apanel))
3340 result.add((StructureViewerBase) frame);