2 // RAFBlockEditorWindow.m
5 // Created by Augie Fackler on 5/26/05.
6 // Copyright 2006 The Adium Team. All rights reserved.
9 #import "RAFBlockEditorWindowController.h"
10 #import "AIAccountController.h"
11 #import "AIContactController.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 #define BLOCK_EDITOR_TITLE AILocalizedString(@"Privacy Settings","Privacy Settings window title")
22 #define BLOCK_DONE AILocalizedString(@"Done","Done button for Privacy Settings")
23 #define BLOCK_BLOCK AILocalizedString(@"Add","Add button for Privacy Settings")
24 #define BLOCK_CANCEL AILocalizedString(@"Cancel","Cancel button for Privacy Settings")
25 #define BLOCK_ACCOUNT AILocalizedString(@"Account:",nil)
26 #define BLOCK_BUDDY_COL AILocalizedString(@"Contact","Title of column containing user IDs of blocked contacts")
27 #define BLOCK_ACCOUNT_COL AILocalizedString(@"Account","Title of column containing blocking accounts")
29 @interface RAFBlockEditorWindowController (PRIVATE)
30 - (NSMenu *)privacyOptionsMenu;
31 - (AIAccount<AIAccount_Privacy> *)selectedAccount;
32 - (void)configureTextField;
33 - (NSSet *)contactsFromTextField;
34 - (AIPrivacyOption)selectedPrivacyOption;
37 @implementation RAFBlockEditorWindowController
39 static RAFBlockEditorWindowController *sharedInstance = nil;
43 if (!sharedInstance) {
44 sharedInstance = [[self alloc] initWithWindowNibName:@"BlockEditorWindow"];
47 [sharedInstance showWindow:nil];
48 [[sharedInstance window] makeKeyAndOrderFront:nil];
53 [[self window] setTitle:BLOCK_EDITOR_TITLE];
54 [cancelButton setLocalizedString:BLOCK_CANCEL];
55 [blockButton setLocalizedString:BLOCK_BLOCK];
56 [accountText setLocalizedString:BLOCK_ACCOUNT];
57 [[buddyCol headerCell] setTitle:BLOCK_BUDDY_COL];
58 [[accountCol headerCell] setTitle:BLOCK_ACCOUNT_COL];
60 [label_account setLocalizedString:BLOCK_ACCOUNT];
61 [label_privacyLevel setLocalizedString:AILocalizedString(@"Privacy level:", nil)];
63 accountColumnsVisible = YES;
66 listContents = [[NSMutableArray alloc] init];
68 [stateChooser setMenu:[self privacyOptionsMenu]];
70 [[table tableColumnWithIdentifier:@"icon"] setDataCell:[[[NSImageCell alloc] init] autorelease]];
72 accountMenu = [[AIAccountMenu accountMenuWithDelegate:self
73 submenuType:AIAccountNoSubmenu
74 showTitleVerbs:NO] retain];
75 [table registerForDraggedTypes:[NSArray arrayWithObjects:@"AIListObject", @"AIListObjectUniqueIDs",nil]];
77 [[adium notificationCenter] addObserver:self
78 selector:@selector(privacySettingsChangedExternally:)
79 name:@"AIPrivacySettingsChangedOutsideOfPrivacyWindow"
82 [super windowDidLoad];
85 - (void)windowWillClose:(id)sender
87 [super windowWillClose:sender];
89 [[adium notificationCenter] removeObserver:self];
90 [sharedInstance release]; sharedInstance = nil;
93 - (NSString *)adiumFrameAutosaveName
95 return @"PrivacyWindow";
100 [accountCol release];
101 [accountMenu release];
102 [listContents release];
103 [listContentsAllAccounts release];
108 - (NSMutableArray*)listContents
113 - (void)setListContents:(NSArray*)newList
115 if (newList != listContents) {
116 [listContents release];
117 listContents = [newList mutableCopy];
121 #pragma mark Adding a contact to the list
122 - (IBAction)runBlockSheet:(id)sender
124 [field setStringValue:@""];
126 sheetAccountMenu = [[AIAccountMenu accountMenuWithDelegate:self
127 submenuType:AIAccountNoSubmenu
128 showTitleVerbs:NO] retain];
130 [NSApp beginSheet:sheet
131 modalForWindow:[self window]
133 didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
137 - (void)selectAccountInSheet:(AIAccount *)inAccount
139 [popUp_sheetAccounts selectItemWithRepresentedObject:inAccount];
140 [self configureTextField];
142 NSString *userNameLabel = [[inAccount service] userNameLabel];
144 [buddyText setStringValue:[(userNameLabel ? userNameLabel :
145 AILocalizedString(@"Contact ID",nil)) stringByAppendingString:@":"]];
148 - (IBAction)cancelBlockSheet:(id)sender
150 [NSApp endSheet:sheet];
153 - (void)addObject:(AIListContact *)inContact
155 if (![listContents containsObject:inContact]) {
156 [listContents addObject:inContact];
159 [inContact setIsOnPrivacyList:YES updateList:YES privacyType:(([self selectedPrivacyOption] == AIPrivacyOptionAllowUsers) ?
160 AIPrivacyTypePermit :
164 - (IBAction)didBlockSheet:(id)sender
166 NSSet *contactArray = [self contactsFromTextField];
168 //Add the contact immediately
169 if (contactArray && [contactArray count]) {
170 NSEnumerator *enumerator;
171 AIListContact *contact;
173 enumerator = [contactArray objectEnumerator];
174 while ((contact = [enumerator nextObject])) {
175 [self addObject:contact];
181 [NSApp endSheet:sheet];
185 - (void)didEndSheet:(NSWindow *)theSheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
187 [sheetAccountMenu release]; sheetAccountMenu = nil;
188 [theSheet orderOut:self];
192 * @brief Get a set of all contacts which are represented by the currently selected account and UID field
194 * @result A set of AIListContact objects
196 - (NSSet *)contactsFromTextField
198 AIListContact *contact = nil;
200 AIAccount *account = [[popUp_sheetAccounts selectedItem] representedObject];;
201 NSArray *accountArray;
202 NSMutableSet *contactsSet = [NSMutableSet set];
203 NSEnumerator *enumerator;
204 id impliedValue = [field impliedValue];
207 accountArray = [NSArray arrayWithObject:account];
210 NSMutableArray *tempArray = [NSMutableArray array];
211 NSMenuItem *menuItem;
213 enumerator = [[[popUp_sheetAccounts menu] itemArray] objectEnumerator];
214 while ((menuItem = [enumerator nextObject])) {
215 AIAccount *anAccount;
217 if ((anAccount = [menuItem representedObject])) {
218 [tempArray addObject:anAccount];
222 accountArray = tempArray;
225 enumerator = [accountArray objectEnumerator];
226 while ((account = [enumerator nextObject])) {
227 if ([impliedValue isKindOfClass:[AIMetaContact class]]) {
228 AIListContact *containedContact;
229 NSEnumerator *contactEnumerator = [[(AIMetaContact *)impliedValue listContactsIncludingOfflineAccounts] objectEnumerator];
231 while ((containedContact = [contactEnumerator nextObject])) {
232 /* For each contact contained my the metacontact, check if its service class matches the current account's.
233 * If it does, add that contact to our list, using the contactController to get an AIListContact specific for the account.
235 if ([[[containedContact service] serviceClass] isEqualToString:[[account service] serviceClass]]) {
236 contact = [[adium contactController] contactWithService:[account service]
238 UID:[containedContact UID]];
239 [contactsSet addObject:contact];
244 if ([impliedValue isKindOfClass:[AIListContact class]]) {
245 UID = [(AIListContact *)impliedValue UID];
247 } else if ([impliedValue isKindOfClass:[NSString class]]) {
248 UID = [[account service] filterUID:impliedValue removeIgnoredCharacters:YES];
252 //Get a contact with this UID on the current account
253 contact = [[adium contactController] contactWithService:[account service]
257 [contactsSet addObject:contact];
266 - (void)configureTextField
268 AIAccount *account = [[popUp_sheetAccounts selectedItem] representedObject];
269 NSEnumerator *enumerator;
270 AIListContact *contact;
272 //Clear the completing strings
273 [field setCompletingStrings:nil];
275 //Configure the auto-complete view to autocomplete for contacts matching the selected account's service
276 enumerator = [[[adium contactController] allContactsInGroup:nil subgroups:YES onAccount:nil] objectEnumerator];
277 while ((contact = [enumerator nextObject])) {
279 [contact service] == [account service]) {
280 NSString *UID = [contact UID];
281 [field addCompletionString:[contact formattedUID] withImpliedCompletion:UID];
282 [field addCompletionString:[contact displayName] withImpliedCompletion:UID];
283 [field addCompletionString:UID];
288 #pragma mark Removing a contact from the list
290 - (IBAction)removeSelection:(id)sender
292 int selection = [[table selectedRowIndexes] firstIndex];
294 if (selection != NSNotFound) {
295 AIListContact *contact = [listContents objectAtIndex:selection];
297 //Remove from our list
298 [listContents removeObject:contact];
302 [contact setIsOnPrivacyList:NO updateList:YES privacyType:(([self selectedPrivacyOption] == AIPrivacyOptionAllowUsers) ?
303 AIPrivacyTypePermit :
308 - (void)tableViewDeleteSelectedRows:(NSTableView *)tableView
310 [self removeSelection:tableView];
313 - (void)setAccountColumnsVisible:(BOOL)visible
315 if (accountColumnsVisible != visible) {
317 [table addTableColumn:accountCol];
319 [table removeTableColumn:accountCol];
323 accountColumnsVisible = visible;
326 #pragma mark Privacy options menu
328 - (NSMenu *)privacyOptionsMenu
330 //build the menu of states
331 NSMenu *stateMenu = [[NSMenu alloc] init];
333 NSMenuItem *menuItem;
335 menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow anyone", nil)
338 [menuItem setTag:AIPrivacyOptionAllowAll];
339 [stateMenu addItem:menuItem];
342 menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow only contacts on my contact list", nil)
345 [menuItem setTag:AIPrivacyOptionAllowContactList];
346 [stateMenu addItem:menuItem];
349 menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow only certain contacts", nil)
352 [menuItem setTag:AIPrivacyOptionAllowUsers];
353 [stateMenu addItem:menuItem];
356 menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Block certain contacts", nil)
359 [menuItem setTag:AIPrivacyOptionDenyUsers];
360 [stateMenu addItem:menuItem];
364 tmpItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Custom settings for each account", nil) action:NULL keyEquivalent:@""];
365 [tmpItem setRepresentedObject:[NSNumber numberWithInt:AIPrivacyOptionCustom]];
366 [stateMenu addItem:[tmpItem autorelease]];
369 return [stateMenu autorelease];
372 - (AIPrivacyOption)selectedPrivacyOption
374 return [[stateChooser selectedItem] tag];
378 * @brief Set a privacy option and update our view for it
380 * @param sender If nil, we update our display without attempting to change anything on our account
382 - (IBAction)setPrivacyOption:(id)sender
384 AIAccount<AIAccount_Privacy> *account = [self selectedAccount];
385 AIPrivacyOption privacyOption = [self selectedPrivacyOption];
387 //First, let's get the right tab view selected
388 switch (privacyOption) {
389 case AIPrivacyOptionAllowAll:
390 case AIPrivacyOptionAllowContactList:
391 case AIPrivacyOptionCustom:
392 if (![[[tabView_contactList selectedTabViewItem] identifier] isEqualToString:@"empty"]) {
393 [tabView_contactList selectTabViewItemWithIdentifier:@"empty"];
394 [tabView_contactList setHidden:YES];
396 NSRect frame = [[self window] frame];
397 float tabViewHeight = [tabView_contactList frame].size.height;
398 frame.size.height -= tabViewHeight;
399 frame.origin.y += tabViewHeight;
401 //Don't resize vertically now...
402 [tabView_contactList setAutoresizingMask:NSViewWidthSizable];
404 [[self window] setMinSize:NSMakeSize(250, frame.size.height)];
405 [[self window] setMaxSize:NSMakeSize(FLT_MAX, frame.size.height)];
406 [[self window] setFrame:frame display:YES animate:YES];
410 case AIPrivacyOptionAllowUsers:
411 case AIPrivacyOptionDenyUsers:
412 if (![[[tabView_contactList selectedTabViewItem] identifier] isEqualToString:@"list"]) {
413 [tabView_contactList selectTabViewItemWithIdentifier:@"list"];
415 NSRect frame = [[self window] frame];
416 float tabViewHeight = [tabView_contactList frame].size.height;
417 frame.size.height += tabViewHeight;
418 frame.origin.y -= tabViewHeight;
420 [[self window] setMinSize:NSMakeSize(250, 320)];
421 [[self window] setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
423 //Set frame after fixing our min/max size so the resize won't fail
424 [[self window] setFrame:frame display:YES animate:YES];
426 [tabView_contactList setHidden:NO];
428 //Allow resizing vertically again
429 [tabView_contactList setAutoresizingMask:(NSViewWidthSizable | NSViewHeightSizable)];
432 case AIPrivacyOptionDenyAll:
433 case AIPrivacyOptionUnknown:
434 NSLog(@"We should never see these...");
440 [account setPrivacyOptions:privacyOption];
443 NSEnumerator *enumerator = [[[popUp_accounts menu] itemArray] objectEnumerator];
444 NSMenuItem *menuItem;
445 AIAccount<AIAccount_Privacy> *representedAccount;
447 while ((menuItem = [enumerator nextObject])) {
448 if ((representedAccount = [menuItem representedObject])) {
449 [representedAccount setPrivacyOptions:privacyOption];
455 //Now make our listContents array match the serverside arrays for the selected account(s)
456 [listContents removeAllObjects];
457 if ((privacyOption == AIPrivacyOptionAllowUsers) ||
458 (privacyOption == AIPrivacyOptionDenyUsers)) {
460 [listContents addObjectsFromArray:[account listObjectsOnPrivacyList:((privacyOption == AIPrivacyOptionAllowUsers) ?
461 AIPrivacyTypePermit :
462 AIPrivacyTypeDeny)]];
464 NSEnumerator *enumerator = [[[popUp_accounts menu] itemArray] objectEnumerator];
465 NSMenuItem *menuItem;
466 AIAccount<AIAccount_Privacy> *representedAccount;
468 while ((menuItem = [enumerator nextObject])) {
469 if ((representedAccount = [menuItem representedObject])) {
470 [listContents addObjectsFromArray:[representedAccount listObjectsOnPrivacyList:((privacyOption == AIPrivacyOptionAllowUsers) ?
471 AIPrivacyTypePermit :
472 AIPrivacyTypeDeny)]];
481 - (void)selectPrivacyOption:(AIPrivacyOption)privacyOption
483 BOOL success = [stateChooser compatibleSelectItemWithTag:privacyOption];
484 if (privacyOption == AIPrivacyOptionCustom) {
486 NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"(Multiple privacy levels are active)", nil)
489 [menuItem setTag:AIPrivacyOptionCustom];
490 [[stateChooser menu] addItem:menuItem];
493 success = [stateChooser compatibleSelectItemWithTag:privacyOption];
497 //Not on custom; make sure custom isn't still in the menu
498 int customItemIndex = [stateChooser indexOfItemWithTag:AIPrivacyOptionCustom];
499 if (customItemIndex != -1) {
500 [[stateChooser menu] removeItemAtIndex:customItemIndex];
504 //Now update our view for this privacy option
505 [self setPrivacyOption:nil];
508 #pragma mark Account menu
510 * @brief Return the currently selected account, or nil if the 'All' item is selected
512 - (AIAccount<AIAccount_Privacy> *)selectedAccount
514 return [[popUp_accounts selectedItem] representedObject];
518 * @brief Action called when the account selection changes
520 * Update our view and the privacy option menu to be appropriate for the newly selected account.
521 * This may be called with a sender of nil by code elsewhere to force an update
523 - (void)accountMenu:(AIAccountMenu *)inAccountMenu didSelectAccount:(AIAccount *)inAccount
525 if (inAccountMenu == accountMenu) {
526 AIAccount<AIAccount_Privacy> *account = [self selectedAccount];
528 //Selected an account
529 AIPrivacyOption privacyOption = [account privacyOptions];
531 //Don't need the account column when we're showing for just one account
532 [self setAccountColumnsVisible:NO];
534 [self selectPrivacyOption:privacyOption];
537 //Selected 'All'. We need to determine what privacy option to display for the set of all accounts.
538 AIPrivacyOption currentState = AIPrivacyOptionUnknown;
539 NSEnumerator *enumerator = [[[popUp_accounts menu] itemArray] objectEnumerator];
540 NSMenuItem *menuItem;
542 while ((menuItem = [enumerator nextObject])) {
543 if ((account = [menuItem representedObject])) {
544 AIPrivacyOption accountState = [account privacyOptions];
546 if (currentState == AIPrivacyOptionUnknown) {
547 //We don't know the state of an account yet
548 currentState = accountState;
549 } else if (accountState != currentState) {
550 currentState = AIPrivacyOptionCustom;
555 [self setAccountColumnsVisible:YES];
557 [self selectPrivacyOption:currentState];
560 } else if (inAccountMenu == sheetAccountMenu) {
561 //Update our sheet for the current account
562 [self selectAccountInSheet:inAccount];
567 * @brief The 'All' menu item for accounts was selected
569 * We simulate an AIAccountMenu delegate call, since the All item was added by RAFBLockEditorWindowController.
571 - (IBAction)selectedAllAccountItem:(id)sender
573 AIAccountMenu *relevantAccountMenu = (([sender menu] == [popUp_accounts menu]) ?
577 [self accountMenu:relevantAccountMenu didSelectAccount:nil];
581 * @brief Select an account in our account menu, then update everything else to be appropriate for it
583 - (void)selectAccount:(AIAccount *)inAccount
585 [popUp_accounts selectItemWithRepresentedObject:inAccount];
587 [self accountMenu:accountMenu didSelectAccount:inAccount];
591 * @brief Add account menu items to our location
593 * Implemented as required by the AccountMenuPlugin protocol.
595 * @param menuItemArray An <tt>NSArray</tt> of <tt>NSMenuItem</tt> objects to be added to the menu
597 - (void)accountMenu:(AIAccountMenu *)inAccountMenu didRebuildMenuItems:(NSArray *)menuItems
599 AIAccount *previouslySelectedAccount = nil;
600 NSEnumerator *enumerator;
601 NSMenuItem *menuItem;
602 NSMenu *menu = [[NSMenu alloc] init];
605 * accountMenu isn't set the first time we get here as the accountMenu is created. Similarly, sheetAccountMenu isn't created its first time.
606 * This code makes the (true) assumption that accountMenu is _always_ created before sheetAccountMenu.
608 BOOL isPrimaryAccountMenu = (!accountMenu || (inAccountMenu == accountMenu));
610 if (isPrimaryAccountMenu) {
611 if ([popUp_accounts menu]) {
612 previouslySelectedAccount = [[popUp_accounts selectedItem] representedObject];
614 } else if (inAccountMenu == sheetAccountMenu) {
615 if ([popUp_sheetAccounts menu]) {
616 previouslySelectedAccount = [[popUp_sheetAccounts selectedItem] representedObject];
620 //Add the All menu item first if we have more than one account listed
621 if ([menuItems count] > 1) {
622 [menu addItemWithTitle:AILocalizedString(@"All", nll)
624 action:@selector(selectedAllAccountItem:)
629 * As we enumerate, we:
630 * 1) Determine what state the accounts within the menu are in
631 * 2) Add the menu items to our menu
633 enumerator = [menuItems objectEnumerator];
634 while ((menuItem = [enumerator nextObject])) {
635 [menu addItem:menuItem];
638 if (isPrimaryAccountMenu) {
639 [popUp_accounts setMenu:menu];
641 /* Restore the previous account selection if there was one.
642 * Whether there was one or not, this will cause the rest of our view update to match the new/current selection
644 [self selectAccount:previouslySelectedAccount];
647 [popUp_sheetAccounts setMenu:menu];
649 [self selectAccountInSheet:previouslySelectedAccount];
655 - (BOOL)accountMenu:(AIAccountMenu *)inAccountMenu shouldIncludeAccount:(AIAccount *)inAccount
657 BOOL isPrimaryAccountMenu = (!accountMenu || (inAccountMenu == accountMenu));
659 if (isPrimaryAccountMenu) {
660 return ([inAccount online] &&
661 [inAccount conformsToProtocol:@protocol(AIAccount_Privacy)]);
663 AIAccount *selectedPrimaryAccount = [self selectedAccount];
664 if (selectedPrimaryAccount) {
665 //An account is selected in the main window; only incldue that account in our sheet
666 return (inAccount == selectedPrimaryAccount);
669 //'All' is selected in the main window; include all accounts which are online and support privacy
670 return ([inAccount online] &&
671 [inAccount conformsToProtocol:@protocol(AIAccount_Privacy)]);
676 - (void)privacySettingsChangedExternally:(NSNotification *)inNotification
678 [self accountMenu:accountMenu didSelectAccount:[self selectedAccount]];
681 #pragma mark Table view
683 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
685 return [listContents count];
688 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
690 NSString *identifier = [aTableColumn identifier];
691 AIListContact *contact = [listContents objectAtIndex:rowIndex];
693 if ([identifier isEqualToString:@"icon"]) {
694 return [contact menuIcon];
696 } else if ([identifier isEqualToString:@"contact"]) {
697 return [contact formattedUID];
699 } else if ([identifier isEqualToString:@"account"]) {
700 return [[contact account] formattedUID];
706 //Below code is from RAFDragArrayController -- check to be sure it still applies here
707 - (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
710 if (dragItems != rows) {
712 dragItems = [rows retain];
715 [pboard declareTypes:[NSArray arrayWithObjects:@"AIListObject",@"AIListObjectUniqueIDs",nil] owner:self];
716 [pboard setString:@"Private" forType:@"AIListObject"];
718 #warning take this debug code out when we're sure this DnD operation stuff works
720 NSEnumerator *enumerator = [dragItems objectEnumerator];
721 AIListObject *listObject;
722 while ((listObject = [enumerator nextObject])) {
723 NSLog(@"dragging %@",[listObject internalObjectID]);
731 - (void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
733 //Provide an array of internalObjectIDs which can be used to reference all the dragged contacts
734 if ([type isEqualToString:@"AIListObjectUniqueIDs"]) {
737 NSMutableArray *dragItemsArray = [NSMutableArray array];
738 NSEnumerator *enumerator = [dragItems objectEnumerator];
739 AIListObject *listObject;
741 while ((listObject = [enumerator nextObject])) {
742 [dragItemsArray addObject:[listObject internalObjectID]];
745 [sender setPropertyList:dragItemsArray forType:@"AIListObjectUniqueIDs"];
750 - (NSDragOperation)tableView:(NSTableView*)tv
751 validateDrop:(id <NSDraggingInfo>)info
753 proposedDropOperation:(NSTableViewDropOperation)op
756 NSDragOperation dragOp = NSDragOperationCopy;
758 if ([info draggingSource] == table) {
759 dragOp = NSDragOperationMove;
761 [tv setDropRow:row dropOperation:NSTableViewDropAbove];
766 - (void)addListObjectToList:(AIListObject *)listObject
769 NSEnumerator *groupEnum;
770 if ([listObject isMemberOfClass:[AIListGroup class]]) {
771 groupEnum = [[(AIListGroup *)listObject listContacts] objectEnumerator];
772 while ((tmp = [groupEnum nextObject]))
773 [self addListObjectToList:tmp];
774 } else if ([listObject isMemberOfClass:[AIMetaContact class]]) {
775 groupEnum = [[(AIMetaContact *)listObject listContacts] objectEnumerator];
776 while ((tmp = [groupEnum nextObject]))
777 [self addListObjectToList:tmp];
778 } else if ([listObject isMemberOfClass:[AIListContact class]]) {
779 //if the account for this contact is connected...
780 if ([[[(AIListContact *)listObject account] statusObjectForKey:@"Online"] boolValue])
781 [self addObject:(AIListContact *)listObject];
785 - (BOOL)tableView:(NSTableView*)tv acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)op
791 if ([[[info draggingPasteboard] types] containsObject:@"AIListObjectUniqueIDs"]) {
792 NSArray *dragItemsUniqueIDs = [[info draggingPasteboard] propertyListForType:@"AIListObjectUniqueIDs"];
794 NSEnumerator *idEnumerator = [dragItemsUniqueIDs objectEnumerator];
795 while ((uniqueUID = [idEnumerator nextObject]))
796 [self addListObjectToList:[[adium contactController] existingListObjectWithUniqueID:uniqueUID]];