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

« back to all changes in this revision

Viewing changes to src/plugins/platforms/cocoa/qcocoasystemtrayicon.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 Digia Plc and/or its subsidiary(-ies).
 
4
** Copyright (C) 2012 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Christoph Schleifenbaum <christoph.schleifenbaum@kdab.com>
 
5
** Contact: http://www.qt-project.org/legal
 
6
**
 
7
** This file is part of the plugins of the Qt Toolkit.
 
8
**
 
9
** $QT_BEGIN_LICENSE:LGPL$
 
10
** Commercial License Usage
 
11
** Licensees holding valid commercial Qt licenses may use this file in
 
12
** accordance with the commercial license agreement provided with the
 
13
** Software or, alternatively, in accordance with the terms contained in
 
14
** a written agreement between you and Digia.  For licensing terms and
 
15
** conditions see http://qt.digia.com/licensing.  For further information
 
16
** use the contact form at http://qt.digia.com/contact-us.
 
17
**
 
18
** GNU Lesser General Public License Usage
 
19
** Alternatively, this file may be used under the terms of the GNU Lesser
 
20
** General Public License version 2.1 as published by the Free Software
 
21
** Foundation and appearing in the file LICENSE.LGPL included in the
 
22
** packaging of this file.  Please review the following information to
 
23
** ensure the GNU Lesser General Public License version 2.1 requirements
 
24
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 
25
**
 
26
** In addition, as a special exception, Digia gives you certain additional
 
27
** rights.  These rights are described in the Digia Qt LGPL Exception
 
28
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 
29
**
 
30
** GNU General Public License Usage
 
31
** Alternatively, this file may be used under the terms of the GNU
 
32
** General Public License version 3.0 as published by the Free Software
 
33
** Foundation and appearing in the file LICENSE.GPL included in the
 
34
** packaging of this file.  Please review the following information to
 
35
** ensure the GNU General Public License version 3.0 requirements will be
 
36
** met: http://www.gnu.org/copyleft/gpl.html.
 
37
**
 
38
**
 
39
** $QT_END_LICENSE$
 
40
**
 
41
****************************************************************************/
 
42
 
 
43
/****************************************************************************
 
44
**
 
45
** Copyright (c) 2007-2008, Apple, Inc.
 
46
**
 
47
** All rights reserved.
 
48
**
 
49
** Redistribution and use in source and binary forms, with or without
 
50
** modification, are permitted provided that the following conditions are met:
 
51
**
 
52
**   * Redistributions of source code must retain the above copyright notice,
 
53
**     this list of conditions and the following disclaimer.
 
54
**
 
55
**   * Redistributions in binary form must reproduce the above copyright notice,
 
56
**     this list of conditions and the following disclaimer in the documentation
 
57
**     and/or other materials provided with the distribution.
 
58
**
 
59
**   * Neither the name of Apple, Inc. nor the names of its contributors
 
60
**     may be used to endorse or promote products derived from this software
 
61
**     without specific prior written permission.
 
62
**
 
63
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 
64
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 
65
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 
66
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 
67
** CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 
68
** EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
69
** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 
70
** PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 
71
** LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 
72
** NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 
73
** SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
74
**
 
75
****************************************************************************/
 
76
 
 
77
#define QT_MAC_SYSTEMTRAY_USE_GROWL
 
78
 
 
79
#include "qcocoasystemtrayicon.h"
 
80
#include <qtemporaryfile.h>
 
81
#include <qimagewriter.h>
 
82
#include <qdebug.h>
 
83
 
 
84
#include "qcocoamenu.h"
 
85
 
 
86
#include "qt_mac_p.h"
 
87
#include "qcocoahelpers.h"
 
88
 
 
89
#import <AppKit/AppKit.h>
 
90
 
 
91
QT_USE_NAMESPACE
 
92
 
 
93
@class QT_MANGLE_NAMESPACE(QNSMenu);
 
94
@class QT_MANGLE_NAMESPACE(QNSImageView);
 
