src/gui/widgets/qmenu_mac.mm
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 06 Jul 2010 15:10:48 +0300
changeset 30 5dc02b23752f
parent 18 2f34d5167611
permissions -rw-r--r--
Revision: 201025 Kit: 2010127

/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtGui module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights.  These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at qt-info@nokia.com.
**
**
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qmenu.h"
#include "qhash.h"
#include <qdebug.h>
#include "qapplication.h"
#include <private/qt_mac_p.h>
#include "qregexp.h"
#include "qmainwindow.h"
#include "qdockwidget.h"
#include "qtoolbar.h"
#include "qevent.h"
#include "qstyle.h"
#include "qwidgetaction.h"
#include "qmacnativewidget_mac.h"

#include <private/qapplication_p.h>
#include <private/qcocoaapplication_mac_p.h>
#include <private/qmenu_p.h>
#include <private/qmenubar_p.h>
#include <private/qcocoamenuloader_mac_p.h>
#include <private/qcocoamenu_mac_p.h>
#include <private/qt_cocoa_helpers_mac_p.h>
#include <Cocoa/Cocoa.h>

QT_BEGIN_NAMESPACE

/*****************************************************************************
  QMenu debug facilities
 *****************************************************************************/

/*****************************************************************************
  QMenu globals
 *****************************************************************************/
bool qt_mac_no_menubar_merge = false;
bool qt_mac_quit_menu_item_enabled = true;
int qt_mac_menus_open_count = 0;

static OSMenuRef qt_mac_create_menu(QWidget *w);

#ifndef QT_MAC_USE_COCOA
static uint qt_mac_menu_static_cmd_id = 'QT00';
const UInt32 kMenuCreatorQt = 'cute';
enum {
    kMenuPropertyQAction = 'QAcT',
    kMenuPropertyQWidget = 'QWId',
    kMenuPropertyCausedQWidget = 'QCAU',
    kMenuPropertyMergeMenu = 'QApP',
    kMenuPropertyMergeList = 'QAmL',
    kMenuPropertyWidgetActionWidget = 'QWid',
    kMenuPropertyWidgetMenu = 'QWMe',

    kHICommandAboutQt = 'AOQT',
    kHICommandCustomMerge = 'AQt0'
};
#endif

static struct {
    QPointer<QMenuBar> qmenubar;
    bool modal;
} qt_mac_current_menubar = { 0, false };




/*****************************************************************************
  Externals
 *****************************************************************************/
extern OSViewRef qt_mac_hiview_for(const QWidget *w); //qwidget_mac.cpp
extern HIViewRef qt_mac_hiview_for(OSWindowRef w); //qwidget_mac.cpp
extern IconRef qt_mac_create_iconref(const QPixmap &px); //qpixmap_mac.cpp
extern QWidget * mac_keyboard_grabber; //qwidget_mac.cpp
extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); //qapplication_xxx.cpp
RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp
void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp

/*****************************************************************************
  QMenu utility functions
 *****************************************************************************/
bool qt_mac_watchingAboutToShow(QMenu *menu)
{
    return menu && menu->receivers(SIGNAL(aboutToShow()));
}

static int qt_mac_CountMenuItems(OSMenuRef menu)
{
    if (menu) {
#ifndef QT_MAC_USE_COCOA
        int ret = 0;
        const int items = CountMenuItems(menu);
        for(int i = 0; i < items; i++) {
            MenuItemAttributes attr;
            if (GetMenuItemAttributes(menu, i+1, &attr) == noErr &&
               attr & kMenuItemAttrHidden)
                continue;
            ++ret;
        }
        return ret;
#else
        return [menu numberOfItems];
#endif
    }
    return 0;
}

static quint32 constructModifierMask(quint32 accel_key)
{
    quint32 ret = 0;
    const bool dontSwap = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta);
#ifndef QT_MAC_USE_COCOA
    if ((accel_key & Qt::ALT) == Qt::ALT)
        ret |= kMenuOptionModifier;
    if ((accel_key & Qt::SHIFT) == Qt::SHIFT)
        ret |= kMenuShiftModifier;
    if (dontSwap) {
        if ((accel_key & Qt::META) != Qt::META)
            ret |= kMenuNoCommandModifier;
        if ((accel_key & Qt::CTRL) == Qt::CTRL)
            ret |= kMenuControlModifier;
    } else {
        if ((accel_key & Qt::CTRL) != Qt::CTRL)
            ret |= kMenuNoCommandModifier;
        if ((accel_key & Qt::META) == Qt::META)
            ret |= kMenuControlModifier;
    }
#else
    if ((accel_key & Qt::CTRL) == Qt::CTRL)
        ret |= (dontSwap ? NSControlKeyMask : NSCommandKeyMask);
    if ((accel_key & Qt::META) == Qt::META)
        ret |= (dontSwap ? NSCommandKeyMask : NSControlKeyMask);
    if ((accel_key & Qt::ALT) == Qt::ALT)
        ret |= NSAlternateKeyMask;
    if ((accel_key & Qt::SHIFT) == Qt::SHIFT)
        ret |= NSShiftKeyMask;
#endif
    return ret;
}

static void cancelAllMenuTracking()
{
#ifdef QT_MAC_USE_COCOA
    QMacCocoaAutoReleasePool pool;
    NSMenu *mainMenu = [NSApp mainMenu];
    [mainMenu cancelTracking];
    for (NSMenuItem *item in [mainMenu itemArray]) {
        if ([item submenu]) {
            [[item submenu] cancelTracking];
        }
    }
#else
    CancelMenuTracking(AcquireRootMenu(), true, 0);
#endif
}

static bool actualMenuItemVisibility(const QMenuBarPrivate::QMacMenuBarPrivate *mbp,
                                     const QMacMenuAction *action)
{
    bool visible = action->action->isVisible();
    if (visible && action->action->text() == QString(QChar(0x14)))
        return false;
    if (visible && action->action->menu() && !action->action->menu()->actions().isEmpty() &&
        !qt_mac_CountMenuItems(action->action->menu()->macMenu(mbp->apple_menu)) &&
        !qt_mac_watchingAboutToShow(action->action->menu())) {
        return false;
    }
    return visible;
}

#ifndef QT_MAC_USE_COCOA
bool qt_mac_activate_action(MenuRef menu, uint command, QAction::ActionEvent action_e, bool by_accel)
{
    //fire event
    QMacMenuAction *action = 0;
    if (GetMenuCommandProperty(menu, command, kMenuCreatorQt, kMenuPropertyQAction, sizeof(action), 0, &action) != noErr) {
        QMenuMergeList *list = 0;
        GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeList,
                            sizeof(list), 0, &list);
        if (!list && qt_mac_current_menubar.qmenubar && qt_mac_current_menubar.qmenubar->isNativeMenuBar()) {
            MenuRef apple_menu = qt_mac_current_menubar.qmenubar->d_func()->mac_menubar->apple_menu;
            GetMenuItemProperty(apple_menu, 0, kMenuCreatorQt, kMenuPropertyMergeList, sizeof(list), 0, &list);
            if (list)
                menu = apple_menu;
        }
        if (list) {
            for(int i = 0; i < list->size(); ++i) {
                QMenuMergeItem item = list->at(i);
                if (item.command == command && item.action) {
                    action = item.action;
                    break;
                }
            }
        }
        if (!action)
            return false;
    }

    if (action_e == QAction::Trigger && by_accel && action->ignore_accel) //no, not a real accel (ie tab)
        return false;

    // Unhighlight the highlighted menu item before triggering the action to
    // prevent items from staying highlighted while a modal dialog is shown.
    // This also fixed the problem that parentless modal dialogs leave
    // the menu item highlighted (since the menu bar is cleared for these types of dialogs).
    if (action_e == QAction::Trigger)
        HiliteMenu(0);

    action->action->activate(action_e);

    //now walk up firing for each "caused" widget (like in the platform independent menu)
    QWidget *caused = 0;
    if (action_e == QAction::Hover && GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), 0, &caused) == noErr) {
        MenuRef caused_menu = 0;
        if (QMenu *qmenu2 = qobject_cast<QMenu*>(caused))
            caused_menu = qmenu2->macMenu();
        else if (QMenuBar *qmenubar2 = qobject_cast<QMenuBar*>(caused))
            caused_menu = qmenubar2->macMenu();
        else
            caused_menu = 0;
        while(caused_menu) {
            //fire
            QWidget *widget = 0;
            GetMenuItemProperty(caused_menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(widget), 0, &widget);
            if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
                action->action->showStatusText(widget);
                emit qmenu->hovered(action->action);
            } else if (QMenuBar *qmenubar = qobject_cast<QMenuBar*>(widget)) {
                action->action->showStatusText(widget);
                emit qmenubar->hovered(action->action);
                break; //nothing more..
            }

            //walk up
            if (GetMenuItemProperty(caused_menu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget,
                                    sizeof(caused), 0, &caused) != noErr)
                break;
            if (QMenu *qmenu2 = qobject_cast<QMenu*>(caused))
                caused_menu = qmenu2->macMenu();
            else if (QMenuBar *qmenubar2 = qobject_cast<QMenuBar*>(caused))
                caused_menu = qmenubar2->macMenu();
            else
                caused_menu = 0;
        }
    }
    return true;
}

//lookup a QMacMenuAction in a menu
static int qt_mac_menu_find_action(MenuRef menu, MenuCommand cmd)
{
    MenuItemIndex ret_idx;
    MenuRef ret_menu;
    if (GetIndMenuItemWithCommandID(menu, cmd, 1, &ret_menu, &ret_idx) == noErr) {
        if (ret_menu == menu)
            return (int)ret_idx;
    }
    return -1;
}
static int qt_mac_menu_find_action(MenuRef menu, QMacMenuAction *action)
{
    return qt_mac_menu_find_action(menu, action->command);
}

typedef QMultiHash<OSMenuRef, EventHandlerRef> EventHandlerHash;
Q_GLOBAL_STATIC(EventHandlerHash, menu_eventHandlers_hash)

