Source/RAFBlockEditorWindowController.m
author Zachary West <zacw@adium.im>
Thu Nov 26 19:47:14 2009 -0500 (2009-11-26)
changeset 2831 fa096eef7c0f
parent 1109 092c44a2cfbb
permissions -rw-r--r--
When loading the Privacy window, resize when it opens appropriately. Fixes #13307.
David@0
     1
//
David@0
     2
//  RAFBlockEditorWindow.m
David@0
     3
//  Adium
David@0
     4
//
David@0
     5
//  Created by Augie Fackler on 5/26/05.
David@0
     6
//  Copyright 2006 The Adium Team. All rights reserved.
David@0
     7
//
David@0
     8
David@0
     9
#import "RAFBlockEditorWindowController.h"
David@0
    10
#import <Adium/AIAccountControllerProtocol.h>
David@0
    11
#import <Adium/AIContactControllerProtocol.h>
David@0
    12
#import <AIUtilities/AICompletingTextField.h>
David@0
    13
#import <AIUtilities/AIPopUpButtonAdditions.h>
David@0
    14
#import <AIUtilities/AIMenuAdditions.h>
David@0
    15
#import <Adium/AIAccount.h>
David@0
    16
#import <Adium/AIListContact.h>
David@0
    17
#import <Adium/AIMetaContact.h>
David@548
    18
#import <Adium/AIListGroup.h>
David@0
    19
#import <Adium/AIService.h>
David@0
    20
David@84
    21
@interface RAFBlockEditorWindowController ()
David@0
    22
- (NSMenu *)privacyOptionsMenu;
David@0
    23
- (AIAccount<AIAccount_Privacy> *)selectedAccount;
David@0
    24
- (void)configureTextField;
David@0
    25
- (NSSet *)contactsFromTextField;
David@0
    26
- (AIPrivacyOption)selectedPrivacyOption;
David@0
    27
@end
David@0
    28
David@0
    29
@implementation RAFBlockEditorWindowController
David@0
    30
David@0
    31
static RAFBlockEditorWindowController *sharedInstance = nil;
David@0
    32
David@0
    33
+ (void)showWindow
David@0
    34
{	
David@0
    35
	if (!sharedInstance) {
David@0
    36
		sharedInstance = [[self alloc] initWithWindowNibName:@"BlockEditorWindow"];
David@0
    37
	}
David@0
    38
David@0
    39
	[sharedInstance showWindow:nil];
David@0
    40
	[[sharedInstance window] makeKeyAndOrderFront:nil];
David@0
    41
}
David@0
    42
David@0
    43
- (void)windowDidLoad
David@0
    44
{
David@0
    45
	[[self window] setTitle:AILocalizedString(@"Privacy Settings", nil)];
David@0
    46
	[cancelButton setLocalizedString:AILocalizedString(@"Cancel","Cancel button for Privacy Settings")];
David@0
    47
	[blockButton setLocalizedString:AILocalizedString(@"Add","Add button for Privacy Settings")];
David@0
    48
	[[buddyCol headerCell] setTitle:AILocalizedString(@"Contact","Title of column containing user IDs of blocked contacts")];
David@0
    49
	[[accountCol headerCell] setTitle:AILocalizedString(@"Account","Title of column containing blocking accounts")];
David@0
    50
	[accountText setLocalizedString:AILocalizedString(@"Account:",nil)];
David@0
    51
David@0
    52
	{
David@0
    53
		//Let the min X margin be resizeable while label_account and label_privacyLevel localize in case the window moves
David@0
    54
		[stateChooser setAutoresizingMask:(NSViewMinYMargin | NSViewMinXMargin)];
David@0
    55
		[popUp_accounts setAutoresizingMask:(NSViewMinYMargin | NSViewMinXMargin)];
David@0
    56
David@0
    57
		//Keep label_privacyLevel in place, too, while label_account potentially resizes the window
David@0
    58
		[label_privacyLevel setAutoresizingMask:(NSViewMinYMargin | NSViewMinXMargin)];
David@0
    59
		[label_account setLocalizedString:AILocalizedString(@"Account:",nil)];
David@0
    60
		[label_privacyLevel setAutoresizingMask:(NSViewMinYMargin | NSViewMaxXMargin)];
David@0
    61
		//Account is in place; popUp_accounts can width-resize again
David@0
    62
		[popUp_accounts setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)];
David@0
    63
David@0
    64
		[label_privacyLevel setLocalizedString:AILocalizedString(@"Privacy level:", nil)];		
David@0
    65
		[stateChooser setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)];
David@0
    66
	}
David@0
    67
David@0
    68
	accountColumnsVisible = YES;
David@0
    69
	[accountCol retain];
David@0
    70
David@0
    71
	listContents = [[NSMutableArray alloc] init];
David@0
    72
David@0
    73
	[stateChooser setMenu:[self privacyOptionsMenu]];
David@0
    74
David@0
    75
	[[table tableColumnWithIdentifier:@"icon"] setDataCell:[[[NSImageCell alloc] init] autorelease]];
David@0
    76
	
David@0
    77
	accountMenu = [[AIAccountMenu accountMenuWithDelegate:self
David@0
    78
											  submenuType:AIAccountNoSubmenu
David@0
    79
										   showTitleVerbs:NO] retain];
David@0
    80
	[table registerForDraggedTypes:[NSArray arrayWithObjects:@"AIListObject", @"AIListObjectUniqueIDs",nil]];
David@0
    81
David@1109
    82
	[[NSNotificationCenter defaultCenter] addObserver:self
David@0
    83
								   selector:@selector(privacySettingsChangedExternally:)
David@0
    84
									   name:@"AIPrivacySettingsChangedOutsideOfPrivacyWindow"
David@0
    85
									 object:nil];
