~ubuntu-branches/ubuntu/wily/qtbase-opensource-src/wily

« back to all changes in this revision

Viewing changes to src/plugins/platforms/cocoa/qcocoamenu.mm

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 12:46:17 UTC
  • Revision ID: package-import@ubuntu.com-20130205124617-c8jouts182j002fx
Tags: upstream-5.0.1+dfsg
ImportĀ upstreamĀ versionĀ 5.0.1+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 KlarƤlvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author James Turner <james.turner@kdab.com>
 
4
** Contact: http://www.qt-project.org/legal
 
5
**
 
6
** This file is part of the plugins of the Qt Toolkit.
 
7
**
 
8
** $QT_BEGIN_LICENSE:LGPL$
 
9
** Commercial License Usage
 
10
** Licensees holding valid commercial Qt licenses may use this file in
 
11
** accordance with the commercial license agreement provided with the
 
12
** Software or, alternatively, in accordance with the terms contained in
 
13
** a written agreement between you and Digia.  For licensing terms and
 
14
** conditions see http://qt.digia.com/licensing.  For further information
 
15
** use the contact form at http://qt.digia.com/contact-us.
 
16
**
 
17
** GNU Lesser General Public License Usage
 
18
** Alternatively, this file may be used under the terms of the GNU Lesser
 
19
** General Public License version 2.1 as published by the Free Software
 
20
** Foundation and appearing in the file LICENSE.LGPL included in the
 
21
** packaging of this file.  Please review the following information to
 
22
** ensure the GNU Lesser General Public License version 2.1 requirements
 
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 
24
**
 
25
** In addition, as a special exception, Digia gives you certain additional
 
26
** rights.  These rights are described in the Digia Qt LGPL Exception
 
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 
28
**
 
29
** GNU General Public License Usage
 
30
** Alternatively, this file may be used under the terms of the GNU
 
31
** General Public License version 3.0 as published by the Free Software
 
32
** Foundation and appearing in the file LICENSE.GPL included in the
 
33
** packaging of this file.  Please review the following information to
 
34
** ensure the GNU General Public License version 3.0 requirements will be
 
35
** met: http://www.gnu.org/copyleft/gpl.html.
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
42
#include "qcocoamenu.h"
 
43
 
 
44
#include "qcocoahelpers.h"
 
45
#include "qcocoaautoreleasepool.h"
 
46
 
 
47
#include <QtCore/QtDebug>
 
48
#include "qcocoaapplication.h"
 
49
#include "qcocoamenuloader.h"
 
50
 
 
51
static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader()
 
52
{
 
53
    return [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)];
 
54
}
 
55
 
 
56
@interface QT_MANGLE_NAMESPACE(QCocoaMenuDelegate) : NSObject <NSMenuDelegate> {
 
57
    QCocoaMenu *m_menu;
 
58
}
 
59
 
 
60
- (id) initWithMenu:(QCocoaMenu*) m;
 
61
 
 
62
@end
 
63
 
 
64
@implementation QT_MANGLE_NAMESPACE(QCocoaMenuDelegate)
 
65
 
 
66
- (id) initWithMenu:(QCocoaMenu*) m
 
67
{
 
68
    if ((self = [super init]))
 
69
        m_menu = m;
 
70
 
 
71
    return self;
 
72
}
 
73
 
 
74
- (void) menuWillOpen:(NSMenu*)m
 
75
{
 
76
    Q_UNUSED(m);
 
77
    emit m_menu->aboutToShow();
 
78
}
 
79
 
 
80
- (void) menuDidClose:(NSMenu*)m
 
81
{
 
82
    Q_UNUSED(m);
 
83
    // wrong, but it's the best we can do
 
84
    emit m_menu->aboutToHide();
 
85
}
 
86
 
 
87
- (void) itemFired:(NSMenuItem*) item
 
88
{
 
89
    QCocoaMenuItem *cocoaItem = reinterpret_cast<QCocoaMenuItem *>([item tag]);
 
90
    cocoaItem->activated();
 
91
}
 
92
 
 
93
- (BOOL)validateMenuItem:(NSMenuItem*)menuItem
 
94
{
 
95
    if (![menuItem tag])
 
96
        return YES;
 
97
 
 
98
 
 
99
    QCocoaMenuItem* cocoaItem = reinterpret_cast<QCocoaMenuItem *>([menuItem tag]);
 
100
    return cocoaItem->isEnabled();
 
101
}
 
102
 
 
103
@end
 
104
 
 
105
QT_BEGIN_NAMESPACE
 
106
 
 
107
QCocoaMenu::QCocoaMenu() :
 
108
    m_enabled(true),
 
109
    m_tag(0)
 
110
{
 
111
    m_delegate = [[QT_MANGLE_NAMESPACE(QCocoaMenuDelegate) alloc] initWithMenu:this];
 
112
    m_nativeItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
 
113
    m_nativeMenu = [[NSMenu alloc] initWithTitle:@"Untitled"];
 
114
    [m_nativeMenu setAutoenablesItems:YES];
 
115
    m_nativeMenu.delegate = (QT_MANGLE_NAMESPACE(QCocoaMenuDelegate) *) m_delegate;
 
116
    [m_nativeItem setSubmenu:m_nativeMenu];
 
117
}
 
