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