static EventTypeSpec widget_in_menu_events[] = {
    { kEventClassMenu, kEventMenuMeasureItemWidth },
    { kEventClassMenu, kEventMenuMeasureItemHeight },
    { kEventClassMenu, kEventMenuDrawItem },
    { kEventClassMenu, kEventMenuCalculateSize }
};

static OSStatus qt_mac_widget_in_menu_eventHandler(EventHandlerCallRef er, EventRef event, void *)
{
    UInt32 ekind = GetEventKind(event);
    UInt32 eclass = GetEventClass(event);
    OSStatus result = eventNotHandledErr;
    switch (eclass) {
    case kEventClassMenu:
        switch (ekind) {
        default:
            break;
        case kEventMenuMeasureItemWidth: {
            MenuItemIndex item;
            GetEventParameter(event, kEventParamMenuItemIndex, typeMenuItemIndex,
                              0, sizeof(item), 0, &item);
            OSMenuRef menu;
            GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu);
            QWidget *widget;
            if (GetMenuItemProperty(menu, item, kMenuCreatorQt, kMenuPropertyWidgetActionWidget,
                                 sizeof(widget), 0, &widget) == noErr) {
                short width = short(widget->sizeHint().width());
                SetEventParameter(event, kEventParamMenuItemWidth, typeSInt16,
                                  sizeof(short), &width);
                result = noErr;
            }
            break; }
        case kEventMenuMeasureItemHeight: {
            MenuItemIndex item;
            GetEventParameter(event, kEventParamMenuItemIndex, typeMenuItemIndex,
                              0, sizeof(item), 0, &item);
            OSMenuRef menu;
            GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu);
            QWidget *widget;
            if (GetMenuItemProperty(menu, item, kMenuCreatorQt, kMenuPropertyWidgetActionWidget,
                                     sizeof(widget), 0, &widget) == noErr && widget) {
                short height = short(widget->sizeHint().height());
                SetEventParameter(event, kEventParamMenuItemHeight, typeSInt16,
                                  sizeof(short), &height);
                result = noErr;
            }
            break; }
        case kEventMenuDrawItem:
            result = noErr;
            break;
        case kEventMenuCalculateSize: {
            result = CallNextEventHandler(er, event);
            if (result == noErr) {
                OSMenuRef menu;
                GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu);
                HIViewRef content;
                HIMenuGetContentView(menu, kThemeMenuTypePullDown, &content);
                UInt16 count = CountMenuItems(menu);
                for (MenuItemIndex i = 1; i <= count; ++i) {
                    QWidget *widget;
                    if (GetMenuItemProperty(menu, i, kMenuCreatorQt, kMenuPropertyWidgetActionWidget,
                            sizeof(widget), 0, &widget) == noErr && widget) {
                        RgnHandle itemRgn = qt_mac_get_rgn();
                        GetControlRegion(content, i, itemRgn);

                        Rect bounds;
                        GetRegionBounds( itemRgn, &bounds );
                        qt_mac_dispose_rgn(itemRgn);
                        widget->setGeometry(bounds.left, bounds.top,
                                            bounds.right - bounds.left, bounds.bottom - bounds.top);
                    }
                }
            }
            break; }
        }
    }
    return result;
}

//handling of events for menurefs created by Qt..
static EventTypeSpec menu_events[] = {
    { kEventClassCommand, kEventCommandProcess },
    { kEventClassMenu, kEventMenuTargetItem },
    { kEventClassMenu, kEventMenuOpening },
    { kEventClassMenu, kEventMenuClosed }
};

// Special case for kEventMenuMatchKey, see qt_mac_create_menu below.
static EventTypeSpec menu_menu_events[] = {
    { kEventClassMenu, kEventMenuMatchKey }
};

OSStatus qt_mac_menu_event(EventHandlerCallRef er, EventRef event, void *)
{
    QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData);

    bool handled_event = true;
    UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event);
    switch(eclass) {
    case kEventClassCommand:
        if (ekind == kEventCommandProcess) {
            UInt32 context;
            GetEventParameter(event, kEventParamMenuContext, typeUInt32,
                              0, sizeof(context), 0, &context);
            HICommand cmd;
            GetEventParameter(event, kEventParamDirectObject, typeHICommand,
                              0, sizeof(cmd), 0, &cmd);
            if (!mac_keyboard_grabber && (context & kMenuContextKeyMatching)) {
                QMacMenuAction *action = 0;
                if (GetMenuCommandProperty(cmd.menu.menuRef, cmd.commandID, kMenuCreatorQt,
                                          kMenuPropertyQAction, sizeof(action), 0, &action) == noErr) {
                    QWidget *widget = 0;
                    if (qApp->activePopupWidget())
                        widget = (qApp->activePopupWidget()->focusWidget() ?
                                  qApp->activePopupWidget()->focusWidget() : qApp->activePopupWidget());
                    else if (QApplicationPrivate::focus_widget)
                        widget = QApplicationPrivate::focus_widget;
                    if (widget) {
                        int key = action->action->shortcut();
                        QKeyEvent accel_ev(QEvent::ShortcutOverride, (key & (~Qt::KeyboardModifierMask)),
                                           Qt::KeyboardModifiers(key & Qt::KeyboardModifierMask));
                        accel_ev.ignore();
                        qt_sendSpontaneousEvent(widget, &accel_ev);
                        if (accel_ev.isAccepted()) {
                            handled_event = false;
                            break;
                        }
                    }
                }
            }
            handled_event = qt_mac_activate_action(cmd.menu.menuRef, cmd.commandID,
                                                   QAction::Trigger, context & kMenuContextKeyMatching);
        }
        break;
    case kEventClassMenu: {
        MenuRef menu;
        GetEventParameter(event, kEventParamDirectObject, typeMenuRef, NULL, sizeof(menu), NULL, &menu);
        if (ekind == kEventMenuMatchKey) {
            // Don't activate any actions if we are showing a native modal dialog,
            // the key events should go to the dialog in this case.
            if (QApplicationPrivate::native_modal_dialog_active)
                return menuItemNotFoundErr;

             handled_event = false;
        } else if (ekind == kEventMenuTargetItem) {
            MenuCommand command;
            GetEventParameter(event, kEventParamMenuCommand, typeMenuCommand,
                              0, sizeof(command), 0, &command);
            handled_event = qt_mac_activate_action(menu, command, QAction::Hover, false);
        } else if (ekind == kEventMenuOpening || ekind == kEventMenuClosed) {
            qt_mac_menus_open_count += (ekind == kEventMenuOpening) ? 1 : -1;
            MenuRef mr;
            GetEventParameter(event, kEventParamDirectObject, typeMenuRef,
                              0, sizeof(mr), 0, &mr);

            QWidget *widget = 0;
            if (GetMenuItemProperty(mr, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(widget), 0, &widget) == noErr) {
                if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
                    handled_event = true;
                    if (ekind == kEventMenuOpening) {
                        emit qmenu->aboutToShow();

                        int merged = 0;
                        const QMenuPrivate::QMacMenuPrivate *mac_menu = qmenu->d_func()->mac_menu;
                        const int ActionItemsCount = mac_menu->actionItems.size();
                        for(int i = 0; i < ActionItemsCount; ++i) {
                            QMacMenuAction *action = mac_menu->actionItems.at(i);
                            if (action->action->isSeparator()) {
                                bool hide = false;
                                if(!action->action->isVisible()) {
                                    hide = true;
                                } else if (merged && merged == i) {
                                    hide = true;
                                } else {
                                    for(int l = i+1; l < mac_menu->actionItems.size(); ++l) {
                                        QMacMenuAction *action = mac_menu->actionItems.at(l);
                                        if (action->merged) {
                                            hide = true;
                                        } else if (action->action->isSeparator()) {
                                            if (hide)
                                                break;
                                        } else if (!action->merged) {
                                            hide = false;
                                            break;
                                        }
                                    }
                                }

                                const int index = qt_mac_menu_find_action(mr, action);
                                if (hide) {
                                    ++merged;
                                    ChangeMenuItemAttributes(mr, index, kMenuItemAttrHidden, 0);
                                } else {
                                    ChangeMenuItemAttributes(mr, index, 0, kMenuItemAttrHidden);
                                }
                            } else if (action->merged) {
                                ++merged;
                            }
                        }
                    } else {
                        emit qmenu->aboutToHide();
                    }
                }
            }
        } else {
            handled_event = false;
        }
        break; }
    default:
        handled_event = false;
        break;
    }
    if (!handled_event) //let the event go through
        return CallNextEventHandler(er, event);
    return noErr; //we eat the event
}
static EventHandlerRef mac_menu_event_handler = 0;
static EventHandlerUPP mac_menu_eventUPP = 0;
static void qt_mac_cleanup_menu_event()
{
    if (mac_menu_event_handler) {
        RemoveEventHandler(mac_menu_event_handler);
        mac_menu_event_handler = 0;
    }
    if (mac_menu_eventUPP) {
        DisposeEventHandlerUPP(mac_menu_eventUPP);
        mac_menu_eventUPP = 0;
    }
}
static inline void qt_mac_create_menu_event_handler()
{
    if (!mac_menu_event_handler) {
        mac_menu_eventUPP = NewEventHandlerUPP(qt_mac_menu_event);
        InstallEventHandler(GetApplicationEventTarget(), mac_menu_eventUPP,
                            GetEventTypeCount(menu_events), menu_events, 0,
                            &mac_menu_event_handler);
        qAddPostRoutine(qt_mac_cleanup_menu_event);
    }
}


//enabling of commands
static void qt_mac_command_set_enabled(MenuRef menu, UInt32 cmd, bool b)
{
    if (cmd == kHICommandQuit)
        qt_mac_quit_menu_item_enabled = b;

    if (b) {
        EnableMenuCommand(menu, cmd);
        if (MenuRef dock_menu = GetApplicationDockTileMenu())
            EnableMenuCommand(dock_menu, cmd);
    } else {
        DisableMenuCommand(menu, cmd);
        if (MenuRef dock_menu = GetApplicationDockTileMenu())
            DisableMenuCommand(dock_menu, cmd);
    }
}

