Extras/GrowlMail/GrowlMailNotifier.m
author Peter Hosey <hg@boredzo.org>
Sat Jun 06 21:30:17 2009 -0700 (2009-06-06)
changeset 4210 c36ddc8e6b22
parent 4209 f8a902d33769
child 4277 61dc661bdb56
permissions -rw-r--r--
Make GrowlMail's main suicide-pill method into a public function. The core method lives on, and the function calls it through the shiny new singleton-instance static variable that I added in the previous commit.
hg@4209
     1
//
hg@4209
     2
//  GrowlMailNotifier.m
hg@4209
     3
//  GrowlMail
hg@4209
     4
//
hg@4209
     5
//  Created by Peter Hosey on 2009-05-10.
hg@4209
     6
//  Copyright 2009 Peter Hosey. All rights reserved.
hg@4209
     7
//
hg@4209
     8
hg@4209
     9
#import "GrowlMailNotifier.h"
hg@4209
    10
#import "GrowlMail.h"
hg@4209
    11
#import "Message+GrowlMail.h"
hg@4209
    12
#import <objc/objc-runtime.h>
hg@4209
    13
hg@4209
    14
#import "MessageFrameworkHeaders.h"
hg@4209
    15
hg@4209
    16
#define AUTO_THRESHOLD	10
hg@4209
    17
hg@4209
    18
#define	MAX_NOTIFICATION_THREADS	5
hg@4209
    19
hg@4209
    20
static int activeNotificationThreads = 0;
hg@4209
    21
hg@4209
    22
static int messageCopies = 0;
hg@4209
    23
hg@4209
    24
static GrowlMailNotifier *sharedNotifier = nil;
hg@4209
    25
hg@4209
    26
static BOOL notifierEnabled = YES;
hg@4209
    27
hg@4209
    28
@implementation GrowlMailNotifier
hg@4209
    29
hg@4209
    30
#pragma mark Panic buttons
hg@4209
    31
hg@4209
    32
//The purpose of this method is to shut down GrowlMail completely: we should not be notified of any messages, nor notify the user of any messages, after this message is called.
hg@4209
    33
- (void) shutDownGrowlMail {
hg@4209
    34
	[[NSNotificationCenter defaultCenter] removeObserver:self];
hg@4209
    35
	[GrowlApplicationBridge setGrowlDelegate:nil];
hg@4209
    36
}
hg@4209
    37
hg@4209
    38
#pragma mark The circle of life
hg@4209
    39
hg@4209
    40
+ (id) sharedNotifier {
hg@4209
    41
	if (!sharedNotifier) {
hg@4209
    42
		//-init and -dealloc will each assign to sharedNotifier.
hg@4209
    43
		[[[GrowlMailNotifier alloc] init] autorelease];
hg@4209
    44
	}
hg@4209
    45
	return sharedNotifier;
hg@4209
    46
}
hg@4209
    47
hg@4209
    48
