Commits

evands  committed 325018a

* Cleanup a lot of mess
* Bridge is now retained and released rather than being a weak reference
* Observation for bridge's notifications is done via the observer system rather than a direct binding to prevent a retain loop
* Use NSObject's perform with delay methods and cancel them as necessary rather than core foundation timers. This makes retain/release happen for the timers automatically and makes the code significantly easier to read and follow

  • Participants
  • Parent commits 9bdf62a
  • Branches default

Comments (0)

Files changed (2)

File Plugins/Displays/GrowlDisplayWindowController.h

 	NSMutableDictionary              *windowTransitions;
 	id					             delegate;
 
-	CFRunLoopTimerRef	             displayTimer;
-	CFRunLoopTimerRef				 delayTimer;
-	CFRunLoopTimerRef				 transitionTimer;
-
 	BOOL				             ignoresOtherNotifications;
 
 	CFTimeInterval                   transitionDuration;
 - (void) willTakeDownNotification;
 - (void)  didTakeDownNotification;
 
-/* provate method called when all transitions finish running */
+/* private method called when all transitions finish running */
 - (void) didFinishTransitionsBeforeDisplay;
 - (void) didFinishTransitionsAfterDisplay;
 
 #pragma mark -
 
-- (void) startDisplayTimer;
-- (void) stopDisplayTimer;
-
-#pragma mark -
-
 - (BOOL) addTransition:(GrowlWindowTransition *)transition;
 - (void) removeTransition:(GrowlWindowTransition *)transition;
 

File Plugins/Displays/GrowlDisplayWindowController.m

 
 static NSMutableDictionary *existingInstances;
 
-extern CFRunLoopRef CFRunLoopGetMain(void);
-
-static void stopDisplay(CFRunLoopTimerRef timer, void *context) {
-#pragma unused(timer)
-	[(GrowlDisplayWindowController *)context stopDisplay];
-}
-
-static void finishedTransitionsBeforeDisplay(CFRunLoopTimerRef timer, void *context) {
-#pragma unused(timer)
-	[(GrowlDisplayWindowController *)context didFinishTransitionsBeforeDisplay];
-}
-static void finishedTransitionsAfterDisplay(CFRunLoopTimerRef timer, void *context) {
-#pragma unused(timer)
-	[(GrowlDisplayWindowController *)context didFinishTransitionsAfterDisplay];
-}
-
-static void startAnimation(CFRunLoopTimerRef timer, void *context) {
-	[(GrowlWindowTransition *)context startAnimation];
-	
-	// we release this timer or it will leak per display window created
-	if (timer) {
-		CFRunLoopTimerInvalidate(timer);
-		CFRelease(timer);
-		timer = NULL;
-	}
-}
+@interface GrowlDisplayWindowController (PRIVATE)
+- (void)cancelDisplayDelayedPerforms;
+@end
 
 @implementation GrowlDisplayWindowController
 
 
 - (id) initWithWindow:(NSWindow *)window {
 	if ((self = [super initWithWindow:window])) {
-		[self bind:@"notification" toObject:self withKeyPath:@"bridge.notification" options:nil];
 		windowTransitions = [[NSMutableDictionary alloc] init];
 		ignoresOtherNotifications = NO;
 		bridge = nil;
 }
 
 - (void) dealloc {
-	[self stopDisplayTimer];
 	[self setDelegate:nil];
-	NSLog(@"Telling %@ to not notify me", [self bridge]);
 	[[self bridge] removeObserver:self forKeyPath:@"notification"];
-	[self unbind:@"notification"];
 
 	NSFreeMapTable(startTimes);
 	NSFreeMapTable(endTimes);
 
-	GrowlLog *growlLog = [GrowlLog sharedController];
-
-	[growlLog writeToLog:@"releasing bridge %@", bridge];
-	[bridge              release];
-	[growlLog writeToLog:@"released"];
+	[bridge				 release];
 	[target              release];
 	[clickContext        release];
 	[clickHandlerEnabled release];
 		[self willDisplayNotification];
 		[window orderFront:nil];
 		if ([self startAllTransitions]) {
-			CFRunLoopTimerContext context = {0, self, NULL, NULL, NULL};
-			delayTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent(), 0, 0, 0, finishedTransitionsBeforeDisplay, &context);
-			CFRunLoopAddTimer(CFRunLoopGetMain(), delayTimer, kCFRunLoopCommonModes);
-			//[self performSelector:@selector(didFinishTransitionsBeforeDisplay) withObject:nil afterDelay:transitionDuration];
+			[self performSelector:@selector(didFinishTransitionsBeforeDisplay)
+					   withObject:nil
+					   afterDelay:transitionDuration];
 		} else {
 			[self didFinishTransitionsBeforeDisplay];
 		}
 }
 
 - (void) stopDisplay {
-	[self stopDisplayTimer];
+	[self cancelDisplayDelayedPerforms];
+
 	[self willTakeDownNotification];
 	if ([self startAllTransitions]) {
-		CFRunLoopTimerContext context = {0, self, NULL, NULL, NULL};
-		delayTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()+transitionDuration, 0, 0, 0, finishedTransitionsAfterDisplay, &context);
-		CFRunLoopAddTimer(CFRunLoopGetMain(), delayTimer, kCFRunLoopCommonModes);
-		//[self performSelector:@selector(didFinishTransitionsAfterDisplay) withObject:nil afterDelay:transitionDuration];
+		[self performSelector:@selector(didFinishTransitionsAfterDisplay) 
+				   withObject:nil
+				   afterDelay:transitionDuration];
 	} else {
 		[self didFinishTransitionsAfterDisplay];
 	}
 #pragma mark -
 #pragma mark Display stages
 