95
 
 
96
@interface QT_MANGLE_NAMESPACE(QNSStatusItem) : NSObject {
 
97
@public
 
98
    QCocoaSystemTrayIcon *systray;
 
99
    NSStatusItem *item;
 
100
    QCocoaMenu *menu;
 
101
    bool menuVisible;
 
102
    QIcon icon;
 
103
    QT_MANGLE_NAMESPACE(QNSImageView) *imageCell;
 
104
}
 
105
-(id)initWithSysTray:(QCocoaSystemTrayIcon *)systray;
 
106
-(void)dealloc;
 
107
-(NSStatusItem*)item;
 
108
-(QRectF)geometry;
 
109
- (void)triggerSelector:(id)sender button:(Qt::MouseButton)mouseButton;
 
110
- (void)doubleClickSelector:(id)sender;
 
111
@end
 
112
 
 
113
@interface QT_MANGLE_NAMESPACE(QNSImageView) : NSImageView {
 
114
    BOOL down;
 
115
    QT_MANGLE_NAMESPACE(QNSStatusItem) *parent;
 
116
}
 
117
-(id)initWithParent:(QT_MANGLE_NAMESPACE(QNSStatusItem)*)myParent;
 
118
-(void)menuTrackingDone:(NSNotification*)notification;
 
119
-(void)mousePressed:(NSEvent *)mouseEvent button:(Qt::MouseButton)mouseButton;
 
120
@end
 
121
 
 
122
@interface QT_MANGLE_NAMESPACE(QNSMenu) : NSMenu <NSMenuDelegate> {
 
123
    QPlatformMenu *qmenu;
 
124
}
 
125
-(QPlatformMenu*)menu;
 
126
-(id)initWithQMenu:(QPlatformMenu*)qmenu;
 
127
@end
 
128
 
 
129
QT_BEGIN_NAMESPACE
 
130
class QSystemTrayIconSys
 
131
{
 
132
public:
 
133
    QSystemTrayIconSys(QCocoaSystemTrayIcon *sys) {
 
134
        item = [[QT_MANGLE_NAMESPACE(QNSStatusItem) alloc] initWithSysTray:sys];
 
135
    }
 
136
    ~QSystemTrayIconSys() {
 
137
        [[[item item] view] setHidden: YES];
 
138
        [item release];
 
139
    }
 
140
    QT_MANGLE_NAMESPACE(QNSStatusItem) *item;
 
141
};
 
142
 
 
143
void QCocoaSystemTrayIcon::init()
 
144
{
 
145
    if (!m_sys)
 
146
        m_sys = new QSystemTrayIconSys(this);
 
147
}
 
148
 
 
149
QRect QCocoaSystemTrayIcon::geometry() const
 
150
{
 
151
    if (!m_sys)
 
152
        return QRect();
 
153
 
 
154
    const QRectF geom = [m_sys->item geometry];
 
155
    if (!geom.isNull())
 
156
        return geom.toRect();
 
157
    else
 
158
        return QRect();
 
159
}
 
160
 
 
161
void QCocoaSystemTrayIcon::cleanup()
 
162
{
 
163
    delete m_sys;
 
164
    m_sys = 0;
 
165
}
 
166
 
 
167
void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon)
 
168
{
 
169
    if (!m_sys)
 
170
        return;
 
171
 
 
172
    m_sys->item->icon = icon;
 
173
 
 
174
    const bool menuVisible = m_sys->item->menu && m_sys->item->menuVisible;
 
175
 
 
176
    CGFloat hgt = [[[NSApplication sharedApplication] mainMenu] menuBarHeight];
 
177
    const short scale = hgt - 4;
 
178
 
 
179
    QPixmap pm = m_sys->item->icon.pixmap(QSize(scale, scale),
 
180
                                          menuVisible ? QIcon::Selected : QIcon::Normal);
 
181
    if (pm.isNull()) {
 
182
        pm = QPixmap(scale, scale);
 
183
        pm.fill(Qt::transparent);
 
184
    }
 
185
    NSImage *nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(pm));
 
186
    [(NSImageView*)[[m_sys->item item] view] setImage: nsimage];
 
187
    [nsimage release];
 
188
}
 