- (id) init {
hg@4209
    49
	if (sharedNotifier) {
hg@4209
    50
		[self release];
hg@4209
    51
		return [sharedNotifier retain];
hg@4209
    52
	}
hg@4209
    53
hg@4209
    54
	//No shared notifier yet; someone is trying to create one. If we previously disabled ourselves, abort this attempt.
hg@4209
    55
	if (!notifierEnabled) {
hg@4209
    56
		[self release];
hg@4209
    57
		return nil;
hg@4209
    58
	}
hg@4209
    59
hg@4209
    60
	if((self = [super init])) {
hg@4209
    61
		NSNumber *automatic = [NSNumber numberWithInt:GrowlMailSummaryModeAutomatic];
hg@4209
    62
		NSDictionary *defaultsDictionary = [NSDictionary dictionaryWithObjectsAndKeys:
hg@4209
    63
			@"(%account) %sender",         @"GMTitleFormat",
hg@4209
    64
			@"%subject\n%body",            @"GMDescriptionFormat",
hg@4209
    65
			automatic,                     @"GMSummaryMode",
hg@4209
    66
			[NSNumber numberWithBool:YES], @"GMEnableGrowlMailBundle",
hg@4209
    67
			[NSNumber numberWithBool:NO],  @"GMInboxOnly",
hg@4209
    68
			nil];
hg@4209
    69
		[[NSUserDefaults standardUserDefaults] registerDefaults:defaultsDictionary];
hg@4209
    70
hg@4209
    71
		[GrowlApplicationBridge setGrowlDelegate:self];
hg@4209
    72
hg@4209
    73
		[[NSNotificationCenter defaultCenter] addObserver:self
hg@4209
    74
												 selector:@selector(messageStoreDidAddMessages:)
hg@4209
    75
													 name:@"MessageStoreMessagesAdded_inMainThread_"
hg@4209
    76
												   object:nil];
hg@4209
    77
		[[NSNotificationCenter defaultCenter] addObserver:self
hg@4209
    78
												 selector:@selector(monitoredActivityStarted:)
hg@4209
    79
													 name:@"MonitoredActivityStarted_inMainThread_"
hg@4209
    80
												   object:nil];
hg@4209
    81
		[[NSNotificationCenter defaultCenter] addObserver:self
hg@4209
    82
												 selector:@selector(monitoredActivityEnded:)
hg@4209
    83
													 name:@"MonitoredActivityEnded_inMainThread_"
hg@4209
    84
												   object:nil];
hg@4209
    85
		
hg@4209
    86
#ifdef GROWL_MAIL_DEBUG
hg@4209
    87
		/*
hg@4209
    88
		[[NSNotificationCenter defaultCenter] addObserver:self
hg@4209
    89
												 selector:@selector(showAllNotifications:)
hg@4209
    90
													 name:nil object:nil];
hg@4209
    91
		 */
hg@4209
    92
#endif
hg@4209
    93
		sharedNotifier = self;
hg@4209
    94
	}
hg@4209
    95
	return self;
hg@4209
    96
}
hg@4209
    97
hg@4209
    98
- (void) dealloc {
hg@4209
    99
	[self shutDownGrowlMail];
hg@4209
   100
	[[NSNotificationCenter defaultCenter] removeObserver:self];
hg@4209
   101
	sharedNotifier = nil;
hg@4209
   102
hg@4209
   103
	[super dealloc];
hg@4209
   104
}
hg@4209
   105
hg@4209
   106
#pragma mark GrowlApplicationBridge delegate methods
hg@4209
   107
hg@4209
   108
- (NSString *) applicationNameForGrowl {
hg@4209
   109
	return @"GrowlMail";
hg@4209
   110
}
hg@4209
   111
hg@4209
   112
- (NSImage *) applicationIconForGrowl {
hg@4209
   113
	return [NSImage imageNamed:@"NSApplicationIcon"];
hg@4209
   114
}
hg@4209
   115
hg@4209
   116
- (void) growlNotificationWasClicked:(NSString *)clickContext {
hg@4209
   117
	if ([clickContext length]) {
hg@4209
   118
		//Make sure we have all the methods we need.
hg@4209
   119
		if (!class_getClassMethod([Library class], @selector(messageWithMessageID:)))
hg@4210
   120
			GMShutDownGrowlMailAndWarn(@"Library does not respond to +messageWithMessageID:");
hg@4209
   121
		if (!class_getInstanceMethod([SingleMessageViewer class], @selector(initForViewingMessage:showAllHeaders:viewingState:fromDefaults:)))
hg@4210
   122
			GMShutDownGrowlMailAndWarn(@"SingleMessageViewer does not respond to -initForViewingMessage:showAllHeaders:viewingState:fromDefaults:");
hg@4209
   123
		if (!class_getInstanceMethod([SingleMessageViewer class], @selector(showAndMakeKey:)))
hg@4210
   124
			GMShutDownGrowlMailAndWarn(@"SingleMessageViewer does not respond to -showAndMakeKey:");
hg@4209
   125
hg@4209
   126
		Message *message = [Library messageWithMessageID:clickContext];
hg@4209
   127
		MessageViewingState *viewingState = [[MessageViewingState alloc] init];
hg@4209
   128
		SingleMessageViewer *messageViewer = [[SingleMessageViewer alloc] initForViewingMessage:message showAllHeaders:NO viewingState:viewingState fromDefaults:NO];
hg@4209
   129
		[viewingState release];
hg@4209
   130
		[messageViewer showAndMakeKey:YES];
hg@4209
   131
		[messageViewer release];
hg@4209
   132
		[Library markMessageAsViewed:message];
hg@4209
   133
	}
hg@4209
   134
	[NSApp activateIgnoringOtherApps:YES];
hg@4209
   135
}
hg@4209
   136
