1
/****************************************************************************
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
7
** This file is part of the plugins of the Qt Toolkit.
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.
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.
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.
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.
41
****************************************************************************/
43
/****************************************************************************
45
** Copyright (c) 2007-2008, Apple, Inc.
47
** All rights reserved.
49
** Redistribution and use in source and binary forms, with or without
50
** modification, are permitted provided that the following conditions are met:
52
** * Redistributions of source code must retain the above copyright notice,
53
** this list of conditions and the following disclaimer.
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.
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.
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.
75
****************************************************************************/
77
#define QT_MAC_SYSTEMTRAY_USE_GROWL
79
#include "qcocoasystemtrayicon.h"
80
#include <qtemporaryfile.h>
81
#include <qimagewriter.h>
84
#include "qcocoamenu.h"
87
#include "qcocoahelpers.h"
89
#import <AppKit/AppKit.h>
93
@class QT_MANGLE_NAMESPACE(QNSMenu);
94
@class QT_MANGLE_NAMESPACE(QNSImageView);
96
@interface QT_MANGLE_NAMESPACE(QNSStatusItem) : NSObject {
98
QCocoaSystemTrayIcon *systray;
103
QT_MANGLE_NAMESPACE(QNSImageView) *imageCell;
105
-(id)initWithSysTray:(QCocoaSystemTrayIcon *)systray;
107
-(NSStatusItem*)item;
109
- (void)triggerSelector:(id)sender button:(Qt::MouseButton)mouseButton;
110
- (void)doubleClickSelector:(id)sender;
113
@interface QT_MANGLE_NAMESPACE(QNSImageView) : NSImageView {
115
QT_MANGLE_NAMESPACE(QNSStatusItem) *parent;
117
-(id)initWithParent:(QT_MANGLE_NAMESPACE(QNSStatusItem)*)myParent;
118
-(void)menuTrackingDone:(NSNotification*)notification;
119
-(void)mousePressed:(NSEvent *)mouseEvent button:(Qt::MouseButton)mouseButton;
122
@interface QT_MANGLE_NAMESPACE(QNSMenu) : NSMenu <NSMenuDelegate> {
123
QPlatformMenu *qmenu;
125
-(QPlatformMenu*)menu;
126
-(id)initWithQMenu:(QPlatformMenu*)qmenu;
130
class QSystemTrayIconSys
133
QSystemTrayIconSys(QCocoaSystemTrayIcon *sys) {
134
item = [[QT_MANGLE_NAMESPACE(QNSStatusItem) alloc] initWithSysTray:sys];
136
~QSystemTrayIconSys() {
137
[[[item item] view] setHidden: YES];
140
QT_MANGLE_NAMESPACE(QNSStatusItem) *item;
143
void QCocoaSystemTrayIcon::init()
146
m_sys = new QSystemTrayIconSys(this);
149
QRect QCocoaSystemTrayIcon::geometry() const
154
const QRectF geom = [m_sys->item geometry];
156
return geom.toRect();
161
void QCocoaSystemTrayIcon::cleanup()
167
void QCocoaSystemTrayIcon::updateIcon(const QIcon &icon)
172
m_sys->item->icon = icon;
174
const bool menuVisible = m_sys->item->menu && m_sys->item->menuVisible;
176
CGFloat hgt = [[[NSApplication sharedApplication] mainMenu] menuBarHeight];
177
const short scale = hgt - 4;
179
QPixmap pm = m_sys->item->icon.pixmap(QSize(scale, scale),
180
menuVisible ? QIcon::Selected : QIcon::Normal);
182
pm = QPixmap(scale, scale);
183
pm.fill(Qt::transparent);
185
NSImage *nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(pm));
186
[(NSImageView*)[[m_sys->item item] view] setImage: nsimage];
190
void QCocoaSystemTrayIcon::updateMenu(QPlatformMenu *menu)
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];
199
[[m_sys->item item] setHighlightMode:NO];
203
void QCocoaSystemTrayIcon::updateToolTip(const QString &toolTip)
207
[[[m_sys->item item] view] setToolTip:QCFString::toNSString(toolTip)];
210
bool QCocoaSystemTrayIcon::isSystemTrayAvailable() const
215
bool QCocoaSystemTrayIcon::supportsMessages() const
220
void QCocoaSystemTrayIcon::showMessage(const QString &title, const QString &message,
221
const QIcon& icon, MessageIcon, int)
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)
233
QCFType<CFBundleRef> bundle = CFBundleCreate(0, cfurl);
235
if (CFStringCompare(CFBundleGetIdentifier(bundle), CFSTR("com.Growl.GrowlHelperApp"),
236
kCFCompareCaseInsensitive | kCFCompareBackwards) != kCFCompareEqualTo)
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(¬ificationIconFile, "PNG");
245
if (writer.write(notificationIconPixmap.toImage()))
246
notificationIcon = QLatin1String("image from location \"file://") + notificationIconFile.fileName() + QLatin1String("\"");
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"
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"
257
"-- Make a list of the notifications (enabled)\n"
258
"set the enabledNotificationsList to {\"") + notificationType + QLatin1String("\"}\n"
260
"-- Register our script with growl.\n"
261
"register as application \"") + notificationApp + QLatin1String("\" all notifications allNotificationsList default notifications enabledNotificationsList\n"
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);
279
@implementation NSStatusItem (Qt)
282
@implementation QT_MANGLE_NAMESPACE(QNSImageView)
283
-(id)initWithParent:(QT_MANGLE_NAMESPACE(QNSStatusItem)*)myParent {
290
-(void)menuTrackingDone:(NSNotification*)notification
292
Q_UNUSED(notification);
295
CGFloat hgt = [[[NSApplication sharedApplication] mainMenu] menuBarHeight];
296
const short scale = hgt - 4;
298
QPixmap pm = parent->icon.pixmap(QSize(scale, scale), QIcon::Normal);
300
pm = QPixmap(scale, scale);
301
pm.fill(Qt::transparent);
303
NSImage *nsaltimage = static_cast<NSImage *>(qt_mac_create_nsimage(pm));
304
[self setImage: nsaltimage];
305
[nsaltimage release];
307
parent->menuVisible = false;
309
[self setNeedsDisplay:YES];
312
-(void)mousePressed:(NSEvent *)mouseEvent button:(Qt::MouseButton)mouseButton
315
int clickCount = [mouseEvent clickCount];
316
[self setNeedsDisplay:YES];
318
CGFloat hgt = [[[NSApplication sharedApplication] mainMenu] menuBarHeight];
319
const short scale = hgt - 4;
321
QPixmap pm = parent->icon.pixmap(QSize(scale, scale),
322
parent->menuVisible ? QIcon::Selected : QIcon::Normal);
324
pm = QPixmap(scale, scale);
325
pm.fill(Qt::transparent);
327
NSImage *nsaltimage = static_cast<NSImage *>(qt_mac_create_nsimage(pm));
328
[self setImage: nsaltimage];
329
[nsaltimage release];
331
if (clickCount == 2) {
332
[self menuTrackingDone:nil];
333
[parent doubleClickSelector:self];
335
[parent triggerSelector:self button:mouseButton];
339
-(void)mouseDown:(NSEvent *)mouseEvent
341
[self mousePressed:mouseEvent button:Qt::LeftButton];
344
-(void)mouseUp:(NSEvent *)mouseEvent
346
Q_UNUSED(mouseEvent);
347
[self menuTrackingDone:nil];
350
- (void)rightMouseDown:(NSEvent *)mouseEvent
352
[self mousePressed:mouseEvent button:Qt::RightButton];
355
-(void)rightMouseUp:(NSEvent *)mouseEvent
357
Q_UNUSED(mouseEvent);
358
[self menuTrackingDone:nil];
361
- (void)otherMouseDown:(NSEvent *)mouseEvent
363
[self mousePressed:mouseEvent button:cocoaButton2QtButton([mouseEvent buttonNumber])];
366
-(void)otherMouseUp:(NSEvent *)mouseEvent
368
Q_UNUSED(mouseEvent);
369
[self menuTrackingDone:nil];
372
-(void)drawRect:(NSRect)rect {
373
[[parent item] drawStatusBarBackgroundInRect:rect withHighlight:down];
374
[super drawRect:rect];
378
@implementation QT_MANGLE_NAMESPACE(QNSStatusItem)
380
-(id)initWithSysTray:(QCocoaSystemTrayIcon *)sys
384
item = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
388
imageCell = [[QT_MANGLE_NAMESPACE(QNSImageView) alloc] initWithParent:self];
389
[item setView: imageCell];
395
[[NSStatusBar systemStatusBar] removeStatusItem:item];
402
-(NSStatusItem*)item {
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);
414
- (void)triggerSelector:(id)sender button:(Qt::MouseButton)mouseButton {
419
if (mouseButton == Qt::MidButton)
420
emit systray->activated(QPlatformSystemTrayIcon::MiddleClick);
422
emit systray->activated(QPlatformSystemTrayIcon::Trigger);
425
NSMenu *m = menu->nsMenu();
426
[[NSNotificationCenter defaultCenter] addObserver:imageCell
427
selector:@selector(menuTrackingDone:)
428
name:NSMenuDidEndTrackingNotification
431
[item popUpStatusItemMenu: m];
435
- (void)doubleClickSelector:(id)sender {
439
emit systray->activated(QPlatformSystemTrayIcon::DoubleClick);
444
class QSystemTrayIconQMenu : public QPlatformMenu
447
void doAboutToShow() { emit aboutToShow(); }
449
QSystemTrayIconQMenu();
452
@implementation QT_MANGLE_NAMESPACE(QNSMenu)
453
-(id)initWithQMenu:(QPlatformMenu*)qm {
457
[self setDelegate:self];
461
-(QPlatformMenu*)menu {