118
 
 
119
QCocoaMenu::~QCocoaMenu()
 
120
{
 
121
    QCocoaAutoReleasePool pool;
 
122
    [m_nativeItem setSubmenu:nil];
 
123
    [m_nativeMenu release];
 
124
    [m_delegate release];
 
125
    [m_nativeItem release];
 
126
}
 
127
 
 
128
void QCocoaMenu::setText(const QString &text)
 
129
{
 
130
    QCocoaAutoReleasePool pool;
 
131
    QString stripped = qt_mac_removeAmpersandEscapes(text);
 
132
    [m_nativeMenu setTitle:QCFString::toNSString(stripped)];
 
133
    [m_nativeItem setTitle:QCFString::toNSString(stripped)];
 
134
}
 
135
 
 
136
void QCocoaMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before)
 
137
{
 
138
    QCocoaAutoReleasePool pool;
 
139
    QCocoaMenuItem *cocoaItem = static_cast<QCocoaMenuItem *>(menuItem);
 
140
    QCocoaMenuItem *beforeItem = static_cast<QCocoaMenuItem *>(before);
 
141
 
 
142
    cocoaItem->sync();
 
143
    if (beforeItem) {
 
144
        int index = m_menuItems.indexOf(beforeItem);
 
145
        // if a before item is supplied, it should be in the menu
 
146
        if (index < 0) {
 
147
            qWarning() << Q_FUNC_INFO << "Before menu item not found";
 
148
            return;
 
149
        }
 
150
        m_menuItems.insert(index, cocoaItem);
 
151
    } else {
 
152
        m_menuItems.append(cocoaItem);
 
153
    }
 
154
 
 
155
    insertNative(cocoaItem, beforeItem);
 
156
}
 
157
 
 
158
void QCocoaMenu::insertNative(QCocoaMenuItem *item, QCocoaMenuItem *beforeItem)
 
159
{
 
160
    [item->nsItem() setTarget:m_delegate];
 
161
    if (!item->menu())
 
162
        [item->nsItem() setAction:@selector(itemFired:)];
 
163
 
 
164
    if (item->isMerged())
 
165
        return;
 
166
 
 
167
    if ([item->nsItem() menu]) {
 
168
        qWarning() << Q_FUNC_INFO << "Menu item is already in a menu, remove it from the other menu first before inserting";
 
169
        return;
 
170
    }
 
171
    // if the item we're inserting before is merged, skip along until
 
172
    // we find a non-merged real item to insert ahead of.
 
173
    while (beforeItem && beforeItem->isMerged()) {
 
174
        beforeItem = itemOrNull(m_menuItems.indexOf(beforeItem) + 1);
 
175
    }
 
176
 
 
177
    if (beforeItem) {
 
178
        if (beforeItem->isMerged()) {
 
179
            qWarning() << Q_FUNC_INFO << "No non-merged before menu item found";
 
180
            return;
 
181
        }
 
182
        NSUInteger nativeIndex = [m_nativeMenu indexOfItem:beforeItem->nsItem()];
 
183
        [m_nativeMenu insertItem: item->nsItem() atIndex: nativeIndex];
 
184
    } else {
 
185
        [m_nativeMenu addItem: item->nsItem()];
 
186
    }
 
187
}
 
188
 
 
189
void QCocoaMenu::removeMenuItem(QPlatformMenuItem *menuItem)
 
190
{
 
191
    QCocoaAutoReleasePool pool;
 
192
    QCocoaMenuItem *cocoaItem = static_cast<QCocoaMenuItem *>(menuItem);
 
193
    if (!m_menuItems.contains(cocoaItem)) {
 
194
        qWarning() << Q_FUNC_INFO << "Menu does not contain the item to be removed";
 
195
        return;
 
196
    }
 
197
    m_menuItems.removeOne(cocoaItem);
 
198
    if (!cocoaItem->isMerged()) {
 
199
        if (m_nativeMenu != [cocoaItem->nsItem() menu]) {
 
200
            qWarning() << Q_FUNC_INFO << "Item to remove does not belong to this menu";
 
201
            return;
 
202
        }
 
203
        [m_nativeMenu removeItem: cocoaItem->nsItem()];
 
204
    }
 
205
}
 
206
 
 
207
QCocoaMenuItem *QCocoaMenu::itemOrNull(int index) const
 
208
{
 
209
    if ((index < 0) || (index >= m_menuItems.size()))
 
210
        return 0;
 
211
 
 
212
    return m_menuItems.at(index);
 
213
}
 
214
 
 
215
void QCocoaMenu::syncMenuItem(QPlatformMenuItem *menuItem)
 