zacw@2831
    86
zacw@2831
    87
	// Force an update, so the window will resize properly.
zacw@2831
    88
	[self accountMenu:accountMenu didSelectAccount:[self selectedAccount]];	
David@0
    89
	
David@14
    90
	[[AIContactObserverManager sharedManager] registerListObjectObserver:self];
David@0
    91
David@0
    92
	[super windowDidLoad];
David@0
    93
}
David@0
    94
David@0
    95
- (void)windowWillClose:(id)sender
David@0
    96
{
David@0
    97
	[super windowWillClose:sender];
David@0
    98
David@14
    99
	[[AIContactObserverManager sharedManager] unregisterListObjectObserver:self];
David@0
   100
David@1109
   101
	[[NSNotificationCenter defaultCenter] removeObserver:self];
David@0
   102
	[sharedInstance release]; sharedInstance = nil;
David@0
   103
}
David@0
   104
David@0
   105
- (NSString *)adiumFrameAutosaveName
David@0
   106
{
David@0
   107
	return @"PrivacyWindow";
David@0
   108
}
David@0
   109
David@0
   110
- (void)dealloc
David@0
   111
{
David@0
   112
	[accountCol release];
David@0
   113
	[accountMenu release];
David@0
   114
	[listContents release];
David@0
   115
	[listContentsAllAccounts release];
David@0
   116
	
David@0
   117
	[super dealloc];
David@0
   118
}
David@0
   119
David@0
   120
- (NSMutableArray*)listContents
David@0
   121
{
David@0
   122
	return listContents;
David@0
   123
}
David@0
   124
David@0
   125
- (void)setListContents:(NSArray*)newList
David@0
   126
{
David@0
   127
	if (newList != listContents) {
David@0
   128
		[listContents release];
David@0
   129
		listContents = [newList mutableCopy];
David@0
   130
	}
David@0
   131
}
David@0
   132
David@0
   133
#pragma mark Adding a contact to the list
David@0
   134
David@0
   135
- (void)selectAccountInSheet:(AIAccount *)inAccount
David@0
   136
{
David@0
   137
	[popUp_sheetAccounts selectItemWithRepresentedObject:inAccount];
David@0
   138
	[self configureTextField];
David@0
   139
	
David@837
   140
	NSString	*userNameLabel = [inAccount.service userNameLabel];
David@0
   141
	
David@0
   142
	[accountText setAutoresizingMask:NSViewMinXMargin];
David@0
   143
	[buddyText setLocalizedString:[(userNameLabel ?
David@0
   144
									userNameLabel : AILocalizedString(@"Contact ID",nil)) stringByAppendingString:AILocalizedString(@":", "Colon which will be appended after a label such as 'User Name', before an input field")]];
David@0
   145
	[accountText setAutoresizingMask:NSViewMaxXMargin];
David@0
   146
}
David@0
   147
David@0
   148
- (IBAction)runBlockSheet:(id)sender
David@0
   149
{
David@0
   150
	[field setStringValue:@""];
David@0
   151
	
David@0
   152
	sheetAccountMenu = [[AIAccountMenu accountMenuWithDelegate:self
David@0
   153
												   submenuType:AIAccountNoSubmenu
David@0
   154
												showTitleVerbs:NO] retain];
David@0
   155
	[self selectAccountInSheet:[[popUp_sheetAccounts selectedItem] representedObject]];
David@0
   156
	
David@0
   157
	[NSApp beginSheet:sheet 
David@0
   158
	   modalForWindow:[self window]
David@0
   159
		modalDelegate:self 
David@0
   160
	   didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
David@0
   161
		  contextInfo:nil];
David@0
   162
}
David@0
   163
David@0
   164
David@0
   165
- (IBAction)cancelBlockSheet:(id)sender
David@0
   166
{
David@0
   167
    [NSApp endSheet:sheet];
David@0
   168
}
David@0
   169
David@0
   170
- (void)addObject:(AIListContact *)inContact
David@0
   171
{
David@0
   172
	if (inContact) {
David@0
   173
		if (![listContents containsObject:inContact]) {
David@0
   174
			[listContents addObject:inContact];
David@0
   175
		}
David@0
   176
		
David@0
   177
		[inContact setIsOnPrivacyList:YES updateList:YES privacyType:(([self selectedPrivacyOption] == AIPrivacyOptionAllowUsers) ?
David@0
   178
																	  AIPrivacyTypePermit :
David@0
   179
																	  AIPrivacyTypeDeny)];	
David@0
   180
	}
David@0
   181
}
David@0
   182
David@0
   183
- (IBAction)didBlockSheet:(id)sender
David@0
   184
{
David@0
   185
	NSSet *contactArray = [self contactsFromTextField];
David@0
   186
David@0
   187
	//Add the contact immediately
David@0
   188
	if (contactArray && [contactArray count]) {
David@0
   189
		AIListContact *contact;
David@0
   190
		
David@75
   191
		for (contact in contactArray) {
David@0
   192
			[self addObject:contact];
David@0
   193
		}
David@0
   194
		
David@0
   195
		[table reloadData];
David@0
   196
	}
David@0
   197
David@0
   198
    [NSApp endSheet:sheet];
David@0
   199
}
David@0
   200
David@0
   201
David@3
   202
- (void)didEndSheet:(NSWindow *)theSheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
David@0
   203
{
David@0
   204
	[sheetAccountMenu release]; sheetAccountMenu = nil;
David@0
   205
    [theSheet orderOut:self];
David@0
   206
}
David@0
   207
David@0
   208