static bool qt_mac_auto_apple_menu(MenuCommand cmd)
{
    return (cmd == kHICommandPreferences || cmd == kHICommandQuit);
}

static void qt_mac_get_accel(quint32 accel_key, quint32 *modif, quint32 *key) {
    if (modif) {
        *modif = constructModifierMask(accel_key);
    }

    accel_key &= ~(Qt::MODIFIER_MASK | Qt::UNICODE_ACCEL);
    if (key) {
        *key = 0;
        if (accel_key == Qt::Key_Return)
            *key = kMenuReturnGlyph;
        else if (accel_key == Qt::Key_Enter)
            *key = kMenuEnterGlyph;
        else if (accel_key == Qt::Key_Tab)
            *key = kMenuTabRightGlyph;
        else if (accel_key == Qt::Key_Backspace)
            *key = kMenuDeleteLeftGlyph;
        else if (accel_key == Qt::Key_Delete)
            *key = kMenuDeleteRightGlyph;
        else if (accel_key == Qt::Key_Escape)
            *key = kMenuEscapeGlyph;
        else if (accel_key == Qt::Key_PageUp)
            *key = kMenuPageUpGlyph;
        else if (accel_key == Qt::Key_PageDown)
            *key = kMenuPageDownGlyph;
        else if (accel_key == Qt::Key_Up)
            *key = kMenuUpArrowGlyph;
        else if (accel_key == Qt::Key_Down)
            *key = kMenuDownArrowGlyph;
        else if (accel_key == Qt::Key_Left)
            *key = kMenuLeftArrowGlyph;
        else if (accel_key == Qt::Key_Right)
            *key = kMenuRightArrowGlyph;
        else if (accel_key == Qt::Key_CapsLock)
            *key = kMenuCapsLockGlyph;
        else if (accel_key >= Qt::Key_F1 && accel_key <= Qt::Key_F15)
            *key = (accel_key - Qt::Key_F1) + kMenuF1Glyph;
        else if (accel_key == Qt::Key_Home)
            *key = kMenuNorthwestArrowGlyph;
        else if (accel_key == Qt::Key_End)
            *key = kMenuSoutheastArrowGlyph;
    }
}
#else // Cocoa
static inline void syncNSMenuItemVisiblity(NSMenuItem *menuItem, bool actionVisibility)
{
    [menuItem setHidden:NO];
    [menuItem setHidden:YES];
    [menuItem setHidden:!actionVisibility];
}

static inline void syncNSMenuItemEnabled(NSMenuItem *menuItem, bool enabled)
{
    [menuItem setEnabled:NO];
    [menuItem setEnabled:YES];
    [menuItem setEnabled:enabled];
}

static inline void syncMenuBarItemsVisiblity(const QMenuBarPrivate::QMacMenuBarPrivate *mac_menubar)
{
    const QList<QMacMenuAction *> &menubarActions = mac_menubar->actionItems;
    for (int i = 0; i < menubarActions.size(); ++i) {
        const QMacMenuAction *action = menubarActions.at(i);
        syncNSMenuItemVisiblity(action->menuItem, actualMenuItemVisibility(mac_menubar, action));
    }
}

static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader()
{
    return [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)];
}

static NSMenuItem *createNSMenuItem(const QString &title)
{
    NSMenuItem *item = [[NSMenuItem alloc] 
                         initWithTitle:qt_mac_QStringToNSString(title)
                         action:@selector(qtDispatcherToQAction:) keyEquivalent:@""];
    [item setTarget:nil];
    return item;
}
#endif



// helper that recurses into a menu structure and en/dis-ables them
void qt_mac_set_modal_state_helper_recursive(OSMenuRef menu, OSMenuRef merge, bool on)
{
#ifndef QT_MAC_USE_COCOA
    for (int i = 0; i < CountMenuItems(menu); i++) {
        OSMenuRef submenu;
        GetMenuItemHierarchicalMenu(menu, i+1, &submenu);
        if (submenu != merge) {
            if (submenu)
                qt_mac_set_modal_state_helper_recursive(submenu, merge, on);
            if (on)
                DisableMenuItem(submenu, 0);
            else
                EnableMenuItem(submenu, 0);
        }
    }
#else
    bool modalWindowOnScreen = qApp->activeModalWidget() != 0;
    for (NSMenuItem *item in [menu itemArray]) {
        OSMenuRef submenu = [item submenu];
        if (submenu != merge) {
            if (submenu)
                qt_mac_set_modal_state_helper_recursive(submenu, merge, on);
            if (!on) {
                // The item should follow what the QAction has.
                if ([item tag]) {
                    QAction *action = reinterpret_cast<QAction *>([item tag]);
                    syncNSMenuItemEnabled(item, action->isEnabled());
                } else {
                    syncNSMenuItemEnabled(item, YES);
                }
                // We sneak in some extra code here to handle a menu problem:
                // If there is no window on screen, we cannot set 'nil' as
                // menu item target, because then cocoa will disable the item
                // (guess it assumes that there will be no first responder to
                // catch the trigger anyway?) OTOH, If we have a modal window,
                // then setting the menu loader as target will make cocoa not
                // deliver the trigger because the loader is then seen as modally
                // shaddowed). So either way there are shortcomings. Instead, we
                // decide the target as late as possible:
                [item setTarget:modalWindowOnScreen ? nil : getMenuLoader()];
            } else {
                syncNSMenuItemEnabled(item, NO);
            }
        }
    }
#endif
}

//toggling of modal state
static void qt_mac_set_modal_state(OSMenuRef menu, bool on)
{
#ifndef QT_MAC_USE_COCOA
    OSMenuRef merge = 0;
    GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu,
            sizeof(merge), 0, &merge);

    qt_mac_set_modal_state_helper_recursive(menu, merge, on);

    UInt32 commands[] = { kHICommandQuit, kHICommandPreferences, kHICommandAbout, kHICommandAboutQt, 0 };
    for(int c = 0; commands[c]; c++) {
        bool enabled = !on;
        if (enabled) {
            QMacMenuAction *action = 0;
            GetMenuCommandProperty(menu, commands[c], kMenuCreatorQt, kMenuPropertyQAction,
                    sizeof(action), 0, &action);
            if (!action && merge) {
                GetMenuCommandProperty(merge, commands[c], kMenuCreatorQt, kMenuPropertyQAction,
                        sizeof(action), 0, &action);
                if (!action) {
                    QMenuMergeList *list = 0;
                    GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
                            sizeof(list), 0, &list);
                    for(int i = 0; list && i < list->size(); ++i) {
                        QMenuMergeItem item = list->at(i);
                        if (item.command == commands[c] && item.action) {
                            action = item.action;
                            break;
                        }
                    }
                }
            }

            if (!action) {
                if (commands[c] != kHICommandQuit)
                    enabled = false;
            } else {
                enabled = action->action ? action->action->isEnabled() : 0;
            }
        }
        qt_mac_command_set_enabled(menu, commands[c], enabled);
    }
#else
    OSMenuRef merge = QMenuPrivate::mergeMenuHash.value(menu);
    qt_mac_set_modal_state_helper_recursive(menu, merge, on);
    // I'm ignoring the special items now, since they should get handled via a syncAction()
#endif
}

bool qt_mac_menubar_is_open()
{
    return qt_mac_menus_open_count > 0;
}

QMacMenuAction::~QMacMenuAction()
{
#ifdef QT_MAC_USE_COCOA
    [menu release];
    // Update the menu item if this action still owns it. For some items
    // (like 'Quit') ownership will be transferred between all menu bars...
    if (action && action.data() == reinterpret_cast<QAction *>([menuItem tag])) {
        QAction::MenuRole role = action->menuRole();
        // Check if the item is owned by Qt, and should be hidden to keep it from causing
        // problems. Do it for everything but the quit menu item since that should always
        // be visible.
        if (role > QAction::ApplicationSpecificRole && role < QAction::QuitRole) {
            [menuItem setHidden:YES];
        } else if (role == QAction::TextHeuristicRole
                   && menuItem != [getMenuLoader() quitMenuItem]) {
            [menuItem setHidden:YES];
        }
        [menuItem setTag:nil];
    }
    [menuItem release];
#endif
}

#ifndef QT_MAC_USE_COCOA
static MenuCommand qt_mac_menu_merge_action(MenuRef merge, QMacMenuAction *action)
#else
static NSMenuItem *qt_mac_menu_merge_action(OSMenuRef merge, QMacMenuAction *action)
#endif
{
    if (qt_mac_no_menubar_merge || action->action->menu() || action->action->isSeparator()
            || action->action->menuRole() == QAction::NoRole)
        return 0;

    QString t = qt_mac_removeMnemonics(action->action->text().toLower());
    int st = t.lastIndexOf(QLatin1Char('\t'));
    if (st != -1)
        t.remove(st, t.length()-st);
    t.replace(QRegExp(QString::fromLatin1("\\.*$")), QLatin1String("")); //no ellipses
    //now the fun part
#ifndef QT_MAC_USE_COCOA
    MenuCommand ret = 0;
#else
    NSMenuItem *ret = 0;
    QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
#endif
    switch (action->action->menuRole()) {
    case QAction::NoRole:
        ret = 0;
        break;
    case QAction::ApplicationSpecificRole:
#ifndef QT_MAC_USE_COCOA
        {
            QMenuMergeList *list = 0;
            if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
                        sizeof(list), 0, &list) == noErr && list) {
                MenuCommand lastCustom = kHICommandCustomMerge;
                for(int i = 0; i < list->size(); ++i) {
                    QMenuMergeItem item = list->at(i);
                    if (item.command == lastCustom)
                        ++lastCustom;
                }
                ret = lastCustom;
            } else {
                // The list hasn't been created, so, must be the first one.
                ret = kHICommandCustomMerge;
            }
        }
#else
        ret = [loader appSpecificMenuItem];
#endif
        break;
    case QAction::AboutRole:
#ifndef QT_MAC_USE_COCOA
        ret = kHICommandAbout;
#else
        ret = [loader aboutMenuItem];
#endif
        break;
    case QAction::AboutQtRole:
#ifndef QT_MAC_USE_COCOA
        ret = kHICommandAboutQt;
#else
        ret = [loader aboutQtMenuItem];
#endif
        break;
    case QAction::QuitRole:
#ifndef QT_MAC_USE_COCOA
        ret = kHICommandQuit;
#else
        ret = [loader quitMenuItem];
#endif
        break;
    case QAction::PreferencesRole:
#ifndef QT_MAC_USE_COCOA
        ret = kHICommandPreferences;
#else
        ret = [loader preferencesMenuItem];
#endif
        break;
    case QAction::TextHeuristicRole: {
        QString aboutString = QMenuBar::tr("About").toLower();
        if (t.startsWith(aboutString) || t.endsWith(aboutString)) {
            if (t.indexOf(QRegExp(QString::fromLatin1("qt$"), Qt::CaseInsensitive)) == -1) {
#ifndef QT_MAC_USE_COCOA
                ret = kHICommandAbout;
#else
                ret = [loader aboutMenuItem];
#endif
            } else {
#ifndef QT_MAC_USE_COCOA
                ret = kHICommandAboutQt;
#else
                ret = [loader aboutQtMenuItem];
#endif
            }
        } else if (t.startsWith(QMenuBar::tr("Config").toLower())
                   || t.startsWith(QMenuBar::tr("Preference").toLower())
                   || t.startsWith(QMenuBar::tr("Options").toLower())
                   || t.startsWith(QMenuBar::tr("Setting").toLower())
                   || t.startsWith(QMenuBar::tr("Setup").toLower())) {
#ifndef QT_MAC_USE_COCOA
            ret = kHICommandPreferences;
#else
            ret = [loader preferencesMenuItem];
#endif
        } else if (t.startsWith(QMenuBar::tr("Quit").toLower())
                   || t.startsWith(QMenuBar::tr("Exit").toLower())) {
#ifndef QT_MAC_USE_COCOA
            ret = kHICommandQuit;
#else
            ret = [loader quitMenuItem];
#endif
        }
    }
        break;
    }

#ifndef QT_MAC_USE_COCOA
    QMenuMergeList *list = 0;
    if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
                           sizeof(list), 0, &list) == noErr && list) {
        for(int i = 0; i < list->size(); ++i) {
            QMenuMergeItem item = list->at(i);
            if (item.command == ret && item.action)
                return 0;
        }
    }

    QAction *cmd_action = 0;
    if (GetMenuCommandProperty(merge, ret, kMenuCreatorQt, kMenuPropertyQAction,
                              sizeof(cmd_action), 0, &cmd_action) == noErr && cmd_action)
        return 0; //already taken
#else
    if (QMenuMergeList *list = QMenuPrivate::mergeMenuItemsHash.value(merge)) {
        for(int i = 0; i < list->size(); ++i) {
            const QMenuMergeItem &item = list->at(i);
            if (item.menuItem == ret && item.action)
                return 0;
        }
    }

#endif
    return ret;
}

static QString qt_mac_menu_merge_text(QMacMenuAction *action)
{
    QString ret;
    extern QString qt_mac_applicationmenu_string(int type);
#ifdef QT_MAC_USE_COCOA
    QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
#endif
    if (action->action->menuRole() == QAction::ApplicationSpecificRole)
        ret = action->action->text();
#ifndef QT_MAC_USE_COCOA
    else if (action->command == kHICommandAbout)
        ret = qt_mac_applicationmenu_string(6).arg(qAppName());
    else if (action->command == kHICommandAboutQt)
        ret = QMenuBar::tr("About Qt");
    else if (action->command == kHICommandPreferences)
        ret = qt_mac_applicationmenu_string(4);
    else if (action->command == kHICommandQuit)
        ret = qt_mac_applicationmenu_string(5).arg(qAppName());
#else
    else if (action->menuItem == [loader aboutMenuItem]) {
        ret = qt_mac_applicationmenu_string(6).arg(qAppName());
    } else if (action->menuItem == [loader aboutQtMenuItem]) {
        if (action->action->text() == QString("About Qt"))
            ret = QMenuBar::tr("About Qt");
        else
            ret = action->action->text();
    } else if (action->menuItem == [loader preferencesMenuItem]) {
        ret = qt_mac_applicationmenu_string(4);
    } else if (action->menuItem == [loader quitMenuItem]) {
        ret = qt_mac_applicationmenu_string(5).arg(qAppName());
    }
#endif
    return ret;
}

static QKeySequence qt_mac_menu_merge_accel(QMacMenuAction *action)
{
    QKeySequence ret;
#ifdef QT_MAC_USE_COCOA
    QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
#endif
    if (action->action->menuRole() == QAction::ApplicationSpecificRole)
        ret = action->action->shortcut();
#ifndef QT_MAC_USE_COCOA
    else if (action->command == kHICommandPreferences)
        ret = QKeySequence(QKeySequence::Preferences);
    else if (action->command == kHICommandQuit)
        ret = QKeySequence(QKeySequence::Quit);
#else
    else if (action->menuItem == [loader preferencesMenuItem])
        ret = QKeySequence(QKeySequence::Preferences);
    else if (action->menuItem == [loader quitMenuItem])
        ret = QKeySequence(QKeySequence::Quit);
#endif
    return ret;
}

void Q_GUI_EXPORT qt_mac_set_menubar_icons(bool b)
{ QApplication::instance()->setAttribute(Qt::AA_DontShowIconsInMenus, !b); }
void Q_GUI_EXPORT qt_mac_set_native_menubar(bool b)
{  QApplication::instance()->setAttribute(Qt::AA_DontUseNativeMenuBar, !b); }
void Q_GUI_EXPORT qt_mac_set_menubar_merge(bool b) { qt_mac_no_menubar_merge = !b; }

/*****************************************************************************
  QMenu bindings
 *****************************************************************************/
QMenuPrivate::QMacMenuPrivate::QMacMenuPrivate() : menu(0)
{
}

QMenuPrivate::QMacMenuPrivate::~QMacMenuPrivate()
{
#ifndef QT_MAC_USE_COCOA
    for(QList<QMacMenuAction*>::Iterator it = actionItems.begin(); it != actionItems.end(); ++it) {
        QMacMenuAction *action = (*it);
        RemoveMenuCommandProperty(action->menu, action->command, kMenuCreatorQt, kMenuPropertyQAction);
        if (action->merged) {
            QMenuMergeList *list = 0;
            GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyMergeList,
                                sizeof(list), 0, &list);
            for(int i = 0; list && i < list->size(); ) {
                QMenuMergeItem item = list->at(i);
                if (item.action == action)
                    list->removeAt(i);
                else
                    ++i;
            }
        }
        delete action;
    }
    if (menu) {
        EventHandlerHash::iterator it = menu_eventHandlers_hash()->find(menu);
        while (it != menu_eventHandlers_hash()->end() && it.key() == menu) {
            RemoveEventHandler(it.value());
            ++it;
        }
        menu_eventHandlers_hash()->remove(menu);
        ReleaseMenu(menu);
    }
#else
    QMacCocoaAutoReleasePool pool;
    while (actionItems.size()) {
        QMacMenuAction *action = actionItems.takeFirst();
        if (QMenuMergeList *list = mergeMenuItemsHash.value(action->menu)) {
            int i = 0;
            while (i < list->size()) {
                const QMenuMergeItem &item = list->at(i);
                if (item.action == action)
                    list->removeAt(i);
                else
                    ++i;
            }
        }
        delete action;
    }
    mergeMenuHash.remove(menu);
    mergeMenuItemsHash.remove(menu);
    [menu release];
#endif
}

void
QMenuPrivate::QMacMenuPrivate::addAction(QAction *a, QMacMenuAction *before, QMenuPrivate *qmenu)
{
    QMacMenuAction *action = new QMacMenuAction;
    action->action = a;
    action->ignore_accel = 0;
    action->merged = 0;
    action->menu = 0;
#ifndef QT_MAC_USE_COCOA
    action->command = qt_mac_menu_static_cmd_id++;
#endif
    addAction(action, before, qmenu);
}

void
QMenuPrivate::QMacMenuPrivate::addAction(QMacMenuAction *action, QMacMenuAction *before, QMenuPrivate *qmenu)
{
#ifdef QT_MAC_USE_COCOA
    QMacCocoaAutoReleasePool pool;
    Q_UNUSED(qmenu);
#endif
    if (!action)
        return;
    int before_index = actionItems.indexOf(before);
    if (before_index < 0) {
        before = 0;
        before_index = actionItems.size();
    }
    actionItems.insert(before_index, action);

#ifndef QT_MAC_USE_COCOA
    int index = qt_mac_menu_find_action(menu, action);
#else
    [menu retain];
    [action->menu release];
#endif
    action->menu = menu;

    /* When the action is considered a mergable action it
       will stay that way, until removed.. */
    if (!qt_mac_no_menubar_merge) {
#ifndef QT_MAC_USE_COCOA
        MenuRef merge = 0;
        GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu,
                            sizeof(merge), 0, &merge);
#else
        OSMenuRef merge = QMenuPrivate::mergeMenuHash.value(menu);
#endif
        if (merge) {
#ifndef QT_MAC_USE_COCOA
            if (MenuCommand cmd = qt_mac_menu_merge_action(merge, action)) {
                action->merged = 1;
                action->menu = merge;
                action->command = cmd;
                if (qt_mac_auto_apple_menu(cmd))
                    index = 0; //no need

                QMenuMergeList *list = 0;
                if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
                                       sizeof(list), 0, &list) != noErr || !list) {
                    list = new QMenuMergeList;
                    SetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
                                        sizeof(list), &list);
                }
                list->append(QMenuMergeItem(cmd, action));
            }
#else
            if (NSMenuItem *cmd = qt_mac_menu_merge_action(merge, action)) {
                action->merged = 1;
                [merge retain];
                [action->menu release];
                action->menu = merge;
                [cmd retain];
                [cmd setAction:@selector(qtDispatcherToQAction:)];
                [cmd setTarget:nil];
                [action->menuItem release];
                action->menuItem = cmd;
                QMenuMergeList *list = QMenuPrivate::mergeMenuItemsHash.value(merge);
                if (!list) {
                    list = new QMenuMergeList;
                    QMenuPrivate::mergeMenuItemsHash.insert(merge, list);
                }
                list->append(QMenuMergeItem(cmd, action));
            }
