From 6555ba030a29d995b2f53814b7999b81920fa0ce Mon Sep 17 00:00:00 2001 From: Samuel Gaist Date: Sun, 20 Jul 2014 16:41:27 +0200 Subject: Backport implementation of OS X QSystemTrayIcon from Qt 5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/gui/util/qsystemtrayicon_mac.mm | 172 +++++++++++++++++++++++------------- 1 file changed, 110 insertions(+), 62 deletions(-) diff --git a/src/gui/util/qsystemtrayicon_mac.mm b/src/gui/util/qsystemtrayicon_mac.mm index bcb3e07..7f8647c 100644 --- a/src/gui/util/qsystemtrayicon_mac.mm +++ b/src/gui/util/qsystemtrayicon_mac.mm @@ -99,7 +99,11 @@ QT_USE_NAMESPACE @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 + +#endif +{ NSStatusItem *item; QSystemTrayIcon *icon; QSystemTrayIconPrivate *iconPrivate; @@ -112,6 +116,11 @@ QT_USE_NAMESPACE -(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 { @@ -148,12 +157,27 @@ public: 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; }; @@ -233,70 +257,80 @@ bool QSystemTrayIconPrivate::supportsMessages_sys() 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 cfurl; - OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, - CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl); - if (status == kLSApplicationNotFoundErr) - return; - QCFType 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(¬ificationIconFile, "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 cfurl; + OSStatus status = LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, + CFSTR("growlTicket"), kLSRolesAll, 0, &cfurl); + if (status == kLSApplicationNotFoundErr) + return; + QCFType 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(¬ificationIconFile, "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 @@ -357,7 +391,7 @@ QT_END_NAMESPACE [nsaltimage release]; } - if ((clickCount == 2)) { + if (clickCount == 2) { [self menuTrackingDone:nil]; [parent doubleClickSelector:self]; } else { @@ -475,6 +509,20 @@ QT_END_NAMESPACE 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 -- cgit v0.12