/*!
David@0
   209
 * @brief Get a set of all contacts which are represented by the currently selected account and UID field
David@0
   210
 *
David@0
   211
 * @result A set of AIListContact objects
David@0
   212
 */
David@0
   213
- (NSSet *)contactsFromTextField
David@0
   214
{
David@0
   215
	AIListContact	*contact = nil;
David@0
   216
	NSString		*UID = nil;
David@0
   217
	AIAccount		*account = [[popUp_sheetAccounts selectedItem] representedObject];;
David@0
   218
	NSArray			*accountArray;
David@0
   219
	NSMutableSet	*contactsSet = [NSMutableSet set];
David@0
   220
	NSEnumerator	*enumerator;
David@0
   221
	id				impliedValue = [field impliedValue];
David@0
   222
David@0
   223
	if (account) {
David@0
   224
		accountArray = [NSArray arrayWithObject:account];
David@0
   225
	} else {
David@0
   226
		//All accounts
David@0
   227
		NSMutableArray	*tempArray = [NSMutableArray array];
David@0
   228
		NSMenuItem		*menuItem;
David@0
   229
		
David@0
   230
		enumerator = [[[popUp_sheetAccounts menu] itemArray] objectEnumerator];
David@0
   231
		while ((menuItem = [enumerator nextObject])) {
David@0
   232
			AIAccount *anAccount;
David@0
   233
			
David@0
   234
			if ((anAccount = [menuItem representedObject])) {
David@0
   235
				[tempArray addObject:anAccount];
David@0
   236
			}
David@0
   237
		}
David@0
   238
		
David@0
   239
		accountArray = tempArray;
David@0
   240
	}
David@0
   241
David@75
   242
	for (account in accountArray) {
David@0
   243
		if ([impliedValue isKindOfClass:[AIMetaContact class]]) {
David@0
   244
			AIListContact *containedContact;
David@0
   245
			NSEnumerator *contactEnumerator = [[(AIMetaContact *)impliedValue listContactsIncludingOfflineAccounts] objectEnumerator];
David@0
   246
			
David@0
   247
			while ((containedContact = [contactEnumerator nextObject])) {
David@0
   248
				/* For each contact contained my the metacontact, check if its service class matches the current account's.
David@0
   249
				 * If it does, add that contact to our list, using the contactController to get an AIListContact specific for the account.
David@0
   250
				 */
David@715
   251
				if ([containedContact.service.serviceClass isEqualToString:account.service.serviceClass]) {
Evan@160
   252
					if ((contact = [adium.contactController contactWithService:account.service
David@0
   253
																		 account:account
Evan@160
   254
																			 UID:containedContact.UID])) {
David@0
   255
						[contactsSet addObject:contact];
David@0
   256
					}
David@0
   257
				}
David@0
   258
			}
David@0
   259
			
David@0
   260
		} else {
David@0
   261
			if ([impliedValue isKindOfClass:[AIListContact class]]) {
David@0
   262
				UID = [(AIListContact *)impliedValue UID];
David@0
   263
			
David@0
   264
			} else  if ([impliedValue isKindOfClass:[NSString class]]) {
David@837
   265
				UID = [account.service normalizeUID:impliedValue removeIgnoredCharacters:YES];
David@0
   266
			}
David@0
   267
			
David@0
   268
			if (UID) {
David@0
   269
				//Get a contact with this UID on the current account
David@837
   270
				if ((contact = [adium.contactController contactWithService:account.service
David@0
   271
																	 account:account 
David@0
   272
																		 UID:UID])) {
David@0
   273
					[contactsSet addObject:contact];
David@0
   274
				}
David@0
   275
			}
David@0
   276
		}
David@0
   277
			
David@0
   278
	}
David@0
   279
	
David@0
   280
	return contactsSet;
David@0
   281
}
David@0
   282
David@0
   283
- (void)configureTextField
David@0
   284
{
David@0
   285
	AIAccount *account = [[popUp_sheetAccounts selectedItem] representedObject];
David@0
   286
	NSEnumerator		*enumerator;
David@0
   287
    AIListContact		*contact;
David@0
   288
	
David@0
   289
	//Clear the completing strings
David@0
   290
	[field setCompletingStrings:nil];
David@0
   291
	
David@0
   292
	//Configure the auto-complete view to autocomplete for contacts matching the selected account's service
David@89
   293
    enumerator = [adium.contactController.allContacts objectEnumerator];
David@0
   294
    while ((contact = [enumerator nextObject])) {
David@0
   295
		if (!account ||
David@837
   296
			contact.service == account.service) {
David@715
   297
			NSString *UID = contact.UID;
David@837
   298
			[field addCompletionString:contact.formattedUID withImpliedCompletion:UID];
David@837
   299
			[field addCompletionString:contact.displayName withImpliedCompletion:UID];
David@0
   300
			[field addCompletionString:UID];
David@0
   301
		}
David@0
   302
    }
David@0
   303
}
David@0
   304
David@0
   305
#pragma mark Removing a contact from the  list
David@0
   306
David@0
   307
- (IBAction)removeSelection:(id)sender
David@0
   308
{
David@0
   309
	NSIndexSet		*selectedItems = [table selectedRowIndexes];
David@0
   310
	
David@0
   311
	// If there's anything selected..
David@0
   312
	if ([selectedItems count]) {
David@0
   313
		AIListContact	*contact;
David@0
   314
		
David@0
   315
		// Iterate through the selected rows (backwards)
David@3
   316
		for (NSInteger selection = [selectedItems lastIndex]; selection != NSNotFound; selection = [selectedItems indexLessThanIndex:selection]) {
David@0
   317
			contact = [listContents objectAtIndex:selection];
David@0
   318
			// Remove from the serverside list
David@0
   319
			[contact setIsOnPrivacyList:NO updateList:YES privacyType:(([self selectedPrivacyOption] == AIPrivacyOptionAllowUsers) ?
David@0
   320
																	   AIPrivacyTypePermit :
David@0
   321
																	   AIPrivacyTypeDeny)];
David@0
   322
			[listContents removeObject:contact];
David@0
   323
		}
David@0
   324
		
David@0
   325
		[table reloadData];
David@0
   326
		[table deselectAll:nil];
David@0
   327
	}
David@0
   328
David@0
   329
}
David@0
   330
