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 <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;
29 @implementation RAFBlockEditorWindowController
31 static RAFBlockEditorWindowController *sharedInstance = nil;
35 if (!sharedInstance) {
36 sharedInstance = [[self alloc] initWithWindowNibName:@"BlockEditorWindow"];
39 [sharedInstance showWindow:nil];
40 [[sharedInstance window] makeKeyAndOrderFront:nil];
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)];
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)];
68 accountColumnsVisible = YES;
71 listContents = [[NSMutableArray alloc] init];
73 [stateChooser setMenu:[self privacyOptionsMenu]];
75 [[table tableColumnWithIdentifier:@"icon"] setDataCell:[[[NSImageCell alloc] init] autorelease]];
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"
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";
109 [accountCol release];
110 [accountMenu release];
111 [listContents release];
112 [listContentsAllAccounts release];
117 - (NSMutableArray*)listContents
122 - (void)setListContents:(NSArray*)newList
124 if (newList != listContents) {
125 [listContents release];
126 listContents = [newList mutableCopy];
130 #pragma mark Adding a contact to the list
132 - (void)selectAccountInSheet:(AIAccount *)inAccount
134 [popUp_sheetAccounts selectItemWithRepresentedObject:inAccount];
135 [self configureTextField];
137 NSString *userNameLabel = [[inAccount service] userNameLabel];
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:@""];
149 sheetAccountMenu = [[AIAccountMenu accountMenuWithDelegate:self
150 submenuType:AIAccountNoSubmenu
151 showTitleVerbs:NO] retain];
152 [self selectAccountInSheet:[[popUp_sheetAccounts selectedItem] representedObject]];
154 [NSApp beginSheet:sheet
155 modalForWindow:[self window]
157 didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
162 - (IBAction)cancelBlockSheet:(id)sender
164 [NSApp endSheet:sheet];
167 - (void)addObject:(AIListContact *)inContact
170 if (![listContents containsObject:inContact]) {
171 [listContents addObject:inContact];
174 [inContact setIsOnPrivacyList:YES updateList:YES privacyType:(([self selectedPrivacyOption] == AIPrivacyOptionAllowUsers) ?
175 AIPrivacyTypePermit :
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;
189 enumerator = [contactArray objectEnumerator];
190 while ((contact = [enumerator nextObject])) {
191 [self addObject:contact];
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
212 - (NSSet *)contactsFromTextField
214 AIListContact *contact = nil;
216 AIAccount *account = [[popUp_sheetAccounts selectedItem] representedObject];;
217 NSArray *accountArray;
218 NSMutableSet *contactsSet = [NSMutableSet set];
219 NSEnumerator *enumerator;
220 id impliedValue = [field impliedValue];
223 accountArray = [NSArray arrayWithObject:account];
226 NSMutableArray *tempArray = [NSMutableArray array];
227 NSMenuItem *menuItem;
229 enumerator = [[[popUp_sheetAccounts menu] itemArray] objectEnumerator];
230 while ((menuItem = [enumerator nextObject])) {
231 AIAccount *anAccount;
233 if ((anAccount = [menuItem representedObject])) {
234 [tempArray addObject:anAccount];
238 accountArray = tempArray;
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];
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.
251 if ([[[containedContact service] serviceClass] isEqualToString:[[account service] serviceClass]]) {
252 if ((contact = [[adium contactController] contactWithService:[account service]
254 UID:[containedContact UID]])) {
255 [contactsSet addObject:contact];
261 if ([impliedValue isKindOfClass:[AIListContact class]]) {
262 UID = [(AIListContact *)impliedValue UID];
264 } else if ([impliedValue isKindOfClass:[NSString class]]) {
265 UID = [[account service] filterUID:impliedValue removeIgnoredCharacters:YES];
269 //Get a contact with this UID on the current account
270 if ((contact = [[adium contactController] contactWithService:[account service]
273 [contactsSet addObject:contact];
283 - (void)configureTextField
285 AIAccount *account = [[popUp_sheetAccounts selectedItem] representedObject];
286 NSEnumerator *enumerator;
287 AIListContact *contact;
289 //Clear the completing strings
290 [field setCompletingStrings:nil];
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])) {
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];
305 #pragma mark Removing a contact from the list
307 - (IBAction)removeSelection:(id)sender
309 int selection = [[table selectedRowIndexes] firstIndex];
311 if (selection != NSNotFound) {
312 AIListContact *contact = [listContents objectAtIndex:selection];
314 //Remove from our list
315 [listContents removeObject:contact];
319 [contact setIsOnPrivacyList:NO updateList:YES privacyType:(([self selectedPrivacyOption] == AIPrivacyOptionAllowUsers) ?
320 AIPrivacyTypePermit :
325 - (void)tableViewDeleteSelectedRows:(NSTableView *)tableView
327 [self removeSelection:tableView];
330 - (void)setAccountColumnsVisible:(BOOL)visible
332 if (accountColumnsVisible != visible) {
334 [table addTableColumn:accountCol];
336 [table removeTableColumn:accountCol];
340 accountColumnsVisible = visible;
343 #pragma mark Privacy options menu
345 - (NSMenu *)privacyOptionsMenu
347 //build the menu of states
348 NSMenu *stateMenu = [[NSMenu alloc] init];
350 NSMenuItem *menuItem;
352 menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow anyone", nil)
355 [menuItem setTag:AIPrivacyOptionAllowAll];
356 [stateMenu addItem:menuItem];
359 menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow only contacts on my contact list", nil)
362 [menuItem setTag:AIPrivacyOptionAllowContactList];
363 [stateMenu addItem:menuItem];
366 menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow only certain contacts", nil)
369 [menuItem setTag:AIPrivacyOptionAllowUsers];
370 [stateMenu addItem:menuItem];
373 menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Block certain contacts", nil)
376 [menuItem setTag:AIPrivacyOptionDenyUsers];
377 [stateMenu addItem:menuItem];
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]];
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
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;
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)];
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];
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;
440 [[self window] setMinSize:NSMakeSize(250, 320)];
441 [[self window] setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
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)];
454 case AIPrivacyOptionDenyAll:
455 case AIPrivacyOptionUnknown:
456 NSLog(@"We should never see these...");
462 [account setPrivacyOptions:privacyOption];
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];
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)) {
482 [listContents addObjectsFromArray:[account listObjectsOnPrivacyList:((privacyOption == AIPrivacyOptionAllowUsers) ?
483 AIPrivacyTypePermit :
484 AIPrivacyTypeDeny)]];
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)]];
503 - (void)selectPrivacyOption:(AIPrivacyOption)privacyOption
505 BOOL success = [stateChooser compatibleSelectItemWithTag:privacyOption];
506 if (privacyOption == AIPrivacyOptionCustom) {
508 NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"(Multiple privacy levels are active)", nil)
511 [menuItem setTag:AIPrivacyOptionCustom];
512 [[stateChooser menu] addItem:menuItem];
515 success = [stateChooser compatibleSelectItemWithTag:privacyOption];
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];
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
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
545 - (void)accountMenu:(AIAccountMenu *)inAccountMenu didSelectAccount:(AIAccount *)inAccount
547 if (inAccountMenu == accountMenu) {
548 AIAccount<AIAccount_Privacy> *account = [self selectedAccount];
550 //Selected an account
551 AIPrivacyOption privacyOption = [account privacyOptions];
553 //Don't need the account column when we're showing for just one account
554 [self setAccountColumnsVisible:NO];
556 [self selectPrivacyOption:privacyOption];
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;
564 while ((menuItem = [enumerator nextObject])) {
565 if ((account = [menuItem representedObject])) {
566 AIPrivacyOption accountState = [account privacyOptions];
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;
577 [self setAccountColumnsVisible:YES];
579 [self selectPrivacyOption:currentState];
582 } else if (inAccountMenu == sheetAccountMenu) {
583 //Update our sheet for the current account
584 [self selectAccountInSheet:inAccount];
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.
593 - (IBAction)selectedAllAccountItem:(id)sender
595 AIAccountMenu *relevantAccountMenu = (([sender menu] == [popUp_accounts menu]) ?
599 [self accountMenu:relevantAccountMenu didSelectAccount:nil];
603 * @brief Select an account in our account menu, then update everything else to be appropriate for it
605 - (void)selectAccount:(AIAccount *)inAccount
607 [popUp_accounts selectItemWithRepresentedObject:inAccount];
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
619 - (void)accountMenu:(AIAccountMenu *)inAccountMenu didRebuildMenuItems:(NSArray *)menuItems
621 AIAccount *previouslySelectedAccount = nil;
622 NSEnumerator *enumerator;
623 NSMenuItem *menuItem;
624 NSMenu *menu = [[NSMenu alloc] init];
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.
630 BOOL isPrimaryAccountMenu = (!accountMenu || (inAccountMenu == accountMenu));
632 if (isPrimaryAccountMenu) {
633 if ([popUp_accounts menu]) {
634 previouslySelectedAccount = [[popUp_accounts selectedItem] representedObject];
636 } else if (inAccountMenu == sheetAccountMenu) {
637 if ([popUp_sheetAccounts menu]) {
638 previouslySelectedAccount = [[popUp_sheetAccounts selectedItem] representedObject];
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)
646 action:@selector(selectedAllAccountItem:)
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
655 enumerator = [menuItems objectEnumerator];
656 while ((menuItem = [enumerator nextObject])) {
657 [menu addItem:menuItem];
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
666 [self selectAccount:previouslySelectedAccount];
669 [popUp_sheetAccounts setMenu:menu];
671 [self selectAccountInSheet:previouslySelectedAccount];
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)]);
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);
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)]);
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];
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];
727 } else if ([identifier isEqualToString:@"contact"]) {
728 return [contact formattedUID];
730 } else if ([identifier isEqualToString:@"account"]) {
731 return [[contact account] formattedUID];
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) {
744 dragItems = [inArray retain];
750 - (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
752 NSMutableArray *itemArray = [NSMutableArray array];
753 NSEnumerator *enumerator = [rows objectEnumerator];
755 while ((rowNumber = [enumerator nextObject])) {
756 [itemArray addObject:[listContents objectAtIndex:[rowNumber intValue]]];
759 return [self writeListObjects:itemArray toPasteboard:pboard];
762 - (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard
764 NSMutableArray *itemArray = [NSMutableArray array];
767 unsigned int bufSize = [rowIndexes count];
768 unsigned int *buf = malloc(bufSize * sizeof(unsigned int));
771 NSRange range = NSMakeRange([rowIndexes firstIndex], ([rowIndexes lastIndex]-[rowIndexes firstIndex]) + 1);
772 [rowIndexes getIndexes:buf maxCount:bufSize inIndexRange:&range];
774 for (i = 0; i != bufSize; i++) {
775 if ((item = [listContents objectAtIndex:buf[i]])) {
776 [itemArray addObject:item];
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"]) {
791 NSMutableArray *dragItemsArray = [NSMutableArray array];
792 NSEnumerator *enumerator = [dragItems objectEnumerator];
793 AIListObject *listObject;
795 while ((listObject = [enumerator nextObject])) {
796 [dragItemsArray addObject:[listObject internalObjectID]];
799 [sender setPropertyList:dragItemsArray forType:@"AIListObjectUniqueIDs"];
804 - (NSDragOperation)tableView:(NSTableView*)tv
805 validateDrop:(id <NSDraggingInfo>)info
807 proposedDropOperation:(NSTableViewDropOperation)op
810 NSDragOperation dragOp = NSDragOperationCopy;
812 if ([info draggingSource] == table) {
813 dragOp = NSDragOperationMove;
815 [tv setDropRow:row dropOperation:NSTableViewDropAbove];
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];
831 } else if ([listObject isKindOfClass:[AIMetaContact class]]) {
832 enumerator = [[(AIMetaContact *)listObject listContacts] objectEnumerator];
833 while ((containedObject = [enumerator nextObject])) {
834 [self addListObjectToList:containedObject];
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];
845 - (BOOL)tableView:(NSTableView*)tv acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)op
851 if ([[[info draggingPasteboard] types] containsObject:@"AIListObjectUniqueIDs"]) {
852 NSArray *dragItemsUniqueIDs = [[info draggingPasteboard] propertyListForType:@"AIListObjectUniqueIDs"];
854 NSEnumerator *idEnumerator = [dragItemsUniqueIDs objectEnumerator];
855 while ((uniqueUID = [idEnumerator nextObject]))
856 [self addListObjectToList:[[adium contactController] existingListObjectWithUniqueID:uniqueUID]];