#endif
        }
    }

#ifdef QT_MAC_USE_COCOA
    NSMenuItem *newItem = action->menuItem;
#endif
    if (
#ifndef QT_MAC_USE_COCOA
        index == -1
#else
        newItem == 0
#endif
       ) {
#ifndef QT_MAC_USE_COCOA
        index = before_index;
        MenuItemAttributes attr = kMenuItemAttrAutoRepeat;
#else
        newItem = createNSMenuItem(action->action->text());
        action->menuItem = newItem;
#endif
        if (before) {
#ifndef QT_MAC_USE_COCOA
            InsertMenuItemTextWithCFString(action->menu, 0, qMax(before_index, 0), attr, action->command);
#else
            [menu insertItem:newItem atIndex:qMax(before_index, 0)];
#endif
        } else {
#ifndef QT_MAC_USE_COCOA
            // Append the menu item to the menu. If it is a kHICommandAbout or a kHICommandAboutQt append
            // a separator also (to get a separator above "Preferences"), but make sure that we don't
            // add separators between two "about" items.

            // Build a set of all commands that could possibly be before the separator.
            QSet<MenuCommand> mergedItems;
            mergedItems.insert(kHICommandAbout);
            mergedItems.insert(kHICommandAboutQt);
            mergedItems.insert(kHICommandCustomMerge);

            QMenuMergeList *list = 0;
            if (GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyMergeList,
                        sizeof(list), 0, &list) == noErr && list) {
                for (int i = 0; i < list->size(); ++i) {
                    MenuCommand command = list->at(i).command;
                    if (command > kHICommandCustomMerge) {
                        mergedItems.insert(command);
                    }
                }
            }

            const int itemCount = CountMenuItems(action->menu);
            MenuItemAttributes testattr;
            GetMenuItemAttributes(action->menu, itemCount , &testattr);
            if (mergedItems.contains(action->command)
                 && (testattr & kMenuItemAttrSeparator)) {
                InsertMenuItemTextWithCFString(action->menu, 0, qMax(itemCount - 1, 0), attr, action->command);
                index = itemCount;
            } else {
                MenuItemIndex tmpIndex;
                AppendMenuItemTextWithCFString(action->menu, 0, attr, action->command, &tmpIndex);
                index = tmpIndex;
                if (mergedItems.contains(action->command))
                    AppendMenuItemTextWithCFString(action->menu, 0, kMenuItemAttrSeparator, 0, &tmpIndex);
            }
#else
            [menu addItem:newItem];
#endif
        }

        QWidget *widget = qmenu ? qmenu->widgetItems.value(action->action) : 0;
        if (widget) {
#ifndef QT_MAC_USE_COCOA
            ChangeMenuAttributes(action->menu, kMenuAttrDoNotCacheImage, 0);
            attr = kMenuItemAttrCustomDraw;
            SetMenuItemProperty(action->menu, index, kMenuCreatorQt, kMenuPropertyWidgetActionWidget,
                                sizeof(QWidget *), &widget);
            HIViewRef content;
            HIMenuGetContentView(action->menu, kThemeMenuTypePullDown, &content);

            EventHandlerRef eventHandlerRef;
            InstallMenuEventHandler(action->menu, qt_mac_widget_in_menu_eventHandler,
                                    GetEventTypeCount(widget_in_menu_events),
                                    widget_in_menu_events, 0, &eventHandlerRef);
            menu_eventHandlers_hash()->insert(action->menu, eventHandlerRef);

            QWidget *menuWidget = 0;
            GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyWidgetMenu,
                                sizeof(menuWidget), 0, &menuWidget);
            if(!menuWidget) {
                menuWidget = new QMacNativeWidget(content);
                SetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyWidgetMenu,
                                    sizeof(menuWidget), &menuWidget);
                menuWidget->show();
            }
            widget->setParent(menuWidget);
#else
            QMacNativeWidget *container = new QMacNativeWidget(0);
            container->resize(widget->sizeHint());
            widget->setAttribute(Qt::WA_LayoutUsesWidgetRect);
            widget->setParent(container);

            NSView *containerView = qt_mac_nativeview_for(container);
            [containerView setAutoresizesSubviews:YES];
            [containerView setAutoresizingMask:NSViewWidthSizable];
            [qt_mac_nativeview_for(widget) setAutoresizingMask:NSViewWidthSizable];

            [newItem setView:containerView];
            container->show();
#endif
            widget->show();
        }

    } else {
#ifndef QT_MAC_USE_COCOA
        qt_mac_command_set_enabled(action->menu, action->command, !QApplicationPrivate::modalState());
#else
        [newItem setEnabled:!QApplicationPrivate::modalState()];
#endif
    }
#ifndef QT_MAC_USE_COCOA
    SetMenuCommandProperty(action->menu, action->command, kMenuCreatorQt, kMenuPropertyQAction,
                           sizeof(action), &action);
#else
    [newItem setTag:long(static_cast<QAction *>(action->action))];
#endif
    syncAction(action);
}

// return an autoreleased string given a QKeySequence (currently only looks at the first one).
NSString *keySequenceToKeyEqivalent(const QKeySequence &accel)
{
    quint32 accel_key = (accel[0] & ~(Qt::MODIFIER_MASK | Qt::UNICODE_ACCEL));
    extern QChar qt_macSymbolForQtKey(int key); // qkeysequence.cpp
    QChar keyEquiv = qt_macSymbolForQtKey(accel_key);
    if (keyEquiv.isNull()) {
        if (accel_key >= Qt::Key_F1 && accel_key <= Qt::Key_F15)
            keyEquiv = (accel_key - Qt::Key_F1) + NSF1FunctionKey;
        else
            keyEquiv = unichar(QChar(accel_key).toLower().unicode());
    }
    return [NSString stringWithCharacters:&keyEquiv.unicode() length:1];
}

// return the cocoa modifier mask for the QKeySequence (currently only looks at the first one).
NSUInteger keySequenceModifierMask(const QKeySequence &accel)
{
    return constructModifierMask(accel[0]);
}

void
QMenuPrivate::QMacMenuPrivate::syncAction(QMacMenuAction *action)
{
    if (!action)
        return;

#ifndef QT_MAC_USE_COCOA
    const int index = qt_mac_menu_find_action(action->menu, action);
    if (index == -1)
        return;
#else
    NSMenuItem *item = action->menuItem;
    if (!item)
        return;
#endif

#ifndef QT_MAC_USE_COCOA
    if (!action->action->isVisible()) {
        ChangeMenuItemAttributes(action->menu, index, kMenuItemAttrHidden, 0);
        return;
    }
    ChangeMenuItemAttributes(action->menu, index, 0, kMenuItemAttrHidden);
#else
    QMacCocoaAutoReleasePool pool;
    NSMenu *menu = [item menu];
    bool actionVisible = action->action->isVisible();
    [item setHidden:!actionVisible];
    if (!actionVisible)
        return;
#endif

#ifndef QT_MAC_USE_COCOA
    if (action->action->isSeparator()) {
        ChangeMenuItemAttributes(action->menu, index, kMenuItemAttrSeparator, 0);
        return;
    }
    ChangeMenuItemAttributes(action->menu, index, 0, kMenuItemAttrSeparator);
#else
    int itemIndex = [menu indexOfItem:item];
    Q_ASSERT(itemIndex != -1);
    if (action->action->isSeparator()) {
        action->menuItem = [NSMenuItem separatorItem];
        [action->menuItem retain];
        [menu insertItem: action->menuItem atIndex:itemIndex];
        [menu removeItem:item];
        [item release];
        item = action->menuItem;
        return;
    } else if ([item isSeparatorItem]) {
        // I'm no longer a separator...
        action->menuItem = createNSMenuItem(action->action->text());
        [menu insertItem:action->menuItem atIndex:itemIndex];
        [menu removeItem:item];
        [item release];
        item = action->menuItem;
    }
#endif

    //find text (and accel)
    action->ignore_accel = 0;
    QString text = action->action->text();
    QKeySequence accel = action->action->shortcut();
    {
        int st = text.lastIndexOf(QLatin1Char('\t'));
        if (st != -1) {
            action->ignore_accel = 1;
            accel = QKeySequence(text.right(text.length()-(st+1)));
            text.remove(st, text.length()-st);
        }
    }
    {
        QString cmd_text = qt_mac_menu_merge_text(action);
        if (!cmd_text.isEmpty()) {
            text = cmd_text;
            accel = qt_mac_menu_merge_accel(action);
        }
    }
    // Show multiple key sequences as part of the menu text.
    if (accel.count() > 1)
        text += QLatin1String(" (") + accel.toString(QKeySequence::NativeText) + QLatin1String(")");

    QString finalString = qt_mac_removeMnemonics(text);

#ifndef QT_MAC_USE_COCOA
    MenuItemDataRec data;
    memset(&data, '\0', sizeof(data));

    //Carbon text
    data.whichData |= kMenuItemDataCFString;
    QCFString cfstring(finalString);  // Hold the reference to the end of the function.
    data.cfText = cfstring;

    // Carbon enabled
    data.whichData |= kMenuItemDataEnabled;
    data.enabled = action->action->isEnabled();
    // Carbon icon
    data.whichData |= kMenuItemDataIconHandle;
    if (!action->action->icon().isNull()
            && action->action->isIconVisibleInMenu()) {
        data.iconType = kMenuIconRefType;
        data.iconHandle = (Handle)qt_mac_create_iconref(action->action->icon().pixmap(16, QIcon::Normal));
    } else {
        data.iconType = kMenuNoIcon;
    }
    if (action->action->font().resolve()) { // Carbon font
        if (action->action->font().bold())
            data.style |= bold;
        if (action->action->font().underline())
            data.style |= underline;
        if (action->action->font().italic())
            data.style |= italic;
        if (data.style)
            data.whichData |= kMenuItemDataStyle;
        data.whichData |= kMenuItemDataFontID;
        data.fontID = action->action->font().macFontID();
    }
#else
    // Cocoa Font and title
    if (action->action->font().resolve()) {
        const QFont &actionFont = action->action->font();
        NSFont *customMenuFont = [NSFont fontWithName:qt_mac_QStringToNSString(actionFont.family())
                                  size:actionFont.pointSize()];
        NSArray *keys = [NSArray arrayWithObjects:NSFontAttributeName, nil];
        NSArray *objects = [NSArray arrayWithObjects:customMenuFont, nil];
        NSDictionary *attributes = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
        NSAttributedString *str = [[[NSAttributedString alloc] initWithString:qt_mac_QStringToNSString(finalString)
                                 attributes:attributes] autorelease];
       [item setAttributedTitle: str];
    } else {
        [item setTitle: qt_mac_QStringToNSString(finalString)];
    }

    if (action->action->menuRole() == QAction::AboutRole || action->action->menuRole() == QAction::QuitRole)
        [item setTitle:qt_mac_QStringToNSString(text)];
    else
        [item setTitle:qt_mac_QStringToNSString(qt_mac_removeMnemonics(text))];

    // Cocoa Enabled
    [item setEnabled: action->action->isEnabled()];

    // Cocoa icon
    NSImage *nsimage = 0;
    if (!action->action->icon().isNull() && action->action->isIconVisibleInMenu()) {
        nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(action->action->icon().pixmap(16, QIcon::Normal)));
    }
    [item setImage:nsimage];
    [nsimage release];
