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