189
 
 
190
void QCocoaSystemTrayIcon::updateMenu(QPlatformMenu *menu)
 
191
{
 
192
    if (!m_sys)
 
193
        return;
 
194
 
 
195
    m_sys->item->menu = static_cast<QCocoaMenu *>(menu);
 
196
    if (menu && [m_sys->item->menu->nsMenu() numberOfItems] > 0) {
 
197
        [[m_sys->item item] setHighlightMode:YES];
 
198
    } else {
 
199
        [[m_sys->item item] setHighlightMode:NO];
 
200
    }
 
201
}
 
202
 
 
203
void QCocoaSystemTrayIcon::updateToolTip(const QString &toolTip)
 
204
{
 
205
    if (!m_sys)
 
206
        return;
 
207
    [[[m_sys->item item] view] setToolTip:QCFString::toNSString(toolTip)];
 
208
}
 
209
 
 
210
bool QCocoaSystemTrayIcon::isSystemTrayAvailable() const
 
211
{
 
212
    return true;
 
213
}
 
214
 
 
215
bool QCocoaSystemTrayIcon::supportsMessages() const
 
216
{
 
217
    return true;
 
218
}
 
219
 
 
220
void QCocoaSystemTrayIcon::showMessage(const QString &title, const QString &message,
 
221
                                       const QIcon& icon, MessageIcon, int)
 
222
{
 
223
    if (!m_sys)
 
224
        return;
 
225
 
 
226
#ifdef QT_MAC_SYSTEMTRAY_USE_GROWL
 
227
    // Make sure that we have Growl installed on the machine we are running on.
 
228
    QCFType<CFURLRef> cfurl;
 
229
    OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator,
 
230
                                              CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl);
 
231
    if (status == kLSApplicationNotFoundErr)
 
232
        return;
 
233
    QCFType<CFBundleRef> bundle = CFBundleCreate(0, cfurl);
 
234
 
 
235
    if (CFStringCompare(CFBundleGetIdentifier(bundle), CFSTR("com.Growl.GrowlHelperApp"),
 
236
                kCFCompareCaseInsensitive |  kCFCompareBackwards) != kCFCompareEqualTo)
 
237
        return;
 
238
    QPixmap notificationIconPixmap = icon.pixmap(32, 32);
 
239
    QTemporaryFile notificationIconFile;
 
240
    QString notificationType(QLatin1String("Notification")), notificationIcon, notificationApp(qt_mac_applicationName());
 
241
    if (notificationApp.isEmpty())
 
242
        notificationApp = QLatin1String("Application");
 
243
    if (!notificationIconPixmap.isNull() && notificationIconFile.open()) {
 
244
        QImageWriter writer(&notificationIconFile, "PNG");
 
245
        if (writer.write(notificationIconPixmap.toImage()))
 
246
            notificationIcon = QLatin1String("image from location \"file://") + notificationIconFile.fileName() + QLatin1String("\"");
 
247
    }
 
248
    const QString script(QLatin1String(
 
249
        "tell application \"System Events\"\n"
 
250
        "set isRunning to (count of (every process whose bundle identifier is \"com.Growl.GrowlHelperApp\")) > 0\n"
 
251
        "end tell\n"
 
252
        "if isRunning\n"
 
253
        "tell application id \"com.Growl.GrowlHelperApp\"\n"
 
254
        "-- Make a list of all the notification types (all)\n"
 
255
        "set the allNotificationsList to {\"") + notificationType + QLatin1String("\"}\n"
 
256
 
 
257
        "-- Make a list of the notifications (enabled)\n"
 
258
        "set the enabledNotificationsList to {\"") + notificationType + QLatin1String("\"}\n"
 
259
 
 
260
        "-- Register our script with growl.\n"
 
261
        "register as application \"") + notificationApp + QLatin1String("\" all notifications allNotificationsList default notifications enabledNotificationsList\n"
 
262
 
 
263
        "-- Send a Notification...\n") +
 
264
        QLatin1String("notify with name \"") + notificationType +
 
265
        QLatin1String("\" title \"") + title +
 
266
        QLatin1String("\" description \"") + message +
 
267
        QLatin1String("\" application name \"") + notificationApp +
 
268
        QLatin1String("\" ")  + notificationIcon +
 
269
        QLatin1String("\nend tell\nend if"));
 