#endif

    if (action->action->menu()) { //submenu
#ifndef QT_MAC_USE_COCOA
        data.whichData |= kMenuItemDataSubmenuHandle;
        data.submenuHandle = action->action->menu()->macMenu();
        QWidget *caused = 0;
        GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(caused), 0, &caused);
        SetMenuItemProperty(data.submenuHandle, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), &caused);
#else
        NSMenu *subMenu  = static_cast<NSMenu *>(action->action->menu()->macMenu());
        if ([subMenu supermenu] && [subMenu supermenu] != [item menu]) {
            // The menu is already a sub-menu of another one. Cocoa will throw an exception,
            // in such cases. For the time being, a new QMenu with same set of actions is the
            // only workaround.
            action->action->setEnabled(false);
        } else {
            [item setSubmenu:subMenu];
        }
#endif
    } else { //respect some other items
#ifndef QT_MAC_USE_COCOA
        //shortcuts (say we are setting them all so that we can also clear them).
        data.whichData |= kMenuItemDataCmdKey;
        data.whichData |= kMenuItemDataCmdKeyModifiers;
        data.whichData |= kMenuItemDataCmdKeyGlyph;
        if (accel.count() == 1) {
            qt_mac_get_accel(accel[0], (quint32*)&data.cmdKeyModifiers, (quint32*)&data.cmdKeyGlyph);
            if (data.cmdKeyGlyph == 0)
                data.cmdKey = (UniChar)accel[0];
        }
#else
        [item setSubmenu:0];
        // No key equivalent set for multiple key QKeySequence.
        if (accel.count() == 1) {
            [item setKeyEquivalent:keySequenceToKeyEqivalent(accel)];
            [item setKeyEquivalentModifierMask:keySequenceModifierMask(accel)];
        } else {
            [item setKeyEquivalent:@""];
            [item setKeyEquivalentModifierMask:NSCommandKeyMask];
        }
#endif
    }
#ifndef QT_MAC_USE_COCOA
    //mark glyph
    data.whichData |= kMenuItemDataMark;
    if (action->action->isChecked()) {
#if 0
        if (action->action->actionGroup() &&
           action->action->actionGroup()->isExclusive())
            data.mark = diamondMark;
        else
#endif
            data.mark = checkMark;
    } else {
        data.mark = noMark;
    }

    //actually set it
    SetMenuItemData(action->menu, action->command, true, &data);

    // Free up memory
    if (data.iconHandle)
        ReleaseIconRef(IconRef(data.iconHandle));
#else
    //mark glyph
    [item setState:action->action->isChecked() ?  NSOnState : NSOffState];
#endif
}

void
QMenuPrivate::QMacMenuPrivate::removeAction(QMacMenuAction *action)
{
    if (!action)
        return;
#ifndef QT_MAC_USE_COCOA
    if (action->command == kHICommandQuit || action->command == kHICommandPreferences)
        qt_mac_command_set_enabled(action->menu, action->command, false);
    else
        DeleteMenuItem(action->menu, qt_mac_menu_find_action(action->menu, action));
#else
    QMacCocoaAutoReleasePool pool;
    if (action->merged) {
        if (reinterpret_cast<QAction *>([action->menuItem tag]) == action->action) {
            QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
            [action->menuItem setEnabled:false];
            if (action->menuItem != [loader quitMenuItem]
                && action->menuItem != [loader preferencesMenuItem]) {
                [[action->menuItem menu] removeItem:action->menuItem];
            }
        }
    } else {
        [[action->menuItem menu] removeItem:action->menuItem];
    }
#endif
    actionItems.removeAll(action);
}

OSMenuRef
QMenuPrivate::macMenu(OSMenuRef merge)
{
    Q_UNUSED(merge);
    Q_Q(QMenu);
    if (mac_menu && mac_menu->menu)
        return mac_menu->menu;
    if (!mac_menu)
        mac_menu = new QMacMenuPrivate;
    mac_menu->menu = qt_mac_create_menu(q);
    if (merge) {
#ifndef QT_MAC_USE_COCOA
        SetMenuItemProperty(mac_menu->menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu, sizeof(merge), &merge);
#else
        mergeMenuHash.insert(mac_menu->menu, merge);
#endif
    }
    QList<QAction*> items = q->actions();
    for(int i = 0; i < items.count(); i++)
        mac_menu->addAction(items[i], 0, this);
    syncSeparatorsCollapsible(collapsibleSeparators);
    return mac_menu->menu;
}

/*!
  \internal
*/
void
QMenuPrivate::syncSeparatorsCollapsible(bool collapse)
{
#ifndef QT_MAC_USE_COCOA
    if (collapse)
        ChangeMenuAttributes(mac_menu->menu, kMenuAttrCondenseSeparators, 0);
    else
        ChangeMenuAttributes(mac_menu->menu, 0, kMenuAttrCondenseSeparators);
#else
    qt_mac_menu_collapseSeparators(mac_menu->menu, collapse);
#endif
}



/*!
  \internal
*/
void QMenuPrivate::setMacMenuEnabled(bool enable)
{
    if (!macMenu(0))
        return;

    QMacCocoaAutoReleasePool pool;
    if (enable) {
        for (int i = 0; i < mac_menu->actionItems.count(); ++i) {
            QMacMenuAction *menuItem = mac_menu->actionItems.at(i);
            if (menuItem && menuItem->action && menuItem->action->isEnabled()) {
#ifndef QT_MAC_USE_COCOA
                // Only enable those items which contains an enabled QAction.
                // i == 0 -> the menu itself, hence i + 1 for items.
                EnableMenuItem(mac_menu->menu, i + 1);
#else
                [menuItem->menuItem setEnabled:true];
#endif
            }
        }
    } else {
#ifndef QT_MAC_USE_COCOA
        DisableAllMenuItems(mac_menu->menu);
#else
        NSMenu *menu = mac_menu->menu;
        for (NSMenuItem *item in [menu itemArray]) {
            [item setEnabled:false];
        }
#endif
    }
}

/*!
    \internal

    This function will return the OSMenuRef used to create the native menu bar
    bindings.

    If Qt is built against Carbon, the OSMenuRef is a MenuRef that can be used
    with Carbon's Menu Manager API.

    If Qt is built against Cocoa, the OSMenuRef is a NSMenu pointer.

    \warning This function is not portable.

    \sa QMenuBar::macMenu()
*/
OSMenuRef QMenu::macMenu(OSMenuRef merge) { return d_func()->macMenu(merge); }

/*****************************************************************************
  QMenuBar bindings
 *****************************************************************************/
typedef QHash<QWidget *, QMenuBar *> MenuBarHash;
Q_GLOBAL_STATIC(MenuBarHash, menubars)
static QMenuBar *fallback = 0;
QMenuBarPrivate::QMacMenuBarPrivate::QMacMenuBarPrivate() : menu(0), apple_menu(0)
{
}

QMenuBarPrivate::QMacMenuBarPrivate::~QMacMenuBarPrivate()
{
    for(QList<QMacMenuAction*>::Iterator it = actionItems.begin(); it != actionItems.end(); ++it)
        delete (*it);
#ifndef QT_MAC_USE_COCOA
    if (apple_menu) {
        QMenuMergeList *list = 0;
        GetMenuItemProperty(apple_menu, 0, kMenuCreatorQt, kMenuPropertyMergeList,
                            sizeof(list), 0, &list);
        if (list) {
            RemoveMenuItemProperty(apple_menu, 0, kMenuCreatorQt, kMenuPropertyMergeList);
            delete list;
        }
        ReleaseMenu(apple_menu);
    }
    if (menu)
        ReleaseMenu(menu);
#else
    [apple_menu release];
    [menu release];
#endif
}

void
QMenuBarPrivate::QMacMenuBarPrivate::addAction(QAction *a, QMacMenuAction *before)
{
    if (a->isSeparator() || !menu)
        return;
    QMacMenuAction *action = new QMacMenuAction;
    action->action = a;
    action->ignore_accel = 1;
#ifndef QT_MAC_USE_COCOA
    action->command = qt_mac_menu_static_cmd_id++;
#endif
    addAction(action, before);
}

