/*
* Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
* Copyright (C) $$Year-Rel$$ The Jalview Authors
*
* This file is part of Jalview.
*
* Jalview is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* Jalview is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jalview. If not, see .
* The Jalview Authors are detailed in the 'AUTHORS' file.
*/
package jalview.gui;
import jalview.api.AlignViewportI;
import jalview.api.AlignmentViewPanel;
import jalview.api.StructureSelectionManagerProvider;
import jalview.bin.ApplicationSingletonProvider;
import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
import jalview.bin.Cache;
import jalview.bin.Jalview;
import jalview.gui.ImageExporter.ImageWriterI;
import jalview.io.BackupFiles;
import jalview.io.DataSourceType;
import jalview.io.FileFormat;
import jalview.io.FileFormatException;
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 jalview.jbgui.GDesktop;
import jalview.jbgui.GSplitFrame;
import jalview.jbgui.GStructureViewer;
import jalview.project.Jalview2XML;
import jalview.structure.StructureSelectionManager;
import jalview.urls.IdOrgSettings;
import jalview.util.BrowserLauncher;
import jalview.util.ImageMaker.TYPE;
import jalview.util.MessageManager;
import jalview.util.Platform;
import jalview.util.UrlConstants;
import jalview.viewmodel.AlignmentViewport;
import jalview.ws.jws1.Discoverer;
import jalview.ws.params.ParamManager;
import jalview.ws.utils.UrlDownloadClient;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.ListIterator;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
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.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JProgressBar;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkEvent.EventType;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import org.stackoverflowusers.file.WindowsShortcut;
/**
* Jalview Desktop
*
*
* @author $author$
* @version $Revision: 1.155 $
*/
@SuppressWarnings("serial")
public class Desktop extends GDesktop
implements DropTargetListener, ClipboardOwner, IProgressIndicator,
StructureSelectionManagerProvider, ApplicationSingletonI
{
private final static int DEFAULT_MIN_WIDTH = 300;
private final static int DEFAULT_MIN_HEIGHT = 250;
private final static int ALIGN_FRAME_DEFAULT_MIN_WIDTH = 600;
private final static int ALIGN_FRAME_DEFAULT_MIN_HEIGHT = 70;
private final static String EXPERIMENTAL_FEATURES = "EXPERIMENTAL_FEATURES";
private JalviewChangeSupport changeSupport = new JalviewChangeSupport();
/**
* news reader - null if it was never started.
*/
BlogReader jvnews = null;
private File projectFile;
/**
* @param listener
* @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.beans.PropertyChangeListener)
*/
public void addJalviewPropertyChangeListener(
PropertyChangeListener listener)
{
changeSupport.addJalviewPropertyChangeListener(listener);
}
/**
* @param propertyName
* @param listener
* @see jalview.gui.JalviewChangeSupport#addJalviewPropertyChangeListener(java.lang.String,
* java.beans.PropertyChangeListener)
*/
public void addJalviewPropertyChangeListener(String propertyName,
PropertyChangeListener listener)
{
changeSupport.addJalviewPropertyChangeListener(propertyName, listener);
}
/**
* @param propertyName
* @param listener
* @see jalview.gui.JalviewChangeSupport#removeJalviewPropertyChangeListener(java.lang.String,
* java.beans.PropertyChangeListener)
*/
public void removeJalviewPropertyChangeListener(String propertyName,
PropertyChangeListener listener)
{
changeSupport.removeJalviewPropertyChangeListener(propertyName,
listener);
}
public static MyDesktopPane getDesktopPane()
{
return Desktop.getInstance().desktopPane;
}
public StructureSelectionManager getStructureSelectionManager()
{
return StructureSelectionManager
.getStructureSelectionManager(this);
}
static int openFrameCount = 0;
static final int xOffset = 30;
static final int yOffset = 30;
public Discoverer discoverer;
public Object[] jalviewClipboard;
public boolean internalCopy = false;
private static int fileLoadingCount = 0;
public JInternalFrame conservationSlider, PIDSlider;
/**
* just an instance (for testng, probably); no actual frames
*/
private boolean instanceOnly;
class MyDesktopManager implements DesktopManager
{
private DesktopManager delegate;
public MyDesktopManager(DesktopManager delegate)
{
this.delegate = delegate;
}
@Override
public void activateFrame(JInternalFrame f)
{
try
{
delegate.activateFrame(f);
} catch (NullPointerException npe)
{
Point p = getMousePosition();
showPasteMenu(p.x, p.y);
}
}
@Override
public void beginDraggingFrame(JComponent f)
{
delegate.beginDraggingFrame(f);
}
@Override
public void beginResizingFrame(JComponent f, int direction)
{
delegate.beginResizingFrame(f, direction);
}
@Override
public void closeFrame(JInternalFrame f)
{
delegate.closeFrame(f);
}
@Override
public void deactivateFrame(JInternalFrame f)
{
delegate.deactivateFrame(f);
}
@Override
public void deiconifyFrame(JInternalFrame f)
{
delegate.deiconifyFrame(f);
}
@Override
public void dragFrame(JComponent f, int newX, int newY)
{
if (newY < 0)
{
newY = 0;
}
delegate.dragFrame(f, newX, newY);
}
@Override
public void endDraggingFrame(JComponent f)
{
delegate.endDraggingFrame(f);
desktopPane.repaint();
}
@Override
public void endResizingFrame(JComponent f)
{
delegate.endResizingFrame(f);
desktopPane.repaint();
}
@Override
public void iconifyFrame(JInternalFrame f)
{
delegate.iconifyFrame(f);
}
@Override
public void maximizeFrame(JInternalFrame f)
{
delegate.maximizeFrame(f);
}
@Override
public void minimizeFrame(JInternalFrame f)
{
delegate.minimizeFrame(f);
}
@Override
public void openFrame(JInternalFrame f)
{
delegate.openFrame(f);
}
@Override
public void resizeFrame(JComponent f, int newX, int newY, int newWidth,
int newHeight)
{
if (newY < 0)
{
newY = 0;
}
delegate.resizeFrame(f, newX, newY, newWidth, newHeight);
}
@Override
public void setBoundsForFrame(JComponent f, int newX, int newY,
int newWidth, int newHeight)
{
delegate.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
}
// All other methods, simply delegate
}
public MyDesktopPane desktopPane;
/**
* Answers an 'application scope' singleton instance of this class. Separate
* SwingJS 'applets' running in the same browser page will each have a
* distinct instance of Desktop.
*
* @return
*/
public static Desktop getInstance()
{
return (Desktop) ApplicationSingletonProvider
.getInstance(Desktop.class);
}
/**
* For testing.
*
* @param forInstance
*/
public Desktop(boolean forInstance)
{
instanceOnly = true;
}
/**
* Private constructor enforces singleton pattern. It is called by reflection
* from ApplicationSingletonProvider.getInstance().
*/
@SuppressWarnings("unused")
private Desktop()
{
/**
* A note to implementors. It is ESSENTIAL that any activities that might
* block are spawned off as threads rather than waited for during this
* constructor.
*/
if (!Platform.isJS())
{
doVamsasClientCheck();
}
doConfigureStructurePrefs();
setTitle("Jalview " + jalview.bin.Cache.getProperty("VERSION"));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
boolean selmemusage = jalview.bin.Cache.getDefault("SHOW_MEMUSAGE",
false);
boolean showjconsole = jalview.bin.Cache.getDefault("SHOW_JAVA_CONSOLE",
false);
desktopPane = new MyDesktopPane(selmemusage);
showMemusage.setSelected(selmemusage);
desktopPane.setBackground(Color.white);
getContentPane().setLayout(new BorderLayout());
// alternate config - have scrollbars - see notes in JAL-153
// JScrollPane sp = new JScrollPane();
// sp.getViewport().setView(desktop);
// getContentPane().add(sp, BorderLayout.CENTER);
// BH 2018 - just an experiment to try unclipped JInternalFrames.
if (Platform.isJS())
{
getRootPane().putClientProperty("swingjs.overflow.hidden", "false");
}
getContentPane().add(desktopPane, BorderLayout.CENTER);
desktopPane.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
// This line prevents Windows Look&Feel resizing all new windows to maximum
// if previous window was maximised
desktopPane.setDesktopManager(new MyDesktopManager(
(Platform.isWindowsAndNotJS() ? new DefaultDesktopManager()
: Platform.isAMacAndNotJS()
? new AquaInternalFrameManager(
desktopPane.getDesktopManager())
: desktopPane.getDesktopManager())));
Rectangle dims = getLastKnownDimensions("");
if (dims != null)
{
setBounds(dims);
}
else
{
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int xPos = Math.max(5, (screenSize.width - 900) / 2);
int yPos = Math.max(5, (screenSize.height - 650) / 2);
setBounds(xPos, yPos, 900, 650);
}
if (!Platform.isJS())
/**
* Java only
*
* @j2sIgnore
*/
{
jconsole = new Console(this, showjconsole);
// add essential build information
jconsole.setHeader("Jalview Version: "
+ jalview.bin.Cache.getProperty("VERSION") + "\n"
+ "Jalview Installation: "
+ jalview.bin.Cache.getDefault("INSTALLATION", "unknown")
+ "\n" + "Build Date: "
+ jalview.bin.Cache.getDefault("BUILD_DATE", "unknown") + "\n"
+ "Java version: " + System.getProperty("java.version") + "\n"
+ System.getProperty("os.arch") + " "
+ System.getProperty("os.name") + " "
+ System.getProperty("os.version"));
showConsole(showjconsole);
showNews.setVisible(false);
experimentalFeatures.setSelected(showExperimental());
getIdentifiersOrgData();
checkURLLinks();
// Spawn a thread that shows the splashscreen
SwingUtilities.invokeLater(new Runnable()
{
@Override
public void run()
{
new SplashScreen();
}
});
// Thread off a new instance of the file chooser - this reduces the time
// it
// takes to open it later on.
new Thread(new Runnable()
{
@Override
public void run()
{
Cache.log.debug("Filechooser init thread started.");
String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT");
JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"),
fileFormat);
Cache.log.debug("Filechooser init thread finished.");
}
}).start();
// Add the service change listener
changeSupport.addJalviewPropertyChangeListener("services",
new PropertyChangeListener()
{
@Override
public void propertyChange(PropertyChangeEvent evt)
{
Cache.log.debug("Firing service changed event for "
+ evt.getNewValue());
JalviewServicesChanged(evt);
}
});
}
this.setDropTarget(new java.awt.dnd.DropTarget(desktopPane, this));
this.addWindowListener(new WindowAdapter()
{
@Override
public void windowClosing(WindowEvent evt)
{
quit();
}
});
MouseAdapter ma;
this.addMouseListener(ma = new MouseAdapter()
{
@Override
public void mousePressed(MouseEvent evt)
{
if (evt.isPopupTrigger()) // Mac
{
showPasteMenu(evt.getX(), evt.getY());
}
}
@Override
public void mouseReleased(MouseEvent evt)
{
if (evt.isPopupTrigger()) // Windows
{
showPasteMenu(evt.getX(), evt.getY());
}
}
});
desktopPane.addMouseListener(ma);
}
/**
* Answers true if user preferences to enable experimental features is True
* (on), else false
*
* @return
*/
public boolean showExperimental()
{
String experimental = Cache.getDefault(EXPERIMENTAL_FEATURES,
Boolean.FALSE.toString());
return Boolean.valueOf(experimental).booleanValue();
}
public void doConfigureStructurePrefs()
{
// configure services
StructureSelectionManager ssm = getStructureSelectionManager();
if (jalview.bin.Cache.getDefault(Preferences.ADD_SS_ANN, true))
{
ssm.setAddTempFacAnnot(jalview.bin.Cache
.getDefault(Preferences.ADD_TEMPFACT_ANN, true));
ssm.setProcessSecondaryStructure(jalview.bin.Cache
.getDefault(Preferences.STRUCT_FROM_PDB, true));
ssm.setSecStructServices(
jalview.bin.Cache.getDefault(Preferences.USE_RNAVIEW, true));
}
else
{
ssm.setAddTempFacAnnot(false);
ssm.setProcessSecondaryStructure(false);
ssm.setSecStructServices(false);
}
}
public void checkForNews()
{
final Desktop me = this;
// Thread off the news reader, in case there are connection problems.
new Thread(new Runnable()
{
@Override
public void run()
{
Cache.log.debug("Starting news thread.");
jvnews = new BlogReader(me);
showNews.setVisible(true);
Cache.log.debug("Completed news thread.");
}
}).start();
}
public void getIdentifiersOrgData()
{
// Thread off the identifiers fetcher
new Thread(new Runnable()
{
@Override
public void run()
{
Cache.log.debug("Downloading data from identifiers.org");
// UrlDownloadClient client = new UrlDownloadClient();
try
{
UrlDownloadClient.download(IdOrgSettings.getUrl(),
IdOrgSettings.getDownloadLocation());
} catch (IOException e)
{
Cache.log.debug("Exception downloading identifiers.org data"
+ e.getMessage());
}
}
}).start();
}
@Override
protected void showNews_actionPerformed(ActionEvent e)
{
showNews(showNews.isSelected());
}
protected void showNews(boolean visible)
{
Cache.log.debug((visible ? "Showing" : "Hiding") + " news.");
showNews.setSelected(visible);
if (visible && !jvnews.isVisible())
{
new Thread(new Runnable()
{
@Override
public void run()
{
long now = System.currentTimeMillis();
setProgressBar(
MessageManager.getString("status.refreshing_news"), now);
jvnews.refreshNews();
setProgressBar(null, now);
jvnews.showNews();
}
}).start();
}
}
/**
* recover the last known dimensions for a jalview window
*
* @param windowName
* - empty string is desktop, all other windows have unique prefix
* @return null or last known dimensions scaled to current geometry (if last
* window geom was known)
*/
Rectangle getLastKnownDimensions(String windowName)
{
// TODO: lock aspect ratio for scaling desktop Bug #0058199
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
String x = jalview.bin.Cache.getProperty(windowName + "SCREEN_X");
String y = jalview.bin.Cache.getProperty(windowName + "SCREEN_Y");
String width = jalview.bin.Cache
.getProperty(windowName + "SCREEN_WIDTH");
String height = jalview.bin.Cache
.getProperty(windowName + "SCREEN_HEIGHT");
if ((x != null) && (y != null) && (width != null) && (height != null))
{
int ix = Integer.parseInt(x), iy = Integer.parseInt(y),
iw = Integer.parseInt(width), ih = Integer.parseInt(height);
if (jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH") != null)
{
// attempt #1 - try to cope with change in screen geometry - this
// version doesn't preserve original jv aspect ratio.
// take ratio of current screen size vs original screen size.
double sw = ((1f * screenSize.width) / (1f * Integer.parseInt(
jalview.bin.Cache.getProperty("SCREENGEOMETRY_WIDTH"))));
double sh = ((1f * screenSize.height) / (1f * Integer.parseInt(
jalview.bin.Cache.getProperty("SCREENGEOMETRY_HEIGHT"))));
// rescale the bounds depending upon the current screen geometry.
ix = (int) (ix * sw);
iw = (int) (iw * sw);
iy = (int) (iy * sh);
ih = (int) (ih * sh);
while (ix >= screenSize.width)
{
jalview.bin.Cache.log.debug(
"Window geometry location recall error: shifting horizontal to within screenbounds.");
ix -= screenSize.width;
}
while (iy >= screenSize.height)
{
jalview.bin.Cache.log.debug(
"Window geometry location recall error: shifting vertical to within screenbounds.");
iy -= screenSize.height;
}
jalview.bin.Cache.log.debug(
"Got last known dimensions for " + windowName + ": x:" + ix
+ " y:" + iy + " width:" + iw + " height:" + ih);
}
// return dimensions for new instance
return new Rectangle(ix, iy, iw, ih);
}
return null;
}
private void doVamsasClientCheck()
{
if (Cache.vamsasJarsPresent())
{
setupVamsasDisconnectedGui();
VamsasMenu.setVisible(true);
final Desktop us = this;
VamsasMenu.addMenuListener(new MenuListener()
{
// this listener remembers when the menu was first selected, and
// doesn't rebuild the session list until it has been cleared and
// reselected again.
boolean refresh = true;
@Override
public void menuCanceled(MenuEvent e)
{
refresh = true;
}
@Override
public void menuDeselected(MenuEvent e)
{
refresh = true;
}
@Override
public void menuSelected(MenuEvent e)
{
if (refresh)
{
us.buildVamsasStMenu();
refresh = false;
}
}
});
vamsasStart.setVisible(true);
}
}
protected void showPasteMenu(int x, int y)
{
JPopupMenu popup = new JPopupMenu();
JMenuItem item = new JMenuItem(
MessageManager.getString("label.paste_new_window"));
item.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent evt)
{
paste();
}
});
popup.add(item);
popup.show(this, x, y);
}
public void paste()
{
try
{
Clipboard c = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable contents = c.getContents(this);
if (contents != null)
{
String file = (String) contents
.getTransferData(DataFlavor.stringFlavor);
FileFormatI format = new IdentifyFile().identify(file,
DataSourceType.PASTE);
new FileLoader().LoadFile(file, DataSourceType.PASTE, format);
}
} catch (Exception ex)
{
System.out.println(
"Unable to paste alignment from system clipboard:\n" + ex);
}
}
/**
* Adds and opens the given frame to the desktop
*
* @param frame
* Frame to show
* @param title
* Visible Title
* @param w
* width
* @param h
* height
*/
public static synchronized void addInternalFrame(
final JInternalFrame frame, String title, int w, int h)
{
addInternalFrame(frame, title, true, w, h, true, false);
}
/**
* Add an internal frame to the Jalview desktop
*
* @param frame
* Frame to show
* @param title
* Visible Title
* @param makeVisible
* When true, display frame immediately, otherwise, caller must call
* setVisible themselves.
* @param w
* width
* @param h
* height
*/
public static synchronized void addInternalFrame(
final JInternalFrame frame, String title, boolean makeVisible,
int w, int h)
{
addInternalFrame(frame, title, makeVisible, w, h, true, false);
}
/**
* Add an internal frame to the Jalview desktop and make it visible
*
* @param frame
* Frame to show
* @param title
* Visible Title
* @param w
* width
* @param h
* height
* @param resizable
* Allow resize
*/
public static synchronized void addInternalFrame(
final JInternalFrame frame, String title, int w, int h,
boolean resizable)
{
addInternalFrame(frame, title, true, w, h, resizable, false);
}
/**
* Add an internal frame to the Jalview desktop
*
* @param frame
* Frame to show
* @param title
* Visible Title
* @param makeVisible
* When true, display frame immediately, otherwise, caller must call
* setVisible themselves.
* @param w
* width
* @param h
* height
* @param resizable
* Allow resize
* @param ignoreMinSize
* Do not set the default minimum size for frame
*/
public static synchronized void addInternalFrame(
final JInternalFrame frame, String title, boolean makeVisible,
int w, int h, boolean resizable, boolean ignoreMinSize)
{
// TODO: allow callers to determine X and Y position of frame (eg. via
// bounds object).
// TODO: consider fixing method to update entries in the window submenu with
// the current window title
frame.setTitle(title);
if (frame.getWidth() < 1 || frame.getHeight() < 1)
{
frame.setSize(w, h);
}
// THIS IS A PUBLIC STATIC METHOD, SO IT MAY BE CALLED EVEN IN
// A HEADLESS STATE WHEN NO DESKTOP EXISTS. MUST RETURN
// IF JALVIEW IS RUNNING HEADLESS
// ///////////////////////////////////////////////
if (Desktop.getInstance().instanceOnly || Jalview.isHeadlessMode())
{
return;
}
openFrameCount++;
if (!ignoreMinSize)
{
frame.setMinimumSize(
new Dimension(DEFAULT_MIN_WIDTH, DEFAULT_MIN_HEIGHT));
// Set default dimension for Alignment Frame window.
// The Alignment Frame window could be added from a number of places,
// hence,
// I did this here in order not to miss out on any Alignment frame.
if (frame instanceof AlignFrame)
{
frame.setMinimumSize(new Dimension(ALIGN_FRAME_DEFAULT_MIN_WIDTH,
ALIGN_FRAME_DEFAULT_MIN_HEIGHT));
}
}
frame.setVisible(makeVisible);
frame.setClosable(true);
frame.setResizable(resizable);
frame.setMaximizable(resizable);
frame.setIconifiable(resizable);
frame.setOpaque(Platform.isJS());
if (frame.getX() < 1 && frame.getY() < 1)
{
frame.setLocation(xOffset * openFrameCount,
yOffset * ((openFrameCount - 1) % 10) + yOffset);
}
/*
* add an entry for the new frame in the Window menu
* (and remove it when the frame is closed)
*/
JMenuItem menuItem = new JMenuItem(title);
frame.addInternalFrameListener(new InternalFrameAdapter()
{
@Override
public void internalFrameActivated(InternalFrameEvent evt)
{
JInternalFrame itf = getDesktopPane().getSelectedFrame();
if (itf != null)
{
if (itf instanceof AlignFrame)
{
Jalview.setCurrentAlignFrame((AlignFrame) itf);
}
itf.requestFocus();
}
}
@Override
public void internalFrameClosed(InternalFrameEvent evt)
{
PaintRefresher.RemoveComponent(frame);
/*
* defensive check to prevent frames being
* added half off the window
*/
if (openFrameCount > 0)
{
openFrameCount--;
}
/*
* ensure no reference to alignFrame retained by menu item listener
*/
if (menuItem.getActionListeners().length > 0)
{
menuItem.removeActionListener(menuItem.getActionListeners()[0]);
}
Desktop.getInstance().windowMenu.remove(menuItem);
}
});
menuItem.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
try
{
frame.setSelected(true);
frame.setIcon(false);
} catch (java.beans.PropertyVetoException ex)
{
// System.err.println(ex.toString());
}
}
});
setKeyBindings(frame);
getDesktopPane().add(frame);
Desktop.getInstance().windowMenu.add(menuItem);
frame.toFront();
try
{
frame.setSelected(true);
frame.requestFocus();
} catch (java.beans.PropertyVetoException ve)
{
} catch (java.lang.ClassCastException cex)
{
Cache.log.warn(
"Squashed a possible GUI implementation error. If you can recreate this, please look at http://issues.jalview.org/browse/JAL-869",
cex);
}
}
/**
* 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)
{
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)
{
if (!internalCopy)
{
Desktop.getInstance().jalviewClipboard = null;
}
internalCopy = false;
}
@Override
public void dragEnter(DropTargetDragEvent evt)
{
}
@Override
public void dragExit(DropTargetEvent evt)
{
}
@Override
public void dragOver(DropTargetDragEvent evt)
{
}
@Override
public void dropActionChanged(DropTargetDragEvent evt)
{
}
/**
* DOCUMENT ME!
*
* @param evt
* DOCUMENT ME!
*/
@Override
public void drop(DropTargetDropEvent evt)
{
boolean success = true;
// JAL-1552 - acceptDrop required before getTransferable call for
// Java's Transferable for native dnd
evt.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
Transferable t = evt.getTransferable();
List