001    /* BasicMenuBarUI.java --
002       Copyright (C) 2002, 2004, 2005  Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    
039    package javax.swing.plaf.basic;
040    
041    import java.awt.Dimension;
042    import java.awt.event.ActionEvent;
043    import java.awt.event.ContainerEvent;
044    import java.awt.event.ContainerListener;
045    import java.awt.event.MouseEvent;
046    import java.beans.PropertyChangeEvent;
047    import java.beans.PropertyChangeListener;
048    
049    import javax.swing.AbstractAction;
050    import javax.swing.Action;
051    import javax.swing.ActionMap;
052    import javax.swing.BoxLayout;
053    import javax.swing.InputMap;
054    import javax.swing.JComponent;
055    import javax.swing.JMenu;
056    import javax.swing.JMenuBar;
057    import javax.swing.LookAndFeel;
058    import javax.swing.MenuElement;
059    import javax.swing.MenuSelectionManager;
060    import javax.swing.SwingUtilities;
061    import javax.swing.UIManager;
062    import javax.swing.event.ChangeEvent;
063    import javax.swing.event.ChangeListener;
064    import javax.swing.event.MouseInputListener;
065    import javax.swing.plaf.ActionMapUIResource;
066    import javax.swing.plaf.ComponentUI;
067    import javax.swing.plaf.MenuBarUI;
068    
069    /**
070     * UI Delegate for JMenuBar.
071     */
072    public 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    }