package jalview.gui; import jalview.jbgui.GSplitFrame; import java.awt.Component; import java.awt.MouseInfo; import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.Map.Entry; import javax.swing.AbstractAction; import javax.swing.JComponent; import javax.swing.JMenuItem; import javax.swing.KeyStroke; import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; public class SplitFrame extends GSplitFrame { private static final long serialVersionUID = 1L; public SplitFrame(JComponent top, JComponent bottom) { super(top, bottom); init(); } /** * Initialise this frame. */ protected void init() { setSize(AlignFrame.DEFAULT_WIDTH, Desktop.instance.getHeight() - 20); addCloseFrameListener(); addKeyListener(); addKeyBindings(); } /** * Add a listener to tidy up when the frame is closed. */ protected void addCloseFrameListener() { addInternalFrameListener(new InternalFrameAdapter() { @Override public void internalFrameClosed(InternalFrameEvent evt) { if (getTopComponent() instanceof AlignFrame) { ((AlignFrame) getTopComponent()) .closeMenuItem_actionPerformed(true); } if (getBottomComponent() instanceof AlignFrame) { ((AlignFrame) getBottomComponent()) .closeMenuItem_actionPerformed(true); } }; }); } /** * Add a key listener that delegates to whichever split component the mouse is * in (or does nothing if neither). */ protected void addKeyListener() { // TODO Key Bindings rather than KeyListener are recommended for Swing addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { Component c = getComponentAtMouse(); if (c != null) { for (KeyListener kl : c.getKeyListeners()) { kl.keyPressed(e); } } } @Override public void keyReleased(KeyEvent e) { Component c = getComponentAtMouse(); if (c != null) { for (KeyListener kl : c.getKeyListeners()) { kl.keyReleased(e); } } } }); } /** * Returns the split pane component the mouse is in, or null if neither. * * @return */ protected Component getComponentAtMouse() { Point loc = MouseInfo.getPointerInfo().getLocation(); if (isIn(loc, getTopComponent())) { return getTopComponent(); } else if (isIn(loc, getBottomComponent())) { return getBottomComponent(); } return null; } private boolean isIn(Point loc, JComponent comp) { Point p = comp.getLocationOnScreen(); Rectangle r = new Rectangle(p.x, p.y, comp.getWidth(), comp.getHeight()); return r.contains(loc); } /** * Set key bindings (recommended for Swing over key accelerators). For now, * delegate to the corresponding key accelerator for the AlignFrame that the * mouse is in. Hopefully can be simplified in future if AlignFrame is changed * to use key bindings rather than accelerators. */ private void addKeyBindings() { if (getTopComponent() instanceof AlignFrame) { for (Entry acc : ((AlignFrame) getTopComponent()) .getAccelerators().entrySet()) { final KeyStroke ks = acc.getKey(); this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ks, ks); this.getActionMap().put(ks, new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { Component c = getComponentAtMouse(); if (c instanceof AlignFrame) { for (ActionListener a : ((AlignFrame) c).getAccelerators() .get(ks).getActionListeners()) { a.actionPerformed(null); } } } }); } /* * Disable unwanted here */ // X expand views - wrecks the split pane view KeyStroke key_X = KeyStroke.getKeyStroke(KeyEvent.VK_X, 0, false); disableAccelerator(key_X); } } /** * Ugly hack for Proof of Concept that disables the key binding in this frame * _and_ disables the bound menu item _and_ removes the key accelerator in the * child frames. * * @param key */ protected void disableAccelerator(KeyStroke key) { disableAccelerator(key, getTopComponent()); disableAccelerator(key, getBottomComponent()); this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).remove(key); } /** * Disable the menu item for which this key is the accelerator, also removes * its action listeners to prevent key accelerator working. * * @param key * @param comp */ private void disableAccelerator(KeyStroke key, JComponent comp) { // HACKED ONLY FOR PROOF OF CONCEPT // Proper solution might involve explicit 'configure menu' method on // AlignFrame, or // changing key listeners to key bindings in AlignFrame, or both if (comp instanceof AlignFrame) { JMenuItem mi = ((AlignFrame) comp).getAccelerators().get(key); if (mi != null) { mi.setEnabled(false); for (ActionListener al : mi.getActionListeners()) { mi.removeActionListener(al); } } } } }