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