void
QMenuBarPrivate::QMacMenuBarPrivate::addAction(QMacMenuAction *action, QMacMenuAction *before)
{
    if (!action || !menu)
        return;

    int before_index = actionItems.indexOf(before);
    if (before_index < 0) {
        before = 0;
        before_index = actionItems.size();
    }
    actionItems.insert(before_index, action);

    MenuItemIndex index = actionItems.size()-1;

    action->menu = menu;
#ifdef QT_MAC_USE_COCOA
    QMacCocoaAutoReleasePool pool;
    [action->menu retain];
    NSMenuItem *newItem = createNSMenuItem(action->action->text());
    action->menuItem = newItem;
#endif
    if (before) {
#ifndef QT_MAC_USE_COCOA
        InsertMenuItemTextWithCFString(action->menu, 0, qMax(1, before_index+1), 0, action->command);
#else
        [menu insertItem:newItem atIndex:qMax(1, before_index + 1)];
#endif
        index = before_index;
    } else {
#ifndef QT_MAC_USE_COCOA
        AppendMenuItemTextWithCFString(action->menu, 0, 0, action->command, &index);
#else
        [menu addItem:newItem];
#endif
    }
#ifndef QT_MAC_USE_COCOA
    SetMenuItemProperty(action->menu, index, kMenuCreatorQt, kMenuPropertyQAction, sizeof(action),
                        &action);
#else
    [newItem setTag:long(static_cast<QAction *>(action->action))];
#endif
    syncAction(action);
}

void
QMenuBarPrivate::QMacMenuBarPrivate::syncAction(QMacMenuAction *action)
{
    if (!action || !menu)
        return;
#ifndef QT_MAC_USE_COCOA
    const int index = qt_mac_menu_find_action(action->menu, action);
#else
    QMacCocoaAutoReleasePool pool;
    NSMenuItem *item = action->menuItem;
#endif

    OSMenuRef submenu = 0;
    bool release_submenu = false;
    if (action->action->menu()) {
        if ((submenu = action->action->menu()->macMenu(apple_menu))) {
#ifndef QT_MAC_USE_COCOA
            QWidget *caused = 0;
            GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(caused), 0, &caused);
            SetMenuItemProperty(submenu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), &caused);
#else
            if ([submenu supermenu] && [submenu supermenu] != [item menu])
                return;
            else
                [item setSubmenu:submenu];
#endif
        }
#ifndef QT_MAC_USE_COCOA
    } else { // create a submenu to act as menu
        release_submenu = true;
        CreateNewMenu(0, 0, &submenu);
#endif
    }

    if (submenu) {
        bool visible = actualMenuItemVisibility(this, action);
#ifndef QT_MAC_USE_COCOA
        SetMenuItemHierarchicalMenu(action->menu, index, submenu);
        SetMenuTitleWithCFString(submenu, QCFString(qt_mac_removeMnemonics(action->action->text())));
        if (visible)
            ChangeMenuAttributes(submenu, 0, kMenuAttrHidden);
        else
            ChangeMenuAttributes(submenu, kMenuAttrHidden, 0);
#else
        [item setSubmenu: submenu];
        [submenu setTitle:qt_mac_QStringToNSString(qt_mac_removeMnemonics(action->action->text()))];
        syncNSMenuItemVisiblity(item, visible);
#endif
        if (release_submenu) { //no pointers to it
#ifndef QT_MAC_USE_COCOA
            ReleaseMenu(submenu);
#else
            [submenu release];
#endif
        }
    } else {
        qWarning("QMenu: No OSMenuRef created for popup menu");
    }
}

void
QMenuBarPrivate::QMacMenuBarPrivate::removeAction(QMacMenuAction *action)
{
    if (!action || !menu)
        return;
#ifndef QT_MAC_USE_COCOA
    DeleteMenuItem(action->menu, qt_mac_menu_find_action(action->menu, action));
#else
    QMacCocoaAutoReleasePool pool;
    [action->menu removeItem:action->menuItem];
#endif
    actionItems.removeAll(action);
}

bool QMenuBarPrivate::macWidgetHasNativeMenubar(QWidget *widget)
{
    // This function is different from q->isNativeMenuBar(), as
    // it returns true only if a native menu bar is actually
    // _created_.
    if (!widget)
        return false;
    return menubars()->contains(widget->window());
}

void
QMenuBarPrivate::macCreateMenuBar(QWidget *parent)
{
    Q_Q(QMenuBar);
    static int dontUseNativeMenuBar = -1;
    // We call the isNativeMenuBar function here
    // because that will make sure that local overrides
    // are dealt with correctly. q->isNativeMenuBar() will, if not
    // overridden, depend on the attribute Qt::AA_DontUseNativeMenuBar:
    bool qt_mac_no_native_menubar = !q->isNativeMenuBar();
    if (qt_mac_no_native_menubar == false && dontUseNativeMenuBar < 0) {
        // The menubar is set to be native. Let's check (one time only
        // for all menubars) if this is OK with the rest of the environment.
        // As a result, Qt::AA_DontUseNativeMenuBar is set. NB: the application
        // might still choose to not respect, or change, this flag.
        bool isPlugin = QApplication::testAttribute(Qt::AA_MacPluginApplication);
        bool environmentSaysNo = !qgetenv("QT_MAC_NO_NATIVE_MENUBAR").isEmpty();
        dontUseNativeMenuBar = isPlugin || environmentSaysNo;
        QApplication::instance()->setAttribute(Qt::AA_DontUseNativeMenuBar, dontUseNativeMenuBar);
        qt_mac_no_native_menubar = !q->isNativeMenuBar();
    }
    if (qt_mac_no_native_menubar == false) {
        // INVARIANT: Use native menubar.
        extern void qt_event_request_menubarupdate(); //qapplication_mac.cpp
        qt_event_request_menubarupdate();
        if (!parent && !fallback) {
            fallback = q;
            mac_menubar = new QMacMenuBarPrivate;
        } else if (parent && parent->isWindow()) {
            menubars()->insert(q->window(), q);
            mac_menubar = new QMacMenuBarPrivate;
        }
    }
}

void QMenuBarPrivate::macDestroyMenuBar()
{
    Q_Q(QMenuBar);
    QMacCocoaAutoReleasePool pool;
    if (fallback == q)
        fallback = 0;
    delete mac_menubar;
    QWidget *tlw = q->window();
    menubars()->remove(tlw);
    mac_menubar = 0;

    if (!qt_mac_current_menubar.qmenubar || qt_mac_current_menubar.qmenubar == q) {
#ifdef QT_MAC_USE_COCOA
        QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
        [loader removeActionsFromAppMenu];
#else
        cancelAllMenuTracking();
#endif
        extern void qt_event_request_menubarupdate(); //qapplication_mac.cpp
        qt_event_request_menubarupdate();
    }
}

OSMenuRef QMenuBarPrivate::macMenu()
{
    Q_Q(QMenuBar);
    if (!q->isNativeMenuBar() || !mac_menubar) {
        return 0;
    } else if (!mac_menubar->menu) {
        mac_menubar->menu = qt_mac_create_menu(q);
        ProcessSerialNumber mine, front;
        if (GetCurrentProcess(&mine) == noErr && GetFrontProcess(&front) == noErr) {
            if (!qt_mac_no_menubar_merge && !mac_menubar->apple_menu) {
                mac_menubar->apple_menu = qt_mac_create_menu(q);
#ifndef QT_MAC_USE_COCOA
                MenuItemIndex index;
                AppendMenuItemTextWithCFString(mac_menubar->menu, 0, 0, 0, &index);

                SetMenuTitleWithCFString(mac_menubar->apple_menu, QCFString(QString(QChar(0x14))));
                SetMenuItemHierarchicalMenu(mac_menubar->menu, index, mac_menubar->apple_menu);
                SetMenuItemProperty(mac_menubar->apple_menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(q), &q);
#else
                [mac_menubar->apple_menu setTitle:qt_mac_QStringToNSString(QString(QChar(0x14)))];
                NSMenuItem *apple_menuItem = [[NSMenuItem alloc] init];
                [apple_menuItem setSubmenu:mac_menubar->menu];
                [mac_menubar->apple_menu addItem:apple_menuItem];
                [apple_menuItem release];
#endif
            }
            if (mac_menubar->apple_menu) {
#ifndef QT_MAC_USE_COCOA
                SetMenuItemProperty(mac_menubar->menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu,
                                    sizeof(mac_menubar->apple_menu), &mac_menubar->apple_menu);
#else
                QMenuPrivate::mergeMenuHash.insert(mac_menubar->menu, mac_menubar->apple_menu);
#endif
            }
            QList<QAction*> items = q->actions();
            for(int i = 0; i < items.count(); i++)
                mac_menubar->addAction(items[i]);
        }
    }
    return mac_menubar->menu;
}

/*!
    \internal

    This function will return the OSMenuRef used to create the native menu bar
    bindings. This OSMenuRef is then set as the root menu for the Menu
    Manager.

    \warning This function is not portable.

    \sa QMenu::macMenu()
*/
OSMenuRef QMenuBar::macMenu() { return d_func()->macMenu(); }

/* !
    \internal
    Ancestor function that crosses windows (QWidget::isAncestorOf
    only considers widgets within the same window).
*/
static bool qt_mac_is_ancestor(QWidget* possibleAncestor, QWidget *child)
{
    if (!possibleAncestor)
        return false;

    QWidget * current = child->parentWidget();
    while (current != 0) {
        if (current == possibleAncestor)
            return true;
        current = current->parentWidget();
    }
    return false;
}

/* !
    \internal
    Returns true if the entries of menuBar should be disabled,
    based on the modality type of modalWidget.
*/
static bool qt_mac_should_disable_menu(QMenuBar *menuBar)
{
    QWidget *modalWidget = qApp->activeModalWidget();
    if (!modalWidget)
        return false;

    if (menuBar && menuBar == menubars()->value(modalWidget))
        // The menu bar is owned by the modal widget.
        // In that case we should enable it:
        return false;

    // When there is an application modal window on screen, the entries of
    // the menubar should be disabled. The exception in Qt is that if the
    // modal window is the only window on screen, then we enable the menu bar.
    QWidget *w = modalWidget;
    QWidgetList topLevelWidgets = QApplication::topLevelWidgets();
    while (w) {
        if (w->isVisible() && w->windowModality() == Qt::ApplicationModal) {
            for (int i=0; i<topLevelWidgets.size(); ++i) {
                QWidget *top = topLevelWidgets.at(i);
                if (w != top && top->isVisible()) {
                    // INVARIANT: we found another visible window
                    // on screen other than our modalWidget. We therefore
                    // disable the menu bar to follow normal modality logic:
                    return true;
                }
            }
            // INVARIANT: We have only one window on screen that happends
            // to be application modal. We choose to enable the menu bar
            // in that case to e.g. enable the quit menu item.
            return false;
        }
        w = w->parentWidget();
    }

    // INVARIANT: modalWidget is window modal. Disable menu entries
    // if the menu bar belongs to an ancestor of modalWidget. If menuBar
    // is nil, we understand it as the default menu bar set by the nib:
    return menuBar ? qt_mac_is_ancestor(menuBar->parentWidget(), modalWidget) : false;
}

