001/* Menu.java -- A Java AWT Menu
002   Copyright (C) 1999, 2002, 2004, 2006 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 java.awt;
040
041import java.awt.peer.MenuPeer;
042import java.io.Serializable;
043import java.util.Enumeration;
044import java.util.Vector;
045
046import javax.accessibility.AccessibleContext;
047import javax.accessibility.AccessibleRole;
048
049/**
050  * This class represents a pull down or tear off menu in Java's AWT.
051  *
052  * @author Aaron M. Renn (arenn@urbanophile.com)
053  */
054public class Menu extends MenuItem implements MenuContainer, Serializable
055{
056
057  /**
058   * The number used to generate the name returned by getName.
059   */
060  private static transient long next_menu_number;
061
062  // Serialization Constant
063  private static final long serialVersionUID = -8809584163345499784L;
064
065  /**
066    * @serial The actual items in the menu
067    */
068  private Vector items = new Vector();
069
070  /**
071   * @serial Flag indicating whether or not this menu is a tear off
072   */
073  private boolean tearOff;
074
075  /**
076   * @serial Indicates whether or not this is a help menu.
077   */
078  private boolean isHelpMenu;
079
080  /*
081   * @serial Unused in this implementation, but present in Sun's
082   * serialization spec.  Value obtained via reflection.
083   */
084  private int menuSerializedDataVersion = 1;
085
086  static final transient String separatorLabel = "-";
087
088  /**
089   * Initializes a new instance of <code>Menu</code> with no label and that
090   * is not a tearoff;
091   *
092   * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true.
093   */
094  public Menu()
095  {
096  }
097
098  /**
099   * Initializes a new instance of <code>Menu</code> that is not a tearoff and
100   * that has the specified label.
101   *
102   * @param label The menu label.
103   *
104   * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true.
105   */
106  public Menu(String label)
107  {
108    this(label, false);
109  }
110
111  /**
112   * Initializes a new instance of <code>Menu</code> with the specified
113   * label and tearoff status.
114   *
115   * @param label The label for this menu
116   * @param isTearOff <code>true</code> if this menu is a tear off menu,
117   * <code>false</code> otherwise.
118   *
119   * @exception HeadlessException If GraphicsEnvironment.isHeadless() is true.
120   */
121  public Menu(String label, boolean isTearOff)
122  {
123    super(label);
124
125    tearOff = isTearOff;
126
127    if (label.equals("Help"))
128      isHelpMenu = true;
129
130    if (GraphicsEnvironment.isHeadless())
131      throw new HeadlessException();
132  }
133
134  /**
135   * Tests whether or not this menu is a tearoff.
136   *
137   * @return <code>true</code> if this menu is a tearoff, <code>false</code>
138   * otherwise.
139   */
140  public boolean isTearOff()
141  {
142    return(tearOff);
143  }
144
145  /**
146   * Returns the number of items in this menu.
147   *
148   * @return The number of items in this menu.
149   */
150  public int getItemCount()
151  {
152    return countItems();
153  }
154
155  /**
156   * Returns the number of items in this menu.
157   *
158   * @return The number of items in this menu.
159   *
160   * @deprecated As of JDK 1.1, replaced by getItemCount().
161   */
162  public int countItems()
163  {
164    return items.size();
165  }
166
167  /**
168   * Returns the item at the specified index.
169   *
170   * @param index  the item index.
171   *
172   * @return The item at the specified index.
173   *
174   * @exception ArrayIndexOutOfBoundsException If the index value is not valid.
175   */
176  public MenuItem getItem(int index)
177  {
178    return((MenuItem) items.elementAt(index));
179  }
180
181  /**
182   * Adds the specified item to this menu.  If it was previously part of
183   * another menu, it is first removed from that menu.
184   *
185   * @param item The new item to add.
186   *
187   * @return The item that was added.
188   */
189  public MenuItem add(MenuItem item)
190  {
191    MenuContainer parent = item.getParent();
192    if (parent != null)
193      parent.remove(item);
194
195    items.addElement(item);
196    item.setParent(this);
197
198    if (peer != null)
199      {
200        item.addNotify();
201        MenuPeer mp = (MenuPeer) peer;
202        mp.addItem(item);
203      }
204
205    return item;
206  }
207
208  /**
209   * Add an item with the specified label to this menu.
210   *
211   * @param label The label of the menu item to add.
212   */
213  public void add(String label)
214  {
215    add(new MenuItem(label));
216  }
217
218  /**
219   * Inserts the specified menu item into this menu at the specified index.  If
220   * the index is greater than or equal to the number of items already in the
221   * menu, the new item is added as the last item in the menu.
222   *
223   * @param item The menu item to add (<code>null</code> not permitted).
224   * @param index The index of the menu item (>= 0).
225   *
226   * @throws IllegalArgumentException if the index is less than zero.
227   * @throws NullPointerException if <code>item</code> is <code>null</code>.
228   */
229  public void insert(MenuItem item, int index)
230  {
231    if (index < 0)
232      throw new IllegalArgumentException("Index is less than zero");
233
234    int count = getItemCount();
235
236    if (index >= count)
237      add(item);
238    else
239      {
240        MenuContainer parent = item.getParent();
241        if (parent != null)
242          parent.remove(item);
243
244        items.insertElementAt(item, index);
245        item.setParent(this);
246
247        MenuPeer peer = (MenuPeer) getPeer();
248        if (peer == null)
249          return;
250
251        for (int i = count - 1; i >= index; i--)
252          peer.delItem(i);
253
254        item.addNotify();
255        peer.addItem(item);
256
257        // bear in mind that count is the number of items *before* the new
258        // item was added
259        for (int i = index + 1; i <= count; i++)
260          peer.addItem((MenuItem) items.elementAt(i));
261      }
262
263  }
264
265  /**
266   * Inserts an item with the specified label into this menu at the specified
267   * index.  If the index is greater than or equal to the number of items
268   * already in the menu, the new item is added as the last item in the menu.
269   *
270   * @param label The label of the item to add.
271   * @param index The index of the menu item (>= 0).
272   *
273   * @throws IllegalArgumentException If the index is less than zero.
274   */
275  public void insert(String label, int index)
276  {
277    insert(new MenuItem(label), index);
278  }
279
280  /**
281   * Adds a separator bar at the current menu location.
282   */
283  public void addSeparator()
284  {
285    add(new MenuItem(separatorLabel));
286  }
287
288  /**
289   * Inserts a separator bar at the specified index value.
290   *
291   * @param index The index at which to insert a separator bar.
292   *
293   * @exception IllegalArgumentException If the index is less than zero.
294   * @exception ArrayIndexOutOfBoundsException If the index is otherwise invalid.
295   */
296  public void insertSeparator(int index)
297  {
298    insert(new MenuItem(separatorLabel), index);
299  }
300
301  /**
302   * Deletes the item at the specified index from this menu.
303   *
304   * @param index The index of the item to remove.
305   *
306   * @exception ArrayIndexOutOfBoundsException If the index is otherwise invalid.
307   */
308  public synchronized void remove(int index)
309  {
310    MenuItem item = (MenuItem) items.remove(index);
311
312    MenuPeer mp = (MenuPeer) getPeer();
313    if (mp != null)
314      {
315        mp.delItem(index);
316        item.removeNotify();
317      }
318    item.setParent(null);
319  }
320
321  /**
322   * Removes the specifed item from the menu.  If the specified component
323   * does not exist, this method does nothing.
324   *
325   * @param item The component to remove.
326   */
327  public void remove(MenuComponent item)
328  {
329    int index = items.indexOf(item);
330    if (index == -1)
331      return;
332
333    remove(index);
334  }
335
336  /**
337   * Removes all the elements from this menu.
338   */
339  public synchronized void removeAll()
340  {
341    int count = getItemCount();
342    for(int i = 0; i < count; i++)
343      {
344        // We must always remove item 0.
345        remove(0);
346      }
347  }
348
349  /**
350   * Creates the native peer for this object.
351   */
352  public void addNotify()
353  {
354    MenuPeer peer = (MenuPeer) getPeer();
355    if (peer == null)
356      {
357        peer = getToolkit().createMenu(this);
358        setPeer(peer);
359      }
360
361    Enumeration e = items.elements();
362    while (e.hasMoreElements())
363    {
364      MenuItem mi = (MenuItem)e.nextElement();
365      mi.addNotify();
366      peer.addItem(mi);
367    }
368
369    super.addNotify();
370  }
371
372  /**
373   * Destroys the native peer for this object.
374   */
375  public void removeNotify()
376  {
377    Enumeration e = items.elements();
378    while (e.hasMoreElements())
379    {
380      MenuItem mi = (MenuItem) e.nextElement();
381      mi.removeNotify();
382    }
383    super.removeNotify();
384  }
385
386  /**
387   * Returns a debugging string for this menu.
388   *
389   * @return A debugging string for this menu.
390   */
391  public String paramString()
392  {
393    return (",tearOff=" + tearOff + ",isHelpMenu=" + isHelpMenu
394            + super.paramString());
395  }
396
397  /**
398   * Basic Accessibility class for Menu.  Details get provided in derived
399   * classes.
400   */
401  protected class AccessibleAWTMenu extends AccessibleAWTMenuItem
402  {
403    private static final long serialVersionUID = 5228160894980069094L;
404
405    protected AccessibleAWTMenu()
406    {
407    }
408
409    public AccessibleRole getAccessibleRole()
410    {
411      return AccessibleRole.MENU;
412    }
413  }
414
415  /**
416   * Gets the AccessibleContext associated with this <code>Menu</code>.
417   * The context is created, if necessary.
418   *
419   * @return the associated context
420   */
421  public AccessibleContext getAccessibleContext()
422  {
423    /* Create the context if this is the first request */
424    if (accessibleContext == null)
425      accessibleContext = new AccessibleAWTMenu();
426    return accessibleContext;
427  }
428
429  /**
430   * Generate a unique name for this <code>Menu</code>.
431   *
432   * @return A unique name for this <code>Menu</code>.
433   */
434  String generateName()
435  {
436    return "menu" + getUniqueLong();
437  }
438
439  private static synchronized long getUniqueLong()
440  {
441    return next_menu_number++;
442  }
443
444} // class Menu