minor Platform.isAMac() clean up
[jalview.git] / src / jalview / appletgui / EmbmenuFrame.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ 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.Platform;
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.HashMap;
39 import java.util.Map;
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("Arial",
57           Font.PLAIN, 11);
58
59   public static final Font DEFAULT_MENU_FONT = FONT_ARIAL_PLAIN_11;
60
61   /**
62    * map from labels to popup menus for the embedded menubar
63    */
64   protected Map<Label, PopupMenu> embeddedPopup = new HashMap<Label, PopupMenu>();
65
66   /**
67    * the embedded menu is built on this and should be added to the frame at the
68    * appropriate position.
69    * 
70    */
71   protected Panel embeddedMenu;
72
73   public EmbmenuFrame() throws HeadlessException
74   {
75     super();
76   }
77
78   public EmbmenuFrame(String title) throws HeadlessException
79   {
80     super(title);
81   }
82
83   /**
84    * Check if the applet is running on a platform that requires the Frame
85    * menuBar to be embedded, and if so, embeds it.
86    * 
87    * @param tobeAdjusted
88    *          the panel that is to be reduced to make space for the embedded
89    *          menu bar
90    * @return true if menuBar was embedded and tobeAdjusted's height modified
91    */
92   protected boolean embedMenuIfNeeded(Panel tobeAdjusted)
93   {
94     MenuBar topMenuBar = getMenuBar();
95     if (topMenuBar == null)
96     {
97       return false;
98     }
99     // DEBUG Hint: can test embedded menus by inserting true here.
100     if (Platform.isAMac())
101     {
102       // Build the embedded menu panel, allowing override with system font
103       embeddedMenu = makeEmbeddedPopupMenu(topMenuBar, true, false);
104       setMenuBar(null);
105       // add the components to the Panel area.
106       add(embeddedMenu, BorderLayout.NORTH);
107       tobeAdjusted.setSize(getSize().width,
108               getSize().height - embeddedMenu.getHeight());
109       return true;
110     }
111     return false;
112   }
113
114   /**
115    * Create or add elements to the embedded menu from menuBar. This removes all
116    * menu from menuBar and it is up to the caller to remove the now useless
117    * menuBar from the Frame if it is already attached.
118    * 
119    * @param menuBar
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,
127           boolean overrideFonts, boolean append)
128   {
129     if (!append)
130     {
131       embeddedPopup.clear(); // TODO: check if j1.1
132       if (embeddedMenu != null)
133       {
134         embeddedMenu.removeAll();
135       }
136     }
137     embeddedMenu = makeEmbeddedPopupMenu(menuBar, DEFAULT_MENU_FONT,
138             overrideFonts, new Panel(), this);
139     return embeddedMenu;
140   }
141
142   /**
143    * Generic method to move elements from menubar onto embeddedMenu using the
144    * existing or the supplied font, and adds binding from panel to attached
145    * menus in embeddedPopup This removes all menu from menuBar and it is up to
146    * the caller to remove the now useless menuBar from the Frame if it is
147    * already attached.
148    * 
149    * @param menuBar
150    *          must be non-null
151    * @param font
152    * @param overrideFonts
153    * @param embeddedMenu
154    *          if null, a new panel will be created and returned
155    * @param clickHandler
156    *          - usually the instance of EmbmenuFrame that holds references to
157    *          embeddedPopup and embeddedMenu
158    * @return the panel instance for convenience.
159    */
160   protected Panel makeEmbeddedPopupMenu(MenuBar menuBar, Font font,
161           boolean overrideFonts, Panel embeddedMenu,
162           MouseListener clickHandler)
163   {
164     if (overrideFonts)
165     {
166       Font mbf = menuBar.getFont();
167       if (mbf != null)
168       {
169         font = mbf;
170       }
171     }
172     if (embeddedMenu == null)
173     {
174       embeddedMenu = new Panel();
175     }
176     FlowLayout flowLayout1 = new FlowLayout();
177     embeddedMenu.setBackground(Color.lightGray);
178     embeddedMenu.setLayout(flowLayout1);
179     // loop thru
180     for (int mbi = 0, nMbi = menuBar.getMenuCount(); mbi < nMbi; mbi++)
181     {
182       Menu mi = menuBar.getMenu(mbi);
183       Label elab = new Label(mi.getLabel());
184       elab.setFont(font);
185       // add the menu entries
186       PopupMenu popup = new PopupMenu();
187       int m, mSize = mi.getItemCount();
188       for (m = 0; m < mSize; m++)
189       {
190         popup.add(mi.getItem(m));
191         mSize--;
192         m--;
193       }
194       embeddedPopup.put(elab, popup);
195       embeddedMenu.add(elab);
196       elab.addMouseListener(clickHandler);
197     }
198     flowLayout1.setAlignment(FlowLayout.LEFT);
199     flowLayout1.setHgap(2);
200     flowLayout1.setVgap(0);
201     return embeddedMenu;
202   }
203
204   public void mousePressed(MouseEvent evt)
205   {
206     PopupMenu popup = null;
207     Label source = (Label) evt.getSource();
208     popup = getPopupMenu(source);
209     if (popup != null)
210     {
211       embeddedMenu.add(popup);
212       popup.show(embeddedMenu, source.getBounds().x,
213               source.getBounds().y + source.getBounds().getSize().height);
214     }
215   }
216
217   /**
218    * get the menu for source from the hash.
219    * 
220    * @param source
221    *          what was clicked on.
222    */
223   PopupMenu getPopupMenu(Label source)
224   {
225     return embeddedPopup.get(source);
226   }
227
228   public void mouseClicked(MouseEvent evt)
229   {
230   }
231
232   public void mouseReleased(MouseEvent evt)
233   {
234   }
235
236   public void mouseEntered(MouseEvent evt)
237   {
238   }
239
240   public void mouseExited(MouseEvent evt)
241   {
242   }
243
244   /**
245    * called to clear the GUI resources taken up for embedding and remove any
246    * self references so we can be garbage collected.
247    */
248   public void destroyMenus()
249   {
250     if (embeddedPopup != null)
251     {
252       for (Label lb : embeddedPopup.keySet())
253       {
254         lb.removeMouseListener(this);
255       }
256       embeddedPopup.clear();
257     }
258     if (embeddedMenu != null)
259     {
260       embeddedMenu.removeAll();
261     }
262   }
263
264   /**
265    * calls destroyMenus()
266    */
267   public void finalize() throws Throwable
268   {
269     destroyMenus();
270     embeddedPopup = null;
271     embeddedMenu = null;
272     super.finalize();
273   }
274 }