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