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