hg@4209
   137
- (NSDictionary *) registrationDictionaryForGrowl {
hg@4209
   138
	// Register our ticket with Growl
hg@4209
   139
	NSArray *allowedNotifications = [NSArray arrayWithObjects:
hg@4209
   140
		NEW_MAIL_NOTIFICATION,
hg@4209
   141
		NEW_JUNK_MAIL_NOTIFICATION,
hg@4209
   142
		NEW_NOTE_NOTIFICATION,
hg@4209
   143
		nil];
hg@4209
   144
	NSDictionary *humanReadableNames = [NSDictionary dictionaryWithObjectsAndKeys:
hg@4209
   145
										NSLocalizedStringFromTableInBundle(@"New mail", nil, GMGetGrowlMailBundle(), ""), NEW_MAIL_NOTIFICATION,
hg@4209
   146
										NSLocalizedStringFromTableInBundle(@"New junk mail", nil, GMGetGrowlMailBundle(), ""), NEW_JUNK_MAIL_NOTIFICATION,
hg@4209
   147
										NSLocalizedStringFromTableInBundle(@"New note", nil, GMGetGrowlMailBundle(), ""), NEW_NOTE_NOTIFICATION,
hg@4209
   148
										nil];
hg@4209
   149
	NSArray *defaultNotifications = [NSArray arrayWithObject:NEW_MAIL_NOTIFICATION];
hg@4209
   150
hg@4209
   151
	NSDictionary *ticket = [NSDictionary dictionaryWithObjectsAndKeys:
hg@4209
   152
		allowedNotifications, GROWL_NOTIFICATIONS_ALL,
hg@4209
   153
		defaultNotifications, GROWL_NOTIFICATIONS_DEFAULT,
hg@4209
   154
		humanReadableNames, GROWL_NOTIFICATIONS_HUMAN_READABLE_NAMES,
hg@4209
   155
		nil];
hg@4209
   156
#ifdef GROWL_MAIL_DEBUG
hg@4209
   157
	NSLog(@"%s: Returning Growl dictionary %@", __PRETTY_FUNCTION__, ticket);
hg@4209
   158
#endif
hg@4209
   159
hg@4209
   160
	return ticket;
hg@4209
   161
}
hg@4209
   162
hg@4209
   163
#pragma mark Mail notification handlers
hg@4209
   164
hg@4209
   165
+ (void)showNotificationForMessage:(Message *)message
hg@4209
   166
{
hg@4209
   167
	if (activeNotificationThreads < MAX_NOTIFICATION_THREADS) { 
hg@4209
   168
		activeNotificationThreads++;
hg@4209
   169
		
hg@4209
   170
		/* Why use a thread?
hg@4209
   171
		 *
hg@4209
   172
		 * If we want the message body, it may not be immediately available.
hg@4209
   173
		 * It can be retrieved without blocking if it's available, which we initially try.
hg@4209
   174
		 * However, if we really, really want it, we may have to request it in a blocking fashion:
hg@4209
   175
		 *		for example, if the user doesn't read the message and doesn't have Mail set to download it automatically,
hg@4209
   176
		 *		we'll never get it without blocking.
hg@4209
   177
		 *
hg@4209
   178
		 * Blocking the main thread is, of course, out of the question.
hg@4209
   179
		 *
hg@4209
   180
		 * We're making some assumptions about Mail's internals, but the fact that notifications are posted on auxiliary threads
hg@4209
   181
		 * and then again with a _inMainThread_ suffix on the main thread indicates that threads are being used for mail access elsewhere.
hg@4209
   182
		 */
hg@4209
   183
		[NSThread detachNewThreadSelector:@selector(GMShowNotificationPart1)
hg@4209
   184
								 toTarget:message
hg@4209
   185
							   withObject:nil];
hg@4209
   186
	} else {
hg@4209
   187
		[self performSelector:@selector(showNotificationForMessage:)
hg@4209
   188
				   withObject:message
hg@4209
   189
				   afterDelay:2.0];
hg@4209
   190
	}
hg@4209
   191
}
hg@4209
   192
