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