David@0
   331
- (void)tableViewDeleteSelectedRows:(NSTableView *)tableView
David@0
   332
{
David@0
   333
	[self removeSelection:tableView];
David@0
   334
}
David@0
   335
David@0
   336
- (void)setAccountColumnsVisible:(BOOL)visible
David@0
   337
{
David@0
   338
	if (accountColumnsVisible != visible) {
David@0
   339
		if (visible) {
David@0
   340
			[table addTableColumn:accountCol];
David@0
   341
		} else {
David@0
   342
			[table removeTableColumn:accountCol];			
David@0
   343
		}
David@0
   344
David@0
   345
		[table sizeToFit];
David@0
   346
		accountColumnsVisible = visible;
David@0
   347
	}
David@0
   348
}
David@0
   349
#pragma mark Privacy options menu
David@0
   350
David@0
   351
- (NSMenu *)privacyOptionsMenu
David@0
   352
{
David@0
   353
	//build the menu of states
David@0
   354
	NSMenu *stateMenu = [[NSMenu alloc] init];
David@0
   355
David@0
   356
	NSMenuItem *menuItem;
David@0
   357
	
David@0
   358
	menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow anyone", nil) 
David@0
   359
										  action:NULL
David@0
   360
								   keyEquivalent:@""];
David@0
   361
	[menuItem setTag:AIPrivacyOptionAllowAll];
David@0
   362
	[stateMenu addItem:menuItem];
David@0
   363
	[menuItem release];
David@0
   364
David@0
   365
	menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow only contacts on my contact list", nil) 
David@0
   366
										  action:NULL
David@0
   367
								   keyEquivalent:@""];
David@0
   368
	[menuItem setTag:AIPrivacyOptionAllowContactList];
David@0
   369
	[stateMenu addItem:menuItem];
David@0
   370
	[menuItem release];
David@0
   371
David@0
   372
	menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow only certain contacts", nil) 
David@0
   373
										  action:NULL
David@0
   374
								   keyEquivalent:@""];
David@0
   375
	[menuItem setTag:AIPrivacyOptionAllowUsers];
David@0
   376
	[stateMenu addItem:menuItem];
David@0
   377
	[menuItem release];
David@0
   378
David@0
   379
	menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Block certain contacts", nil) 
David@0
   380
										  action:NULL
David@0
   381
								   keyEquivalent:@""];
David@0
   382
	[menuItem setTag:AIPrivacyOptionDenyUsers];
David@0
   383
	[stateMenu addItem:menuItem];
David@0
   384
	[menuItem release];
David@0
   385
David@0
   386
	/*
David@0
   387
	tmpItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Custom settings for each account", nil) action:NULL keyEquivalent:@""];
David@0
   388
	[tmpItem setRepresentedObject:[NSNumber numberWithInt:AIPrivacyOptionCustom]];
David@0
   389
	[stateMenu addItem:[tmpItem autorelease]];
David@0
   390
	*/
David@0
   391
David@0
   392
	return [stateMenu autorelease];
David@0
   393
}
David@0
   394
David@0
   395
- (AIPrivacyOption)selectedPrivacyOption
David@0
   396
{
David@0
   397
	return [[stateChooser selectedItem] tag];
David@0
   398
}
David@0
   399
David@0
   400
/*!
David@0
   401
 * @brief Set a privacy option and update our view for it
David@0
   402
 *
David@0
   403
 * @param sender If nil, we update our display without attempting to change anything on our account
David@0
   404
 */
David@0
   405
