JAL-3111 help file spelling correction
[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 java.awt.BorderLayout;
24 import java.awt.Color;
25 import java.awt.FlowLayout;
26 import java.awt.Font;
27 import java.awt.Frame;
28 import java.awt.HeadlessException;
29 import java.awt.Label;
30 import java.awt.Menu;
31 import java.awt.MenuBar;
32 import java.awt.Panel;
33 import java.awt.PopupMenu;
34 import java.awt.event.MouseEvent;
35 import java.awt.event.MouseListener;
36 import java.util.HashMap;
37 import java.util.Map;
38
39 /**
40  * This class implements a pattern for embedding toolbars as a panel with popups
41  * for situations where the system menu bar is either invisible or
42  * inappropriate. It was derived from the code for embedding the jalview applet
43  * alignFrame as a component on the web-page, which requires the local
44  * alignFrame menu to be attached to that panel rather than placed on the parent
45  * (which isn't allowed anyhow). TODO: try to modify the embeddedMenu display so
46  * it looks like a real toolbar menu TODO: modify click/mouse handler for
47  * embeddedMenu so it behaves more like a real pulldown menu toolbar
48  * 
49  * @author Jim Procter and Andrew Waterhouse
50  * 
51  */
52 public class EmbmenuFrame extends Frame
53         implements MouseListener, AutoCloseable
54 {
55   protected static final Font FONT_ARIAL_PLAIN_11 = new Font("Arial",
56           Font.PLAIN, 11);
57
58   public static final Font DEFAULT_MENU_FONT = FONT_ARIAL_PLAIN_11;
59
60   /**
61    * map from labels to popup menus for the embedded menubar
62    */
63   protected Map<Label, PopupMenu> embeddedPopup = new HashMap<>();
64
65   /**
66    * the embedded menu is built on this and should be added to the frame at the
67    * appropriate position.
68    * 
69    */
70   protected Panel embeddedMenu;
71
72   public EmbmenuFrame() throws HeadlessException
73   {
74     super();
75   }
76
77   public EmbmenuFrame(String title) throws HeadlessException
78   {
79     super(title);
80   }
81
82   /**
83    * Check if the applet is running on a platform that requires the Frame
84    * menuBar to be embedded, and if so, embeds it.
85    * 
86    * @param tobeAdjusted
87    *          the panel that is to be reduced to make space for the embedded
88    *          menu bar
89    * @return true if menuBar was embedded and tobeAdjusted's height modified
90    */
91   protected boolean embedMenuIfNeeded(Panel tobeAdjusted)
92   {
93     MenuBar topMenuBar = getMenuBar();
94     if (topMenuBar == null)
95     {
96       return false;
97     }
98     // DEBUG Hint: can test embedded menus by inserting true here.
99     if (new jalview.util.Platform().isAMac())
100     {
101       // Build the embedded menu panel, allowing override with system font
102       embeddedMenu = makeEmbeddedPopupMenu(topMenuBar, true, false);
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 overrideFonts
120    * @param append
121    *          true means existing menu will be emptied before adding new
122    *          elements
123    * @return
124    */
125   protected Panel makeEmbeddedPopupMenu(MenuBar menuBar,
126           boolean overrideFonts, boolean append)
127   {
128     if (!append)
129     {
130       embeddedPopup.clear(); // TODO: check if j1.1
131       if (embeddedMenu != null)
132       {
133         embeddedMenu.removeAll();
134       }
135     }
136     embeddedMenu = makeEmbeddedPopupMenu(menuBar, DEFAULT_MENU_FONT,
137             overrideFonts, new Panel(), this);
138     return embeddedMenu;
139   }
140
141   /**
142    * Generic method to move elements from menubar onto embeddedMenu using the
143    * existing or the supplied font, and adds binding from panel to attached
144    * menus in embeddedPopup This removes all menu from menuBar and it is up to
145    * the caller to remove the now useless menuBar from the Frame if it is
146    * already attached.
147    * 
148    * @param menuBar
149    *          must be non-null
150    * @param font
151    * @param overrideFonts
152    * @param embeddedMenu
153    *          if null, a new panel will be created and returned
154    * @param clickHandler
155    *          - usually the instance of EmbmenuFrame that holds references to
156    *          embeddedPopup and embeddedMenu
157    * @return the panel instance for convenience.
158    */
159   protected Panel makeEmbeddedPopupMenu(MenuBar menuBar, Font font,
160           boolean overrideFonts, Panel embeddedMenu,
161           MouseListener clickHandler)
162   {
163     if (overrideFonts)
164     {
165       Font mbf = menuBar.getFont();
166       if (mbf != null)
167       {
168         font = mbf;
169       }
170     }
171     if (embeddedMenu == null)
172     {
173       embeddedMenu = new Panel();
174     }
175     FlowLayout flowLayout1 = new FlowLayout();
176     embeddedMenu.setBackground(Color.lightGray);
177     embeddedMenu.setLayout(flowLayout1);
178     // loop thru
179     for (int mbi = 0, nMbi = menuBar.getMenuCount(); mbi < nMbi; mbi++)
180     {
181       Menu mi = menuBar.getMenu(mbi);
182       Label elab = new Label(mi.getLabel());
183       elab.setFont(font);
184       // add the menu entries
185       PopupMenu popup = new PopupMenu();
186       int m, mSize = mi.getItemCount();
187       for (m = 0; m < mSize; m++)
188       {
189         popup.add(mi.getItem(m));
190         mSize--;
191         m--;
192       }
193       embeddedPopup.put(elab, popup);
194       embeddedMenu.add(elab);
195       elab.addMouseListener(clickHandler);
196     }
197     flowLayout1.setAlignment(FlowLayout.LEFT);
198     flowLayout1.setHgap(2);
199     flowLayout1.setVgap(0);
200     return embeddedMenu;
201   }
202
203   @Override
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   @Override
229   public void mouseClicked(MouseEvent evt)
230   {
231   }
232
233   @Override
234   public void mouseReleased(MouseEvent evt)
235   {
236   }
237
238   @Override
239   public void mouseEntered(MouseEvent evt)
240   {
241   }
242
243   @Override
244   public void mouseExited(MouseEvent evt)
245   {
246   }
247
248   /**
249    * called to clear the GUI resources taken up for embedding and remove any
250    * self references so we can be garbage collected.
251    */
252   public void destroyMenus()
253   {
254     if (embeddedPopup != null)
255     {
256       for (Label lb : embeddedPopup.keySet())
257       {
258         lb.removeMouseListener(this);
259       }
260       embeddedPopup.clear();
261     }
262     if (embeddedMenu != null)
263     {
264       embeddedMenu.removeAll();
265     }
266   }
267
268   /**
269    * calls destroyMenus()
270    */
271   @Override
272   public void close()
273   {
274     destroyMenus();
275     embeddedPopup = null;
276     embeddedMenu = null;
277     // no close for Frame
278     // super.finalize();
279   }
280 }