Skip to content

Commit

Permalink
Backport implementation of OS X QSystemTrayIcon from Qt 5
Browse files Browse the repository at this point in the history
This patch aims to bring support of OS X >= 10.8 notification center and
update the growl support code.

[ChangeLog][OS X][Nofication] Added support for OS X 10.8 and upper
notification center.

Task-number: QTBUG-21830
Change-Id: Iad19c5e3a915e2caf15730a27ac762c9c11e493c
Reviewed-by: Morten Johan Sørvig <[email protected]>
  • Loading branch information
Samuel Gaist authored and msorvig committed Feb 12, 2015
1 parent 97b85f4 commit 6555ba0
Showing 1 changed file with 110 additions and 62 deletions.
172 changes: 110 additions & 62 deletions src/gui/util/qsystemtrayicon_mac.mm
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,11 @@
@class QT_MANGLE_NAMESPACE(QNSMenu);
@class QT_MANGLE_NAMESPACE(QNSImageView);

@interface QT_MANGLE_NAMESPACE(QNSStatusItem) : NSObject {
@interface QT_MANGLE_NAMESPACE(QNSStatusItem) : NSObject
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
<NSUserNotificationCenterDelegate>
#endif
{
NSStatusItem *item;
QSystemTrayIcon *icon;
QSystemTrayIconPrivate *iconPrivate;
Expand All @@ -112,6 +116,11 @@ -(NSStatusItem*)item;
-(QRectF)geometry;
- (void)triggerSelector:(id)sender button:(Qt::MouseButton)mouseButton;
- (void)doubleClickSelector:(id)sender;

#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification;
- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification;
#endif
@end

@interface QT_MANGLE_NAMESPACE(QNSImageView) : NSImageView {
Expand Down Expand Up @@ -148,12 +157,27 @@ -(void)selectedAction:(id)item;
QSystemTrayIconSys(QSystemTrayIcon *icon, QSystemTrayIconPrivate *d) {
QMacCocoaAutoReleasePool pool;
item = [[QT_MANGLE_NAMESPACE(QNSStatusItem) alloc] initWithIcon:icon iconPrivate:d];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_8) {
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:item];
}
#endif
}
~QSystemTrayIconSys() {
QMacCocoaAutoReleasePool pool;
[[[item item] view] setHidden: YES];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_8) {
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:nil];
}
#endif
[item release];
}

void emitMessageClicked() {
emit [item icon]->messageClicked();
}

QT_MANGLE_NAMESPACE(QNSStatusItem) *item;
};

Expand Down Expand Up @@ -233,70 +257,80 @@ -(void)selectedAction:(id)item;

void QSystemTrayIconPrivate::showMessage_sys(const QString &title, const QString &message, QSystemTrayIcon::MessageIcon icon, int)
{
if (!sys)
return;

#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_8) {
NSUserNotification *notification = [[NSUserNotification alloc] init];
notification.title = [NSString stringWithUTF8String:title.toUtf8().data()];
notification.informativeText = [NSString stringWithUTF8String:message.toUtf8().data()];

[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];

return;
}
#endif

if(sys) {
#ifdef QT_MAC_SYSTEMTRAY_USE_GROWL
// Make sure that we have Growl installed on the machine we are running on.
QCFType<CFURLRef> cfurl;
OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator,
CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl);
if (status == kLSApplicationNotFoundErr)
return;
QCFType<CFBundleRef> bundle = CFBundleCreate(0, cfurl);

if (CFStringCompare(CFBundleGetIdentifier(bundle), CFSTR("com.Growl.GrowlHelperApp"),
kCFCompareCaseInsensitive | kCFCompareBackwards) != kCFCompareEqualTo)
return;
QPixmap notificationIconPixmap;
if(icon == QSystemTrayIcon::Information)
notificationIconPixmap = QApplication::style()->standardPixmap(QStyle::SP_MessageBoxInformation);
else if(icon == QSystemTrayIcon::Warning)
notificationIconPixmap = QApplication::style()->standardPixmap(QStyle::SP_MessageBoxWarning);
else if(icon == QSystemTrayIcon::Critical)
notificationIconPixmap = QApplication::style()->standardPixmap(QStyle::SP_MessageBoxCritical);
QTemporaryFile notificationIconFile;
QString notificationType(QLatin1String("Notification")), notificationIcon, notificationApp(QApplication::applicationName());
if(notificationApp.isEmpty())
notificationApp = QLatin1String("Application");
if(!notificationIconPixmap.isNull() && notificationIconFile.open()) {
QImageWriter writer(&notificationIconFile, "PNG");
if(writer.write(notificationIconPixmap.toImage()))
notificationIcon = QLatin1String("image from location \"file://") + notificationIconFile.fileName() + QLatin1String("\"");
}
const QString script(QLatin1String(
"tell application \"GrowlHelperApp\"\n"
"-- Make a list of all the notification types (all)\n"
"set the allNotificationsList to {\"") + notificationType + QLatin1String("\"}\n"

"-- Make a list of the notifications (enabled)\n"
"set the enabledNotificationsList to {\"") + notificationType + QLatin1String("\"}\n"

"-- Register our script with growl.\n"
"register as application \"") + notificationApp + QLatin1String("\" all notifications allNotificationsList default notifications enabledNotificationsList\n"

"-- Send a Notification...\n") +
QLatin1String("notify with name \"") + notificationType +
QLatin1String("\" title \"") + title +
QLatin1String("\" description \"") + message +
QLatin1String("\" application name \"") + notificationApp +
QLatin1String("\" ") + notificationIcon +
QLatin1String("\nend tell"));
qt_mac_execute_apple_script(script, 0);
#elif 0
Q_Q(QSystemTrayIcon);
NSView *v = [[sys->item item] view];
NSWindow *w = [v window];
w = [[sys->item item] window];
qDebug() << w << v;
QPoint p(qRound([w frame].origin.x), qRound([w frame].origin.y));
qDebug() << p;
QBalloonTip::showBalloon(icon, message, title, q, QPoint(0, 0), msecs);
// Make sure that we have Growl installed on the machine we are running on.
QCFType<CFURLRef> cfurl;
OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator,
CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl);
if (status == kLSApplicationNotFoundErr)
return;
QCFType<CFBundleRef> bundle = CFBundleCreate(0, cfurl);