hg@4209
   193
- (void)didFinishNotificationForMessage:(Message *)message
hg@4209
   194
{
hg@4209
   195
#pragma unused(message)
hg@4209
   196
	activeNotificationThreads--;	
hg@4209
   197
}
hg@4209
   198
hg@4209
   199
- (void)messageStoreDidAddMessages:(NSNotification *)notification {
hg@4209
   200
	if (![self isEnabled]) return;
hg@4209
   201
hg@4209
   202
#ifdef GROWL_MAIL_DEBUG
hg@4209
   203
	NSLog(@"%s called", __PRETTY_FUNCTION__);
hg@4209
   204
#endif
hg@4209
   205
	
hg@4209
   206
	if (messageCopies) {
hg@4209
   207
#ifdef GROWL_MAIL_DEBUG
hg@4209
   208
		NSLog(@"Ignoring because %i message copies are in process", messageCopies);
hg@4209
   209
#endif
hg@4209
   210
		return;
hg@4209
   211
	}
hg@4209
   212
hg@4209
   213
	Library *store = [notification object];
hg@4209
   214
	if (!store) {
hg@4210
   215
		GMShutDownGrowlMailAndWarn([NSString stringWithFormat:@"'%@' notification has no object", [notification name]]);
hg@4209
   216
	}
hg@4209
   217
	if ([store isKindOfClass:[LibraryStore class]]) {
hg@4209
   218
		//As of Tiger, this is normal; this notification is posted a couple times (perhaps once per inbox) with a LibraryStore object.
hg@4209
   219
		//This is not the notification we're looking for; we don't need to see its papers. We will move along now.
hg@4209
   220
		return;
hg@4209
   221
	}
hg@4209
   222
	//We don't actually use the store. We only retrieve it and examine it at all because we know we don't want the one with a LibraryStore as its object.
hg@4209
   223
	//The rest of the handler should be able to work just fine without proving anything else about the store, since it doesn't use the store.
hg@4209
   224
hg@4209
   225
	NSDictionary *userInfo = [notification userInfo];
hg@4210
   226
	if (!userInfo) GMShutDownGrowlMailAndWarn(@"Notification had no userInfo");
hg@4209
   227
hg@4209
   228
	NSArray *mailboxes = [userInfo objectForKey:@"mailboxes"];
hg@4209
   229
#ifdef GROWL_MAIL_DEBUG
hg@4209
   230
	NSLog(@"%s: Adding messages to mailboxes %@", __PRETTY_FUNCTION__, mailboxes);
hg@4209
   231
#endif
hg@4209
   232
hg@4209
   233
	//As of Tiger, it's normal for about half of these notifications to not have any mailboxes. We simply ignore the notification in this case.
hg@4209
   234
	if (!(mailboxes && [mailboxes count])) return;
hg@4209
   235
hg@4209
   236
	//Ignore a notification if we're ignoring all of the mailboxes involved.
hg@4209
   237
	Class MailAccount_class = [MailAccount class];
hg@4209
   238
	if (!class_getClassMethod(MailAccount_class, @selector(draftMailboxUids)))
hg@4210
   239
		GMShutDownGrowlMailAndWarn(@"MailAccount does not respond to +draftMailboxUids");
hg@4209
   240
	if (!class_getClassMethod(MailAccount_class, @selector(outboxMailboxUids)))
hg@4210
   241
		GMShutDownGrowlMailAndWarn(@"MailAccount does not respond to +outboxMailboxUids");
hg@4209
   242
	if (!class_getClassMethod(MailAccount_class, @selector(sentMessagesMailboxUids)))
hg@4210
   243
		GMShutDownGrowlMailAndWarn(@"MailAccount does not respond to +sentMessagesMailboxUids");
hg@4209
   244
	if (!class_getClassMethod(MailAccount_class, @selector(trashMailboxUids)))
hg@4210
   245
		GMShutDownGrowlMailAndWarn(@"MailAccount does not respond to +trashMailboxUids");
hg@4209
   246
	//We need this method to support the Inbox Only preference.
hg@4209
   247
	if (!class_getClassMethod(MailAccount_class, @selector(inboxMailboxUids)))
hg@4210
   248
		GMShutDownGrowlMailAndWarn(@"MailAccount does not respond to +inboxMailboxUids");
hg@4209
   249
hg@4209
   250
	//Ignore messages being written.
hg@4209
   251
	NSMutableSet *mailboxesToIgnore = [NSMutableSet setWithArray:[MailAccount draftMailboxUids]];
hg@4209
   252
	//Ignore messages being sent.
hg@4209
   253
	[mailboxesToIgnore unionSet:[NSSet setWithArray:[MailAccount outboxMailboxUids]]];
hg@4209
   254
	[mailboxesToIgnore unionSet:[NSSet setWithArray:[MailAccount sentMessagesMailboxUids]]];
hg@4209
   255
	//Ignore messages being deleted.
hg@4209
   256
	[mailboxesToIgnore unionSet:[NSSet setWithArray:[MailAccount trashMailboxUids]]];
hg@4209
   257
hg@4209
   258
	NSSet *mailboxesSet = [NSSet setWithArray:mailboxes];
hg@4209
   259
	NSMutableSet *mailboxesNotIgnored = [[mailboxesSet mutableCopy] autorelease];
hg@4209
   260
	[mailboxesNotIgnored minusSet:mailboxesToIgnore];
hg@4209
   261
	if ([mailboxesNotIgnored count] == 0U)
hg@4209
   262
		return;
hg@4209
   263
hg@4209
   264
	NSArray *messages = [userInfo objectForKey:@"messages"];
hg@4210
   265
	if (!messages) GMShutDownGrowlMailAndWarn(@"Notification's userInfo has no messages");
hg@4209
   266
	
hg@4209
   267
#ifdef GROWL_MAIL_DEBUG
hg@4209
   268
	NSLog(@"%s: Mail added messages [1] to mailboxes [2].\n[1]: %@\n[2]: %@", __PRETTY_FUNCTION__, messages, mailboxes);
hg@4209
   269
#endif
hg@4209
   270
	
hg@4209
   271
	unsigned count = [messages count];
hg@4209
   272
hg@4209
   273
	int summaryMode = [self summaryMode];
hg@4209
   274
	if (summaryMode == GrowlMailSummaryModeAutomatic) {
hg@4209
   275
		if (count >= AUTO_THRESHOLD)
hg@4209
   276
			summaryMode = GrowlMailSummaryModeAlways;
hg@4209
   277
		else
hg@4209
   278
			summaryMode = GrowlMailSummaryModeDisabled;
hg@4209
   279
	}
hg@4209
   280
hg@4209
   281
#ifdef GROWL_MAIL_DEBUG
hg@4209
   282
	NSLog(@"Got %i new messages. Summary mode was %i and is now %i", count, [self summaryMode], summaryMode);
hg@4209
   283
#endif
hg@4209
   284
hg@4209
   285
	Class Message_class = [Message class];
hg@4209
   286
hg@4209
   287
	switch (summaryMode) {
hg@4209
   288
		default:
hg@4209
   289
		case GrowlMailSummaryModeDisabled: {
hg@4209
   290
			NSEnumerator *messagesEnum = [messages objectEnumerator];
hg@4209
   291
			Message *message;
hg@4209
   292
			while ((message = [messagesEnum nextObject])) {
hg@4209
   293
				MailboxUid *mailbox = [message mailbox];
hg@4209
   294
				//If this mailbox is not an inbox, and we only care about inboxes, then skip this message.
hg@4209
   295
				if ([self inboxOnly] && ![[MailAccount inboxMailboxUids] containsObject:mailbox])
hg@4209
   296
					continue;
hg@4209
   297
hg@4209
   298
				MailAccount *account = [mailbox account];
hg@4209
   299
				if (![self isAccountEnabled:account])
hg@4209
   300
					continue;
hg@4209
   301
hg@4209
   302
				if (![message isKindOfClass:Message_class])
hg@4210
   303
					GMShutDownGrowlMailAndWarn([NSString stringWithFormat:@"Message in notification was not a Message; it is %@", message]);
hg@4209
   304
hg@4209
   305
				if (![message respondsToSelector:@selector(isRead)] || ![message isRead]) {
hg@4209
   306
					/* Don't display read messages */
hg@4209
   307
					[[self class] showNotificationForMessage:message];
hg@4209
   308
				}
hg@4209
   309
			}
hg@4209
   310
			break;
hg@4209
   311
		}
hg@4209
   312
		case GrowlMailSummaryModeAlways: {
hg@4209
   313
			if (!class_getClassMethod([MailAccount class], @selector(mailAccounts)))
hg@4210
   314
				GMShutDownGrowlMailAndWarn(@"MailAccount does not respond to +mailAccounts");
hg@4209
   315
			if (!class_getInstanceMethod(Message_class, @selector(mailbox)))
hg@4210
   316
				GMShutDownGrowlMailAndWarn(@"Message does not respond to -mailbox");
hg@4209
   317
hg@4209
   318
			NSArray *accounts = [MailAccount mailAccounts];
hg@4209
   319
			unsigned accountsCount = [accounts count];
hg@4209
   320
			NSCountedSet *accountSummary = [NSCountedSet setWithCapacity:accountsCount];
hg@4209
   321
			NSCountedSet *accountJunkSummary = [NSCountedSet setWithCapacity:accountsCount];
hg@4209
   322
			NSEnumerator *messagesEnum = [messages objectEnumerator];
hg@4209
   323
			NSArray *junkMailboxUids = [MailAccount junkMailboxUids];
hg@4209
   324
			Message *message;
hg@4209
   325
			while ((message = [messagesEnum nextObject])) {
hg@4209
   326
				MailboxUid *mailbox = [message mailbox];
hg@4209
   327
				//If this mailbox is not an inbox, and we only care about inboxes, then skip this message.
hg@4209
   328
				if ([self inboxOnly] && ![[MailAccount inboxMailboxUids] containsObject:mailbox])
hg@4209
   329
					continue;
hg@4209
   330
hg@4209
   331
				MailAccount *account = [mailbox account];
hg@4209
   332
				if (![self isAccountEnabled:account])
hg@4209
   333
					continue;
hg@4209
   334
hg@4209
   335
				if (([message isJunk]) || [junkMailboxUids containsObject:[message mailbox]])
hg@4209
   336
					[accountJunkSummary addObject:account];
hg@4209
   337
				else
hg@4209
   338
					[accountSummary addObject:account];
hg@4209
   339
			}
hg@4209
   340
			NSString *title = NSLocalizedStringFromTableInBundle(@"New mail", NULL, GMGetGrowlMailBundle(), "");
hg@4209
   341
			NSString *titleJunk = NSLocalizedStringFromTableInBundle(@"New junk mail", NULL, GMGetGrowlMailBundle(), "");
hg@4209
   342
			NSString *description;
hg@4209
   343
hg@4209
   344
			MailAccount *account;
hg@4209
   345
hg@4209
   346
			NSEnumerator *accountSummaryEnum = [accountSummary objectEnumerator];
hg@4209
   347
			while ((account = [accountSummaryEnum nextObject])) {
hg@4209
   348
				if (![self isAccountEnabled:account])
hg@4209
   349
					continue;
hg@4209
   350
hg@4209
   351
				unsigned summaryCount = [accountSummary countForObject:account];
hg@4209
   352
				if (summaryCount) {
hg@4209
   353
					if (summaryCount == 1) {
hg@4209
   354
						description = [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"%@ \n 1 new mail", NULL, GMGetGrowlMailBundle(), "%@ is an account name"), [account displayName]];
hg@4209
   355
					} else {
hg@4209
   356
						description = [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"%@ \n %u new mails", NULL, GMGetGrowlMailBundle(), "%@ is an account name; %u becomes a number"), [account displayName], summaryCount];
hg@4209
   357
					}
hg@4209
   358
					[GrowlApplicationBridge notifyWithTitle:title
hg@4209
   359
												description:description
hg@4209
   360
										   notificationName:NEW_MAIL_NOTIFICATION
hg@4209
   361
												   iconData:nil
hg@4209
   362
												   priority:0
hg@4209
   363
												   isSticky:NO
hg@4209
   364
											   clickContext:@""];	// non-nil click context
hg@4209
   365
				}
hg@4209
   366
			}
hg@4209
   367
hg@4209
   368
			NSEnumerator *accountJunkSummaryEnum = [accountJunkSummary objectEnumerator];
hg@4209
   369
			while ((account = [accountJunkSummaryEnum nextObject])) {
hg@4209
   370
				if (![self isAccountEnabled:account])
hg@4209
   371
					continue;
hg@4209
   372
hg@4209
   373
				unsigned summaryCount = [accountJunkSummary countForObject:account];
hg@4209
   374
				if (summaryCount) {
hg@4209
   375
					if (summaryCount == 1) {
hg@4209
   376
						description = [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"%@ \n 1 new mail", NULL, GMGetGrowlMailBundle(), "%@ is an account name"), [account displayName]];
hg@4209
   377
					} else {
hg@4209
   378
						description = [NSString stringWithFormat:NSLocalizedStringFromTableInBundle(@"%@ \n %u new mails", NULL, GMGetGrowlMailBundle(), "%@ is an account name; %u becomes a number"), [account displayName], summaryCount];
hg@4209
   379
					}					
hg@4209
   380
					[GrowlApplicationBridge notifyWithTitle:titleJunk
hg@4209
   381
												description:description
hg@4209
   382
										   notificationName:NEW_JUNK_MAIL_NOTIFICATION
hg@4209
   383
												   iconData:nil
hg@4209
   384
												   priority:0
hg@4209
   385
												   isSticky:NO
hg@4209
   386
											   clickContext:@""];	// non-nil click context
hg@4209
   387
				}
hg@4209
   388
			}
