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