+- (void)cancelDisplayDelayedPerforms
+{
+	[[self class] cancelPreviousPerformRequestsWithTarget:self
+												 selector:@selector(didFinishTransitionsBeforeDisplay) 
+												   object:nil];
+	
+	[[self class] cancelPreviousPerformRequestsWithTarget:self
+												 selector:@selector(didFinishTransitionsAfterDisplay) 
+												   object:nil];
+
+	[[self class] cancelPreviousPerformRequestsWithTarget:self
+												 selector:@selector(stopDisplay) 
+												   object:nil];	
+}
+
 - (void) willDisplayNotification {
 	[[NSNotificationCenter defaultCenter] postNotificationName:GrowlDisplayWindowControllerWillDisplayWindowNotification
 														object:self];
 }
 
 - (void) didFinishTransitionsBeforeDisplay {
-	if (delayTimer) {
-		CFRunLoopTimerInvalidate(delayTimer);
-		CFRelease(delayTimer);
-		delayTimer = NULL;
+	[self cancelDisplayDelayedPerforms];
+
+	if (![[[notification auxiliaryDictionary] objectForKey:GROWL_NOTIFICATION_STICKY] boolValue]) {
+		[self performSelector:@selector(stopDisplay)
+				   withObject:nil
+				   afterDelay:(displayDuration+transitionDuration)];		
 	}
-	if (![[[notification auxiliaryDictionary] objectForKey:GROWL_NOTIFICATION_STICKY] boolValue])
-		[self startDisplayTimer];
 }
 
 - (void) didFinishTransitionsAfterDisplay {
-	if (delayTimer) {
-		CFRunLoopTimerInvalidate(delayTimer);
-		CFRelease(delayTimer);
-		delayTimer = NULL;
-	}
+	[self cancelDisplayDelayedPerforms];
+
 	//Clear the rect we reserved...
 	NSWindow *window = [self window];
 	[window orderOut:nil];
 
 	if ((bridge) && ([bridge respondsToSelector:@selector(display)]))
 		[[bridge display] displayWindowControllerDidTakeDownWindow:self];
+	else {
+		NSLog(@"%@ bridge does not respond to display",bridge);
+	}
 }
 
 - (void) didDisplayNotification {
 		//Avoid duplicate click messages by immediately clearing the clickContext
 		clickContext = nil;
 	}
-	[nc postNotificationName:GrowlDisplayWindowControllerWillDisplayWindowNotification object:self];
+	[nc postNotificationName:GrowlDisplayWindowControllerDidTakeWindowDownNotification object:self];
 }
-
-#pragma mark -
-#pragma mark Display timer
-
-- (void) startDisplayTimer {
-	CFRunLoopTimerContext context = {0, self, NULL, NULL, NULL};
-	displayTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()+displayDuration+transitionDuration, 0, 0, 0, stopDisplay, &context);
-	CFRunLoopAddTimer(CFRunLoopGetMain(), displayTimer, kCFRunLoopCommonModes);
-}
-
-- (void) stopDisplayTimer {
-	if (displayTimer) {
-		CFRunLoopTimerInvalidate(displayTimer);
-		CFRelease(displayTimer);
-		displayTimer = NULL;
-	}
-}
-
 #pragma mark -
 #pragma mark Click feedback
 
 
 	// Set up this transition...
 	[transition setDuration: (endTime - startTime)];
