Burninating LoginItemsAE. We use LSSharedFileList now.
authorPeter Hosey <hg@boredzo.org>
Sun Aug 02 13:03:53 2009 -0700 (2009-08-02)
changeset 42705c1d8f5e64c2
parent 4269 6a87f5b6cc6b
child 4273 b846a7b597a6
Burninating LoginItemsAE. We use LSSharedFileList now.

Also, first use of fast enumeration in Growl code! Woo!
Common/Source/LoginItemsAE.c
Common/Source/LoginItemsAE.h
Core/Source/GrowlPreferencesController.h
Core/Source/GrowlPreferencesController.m
Growl.xcodeproj/project.pbxproj
     1.1 --- a/Common/Source/LoginItemsAE.c	Sun Aug 02 00:52:57 2009 -0700
     1.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.3 @@ -1,805 +0,0 @@
     1.4 - /*
     1.5 -	File:		LoginItemsAE.c
     1.6 -
     1.7 -	Contains:	Login items manipulation via Apple events.
     1.8 -
     1.9 -	Copyright:	Copyright (c) 2005 by Apple Computer, Inc., All Rights Reserved.
    1.10 -
    1.11 -	Disclaimer:	IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
    1.12 -				("Apple") in consideration of your agreement to the following terms, and your
    1.13 -				use, installation, modification or redistribution of this Apple software
    1.14 -				constitutes acceptance of these terms.  If you do not agree with these terms,
    1.15 -				please do not use, install, modify or redistribute this Apple software.
    1.16 -
    1.17 -				In consideration of your agreement to abide by the following terms, and subject
    1.18 -				to these terms, Apple grants you a personal, non-exclusive license, under Apple’s
    1.19 -				copyrights in this original Apple software (the "Apple Software"), to use,
    1.20 -				reproduce, modify and redistribute the Apple Software, with or without
    1.21 -				modifications, in source and/or binary forms; provided that if you redistribute
    1.22 -				the Apple Software in its entirety and without modifications, you must retain
    1.23 -				this notice and the following text and disclaimers in all such redistributions of
    1.24 -				the Apple Software.  Neither the name, trademarks, service marks or logos of
    1.25 -				Apple Computer, Inc. may be used to endorse or promote products derived from the
    1.26 -				Apple Software without specific prior written permission from Apple.  Except as
    1.27 -				expressly stated in this notice, no other rights or licenses, express or implied,
    1.28 -				are granted by Apple herein, including but not limited to any patent rights that
    1.29 -				may be infringed by your derivative works or by other works in which the Apple
    1.30 -				Software may be incorporated.
    1.31 -
    1.32 -				The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
    1.33 -				WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
    1.34 -				WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    1.35 -				PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
    1.36 -				COMBINATION WITH YOUR PRODUCTS.
    1.37 -
    1.38 -				IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
    1.39 -				CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
    1.40 -				GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    1.41 -				ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
    1.42 -				OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
    1.43 -				(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
    1.44 -				ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.45 -
    1.46 -	Change History (most recent first):
    1.47 -
    1.48 -$Log: LoginItemsAE.c,v $
    1.49 -Revision 1.1  2005/09/27 12:29:26
    1.50 -First checked in.
    1.51 -
    1.52 -
    1.53 -*/
    1.54 -
    1.55 -/////////////////////////////////////////////////////////////////
    1.56 -
    1.57 -// Our prototypes
    1.58 -
    1.59 -#include "LoginItemsAE.h"
    1.60 -
    1.61 -// System interfaces
    1.62 -
    1.63 -// We need to pull in all of Carbon just to get the definition of
    1.64 -// pProperties.  *sigh*  This is purely a compile-time dependency,
    1.65 -// which is why we include it in the implementation and not the
    1.66 -// header.
    1.67 -
    1.68 -#include <Carbon/Carbon.h>
    1.69 -
    1.70 -#include <string.h>
    1.71 -
    1.72 -extern OSStatus LSOpenApplication() __attribute__((weak_import));
    1.73 -
    1.74 -/////////////////////////////////////////////////////////////////
    1.75 -#pragma mark ***** Apple event utilities
    1.76 -
    1.77 -enum {
    1.78 -	kSystemEventsCreator = 'sevs'
    1.79 -};
    1.80 -
    1.81 -static OSStatus LaunchSystemEvents(ProcessSerialNumber *psnPtr)
    1.82 -	// Launches the "System Events" process.
    1.83 -{
    1.84 -	OSStatus 			err;
    1.85 -	FSRef				appRef;
    1.86 -
    1.87 -	assert(psnPtr != NULL);
    1.88 -
    1.89 -	// Ask Launch Services to find System Events by creator.
    1.90 -
    1.91 -	err = LSFindApplicationForInfo(
    1.92 -		kSystemEventsCreator,
    1.93 -		NULL,
    1.94 -		NULL,
    1.95 -		&appRef,
    1.96 -		NULL
    1.97 -	);
    1.98 -
    1.99 -    // Launch it!
   1.100 -
   1.101 -    if (err == noErr) {
   1.102 -        if (LSOpenApplication) {
   1.103 -            LSApplicationParameters     appParams;
   1.104 -
   1.105 -            // Do it the easy way on 10.4 and later.
   1.106 -
   1.107 -            memset(&appParams, 0, sizeof(appParams));
   1.108 -            appParams.version = 0;
   1.109 -            appParams.flags = kLSLaunchDefaults;
   1.110 -            appParams.application = &appRef;
   1.111 -
   1.112 -            err = LSOpenApplication(&appParams, psnPtr);
   1.113 -        } else {
   1.114 -            FSSpec				appSpec;
   1.115 -            LaunchParamBlockRec lpb;
   1.116 -
   1.117 -            // Do it the compatible way on earlier systems.
   1.118 -
   1.119 -            // I launch System Events using LaunchApplication, rather than
   1.120 -            // Launch Services, because LaunchApplication gives me back
   1.121 -            // the ProcessSerialNumber.  Unfortunately this requires me to
   1.122 -            // get an FSSpec for the application because there's no
   1.123 -            // FSRef version of Launch Application.
   1.124 -
   1.125 -#ifdef __LP64__
   1.126 -#pragma unused(appSpec)
   1.127 -#else
   1.128 -            if (err == noErr)
   1.129 -                err = FSGetCatalogInfo(&appRef, kFSCatInfoNone, NULL, NULL, &appSpec, NULL);
   1.130 -#endif
   1.131 -            if (err == noErr) {
   1.132 -                memset(&lpb, 0, sizeof(lpb));
   1.133 -                lpb.launchBlockID      = extendedBlock;
   1.134 -                lpb.launchEPBLength    = extendedBlockLen;
   1.135 -                lpb.launchControlFlags = launchContinue | launchNoFileFlags;
   1.136 -#ifdef __LP64__
   1.137 -				lpb.launchAppRef       = &appRef;
   1.138 -#else
   1.139 -                lpb.launchAppSpec      = &appSpec;
   1.140 -#endif
   1.141 -
   1.142 -                err = LaunchApplication(&lpb);
   1.143 -            }
   1.144 -            if (err == noErr)
   1.145 -                *psnPtr = lpb.launchProcessSN;
   1.146 -        }
   1.147 -    }
   1.148 -
   1.149 -	return err;
   1.150 -}
   1.151 -
   1.152 -static OSStatus FindSystemEvents(ProcessSerialNumber *psnPtr)
   1.153 -	// Finds the "System Events" process or, if it's not
   1.154 -	// running, launches it.
   1.155 -{
   1.156 -	OSStatus		err;
   1.157 -	Boolean			found;
   1.158 -	ProcessInfoRec	info;
   1.159 -
   1.160 -	assert(psnPtr != NULL);
   1.161 -
   1.162 -	found = false;
   1.163 -	psnPtr->lowLongOfPSN	= kNoProcess;
   1.164 -	psnPtr->highLongOfPSN	= kNoProcess;
   1.165 -
   1.166 -	do {
   1.167 -		err = GetNextProcess(psnPtr);
   1.168 -		if (err == noErr) {
   1.169 -			memset(&info, 0, sizeof(info));
   1.170 -			err = GetProcessInformation(psnPtr, &info);
   1.171 -		}
   1.172 -		if (err == noErr)
   1.173 -			found = (info.processSignature == kSystemEventsCreator);
   1.174 -	} while ((err == noErr) && !found);
   1.175 -
   1.176 -	if (err == procNotFound)
   1.177 -		err = LaunchSystemEvents(psnPtr);
   1.178 -	return err;
   1.179 -}
   1.180 -
   1.181 -#if ! defined(LOGIN_ITEMS_AE_PRINT_DESC)
   1.182 -    #if defined(NDEBUG)
   1.183 -        #define LOGIN_ITEMS_AE_PRINT_DESC 0
   1.184 -    #else
   1.185 -        #define LOGIN_ITEMS_AE_PRINT_DESC 0         // change this to 1 to get output in debug build
   1.186 -    #endif
   1.187 -#endif
   1.188 -
   1.189 -static OSStatus SendAppleEvent(const AEDesc *event, AEDesc *reply)
   1.190 -	// This is the bottleneck routine we use for sending Apple events.
   1.191 -	// It has a number of neato features.
   1.192 -	//
   1.193 -	// o It use the "AEMach.h" routine AESendMessage because that allows
   1.194 -	//   us to do an RPC without having to field UI events while waiting
   1.195 -	//   for the reply.  Yay for Mac OS X!
   1.196 -	//
   1.197 -	// o It automatically extracts the error from the reply.
   1.198 -	//
   1.199 -	// o It allows you to enable printing of events and their replies
   1.200 -	//   for debugging purposes.
   1.201 -{
   1.202 -	static const long kAETimeoutTicks = 5 * 60;
   1.203 -	OSStatus 	err;
   1.204 -	OSErr		replyErr;
   1.205 -	DescType	junkType;
   1.206 -	Size		junkSize;
   1.207 -
   1.208 -	// Normally I don't declare function prototypes in local scope,
   1.209 -	// but I made this exception because I don't want anyone except
   1.210 -	// for this routine calling GDBPrintAEDesc.  This routine takes
   1.211 -	// care to only link with the routine when debugging is enabled;
   1.212 -	// everyone else might not be so careful.
   1.213 -
   1.214 -	#if LOGIN_ITEMS_AE_PRINT_DESC
   1.215 -
   1.216 -		extern void GDBPrintAEDesc(const AEDesc *desc);
   1.217 -			// This is private system function used to print a
   1.218 -			// textual representation of an AEDesc to stderr.
   1.219 -			// It's very handy when debugging, and is meant only
   1.220 -			// for that purpose.  It's only available to Mach-O
   1.221 -			// clients.  We use it when debugging *only*.
   1.222 -
   1.223 -	#endif
   1.224 -
   1.225 -	assert(event != NULL);
   1.226 -	assert(reply != NULL);
   1.227 -
   1.228 -	#if LOGIN_ITEMS_AE_PRINT_DESC
   1.229 -		GDBPrintAEDesc(event);
   1.230 -	#endif
   1.231 -
   1.232 -	err = AESendMessage(event, reply, kAEWaitReply, kAETimeoutTicks);
   1.233 -
   1.234 -	#if LOGIN_ITEMS_AE_PRINT_DESC
   1.235 -		GDBPrintAEDesc(reply);
   1.236 -	#endif
   1.237 -
   1.238 -	// Extract any error from the Apple event handler via the
   1.239 -	// keyErrorNumber parameter of the reply.
   1.240 -
   1.241 -	if ((err == noErr) && (reply->descriptorType != typeNull)) {
   1.242 -		err = AEGetParamPtr(
   1.243 -			reply,
   1.244 -			keyErrorNumber,
   1.245 -			typeSInt16,
   1.246 -			&junkType,
   1.247 -			&replyErr,
   1.248 -			sizeof(replyErr),
   1.249 -			&junkSize
   1.250 -		);
   1.251 -
   1.252 -		if (err == errAEDescNotFound )
   1.253 -			err = noErr;
   1.254 -		else
   1.255 -			err = replyErr;
   1.256 -	}
   1.257 -
   1.258 -	return err;
   1.259 -}
   1.260 -
   1.261 -/////////////////////////////////////////////////////////////////
   1.262 -#pragma mark ***** Constants from Login Items AppleScript Dictionary
   1.263 -
   1.264 -enum {
   1.265 -	cLoginItem = 'logi',
   1.266 -
   1.267 -	propPath   = 'ppth',
   1.268 -	propHidden = 'hidn'
   1.269 -};
   1.270 -
   1.271 -/////////////////////////////////////////////////////////////////
   1.272 -#pragma mark ***** Public routines (and helpers)
   1.273 -
   1.274 -static const AEDesc kAENull = { typeNull, NULL };
   1.275 -
   1.276 -static void AEDisposeDescQ(AEDesc *descPtr)
   1.277 -{
   1.278 -    OSStatus    junk;
   1.279 -
   1.280 -    junk = AEDisposeDesc(descPtr);
   1.281 -    assert(junk == noErr);
   1.282 -    *descPtr = kAENull;
   1.283 -}
   1.284 -
   1.285 -static OSStatus CreateCFArrayFromAEDescList(
   1.286 -	const AEDescList *	descList,
   1.287 -	CFArrayRef *		itemsPtr
   1.288 -)
   1.289 -	// This routine's input is an AEDescList that contains replies
   1.290 -	// from the "properties of every login item" event.  Each element
   1.291 -	// of the list is an AERecord with two important properties,
   1.292 -	// "path" and "hidden".  This routine creates a CFArray that
   1.293 -	// corresponds to this list.  Each element of the CFArray
   1.294 -	// contains two properties, kLIAEURL and
   1.295 -	// kLIAEHidden, that are derived from the corresponding
   1.296 -	// AERecord properties.
   1.297 -	//
   1.298 -	// On entry, descList must not be NULL
   1.299 -	// On entry,  itemsPtr must not be NULL
   1.300 -	// On entry, *itemsPtr must be NULL
   1.301 -	// On success, *itemsPtr will be a valid CFArray
   1.302 -	// On error, *itemsPtr will be NULL
   1.303 -{
   1.304 -	OSStatus			err;
   1.305 -	CFMutableArrayRef	result;
   1.306 -	long				itemCount;
   1.307 -	long				itemIndex;
   1.308 -	AEKeyword			junkKeyword;
   1.309 -	DescType			junkType;
   1.310 -	Size				junkSize;
   1.311 -
   1.312 -	assert( itemsPtr != NULL);
   1.313 -	assert(*itemsPtr == NULL);
   1.314 -
   1.315 -	result = NULL;
   1.316 -
   1.317 -	// Create a place for the result.
   1.318 -
   1.319 -    err = noErr;
   1.320 -    result = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
   1.321 -    if (!result)
   1.322 -        err = coreFoundationUnknownErr;
   1.323 -
   1.324 -	// For each element in the descriptor list...
   1.325 -
   1.326 -	if (err == noErr)
   1.327 -		err = AECountItems(descList, &itemCount);
   1.328 -	if (err == noErr) {
   1.329 -		for (itemIndex = 1; itemIndex <= itemCount; itemIndex++) {
   1.330 -			AERecord 			thisItem;
   1.331 -			UInt8 				thisPath[1024];
   1.332 -			Size				thisPathSize;
   1.333 -			FSRef				thisItemRef;
   1.334 -			CFURLRef			thisItemURL;
   1.335 -			Boolean				thisItemHidden;
   1.336 -			CFDictionaryRef		thisItemDict;
   1.337 -
   1.338 -			thisItem = kAENull;
   1.339 -			thisItemURL = NULL;
   1.340 -			thisItemDict = NULL;
   1.341 -
   1.342 -			// Get this element's AERecord.
   1.343 -
   1.344 -			err = AEGetNthDesc(descList, itemIndex, typeAERecord, &junkKeyword, &thisItem);
   1.345 -
   1.346 -			// Extract the path and create a CFURL.
   1.347 -
   1.348 -			if (err == noErr)
   1.349 -				err = AEGetKeyPtr(
   1.350 -					&thisItem,
   1.351 -					propPath,
   1.352 -					typeUTF8Text,
   1.353 -					&junkType,
   1.354 -					thisPath,
   1.355 -					sizeof(thisPath) - 1, 		// to ensure that we can always add null terminator
   1.356 -					&thisPathSize
   1.357 -				);
   1.358 -			if (err == noErr) {
   1.359 -				thisPath[thisPathSize] = 0;
   1.360 -
   1.361 -				err = FSPathMakeRef(thisPath, &thisItemRef, NULL);
   1.362 -
   1.363 -				if (err == noErr) {
   1.364 -					thisItemURL = CFURLCreateFromFSRef(kCFAllocatorDefault, &thisItemRef);
   1.365 -				} else {
   1.366 -					err = noErr;			// swallow error and create an imprecise URL
   1.367 -
   1.368 -					thisItemURL = CFURLCreateFromFileSystemRepresentation(
   1.369 -						NULL,
   1.370 -						thisPath,
   1.371 -						thisPathSize,
   1.372 -						false
   1.373 -					);
   1.374 -				}
   1.375 -				if (!thisItemURL)
   1.376 -					err = coreFoundationUnknownErr;
   1.377 -			}
   1.378 -
   1.379 -			// Extract the hidden flag.
   1.380 -
   1.381 -			if (err == noErr) {
   1.382 -				err = AEGetKeyPtr(
   1.383 -					&thisItem,
   1.384 -					propHidden,
   1.385 -					typeBoolean,
   1.386 -					&junkType,
   1.387 -					&thisItemHidden,
   1.388 -					sizeof(thisItemHidden),
   1.389 -					&junkSize
   1.390 -				);
   1.391 -
   1.392 -                // Work around <rdar://problem/4052117> by assuming that hidden
   1.393 -                // is false if we can't get its value.
   1.394 -
   1.395 -                if (err != noErr) {
   1.396 -                    thisItemHidden = false;
   1.397 -                    err = noErr;
   1.398 -                }
   1.399 -			}
   1.400 -
   1.401 -			// Create the CFDictionary for this item.
   1.402 -
   1.403 -			if (err == noErr) {
   1.404 -				CFStringRef keys[2];
   1.405 -				CFTypeRef	values[2];
   1.406 -
   1.407 -				keys[0] = kLIAEURL;
   1.408 -				keys[1] = kLIAEHidden;
   1.409 -
   1.410 -				values[0] = thisItemURL;
   1.411 -				values[1] = (thisItemHidden ? kCFBooleanTrue : kCFBooleanFalse);
   1.412 -
   1.413 -				thisItemDict = CFDictionaryCreate(
   1.414 -					kCFAllocatorDefault,
   1.415 -					(const void **) keys,
   1.416 -					values,
   1.417 -					2,
   1.418 -					&kCFTypeDictionaryKeyCallBacks,
   1.419 -					&kCFTypeDictionaryValueCallBacks
   1.420 -				);
   1.421 -				if (!thisItemDict)
   1.422 -					err = coreFoundationUnknownErr;
   1.423 -			}
   1.424 -
   1.425 -			// Add it to the results array.
   1.426 -
   1.427 -			if (err == noErr)
   1.428 -				CFArrayAppendValue(result, thisItemDict);
   1.429 -
   1.430 -			AEDisposeDescQ(&thisItem);
   1.431 -			if (thisItemURL)
   1.432 -				CFRelease(thisItemURL);
   1.433 -			if (thisItemDict)
   1.434 -				CFRelease(thisItemDict);
   1.435 -
   1.436 -			if (err != noErr)
   1.437 -				break;
   1.438 -		}
   1.439 -	}
   1.440 -
   1.441 -	// Clean up.
   1.442 -
   1.443 -	if (err != noErr && result) {
   1.444 -		CFRelease(result);
   1.445 -		result = NULL;
   1.446 -	}
   1.447 -	*itemsPtr = result;
   1.448 -	assert((err == noErr) == (*itemsPtr != NULL));
   1.449 -
   1.450 -	return err;
   1.451 -}
   1.452 -
   1.453 -static OSStatus SendEventToSystemEventsWithParameters(
   1.454 -	AEEventClass 	theClass,
   1.455 -	AEEventID    	theEvent,
   1.456 -	AppleEvent *	reply,
   1.457 -	...
   1.458 -)
   1.459 -	// Creates an Apple event and sends it to the System Events
   1.460 -	// process.  theClass and theEvent are the event class and ID,
   1.461 -	// respectively.  If reply is not NULL, the caller gets a copy
   1.462 -	// of the reply.  Following reply is a variable number of Apple event
   1.463 -	// parameters.  Each AE parameter is made up of two C parameters,
   1.464 -	// the first being the AEKeyword, the second being a pointer to
   1.465 -	// the AEDesc for that parameter.  This list is terminated by an
   1.466 -	// AEKeyword of value 0.
   1.467 -	//
   1.468 -	// You typically call this as:
   1.469 -	//
   1.470 -	// err = SendEventToSystemEventsWithParameters(
   1.471 -	//     kClass,
   1.472 -	//     kEvent,
   1.473 -	//     NULL,
   1.474 -	//     param1_keyword, param1_desc_ptr,
   1.475 -	//     param2_keyword, param2_desc_ptr,
   1.476 -	//     0
   1.477 -	// );
   1.478 -	//
   1.479 -	// On entry, reply must be NULL or *reply must be the null AEDesc.
   1.480 -	// On success, if reply is not NULL, *reply will be the AE reply
   1.481 -	// (that is, not a null desc).
   1.482 -	// On error, if reply is not NULL, *reply will be the null AEDesc.
   1.483 -{
   1.484 -	OSStatus			err;
   1.485 -	ProcessSerialNumber	psn;
   1.486 -	AppleEvent			target;
   1.487 -	AppleEvent			event;
   1.488 -	AppleEvent			localReply;
   1.489 -	AEDescList			results;
   1.490 -
   1.491 -	assert((reply == NULL) || (reply->descriptorType == typeNull));
   1.492 -
   1.493 -	target = kAENull;
   1.494 -	event = kAENull;
   1.495 -	localReply = kAENull;
   1.496 -    results = kAENull;
   1.497 -
   1.498 -	// Create Apple event.
   1.499 -
   1.500 -	err = FindSystemEvents(&psn);
   1.501 -	if (err == noErr)
   1.502 -		err = AECreateDesc(typeProcessSerialNumber, &psn, sizeof(psn), &target);
   1.503 -	if (err == noErr)
   1.504 -		err = AECreateAppleEvent(
   1.505 -			theClass,
   1.506 -			theEvent,
   1.507 -			&target,
   1.508 -			kAutoGenerateReturnID,
   1.509 -			kAnyTransactionID,
   1.510 -			&event
   1.511 -		);
   1.512 -
   1.513 -	// Handle varargs parameters.
   1.514 -
   1.515 -	if (err == noErr) {
   1.516 -		va_list 		ap;
   1.517 -		AEKeyword		thisKeyword;
   1.518 -		const AEDesc *	thisDesc;
   1.519 -
   1.520 -		va_start(ap, reply);
   1.521 -
   1.522 -		do {
   1.523 -			thisKeyword = va_arg(ap, AEKeyword);
   1.524 -			if (thisKeyword != 0) {
   1.525 -				thisDesc = va_arg(ap, const AEDesc *);
   1.526 -				assert(thisDesc != NULL);
   1.527 -
   1.528 -				err = AEPutParamDesc(&event, thisKeyword, thisDesc);
   1.529 -			}
   1.530 -		} while ((err == noErr) && (thisKeyword != 0));
   1.531 -
   1.532 -		va_end(ap);
   1.533 -	}
   1.534 -
   1.535 -	// Send event and get reply.
   1.536 -
   1.537 -	if (err == noErr)
   1.538 -		err = SendAppleEvent(&event, &localReply);
   1.539 -
   1.540 -	// Clean up.
   1.541 -
   1.542 -	if (!reply || (err != noErr)) {
   1.543 -		// *reply is already null because of our precondition
   1.544 -		AEDisposeDescQ(&localReply);
   1.545 -	} else {
   1.546 -		*reply = localReply;
   1.547 -	}
   1.548 -	AEDisposeDescQ(&event);
   1.549 -	AEDisposeDescQ(&target);
   1.550 -	assert((reply == NULL) || ((err == noErr) == (reply->descriptorType != typeNull)));
   1.551 -
   1.552 -	return err;
   1.553 -}
   1.554 -
   1.555 -extern OSStatus LIAECopyLoginItems(CFArrayRef *itemsPtr)
   1.556 -	// See comment in header.
   1.557 -	//
   1.558 -	// This routine creates an Apple event that corresponds to the
   1.559 -	// AppleScript:
   1.560 -	//
   1.561 -	//     get properties of every login item
   1.562 -	//
   1.563 -	// and sends it to System Events.  It then processes the reply
   1.564 -	// into a CFArray in the format that's documented in the header
   1.565 -	// comments.
   1.566 -{
   1.567 -	OSStatus			err;
   1.568 -	AppleEvent			reply;
   1.569 -	AEDescList			results;
   1.570 -	AEDesc				propertiesOfEveryLoginItem;
   1.571 -
   1.572 -	assert( itemsPtr != NULL);
   1.573 -	assert(*itemsPtr == NULL);
   1.574 -
   1.575 -	reply = kAENull;
   1.576 -	results = kAENull;
   1.577 -	propertiesOfEveryLoginItem = kAENull;
   1.578 -
   1.579 -	// Build object specifier for "properties of every login item".
   1.580 -
   1.581 -	{
   1.582 -		static const DescType keyAEPropertiesLocal = pProperties;
   1.583 -		static const DescType kAEAllLocal = kAEAll;
   1.584 -		AEDesc	every;
   1.585 -		AEDesc	everyLoginItem;
   1.586 -		AEDesc	properties;
   1.587 -
   1.588 -		every = kAENull;
   1.589 -		everyLoginItem = kAENull;
   1.590 -		properties = kAENull;
   1.591 -
   1.592 -		err = AECreateDesc(typeAbsoluteOrdinal, &kAEAllLocal, sizeof(kAEAllLocal), &every);
   1.593 -		if (err == noErr)
   1.594 -			err = CreateObjSpecifier(cLoginItem, (AEDesc *) &kAENull, formAbsolutePosition, &every, false, &everyLoginItem);
   1.595 -		if (err == noErr)
   1.596 -			err = AECreateDesc(typeType, &keyAEPropertiesLocal, sizeof(keyAEPropertiesLocal), &properties);
   1.597 -		if (err == noErr)
   1.598 -			err = CreateObjSpecifier(
   1.599 -				typeProperty,
   1.600 -				&everyLoginItem,
   1.601 -				formPropertyID,
   1.602 -				&properties,
   1.603 -				false,
   1.604 -				&propertiesOfEveryLoginItem);
   1.605 -
   1.606 -		AEDisposeDescQ(&every);
   1.607 -		AEDisposeDescQ(&everyLoginItem);
   1.608 -		AEDisposeDescQ(&properties);
   1.609 -	}
   1.610 -
   1.611 -	// Send event and get reply.
   1.612 -
   1.613 -	if (err == noErr)
   1.614 -		err = SendEventToSystemEventsWithParameters(
   1.615 -			kAECoreSuite,
   1.616 -			kAEGetData,
   1.617 -			&reply,
   1.618 -			keyDirectObject, &propertiesOfEveryLoginItem,
   1.619 -			0
   1.620 -		);
   1.621 -
   1.622 -	// Process reply.
   1.623 -
   1.624 -	if (err == noErr)
   1.625 -		err = AEGetParamDesc(&reply, keyDirectObject, typeAEList, &results);
   1.626 -	if (err == noErr)
   1.627 -		err = CreateCFArrayFromAEDescList(&results, itemsPtr);
   1.628 -
   1.629 -	// Clean up.
   1.630 -
   1.631 -	AEDisposeDescQ(&reply);
   1.632 -	AEDisposeDescQ(&results);
   1.633 -	AEDisposeDescQ(&propertiesOfEveryLoginItem);
   1.634 -	assert((err == noErr) == (*itemsPtr != NULL));
   1.635 -
   1.636 -	return err;
   1.637 -}
   1.638 -
   1.639 -extern OSStatus LIAEAddRefAtEnd(const FSRef *item, Boolean hideIt)
   1.640 -	// See comment in header.
   1.641 -	//
   1.642 -	// This routine creates an Apple event that corresponds to the
   1.643 -	// AppleScript:
   1.644 -	//
   1.645 -	//     make new login item
   1.646 -	// 	       with properties {
   1.647 -	//			   path:<path of item>,
   1.648 -	//			   hidden:hideIt
   1.649 -	//		   }
   1.650 -	//         at end
   1.651 -	//
   1.652 -	// and sends it to System Events.
   1.653 -{
   1.654 -	OSStatus 			err;
   1.655 -	AEDesc				newLoginItem;
   1.656 -	AERecord			properties;
   1.657 -	AERecord			endLoc;
   1.658 -	static const DescType cLoginItemLocal = cLoginItem;
   1.659 -
   1.660 -	assert(item != NULL);
   1.661 -
   1.662 -	newLoginItem = kAENull;
   1.663 -	endLoc = kAENull;
   1.664 -	properties = kAENull;
   1.665 -
   1.666 -	// Create "new login item" parameter.
   1.667 -
   1.668 -	err = AECreateDesc(typeType, &cLoginItemLocal, sizeof(cLoginItemLocal), &newLoginItem);
   1.669 -
   1.670 -	// Create "with properties" parameter.
   1.671 -
   1.672 -	if (err == noErr) {
   1.673 -		char		path[1024];
   1.674 -		AEDesc		pathDesc;
   1.675 -
   1.676 -		pathDesc = kAENull;
   1.677 -
   1.678 -		err = AECreateList(NULL, 0, true, &properties);
   1.679 -		if (err == noErr)
   1.680 -			err = FSRefMakePath(item, (UInt8 *) path, (UInt32)sizeof(path));
   1.681 -
   1.682 -		// System Events complains if you pass it typeUTF8Text directly, so
   1.683 -		// we do the conversion from typeUTF8Text to typeUnicodeText on our
   1.684 -		// side of the world.
   1.685 -
   1.686 -		if (err == noErr)
   1.687 -			err = AECoercePtr(typeUTF8Text, path, (Size) strlen(path), typeUnicodeText, &pathDesc);
   1.688 -		if (err == noErr)
   1.689 -			err = AEPutKeyDesc(&properties, propPath, &pathDesc);
   1.690 -		if (err == noErr)
   1.691 -			err = AEPutKeyPtr(&properties, propHidden, typeBoolean, &hideIt, sizeof(hideIt));
   1.692 -
   1.693 -		AEDisposeDescQ(&pathDesc);
   1.694 -	}
   1.695 -
   1.696 -	// Create "at end" parameter.
   1.697 -
   1.698 -	if (err == noErr) {
   1.699 -		AERecord	end;
   1.700 -		static const DescType kAEEndLocal = kAEEnd;
   1.701 -
   1.702 -		end = kAENull;
   1.703 -
   1.704 -		err = AECreateList(NULL, 0, true, &end);
   1.705 -		if (err == noErr)
   1.706 -			err = AEPutKeyPtr(&end, keyAEObject, typeNull, NULL, 0);
   1.707 -		if (err == noErr)
   1.708 -			err = AEPutKeyPtr(&end, keyAEPosition, typeEnumerated, &kAEEndLocal, (Size) sizeof(kAEEndLocal));
   1.709 -		if (err == noErr)
   1.710 -			err = AECoerceDesc(&end, cInsertionLoc, &endLoc);
   1.711 -
   1.712 -		AEDisposeDescQ(&end);
   1.713 -	}
   1.714 -
   1.715 -	// Send the event.
   1.716 -
   1.717 -	if (err == noErr)
   1.718 -		err = SendEventToSystemEventsWithParameters(
   1.719 -			kAECoreSuite,
   1.720 -			kAECreateElement,
   1.721 -			NULL,
   1.722 -			keyAEObjectClass, 	&newLoginItem,
   1.723 -			keyAEPropData, 		&properties,
   1.724 -			keyAEInsertHere, 	&endLoc,
   1.725 -			0
   1.726 -		);
   1.727 -
   1.728 -	// Clean up.
   1.729 -
   1.730 -	AEDisposeDescQ(&newLoginItem);
   1.731 -	AEDisposeDescQ(&endLoc);
   1.732 -	AEDisposeDescQ(&properties);
   1.733 -
   1.734 -	return err;
   1.735 -}
   1.736 -
   1.737 -extern OSStatus LIAEAddURLAtEnd(CFURLRef item, Boolean hideIt)
   1.738 -	// See comment in header.
   1.739 -	//
   1.740 -	// This is implemented as a wrapper around LIAEAddRef.
   1.741 -	// I chose to do it this way because an URL can reference a
   1.742 -	// file that doesn't except, whereas an FSRef can't, so by
   1.743 -	// having the URL routine call the FSRef routine, I naturally
   1.744 -	// ensure that the item exists on disk.
   1.745 -{
   1.746 -	OSStatus 	err;
   1.747 -	Boolean		success;
   1.748 -	FSRef		ref;
   1.749 -
   1.750 -	assert(item != NULL);
   1.751 -
   1.752 -	success = CFURLGetFSRef(item, &ref);
   1.753 -	if (success)
   1.754 -		err = LIAEAddRefAtEnd(&ref, hideIt);
   1.755 -	else
   1.756 -		// I have no idea what went wrong (thanks CF!).  Normally I'd
   1.757 -		// return coreFoundationUnknownErr here, but in this case I'm
   1.758 -		// going to go out on a limb and say that we have a file not found.
   1.759 -		err = fnfErr;
   1.760 -
   1.761 -	return err;
   1.762 -}
   1.763 -
   1.764 -extern OSStatus LIAERemove(CFIndex itemIndex)
   1.765 -	// See comment in header.
   1.766 -	//
   1.767 -	// This routine creates an Apple event that corresponds to the
   1.768 -	// AppleScript:
   1.769 -	//
   1.770 -	//     delete login item itemIndex
   1.771 -	//
   1.772 -	// and sends it to System Events.
   1.773 -{
   1.774 -	OSStatus	err;
   1.775 -	SInt32		itemIndexPlusOne;
   1.776 -	AEDesc		indexDesc;
   1.777 -	AEDesc		loginItemAtIndex;
   1.778 -
   1.779 -	assert(itemIndex >= 0);
   1.780 -
   1.781 -	indexDesc = kAENull;
   1.782 -	loginItemAtIndex = kAENull;
   1.783 -
   1.784 -	// Build object specifier for "login item X".
   1.785 -
   1.786 -	itemIndexPlusOne = (SInt32)itemIndex + 1;	// AppleScript is one-based, CF is zero-based
   1.787 -	err = AECreateDesc(typeSInt32, &itemIndexPlusOne, sizeof(itemIndexPlusOne), &indexDesc);
   1.788 -	if (err == noErr)
   1.789 -		err = CreateObjSpecifier(cLoginItem, (AEDesc *) &kAENull, formAbsolutePosition, &indexDesc, false, &loginItemAtIndex);
   1.790 -
   1.791 -	// Send the event.
   1.792 -
   1.793 -	if (err == noErr)
   1.794 -		err = SendEventToSystemEventsWithParameters(
   1.795 -			kAECoreSuite,
   1.796 -			kAEDelete,
   1.797 -			NULL,
   1.798 -			keyDirectObject, &loginItemAtIndex,
   1.799 -			0
   1.800 -		);
   1.801 -
   1.802 -	// Clean up.
   1.803 -
   1.804 -	AEDisposeDescQ(&indexDesc);
   1.805 -	AEDisposeDescQ(&loginItemAtIndex);
   1.806 -
   1.807 -	return err;
   1.808 -}
     2.1 --- a/Common/Source/LoginItemsAE.h	Sun Aug 02 00:52:57 2009 -0700
     2.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.3 @@ -1,101 +0,0 @@
     2.4 -/*
     2.5 -	File:		LoginItemsAE.h
     2.6 -
     2.7 -	Contains:	Login items manipulation via Apple events.
     2.8 -
     2.9 -	Copyright:	Copyright (c) 2005 by Apple Computer, Inc., All Rights Reserved.
    2.10 -
    2.11 -	Disclaimer:	IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
    2.12 -				("Apple") in consideration of your agreement to the following terms, and your
    2.13 -				use, installation, modification or redistribution of this Apple software
    2.14 -				constitutes acceptance of these terms.  If you do not agree with these terms,
    2.15 -				please do not use, install, modify or redistribute this Apple software.
    2.16 -
    2.17 -				In consideration of your agreement to abide by the following terms, and subject
    2.18 -				to these terms, Apple grants you a personal, non-exclusive license, under Apple’s
    2.19 -				copyrights in this original Apple software (the "Apple Software"), to use,
    2.20 -				reproduce, modify and redistribute the Apple Software, with or without
    2.21 -				modifications, in source and/or binary forms; provided that if you redistribute
    2.22 -				the Apple Software in its entirety and without modifications, you must retain
    2.23 -				this notice and the following text and disclaimers in all such redistributions of
    2.24 -				the Apple Software.  Neither the name, trademarks, service marks or logos of
    2.25 -				Apple Computer, Inc. may be used to endorse or promote products derived from the
    2.26 -				Apple Software without specific prior written permission from Apple.  Except as
    2.27 -				expressly stated in this notice, no other rights or licenses, express or implied,
    2.28 -				are granted by Apple herein, including but not limited to any patent rights that
    2.29 -				may be infringed by your derivative works or by other works in which the Apple
    2.30 -				Software may be incorporated.
    2.31 -
    2.32 -				The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
    2.33 -				WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
    2.34 -				WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    2.35 -				PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
    2.36 -				COMBINATION WITH YOUR PRODUCTS.
    2.37 -
    2.38 -				IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
    2.39 -				CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
    2.40 -				GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    2.41 -				ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
    2.42 -				OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
    2.43 -				(INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
    2.44 -				ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    2.45 -
    2.46 -	Change History (most recent first):
    2.47 -
    2.48 -$Log: LoginItemsAE.h,v $
    2.49 -Revision 1.1  2005/09/27 12:29:29
    2.50 -First checked in.
    2.51 -
    2.52 -
    2.53 -*/
    2.54 -
    2.55 -#ifndef _LOGINITEMSAE_H
    2.56 -#define _LOGINITEMSAE_H
    2.57 -
    2.58 -/////////////////////////////////////////////////////////////////
    2.59 -
    2.60 -// System prototypes
    2.61 -
    2.62 -#include <ApplicationServices/ApplicationServices.h>
    2.63 -
    2.64 -/////////////////////////////////////////////////////////////////
    2.65 -
    2.66 -#ifdef __cplusplus
    2.67 -extern "C" {
    2.68 -#endif
    2.69 -
    2.70 -/////////////////////////////////////////////////////////////////
    2.71 -
    2.72 -// Keys for the dictionary return by LIAECopyLoginItems.
    2.73 -
    2.74 -#define kLIAEURL    CFSTR("URL")		// CFURL
    2.75 -#define kLIAEHidden CFSTR("Hidden")		// CFBoolean
    2.76 -
    2.77 -extern OSStatus LIAECopyLoginItems(CFArrayRef *itemsPtr);
    2.78 -	// Returns an array of CFDictionaries, each one describing a
    2.79 -	// login item.  Each dictionary has two elements,
    2.80 -	// kLIAEURL and kLIAEHidden, which are
    2.81 -	// documented above.
    2.82 -	//
    2.83 -	// On input,    itemsPtr must not be NULL.
    2.84 -	// On input,   *itemsPtr must be NULL.
    2.85 -	// On success, *itemsPtr will be a pointer to a CFArray.
    2.86 -	// Or error,   *itemsPtr will be NULL.
    2.87 -
    2.88 -extern OSStatus LIAEAddRefAtEnd(const FSRef *item, Boolean hideIt);
    2.89 -extern OSStatus LIAEAddURLAtEnd(CFURLRef item,     Boolean hideIt);
    2.90 -	// Add a new login item at the end of the list, using either
    2.91 -	// an FSRef or a CFURL.  The hideIt parameter controls whether
    2.92 -	// the item is hidden when it's launched.
    2.93 -
    2.94 -extern OSStatus LIAERemove(CFIndex itemIndex);
    2.95 -	// Remove a login item.  itemIndex is an index into the array
    2.96 -	// of login items as returned by LIAECopyLoginItems.
    2.97 -
    2.98 -/////////////////////////////////////////////////////////////////
    2.99 -
   2.100 -#ifdef __cplusplus
   2.101 -}
   2.102 -#endif
   2.103 -
   2.104 -#endif
     3.1 --- a/Core/Source/GrowlPreferencesController.h	Sun Aug 02 00:52:57 2009 -0700
     3.2 +++ b/Core/Source/GrowlPreferencesController.h	Sun Aug 02 13:03:53 2009 -0700
     3.3 @@ -50,6 +50,7 @@
     3.4  #import "GrowlAbstractSingletonObject.h"
     3.5  
     3.6  @interface GrowlPreferencesController : GrowlAbstractSingletonObject {
     3.7 +	LSSharedFileListRef loginItems;
     3.8  }
     3.9  
    3.10  + (GrowlPreferencesController *) sharedController;
     4.1 --- a/Core/Source/GrowlPreferencesController.m	Sun Aug 02 00:52:57 2009 -0700
     4.2 +++ b/Core/Source/GrowlPreferencesController.m	Sun Aug 02 13:03:53 2009 -0700
     4.3 @@ -60,12 +60,14 @@
     4.4  															selector:@selector(growlPreferencesChanged:)
     4.5  																name:GrowlPreferencesChanged
     4.6  															  object:nil];
     4.7 +		loginItems = LSSharedFileListCreate(kCFAllocatorDefault, kLSSharedFileListSessionLoginItems, /*options*/ NULL);
     4.8  	}
     4.9  	return self;
    4.10  }
    4.11  
    4.12  - (void) destroy {
    4.13  	[[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
    4.14 +	CFRelease(loginItems);
    4.15  
    4.16  	[super destroy];
    4.17  }
    4.18 @@ -158,24 +160,28 @@
    4.19  #pragma mark Start-at-login control
    4.20  
    4.21  - (BOOL) shouldStartGrowlAtLogin {
    4.22 -	OSStatus   status;
    4.23  	Boolean    foundIt = false;
    4.24 -	CFArrayRef loginItems = NULL;
    4.25  
    4.26  	//get the prefpane bundle and find GHA within it.
    4.27  	NSString *pathToGHA      = [[NSBundle bundleWithIdentifier:GROWL_PREFPANE_BUNDLE_IDENTIFIER] pathForResource:@"GrowlHelperApp" ofType:@"app"];
    4.28  	//get the file url to GHA.
    4.29  	CFURLRef urlToGHA = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, (CFStringRef)pathToGHA, kCFURLPOSIXPathStyle, true);
    4.30  
    4.31 -	status = LIAECopyLoginItems(&loginItems);
    4.32 -	if (status == noErr) {
    4.33 -		for (CFIndex i=0, count=CFArrayGetCount(loginItems); i<count; ++i) {
    4.34 -			CFDictionaryRef loginItem = CFArrayGetValueAtIndex(loginItems, i);
    4.35 -			foundIt = CFEqual(CFDictionaryGetValue(loginItem, kLIAEURL), urlToGHA);
    4.36 +	UInt32 seed = 0U;
    4.37 +	NSArray *currentLoginItems = [NSMakeCollectable(LSSharedFileListCopySnapshot(loginItems, &seed)) autorelease];
    4.38 +	for (id itemObject in currentLoginItems) {
    4.39 +		LSSharedFileListItemRef item = (LSSharedFileListItemRef)itemObject;
    4.40 +
    4.41 +		UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
    4.42 +		CFURLRef URL = NULL;
    4.43 +		OSStatus err = LSSharedFileListItemResolve(item, resolutionFlags, &URL, /*outRef*/ NULL);
    4.44 +		if (err == noErr) {
    4.45 +			foundIt = CFEqual(URL, urlToGHA);
    4.46 +			CFRelease(URL);
    4.47 +
    4.48  			if (foundIt)
    4.49  				break;
    4.50  		}
    4.51 -		CFRelease(loginItems);
    4.52  	}
    4.53  
    4.54  	CFRelease(urlToGHA);
    4.55 @@ -191,31 +197,47 @@
    4.56  
    4.57  - (void) setStartAtLogin:(NSString *)path enabled:(BOOL)enabled {
    4.58  	OSStatus status;
    4.59 -	CFArrayRef loginItems = NULL;
    4.60 -	NSURL *url = [NSURL fileURLWithPath:path];
    4.61 -	NSInteger existingLoginItemIndex = -1;
    4.62 -
    4.63 -	status = LIAECopyLoginItems(&loginItems);
    4.64 -
    4.65 -	if (status == noErr) {
    4.66 -		NSEnumerator *enumerator = [(NSArray *)loginItems objectEnumerator];
    4.67 -		NSDictionary *loginItemDict;
    4.68 -
    4.69 -		while ((loginItemDict = [enumerator nextObject])) {
    4.70 -			if ([[loginItemDict objectForKey:(NSString *)kLIAEURL] isEqual:url]) {
    4.71 -				existingLoginItemIndex = [(NSArray *)loginItems indexOfObjectIdenticalTo:loginItemDict];
    4.72 +	CFURLRef URLToToggle = (CFURLRef)[NSURL fileURLWithPath:path];
    4.73 +	LSSharedFileListItemRef existingItem = NULL;
    4.74 +
    4.75 +	UInt32 seed = 0U;
    4.76 +	NSArray *currentLoginItems = [NSMakeCollectable(LSSharedFileListCopySnapshot(loginItems, &seed)) autorelease];
    4.77 +	for (id itemObject in currentLoginItems) {
    4.78 +		LSSharedFileListItemRef item = (LSSharedFileListItemRef)itemObject;
    4.79 +
    4.80 +		UInt32 resolutionFlags = kLSSharedFileListNoUserInteraction | kLSSharedFileListDoNotMountVolumes;
    4.81 +		CFURLRef URL = NULL;
    4.82 +		OSStatus err = LSSharedFileListItemResolve(item, resolutionFlags, &URL, /*outRef*/ NULL);
    4.83 +		if (err == noErr) {
    4.84 +			Boolean foundIt = CFEqual(URL, URLToToggle);
    4.85 +			CFRelease(URL);
    4.86 +
    4.87 +			if (foundIt) {
    4.88 +				existingItem = item;
    4.89  				break;
    4.90  			}
    4.91  		}
    4.92  	}
    4.93  
    4.94 -	if (enabled && (existingLoginItemIndex == -1))
    4.95 -		LIAEAddURLAtEnd((CFURLRef)url, false);
    4.96 -	else if (!enabled && (existingLoginItemIndex != -1))
    4.97 -		LIAERemove(existingLoginItemIndex);
    4.98 -
    4.99 -	if(loginItems)
   4.100 -		CFRelease(loginItems);
   4.101 +	if (enabled && (existingItem == NULL)) {
   4.102 +		NSString *displayName = [[NSFileManager defaultManager] displayNameAtPath:path];
   4.103 +		IconRef icon = NULL;
   4.104 +		FSRef ref;
   4.105 +		Boolean gotRef = CFURLGetFSRef(URLToToggle, &ref);
   4.106 +		if (gotRef) {
   4.107 +			status = GetIconRefFromFileInfo(&ref,
   4.108 +											/*fileNameLength*/ 0, /*fileName*/ NULL,
   4.109 +											kFSCatInfoNone, /*catalogInfo*/ NULL,
   4.110 +											kIconServicesNormalUsageFlag,
   4.111 +											&icon,
   4.112 +											/*outLabel*/ NULL);
   4.113 +			if (status != noErr)
   4.114 +				icon = NULL;
   4.115 +		}
   4.116 +
   4.117 +		LSSharedFileListInsertItemURL(loginItems, kLSSharedFileListItemBeforeFirst, (CFStringRef)displayName, icon, URLToToggle, /*propertiesToSet*/ NULL, /*propertiesToClear*/ NULL);
   4.118 +	} else if (!enabled && (existingItem != NULL))
   4.119 +		LSSharedFileListItemRemove(loginItems, existingItem);
   4.120  }
   4.121  
   4.122  #pragma mark -
     5.1 --- a/Growl.xcodeproj/project.pbxproj	Sun Aug 02 00:52:57 2009 -0700
     5.2 +++ b/Growl.xcodeproj/project.pbxproj	Sun Aug 02 13:03:53 2009 -0700
     5.3 @@ -329,9 +329,6 @@
     5.4  		95EC864C08DDB4D800110810 /* CFDictionaryAdditions.c in Sources */ = {isa = PBXBuildFile; fileRef = 95F9767A084A297400915BFE /* CFDictionaryAdditions.c */; };
     5.5  		95EC864D08DDB4DD00110810 /* CFDictionaryAdditions.c in Sources */ = {isa = PBXBuildFile; fileRef = 95F9767A084A297400915BFE /* CFDictionaryAdditions.c */; };
     5.6  		95EC864E08DDB4E600110810 /* CFDictionaryAdditions.c in Sources */ = {isa = PBXBuildFile; fileRef = 95F9767A084A297400915BFE /* CFDictionaryAdditions.c */; };
     5.7 -		95F0A21D0904D7EA00BD679D /* LoginItemsAE.c in Sources */ = {isa = PBXBuildFile; fileRef = 95F0A21B0904D7EA00BD679D /* LoginItemsAE.c */; };
     5.8 -		95F0A21F0904D7EA00BD679D /* LoginItemsAE.c in Sources */ = {isa = PBXBuildFile; fileRef = 95F0A21B0904D7EA00BD679D /* LoginItemsAE.c */; };
     5.9 -		95F0A2200904D7EA00BD679D /* LoginItemsAE.c in Sources */ = {isa = PBXBuildFile; fileRef = 95F0A21B0904D7EA00BD679D /* LoginItemsAE.c */; };
    5.10  		95F975AE084A23F500915BFE /* CFMutableDictionaryAdditions.c in Sources */ = {isa = PBXBuildFile; fileRef = 95F975AC084A23F500915BFE /* CFMutableDictionaryAdditions.c */; };
    5.11  		95F9767C084A297400915BFE /* CFDictionaryAdditions.c in Sources */ = {isa = PBXBuildFile; fileRef = 95F9767A084A297400915BFE /* CFDictionaryAdditions.c */; };
    5.12  		95F9771E084A2C7700915BFE /* CFMutableDictionaryAdditions.c in Sources */ = {isa = PBXBuildFile; fileRef = 95F975AC084A23F500915BFE /* CFMutableDictionaryAdditions.c */; };
    5.13 @@ -1073,8 +1070,6 @@
    5.14  		95E2458008EEAC8F0092EAAC /* GrowlNotificationDisplayBridge.m */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.objc; name = GrowlNotificationDisplayBridge.m; path = Plugins/Displays/GrowlNotificationDisplayBridge.m; sourceTree = "<group>"; };
    5.15  		95E678F307511471002BF4E6 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = /System/Library/Frameworks/Security.framework; sourceTree = "<absolute>"; };
    5.16  		95EF04F207C77C1500218584 /* CoreGraphicsServices.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = CoreGraphicsServices.h; path = Core/Source/CoreGraphicsServices.h; sourceTree = "<group>"; };
    5.17 -		95F0A21B0904D7EA00BD679D /* LoginItemsAE.c */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.c; name = LoginItemsAE.c; path = Common/Source/LoginItemsAE.c; sourceTree = "<group>"; };
    5.18 -		95F0A21C0904D7EA00BD679D /* LoginItemsAE.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = LoginItemsAE.h; path = Common/Source/LoginItemsAE.h; sourceTree = "<group>"; };
    5.19  		95F975AB084A23F500915BFE /* CFMutableDictionaryAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CFMutableDictionaryAdditions.h; path = Common/Source/CFMutableDictionaryAdditions.h; sourceTree = "<group>"; };
    5.20  		95F975AC084A23F500915BFE /* CFMutableDictionaryAdditions.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = CFMutableDictionaryAdditions.c; path = Common/Source/CFMutableDictionaryAdditions.c; sourceTree = "<group>"; };
    5.21  		95F97679084A297400915BFE /* CFDictionaryAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CFDictionaryAdditions.h; path = Common/Source/CFDictionaryAdditions.h; sourceTree = "<group>"; };
    5.22 @@ -1373,8 +1368,6 @@
    5.23  				070C1AF80912D1F500884E7D /* Headers */,
    5.24  				0790135207D932140023B640 /* GrowlVersionUtilities.h */,
    5.25  				0790135507D9321B0023B640 /* GrowlVersionUtilities.c */,
    5.26 -				95F0A21C0904D7EA00BD679D /* LoginItemsAE.h */,
    5.27 -				95F0A21B0904D7EA00BD679D /* LoginItemsAE.c */,
    5.28  				95CF98A0082A5B8E001A409D /* cdsa.h */,
    5.29  				95CF98A1082A5B8E001A409D /* cdsa.c */,
    5.30  			);
    5.31 @@ -2858,7 +2851,6 @@
    5.32  				95E2457808EEAC630092EAAC /* GrowlDisplayPlugin.m in Sources */,
    5.33  				95E2458208EEAC8F0092EAAC /* GrowlNotificationDisplayBridge.m in Sources */,
    5.34  				95E246A008EF02E80092EAAC /* GrowlPositionController.m in Sources */,
    5.35 -				95F0A21D0904D7EA00BD679D /* LoginItemsAE.c in Sources */,
    5.36  				743EDE0809273C8A002A9670 /* GrowlWebKitPluginHandler.m in Sources */,
    5.37  				743EDE14092740F5002A9670 /* GrowlWebKitDisplayPlugin.m in Sources */,
    5.38  				743EE18E0927A85E002A9670 /* GrowlNonCopyingMutableDictionary.m in Sources */,
    5.39 @@ -2911,7 +2903,6 @@
    5.40  				9507CEA208C9D6C900209F9E /* CFMutableDictionaryAdditions.c in Sources */,
    5.41  				9555388808DACC1B00D49CB1 /* CFDictionaryAdditions.c in Sources */,
    5.42  				9555388908DACC2500D49CB1 /* CFGrowlAdditions.c in Sources */,
    5.43 -				95F0A21F0904D7EA00BD679D /* LoginItemsAE.c in Sources */,
    5.44  				934EBCB80931F529002C378F /* GrowlApplicationBridge.m in Sources */,
    5.45  			);
    5.46  			runOnlyForDeploymentPostprocessing = 0;
    5.47 @@ -2981,7 +2972,6 @@
    5.48  				95E2457A08EEAC630092EAAC /* GrowlDisplayPlugin.m in Sources */,
    5.49  				95E2458408EEAC8F0092EAAC /* GrowlNotificationDisplayBridge.m in Sources */,
    5.50  				EE17E56D08EFFCDB0086F0C3 /* GrowlThreadedView.m in Sources */,
    5.51 -				95F0A2200904D7EA00BD679D /* LoginItemsAE.c in Sources */,
    5.52  				070C1AE80912D16600884E7D /* GrowlNonCopyingMutableDictionary.m in Sources */,
    5.53  				745352AF09313ABA0000B26A /* GrowlWebKitPrefsController.m in Sources */,
    5.54  				745352B009313ABB0000B26A /* GrowlWebKitPluginHandler.m in Sources */,