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