if (CFStringCompare(CFBundleGetIdentifier(bundle), CFSTR("com.Growl.GrowlHelperApp"),
kCFCompareCaseInsensitive | kCFCompareBackwards) != kCFCompareEqualTo)
return;

QPixmap notificationIconPixmap;
if (icon == QSystemTrayIcon::Information)
notificationIconPixmap = QApplication::style()->standardPixmap(QStyle::SP_MessageBoxInformation);
else if (icon == QSystemTrayIcon::Warning)
notificationIconPixmap = QApplication::style()->standardPixmap(QStyle::SP_MessageBoxWarning);
else if (icon == QSystemTrayIcon::Critical)
notificationIconPixmap = QApplication::style()->standardPixmap(QStyle::SP_MessageBoxCritical);

QTemporaryFile notificationIconFile;
QString notificationType(QLatin1String("Notification")), notificationIcon, notificationApp(QApplication::applicationName());
if (notificationApp.isEmpty())
notificationApp = QLatin1String("Application");
if (!notificationIconPixmap.isNull() && notificationIconFile.open()) {
QImageWriter writer(&notificationIconFile, "PNG");
if (writer.write(notificationIconPixmap.toImage()))
notificationIcon = QLatin1String("image from location \"file://") + notificationIconFile.fileName() + QLatin1String("\"");
}
const QString script(QLatin1String(
"tell application \"System Events\"\n"
"set isRunning to (count of (every process whose bundle identifier is \"com.Growl.GrowlHelperApp\")) > 0\n"
"end tell\n"
"if isRunning\n"
"tell application id \"com.Growl.GrowlHelperApp\"\n"
"-- Make a list of all the notification types (all)\n"
"set the allNotificationsList to {\"") + notificationType + QLatin1String("\"}\n"

"-- Make a list of the notifications (enabled)\n"
"set the enabledNotificationsList to {\"") + notificationType + QLatin1String("\"}\n"

"-- Register our script with growl.\n"
"register as application \"") + notificationApp + QLatin1String("\" all notifications allNotificationsList default notifications enabledNotificationsList\n"

"-- Send a Notification...\n") +
QLatin1String("notify with name \"") + notificationType +
QLatin1String("\" title \"") + title +
QLatin1String("\" description \"") + message +
QLatin1String("\" application name \"") + notificationApp +
QLatin1String("\" ") + notificationIcon +
QLatin1String("\nend tell\nend if"));
qt_mac_execute_apple_script(script, 0);
#else
Q_UNUSED(icon);
Q_UNUSED(title);
Q_UNUSED(message);
Q_UNUSED(icon);
Q_UNUSED(title);
Q_UNUSED(message);
#endif
}

}
QT_END_NAMESPACE

Expand Down Expand Up @@ -357,7 +391,7 @@ -(void)mousePressed:(NSEvent *)mouseEvent button:(Qt::MouseButton)mouseButton
[nsaltimage release];
}

if ((clickCount == 2)) {
if (clickCount == 2) {
[self menuTrackingDone:nil];
[parent doubleClickSelector:self];
} else {
Expand Down Expand Up @@ -475,6 +509,20 @@ - (void)doubleClickSelector:(id)sender {
qtsystray_sendActivated(icon, QSystemTrayIcon::DoubleClick);
}

#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
- (BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification {
Q_UNUSED(center);
Q_UNUSED(notification);
return YES;
}

- (void)userNotificationCenter:(NSUserNotificationCenter *)center didActivateNotification:(NSUserNotification *)notification {
Q_UNUSED(center);
Q_UNUSED(notification);
emit iconPrivate->sys->emitMessageClicked();
}
#endif

@end

class QSystemTrayIconQMenu : public QMenu
Expand Down

0 comments on commit 6555ba0

Please sign in to comment.