static QWidget *findWindowThatShouldDisplayMenubar()
{
    QWidget *w = qApp->activeWindow();
    if (!w) {
        // We have no active window on screen. Try to
        // find a window from the list of top levels:
        QWidgetList tlws = QApplication::topLevelWidgets();
        for(int i = 0; i < tlws.size(); ++i) {
            QWidget *tlw = tlws.at(i);
            if ((tlw->isVisible() && tlw->windowType() != Qt::Tool &&
                tlw->windowType() != Qt::Popup)) {
                w = tlw;
                break;
            }
        }
    }
    return w;
}

static QMenuBar *findMenubarForWindow(QWidget *w)
{
    QMenuBar *mb = 0;
    if (w) {
        mb = menubars()->value(w);
#ifndef QT_NO_MAINWINDOW
        QDockWidget *dw = qobject_cast<QDockWidget *>(w);
        if (!mb && dw) {
            QMainWindow *mw = qobject_cast<QMainWindow *>(dw->parentWidget());
            if (mw && (mb = menubars()->value(mw)))
                w = mw;
        }
#endif
        while(w && !mb)
            mb = menubars()->value((w = w->parentWidget()));
    }

    if (!mb) {
        // We could not find a menu bar for the window. Lets
        // check if we have a global (parentless) menu bar instead:
        mb = fallback;
    }

    return mb;
}

void qt_mac_clear_menubar()
{
    if (QApplication::testAttribute(Qt::AA_MacPluginApplication))
        return;

#ifndef QT_MAC_USE_COCOA
    MenuRef clear_menu = 0;
    if (CreateNewMenu(0, 0, &clear_menu) == noErr) {
        SetRootMenu(clear_menu);
        ReleaseMenu(clear_menu);
    } else {
        qWarning("QMenu: Internal error at %s:%d", __FILE__, __LINE__);
    }
    ClearMenuBar();
    qt_mac_command_set_enabled(0, kHICommandPreferences, false);
    InvalMenuBar();
#else
    QMacCocoaAutoReleasePool pool;
    QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
    NSMenu *menu = [loader menu];
    [loader ensureAppMenuInMenu:menu];
    [NSApp setMainMenu:menu];
    const bool modal = qt_mac_should_disable_menu(0);
    if (qt_mac_current_menubar.qmenubar || modal != qt_mac_current_menubar.modal)
        qt_mac_set_modal_state(menu, modal);
    qt_mac_current_menubar.qmenubar = 0;
    qt_mac_current_menubar.modal = modal;
#endif
}

/*!
  \internal

  This function will update the current menu bar and set it as the
  active menu bar in the Menu Manager.

  \warning This function is not portable.

  \sa QMenu::macMenu(), QMenuBar::macMenu()
*/
bool QMenuBar::macUpdateMenuBar()
{
#ifdef QT_MAC_USE_COCOA
    QMacCocoaAutoReleasePool pool;
    if (!qt_cocoaPostMessage(getMenuLoader(), @selector(qtUpdateMenubar)))
        return QMenuBarPrivate::macUpdateMenuBarImmediatly();
    return true;
#else
    return QMenuBarPrivate::macUpdateMenuBarImmediatly();
#endif
}

bool QMenuBarPrivate::macUpdateMenuBarImmediatly()
{
    bool ret = false;
    cancelAllMenuTracking();
    QWidget *w = findWindowThatShouldDisplayMenubar();
    QMenuBar *mb = findMenubarForWindow(w);
    extern bool qt_mac_app_fullscreen; //qapplication_mac.mm

    // We need to see if we are in full screen mode, if so we need to
    // switch the full screen mode to be able to show or hide the menubar.
    if(w && mb) {
        // This case means we are creating a menubar, check if full screen
        if(w->isFullScreen()) {
            // Ok, switch to showing the menubar when hovering over it.
            SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);
            qt_mac_app_fullscreen = true;
        }
    } else if(w) {
        // Removing a menubar
        if(w->isFullScreen()) {
            // Ok, switch to not showing the menubar when hovering on it
            SetSystemUIMode(kUIModeAllHidden, 0);
            qt_mac_app_fullscreen = true;
        }
    }

    if (mb && mb->isNativeMenuBar()) {
        bool modal = QApplicationPrivate::modalState();
#ifdef QT_MAC_USE_COCOA
        QMacCocoaAutoReleasePool pool;
#endif
        if (OSMenuRef menu = mb->macMenu()) {
#ifndef QT_MAC_USE_COCOA
            SetRootMenu(menu);
#else
            QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
            [loader ensureAppMenuInMenu:menu];
            [NSApp setMainMenu:menu];
            syncMenuBarItemsVisiblity(mb->d_func()->mac_menubar);

            if (OSMenuRef tmpMerge = QMenuPrivate::mergeMenuHash.value(menu)) {
                if (QMenuMergeList *mergeList
                        = QMenuPrivate::mergeMenuItemsHash.value(tmpMerge)) {
                    const int mergeListSize = mergeList->size();

                    for (int i = 0; i < mergeListSize; ++i) {
                        const QMenuMergeItem &mergeItem = mergeList->at(i);
                        // Ideally we would call QMenuPrivate::syncAction, but that requires finding
                        // the original QMen and likely doing more work than we need.
                        // For example, enabled is handled below.
                        [mergeItem.menuItem setTag:reinterpret_cast<long>(
                                                    static_cast<QAction *>(mergeItem.action->action))];
                        [mergeItem.menuItem setHidden:!(mergeItem.action->action->isVisible())];
                    }
                }
            }
#endif
            // Check if menu is modally shaddowed and should  be disabled:
            modal = qt_mac_should_disable_menu(mb);
            if (mb != qt_mac_current_menubar.qmenubar || modal != qt_mac_current_menubar.modal)
                qt_mac_set_modal_state(menu, modal);
        }
        qt_mac_current_menubar.qmenubar = mb;
        qt_mac_current_menubar.modal = modal;
        ret = true;
    } else if (qt_mac_current_menubar.qmenubar && qt_mac_current_menubar.qmenubar->isNativeMenuBar()) {
        // INVARIANT: The currently active menu bar (if any) is not native. But we do have a
        // native menu bar from before. So we need to decide whether or not is should be enabled:
        const bool modal = qt_mac_should_disable_menu(qt_mac_current_menubar.qmenubar);
        if (modal != qt_mac_current_menubar.modal) {
            ret = true;
            if (OSMenuRef menu = qt_mac_current_menubar.qmenubar->macMenu()) {
#ifndef QT_MAC_USE_COCOA
                SetRootMenu(menu);
#else
                QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
                [loader ensureAppMenuInMenu:menu];
                [NSApp setMainMenu:menu];
                syncMenuBarItemsVisiblity(qt_mac_current_menubar.qmenubar->d_func()->mac_menubar);
#endif
                qt_mac_set_modal_state(menu, modal);
            }
            qt_mac_current_menubar.modal = modal;
        }
    }

    if (!ret) {
        qt_mac_clear_menubar();
    }
    return ret;
}

QHash<OSMenuRef, OSMenuRef> QMenuPrivate::mergeMenuHash;
QHash<OSMenuRef, QMenuMergeList*> QMenuPrivate::mergeMenuItemsHash;

bool QMenuPrivate::QMacMenuPrivate::merged(const QAction *action) const
{
#ifndef QT_MAC_USE_COCOA
    MenuRef merge = 0;
    GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu,
            sizeof(merge), 0, &merge);
    if (merge) {
        QMenuMergeList *list = 0;
        if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
                    sizeof(list), 0, &list) == noErr && list) {
            for(int i = 0; i < list->size(); ++i) {
                QMenuMergeItem item = list->at(i);
                if (item.action->action == action)
                    return true;
            }
        }
    }
#else
    if (OSMenuRef merge = mergeMenuHash.value(menu)) {
        if (QMenuMergeList *list = mergeMenuItemsHash.value(merge)) {
            for(int i = 0; i < list->size(); ++i) {
                const QMenuMergeItem &item = list->at(i);
                if (item.action->action == action)
                    return true;
            }
        }
    }
#endif
    return false;
}

//creation of the OSMenuRef
static OSMenuRef qt_mac_create_menu(QWidget *w)
{
    OSMenuRef ret;
#ifndef QT_MAC_USE_COCOA
    ret = 0;
    if (CreateNewMenu(0, 0, &ret) == noErr) {
        qt_mac_create_menu_event_handler();
        SetMenuItemProperty(ret, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(w), &w);

        // kEventMenuMatchKey is only sent to the menu itself and not to
        // the application, install a separate handler for that event.
        EventHandlerRef eventHandlerRef;
        InstallMenuEventHandler(ret, qt_mac_menu_event,
                                GetEventTypeCount(menu_menu_events),
                                menu_menu_events, 0, &eventHandlerRef);
        menu_eventHandlers_hash()->insert(ret, eventHandlerRef);
    } else {
        qWarning("QMenu: Internal error");
    }
#else
    if (QMenu *qmenu = qobject_cast<QMenu *>(w)){
        ret = [[QT_MANGLE_NAMESPACE(QCocoaMenu) alloc] initWithQMenu:qmenu];
    } else {
        ret = [[NSMenu alloc] init];
    }
#endif
    return ret;
}



QT_END_NAMESPACE