Prevent sending 0-byte files. Fixes #8711.
[adiumx.git] / Source / RAFBlockEditorWindowController.m
blob1bcb185cd58ad50a6a4e45ef29336750cf2d57c0
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         NSIndexSet              *selectedItems = [table selectedRowIndexes];
310         
311         // If there's anything selected..
312         if ([selectedItems count]) {
313                 AIListContact   *contact;
314                 
315                 // Iterate through the selected rows (backwards)
316                 for (int selection = [selectedItems lastIndex]; selection != NSNotFound; selection = [selectedItems indexLessThanIndex:selection]) {
317                         contact = [listContents objectAtIndex:selection];
318                         // Remove from the serverside list
319                         [contact setIsOnPrivacyList:NO updateList:YES privacyType:(([self selectedPrivacyOption] == AIPrivacyOptionAllowUsers) ?
320                                                                                                                                            AIPrivacyTypePermit :
321                                                                                                                                            AIPrivacyTypeDeny)];
322                         [listContents removeObject:contact];
323                 }
324                 
325                 [table reloadData];
326                 [table deselectAll:nil];
327         }
331 - (void)tableViewDeleteSelectedRows:(NSTableView *)tableView
333         [self removeSelection:tableView];
336 - (void)setAccountColumnsVisible:(BOOL)visible
338         if (accountColumnsVisible != visible) {
339                 if (visible) {
340                         [table addTableColumn:accountCol];
341                 } else {
342                         [table removeTableColumn:accountCol];                   
343                 }
345                 [table sizeToFit];
346                 accountColumnsVisible = visible;
347         }
349 #pragma mark Privacy options menu
351 - (NSMenu *)privacyOptionsMenu
353         //build the menu of states
354         NSMenu *stateMenu = [[NSMenu alloc] init];
356         NSMenuItem *menuItem;
357         
358         menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow anyone", nil) 
359                                                                                   action:NULL
360                                                                    keyEquivalent:@""];
361         [menuItem setTag:AIPrivacyOptionAllowAll];
362         [stateMenu addItem:menuItem];
363         [menuItem release];
365         menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow only contacts on my contact list", nil) 
366                                                                                   action:NULL
367                                                                    keyEquivalent:@""];
368         [menuItem setTag:AIPrivacyOptionAllowContactList];
369         [stateMenu addItem:menuItem];
370         [menuItem release];
372         menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow only certain contacts", nil) 
373                                                                                   action:NULL
374                                                                    keyEquivalent:@""];
375         [menuItem setTag:AIPrivacyOptionAllowUsers];
376         [stateMenu addItem:menuItem];
377         [menuItem release];
379         menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Block certain contacts", nil) 
380                                                                                   action:NULL
381                                                                    keyEquivalent:@""];
382         [menuItem setTag:AIPrivacyOptionDenyUsers];
383         [stateMenu addItem:menuItem];
384         [menuItem release];
386         /*
387         tmpItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Custom settings for each account", nil) action:NULL keyEquivalent:@""];
388         [tmpItem setRepresentedObject:[NSNumber numberWithInt:AIPrivacyOptionCustom]];
389         [stateMenu addItem:[tmpItem autorelease]];
390         */
392         return [stateMenu autorelease];
395 - (AIPrivacyOption)selectedPrivacyOption
397         return [[stateChooser selectedItem] tag];
401  * @brief Set a privacy option and update our view for it
403  * @param sender If nil, we update our display without attempting to change anything on our account
404  */
405 - (IBAction)setPrivacyOption:(id)sender
407         AIAccount<AIAccount_Privacy> *account = [self selectedAccount];
408         AIPrivacyOption privacyOption = [self selectedPrivacyOption];
410         //First, let's get the right tab view selected
411         switch (privacyOption) {
412                 case AIPrivacyOptionAllowAll:
413                 case AIPrivacyOptionAllowContactList:
414                 case AIPrivacyOptionCustom:
415                         if (![[[tabView_contactList selectedTabViewItem] identifier] isEqualToString:@"empty"]) {
416                                 [tabView_contactList selectTabViewItemWithIdentifier:@"empty"];
417                                 [tabView_contactList setHidden:YES];
419                                 NSRect frame = [[self window] frame];
420                                 float tabViewHeight = [tabView_contactList frame].size.height;
421                                 frame.size.height -= tabViewHeight;
422                                 frame.origin.y += tabViewHeight;
423                                 
424                                 //Don't resize vertically now...
425                                 [tabView_contactList setAutoresizingMask:NSViewWidthSizable];
427                                 [[self window] setMinSize:NSMakeSize(250, frame.size.height)];
428                                 [[self window] setMaxSize:NSMakeSize(FLT_MAX, frame.size.height)];
429                                 
430                                 AILog(@"Because of privacy option %i, resizing from %@ to %@",privacyOption,
431                                           NSStringFromRect([[self window] frame]),NSStringFromRect(frame));
432                                 [[self window] setFrame:frame display:YES animate:YES];
433                         }
434                         break;
435                         
436                 case AIPrivacyOptionAllowUsers:
437                 case AIPrivacyOptionDenyUsers:
438                         if (![[[tabView_contactList selectedTabViewItem] identifier] isEqualToString:@"list"]) {
439                                 [tabView_contactList selectTabViewItemWithIdentifier:@"list"];
441                                 NSRect frame = [[self window] frame];
442                                 float tabViewHeight = [tabView_contactList frame].size.height;
443                                 frame.size.height += tabViewHeight;
444                                 frame.origin.y -= tabViewHeight;
445                                 
446                                 [[self window] setMinSize:NSMakeSize(250, 320)];
447                                 [[self window] setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
448                                 
449                                 //Set frame after fixing our min/max size so the resize won't fail
450                                 AILog(@"Because of privacy option %i, resizing from %@ to %@",privacyOption,
451                                           NSStringFromRect([[self window] frame]),NSStringFromRect(frame));
452                                 [[self window] setFrame:frame display:YES animate:YES];
454                                 [tabView_contactList setHidden:NO];
456                                 //Allow resizing vertically again
457                                 [tabView_contactList setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
458                         }
459                         break;
460                 case AIPrivacyOptionDenyAll:
461                 case AIPrivacyOptionUnknown:
462                         NSLog(@"We should never see these...");
463                         break;
464         }
465         
466         if (sender) {
467                 if (account) {
468                         [account setPrivacyOptions:privacyOption];
469                         
470                 } else {
471                         NSEnumerator    *enumerator = [[[popUp_accounts menu] itemArray] objectEnumerator];
472                         NSMenuItem                                              *menuItem;
473                         AIAccount<AIAccount_Privacy>    *representedAccount;
475                         while ((menuItem = [enumerator nextObject])) {
476                                 if ((representedAccount = [menuItem representedObject])) {
477                                         [representedAccount setPrivacyOptions:privacyOption];
478                                 }
479                         }
480                 }
481         }
482         
483         //Now make our listContents array match the serverside arrays for the selected account(s)
484         [listContents removeAllObjects];
485         if ((privacyOption == AIPrivacyOptionAllowUsers) ||
486                 (privacyOption == AIPrivacyOptionDenyUsers)) {
487                 if (account) {
488                         [listContents addObjectsFromArray:[account listObjectsOnPrivacyList:((privacyOption == AIPrivacyOptionAllowUsers) ?
489                                                                                                                                                                  AIPrivacyTypePermit :
490                                                                                                                                                                  AIPrivacyTypeDeny)]];          
491                 } else {
492                         NSEnumerator                                    *enumerator = [[[popUp_accounts menu] itemArray] objectEnumerator];
493                         NSMenuItem                                              *menuItem;
494                         AIAccount<AIAccount_Privacy>    *representedAccount;
496                         while ((menuItem = [enumerator nextObject])) {
497                                 if ((representedAccount = [menuItem representedObject])) {
498                                         [listContents addObjectsFromArray:[representedAccount listObjectsOnPrivacyList:((privacyOption == AIPrivacyOptionAllowUsers) ?
499                                                                                                                                                                                                         AIPrivacyTypePermit :
500                                                                                                                                                                                                         AIPrivacyTypeDeny)]];           
501                                 }
502                         }
503                 }
504         }
506         [table reloadData];
509 - (void)selectPrivacyOption:(AIPrivacyOption)privacyOption
511         BOOL success = [stateChooser compatibleSelectItemWithTag:privacyOption];
512         if (privacyOption == AIPrivacyOptionCustom) {
513                 if (!success) {
514                         NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"(Multiple privacy levels are active)", nil) 
515                                                                                                                           action:NULL
516                                                                                                            keyEquivalent:@""];
517                         [menuItem setTag:AIPrivacyOptionCustom];
518                         [[stateChooser menu] addItem:menuItem];
519                         [menuItem release];
520                         
521                         success = [stateChooser compatibleSelectItemWithTag:privacyOption];
522                 }
524         } else {
525                 //Not on custom; make sure custom isn't still in the menu
526                 int customItemIndex = [stateChooser indexOfItemWithTag:AIPrivacyOptionCustom];
527                 if (customItemIndex != -1) {
528                         [[stateChooser menu] removeItemAtIndex:customItemIndex];
529                 }
530         }
532         //Now update our view for this privacy option
533         [self setPrivacyOption:nil];
536 #pragma mark Account menu
538  * @brief Return the currently selected account, or nil if the 'All' item is selected
539  */
540 - (AIAccount<AIAccount_Privacy> *)selectedAccount
542         return [[popUp_accounts selectedItem] representedObject];
546  * @brief Action called when the account selection changes
548  * Update our view and the privacy option menu to be appropriate for the newly selected account.
549  * This may be called with a sender of nil by code elsewhere to force an update
550  */
551 - (void)accountMenu:(AIAccountMenu *)inAccountMenu didSelectAccount:(AIAccount *)inAccount
553         if (inAccountMenu == accountMenu) {
554                 AIAccount<AIAccount_Privacy> *account = [self selectedAccount];
555                 if (account) {
556                         //Selected an account
557                         AIPrivacyOption privacyOption = [account privacyOptions];
558                         
559                         //Don't need the account column when we're showing for just one account
560                         [self setAccountColumnsVisible:NO];
562                         [self selectPrivacyOption:privacyOption];                       
564                 } else {
565                         //Selected 'All'. We need to determine what privacy option to display for the set of all accounts.
566                         AIPrivacyOption currentState = AIPrivacyOptionUnknown;
567                         NSEnumerator    *enumerator = [[[popUp_accounts menu] itemArray] objectEnumerator];
568                         NSMenuItem              *menuItem;
569                         
570                         while ((menuItem = [enumerator nextObject])) {
571                                 if ((account = [menuItem representedObject])) {
572                                         AIPrivacyOption accountState = [account privacyOptions];
573                                         
574                                         if (currentState == AIPrivacyOptionUnknown) {
575                                                 //We don't know the state of an account yet
576                                                 currentState = accountState;
577                                         } else if (accountState != currentState) {
578                                                 currentState = AIPrivacyOptionCustom;
579                                         }                               
580                                 }
581                         }
582                         
583                         [self setAccountColumnsVisible:YES];
585                         [self selectPrivacyOption:currentState];
586                 }
588         } else if (inAccountMenu == sheetAccountMenu) {
589                 //Update our sheet for the current account
590                 [self selectAccountInSheet:inAccount];
591         }
595  * @brief The 'All' menu item for accounts was selected
597  * We simulate an AIAccountMenu delegate call, since the All item was added by RAFBLockEditorWindowController.
598  */
599 - (IBAction)selectedAllAccountItem:(id)sender
601         AIAccountMenu *relevantAccountMenu = (([sender menu] == [popUp_accounts menu]) ?
602                                                                                   accountMenu :
603                                                                                   sheetAccountMenu);
605         [self accountMenu:relevantAccountMenu didSelectAccount:nil];
609  * @brief Select an account in our account menu, then update everything else to be appropriate for it
610  */
611 - (void)selectAccount:(AIAccount *)inAccount
613         [popUp_accounts selectItemWithRepresentedObject:inAccount];
614         
615         [self accountMenu:accountMenu didSelectAccount:inAccount];
619  * @brief Add account menu items to our location
621  * Implemented as required by the AccountMenuPlugin protocol.
623  * @param menuItemArray An <tt>NSArray</tt> of <tt>NSMenuItem</tt> objects to be added to the menu
624  */
625 - (void)accountMenu:(AIAccountMenu *)inAccountMenu didRebuildMenuItems:(NSArray *)menuItems
627         AIAccount        *previouslySelectedAccount = nil;
628         NSEnumerator *enumerator;
629         NSMenuItem       *menuItem;
630         NSMenu           *menu = [[NSMenu alloc] init];
632         /*
633          * accountMenu isn't set the first time we get here as the accountMenu is created. Similarly, sheetAccountMenu isn't created its first time.
634          * This code makes the (true) assumption that accountMenu is _always_ created before sheetAccountMenu.
635          */     
636         BOOL isPrimaryAccountMenu = (!accountMenu || (inAccountMenu == accountMenu));
638         if (isPrimaryAccountMenu) {
639                 if ([popUp_accounts menu]) {
640                         previouslySelectedAccount = [[popUp_accounts selectedItem] representedObject];
641                 }
642         } else if (inAccountMenu == sheetAccountMenu) {
643                 if ([popUp_sheetAccounts menu]) {
644                         previouslySelectedAccount = [[popUp_sheetAccounts selectedItem] representedObject];
645                 }               
646         }
648         //Add the All menu item first if we have more than one account listed
649         if ([menuItems count] > 1) {
650                 [menu addItemWithTitle:AILocalizedString(@"All", nll)
651                                                 target:self
652                                                 action:@selector(selectedAllAccountItem:)
653                                  keyEquivalent:@""];
654         }
656         /*
657          * As we enumerate, we:
658          *      1) Determine what state the accounts within the menu are in
659          *  2) Add the menu items to our menu
660          */
661         enumerator = [menuItems objectEnumerator];
662         while ((menuItem = [enumerator nextObject])) {          
663                 [menu addItem:menuItem];
664         }
666         if (isPrimaryAccountMenu) {
667                 [popUp_accounts setMenu:menu];
669                 /* Restore the previous account selection if there was one.
670                  * Whether there was one or not, this will cause the rest of our view update to match the new/current selection
671                  */
672                 [self selectAccount:previouslySelectedAccount];
674         } else {
675                 [popUp_sheetAccounts setMenu:menu];
676                 
677                 [self selectAccountInSheet:previouslySelectedAccount];
678         }
680         [menu release];
683 - (BOOL)accountMenu:(AIAccountMenu *)inAccountMenu shouldIncludeAccount:(AIAccount *)inAccount
685         BOOL isPrimaryAccountMenu = (!accountMenu || (inAccountMenu == accountMenu));
687         if (isPrimaryAccountMenu) {
688                 return ([inAccount online] &&
689                                 [inAccount conformsToProtocol:@protocol(AIAccount_Privacy)]);
690         } else {
691                 AIAccount *selectedPrimaryAccount = [self selectedAccount];
692                 if (selectedPrimaryAccount) {
693                         //An account is selected in the main window; only incldue that account in our sheet
694                         return (inAccount == selectedPrimaryAccount);
696                 } else {
697                         //'All' is selected in the main window; include all accounts which are online and support privacy
698                         return ([inAccount online] &&
699                                         [inAccount conformsToProtocol:@protocol(AIAccount_Privacy)]);                   
700                 }
701         }
704 - (void)privacySettingsChangedExternally:(NSNotification *)inNotification
706         [self accountMenu:accountMenu didSelectAccount:[self selectedAccount]]; 
709 - (NSSet *)updateListObject:(AIListObject *)inObject keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
711         if ([inModifiedKeys containsObject:KEY_IS_BLOCKED]) {
712                 [self privacySettingsChangedExternally:nil];
713         }
714         
715         return nil;
718 #pragma mark Table view
720 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
722         return [listContents count];
725 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
727         NSString                *identifier = [aTableColumn identifier];
728         AIListContact   *contact = [listContents objectAtIndex:rowIndex];
730         if ([identifier isEqualToString:@"icon"]) {
731                 return [contact menuIcon];
732                 
733         } else if ([identifier isEqualToString:@"contact"]) {
734                 return [contact formattedUID];
736         } else if ([identifier isEqualToString:@"account"]) {
737                 return [[contact account] formattedUID];
738         }
739         
740         return nil;
743 - (BOOL)writeListObjects:(NSArray *)inArray toPasteboard:(NSPasteboard*)pboard
745         [pboard declareTypes:[NSArray arrayWithObjects:@"AIListObject",@"AIListObjectUniqueIDs",nil] owner:self];
746         [pboard setString:@"Private" forType:@"AIListObject"];
748         if (dragItems != inArray) {
749                 [dragItems release];
750                 dragItems = [inArray retain];
751         }
752         
753         return YES;
756 - (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
757 {       
758         NSMutableArray  *itemArray = [NSMutableArray array];
759         NSEnumerator    *enumerator = [rows objectEnumerator];
760         NSNumber                *rowNumber;
761         while ((rowNumber = [enumerator nextObject])) {
762                 [itemArray addObject:[listContents objectAtIndex:[rowNumber intValue]]];
763         }
765         return [self writeListObjects:itemArray toPasteboard:pboard];
768 - (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard
770         NSMutableArray  *itemArray = [NSMutableArray array];
771         id                              item;
772         
773         unsigned int bufSize = [rowIndexes count];
774         unsigned int *buf = malloc(bufSize * sizeof(unsigned int));
775         unsigned int i;
776         
777         NSRange range = NSMakeRange([rowIndexes firstIndex], ([rowIndexes lastIndex]-[rowIndexes firstIndex]) + 1);
778         [rowIndexes getIndexes:buf maxCount:bufSize inIndexRange:&range];
779         
780         for (i = 0; i != bufSize; i++) {
781                 if ((item = [listContents objectAtIndex:buf[i]])) {
782                         [itemArray addObject:item];
783                 }
784         }
785         
786         free(buf);
787         
788         return [self writeListObjects:itemArray toPasteboard:pboard];
791 - (void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
793         //Provide an array of internalObjectIDs which can be used to reference all the dragged contacts
794         if ([type isEqualToString:@"AIListObjectUniqueIDs"]) {
795                 
796                 if (dragItems) {
797                         NSMutableArray  *dragItemsArray = [NSMutableArray array];
798                         NSEnumerator    *enumerator = [dragItems objectEnumerator];
799                         AIListObject    *listObject;
800                         
801                         while ((listObject = [enumerator nextObject])) {
802                                 [dragItemsArray addObject:[listObject internalObjectID]];
803                         }
804                         
805                         [sender setPropertyList:dragItemsArray forType:@"AIListObjectUniqueIDs"];
806                 }
807         }
810 - (NSDragOperation)tableView:(NSTableView*)tv
811                                 validateDrop:(id <NSDraggingInfo>)info
812                                  proposedRow:(int)row
813            proposedDropOperation:(NSTableViewDropOperation)op
815     
816     NSDragOperation dragOp = NSDragOperationCopy;
817         
818     if ([info draggingSource] == table) {
819                 dragOp =  NSDragOperationMove;
820     }
821     [tv setDropRow:row dropOperation:NSTableViewDropAbove];
822         
823     return dragOp;
826 - (void)addListObjectToList:(AIListObject *)listObject
828         AIListObject *containedObject;
829         NSEnumerator *enumerator;
831         if ([listObject isKindOfClass:[AIListGroup class]]) {
832                 enumerator = [[(AIListGroup *)listObject listContacts] objectEnumerator];
833                 while ((containedObject = [enumerator nextObject])) {
834                         [self addListObjectToList:containedObject];
835                 }
837         } else if ([listObject isKindOfClass:[AIMetaContact class]]) {
838                 enumerator = [[(AIMetaContact *)listObject listContacts] objectEnumerator];
839                 while ((containedObject = [enumerator nextObject])) {
840                         [self addListObjectToList:containedObject];
841                 }
843         } else if ([listObject isKindOfClass:[AIListContact class]]) {
844                 //if the account for this contact is connected...
845                 if ([[(AIListContact *)listObject account] online]) {
846                         [self addObject:(AIListContact *)listObject];
847                 }
848         }
851 - (BOOL)tableView:(NSTableView*)tv acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)op
853         BOOL accept = NO;
854     if (row < 0)
855                 row = 0;
856         
857         if ([[[info draggingPasteboard] types] containsObject:@"AIListObjectUniqueIDs"]) {
858                 NSArray                 *dragItemsUniqueIDs = [[info draggingPasteboard] propertyListForType:@"AIListObjectUniqueIDs"];
859                 NSString *uniqueUID;
860                 NSEnumerator *idEnumerator = [dragItemsUniqueIDs objectEnumerator];
861                 while ((uniqueUID = [idEnumerator nextObject]))
862                         [self addListObjectToList:[[adium contactController] existingListObjectWithUniqueID:uniqueUID]];
863                 accept = YES;
864         }
865         
866     return accept;
869 @end