*/
package jalview.gui;
-import static jalview.util.UrlConstants.SEQUENCE_ID;
-
import jalview.api.AlignViewportI;
import jalview.api.AlignmentViewPanel;
import jalview.bin.Cache;
import jalview.io.FileFormatI;
import jalview.io.FileFormats;
import jalview.io.FileLoader;
+import jalview.io.FormatAdapter;
import jalview.io.IdentifyFile;
import jalview.io.JalviewFileChooser;
import jalview.io.JalviewFileView;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
+import java.awt.desktop.AboutEvent;
+import java.awt.desktop.AboutHandler;
+import java.awt.desktop.PreferencesEvent;
+import java.awt.desktop.PreferencesHandler;
+import java.awt.desktop.QuitEvent;
+import java.awt.desktop.QuitHandler;
+import java.awt.desktop.QuitResponse;
+import java.awt.desktop.QuitStrategy;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
-import java.awt.event.FocusEvent;
-import java.awt.event.FocusListener;
+import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
+import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.Semaphore;
import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.DefaultDesktopManager;
import javax.swing.DesktopManager;
+import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
-import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JProgressBar;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
+import org.stackoverflowusers.file.WindowsShortcut;
+
/**
* Jalview Desktop
*
private static final String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
+ private static final String CONFIRM_KEYBOARD_QUIT = "CONFIRM_KEYBOARD_QUIT";
+
+ public static HashMap<String, FileWriter> savingFiles = new HashMap<>();
+
private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
/**
private File projectFile;
+ private static boolean setAPQHandlers = false;
+
/**
* @param listener
* @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
doConfigureStructurePrefs();
setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
- setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ /*
+ if (!Platform.isAMac())
+ {
+ // this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+ }
+ else
+ {
+ this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
+ }
+ */
+
+ // flagging this test to avoid unnecessary reflection
+ if (!setAPQHandlers)
+ {
+ // see if the Quit, About and Preferences handlers are available
+ Class desktopClass = java.awt.Desktop.class;
+ java.awt.Desktop hdesktop = java.awt.Desktop.getDesktop();
+
+ try
+ {
+
+ if (desktopClass.getDeclaredMethod("setAboutHandler",
+ new Class[]
+ { AboutHandler.class }) != null)
+ {
+
+ hdesktop.setAboutHandler(new AboutHandler()
+ {
+ @Override
+ public void handleAbout(AboutEvent e)
+ {
+ aboutMenuItem_actionPerformed(null);
+ }
+ });
+
+ }
+
+ if (desktopClass.getDeclaredMethod("setPreferencesHandler",
+ new Class[]
+ { PreferencesHandler.class }) != null)
+ {
+
+ hdesktop.setPreferencesHandler(new PreferencesHandler()
+ {
+ @Override
+ public void handlePreferences(PreferencesEvent e)
+ {
+ preferences_actionPerformed(null);
+ }
+ });
+
+ }
+
+ if (desktopClass.getDeclaredMethod("setQuitHandler",
+ new Class[]
+ { QuitHandler.class }) != null)
+ {
+
+ hdesktop.setQuitHandler(new QuitHandler()
+ {
+ @Override
+ public void handleQuitRequestWith(QuitEvent e, QuitResponse r)
+ {
+ boolean confirmQuit = jalview.bin.Cache
+ .getDefault(CONFIRM_KEYBOARD_QUIT, true);
+ int n;
+ if (confirmQuit)
+ {
+ n = JOptionPane.showConfirmDialog(null,
+ MessageManager.getString("label.quit_jalview"),
+ MessageManager.getString("action.quit"),
+ JOptionPane.OK_CANCEL_OPTION,
+ JOptionPane.PLAIN_MESSAGE, null);
+ }
+ else
+ {
+ n = JOptionPane.OK_OPTION;
+ }
+ if (n == JOptionPane.OK_OPTION)
+ {
+ System.out.println("Shortcut Quit confirmed by user");
+ quit();
+ r.performQuit(); // probably won't reach this line, but just in
+ // case
+ }
+ else
+ {
+ r.cancelQuit();
+ System.out.println("Shortcut Quit cancelled by user");
+ }
+ }
+ });
+ hdesktop.setQuitStrategy(QuitStrategy.CLOSE_ALL_WINDOWS);
+
+ }
+
+ } catch (NoSuchMethodException e)
+ {
+ System.out.println(
+ "NoSuchMethodException when looking for About, Preferences, Quit Handlers");
+ e.printStackTrace();
+ }
+
+ setAPQHandlers = true;
+ }
+
+ addWindowListener(new WindowAdapter()
+ {
+
+ @Override
+ public void windowClosing(WindowEvent ev)
+ {
+ quit();
+ }
+ });
+
boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
false);
+
boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
false);
desktop = new MyDesktopPane(selmemusage);
- if (Platform.isAMac())
- {
- desktop.setDoubleBuffered(false);
- }
showMemusage.setSelected(selmemusage);
desktop.setBackground(Color.white);
+
getContentPane().setLayout(new BorderLayout());
// alternate config - have scrollbars - see notes in JAL-153
// JScrollPane sp = new JScrollPane();
// This line prevents Windows Look&Feel resizing all new windows to maximum
// if previous window was maximised
- desktop.setDesktopManager(
- new MyDesktopManager(new DefaultDesktopManager()));
+ desktop.setDesktopManager(new MyDesktopManager(
+ (Platform.isWindows() ? new DefaultDesktopManager()
+ : Platform.isAMac()
+ ? new AquaInternalFrameManager(
+ desktop.getDesktopManager())
+ : desktop.getDesktopManager())));
Rectangle dims = getLastKnownDimensions("");
if (dims != null)
});
desktop.addMouseListener(ma);
- this.addFocusListener(new FocusListener()
- {
-
- @Override
- public void focusLost(FocusEvent e)
- {
- // TODO Auto-generated method stub
-
- }
-
- @Override
- public void focusGained(FocusEvent e)
- {
- Cache.log.debug("Relaying windows after focus gain");
- // make sure that we sort windows properly after we gain focus
- instance.relayerWindows();
- }
- });
this.setDropTarget(new java.awt.dnd.DropTarget(desktop, this));
// Spawn a thread that shows the splashscreen
SwingUtilities.invokeLater(new Runnable()
{
final Desktop me = this;
// Thread off the news reader, in case there are connection problems.
- addDialogThread(new Runnable()
+ new Thread(new Runnable()
{
@Override
public void run()
showNews.setVisible(true);
Cache.log.debug("Completed news thread.");
}
- });
+ }).start();
}
public void getIdentifiersOrgData()
{
// Thread off the identifiers fetcher
- addDialogThread(new Runnable()
+ new Thread(new Runnable()
{
@Override
public void run()
+ e.getMessage());
}
}
- });
+ }).start();
+ ;
}
@Override
frame.setResizable(resizable);
frame.setMaximizable(resizable);
frame.setIconifiable(resizable);
- if (Platform.isAMac())
- {
- frame.setIconifiable(false);
- frame.setFrameIcon(null);
- // frame.setDesktopIcon(null);
- frame.setDoubleBuffered(false);
- }
+ frame.setOpaque(false);
+
if (frame.getX() < 1 && frame.getY() < 1)
{
frame.setLocation(xOffset * openFrameCount,
JInternalFrame itf = desktop.getSelectedFrame();
if (itf != null)
{
+ if (itf instanceof AlignFrame)
+ {
+ Jalview.setCurrentAlignFrame((AlignFrame) itf);
+ }
itf.requestFocus();
}
}
menuItem.removeActionListener(menuItem.getActionListeners()[0]);
}
windowMenu.remove(menuItem);
- JInternalFrame itf = desktop.getSelectedFrame();
- if (itf != null)
- {
- itf.requestFocus();
- if (itf instanceof AlignFrame)
- {
- Jalview.setCurrentAlignFrame((AlignFrame) itf);
- }
- }
- System.gc();
};
});
}
});
+ setKeyBindings(frame);
+
desktop.add(frame);
windowMenu.add(menuItem);
}
}
+ /**
+ * Add key bindings to a JInternalFrame so that Ctrl-W and Cmd-W will close
+ * the window
+ *
+ * @param frame
+ */
+ private static void setKeyBindings(JInternalFrame frame)
+ {
+ @SuppressWarnings("serial")
+ final Action closeAction = new AbstractAction()
+ {
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ frame.dispose();
+ }
+ };
+
+ /*
+ * set up key bindings for Ctrl-W and Cmd-W, with the same (Close) action
+ */
+ KeyStroke ctrlWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
+ InputEvent.CTRL_DOWN_MASK);
+ KeyStroke cmdWKey = KeyStroke.getKeyStroke(KeyEvent.VK_W,
+ Toolkit.getDefaultToolkit().getMenuShortcutKeyMask());
+
+ InputMap inputMap = frame
+ .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
+ String ctrlW = ctrlWKey.toString();
+ inputMap.put(ctrlWKey, ctrlW);
+ inputMap.put(cmdWKey, ctrlW);
+
+ ActionMap actionMap = frame.getActionMap();
+ actionMap.put(ctrlW, closeAction);
+ }
+
@Override
public void lostOwnership(Clipboard clipboard, Transferable contents)
{
// Java's Transferable for native dnd
evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
Transferable t = evt.getTransferable();
- List<String> files = new ArrayList<String>();
- List<DataSourceType> protocols = new ArrayList<DataSourceType>();
+ List<String> files = new ArrayList<>();
+ List<DataSourceType> protocols = new ArrayList<>();
try
{
@Override
public void quit()
{
+ System.out.println("********** Desktop.quit()");
+ System.out.println(savingFiles.toString());
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
jalview.bin.Cache.setProperty("SCREENGEOMETRY_WIDTH",
screen.width + "");
{
ssm.resetAll();
}
- System.gc();
}
@Override
JPanel progressPanel;
- ArrayList<JPanel> fileLoadingPanels = new ArrayList<JPanel>();
+ ArrayList<JPanel> fileLoadingPanels = new ArrayList<>();
public void startLoading(final String fileName)
{
// TODO: verify that frames are recoverable when in headless mode
return null;
}
- List<AlignmentPanel> aps = new ArrayList<AlignmentPanel>();
+ List<AlignmentPanel> aps = new ArrayList<>();
AlignFrame[] frames = getAlignFrames();
if (frames == null)
{
*/
public static AlignmentViewport[] getViewports(String sequenceSetId)
{
- List<AlignmentViewport> viewp = new ArrayList<AlignmentViewport>();
+ List<AlignmentViewport> viewp = new ArrayList<>();
if (desktop != null)
{
AlignFrame[] frames = Desktop.getAlignFrames();
// SEQUENCE_ID which is not the default EMBL_EBI link
ListIterator<String> li = links.listIterator();
boolean check = false;
- List<JLabel> urls = new ArrayList<JLabel>();
+ List<JLabel> urls = new ArrayList<>();
while (li.hasNext())
{
String link = li.next();
- if (link.contains(SEQUENCE_ID)
- && !link.equals(UrlConstants.DEFAULT_STRING))
+ if (link.contains(jalview.util.UrlConstants.SEQUENCE_ID)
+ && !UrlConstants.isDefaultString(link))
{
check = true;
int barPos = link.indexOf("|");
Thread worker = new Thread(this);
worker.start();
}
+ repaint();
}
public boolean isShowMemoryUsage()
}
/**
- * fixes stacking order after a modal dialog to ensure windows that should be
- * on top actually are
- */
- public void relayerWindows()
- {
-
- }
-
- /**
* Accessor method to quickly get all the AlignmentFrames loaded.
*
* @return an array of AlignFrame, or null if none found
{
return null;
}
- List<AlignFrame> avp = new ArrayList<AlignFrame>();
+ List<AlignFrame> avp = new ArrayList<>();
// REVERSE ORDER
for (int i = frames.length - 1; i > -1; i--)
{
{
return null;
}
- List<GStructureViewer> avp = new ArrayList<GStructureViewer>();
+ List<GStructureViewer> avp = new ArrayList<>();
// REVERSE ORDER
for (int i = frames.length - 1; i > -1; i--)
{
{
if (progressBars == null)
{
- progressBars = new Hashtable<Long, JPanel>();
- progressBarHandlers = new Hashtable<Long, IProgressIndicatorHandler>();
+ progressBars = new Hashtable<>();
+ progressBarHandlers = new Hashtable<>();
}
if (progressBars.get(new Long(id)) != null)
return groovyConsole;
}
+ /**
+ * handles the payload of a drag and drop event.
+ *
+ * TODO refactor to desktop utilities class
+ *
+ * @param files
+ * - Data source strings extracted from the drop event
+ * @param protocols
+ * - protocol for each data source extracted from the drop event
+ * @param evt
+ * - the drop event
+ * @param t
+ * - the payload from the drop event
+ * @throws Exception
+ */
public static void transferFromDropTarget(List<String> files,
List<DataSourceType> protocols, DropTargetDropEvent evt,
Transferable t) throws Exception
{
DataFlavor uriListFlavor = new DataFlavor(
- "text/uri-list;class=java.lang.String");
+ "text/uri-list;class=java.lang.String"), urlFlavour = null;
+ try
+ {
+ urlFlavour = new DataFlavor(
+ "application/x-java-url; class=java.net.URL");
+ } catch (ClassNotFoundException cfe)
+ {
+ Cache.log.debug("Couldn't instantiate the URL dataflavor.", cfe);
+ }
+
+ if (urlFlavour != null && t.isDataFlavorSupported(urlFlavour))
+ {
+
+ try
+ {
+ java.net.URL url = (URL) t.getTransferData(urlFlavour);
+ // nb: java 8 osx bug https://bugs.openjdk.java.net/browse/JDK-8156099
+ // means url may be null.
+ if (url != null)
+ {
+ protocols.add(DataSourceType.URL);
+ files.add(url.toString());
+ Cache.log.debug("Drop handled as URL dataflavor "
+ + files.get(files.size() - 1));
+ return;
+ }
+ else
+ {
+ if (Platform.isAMac())
+ {
+ System.err.println(
+ "Please ignore plist error - occurs due to problem with java 8 on OSX");
+ }
+ ;
+ }
+ } catch (Throwable ex)
+ {
+ Cache.log.debug("URL drop handler failed.", ex);
+ }
+ }
if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor))
{
// Works on Windows and MacOSX
// fallback to text: workaround - on OSX where there's a JVM bug
Cache.log.debug("standard URIListFlavor failed. Trying text");
// try text fallback
- data = (String) t.getTransferData(
- new DataFlavor("text/plain;class=java.lang.String"));
- if (Cache.log.isDebugEnabled())
+ DataFlavor textDf = new DataFlavor(
+ "text/plain;class=java.lang.String");
+ if (t.isDataFlavorSupported(textDf))
{
- Cache.log.debug("fallback returned " + data);
+ data = (String) t.getTransferData(textDf);
}
+
+ Cache.log.debug("Plain text drop content returned "
+ + (data == null ? "Null - failed" : data));
+
}
- while (protocols.size() < files.size())
- {
- Cache.log.debug("Adding missing FILE protocol for "
- + files.get(protocols.size()));
- protocols.add(DataSourceType.FILE);
- }
- for (java.util.StringTokenizer st = new java.util.StringTokenizer(
- data, "\r\n"); st.hasMoreTokens();)
+ if (data != null)
{
- added = true;
- String s = st.nextToken();
- if (s.startsWith("#"))
- {
- // the line is a comment (as per the RFC 2483)
- continue;
- }
- java.net.URI uri = new java.net.URI(s);
- if (uri.getScheme().toLowerCase().startsWith("http"))
+ while (protocols.size() < files.size())
{
- protocols.add(DataSourceType.URL);
- files.add(uri.toString());
+ Cache.log.debug("Adding missing FILE protocol for "
+ + files.get(protocols.size()));
+ protocols.add(DataSourceType.FILE);
}
- else
+ for (java.util.StringTokenizer st = new java.util.StringTokenizer(
+ data, "\r\n"); st.hasMoreTokens();)
{
- // otherwise preserve old behaviour: catch all for file objects
- java.io.File file = new java.io.File(uri);
- protocols.add(DataSourceType.FILE);
- files.add(file.toString());
+ added = true;
+ String s = st.nextToken();
+ if (s.startsWith("#"))
+ {
+ // the line is a comment (as per the RFC 2483)
+ continue;
+ }
+ java.net.URI uri = new java.net.URI(s);
+ if (uri.getScheme().toLowerCase().startsWith("http"))
+ {
+ protocols.add(DataSourceType.URL);
+ files.add(uri.toString());
+ }
+ else
+ {
+ // otherwise preserve old behaviour: catch all for file objects
+ java.io.File file = new java.io.File(uri);
+ protocols.add(DataSourceType.FILE);
+ files.add(file.toString());
+ }
}
}
+
if (Cache.log.isDebugEnabled())
{
if (data == null || !added)
{
- Cache.log.debug(
- "Couldn't resolve drop data. Here are the supported flavors:");
- for (DataFlavor fl : t.getTransferDataFlavors())
+
+ if (t.getTransferDataFlavors() != null
+ && t.getTransferDataFlavors().length > 0)
{
Cache.log.debug(
- "Supported transfer dataflavor: " + fl.toString());
- Object df = t.getTransferData(fl);
- if (df != null)
+ "Couldn't resolve drop data. Here are the supported flavors:");
+ for (DataFlavor fl : t.getTransferDataFlavors())
{
- Cache.log.debug("Retrieves: " + df);
- }
- else
- {
- Cache.log.debug("Retrieved nothing");
+ Cache.log.debug(
+ "Supported transfer dataflavor: " + fl.toString());
+ Object df = t.getTransferData(fl);
+ if (df != null)
+ {
+ Cache.log.debug("Retrieves: " + df);
+ }
+ else
+ {
+ Cache.log.debug("Retrieved nothing");
+ }
}
}
+ else
+ {
+ Cache.log.debug("Couldn't resolve dataflavor for drop: "
+ + t.toString());
+ }
+ }
+ }
+ }
+ if (Platform.isWindows())
+
+ {
+ Cache.log.debug("Scanning dropped content for Windows Link Files");
+
+ // resolve any .lnk files in the file drop
+ for (int f = 0; f < files.size(); f++)
+ {
+ String source = files.get(f).toLowerCase();
+ if (protocols.get(f).equals(DataSourceType.FILE)
+ && (source.endsWith(".lnk") || source.endsWith(".url")
+ || source.endsWith(".site")))
+ {
+ try
+ {
+ File lf = new File(files.get(f));
+ // process link file to get a URL
+ Cache.log.debug("Found potential link file: " + lf);
+ WindowsShortcut wscfile = new WindowsShortcut(lf);
+ String fullname = wscfile.getRealFilename();
+ protocols.set(f, FormatAdapter.checkProtocol(fullname));
+ files.set(f, fullname);
+ Cache.log.debug("Parsed real filename " + fullname
+ + " to extract protocol: " + protocols.get(f));
+ } catch (Exception ex)
+ {
+ Cache.log.error(
+ "Couldn't parse " + files.get(f) + " as a link file.",
+ ex);
+ }
}
}
}
{
Cache.setProperty(EXPERIMENTAL_FEATURES, Boolean.toString(selected));
}
+
+ /**
+ * Answers a (possibly empty) list of any structure viewer frames (currently
+ * for either Jmol or Chimera) which are currently open. This may optionally
+ * be restricted to viewers of a specified class, or viewers linked to a
+ * specified alignment panel.
+ *
+ * @param apanel
+ * if not null, only return viewers linked to this panel
+ * @param structureViewerClass
+ * if not null, only return viewers of this class
+ * @return
+ */
+ public List<StructureViewerBase> getStructureViewers(
+ AlignmentPanel apanel,
+ Class<? extends StructureViewerBase> structureViewerClass)
+ {
+ List<StructureViewerBase> result = new ArrayList<>();
+ JInternalFrame[] frames = Desktop.instance.getAllFrames();
+
+ for (JInternalFrame frame : frames)
+ {
+ if (frame instanceof StructureViewerBase)
+ {
+ if (structureViewerClass == null
+ || structureViewerClass.isInstance(frame))
+ {
+ if (apanel == null
+ || ((StructureViewerBase) frame).isLinkedWith(apanel))
+ {
+ result.add((StructureViewerBase) frame);
+ }
+ }
+ }
+ }
+ return result;
+ }
}