001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package java.awt;
019:
020: import java.awt.event.FocusListener;
021: import java.awt.event.MouseEvent;
022: import java.awt.peer.MenuComponentPeer;
023: import java.io.Serializable;
024: import java.util.Locale;
025: import javax.accessibility.Accessible;
026: import javax.accessibility.AccessibleComponent;
027: import javax.accessibility.AccessibleContext;
028: import javax.accessibility.AccessibleRole;
029: import javax.accessibility.AccessibleSelection;
030: import javax.accessibility.AccessibleStateSet;
031: import org.apache.harmony.awt.gl.MultiRectArea;
032: import org.apache.harmony.awt.state.MenuItemState;
033: import org.apache.harmony.awt.state.MenuState;
034:
035: public abstract class MenuComponent implements Serializable {
036: private static final long serialVersionUID = -4536902356223894379L;
037:
038: private String name;
039:
040: private Font font;
041:
042: MenuContainer parent;
043:
044: boolean deprecatedEventHandler = true;
045:
046: private int selectedItemIndex;
047:
048: private AccessibleContext accessibleContext;
049:
050: final Toolkit toolkit = Toolkit.getDefaultToolkit();
051:
052: protected abstract class AccessibleAWTMenuComponent extends
053: AccessibleContext implements Serializable,
054: AccessibleComponent, AccessibleSelection {
055: private static final long serialVersionUID = -4269533416223798698L;
056:
057: public void addFocusListener(FocusListener listener) {
058: }
059:
060: public boolean contains(Point pt) {
061: return false;
062: }
063:
064: public Accessible getAccessibleAt(Point pt) {
065: return null;
066: }
067:
068: public Color getBackground() {
069: return null;
070: }
071:
072: public Rectangle getBounds() {
073: return null;
074: }
075:
076: public Cursor getCursor() {
077: return null;
078: }
079:
080: public Font getFont() {
081: return MenuComponent.this .getFont();
082: }
083:
084: public FontMetrics getFontMetrics(Font font) {
085: return null;
086: }
087:
088: public Color getForeground() {
089: return null;
090: }
091:
092: public Point getLocation() {
093: return null;
094: }
095:
096: public Point getLocationOnScreen() {
097: return null;
098: }
099:
100: public Dimension getSize() {
101: return null;
102: }
103:
104: public boolean isEnabled() {
105: return true; // always enabled
106: }
107:
108: public boolean isFocusTraversable() {
109: return true; // always focus traversable
110: }
111:
112: public boolean isShowing() {
113: return true;// always showing
114: }
115:
116: public boolean isVisible() {
117: return true; // always visible
118: }
119:
120: public void removeFocusListener(FocusListener listener) {
121: }
122:
123: public void requestFocus() {
124: }
125:
126: public void setBackground(Color color) {
127: }
128:
129: public void setBounds(Rectangle rect) {
130: }
131:
132: public void setCursor(Cursor cursor) {
133: }
134:
135: public void setEnabled(boolean enabled) {
136: }
137:
138: public void setFont(Font font) {
139: MenuComponent.this .setFont(font);
140: }
141:
142: public void setForeground(Color color) {
143: }
144:
145: public void setLocation(Point pt) {
146: }
147:
148: public void setSize(Dimension pt) {
149: }
150:
151: public void setVisible(boolean visible) {
152: }
153:
154: public void addAccessibleSelection(int index) {
155: }
156:
157: public void clearAccessibleSelection() {
158: }
159:
160: public Accessible getAccessibleSelection(int index) {
161: return null;
162: }
163:
164: public int getAccessibleSelectionCount() {
165: return 0;
166: }
167:
168: public boolean isAccessibleChildSelected(int index) {
169: return false;
170: }
171:
172: public void removeAccessibleSelection(int index) {
173: }
174:
175: public void selectAllAccessibleSelection() {
176: }
177:
178: @Override
179: public Accessible getAccessibleChild(int index) {
180: return null;
181: }
182:
183: @Override
184: public int getAccessibleChildrenCount() {
185: return 0;
186: }
187:
188: @Override
189: public AccessibleComponent getAccessibleComponent() {
190: return this ;
191: }
192:
193: @Override
194: public String getAccessibleDescription() {
195: return super .getAccessibleDescription();
196: }
197:
198: @Override
199: public int getAccessibleIndexInParent() {
200: toolkit.lockAWT();
201: try {
202: Accessible aParent = getAccessibleParent();
203: int aIndex = -1;
204: if (aParent instanceof MenuComponent) {
205: MenuComponent parent = (MenuComponent) aParent;
206: int count = parent.getItemCount();
207: for (int i = 0; i < count; i++) {
208: MenuComponent comp = parent.getItem(i);
209: if (comp instanceof Accessible) {
210: aIndex++;
211: if (comp == MenuComponent.this ) {
212: return aIndex;
213: }
214: }
215: }
216: }
217: return -1;
218: } finally {
219: toolkit.unlockAWT();
220: }
221: }
222:
223: @Override
224: public String getAccessibleName() {
225: return super .getAccessibleName();
226: }
227:
228: @Override
229: public Accessible getAccessibleParent() {
230: toolkit.lockAWT();
231: try {
232: Accessible aParent = super .getAccessibleParent();
233: if (aParent != null) {
234: return aParent;
235: }
236: MenuContainer parent = getParent();
237: if (parent instanceof Accessible) {
238: aParent = (Accessible) parent;
239: }
240: return aParent;
241: } finally {
242: toolkit.unlockAWT();
243: }
244: }
245:
246: @Override
247: public AccessibleRole getAccessibleRole() {
248: return AccessibleRole.AWT_COMPONENT;
249: }
250:
251: @Override
252: public AccessibleSelection getAccessibleSelection() {
253: return this ;
254: }
255:
256: @Override
257: public AccessibleStateSet getAccessibleStateSet() {
258: return new AccessibleStateSet();
259: }
260:
261: @Override
262: public Locale getLocale() {
263: return Locale.getDefault();
264: }
265: }
266:
267: /**
268: * The accessor to MenuComponent internal state,
269: * utilized by the visual theme
270: */
271: class State implements MenuState {
272: Dimension size;
273:
274: Dimension getSize() {
275: if (size == null) {
276: calculate();
277: }
278: return size;
279: }
280:
281: public int getWidth() {
282: return getSize().width;
283: }
284:
285: public int getHeight() {
286: return getSize().height;
287: }
288:
289: public Font getFont() {
290: return MenuComponent.this .getFont();
291: }
292:
293: public int getItemCount() {
294: return MenuComponent.this .getItemCount();
295: }
296:
297: public int getSelectedItemIndex() {
298: return MenuComponent.this .getSelectedItemIndex();
299: }
300:
301: public boolean isFontSet() {
302: return MenuComponent.this .isFontSet();
303: }
304:
305: @SuppressWarnings("deprecation")
306: public FontMetrics getFontMetrics(Font f) {
307: return MenuComponent.this .toolkit.getFontMetrics(f);
308: }
309:
310: public Point getLocation() {
311: return MenuComponent.this .getLocation();
312: }
313:
314: public MenuItemState getItem(int index) {
315: MenuItem item = MenuComponent.this .getItem(index);
316: return item.itemState;
317: }
318:
319: public void setSize(int w, int h) {
320: this .size = new Dimension(w, h);
321: }
322:
323: void calculate() {
324: size = new Dimension();
325: size.setSize(toolkit.theme.calculateMenuSize(this ));
326: }
327:
328: void reset() {
329: for (int i = 0; i < getItemCount(); i++) {
330: ((MenuItem.State) getItem(i)).reset();
331: }
332: }
333: }
334:
335: /**
336: * Pop-up box for menu. It transfers the paint events,
337: * keyboard and mouse events to the menu component itself
338: */
339: class MenuPopupBox extends PopupBox {
340: private final Point lastMousePos = new Point();
341:
342: @Override
343: boolean isMenu() {
344: return true;
345: }
346:
347: @Override
348: void paint(Graphics gr) {
349: MenuComponent.this .paint(gr);
350: }
351:
352: @Override
353: void onKeyEvent(int eventId, int vKey, long when, int modifiers) {
354: MenuComponent.this .onKeyEvent(eventId, vKey, when,
355: modifiers);
356: }
357:
358: @Override
359: void onMouseEvent(int eventId, Point where, int mouseButton,
360: long when, int modifiers, int wheelRotation) {
361: // prevent conflict of mouse and keyboard
362: // when sub-menu drops down due to keyboard navigation
363: if (lastMousePos.equals(where)
364: && (eventId == MouseEvent.MOUSE_MOVED || eventId == MouseEvent.MOUSE_ENTERED)) {
365: return;
366: }
367: lastMousePos.setLocation(where);
368: MenuComponent.this .onMouseEvent(eventId, where,
369: mouseButton, when, modifiers);
370: }
371: }
372:
373: public MenuComponent() throws HeadlessException {
374: toolkit.lockAWT();
375: try {
376: Toolkit.checkHeadless();
377: name = autoName();
378: selectedItemIndex = -1;
379: } finally {
380: toolkit.unlockAWT();
381: }
382: }
383:
384: public String getName() {
385: toolkit.lockAWT();
386: try {
387: return name;
388: } finally {
389: toolkit.unlockAWT();
390: }
391: }
392:
393: @Override
394: public String toString() {
395: toolkit.lockAWT();
396: try {
397: return getClass().getName() + "[" + paramString() + "]"; //$NON-NLS-1$ //$NON-NLS-2$
398: } finally {
399: toolkit.unlockAWT();
400: }
401: }
402:
403: public MenuContainer getParent() {
404: toolkit.lockAWT();
405: try {
406: return parent;
407: } finally {
408: toolkit.unlockAWT();
409: }
410: }
411:
412: public void setName(String name) {
413: toolkit.lockAWT();
414: try {
415: this .name = name;
416: } finally {
417: toolkit.unlockAWT();
418: }
419: }
420:
421: public final void dispatchEvent(AWTEvent event) {
422: toolkit.lockAWT();
423: try {
424: processEvent(event);
425: if (deprecatedEventHandler) {
426: postDeprecatedEvent(event);
427: }
428: } finally {
429: toolkit.unlockAWT();
430: }
431: }
432:
433: void postDeprecatedEvent(AWTEvent event) {
434: Event evt = event.getEvent();
435: if (evt != null) {
436: postEvent(evt);
437: }
438: }
439:
440: /**
441: * @deprecated
442: */
443: @Deprecated
444: public MenuComponentPeer getPeer()
445: throws org.apache.harmony.luni.util.NotImplementedException {
446: toolkit.lockAWT();
447: try {
448: } finally {
449: toolkit.unlockAWT();
450: }
451: if (true) {
452: throw new RuntimeException("Method is not implemented"); //TODO: implement //$NON-NLS-1$
453: }
454: return null;
455: }
456:
457: protected final Object getTreeLock() {
458: return toolkit.awtTreeLock;
459: }
460:
461: @SuppressWarnings("deprecation")
462: @Deprecated
463: public boolean postEvent(Event e) {
464: toolkit.lockAWT();
465: try {
466: if (parent != null) {
467: return parent.postEvent(e);
468: }
469: return false;
470: } finally {
471: toolkit.unlockAWT();
472: }
473: }
474:
475: protected String paramString() {
476: toolkit.lockAWT();
477: try {
478: return getName();
479: } finally {
480: toolkit.unlockAWT();
481: }
482: }
483:
484: public AccessibleContext getAccessibleContext() {
485: toolkit.lockAWT();
486: try {
487: if (accessibleContext == null) {
488: accessibleContext = createAccessibleContext();
489: }
490: return accessibleContext;
491: } finally {
492: toolkit.unlockAWT();
493: }
494: }
495:
496: public Font getFont() {
497: toolkit.lockAWT();
498: try {
499: if (font == null && hasDefaultFont()) {
500: return toolkit.getDefaultFont();
501: }
502: if (font == null && parent != null) {
503: return parent.getFont();
504: }
505: return font;
506: } finally {
507: toolkit.unlockAWT();
508: }
509: }
510:
511: boolean isFontSet() {
512: return font != null
513: || ((parent instanceof MenuComponent) && ((MenuComponent) parent)
514: .isFontSet());
515: }
516:
517: boolean hasDefaultFont() {
518: return false;
519: }
520:
521: protected void processEvent(AWTEvent event) {
522: toolkit.lockAWT();
523: try {
524: // do nothing
525: } finally {
526: toolkit.unlockAWT();
527: }
528: }
529:
530: public void removeNotify() {
531: toolkit.lockAWT();
532: try {
533: } finally {
534: toolkit.unlockAWT();
535: }
536: }
537:
538: public void setFont(Font font) {
539: toolkit.lockAWT();
540: try {
541: this .font = font;
542: } finally {
543: toolkit.unlockAWT();
544: }
545: }
546:
547: void setParent(MenuContainer parent) {
548: this .parent = parent;
549: }
550:
551: Point getLocation() {
552: // to be overridden
553: return new Point(0, 0);
554: }
555:
556: int getWidth() {
557: // to be overridden
558: return 1;
559: }
560:
561: int getHeight() {
562: // to be overridden
563: return 1;
564: }
565:
566: /**
567: * Recursively find the menu item for a menu shortcut
568: * @param ms - the shortcut
569: * @return the menu item;
570: * or null if the item is not available for this shortcut
571: */
572: MenuItem getShortcutMenuItemImpl(MenuShortcut ms) {
573: if (ms == null) {
574: return null;
575: }
576: for (int i = 0; i < getItemCount(); i++) {
577: MenuItem mi = getItem(i);
578: if (mi instanceof Menu) {
579: mi = ((Menu) mi).getShortcutMenuItemImpl(ms);
580: if (mi != null) {
581: return mi;
582: }
583: } else if (ms.equals(mi.getShortcut())) {
584: return mi;
585: }
586: }
587: return null;
588: }
589:
590: void paint(Graphics gr) {
591: gr.setColor(Color.LIGHT_GRAY);
592: gr.fillRect(0, 0, getWidth(), getHeight());
593: gr.setColor(Color.BLACK);
594: }
595:
596: /**
597: * Mouse events handler
598: * @param eventId - one of the MouseEvent.MOUSE_* constants
599: * @param where - mouse location
600: * @param mouseButton - mouse button that was pressed or released
601: * @param when - event time
602: * @param modifiers - input event modifiers
603: */
604: void onMouseEvent(int eventId, Point where, int mouseButton,
605: long when, int modifiers) {
606: // to be overridden
607: }
608:
609: /**
610: * Keyboard event handler
611: * @param eventId - one of the KeyEvent.KEY_* constants
612: * @param vKey - the key code
613: * @param when - event time
614: * @param modifiers - input event modifiers
615: */
616: void onKeyEvent(int eventId, int vKey, long when, int modifiers) {
617: // to be overridden
618: }
619:
620: /**
621: * Post the ActionEvent or ItemEvent,
622: * depending on type of the menu item.
623: * @param item - the index of menu item
624: * @param when - event time
625: * @param modifiers - input event modifiers
626: */
627: void fireItemAction(int item, long when, int modifiers) {
628: MenuItem mi = getItem(item);
629: mi.itemSelected(when, modifiers);
630: }
631:
632: MenuItem getItem(int index) {
633: // to be overridden
634: return null;
635: }
636:
637: int getItemCount() {
638: return 0;
639: }
640:
641: /**
642: * @return The sub-menu of currently selecetd item,
643: * or null if such a sub-menu is not available
644: */
645: Menu getSelectedSubmenu() {
646: if (selectedItemIndex < 0) {
647: return null;
648: }
649: MenuItem item = getItem(selectedItemIndex);
650: return (item instanceof Menu) ? (Menu) item : null;
651: }
652:
653: /**
654: * Convenience method for selectItem(index, true)
655: */
656: void selectItem(int index) {
657: selectItem(index, true);
658: }
659:
660: /**
661: * Change the selection in the menu
662: * @param index - new selecetd item's index
663: * @param showSubMenu - if new selected item has a sub-menu,
664: * should that sub-menu be displayed
665: */
666: void selectItem(int index, boolean showSubMenu) {
667: if (selectedItemIndex == index) {
668: return;
669: }
670: if (selectedItemIndex >= 0
671: && getItem(selectedItemIndex) instanceof Menu) {
672: ((Menu) getItem(selectedItemIndex)).hide();
673: }
674: MultiRectArea clip = getUpdateClip(index, selectedItemIndex);
675: selectedItemIndex = index;
676: Graphics gr = getGraphics(clip);
677: if (gr != null) {
678: paint(gr);
679: }
680: gr.dispose();
681: if (showSubMenu) {
682: showSubMenu(selectedItemIndex);
683: }
684: }
685:
686: /**
687: * Change the selected item to the next one in the requested direction
688: * moving cyclically, skipping separators
689: * @param forward - the direction to move the selection
690: * @param showSubMenu - if new selected item has a sub-menu,
691: * should that sub-menu be displayed
692: */
693: void selectNextItem(boolean forward, boolean showSubMenu) {
694: int selected = getSelectedItemIndex();
695: int count = getItemCount();
696: if (count == 0) {
697: return;
698: }
699: if (selected < 0) {
700: selected = (forward ? count - 1 : 0);
701: }
702: int i = selected;
703: do {
704: i = (forward ? (i + 1) : (i + count - 1)) % count;
705: i %= count;
706: MenuItem item = getItem(i);
707: if (!"-".equals(item.getLabel())) { //$NON-NLS-1$
708: selectItem(i, showSubMenu);
709: return;
710: }
711: } while (i != selected);
712: }
713:
714: void showSubMenu(int index) {
715: if ((index < 0) || !isActive()) {
716: return;
717: }
718: MenuItem item = getItem(index);
719: if (item instanceof Menu) {
720: Menu menu = ((Menu) getItem(index));
721: if (menu.getItemCount() == 0) {
722: return;
723: }
724: Point location = getSubmenuLocation(index);
725: menu.show(location.x, location.y, false);
726: }
727: }
728:
729: /**
730: * @return - the menu bar which is the root of crrent menu's hierarchy;
731: * or null if the hierarchy root is not a menu bar
732: */
733: MenuBar getMenuBar() {
734: if (parent instanceof MenuBar) {
735: return (MenuBar) parent;
736: }
737: if (parent instanceof MenuComponent) {
738: return ((MenuComponent) parent).getMenuBar();
739: }
740: return null;
741: }
742:
743: PopupBox getPopupBox() {
744: return null;
745: }
746:
747: Rectangle getItemRect(int index) {
748: // to be overridden
749: return null;
750: }
751:
752: /**
753: * Determine the clip region when menu selection is changed
754: * from index1 to index2
755: * @param index1 - old selected intem
756: * @param index2 - new selected item
757: * @return - the region to repaint
758: */
759: final MultiRectArea getUpdateClip(int index1, int index2) {
760: MultiRectArea clip = new MultiRectArea();
761: if (index1 >= 0) {
762: clip.add(getItemRect(index1));
763: }
764: if (index2 >= 0) {
765: clip.add(getItemRect(index2));
766: }
767: return clip;
768: }
769:
770: Point getSubmenuLocation(int index) {
771: // to be overridden
772: return new Point(0, 0);
773: }
774:
775: int getSelectedItemIndex() {
776: return selectedItemIndex;
777: }
778:
779: void hide() {
780: selectedItemIndex = -1;
781: if (parent instanceof MenuComponent) {
782: ((MenuComponent) parent).itemHidden(this );
783: }
784: }
785:
786: void itemHidden(MenuComponent mc) {
787: // to be overridden
788: }
789:
790: boolean isVisible() {
791: return true;
792: }
793:
794: boolean isActive() {
795: return true;
796: }
797:
798: /**
799: * Hide all menu hierarchy
800: */
801: void endMenu() {
802: toolkit.dispatcher.popupDispatcher.deactivateAll();
803: }
804:
805: /**
806: * Handle the mouse click or Enter key event on a menu's item
807: * @param when - the event time
808: * @param modifiers - input event modifiers
809: */
810: void itemSelected(long when, int modifiers) {
811: endMenu();
812: }
813:
814: String autoName() {
815: String name = getClass().getName();
816: if (name.indexOf("$") != -1) { //$NON-NLS-1$
817: return null;
818: }
819: int number = toolkit.autoNumber.nextMenuComponent++;
820: name = name.substring(name.lastIndexOf(".") + 1) + Integer.toString(number); //$NON-NLS-1$
821: return name;
822: }
823:
824: /**
825: * Creates the Graphics object for the pop-up box of this menu component
826: * @param clip - the clip to set on this Graphics
827: * @return - the created Graphics object,
828: * or null if such object is not available.
829: */
830: Graphics getGraphics(MultiRectArea clip) {
831: // to be overridden
832: return null;
833: }
834:
835: /**
836: * @return accessible context specific for particular menu component
837: */
838: AccessibleContext createAccessibleContext() {
839: return null;
840: }
841: }
|