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