StatusItem/Source/GrowlMenu.m
author boredzo
Sun Jul 06 17:12:11 2008 +0000 (2008-07-06)
changeset 4140 ab8742429e22
parent 4016 3b1da316d31a
child 4258 449b389f31b6
permissions -rw-r--r--
This wasn't *really* a leak, but this makes the static analyzer happy and is cleaner, anyway.
     1 //
     2 //  GrowlMenu.m
     3 //
     4 //
     5 //  Created by rudy on Sun Apr 17 2005.
     6 //  Copyright (c) 2005 The Growl Project. All rights reserved.
     7 //
     8 
     9 #import "GrowlMenu.h"
    10 #import "GrowlPreferencesController.h"
    11 #import "GrowlPathUtilities.h"
    12 //#import "GrowlPluginController.h"
    13 #include <unistd.h>
    14 
    15 #define kRestartGrowl                NSLocalizedString(@"Restart Growl", @"")
    16 #define kRestartGrowlTooltip         NSLocalizedString(@"Restart Growl", @"")
    17 #define kStartGrowl                  NSLocalizedString(@"Start Growl", @"")
    18 #define kStartGrowlTooltip           NSLocalizedString(@"Start Growl", @"")
    19 #define kStopGrowl                   NSLocalizedString(@"Stop Growl", @"")
    20 #define kStopGrowlTooltip            NSLocalizedString(@"Stop Growl", @"")
    21 #define kDefaultDisplay              NSLocalizedString(@"Default display", @"")
    22 #define kDefaultDisplayTooltip       NSLocalizedString(@"Set the default display style", @"")
    23 #define kOpenGrowlPreferences        NSLocalizedString(@"Open Growl Preferences...", @"")
    24 #define kOpenGrowlPreferencesTooltip NSLocalizedString(@"Open the Growl preference pane", @"")
    25 #define kSquelchMode                 NSLocalizedString(@"Log only, don't display", @"")
    26 #define kSquelchModeTooltip          NSLocalizedString(@"Don't show notifications, but still log them", @"")
    27 #define kStopGrowlMenu               NSLocalizedString(@"Hide Status Item", @"")
    28 #define kStopGrowlMenuTooltip        NSLocalizedString(@"Hide this status item", @"")
    29 #define kStickyWhenAwayMenu			 NSLocalizedString(@"Sticky Notifications", @"")
    30 #define kStickyWhenAwayMenuTooltip   NSLocalizedString(@"Toggles the sticky notification state", @"")
    31 #define kDefaultDisplayChanged       NSLocalizedString(@"Default display changed to %@", @"")
    32 
    33 int main(void) {
    34 	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    35 	[NSApplication sharedApplication];
    36 
    37 	GrowlMenu *menu = [[[GrowlMenu alloc] init] autorelease];
    38 	[NSApp setDelegate:menu];
    39 	[NSApp run];
    40 
    41 	// dead code
    42 	[pool release];
    43 
    44 	return EXIT_SUCCESS;
    45 }
    46 
    47 @implementation GrowlMenu
    48 
    49 - (void) applicationDidFinishLaunching:(NSNotification *)aNotification {
    50 #pragma unused(aNotification)
    51 	pid = getpid();
    52 	preferences = [GrowlPreferencesController sharedController];
    53 
    54 	NSMenu *m = [self createMenu];
    55 
    56 	statusItem = [[[NSStatusBar systemStatusBar] statusItemWithLength:NSSquareStatusItemLength] retain];
    57 
    58 	NSBundle *bundle = [NSBundle mainBundle];
    59 
    60 	clawImage = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:@"growlmenu" ofType:@"png"]];
    61 	clawHighlightImage = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:@"growlmenu-alt" ofType:@"png"]];
    62 	squelchImage = [[NSImage alloc] initWithContentsOfFile:[bundle pathForResource:@"squelch" ofType:@"png"]];
    63 
    64 	[self setImage];
    65 
    66 	[statusItem setMenu:m]; // retains m
    67 	[statusItem setToolTip:@"Growl"];
    68 	[statusItem setHighlightMode:YES];
    69 
    70 	[m release];
    71 
    72 	[self setGrowlMenuEnabled:YES];
    73 
    74 	NSNotificationCenter *nc = [NSDistributedNotificationCenter defaultCenter];
    75 	[nc addObserver:self
    76 		   selector:@selector(shutdown:)
    77 			   name:@"GrowlMenuShutdown"
    78 			 object:nil];
    79 	[nc addObserver:self
    80 		   selector:@selector(reloadPrefs:)
    81 			   name:GrowlPreferencesChanged
    82 			 object:nil];
    83 
    84 	[GrowlApplicationBridge setGrowlDelegate:self];
    85 }
    86 
    87 #pragma mark Growl delegate methods
    88 
    89 - (NSString *) applicationNameForGrowl {
    90 	return @"GrowlMenu";
    91 }
    92 
    93 - (NSDictionary *) registrationDictionaryForGrowl {
    94 	NSArray *notifications = [NSArray arrayWithObject:@"Display changed"];
    95 	NSDictionary *registrationDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
    96 											notifications, GROWL_NOTIFICATIONS_ALL,
    97 											notifications, GROWL_NOTIFICATIONS_DEFAULT,
    98 											[NSDictionary dictionaryWithObject:NSLocalizedString(@"Display changed", "Notification posted by GrowlMenu when the display style changes")
    99 																		forKey:@"Display changed"], GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES,
   100 											nil];
   101 
   102 	return registrationDictionary;
   103 }
   104 
   105 #pragma mark -
   106 
   107 - (void) setGrowlMenuEnabled:(BOOL)state {
   108 	NSString *growlMenuPath = [[NSBundle mainBundle] bundlePath];
   109 	[preferences setStartAtLogin:growlMenuPath enabled:state];
   110 	[preferences setBool:state forKey:GrowlMenuExtraKey];
   111 }
   112 
   113 - (void) applicationWillTerminate:(NSNotification *)aNotification {
   114 #pragma unused(aNotification)
   115 	[self release];
   116 }
   117 
   118 - (void) dealloc {
   119 //	[[NSStatusBar systemStatusBar] removeStatusItem:statusItem];
   120 //	[statusItem            release];
   121 	[clawImage             release];
   122 	[clawHighlightImage    release];
   123 	[squelchImage          release];
   124 	[super dealloc];
   125 }
   126 
   127 - (void) shutdown:(id)sender {
   128 	[self setGrowlMenuEnabled:NO];
   129 	[NSApp terminate:sender];
   130 }
   131 
   132 - (void) reloadPrefs:(NSNotification *)notification {
   133 	// ignore notifications which are sent by ourselves
   134 	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
   135 	NSNumber *pidValue = [[notification userInfo] objectForKey:@"pid"];
   136 	if (!pidValue || [pidValue intValue] != pid)
   137 		[self setImage];
   138 	[pool release];
   139 }
   140 
   141 - (void) openGrowlPreferences:(id)sender {
   142 #pragma unused(sender)
   143 	NSString *prefPane = [[GrowlPathUtilities growlPrefPaneBundle] bundlePath];
   144 	[[NSWorkspace sharedWorkspace] openFile:prefPane];
   145 }
   146 
   147 - (void) defaultDisplay:(id)sender {
   148 	NSString *pluginName = [sender title];
   149 	[preferences setDefaultDisplayPluginName:pluginName];
   150 
   151 	CFStringRef description = CFStringCreateWithFormat(kCFAllocatorDefault, NULL, (CFStringRef)kDefaultDisplayChanged, pluginName);
   152 	const NSString *keys[5] = { GROWL_APP_NAME, GROWL_NOTIFICATION_NAME, GROWL_NOTIFICATION_TITLE, GROWL_NOTIFICATION_DESCRIPTION, GROWL_DISPLAY_PLUGIN };
   153 	const CFStringRef values[5] = { CFSTR("GrowlMenu"), CFSTR("Display changed"), CFSTR("Display changed"), description, (CFStringRef)pluginName };
   154 	CFDictionaryRef feedbackDictionary = CFDictionaryCreate(kCFAllocatorDefault, (const void **)keys, (const void **)values, 5, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
   155 	CFRelease(description);
   156 	[GrowlApplicationBridge notifyWithDictionary:(NSDictionary *)feedbackDictionary];
   157 	CFRelease(feedbackDictionary);
   158 }
   159 
   160 - (void) stopGrowl:(id)sender {
   161 #pragma unused(sender)
   162 	//If Growl is running, we should stop it.
   163 	if ([preferences isGrowlRunning])
   164 		[preferences setGrowlRunning:NO noMatterWhat:NO];
   165 }
   166 
   167 - (void) startGrowl:(id)sender {
   168 #pragma unused(sender)
   169 	if (![preferences isGrowlRunning]) {
   170 		//If Growl isn't running, we should start it.
   171 		[preferences setGrowlRunning:YES noMatterWhat:NO];
   172 	} else {
   173 		//If Growl is running, we should restart it.
   174 		//Actually, we should HUP it, but we don't.
   175 		[preferences setGrowlRunning:NO noMatterWhat:NO];
   176 		[preferences setGrowlRunning:YES noMatterWhat:YES];
   177 	}
   178 }
   179 
   180 - (void) squelchMode:(id)sender {
   181 #pragma unused(sender)
   182 	BOOL squelchMode = ![preferences squelchMode];
   183 	[preferences setSquelchMode:squelchMode];
   184 	[self setImage];
   185 }
   186 
   187 - (void) stickyWhenIdle:(id)sender {
   188 #pragma unused(sender)
   189 	BOOL idleModeState = ![preferences stickyWhenAway];
   190 	[preferences setStickyWhenAway:idleModeState];
   191 }
   192 
   193 - (void) setImage {
   194 	if ([preferences squelchMode]) {
   195 		[statusItem setImage:squelchImage];
   196 	} else {
   197 		[statusItem setImage:clawImage];
   198 		[statusItem setAlternateImage:clawHighlightImage];
   199 	}
   200 }
   201 
   202 - (NSMenu *) createMenu {
   203 	NSZone *menuZone = [NSMenu menuZone];
   204 	NSMenu *m = [[NSMenu allocWithZone:menuZone] init];
   205 
   206 	NSMenuItem *tempMenuItem;
   207 
   208 	tempMenuItem = (NSMenuItem *)[m addItemWithTitle:kStartGrowl action:@selector(startGrowl:) keyEquivalent:@""];
   209 	[tempMenuItem setTarget:self];
   210 	[tempMenuItem setTag:1];
   211 
   212 	if ([preferences isGrowlRunning]) {
   213 		[tempMenuItem setTitle:kRestartGrowl];
   214 		[tempMenuItem setToolTip:kRestartGrowlTooltip];
   215 	} else {
   216 		[tempMenuItem setToolTip:kStartGrowlTooltip];
   217 	}
   218 
   219 	tempMenuItem = (NSMenuItem *)[m addItemWithTitle:kStopGrowl action:@selector(stopGrowl:) keyEquivalent:@""];
   220 	[tempMenuItem setTag:2];
   221 	[tempMenuItem setTarget:self];
   222 	[tempMenuItem setToolTip:kStopGrowlTooltip];
   223 
   224 	[m addItem:[NSMenuItem separatorItem]];
   225 
   226 	/*
   227 	 //Squelch mode is "log-only" mode... but logging was removed from Growl 1.1.
   228 	tempMenuItem = (NSMenuItem *)[m addItemWithTitle:kSquelchMode action:@selector(squelchMode:) keyEquivalent:@""];
   229 	[tempMenuItem setTarget:self];
   230 	[tempMenuItem setTag:4];
   231 	[tempMenuItem setToolTip:kSquelchModeTooltip];
   232 	 */
   233 	
   234 	tempMenuItem = (NSMenuItem *)[m addItemWithTitle:kStickyWhenAwayMenu action:@selector(stickyWhenIdle:) keyEquivalent:@""];
   235 	[tempMenuItem setTarget:self];
   236 	[tempMenuItem setTag:6];
   237 	[tempMenuItem setToolTip:kStickyWhenAwayMenuTooltip];
   238 
   239 	/*NSMenu *displays = [[NSMenu allocWithZone:menuZone] init];
   240 	NSString *name;
   241 	NSEnumerator *displayEnumerator = [[[GrowlPluginController sharedController] displayPlugins] objectEnumerator];
   242 	while ((name = [displayEnumerator nextObject])) {
   243 		tempMenuItem = (NSMenuItem *)[displays addItemWithTitle:name action:@selector(defaultDisplay:) keyEquivalent:@""];
   244 		[tempMenuItem setTarget:self];
   245 		[tempMenuItem setTag:3];
   246 	}
   247 	tempMenuItem = (NSMenuItem *)[m addItemWithTitle:kDefaultDisplay action:NULL keyEquivalent:@""];
   248 	[tempMenuItem setTarget:self];
   249 	[tempMenuItem setSubmenu:displays];
   250 	[displays release];*/
   251 	[m addItem:[NSMenuItem separatorItem]];
   252 
   253 	tempMenuItem = (NSMenuItem *)[m addItemWithTitle:kStopGrowlMenu action:@selector(shutdown:) keyEquivalent:@""];
   254 	[tempMenuItem setTag:5];
   255 	[tempMenuItem setTarget:self];
   256 	[tempMenuItem setToolTip:kStopGrowlMenuTooltip];
   257 
   258 	tempMenuItem = (NSMenuItem *)[m addItemWithTitle:kOpenGrowlPreferences action:@selector(openGrowlPreferences:) keyEquivalent:@""];
   259 	[tempMenuItem setTarget:self];
   260 	[tempMenuItem setToolTip:kOpenGrowlPreferencesTooltip];
   261 
   262 	return m;
   263 }
   264 
   265 - (BOOL) validateMenuItem:(NSMenuItem *)item {
   266 	switch ([item tag]) {
   267 		case 1:
   268 			if ([preferences isGrowlRunning]) {
   269 				[item setTitle:kRestartGrowl];
   270 				[item setToolTip:kRestartGrowlTooltip];
   271 			} else {
   272 				[item setTitle:kStartGrowl];
   273 				[item setToolTip:kStartGrowlTooltip];
   274 			}
   275 			break;
   276 		case 2:
   277 			return [preferences isGrowlRunning];
   278 		case 3:
   279 			[item setState:[[item title] isEqualToString:[preferences defaultDisplayPluginName]]];
   280 			break;
   281 		case 4:
   282 			[item setState:[preferences squelchMode]];
   283 			break;
   284 		case 6:
   285 			[item setState:[preferences stickyWhenAway]];
   286 			break;
   287 	}
   288 	return YES;
   289 }
   290 
   291 @end