- (IBAction)setPrivacyOption:(id)sender
David@0
   406
{
David@0
   407
	AIAccount<AIAccount_Privacy> *account = [self selectedAccount];
David@0
   408
	AIPrivacyOption privacyOption = [self selectedPrivacyOption];
David@0
   409
David@0
   410
	//First, let's get the right tab view selected
David@0
   411
	switch (privacyOption) {
David@0
   412
		case AIPrivacyOptionAllowAll:
David@0
   413
		case AIPrivacyOptionAllowContactList:
David@0
   414
		case AIPrivacyOptionCustom:
David@0
   415
			if (![[[tabView_contactList selectedTabViewItem] identifier] isEqualToString:@"empty"]) {
David@0
   416
				[tabView_contactList selectTabViewItemWithIdentifier:@"empty"];
David@0
   417
				[tabView_contactList setHidden:YES];
David@0
   418
David@0
   419
				NSRect frame = [[self window] frame];
David@3
   420
				CGFloat tabViewHeight = [tabView_contactList frame].size.height;
David@0
   421
				frame.size.height -= tabViewHeight;
David@0
   422
				frame.origin.y += tabViewHeight;
David@0
   423
				
David@0
   424
				//Don't resize vertically now...
David@0
   425
				[tabView_contactList setAutoresizingMask:NSViewWidthSizable];
David@0
   426
David@0
   427
				[[self window] setMinSize:NSMakeSize(250, frame.size.height)];
David@3
   428
				[[self window] setMaxSize:NSMakeSize(CGFLOAT_MAX, frame.size.height)];
David@0
   429
				
David@0
   430
				AILog(@"Because of privacy option %i, resizing from %@ to %@",privacyOption,
David@0
   431
					  NSStringFromRect([[self window] frame]),NSStringFromRect(frame));
David@0
   432
				[[self window] setFrame:frame display:YES animate:YES];
David@0
   433
			}
David@0
   434
			break;
David@0
   435
			
David@0
   436
		case AIPrivacyOptionAllowUsers:
David@0
   437
		case AIPrivacyOptionDenyUsers:
David@0
   438
			if (![[[tabView_contactList selectedTabViewItem] identifier] isEqualToString:@"list"]) {
David@0
   439
				[tabView_contactList selectTabViewItemWithIdentifier:@"list"];
David@0
   440
David@0
   441
				NSRect frame = [[self window] frame];
David@3
   442
				CGFloat tabViewHeight = [tabView_contactList frame].size.height;
David@0
   443
				frame.size.height += tabViewHeight;
David@0
   444
				frame.origin.y -= tabViewHeight;
David@0
   445
				
David@0
   446
				[[self window] setMinSize:NSMakeSize(250, 320)];
David@3
   447
				[[self window] setMaxSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)];
David@0
   448
				
David@0
   449
				//Set frame after fixing our min/max size so the resize won't fail
David@0
   450
				AILog(@"Because of privacy option %i, resizing from %@ to %@",privacyOption,
David@0
   451
					  NSStringFromRect([[self window] frame]),NSStringFromRect(frame));
David@0
   452
				[[self window] setFrame:frame display:YES animate:YES];
David@0
   453
David@0
   454
				[tabView_contactList setHidden:NO];
David@0
   455
David@0
   456
				//Allow resizing vertically again
David@0
   457
				[tabView_contactList setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
David@0
   458
			}
David@0
   459
			break;
David@0
   460
		case AIPrivacyOptionDenyAll:
David@0
   461
		case AIPrivacyOptionUnknown:
David@0
   462
			NSLog(@"We should never see these...");
David@0
   463
			break;
David@0
   464
	}
David@0
   465
	
David@0
   466
	if (sender) {
David@0
   467
		if (account) {
David@0
   468
			[account setPrivacyOptions:privacyOption];
David@0
   469
			
David@0
   470
		} else {
David@0
   471
			NSEnumerator	*enumerator = [[[popUp_accounts menu] itemArray] objectEnumerator];
David@0
   472
			NSMenuItem						*menuItem;
David@0
   473
			AIAccount<AIAccount_Privacy>	*representedAccount;
David@0
   474
David@0
   475
			while ((menuItem = [enumerator nextObject])) {
David@0
   476
				if ((representedAccount = [menuItem representedObject])) {
David@0
   477
					[representedAccount setPrivacyOptions:privacyOption];
David@0
   478
				}
David@0
   479
			}
David@0
   480
		}
David@0
   481
	}
David@0
   482
	
David@0
   483
	//Now make our listContents array match the serverside arrays for the selected account(s)
David@0
   484
	[listContents removeAllObjects];
David@0
   485
	if ((privacyOption == AIPrivacyOptionAllowUsers) ||
David@0
   486
		(privacyOption == AIPrivacyOptionDenyUsers)) {
David@0
   487
		if (account) {
David@0
   488
			[listContents addObjectsFromArray:[account listObjectsOnPrivacyList:((privacyOption == AIPrivacyOptionAllowUsers) ?
David@0
   489
																				 AIPrivacyTypePermit :
David@0
   490
																				 AIPrivacyTypeDeny)]];		
David@0
   491
		} else {
David@0
   492
			NSEnumerator					*enumerator = [[[popUp_accounts menu] itemArray] objectEnumerator];
David@0
   493
			NSMenuItem						*menuItem;
David@0
   494
			AIAccount<AIAccount_Privacy>	*representedAccount;
David@0
   495
David@0
   496
			while ((menuItem = [enumerator nextObject])) {
David@0
   497
				if ((representedAccount = [menuItem representedObject])) {
David@0
   498
					[listContents addObjectsFromArray:[representedAccount listObjectsOnPrivacyList:((privacyOption == AIPrivacyOptionAllowUsers) ?
David@0
   499
																									AIPrivacyTypePermit :
David@0
   500
																									AIPrivacyTypeDeny)]];		
David@0
   501
				}
David@0
   502
			}
David@0
   503
		}
David@0
   504
	}
David@0
   505
David@0
   506
	[table reloadData];
David@0
   507
}
David@0
   508
David@0
   509
- (void)selectPrivacyOption:(AIPrivacyOption)privacyOption
David@0
   510
{
David@413
   511
	BOOL success = [stateChooser selectItemWithTag:privacyOption];
David@0
   512
	if (privacyOption == AIPrivacyOptionCustom) {
David@0
   513
		if (!success) {
David@0
   514
			NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"(Multiple privacy levels are active)", nil) 
David@0
   515
															  action:NULL
David@0
   516
													   keyEquivalent:@""];
David@0
   517
			[menuItem setTag:AIPrivacyOptionCustom];
David@0
   518
			[[stateChooser menu] addItem:menuItem];
David@0
   519
			[menuItem release];
David@0
   520
			
David@413
   521
			success = [stateChooser selectItemWithTag:privacyOption];
David@0
   522
		}
David@0
   523
David@0
   524
	} else {
David@0
   525
		//Not on custom; make sure custom isn't still in the menu
David@3
   526
		NSInteger customItemIndex = [stateChooser indexOfItemWithTag:AIPrivacyOptionCustom];
David@0
   527
		if (customItemIndex != -1) {
David@0
   528
			[[stateChooser menu] removeItemAtIndex:customItemIndex];
David@0
   529
		}
David@0
   530
	}
