OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / javax / swing / JMenuBar.java
1 /* JMenuBar.java --
2    Copyright (C) 2002, 2004, 2005, 2006  Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING.  If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library.  Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module.  An independent module is a module which is not derived from
33 or based on this library.  If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so.  If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package javax.swing;
40
41 import java.awt.Component;
42 import java.awt.Graphics;
43 import java.awt.Insets;
44 import java.awt.event.KeyEvent;
45 import java.awt.event.MouseEvent;
46
47 import javax.accessibility.Accessible;
48 import javax.accessibility.AccessibleContext;
49 import javax.accessibility.AccessibleRole;
50 import javax.accessibility.AccessibleSelection;
51 import javax.accessibility.AccessibleStateSet;
52 import javax.swing.plaf.MenuBarUI;
53
54 import javax.swing.border.Border;
55
56 /**
57  * JMenuBar is a container for menu's. For a menu bar to be seen on the
58  * screen, at least one menu should be added to it. Just like adding
59  * components to container, one can use add() to add menu's to the menu bar.
60  * Menu's will be displayed in the menu  bar in the order they were added.
61  * The JMenuBar uses selectionModel to keep track of selected menu index.
62  * JMenuBar's selectionModel will fire ChangeEvents to its registered 
63  * listeners when the selected index changes.
64  */
65 public class JMenuBar extends JComponent implements Accessible, MenuElement
66 {
67   /**
68    * Provides accessibility support for <code>JMenuBar</code>.
69    * 
70    * @author Roman Kennke (kennke@aicas.com)
71    */
72   protected class AccessibleJMenuBar extends AccessibleJComponent
73     implements AccessibleSelection
74   {
75
76     /**
77      * Returns the number of selected items in the menu bar. Possible values
78      * are <code>0</code> if nothing is selected, or <code>1</code> if one
79      * item is selected.
80      *
81      * @return the number of selected items in the menu bar
82      */
83     public int getAccessibleSelectionCount()
84     {
85       int count = 0;
86       if (getSelectionModel().getSelectedIndex() != -1)
87         count = 1;
88       return count;
89     }
90
91     /**
92      * Returns the selected with index <code>i</code> menu, or
93      * <code>null</code> if the specified menu is not selected.
94      *
95      * @param i the index of the menu to return
96      *
97      * @return the selected with index <code>i</code> menu, or
98      *         <code>null</code> if the specified menu is not selected
99      */
100     public Accessible getAccessibleSelection(int i)
101     {
102       if (getSelectionModel().getSelectedIndex() != i)
103         return null;
104       return getMenu(i);
105     }
106
107     /**
108      * Returns <code>true</code> if the specified menu is selected,
109      * <code>false</code> otherwise.
110      *
111      * @param i the index of the menu to check
112      *
113      *@return <code>true</code> if the specified menu is selected,
114      *        <code>false</code> otherwise
115      */
116     public boolean isAccessibleChildSelected(int i)
117     {
118       return getSelectionModel().getSelectedIndex() == i;
119     }
120
121     /**
122      * Selects the menu with index <code>i</code>. If another menu is already
123      * selected, this will be deselected.
124      *
125      * @param i the menu to be selected
126      */
127     public void addAccessibleSelection(int i)
128     {
129       getSelectionModel().setSelectedIndex(i);
130     }
131
132     /**
133      * Deselects the menu with index <code>i</code>.
134      *
135      * @param i the menu index to be deselected
136      */
137     public void removeAccessibleSelection(int i)
138     {
139       if (getSelectionModel().getSelectedIndex() == i)
140         getSelectionModel().clearSelection();
141     }
142
143     /**
144      * Deselects all possibly selected menus.
145      */
146     public void clearAccessibleSelection()
147     {
148       getSelectionModel().clearSelection();
149     }
150
151     /**
152      * In menu bars it is not possible to select all items, so this method
153      * does nothing.
154      */
155     public void selectAllAccessibleSelection()
156     {
157       // In menu bars it is not possible to select all items, so this method
158       // does nothing.
159     }
160
161     /**
162      * Returns the accessible role of <code>JMenuBar</code>, which is
163      * {@link AccessibleRole#MENU_BAR}.
164      *
165      * @return the accessible role of <code>JMenuBar</code>, which is
166      *         {@link AccessibleRole#MENU_BAR}
167      */
168     public AccessibleRole getAccessibleRole()
169     {
170       return AccessibleRole.MENU_BAR;
171     }
172
173     /**
174      * Returns the <code>AccessibleSelection</code> for this object. This
175      * method returns <code>this</code>, since the
176      * <code>AccessibleJMenuBar</code> manages its selection itself.
177      *
178      * @return the <code>AccessibleSelection</code> for this object
179      */
180     public AccessibleSelection getAccessibleSelection()
181     {
182       return this;
183     }
184
185     /**
186      * Returns the state of this <code>AccessibleJMenuBar</code>.
187      *
188      * @return the state of this <code>AccessibleJMenuBar</code>.
189      */
190     public AccessibleStateSet getAccessibleStateSet()
191     {
192       AccessibleStateSet stateSet = super.getAccessibleStateSet();
193       // TODO: Figure out what state must be added to the super state set.
194       return stateSet;
195     }
196   }
197
198   private static final long serialVersionUID = -8191026883931977036L;
199
200   /** JMenuBar's model. It keeps track of selected menu's index */
201   private transient SingleSelectionModel selectionModel;
202
203   /* borderPainted property indicating if the menuBar's border will be painted*/
204   private boolean borderPainted;
205
206   /* margin between menu bar's border and its menues*/
207   private Insets margin;
208
209   /**
210    * Creates a new JMenuBar object.
211    */
212   public JMenuBar()
213   {
214     selectionModel = new DefaultSingleSelectionModel();
215     borderPainted = true;
216     updateUI();
217   }
218
219   /**
220    * Adds menu to the menu bar
221    *
222    * @param c menu to add
223    *
224    * @return reference to the added menu
225    */
226   public JMenu add(JMenu c)
227   {
228     c.setAlignmentX(Component.LEFT_ALIGNMENT);
229     super.add(c);
230     return c;
231   }
232
233   /**
234    * This method overrides addNotify() in the Container to register
235    * this menu bar with the current keyboard manager.
236    */
237   public void addNotify()
238   {
239     super.addNotify();
240     KeyboardManager.getManager().registerJMenuBar(this);
241   }
242
243   public AccessibleContext getAccessibleContext()
244   {
245     if (accessibleContext == null)
246       accessibleContext = new AccessibleJMenuBar();
247     return accessibleContext;
248   }
249
250   /**
251    * Returns reference to this menu bar
252    *
253    * @return reference to this menu bar
254    */
255   public Component getComponent()
256   {
257     return this;
258   }
259
260   /**
261    * Returns component at the specified index.
262    *
263    * @param i index of the component to get
264    *
265    * @return component at the specified index. Null is returned if
266    * component at the specified index doesn't exist.
267    * @deprecated Replaced by getComponent(int)
268    */
269   public Component getComponentAtIndex(int i)
270   {
271     return getComponent(i);
272   }
273
274   /**
275    * Returns index of the specified component
276    *
277    * @param c Component to search for
278    *
279    * @return index of the specified component. -1 is returned if
280    * specified component doesnt' exist in the menu bar.
281    */
282   public int getComponentIndex(Component c)
283   {
284     Component[] comps = getComponents();
285
286     int index = -1;
287
288     for (int i = 0; i < comps.length; i++)
289       {
290         if (comps[i].equals(c))
291           {
292             index = i;
293             break;
294           }
295       }
296
297     return index;
298   }
299
300   /**
301    * This method is not implemented and will throw an {@link Error} if called.
302    *
303    * @return This method never returns anything, it throws an exception.
304    */
305   public JMenu getHelpMenu()
306   {
307     // the following error matches the behaviour of the reference 
308     // implementation...
309     throw new Error("getHelpMenu() is not implemented");
310   }
311
312   /**
313    * Returns the margin between the menu bar's border and its menus.  If the
314    * margin is <code>null</code>, this method returns 
315    * <code>new Insets(0, 0, 0, 0)</code>.
316    *
317    * @return The margin (never <code>null</code>).
318    * 
319    * @see #setMargin(Insets)
320    */
321   public Insets getMargin()
322   {
323     if (margin == null)
324       return new Insets(0, 0, 0, 0);
325     else
326       return margin;
327   }
328
329   /**
330    * Return menu at the specified index. If component at the
331    * specified index is not a menu, then null is returned.
332    *
333    * @param index index to look for the menu
334    *
335    * @return menu at specified index, or null if menu doesn't exist
336    * at the specified index.
337    */
338   public JMenu getMenu(int index)
339   {
340     if (getComponentAtIndex(index) instanceof JMenu)
341       return (JMenu) getComponentAtIndex(index);
342     else
343       return null;
344   }
345
346   /**
347    * Returns number of menu's in this menu bar
348    *
349    * @return number of menu's in this menu bar
350    */
351   public int getMenuCount()
352   {
353     return getComponentCount();
354   }
355
356   /**
357    * Returns selection model for this menu bar. SelectionModel
358    * keeps track of the selected menu in the menu bar. Whenever
359    * selected property of selectionModel changes, the ChangeEvent
360    * will be fired its ChangeListeners.
361    *
362    * @return selection model for this menu bar.
363    */
364   public SingleSelectionModel getSelectionModel()
365   {
366     return selectionModel;
367   }
368
369   /**
370    * Method of MenuElement interface. It returns subcomponents
371    * of the menu bar, which are all the menues that it contains.
372    *
373    * @return MenuElement[] array containing menues in this menu bar
374    */
375   public MenuElement[] getSubElements()
376   {
377     MenuElement[] subElements = new MenuElement[getComponentCount()];
378
379     int j = 0;
380     boolean doResize = false;
381     MenuElement menu;
382     for (int i = 0; i < getComponentCount(); i++)
383       {
384         menu = getMenu(i);
385         if (menu != null)
386           {
387             subElements[j++] = (MenuElement) menu;
388           }
389         else
390           doResize = true;
391       }
392
393     if (! doResize)
394       return subElements;
395     else
396       {
397         MenuElement[] subElements2 = new MenuElement[j];
398         for (int i = 0; i < j; i++)
399           subElements2[i] = subElements[i];
400
401         return subElements2;
402       }
403   }
404
405   /**
406     * Set the "UI" property of the menu bar, which is a look and feel class
407     * responsible for handling the menuBar's input events and painting it.
408     *
409     * @return The current "UI" property
410     */
411   public MenuBarUI getUI()
412   {
413     return (MenuBarUI) ui;
414   }
415
416   /**
417    * This method returns a name to identify which look and feel class will be
418    * the UI delegate for the menu bar.
419    *
420    * @return The Look and Feel classID. "MenuBarUI"
421    */
422   public String getUIClassID()
423   {
424     return "MenuBarUI";
425   }
426
427   /**
428    * Returns true if menu bar paints its border and false otherwise
429    *
430    * @return true if menu bar paints its border and false otherwise
431    */
432   public boolean isBorderPainted()
433   {
434     return borderPainted;
435   }
436
437   /**
438    * Returns true if some menu in menu bar is selected.
439    *
440    * @return true if some menu in menu bar is selected and false otherwise
441    */
442   public boolean isSelected()
443   {
444     return selectionModel.isSelected();
445   }
446
447   /**
448    * This method does nothing by default. This method is need for the
449    * MenuElement interface to be implemented.
450    *
451    * @param isIncluded true if menuBar is included in the selection
452    * and false otherwise
453    */
454   public void menuSelectionChanged(boolean isIncluded)
455   {
456     // Do nothing - needed for implementation of MenuElement interface
457   }
458
459   /**
460    * Paints border of the menu bar, if its borderPainted property is set to
461    * true.
462    *
463    * @param g The graphics context with which to paint the border
464    */
465   protected void paintBorder(Graphics g)
466   {
467     if (borderPainted)
468       {
469         Border border = getBorder();
470         if (border != null)
471           getBorder().paintBorder(this, g, 0, 0, getSize(null).width,
472                                   getSize(null).height);
473       }
474   }
475
476   /**
477    * A string that describes this JMenuBar. Normally only used
478    * for debugging.
479    *
480    * @return A string describing this JMenuBar
481    */
482   protected String paramString()
483   {
484     StringBuffer sb = new StringBuffer();
485     sb.append(super.paramString());
486     sb.append(",margin=");
487     if (getMargin() != null)
488       sb.append(getMargin());
489     sb.append(",paintBorder=").append(isBorderPainted());
490     return sb.toString();
491   }
492
493   /**
494    * Process key events forwarded from MenuSelectionManager. This method
495    * doesn't do anything. It is here to conform to the MenuElement interface.
496    *
497    * @param e event forwarded from MenuSelectionManager
498    * @param path path to the menu element from which event was generated
499    * @param manager MenuSelectionManager for the current menu hierarchy
500    *
501    */
502   public void processKeyEvent(KeyEvent e, MenuElement[] path,
503                               MenuSelectionManager manager)
504   {
505     // Do nothing - needed for implementation of MenuElement interface
506   }
507
508   /**
509    * This method overrides JComponent.processKeyBinding to allow the 
510    * JMenuBar to check all the child components (recursiveley) to see 
511    * if they'll consume the event.
512    * 
513    * @param ks the KeyStroke for the event
514    * @param e the KeyEvent for the event
515    * @param condition the focus condition for the binding
516    * @param pressed true if the key is pressed 
517    */
518   protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition,
519                                       boolean pressed)
520   {
521     // See if the regular JComponent behavior consumes the event
522     if (super.processKeyBinding(ks, e, condition, pressed))
523       return true;
524     
525     // If not, have to recursively check all the child menu elements to see 
526     // if they want it    
527     MenuElement[] children = getSubElements();
528     for (int i = 0; i < children.length; i++)
529       if (processKeyBindingHelper(children[i], ks, e, condition, pressed))
530         return true;
531     return false;
532   }
533   
534   /**
535    * This is a helper method to recursively check the children of this
536    * JMenuBar to see if they will consume a key event via key bindings.  
537    * This is used for menu accelerators.
538    * @param menuElement the menuElement to check (and check all its children)
539    * @param ks the KeyStroke for the event
540    * @param e the KeyEvent that may be consumed
541    * @param condition the focus condition for the binding
542    * @param pressed true if the key was pressed
543    * @return true <code>menuElement</code> or one of its children consume
544    * the event (processKeyBinding returns true for menuElement or one of
545    * its children).
546    */
547   static boolean processKeyBindingHelper(MenuElement menuElement, KeyStroke ks,
548                                          KeyEvent e, int condition,
549                                          boolean pressed)
550   {
551     if (menuElement == null)
552       return false;
553
554     // First check the menuElement itself, if it's a JComponent
555     if (menuElement instanceof JComponent
556         && ((JComponent) menuElement).processKeyBinding(ks, e, condition,
557                                                         pressed))
558       return true;
559     
560     // If that didn't consume it, check all the children recursively
561     MenuElement[] children = menuElement.getSubElements();
562     for (int i = 0; i < children.length; i++)
563       if (processKeyBindingHelper(children[i], ks, e, condition, pressed))
564         return true;
565     return false;
566   }
567   
568   /**
569    * Process mouse events forwarded from MenuSelectionManager. This method
570    * doesn't do anything. It is here to conform to the MenuElement interface.
571    *
572    * @param event event forwarded from MenuSelectionManager
573    * @param path path to the menu element from which event was generated
574    * @param manager MenuSelectionManager for the current menu hierarchy
575    *
576    */
577   public void processMouseEvent(MouseEvent event, MenuElement[] path,
578                                 MenuSelectionManager manager)
579   {
580     // Do nothing - needed for implementation of MenuElement interface
581   }
582
583   /**
584    * This method overrides removeNotify() in the Container to
585    * unregister this menu bar from the current keyboard manager.
586    */
587   public void removeNotify()
588   {
589     KeyboardManager.getManager().unregisterJMenuBar(this);
590     super.removeNotify();
591   }
592
593   /**
594    * Sets painting status of the border. If 'b' is true then menu bar's
595    * border will be painted, and it will not be painted otherwise.
596    *
597    * @param b indicates if menu bar's border should be painted.
598    */
599   public void setBorderPainted(boolean b)
600   {
601     if (b != borderPainted)
602       {
603         boolean old = borderPainted;
604         borderPainted = b;
605         firePropertyChange("borderPainted", old, b);
606         revalidate();
607         repaint();
608       }
609   }
610
611   /**
612    * Sets help menu for this menu bar
613    *
614    * @param menu help menu
615    *
616    * @specnote The specification states that this method is not yet implemented
617    *           and should throw an exception.
618    */
619   public void setHelpMenu(JMenu menu)
620   {
621     // We throw an Error here, just as Sun's JDK does.
622     throw new Error("setHelpMenu() not yet implemented.");
623   }
624
625   /**
626    * Sets the margin between the menu bar's border and its menus (this is a
627    * bound property with the name 'margin').
628    *
629    * @param m  the margin (<code>null</code> permitted).
630    * 
631    * @see #getMargin()
632    */
633   public void setMargin(Insets m)
634   {
635     if (m != margin)
636       {
637         Insets oldMargin = margin;
638         margin = m;
639         firePropertyChange("margin", oldMargin, margin);
640       }
641   }
642
643   /**
644    * Changes menu bar's selection to the specified menu.
645    * This method updates selected index of menu bar's selection model,
646    * which results in a model firing change event.
647    *
648    * @param sel menu to select
649    */
650   public void setSelected(Component sel)
651   {
652     int index = getComponentIndex(sel);
653     selectionModel.setSelectedIndex(index);
654   }
655
656   /**
657    * Sets menuBar's selection model to the one specified
658    *
659    * @param model SingleSelectionModel that needs to be set for this menu bar
660    */
661   public void setSelectionModel(SingleSelectionModel model)
662   {
663     if (selectionModel != model)
664       {
665         SingleSelectionModel oldModel = selectionModel;
666         selectionModel = model;
667         firePropertyChange("model", oldModel, selectionModel);
668       }
669   }
670
671   /**
672    * Set the "UI" property of the menu bar, which is a look and feel class
673    * responsible for handling menuBar's input events and painting it.
674    *
675    * @param ui The new "UI" property
676    */
677   public void setUI(MenuBarUI ui)
678   {
679     super.setUI(ui);
680   }
681
682   /**
683    * Set the "UI" property to a class constructed, via the {@link
684    * UIManager}, from the current look and feel.
685    */
686   public void updateUI()
687   {
688     setUI((MenuBarUI) UIManager.getUI(this));
689   }
690 }