When loading the Privacy window, resize when it opens appropriately. Fixes #13307.
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/AIListContact.h>
17 #import <Adium/AIMetaContact.h>
18 #import <Adium/AIListGroup.h>
19 #import <Adium/AIService.h>
21 @interface RAFBlockEditorWindowController ()
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 [[NSNotificationCenter defaultCenter] addObserver:self
83 selector:@selector(privacySettingsChangedExternally:)
84 name:@"AIPrivacySettingsChangedOutsideOfPrivacyWindow"
87 // Force an update, so the window will resize properly.
88 [self accountMenu:accountMenu didSelectAccount:[self selectedAccount]];
90 [[AIContactObserverManager sharedManager] registerListObjectObserver:self];
92 [super windowDidLoad];
95 - (void)windowWillClose:(id)sender
97 [super windowWillClose:sender];
99 [[AIContactObserverManager sharedManager] unregisterListObjectObserver:self];
101 [[NSNotificationCenter defaultCenter] removeObserver:self];
102 [sharedInstance release]; sharedInstance = nil;
105 - (NSString *)adiumFrameAutosaveName
107 return @"PrivacyWindow";
112 [accountCol release];
113 [accountMenu release];
114 [listContents release];
115 [listContentsAllAccounts release];
120 - (NSMutableArray*)listContents
125 - (void)setListContents:(NSArray*)newList
127 if (newList != listContents) {
128 [listContents release];
129 listContents = [newList mutableCopy];
133 #pragma mark Adding a contact to the list
135 - (void)selectAccountInSheet:(AIAccount *)inAccount
137 [popUp_sheetAccounts selectItemWithRepresentedObject:inAccount];
138 [self configureTextField];
140 NSString *userNameLabel = [inAccount.service userNameLabel];
142 [accountText setAutoresizingMask:NSViewMinXMargin];
143 [buddyText setLocalizedString:[(userNameLabel ?
144 userNameLabel : AILocalizedString(@"Contact ID",nil)) stringByAppendingString:AILocalizedString(@":", "Colon which will be appended after a label such as 'User Name', before an input field")]];
145 [accountText setAutoresizingMask:NSViewMaxXMargin];
148 - (IBAction)runBlockSheet:(id)sender
150 [field setStringValue:@""];
152 sheetAccountMenu = [[AIAccountMenu accountMenuWithDelegate:self
153 submenuType:AIAccountNoSubmenu
154 showTitleVerbs:NO] retain];
155 [self selectAccountInSheet:[[popUp_sheetAccounts selectedItem] representedObject]];
157 [NSApp beginSheet:sheet
158 modalForWindow:[self window]
160 didEndSelector:@selector(didEndSheet:returnCode:contextInfo:)
165 - (IBAction)cancelBlockSheet:(id)sender
167 [NSApp endSheet:sheet];
170 - (void)addObject:(AIListContact *)inContact
173 if (![listContents containsObject:inContact]) {
174 [listContents addObject:inContact];
177 [inContact setIsOnPrivacyList:YES updateList:YES privacyType:(([self selectedPrivacyOption] == AIPrivacyOptionAllowUsers) ?
178 AIPrivacyTypePermit :
183 - (IBAction)didBlockSheet:(id)sender
185 NSSet *contactArray = [self contactsFromTextField];
187 //Add the contact immediately
188 if (contactArray && [contactArray count]) {
189 AIListContact *contact;
191 for (contact in contactArray) {
192 [self addObject:contact];
198 [NSApp endSheet:sheet];
202 - (void)didEndSheet:(NSWindow *)theSheet returnCode:(NSInteger)returnCode contextInfo:(void *)contextInfo
204 [sheetAccountMenu release]; sheetAccountMenu = nil;
205 [theSheet orderOut:self];
209 * @brief Get a set of all contacts which are represented by the currently selected account and UID field
211 * @result A set of AIListContact objects
213 - (NSSet *)contactsFromTextField
215 AIListContact *contact = nil;
217 AIAccount *account = [[popUp_sheetAccounts selectedItem] representedObject];;
218 NSArray *accountArray;
219 NSMutableSet *contactsSet = [NSMutableSet set];
220 NSEnumerator *enumerator;
221 id impliedValue = [field impliedValue];
224 accountArray = [NSArray arrayWithObject:account];
227 NSMutableArray *tempArray = [NSMutableArray array];
228 NSMenuItem *menuItem;
230 enumerator = [[[popUp_sheetAccounts menu] itemArray] objectEnumerator];
231 while ((menuItem = [enumerator nextObject])) {
232 AIAccount *anAccount;
234 if ((anAccount = [menuItem representedObject])) {
235 [tempArray addObject:anAccount];
239 accountArray = tempArray;
242 for (account in accountArray) {
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 normalizeUID: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 NSIndexSet *selectedItems = [table selectedRowIndexes];
311 // If there's anything selected..
312 if ([selectedItems count]) {
313 AIListContact *contact;
315 // Iterate through the selected rows (backwards)
316 for (NSInteger 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 :
322 [listContents removeObject:contact];
326 [table deselectAll:nil];
331 - (void)tableViewDeleteSelectedRows:(NSTableView *)tableView
333 [self removeSelection:tableView];
336 - (void)setAccountColumnsVisible:(BOOL)visible
338 if (accountColumnsVisible != visible) {
340 [table addTableColumn:accountCol];
342 [table removeTableColumn:accountCol];
346 accountColumnsVisible = visible;
349 #pragma mark Privacy options menu
351 - (NSMenu *)privacyOptionsMenu
353 //build the menu of states
354 NSMenu *stateMenu = [[NSMenu alloc] init];
356 NSMenuItem *menuItem;
358 menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow anyone", nil)
361 [menuItem setTag:AIPrivacyOptionAllowAll];
362 [stateMenu addItem:menuItem];
365 menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow only contacts on my contact list", nil)
368 [menuItem setTag:AIPrivacyOptionAllowContactList];
369 [stateMenu addItem:menuItem];
372 menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Allow only certain contacts", nil)
375 [menuItem setTag:AIPrivacyOptionAllowUsers];
376 [stateMenu addItem:menuItem];
379 menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"Block certain contacts", nil)
382 [menuItem setTag:AIPrivacyOptionDenyUsers];
383 [stateMenu addItem:menuItem];
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]];
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
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 CGFloat tabViewHeight = [tabView_contactList frame].size.height;
421 frame.size.height -= tabViewHeight;
422 frame.origin.y += tabViewHeight;
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(CGFLOAT_MAX, frame.size.height)];
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];
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 CGFloat tabViewHeight = [tabView_contactList frame].size.height;
443 frame.size.height += tabViewHeight;
444 frame.origin.y -= tabViewHeight;
446 [[self window] setMinSize:NSMakeSize(250, 320)];
447 [[self window] setMaxSize:NSMakeSize(CGFLOAT_MAX, CGFLOAT_MAX)];
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)];
460 case AIPrivacyOptionDenyAll:
461 case AIPrivacyOptionUnknown:
462 NSLog(@"We should never see these...");
468 [account setPrivacyOptions:privacyOption];
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];
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)) {
488 [listContents addObjectsFromArray:[account listObjectsOnPrivacyList:((privacyOption == AIPrivacyOptionAllowUsers) ?
489 AIPrivacyTypePermit :
490 AIPrivacyTypeDeny)]];
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)]];
509 - (void)selectPrivacyOption:(AIPrivacyOption)privacyOption
511 BOOL success = [stateChooser selectItemWithTag:privacyOption];
512 if (privacyOption == AIPrivacyOptionCustom) {
514 NSMenuItem *menuItem = [[NSMenuItem alloc] initWithTitle:AILocalizedString(@"(Multiple privacy levels are active)", nil)
517 [menuItem setTag:AIPrivacyOptionCustom];
518 [[stateChooser menu] addItem:menuItem];
521 success = [stateChooser selectItemWithTag:privacyOption];
525 //Not on custom; make sure custom isn't still in the menu
526 NSInteger customItemIndex = [stateChooser indexOfItemWithTag:AIPrivacyOptionCustom];
527 if (customItemIndex != -1) {
528 [[stateChooser menu] removeItemAtIndex:customItemIndex];
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
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
551 - (void)accountMenu:(AIAccountMenu *)inAccountMenu didSelectAccount:(AIAccount *)inAccount
553 if (inAccountMenu == accountMenu) {
554 AIAccount<AIAccount_Privacy> *account = [self selectedAccount];
556 //Selected an account
557 AIPrivacyOption privacyOption = [account privacyOptions];
559 //Don't need the account column when we're showing for just one account
560 [self setAccountColumnsVisible:NO];
562 [self selectPrivacyOption:privacyOption];
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;
570 while ((menuItem = [enumerator nextObject])) {
571 if ((account = [menuItem representedObject])) {
572 AIPrivacyOption accountState = [account privacyOptions];
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;
583 [self setAccountColumnsVisible:YES];
585 [self selectPrivacyOption:currentState];
588 } else if (inAccountMenu == sheetAccountMenu) {
589 //Update our sheet for the current account
590 [self selectAccountInSheet:inAccount];
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.
599 - (IBAction)selectedAllAccountItem:(id)sender
601 AIAccountMenu *relevantAccountMenu = (([sender menu] == [popUp_accounts menu]) ?
605 [self accountMenu:relevantAccountMenu didSelectAccount:nil];
609 * @brief Select an account in our account menu, then update everything else to be appropriate for it
611 - (void)selectAccount:(AIAccount *)inAccount
613 [popUp_accounts selectItemWithRepresentedObject:inAccount];
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
625 - (void)accountMenu:(AIAccountMenu *)inAccountMenu didRebuildMenuItems:(NSArray *)menuItems
627 AIAccount *previouslySelectedAccount = nil;
628 NSMenuItem *menuItem;
629 NSMenu *menu = [[NSMenu alloc] init];
632 * accountMenu isn't set the first time we get here as the accountMenu is created. Similarly, sheetAccountMenu isn't created its first time.
633 * This code makes the (true) assumption that accountMenu is _always_ created before sheetAccountMenu.
635 BOOL isPrimaryAccountMenu = (!accountMenu || (inAccountMenu == accountMenu));
637 if (isPrimaryAccountMenu) {
638 if ([popUp_accounts menu]) {
639 previouslySelectedAccount = [[popUp_accounts selectedItem] representedObject];
641 } else if (inAccountMenu == sheetAccountMenu) {
642 if ([popUp_sheetAccounts menu]) {
643 previouslySelectedAccount = [[popUp_sheetAccounts selectedItem] representedObject];
647 //Add the All menu item first if we have more than one account listed
648 if ([menuItems count] > 1) {
649 [menu addItemWithTitle:AILocalizedString(@"All", nll)
651 action:@selector(selectedAllAccountItem:)
656 * As we enumerate, we:
657 * 1) Determine what state the accounts within the menu are in
658 * 2) Add the menu items to our menu
660 for (menuItem in menuItems) {
661 [menu addItem:menuItem];
664 if (isPrimaryAccountMenu) {
665 [popUp_accounts setMenu:menu];
667 /* Restore the previous account selection if there was one.
668 * Whether there was one or not, this will cause the rest of our view update to match the new/current selection
670 [self selectAccount:previouslySelectedAccount];
673 [popUp_sheetAccounts setMenu:menu];
675 [self selectAccountInSheet:previouslySelectedAccount];
681 - (BOOL)accountMenu:(AIAccountMenu *)inAccountMenu shouldIncludeAccount:(AIAccount *)inAccount
683 BOOL isPrimaryAccountMenu = (!accountMenu || (inAccountMenu == accountMenu));
685 if (isPrimaryAccountMenu) {
686 return (inAccount.online &&
687 [inAccount conformsToProtocol:@protocol(AIAccount_Privacy)]);
689 AIAccount *selectedPrimaryAccount = self.selectedAccount;
690 if (selectedPrimaryAccount) {
691 //An account is selected in the main window; only incldue that account in our sheet
692 return (inAccount == selectedPrimaryAccount);
695 //'All' is selected in the main window; include all accounts which are online and support privacy
696 return (inAccount.online &&
697 [inAccount conformsToProtocol:@protocol(AIAccount_Privacy)]);
702 - (void)privacySettingsChangedExternally:(NSNotification *)inNotification
704 [self accountMenu:accountMenu didSelectAccount:[self selectedAccount]];
707 - (NSSet *)updateListObject:(AIListObject *)inObject keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
709 if ([inModifiedKeys containsObject:KEY_IS_BLOCKED]) {
710 [self privacySettingsChangedExternally:nil];
716 #pragma mark Table view
718 - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
720 return [listContents count];
723 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
725 NSString *identifier = [aTableColumn identifier];
726 AIListContact *contact = [listContents objectAtIndex:rowIndex];
728 if ([identifier isEqualToString:@"icon"]) {
729 return [contact menuIcon];
731 } else if ([identifier isEqualToString:@"contact"]) {
732 return contact.formattedUID;
734 } else if ([identifier isEqualToString:@"account"]) {
735 return contact.account.formattedUID;
741 - (BOOL)writeListObjects:(NSArray *)inArray toPasteboard:(NSPasteboard*)pboard
743 [pboard declareTypes:[NSArray arrayWithObjects:@"AIListObject",@"AIListObjectUniqueIDs",nil] owner:self];
744 [pboard setString:@"Private" forType:@"AIListObject"];
746 if (dragItems != inArray) {
748 dragItems = [inArray retain];
754 - (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
756 NSMutableArray *itemArray = [NSMutableArray array];
758 for (rowNumber in rows) {
759 [itemArray addObject:[listContents objectAtIndex:[rowNumber integerValue]]];
762 return [self writeListObjects:itemArray toPasteboard:pboard];
765 - (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard
767 NSMutableArray *itemArray = [NSMutableArray array];
770 NSUInteger bufSize = [rowIndexes count];
771 NSUInteger *buf = malloc(bufSize * sizeof(NSUInteger));
774 NSRange range = NSMakeRange([rowIndexes firstIndex], ([rowIndexes lastIndex]-[rowIndexes firstIndex]) + 1);
775 [rowIndexes getIndexes:buf maxCount:bufSize inIndexRange:&range];
777 for (i = 0; i != bufSize; i++) {
778 if ((item = [listContents objectAtIndex:buf[i]])) {
779 [itemArray addObject:item];
785 return [self writeListObjects:itemArray toPasteboard:pboard];
788 - (void)pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
790 //Provide an array of internalObjectIDs which can be used to reference all the dragged contacts
791 if ([type isEqualToString:@"AIListObjectUniqueIDs"]) {
794 NSMutableArray *dragItemsArray = [NSMutableArray array];
795 AIListObject *listObject;
797 for (listObject in dragItems) {
798 [dragItemsArray addObject:listObject.internalObjectID];
801 [sender setPropertyList:dragItemsArray forType:@"AIListObjectUniqueIDs"];
806 - (NSDragOperation)tableView:(NSTableView*)tv
807 validateDrop:(id <NSDraggingInfo>)info
808 proposedRow:(NSInteger)row
809 proposedDropOperation:(NSTableViewDropOperation)op
812 NSDragOperation dragOp = NSDragOperationCopy;
814 if ([info draggingSource] == table) {
815 dragOp = NSDragOperationMove;
817 [tv setDropRow:row dropOperation:NSTableViewDropAbove];
822 - (void)addListObjectToList:(AIListObject *)listObject
824 AIListObject *containedObject;
825 NSEnumerator *enumerator;
827 if ([listObject isKindOfClass:[AIListGroup class]]) {
828 enumerator = [[(AIListGroup *)listObject uniqueContainedObjects] objectEnumerator];
829 while ((containedObject = [enumerator nextObject])) {
830 [self addListObjectToList:containedObject];
833 } else if ([listObject isKindOfClass:[AIMetaContact class]]) {
834 enumerator = [[(AIMetaContact *)listObject uniqueContainedObjects] objectEnumerator];
835 while ((containedObject = [enumerator nextObject])) {
836 [self addListObjectToList:containedObject];
839 } else if ([listObject isKindOfClass:[AIListContact class]]) {
840 //if the account for this contact is connected...
841 if ([(AIListContact *)listObject account].online) {
842 [self addObject:(AIListContact *)listObject];
847 - (BOOL)tableView:(NSTableView*)tv acceptDrop:(id <NSDraggingInfo>)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)op
853 if ([info.draggingPasteboard.types containsObject:@"AIListObjectUniqueIDs"]) {
854 for (NSString *uniqueUID in [info.draggingPasteboard propertyListForType:@"AIListObjectUniqueIDs"])
855 [self addListObjectToList:[adium.contactController existingListObjectWithUniqueID:uniqueUID]];