hg@4209
   389
			break;
hg@4209
   390
		}
hg@4209
   391
	}
hg@4209
   392
}
hg@4209
   393
hg@4209
   394
- (void)showAllNotifications:(NSNotification *)notification
hg@4209
   395
{
hg@4209
   396
	if (([[notification name] rangeOfString:@"NSWindow"].location == NSNotFound) &&
hg@4209
   397
		([[notification name] rangeOfString:@"NSMouse"].location == NSNotFound) &&
hg@4209
   398
		([[notification name] rangeOfString:@"_NSThread"].location == NSNotFound)) {
hg@4209
   399
		NSLog(@"%@", notification);
hg@4209
   400
	}
hg@4209
   401
}
hg@4209
   402
hg@4209
   403
- (void)monitoredActivityStarted:(NSNotification *)notification
hg@4209
   404
{
hg@4209
   405
	if ([[[notification object] description] isEqualToString:@"Copying messages"]) {
hg@4209
   406
		messageCopies++;
hg@4209
   407
#ifdef GROWL_MAIL_DEBUG
hg@4209
   408
		NSLog(@"Copying a message: messageCopies is now %i", messageCopies);
hg@4209
   409
#endif
hg@4209
   410
		if (messageCopies <= 0)
hg@4210
   411
			GMShutDownGrowlMailAndWarn(@"Number of message-copying operations overflowed. How on earth did you accomplish starting more than 2 billion copying operations at a time?!");
hg@4209
   412
	}
hg@4209
   413
}
hg@4209
   414
