From: Joseph Spiros Date: Sun, 1 Mar 2009 08:10:43 +0000 (-0500) Subject: Initial commit of GrowlITTSW. Currently, all settings are hardcoded. X-Git-Tag: v1.0~5 X-Git-Url: http://git.ithinksw.org/GrowlITTSW.git/commitdiff_plain/1355f099f7c9b800fe305e04ae4fbe89e440c0d7 Initial commit of GrowlITTSW. Currently, all settings are hardcoded. --- 1355f099f7c9b800fe305e04ae4fbe89e440c0d7 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ab810ff --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.pbxuser +*.mode1v3 +.DS_Store diff --git a/English.lproj/GrowlITTSWPrefs.nib/classes.nib b/English.lproj/GrowlITTSWPrefs.nib/classes.nib new file mode 100644 index 0000000..1f69d3a --- /dev/null +++ b/English.lproj/GrowlITTSWPrefs.nib/classes.nib @@ -0,0 +1,73 @@ + + + + + IBClasses + + + CLASS + NSView + LANGUAGE + ObjC + SUPERCLASS + NSResponder + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + CLASS + NSObject + LANGUAGE + ObjC + + + CLASS + NSPreferencePane + LANGUAGE + ObjC + OUTLETS + + _firstKeyView + NSView + _initialKeyView + NSView + _lastKeyView + NSView + _window + NSWindow + + SUPERCLASS + NSObject + + + CLASS + NSWindow + LANGUAGE + ObjC + SUPERCLASS + NSResponder + + + CLASS + GrowlITTSWPrefs + LANGUAGE + ObjC + OUTLETS + + slider_opacity + NSSlider + + SUPERCLASS + NSPreferencePane + + + IBVersion + 1 + + diff --git a/English.lproj/GrowlITTSWPrefs.nib/info.nib b/English.lproj/GrowlITTSWPrefs.nib/info.nib new file mode 100644 index 0000000..5155f4a --- /dev/null +++ b/English.lproj/GrowlITTSWPrefs.nib/info.nib @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 672 + IBLastKnownRelativeProjectPath + ../../../../Growl.xcodeproj + IBOldestOS + 3 + IBOpenObjects + + 6 + + IBSystem Version + 9G55 + targetFramework + IBCocoaFramework + + diff --git a/English.lproj/GrowlITTSWPrefs.nib/keyedobjects.nib b/English.lproj/GrowlITTSWPrefs.nib/keyedobjects.nib new file mode 100644 index 0000000..8994fc5 Binary files /dev/null and b/English.lproj/GrowlITTSWPrefs.nib/keyedobjects.nib differ diff --git a/English.lproj/InfoPlist.strings b/English.lproj/InfoPlist.strings new file mode 100644 index 0000000..5e45963 Binary files /dev/null and b/English.lproj/InfoPlist.strings differ diff --git a/GrowlApplicationNotification.h b/GrowlApplicationNotification.h new file mode 100644 index 0000000..2c99ef5 --- /dev/null +++ b/GrowlApplicationNotification.h @@ -0,0 +1,72 @@ +// +// GrowlApplicationNotification.h +// Growl +// +// Created by Mac-arena the Bored Zo on 2005-07-31. +// Copyright 2005-2006 The Growl Project. All rights reserved. +// + +@interface GrowlApplicationNotification: NSObject +{ + NSString *name, *applicationName; + NSString *title, *description; + NSAttributedString *attributedTitle, *attributedDescription; + + NSDictionary *dictionary, *auxiliaryDictionary; + + unsigned GANReserved: 30; +} + ++ (GrowlApplicationNotification *) notificationWithDictionary:(NSDictionary *)dict; + +- (GrowlApplicationNotification *) initWithDictionary:(NSDictionary *)dict; + +//you can pass nil for description. +- (GrowlApplicationNotification *) initWithName:(NSString *)newName + applicationName:(NSString *)newAppName + title:(NSString *)newTitle + description:(NSString *)newDesc; + +//you can pass nil for description. +- (GrowlApplicationNotification *) initWithName:(NSString *)newName + applicationName:(NSString *)newAppName + title:(NSString *)newTitle + description:(NSString *)newDesc; + +#pragma mark - + +/*as of 0.8, this returns: + * * GROWL_NOTIFICATION_NAME + * * GROWL_APP_NAME + * * GROWL_NOTIFICATION_TITLE + * * GROWL_NOTIFICATION_DESCRIPTION + *you can pass this set to -dictionaryRepresentationWithKeys:. + */ ++ (NSSet *) standardKeys; + +//same as dictionaryRepresentationWithKeys:nil. +- (NSDictionary *) dictionaryRepresentation; + +/*with nil, returns all of the standard keys plus the auxiliary dictionary. + *with non-nil, returns only the keys (from internal storage plus the auxiliary + * dictionary) that are in the set. + *in other words, returns the intersection of the standard dictionary keys, the + * auxiliary dictionary, and the provided keys. + */ +- (NSDictionary *) dictionaryRepresentationWithKeys:(NSSet *)keys; + +#pragma mark - + +- (NSString *) name; +- (NSString *) applicationName; + +- (NSString *) title; +- (NSAttributedString *) attributedTitle; + +- (NSString *) notificationDescription; +- (NSAttributedString *) attributedDescription; + +- (NSDictionary *) auxiliaryDictionary; +- (void) setAuxiliaryDictionary:(NSDictionary *)newAuxDict; + +@end diff --git a/GrowlDefines.h b/GrowlDefines.h new file mode 100644 index 0000000..2b971cf --- /dev/null +++ b/GrowlDefines.h @@ -0,0 +1,348 @@ +// +// GrowlDefines.h +// + +#ifndef _GROWLDEFINES_H +#define _GROWLDEFINES_H + +#ifdef __OBJC__ +#define XSTR(x) (@x) +#define STRING_TYPE NSString * +#else +#define XSTR CFSTR +#define STRING_TYPE CFStringRef +#endif + +/*! @header GrowlDefines.h + * @abstract Defines all the notification keys. + * @discussion Defines all the keys used for registration with Growl and for + * Growl notifications. + * + * Most applications should use the functions or methods of Growl.framework + * instead of posting notifications such as those described here. + * @updated 2004-01-25 + */ + +// UserInfo Keys for Registration +#pragma mark UserInfo Keys for Registration + +/*! @group Registration userInfo keys */ +/* @abstract Keys for the userInfo dictionary of a GROWL_APP_REGISTRATION distributed notification. + * @discussion The values of these keys describe the application and the + * notifications it may post. + * + * Your application must register with Growl before it can post Growl + * notifications (and have them not be ignored). However, as of Growl 0.6, + * posting GROWL_APP_REGISTRATION notifications directly is no longer the + * preferred way to register your application. Your application should instead + * use Growl.framework's delegate system. + * See +[GrowlApplicationBridge setGrowlDelegate:] or Growl_SetDelegate for + * more information. + */ + +/*! @defined GROWL_APP_NAME + * @abstract The name of your application. + * @discussion The name of your application. This should remain stable between + * different versions and incarnations of your application. + * For example, "SurfWriter" is a good app name, whereas "SurfWriter 2.0" and + * "SurfWriter Lite" are not. + */ +#define GROWL_APP_NAME XSTR("ApplicationName") +/*! @defined GROWL_APP_ID + * @abstract The bundle identifier of your application. + * @discussion The bundle identifier of your application. This key should + * be unique for your application while there may be several applications + * with the same GROWL_APP_NAME. + * This key is optional. + */ +#define GROWL_APP_ID XSTR("ApplicationId") +/*! @defined GROWL_APP_ICON + * @abstract The image data for your application's icon. + * @discussion Image data representing your application's icon. This may be + * superimposed on a notification icon as a badge, used as the notification + * icon when a notification-specific icon is not supplied, or ignored + * altogether, depending on the display. Must be in a format supported by + * NSImage, such as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF. + * + * Optional. Not supported by all display plugins. + */ +#define GROWL_APP_ICON XSTR("ApplicationIcon") +/*! @defined GROWL_NOTIFICATIONS_DEFAULT + * @abstract The array of notifications to turn on by default. + * @discussion These are the names of the notifications that should be enabled + * by default when your application registers for the first time. If your + * application reregisters, Growl will look here for any new notification + * names found in GROWL_NOTIFICATIONS_ALL, but ignore any others. + */ +#define GROWL_NOTIFICATIONS_DEFAULT XSTR("DefaultNotifications") +/*! @defined GROWL_NOTIFICATIONS_ALL + * @abstract The array of all notifications your application can send. + * @discussion These are the names of all of the notifications that your + * application may post. See GROWL_NOTIFICATION_NAME for a discussion of good + * notification names. + */ +#define GROWL_NOTIFICATIONS_ALL XSTR("AllNotifications") +/*! @defined GROWL_NOTIFICATIONS_HUMAN_READABLE_DESCRIPTIONS + * @abstract A dictionary of human-readable names for your notifications. + * @discussion By default, the Growl UI will display notifications by the names given in GROWL_NOTIFICATIONS_ALL + * which correspond to the GROWL_NOTIFICATION_NAME. This dictionary specifies the human-readable name to display. + * The keys of the dictionary are GROWL_NOTIFICATION_NAME strings; the objects are the human-readable versions. + * For any GROWL_NOTIFICATION_NAME not specific in this dictionary, the GROWL_NOTIFICATION_NAME will be displayed. + * + * This key is optional. + */ +#define GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES XSTR("HumanReadableNames") +/*! @defined GROWL_NOTIFICATIONS_DESCRIPTIONS +* @abstract A dictionary of descriptions of _when_ each notification occurs +* @discussion This is an NSDictionary whose keys are GROWL_NOTIFICATION_NAME strings and whose objects are +* descriptions of _when_ each notification occurs, such as "You received a new mail message" or +* "A file finished downloading". +* +* This key is optional. +*/ +#define GROWL_NOTIFICATIONS_DESCRIPTIONS XSTR("NotificationDescriptions") + +/*! @defined GROWL_TICKET_VERSION + * @abstract The version of your registration ticket. + * @discussion Include this key in a ticket plist file that you put in your + * application bundle for auto-discovery. The current ticket version is 1. + */ +#define GROWL_TICKET_VERSION XSTR("TicketVersion") +// UserInfo Keys for Notifications +#pragma mark UserInfo Keys for Notifications + +/*! @group Notification userInfo keys */ +/* @abstract Keys for the userInfo dictionary of a GROWL_NOTIFICATION distributed notification. + * @discussion The values of these keys describe the content of a Growl + * notification. + * + * Not all of these keys are supported by all displays. Only the name, title, + * and description of a notification are universal. Most of the built-in + * displays do support all of these keys, and most other visual displays + * probably will also. But, as of 0.6, the Log, MailMe, and Speech displays + * support only textual data. + */ + +/*! @defined GROWL_NOTIFICATION_NAME + * @abstract The name of the notification. + * @discussion The name of the notification. Note that if you do not define + * GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES when registering your ticket originally this name + * will the one displayed within the Growl preference pane and should be human-readable. + */ +#define GROWL_NOTIFICATION_NAME XSTR("NotificationName") +/*! @defined GROWL_NOTIFICATION_TITLE + * @abstract The title to display in the notification. + * @discussion The title of the notification. Should be very brief. + * The title usually says what happened, e.g. "Download complete". + */ +#define GROWL_NOTIFICATION_TITLE XSTR("NotificationTitle") +/*! @defined GROWL_NOTIFICATION_DESCRIPTION + * @abstract The description to display in the notification. + * @discussion The description should be longer and more verbose than the title. + * The description usually tells the subject of the action, + * e.g. "Growl-0.6.dmg downloaded in 5.02 minutes". + */ +#define GROWL_NOTIFICATION_DESCRIPTION XSTR("NotificationDescription") +/*! @defined GROWL_NOTIFICATION_ICON + * @discussion Image data for the notification icon. Must be in a format + * supported by NSImage, such as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF. + * + * Optional. Not supported by all display plugins. + */ +#define GROWL_NOTIFICATION_ICON XSTR("NotificationIcon") +/*! @defined GROWL_NOTIFICATION_APP_ICON + * @discussion Image data for the application icon, in case GROWL_APP_ICON does + * not apply for some reason. Must be in a format supported by NSImage, such + * as TIFF, PNG, GIF, JPEG, BMP, PICT, or PDF. + * + * Optional. Not supported by all display plugins. + */ +#define GROWL_NOTIFICATION_APP_ICON XSTR("NotificationAppIcon") +/*! @defined GROWL_NOTIFICATION_PRIORITY + * @discussion The priority of the notification as an integer number from + * -2 to +2 (+2 being highest). + * + * Optional. Not supported by all display plugins. + */ +#define GROWL_NOTIFICATION_PRIORITY XSTR("NotificationPriority") +/*! @defined GROWL_NOTIFICATION_STICKY + * @discussion A Boolean number controlling whether the notification is sticky. + * + * Optional. Not supported by all display plugins. + */ +#define GROWL_NOTIFICATION_STICKY XSTR("NotificationSticky") +/*! @defined GROWL_NOTIFICATION_CLICK_CONTEXT + * @abstract Identifies which notification was clicked. + * @discussion An identifier for the notification for clicking purposes. + * + * This will be passed back to the application when the notification is + * clicked. It must be plist-encodable (a data, dictionary, array, number, or + * string object), and it should be unique for each notification you post. + * A good click context would be a UUID string returned by NSProcessInfo or + * CFUUID. + * + * Optional. Not supported by all display plugins. + */ +#define GROWL_NOTIFICATION_CLICK_CONTEXT XSTR("NotificationClickContext") + +/*! @defined GROWL_DISPLAY_PLUGIN + * @discussion The name of a display plugin which should be used for this notification. + * Optional. If this key is not set or the specified display plugin does not + * exist, the display plugin stored in the application ticket is used. This key + * allows applications to use different default display plugins for their + * notifications. The user can still override those settings in the preference + * pane. + */ +#define GROWL_DISPLAY_PLUGIN XSTR("NotificationDisplayPlugin") + +/*! @defined GROWL_NOTIFICATION_IDENTIFIER + * @abstract An identifier for the notification for coalescing purposes. + * Notifications with the same identifier fall into the same class; only + * the last notification of a class is displayed on the screen. If a + * notification of the same class is currently being displayed, it is + * replaced by this notification. + * + * Optional. Not supported by all display plugins. + */ +#define GROWL_NOTIFICATION_IDENTIFIER XSTR("GrowlNotificationIdentifier") + +/*! @defined GROWL_APP_PID + * @abstract The process identifier of the process which sends this + * notification. If this field is set, the application will only receive + * clicked and timed out notifications which originate from this process. + * + * Optional. + */ +#define GROWL_APP_PID XSTR("ApplicationPID") + +/*! @defined GROWL_NOTIFICATION_PROGRESS +* @abstract If this key is set, it should contain a double value wrapped +* in a NSNumber which describes some sort of progress (from 0.0 to 100.0). +* If this is key is not set, no progress bar is shown. +* +* Optional. Not supported by all display plugins. +*/ +#define GROWL_NOTIFICATION_PROGRESS XSTR("NotificationProgress") + +// Notifications +#pragma mark Notifications + +/*! @group Notification names */ +/* @abstract Names of distributed notifications used by Growl. + * @discussion These are notifications used by applications (directly or + * indirectly) to interact with Growl, and by Growl for interaction between + * its components. + * + * Most of these should no longer be used in Growl 0.6 and later, in favor of + * Growl.framework's GrowlApplicationBridge APIs. + */ + +/*! @defined GROWL_APP_REGISTRATION + * @abstract The distributed notification for registering your application. + * @discussion This is the name of the distributed notification that can be + * used to register applications with Growl. + * + * The userInfo dictionary for this notification can contain these keys: + * + * + * No longer recommended as of Growl 0.6. An alternate method of registering + * is to use Growl.framework's delegate system. + * See +[GrowlApplicationBridge setGrowlDelegate:] or Growl_SetDelegate for + * more information. + */ +#define GROWL_APP_REGISTRATION XSTR("GrowlApplicationRegistrationNotification") +/*! @defined GROWL_APP_REGISTRATION_CONF + * @abstract The distributed notification for confirming registration. + * @discussion The name of the distributed notification sent to confirm the + * registration. Used by the Growl preference pane. Your application probably + * does not need to use this notification. + */ +#define GROWL_APP_REGISTRATION_CONF XSTR("GrowlApplicationRegistrationConfirmationNotification") +/*! @defined GROWL_NOTIFICATION + * @abstract The distributed notification for Growl notifications. + * @discussion This is what it all comes down to. This is the name of the + * distributed notification that your application posts to actually send a + * Growl notification. + * + * The userInfo dictionary for this notification can contain these keys: + * + * + * No longer recommended as of Growl 0.6. Three alternate methods of posting + * notifications are +[GrowlApplicationBridge notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:], + * Growl_NotifyWithTitleDescriptionNameIconPriorityStickyClickContext, and + * Growl_PostNotification. + */ +#define GROWL_NOTIFICATION XSTR("GrowlNotification") +/*! @defined GROWL_SHUTDOWN +* @abstract The distributed notification name that tells Growl to shutdown. +* @discussion The Growl preference pane posts this notification when the +* "Stop Growl" button is clicked. +*/ +#define GROWL_SHUTDOWN XSTR("GrowlShutdown") +/*! @defined GROWL_PING + * @abstract A distributed notification to check whether Growl is running. + * @discussion This is used by the Growl preference pane. If it receives a + * GROWL_PONG, the preference pane takes this to mean that Growl is running. + */ +#define GROWL_PING XSTR("Honey, Mind Taking Out The Trash") +/*! @defined GROWL_PONG + * @abstract The distributed notification sent in reply to GROWL_PING. + * @discussion GrowlHelperApp posts this in reply to GROWL_PING. + */ +#define GROWL_PONG XSTR("What Do You Want From Me, Woman") +/*! @defined GROWL_IS_READY + * @abstract The distributed notification sent when Growl starts up. + * @discussion GrowlHelperApp posts this when it has begin listening on all of + * its sources for new notifications. GrowlApplicationBridge (in + * Growl.framework), upon receiving this notification, reregisters using the + * registration dictionary supplied by its delegate. + */ +#define GROWL_IS_READY XSTR("Lend Me Some Sugar; I Am Your Neighbor!") +/*! @defined GROWL_NOTIFICATION_CLICKED + * @abstract The distributed notification sent when a supported notification is clicked. + * @discussion When a Growl notification with a click context is clicked on by + * the user, Growl posts this distributed notification. + * The GrowlApplicationBridge responds to this notification by calling a + * callback in its delegate. + */ +#define GROWL_NOTIFICATION_CLICKED XSTR("GrowlClicked!") +#define GROWL_NOTIFICATION_TIMED_OUT XSTR("GrowlTimedOut!") + +/*! @group Other symbols */ +/* Symbols which don't fit into any of the other categories. */ + +/*! @defined GROWL_KEY_CLICKED_CONTEXT + * @abstract Used internally as the key for the clickedContext passed over DNC. + * @discussion This key is used in GROWL_NOTIFICATION_CLICKED, and contains the + * click context that was supplied in the original notification. + */ +#define GROWL_KEY_CLICKED_CONTEXT XSTR("ClickedContext") +/*! @defined GROWL_REG_DICT_EXTENSION + * @abstract The filename extension for registration dictionaries. + * @discussion The GrowlApplicationBridge in Growl.framework registers with + * Growl by creating a file with the extension of .(GROWL_REG_DICT_EXTENSION) + * and opening it in the GrowlHelperApp. This happens whether or not Growl is + * running; if it was stopped, it quits immediately without listening for + * notifications. + */ +#define GROWL_REG_DICT_EXTENSION XSTR("growlRegDict") + + +#define GROWL_POSITION_PREFERENCE_KEY @"GrowlSelectedPosition" + +#endif //ndef _GROWLDEFINES_H diff --git a/GrowlDefinesInternal.h b/GrowlDefinesInternal.h new file mode 100644 index 0000000..14067e3 --- /dev/null +++ b/GrowlDefinesInternal.h @@ -0,0 +1,430 @@ +// +// GrowlDefinesInternal.h +// Growl +// +// Created by Karl Adam on Mon May 17 2004. +// Copyright (c) 2004 the Growl Project. All rights reserved. +// + +#ifndef _GROWL_GROWLDEFINESINTERNAL_H +#define _GROWL_GROWLDEFINESINTERNAL_H + +#include +#include +#include + +#ifdef __OBJC__ +#define XSTR(x) (@x) +#else /* !__OBJC__ */ +#define XSTR CFSTR +#endif /* __OBJC__ */ + +/*! @header GrowlDefinesInternal.h + * @abstract Defines internal Growl macros and types. + * @ignore ATTRIBUTE_PACKED + * @discussion These constants are used both by GrowlHelperApp and by plug-ins. + * + * Notification keys (used in GrowlHelperApp, in GrowlApplicationBridge, and + * by applications that don't use GrowlApplicationBridge) are defined in + * GrowlDefines.h. + */ + +/*! @defined GROWL_TCP_PORT + * @abstract The TCP listen port for Growl notification servers. + */ +#define GROWL_TCP_PORT 23052 + +/*! @defined GROWL_UDP_PORT + * @abstract The UDP listen port for Growl notification servers. + */ +#define GROWL_UDP_PORT 9887 + +/*! @defined GROWL_PROTOCOL_VERSION + * @abstract The current version of the Growl network-notifications protocol (without encryption). + */ +#define GROWL_PROTOCOL_VERSION 1 + +/*! @defined GROWL_PROTOCOL_VERSION_AES128 +* @abstract The current version of the Growl network-notifications protocol (with AES-128 encryption). +*/ +#define GROWL_PROTOCOL_VERSION_AES128 2 + +/*! @defined GROWL_TYPE_REGISTRATION + * @abstract The packet type of registration packets with MD5 authentication. + */ +#define GROWL_TYPE_REGISTRATION 0 +/*! @defined GROWL_TYPE_NOTIFICATION + * @abstract The packet type of notification packets with MD5 authentication. + */ +#define GROWL_TYPE_NOTIFICATION 1 +/*! @defined GROWL_TYPE_REGISTRATION_SHA256 + * @abstract The packet type of registration packets with SHA-256 authentication. + */ +#define GROWL_TYPE_REGISTRATION_SHA256 2 +/*! @defined GROWL_TYPE_NOTIFICATION_SHA256 + * @abstract The packet type of notification packets with SHA-256 authentication. + */ +#define GROWL_TYPE_NOTIFICATION_SHA256 3 +/*! @defined GROWL_TYPE_REGISTRATION_NOAUTH +* @abstract The packet type of registration packets without authentication. +*/ +#define GROWL_TYPE_REGISTRATION_NOAUTH 4 +/*! @defined GROWL_TYPE_NOTIFICATION_NOAUTH +* @abstract The packet type of notification packets without authentication. +*/ +#define GROWL_TYPE_NOTIFICATION_NOAUTH 5 + +#define ATTRIBUTE_PACKED __attribute((packed)) + +/*! @struct GrowlNetworkPacket + * @abstract This struct is a header common to all incoming Growl network + * packets which identifies the type and version of the packet. + */ +struct GrowlNetworkPacket { + unsigned char version; + unsigned char type; +} ATTRIBUTE_PACKED; + +/*! + * @struct GrowlNetworkRegistration + * @abstract The format of a registration packet. + * @discussion A Growl client that wants to register with a Growl server sends + * a packet in this format. + * @field common The Growl packet header. + * @field appNameLen The name of the application that is registering. + * @field numAllNotifications The number of notifications in the list. + * @field numDefaultNotifications The number of notifications in the list that are enabled by default. + * @field data Variable-sized data. + */ +struct GrowlNetworkRegistration { + struct GrowlNetworkPacket common; + /* This name is used both internally and in the Growl + * preferences. + * + * The application name should remain stable between different versions + * and incarnations of your application. + * For example, "SurfWriter" is a good app name, whereas "SurfWriter 2.0" + * and "SurfWriter Lite" are not. + * + * In addition to being unsigned, the application name length is in + * network byte order. + */ + unsigned short appNameLen; + /* These names are used both internally and in the Growl + * preferences. For this reason, they should be human-readable. + */ + unsigned char numAllNotifications; + + unsigned char numDefaultNotifications; + /* The variable-sized data of a registration is: + * - The application name, in UTF-8 encoding, for appNameLen bytes. + * - The list of all notification names. + * - The list of default notifications, as 8-bit unsigned indices into the list of all notifications. + * - The MD5/SHA256 checksum of all the data preceding the checksum. + * + * Each notification name is encoded as: + * - Length: two bytes, unsigned, network byte order. + * - Name: As many bytes of UTF-8-encoded text as the length says. + * And there are numAllNotifications of these. + */ + unsigned char data[]; +} ATTRIBUTE_PACKED; + +/*! + * @struct GrowlNetworkNotification + * @abstract The format of a notification packet. + * @discussion A Growl client that wants to post a notification to a Growl + * server sends a packet in this format. + * @field common The Growl packet header. + * @field flags The priority number and the sticky bit. + * @field nameLen The length of the notification name. + * @field titleLen The length of the notification title. + * @field descriptionLen The length of the notification description. + * @field appNameLen The length of the application name. + * @field data Variable-sized data. + */ +struct GrowlNetworkNotification { + struct GrowlNetworkPacket common; + /*! + * @struct GrowlNetworkNotificationFlags + * @abstract Various flags. + * @discussion This 16-bit packed structure contains the priority as a + * signed 3-bit integer from -2 to +2, and the sticky flag as a single bit. + * The high 12 bits of the structure are reserved for future use. + * @field reserved reserved for future use. + * @field priority the priority as a signed 3-bit integer from -2 to +2. + * @field sticky the sticky flag. + */ + struct GrowlNetworkNotificationFlags { +#ifdef __BIG_ENDIAN__ + unsigned reserved: 12; + signed priority: 3; + unsigned sticky: 1; +#else + unsigned sticky: 1; + signed priority: 3; + unsigned reserved: 12; +#endif + } ATTRIBUTE_PACKED flags; //size = 16 (12 + 3 + 1) + + /* In addition to being unsigned, the notification name length + * is in network byte order. + */ + unsigned short nameLen; + /* @discussion In addition to being unsigned, the title length is in + * network byte order. + */ + unsigned short titleLen; + /* In addition to being unsigned, the description length is in + * network byte order. + */ + unsigned short descriptionLen; + /* In addition to being unsigned, the application name length + * is in network byte order. + */ + unsigned short appNameLen; + /* The variable-sized data of a notification is: + * - Notification name, in UTF-8 encoding, for nameLen bytes. + * - Title, in UTF-8 encoding, for titleLen bytes. + * - Description, in UTF-8 encoding, for descriptionLen bytes. + * - Application name, in UTF-8 encoding, for appNameLen bytes. + * - The MD5/SHA256 checksum of all the data preceding the checksum. + */ + unsigned char data[]; +} ATTRIBUTE_PACKED; + +/*! @defined GrowlEnabledKey + * @abstract Preference key controlling whether Growl is enabled. + * @discussion If this is false, then when GrowlHelperApp is launched to open + * a Growl registration dictionary file, GrowlHelperApp will quit when it has + * finished processing the file instead of listening for notifications. + */ +#define GrowlEnabledKey XSTR("GrowlEnabled") + +/*! @defined GROWL_SCREENSHOT_MODE + * @abstract Preference and notification key controlling whether to save a screenshot of the notification. + * @discussion This is for GHA's private usage. If your application puts this + * key into a notification dictionary, GHA will clobber it. This key is only + * allowed in the notification dictionaries GHA passes to displays. + * + * If this key contains an object whose boolValue is not NO, the display is + * asked to save a screenshot of the notification to + * ~/Library/Application\ Support/Growl/Screenshots. + */ +#define GROWL_SCREENSHOT_MODE XSTR("ScreenshotMode") + +/*! @defined GROWL_APP_LOCATION + * @abstract The location of this application. + * @discussion Contains either the POSIX path to the application, or a file-data dictionary (as used by the Dock). + * contains the file's alias record and its pathname. + */ +#define GROWL_APP_LOCATION XSTR("AppLocation") + +/*! @defined GROWL_REMOTE_ADDRESS + * @abstract The address of the host who sent this notification/registration. + * @discussion Contains an NSData with the address of the remote host who + * sent this notification/registration. + */ +#define GROWL_REMOTE_ADDRESS XSTR("RemoteAddress") + +/*! + * @defined GROWL_PREFPANE_BUNDLE_IDENTIFIER + * @discussion The bundle identifier for the Growl preference pane. + */ +#define GROWL_PREFPANE_BUNDLE_IDENTIFIER XSTR("com.growl.prefpanel") +/*! + * @defined GROWL_HELPERAPP_BUNDLE_IDENTIFIER + * @discussion The bundle identifier for the Growl background application (GrowlHelperApp). + */ +#define GROWL_HELPERAPP_BUNDLE_IDENTIFIER XSTR("com.Growl.GrowlHelperApp") + +/*! + * @defined GROWL_PREFPANE_NAME + * @discussion The file name of the Growl preference pane. + */ +#define GROWL_PREFPANE_NAME XSTR("Growl.prefPane") +#define PREFERENCE_PANES_SUBFOLDER_OF_LIBRARY XSTR("PreferencePanes") +#define PREFERENCE_PANE_EXTENSION XSTR("prefPane") + +//plug-in bundle filename extensions +#define GROWL_PLUGIN_EXTENSION XSTR("growlPlugin") +#define GROWL_PATHWAY_EXTENSION XSTR("growlPathway") +#define GROWL_VIEW_EXTENSION XSTR("growlView") +#define GROWL_STYLE_EXTENSION XSTR("growlStyle") + +/* --- These following macros are intended for plug-ins --- */ + +/*! @function SYNCHRONIZE_GROWL_PREFS + * @abstract Synchronizes Growl prefs so they're up-to-date. + * @discussion This macro is intended for use by GrowlHelperApp and by + * plug-ins (when the prefpane is selected). + */ +#define SYNCHRONIZE_GROWL_PREFS() CFPreferencesAppSynchronize(CFSTR("com.Growl.GrowlHelperApp")) + +/*! @function UPDATE_GROWL_PREFS + * @abstract Tells GrowlHelperApp to update its prefs. + * @discussion This macro is intended for use by plug-ins. + * It sends a notification to tell GrowlHelperApp to update its preferences. + */ +#define UPDATE_GROWL_PREFS() do { \ + SYNCHRONIZE_GROWL_PREFS(); \ + CFStringRef _key = CFSTR("pid"); \ + int pid = getpid(); \ + CFNumberRef _value = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pid); \ + CFDictionaryRef userInfo = CFDictionaryCreate(kCFAllocatorDefault, (const void **)&_key, (const void **)&_value, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); \ + CFRelease(_value); \ + CFNotificationCenterPostNotification(CFNotificationCenterGetDistributedCenter(), \ + CFSTR("GrowlPreferencesChanged"), \ + CFSTR("GrowlUserDefaults"), \ + userInfo, false); \ + CFRelease(userInfo); \ + } while(0) + +/*! @function READ_GROWL_PREF_VALUE + * @abstract Reads the given pref value from the plug-in's preferences. + * @discussion This macro is intended for use by plug-ins. It reads the value for the + * given key from the plug-in's preferences (which are stored in a dictionary inside of + * GrowlHelperApp's prefs). + * @param key The preference key to read the value of. + * @param domain The bundle ID of the plug-in. + * @param type The type of the result expected. + * @param result A pointer to an id. Set to the value if exists, left unchanged if not. + * + * If the value is set, you are responsible for releasing it. + */ +#define READ_GROWL_PREF_VALUE(key, domain, type, result) do {\ + CFDictionaryRef prefs = (CFDictionaryRef)CFPreferencesCopyAppValue((CFStringRef)domain, \ + CFSTR("com.Growl.GrowlHelperApp")); \ + if (prefs) {\ + if (CFDictionaryContainsKey(prefs, key)) {\ + *result = (type)CFDictionaryGetValue(prefs, key); \ + CFRetain(*result); \ + } \ + CFRelease(prefs); } \ + } while(0) + +/*! @function WRITE_GROWL_PREF_VALUE + * @abstract Writes the given pref value to the plug-in's preferences. + * @discussion This macro is intended for use by plug-ins. It writes the given + * value to the plug-in's preferences. + * @param key The preference key to write the value of. + * @param value The value to write to the preferences. It should be either a + * CoreFoundation type or toll-free bridged with one. + * @param domain The bundle ID of the plug-in. + */ +#define WRITE_GROWL_PREF_VALUE(key, value, domain) do {\ + CFDictionaryRef staticPrefs = (CFDictionaryRef)CFPreferencesCopyAppValue((CFStringRef)domain, \ + CFSTR("com.Growl.GrowlHelperApp")); \ + CFMutableDictionaryRef prefs; \ + if (staticPrefs == NULL) {\ + prefs = CFDictionaryCreateMutable(NULL, 0, NULL, NULL); \ + } else {\ + prefs = CFDictionaryCreateMutableCopy(NULL, 0, staticPrefs); \ + CFRelease(staticPrefs); \ + }\ + CFDictionarySetValue(prefs, key, value); \ + CFPreferencesSetAppValue((CFStringRef)domain, prefs, CFSTR("com.Growl.GrowlHelperApp")); \ + CFRelease(prefs); } while(0) + +/*! @function READ_GROWL_PREF_BOOL + * @abstract Reads the given Boolean from the plug-in's preferences. + * @discussion This is a wrapper around READ_GROWL_PREF_VALUE() intended for + * use with Booleans. + * @param key The preference key to read the Boolean from. + * @param domain The bundle ID of the plug-in. + * @param result A pointer to a Boolean type. Left unchanged if the value doesn't exist. + */ +#define READ_GROWL_PREF_BOOL(key, domain, result) do {\ + CFBooleanRef boolValue = NULL; \ + READ_GROWL_PREF_VALUE(key, domain, CFBooleanRef, &boolValue); \ + if (boolValue) {\ + *result = CFBooleanGetValue(boolValue); \ + CFRelease(boolValue); \ + } } while(0) + +/*! @function WRITE_GROWL_PREF_BOOL + * @abstract Writes the given Boolean to the plug-in's preferences. + * @discussion This is a wrapper around WRITE_GROWL_PREF_VALUE() intended for + * use with Booleans. + * @param key The preference key to write the Boolean for. + * @param value The Boolean value to write to the preferences. + * @param domain The bundle ID of the plug-in. + */ +#define WRITE_GROWL_PREF_BOOL(key, value, domain) do {\ + WRITE_GROWL_PREF_VALUE(key, value ? kCFBooleanTrue : kCFBooleanFalse, domain); } while(0) + +/*! @function READ_GROWL_PREF_INT + * @abstract Reads the given integer from the plug-in's preferences. + * @discussion This is a wrapper around READ_GROWL_PREF_VALUE() intended for + * use with integers. + * @param key The preference key to read the integer from. + * @param domain The bundle ID of the plug-in. + * @param result A pointer to an integer. Leaves unchanged if the value doesn't exist. + */ +#define READ_GROWL_PREF_INT(key, domain, result) do {\ + CFNumberRef intValue = NULL; \ + READ_GROWL_PREF_VALUE(key, domain, CFNumberRef, &intValue); \ + if (intValue) {\ + CFNumberGetValue(intValue, kCFNumberIntType, result); \ + CFRelease(intValue); \ + } } while(0) + +/*! @function WRITE_GROWL_PREF_INT + * @abstract Writes the given integer to the plug-in's preferences. + * @discussion This is a wrapper around WRITE_GROWL_PREF_VALUE() intended for + * use with integers. + * @param key The preference key to write the integer for. + * @param value The integer value to write to the preferences. + * @param domain The bundle ID of the plug-in. + */ +#define WRITE_GROWL_PREF_INT(key, value, domain) do {\ + CFNumberRef intValue = CFNumberCreate(NULL, kCFNumberIntType, &value); \ + WRITE_GROWL_PREF_VALUE(key, intValue, domain); \ + CFRelease(intValue); } while(0) + +/*! @function READ_GROWL_PREF_FLOAT + * @abstract Reads the given float from the plug-in's preferences. + * @discussion This is a wrapper around READ_GROWL_PREF_VALUE() intended for + * use with floats. + * @param key The preference key to read the float from. + * @param domain The bundle ID of the plug-in. + * @param result A pointer to a float. Leaves unchanged if the value doesn't exist. + */ +#define READ_GROWL_PREF_FLOAT(key, domain, result) do {\ + CFNumberRef floatValue = NULL; \ + READ_GROWL_PREF_VALUE(key, domain, CFNumberRef, &floatValue); \ + if (floatValue) {\ + CFNumberGetValue(floatValue, kCFNumberFloatType, result); \ + CFRelease(floatValue); \ + } } while(0) + +/*! @function WRITE_GROWL_PREF_FLOAT + * @abstract Writes the given float to the plug-in's preferences. + * @discussion This is a wrapper around WRITE_GROWL_PREF_VALUE() intended for + * use with floats. + * @param key The preference key to write the float for. + * @param value The float value to write to the preferences. + * @param domain The bundle ID of the plug-in. + */ +#define WRITE_GROWL_PREF_FLOAT(key, value, domain) do {\ + CFNumberRef floatValue = CFNumberCreate(NULL, kCFNumberFloatType, &value); \ + WRITE_GROWL_PREF_VALUE(key, floatValue, domain); \ + CFRelease(floatValue); } while(0) + + +/*! @defined GROWL_CLOSE_ALL_NOTIFICATIONS + * @abstract Notification to close all Growl notifications + * @discussion Should be posted to the default notification center when a close widget is option+clicked. + * All notifications should close in response. + */ +#define GROWL_CLOSE_ALL_NOTIFICATIONS XSTR("GrowlCloseAllNotifications") + +#pragma mark Small utilities + +/*! + * @defined FLOAT_EQ(x,y) + * @abstract Compares two floats. + */ +#define FLOAT_EQ(x,y) (((y - FLT_EPSILON) < x) && (x < (y + FLT_EPSILON))) + +#endif //ndef _GROWL_GROWLDEFINESINTERNAL_H diff --git a/GrowlDisplayPlugin.h b/GrowlDisplayPlugin.h new file mode 100644 index 0000000..1cb30c3 --- /dev/null +++ b/GrowlDisplayPlugin.h @@ -0,0 +1,75 @@ +// +// GrowlDisplayPlugin.h +// Growl +// +// Created by Mac-arena the Bored Zo on 2005-06-01. +// Copyright 2005-2006 The Growl Project. All rights reserved. +// + +#import +#import "GrowlPlugin.h" + +@class GrowlApplicationNotification, GrowlNotificationDisplayBridge; +@class GrowlDisplayWindowController; + +//Info.plist keys for plug-in bundles. +extern NSString *GrowlDisplayPluginInfoKeyUsesQueue; +extern NSString *GrowlDisplayPluginInfoKeyWindowNibName; + +/*! + * @class GrowlDisplayPlugin + * @abstract Base class for all display plugins. + */ +@interface GrowlDisplayPlugin : GrowlPlugin { + Class windowControllerClass; + + //for all displays + NSMutableDictionary *coalescableBridges; + + //for non-queueing displays + NSMutableArray *activeBridges; //GrowlNotificationDisplayBridges currently being displayed + + //for queueing displays + GrowlNotificationDisplayBridge *bridge; + NSMutableArray *queue; //GrowlNotificationDisplayBridges yet to be displayed +} + +/*! @method displayNotification: + * @abstract Display a notification to the user. + * @param notification The notification to display. + * @discussion Unless you have a specific reason to override this method you should not do so. + * All the magic should happen in configureBridge: + */ +- (void) displayNotification:(GrowlApplicationNotification *)notification; + +/*! @method configureBridge: + * @abstract Configures the chosen bridge before a notificaion is displayed. + * @param bridge The bridge to configure. + * @discussion This is the place where the magic happens. Override this method and do any + * specific configuration here. This is the last port-of-call before a notification is displayed. + * The default implementation does nothing so it is important that you override and provide an + * implementation. + */ +- (void) configureBridge:(GrowlNotificationDisplayBridge *)theBridge; + +/*! @method windowNibName + * @abstract Returns the name of the display's sole nib file (resulting in + * the creation of a window controller for the window in that file). + * @discussion When subclassing GrowlDisplayPlugin, override this + * method and return the name of the nib (without the ".nib" extension) that + * contains the display window. This method is called by + * displayNotification: to create a + * GrowlNotificationDisplayBridge, which is the File's Owner for + * the nib. + * + * The default implementation returns the value of + * GrowlDisplayWindowNibName in the Info.plist of the bundle for + * the display plug-in. + * @result The name of the window nib. + */ +- (NSString *) windowNibName; + +/* */ +- (void) displayWindowControllerDidTakeDownWindow:(GrowlDisplayWindowController *)wc; + +@end diff --git a/GrowlITTSW.xcodeproj/TemplateIcon.icns b/GrowlITTSW.xcodeproj/TemplateIcon.icns new file mode 100644 index 0000000..62cb701 Binary files /dev/null and b/GrowlITTSW.xcodeproj/TemplateIcon.icns differ diff --git a/GrowlITTSW.xcodeproj/project.pbxproj b/GrowlITTSW.xcodeproj/project.pbxproj new file mode 100644 index 0000000..479620c --- /dev/null +++ b/GrowlITTSW.xcodeproj/project.pbxproj @@ -0,0 +1,468 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C167DFE841241C02AAC07 /* InfoPlist.strings */; }; + 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */; }; + FA4A47250F5A3C2A00F37A2B /* GrowlITTSWDisplay.m in Sources */ = {isa = PBXBuildFile; fileRef = FA4A471E0F5A3C2A00F37A2B /* GrowlITTSWDisplay.m */; }; + FA4A47270F5A3C2A00F37A2B /* GrowlITTSWController.m in Sources */ = {isa = PBXBuildFile; fileRef = FA4A47220F5A3C2A00F37A2B /* GrowlITTSWController.m */; }; + FA4A47280F5A3C2A00F37A2B /* GrowlITTSWWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = FA4A47240F5A3C2A00F37A2B /* GrowlITTSWWindow.m */; }; + FA4A47400F5A3C7100F37A2B /* ITKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA4A473D0F5A3C6900F37A2B /* ITKit.framework */; }; + FA4A47410F5A3C7300F37A2B /* ITFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA4A47340F5A3C5900F37A2B /* ITFoundation.framework */; }; + FA4A47510F5A3CFC00F37A2B /* GrowlITTSWPrefs.nib in Resources */ = {isa = PBXBuildFile; fileRef = FA4A474F0F5A3CFC00F37A2B /* GrowlITTSWPrefs.nib */; }; + FA5074430F5A56DA008DEAD9 /* ITFoundation.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = FA4A47340F5A3C5900F37A2B /* ITFoundation.framework */; }; + FA5074440F5A56DD008DEAD9 /* ITKit.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = FA4A473D0F5A3C6900F37A2B /* ITKit.framework */; }; + FA50774D0F5A7A2D008DEAD9 /* PreferencePanes.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA50774C0F5A7A2D008DEAD9 /* PreferencePanes.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + FA4A47330F5A3C5900F37A2B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FA4A472F0F5A3C5900F37A2B /* ITFoundation.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8DC2EF5B0486A6940098B216; + remoteInfo = ITFoundation; + }; + FA4A473C0F5A3C6900F37A2B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FA4A47370F5A3C6900F37A2B /* ITKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8DC2EF5B0486A6940098B216; + remoteInfo = ITKit; + }; + FA4A473E0F5A3C6900F37A2B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FA4A47370F5A3C6900F37A2B /* ITKit.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 7C992F87054F5389000B93EA; + remoteInfo = ITKitShowcase; + }; + FA4A47490F5A3CD300F37A2B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FA4A472F0F5A3C5900F37A2B /* ITFoundation.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 8DC2EF4F0486A6940098B216; + remoteInfo = ITFoundation; + }; + FA4A474B0F5A3CD500F37A2B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FA4A47370F5A3C6900F37A2B /* ITKit.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 8DC2EF4F0486A6940098B216; + remoteInfo = ITKit; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + FA50745D0F5A570F008DEAD9 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + FA5074430F5A56DA008DEAD9 /* ITFoundation.framework in CopyFiles */, + FA5074440F5A56DD008DEAD9 /* ITKit.framework in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 089C1672FE841209C02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 089C167EFE841241C02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; + 089C167FFE841241C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + 32DBCF630370AF2F00C91783 /* GrowlITTSW_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrowlITTSW_Prefix.pch; sourceTree = ""; }; + 8D5B49B6048680CD000E48DA /* GrowlITTSW.growlView */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GrowlITTSW.growlView; sourceTree = BUILT_PRODUCTS_DIR; }; + 8D5B49B7048680CD000E48DA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + D2F7E65807B2D6F200F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; + FA4A471D0F5A3C2A00F37A2B /* GrowlITTSWDisplay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrowlITTSWDisplay.h; sourceTree = ""; }; + FA4A471E0F5A3C2A00F37A2B /* GrowlITTSWDisplay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GrowlITTSWDisplay.m; sourceTree = ""; }; + FA4A471F0F5A3C2A00F37A2B /* GrowlITTSWPrefs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrowlITTSWPrefs.h; sourceTree = ""; }; + FA4A47200F5A3C2A00F37A2B /* GrowlITTSWPrefs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GrowlITTSWPrefs.m; sourceTree = ""; }; + FA4A47210F5A3C2A00F37A2B /* GrowlITTSWController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrowlITTSWController.h; sourceTree = ""; }; + FA4A47220F5A3C2A00F37A2B /* GrowlITTSWController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GrowlITTSWController.m; sourceTree = ""; }; + FA4A47230F5A3C2A00F37A2B /* GrowlITTSWWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrowlITTSWWindow.h; sourceTree = ""; }; + FA4A47240F5A3C2A00F37A2B /* GrowlITTSWWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GrowlITTSWWindow.m; sourceTree = ""; }; + FA4A472B0F5A3C3100F37A2B /* GrowlPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrowlPlugin.h; sourceTree = ""; }; + FA4A472C0F5A3C3100F37A2B /* GrowlDisplayPlugin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrowlDisplayPlugin.h; sourceTree = ""; }; + FA4A472F0F5A3C5900F37A2B /* ITFoundation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ITFoundation.xcodeproj; path = ../ITFoundation/ITFoundation.xcodeproj; sourceTree = SOURCE_ROOT; }; + FA4A47370F5A3C6900F37A2B /* ITKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ITKit.xcodeproj; path = ../ITKit/ITKit.xcodeproj; sourceTree = SOURCE_ROOT; }; + FA4A47500F5A3CFC00F37A2B /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = English.lproj/GrowlITTSWPrefs.nib; sourceTree = ""; }; + FA50739B0F5A4D74008DEAD9 /* GrowlDefinesInternal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrowlDefinesInternal.h; sourceTree = ""; }; + FA5073AA0F5A4D9A008DEAD9 /* GrowlApplicationNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrowlApplicationNotification.h; sourceTree = ""; }; + FA5074060F5A5562008DEAD9 /* GrowlDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GrowlDefines.h; sourceTree = ""; }; + FA50774C0F5A7A2D008DEAD9 /* PreferencePanes.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PreferencePanes.framework; path = /System/Library/Frameworks/PreferencePanes.framework; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D5B49B3048680CD000E48DA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D5B49B4048680CD000E48DA /* Cocoa.framework in Frameworks */, + FA4A47400F5A3C7100F37A2B /* ITKit.framework in Frameworks */, + FA4A47410F5A3C7300F37A2B /* ITFoundation.framework in Frameworks */, + FA50774D0F5A7A2D008DEAD9 /* PreferencePanes.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 089C166AFE841209C02AAC07 /* GrowlITTSW */ = { + isa = PBXGroup; + children = ( + 08FB77AFFE84173DC02AAC07 /* Classes */, + 32C88E010371C26100C91783 /* Other Sources */, + 089C167CFE841241C02AAC07 /* Resources */, + 089C1671FE841209C02AAC07 /* Frameworks and Libraries */, + 19C28FB8FE9D52D311CA2CBB /* Products */, + ); + name = GrowlITTSW; + sourceTree = ""; + }; + 089C1671FE841209C02AAC07 /* Frameworks and Libraries */ = { + isa = PBXGroup; + children = ( + FA4A472F0F5A3C5900F37A2B /* ITFoundation.xcodeproj */, + FA4A47370F5A3C6900F37A2B /* ITKit.xcodeproj */, + 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */, + 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */, + ); + name = "Frameworks and Libraries"; + sourceTree = ""; + }; + 089C167CFE841241C02AAC07 /* Resources */ = { + isa = PBXGroup; + children = ( + FA4A474F0F5A3CFC00F37A2B /* GrowlITTSWPrefs.nib */, + 8D5B49B7048680CD000E48DA /* Info.plist */, + 089C167DFE841241C02AAC07 /* InfoPlist.strings */, + ); + name = Resources; + sourceTree = ""; + }; + 08FB77AFFE84173DC02AAC07 /* Classes */ = { + isa = PBXGroup; + children = ( + FA4A471D0F5A3C2A00F37A2B /* GrowlITTSWDisplay.h */, + FA4A471E0F5A3C2A00F37A2B /* GrowlITTSWDisplay.m */, + FA4A471F0F5A3C2A00F37A2B /* GrowlITTSWPrefs.h */, + FA4A47200F5A3C2A00F37A2B /* GrowlITTSWPrefs.m */, + FA4A47210F5A3C2A00F37A2B /* GrowlITTSWController.h */, + FA4A47220F5A3C2A00F37A2B /* GrowlITTSWController.m */, + FA4A47230F5A3C2A00F37A2B /* GrowlITTSWWindow.h */, + FA4A47240F5A3C2A00F37A2B /* GrowlITTSWWindow.m */, + ); + name = Classes; + sourceTree = ""; + }; + 1058C7ACFEA557BF11CA2CBB /* Linked Frameworks */ = { + isa = PBXGroup; + children = ( + 1058C7ADFEA557BF11CA2CBB /* Cocoa.framework */, + FA50774C0F5A7A2D008DEAD9 /* PreferencePanes.framework */, + ); + name = "Linked Frameworks"; + sourceTree = ""; + }; + 1058C7AEFEA557BF11CA2CBB /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + 089C167FFE841241C02AAC07 /* AppKit.framework */, + D2F7E65807B2D6F200F64583 /* CoreData.framework */, + 089C1672FE841209C02AAC07 /* Foundation.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 19C28FB8FE9D52D311CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8D5B49B6048680CD000E48DA /* GrowlITTSW.growlView */, + ); + name = Products; + sourceTree = ""; + }; + 32C88E010371C26100C91783 /* Other Sources */ = { + isa = PBXGroup; + children = ( + FA5074060F5A5562008DEAD9 /* GrowlDefines.h */, + FA50739B0F5A4D74008DEAD9 /* GrowlDefinesInternal.h */, + FA5073AA0F5A4D9A008DEAD9 /* GrowlApplicationNotification.h */, + FA4A472B0F5A3C3100F37A2B /* GrowlPlugin.h */, + FA4A472C0F5A3C3100F37A2B /* GrowlDisplayPlugin.h */, + 32DBCF630370AF2F00C91783 /* GrowlITTSW_Prefix.pch */, + ); + name = "Other Sources"; + sourceTree = ""; + }; + FA4A47300F5A3C5900F37A2B /* Products */ = { + isa = PBXGroup; + children = ( + FA4A47340F5A3C5900F37A2B /* ITFoundation.framework */, + ); + name = Products; + sourceTree = ""; + }; + FA4A47380F5A3C6900F37A2B /* Products */ = { + isa = PBXGroup; + children = ( + FA4A473D0F5A3C6900F37A2B /* ITKit.framework */, + FA4A473F0F5A3C6900F37A2B /* ITKitShowcase.app */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8D5B49AC048680CD000E48DA /* GrowlITTSW */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB913A08733D840010E9CD /* Build configuration list for PBXNativeTarget "GrowlITTSW" */; + buildPhases = ( + 8D5B49AF048680CD000E48DA /* Resources */, + 8D5B49B1048680CD000E48DA /* Sources */, + 8D5B49B3048680CD000E48DA /* Frameworks */, + FA50745D0F5A570F008DEAD9 /* CopyFiles */, + FA50761F0F5A692D008DEAD9 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + FA4A474A0F5A3CD300F37A2B /* PBXTargetDependency */, + FA4A474C0F5A3CD500F37A2B /* PBXTargetDependency */, + ); + name = GrowlITTSW; + productInstallPath = "$(HOME)/Library/Bundles"; + productName = GrowlITTSW; + productReference = 8D5B49B6048680CD000E48DA /* GrowlITTSW.growlView */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 089C1669FE841209C02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "GrowlITTSW" */; + compatibilityVersion = "Xcode 3.1"; + hasScannedForEncodings = 1; + mainGroup = 089C166AFE841209C02AAC07 /* GrowlITTSW */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = FA4A47300F5A3C5900F37A2B /* Products */; + ProjectRef = FA4A472F0F5A3C5900F37A2B /* ITFoundation.xcodeproj */; + }, + { + ProductGroup = FA4A47380F5A3C6900F37A2B /* Products */; + ProjectRef = FA4A47370F5A3C6900F37A2B /* ITKit.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 8D5B49AC048680CD000E48DA /* GrowlITTSW */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + FA4A47340F5A3C5900F37A2B /* ITFoundation.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = ITFoundation.framework; + remoteRef = FA4A47330F5A3C5900F37A2B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + FA4A473D0F5A3C6900F37A2B /* ITKit.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = ITKit.framework; + remoteRef = FA4A473C0F5A3C6900F37A2B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + FA4A473F0F5A3C6900F37A2B /* ITKitShowcase.app */ = { + isa = PBXReferenceProxy; + fileType = wrapper.application; + path = ITKitShowcase.app; + remoteRef = FA4A473E0F5A3C6900F37A2B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D5B49AF048680CD000E48DA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D5B49B0048680CD000E48DA /* InfoPlist.strings in Resources */, + FA4A47510F5A3CFC00F37A2B /* GrowlITTSWPrefs.nib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + FA50761F0F5A692D008DEAD9 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "install_name_tool -change \\\n\t\"@executable_path/../Frameworks/ITFoundation.framework/Versions/A/ITFoundation\" \\\n\t\"@loader_path/../Frameworks/ITFoundation.framework/Versions/A/ITFoundation\" \\\n\t\"$TARGET_BUILD_DIR/$EXECUTABLE_FOLDER_PATH/$EXECUTABLE_NAME\"\n\ninstall_name_tool -change \\\n\t\"@executable_path/../Frameworks/ITKit.framework/Versions/A/ITKit\" \\\n\t\"@loader_path/../Frameworks/ITKit.framework/Versions/A/ITKit\" \\\n\t\"$TARGET_BUILD_DIR/$EXECUTABLE_FOLDER_PATH/$EXECUTABLE_NAME\"\n\ninstall_name_tool -change \\\n\t\"@executable_path/../Frameworks/ITFoundation.framework/Versions/A/ITFoundation\" \\\n\t\"@loader_path/../../../../Frameworks/ITFoundation.framework/Versions/A/ITFoundation\" \\\n\t\"$TARGET_BUILD_DIR/$FRAMEWORKS_FOLDER_PATH/ITKit.framework/Versions/A/ITKit\""; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D5B49B1048680CD000E48DA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FA4A47250F5A3C2A00F37A2B /* GrowlITTSWDisplay.m in Sources */, + FA4A47270F5A3C2A00F37A2B /* GrowlITTSWController.m in Sources */, + FA4A47280F5A3C2A00F37A2B /* GrowlITTSWWindow.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + FA4A474A0F5A3CD300F37A2B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = ITFoundation; + targetProxy = FA4A47490F5A3CD300F37A2B /* PBXContainerItemProxy */; + }; + FA4A474C0F5A3CD500F37A2B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = ITKit; + targetProxy = FA4A474B0F5A3CD500F37A2B /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 089C167DFE841241C02AAC07 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 089C167EFE841241C02AAC07 /* English */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + FA4A474F0F5A3CFC00F37A2B /* GrowlITTSWPrefs.nib */ = { + isa = PBXVariantGroup; + children = ( + FA4A47500F5A3CFC00F37A2B /* English */, + ); + name = GrowlITTSWPrefs.nib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 1DEB913B08733D840010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = GrowlITTSW_Prefix.pch; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(USER_LIBRARY_DIR)/Application Support/Growl/Plugins"; + LIBRARY_STYLE = BUNDLE; + OTHER_LDFLAGS = ( + "-undefined", + dynamic_lookup, + ); + PRODUCT_NAME = GrowlITTSW; + REZ_RESOURCE_MAP_READ_ONLY = YES; + WRAPPER_EXTENSION = growlView; + }; + name = Debug; + }; + 1DEB913C08733D840010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = GrowlITTSW_Prefix.pch; + INFOPLIST_FILE = Info.plist; + INSTALL_PATH = "$(USER_LIBRARY_DIR)/Application Support/Growl/Plugins"; + LIBRARY_STYLE = BUNDLE; + OTHER_LDFLAGS = ( + "-undefined", + dynamic_lookup, + ); + PRODUCT_NAME = GrowlITTSW; + REZ_RESOURCE_MAP_READ_ONLY = YES; + WRAPPER_EXTENSION = growlView; + }; + name = Release; + }; + 1DEB913F08733D840010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + PREBINDING = NO; + SDKROOT = macosx10.5; + }; + name = Debug; + }; + 1DEB914008733D840010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = macosx10.5; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB913A08733D840010E9CD /* Build configuration list for PBXNativeTarget "GrowlITTSW" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB913B08733D840010E9CD /* Debug */, + 1DEB913C08733D840010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB913E08733D840010E9CD /* Build configuration list for PBXProject "GrowlITTSW" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB913F08733D840010E9CD /* Debug */, + 1DEB914008733D840010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 089C1669FE841209C02AAC07 /* Project object */; +} diff --git a/GrowlITTSWController.h b/GrowlITTSWController.h new file mode 100644 index 0000000..469d6e5 --- /dev/null +++ b/GrowlITTSWController.h @@ -0,0 +1,19 @@ +// +// GrowlITTSWController.h +// Growl +// +// Created by Joseph Spiros on 2/28/09. +// Copyright 2009 iThink Software. All rights reserved. +// + +#import +#import +#import "GrowlITTSWWindow.h" + +@interface GrowlITTSWController : ITSharedController { + GrowlITTSWWindow *_window; +} + +- (void)showWindowWithText:(NSString *)text image:(NSImage *)image; + +@end diff --git a/GrowlITTSWController.m b/GrowlITTSWController.m new file mode 100644 index 0000000..0f1cd0e --- /dev/null +++ b/GrowlITTSWController.m @@ -0,0 +1,93 @@ +// +// GrowlITTSWController.m +// Growl +// +// Created by Joseph Spiros on 2/28/09. +// Copyright 2009 iThink Software. All rights reserved. +// + +#import "GrowlITTSWController.h" +#import "GrowlITTSWWindow.h" + +#import +#import + +@implementation NSImage (SmoothAdditions) + +- (NSImage *)imageScaledSmoothlyToSize:(NSSize)scaledSize +{ + NSImage *newImage; + NSImageRep *rep = [self bestRepresentationForDevice:nil]; + + newImage = [[NSImage alloc] initWithSize:scaledSize]; + [newImage lockFocus]; + { + [[NSGraphicsContext currentContext] setImageInterpolation:NSImageInterpolationHigh]; + [[NSGraphicsContext currentContext] setShouldAntialias:YES]; + [rep drawInRect:NSMakeRect(3, 3, scaledSize.width - 6, scaledSize.height - 6)]; + } + [newImage unlockFocus]; + return [newImage autorelease]; +} + +@end + +@implementation GrowlITTSWController + +- (id)init +{ + if ( ( self = [super init] ) ) { + NSArray *screens = [NSScreen screens]; + + _window = [[GrowlITTSWWindow sharedWindow] retain]; + + [_window setScreen:[screens objectAtIndex:0]]; + + [_window setExitMode:ITTransientStatusWindowExitAfterDelay]; + [_window setExitDelay:4.0]; + + [_window setHorizontalPosition:ITWindowPositionRight]; + [_window setVerticalPosition:ITWindowPositionTop]; + + [_window setSizing:ITTransientStatusWindowMini]; + + [_window setEntryEffect:[[[NSClassFromString(@"ITSlideVerticallyWindowEffect") alloc] initWithWindow:_window] autorelease]]; + [_window setExitEffect:[[[NSClassFromString(@"ITSlideHorizontallyWindowEffect") alloc] initWithWindow:_window] autorelease]]; + + [[_window entryEffect] setEffectTime:0.8]; + [[_window exitEffect] setEffectTime:0.8]; + + [(ITTSWBackgroundView *)[_window contentView]setBackgroundMode: + ITTSWBackgroundReadable]; + } + + return self; +} + +- (void)dealloc +{ + [_window release]; + [super dealloc]; +} + +- (void)showWindowWithText:(NSString *)text image:(NSImage *)image +{ + NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:text]; + NSSize newSize; + NSSize oldSize = [image size]; + + if (oldSize.width > oldSize.height) { + newSize = NSMakeSize(110.0f, (oldSize.height * (110.0f / oldSize.width))); + } else { + newSize = NSMakeSize((oldSize.width * (110.0f / oldSize.height)), 110.0f); + } + + image = [[[[NSImage alloc] initWithData:[image TIFFRepresentation]] autorelease] imageScaledSmoothlyToSize:newSize]; + + [_window setImage:image]; + [_window buildTextWindowWithString:attributedText]; + [_window appear:self]; + [attributedText release]; +} + +@end \ No newline at end of file diff --git a/GrowlITTSWDisplay.h b/GrowlITTSWDisplay.h new file mode 100644 index 0000000..1330358 --- /dev/null +++ b/GrowlITTSWDisplay.h @@ -0,0 +1,11 @@ +#import +#import "GrowlDisplayPlugin.h" + +@class GrowlApplicationNotification; + +@interface GrowlITTSWDisplay : GrowlDisplayPlugin { +} + +- (void) displayNotification:(GrowlApplicationNotification *)notification; + +@end diff --git a/GrowlITTSWDisplay.m b/GrowlITTSWDisplay.m new file mode 100644 index 0000000..bbd7515 --- /dev/null +++ b/GrowlITTSWDisplay.m @@ -0,0 +1,42 @@ +#import "GrowlITTSWDisplay.h" +#import "GrowlITTSWController.h" +#import "GrowlITTSWPrefs.h" +#import "GrowlDefines.h" +#import "GrowlDefinesInternal.h" +#import "GrowlApplicationNotification.h" + +@implementation GrowlITTSWDisplay + +- (void) dealloc { + [preferencePane release]; + [super dealloc]; +} + +/* - (NSPreferencePane *) preferencePane { + if (!preferencePane) + preferencePane = [[GrowlITTSWPrefs alloc] initWithBundle:[NSBundle bundleWithIdentifier:@"com.ithinksw.growl.ittsw"]]; + return preferencePane; +}*/ + +//we implement requiresPositioning entirely because it was added as a requirement for doing 1.1 plugins, however +//we don't really care if positioning is required or not, because we are only ever in the menubar. +- (BOOL)requiresPositioning { + return NO; +} + +#pragma mark - +- (void) displayNotification:(GrowlApplicationNotification *)notification { + NSDictionary *dict = [notification dictionaryRepresentation]; + NSString *title = [dict objectForKey:GROWL_NOTIFICATION_TITLE]; + NSString *desc = [dict objectForKey:GROWL_NOTIFICATION_DESCRIPTION]; + NSImage *image = [dict objectForKey:GROWL_NOTIFICATION_ICON]; + NSString *text; + if (desc) { + text = [title stringByAppendingFormat:@"\n%@", desc]; + } else { + text = title; + } + [[GrowlITTSWController sharedController] showWindowWithText:text image:image]; +} + +@end diff --git a/GrowlITTSWPrefs.h b/GrowlITTSWPrefs.h new file mode 100644 index 0000000..e2c287d --- /dev/null +++ b/GrowlITTSWPrefs.h @@ -0,0 +1,6 @@ +#import + +@interface GrowlITTSWPrefs : NSPreferencePane { +} + +@end diff --git a/GrowlITTSWPrefs.m b/GrowlITTSWPrefs.m new file mode 100644 index 0000000..395b2ff --- /dev/null +++ b/GrowlITTSWPrefs.m @@ -0,0 +1,6 @@ +#import "GrowlITTSWPrefs.h" +#import "GrowlDefinesInternal.h" + +@implementation GrowlITTSWPrefs + +@end diff --git a/GrowlITTSWWindow.h b/GrowlITTSWWindow.h new file mode 100644 index 0000000..2dc64c9 --- /dev/null +++ b/GrowlITTSWWindow.h @@ -0,0 +1,15 @@ +#import +#import + +#define SMALL_DIVISOR 1.33333 +#define MINI_DIVISOR 1.66667 + +@interface GrowlITTSWWindow : ITTransientStatusWindow { + NSImage *_image; + NSTextField *_textField; +} + +- (void)setImage:(NSImage *)newImage; +- (void)buildTextWindowWithString:(id)text; + +@end diff --git a/GrowlITTSWWindow.m b/GrowlITTSWWindow.m new file mode 100644 index 0000000..0616224 --- /dev/null +++ b/GrowlITTSWWindow.m @@ -0,0 +1,252 @@ +// +// GrowlITTSWWindow.m +// Growl +// +// Created by Joseph Spiros on 2/28/09. +// Copyright 2009 iThink Software. All rights reserved. +// + +#import "GrowlITTSWWindow.h" + +#define SW_PAD 24.00 +#define SW_SPACE 24.00 +#define SW_MINW 211.00 +#define SW_BORDER 32.00 +#define SW_METER_PAD 4.00 +#define SW_BUTTON_PAD_R 30.00 +#define SW_BUTTON_PAD_B 24.00 +#define SW_BUTTON_DIV 12.00 +#define SW_BUTTON_EXTRA_W 8.00 +#define SW_SHADOW_SAT 1.25 + +@interface GrowlITTSWWindow (Private) +- (NSRect)setupWindowWithDataSize:(NSSize)dataSize; +@end + +@implementation GrowlITTSWWindow + + +/*************************************************************************/ +#pragma mark - +#pragma mark INITIALIZATION / DEALLOCATION METHODS +/*************************************************************************/ + +- (id)initWithContentView:(NSView *)contentView + exitMode:(ITTransientStatusWindowExitMode)exitMode + backgroundType:(ITTransientStatusWindowBackgroundType)backgroundType +{ + if ( ( self = [super initWithContentView:contentView + exitMode:exitMode + backgroundType:backgroundType] ) ) { + // Set default values. + _image = [[NSImage imageNamed:@"NSApplicationIcon"] retain]; + _sizing = ITTransientStatusWindowRegular; + } + + return self; +} + +- (void)dealloc +{ + [_image release]; + [super dealloc]; +} + + +/*************************************************************************/ +#pragma mark - +#pragma mark ACCESSOR METHODS +/*************************************************************************/ + +- (void)setImage:(NSImage *)newImage +{ + [_image autorelease]; + _image = [newImage copy]; +} + +- (void)setSizing:(ITTransientStatusWindowSizing)newSizing +{ + _sizing = newSizing; +} + +/*************************************************************************/ +#pragma mark - +#pragma mark INSTANCE METHODS +/*************************************************************************/ + +- (void)appear:(id)sender +{ + [super appear:sender]; +} + +- (void)vanish:(id)sender +{ + [super vanish:sender]; +} + +- (NSRect)setupWindowWithDataSize:(NSSize)dataSize +{ + float divisor = 1.0; + NSRect imageRect; + float imageWidth = 0.0; + float imageHeight = 0.0; + float dataWidth = dataSize.width; + float dataHeight = dataSize.height; + float contentHeight = 0.0; + float windowWidth = 0.0; + float windowHeight = 0.0; + NSRect visibleFrame = [[self screen] visibleFrame]; + NSPoint screenOrigin = visibleFrame.origin; + float screenWidth = visibleFrame.size.width; + float screenHeight = visibleFrame.size.height; + float maxWidth = ( screenWidth - (SW_BORDER * 2) ); + float maxHeight = ( screenHeight - (SW_BORDER * 2) ); + float excessWidth = 0.0; + float excessHeight = 0.0; + NSPoint windowOrigin = NSZeroPoint; + ITImageView *imageView; + BOOL shouldAnimate = ( ! (([self visibilityState] == ITWindowAppearingState) || + ([self visibilityState] == ITWindowVanishingState)) ); + + if ( _sizing == ITTransientStatusWindowSmall ) { + divisor = SMALL_DIVISOR; + } else if ( _sizing == ITTransientStatusWindowMini ) { + divisor = MINI_DIVISOR; + } + + // Get image width and height. + imageWidth = ( [_image size].width / divisor ); + imageHeight = ( [_image size].height / divisor ); + + // Set the content height to the greater of the text and image heights. + contentHeight = ( ( imageHeight > dataHeight ) ? imageHeight : dataHeight ); + + // Setup the Window, and remove all its contentview's subviews. + windowWidth = ( (SW_PAD / divisor) + imageWidth + ((dataWidth > 0) ? (SW_SPACE / divisor) + dataWidth : 0) + (SW_PAD / divisor) ); + windowHeight = ( (SW_PAD / divisor) + contentHeight + (SW_PAD / divisor) ); + + // Constrain size to max limits. Adjust data sizes accordingly. + excessWidth = (windowWidth - maxWidth ); + excessHeight = (windowHeight - maxHeight); + + if ( excessWidth > 0.0 ) { + windowWidth = maxWidth; + dataWidth -= excessWidth; + } + + if ( excessHeight > 0.0 ) { + windowHeight = maxHeight; + dataHeight -= excessHeight; + } + + if ( [self horizontalPosition] == ITWindowPositionLeft ) { + windowOrigin.x = ( SW_BORDER + screenOrigin.x ); + } else if ( [self horizontalPosition] == ITWindowPositionCenter ) { + windowOrigin.x = ( screenOrigin.x + (screenWidth / 2) - (windowWidth / 2) ); + } else if ( [self horizontalPosition] == ITWindowPositionRight ) { + windowOrigin.x = ( screenOrigin.x + screenWidth - (windowWidth + SW_BORDER) ); + } + + if ( [self verticalPosition] == ITWindowPositionTop ) { + windowOrigin.y = ( screenOrigin.y + screenHeight - (windowHeight + SW_BORDER) ); + } else if ( [self verticalPosition] == ITWindowPositionMiddle ) { + // Middle-oriented windows should be slightly proud of the screen's middle. + windowOrigin.y = ( (screenOrigin.y + (screenHeight / 2) - (windowHeight / 2)) + (screenHeight / 8) ); + } else if ( [self verticalPosition] == ITWindowPositionBottom ) { + windowOrigin.y = ( SW_BORDER + screenOrigin.y ); + } + + [self setFrame:NSMakeRect( windowOrigin.x, + windowOrigin.y, + windowWidth, + windowHeight) display:YES animate:shouldAnimate]; + + [[[self contentView] subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)]; + + // Setup, position, fill, and add the image view to the content view. + imageRect = NSMakeRect( (SW_PAD / divisor) + ((dataWidth > 0) ? 4 : 0), + ((SW_PAD / divisor) + ((contentHeight - imageHeight) / 2)), + imageWidth, + imageHeight ); + imageView = [[[ITImageView alloc] initWithFrame:imageRect] autorelease]; + [imageView setAutoresizingMask:(NSViewMinYMargin | NSViewMaxYMargin)]; + [imageView setImage:_image]; + [imageView setCastsShadow:YES]; + [[self contentView] addSubview:imageView]; + + return NSMakeRect( ((SW_PAD / divisor) + imageWidth + (SW_SPACE / divisor)), + ((SW_PAD / divisor) + ((contentHeight - dataHeight) / 2)), + dataWidth, + dataHeight); +} + +- (void)buildTextWindowWithString:(id)text +{ + float divisor = 1.0; + float dataWidth = 0.0; + float dataHeight = 0.0; + NSRect dataRect; + NSArray *lines = [(([text isKindOfClass:[NSString class]]) ? text : [text mutableString]) componentsSeparatedByString:@"\n"]; + id oneLine = nil; + NSEnumerator *lineEnum = [lines objectEnumerator]; + float baseFontSize = 18.0; + ITTextField *textField; + NSFont *font; + NSDictionary *attr; + + if ( _sizing == ITTransientStatusWindowSmall ) { + divisor = SMALL_DIVISOR; + } else if ( _sizing == ITTransientStatusWindowMini ) { + divisor = MINI_DIVISOR; + } + + font = [NSFont fontWithName:@"LucidaGrande-Bold" size:(baseFontSize / divisor)]; + attr = [NSDictionary dictionaryWithObject:font forKey:NSFontAttributeName]; + + // Iterate over each line to get text width and height + while ( (oneLine = [lineEnum nextObject]) ) { + // Get the width of one line, adding 8.0 because Apple sucks donkey rectum. + float oneLineWidth = ( [oneLine sizeWithAttributes:attr].width + 8.0 ); + // Add the height of this line to the total text height + dataHeight += [oneLine sizeWithAttributes:attr].height; + // If this line wider than the last one, set it as the text width. + dataWidth = ( ( dataWidth > oneLineWidth ) ? dataWidth : oneLineWidth ); + } + + // Add 4.0 to the final dataHeight to accomodate the shadow. + dataHeight += 4.0; + + dataRect = [self setupWindowWithDataSize:NSMakeSize(dataWidth, dataHeight)]; + + // Create, position, setup, fill, and add the text view to the content view. + textField = [[[ITTextField alloc] initWithFrame:dataRect] autorelease]; + [textField setAutoresizingMask:(NSViewHeightSizable | NSViewWidthSizable)]; + [textField setEditable:NO]; + [textField setSelectable:NO]; + [textField setBordered:NO]; + [textField setDrawsBackground:NO]; + [textField setFont:font]; + [textField setTextColor:[NSColor whiteColor]]; + [textField setCastsShadow:YES]; + [[textField cell] setWraps:NO]; + + if ([text isKindOfClass:[NSString class]]) { + [textField setStringValue:text]; + } else { + [textField setAttributedStringValue:text]; + } + + [textField setShadowSaturation:SW_SHADOW_SAT]; + [[self contentView] addSubview:textField]; + + // Display the window. + [[self contentView] setNeedsDisplay:YES]; + _textField = textField; +} + +- (NSTimeInterval)animationResizeTime:(NSRect)newFrame +{ + return (NSTimeInterval)0.25; +} + +@end diff --git a/GrowlITTSW_Prefix.pch b/GrowlITTSW_Prefix.pch new file mode 100644 index 0000000..399baf0 --- /dev/null +++ b/GrowlITTSW_Prefix.pch @@ -0,0 +1,7 @@ +// +// Prefix header for all source files of the 'GrowlITTSW' target in the 'GrowlITTSW' project. +// + +#ifdef __OBJC__ + #import +#endif diff --git a/GrowlPlugin.h b/GrowlPlugin.h new file mode 100644 index 0000000..fd3d30a --- /dev/null +++ b/GrowlPlugin.h @@ -0,0 +1,105 @@ +// +// GrowlPlugin.h +// Growl +// +// Created by Mac-arena the Bored Zo on 2005-06-01. +// Copyright 2005-2006 The Growl Project. All rights reserved. +// + +#import + +@class NSPreferencePane; + +/*! @class GrowlPlugin + * @abstract The base plug-in class. + * @discussion All Growl plug-in instances are a kind of this class, including + * display plug-ins, which are kinds of GrowlDisplayPlugin. + */ +@interface GrowlPlugin : NSObject { + NSString *pluginName, *pluginAuthor, *pluginVersion, *pluginDesc; + NSBundle *pluginBundle; + NSString *pluginPathname; + + NSPreferencePane *preferencePane; + NSString *prefDomain; +} + +/*! + * @method initWithName:author:version:pathname: + * @abstract Designated initializer. + * @param name The name of the plugin. + * @param author The author of the plugin. + * @param version The version of the plugin. + * @param pathname The pathname of the plugin. + * @result An initialized GrowlPlugin object. + */ +- (id) initWithName:(NSString *)name author:(NSString *)author version:(NSString *)version pathname:(NSString *)pathname; + +/*! + * @method initWithBundle: + * @abstract Initializer for plug-ins in bundles. The name, author, version, and pathname will be obtained from the bundle. + * @result An initialized GrowlPlugin object. + */ +- (id) initWithBundle:(NSBundle *)bundle; + + +/*! + * @method name + * @abstract Returns the name of the receiver. + */ +- (NSString *) name; + +/*! + * @method author + * @abstract Returns the author of the receiver. + */ +- (NSString *) author; + +/*! + * @method pluginDescription + * @abstract Returns the description of the receiver. + */ +- (NSString *) pluginDescription; + +/*! + * @method version + * @abstract Returns the version of the receiver. + */ +- (NSString *) version; + +/*! + * @method bundle + * @abstract Returns the bundle of the receiver. + */ +- (NSBundle *) bundle; + +/*! + * @method pathname + * @abstract Returns the pathname of the receiver. + */ +- (NSString *) pathname; + +/*! +* @method pathname + * @abstract Returns the string used to access the preference domain of the receiver. + */ +- (NSString *) prefDomain; + + +/*! @method preferencePane + * @abstract Return an NSPreferencePane instance that manages + * the plugin's preferences. + * @discussion Your plug-in should put the controls for its preferences in + * this preference pane. + * + * Currently, the size of the preference pane's view should be 354 pixels by + * 289 pixels, but you should set the springs of the view and its subviews + * under the assumption that it can be resized horizontally and vertically to + * any size. + * + * The default implementation of this method returns nil. + * @result The preference pane. Can be nil. + */ +- (NSPreferencePane *) preferencePane; + +@end diff --git a/Info.plist b/Info.plist new file mode 100644 index 0000000..7bc2d5d --- /dev/null +++ b/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + GrowlITTSW + CFBundleIdentifier + com.ithinksw.growl-ittsw + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + GrowlITTSW + CFBundlePackageType + DISP + CFBundleSignature + GRRR + CFBundleVersion + 1.0 + CSResourcesFileMapped + yes + GrowlDisplayUsesQueue + + GrowlPluginAuthor + iThink Software + GrowlPluginDescription + ITTSW uses the ITTransientStatusWindow class from ITKit + NSPrincipalClass + GrowlITTSWDisplay + +