JAL-845 further code/tests/refactoring
[jalview.git] / src / jalview / gui / SplitFrame.java
1 package jalview.gui;
2
3 import jalview.jbgui.GSplitFrame;
4
5 import java.awt.Component;
6 import java.awt.MouseInfo;
7 import java.awt.Point;
8 import java.awt.Rectangle;
9 import java.awt.event.ActionEvent;
10 import java.awt.event.ActionListener;
11 import java.awt.event.KeyAdapter;
12 import java.awt.event.KeyEvent;
13 import java.awt.event.KeyListener;
14 import java.util.Map.Entry;
15
16 import javax.swing.AbstractAction;
17 import javax.swing.JComponent;
18 import javax.swing.JMenuItem;
19 import javax.swing.KeyStroke;
20 import javax.swing.event.InternalFrameAdapter;
21 import javax.swing.event.InternalFrameEvent;
22
23 public class SplitFrame extends GSplitFrame
24 {
25   private static final long serialVersionUID = 1L;
26
27   public SplitFrame(JComponent top, JComponent bottom)
28   {
29     super(top, bottom);
30     init();
31   }
32
33   /**
34    * Initialise this frame.
35    */
36   protected void init()
37   {
38     setSize(AlignFrame.DEFAULT_WIDTH, Desktop.instance.getHeight() - 20);
39
40     addCloseFrameListener();
41     
42     addKeyListener();
43
44     addKeyBindings();
45
46   }
47
48   /**
49    * Add a listener to tidy up when the frame is closed.
50    */
51   protected void addCloseFrameListener()
52   {
53     addInternalFrameListener(new InternalFrameAdapter()
54     {
55       @Override
56       public void internalFrameClosed(InternalFrameEvent evt)
57       {
58         if (getTopComponent() instanceof AlignFrame)
59         {
60           ((AlignFrame) getTopComponent())
61                   .closeMenuItem_actionPerformed(true);
62         }
63         if (getBottomComponent() instanceof AlignFrame)
64         {
65           ((AlignFrame) getBottomComponent())
66                   .closeMenuItem_actionPerformed(true);
67         }
68       };
69     });
70   }
71
72   /**
73    * Add a key listener that delegates to whichever split component the mouse is
74    * in (or does nothing if neither).
75    */
76   protected void addKeyListener()
77   {
78     // TODO Key Bindings rather than KeyListener are recommended for Swing
79     addKeyListener(new KeyAdapter() {
80
81       @Override
82       public void keyPressed(KeyEvent e)
83       {
84         Component c = getComponentAtMouse();
85         if (c != null)
86         {
87           for (KeyListener kl : c.getKeyListeners())
88           {
89             kl.keyPressed(e);
90           }
91         }
92       }
93
94       @Override
95       public void keyReleased(KeyEvent e)
96       {
97         Component c = getComponentAtMouse();
98         if (c != null)
99         {
100           for (KeyListener kl : c.getKeyListeners())
101           {
102             kl.keyReleased(e);
103           }
104         }
105       }
106       
107     });
108   }
109
110   /**
111    * Returns the split pane component the mouse is in, or null if neither.
112    * 
113    * @return
114    */
115   protected Component getComponentAtMouse()
116   {
117     Point loc = MouseInfo.getPointerInfo().getLocation();
118     
119     if (isIn(loc, getTopComponent())) {
120       return getTopComponent();
121     }
122     else if (isIn(loc, getBottomComponent()))
123     {
124       return getBottomComponent();
125     }
126     return null;
127   }
128
129   private boolean isIn(Point loc, JComponent comp)
130   {
131     Point p = comp.getLocationOnScreen();
132     Rectangle r = new Rectangle(p.x, p.y, comp.getWidth(), comp.getHeight());
133     return r.contains(loc);
134   }
135
136   /**
137    * Set key bindings (recommended for Swing over key accelerators). For now,
138    * delegate to the corresponding key accelerator for the AlignFrame that the
139    * mouse is in. Hopefully can be simplified in future if AlignFrame is changed
140    * to use key bindings rather than accelerators.
141    */
142   private void addKeyBindings()
143   {
144     if (getTopComponent() instanceof AlignFrame)
145     {
146       for (Entry<KeyStroke, JMenuItem> acc : ((AlignFrame) getTopComponent())
147               .getAccelerators().entrySet())
148       {
149
150         final KeyStroke ks = acc.getKey();
151         this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ks, ks);
152         this.getActionMap().put(ks, new AbstractAction()
153         {
154           @Override
155           public void actionPerformed(ActionEvent e)
156           {
157             Component c = getComponentAtMouse();
158             if (c instanceof AlignFrame)
159             {
160               for (ActionListener a : ((AlignFrame) c).getAccelerators()
161                       .get(ks).getActionListeners())
162               {
163
164                 a.actionPerformed(null);
165               }
166             }
167           }
168         });
169       }
170       /*
171        * Disable unwanted here
172        */
173       // X expand views - wrecks the split pane view
174       KeyStroke key_X = KeyStroke.getKeyStroke(KeyEvent.VK_X, 0, false);
175       disableAccelerator(key_X);
176     }
177   }
178
179   /**
180    * Ugly hack for Proof of Concept that disables the key binding in this frame
181    * _and_ disables the bound menu item _and_ removes the key accelerator in the
182    * child frames.
183    * 
184    * @param key
185    */
186   protected void disableAccelerator(KeyStroke key)
187   {
188     disableAccelerator(key, getTopComponent());
189     disableAccelerator(key, getBottomComponent());
190     this.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).remove(key);
191   }
192
193   /**
194    * Disable the menu item for which this key is the accelerator, also removes
195    * its action listeners to prevent key accelerator working.
196    * 
197    * @param key
198    * @param comp
199    */
200   private void disableAccelerator(KeyStroke key, JComponent comp)
201   {
202     // HACKED ONLY FOR PROOF OF CONCEPT
203     // Proper solution might involve explicit 'configure menu' method on
204     // AlignFrame, or
205     // changing key listeners to key bindings in AlignFrame, or both
206     if (comp instanceof AlignFrame)
207     {
208       JMenuItem mi = ((AlignFrame) comp).getAccelerators().get(key);
209       if (mi != null)
210       {
211         mi.setEnabled(false);
212         for (ActionListener al : mi.getActionListeners())
213         {
214           mi.removeActionListener(al);
215         }
216       }
217     }
218   }
219 }