hg@4209
   415
- (void)monitoredActivityEnded:(NSNotification *)notification
hg@4209
   416
{
hg@4209
   417
	if ([[[notification object] description] isEqualToString:@"Copying messages"]) {
hg@4209
   418
		if (messageCopies <= 0)
hg@4210
   419
			GMShutDownGrowlMailAndWarn(@"Number of message-copying operations went below 0. It is not possible to have a negative number of copying operations!");
hg@4209
   420
		messageCopies--;
hg@4209
   421
#ifdef GROWL_MAIL_DEBUG
hg@4209
   422
		NSLog(@"Finished copying a message: messageCopies is now %i", messageCopies);
hg@4209
   423
#endif
hg@4209
   424
	}
hg@4209
   425
}
hg@4209
   426
hg@4209
   427
#pragma mark Preferences
hg@4209
   428
hg@4209
   429
- (BOOL) isAccountEnabled:(MailAccount *)account {
hg@4209
   430
	BOOL isEnabled = YES;
hg@4209
   431
	NSDictionary *accountSettings = [[NSUserDefaults standardUserDefaults] objectForKey:@"GMAccounts"];
hg@4209
   432
	if (accountSettings) {
hg@4209
   433
		NSNumber *value = [accountSettings objectForKey:[account path]];
hg@4209
   434
		if (value)
hg@4209
   435
			isEnabled = [value boolValue];
hg@4209
   436
	}
hg@4209
   437
	return isEnabled;
hg@4209
   438
}
hg@4209
   439