David@0
   531
David@0
   532
	//Now update our view for this privacy option
David@0
   533
	[self setPrivacyOption:nil];
David@0
   534
}
David@0
   535
David@0
   536
#pragma mark Account menu
David@0
   537
/*!
David@0
   538
 * @brief Return the currently selected account, or nil if the 'All' item is selected
David@0
   539
 */
David@0
   540
- (AIAccount<AIAccount_Privacy> *)selectedAccount
David@0
   541
{
David@0
   542
	return [[popUp_accounts selectedItem] representedObject];
David@0
   543
}
David@0
   544
David@0
   545
/*!
David@0
   546
 * @brief Action called when the account selection changes
David@0
   547
 *
David@0
   548
 * Update our view and the privacy option menu to be appropriate for the newly selected account.
David@0
   549
 * This may be called with a sender of nil by code elsewhere to force an update
David@0
   550
 */
David@0
   551
- (void)accountMenu:(AIAccountMenu *)inAccountMenu didSelectAccount:(AIAccount *)inAccount
David@0
   552
{
David@0
   553
	if (inAccountMenu == accountMenu) {
David@0
   554
		AIAccount<AIAccount_Privacy> *account = [self selectedAccount];
David@0
   555
		if (account) {
David@0
   556
			//Selected an account
David@0
   557
			AIPrivacyOption privacyOption = [account privacyOptions];
David@0
   558
			
David@0
   559
			//Don't need the account column when we're showing for just one account
David@0
   560
			[self setAccountColumnsVisible:NO];
David@0
   561
David@0
   562
			[self selectPrivacyOption:privacyOption];			
David@0
   563
David@0
   564
		} else {
David@0
   565
			//Selected 'All'. We need to determine what privacy option to display for the set of all accounts.
David@0
   566
			AIPrivacyOption currentState = AIPrivacyOptionUnknown;
David@0
   567
			NSEnumerator	*enumerator = [[[popUp_accounts menu] itemArray] objectEnumerator];
David@0
   568
			NSMenuItem		*menuItem;
David@0
   569
			
David@0
   570
			while ((menuItem = [enumerator nextObject])) {
David@0
   571
				if ((account = [menuItem representedObject])) {
David@0
   572
					AIPrivacyOption accountState = [account privacyOptions];
David@0
   573
					
David@0
   574
					if (currentState == AIPrivacyOptionUnknown) {
David@0
   575
						//We don't know the state of an account yet
David@0
   576
						currentState = accountState;
David@0
   577
					} else if (accountState != currentState) {
David@0
   578
						currentState = AIPrivacyOptionCustom;
David@0
   579
					}				
David@0
   580
				}
David@0
   581
			}
David@0
   582
			
David@0
   583
			[self setAccountColumnsVisible:YES];
David@0
   584
David@0
   585
			[self selectPrivacyOption:currentState];
David@0
   586
		}
David@0
   587
David@0
   588
	} else if (inAccountMenu == sheetAccountMenu) {
David@0
   589
		//Update our sheet for the current account
David@0
   590
		[self selectAccountInSheet:inAccount];
David@0
   591
	}
David@0
   592
}
David@0
   593
David@0
   594
/*!
David@0
   595
 * @brief The 'All' menu item for accounts was selected
David@0
   596
 *
David@0
   597
 * We simulate an AIAccountMenu delegate call, since the All item was added by RAFBLockEditorWindowController.
David@0
   598
 */
David@0
   599
- (IBAction)selectedAllAccountItem:(id)sender
David@0
   600
{
David@0
   601
	AIAccountMenu *relevantAccountMenu = (([sender menu] == [popUp_accounts menu]) ?
David@0
   602
										  accountMenu :
David@0
   603
										  sheetAccountMenu);
David@0
   604
David@0
   605
	[self accountMenu:relevantAccountMenu didSelectAccount:nil];
David@0
   606
}
David@0
   607
David@0
   608
/*!
David@0
   609
 * @brief Select an account in our account menu, then update everything else to be appropriate for it
David@0
   610
 */
David@0
   611
- (void)selectAccount:(AIAccount *)inAccount
David@0
   612
{
David@0
   613
	[popUp_accounts selectItemWithRepresentedObject:inAccount];
David@0
   614
	
David@0
   615
	[self accountMenu:accountMenu didSelectAccount:inAccount];
David@0
   616
}
David@0
   617
David@0
   618
/*!
David@0
   619
 * @brief Add account menu items to our location
David@0
   620
 *
David@0
   621
 * Implemented as required by the AccountMenuPlugin protocol.
David@0
   622
 *
David@0
   623
 * @param menuItemArray An <tt>NSArray</tt> of <tt>NSMenuItem</tt> objects to be added to the menu
David@0
   624
 */
David@0
   625
