update author list in license for (JAL-826)
[jalview.git] / src / jalview / appletgui / EmbmenuFrame.java
1 /*\r
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)\r
3  * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, G Barton, M Clamp, S Searle\r
4  * \r
5  * This file is part of Jalview.\r
6  * \r
7  * Jalview is free software: you can redistribute it and/or\r
8  * modify it under the terms of the GNU General Public License \r
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.\r
10  * \r
11  * Jalview is distributed in the hope that it will be useful, but \r
12  * WITHOUT ANY WARRANTY; without even the implied warranty \r
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR \r
14  * PURPOSE.  See the GNU General Public License for more details.\r
15  * \r
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.\r
17  */\r
18 package jalview.appletgui;\r
19 \r
20 import java.awt.BorderLayout;\r
21 import java.awt.Color;\r
22 import java.awt.Container;\r
23 import java.awt.FlowLayout;\r
24 import java.awt.Font;\r
25 import java.awt.Frame;\r
26 import java.awt.GraphicsConfiguration;\r
27 import java.awt.HeadlessException;\r
28 import java.awt.Label;\r
29 import java.awt.Menu;\r
30 import java.awt.MenuBar;\r
31 import java.awt.Panel;\r
32 import java.awt.PopupMenu;\r
33 import java.awt.event.ActionListener;\r
34 import java.awt.event.ItemListener;\r
35 import java.awt.event.MouseEvent;\r
36 import java.awt.event.MouseListener;\r
37 import java.util.Enumeration;\r
38 import java.util.Hashtable;\r
39 \r
40 /**\r
41  * This class implements a pattern form embedding toolbars as a panel with\r
42  * popups for situations where the system menu bar is either invisible or\r
43  * inappropriate. It was derived from the code for embedding the jalview applet\r
44  * alignFrame as a component on the web-page, which requires the local\r
45  * alignFrame menu to be attached to that panel rather than placed on the parent\r
46  * (which isn't allowed anyhow). TODO: try to modify the embeddedMenu display so\r
47  * it looks like a real toolbar menu TODO: modify click/mouse handler for\r
48  * embeddedMenu so it behaves more like a real pulldown menu toolbar\r
49  * \r
50  * @author Jim Procter and Andrew Waterhouse\r
51  * \r
52  */\r
53 public class EmbmenuFrame extends Frame implements MouseListener\r
54 {\r
55   /**\r
56    * map from labels to popup menus for the embedded menubar\r
57    */\r
58   protected Hashtable embeddedPopup;\r
59 \r
60   /**\r
61    * the embedded menu is built on this and should be added to the frame at the\r
62    * appropriate position.\r
63    * \r
64    */\r
65   protected Panel embeddedMenu;\r
66 \r
67   public EmbmenuFrame() throws HeadlessException\r
68   {\r
69     super();\r
70   }\r
71 \r
72   public EmbmenuFrame(String title) throws HeadlessException\r
73   {\r
74     super(title);\r
75   }\r
76 \r
77   /**\r
78    * Check if the applet is running on a platform that requires the Frame\r
79    * menuBar to be embedded, and if so, embeds it.\r
80    * \r
81    * @param tobeAdjusted\r
82    *          the panel that is to be reduced to make space for the embedded\r
83    *          menu bar\r
84    * @return true if menuBar was embedded and tobeAdjusted's height modified\r
85    */\r
86   protected boolean embedMenuIfNeeded(Panel tobeAdjusted)\r
87   {\r
88     MenuBar topMenuBar = getMenuBar();\r
89     if (topMenuBar == null)\r
90     {\r
91       return false;\r
92     }\r
93     // DEBUG Hint: can test embedded menus by inserting true here.\r
94     if (new jalview.util.Platform().isAMac())\r
95     {\r
96       // Build the embedded menu panel\r
97       embeddedMenu = makeEmbeddedPopupMenu(topMenuBar, "Arial", Font.PLAIN,\r
98               10, true); // try to pickup system font.\r
99       setMenuBar(null);\r
100       // add the components to the TreePanel area.\r
101       add(embeddedMenu, BorderLayout.NORTH);\r
102       tobeAdjusted.setSize(getSize().width, getSize().height\r
103               - embeddedMenu.HEIGHT);\r
104       return true;\r
105     }\r
106     return false;\r
107   }\r
108 \r
109   /**\r
110    * move all menus on menuBar onto embeddedMenu. embeddedPopup is used to store\r
111    * the popups for each menu removed from the menuBar and added to the panel.\r
112    * NOTE: it is up to the caller to remove menuBar from the Frame if it is\r
113    * already attached.\r
114    * \r
115    * @param menuBar\r
116    * @param fn\r
117    * @param fstyle\r
118    * @param fsz\r
119    * @param overrideFonts\r
120    *          true if we take the menuBar fonts in preference to the supplied\r
121    *          defaults\r
122    * @return the embedded menu instance to be added to the frame.\r
123    */\r
124   protected Panel makeEmbeddedPopupMenu(MenuBar menuBar, String fn,\r
125           int fstyle, int fsz, boolean overrideFonts)\r
126   {\r
127     return makeEmbeddedPopupMenu(menuBar, fn, fstyle, fsz, overrideFonts,\r
128             false);\r
129   }\r
130 \r
131   /**\r
132    * Create or add elements to the embedded menu from menuBar. This removes all\r
133    * menu from menuBar and it is up to the caller to remove the now useless\r
134    * menuBar from the Frame if it is already attached.\r
135    * \r
136    * @param menuBar\r
137    * @param fn\r
138    * @param fstyle\r
139    * @param fsz\r
140    * @param overrideFonts\r
141    * @param append\r
142    *          true means existing menu will be emptied before adding new\r
143    *          elements\r
144    * @return\r
145    */\r
146   protected Panel makeEmbeddedPopupMenu(MenuBar menuBar, String fn,\r
147           int fstyle, int fsz, boolean overrideFonts, boolean append)\r
148   {\r
149     if (!append)\r
150     {\r
151       if (embeddedPopup != null)\r
152       {\r
153         embeddedPopup.clear(); // TODO: check if j1.1\r
154       }\r
155       if (embeddedMenu != null)\r
156       {\r
157         embeddedMenu.removeAll();\r
158       }\r
159     }\r
160     if (embeddedPopup == null)\r
161     {\r
162       embeddedPopup = new Hashtable();\r
163     }\r
164 \r
165     embeddedMenu = makeEmbeddedPopupMenu(menuBar, fn, fstyle, fsz,\r
166             overrideFonts, embeddedPopup, new Panel(), this);\r
167     return embeddedMenu;\r
168   }\r
169 \r
170   /**\r
171    * Generic method to move elements from menubar onto embeddedMenu using the\r
172    * existing or the supplied font, and adds binding from panel to attached\r
173    * menus in embeddedPopup This removes all menu from menuBar and it is up to\r
174    * the caller to remove the now useless menuBar from the Frame if it is\r
175    * already attached.\r
176    * \r
177    * @param menuBar\r
178    *          must be non-null\r
179    * @param fn\r
180    * @param fstyle\r
181    * @param fsz\r
182    * @param overrideFonts\r
183    * @param embeddedPopup\r
184    *          must be non-null\r
185    * @param embeddedMenu\r
186    *          if null, a new panel will be created and returned\r
187    * @param clickHandler\r
188    *          - usually the instance of EmbmenuFrame that holds references to\r
189    *          embeddedPopup and embeddedMenu\r
190    * @return the panel instance for convenience.\r
191    */\r
192   protected Panel makeEmbeddedPopupMenu(MenuBar menuBar, String fn,\r
193           int fstyle, int fsz, boolean overrideFonts,\r
194           Hashtable embeddedPopup, Panel embeddedMenu,\r
195           MouseListener clickHandler)\r
196   {\r
197     if (embeddedPopup == null)\r
198     {\r
199       throw new Error(\r
200               "Implementation error - embeddedPopup must be non-null");\r
201     }\r
202     if (overrideFonts)\r
203     {\r
204       Font mbf = menuBar.getFont();\r
205       if (mbf != null)\r
206       {\r
207         fn = mbf.getName();\r
208         fstyle = mbf.getStyle();\r
209         fsz = mbf.getSize();\r
210       }\r
211     }\r
212     if (embeddedMenu == null)\r
213       embeddedMenu = new Panel();\r
214     FlowLayout flowLayout1 = new FlowLayout();\r
215     embeddedMenu.setBackground(Color.lightGray);\r
216     embeddedMenu.setLayout(flowLayout1);\r
217     // loop thru\r
218     for (int mbi = 0, nMbi = menuBar.getMenuCount(); mbi < nMbi; mbi++)\r
219     {\r
220       Menu mi = menuBar.getMenu(mbi);\r
221       Label elab = new Label(mi.getLabel());\r
222       elab.setFont(new java.awt.Font(fn, fstyle, fsz));\r
223       // add the menu entries\r
224       PopupMenu popup = new PopupMenu();\r
225       int m, mSize = mi.getItemCount();\r
226       for (m = 0; m < mSize; m++)\r
227       {\r
228         popup.add(mi.getItem(m));\r
229         mSize--;\r
230         m--;\r
231       }\r
232       embeddedPopup.put(elab, popup);\r
233       embeddedMenu.add(elab);\r
234       elab.addMouseListener(clickHandler);\r
235     }\r
236     flowLayout1.setAlignment(FlowLayout.LEFT);\r
237     flowLayout1.setHgap(2);\r
238     flowLayout1.setVgap(0);\r
239     return embeddedMenu;\r
240   }\r
241 \r
242   public void mousePressed(MouseEvent evt)\r
243   {\r
244     PopupMenu popup = null;\r
245     Label source = (Label) evt.getSource();\r
246     popup = getPopupMenu(source);\r
247     if (popup != null)\r
248     {\r
249       embeddedMenu.add(popup);\r
250       popup.show(embeddedMenu, source.getBounds().x, source.getBounds().y\r
251               + source.getBounds().getSize().height);\r
252     }\r
253   }\r
254 \r
255   /**\r
256    * get the menu for source from the hash.\r
257    * \r
258    * @param source\r
259    *          what was clicked on.\r
260    */\r
261   PopupMenu getPopupMenu(Label source)\r
262   {\r
263     return (PopupMenu) embeddedPopup.get(source);\r
264   }\r
265 \r
266   public void mouseClicked(MouseEvent evt)\r
267   {\r
268   }\r
269 \r
270   public void mouseReleased(MouseEvent evt)\r
271   {\r
272   }\r
273 \r
274   public void mouseEntered(MouseEvent evt)\r
275   {\r
276   }\r
277 \r
278   public void mouseExited(MouseEvent evt)\r
279   {\r
280   }\r
281 \r
282   /**\r
283    * called to clear the GUI resources taken up for embedding and remove any\r
284    * self references so we can be garbage collected.\r
285    */\r
286   public void destroyMenus()\r
287   {\r
288     if (embeddedPopup != null)\r
289     {\r
290       Enumeration e = embeddedPopup.keys();\r
291       while (e.hasMoreElements())\r
292       {\r
293         Label lb = (Label) e.nextElement();\r
294         lb.removeMouseListener(this);\r
295       }\r
296       embeddedPopup.clear();\r
297     }\r
298     if (embeddedMenu != null)\r
299     {\r
300       embeddedMenu.removeAll();\r
301     }\r
302   }\r
303 \r
304   /**\r
305    * calls destroyMenus()\r
306    */\r
307   public void finalize() throws Throwable\r
308   {\r
309     destroyMenus();\r
310     embeddedPopup = null;\r
311     embeddedMenu = null;\r
312     super.finalize();\r
313   }\r
314 }\r