mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 421551: Update Growl framework to 1.1.4. rs=sdwilsh
This commit is contained in:
parent
babe951bb6
commit
e62d0391bf
@ -3,8 +3,9 @@
|
||||
// Growl
|
||||
//
|
||||
// Created by Mac-arena the Bored Zo on Wed Jun 18 2004.
|
||||
// Copyright 2005 The Growl Project.
|
||||
// Copyright 2005-2006 The Growl Project.
|
||||
//
|
||||
// This file is under the BSD License, refer to License.txt for details
|
||||
|
||||
#include <Carbon/Carbon.h>
|
||||
#include <unistd.h>
|
||||
@ -13,14 +14,102 @@
|
||||
#include <netdb.h>
|
||||
#include "CFGrowlAdditions.h"
|
||||
|
||||
static CFStringRef _CFURLAliasDataKey = CFSTR("_CFURLAliasData");
|
||||
static CFStringRef _CFURLStringKey = CFSTR("_CFURLString");
|
||||
static CFStringRef _CFURLStringTypeKey = CFSTR("_CFURLStringType");
|
||||
#ifndef MIN
|
||||
# define MIN(a,b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
//see GrowlApplicationBridge-Carbon.c for rationale of using NSLog.
|
||||
extern void NSLog(CFStringRef format, ...);
|
||||
extern Boolean CFStringGetFileSystemRepresentation() __attribute__((weak_import));
|
||||
extern CFIndex CFStringGetMaximumSizeOfFileSystemRepresentation(CFStringRef string) __attribute__((weak_import));
|
||||
|
||||
CFStringRef copyCurrentProcessName(void) {
|
||||
char *createFileSystemRepresentationOfString(CFStringRef str) {
|
||||
char *buffer;
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_4
|
||||
/* CFStringGetFileSystemRepresentation will cause a link error despite the weak_import attribute above on 10.5 when compiling with 10.2 compatibility using gcc 3.3.
|
||||
* PPC will therefore always use the 10.3 and below method of creating a file system representation.
|
||||
*/
|
||||
if (CFStringGetFileSystemRepresentation) {
|
||||
CFIndex size = CFStringGetMaximumSizeOfFileSystemRepresentation(str);
|
||||
buffer = malloc(size);
|
||||
CFStringGetFileSystemRepresentation(str, buffer, size);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
buffer = malloc(512);
|
||||
CFURLRef url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, str, kCFURLPOSIXPathStyle, false);
|
||||
if (!CFURLGetFileSystemRepresentation(url, false, (UInt8 *)buffer, 512)) {
|
||||
free(buffer);
|
||||
buffer = NULL;
|
||||
}
|
||||
CFRelease(url);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
STRING_TYPE createStringWithDate(CFDateRef date) {
|
||||
CFLocaleRef locale = CFLocaleCopyCurrent();
|
||||
CFDateFormatterRef dateFormatter = CFDateFormatterCreate(kCFAllocatorDefault,
|
||||
locale,
|
||||
kCFDateFormatterMediumStyle,
|
||||
kCFDateFormatterMediumStyle);
|
||||
CFRelease(locale);
|
||||
CFStringRef dateString = CFDateFormatterCreateStringWithDate(kCFAllocatorDefault,
|
||||
dateFormatter,
|
||||
date);
|
||||
CFRelease(dateFormatter);
|
||||
return dateString;
|
||||
}
|
||||
|
||||
STRING_TYPE createStringWithContentsOfFile(CFStringRef filename, CFStringEncoding encoding) {
|
||||
CFStringRef str = NULL;
|
||||
|
||||
char *path = createFileSystemRepresentationOfString(filename);
|
||||
if (path) {
|
||||
FILE *fp = fopen(path, "rb");
|
||||
if (fp) {
|
||||
fseek(fp, 0, SEEK_END);
|
||||
unsigned long size = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
unsigned char *buffer = malloc(size);
|
||||
if (buffer && fread(buffer, 1, size, fp) == size)
|
||||
str = CFStringCreateWithBytes(kCFAllocatorDefault, buffer, size, encoding, true);
|
||||
fclose(fp);
|
||||
}
|
||||
free(path);
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
STRING_TYPE createStringWithStringAndCharacterAndString(STRING_TYPE str0, UniChar ch, STRING_TYPE str1) {
|
||||
CFStringRef cfstr0 = (CFStringRef)str0;
|
||||
CFStringRef cfstr1 = (CFStringRef)str1;
|
||||
CFIndex len0 = (cfstr0 ? CFStringGetLength(cfstr0) : 0);
|
||||
CFIndex len1 = (cfstr1 ? CFStringGetLength(cfstr1) : 0);
|
||||
unsigned length = (len0 + (ch != 0xffff) + len1);
|
||||
|
||||
UniChar *buf = malloc(sizeof(UniChar) * length);
|
||||
unsigned i = 0U;
|
||||
|
||||
if (cfstr0) {
|
||||
CFStringGetCharacters(cfstr0, CFRangeMake(0, len0), buf);
|
||||
i += len0;
|
||||
}
|
||||
if (ch != 0xffff)
|
||||
buf[i++] = ch;
|
||||
if (cfstr1)
|
||||
CFStringGetCharacters(cfstr1, CFRangeMake(0, len1), &buf[i]);
|
||||
|
||||
return CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, buf, length, /*contentsDeallocator*/ kCFAllocatorMalloc);
|
||||
}
|
||||
|
||||
char *copyCString(STRING_TYPE str, CFStringEncoding encoding) {
|
||||
CFIndex size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), encoding) + 1;
|
||||
char *buffer = calloc(size, 1);
|
||||
CFStringGetCString(str, buffer, size, encoding);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
STRING_TYPE copyCurrentProcessName(void) {
|
||||
ProcessSerialNumber PSN = { 0, kCurrentProcess };
|
||||
CFStringRef name = NULL;
|
||||
OSStatus err = CopyProcessName(&PSN, &name);
|
||||
@ -31,7 +120,7 @@ CFStringRef copyCurrentProcessName(void) {
|
||||
return name;
|
||||
}
|
||||
|
||||
CFURLRef copyCurrentProcessURL(void) {
|
||||
URL_TYPE copyCurrentProcessURL(void) {
|
||||
ProcessSerialNumber psn = { 0, kCurrentProcess };
|
||||
FSRef fsref;
|
||||
CFURLRef URL = NULL;
|
||||
@ -43,14 +132,14 @@ CFURLRef copyCurrentProcessURL(void) {
|
||||
}
|
||||
return URL;
|
||||
}
|
||||
CFStringRef copyCurrentProcessPath(void) {
|
||||
STRING_TYPE copyCurrentProcessPath(void) {
|
||||
CFURLRef URL = copyCurrentProcessURL();
|
||||
CFStringRef path = CFURLCopyFileSystemPath(URL, kCFURLPOSIXPathStyle);
|
||||
CFRelease(URL);
|
||||
return path;
|
||||
}
|
||||
|
||||
CFURLRef copyTemporaryFolderURL(void) {
|
||||
URL_TYPE copyTemporaryFolderURL(void) {
|
||||
FSRef ref;
|
||||
CFURLRef url = NULL;
|
||||
|
||||
@ -62,7 +151,7 @@ CFURLRef copyTemporaryFolderURL(void) {
|
||||
|
||||
return url;
|
||||
}
|
||||
CFStringRef copyTemporaryFolderPath(void) {
|
||||
STRING_TYPE copyTemporaryFolderPath(void) {
|
||||
CFStringRef path = NULL;
|
||||
|
||||
CFURLRef url = copyTemporaryFolderURL();
|
||||
@ -74,74 +163,37 @@ CFStringRef copyTemporaryFolderPath(void) {
|
||||
return path;
|
||||
}
|
||||
|
||||
CFDictionaryRef createDockDescriptionForURL(CFURLRef url) {
|
||||
if (!url) {
|
||||
NSLog(CFSTR("%@"), CFSTR("in copyDockDescriptionForURL in CFGrowlAdditions: Cannot copy Dock description for a NULL URL"));
|
||||
return NULL;
|
||||
}
|
||||
DATA_TYPE readFile(const char *filename)
|
||||
{
|
||||
CFDataRef data;
|
||||
// read the file into a CFDataRef
|
||||
FILE *fp = fopen(filename, "r");
|
||||
if (fp) {
|
||||
fseek(fp, 0, SEEK_END);
|
||||
long dataLength = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
unsigned char *fileData = malloc(dataLength);
|
||||
fread(fileData, 1, dataLength, fp);
|
||||
fclose(fp);
|
||||
data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, fileData, dataLength, kCFAllocatorMalloc);
|
||||
} else
|
||||
data = NULL;
|
||||
|
||||
//return NULL for non-file: URLs.
|
||||
CFStringRef scheme = CFURLCopyScheme(url);
|
||||
Boolean isFileURL = (CFStringCompare(scheme, CFSTR("file"), kCFCompareCaseInsensitive) == kCFCompareEqualTo);
|
||||
CFRelease(scheme);
|
||||
if (!isFileURL)
|
||||
return NULL;
|
||||
|
||||
CFDictionaryRef dict = NULL;
|
||||
CFStringRef path = NULL;
|
||||
CFDataRef aliasData = NULL;
|
||||
|
||||
FSRef fsref;
|
||||
if (CFURLGetFSRef(url, &fsref)) {
|
||||
AliasHandle alias = NULL;
|
||||
OSStatus err = FSNewAlias(/*fromFile*/ NULL, &fsref, &alias);
|
||||
if (err != noErr) {
|
||||
NSLog(CFSTR("in copyDockDescriptionForURL in CFGrowlAdditions: FSNewAlias for %@ returned %li"), url, (long)err);
|
||||
} else {
|
||||
HLock((Handle)alias);
|
||||
|
||||
err = FSCopyAliasInfo(alias, /*targetName*/ NULL, /*volumeName*/ NULL, (CFStringRef *)&path, /*whichInfo*/ NULL, /*info*/ NULL);
|
||||
if (err != noErr) {
|
||||
NSLog(CFSTR("in copyDockDescriptionForURL in CFGrowlAdditions: FSCopyAliasInfo for %@ returned %li"), url, (long)err);
|
||||
}
|
||||
|
||||
aliasData = CFDataCreate(kCFAllocatorDefault, (UInt8 *)*alias, GetHandleSize((Handle)alias));
|
||||
|
||||
HUnlock((Handle)alias);
|
||||
DisposeHandle((Handle)alias);
|
||||
}
|
||||
}
|
||||
|
||||
if (!path) {
|
||||
path = CFURLCopyFileSystemPath(url, kCFURLPOSIXPathStyle);
|
||||
}
|
||||
|
||||
if (path || aliasData) {
|
||||
CFMutableDictionaryRef temp = CFDictionaryCreateMutable(kCFAllocatorDefault, /*capacity*/ 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
|
||||
if (path) {
|
||||
CFDictionarySetValue(temp, _CFURLStringKey, path);
|
||||
CFRelease(path);
|
||||
|
||||
int pathStyle = kCFURLPOSIXPathStyle;
|
||||
CFNumberRef pathStyleNum = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pathStyle);
|
||||
CFDictionarySetValue(temp, _CFURLStringTypeKey, pathStyleNum);
|
||||
CFRelease(pathStyleNum);
|
||||
}
|
||||
|
||||
if (aliasData) {
|
||||
CFDictionarySetValue(temp, _CFURLAliasDataKey, aliasData);
|
||||
CFRelease(aliasData);
|
||||
}
|
||||
|
||||
dict = temp;
|
||||
}
|
||||
|
||||
return dict;
|
||||
return data;
|
||||
}
|
||||
|
||||
URL_TYPE copyURLForApplication(STRING_TYPE appName)
|
||||
{
|
||||
CFURLRef appURL = NULL;
|
||||
OSStatus err = LSFindApplicationForInfo(/*inCreator*/ kLSUnknownCreator,
|
||||
/*inBundleID*/ NULL,
|
||||
/*inName*/ appName,
|
||||
/*outAppRef*/ NULL,
|
||||
/*outAppURL*/ &appURL);
|
||||
return (err == noErr) ? appURL : NULL;
|
||||
}
|
||||
|
||||
CFStringRef createStringWithAddressData(CFDataRef aAddressData) {
|
||||
STRING_TYPE createStringWithAddressData(DATA_TYPE aAddressData) {
|
||||
struct sockaddr *socketAddress = (struct sockaddr *)CFDataGetBytePtr(aAddressData);
|
||||
// IPv6 Addresses are "FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF"
|
||||
// at max, which is 40 bytes (0-terminated)
|
||||
@ -167,7 +219,7 @@ CFStringRef createStringWithAddressData(CFDataRef aAddressData) {
|
||||
return addressAsString;
|
||||
}
|
||||
|
||||
CFStringRef createHostNameForAddressData(CFDataRef aAddressData) {
|
||||
STRING_TYPE createHostNameForAddressData(DATA_TYPE aAddressData) {
|
||||
char hostname[NI_MAXHOST];
|
||||
struct sockaddr *socketAddress = (struct sockaddr *)CFDataGetBytePtr(aAddressData);
|
||||
if (getnameinfo(socketAddress, CFDataGetLength(aAddressData),
|
||||
@ -179,7 +231,7 @@ CFStringRef createHostNameForAddressData(CFDataRef aAddressData) {
|
||||
return CFStringCreateWithCString(kCFAllocatorDefault, hostname, kCFStringEncodingASCII);
|
||||
}
|
||||
|
||||
CFDataRef copyIconDataForPath(CFStringRef path) {
|
||||
DATA_TYPE copyIconDataForPath(STRING_TYPE path) {
|
||||
CFDataRef data = NULL;
|
||||
|
||||
//false is probably safest, and is harmless when the object really is a directory.
|
||||
@ -192,7 +244,8 @@ CFDataRef copyIconDataForPath(CFStringRef path) {
|
||||
return data;
|
||||
}
|
||||
|
||||
CFDataRef copyIconDataForURL(CFURLRef URL) {
|
||||
DATA_TYPE copyIconDataForURL(URL_TYPE URL)
|
||||
{
|
||||
CFDataRef data = NULL;
|
||||
|
||||
if (URL) {
|
||||
@ -227,7 +280,8 @@ CFDataRef copyIconDataForURL(CFURLRef URL) {
|
||||
return data;
|
||||
}
|
||||
|
||||
URL_TYPE createURLByMakingDirectoryAtURLWithName(URL_TYPE parent, STRING_TYPE name) {
|
||||
URL_TYPE createURLByMakingDirectoryAtURLWithName(URL_TYPE parent, STRING_TYPE name)
|
||||
{
|
||||
CFURLRef newDirectory = NULL;
|
||||
|
||||
CFAllocatorRef allocator = parent ? CFGetAllocator(parent) : name ? CFGetAllocator(name) : kCFAllocatorDefault;
|
||||
@ -264,8 +318,7 @@ URL_TYPE createURLByMakingDirectoryAtURLWithName(URL_TYPE parent, STRING_TYPE na
|
||||
FSRef newDirectoryRef;
|
||||
|
||||
struct HFSUniStr255 nameUnicode;
|
||||
CFIndex nameLength = CFStringGetLength(name);
|
||||
CFRange range = { 0, (nameLength < USHRT_MAX ? nameLength : USHRT_MAX) };
|
||||
CFRange range = { 0, MIN(CFStringGetLength(name), USHRT_MAX) };
|
||||
CFStringGetCharacters(name, range, nameUnicode.unicode);
|
||||
nameUnicode.length = range.length;
|
||||
|
||||
@ -278,7 +331,7 @@ URL_TYPE createURLByMakingDirectoryAtURLWithName(URL_TYPE parent, STRING_TYPE na
|
||||
.textEncodingHint = kTextEncodingUnknown,
|
||||
.newRef = &newDirectoryRef,
|
||||
};
|
||||
|
||||
|
||||
OSStatus err = PBCreateDirectoryUnicodeSync(&refPB);
|
||||
if (err == dupFNErr) {
|
||||
//dupFNErr == file (or folder) exists already. this is fine.
|
||||
@ -475,7 +528,7 @@ static OSStatus GrowlCopyObjectSync(const FSRef *fileRef, const FSRef *destRef,
|
||||
},
|
||||
.outForkName = &forkName,
|
||||
};
|
||||
|
||||
|
||||
do {
|
||||
err = PBIterateForksSync(&forkPB);
|
||||
NSLog(CFSTR("PBIterateForksSync returned %li"), (long)err);
|
||||
@ -492,7 +545,8 @@ static OSStatus GrowlCopyObjectSync(const FSRef *fileRef, const FSRef *destRef,
|
||||
return err;
|
||||
}
|
||||
|
||||
CFURLRef createURLByCopyingFileFromURLToDirectoryURL(CFURLRef file, CFURLRef dest) {
|
||||
URL_TYPE createURLByCopyingFileFromURLToDirectoryURL(URL_TYPE file, URL_TYPE dest)
|
||||
{
|
||||
CFURLRef destFileURL = NULL;
|
||||
|
||||
FSRef fileRef, destRef, destFileRef;
|
||||
@ -528,7 +582,8 @@ CFURLRef createURLByCopyingFileFromURLToDirectoryURL(CFURLRef file, CFURLRef des
|
||||
return destFileURL;
|
||||
}
|
||||
|
||||
CFPropertyListRef createPropertyListFromURL(CFURLRef file, u_int32_t mutability, CFPropertyListFormat *outFormat, CFStringRef *outErrorString) {
|
||||
PLIST_TYPE createPropertyListFromURL(URL_TYPE file, u_int32_t mutability, CFPropertyListFormat *outFormat, STRING_TYPE *outErrorString)
|
||||
{
|
||||
CFPropertyListRef plist = NULL;
|
||||
|
||||
if (!file)
|
||||
|
@ -3,24 +3,27 @@
|
||||
// Growl
|
||||
//
|
||||
// Created by Mac-arena the Bored Zo on Wed Jun 18 2004.
|
||||
// Copyright 2005 The Growl Project.
|
||||
// Copyright 2005-2006 The Growl Project.
|
||||
//
|
||||
// This file is under the BSD License, refer to License.txt for details
|
||||
|
||||
#ifdef __OBJC__
|
||||
# define DATA_TYPE NSData *
|
||||
# define DICTIONARY_TYPE NSDictionary *
|
||||
# define STRING_TYPE NSString *
|
||||
# define ARRAY_TYPE NSArray *
|
||||
# define URL_TYPE NSURL *
|
||||
# define PLIST_TYPE NSObject *
|
||||
#else
|
||||
# define DATA_TYPE CFDataRef
|
||||
# define DICTIONARY_TYPE CFDictionaryRef
|
||||
# define STRING_TYPE CFStringRef
|
||||
# define ARRAY_TYPE CFArrayRef
|
||||
# define URL_TYPE CFURLRef
|
||||
# define PLIST_TYPE CFPropertyListRef
|
||||
#endif
|
||||
#ifndef HAVE_CFGROWLADDITIONS_H
|
||||
#define HAVE_CFGROWLADDITIONS_H
|
||||
|
||||
#include "CFGrowlDefines.h"
|
||||
|
||||
//see GrowlApplicationBridge-Carbon.c for rationale of using NSLog.
|
||||
extern void NSLog(STRING_TYPE format, ...);
|
||||
|
||||
char *createFileSystemRepresentationOfString(STRING_TYPE str);
|
||||
STRING_TYPE createStringWithDate(DATE_TYPE date);
|
||||
|
||||
STRING_TYPE createStringWithContentsOfFile(STRING_TYPE filename, CFStringEncoding encoding);
|
||||
|
||||
//you can leave out any of these three components. to leave out the character, pass 0xffff.
|
||||
STRING_TYPE createStringWithStringAndCharacterAndString(STRING_TYPE str0, UniChar ch, STRING_TYPE str1);
|
||||
|
||||
char *copyCString(STRING_TYPE str, CFStringEncoding encoding);
|
||||
|
||||
STRING_TYPE copyCurrentProcessName(void);
|
||||
URL_TYPE copyCurrentProcessURL(void);
|
||||
@ -32,7 +35,8 @@ STRING_TYPE copyTemporaryFolderPath(void);
|
||||
STRING_TYPE createStringWithAddressData(DATA_TYPE aAddressData);
|
||||
STRING_TYPE createHostNameForAddressData(DATA_TYPE aAddressData);
|
||||
|
||||
DICTIONARY_TYPE createDockDescriptionForURL(URL_TYPE url);
|
||||
DATA_TYPE readFile(const char *filename);
|
||||
URL_TYPE copyURLForApplication(STRING_TYPE appName);
|
||||
|
||||
/* @function copyIconDataForPath
|
||||
* @param path The POSIX path to the file or folder whose icon you want.
|
||||
@ -78,3 +82,5 @@ URL_TYPE createURLByCopyingFileFromURLToDirectoryURL(URL_TYPE file, URL_TYPE des
|
||||
* @result The property list. You are responsible for releasing this object.
|
||||
*/
|
||||
PLIST_TYPE createPropertyListFromURL(URL_TYPE file, u_int32_t mutability, CFPropertyListFormat *outFormat, STRING_TYPE *outErrorString);
|
||||
|
||||
#endif
|
||||
|
38
toolkit/components/alerts/src/mac/growl/CFGrowlDefines.h
Normal file
38
toolkit/components/alerts/src/mac/growl/CFGrowlDefines.h
Normal file
@ -0,0 +1,38 @@
|
||||
//
|
||||
// CFURLDefines.h
|
||||
// Growl
|
||||
//
|
||||
// Created by Ingmar Stein on Fri Sep 16 2005.
|
||||
// Copyright 2005-2006 The Growl Project. All rights reserved.
|
||||
//
|
||||
// This file is under the BSD License, refer to License.txt for details
|
||||
|
||||
#ifndef HAVE_CFGROWLDEFINES_H
|
||||
#define HAVE_CFGROWLDEFINES_H
|
||||
|
||||
#ifdef __OBJC__
|
||||
# define DATA_TYPE NSData *
|
||||
# define DATE_TYPE NSDate *
|
||||
# define DICTIONARY_TYPE NSDictionary *
|
||||
# define MUTABLE_DICTIONARY_TYPE NSMutableDictionary *
|
||||
# define STRING_TYPE NSString *
|
||||
# define ARRAY_TYPE NSArray *
|
||||
# define URL_TYPE NSURL *
|
||||
# define PLIST_TYPE NSObject *
|
||||
# define OBJECT_TYPE id
|
||||
# define BOOL_TYPE BOOL
|
||||
#else
|
||||
# include <CoreFoundation/CoreFoundation.h>
|
||||
# define DATA_TYPE CFDataRef
|
||||
# define DATE_TYPE CFDateRef
|
||||
# define DICTIONARY_TYPE CFDictionaryRef
|
||||
# define MUTABLE_DICTIONARY_TYPE CFMutableDictionaryRef
|
||||
# define STRING_TYPE CFStringRef
|
||||
# define ARRAY_TYPE CFArrayRef
|
||||
# define URL_TYPE CFURLRef
|
||||
# define PLIST_TYPE CFPropertyListRef
|
||||
# define OBJECT_TYPE CFTypeRef
|
||||
# define BOOL_TYPE Boolean
|
||||
#endif
|
||||
|
||||
#endif
|
@ -0,0 +1,24 @@
|
||||
//
|
||||
// CFMutableDictionaryAdditions.c
|
||||
// Growl
|
||||
//
|
||||
// Created by Ingmar Stein on 29.05.05.
|
||||
// Copyright 2005-2006 The Growl Project. All rights reserved.
|
||||
//
|
||||
// This file is under the BSD License, refer to License.txt for details
|
||||
|
||||
#include "CFMutableDictionaryAdditions.h"
|
||||
|
||||
void setObjectForKey(CFMutableDictionaryRef dict, const void *key, CFTypeRef value) {
|
||||
CFDictionarySetValue(dict, key, value);
|
||||
}
|
||||
|
||||
void setIntegerForKey(CFMutableDictionaryRef dict, const void *key, int value) {
|
||||
CFNumberRef num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &value);
|
||||
CFDictionarySetValue(dict, key, num);
|
||||
CFRelease(num);
|
||||
}
|
||||
|
||||
void setBooleanForKey(CFMutableDictionaryRef dict, const void *key, Boolean value) {
|
||||
CFDictionarySetValue(dict, key, value ? kCFBooleanTrue : kCFBooleanFalse);
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
//
|
||||
// CFMutableDictionaryAdditions.h
|
||||
// Growl
|
||||
//
|
||||
// Created by Ingmar Stein on 29.05.05.
|
||||
// Copyright 2005-2006 The Growl Project. All rights reserved.
|
||||
//
|
||||
// This file is under the BSD License, refer to License.txt for details
|
||||
|
||||
#ifndef HAVE_CFMUTABLEDICTIONARYADDITIONS_H
|
||||
#define HAVE_CFMUTABLEDICTIONARYADDITIONS_H
|
||||
|
||||
#include "CFGrowlDefines.h"
|
||||
|
||||
void setObjectForKey(MUTABLE_DICTIONARY_TYPE dict, const void *key, OBJECT_TYPE value);
|
||||
void setIntegerForKey(MUTABLE_DICTIONARY_TYPE dict, const void *key, int value);
|
||||
void setBooleanForKey(MUTABLE_DICTIONARY_TYPE dict, const void *key, BOOL_TYPE value);
|
||||
|
||||
#endif
|
155
toolkit/components/alerts/src/mac/growl/CFURLAdditions.c
Normal file
155
toolkit/components/alerts/src/mac/growl/CFURLAdditions.c
Normal file
@ -0,0 +1,155 @@
|
||||
//
|
||||
// CFURLAdditions.c
|
||||
// Growl
|
||||
//
|
||||
// Created by Karl Adam on Fri May 28 2004.
|
||||
// Copyright 2004-2006 The Growl Project. All rights reserved.
|
||||
//
|
||||
// This file is under the BSD License, refer to License.txt for details
|
||||
|
||||
#include "CFURLAdditions.h"
|
||||
#include "CFGrowlAdditions.h"
|
||||
#include "CFMutableDictionaryAdditions.h"
|
||||
#include <Carbon/Carbon.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define _CFURLAliasDataKey CFSTR("_CFURLAliasData")
|
||||
#define _CFURLStringKey CFSTR("_CFURLString")
|
||||
#define _CFURLStringTypeKey CFSTR("_CFURLStringType")
|
||||
|
||||
//'alias' as in the Alias Manager.
|
||||
URL_TYPE createFileURLWithAliasData(DATA_TYPE aliasData) {
|
||||
if (!aliasData) {
|
||||
NSLog(CFSTR("WARNING: createFileURLWithAliasData called with NULL aliasData"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CFURLRef url = NULL;
|
||||
|
||||
AliasHandle alias = NULL;
|
||||
OSStatus err = PtrToHand(CFDataGetBytePtr(aliasData), (Handle *)&alias, CFDataGetLength(aliasData));
|
||||
if (err != noErr) {
|
||||
NSLog(CFSTR("in createFileURLWithAliasData: Could not allocate an alias handle from %u bytes of alias data (data follows) because PtrToHand returned %li\n%@"), CFDataGetLength(aliasData), aliasData, (long)err);
|
||||
} else {
|
||||
CFStringRef path = NULL;
|
||||
/*
|
||||
* FSResolveAlias mounts disk images or network shares to resolve
|
||||
* aliases, thus we resort to FSCopyAliasInfo.
|
||||
*/
|
||||
err = FSCopyAliasInfo(alias,
|
||||
/* targetName */ NULL,
|
||||
/* volumeName */ NULL,
|
||||
&path,
|
||||
/* whichInfo */ NULL,
|
||||
/* info */ NULL);
|
||||
DisposeHandle((Handle)alias);
|
||||
if (err != noErr) {
|
||||
if (err != fnfErr) //ignore file-not-found; it's harmless
|
||||
NSLog(CFSTR("in createFileURLWithAliasData: Could not resolve alias (alias data follows) because FSResolveAlias returned %li - will try path\n%@"), (long)err, aliasData);
|
||||
} else if (path) {
|
||||
url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, true);
|
||||
} else {
|
||||
NSLog(CFSTR("in createFileURLWithAliasData: FSCopyAliasInfo returned a NULL path"));
|
||||
}
|
||||
CFRelease(path);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
DATA_TYPE createAliasDataWithURL(URL_TYPE theURL) {
|
||||
//return NULL for non-file: URLs.
|
||||
CFStringRef scheme = CFURLCopyScheme(theURL);
|
||||
CFComparisonResult isFileURL = CFStringCompare(scheme, CFSTR("file"), kCFCompareCaseInsensitive);
|
||||
CFRelease(scheme);
|
||||
if (isFileURL != kCFCompareEqualTo)
|
||||
return NULL;
|
||||
|
||||
CFDataRef aliasData = NULL;
|
||||
|
||||
FSRef fsref;
|
||||
if (CFURLGetFSRef(theURL, &fsref)) {
|
||||
AliasHandle alias = NULL;
|
||||
OSStatus err = FSNewAlias(/*fromFile*/ NULL, &fsref, &alias);
|
||||
if (err != noErr) {
|
||||
NSLog(CFSTR("in createAliasDataForURL: FSNewAlias for %@ returned %li"), theURL, (long)err);
|
||||
} else {
|
||||
HLock((Handle)alias);
|
||||
|
||||
aliasData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)*alias, GetHandleSize((Handle)alias));
|
||||
|
||||
HUnlock((Handle)alias);
|
||||
DisposeHandle((Handle)alias);
|
||||
}
|
||||
}
|
||||
|
||||
return aliasData;
|
||||
}
|
||||
|
||||
//these are the type of external representations used by Dock.app.
|
||||
URL_TYPE createFileURLWithDockDescription(DICTIONARY_TYPE dict) {
|
||||
CFURLRef url = NULL;
|
||||
|
||||
CFStringRef path = CFDictionaryGetValue(dict, _CFURLStringKey);
|
||||
CFDataRef aliasData = CFDictionaryGetValue(dict, _CFURLAliasDataKey);
|
||||
|
||||
if (aliasData)
|
||||
url = createFileURLWithAliasData(aliasData);
|
||||
|
||||
if (!url) {
|
||||
if (path) {
|
||||
CFNumberRef pathStyleNum = CFDictionaryGetValue(dict, _CFURLStringTypeKey);
|
||||
CFURLPathStyle pathStyle;
|
||||
if (pathStyleNum)
|
||||
CFNumberGetValue(pathStyleNum, kCFNumberIntType, &pathStyle);
|
||||
else
|
||||
pathStyleNum = kCFURLPOSIXPathStyle;
|
||||
|
||||
char *filename = createFileSystemRepresentationOfString(path);
|
||||
int fd = open(filename, O_RDONLY, 0);
|
||||
free(filename);
|
||||
if (fd != -1) {
|
||||
struct stat sb;
|
||||
fstat(fd, &sb);
|
||||
close(fd);
|
||||
url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, path, pathStyle, /*isDirectory*/ (sb.st_mode & S_IFDIR));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
DICTIONARY_TYPE createDockDescriptionWithURL(URL_TYPE theURL) {
|
||||
CFMutableDictionaryRef dict;
|
||||
|
||||
if (!theURL) {
|
||||
NSLog(CFSTR("%@"), CFSTR("in createDockDescriptionWithURL: Cannot copy Dock description for a NULL URL"));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
CFStringRef path = CFURLCopyFileSystemPath(theURL, kCFURLPOSIXPathStyle);
|
||||
CFDataRef aliasData = createAliasDataWithURL(theURL);
|
||||
|
||||
if (path || aliasData) {
|
||||
dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 3, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
|
||||
|
||||
if (path) {
|
||||
CFDictionarySetValue(dict, _CFURLStringKey, path);
|
||||
CFRelease(path);
|
||||
setIntegerForKey(dict, _CFURLStringTypeKey, kCFURLPOSIXPathStyle);
|
||||
}
|
||||
|
||||
if (aliasData) {
|
||||
CFDictionarySetValue(dict, _CFURLAliasDataKey, aliasData);
|
||||
CFRelease(aliasData);
|
||||
}
|
||||
} else {
|
||||
dict = NULL;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
25
toolkit/components/alerts/src/mac/growl/CFURLAdditions.h
Normal file
25
toolkit/components/alerts/src/mac/growl/CFURLAdditions.h
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// CFURLAdditions.h
|
||||
// Growl
|
||||
//
|
||||
// Created by Karl Adam on Fri May 28 2004.
|
||||
// Copyright 2004-2006 The Growl Project. All rights reserved.
|
||||
//
|
||||
// This file is under the BSD License, refer to License.txt for details
|
||||
|
||||
#ifndef HAVE_CFURLADDITIONS_H
|
||||
#define HAVE_CFURLADDITIONS_H
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include "CFGrowlDefines.h"
|
||||
|
||||
//'alias' as in the Alias Manager.
|
||||
URL_TYPE createFileURLWithAliasData(DATA_TYPE aliasData);
|
||||
DATA_TYPE createAliasDataWithURL(URL_TYPE theURL);
|
||||
|
||||
//these are the type of external representations used by Dock.app.
|
||||
URL_TYPE createFileURLWithDockDescription(DICTIONARY_TYPE dict);
|
||||
//createDockDescriptionWithURL returns NULL for non-file: URLs.
|
||||
DICTIONARY_TYPE createDockDescriptionWithURL(URL_TYPE theURL);
|
||||
|
||||
#endif
|
@ -0,0 +1,71 @@
|
||||
//
|
||||
// GrowlAbstractSingletonObject.h
|
||||
// GBUtilities
|
||||
//
|
||||
// Renamed from GBAbstractSingletonObject to GrowlAbstractSingletonObject.
|
||||
// Created by Ofri Wolfus on 15/08/05.
|
||||
// Copyright 2005-2006 The Growl Project. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
|
||||
/*!
|
||||
* @class GrowlAbstractSingletonObject
|
||||
* @brief An Abstract Singleton Object
|
||||
*
|
||||
* This is an abstract object for object that should have only one instnace
|
||||
* that is never released (singleton object).
|
||||
* This class is thread safe.
|
||||
*/
|
||||
@interface GrowlAbstractSingletonObject : NSObject {
|
||||
BOOL _isInitialized;
|
||||
}
|
||||
|
||||
/*!
|
||||
* @brief Returns the shared instance of this class.
|
||||
*/
|
||||
+ (id) sharedInstance;
|
||||
|
||||
/*!
|
||||
* @brief Releases and deallocates all the singletons that are subclasses of this object.
|
||||
*
|
||||
* Once +destroyAllSingletons has been called, no more singletons can be created
|
||||
* and every call to [SomeSingletonSubclass sharedInstance] will return nil.
|
||||
* Also note that a call to this method will destroy GBAbstractSingletonObject and all it's subclasses.
|
||||
* Even though that you generally can't release a singleton object, it's dealloc message WILL be called
|
||||
* when it's beeing destroyed.
|
||||
*
|
||||
* USE THIS METHOD WITH GREAT CAUTION!!!
|
||||
*/
|
||||
+ (void) destroyAllSingletons;
|
||||
|
||||
@end
|
||||
|
||||
/*!
|
||||
* @category GrowlSingletonObjectInit
|
||||
* @brief A private category for subclasses only.
|
||||
*
|
||||
* Only subclasses should override/call methods in the category.
|
||||
*/
|
||||
@interface GrowlAbstractSingletonObject (GrowlAbstractSingletonObjectInit)
|
||||
|
||||
/*!
|
||||
* @brief An init method for your singleton object.
|
||||
*
|
||||
* Implement this in your subclass to init your shared object.
|
||||
* You should call [super initSingleton] and return your initialized object.
|
||||
* Never call this method directly! It'll be automatically called when needed.
|
||||
*/
|
||||
- (id) initSingleton;
|
||||
|
||||
/*!
|
||||
* @brief Finish and clean up whatever your singleton does.
|
||||
*
|
||||
* This will be called before the singleton will be destroyed.
|
||||
* You should put whatever you would put in the -dealloc method here instead
|
||||
* and then call [super destroy].
|
||||
*/
|
||||
- (void) destroy;
|
||||
|
||||
@end
|
@ -3,7 +3,7 @@
|
||||
// Growl
|
||||
//
|
||||
// Created by Evan Schoenberg on Wed Jun 16 2004.
|
||||
// Copyright 2004-2005 The Growl Project. All rights reserved.
|
||||
// Copyright 2004-2006 The Growl Project. All rights reserved.
|
||||
//
|
||||
|
||||
/*!
|
||||
@ -17,23 +17,12 @@
|
||||
#define __GrowlApplicationBridge_h__
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <AppKit/AppKit.h>
|
||||
#import "GrowlDefines.h"
|
||||
|
||||
//Forward declarations
|
||||
@protocol GrowlApplicationBridgeDelegate;
|
||||
|
||||
/*!
|
||||
* @defined GROWL_PREFPANE_BUNDLE_IDENTIFIER
|
||||
* @discussion The bundle identifier for the Growl prefpane.
|
||||
*/
|
||||
#define GROWL_PREFPANE_BUNDLE_IDENTIFIER @"com.growl.prefpanel"
|
||||
|
||||
/*!
|
||||
* @defined GROWL_PREFPANE_NAME
|
||||
* @discussion The file name of the Growl prefpane.
|
||||
*/
|
||||
#define GROWL_PREFPANE_NAME @"Growl.prefPane"
|
||||
|
||||
//Internal notification when the user chooses not to install (to avoid continuing to cache notifications awaiting installation)
|
||||
#define GROWL_USER_CHOSE_NOT_TO_INSTALL_NOTIFICATION @"User chose not to install"
|
||||
|
||||
@ -176,6 +165,40 @@
|
||||
clickContext:(id)clickContext
|
||||
identifier:(NSString *)identifier;
|
||||
|
||||
/*!
|
||||
* @method notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:identifier:
|
||||
* @abstract Send a Growl notification.
|
||||
* @discussion This is the preferred means for sending a Growl notification.
|
||||
* The notification name and at least one of the title and description are
|
||||
* required (all three are preferred). All other parameters may be
|
||||
* <code>nil</code> (or 0 or NO as appropriate) to accept default values.
|
||||
*
|
||||
* If using the Growl-WithInstaller framework, if Growl is not installed the
|
||||
* user will be prompted to install Growl. If the user cancels, this method
|
||||
* will have no effect until the next application session, at which time when
|
||||
* it is called the user will be prompted again. The user is also given the
|
||||
* option to not be prompted again. If the user does choose to install Growl,
|
||||
* the requested notification will be displayed once Growl is installed and
|
||||
* running.
|
||||
*
|
||||
* @param title The title of the notification displayed to the user.
|
||||
* @param description The full description of the notification displayed to the user.
|
||||
* @param notifName The internal name of the notification. Should be human-readable, as it will be displayed in the Growl preference pane.
|
||||
* @param iconData <code>NSData</code> object to show with the notification as its icon. If <code>nil</code>, the application's icon will be used instead.
|
||||
* @param priority The priority of the notification. The default value is 0; positive values are higher priority and negative values are lower priority. Not all Growl displays support priority.
|
||||
* @param isSticky If YES, the notification will remain on screen until clicked. Not all Growl displays support sticky notifications.
|
||||
* @param clickContext A context passed back to the Growl delegate if it implements -(void)growlNotificationWasClicked: and the notification is clicked. Not all display plugins support clicking. The clickContext must be plist-encodable (completely of <code>NSString</code>, <code>NSArray</code>, <code>NSNumber</code>, <code>NSDictionary</code>, and <code>NSData</code> types).
|
||||
* @param identifier An identifier for this notification. Notifications with equal identifiers are coalesced.
|
||||
*/
|
||||
+ (void) notifyWithTitle:(NSString *)title
|
||||
description:(NSString *)description
|
||||
notificationName:(NSString *)notifName
|
||||
iconData:(NSData *)iconData
|
||||
priority:(signed int)priority
|
||||
isSticky:(BOOL)isSticky
|
||||
clickContext:(id)clickContext
|
||||
identifier:(NSString *)identifier;
|
||||
|
||||
/*! @method notifyWithDictionary:
|
||||
* @abstract Notifies using a userInfo dictionary suitable for passing to
|
||||
* <code>NSDistributedNotificationCenter</code>.
|
||||
@ -371,6 +394,21 @@
|
||||
*/
|
||||
+ (NSDictionary *) registrationDictionaryByFillingInDictionary:(NSDictionary *)regDict restrictToKeys:(NSSet *)keys;
|
||||
|
||||
/*! @brief Tries to fill in missing keys in a notification dictionary.
|
||||
* @param notifDict The dictionary to fill in.
|
||||
* @return The dictionary with the keys filled in. This will be a separate instance from \a notifDict.
|
||||
* @discussion This function examines the \a notifDict for missing keys, and
|
||||
* tries to get them from the last known registration dictionary. As of 1.1,
|
||||
* the keys that it will look for are:
|
||||
*
|
||||
* \li <code>GROWL_APP_NAME</code>
|
||||
* \li <code>GROWL_APP_ICON</code>
|
||||
*
|
||||
* @since Growl.framework 1.1
|
||||
*/
|
||||
+ (NSDictionary *) notificationDictionaryByFillingInDictionary:(NSDictionary *)regDict;
|
||||
|
||||
+ (NSDictionary *) frameworkInfoDictionary;
|
||||
@end
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -417,10 +455,13 @@
|
||||
* <code>+[GrowlApplicationBridge
|
||||
* notifyWithTitle:description:notificationName:iconData:priority:isSticky:clickContext:]</code> calls.
|
||||
*
|
||||
* The dictionary should have 2 key object pairs:
|
||||
* The dictionary should have the required key object pairs:
|
||||
* key: GROWL_NOTIFICATIONS_ALL object: <code>NSArray</code> of <code>NSString</code> objects
|
||||
* key: GROWL_NOTIFICATIONS_DEFAULT object: <code>NSArray</code> of <code>NSString</code> objects
|
||||
*
|
||||
* The dictionary may have the following key object pairs:
|
||||
* key: GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES object: <code>NSDictionary</code> of key: notification name object: human-readable notification name
|
||||
*
|
||||
* You do not need to implement this method if you have an auto-discoverable
|
||||
* plist file in your app bundle. (XXX refer to more information on that)
|
||||
*
|
||||
@ -447,6 +488,18 @@
|
||||
*/
|
||||
- (NSString *) applicationNameForGrowl;
|
||||
|
||||
/*!
|
||||
* @method applicationIconForGrowl
|
||||
* @abstract Return the <code>NSImage</code> to treat as the application icon.
|
||||
* @discussion The delegate may optionally return an <code>NSImage</code>
|
||||
* object to use as the application icon. If this method is not implemented,
|
||||
* {{{-applicationIconDataForGrowl}}} is tried. If that method is not
|
||||
* implemented, the application's own icon is used. Neither method is
|
||||
* generally needed.
|
||||
* @result The <code>NSImage</code> to treat as the application icon.
|
||||
*/
|
||||
- (NSImage *) applicationIconForGrowl;
|
||||
|
||||
/*!
|
||||
* @method applicationIconDataForGrowl
|
||||
* @abstract Return the <code>NSData</code> to treat as the application icon.
|
||||
@ -454,6 +507,7 @@
|
||||
* object to use as the application icon; if this is not implemented, the
|
||||
* application's own icon is used. This is not generally needed.
|
||||
* @result The <code>NSData</code> to treat as the application icon.
|
||||
* @deprecated In version 1.1, in favor of {{{-applicationIconForGrowl}}}.
|
||||
*/
|
||||
- (NSData *) applicationIconDataForGrowl;
|
||||
|
||||
@ -461,9 +515,8 @@
|
||||
* @method growlIsReady
|
||||
* @abstract Informs the delegate that Growl has launched.
|
||||
* @discussion Informs the delegate that Growl (specifically, the
|
||||
* GrowlHelperApp) was launched successfully or was already running. The
|
||||
* application can take actions with the knowledge that Growl is installed and
|
||||
* functional.
|
||||
* GrowlHelperApp) was launched successfully. The application can take actions
|
||||
* with the knowledge that Growl is installed and functional.
|
||||
*/
|
||||
- (void) growlIsReady;
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
// Growl
|
||||
//
|
||||
// Created by Evan Schoenberg on Wed Jun 16 2004.
|
||||
// Copyright 2004-2005 The Growl Project. All rights reserved.
|
||||
// Copyright 2004-2006 The Growl Project. All rights reserved.
|
||||
//
|
||||
|
||||
#import "GrowlApplicationBridge.h"
|
||||
@ -11,16 +11,37 @@
|
||||
#import "GrowlInstallationPrompt.h"
|
||||
#import "GrowlVersionUtilities.h"
|
||||
#endif
|
||||
#import "NSURLAdditions.h"
|
||||
#import "CFGrowlAdditions.h"
|
||||
#include "CFGrowlAdditions.h"
|
||||
#include "CFURLAdditions.h"
|
||||
#include "CFMutableDictionaryAdditions.h"
|
||||
#import "GrowlDefinesInternal.h"
|
||||
#import "GrowlPathUtil.h"
|
||||
#import "GrowlPathUtilities.h"
|
||||
#import "GrowlPathway.h"
|
||||
|
||||
#import <ApplicationServices/ApplicationServices.h>
|
||||
|
||||
#define PREFERENCE_PANES_SUBFOLDER_OF_LIBRARY @"PreferencePanes"
|
||||
#define PREFERENCE_PANE_EXTENSION @"prefPane"
|
||||
|
||||
/*!
|
||||
* The 10.3+ exception handling can only work if -fobjc-exceptions is enabled
|
||||
*/
|
||||
#if 0
|
||||
#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_3
|
||||
# define TRY @try {
|
||||
# define ENDTRY }
|
||||
# define CATCH @catch(NSException *localException) {
|
||||
# define ENDCATCH }
|
||||
#else
|
||||
# define TRY NS_DURING
|
||||
# define ENDTRY
|
||||
# define CATCH NS_HANDLER
|
||||
# define ENDCATCH NS_ENDHANDLER
|
||||
#endif
|
||||
#else
|
||||
# define TRY NS_DURING
|
||||
# define ENDTRY
|
||||
# define CATCH NS_HANDLER
|
||||
# define ENDCATCH NS_ENDHANDLER
|
||||
#endif
|
||||
|
||||
@interface GrowlApplicationBridge (PRIVATE)
|
||||
/*!
|
||||
@ -66,13 +87,19 @@
|
||||
*/
|
||||
+ (NSData *) _applicationIconDataForGrowlSearchingRegistrationDictionary:(NSDictionary *)regDict;
|
||||
|
||||
/*! @method growlProxy
|
||||
* @abstract Obtain (creating a connection if needed) a proxy to the Growl Helper Application
|
||||
*/
|
||||
+ (NSProxy<GrowlNotificationProtocol> *) growlProxy;
|
||||
@end
|
||||
|
||||
static NSDictionary *cachedRegistrationDictionary = nil;
|
||||
static NSString *appName = nil;
|
||||
static NSData *appIconData = nil;
|
||||
|
||||
static id delegate = nil;
|
||||
static BOOL growlLaunched = NO;
|
||||
static NSProxy<GrowlNotificationProtocol> *growlProxy = nil;
|
||||
|
||||
#ifdef GROWL_WITH_INSTALLER
|
||||
static NSMutableArray *queuedGrowlNotifications = nil;
|
||||
@ -92,20 +119,27 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
+ (void) setGrowlDelegate:(NSObject<GrowlApplicationBridgeDelegate> *)inDelegate {
|
||||
NSDistributedNotificationCenter *NSDNC = [NSDistributedNotificationCenter defaultCenter];
|
||||
|
||||
[delegate autorelease];
|
||||
delegate = [inDelegate retain];
|
||||
if (inDelegate != delegate) {
|
||||
[delegate release];
|
||||
delegate = [inDelegate retain];
|
||||
}
|
||||
|
||||
NSDictionary *regDict = [self bestRegistrationDictionary];
|
||||
[cachedRegistrationDictionary release];
|
||||
cachedRegistrationDictionary = [[self bestRegistrationDictionary] retain];
|
||||
|
||||
//Cache the appName from the delegate or the process name
|
||||
[appName autorelease];
|
||||
appName = [[self _applicationNameForGrowlSearchingRegistrationDictionary:regDict] retain];
|
||||
if (!appName)
|
||||
appName = [[self _applicationNameForGrowlSearchingRegistrationDictionary:cachedRegistrationDictionary] retain];
|
||||
if (!appName) {
|
||||
NSLog(@"%@", @"GrowlApplicationBridge: Cannot register because the application name was not supplied and could not be determined");
|
||||
return;
|
||||
}
|
||||
|
||||
//Cache the appIconData from the delegate if it responds to the applicationIconDataForGrowl selector, or the application if not
|
||||
/* Cache the appIconData from the delegate if it responds to the
|
||||
* applicationIconDataForGrowl selector, or the application if not
|
||||
*/
|
||||
[appIconData autorelease];
|
||||
appIconData = [[self _applicationIconDataForGrowlSearchingRegistrationDictionary:regDict] retain];
|
||||
appIconData = [[self _applicationIconDataForGrowlSearchingRegistrationDictionary:cachedRegistrationDictionary] retain];
|
||||
|
||||
//Add the observer for GROWL_IS_READY which will be triggered later if all goes well
|
||||
[NSDNC addObserver:self
|
||||
@ -113,35 +147,36 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
name:GROWL_IS_READY
|
||||
object:nil];
|
||||
|
||||
//Watch for notification clicks if our delegate responds to the growlNotificationWasClicked: selector
|
||||
//Notifications will come in on a unique notification name based on our app name and GROWL_NOTIFICATION_CLICKED
|
||||
/* Watch for notification clicks if our delegate responds to the
|
||||
* growlNotificationWasClicked: selector. Notifications will come in on a
|
||||
* unique notification name based on our app name, pid and
|
||||
* GROWL_NOTIFICATION_CLICKED.
|
||||
*/
|
||||
int pid = [[NSProcessInfo processInfo] processIdentifier];
|
||||
NSString *growlNotificationClickedName = [[NSString alloc] initWithFormat:@"%@-%d-%@",
|
||||
appName, pid, GROWL_NOTIFICATION_CLICKED];
|
||||
if ([delegate respondsToSelector:@selector(growlNotificationWasClicked:)]) {
|
||||
if ([delegate respondsToSelector:@selector(growlNotificationWasClicked:)])
|
||||
[NSDNC addObserver:self
|
||||
selector:@selector(_growlNotificationWasClicked:)
|
||||
selector:@selector(growlNotificationWasClicked:)
|
||||
name:growlNotificationClickedName
|
||||
object:nil];
|
||||
} else {
|
||||
else
|
||||
[NSDNC removeObserver:self
|
||||
name:growlNotificationClickedName
|
||||
object:nil];
|
||||
}
|
||||
[growlNotificationClickedName release];
|
||||
|
||||
NSString *growlNotificationTimedOutName = [[NSString alloc] initWithFormat:@"%@-%d-%@",
|
||||
appName, pid, GROWL_NOTIFICATION_TIMED_OUT];
|
||||
if ([delegate respondsToSelector:@selector(growlNotificationTimedOut:)]) {
|
||||
if ([delegate respondsToSelector:@selector(growlNotificationTimedOut:)])
|
||||
[NSDNC addObserver:self
|
||||
selector:@selector(_growlNotificationTimedOut:)
|
||||
selector:@selector(growlNotificationTimedOut:)
|
||||
name:growlNotificationTimedOutName
|
||||
object:nil];
|
||||
} else {
|
||||
else
|
||||
[NSDNC removeObserver:self
|
||||
name:growlNotificationTimedOutName
|
||||
object:nil];
|
||||
}
|
||||
[growlNotificationTimedOutName release];
|
||||
|
||||
#ifdef GROWL_WITH_INSTALLER
|
||||
@ -149,7 +184,7 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
userChoseNotToInstallGrowl = [[NSUserDefaults standardUserDefaults] boolForKey:@"Growl Installation:Do Not Prompt Again"];
|
||||
#endif
|
||||
|
||||
growlLaunched = [self _launchGrowlIfInstalledWithRegistrationDictionary:regDict];
|
||||
growlLaunched = [self _launchGrowlIfInstalledWithRegistrationDictionary:cachedRegistrationDictionary];
|
||||
}
|
||||
|
||||
+ (NSObject<GrowlApplicationBridgeDelegate> *) growlDelegate {
|
||||
@ -176,10 +211,10 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
identifier:nil];
|
||||
}
|
||||
|
||||
/*Send a notification to Growl for display.
|
||||
*title, description, and notifName are required.
|
||||
*All other id parameters may be nil to accept defaults.
|
||||
*priority is 0 by default; isSticky is NO by default.
|
||||
/* Send a notification to Growl for display.
|
||||
* title, description, and notifName are required.
|
||||
* All other id parameters may be nil to accept defaults.
|
||||
* priority is 0 by default; isSticky is NO by default.
|
||||
*/
|
||||
+ (void) notifyWithTitle:(NSString *)title
|
||||
description:(NSString *)description
|
||||
@ -193,75 +228,61 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
NSParameterAssert(notifName); //Notification name is required.
|
||||
NSParameterAssert(title || description); //At least one of title or description is required.
|
||||
|
||||
NSDictionary *regDict = [self bestRegistrationDictionary];
|
||||
if (!appName)
|
||||
appName = [[self _applicationNameForGrowlSearchingRegistrationDictionary:regDict] retain];
|
||||
if (!appIconData)
|
||||
appIconData = [[self _applicationIconDataForGrowlSearchingRegistrationDictionary:regDict] retain];
|
||||
NSNumber *pid = [[NSNumber alloc] initWithInt:[[NSProcessInfo processInfo] processIdentifier]];
|
||||
|
||||
// Build our noteDict from all passed parameters
|
||||
NSMutableDictionary *noteDict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
|
||||
appName, GROWL_APP_NAME,
|
||||
pid, GROWL_APP_PID,
|
||||
notifName, GROWL_NOTIFICATION_NAME,
|
||||
appIconData, GROWL_NOTIFICATION_APP_ICON,
|
||||
nil];
|
||||
|
||||
[pid release];
|
||||
|
||||
if (title) [noteDict setObject:title forKey:GROWL_NOTIFICATION_TITLE];
|
||||
if (description) [noteDict setObject:description forKey:GROWL_NOTIFICATION_DESCRIPTION];
|
||||
if (iconData) [noteDict setObject:iconData forKey:GROWL_NOTIFICATION_ICON];
|
||||
if (clickContext) [noteDict setObject:clickContext forKey:GROWL_NOTIFICATION_CLICK_CONTEXT];
|
||||
if (priority) {
|
||||
NSNumber *value = [[NSNumber alloc] initWithInt:priority];
|
||||
[noteDict setObject:value forKey:GROWL_NOTIFICATION_PRIORITY];
|
||||
[value release];
|
||||
}
|
||||
if (isSticky) {
|
||||
NSNumber *value = [[NSNumber alloc] initWithBool:isSticky];
|
||||
[noteDict setObject:value forKey:GROWL_NOTIFICATION_STICKY];
|
||||
[value release];
|
||||
}
|
||||
if (identifier) [noteDict setObject:identifier forKey:GROWL_NOTIFICATION_IDENTIFIER];
|
||||
if (title) setObjectForKey(noteDict, GROWL_NOTIFICATION_TITLE, title);
|
||||
if (description) setObjectForKey(noteDict, GROWL_NOTIFICATION_DESCRIPTION, description);
|
||||
if (iconData) setObjectForKey(noteDict, GROWL_NOTIFICATION_ICON, iconData);
|
||||
if (clickContext) setObjectForKey(noteDict, GROWL_NOTIFICATION_CLICK_CONTEXT, clickContext);
|
||||
if (priority) setIntegerForKey(noteDict, GROWL_NOTIFICATION_PRIORITY, priority);
|
||||
if (isSticky) setBooleanForKey(noteDict, GROWL_NOTIFICATION_STICKY, isSticky);
|
||||
if (identifier) setObjectForKey(noteDict, GROWL_NOTIFICATION_IDENTIFIER, identifier);
|
||||
|
||||
[self notifyWithDictionary:noteDict];
|
||||
[noteDict release];
|
||||
}
|
||||
|
||||
+ (void) notifyWithDictionary:(NSDictionary *)userInfo {
|
||||
//clean up things that need to be cleaned up.
|
||||
NSMutableDictionary *mUserInfo = [userInfo mutableCopy];
|
||||
|
||||
Class NSImageClass = [NSImage class];
|
||||
//notification icon.
|
||||
NSImage *icon = [mUserInfo objectForKey:GROWL_NOTIFICATION_ICON];
|
||||
if (icon && [icon isKindOfClass:NSImageClass])
|
||||
[mUserInfo setObject:[icon TIFFRepresentation] forKey:GROWL_NOTIFICATION_ICON];
|
||||
//per-notification application icon.
|
||||
icon = [mUserInfo objectForKey:GROWL_NOTIFICATION_APP_ICON];
|
||||
if (icon && [icon isKindOfClass:NSImageClass])
|
||||
[mUserInfo setObject:[icon TIFFRepresentation] forKey:GROWL_NOTIFICATION_APP_ICON];
|
||||
|
||||
userInfo = [mUserInfo autorelease];
|
||||
|
||||
//post it.
|
||||
if (growlLaunched) {
|
||||
NSConnection *connection = [NSConnection connectionWithRegisteredName:@"GrowlApplicationBridgePathway" host:nil];
|
||||
if (connection) {
|
||||
if (growlLaunched) {
|
||||
NSProxy<GrowlNotificationProtocol> *currentGrowlProxy = [self growlProxy];
|
||||
|
||||
//Make sure we have everything that we need (that we can retrieve from the registration dictionary).
|
||||
userInfo = [self notificationDictionaryByFillingInDictionary:userInfo];
|
||||
|
||||
if (currentGrowlProxy) {
|
||||
//Post to Growl via GrowlApplicationBridgePathway
|
||||
NS_DURING
|
||||
NSDistantObject *theProxy = [connection rootProxy];
|
||||
[theProxy setProtocolForProxy:@protocol(GrowlNotificationProtocol)];
|
||||
id<GrowlNotificationProtocol> growlProxy = (id)theProxy;
|
||||
[growlProxy postNotificationWithDictionary:userInfo];
|
||||
NS_HANDLER
|
||||
TRY
|
||||
[currentGrowlProxy postNotificationWithDictionary:userInfo];
|
||||
ENDTRY
|
||||
CATCH
|
||||
NSLog(@"GrowlApplicationBridge: exception while sending notification: %@", localException);
|
||||
NS_ENDHANDLER
|
||||
ENDCATCH
|
||||
} else {
|
||||
//Post to Growl via NSDistributedNotificationCenter
|
||||
//NSLog(@"GrowlApplicationBridge: could not find local GrowlApplicationBridgePathway, falling back to NSDistributedNotificationCenter");
|
||||
|
||||
//DNC needs a plist. this means we must pass data, not an NSImage.
|
||||
Class NSImageClass = [NSImage class];
|
||||
NSImage *icon = [userInfo objectForKey:GROWL_NOTIFICATION_ICON];
|
||||
NSImage *appIcon = [userInfo objectForKey:GROWL_NOTIFICATION_APP_ICON];
|
||||
BOOL iconIsImage = icon && [icon isKindOfClass:NSImageClass];
|
||||
BOOL appIconIsImage = appIcon && [appIcon isKindOfClass:NSImageClass];
|
||||
if (iconIsImage || appIconIsImage) {
|
||||
NSMutableDictionary *mUserInfo = [userInfo mutableCopy];
|
||||
//notification icon.
|
||||
if (iconIsImage)
|
||||
[mUserInfo setObject:[icon TIFFRepresentation] forKey:GROWL_NOTIFICATION_ICON];
|
||||
//per-notification application icon.
|
||||
if (appIconIsImage)
|
||||
[mUserInfo setObject:[appIcon TIFFRepresentation] forKey:GROWL_NOTIFICATION_APP_ICON];
|
||||
|
||||
userInfo = [mUserInfo autorelease];
|
||||
}
|
||||
|
||||
//Post to Growl via NSDistributedNotificationCenter
|
||||
[[NSDistributedNotificationCenter defaultCenter] postNotificationName:GROWL_NOTIFICATION
|
||||
object:nil
|
||||
userInfo:userInfo
|
||||
@ -273,12 +294,8 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
* it, store this notification for posting
|
||||
*/
|
||||
if (!userChoseNotToInstallGrowl) {
|
||||
//in case the dictionary is mutable, make a copy.
|
||||
userInfo = [userInfo copy];
|
||||
|
||||
if (!queuedGrowlNotifications) {
|
||||
if (!queuedGrowlNotifications)
|
||||
queuedGrowlNotifications = [[NSMutableArray alloc] init];
|
||||
}
|
||||
[queuedGrowlNotifications addObject:userInfo];
|
||||
|
||||
//if we have not already asked the user to install Growl, do it now
|
||||
@ -286,7 +303,6 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
[GrowlInstallationPrompt showInstallationPrompt];
|
||||
promptedToInstallGrowl = YES;
|
||||
}
|
||||
[userInfo release];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@ -295,7 +311,7 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
#pragma mark -
|
||||
|
||||
+ (BOOL) isGrowlInstalled {
|
||||
return ([GrowlPathUtil growlPrefPaneBundle] != nil);
|
||||
return ([GrowlPathUtilities growlPrefPaneBundle] != nil);
|
||||
}
|
||||
|
||||
+ (BOOL) isGrowlRunning {
|
||||
@ -303,19 +319,30 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
ProcessSerialNumber PSN = { kNoProcess, kNoProcess };
|
||||
|
||||
while (GetNextProcess(&PSN) == noErr) {
|
||||
NSDictionary *infoDict = (NSDictionary *)ProcessInformationCopyDictionary(&PSN, kProcessDictionaryIncludeAllInformationMask);
|
||||
CFDictionaryRef infoDict = ProcessInformationCopyDictionary(&PSN, kProcessDictionaryIncludeAllInformationMask);
|
||||
CFStringRef bundleId = CFDictionaryGetValue(infoDict, kCFBundleIdentifierKey);
|
||||
|
||||
if ([[infoDict objectForKey:(NSString *)kCFBundleIdentifierKey] isEqualToString:@"com.Growl.GrowlHelperApp"]) {
|
||||
if (bundleId && CFStringCompare(bundleId, CFSTR("com.Growl.GrowlHelperApp"), 0) == kCFCompareEqualTo) {
|
||||
growlIsRunning = YES;
|
||||
[infoDict release];
|
||||
CFRelease(infoDict);
|
||||
break;
|
||||
}
|
||||
[infoDict release];
|
||||
CFRelease(infoDict);
|
||||
}
|
||||
|
||||
return growlIsRunning;
|
||||
}
|
||||
|
||||
+ (void) displayInstallationPromptIfNeeded {
|
||||
#ifdef GROWL_WITH_INSTALLER
|
||||
//if we have not already asked the user to install Growl, do it now
|
||||
if (!promptedToInstallGrowl) {
|
||||
[GrowlInstallationPrompt showInstallationPrompt];
|
||||
promptedToInstallGrowl = YES;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
+ (BOOL) registerWithDictionary:(NSDictionary *)regDict {
|
||||
@ -323,6 +350,10 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
regDict = [self registrationDictionaryByFillingInDictionary:regDict];
|
||||
else
|
||||
regDict = [self bestRegistrationDictionary];
|
||||
|
||||
[cachedRegistrationDictionary release];
|
||||
cachedRegistrationDictionary = [regDict retain];
|
||||
|
||||
return [self _launchGrowlIfInstalledWithRegistrationDictionary:regDict];
|
||||
}
|
||||
|
||||
@ -365,16 +396,13 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
|
||||
+ (NSDictionary *) bestRegistrationDictionary {
|
||||
NSDictionary *registrationDictionary = [self registrationDictionaryFromDelegate];
|
||||
if (!registrationDictionary)
|
||||
registrationDictionary = [self registrationDictionaryFromBundle:nil];
|
||||
|
||||
if (!registrationDictionary) {
|
||||
NSLog(@"GrowlApplicationBridge: The Growl delegate did not supply a registration dictionary, and the app bundle at %@ does not have one. Please tell this application's developer.", [[NSBundle mainBundle] bundlePath]);
|
||||
registrationDictionary = [self registrationDictionaryFromBundle:nil];
|
||||
if (!registrationDictionary)
|
||||
NSLog(@"GrowlApplicationBridge: The Growl delegate did not supply a registration dictionary, and the app bundle at %@ does not have one. Please tell this application's developer.", [[NSBundle mainBundle] bundlePath]);
|
||||
}
|
||||
|
||||
registrationDictionary = [self registrationDictionaryByFillingInDictionary:registrationDictionary];
|
||||
|
||||
return registrationDictionary;
|
||||
return [self registrationDictionaryByFillingInDictionary:registrationDictionary];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
@ -400,16 +428,10 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
|
||||
if ((!keys) || [keys containsObject:GROWL_APP_ICON]) {
|
||||
if (![mRegDict objectForKey:GROWL_APP_ICON]) {
|
||||
if (!appIconData) {
|
||||
appIconData = [self _applicationIconDataForGrowlSearchingRegistrationDictionary:regDict];
|
||||
if(appIconData && [appIconData isKindOfClass:[NSImage class]])
|
||||
appIconData = [(NSImage *)appIconData TIFFRepresentation];
|
||||
appIconData = [appIconData retain];
|
||||
}
|
||||
if (appIconData) {
|
||||
[mRegDict setObject:appIconData
|
||||
forKey:GROWL_APP_ICON];
|
||||
}
|
||||
if (!appIconData)
|
||||
appIconData = [[self _applicationIconDataForGrowlSearchingRegistrationDictionary:regDict] retain];
|
||||
if (appIconData)
|
||||
[mRegDict setObject:appIconData forKey:GROWL_APP_ICON];
|
||||
}
|
||||
}
|
||||
|
||||
@ -417,11 +439,11 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
if (![mRegDict objectForKey:GROWL_APP_LOCATION]) {
|
||||
NSURL *myURL = copyCurrentProcessURL();
|
||||
if (myURL) {
|
||||
NSDictionary *file_data = [myURL dockDescription];
|
||||
NSDictionary *file_data = createDockDescriptionWithURL(myURL);
|
||||
if (file_data) {
|
||||
NSDictionary *location = [[NSDictionary alloc] initWithObjectsAndKeys:file_data, @"file-data", nil];
|
||||
[mRegDict setObject:location
|
||||
forKey:GROWL_APP_LOCATION];
|
||||
[file_data release];
|
||||
[mRegDict setObject:location forKey:GROWL_APP_LOCATION];
|
||||
[location release];
|
||||
} else {
|
||||
[mRegDict removeObjectForKey:GROWL_APP_LOCATION];
|
||||
@ -434,16 +456,60 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
if ((!keys) || [keys containsObject:GROWL_NOTIFICATIONS_DEFAULT]) {
|
||||
if (![mRegDict objectForKey:GROWL_NOTIFICATIONS_DEFAULT]) {
|
||||
NSArray *all = [mRegDict objectForKey:GROWL_NOTIFICATIONS_ALL];
|
||||
if (all) {
|
||||
[mRegDict setObject:all
|
||||
forKey:GROWL_NOTIFICATIONS_DEFAULT];
|
||||
}
|
||||
if (all)
|
||||
[mRegDict setObject:all forKey:GROWL_NOTIFICATIONS_DEFAULT];
|
||||
}
|
||||
}
|
||||
|
||||
NSDictionary *result = [NSDictionary dictionaryWithDictionary:mRegDict];
|
||||
[mRegDict release];
|
||||
return result;
|
||||
if ((!keys) || [keys containsObject:GROWL_APP_ID])
|
||||
if (![mRegDict objectForKey:GROWL_APP_ID])
|
||||
[mRegDict setObject:(NSString *)CFBundleGetIdentifier(CFBundleGetMainBundle()) forKey:GROWL_APP_ID];
|
||||
|
||||
return [mRegDict autorelease];
|
||||
}
|
||||
|
||||
+ (NSDictionary *) notificationDictionaryByFillingInDictionary:(NSDictionary *)notifDict {
|
||||
NSMutableDictionary *mNotifDict = [notifDict mutableCopy];
|
||||
|
||||
if (![mNotifDict objectForKey:GROWL_APP_NAME]) {
|
||||
if (!appName)
|
||||
appName = [[self _applicationNameForGrowlSearchingRegistrationDictionary:cachedRegistrationDictionary] retain];
|
||||
|
||||
if (appName) {
|
||||
[mNotifDict setObject:appName
|
||||
forKey:GROWL_APP_NAME];
|
||||
}
|
||||
}
|
||||
|
||||
if (![mNotifDict objectForKey:GROWL_APP_ICON]) {
|
||||
if (!appIconData)
|
||||
appIconData = [[self _applicationIconDataForGrowlSearchingRegistrationDictionary:cachedRegistrationDictionary] retain];
|
||||
|
||||
if (appIconData) {
|
||||
[mNotifDict setObject:appIconData
|
||||
forKey:GROWL_APP_ICON];
|
||||
}
|
||||
}
|
||||
|
||||
//Only include the PID when there's a click context. We do this because NSDNC imposes a 15-MiB limit on the serialized notification, and we wouldn't want to overrun it because of a 4-byte PID.
|
||||
if ([mNotifDict objectForKey:GROWL_NOTIFICATION_CLICK_CONTEXT] && ![mNotifDict objectForKey:GROWL_APP_PID]) {
|
||||
NSNumber *pidNum = [[NSNumber alloc] initWithInt:[[NSProcessInfo processInfo] processIdentifier]];
|
||||
|
||||
[mNotifDict setObject:pidNum
|
||||
forKey:GROWL_APP_PID];
|
||||
|
||||
[pidNum release];
|
||||
}
|
||||
|
||||
return [mNotifDict autorelease];
|
||||
}
|
||||
|
||||
+ (NSDictionary *) frameworkInfoDictionary {
|
||||
#ifdef GROWL_WITH_INSTALLER
|
||||
return (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetBundleWithIdentifier(CFSTR("com.growl.growlwithinstallerframework")));
|
||||
#else
|
||||
return (NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetBundleWithIdentifier(CFSTR("com.growl.growlframework")));
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
@ -455,23 +521,31 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
if (delegate && [delegate respondsToSelector:@selector(applicationNameForGrowl)])
|
||||
applicationNameForGrowl = [delegate applicationNameForGrowl];
|
||||
|
||||
if (!applicationNameForGrowl)
|
||||
if (!applicationNameForGrowl) {
|
||||
applicationNameForGrowl = [regDict objectForKey:GROWL_APP_NAME];
|
||||
|
||||
if (!applicationNameForGrowl)
|
||||
applicationNameForGrowl = [[NSProcessInfo processInfo] processName];
|
||||
if (!applicationNameForGrowl)
|
||||
applicationNameForGrowl = [[NSProcessInfo processInfo] processName];
|
||||
}
|
||||
|
||||
return applicationNameForGrowl;
|
||||
}
|
||||
+ (NSData *) _applicationIconDataForGrowlSearchingRegistrationDictionary:(NSDictionary *)regDict {
|
||||
NSData *iconData = nil;
|
||||
|
||||
if (delegate && [delegate respondsToSelector:@selector(applicationIconDataForGrowl)])
|
||||
iconData = [delegate applicationIconDataForGrowl];
|
||||
if (delegate) {
|
||||
if ([delegate respondsToSelector:@selector(applicationIconForGrowl)])
|
||||
iconData = (NSData *)[delegate applicationIconForGrowl];
|
||||
else if ([delegate respondsToSelector:@selector(applicationIconDataForGrowl)])
|
||||
iconData = [delegate applicationIconDataForGrowl];
|
||||
}
|
||||
|
||||
if (!iconData)
|
||||
iconData = [regDict objectForKey:GROWL_APP_ICON];
|
||||
|
||||
if (iconData && [iconData isKindOfClass:[NSImage class]])
|
||||
iconData = [(NSImage *)iconData TIFFRepresentation];
|
||||
|
||||
if (!iconData) {
|
||||
NSURL *URL = copyCurrentProcessURL();
|
||||
iconData = [copyIconDataForURL(URL) autorelease];
|
||||
@ -485,19 +559,65 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
* called manually, and the calling observer should only be registered if the
|
||||
* delegate responds to growlNotificationWasClicked:.
|
||||
*/
|
||||
+ (void) _growlNotificationWasClicked:(NSNotification *)notification {
|
||||
[delegate performSelector:@selector(growlNotificationWasClicked:)
|
||||
withObject:[[notification userInfo] objectForKey:GROWL_KEY_CLICKED_CONTEXT]];
|
||||
+ (void) growlNotificationWasClicked:(NSNotification *)notification {
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
[delegate growlNotificationWasClicked:
|
||||
[[notification userInfo] objectForKey:GROWL_KEY_CLICKED_CONTEXT]];
|
||||
[pool release];
|
||||
}
|
||||
+ (void) _growlNotificationTimedOut:(NSNotification *)notification {
|
||||
[delegate performSelector:@selector(growlNotificationTimedOut:)
|
||||
withObject:[[notification userInfo] objectForKey:GROWL_KEY_CLICKED_CONTEXT]];
|
||||
+ (void) growlNotificationTimedOut:(NSNotification *)notification {
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
[delegate growlNotificationTimedOut:
|
||||
[[notification userInfo] objectForKey:GROWL_KEY_CLICKED_CONTEXT]];
|
||||
[pool release];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
//When a connection dies, release our reference to its proxy
|
||||
+ (void) connectionDidDie:(NSNotification *)notification {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||
name:NSConnectionDidDieNotification
|
||||
object:[notification object]];
|
||||
[growlProxy release]; growlProxy = nil;
|
||||
}
|
||||
|
||||
+ (NSProxy<GrowlNotificationProtocol> *) growlProxy {
|
||||
if (!growlProxy) {
|
||||
NSConnection *connection = [NSConnection connectionWithRegisteredName:@"GrowlApplicationBridgePathway" host:nil];
|
||||
if (connection) {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(connectionDidDie:)
|
||||
name:NSConnectionDidDieNotification
|
||||
object:connection];
|
||||
|
||||
TRY
|
||||
{
|
||||
NSDistantObject *theProxy = [connection rootProxy];
|
||||
if ([theProxy respondsToSelector:@selector(registerApplicationWithDictionary:)]) {
|
||||
[theProxy setProtocolForProxy:@protocol(GrowlNotificationProtocol)];
|
||||
growlProxy = [(NSProxy<GrowlNotificationProtocol> *)theProxy retain];
|
||||
} else {
|
||||
NSLog(@"Received a fake GrowlApplicationBridgePathway object. Some other application is interfering with Growl, or something went horribly wrong. Please file a bug report.");
|
||||
growlProxy = nil;
|
||||
}
|
||||
}
|
||||
ENDTRY
|
||||
CATCH
|
||||
{
|
||||
NSLog(@"GrowlApplicationBridge: exception while sending notification: %@", localException);
|
||||
growlProxy = nil;
|
||||
}
|
||||
ENDCATCH
|
||||
}
|
||||
}
|
||||
|
||||
return growlProxy;
|
||||
}
|
||||
|
||||
+ (void) _growlIsReady:(NSNotification *)notification {
|
||||
#pragma unused(notification)
|
||||
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
|
||||
|
||||
//Growl has now launched; we may get here with (growlLaunched == NO) when the user first installs
|
||||
growlLaunched = YES;
|
||||
@ -508,13 +628,13 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
|
||||
//Post a notification locally
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:GROWL_IS_READY
|
||||
object:nil];
|
||||
object:nil
|
||||
userInfo:nil];
|
||||
|
||||
//Stop observing for GROWL_IS_READY
|
||||
NSDistributedNotificationCenter *distCenter = [NSDistributedNotificationCenter defaultCenter];
|
||||
[distCenter removeObserver:self
|
||||
name:GROWL_IS_READY
|
||||
object:nil];
|
||||
[[NSDistributedNotificationCenter defaultCenter] removeObserver:self
|
||||
name:GROWL_IS_READY
|
||||
object:nil];
|
||||
|
||||
//register (fixes #102: this is necessary if we got here by Growl having just been installed)
|
||||
if (registerWhenGrowlIsReady) {
|
||||
@ -527,30 +647,31 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
NSEnumerator *enumerator = [queuedGrowlNotifications objectEnumerator];
|
||||
NSDictionary *noteDict;
|
||||
|
||||
while ((noteDict = [enumerator nextObject])) {
|
||||
NSConnection *connection = [NSConnection connectionWithRegisteredName:@"GrowlApplicationBridgePathway" host:nil];
|
||||
if (connection) {
|
||||
//Configure the growl proxy if it isn't currently configured
|
||||
NSProxy<GrowlNotificationProtocol> *currentGrowlProxy = [self growlProxy];
|
||||
|
||||
while ((noteDict = [enumerator nextObject])) {
|
||||
if (currentGrowlProxy) {
|
||||
//Post to Growl via GrowlApplicationBridgePathway
|
||||
NS_DURING
|
||||
NSDistantObject *theProxy = [connection rootProxy];
|
||||
[theProxy setProtocolForProxy:@protocol(GrowlNotificationProtocol)];
|
||||
id<GrowlNotificationProtocol> growlProxy = (id)theProxy;
|
||||
[growlProxy postNotificationWithDictionary:noteDict];
|
||||
[currentGrowlProxy postNotificationWithDictionary:noteDict];
|
||||
NS_HANDLER
|
||||
NSLog(@"GrowlApplicationBridge: exception while sending notification: %@", localException);
|
||||
NS_ENDHANDLER
|
||||
} else {
|
||||
//Post to Growl via NSDistributedNotificationCenter
|
||||
//NSLog(@"GrowlApplicationBridge: could not find local GrowlApplicationBridgePathway, falling back to NSDistributedNotificationCenter");
|
||||
[distCenter postNotificationName:GROWL_NOTIFICATION
|
||||
object:nil
|
||||
userInfo:noteDict
|
||||
deliverImmediately:NO];
|
||||
NSLog(@"GrowlApplicationBridge: could not find local GrowlApplicationBridgePathway, falling back to NSDistributedNotificationCenter");
|
||||
[[NSDistributedNotificationCenter defaultCenter] postNotificationName:GROWL_NOTIFICATION
|
||||
object:NULL
|
||||
userInfo:noteDict
|
||||
deliverImmediately:FALSE];
|
||||
}
|
||||
}
|
||||
|
||||
[queuedGrowlNotifications release]; queuedGrowlNotifications = nil;
|
||||
#endif
|
||||
|
||||
[pool release];
|
||||
}
|
||||
|
||||
#ifdef GROWL_WITH_INSTALLER
|
||||
@ -572,10 +693,14 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
NSString *packagedVersion, *installedVersion;
|
||||
BOOL upgradeIsAvailable;
|
||||
|
||||
ourGrowlPrefPaneInfoPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"GrowlPrefPaneInfo"
|
||||
ofType:@"plist"];
|
||||
ourGrowlPrefPaneInfoPath = [[NSBundle bundleWithIdentifier:@"com.growl.growlwithinstallerframework"] pathForResource:@"GrowlPrefPaneInfo"
|
||||
ofType:@"plist"];
|
||||
|
||||
NSObject *infoPropertyList = createPropertyListFromURL([NSURL fileURLWithPath:ourGrowlPrefPaneInfoPath],
|
||||
kCFPropertyListImmutable,
|
||||
/* outFormat */ NULL, /* outErrorString */ NULL);
|
||||
NSDictionary *infoDict = ([infoPropertyList isKindOfClass:[NSDictionary class]] ? (NSDictionary *)infoPropertyList : nil);
|
||||
|
||||
NSDictionary *infoDict = [[NSDictionary alloc] initWithContentsOfFile:ourGrowlPrefPaneInfoPath];
|
||||
packagedVersion = [infoDict objectForKey:(NSString *)kCFBundleVersionKey];
|
||||
|
||||
infoDictionary = [growlPrefPaneBundle infoDictionary];
|
||||
@ -603,7 +728,7 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
NSBundle *growlPrefPaneBundle;
|
||||
BOOL success = NO;
|
||||
|
||||
growlPrefPaneBundle = [GrowlPathUtil growlPrefPaneBundle];
|
||||
growlPrefPaneBundle = [GrowlPathUtilities growlPrefPaneBundle];
|
||||
|
||||
if (growlPrefPaneBundle) {
|
||||
NSString *growlHelperAppPath = [growlPrefPaneBundle pathForResource:@"GrowlHelperApp"
|
||||
@ -629,16 +754,18 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
NSString *regDictPath;
|
||||
|
||||
//Obtain a truly unique file name
|
||||
regDictFileName = [[[[self _applicationNameForGrowlSearchingRegistrationDictionary:regDict] stringByAppendingString:@"-"] stringByAppendingString:[[NSProcessInfo processInfo] globallyUniqueString]] stringByAppendingPathExtension:GROWL_REG_DICT_EXTENSION];
|
||||
if ([regDictFileName length] > NAME_MAX) {
|
||||
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
|
||||
CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuid);
|
||||
CFRelease(uuid);
|
||||
regDictFileName = [[NSString stringWithFormat:@"%@-%u-%@", [self _applicationNameForGrowlSearchingRegistrationDictionary:regDict], getpid(), (NSString *)uuidString] stringByAppendingPathExtension:GROWL_REG_DICT_EXTENSION];
|
||||
CFRelease(uuidString);
|
||||
if ([regDictFileName length] > NAME_MAX)
|
||||
regDictFileName = [[regDictFileName substringToIndex:(NAME_MAX - [GROWL_REG_DICT_EXTENSION length])] stringByAppendingPathExtension:GROWL_REG_DICT_EXTENSION];
|
||||
}
|
||||
|
||||
//make sure it's within pathname length constraints
|
||||
regDictPath = [NSTemporaryDirectory() stringByAppendingPathComponent:regDictFileName];
|
||||
if ([regDictPath length] > PATH_MAX) {
|
||||
if ([regDictPath length] > PATH_MAX)
|
||||
regDictPath = [[regDictPath substringToIndex:(PATH_MAX - [GROWL_REG_DICT_EXTENSION length])] stringByAppendingPathExtension:GROWL_REG_DICT_EXTENSION];
|
||||
}
|
||||
|
||||
//Write the registration dictionary out to the temporary directory
|
||||
NSData *plistData;
|
||||
@ -647,7 +774,8 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
format:NSPropertyListBinaryFormat_v1_0
|
||||
errorDescription:&error];
|
||||
if (plistData) {
|
||||
success = [plistData writeToFile:regDictPath atomically:NO];
|
||||
if (![plistData writeToFile:regDictPath atomically:NO])
|
||||
NSLog(@"GrowlApplicationBridge: Error writing registration dictionary at %@", regDictPath);
|
||||
} else {
|
||||
NSLog(@"GrowlApplicationBridge: Error writing registration dictionary at %@: %@", regDictPath, error);
|
||||
NSLog(@"GrowlApplicationBridge: Registration dictionary follows\n%@", regDict);
|
||||
@ -655,9 +783,8 @@ static BOOL registerWhenGrowlIsReady = NO;
|
||||
}
|
||||
|
||||
regStatus = FSPathMakeRef((UInt8 *)[regDictPath fileSystemRepresentation], ®ItemRef, NULL);
|
||||
if (regStatus == noErr) {
|
||||
if (regStatus == noErr)
|
||||
passRegDict = YES;
|
||||
}
|
||||
}
|
||||
|
||||
spec.appRef = &appRef;
|
||||
|
@ -7,10 +7,10 @@
|
||||
|
||||
#ifdef __OBJC__
|
||||
#define XSTR(x) (@x)
|
||||
#define STRING NSString *
|
||||
#define STRING_TYPE NSString *
|
||||
#else
|
||||
#define XSTR CFSTR
|
||||
#define STRING CFStringRef
|
||||
#define STRING_TYPE CFStringRef
|
||||
#endif
|
||||
|
||||
/*! @header GrowlDefines.h
|
||||
@ -48,6 +48,14 @@
|
||||
* "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
|
||||
@ -74,6 +82,26 @@
|
||||
* 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
|
||||
@ -97,9 +125,10 @@
|
||||
|
||||
/*! @defined GROWL_NOTIFICATION_NAME
|
||||
* @abstract The name of the notification.
|
||||
* @discussion The name of the notification. This should be human-readable, as
|
||||
* it's shown in the prefpane, in the list of notifications your application
|
||||
* supports. */
|
||||
* @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.
|
||||
@ -186,6 +215,15 @@
|
||||
*/
|
||||
#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
|
||||
|
||||
@ -304,4 +342,7 @@
|
||||
*/
|
||||
#define GROWL_REG_DICT_EXTENSION XSTR("growlRegDict")
|
||||
|
||||
|
||||
#define GROWL_POSITION_PREFERENCE_KEY @"GrowlSelectedPosition"
|
||||
|
||||
#endif //ndef _GROWLDEFINES_H
|
||||
|
@ -10,6 +10,8 @@
|
||||
#define _GROWL_GROWLDEFINESINTERNAL_H
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __OBJC__
|
||||
#define XSTR(x) (@x)
|
||||
@ -154,11 +156,17 @@ struct GrowlNetworkNotification {
|
||||
* @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.
|
||||
*/
|
||||
@ -212,16 +220,40 @@ struct GrowlNetworkNotification {
|
||||
*/
|
||||
#define GROWL_APP_LOCATION XSTR("AppLocation")
|
||||
|
||||
#endif //ndef _GROWL_GROWLDEFINESINTERNAL_H
|
||||
/*! @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 --- */
|
||||
|
||||
/*Since anything that needs the include guards won't be using these macros, we
|
||||
* don't need the include guards here.
|
||||
*/
|
||||
|
||||
#ifdef __OBJC__
|
||||
|
||||
/*! @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
|
||||
@ -236,14 +268,16 @@ struct GrowlNetworkNotification {
|
||||
*/
|
||||
#define UPDATE_GROWL_PREFS() do { \
|
||||
SYNCHRONIZE_GROWL_PREFS(); \
|
||||
NSNumber *pid = [[NSNumber alloc] initWithInt:[[NSProcessInfo processInfo] processIdentifier]];\
|
||||
NSDictionary *userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:\
|
||||
pid, @"pid",\
|
||||
nil];\
|
||||
[pid release];\
|
||||
[[NSDistributedNotificationCenter defaultCenter]\
|
||||
postNotificationName:@"GrowlPreferencesChanged" object:@"GrowlUserDefaults" userInfo:userInfo];\
|
||||
[userInfo release];\
|
||||
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
|
||||
@ -317,13 +351,7 @@ struct GrowlNetworkNotification {
|
||||
* @param domain The bundle ID of the plug-in.
|
||||
*/
|
||||
#define WRITE_GROWL_PREF_BOOL(key, value, domain) do {\
|
||||
CFBooleanRef boolValue; \
|
||||
if (value) {\
|
||||
boolValue = kCFBooleanTrue; \
|
||||
} else {\
|
||||
boolValue = kCFBooleanFalse; \
|
||||
}\
|
||||
WRITE_GROWL_PREF_VALUE(key, boolValue, domain); } while(0)
|
||||
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.
|
||||
@ -383,4 +411,20 @@ struct GrowlNetworkNotification {
|
||||
WRITE_GROWL_PREF_VALUE(key, floatValue, domain); \
|
||||
CFRelease(floatValue); } while(0)
|
||||
|
||||
#endif /* __OBJC__ */
|
||||
|
||||
/*! @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
|
||||
|
@ -1,30 +0,0 @@
|
||||
//
|
||||
// GrowlPathUtil.h
|
||||
// Growl
|
||||
//
|
||||
// Created by Ingmar Stein on 17.04.05.
|
||||
// Copyright 2005 The Growl Project. All rights reserved.
|
||||
//
|
||||
// This file is under the BSD License, refer to License.txt for details
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface GrowlPathUtil : NSObject {
|
||||
|
||||
}
|
||||
/*!
|
||||
* @method growlPrefPaneBundle
|
||||
* @abstract Returns the bundle containing Growl's PrefPane.
|
||||
* @discussion Searches all installed PrefPanes for the Growl PrefPane.
|
||||
* @result Returns an NSBundle if Growl's PrefPane is installed, nil otherwise
|
||||
*/
|
||||
+ (NSBundle *) growlPrefPaneBundle;
|
||||
+ (NSBundle *) helperAppBundle;
|
||||
+ (NSString *) growlSupportDir;
|
||||
|
||||
//the current screenshots directory: $HOME/Library/Application\ Support/Growl/Screenshots
|
||||
+ (NSString *) screenshotsDirectory;
|
||||
//returns e.g. @"Screenshot 1". you append your own pathname extension; it is guaranteed not to exist.
|
||||
+ (NSString *) nextScreenshotName;
|
||||
|
||||
@end
|
@ -1,171 +0,0 @@
|
||||
//
|
||||
// GrowlPathUtil.m
|
||||
// Growl
|
||||
//
|
||||
// Created by Ingmar Stein on 17.04.05.
|
||||
// Copyright 2005 The Growl Project. All rights reserved.
|
||||
//
|
||||
// This file is under the BSD License, refer to License.txt for details
|
||||
|
||||
#import "GrowlPathUtil.h"
|
||||
|
||||
#define HelperAppBundleIdentifier @"com.Growl.GrowlHelperApp"
|
||||
#define GROWL_PREFPANE_BUNDLE_IDENTIFIER @"com.growl.prefpanel"
|
||||
#define GROWL_PREFPANE_NAME @"Growl.prefPane"
|
||||
#define PREFERENCE_PANES_SUBFOLDER_OF_LIBRARY @"PreferencePanes"
|
||||
#define PREFERENCE_PANE_EXTENSION @"prefPane"
|
||||
|
||||
static NSBundle *helperAppBundle;
|
||||
static NSBundle *prefPaneBundle;
|
||||
|
||||
@implementation GrowlPathUtil
|
||||
|
||||
+ (NSBundle *) growlPrefPaneBundle {
|
||||
NSArray *librarySearchPaths;
|
||||
NSString *path;
|
||||
NSString *bundleIdentifier;
|
||||
NSEnumerator *searchPathEnumerator;
|
||||
NSBundle *bundle;
|
||||
|
||||
if (prefPaneBundle) {
|
||||
return prefPaneBundle;
|
||||
}
|
||||
|
||||
static const unsigned bundleIDComparisonFlags = NSCaseInsensitiveSearch | NSBackwardsSearch;
|
||||
|
||||
//Find Library directories in all domains except /System (as of Panther, that's ~/Library, /Library, and /Network/Library)
|
||||
librarySearchPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask & ~NSSystemDomainMask, YES);
|
||||
|
||||
/*First up, we'll have a look for Growl.prefPane, and if it exists, check
|
||||
* whether it is our prefPane.
|
||||
*This is much faster than having to enumerate all preference panes, and
|
||||
* can drop a significant amount of time off this code.
|
||||
*/
|
||||
searchPathEnumerator = [librarySearchPaths objectEnumerator];
|
||||
while ((path = [searchPathEnumerator nextObject])) {
|
||||
path = [path stringByAppendingPathComponent:PREFERENCE_PANES_SUBFOLDER_OF_LIBRARY];
|
||||
path = [path stringByAppendingPathComponent:GROWL_PREFPANE_NAME];
|
||||
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
|
||||
bundle = [NSBundle bundleWithPath:path];
|
||||
|
||||
if (bundle) {
|
||||
bundleIdentifier = [bundle bundleIdentifier];
|
||||
|
||||
if (bundleIdentifier && ([bundleIdentifier compare:GROWL_PREFPANE_BUNDLE_IDENTIFIER options:bundleIDComparisonFlags] == NSOrderedSame)) {
|
||||
prefPaneBundle = bundle;
|
||||
return prefPaneBundle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*Enumerate all installed preference panes, looking for the Growl prefpane
|
||||
* bundle identifier and stopping when we find it.
|
||||
*Note that we check the bundle identifier because we should not insist
|
||||
* that the user not rename his preference pane files, although most users
|
||||
* of course will not. If the user wants to mutilate the Info.plist file
|
||||
* inside the bundle, he/she deserves to not have a working Growl
|
||||
* installation.
|
||||
*/
|
||||
searchPathEnumerator = [librarySearchPaths objectEnumerator];
|
||||
while ((path = [searchPathEnumerator nextObject])) {
|
||||
NSString *bundlePath;
|
||||
NSDirectoryEnumerator *bundleEnum;
|
||||
|
||||
path = [path stringByAppendingPathComponent:PREFERENCE_PANES_SUBFOLDER_OF_LIBRARY];
|
||||
bundleEnum = [[NSFileManager defaultManager] enumeratorAtPath:path];
|
||||
|
||||
while ((bundlePath = [bundleEnum nextObject])) {
|
||||
if ([[bundlePath pathExtension] isEqualToString:PREFERENCE_PANE_EXTENSION]) {
|
||||
bundle = [NSBundle bundleWithPath:[path stringByAppendingPathComponent:bundlePath]];
|
||||
|
||||
if (bundle) {
|
||||
bundleIdentifier = [bundle bundleIdentifier];
|
||||
|
||||
if (bundleIdentifier && ([bundleIdentifier compare:GROWL_PREFPANE_BUNDLE_IDENTIFIER options:bundleIDComparisonFlags] == NSOrderedSame)) {
|
||||
prefPaneBundle = bundle;
|
||||
return prefPaneBundle;
|
||||
}
|
||||
}
|
||||
|
||||
[bundleEnum skipDescendents];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Important file-system objects
|
||||
|
||||
+ (NSBundle *) helperAppBundle {
|
||||
if (!helperAppBundle) {
|
||||
NSBundle *bundle = [NSBundle mainBundle];
|
||||
if ([[bundle bundleIdentifier] isEqualToString:HelperAppBundleIdentifier]) {
|
||||
//we are running in GHA.
|
||||
helperAppBundle = bundle;
|
||||
} else {
|
||||
//look in the prefpane bundle.
|
||||
bundle = [NSBundle bundleForClass:[GrowlPathUtil class]];
|
||||
if (![[bundle bundleIdentifier] isEqualToString:GROWL_PREFPANE_BUNDLE_IDENTIFIER]) {
|
||||
bundle = [GrowlPathUtil growlPrefPaneBundle];
|
||||
}
|
||||
NSString *helperAppPath = [bundle pathForResource:@"GrowlHelperApp" ofType:@"app"];
|
||||
helperAppBundle = [NSBundle bundleWithPath:helperAppPath];
|
||||
}
|
||||
}
|
||||
return helperAppBundle;
|
||||
}
|
||||
|
||||
+ (NSString *) growlSupportDir {
|
||||
NSString *supportDir;
|
||||
NSArray *searchPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, /* expandTilde */ YES);
|
||||
|
||||
supportDir = [searchPath objectAtIndex:0U];
|
||||
supportDir = [supportDir stringByAppendingPathComponent:@"Application Support/Growl"];
|
||||
|
||||
return supportDir;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
+ (NSString *) screenshotsDirectory {
|
||||
NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/Growl/Screenshots"];
|
||||
[[NSFileManager defaultManager] createDirectoryAtPath:path
|
||||
attributes:nil];
|
||||
return path;
|
||||
}
|
||||
|
||||
+ (NSString *) nextScreenshotName {
|
||||
NSFileManager *mgr = [NSFileManager defaultManager];
|
||||
|
||||
NSString *directory = [GrowlPathUtil screenshotsDirectory];
|
||||
NSString *filename = nil;
|
||||
|
||||
NSArray *origContents = [mgr directoryContentsAtPath:directory];
|
||||
NSMutableSet *directoryContents = [[NSMutableSet alloc] initWithCapacity:[origContents count]];
|
||||
|
||||
NSEnumerator *filesEnum = [origContents objectEnumerator];
|
||||
NSString *existingFilename;
|
||||
while ((existingFilename = [filesEnum nextObject])) {
|
||||
existingFilename = [directory stringByAppendingPathComponent:[existingFilename stringByDeletingPathExtension]];
|
||||
[directoryContents addObject:existingFilename];
|
||||
}
|
||||
|
||||
unsigned long i;
|
||||
for (i = 1UL; i < ULONG_MAX; ++i) {
|
||||
[filename release];
|
||||
filename = [[NSString alloc] initWithFormat:@"Screenshot %lu", i];
|
||||
NSString *path = [directory stringByAppendingPathComponent:filename];
|
||||
if (![directoryContents containsObject:path]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
[directoryContents release];
|
||||
|
||||
return [filename autorelease];
|
||||
}
|
||||
|
||||
@end
|
171
toolkit/components/alerts/src/mac/growl/GrowlPathUtilities.h
Normal file
171
toolkit/components/alerts/src/mac/growl/GrowlPathUtilities.h
Normal file
@ -0,0 +1,171 @@
|
||||
//
|
||||
// GrowlPathUtilities.h
|
||||
// Growl
|
||||
//
|
||||
// Created by Ingmar Stein on 17.04.05.
|
||||
// Copyright 2005-2006 The Growl Project. All rights reserved.
|
||||
//
|
||||
// This file is under the BSD License, refer to License.txt for details
|
||||
|
||||
/*we can't typedef the enum, because then NSSearchPathDirectory constants
|
||||
* cannot be used in GrowlSearchPathDirectory arguments/variables without a
|
||||
* compiler warning (because NSSearchPathDirectory and GrowlSearchPathDirectory
|
||||
* would then be different types).
|
||||
*/
|
||||
typedef int GrowlSearchPathDirectory;
|
||||
enum {
|
||||
//Library/Application\ Support/Growl
|
||||
GrowlSupportDirectory = 0x10000,
|
||||
//all other directory constants refer to subdirectories of Growl Support.
|
||||
GrowlScreenshotsDirectory,
|
||||
GrowlTicketsDirectory,
|
||||
GrowlPluginsDirectory,
|
||||
};
|
||||
typedef NSSearchPathDomainMask GrowlSearchPathDomainMask; //consistency
|
||||
|
||||
@interface GrowlPathUtilities : NSObject {
|
||||
|
||||
}
|
||||
|
||||
#pragma mark Bundles
|
||||
|
||||
/*! @method growlPrefPaneBundle
|
||||
* @abstract Returns the Growl preference pane bundle.
|
||||
* @discussion First, attempts to retrieve the bundle for a running
|
||||
* GrowlHelperApp process using <code>runningHelperAppBundle</code>, and if
|
||||
* that was successful, returns the .prefpane bundle that contains it (if any).
|
||||
* Then, if that failed, searches all installed preference panes for the Growl
|
||||
* preference pane.
|
||||
* @result The <code>NSBundle</code> for the Growl preference pane if it is
|
||||
* installed; <code>nil</code> otherwise.
|
||||
*/
|
||||
+ (NSBundle *) growlPrefPaneBundle;
|
||||
/*! @method helperAppBundle
|
||||
* @abstract Returns the GrowlHelperApp bundle.
|
||||
* @discussion First, attempts to retrieve the bundle for a running
|
||||
* GrowlHelperApp process using <code>runningHelperAppBundle</code>, and
|
||||
* returns that if it was successful.
|
||||
* Then, if it wasn't, searches for a Growl preference pane, and, if one is
|
||||
* installed, returns the GrowlHelperApp bundle inside it.
|
||||
* @result The <code>NSBundle</code> for GrowlHelperApp if it is present;
|
||||
* <code>nil</code> otherwise.
|
||||
*/
|
||||
+ (NSBundle *) helperAppBundle;
|
||||
|
||||
/*! @method runningHelperAppBundle
|
||||
* @abstract Returns the bundle for the running GrowlHelperApp process.
|
||||
* @discussion If GrowlHelperApp is running, returns an NSBundle for the .app
|
||||
* bundle it was loaded from.
|
||||
* If GrowlHelperApp is not running, returns <code>nil</code>.
|
||||
* @result The <code>NSBundle</code> for GrowlHelperApp if it is running;
|
||||
* <code>nil</code> otherwise.
|
||||
*/
|
||||
+ (NSBundle *) runningHelperAppBundle;
|
||||
|
||||
#pragma mark Directories
|
||||
|
||||
/*! @method searchPathForDirectory:inDomains:mustBeWritable:
|
||||
* @abstract Returns an array of absolute paths to a given directory.
|
||||
* @discussion This method returns an array of all the directories of a given
|
||||
* type that exist (and, if <code>flag</code> is <code>YES</code>, are
|
||||
* writable). If no directories match this criteria, a valid (but empty)
|
||||
* array is returned.
|
||||
*
|
||||
* Unlike the <code>NSSearchPathForDirectoriesInDomains</code> function in
|
||||
* Foundation, this method does not allow you to specify whether tildes are
|
||||
* expanded: they will always be expanded.
|
||||
* @result An array of zero or more absolute paths.
|
||||
*/
|
||||
+ (NSArray *) searchPathForDirectory:(GrowlSearchPathDirectory) directory inDomains:(GrowlSearchPathDomainMask) domainMask mustBeWritable:(BOOL)flag;
|
||||
/*! @method searchPathForDirectory:inDomains:
|
||||
* @abstract Returns an array of absolute paths to a given directory.
|
||||
* @discussion This method returns an array of all the directories of a given
|
||||
* type that exist. They need not be writable.
|
||||
*
|
||||
* Unlike the <code>NSSearchPathForDirectoriesInDomains</code> function in
|
||||
* Foundation, this method does not allow you to specify whether tildes are
|
||||
* expanded: they will always be expanded.
|
||||
* @result An array of zero or more absolute paths.
|
||||
*/
|
||||
+ (NSArray *) searchPathForDirectory:(GrowlSearchPathDirectory) directory inDomains:(GrowlSearchPathDomainMask) domainMask;
|
||||
|
||||
/*! @method growlSupportDirectory
|
||||
* @abstract Returns the path for Growl's folder inside Application Support.
|
||||
* @discussion This method creates the folder if it does not already exist.
|
||||
* @result The path to Growl's support directory.
|
||||
*/
|
||||
+ (NSString *) growlSupportDirectory;
|
||||
|
||||
/*! @method screenshotsDirectory
|
||||
* @abstract Returns the directory where screenshots are to be stored.
|
||||
* @discussion The default location of this directory is
|
||||
* $HOME/Library/Application\ Support/Growl/Screenshots. This method creates
|
||||
* the folder if it does not already exist.
|
||||
* @result The absolute path to the screenshot directory.
|
||||
*/
|
||||
+ (NSString *) screenshotsDirectory;
|
||||
|
||||
/*! @method ticketsDirectory
|
||||
* @abstract Returns the directory where tickets are to be saved.
|
||||
* @discussion The default location of this directory is
|
||||
* $HOME/Library/Application\ Support/Growl/Tickets. This method creates
|
||||
* the folder if it does not already exist.
|
||||
* @result The absolute path to the ticket directory.
|
||||
*/
|
||||
+ (NSString *) ticketsDirectory;
|
||||
|
||||
#pragma mark Screenshot names
|
||||
|
||||
/*! @method nextScreenshotName
|
||||
* @abstract Returns the name you should use for the next screenshot.
|
||||
* @discussion Names returned by this method are currently in the format
|
||||
* 'Screenshot N', where N starts at 1 and continues indefinitely. Note the
|
||||
* lack of a filename extension: you append it yourself.
|
||||
*
|
||||
* The name returned by this method is guaranteed to not exist with any
|
||||
* filename extension. This is intentional: it would be confusing for the
|
||||
* user if the fifth screenshot were assigned the name 'Screenshot 1' simply
|
||||
* because the previous four screenshots had a different filename extension.
|
||||
*
|
||||
* Calling this method is the same as calling
|
||||
* <code>nextScreenshotNameInDirectory:</code> with a directory of
|
||||
* <code>nil</code>.
|
||||
* @result A valid, non-existing, serial filename for a screenshot.
|
||||
*/
|
||||
+ (NSString *) nextScreenshotName;
|
||||
/*! @method nextScreenshotNameInDirectory:
|
||||
* @abstract Returns the name you should use for the next screenshot in a directory.
|
||||
* @discussion Names returned by this method are currently in the format
|
||||
* 'Screenshot N', where N starts at 1 and continues indefinitely. Note the
|
||||
* lack of a filename extension: you append it yourself. Note also that the
|
||||
* directory is not included as a prefix on the result.
|
||||
*
|
||||
* The name returned by this method is guaranteed to not exist in the given
|
||||
* directory with any filename extension. This is intentional: it would be
|
||||
* confusing for the user if the fifth screenshot were assigned the name
|
||||
* 'Screenshot 1' simply because the previous four screenshots had a
|
||||
* different filename extension.
|
||||
* @result A valid, non-existing, serial filename for a screenshot.
|
||||
*/
|
||||
+ (NSString *) nextScreenshotNameInDirectory:(NSString *) dirName;
|
||||
|
||||
#pragma mark Tickets
|
||||
|
||||
/*! @method defaultSavePathForTicketWithApplicationName:
|
||||
* @abstract Returns an absolute path that can be used for saving a ticket.
|
||||
* @discussion When called with an application name, ".ticket" is appended to
|
||||
* it, and the result is appended to the absolute path to the ticket
|
||||
* directory. When called with <code>nil</code>, the ticket directory itself
|
||||
* is returned.
|
||||
*
|
||||
* For the purpose of this method, 'the ticket directory' refers to the first
|
||||
* writable directory returned by
|
||||
* <code>+searchPathForDirectory:inDomains:</code>. If there is no writable
|
||||
* directory, this method returns <code>nil</code>.
|
||||
* @param The application name for the ticket, or <code>nil</code>.
|
||||
* @result The absolute path to a ticket file, or the ticket directory where a
|
||||
* ticket file can be saved.
|
||||
*/
|
||||
+ (NSString *) defaultSavePathForTicketWithApplicationName:(NSString *) appName;
|
||||
|
||||
@end
|
381
toolkit/components/alerts/src/mac/growl/GrowlPathUtilities.m
Normal file
381
toolkit/components/alerts/src/mac/growl/GrowlPathUtilities.m
Normal file
@ -0,0 +1,381 @@
|
||||
//
|
||||
// GrowlPathUtil.m
|
||||
// Growl
|
||||
//
|
||||
// Created by Ingmar Stein on 17.04.05.
|
||||
// Copyright 2005-2006 The Growl Project. All rights reserved.
|
||||
//
|
||||
// This file is under the BSD License, refer to License.txt for details
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "GrowlPathUtilities.h"
|
||||
#import "GrowlPreferencesController.h"
|
||||
#import "GrowlTicketController.h"
|
||||
#import "GrowlDefinesInternal.h"
|
||||
|
||||
static NSBundle *helperAppBundle;
|
||||
static NSBundle *prefPaneBundle;
|
||||
|
||||
#define NAME_OF_SCREENSHOTS_DIRECTORY @"Screenshots"
|
||||
#define NAME_OF_TICKETS_DIRECTORY @"Tickets"
|
||||
#define NAME_OF_PLUGINS_DIRECTORY @"Plugins"
|
||||
|
||||
@implementation GrowlPathUtilities
|
||||
|
||||
#pragma mark Bundles
|
||||
|
||||
//Searches the process list (as yielded by GetNextProcess) for a process with the given bundle identifier.
|
||||
//Returns the oldest matching process.
|
||||
+ (NSBundle *) bundleForProcessWithBundleIdentifier:(NSString *)identifier
|
||||
{
|
||||
|
||||
restart:;
|
||||
OSStatus err;
|
||||
NSBundle *bundle = nil;
|
||||
struct ProcessSerialNumber psn = { 0, 0 };
|
||||
UInt32 oldestProcessLaunchDate = UINT_MAX;
|
||||
|
||||
while ((err = GetNextProcess(&psn)) == noErr) {
|
||||
struct ProcessInfoRec info = { .processInfoLength = sizeof(struct ProcessInfoRec) };
|
||||
err = GetProcessInformation(&psn, &info);
|
||||
if (err == noErr) {
|
||||
//Compare the launch dates first, since it's cheaper than comparing bundle IDs.
|
||||
if (info.processLaunchDate < oldestProcessLaunchDate) {
|
||||
//This one is older (fewer ticks since startup), so this is our current prospect to be the result.
|
||||
NSDictionary *dict = (NSDictionary *)ProcessInformationCopyDictionary(&psn, kProcessDictionaryIncludeAllInformationMask);
|
||||
|
||||
if (dict) {
|
||||
pid_t pid = 0;
|
||||
GetProcessPID(&psn, &pid);
|
||||
if ([[dict objectForKey:(NSString *)kCFBundleIdentifierKey] isEqualToString:identifier]) {
|
||||
NSString *bundlePath = [dict objectForKey:@"BundlePath"];
|
||||
if (bundlePath) {
|
||||
bundle = [NSBundle bundleWithPath:bundlePath];
|
||||
oldestProcessLaunchDate = info.processLaunchDate;
|
||||
}
|
||||
}
|
||||
|
||||
[dict release];
|
||||
} else {
|
||||
//ProcessInformationCopyDictionary returning NULL probably means that the process disappeared out from under us (i.e., exited) in between GetProcessInformation and ProcessInformationCopyDictionary. Start over.
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (err != noErr) {
|
||||
//Unexpected failure of GetProcessInformation (Process Manager got confused?). Assume severe breakage and bail.
|
||||
NSLog(@"Couldn't get information about process %lu,%lu: GetProcessInformation returned %i/%s", psn.highLongOfPSN, psn.lowLongOfPSN, err, GetMacOSStatusCommentString(err));
|
||||
err = noErr; //So our NSLog for GetNextProcess doesn't complain. (I wish I had Python's while..else block.)
|
||||
break;
|
||||
} else {
|
||||
//Process disappeared out from under us (i.e., exited) in between GetNextProcess and GetProcessInformation. Start over.
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (err != procNotFound) {
|
||||
NSLog(@"%s: GetNextProcess returned %i/%s", __PRETTY_FUNCTION__, err, GetMacOSStatusCommentString(err));
|
||||
}
|
||||
|
||||
return bundle;
|
||||
}
|
||||
|
||||
//Obtains the bundle for the active GrowlHelperApp process. Returns nil if there is no such process.
|
||||
+ (NSBundle *) runningHelperAppBundle {
|
||||
return [self bundleForProcessWithBundleIdentifier:GROWL_HELPERAPP_BUNDLE_IDENTIFIER];
|
||||
}
|
||||
|
||||
+ (NSBundle *) growlPrefPaneBundle {
|
||||
NSArray *librarySearchPaths;
|
||||
NSString *path;
|
||||
NSString *bundleIdentifier;
|
||||
NSEnumerator *searchPathEnumerator;
|
||||
NSBundle *bundle;
|
||||
|
||||
if (prefPaneBundle)
|
||||
return prefPaneBundle;
|
||||
|
||||
prefPaneBundle = [NSBundle bundleWithIdentifier:GROWL_PREFPANE_BUNDLE_IDENTIFIER];
|
||||
if (prefPaneBundle)
|
||||
return prefPaneBundle;
|
||||
|
||||
//If GHA is running, the prefpane bundle is the bundle that contains it.
|
||||
NSBundle *runningHelperAppBundle = [self runningHelperAppBundle];
|
||||
NSString *runningHelperAppBundlePath = [runningHelperAppBundle bundlePath];
|
||||
//GHA in Growl.prefPane/Contents/Resources/
|
||||
NSString *possiblePrefPaneBundlePath1 = [runningHelperAppBundlePath stringByDeletingLastPathComponent];
|
||||
//GHA in Growl.prefPane/ (hypothetical)
|
||||
NSString *possiblePrefPaneBundlePath2 = [[possiblePrefPaneBundlePath1 stringByDeletingLastPathComponent] stringByDeletingLastPathComponent];
|
||||
if ([[[possiblePrefPaneBundlePath1 pathExtension] lowercaseString] isEqualToString:@"prefpane"]) {
|
||||
prefPaneBundle = [NSBundle bundleWithPath:possiblePrefPaneBundlePath1];
|
||||
if (prefPaneBundle)
|
||||
return prefPaneBundle;
|
||||
}
|
||||
if ([[[possiblePrefPaneBundlePath2 pathExtension] lowercaseString] isEqualToString:@"prefpane"]) {
|
||||
prefPaneBundle = [NSBundle bundleWithPath:possiblePrefPaneBundlePath2];
|
||||
if (prefPaneBundle)
|
||||
return prefPaneBundle;
|
||||
}
|
||||
|
||||
static const unsigned bundleIDComparisonFlags = NSCaseInsensitiveSearch | NSBackwardsSearch;
|
||||
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
|
||||
//Find Library directories in all domains except /System (as of Panther, that's ~/Library, /Library, and /Network/Library)
|
||||
librarySearchPaths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSAllDomainsMask & ~NSSystemDomainMask, YES);
|
||||
|
||||
/*First up, we'll look for Growl.prefPane, and if it exists, check whether
|
||||
* it is our prefPane.
|
||||
*This is much faster than having to enumerate all preference panes, and
|
||||
* can drop a significant amount of time off this code.
|
||||
*/
|
||||
searchPathEnumerator = [librarySearchPaths objectEnumerator];
|
||||
while ((path = [searchPathEnumerator nextObject])) {
|
||||
path = [path stringByAppendingPathComponent:PREFERENCE_PANES_SUBFOLDER_OF_LIBRARY];
|
||||
path = [path stringByAppendingPathComponent:GROWL_PREFPANE_NAME];
|
||||
|
||||
if ([fileManager fileExistsAtPath:path]) {
|
||||
bundle = [NSBundle bundleWithPath:path];
|
||||
|
||||
if (bundle) {
|
||||
bundleIdentifier = [bundle bundleIdentifier];
|
||||
|
||||
if (bundleIdentifier && ([bundleIdentifier compare:GROWL_PREFPANE_BUNDLE_IDENTIFIER options:bundleIDComparisonFlags] == NSOrderedSame)) {
|
||||
prefPaneBundle = bundle;
|
||||
return prefPaneBundle;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*Enumerate all installed preference panes, looking for the Growl prefpane
|
||||
* bundle identifier and stopping when we find it.
|
||||
*Note that we check the bundle identifier because we should not insist
|
||||
* that the user not rename his preference pane files, although most users
|
||||
* of course will not. If the user wants to mutilate the Info.plist file
|
||||
* inside the bundle, he/she deserves to not have a working Growl
|
||||
* installation.
|
||||
*/
|
||||
searchPathEnumerator = [librarySearchPaths objectEnumerator];
|
||||
while ((path = [searchPathEnumerator nextObject])) {
|
||||
NSString *bundlePath;
|
||||
NSDirectoryEnumerator *bundleEnum;
|
||||
|
||||
path = [path stringByAppendingPathComponent:PREFERENCE_PANES_SUBFOLDER_OF_LIBRARY];
|
||||
bundleEnum = [fileManager enumeratorAtPath:path];
|
||||
|
||||
while ((bundlePath = [bundleEnum nextObject])) {
|
||||
if ([[bundlePath pathExtension] isEqualToString:PREFERENCE_PANE_EXTENSION]) {
|
||||
bundle = [NSBundle bundleWithPath:[path stringByAppendingPathComponent:bundlePath]];
|
||||
|
||||
if (bundle) {
|
||||
bundleIdentifier = [bundle bundleIdentifier];
|
||||
|
||||
if (bundleIdentifier && ([bundleIdentifier compare:GROWL_PREFPANE_BUNDLE_IDENTIFIER options:bundleIDComparisonFlags] == NSOrderedSame)) {
|
||||
prefPaneBundle = bundle;
|
||||
return prefPaneBundle;
|
||||
}
|
||||
}
|
||||
|
||||
[bundleEnum skipDescendents];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSBundle *) helperAppBundle {
|
||||
if (!helperAppBundle) {
|
||||
helperAppBundle = [self runningHelperAppBundle];
|
||||
if (!helperAppBundle) {
|
||||
//look in the prefpane bundle.
|
||||
NSBundle *bundle = [GrowlPathUtilities growlPrefPaneBundle];
|
||||
NSString *helperAppPath = [bundle pathForResource:@"GrowlHelperApp" ofType:@"app"];
|
||||
helperAppBundle = [NSBundle bundleWithPath:helperAppPath];
|
||||
}
|
||||
}
|
||||
return helperAppBundle;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Directories
|
||||
|
||||
+ (NSArray *) searchPathForDirectory:(GrowlSearchPathDirectory) directory inDomains:(GrowlSearchPathDomainMask) domainMask mustBeWritable:(BOOL)flag {
|
||||
if (directory < GrowlSupportDirectory) {
|
||||
NSArray *searchPath = NSSearchPathForDirectoriesInDomains(directory, domainMask, /*expandTilde*/ YES);
|
||||
if (!flag)
|
||||
return searchPath;
|
||||
else {
|
||||
//flag is not NO: exclude non-writable directories.
|
||||
NSMutableArray *result = [NSMutableArray arrayWithCapacity:[searchPath count]];
|
||||
NSFileManager *mgr = [NSFileManager defaultManager];
|
||||
|
||||
NSEnumerator *searchPathEnum = [searchPath objectEnumerator];
|
||||
NSString *dir;
|
||||
while ((dir = [searchPathEnum nextObject])) {
|
||||
if ([mgr isWritableFileAtPath:dir])
|
||||
[result addObject:dir];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
//determine what to append to each Application Support folder.
|
||||
NSString *subpath = nil;
|
||||
switch (directory) {
|
||||
case GrowlSupportDirectory:
|
||||
//do nothing.
|
||||
break;
|
||||
|
||||
case GrowlScreenshotsDirectory:
|
||||
subpath = NAME_OF_SCREENSHOTS_DIRECTORY;
|
||||
break;
|
||||
|
||||
case GrowlTicketsDirectory:
|
||||
subpath = NAME_OF_TICKETS_DIRECTORY;
|
||||
break;
|
||||
|
||||
case GrowlPluginsDirectory:
|
||||
subpath = NAME_OF_PLUGINS_DIRECTORY;
|
||||
break;
|
||||
|
||||
default:
|
||||
NSLog(@"ERROR: GrowlPathUtil was asked for directory 0x%x, but it doesn't know what directory that is. Please tell the Growl developers.", directory);
|
||||
return nil;
|
||||
}
|
||||
if (subpath)
|
||||
subpath = [@"Application Support/Growl" stringByAppendingPathComponent:subpath];
|
||||
else
|
||||
subpath = @"Application Support/Growl";
|
||||
|
||||
/*get the search path, and append the subpath to all the items therein.
|
||||
*exclude results that don't exist.
|
||||
*/
|
||||
NSFileManager *mgr = [NSFileManager defaultManager];
|
||||
BOOL isDir = NO;
|
||||
|
||||
NSArray *searchPath = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, domainMask, /*expandTilde*/ YES);
|
||||
NSMutableArray *mSearchPath = [NSMutableArray arrayWithCapacity:[searchPath count]];
|
||||
NSEnumerator *searchPathEnum = [searchPath objectEnumerator];
|
||||
NSString *path;
|
||||
while ((path = [searchPathEnum nextObject])) {
|
||||
path = [path stringByAppendingPathComponent:subpath];
|
||||
if ([mgr fileExistsAtPath:path isDirectory:&isDir] && isDir)
|
||||
[mSearchPath addObject:path];
|
||||
}
|
||||
|
||||
return mSearchPath;
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSArray *) searchPathForDirectory:(GrowlSearchPathDirectory) directory inDomains:(GrowlSearchPathDomainMask) domainMask {
|
||||
//NO to emulate the default NSSearchPathForDirectoriesInDomains behaviour.
|
||||
return [self searchPathForDirectory:directory inDomains:domainMask mustBeWritable:NO];
|
||||
}
|
||||
|
||||
+ (NSString *) growlSupportDirectory {
|
||||
NSArray *searchPath = [self searchPathForDirectory:GrowlSupportDirectory inDomains:NSUserDomainMask mustBeWritable:YES];
|
||||
if ([searchPath count])
|
||||
return [searchPath objectAtIndex:0U];
|
||||
else {
|
||||
NSString *path = nil;
|
||||
|
||||
//if this doesn't return any writable directories, path will still be nil.
|
||||
searchPath = [self searchPathForDirectory:NSLibraryDirectory inDomains:NSAllDomainsMask mustBeWritable:YES];
|
||||
if ([searchPath count]) {
|
||||
path = [[searchPath objectAtIndex:0U] stringByAppendingPathComponent:@"Application Support/Growl"];
|
||||
//try to create it. if that doesn't work, don't return it. return nil instead.
|
||||
if (![[NSFileManager defaultManager] createDirectoryAtPath:path attributes:nil])
|
||||
path = nil;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString *) screenshotsDirectory {
|
||||
NSArray *searchPath = [self searchPathForDirectory:GrowlScreenshotsDirectory inDomains:NSAllDomainsMask mustBeWritable:YES];
|
||||
if ([searchPath count])
|
||||
return [searchPath objectAtIndex:0U];
|
||||
else {
|
||||
NSString *path = nil;
|
||||
|
||||
//if this doesn't return any writable directories, path will still be nil.
|
||||
path = [self growlSupportDirectory];
|
||||
if (path) {
|
||||
path = [path stringByAppendingPathComponent:NAME_OF_SCREENSHOTS_DIRECTORY];
|
||||
//try to create it. if that doesn't work, don't return it. return nil instead.
|
||||
if (![[NSFileManager defaultManager] createDirectoryAtPath:path attributes:nil])
|
||||
path = nil;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
+ (NSString *) ticketsDirectory {
|
||||
NSArray *searchPath = [self searchPathForDirectory:GrowlTicketsDirectory inDomains:NSAllDomainsMask mustBeWritable:YES];
|
||||
if ([searchPath count])
|
||||
return [searchPath objectAtIndex:0U];
|
||||
else {
|
||||
NSString *path = nil;
|
||||
|
||||
//if this doesn't return any writable directories, path will still be nil.
|
||||
path = [self growlSupportDirectory];
|
||||
if (path) {
|
||||
path = [path stringByAppendingPathComponent:NAME_OF_TICKETS_DIRECTORY];
|
||||
//try to create it. if that doesn't work, don't return it. return nil instead.
|
||||
if (![[NSFileManager defaultManager] createDirectoryAtPath:path attributes:nil])
|
||||
path = nil;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Screenshot names
|
||||
|
||||
+ (NSString *) nextScreenshotName {
|
||||
return [self nextScreenshotNameInDirectory:nil];
|
||||
}
|
||||
|
||||
+ (NSString *) nextScreenshotNameInDirectory:(NSString *) directory {
|
||||
NSFileManager *mgr = [NSFileManager defaultManager];
|
||||
|
||||
if (!directory)
|
||||
directory = [GrowlPathUtilities screenshotsDirectory];
|
||||
|
||||
//build a set of all the files in the directory, without their filename extensions.
|
||||
NSArray *origContents = [mgr directoryContentsAtPath:directory];
|
||||
NSMutableSet *directoryContents = [[NSMutableSet alloc] initWithCapacity:[origContents count]];
|
||||
|
||||
NSEnumerator *filesEnum = [origContents objectEnumerator];
|
||||
NSString *existingFilename;
|
||||
while ((existingFilename = [filesEnum nextObject]))
|
||||
[directoryContents addObject:[existingFilename stringByDeletingPathExtension]];
|
||||
|
||||
//look for a filename that doesn't exist (with any extension) in the directory.
|
||||
NSString *filename = nil;
|
||||
unsigned long long i;
|
||||
for (i = 1ULL; i < ULLONG_MAX; ++i) {
|
||||
[filename release];
|
||||
filename = [[NSString alloc] initWithFormat:@"Screenshot %llu", i];
|
||||
if (![directoryContents containsObject:filename])
|
||||
break;
|
||||
}
|
||||
[directoryContents release];
|
||||
|
||||
return [filename autorelease];
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Tickets
|
||||
|
||||
+ (NSString *) defaultSavePathForTicketWithApplicationName:(NSString *) appName {
|
||||
return [[self ticketsDirectory] stringByAppendingPathComponent:[appName stringByAppendingPathExtension:GROWL_PATHEXTENSION_TICKET]];
|
||||
}
|
||||
|
||||
@end
|
@ -3,7 +3,7 @@
|
||||
// Growl
|
||||
//
|
||||
// Created by Ingmar Stein on 15.11.04.
|
||||
// Copyright 2004-2005 The Growl Project. All rights reserved.
|
||||
// Copyright 2004-2006 The Growl Project. All rights reserved.
|
||||
//
|
||||
// This file is under the BSD License, refer to License.txt for details
|
||||
|
||||
@ -15,10 +15,9 @@
|
||||
- (bycopy NSString *) growlVersion;
|
||||
@end
|
||||
|
||||
@class GrowlApplicationController;
|
||||
|
||||
@interface GrowlPathway : NSObject <GrowlNotificationProtocol> {
|
||||
}
|
||||
|
||||
- (void) registerApplicationWithDictionary:(NSDictionary *)dict;
|
||||
- (void) postNotificationWithDictionary:(NSDictionary *)dict;
|
||||
|
||||
@end
|
||||
|
@ -0,0 +1,126 @@
|
||||
//
|
||||
// GrowlPreferencesController.h
|
||||
// Growl
|
||||
//
|
||||
// Created by Nelson Elhage on 8/24/04.
|
||||
// Renamed from GrowlPreferences.h by Mac-arena the Bored Zo on 2005-06-27.
|
||||
// Copyright 2004-2006 The Growl Project. All rights reserved.
|
||||
//
|
||||
// This file is under the BSD License, refer to License.txt for details
|
||||
|
||||
#ifndef GROWL_PREFERENCES_CONTROLLER_H
|
||||
#define GROWL_PREFERENCES_CONTROLLER_H
|
||||
|
||||
#ifdef __OBJC__
|
||||
#define XSTR(x) (@x)
|
||||
#else
|
||||
#define XSTR CFSTR
|
||||
#endif
|
||||
|
||||
#define GrowlPreferencesChanged XSTR("GrowlPreferencesChanged")
|
||||
#define GrowlPreview XSTR("GrowlPreview")
|
||||
#define GrowlDisplayPluginKey XSTR("GrowlDisplayPluginName")
|
||||
#define GrowlUserDefaultsKey XSTR("GrowlUserDefaults")
|
||||
#define GrowlStartServerKey XSTR("GrowlStartServer")
|
||||
#define GrowlRemoteRegistrationKey XSTR("GrowlRemoteRegistration")
|
||||
#define GrowlEnableForwardKey XSTR("GrowlEnableForward")
|
||||
#define GrowlForwardDestinationsKey XSTR("GrowlForwardDestinations")
|
||||
#define GrowlUDPPortKey XSTR("GrowlUDPPort")
|
||||
#define GrowlTCPPortKey XSTR("GrowlTCPPort")
|
||||
#define GrowlUpdateCheckKey XSTR("GrowlUpdateCheck")
|
||||
#define LastUpdateCheckKey XSTR("LastUpdateCheck")
|
||||
#define GrowlLoggingEnabledKey XSTR("GrowlLoggingEnabled")
|
||||
#define GrowlLogTypeKey XSTR("GrowlLogType")
|
||||
#define GrowlCustomHistKey1 XSTR("Custom log history 1")
|
||||
#define GrowlCustomHistKey2 XSTR("Custom log history 2")
|
||||
#define GrowlCustomHistKey3 XSTR("Custom log history 3")
|
||||
#define GrowlMenuExtraKey XSTR("GrowlMenuExtra")
|
||||
#define GrowlSquelchModeKey XSTR("GrowlSquelchMode")
|
||||
#define LastKnownVersionKey XSTR("LastKnownVersion")
|
||||
#define GrowlStickyWhenAwayKey XSTR("StickyWhenAway")
|
||||
#define GrowlStickyIdleThresholdKey XSTR("IdleThreshold")
|
||||
|
||||
CFTypeRef GrowlPreferencesController_objectForKey(CFTypeRef key);
|
||||
int GrowlPreferencesController_integerForKey(CFTypeRef key);
|
||||
Boolean GrowlPreferencesController_boolForKey(CFTypeRef key);
|
||||
|
||||
#ifdef __OBJC__
|
||||
|
||||
#import "GrowlAbstractSingletonObject.h"
|
||||
|
||||
@interface GrowlPreferencesController : GrowlAbstractSingletonObject {
|
||||
}
|
||||
|
||||
+ (GrowlPreferencesController *) sharedController;
|
||||
|
||||
- (void) registerDefaults:(NSDictionary *)inDefaults;
|
||||
- (id) objectForKey:(NSString *)key;
|
||||
- (void) setObject:(id)object forKey:(NSString *)key;
|
||||
- (BOOL) boolForKey:(NSString*) key;
|
||||
- (void) setBool:(BOOL)value forKey:(NSString *)key;
|
||||
- (int) integerForKey:(NSString *)key;
|
||||
- (void) setInteger:(int)value forKey:(NSString *)key;
|
||||
- (void) synchronize;
|
||||
|
||||
- (BOOL) shouldStartGrowlAtLogin;
|
||||
- (void) setShouldStartGrowlAtLogin:(BOOL)flag;
|
||||
- (void) setStartAtLogin:(NSString *)path enabled:(BOOL)flag;
|
||||
|
||||
- (BOOL) isRunning:(NSString *)theBundleIdentifier;
|
||||
- (BOOL) isGrowlRunning;
|
||||
- (void) setGrowlRunning:(BOOL)flag noMatterWhat:(BOOL)nmw;
|
||||
- (void) launchGrowl:(BOOL)noMatterWhat;
|
||||
- (void) terminateGrowl;
|
||||
|
||||
#pragma mark GrowlMenu running state
|
||||
|
||||
- (void) enableGrowlMenu;
|
||||
- (void) disableGrowlMenu;
|
||||
|
||||
#pragma mark -
|
||||
//Simplified accessors
|
||||
|
||||
#pragma mark UI
|
||||
|
||||
- (BOOL) isBackgroundUpdateCheckEnabled;
|
||||
- (void) setIsBackgroundUpdateCheckEnabled:(BOOL)flag;
|
||||
|
||||
- (NSString *) defaultDisplayPluginName;
|
||||
- (void) setDefaultDisplayPluginName:(NSString *)name;
|
||||
|
||||
- (BOOL) squelchMode;
|
||||
- (void) setSquelchMode:(BOOL)flag;
|
||||
|
||||
- (BOOL) stickyWhenAway;
|
||||
- (void) setStickyWhenAway:(BOOL)flag;
|
||||
|
||||
- (NSNumber*) idleThreshold;
|
||||
- (void) setIdleThreshold:(NSNumber*)value;
|
||||
|
||||
#pragma mark GrowlMenu methods
|
||||
|
||||
- (BOOL) isGrowlMenuEnabled;
|
||||
- (void) setGrowlMenuEnabled:(BOOL)state;
|
||||
|
||||
#pragma mark "Network" tab pane
|
||||
|
||||
- (BOOL) isGrowlServerEnabled;
|
||||
- (void) setGrowlServerEnabled:(BOOL)enabled;
|
||||
|
||||
- (BOOL) isRemoteRegistrationAllowed;
|
||||
- (void) setRemoteRegistrationAllowed:(BOOL)flag;
|
||||
|
||||
- (BOOL) isForwardingEnabled;
|
||||
- (void) setForwardingEnabled:(BOOL)enabled;
|
||||
|
||||
- (NSString *) remotePassword;
|
||||
- (void) setRemotePassword:(NSString *)value;
|
||||
|
||||
- (int) UDPPort;
|
||||
- (void) setUDPPort:(int)value;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
@ -0,0 +1,29 @@
|
||||
//
|
||||
// GrowlTicketController.h
|
||||
// Growl
|
||||
//
|
||||
// Created by Mac-arena the Bored Zo on 2005-06-08.
|
||||
// Copyright 2005-2006 Mac-arena the Bored Zo. All rights reserved.
|
||||
//
|
||||
|
||||
#import "GrowlAbstractSingletonObject.h"
|
||||
|
||||
#define GROWL_PATHEXTENSION_TICKET @"growlTicket"
|
||||
|
||||
@class GrowlApplicationTicket;
|
||||
|
||||
@interface GrowlTicketController: GrowlAbstractSingletonObject
|
||||
{
|
||||
NSMutableDictionary *ticketsByApplicationName;
|
||||
}
|
||||
|
||||
+ (id) sharedController;
|
||||
|
||||
- (NSDictionary *) allSavedTickets;
|
||||
|
||||
- (GrowlApplicationTicket *) ticketForApplicationName:(NSString *) appName;
|
||||
- (void) addTicket:(GrowlApplicationTicket *) newTicket;
|
||||
- (void) removeTicketForApplicationName:(NSString *)appName;
|
||||
|
||||
- (void) loadAllSavedTickets;
|
||||
@end
|
@ -48,12 +48,13 @@ FORCE_USE_PIC = 1
|
||||
|
||||
CMSRCS = \
|
||||
GrowlApplicationBridge.m \
|
||||
GrowlPathUtil.m \
|
||||
NSURLAdditions.m \
|
||||
GrowlPathUtilities.m \
|
||||
$(NULL)
|
||||
|
||||
CSRCS = \
|
||||
CFGrowlAdditions.c \
|
||||
CFMutableDictionaryAdditions.c \
|
||||
CFURLAdditions.c \
|
||||
$(NULL)
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
@ -1,23 +0,0 @@
|
||||
//
|
||||
// NSURLAdditions.h
|
||||
// Growl
|
||||
//
|
||||
// Created by Karl Adam on Fri May 28 2004.
|
||||
// Copyright 2004-2005 The Growl Project. All rights reserved.
|
||||
//
|
||||
// This file is under the BSD License, refer to License.txt for details
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface NSURL (GrowlAdditions)
|
||||
|
||||
//'alias' as in the Alias Manager.
|
||||
+ (NSURL *) fileURLWithAliasData:(NSData *)aliasData;
|
||||
- (NSData *) aliasData;
|
||||
|
||||
//these are the type of external representations used by Dock.app.
|
||||
+ (NSURL *) fileURLWithDockDescription:(NSDictionary *)dict;
|
||||
//-dockDescription returns nil for non-file: URLs.
|
||||
- (NSDictionary *) dockDescription;
|
||||
|
||||
@end
|
@ -1,132 +0,0 @@
|
||||
//
|
||||
// NSURLAdditions.m
|
||||
// Growl
|
||||
//
|
||||
// Created by Karl Adam on Fri May 28 2004.
|
||||
// Copyright 2004-2005 The Growl Project. All rights reserved.
|
||||
//
|
||||
// This file is under the BSD License, refer to License.txt for details
|
||||
|
||||
#import "NSURLAdditions.h"
|
||||
|
||||
#define _CFURLAliasDataKey @"_CFURLAliasData"
|
||||
#define _CFURLStringKey @"_CFURLString"
|
||||
#define _CFURLStringTypeKey @"_CFURLStringType"
|
||||
|
||||
@implementation NSURL (GrowlAdditions)
|
||||
|
||||
//'alias' as in the Alias Manager.
|
||||
+ (NSURL *) fileURLWithAliasData:(NSData *)aliasData {
|
||||
NSParameterAssert(aliasData != nil);
|
||||
|
||||
NSURL *url = nil;
|
||||
|
||||
AliasHandle alias = NULL;
|
||||
OSStatus err = PtrToHand([aliasData bytes], (Handle *)&alias, [aliasData length]);
|
||||
if (err != noErr) {
|
||||
NSLog(@"in +[NSURL(GrowlAdditions) fileURLWithAliasData:]: Could not allocate an alias handle from %u bytes of alias data (data follows) because PtrToHand returned %li\n%@", [aliasData length], aliasData, (long)err);
|
||||
} else {
|
||||
NSString *path = nil;
|
||||
/*
|
||||
* FSResolveAlias mounts disk images or network shares to resolve
|
||||
* aliases, thus we resort to FSCopyAliasInfo.
|
||||
*/
|
||||
err = FSCopyAliasInfo(alias,
|
||||
/* targetName */ NULL,
|
||||
/* volumeName */ NULL,
|
||||
(CFStringRef *)&path,
|
||||
/* whichInfo */ NULL,
|
||||
/* info */ NULL);
|
||||
if (err != noErr) {
|
||||
if (err != fnfErr) { //ignore file-not-found; it's harmless
|
||||
NSLog(@"in +[NSURL(GrowlAdditions) fileURLWithAliasData:]: Could not resolve alias (alias data follows) because FSResolveAlias returned %li - will try path\n%@", (long)err, aliasData);
|
||||
}
|
||||
} else if (path) {
|
||||
url = [NSURL fileURLWithPath:path];
|
||||
} else {
|
||||
NSLog(@"in +[NSURL(GrowlAdditions) fileURLWithAliasData:]: FSCopyAliasInfo returned a nil path");
|
||||
}
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
- (NSData *) aliasData {
|
||||
//return nil for non-file: URLs.
|
||||
if ([[self scheme] caseInsensitiveCompare:@"file"] != NSOrderedSame)
|
||||
return nil;
|
||||
|
||||
NSData *aliasData = nil;
|
||||
|
||||
FSRef fsref;
|
||||
if (CFURLGetFSRef((CFURLRef)self, &fsref)) {
|
||||
AliasHandle alias = NULL;
|
||||
OSStatus err = FSNewAlias(/*fromFile*/ NULL, &fsref, &alias);
|
||||
if (err != noErr) {
|
||||
NSLog(@"in -[NSURL(GrowlAdditions) dockDescription]: FSNewAlias for %@ returned %li", self, (long)err);
|
||||
} else {
|
||||
HLock((Handle)alias);
|
||||
|
||||
aliasData = [NSData dataWithBytes:*alias length:GetHandleSize((Handle)alias)];
|
||||
|
||||
HUnlock((Handle)alias);
|
||||
DisposeHandle((Handle)alias);
|
||||
}
|
||||
}
|
||||
|
||||
return aliasData;
|
||||
}
|
||||
|
||||
//these are the type of external representations used by Dock.app.
|
||||
+ (NSURL *) fileURLWithDockDescription:(NSDictionary *)dict {
|
||||
NSURL *URL = nil;
|
||||
|
||||
NSString *path = [dict objectForKey:_CFURLStringKey];
|
||||
NSData *aliasData = [dict objectForKey:_CFURLAliasDataKey];
|
||||
|
||||
if (aliasData)
|
||||
URL = [self fileURLWithAliasData:aliasData];
|
||||
|
||||
if (!URL) {
|
||||
if (path) {
|
||||
NSNumber *pathStyleNum = [dict objectForKey:_CFURLStringTypeKey];
|
||||
CFURLPathStyle pathStyle = pathStyleNum ? [pathStyleNum intValue] : kCFURLPOSIXPathStyle;
|
||||
|
||||
BOOL isDir = YES;
|
||||
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDir];
|
||||
|
||||
if (exists) {
|
||||
URL = [(NSURL *)CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)path, pathStyle, /*isDirectory*/ isDir) autorelease];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return URL;
|
||||
}
|
||||
|
||||
- (NSDictionary *) dockDescription {
|
||||
NSMutableDictionary *dict;
|
||||
NSString *path = [self path];
|
||||
NSData *aliasData = [self aliasData];
|
||||
|
||||
if (path || aliasData) {
|
||||
dict = [NSMutableDictionary dictionaryWithCapacity:3U];
|
||||
|
||||
if (path) {
|
||||
NSNumber *type = [[NSNumber alloc] initWithInt:kCFURLPOSIXPathStyle];
|
||||
[dict setObject:path forKey:_CFURLStringKey];
|
||||
[dict setObject:type forKey:_CFURLStringTypeKey];
|
||||
[type release];
|
||||
}
|
||||
|
||||
if (aliasData) {
|
||||
[dict setObject:aliasData forKey:_CFURLAliasDataKey];
|
||||
}
|
||||
} else {
|
||||
dict = nil;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in New Issue
Block a user