270
    qt_mac_execute_apple_script(script, 0);
 
271
#else
 
272
    Q_UNUSED(icon);
 
273
    Q_UNUSED(title);
 
274
    Q_UNUSED(message);
 
275
#endif
 
276
}
 
277
QT_END_NAMESPACE
 
278
 
 
279
@implementation NSStatusItem (Qt)
 
280
@end
 
281
 
 
282
@implementation QT_MANGLE_NAMESPACE(QNSImageView)
 
283
-(id)initWithParent:(QT_MANGLE_NAMESPACE(QNSStatusItem)*)myParent {
 
284
    self = [super init];
 
285
    parent = myParent;
 
286
    down = NO;
 
287
    return self;
 
288
}
 
289
 
 
290
-(void)menuTrackingDone:(NSNotification*)notification
 
291
{
 
292
    Q_UNUSED(notification);
 
293
    down = NO;
 
294
 
 
295
    CGFloat hgt = [[[NSApplication sharedApplication] mainMenu] menuBarHeight];
 
296
    const short scale = hgt - 4;
 
297
 
 
298
    QPixmap pm = parent->icon.pixmap(QSize(scale, scale), QIcon::Normal);
 
299
    if (pm.isNull()) {
 
300
        pm = QPixmap(scale, scale);
 
301
        pm.fill(Qt::transparent);
 
302
    }
 
303
    NSImage *nsaltimage = static_cast<NSImage *>(qt_mac_create_nsimage(pm));
 
304
    [self setImage: nsaltimage];
 
305
    [nsaltimage release];
 
306
 
 
307
    parent->menuVisible = false;
 
308
 
 
309
    [self setNeedsDisplay:YES];
 
310
}
 
311
 
 
312
-(void)mousePressed:(NSEvent *)mouseEvent button:(Qt::MouseButton)mouseButton
 
313
{
 
314
    down = YES;
 
315
    int clickCount = [mouseEvent clickCount];
 
316
    [self setNeedsDisplay:YES];
 
317
 
 
318
    CGFloat hgt = [[[NSApplication sharedApplication] mainMenu] menuBarHeight];
 
319
    const short scale = hgt - 4;
 
320
 
 
321
    QPixmap pm = parent->icon.pixmap(QSize(scale, scale),
 
322
                                     parent->menuVisible ? QIcon::Selected : QIcon::Normal);
 
323
    if (pm.isNull()) {
 
324
        pm = QPixmap(scale, scale);
 
325
        pm.fill(Qt::transparent);
 
326
    }
 
327
    NSImage *nsaltimage = static_cast<NSImage *>(qt_mac_create_nsimage(pm));
 
328
    [self setImage: nsaltimage];
 
329
    [nsaltimage release];
 
330
 
 
331
    if (clickCount == 2) {
 
332
        [self menuTrackingDone:nil];
 
333
        [parent doubleClickSelector:self];
 
334
    } else {
 
335
        [parent triggerSelector:self button:mouseButton];
 
336
    }
 
337
}
 
338
 
 
339
-(void)mouseDown:(NSEvent *)mouseEvent
 
340
{
 
341
    [self mousePressed:mouseEvent button:Qt::LeftButton];
 
342
}
 
343
 
 
344
-(void)mouseUp:(NSEvent *)mouseEvent
 
345
{
 
346
    Q_UNUSED(mouseEvent);
 
347
    [self menuTrackingDone:nil];
 
348
}
 
349
 
 
350
- (void)rightMouseDown:(NSEvent *)mouseEvent
 
351
{
 
352
    [self mousePressed:mouseEvent button:Qt::RightButton];
 
353
}
 
354
 
 
355
-(void)rightMouseUp:(NSEvent *)mouseEvent
 
356
{
 
357
    Q_UNUSED(mouseEvent);
 
358
    [self menuTrackingDone:nil];
 
359
}
 