-	CFRunLoopTimerContext context = {0, transition, NULL, NULL, NULL};
-	transitionTimer = CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent()+startTime, 0, 0, 0, startAnimation, &context);
-	CFRunLoopAddTimer(CFRunLoopGetMain(), transitionTimer, kCFRunLoopCommonModes);
-	//[transition performSelector:@selector(startAnimation) withObject:nil afterDelay:startTime];
+	[transition performSelector:@selector(startAnimation) 
+					 withObject:nil
+					 afterDelay:startTime];
 
 	return YES;
 }
 
 - (void) stopTransition:(GrowlWindowTransition *)transition {
 	[transition stopAnimation];
-	if (transitionTimer) {
-		CFRunLoopTimerInvalidate(transitionTimer);
-		CFRelease(transitionTimer);
-		transitionTimer = NULL;
-	}
-	//[[self class] cancelPreviousPerformRequestsWithTarget:transition
-	//											 selector:@selector(startAnimation)
-	//											   object:nil];
+	
+	[[self class] cancelPreviousPerformRequestsWithTarget:transition
+												 selector:@selector(startAnimation)
+												   object:nil];
 }
 
 - (void) stopTransitionOfKind:(Class)transitionClass {
 #pragma mark -
 
 - (GrowlNotificationDisplayBridge *) bridge {
-    //NSLog(@"in -bridge, returned bridge = %@", bridge);
-
     return bridge;
 }
 
 - (void) setBridge:(GrowlNotificationDisplayBridge *)theBridge {
-	//This must not retain the bridge, because the bridge retains us.
-	bridge = theBridge;
+	if (bridge != theBridge) {
+		if (bridge) {
+			NSLog(@"*** This may be an error. %@ had its bridge reset", self);
+			[bridge removeObserver:self forKeyPath:@"notification"];
+		}
+		
+		bridge = [theBridge retain];
+		
+		[bridge addObserver:self forKeyPath:@"notification" options:NSKeyValueObservingOptionNew context:NULL];
+		[self observeValueForKeyPath:@"notification" ofObject:bridge change:nil context:NULL];
+	}
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+#pragma unused(change)
+#pragma unused(context)
+	if ((object == bridge) &&
+		[keyPath isEqualToString:@"notification"]) {
+		[self setNotification:[bridge notification]];
+	}
 }
 
 #pragma mark -
 		if ([observer respondsToSelector:@selector(displayWindowControllerWillDisplayWindow:)])
 			[nc addObserver:observer
 				   selector:@selector(displayWindowControllerWillDisplayWindow:)
-					   name:(NSString *)GrowlDisplayWindowControllerWillDisplayWindowNotification
+					   name:GrowlDisplayWindowControllerWillDisplayWindowNotification
 					 object:self];
 		if ([observer respondsToSelector:@selector(displayWindowControllerDidDisplayWindow:)])
 			[nc addObserver:observer
 				   selector:@selector(displayWindowControllerDidDisplayWindow:)
-					   name:(NSString *)GrowlDisplayWindowControllerDidDisplayWindowNotification
+					   name:GrowlDisplayWindowControllerDidDisplayWindowNotification
 					 object:self];
 
 		if ([observer respondsToSelector:@selector(displayWindowControllerWillTakeDownWindow:)])
 			[nc addObserver:observer
 				   selector:@selector(displayWindowControllerWillTakeWindowDown:)
-					   name:(NSString *)GrowlDisplayWindowControllerWillTakeWindowDownNotification
+					   name:GrowlDisplayWindowControllerWillTakeWindowDownNotification
 					 object:self];
 		if ([observer respondsToSelector:@selector(displayWindowControllerDidTakeWindowDown:)])
 			[nc addObserver:observer
 				   selector:@selector(displayWindowControllerDidTakeWindowDown:)
-					   name:(NSString *)GrowlDisplayWindowControllerDidTakeWindowDownNotification
+					   name:GrowlDisplayWindowControllerDidTakeWindowDownNotification
 					 object:self];
 		if ([observer respondsToSelector:@selector(displayWindowControllerNotificationBlocked:)])
 			[nc addObserver:observer
 				   selector:@selector(displayWindowControllerNotificationBlocked:)
-					   name:(NSString *)GrowlDisplayWindowControllerNotificationBlockedNotification
+					   name:GrowlDisplayWindowControllerNotificationBlockedNotification
 					 object:self];
 	}
 }