216
{
 
217
    QCocoaAutoReleasePool pool;
 
218
    QCocoaMenuItem *cocoaItem = static_cast<QCocoaMenuItem *>(menuItem);
 
219
    if (!m_menuItems.contains(cocoaItem)) {
 
220
        qWarning() << Q_FUNC_INFO << "Item does not belong to this menu";
 
221
        return;
 
222
    }
 
223
 
 
224
    bool wasMerged = cocoaItem->isMerged();
 
225
    NSMenu *oldMenu = wasMerged ? [getMenuLoader() applicationMenu] : m_nativeMenu;
 
226
    NSMenuItem *oldItem = [oldMenu itemWithTag:(NSInteger) cocoaItem];
 
227
 
 
228
    if (cocoaItem->sync() != oldItem) {
 
229
        // native item was changed for some reason
 
230
        if (oldItem) {
 
231
            if (wasMerged) {
 
232
                [oldItem setEnabled:NO];
 
233
                [oldItem setHidden:YES];
 
234
            } else {
 
235
                [m_nativeMenu removeItem:oldItem];
 
236
            }
 
237
        }
 
238
 
 
239
        QCocoaMenuItem* beforeItem = itemOrNull(m_menuItems.indexOf(cocoaItem) + 1);
 
240
        insertNative(cocoaItem, beforeItem);
 
241
    }
 
242
}
 
243
 
 
244
void QCocoaMenu::syncSeparatorsCollapsible(bool enable)
 
245
{
 
246
    QCocoaAutoReleasePool pool;
 
247
    if (enable) {
 
248
        bool previousIsSeparator = true; // setting to true kills all the separators placed at the top.
 
249
        NSMenuItem *previousItem = nil;
 
250
 
 
251
        NSArray *itemArray = [m_nativeMenu itemArray];
 
252
        for (unsigned int i = 0; i < [itemArray count]; ++i) {
 
253
            NSMenuItem *item = reinterpret_cast<NSMenuItem *>([itemArray objectAtIndex:i]);
 
254
            if ([item isSeparatorItem])
 
255
                [item setHidden:previousIsSeparator];
 
256
 
 
257
            if (![item isHidden]) {
 
258
                previousItem = item;
 
259
                previousIsSeparator = ([previousItem isSeparatorItem]);
 
260
            }
 
261
        }
 
262
 
 
263
        // We now need to check the final item since we don't want any separators at the end of the list.
 
264
        if (previousItem && previousIsSeparator)
 
265
            [previousItem setHidden:YES];
 
266
    } else {
 
267
        foreach (QCocoaMenuItem *item, m_menuItems) {
 
268
            if (!item->isSeparator())
 
269
                continue;
 
270
 
 
271
            // sync the visiblity directly
 
272
            item->sync();
 
273
        }
 
274
    }
 
275
}
 
276
 
 
277
void QCocoaMenu::setParentItem(QCocoaMenuItem *item)
 
278
{
 
279
    Q_UNUSED(item);
 
280
}
 
281
 
 
282
void QCocoaMenu::setEnabled(bool enabled)
 
283
{
 
284
    m_enabled = enabled;
 
285
    syncModalState(!m_enabled);
 
286
}
 
287
 
 
288
void QCocoaMenu::setVisible(bool visible)
 
289
{
 
290
    [m_nativeItem setSubmenu:(visible ? m_nativeMenu : nil)];
 
291
}
 
292
 
 
293
QPlatformMenuItem *QCocoaMenu::menuItemAt(int position) const
 
294
{
 
295
    return m_menuItems.at(position);
 
296
}
 
297
 
 
298
QPlatformMenuItem *QCocoaMenu::menuItemForTag(quintptr tag) const
 
299
{
 
300
    foreach (QCocoaMenuItem *item, m_menuItems) {
 
301
        if (item->tag() ==  tag)
 
302
            return item;
 
303
    }
 
304
 
 
305
    return 0;
 
306
}
 
307
 
 
308
QList<QCocoaMenuItem *> QCocoaMenu::merged() const
 
309
{
 
310
    QList<QCocoaMenuItem *> result;
 
311
    foreach (QCocoaMenuItem *item, m_menuItems) {
 
312
        if (item->menu()) { // recurse into submenus
 
313
            result.append(item->menu()->merged());
 
314
            continue;
 
315
        }
 
316
 
 
317
        if (item->isMerged())
 
318
            result.append(item);
 
319
    }
 
320
 
 
321
    return result;
 
322
}
 
323
 
 
324
void QCocoaMenu::syncModalState(bool modal)
 
325
{
 
326
    if (!m_enabled)
 
327
        modal = true;
 
328
 
 
329
    [m_nativeItem setEnabled:!modal];
 
330
 
 
331
    foreach (QCocoaMenuItem *item, m_menuItems) {
 
332
        if (item->menu()) { // recurse into submenus
 
333
            item->menu()->syncModalState(modal);
 
334
            continue;
 
335
        }
 
336
 
 
337
        item->syncModalState(modal);
 
338
    }
 
339
}
 
340
 
 
341
QT_END_NAMESPACE