0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.swt.widgets;
0011:
0012: import org.eclipse.swt.internal.carbon.ATSFontMetrics;
0013: import org.eclipse.swt.internal.carbon.GDevice;
0014: import org.eclipse.swt.internal.carbon.OS;
0015: import org.eclipse.swt.internal.carbon.MenuTrackingData;
0016: import org.eclipse.swt.internal.carbon.Rect;
0017:
0018: import org.eclipse.swt.*;
0019: import org.eclipse.swt.events.*;
0020: import org.eclipse.swt.graphics.*;
0021:
0022: /**
0023: * Instances of this class are user interface objects that contain
0024: * menu items.
0025: * <dl>
0026: * <dt><b>Styles:</b></dt>
0027: * <dd>BAR, DROP_DOWN, POP_UP, NO_RADIO_GROUP</dd>
0028: * <dd>LEFT_TO_RIGHT, RIGHT_TO_LEFT</dd>
0029: * <dt><b>Events:</b></dt>
0030: * <dd>Help, Hide, Show </dd>
0031: * </dl>
0032: * <p>
0033: * Note: Only one of BAR, DROP_DOWN and POP_UP may be specified.
0034: * Only one of LEFT_TO_RIGHT or RIGHT_TO_LEFT may be specified.
0035: * </p><p>
0036: * IMPORTANT: This class is <em>not</em> intended to be subclassed.
0037: * </p>
0038: */
0039: public class Menu extends Widget {
0040: /**
0041: * the handle to the OS resource
0042: * (Warning: This field is platform dependent)
0043: * <p>
0044: * <b>IMPORTANT:</b> This field is <em>not</em> part of the SWT
0045: * public API. It is marked public only so that it can be shared
0046: * within the packages provided by SWT. It is not available on all
0047: * platforms and should never be accessed from application code.
0048: * </p>
0049: */
0050: int handle;
0051: short id;
0052: int x, y, itemCount;
0053: // int width, height;
0054: boolean hasLocation, modified, closed;
0055: MenuItem[] items;
0056: MenuItem cascade, defaultItem, lastTarget;
0057: Decorations parent;
0058:
0059: /**
0060: * Constructs a new instance of this class given its parent,
0061: * and sets the style for the instance so that the instance
0062: * will be a popup menu on the given parent's shell.
0063: *
0064: * @param parent a control which will be the parent of the new instance (cannot be null)
0065: *
0066: * @exception IllegalArgumentException <ul>
0067: * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
0068: * </ul>
0069: * @exception SWTException <ul>
0070: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
0071: * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
0072: * </ul>
0073: *
0074: * @see SWT#POP_UP
0075: * @see Widget#checkSubclass
0076: * @see Widget#getStyle
0077: */
0078: public Menu(Control parent) {
0079: this (checkNull(parent).menuShell(), SWT.POP_UP);
0080: }
0081:
0082: /**
0083: * Constructs a new instance of this class given its parent
0084: * (which must be a <code>Decorations</code>) and a style value
0085: * describing its behavior and appearance.
0086: * <p>
0087: * The style value is either one of the style constants defined in
0088: * class <code>SWT</code> which is applicable to instances of this
0089: * class, or must be built by <em>bitwise OR</em>'ing together
0090: * (that is, using the <code>int</code> "|" operator) two or more
0091: * of those <code>SWT</code> style constants. The class description
0092: * lists the style constants that are applicable to the class.
0093: * Style bits are also inherited from superclasses.
0094: * </p>
0095: *
0096: * @param parent a decorations control which will be the parent of the new instance (cannot be null)
0097: * @param style the style of menu to construct
0098: *
0099: * @exception IllegalArgumentException <ul>
0100: * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
0101: * </ul>
0102: * @exception SWTException <ul>
0103: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
0104: * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
0105: * </ul>
0106: *
0107: * @see SWT#BAR
0108: * @see SWT#DROP_DOWN
0109: * @see SWT#POP_UP
0110: * @see Widget#checkSubclass
0111: * @see Widget#getStyle
0112: */
0113: public Menu(Decorations parent, int style) {
0114: super (parent, checkStyle(style));
0115: this .parent = parent;
0116: createWidget();
0117: }
0118:
0119: /**
0120: * Constructs a new instance of this class given its parent
0121: * (which must be a <code>Menu</code>) and sets the style
0122: * for the instance so that the instance will be a drop-down
0123: * menu on the given parent's parent.
0124: *
0125: * @param parentMenu a menu which will be the parent of the new instance (cannot be null)
0126: *
0127: * @exception IllegalArgumentException <ul>
0128: * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
0129: * </ul>
0130: * @exception SWTException <ul>
0131: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
0132: * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
0133: * </ul>
0134: *
0135: * @see SWT#DROP_DOWN
0136: * @see Widget#checkSubclass
0137: * @see Widget#getStyle
0138: */
0139: public Menu(Menu parentMenu) {
0140: this (checkNull(parentMenu).parent, SWT.DROP_DOWN);
0141: }
0142:
0143: /**
0144: * Constructs a new instance of this class given its parent
0145: * (which must be a <code>MenuItem</code>) and sets the style
0146: * for the instance so that the instance will be a drop-down
0147: * menu on the given parent's parent menu.
0148: *
0149: * @param parentItem a menu item which will be the parent of the new instance (cannot be null)
0150: *
0151: * @exception IllegalArgumentException <ul>
0152: * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
0153: * </ul>
0154: * @exception SWTException <ul>
0155: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
0156: * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
0157: * </ul>
0158: *
0159: * @see SWT#DROP_DOWN
0160: * @see Widget#checkSubclass
0161: * @see Widget#getStyle
0162: */
0163: public Menu(MenuItem parentItem) {
0164: this (checkNull(parentItem).parent);
0165: }
0166:
0167: static Control checkNull(Control control) {
0168: if (control == null)
0169: SWT.error(SWT.ERROR_NULL_ARGUMENT);
0170: return control;
0171: }
0172:
0173: static Menu checkNull(Menu menu) {
0174: if (menu == null)
0175: SWT.error(SWT.ERROR_NULL_ARGUMENT);
0176: return menu;
0177: }
0178:
0179: static MenuItem checkNull(MenuItem item) {
0180: if (item == null)
0181: SWT.error(SWT.ERROR_NULL_ARGUMENT);
0182: return item;
0183: }
0184:
0185: static int checkStyle(int style) {
0186: return checkBits(style, SWT.POP_UP, SWT.BAR, SWT.DROP_DOWN, 0,
0187: 0, 0);
0188: }
0189:
0190: void _setVisible(boolean visible) {
0191: if ((style & (SWT.BAR | SWT.DROP_DOWN)) != 0)
0192: return;
0193: if (!visible)
0194: return;
0195: org.eclipse.swt.internal.carbon.Point where = new org.eclipse.swt.internal.carbon.Point();
0196: if (hasLocation) {
0197: where.h = (short) x;
0198: where.v = (short) y;
0199: } else {
0200: OS.GetGlobalMouse(where);
0201: }
0202: /*
0203: * Feature in the Macintosh. When the application FruitMenu is installed,
0204: * the output parameters cannot be NULL or ContextualMenuSelect() crashes.
0205: * The fix is to ensure they are not NULL.
0206: */
0207: OS.ContextualMenuSelect(handle, where, false,
0208: OS.kCMHelpItemRemoveHelp, null, null, new int[1],
0209: new short[1], new short[1]);
0210: }
0211:
0212: /**
0213: * Adds the listener to the collection of listeners who will
0214: * be notified when help events are generated for the control,
0215: * by sending it one of the messages defined in the
0216: * <code>HelpListener</code> interface.
0217: *
0218: * @param listener the listener which should be notified
0219: *
0220: * @exception IllegalArgumentException <ul>
0221: * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
0222: * </ul>
0223: * @exception SWTException <ul>
0224: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
0225: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
0226: * </ul>
0227: *
0228: * @see HelpListener
0229: * @see #removeHelpListener
0230: */
0231: public void addHelpListener(HelpListener listener) {
0232: checkWidget();
0233: if (listener == null)
0234: error(SWT.ERROR_NULL_ARGUMENT);
0235: TypedListener typedListener = new TypedListener(listener);
0236: addListener(SWT.Help, typedListener);
0237: }
0238:
0239: /**
0240: * Adds the listener to the collection of listeners who will
0241: * be notified when menus are hidden or shown, by sending it
0242: * one of the messages defined in the <code>MenuListener</code>
0243: * interface.
0244: *
0245: * @param listener the listener which should be notified
0246: *
0247: * @exception IllegalArgumentException <ul>
0248: * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
0249: * </ul>
0250: * @exception SWTException <ul>
0251: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
0252: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
0253: * </ul>
0254: *
0255: * @see MenuListener
0256: * @see #removeMenuListener
0257: */
0258: public void addMenuListener(MenuListener listener) {
0259: checkWidget();
0260: if (listener == null)
0261: error(SWT.ERROR_NULL_ARGUMENT);
0262: TypedListener typedListener = new TypedListener(listener);
0263: addListener(SWT.Hide, typedListener);
0264: addListener(SWT.Show, typedListener);
0265: }
0266:
0267: void createHandle() {
0268: display.addMenu(this );
0269: int outMenuRef[] = new int[1];
0270: OS.CreateNewMenu(id, 0, outMenuRef);
0271: if (outMenuRef[0] == 0) {
0272: display.removeMenu(this );
0273: error(SWT.ERROR_NO_HANDLES);
0274: }
0275: handle = outMenuRef[0];
0276: }
0277:
0278: void createItem(MenuItem item, int index) {
0279: checkWidget();
0280: if (!(0 <= index && index <= itemCount))
0281: error(SWT.ERROR_INVALID_RANGE);
0282: int attributes = OS.kMenuItemAttrAutoRepeat
0283: | OS.kMenuItemAttrCustomDraw;
0284: if ((item.style & SWT.SEPARATOR) != 0)
0285: attributes = OS.kMenuItemAttrSeparator;
0286: int result = OS.InsertMenuItemTextWithCFString(handle, 0,
0287: (short) index, attributes, 0);
0288: if (result != OS.noErr) {
0289: error(SWT.ERROR_ITEM_NOT_ADDED);
0290: }
0291: if (itemCount == items.length) {
0292: MenuItem[] newItems = new MenuItem[items.length + 4];
0293: System.arraycopy(items, 0, newItems, 0, items.length);
0294: items = newItems;
0295: }
0296: System.arraycopy(items, index, items, index + 1, itemCount++
0297: - index);
0298: items[index] = item;
0299: modified = true;
0300: int emptyMenu = item.createEmptyMenu();
0301: if (emptyMenu != 0) {
0302: OS.SetMenuItemHierarchicalMenu(handle, (short) (index + 1),
0303: emptyMenu);
0304: OS.ReleaseMenu(emptyMenu);
0305: }
0306: }
0307:
0308: void createWidget() {
0309: checkOrientation(parent);
0310: super .createWidget();
0311: items = new MenuItem[4];
0312: }
0313:
0314: void destroyItem(MenuItem item) {
0315: int index = 0;
0316: while (index < itemCount) {
0317: if (items[index] == item)
0318: break;
0319: index++;
0320: }
0321: if (index == itemCount)
0322: return;
0323: System.arraycopy(items, index + 1, items, index, --itemCount
0324: - index);
0325: items[itemCount] = null;
0326: if (itemCount == 0)
0327: items = new MenuItem[4];
0328: modified = true;
0329: OS.DeleteMenuItem(handle, (short) (index + 1));
0330: }
0331:
0332: void destroyWidget() {
0333: int theMenu = handle;
0334: releaseHandle();
0335: if (theMenu != 0) {
0336: OS.DisposeMenu(theMenu);
0337: }
0338: }
0339:
0340: void fixMenus(Decorations newParent) {
0341: this .parent = newParent;
0342: }
0343:
0344: /*public*/Rectangle getBounds() {
0345: checkWidget();
0346: if ((style & SWT.BAR) != 0) {
0347: Menu menu = display.getMenuBar();
0348: if (this != menu)
0349: return new Rectangle(0, 0, 0, 0);
0350: int height = OS.GetMBarHeight();
0351: int gdevice = OS.GetMainDevice();
0352: int[] ptr = new int[1];
0353: OS.memmove(ptr, gdevice, 4);
0354: GDevice device = new GDevice();
0355: OS.memmove(device, ptr[0], GDevice.sizeof);
0356: return new Rectangle(0, 0, device.right - device.left,
0357: height);
0358: }
0359: OS.CalcMenuSize(handle);
0360: return new Rectangle(x, y, 0, 0);
0361: // return new Rectangle (x, y, width, height);
0362: }
0363:
0364: /**
0365: * Returns the default menu item or null if none has
0366: * been previously set.
0367: *
0368: * @return the default menu item.
0369: *
0370: * </ul>
0371: * @exception SWTException <ul>
0372: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
0373: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
0374: * </ul>
0375: */
0376: public MenuItem getDefaultItem() {
0377: checkWidget();
0378: return defaultItem;
0379: }
0380:
0381: /**
0382: * Returns <code>true</code> if the receiver is enabled, and
0383: * <code>false</code> otherwise. A disabled menu is typically
0384: * not selectable from the user interface and draws with an
0385: * inactive or "grayed" look.
0386: *
0387: * @return the receiver's enabled state
0388: *
0389: * @exception SWTException <ul>
0390: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
0391: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
0392: * </ul>
0393: *
0394: * @see #isEnabled
0395: */
0396: public boolean getEnabled() {
0397: checkWidget();
0398: return (state & DISABLED) == 0;
0399: }
0400:
0401: /**
0402: * Returns the item at the given, zero-relative index in the
0403: * receiver. Throws an exception if the index is out of range.
0404: *
0405: * @param index the index of the item to return
0406: * @return the item at the given index
0407: *
0408: * @exception IllegalArgumentException <ul>
0409: * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
0410: * </ul>
0411: * @exception SWTException <ul>
0412: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
0413: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
0414: * </ul>
0415: */
0416: public MenuItem getItem(int index) {
0417: checkWidget();
0418: if (!(0 <= index && index < itemCount))
0419: error(SWT.ERROR_INVALID_RANGE);
0420: return items[index];
0421: }
0422:
0423: /**
0424: * Returns the number of items contained in the receiver.
0425: *
0426: * @return the number of items
0427: *
0428: * @exception SWTException <ul>
0429: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
0430: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
0431: * </ul>
0432: */
0433: public int getItemCount() {
0434: checkWidget();
0435: return itemCount;
0436: }
0437:
0438: /**
0439: * Returns a (possibly empty) array of <code>MenuItem</code>s which
0440: * are the items in the receiver.
0441: * <p>
0442: * Note: This is not the actual structure used by the receiver
0443: * to maintain its list of items, so modifying the array will
0444: * not affect the receiver.
0445: * </p>
0446: *
0447: * @return the items in the receiver
0448: *
0449: * @exception SWTException <ul>
0450: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
0451: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
0452: * </ul>
0453: */
0454: public MenuItem[] getItems() {
0455: checkWidget();
0456: MenuItem[] result = new MenuItem[itemCount];
0457: int index = 0;
0458: if (items != null) {
0459: for (int i = 0; i < itemCount; i++) {
0460: MenuItem item = items[i];
0461: if (item != null && !item.isDisposed()) {
0462: result[index++] = item;
0463: }
0464: }
0465: }
0466: if (index != result.length) {
0467: MenuItem[] newItems = new MenuItem[index];
0468: System.arraycopy(result, 0, newItems, 0, index);
0469: result = newItems;
0470: }
0471: return result;
0472: }
0473:
0474: String getNameText() {
0475: String result = "";
0476: MenuItem[] items = getItems();
0477: int length = items.length;
0478: if (length > 0) {
0479: for (int i = 0; i < length - 1; i++) {
0480: result = result + items[i].getNameText() + ", ";
0481: }
0482: result = result + items[length - 1].getNameText();
0483: }
0484: return result;
0485: }
0486:
0487: /**
0488: * Returns the receiver's parent, which must be a <code>Decorations</code>.
0489: *
0490: * @return the receiver's parent
0491: *
0492: * @exception SWTException <ul>
0493: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
0494: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
0495: * </ul>
0496: */
0497: public Decorations getParent() {
0498: checkWidget();
0499: return parent;
0500: }
0501:
0502: /**
0503: * Returns the receiver's parent item, which must be a
0504: * <code>MenuItem</code> or null when the receiver is a
0505: * root.
0506: *
0507: * @return the receiver's parent item
0508: *
0509: * @exception SWTException <ul>
0510: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
0511: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
0512: * </ul>
0513: */
0514: public MenuItem getParentItem() {
0515: checkWidget();
0516: return cascade;
0517: }
0518:
0519: /**
0520: * Returns the receiver's parent item, which must be a
0521: * <code>Menu</code> or null when the receiver is a
0522: * root.
0523: *
0524: * @return the receiver's parent item
0525: *
0526: * @exception SWTException <ul>
0527: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
0528: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
0529: * </ul>
0530: */
0531: public Menu getParentMenu() {
0532: checkWidget();
0533: if (cascade != null)
0534: return cascade.parent;
0535: return null;
0536: }
0537:
0538: /**
0539: * Returns the receiver's shell. For all controls other than
0540: * shells, this simply returns the control's nearest ancestor
0541: * shell. Shells return themselves, even if they are children
0542: * of other shells.
0543: *
0544: * @return the receiver's shell
0545: *
0546: * @exception SWTException <ul>
0547: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
0548: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
0549: * </ul>
0550: *
0551: * @see #getParent
0552: */
0553: public Shell getShell() {
0554: checkWidget();
0555: return parent.getShell();
0556: }
0557:
0558: /**
0559: * Returns <code>true</code> if the receiver is visible, and
0560: * <code>false</code> otherwise.
0561: * <p>
0562: * If one of the receiver's ancestors is not visible or some
0563: * other condition makes the receiver not visible, this method
0564: * may still indicate that it is considered visible even though
0565: * it may not actually be showing.
0566: * </p>
0567: *
0568: * @return the receiver's visibility state
0569: *
0570: * @exception SWTException <ul>
0571: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
0572: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
0573: * </ul>
0574: */
0575: public boolean getVisible() {
0576: checkWidget();
0577: if ((style & SWT.BAR) != 0) {
0578: return this == parent.menuShell().menuBar;
0579: }
0580: if ((style & SWT.POP_UP) != 0) {
0581: Menu[] popups = display.popups;
0582: if (popups == null)
0583: return false;
0584: for (int i = 0; i < popups.length; i++) {
0585: if (popups[i] == this )
0586: return true;
0587: }
0588: }
0589: MenuTrackingData outData = new MenuTrackingData();
0590: return OS.GetMenuTrackingData(handle, outData) == OS.noErr;
0591: }
0592:
0593: void hookEvents() {
0594: super .hookEvents();
0595: int menuProc = display.menuProc;
0596: int[] mask = new int[] { OS.kEventClassMenu,
0597: OS.kEventMenuCalculateSize, OS.kEventClassMenu,
0598: OS.kEventMenuClosed, OS.kEventClassMenu,
0599: OS.kEventMenuCreateFrameView, OS.kEventClassMenu,
0600: OS.kEventMenuDrawItem, OS.kEventClassMenu,
0601: OS.kEventMenuDrawItemContent, OS.kEventClassMenu,
0602: OS.kEventMenuMeasureItemWidth, OS.kEventClassMenu,
0603: OS.kEventMenuOpening, OS.kEventClassMenu,
0604: OS.kEventMenuTargetItem, };
0605: int menuTarget = OS.GetMenuEventTarget(handle);
0606: OS.InstallEventHandler(menuTarget, menuProc, mask.length / 2,
0607: mask, handle, null);
0608: }
0609:
0610: int kEventMenuCalculateSize(int nextHandler, int theEvent,
0611: int userData) {
0612: int result = super .kEventMenuCalculateSize(nextHandler,
0613: theEvent, userData);
0614: if (result == OS.noErr)
0615: return result;
0616: int[] theControl = new int[1];
0617: OS.GetEventParameter(theEvent, OS.kEventParamControlRef,
0618: OS.typeControlRef, null, 4, null, theControl);
0619: int menuProc = display.menuProc;
0620: int[] mask = new int[] { OS.kEventClassMenu,
0621: OS.kEventMenuGetFrameBounds, };
0622: int controlTarget = OS.GetControlEventTarget(theControl[0]);
0623: //TODO - installed multi-times, does this matter?
0624: OS.InstallEventHandler(controlTarget, menuProc,
0625: mask.length / 2, mask, handle, null);
0626: return result;
0627: }
0628:
0629: int kEventMenuClosed(int nextHandler, int theEvent, int userData) {
0630: int result = super .kEventMenuClosed(nextHandler, theEvent,
0631: userData);
0632: if (result == OS.noErr)
0633: return result;
0634: closed = true;
0635: // width = height = 0;
0636: // int count = OS.CountMenuItems (handle);
0637: // for (int i=0; i<count; i++) {
0638: // MenuItem item = items [i];
0639: // item.x = item.y = item.width = item.height = 0;
0640: // }
0641: sendEvent(SWT.Hide);
0642: return OS.eventNotHandledErr;
0643: }
0644:
0645: int kEventMenuCreateFrameView(int nextHandler, int theEvent,
0646: int userData) {
0647: int result = super .kEventMenuCreateFrameView(nextHandler,
0648: theEvent, userData);
0649: if (result == OS.noErr)
0650: return result;
0651: int[] theControl = new int[1];
0652: OS.GetEventParameter(theEvent, OS.kEventParamControlRef,
0653: OS.typeControlRef, null, 4, null, theControl);
0654: int menuProc = display.menuProc;
0655: int[] mask = new int[] { OS.kEventClassMenu,
0656: OS.kEventMenuGetFrameBounds, };
0657: int controlTarget = OS.GetControlEventTarget(theControl[0]);
0658: OS.InstallEventHandler(controlTarget, menuProc,
0659: mask.length / 2, mask, handle, null);
0660: return OS.eventNotHandledErr;
0661: }
0662:
0663: int kEventMenuDrawItem(int nextHandler, int theEvent, int userData) {
0664: int result = super .kEventMenuDrawItem(nextHandler, theEvent,
0665: userData);
0666: if (result == OS.noErr)
0667: return result;
0668: // short [] index = new short [1];
0669: // OS.GetEventParameter (theEvent, OS.kEventParamMenuItemIndex, OS.typeMenuItemIndex, null, 2, null, index);
0670: // MenuItem item = items [index [0] - 1];
0671: // Rect rect = new Rect ();
0672: // OS.GetEventParameter (theEvent, OS.kEventParamMenuItemBounds, OS.typeQDRectangle, null, Rect.sizeof, null, rect);
0673: // item.x = rect.left - x;
0674: // item.y = rect.top - y;
0675: // item.width = rect.right - rect.left;
0676: // item.height = rect.bottom - rect.top;
0677: return OS.eventNotHandledErr;
0678: }
0679:
0680: int kEventMenuDrawItemContent(int nextHandler, int theEvent,
0681: int userData) {
0682: int result = super .kEventMenuDrawItemContent(nextHandler,
0683: theEvent, userData);
0684: if (result == OS.noErr)
0685: return result;
0686: short[] index = new short[1];
0687: OS.GetEventParameter(theEvent, OS.kEventParamMenuItemIndex,
0688: OS.typeMenuItemIndex, null, 2, null, index);
0689: MenuItem item = items[index[0] - 1];
0690: if (item.accelerator == 0) {
0691: int accelIndex = item.text.indexOf('\t');
0692: if (accelIndex != -1) {
0693: String accelText = item.text.substring(accelIndex + 1);
0694: int length = accelText.length();
0695: if (length != 0) {
0696: result = OS.CallNextEventHandler(nextHandler,
0697: theEvent);
0698: Rect rect = new Rect();
0699: OS.GetEventParameter(theEvent,
0700: OS.kEventParamMenuItemBounds,
0701: OS.typeQDRectangle, null, Rect.sizeof,
0702: null, rect);
0703: int[] context = new int[1];
0704: OS
0705: .GetEventParameter(theEvent,
0706: OS.kEventParamCGContextRef,
0707: OS.typeCGContextRef, null, 4, null,
0708: context);
0709:
0710: /* Draw the key */
0711: int modifierIndex = modifierIndex(accelText);
0712: char[] buffer = new char[length - modifierIndex - 1];
0713: accelText.getChars(modifierIndex + 1, length,
0714: buffer, 0);
0715: int themeFont = OS.kThemeMenuItemFont;
0716: if (buffer.length > 1)
0717: themeFont = OS.kThemeMenuItemCmdKeyFont;
0718: byte[] family = new byte[256];
0719: short[] size = new short[1];
0720: byte[] style = new byte[1];
0721: OS.GetThemeFont((short) themeFont,
0722: (short) OS.smSystemScript, family, size,
0723: style);
0724: short id = OS.FMGetFontFamilyFromName(family);
0725: int[] font = new int[1];
0726: OS.FMGetFontFromFontFamilyInstance(id, style[0],
0727: font, null);
0728: int atsFont = OS.FMGetATSFontRefFromFont(font[0]);
0729: ATSFontMetrics fontMetrics = new ATSFontMetrics();
0730: OS.ATSFontGetVerticalMetrics(atsFont,
0731: OS.kATSOptionFlagsDefault, fontMetrics);
0732: OS.ATSFontGetHorizontalMetrics(atsFont,
0733: OS.kATSOptionFlagsDefault, fontMetrics);
0734: int[] metric = new int[1];
0735: OS.GetThemeMetric(
0736: OS.kThemeMetricMenuIconTrailingEdgeMargin,
0737: metric);
0738: int str = OS.CFStringCreateWithCharacters(
0739: OS.kCFAllocatorDefault, buffer,
0740: buffer.length);
0741: org.eclipse.swt.internal.carbon.Point size1 = new org.eclipse.swt.internal.carbon.Point();
0742: OS.GetThemeTextDimensions(str, (short) themeFont,
0743: 0, false, size1, null);
0744: rect.left = (short) (rect.right
0745: - Math
0746: .max(
0747: (int) (fontMetrics.maxAdvanceWidth * size[0]),
0748: size1.h) - metric[0]);
0749: OS.DrawThemeTextBox(str, (short) themeFont,
0750: OS.kThemeStateActive, false, rect,
0751: (short) OS.teFlushLeft, context[0]);
0752: OS.CFRelease(str);
0753:
0754: /* Draw the modifiers */
0755: if (modifierIndex != -1) {
0756: buffer = new char[modifierIndex + 1];
0757: accelText.getChars(0, buffer.length, buffer, 0);
0758: str = OS.CFStringCreateWithCharacters(
0759: OS.kCFAllocatorDefault, buffer,
0760: buffer.length);
0761: OS.GetThemeTextDimensions(str,
0762: (short) OS.kThemeMenuItemCmdKeyFont, 0,
0763: false, size1, null);
0764: rect.right = rect.left;
0765: rect.left = (short) (rect.right - size1.h);
0766: OS.DrawThemeTextBox(str,
0767: (short) OS.kThemeMenuItemCmdKeyFont,
0768: OS.kThemeStateActive, false, rect,
0769: (short) OS.teFlushLeft, context[0]);
0770: OS.CFRelease(str);
0771: }
0772: return result;
0773: }
0774: }
0775: }
0776: return OS.eventNotHandledErr;
0777: }
0778:
0779: int kEventMenuGetFrameBounds(int nextHandler, int theEvent,
0780: int userData) {
0781: int result = super .kEventMenuGetFrameBounds(nextHandler,
0782: theEvent, userData);
0783: if (result == OS.noErr)
0784: return result;
0785: result = OS.CallNextEventHandler(nextHandler, theEvent);
0786: // CGRect rect = new CGRect ();
0787: // OS.GetEventParameter (theEvent, OS.kEventParamBounds, OS.typeHIRect, null, CGRect.sizeof, null, rect);
0788: // x = (int) rect.x;
0789: // y = (int) rect.y;
0790: // width = (int) rect.width;
0791: // height = (int) rect.height;
0792: // if (cascade != null) {
0793: // OS.GetEventParameter (theEvent, OS.kEventParamMenuItemBounds, OS.typeHIRect, null, CGRect.sizeof, null, rect);
0794: // cascade.x = (int) rect.x - x;
0795: // cascade.y = (int) rect.y - y;
0796: // cascade.width = (int) rect.width;
0797: // cascade.height = (int) rect.height;
0798: // }
0799: return result;
0800: }
0801:
0802: int kEventMenuMeasureItemWidth(int nextHandler, int theEvent,
0803: int userData) {
0804: int result = super .kEventMenuMeasureItemWidth(nextHandler,
0805: theEvent, userData);
0806: if (result == OS.noErr)
0807: return result;
0808: short[] index = new short[1];
0809: OS.GetEventParameter(theEvent, OS.kEventParamMenuItemIndex,
0810: OS.typeMenuItemIndex, null, 2, null, index);
0811: MenuItem item = items[index[0] - 1];
0812: if (item.accelerator == 0) {
0813: int accelIndex = item.text.indexOf('\t');
0814: if (accelIndex != -1) {
0815: String accelText = item.text.substring(accelIndex + 1);
0816: if (accelText.length() != 0) {
0817: result = OS.CallNextEventHandler(nextHandler,
0818: theEvent);
0819: char[] buffer = new char[accelText.length()];
0820: accelText.getChars(0, buffer.length, buffer, 0);
0821: int str = OS.CFStringCreateWithCharacters(
0822: OS.kCFAllocatorDefault, buffer,
0823: buffer.length);
0824: org.eclipse.swt.internal.carbon.Point size = new org.eclipse.swt.internal.carbon.Point();
0825: OS.GetThemeTextDimensions(str,
0826: (short) OS.kThemeMenuItemCmdKeyFont, 0,
0827: false, size, null);
0828: OS.CFRelease(str);
0829: short[] width = new short[1];
0830: OS.GetEventParameter(theEvent,
0831: OS.kEventParamMenuItemWidth, OS.typeSInt16,
0832: null, 2, null, width);
0833: int[] metric = new int[1];
0834: OS.GetThemeMetric(
0835: OS.kThemeMetricMenuTextTrailingEdgeMargin,
0836: metric);
0837: width[0] += metric[0] + size.h;
0838: OS.SetEventParameter(theEvent,
0839: OS.kEventParamMenuItemWidth, OS.typeSInt16,
0840: 2, width);
0841: return result;
0842: }
0843: }
0844: }
0845: return OS.eventNotHandledErr;
0846: }
0847:
0848: int kEventMenuOpening(int nextHandler, int theEvent, int userData) {
0849: int result = super .kEventMenuOpening(nextHandler, theEvent,
0850: userData);
0851: if (result == OS.noErr)
0852: return result;
0853: closed = false;
0854: sendEvent(SWT.Show);
0855: modified = false;
0856: return OS.eventNotHandledErr;
0857: }
0858:
0859: int kEventMenuTargetItem(int nextHandler, int theEvent, int userData) {
0860: int result = super .kEventMenuTargetItem(nextHandler, theEvent,
0861: userData);
0862: if (result == OS.noErr)
0863: return result;
0864: lastTarget = null;
0865: short[] index = new short[1];
0866: if (OS.GetEventParameter(theEvent, OS.kEventParamMenuItemIndex,
0867: OS.typeMenuItemIndex, null, 2, null, index) == OS.noErr) {
0868: if (index[0] > 0)
0869: lastTarget = items[index[0] - 1];
0870: if (lastTarget != null) {
0871: lastTarget.sendEvent(SWT.Arm);
0872: }
0873: }
0874: return OS.eventNotHandledErr;
0875: }
0876:
0877: /**
0878: * Searches the receiver's list starting at the first item
0879: * (index 0) until an item is found that is equal to the
0880: * argument, and returns the index of that item. If no item
0881: * is found, returns -1.
0882: *
0883: * @param item the search item
0884: * @return the index of the item
0885: *
0886: * @exception IllegalArgumentException <ul>
0887: * <li>ERROR_NULL_ARGUMENT - if the item is null</li>
0888: * </ul>
0889: * @exception SWTException <ul>
0890: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
0891: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
0892: * </ul>
0893: */
0894: public int indexOf(MenuItem item) {
0895: checkWidget();
0896: if (item == null)
0897: error(SWT.ERROR_NULL_ARGUMENT);
0898: for (int i = 0; i < itemCount; i++) {
0899: if (items[i] == item)
0900: return i;
0901: }
0902: return -1;
0903: }
0904:
0905: /**
0906: * Returns <code>true</code> if the receiver is enabled and all
0907: * of the receiver's ancestors are enabled, and <code>false</code>
0908: * otherwise. A disabled menu is typically not selectable from the
0909: * user interface and draws with an inactive or "grayed" look.
0910: *
0911: * @return the receiver's enabled state
0912: *
0913: * @exception SWTException <ul>
0914: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
0915: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
0916: * </ul>
0917: *
0918: * @see #getEnabled
0919: */
0920: public boolean isEnabled() {
0921: checkWidget();
0922: Menu parentMenu = getParentMenu();
0923: if (parentMenu == null) {
0924: return getEnabled() && parent.isEnabled();
0925: }
0926: return getEnabled() && parentMenu.isEnabled();
0927: }
0928:
0929: /**
0930: * Returns <code>true</code> if the receiver is visible and all
0931: * of the receiver's ancestors are visible and <code>false</code>
0932: * otherwise.
0933: *
0934: * @return the receiver's visibility state
0935: *
0936: * @exception SWTException <ul>
0937: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
0938: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
0939: * </ul>
0940: *
0941: * @see #getVisible
0942: */
0943: public boolean isVisible() {
0944: checkWidget();
0945: return getVisible();
0946: }
0947:
0948: int modifierIndex(String accelText) {
0949: int start = accelText.length() - 1;
0950: int index = start;
0951: while (index >= 0) {
0952: char c = accelText.charAt(index);
0953: switch (c) {
0954: case ' ':
0955: if (index != start)
0956: return index;
0957: break;
0958: case '\u2303':
0959: case '\u2325':
0960: case '\u21E7':
0961: case '\u2318':
0962: return index;
0963: }
0964: index--;
0965: }
0966: return -1;
0967: }
0968:
0969: void releaseChildren(boolean destroy) {
0970: if (items != null) {
0971: for (int i = 0; i < items.length; i++) {
0972: MenuItem item = items[i];
0973: if (item != null && !item.isDisposed()) {
0974: item.release(false);
0975: }
0976: }
0977: items = null;
0978: }
0979: super .releaseChildren(destroy);
0980: }
0981:
0982: void releaseHandle() {
0983: super .releaseHandle();
0984: handle = 0;
0985: }
0986:
0987: void releaseParent() {
0988: super .releaseParent();
0989: if (cascade != null)
0990: cascade.setMenu(null);
0991: if ((style & SWT.BAR) != 0 && this == parent.menuBar) {
0992: parent.setMenuBar(null);
0993: }
0994: }
0995:
0996: void releaseWidget() {
0997: super .releaseWidget();
0998: display.removeMenu(this );
0999: parent = null;
1000: cascade = defaultItem = lastTarget = null;
1001: }
1002:
1003: /**
1004: * Removes the listener from the collection of listeners who will
1005: * be notified when the help events are generated for the control.
1006: *
1007: * @param listener the listener which should no longer be notified
1008: *
1009: * @exception IllegalArgumentException <ul>
1010: * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1011: * </ul>
1012: * @exception SWTException <ul>
1013: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1014: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1015: * </ul>
1016: *
1017: * @see HelpListener
1018: * @see #addHelpListener
1019: */
1020: public void removeHelpListener(HelpListener listener) {
1021: checkWidget();
1022: if (listener == null)
1023: error(SWT.ERROR_NULL_ARGUMENT);
1024: if (eventTable == null)
1025: return;
1026: eventTable.unhook(SWT.Help, listener);
1027: }
1028:
1029: /**
1030: * Removes the listener from the collection of listeners who will
1031: * be notified when the menu events are generated for the control.
1032: *
1033: * @param listener the listener which should no longer be notified
1034: *
1035: * @exception IllegalArgumentException <ul>
1036: * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1037: * </ul>
1038: * @exception SWTException <ul>
1039: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1040: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1041: * </ul>
1042: *
1043: * @see MenuListener
1044: * @see #addMenuListener
1045: */
1046: public void removeMenuListener(MenuListener listener) {
1047: checkWidget();
1048: if (listener == null)
1049: error(SWT.ERROR_NULL_ARGUMENT);
1050: if (eventTable == null)
1051: return;
1052: eventTable.unhook(SWT.Hide, listener);
1053: eventTable.unhook(SWT.Show, listener);
1054: }
1055:
1056: /**
1057: * Sets the default menu item to the argument or removes
1058: * the default emphasis when the argument is <code>null</code>.
1059: *
1060: * @param item the default menu item or null
1061: *
1062: * @exception IllegalArgumentException <ul>
1063: * <li>ERROR_INVALID_ARGUMENT - if the menu item has been disposed</li>
1064: * </ul>
1065: * @exception SWTException <ul>
1066: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1067: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1068: * </ul>
1069: */
1070: public void setDefaultItem(MenuItem item) {
1071: checkWidget();
1072: if (item != null && item.isDisposed())
1073: error(SWT.ERROR_INVALID_ARGUMENT);
1074: defaultItem = item;
1075: }
1076:
1077: /**
1078: * Enables the receiver if the argument is <code>true</code>,
1079: * and disables it otherwise. A disabled menu is typically
1080: * not selectable from the user interface and draws with an
1081: * inactive or "grayed" look.
1082: *
1083: * @param enabled the new enabled state
1084: *
1085: * @exception SWTException <ul>
1086: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1087: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1088: * </ul>
1089: */
1090: public void setEnabled(boolean enabled) {
1091: checkWidget();
1092: if (enabled) {
1093: state &= ~DISABLED;
1094: OS.EnableMenuItem(handle, (short) 0);
1095: } else {
1096: state |= DISABLED;
1097: OS.DisableMenuItem(handle, (short) 0);
1098: }
1099: }
1100:
1101: /**
1102: * Sets the location of the receiver, which must be a popup,
1103: * to the point specified by the arguments which are relative
1104: * to the display.
1105: * <p>
1106: * Note that this is different from most widgets where the
1107: * location of the widget is relative to the parent.
1108: * </p><p>
1109: * Note that the platform window manager ultimately has control
1110: * over the location of popup menus.
1111: * </p>
1112: *
1113: * @param x the new x coordinate for the receiver
1114: * @param y the new y coordinate for the receiver
1115: *
1116: * @exception SWTException <ul>
1117: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1118: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1119: * </ul>
1120: */
1121: public void setLocation(int x, int y) {
1122: checkWidget();
1123: this .x = x;
1124: this .y = y;
1125: hasLocation = true;
1126: }
1127:
1128: /**
1129: * Sets the location of the receiver, which must be a popup,
1130: * to the point specified by the argument which is relative
1131: * to the display.
1132: * <p>
1133: * Note that this is different from most widgets where the
1134: * location of the widget is relative to the parent.
1135: * </p><p>
1136: * Note that the platform window manager ultimately has control
1137: * over the location of popup menus.
1138: * </p>
1139: *
1140: * @param location the new location for the receiver
1141: *
1142: * @exception IllegalArgumentException <ul>
1143: * <li>ERROR_NULL_ARGUMENT - if the point is null</li>
1144: * </ul>
1145: * @exception SWTException <ul>
1146: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1147: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1148: * </ul>
1149: *
1150: * @since 2.1
1151: */
1152: public void setLocation(Point location) {
1153: checkWidget();
1154: if (location == null)
1155: error(SWT.ERROR_NULL_ARGUMENT);
1156: setLocation(location.x, location.y);
1157: }
1158:
1159: /**
1160: * Marks the receiver as visible if the argument is <code>true</code>,
1161: * and marks it invisible otherwise.
1162: * <p>
1163: * If one of the receiver's ancestors is not visible or some
1164: * other condition makes the receiver not visible, marking
1165: * it visible may not actually cause it to be displayed.
1166: * </p>
1167: *
1168: * @param visible the new visibility state
1169: *
1170: * @exception SWTException <ul>
1171: * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1172: * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1173: * </ul>
1174: */
1175: public void setVisible(boolean visible) {
1176: checkWidget();
1177: if ((style & (SWT.BAR | SWT.DROP_DOWN)) != 0)
1178: return;
1179: if (visible) {
1180: display.addPopup(this );
1181: } else {
1182: display.removePopup(this );
1183: _setVisible(false);
1184: }
1185: }
1186:
1187: }
|