hg@4209
   440
- (void) setAccount:(MailAccount *)account enabled:(BOOL)yesOrNo {
hg@4209
   441
	NSDictionary *accountSettings = [[NSUserDefaults standardUserDefaults] objectForKey:@"GMAccounts"];
hg@4209
   442
	NSMutableDictionary *newSettings = [[accountSettings mutableCopy] autorelease];
hg@4209
   443
	if (!newSettings)
hg@4209
   444
		newSettings = [NSMutableDictionary dictionaryWithCapacity:1U];
hg@4209
   445
	[newSettings setObject:[NSNumber numberWithBool:yesOrNo] forKey:[account path]];
hg@4209
   446
	[[NSUserDefaults standardUserDefaults] setObject:newSettings forKey:@"GMAccounts"];
hg@4209
   447
}
hg@4209
   448
hg@4209
   449
#pragma mark Accessors
hg@4209
   450
hg@4209
   451
- (BOOL) isEnabled {
hg@4209
   452
	NSNumber *enabledNum = [[NSUserDefaults standardUserDefaults] objectForKey:@"GMEnableGrowlMailBundle"];
hg@4209
   453
	return enabledNum ? [enabledNum boolValue] : YES;
hg@4209
   454
}
hg@4209
   455
- (GrowlMailSummaryMode) summaryMode {
hg@4209
   456
	NSNumber *summaryModeNum = [[NSUserDefaults standardUserDefaults] objectForKey:@"GMSummaryMode"];
hg@4209
   457
	return summaryModeNum ? [summaryModeNum intValue] : GrowlMailSummaryModeAutomatic;
hg@4209
   458
}
hg@4209
   459
