1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.awt;
19
20import java.awt.event.FocusListener;
21import java.awt.event.MouseEvent;
22import java.awt.peer.MenuComponentPeer;
23import java.io.Serializable;
24import java.util.Locale; //import javax.accessibility.Accessible;
25//import javax.accessibility.AccessibleComponent;
26//import javax.accessibility.AccessibleContext;
27//import javax.accessibility.AccessibleRole;
28//import javax.accessibility.AccessibleSelection;
29//import javax.accessibility.AccessibleStateSet;
30import org.apache.harmony.awt.gl.MultiRectArea;
31import org.apache.harmony.awt.state.MenuItemState;
32import org.apache.harmony.awt.state.MenuState;
33import org.apache.harmony.luni.util.NotImplementedException;
34
35/**
36 * The MenuComponent abstract class is the superclass for menu components. Menu
37 * components receive and process AWT events.
38 *
39 * @since Android 1.0
40 */
41public abstract class MenuComponent implements Serializable {
42
43    /**
44     * The Constant serialVersionUID.
45     */
46    private static final long serialVersionUID = -4536902356223894379L;
47
48    /**
49     * The name.
50     */
51    private String name;
52
53    /**
54     * The font.
55     */
56    private Font font;
57
58    /**
59     * The parent.
60     */
61    MenuContainer parent;
62
63    /**
64     * The deprecated event handler.
65     */
66    boolean deprecatedEventHandler = true;
67
68    /**
69     * The selected item index.
70     */
71    private int selectedItemIndex;
72
73    // ???AWT: private AccessibleContext accessibleContext;
74
75    /**
76     * The toolkit.
77     */
78    final Toolkit toolkit = Toolkit.getDefaultToolkit();
79
80    // ???AWT
81    /*
82     * protected abstract class AccessibleAWTMenuComponent extends
83     * AccessibleContext implements Serializable, AccessibleComponent,
84     * AccessibleSelection { private static final long serialVersionUID =
85     * -4269533416223798698L; public void addFocusListener(FocusListener
86     * listener) { } public boolean contains(Point pt) { return false; } public
87     * Accessible getAccessibleAt(Point pt) { return null; } public Color
88     * getBackground() { return null; } public Rectangle getBounds() { return
89     * null; } public Cursor getCursor() { return null; } public Font getFont()
90     * { return MenuComponent.this.getFont(); } public FontMetrics
91     * getFontMetrics(Font font) { return null; } public Color getForeground() {
92     * return null; } public Point getLocation() { return null; } public Point
93     * getLocationOnScreen() { return null; } public Dimension getSize() {
94     * return null; } public boolean isEnabled() { return true; // always
95     * enabled } public boolean isFocusTraversable() { return true; // always
96     * focus traversable } public boolean isShowing() { return true;// always
97     * showing } public boolean isVisible() { return true; // always visible }
98     * public void removeFocusListener(FocusListener listener) { } public void
99     * requestFocus() { } public void setBackground(Color color) { } public void
100     * setBounds(Rectangle rect) { } public void setCursor(Cursor cursor) { }
101     * public void setEnabled(boolean enabled) { } public void setFont(Font
102     * font) { MenuComponent.this.setFont(font); } public void
103     * setForeground(Color color) { } public void setLocation(Point pt) { }
104     * public void setSize(Dimension pt) { } public void setVisible(boolean
105     * visible) { } public void addAccessibleSelection(int index) { } public
106     * void clearAccessibleSelection() { } public Accessible
107     * getAccessibleSelection(int index) { return null; } public int
108     * getAccessibleSelectionCount() { return 0; } public boolean
109     * isAccessibleChildSelected(int index) { return false; } public void
110     * removeAccessibleSelection(int index) { } public void
111     * selectAllAccessibleSelection() { }
112     * @Override public Accessible getAccessibleChild(int index) { return null;
113     * }
114     * @Override public int getAccessibleChildrenCount() { return 0; }
115     * @Override public AccessibleComponent getAccessibleComponent() { return
116     * this; }
117     * @Override public String getAccessibleDescription() { return
118     * super.getAccessibleDescription(); }
119     * @Override public int getAccessibleIndexInParent() { toolkit.lockAWT();
120     * try { Accessible aParent = getAccessibleParent(); int aIndex = -1; if
121     * (aParent instanceof MenuComponent) { MenuComponent parent =
122     * (MenuComponent) aParent; int count = parent.getItemCount(); for (int i =
123     * 0; i < count; i++) { MenuComponent comp = parent.getItem(i); if (comp
124     * instanceof Accessible) { aIndex++; if (comp == MenuComponent.this) {
125     * return aIndex; } } } } return -1; } finally { toolkit.unlockAWT(); } }
126     * @Override public String getAccessibleName() { return
127     * super.getAccessibleName(); }
128     * @Override public Accessible getAccessibleParent() { toolkit.lockAWT();
129     * try { Accessible aParent = super.getAccessibleParent(); if (aParent !=
130     * null) { return aParent; } MenuContainer parent = getParent(); if (parent
131     * instanceof Accessible) { aParent = (Accessible) parent; } return aParent;
132     * } finally { toolkit.unlockAWT(); } }
133     * @Override public AccessibleRole getAccessibleRole() { return
134     * AccessibleRole.AWT_COMPONENT; }
135     * @Override public AccessibleSelection getAccessibleSelection() { return
136     * this; }
137     * @Override public AccessibleStateSet getAccessibleStateSet() { return new
138     * AccessibleStateSet(); }
139     * @Override public Locale getLocale() { return Locale.getDefault(); } }
140     */
141
142    /**
143     * The accessor to MenuComponent internal state, utilized by the visual
144     * theme.
145     *
146     * @throws HeadlessException
147     *             the headless exception.
148     */
149    // ???AWT
150    /*
151     * class State implements MenuState { Dimension size; Dimension getSize() {
152     * if (size == null) { calculate(); } return size; } public int getWidth() {
153     * return getSize().width; } public int getHeight() { return
154     * getSize().height; } public Font getFont() { return
155     * MenuComponent.this.getFont(); } public int getItemCount() { return
156     * MenuComponent.this.getItemCount(); } public int getSelectedItemIndex() {
157     * return MenuComponent.this.getSelectedItemIndex(); } public boolean
158     * isFontSet() { return MenuComponent.this.isFontSet(); }
159     * @SuppressWarnings("deprecation") public FontMetrics getFontMetrics(Font
160     * f) { return MenuComponent.this.toolkit.getFontMetrics(f); } public Point
161     * getLocation() { return MenuComponent.this.getLocation(); } public
162     * MenuItemState getItem(int index) { MenuItem item =
163     * MenuComponent.this.getItem(index); return item.itemState; } public void
164     * setSize(int w, int h) { this.size = new Dimension(w, h); } void
165     * calculate() { size = new Dimension();
166     * size.setSize(toolkit.theme.calculateMenuSize(this)); } void reset() { for
167     * (int i = 0; i < getItemCount(); i++) { ((MenuItem.State)
168     * getItem(i)).reset(); } } }
169     */
170
171    /**
172     * Pop-up box for menu. It transfers the paint events, keyboard and mouse
173     * events to the menu component itself.
174     */
175    // ???AWT
176    /*
177     * class MenuPopupBox extends PopupBox { private final Point lastMousePos =
178     * new Point();
179     * @Override boolean isMenu() { return true; }
180     * @Override void paint(Graphics gr) { MenuComponent.this.paint(gr); }
181     * @Override void onKeyEvent(int eventId, int vKey, long when, int
182     * modifiers) { MenuComponent.this.onKeyEvent(eventId, vKey, when,
183     * modifiers); }
184     * @Override void onMouseEvent(int eventId, Point where, int mouseButton,
185     * long when, int modifiers, int wheelRotation) { // prevent conflict of
186     * mouse and keyboard // when sub-menu drops down due to keyboard navigation
187     * if (lastMousePos.equals(where) && (eventId == MouseEvent.MOUSE_MOVED ||
188     * eventId == MouseEvent.MOUSE_ENTERED)) { return; }
189     * lastMousePos.setLocation(where); MenuComponent.this.onMouseEvent(eventId,
190     * where, mouseButton, when, modifiers); } }
191     */
192
193    /**
194     * Instantiates a new MenuComponent object.
195     *
196     * @throws HeadlessException
197     *             if the graphical interface environment can't support
198     *             MenuComponents.
199     */
200    public MenuComponent() throws HeadlessException {
201        toolkit.lockAWT();
202        try {
203            Toolkit.checkHeadless();
204            name = autoName();
205            selectedItemIndex = -1;
206        } finally {
207            toolkit.unlockAWT();
208        }
209    }
210
211    /**
212     * Gets the name of the MenuComponent object.
213     *
214     * @return the name of the MenuComponent object.
215     */
216    public String getName() {
217        toolkit.lockAWT();
218        try {
219            return name;
220        } finally {
221            toolkit.unlockAWT();
222        }
223    }
224
225    /**
226     * Returns a String representation of the MenuComponent object.
227     *
228     * @return a String representation of the MenuComponent object.
229     */
230    @Override
231    public String toString() {
232        toolkit.lockAWT();
233        try {
234            return getClass().getName() + "[" + paramString() + "]"; //$NON-NLS-1$ //$NON-NLS-2$
235        } finally {
236            toolkit.unlockAWT();
237        }
238    }
239
240    /**
241     * Gets the parent menu container.
242     *
243     * @return the parent.
244     */
245    public MenuContainer getParent() {
246        toolkit.lockAWT();
247        try {
248            return parent;
249        } finally {
250            toolkit.unlockAWT();
251        }
252    }
253
254    /**
255     * Sets the name of the MenuComponent to the specified string.
256     *
257     * @param name
258     *            the new name of the MenuComponent object.
259     */
260    public void setName(String name) {
261        toolkit.lockAWT();
262        try {
263            this.name = name;
264        } finally {
265            toolkit.unlockAWT();
266        }
267    }
268
269    /**
270     * Dispatches AWT event.
271     *
272     * @param event
273     *            the AWTEvent.
274     */
275    public final void dispatchEvent(AWTEvent event) {
276        toolkit.lockAWT();
277        try {
278            processEvent(event);
279            if (deprecatedEventHandler) {
280                postDeprecatedEvent(event);
281            }
282        } finally {
283            toolkit.unlockAWT();
284        }
285    }
286
287    /**
288     * Post deprecated event.
289     *
290     * @param event
291     *            the event.
292     */
293    void postDeprecatedEvent(AWTEvent event) {
294        Event evt = event.getEvent();
295        if (evt != null) {
296            postEvent(evt);
297        }
298    }
299
300    /**
301     * Gets the peer of the MenuComponent; an application must not use this
302     * method directly.
303     *
304     * @return the MenuComponentPeer object.
305     * @throws NotImplementedException
306     *             if this method is not implemented by a subclass.
307     * @deprecated an application must not use this method directly.
308     */
309    @Deprecated
310    public MenuComponentPeer getPeer() throws org.apache.harmony.luni.util.NotImplementedException {
311        toolkit.lockAWT();
312        try {
313        } finally {
314            toolkit.unlockAWT();
315        }
316        if (true) {
317            throw new RuntimeException("Method is not implemented"); //TODO: implement //$NON-NLS-1$
318        }
319        return null;
320    }
321
322    /**
323     * Gets the locking object of this MenuComponent.
324     *
325     * @return the locking object of this MenuComponent.
326     */
327    protected final Object getTreeLock() {
328        return toolkit.awtTreeLock;
329    }
330
331    /**
332     * Posts the Event to the MenuComponent.
333     *
334     * @param e
335     *            the Event.
336     * @return true, if the event is posted successfully, false otherwise.
337     * @deprecated Replaced dispatchEvent method.
338     */
339    @SuppressWarnings("deprecation")
340    @Deprecated
341    public boolean postEvent(Event e) {
342        toolkit.lockAWT();
343        try {
344            if (parent != null) {
345                return parent.postEvent(e);
346            }
347            return false;
348        } finally {
349            toolkit.unlockAWT();
350        }
351    }
352
353    /**
354     * Returns the string representation of the MenuComponent state.
355     *
356     * @return returns the string representation of the MenuComponent state.
357     */
358    protected String paramString() {
359        toolkit.lockAWT();
360        try {
361            return getName();
362        } finally {
363            toolkit.unlockAWT();
364        }
365    }
366
367    // ???AWT
368    /*
369     * public AccessibleContext getAccessibleContext() { toolkit.lockAWT(); try
370     * { if (accessibleContext == null) { accessibleContext =
371     * createAccessibleContext(); } return accessibleContext; } finally {
372     * toolkit.unlockAWT(); } }
373     */
374
375    /**
376     * Gets the font of the MenuComponent object.
377     *
378     * @return the Font of the MenuComponent object.
379     */
380    public Font getFont() {
381        toolkit.lockAWT();
382        try {
383            if (font == null && hasDefaultFont()) {
384                return toolkit.getDefaultFont();
385            }
386            if (font == null && parent != null) {
387                return parent.getFont();
388            }
389            return font;
390        } finally {
391            toolkit.unlockAWT();
392        }
393    }
394
395    /**
396     * Checks if is font set.
397     *
398     * @return true, if is font set
399     */
400    boolean isFontSet() {
401        return font != null
402                || ((parent instanceof MenuComponent) && ((MenuComponent)parent).isFontSet());
403    }
404
405    /**
406     * Checks for default font.
407     *
408     * @return true, if successful.
409     */
410    boolean hasDefaultFont() {
411        return false;
412    }
413
414    /**
415     * Processes an AWTEevent on this menu component.
416     *
417     * @param event
418     *            the AWTEvent.
419     */
420    protected void processEvent(AWTEvent event) {
421        toolkit.lockAWT();
422        try {
423            // do nothing
424        } finally {
425            toolkit.unlockAWT();
426        }
427    }
428
429    /**
430     * Removes the peer of the MenuComponent.
431     */
432    public void removeNotify() {
433        toolkit.lockAWT();
434        try {
435        } finally {
436            toolkit.unlockAWT();
437        }
438    }
439
440    /**
441     * Sets the Font for this MenuComponent object.
442     *
443     * @param font
444     *            the new Font to be used for this MenuComponent.
445     */
446    public void setFont(Font font) {
447        toolkit.lockAWT();
448        try {
449            this.font = font;
450        } finally {
451            toolkit.unlockAWT();
452        }
453    }
454
455    /**
456     * Sets the parent.
457     *
458     * @param parent
459     *            the new parent.
460     */
461    void setParent(MenuContainer parent) {
462        this.parent = parent;
463    }
464
465    /**
466     * Gets the location.
467     *
468     * @return the location.
469     */
470    Point getLocation() {
471        // to be overridden
472        return new Point(0, 0);
473    }
474
475    /**
476     * Gets the width.
477     *
478     * @return the width.
479     */
480    int getWidth() {
481        // to be overridden
482        return 1;
483    }
484
485    /**
486     * Gets the height.
487     *
488     * @return the height.
489     */
490    int getHeight() {
491        // to be overridden
492        return 1;
493    }
494
495    /**
496     * Recursively find the menu item for a menu shortcut.
497     *
498     * @param gr
499     *            the gr.
500     * @return the menu item; or null if the item is not available for this
501     *         shortcut.
502     */
503    // ???AWT
504    /*
505     * MenuItem getShortcutMenuItemImpl(MenuShortcut ms) { if (ms == null) {
506     * return null; } for (int i = 0; i < getItemCount(); i++) { MenuItem mi =
507     * getItem(i); if (mi instanceof Menu) { mi = ((Menu)
508     * mi).getShortcutMenuItemImpl(ms); if (mi != null) { return mi; } } else if
509     * (ms.equals(mi.getShortcut())) { return mi; } } return null; }
510     */
511
512    void paint(Graphics gr) {
513        gr.setColor(Color.LIGHT_GRAY);
514        gr.fillRect(0, 0, getWidth(), getHeight());
515        gr.setColor(Color.BLACK);
516    }
517
518    /**
519     * Mouse events handler.
520     *
521     * @param eventId
522     *            one of the MouseEvent.MOUSE_* constants.
523     * @param where
524     *            mouse location.
525     * @param mouseButton
526     *            mouse button that was pressed or released.
527     * @param when
528     *            event time.
529     * @param modifiers
530     *            input event modifiers.
531     */
532    void onMouseEvent(int eventId, Point where, int mouseButton, long when, int modifiers) {
533        // to be overridden
534    }
535
536    /**
537     * Keyboard event handler.
538     *
539     * @param eventId
540     *            one of the KeyEvent.KEY_* constants.
541     * @param vKey
542     *            the key code.
543     * @param when
544     *            event time.
545     * @param modifiers
546     *            input event modifiers.
547     */
548    void onKeyEvent(int eventId, int vKey, long when, int modifiers) {
549        // to be overridden
550    }
551
552    /**
553     * Post the ActionEvent or ItemEvent, depending on type of the menu item.
554     *
555     * @param index
556     *            the index.
557     * @return the item rect.
558     */
559    // ???AWT
560    /*
561     * void fireItemAction(int item, long when, int modifiers) { MenuItem mi =
562     * getItem(item); mi.itemSelected(when, modifiers); } MenuItem getItem(int
563     * index) { // to be overridden return null; } int getItemCount() { return
564     * 0; }
565     */
566
567    /**
568     * @return The sub-menu of currently selecetd item, or null if such a
569     *         sub-menu is not available.
570     */
571    // ???AWT
572    /*
573     * Menu getSelectedSubmenu() { if (selectedItemIndex < 0) { return null; }
574     * MenuItem item = getItem(selectedItemIndex); return (item instanceof Menu)
575     * ? (Menu) item : null; }
576     */
577
578    /**
579     * Convenience method for selectItem(index, true).
580     */
581    // ???AWT
582    /*
583     * void selectItem(int index) { selectItem(index, true); }
584     */
585
586    /**
587     * Change the selection in the menu.
588     *
589     * @param index
590     *            new selecetd item's index.
591     * @param showSubMenu
592     *            if new selected item has a sub-menu, should that sub-menu be
593     *            displayed.
594     */
595    // ???AWT
596    /*
597     * void selectItem(int index, boolean showSubMenu) { if (selectedItemIndex
598     * == index) { return; } if (selectedItemIndex >= 0 &&
599     * getItem(selectedItemIndex) instanceof Menu) { ((Menu)
600     * getItem(selectedItemIndex)).hide(); } MultiRectArea clip =
601     * getUpdateClip(index, selectedItemIndex); selectedItemIndex = index;
602     * Graphics gr = getGraphics(clip); if (gr != null) { paint(gr); } if
603     * (showSubMenu) { showSubMenu(selectedItemIndex); } }
604     */
605
606    /**
607     * Change the selected item to the next one in the requested direction
608     * moving cyclically, skipping separators
609     *
610     * @param forward
611     *            the direction to move the selection.
612     * @param showSubMenu
613     *            if new selected item has a sub-menu, should that sub-menu be
614     *            displayed.
615     */
616    // ???AWT
617    /*
618     * void selectNextItem(boolean forward, boolean showSubMenu) { int selected
619     * = getSelectedItemIndex(); int count = getItemCount(); if (count == 0) {
620     * return; } if (selected < 0) { selected = (forward ? count - 1 : 0); } int
621     * i = selected; do { i = (forward ? (i + 1) : (i + count - 1)) % count; i
622     * %= count; MenuItem item = getItem(i); if (!"-".equals(item.getLabel())) {
623     * //$NON-NLS-1$ selectItem(i, showSubMenu); return; } } while (i !=
624     * selected); } void showSubMenu(int index) { if ((index < 0) ||
625     * !isActive()) { return; } MenuItem item = getItem(index); if (item
626     * instanceof Menu) { Menu menu = ((Menu) getItem(index)); if
627     * (menu.getItemCount() == 0) { return; } Point location =
628     * getSubmenuLocation(index); menu.show(location.x, location.y, false); } }
629     */
630
631    /**
632     * @return the menu bar which is the root of current menu's hierarchy; or
633     *         null if the hierarchy root is not a menu bar.
634     */
635    // ???AWT
636    /*
637     * MenuBar getMenuBar() { if (parent instanceof MenuBar) { return (MenuBar)
638     * parent; } if (parent instanceof MenuComponent) { return ((MenuComponent)
639     * parent).getMenuBar(); } return null; } PopupBox getPopupBox() { return
640     * null; }
641     */
642
643    Rectangle getItemRect(int index) {
644        // to be overridden
645        return null;
646    }
647
648    /**
649     * Determine the clip region when menu selection is changed from index1 to
650     * index2.
651     *
652     * @param index1
653     *            old selected item.
654     * @param index2
655     *            new selected item.
656     * @return the region to repaint.
657     */
658    final MultiRectArea getUpdateClip(int index1, int index2) {
659        MultiRectArea clip = new MultiRectArea();
660        if (index1 >= 0) {
661            clip.add(getItemRect(index1));
662        }
663        if (index2 >= 0) {
664            clip.add(getItemRect(index2));
665        }
666        return clip;
667    }
668
669    /**
670     * Gets the submenu location.
671     *
672     * @param index
673     *            the index.
674     * @return the submenu location.
675     */
676    Point getSubmenuLocation(int index) {
677        // to be overridden
678        return new Point(0, 0);
679    }
680
681    /**
682     * Gets the selected item index.
683     *
684     * @return the selected item index.
685     */
686    int getSelectedItemIndex() {
687        return selectedItemIndex;
688    }
689
690    /**
691     * Hide.
692     */
693    void hide() {
694        selectedItemIndex = -1;
695        if (parent instanceof MenuComponent) {
696            ((MenuComponent)parent).itemHidden(this);
697        }
698    }
699
700    /**
701     * Item hidden.
702     *
703     * @param mc
704     *            the mc.
705     */
706    void itemHidden(MenuComponent mc) {
707        // to be overridden
708    }
709
710    /**
711     * Checks if is visible.
712     *
713     * @return true, if is visible.
714     */
715    boolean isVisible() {
716        return true;
717    }
718
719    /**
720     * Checks if is active.
721     *
722     * @return true, if is active.
723     */
724    boolean isActive() {
725        return true;
726    }
727
728    /**
729     * Hide all menu hierarchy.
730     */
731    void endMenu() {
732        // ???AWT: toolkit.dispatcher.popupDispatcher.deactivateAll();
733    }
734
735    /**
736     * Handle the mouse click or Enter key event on a menu's item.
737     *
738     * @param when
739     *            the event time.
740     * @param modifiers
741     *            input event modifiers.
742     */
743    void itemSelected(long when, int modifiers) {
744        endMenu();
745    }
746
747    /**
748     * Auto name.
749     *
750     * @return the string.
751     */
752    String autoName() {
753        String name = getClass().getName();
754        if (name.indexOf("$") != -1) { //$NON-NLS-1$
755            return null;
756        }
757        // ???AWT: int number = toolkit.autoNumber.nextMenuComponent++;
758        int number = 0;
759        name = name.substring(name.lastIndexOf(".") + 1) + Integer.toString(number); //$NON-NLS-1$
760        return name;
761    }
762
763    /**
764     * Creates the Graphics object for the pop-up box of this menu component.
765     *
766     * @param clip
767     *            the clip to set on this Graphics.
768     * @return the created Graphics object, or null if such object is not
769     *         available.
770     */
771    Graphics getGraphics(MultiRectArea clip) {
772        // to be overridden
773        return null;
774    }
775
776    /**
777     * @return accessible context specific for particular menu component.
778     */
779    // ???AWT
780    /*
781     * AccessibleContext createAccessibleContext() { return null; }
782     */
783}
784