001/* BasicMenuBarUI.java --
002   Copyright (C) 2002, 2004, 2005  Free Software Foundation, Inc.
003
004This file is part of GNU Classpath.
005
006GNU Classpath is free software; you can redistribute it and/or modify
007it under the terms of the GNU General Public License as published by
008the Free Software Foundation; either version 2, or (at your option)
009any later version.
010
011GNU Classpath is distributed in the hope that it will be useful, but
012WITHOUT ANY WARRANTY; without even the implied warranty of
013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014General Public License for more details.
015
016You should have received a copy of the GNU General Public License
017along with GNU Classpath; see the file COPYING.  If not, write to the
018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
01902110-1301 USA.
020
021Linking this library statically or dynamically with other modules is
022making a combined work based on this library.  Thus, the terms and
023conditions of the GNU General Public License cover the whole
024combination.
025
026As a special exception, the copyright holders of this library give you
027permission to link this library with independent modules to produce an
028executable, regardless of the license terms of these independent
029modules, and to copy and distribute the resulting executable under
030terms of your choice, provided that you also meet, for each linked
031independent module, the terms and conditions of the license of that
032module.  An independent module is a module which is not derived from
033or based on this library.  If you modify this library, you may extend
034this exception to your version of the library, but you are not
035obligated to do so.  If you do not wish to do so, delete this
036exception statement from your version. */
037
038
039package javax.swing.plaf.basic;
040
041import java.awt.Dimension;
042import java.awt.event.ActionEvent;
043import java.awt.event.ContainerEvent;
044import java.awt.event.ContainerListener;
045import java.awt.event.MouseEvent;
046import java.beans.PropertyChangeEvent;
047import java.beans.PropertyChangeListener;
048
049import javax.swing.AbstractAction;
050import javax.swing.Action;
051import javax.swing.ActionMap;
052import javax.swing.BoxLayout;
053import javax.swing.InputMap;
054import javax.swing.JComponent;
055import javax.swing.JMenu;
056import javax.swing.JMenuBar;
057import javax.swing.LookAndFeel;
058import javax.swing.MenuElement;
059import javax.swing.MenuSelectionManager;
060import javax.swing.SwingUtilities;
061import javax.swing.UIManager;
062import javax.swing.event.ChangeEvent;
063import javax.swing.event.ChangeListener;
064import javax.swing.event.MouseInputListener;
065import javax.swing.plaf.ActionMapUIResource;
066import javax.swing.plaf.ComponentUI;
067import javax.swing.plaf.MenuBarUI;
068
069/**
070 * UI Delegate for JMenuBar.
071 */
072public class BasicMenuBarUI extends MenuBarUI
073{
074
075  /**
076   * This action is performed for the action command 'takeFocus'.
077   */
078  private static class FocusAction
079    extends AbstractAction
080  {
081
082    /**
083     * Creates a new FocusAction.
084     */
085    FocusAction()
086    {
087      super("takeFocus");
088    }
089
090    /**
091     * Performs the action.
092     */
093    public void actionPerformed(ActionEvent event)
094    {
095      // In the JDK this action seems to pop up the first menu of the
096      // menu bar.
097      JMenuBar menuBar = (JMenuBar) event.getSource();
098      MenuSelectionManager defaultManager =
099        MenuSelectionManager.defaultManager();
100      MenuElement me[];
101      MenuElement subElements[];
102      JMenu menu = menuBar.getMenu(0);
103      if (menu != null)
104        {
105          me = new MenuElement[3];
106          me[0] = (MenuElement) menuBar;
107          me[1] = (MenuElement) menu;
108          me[2] = (MenuElement) menu.getPopupMenu();
109          defaultManager.setSelectedPath(me);
110        }
111    }
112
113  }
114
115  protected ChangeListener changeListener;
116
117  /*ContainerListener that listens to the ContainerEvents fired from menu bar*/
118  protected ContainerListener containerListener;
119
120  /*Property change listeners that listener to PropertyChangeEvent from menu bar*/
121  private PropertyChangeListener propertyChangeListener;
122
123  /* menu bar for which this UI delegate is for*/
124  protected JMenuBar menuBar;
125
126  /* MouseListener that listens to the mouseEvents fired from menu bar*/
127  private MouseInputListener mouseListener;
128
129  /**
130   * Creates a new BasicMenuBarUI object.
131   */
132  public BasicMenuBarUI()
133  {
134    changeListener = createChangeListener();
135    containerListener = createContainerListener();
136    propertyChangeListener = new PropertyChangeHandler();
137    mouseListener = new MouseInputHandler();
138  }
139
140  /**
141   * Creates ChangeListener
142   *
143   * @return The ChangeListener
144   */
145  protected ChangeListener createChangeListener()
146  {
147    return new ChangeHandler();
148  }
149
150  /**
151   * Creates ContainerListener() to listen for ContainerEvents
152   * fired by JMenuBar.
153   *
154   * @return The ContainerListener
155   */
156  protected ContainerListener createContainerListener()
157  {
158    return new ContainerHandler();
159  }
160
161  /**
162   * Factory method to create a BasicMenuBarUI for the given {@link
163   * JComponent}, which should be a {@link JMenuBar}.
164   *
165   * @param x The {@link JComponent} a UI is being created for.
166   *
167   * @return A BasicMenuBarUI for the {@link JComponent}.
168   */
169  public static ComponentUI createUI(JComponent x)
170  {
171    return new BasicMenuBarUI();
172  }
173
174  /**
175   * Returns maximum size for the specified menu bar
176   *
177   * @param c component for which to get maximum size
178   *
179   * @return  Maximum size for the specified menu bar
180   */
181  public Dimension getMaximumSize(JComponent c)
182  {
183    // let layout manager calculate its size
184    return null;
185  }
186
187  /**
188   * Returns maximum allowed size of JMenuBar.
189   *
190   * @param c menuBar for which to return maximum size
191   *
192   * @return Maximum size of the give menu bar.
193   */
194  public Dimension getMinimumSize(JComponent c)
195  {
196    // let layout manager calculate its size
197    return null;
198  }
199
200  /**
201   * Returns preferred size of JMenuBar.
202   *
203   * @param c menuBar for which to return preferred size
204   *
205   * @return Preferred size of the give menu bar.
206   */
207  public Dimension getPreferredSize(JComponent c)
208  {
209    // let layout manager calculate its size
210    return null;
211  }
212
213  /**
214   * Initializes any default properties that this UI has from the defaults for
215   * the Basic look and feel.
216   */
217  protected void installDefaults()
218  {
219    LookAndFeel.installBorder(menuBar, "MenuBar.border");
220    LookAndFeel.installColorsAndFont(menuBar, "MenuBar.background",
221                                     "MenuBar.foreground", "MenuBar.font");
222    menuBar.setOpaque(true);
223  }
224
225  /**
226   * This method installs the keyboard actions for the JMenuBar.
227   */
228  protected void installKeyboardActions()
229  {
230    // Install InputMap.
231    Object[] bindings =
232      (Object[]) SharedUIDefaults.get("MenuBar.windowBindings");
233    InputMap inputMap = LookAndFeel.makeComponentInputMap(menuBar, bindings);
234    SwingUtilities.replaceUIInputMap(menuBar,
235                                     JComponent.WHEN_IN_FOCUSED_WINDOW,
236                                     inputMap);
237
238    // Install ActionMap.
239    SwingUtilities.replaceUIActionMap(menuBar, getActionMap());
240  }
241
242  /**
243   * Creates and returns the shared action map for JTrees.
244   *
245   * @return the shared action map for JTrees
246   */
247  private ActionMap getActionMap()
248  {
249    ActionMap am = (ActionMap) UIManager.get("MenuBar.actionMap");
250    if (am == null)
251      {
252        am = createDefaultActions();
253        UIManager.getLookAndFeelDefaults().put("MenuBar.actionMap", am);
254      }
255    return am;
256  }
257
258  /**
259   * Creates the default actions when there are none specified by the L&F.
260   *
261   * @return the default actions
262   */
263  private ActionMap createDefaultActions()
264  {
265    ActionMapUIResource am = new ActionMapUIResource();
266    Action action = new FocusAction();
267    am.put(action.getValue(Action.NAME), action);
268    return am;
269  }
270
271  /**
272   * This method installs the listeners needed for this UI to function.
273   */
274  protected void installListeners()
275  {
276    menuBar.addContainerListener(containerListener);
277    menuBar.addPropertyChangeListener(propertyChangeListener);
278    menuBar.addMouseListener(mouseListener);
279  }
280
281  /**
282  * Installs and initializes all fields for this UI delegate. Any properties
283  * of the UI that need to be initialized and/or set to defaults will be
284  * done now. It will also install any listeners necessary.
285  *
286  * @param c The {@link JComponent} that is having this UI installed.
287  */
288  public void installUI(JComponent c)
289  {
290    super.installUI(c);
291    menuBar = (JMenuBar) c;
292    menuBar.setLayout(new BoxLayout(menuBar, BoxLayout.X_AXIS));
293    installDefaults();
294    installListeners();
295    installKeyboardActions();
296  }
297
298  /**
299   * This method uninstalls the defaults and nulls any objects created during
300   * install.
301   */
302  protected void uninstallDefaults()
303  {
304    menuBar.setBackground(null);
305    menuBar.setBorder(null);
306    menuBar.setFont(null);
307    menuBar.setForeground(null);
308  }
309
310  /**
311   * This method reverses the work done in installKeyboardActions.
312   */
313  protected void uninstallKeyboardActions()
314  {
315    SwingUtilities.replaceUIInputMap(menuBar,
316                                     JComponent.WHEN_IN_FOCUSED_WINDOW, null);
317    SwingUtilities.replaceUIActionMap(menuBar, null);
318  }
319
320  /**
321   * Unregisters all the listeners that this UI delegate was using.
322   */
323  protected void uninstallListeners()
324  {
325    menuBar.removeContainerListener(containerListener);
326    menuBar.removePropertyChangeListener(propertyChangeListener);
327    menuBar.removeMouseListener(mouseListener);
328  }
329
330  /**
331   * Performs the opposite of installUI. Any properties or resources that need
332   * to be cleaned up will be done now. It will also uninstall any listeners
333   * it has. In addition, any properties of this UI will be nulled.
334   *
335   * @param c The {@link JComponent} that is having this UI uninstalled.
336   */
337  public void uninstallUI(JComponent c)
338  {
339    uninstallDefaults();
340    uninstallListeners();
341    uninstallKeyboardActions();
342    menuBar = null;
343  }
344
345  private class ChangeHandler implements ChangeListener
346  {
347    public void stateChanged(ChangeEvent event)
348    {
349      // TODO: What should be done here, if anything?
350    }
351  }
352
353  /**
354   * This class handles ContainerEvents fired by JMenuBar. It revalidates
355   * and repaints menu bar whenever menu is added or removed from it.
356   */
357  private class ContainerHandler implements ContainerListener
358  {
359    /**
360     * This method is called whenever menu is added to the menu bar
361     *
362     * @param e The ContainerEvent.
363     */
364    public void componentAdded(ContainerEvent e)
365    {
366      menuBar.revalidate();
367      menuBar.repaint();
368    }
369
370    /**
371     * This method is called whenever menu is removed from the menu bar.
372     *
373     * @param e The ContainerEvent.
374     */
375    public void componentRemoved(ContainerEvent e)
376    {
377      menuBar.revalidate();
378      menuBar.repaint();
379    }
380  }
381
382  /**
383   * This class handles PropertyChangeEvents fired from the JMenuBar
384   */
385  private class PropertyChangeHandler implements PropertyChangeListener
386  {
387    /**
388     * This method is called whenever one of the properties of the MenuBar
389     * changes.
390     *
391     * @param e The PropertyChangeEvent.
392     */
393    public void propertyChange(PropertyChangeEvent e)
394    {
395      if (e.getPropertyName().equals("borderPainted"))
396        menuBar.repaint();
397      if (e.getPropertyName().equals("margin"))
398        menuBar.repaint();
399    }
400  }
401
402  private class MouseInputHandler implements MouseInputListener
403  {
404    /**
405     * Handles mouse clicked event
406     *
407     * @param e Mouse event
408     */
409    public void mouseClicked(MouseEvent e)
410    {
411      MenuElement[] me = menuBar.getSubElements();
412
413      for (int i = 0; i < me.length; i++)
414        {
415          JMenu menu = menuBar.getMenu(i);
416          if (menu != null)
417            menu.setSelected(false);
418        }
419    }
420
421    /**
422     * Handles mouse pressed event
423     *
424     * @param e Mouse event
425     */
426    public void mousePressed(MouseEvent e)
427    {
428      // TODO: What should be done here, if anything?
429    }
430
431    /**
432     * Handles mouse released event
433     *
434     * @param e Mouse event
435     */
436    public void mouseReleased(MouseEvent e)
437    {
438      // TODO: What should be done here, if anything?
439    }
440
441    /**
442     * Handles mouse exited event
443     *
444     * @param e Mouse event
445     */
446    public void mouseExited(MouseEvent e)
447    {
448      // TODO: What should be done here, if anything?
449    }
450
451    /**
452     * Handles mouse dragged event
453     *
454     * @param e Mouse event
455     */
456    public void mouseDragged(MouseEvent e)
457    {
458      // TODO: What should be done here, if anything?
459    }
460
461    /**
462     * Handles mouse moved event
463     *
464     * @param e Mouse event
465     */
466    public void mouseMoved(MouseEvent e)
467    {
468      // TODO: What should be done here, if anything?
469    }
470
471    /**
472     * Handles mouse entered event
473     *
474     * @param e Mouse event
475     */
476    public void mouseEntered(MouseEvent e)
477    {
478      // TODO: What should be done here, if anything?
479    }
480  }
481}