- (void)accountMenu:(AIAccountMenu *)inAccountMenu didRebuildMenuItems:(NSArray *)menuItems
David@0
   626
{
David@0
   627
	AIAccount	 *previouslySelectedAccount = nil;
David@0
   628
	NSMenuItem	 *menuItem;
David@0
   629
	NSMenu		 *menu = [[NSMenu alloc] init];
David@0
   630
David@0
   631
	/*
David@0
   632
	 * accountMenu isn't set the first time we get here as the accountMenu is created. Similarly, sheetAccountMenu isn't created its first time.
David@0
   633
	 * This code makes the (true) assumption that accountMenu is _always_ created before sheetAccountMenu.
David@0
   634
	 */	
David@0
   635
	BOOL isPrimaryAccountMenu = (!accountMenu || (inAccountMenu == accountMenu));
David@0
   636
David@0
   637
	if (isPrimaryAccountMenu) {
David@0
   638
		if ([popUp_accounts menu]) {
David@0
   639
			previouslySelectedAccount = [[popUp_accounts selectedItem] representedObject];
David@0
   640
		}
David@0
   641
	} else if (inAccountMenu == sheetAccountMenu) {
David@0
   642
		if ([popUp_sheetAccounts menu]) {
David@0
   643
			previouslySelectedAccount = [[popUp_sheetAccounts selectedItem] representedObject];
David@0
   644
		}		
David@0
   645
	}
David@0
   646
David@0
   647
	//Add the All menu item first if we have more than one account listed
David@0
   648
	if ([menuItems count] > 1) {
David@0
   649
		[menu addItemWithTitle:AILocalizedString(@"All", nll)
David@0
   650
						target:self
David@0
   651
						action:@selector(selectedAllAccountItem:)
David@0
   652
				 keyEquivalent:@""];
David@0
   653
	}
David@0
   654
David@0
   655
	/*
David@0
   656
	 * As we enumerate, we:
David@0
   657
	 *	1) Determine what state the accounts within the menu are in
David@0
   658
	 *  2) Add the menu items to our menu
David@0
   659
	 */
David@75
   660
	for (menuItem in menuItems) {		
David@0
   661
		[menu addItem:menuItem];
David@0
   662
	}
David@0
   663
David@0
   664
	if (isPrimaryAccountMenu) {
David@0
   665
		[popUp_accounts setMenu:menu];
David@0
   666
David@0
   667
		/* Restore the previous account selection if there was one.
David@0
   668
		 * Whether there was one or not, this will cause the rest of our view update to match the new/current selection
David@0
   669
		 */
David@0
   670
		[self selectAccount:previouslySelectedAccount];
David@0
   671
David@0
   672
	} else {
David@0
   673
		[popUp_sheetAccounts setMenu:menu];
David@0
   674
		
David@0
   675
		[self selectAccountInSheet:previouslySelectedAccount];
David@0
   676
	}
David@0
   677
David@0
   678
	[menu release];
David@0
   679
}
David@0
   680
David@0
   681
- (BOOL)accountMenu:(AIAccountMenu *)inAccountMenu shouldIncludeAccount:(AIAccount *)inAccount
David@0
   682
{
David@0
   683
	BOOL isPrimaryAccountMenu = (!accountMenu || (inAccountMenu == accountMenu));
David@0
   684
David@0
   685
	if (isPrimaryAccountMenu) {
David@837
   686
		return (inAccount.online &&
David@0
   687
				[inAccount conformsToProtocol:@protocol(AIAccount_Privacy)]);
David@0
   688
	} else {
David@837
   689
		AIAccount *selectedPrimaryAccount = self.selectedAccount;
David@0
   690
		if (selectedPrimaryAccount) {
David@0
   691
			//An account is selected in the main window; only incldue that account in our sheet
David@0
   692
			return (inAccount == selectedPrimaryAccount);
David@0
   693
David@0
   694
		} else {
David@0
   695
			//'All' is selected in the main window; include all accounts which are online and support privacy
David@837
   696
			return (inAccount.online &&
David@0
   697
					[inAccount conformsToProtocol:@protocol(AIAccount_Privacy)]);			
David@0
   698
		}
David@0
   699
	}
David@0
   700
}
David@0
   701
David@0
   702
- (void)privacySettingsChangedExternally:(NSNotification *)inNotification
David@0
   703
{
David@0
   704
	[self accountMenu:accountMenu didSelectAccount:[self selectedAccount]];	
David@0
   705
}
David@0
   706
David@0
   707
- (NSSet *)updateListObject:(AIListObject *)inObject keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
David@0
   708
{
David@0
   709
	if ([inModifiedKeys containsObject:KEY_IS_BLOCKED]) {
David@0
   710
		[self privacySettingsChangedExternally:nil];
David@0
   711
	}
David@0
   712
	
David@0
   713
	return nil;
David@0
   714
}
David@0
   715
David@0
   716
#pragma mark Table view
David@0
   717
David@3
   718