360
 
 
361
- (void)otherMouseDown:(NSEvent *)mouseEvent
 
362
{
 
363
    [self mousePressed:mouseEvent button:cocoaButton2QtButton([mouseEvent buttonNumber])];
 
364
}
 
365
 
 
366
-(void)otherMouseUp:(NSEvent *)mouseEvent
 
367
{
 
368
    Q_UNUSED(mouseEvent);
 
369
    [self menuTrackingDone:nil];
 
370
}
 
371
 
 
372
-(void)drawRect:(NSRect)rect {
 
373
    [[parent item] drawStatusBarBackgroundInRect:rect withHighlight:down];
 
374
    [super drawRect:rect];
 
375
}
 
376
@end
 
377
 
 
378
@implementation QT_MANGLE_NAMESPACE(QNSStatusItem)
 
379
 
 
380
-(id)initWithSysTray:(QCocoaSystemTrayIcon *)sys
 
381
{
 
382
    self = [super init];
 
383
    if (self) {
 
384
        item = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
 
385
        menu = 0;
 
386
        menuVisible = false;
 
387
        systray = sys;
 
388
        imageCell = [[QT_MANGLE_NAMESPACE(QNSImageView) alloc] initWithParent:self];
 
389
        [item setView: imageCell];
 
390
    }
 
391
    return self;
 
392
}
 
393
 
 
394
-(void)dealloc {
 
395
    [[NSStatusBar systemStatusBar] removeStatusItem:item];
 
396
    [imageCell release];
 
397
    [item release];
 
398
    [super dealloc];
 
399
 
 
400
}
 
401
 
 
402
-(NSStatusItem*)item {
 
403
    return item;
 
404
}
 
405
-(QRectF)geometry {
 
406
    if (NSWindow *window = [[item view] window]) {
 
407
        NSRect screenRect = [[window screen] frame];
 
408
        NSRect windowRect = [window frame];
 
409
        return QRectF(windowRect.origin.x, screenRect.size.height-windowRect.origin.y-windowRect.size.height, windowRect.size.width, windowRect.size.height);
 
410
    }
 
411
    return QRectF();
 
412
}
 
413
 
 
414
- (void)triggerSelector:(id)sender button:(Qt::MouseButton)mouseButton {
 
415
    Q_UNUSED(sender);
 
416
    if (!systray)
 
417
        return;
 
418
 
 
419
    if (mouseButton == Qt::MidButton)
 
420
        emit systray->activated(QPlatformSystemTrayIcon::MiddleClick);
 
421
    else
 
422
        emit systray->activated(QPlatformSystemTrayIcon::Trigger);
 
423
 
 
424
    if (menu) {
 
425
        NSMenu *m = menu->nsMenu();
 
426
        [[NSNotificationCenter defaultCenter] addObserver:imageCell
 
427
         selector:@selector(menuTrackingDone:)
 
428
             name:NSMenuDidEndTrackingNotification
 
429
                 object:m];
 
430
        menuVisible = true;
 
431
        [item popUpStatusItemMenu: m];
 
432
    }
 
433
}
 
434
 
 
435
- (void)doubleClickSelector:(id)sender {
 
436
    Q_UNUSED(sender);
 
437
    if (!systray)
 
438
        return;
 
439
    emit systray->activated(QPlatformSystemTrayIcon::DoubleClick);
 
440
}
 
441
 
 
442
@end
 
443
 
 
444
class QSystemTrayIconQMenu : public QPlatformMenu
 
445
{
 
446
public:
 
447
    void doAboutToShow() { emit aboutToShow(); }
 
448
private:
 
449
    QSystemTrayIconQMenu();
 
450
};
 
451
 
 
452
@implementation QT_MANGLE_NAMESPACE(QNSMenu)
 
453
-(id)initWithQMenu:(QPlatformMenu*)qm {
 
454
    self = [super init];
 
455
    if (self) {
 
456
        self->qmenu = qm;
 
457
        [self setDelegate:self];
 
458
    }
 
459
    return self;
 
460
}
 
461
-(QPlatformMenu*)menu {
 
462
    return qmenu;
 
463
}
 
464
@end