Fix account text's positioning while the contact id text resizes. Fixes #6093
[adiumx.git] / Source / RAFBlockEditorWindowController.m
blobb2f72171cb27235f27d26032ec85d18cd0725f20
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:(NSViewMinYMargin | NSViewMinXMargin)];
55                 [popUp_accounts setAutoresizingMask:(NSViewMinYMargin | NSViewMinXMargin)];
57                 //Keep label_privacyLevel in place, too, while label_account potentially resizes the window
58                 [label_privacyLevel setAutoresizingMask:(NSViewMinYMargin | NSViewMinXMargin)];
59                 [label_account setLocalizedString:AILocalizedString(@"Account:",nil)];
60                 [label_privacyLevel setAutoresizingMask:(NSViewMinYMargin | NSViewMaxXMargin)];
61                 //Account is in place; popUp_accounts can width-resize again
62                 [popUp_accounts setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)];
64                 [label_privacyLevel setLocalizedString:AILocalizedString(@"Privacy level:", nil)];              
65                 [stateChooser setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)];
66         }
68         accountColumnsVisible = YES;
69         [accountCol retain];
71         listContents = [[NSMutableArray alloc] init];
73         [stateChooser setMenu:[self privacyOptionsMenu]];
75         [[table tableColumnWithIdentifier:@"icon"] setDataCell:[[[NSImageCell alloc] init] autorelease]];
76         
77         accountMenu = [[AIAccountMenu accountMenuWithDelegate:self
78                                                                                           submenuType:AIAccountNoSubmenu
79                                                                                    showTitleVerbs:NO] retain];
80         [table registerForDraggedTypes:[NSArray arrayWithObjects:@"AIListObject", @"AIListObjectUniqueIDs",nil]];
82         [[adium notificationCenter] addObserver:self
83                                                                    selector:@selector(privacySettingsChangedExternally:)
84                                                                            name:@"AIPrivacySettingsChangedOutsideOfPrivacyWindow"
85                                                                          object:nil];
86         
87         [[adium contactController] registerListObjectObserver:self];
89         [super windowDidLoad];
92 - (void)windowWillClose:(id)sender
94         [super windowWillClose:sender];
96         [[adium contactController] unregisterListObjectObserver:self];
98         [[adium notificationCenter] removeObserver:self];
99         [sharedInstance release]; sharedInstance = nil;
102 - (NSString *)adiumFrameAutosaveName
104         return @"PrivacyWindow";
107 - (void)dealloc
109         [accountCol release];
110         [accountMenu release];
111         [listContents release];
112         [listContentsAllAccounts release];
113         
114         [super dealloc];
117 - (NSMutableArray*)listContents
119         return listContents;
122 - (void)setListContents:(NSArray*)newList
124         if (newList != listContents) {
125                 [listContents release];
126                 listContents = [newList mutableCopy];
127         }
130 #pragma mark Adding a contact to the list
132 - (void)selectAccountInSheet:(AIAccount *)inAccount
134         [popUp_sheetAccounts selectItemWithRepresentedObject:inAccount];
135         [self configureTextField];
136         
137         NSString        *userNameLabel = [[inAccount service] userNameLabel];
138         
139         [accountText setAutoresizingMask:NSViewMinXMargin];
140         [buddyText setLocalizedString:[(userNameLabel ?
141                                                                         userNameLabel : AILocalizedString(@"Contact ID",nil)) stringByAppendingString:AILocalizedString(@":", "Colon which will be appended after a label such as 'User Name', before an input field")]];
142         [accountText setAutoresizingMask:NSViewMaxXMargin];
145 - (IBAction)runBlockSheet:(id)sender
147         [field setStringValue:@""];
148         
149         sheetAccountMenu = [[AIAccountMenu accountMenuWithDelegate:self
150                                                                                                    submenuType:AIAccountNoSubmenu
151                                                                                                 showTitleVerbs:NO] retain];
152         [self selectAccountInSheet:[[popUp_sheetAccounts selectedItem] representedObject]];
153         
154         [NSApp beginSheet:sheet 
155            modalForWindow:[self window]
156                 modalDelegate:self 
157            didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
158                   contextInfo:nil];
162 - (IBAction)cancelBlockSheet:(id)sender
164     [NSApp endSheet:sheet];
167 - (void)addObject:(AIListContact *)inContact
169         if (inContact) {
170                 if (![listContents containsObject:inContact]) {
171                         [listContents addObject:inContact];
172                 }
173                 
174                 [inContact setIsOnPrivacyList:YES updateList:YES privacyType:(([self selectedPrivacyOption] == AIPrivacyOptionAllowUsers) ?
175                                                                                                                                           AIPrivacyTypePermit :
176                                                                                                                                           AIPrivacyTypeDeny)];  
177         }
180 - (IBAction)didBlockSheet:(id)sender
182         NSSet *contactArray = [self contactsFromTextField];
184         //Add the contact immediately
185         if (contactArray && [contactArray count]) {
186                 NSEnumerator *enumerator;
187                 AIListContact *contact;
188                 
189                 enumerator = [contactArray objectEnumerator];
190                 while ((contact = [enumerator nextObject])) {
191                         [self addObject:contact];
192                 }
193                 
194                 [table reloadData];
195         }
197     [NSApp endSheet:sheet];
201 - (void)didEndSheet:(NSWindow *)theSheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
203         [sheetAccountMenu release]; sheetAccountMenu = nil;
204     [theSheet orderOut:self];
208  * @brief Get a set of all contacts which are represented by the currently selected account and UID field
210  * @result A set of AIListContact objects
211  */
212 - (NSSet *)contactsFromTextField
214         AIListContact   *contact = nil;
215         NSString                *UID = nil;
216         AIAccount               *account = [[popUp_sheetAccounts selectedItem] representedObject];;
217         NSArray                 *accountArray;
218         NSMutableSet    *contactsSet = [NSMutableSet set];
219         NSEnumerator    *enumerator;
220         id                              impliedValue = [field impliedValue];
222         if (account) {
223                 accountArray = [NSArray arrayWithObject:account];
224         } else {
225                 //All accounts
226                 NSMutableArray  *tempArray = [NSMutableArray array];
227                 NSMenuItem              *menuItem;
228                 
229                 enumerator = [[[popUp_sheetAccounts menu] itemArray] objectEnumerator];
230                 while ((menuItem = [enumerator nextObject])) {
231                         AIAccount *anAccount;
232                         
233                         if ((anAccount = [menuItem representedObject])) {
234                                 [tempArray addObject:anAccount];
235                         }
236                 }
237                 
238                 accountArray = tempArray;
239         }
241         enumerator = [accountArray objectEnumerator];
242         while ((account = [enumerator nextObject])) {
243                 if ([impliedValue isKindOfClass:[AIMetaContact class]]) {
244                         AIListContact *containedContact;
245                         NSEnumerator *contactEnumerator = [[(AIMetaContact *)impliedValue listContactsIncludingOfflineAccounts] objectEnumerator];
246                         
247                         while ((containedContact = [contactEnumerator nextObject])) {
248                                 /* For each contact contained my the metacontact, check if its service class matches the current account's.
249                                  * If it does, add that contact to our list, using the contactController to get an AIListContact specific for the account.
250                                  */
251                                 if ([[[containedContact service] serviceClass] isEqualToString:[[account service] serviceClass]]) {
252                                         if ((contact = [[adium contactController] contactWithService:[account service]
253                                                                                                                                                  account:account
254                                                                                                                                                          UID:[containedContact UID]])) {
255                                                 [contactsSet addObject:contact];
256                                         }
257                                 }
258                         }
259                         
260                 } else {
261                         if ([impliedValue isKindOfClass:[AIListContact class]]) {
262                                 UID = [(AIListContact *)impliedValue UID];
263                         
264                         } else  if ([impliedValue isKindOfClass:[NSString class]]) {
265                                 UID = [[account service] filterUID:impliedValue removeIgnoredCharacters:YES];
266                         }
267                         
268                         if (UID) {
269                                 //Get a contact with this UID on the current account
270                                 if ((contact = [[adium contactController] contactWithService:[account service]
271                                                                                                                                          account:account 
272                                                                                                                                                  UID:UID])) {
273                                         [contactsSet addObject:contact];
274                                 }
275                         }
276                 }
277                         
278         }
279         
280         return contactsSet;
283 - (void)configureTextField
285         AIAccount *account = [[popUp_sheetAccounts selectedItem] representedObject];
286         NSEnumerator            *enumerator;
287     AIListContact               *contact;
288         
289         //Clear the completing strings
290         [field setCompletingStrings:nil];
291         
292         //Configure the auto-complete view to autocomplete for contacts matching the selected account's service
293     enumerator = [[[adium contactController] allContacts] objectEnumerator];
294     while ((contact = [enumerator nextObject])) {
295                 if (!account ||
296                         [contact service] == [account service]) {
297                         NSString *UID = [contact UID];
298                         [field addCompletionString:[contact formattedUID] withImpliedCompletion:UID];
299                         [field addCompletionString:[contact displayName] withImpliedCompletion:UID];
300                         [field addCompletionString:UID];
301                 }
302     }
305 #pragma mark Removing a contact from the  list
307 - (IBAction)removeSelection:(id)sender
309         int selection = [[table selectedRowIndexes] firstIndex];
310         
311         if (selection != NSNotFound) {
312                 AIListContact *contact = [listContents objectAtIndex:selection];
314                 //Remove from our list
315                 [listContents removeObject:contact];
316                 [table reloadData];
318                 //Update serverside
319                 [contact setIsOnPrivacyList:NO updateList:YES privacyType:(([self selectedPrivacyOption] == AIPrivacyOptionAllowUsers) ?
320                                                                                                                                    AIPrivacyTypePermit :
321                                                                                                                                    AIPrivacyTypeDeny)];
322         }
325 - (void)tableViewDeleteSelectedRows:(NSTableView *)tableView
327         [self removeSelection:tableView];
330 - (void)setAccountColumnsVisible:(BOOL)visible
332         if (accountColumnsVisible != visible) {
333                 if (visible) {
334                         [table addTableColumn:accountCol];
335                 } else {
336                         [table removeTableColumn:accountCol];                   
337                 }
339                 [table sizeToFit];
340                 accountColumnsVisible = visible;
341         }
343 #pragma mark Privacy options menu
345 - (NSMenu *)privacyOptionsMenu
347         //build the menu of states
348         NSMenu *stateMenu = [[NSMenu alloc] init];
350         NSMenuItem *menuItem;
351         
352         menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow anyone", nil) 
353                                                                                   action:NULL
354                                                                    keyEquivalent:@""];
355         [menuItem setTag:AIPrivacyOptionAllowAll];
356         [stateMenu addItem:menuItem];
357         [menuItem release];
359         menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow only contacts on my contact list", nil) 
360                                                                                   action:NULL
361                                                                    keyEquivalent:@""];
362         [menuItem setTag:AIPrivacyOptionAllowContactList];
363         [stateMenu addItem:menuItem];
364         [menuItem release];
366         menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow only certain contacts", nil) 
367                                                                                   action:NULL
368                                                                    keyEquivalent:@""];
369         [menuItem setTag:AIPrivacyOptionAllowUsers];
370         [stateMenu addItem:menuItem];
371         [menuItem release];
373         menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Block certain contacts", nil) 
374                                                                                   action:NULL
375                                                                    keyEquivalent:@""];
376         [menuItem setTag:AIPrivacyOptionDenyUsers];
377         [stateMenu addItem:menuItem];
378         [menuItem release];
380         /*
381         tmpItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Custom settings for each account", nil) action:NULL keyEquivalent:@""];
382         [tmpItem setRepresentedObject:[NSNumber numberWithInt:AIPrivacyOptionCustom]];
383         [stateMenu addItem:[tmpItem autorelease]];
384         */
386         return [stateMenu autorelease];
389 - (AIPrivacyOption)selectedPrivacyOption
391         return [[stateChooser selectedItem] tag];
395  * @brief Set a privacy option and update our view for it
397  * @param sender If nil, we update our display without attempting to change anything on our account
398  */
399 - (IBAction)setPrivacyOption:(id)sender
401         AIAccount<AIAccount_Privacy> *account = [self selectedAccount];
402         AIPrivacyOption privacyOption = [self selectedPrivacyOption];
404         //First, let's get the right tab view selected
405         switch (privacyOption) {
406                 case AIPrivacyOptionAllowAll:
407                 case AIPrivacyOptionAllowContactList:
408                 case AIPrivacyOptionCustom:
409                         if (![[[tabView_contactList selectedTabViewItem] identifier] isEqualToString:@"empty"]) {
410                                 [tabView_contactList selectTabViewItemWithIdentifier:@"empty"];
411                                 [tabView_contactList setHidden:YES];
413                                 NSRect frame = [[self window] frame];
414                                 float tabViewHeight = [tabView_contactList frame].size.height;
415                                 frame.size.height -= tabViewHeight;
416                                 frame.origin.y += tabViewHeight;
417                                 
418                                 //Don't resize vertically now...
419                                 [tabView_contactList setAutoresizingMask:NSViewWidthSizable];
421                                 [[self window] setMinSize:NSMakeSize(250, frame.size.height)];
422                                 [[self window] setMaxSize:NSMakeSize(FLT_MAX, frame.size.height)];
423                                 
424                                 AILog(@"Because of privacy option %i, resizing from %@ to %@",privacyOption,
425                                           NSStringFromRect([[self window] frame]),NSStringFromRect(frame));
426                                 [[self window] setFrame:frame display:YES animate:YES];
427                         }
428                         break;
429                         
430                 case AIPrivacyOptionAllowUsers:
431                 case AIPrivacyOptionDenyUsers:
432                         if (![[[tabView_contactList selectedTabViewItem] identifier] isEqualToString:@"list"]) {
433                                 [tabView_contactList selectTabViewItemWithIdentifier:@"list"];
435                                 NSRect frame = [[self window] frame];
436                                 float tabViewHeight = [tabView_contactList frame].size.height;
437                                 frame.size.height += tabViewHeight;
438                                 frame.origin.y -= tabViewHeight;
439                                 
440                                 [[self window] setMinSize:NSMakeSize(250, 320)];
441                                 [[self window] setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
442                                 
443                                 //Set frame after fixing our min/max size so the resize won't fail
444                                 AILog(@"Because of privacy option %i, resizing from %@ to %@",privacyOption,
445                                           NSStringFromRect([[self window] frame]),NSStringFromRect(frame));
446                                 [[self window] setFrame:frame display:YES animate:YES];
448                                 [tabView_contactList setHidden:NO];
450                                 //Allow resizing vertically again
451                                 [tabView_contactList setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
452                         }
453                         break;
454                 case AIPrivacyOptionDenyAll:
455                 case AIPrivacyOptionUnknown:
456                         NSLog(@"We should never see these...");
457                         break;
458         }
459         
460         if (sender) {
461                 if (account) {
462                         [account setPrivacyOptions:privacyOption];
463                         
464                 } else {
465                         NSEnumerator    *enumerator = [[[popUp_accounts menu] itemArray] objectEnumerator];
466                         NSMenuItem                                              *menuItem;
467                         AIAccount<AIAccount_Privacy>    *representedAccount;
469                         while ((menuItem = [enumerator nextObject])) {
470                                 if ((representedAccount = [menuItem representedObject])) {
471                                         [representedAccount setPrivacyOptions:privacyOption];
472                                 }
473                         }
474                 }
475         }
476         
477         //Now make our listContents array match the serverside arrays for the selected account(s)
478         [listContents removeAllObjects];
479         if ((privacyOption == AIPrivacyOptionAllowUsers) ||
480                 (privacyOption == AIPrivacyOptionDenyUsers)) {
481                 if (account) {
482                         [listContents addObjectsFromArray:[account listObjectsOnPrivacyList:((privacyOption == AIPrivacyOptionAllowUsers) ?
483                                                                                                                                                                  AIPrivacyTypePermit :
484                                                                                                                                                                  AIPrivacyTypeDeny)]];          
485                 } else {
486                         NSEnumerator                                    *enumerator = [[[popUp_accounts menu] itemArray] objectEnumerator];
487                         NSMenuItem                                              *menuItem;
488                         AIAccount<AIAccount_Privacy>    *representedAccount;
490                         while ((menuItem = [enumerator nextObject])) {
491                                 if ((representedAccount = [menuItem representedObject])) {
492                                         [listContents addObjectsFromArray:[representedAccount listObjectsOnPrivacyList:((privacyOption == AIPrivacyOptionAllowUsers) ?
493                                                                                                                                                                                                         AIPrivacyTypePermit :
494                                                                                                                                                                                                         AIPrivacyTypeDeny)]];           
495                                 }
496                         }
497                 }
498         }
500         [table reloadData];
503 - (void)selectPrivacyOption:(AIPrivacyOption)privacyOption
505         BOOL success = [stateChooser compatibleSelectItemWithTag:privacyOption];
506         if (privacyOption == AIPrivacyOptionCustom) {
507                 if (!success) {
508                         NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"(Multiple privacy levels are active)", nil) 
509                                                                                                                           action:NULL
510                                                                                                            keyEquivalent:@""];
511                         [menuItem setTag:AIPrivacyOptionCustom];
512                         [[stateChooser menu] addItem:menuItem];
513                         [menuItem release];
514                         
515                         success = [stateChooser compatibleSelectItemWithTag:privacyOption];
516                 }
518         } else {
519                 //Not on custom; make sure custom isn't still in the menu
520                 int customItemIndex = [stateChooser indexOfItemWithTag:AIPrivacyOptionCustom];
521                 if (customItemIndex != -1) {
522                         [[stateChooser menu] removeItemAtIndex:customItemIndex];
523                 }
524         }
526         //Now update our view for this privacy option
527         [self setPrivacyOption:nil];
530 #pragma mark Account menu
532  * @brief Return the currently selected account, or nil if the 'All' item is selected
533  */
534 - (AIAccount<AIAccount_Privacy> *)selectedAccount
536         return [[popUp_accounts selectedItem] representedObject];
540  * @brief Action called when the account selection changes
542  * Update our view and the privacy option menu to be appropriate for the newly selected account.
543  * This may be called with a sender of nil by code elsewhere to force an update
544  */
545 - (void)accountMenu:(AIAccountMenu *)inAccountMenu didSelectAccount:(AIAccount *)inAccount
547         if (inAccountMenu == accountMenu) {
548                 AIAccount<AIAccount_Privacy> *account = [self selectedAccount];
549                 if (account) {
550                         //Selected an account
551                         AIPrivacyOption privacyOption = [account privacyOptions];
552                         
553                         //Don't need the account column when we're showing for just one account
554                         [self setAccountColumnsVisible:NO];
556                         [self selectPrivacyOption:privacyOption];                       
558                 } else {
559                         //Selected 'All'. We need to determine what privacy option to display for the set of all accounts.
560                         AIPrivacyOption currentState = AIPrivacyOptionUnknown;
561                         NSEnumerator    *enumerator = [[[popUp_accounts menu] itemArray] objectEnumerator];
562                         NSMenuItem              *menuItem;
563                         
564                         while ((menuItem = [enumerator nextObject])) {
565                                 if ((account = [menuItem representedObject])) {
566                                         AIPrivacyOption accountState = [account privacyOptions];
567                                         
568                                         if (currentState == AIPrivacyOptionUnknown) {
569                                                 //We don't know the state of an account yet
570                                                 currentState = accountState;
571                                         } else if (accountState != currentState) {
572                                                 currentState = AIPrivacyOptionCustom;
573                                         }                               
574                                 }
575                         }
576                         
577                         [self setAccountColumnsVisible:YES];
579                         [self selectPrivacyOption:currentState];
580                 }
582         } else if (inAccountMenu == sheetAccountMenu) {
583                 //Update our sheet for the current account
584                 [self selectAccountInSheet:inAccount];
585         }
589  * @brief The 'All' menu item for accounts was selected
591  * We simulate an AIAccountMenu delegate call, since the All item was added by RAFBLockEditorWindowController.
592  */
593 - (IBAction)selectedAllAccountItem:(id)sender
595         AIAccountMenu *relevantAccountMenu = (([sender menu] == [popUp_accounts menu]) ?
596                                                                                   accountMenu :
597                                                                                   sheetAccountMenu);
599         [self accountMenu:relevantAccountMenu didSelectAccount:nil];
603  * @brief Select an account in our account menu, then update everything else to be appropriate for it
604  */
605 - (void)selectAccount:(AIAccount *)inAccount
607         [popUp_accounts selectItemWithRepresentedObject:inAccount];
608         
609         [self accountMenu:accountMenu didSelectAccount:inAccount];
613  * @brief Add account menu items to our location
615  * Implemented as required by the AccountMenuPlugin protocol.
617  * @param menuItemArray An <tt>NSArray</tt> of <tt>NSMenuItem</tt> objects to be added to the menu
618  */
619 - (void)accountMenu:(AIAccountMenu *)inAccountMenu didRebuildMenuItems:(NSArray *)menuItems
621         AIAccount        *previouslySelectedAccount = nil;
622         NSEnumerator *enumerator;
623         NSMenuItem       *menuItem;
624         NSMenu           *menu = [[NSMenu alloc] init];
626         /*
627          * accountMenu isn't set the first time we get here as the accountMenu is created. Similarly, sheetAccountMenu isn't created its first time.
628          * This code makes the (true) assumption that accountMenu is _always_ created before sheetAccountMenu.
629          */     
630         BOOL isPrimaryAccountMenu = (!accountMenu || (inAccountMenu == accountMenu));
632         if (isPrimaryAccountMenu) {
633                 if ([popUp_accounts menu]) {
634                         previouslySelectedAccount = [[popUp_accounts selectedItem] representedObject];
635                 }
636         } else if (inAccountMenu == sheetAccountMenu) {
637                 if ([popUp_sheetAccounts menu]) {
638                         previouslySelectedAccount = [[popUp_sheetAccounts selectedItem] representedObject];
639                 }               
640         }
642         //Add the All menu item first if we have more than one account listed
643         if ([menuItems count] > 1) {
644                 [menu addItemWithTitle:AILocalizedString(@"All", nll)
645                                                 target:self
646                                                 action:@selector(selectedAllAccountItem:)
647                                  keyEquivalent:@""];
648         }
650         /*
651          * As we enumerate, we:
652          *      1) Determine what state the accounts within the menu are in
653          *  2) Add the menu items to our menu
654          */
655         enumerator = [menuItems objectEnumerator];
656         while ((menuItem = [enumerator nextObject])) {          
657                 [menu addItem:menuItem];
658         }
660         if (isPrimaryAccountMenu) {
661                 [popUp_accounts setMenu:menu];
663                 /* Restore the previous account selection if there was one.
664                  * Whether there was one or not, this will cause the rest of our view update to match the new/current selection
665                  */
666                 [self selectAccount:previouslySelectedAccount];
668         } else {
669                 [popUp_sheetAccounts setMenu:menu];
670                 
671                 [self selectAccountInSheet:previouslySelectedAccount];
672         }
674         [menu release];
677 - (BOOL)accountMenu:(AIAccountMenu *)inAccountMenu shouldIncludeAccount:(AIAccount *)inAccount
679         BOOL isPrimaryAccountMenu = (!accountMenu || (inAccountMenu == accountMenu));
681         if (isPrimaryAccountMenu) {
682                 return ([inAccount online] &&
683                                 [inAccount conformsToProtocol:@protocol(AIAccount_Privacy)]);
684         } else {
685                 AIAccount *selectedPrimaryAccount = [self selectedAccount];
686                 if (selectedPrimaryAccount) {
687                         //An account is selected in the main window; only incldue that account in our sheet
688                         return (inAccount == selectedPrimaryAccount);
690                 } else {
691                         //'All' is selected in the main window; include all accounts which are online and support privacy
692                         return ([inAccount online] &&
693                                         [inAccount conformsToProtocol:@protocol(AIAccount_Privacy)]);                   
694                 }
695         }
698 - (void)privacySettingsChangedExternally:(NSNotification *)inNotification
700         [self accountMenu:accountMenu didSelectAccount:[self selectedAccount]]; 
703 - (NSSet *)updateListObject:(AIListObject *)inObject keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
705         if ([inModifiedKeys containsObject:KEY_IS_BLOCKED]) {
706                 [self privacySettingsChangedExternally:nil];
707         }
708         
709         return nil;
712 #pragma mark Table view
714 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
716         return [listContents count];
719 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
721         NSString                *identifier = [aTableColumn identifier];
722         AIListContact   *contact = [listContents objectAtIndex:rowIndex];
724         if ([identifier isEqualToString:@"icon"]) {
725                 return [contact menuIcon];
726                 
727         } else if ([identifier isEqualToString:@"contact"]) {
728                 return [contact formattedUID];
730         } else if ([identifier isEqualToString:@"account"]) {
731                 return [[contact account] formattedUID];
732         }
733         
734         return nil;
737 - (BOOL)writeListObjects:(NSArray *)inArray toPasteboard:(NSPasteboard*)pboard
739         [pboard declareTypes:[NSArray arrayWithObjects:@"AIListObject",@"AIListObjectUniqueIDs",nil] owner:self];
740         [pboard setString:@"Private" forType:@"AIListObject"];
742         if (dragItems != inArray) {
743                 [dragItems release];
744                 dragItems = [inArray retain];
745         }
746         
747         return YES;
750 - (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
751 {       
752         NSMutableArray  *itemArray = [NSMutableArray array];
753         NSEnumerator    *enumerator = [rows objectEnumerator];
754         NSNumber                *rowNumber;
755         while ((rowNumber = [enumerator nextObject])) {
756                 [itemArray addObject:[listContents objectAtIndex:[rowNumber intValue]]];
757         }
759         return [self writeListObjects:itemArray toPasteboard:pboard];
762 - (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard
764         NSMutableArray  *itemArray = [NSMutableArray array];
765         id                              item;
766         
767         unsigned int bufSize = [rowIndexes count];
768         unsigned int *buf = malloc(bufSize * sizeof(unsigned int));
769         unsigned int i;
770         
771         NSRange range = NSMakeRange([rowIndexes firstIndex], ([rowIndexes lastIndex]-[rowIndexes firstIndex]) + 1);
772         [rowIndexes getIndexes:buf maxCount:bufSize inIndexRange:&range];
773         
774         for (i = 0; i != bufSize; i++) {
775                 if ((item = [listContents objectAtIndex:buf[i]])) {
776                         [itemArray addObject:item];
777                 }
778         }
779         
780         free(buf);
781         
782         return [self writeListObjects:itemArray toPasteboard:pboard];
785 - (void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
787         //Provide an array of internalObjectIDs which can be used to reference all the dragged contacts
788         if ([type isEqualToString:@"AIListObjectUniqueIDs"]) {
789                 
790                 if (dragItems) {
791                         NSMutableArray  *dragItemsArray = [NSMutableArray array];
792                         NSEnumerator    *enumerator = [dragItems objectEnumerator];
793                         AIListObject    *listObject;
794                         
795                         while ((listObject = [enumerator nextObject])) {
796                                 [dragItemsArray addObject:[listObject internalObjectID]];
797                         }
798                         
799                         [sender setPropertyList:dragItemsArray forType:@"AIListObjectUniqueIDs"];
800                 }
801         }
804 - (NSDragOperation)tableView:(NSTableView*)tv
805                                 validateDrop:(id <NSDraggingInfo>)info
806                                  proposedRow:(int)row
807            proposedDropOperation:(NSTableViewDropOperation)op
809     
810     NSDragOperation dragOp = NSDragOperationCopy;
811         
812     if ([info draggingSource] == table) {
813                 dragOp =  NSDragOperationMove;
814     }
815     [tv setDropRow:row dropOperation:NSTableViewDropAbove];
816         
817     return dragOp;
820 - (void)addListObjectToList:(AIListObject *)listObject
822         AIListObject *containedObject;
823         NSEnumerator *enumerator;
825         if ([listObject isKindOfClass:[AIListGroup class]]) {
826                 enumerator = [[(AIListGroup *)listObject listContacts] objectEnumerator];
827                 while ((containedObject = [enumerator nextObject])) {
828                         [self addListObjectToList:containedObject];
829                 }
831         } else if ([listObject isKindOfClass:[AIMetaContact class]]) {
832                 enumerator = [[(AIMetaContact *)listObject listContacts] objectEnumerator];
833                 while ((containedObject = [enumerator nextObject])) {
834                         [self addListObjectToList:containedObject];
835                 }
837         } else if ([listObject isKindOfClass:[AIListContact class]]) {
838                 //if the account for this contact is connected...
839                 if ([[(AIListContact *)listObject account] online]) {
840                         [self addObject:(AIListContact *)listObject];
841                 }
842         }
845 - (BOOL)tableView:(NSTableView*)tv acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)op
847         BOOL accept = NO;
848     if (row < 0)
849                 row = 0;
850         
851         if ([[[info draggingPasteboard] types] containsObject:@"AIListObjectUniqueIDs"]) {
852                 NSArray                 *dragItemsUniqueIDs = [[info draggingPasteboard] propertyListForType:@"AIListObjectUniqueIDs"];
853                 NSString *uniqueUID;
854                 NSEnumerator *idEnumerator = [dragItemsUniqueIDs objectEnumerator];
855                 while ((uniqueUID = [idEnumerator nextObject]))
856                         [self addListObjectToList:[[adium contactController] existingListObjectWithUniqueID:uniqueUID]];
857                 accept = YES;
858         }
859         
860     return accept;
863 @end