- (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
David@0
   719
{
David@0
   720
	return [listContents count];
David@0
   721
}
David@0
   722
David@3
   723
- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
David@0
   724
{
David@0
   725
	NSString		*identifier = [aTableColumn identifier];
David@0
   726
	AIListContact	*contact = [listContents objectAtIndex:rowIndex];
David@0
   727
David@0
   728
	if ([identifier isEqualToString:@"icon"]) {
David@0
   729
		return [contact menuIcon];
David@0
   730
		
David@0
   731
	} else if ([identifier isEqualToString:@"contact"]) {
David@837
   732
		return contact.formattedUID;
David@0
   733
David@0
   734
	} else if ([identifier isEqualToString:@"account"]) {
David@837
   735
		return contact.account.formattedUID;
David@0
   736
	}
David@0
   737
	
David@0
   738
	return nil;
David@0
   739
}
David@0
   740
David@0
   741
- (BOOL)writeListObjects:(NSArray *)inArray toPasteboard:(NSPasteboard*)pboard
David@0
   742
{
David@0
   743
	[pboard declareTypes:[NSArray arrayWithObjects:@"AIListObject",@"AIListObjectUniqueIDs",nil] owner:self];
David@0
   744
	[pboard setString:@"Private" forType:@"AIListObject"];
David@0
   745
David@0
   746
	if (dragItems != inArray) {
David@0
   747
		[dragItems release];
David@0
   748
		dragItems = [inArray retain];
David@0
   749
	}
David@0
   750
	
David@0
   751
	return YES;
David@0
   752
}
David@0
   753
David@0
   754
- (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
David@0
   755
{	
David@0
   756
	NSMutableArray 	*itemArray = [NSMutableArray array];
David@0
   757
	NSNumber		*rowNumber;
David@75
   758
	for (rowNumber in rows) {
David@3
   759
		[itemArray addObject:[listContents objectAtIndex:[rowNumber integerValue]]];
David@0
   760
	}
David@0
   761
David@0
   762
	return [self writeListObjects:itemArray toPasteboard:pboard];
David@0
   763
}
David@0
   764
David@0
   765
- (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard
David@0
   766
{
David@0
   767
	NSMutableArray 	*itemArray = [NSMutableArray array];
David@0
   768
	id 				item;
David@0
   769
	
David@3
   770
	NSUInteger bufSize = [rowIndexes count];
David@3
   771
	NSUInteger *buf = malloc(bufSize * sizeof(NSUInteger));
David@3
   772
	NSUInteger i;
David@0
   773
	
David@0
   774
	NSRange range = NSMakeRange([rowIndexes firstIndex], ([rowIndexes lastIndex]-[rowIndexes firstIndex]) + 1);
David@0
   775
	[rowIndexes getIndexes:buf maxCount:bufSize inIndexRange:&range];
David@0
   776
	
David@0
   777
	for (i = 0; i != bufSize; i++) {
David@0
   778
		if ((item = [listContents objectAtIndex:buf[i]])) {
David@0
   779
			[itemArray addObject:item];
David@0
   780
		}
David@0
   781
	}
David@0
   782
	
David@0
   783
	free(buf);
David@0
   784
	
David@0
   785
	return [self writeListObjects:itemArray toPasteboard:pboard];
David@0
   786
}
David@0
   787
David@0
   788
- (void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
David@0
   789
{
David@0
   790
	//Provide an array of internalObjectIDs which can be used to reference all the dragged contacts
David@0
   791
	if ([type isEqualToString:@"AIListObjectUniqueIDs"]) {
David@0
   792
		
David@0
   793
		if (dragItems) {
David@0
   794
			NSMutableArray	*dragItemsArray = [NSMutableArray array];
David@0
   795
			AIListObject	*listObject;
David@0
   796
			
David@75
   797
			for (listObject in dragItems) {
David@838
   798
				[dragItemsArray addObject:listObject.internalObjectID];
David@0
   799
			}
David@0
   800
			
David@0
   801
			[sender setPropertyList:dragItemsArray forType:@"AIListObjectUniqueIDs"];
David@0
   802
		}
David@0
   803
	}
David@0
   804
}
David@0
   805
David@0
   806
- (NSDragOperation)tableView:(NSTableView*)tv
David@0
   807
				validateDrop:(id <NSDraggingInfo>)info
David@3
   808
				 proposedRow:(NSInteger)row
David@0
   809
	   proposedDropOperation:(NSTableViewDropOperation)op
David@0
   810
{
David@0
   811
    
David@0
   812
    NSDragOperation dragOp = NSDragOperationCopy;
David@0
   813
	
David@0
   814
    if ([info draggingSource] == table) {
David@0
   815
		dragOp =  NSDragOperationMove;
David@0
   816
    }
David@0
   817
    [tv setDropRow:row dropOperation:NSTableViewDropAbove];
David@0
   818
	
David@0
   819
    return dragOp;
David@0
   820
}
David@0
   821
David@0
   822
- (void)addListObjectToList:(AIListObject *)listObject
David@0
   823
{
David@0
   824
	AIListObject *containedObject;
David@0
   825
	NSEnumerator *enumerator;
David@0
   826
David@0
   827
	if ([listObject isKindOfClass:[AIListGroup class]]) {
David@382
   828
		enumerator = [[(AIListGroup *)listObject uniqueContainedObjects] objectEnumerator];
David@0
   829
		while ((containedObject = [enumerator nextObject])) {
David@0
   830
			[self addListObjectToList:containedObject];
David@0
   831
		}
David@0
   832
David@0
   833
	} else if ([listObject isKindOfClass:[AIMetaContact class]]) {
David@382
   834
		enumerator = [[(AIMetaContact *)listObject uniqueContainedObjects] objectEnumerator];
David@0
   835
		while ((containedObject = [enumerator nextObject])) {
David@0
   836
			[self addListObjectToList:containedObject];
David@0
   837
		}
David@0
   838
David@0
   839
	} else if ([listObject isKindOfClass:[AIListContact class]]) {
David@0
   840
		//if the account for this contact is connected...
David@837
   841
		if ([(AIListContact *)listObject account].online) {
David@0
   842
			[self addObject:(AIListContact *)listObject];
David@0
   843
		}
David@0
   844
	}
David@0
   845
}
David@0
   846
David@3
   847
- (BOOL)tableView:(NSTableView*)tv acceptDrop:(id <NSDraggingInfo>)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)op
David@0
   848
{
David@0
   849
	BOOL accept = NO;
David@0
   850
    if (row < 0)
David@0
   851
		row = 0;
David@0
   852
	
David@837
   853
	if ([info.draggingPasteboard.types containsObject:@"AIListObjectUniqueIDs"]) {
David@837
   854
		for (NSString *uniqueUID in [info.draggingPasteboard propertyListForType:@"AIListObjectUniqueIDs"])
David@89
   855
			[self addListObjectToList:[adium.contactController existingListObjectWithUniqueID:uniqueUID]];
David@0
   856
		accept = YES;
David@0
   857
	}
David@0
   858
	
David@0
   859
    return accept;
David@0
   860
}
David@0
   861
David@0
   862
@end