- (BOOL) inboxOnly {
hg@4209
   460
	NSNumber *inboxOnlyNum = [[NSUserDefaults standardUserDefaults] objectForKey:@"GMInboxOnly"];
hg@4209
   461
	return inboxOnlyNum ? [inboxOnlyNum boolValue] : YES;
hg@4209
   462
}
hg@4209
   463
hg@4209
   464
- (NSString *) titleFormat {
hg@4209
   465
	NSString *titleFormat = [[NSUserDefaults standardUserDefaults] stringForKey:@"GMTitleFormat"];
hg@4209
   466
	return titleFormat ? titleFormat : @"(%account) %sender";
hg@4209
   467
}
hg@4209
   468
- (NSString *) descriptionFormat {
hg@4209
   469
	NSString *descriptionFormat = [[NSUserDefaults standardUserDefaults] stringForKey:@"GMDescriptionFormat"];
hg@4209
   470
	return descriptionFormat ? descriptionFormat : @"%subject\n%body";
hg@4209
   471
}
hg@4209
   472
hg@4210
   473
#pragma mark Panic buttons
hg@4210
   474
hg@4210
   475
//This is a suicide pill. GrowlMail calls this function any time it detects a change in Mail's implementation, such as a missing method or an object of the wrong class.
hg@4210
   476
void GMShutDownGrowlMailAndWarn(NSString *specificWarning) {
hg@4210
   477
	NSLog(NSLocalizedString(@"WARNING: Mail is not behaving in the way that GrowlMail expects. This is probably because GrowlMail is incompatible with the version of Mail you're using. GrowlMail will now turn itself off. Please check the Growl website for a new version. If you're a programmer and want to debug this error, run gdb, load Mail, set a breakpoint on %s, and run.", /*comment*/ nil), __PRETTY_FUNCTION__);
hg@4210
   478
	if (specificWarning)
hg@4210
   479
		NSLog(@"Furthermore, the caller provided a more specific message: %@", specificWarning);
hg@4210
   480
hg@4210
   481
	[sharedNotifier shutDownGrowlMail];
hg@4210
   482
hg@4210
   483
	//Prevent ourselves from re-enabling later.
hg@4210
   484
	notifierEnabled = NO;
hg@4210
   485
}
hg@4210
   486
hg@4209
   487
@end