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 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 (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 :
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 float 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(FLT_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 float 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(FLT_MAX, FLT_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 compatibleSelectItemWithTag: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 compatibleSelectItemWithTag:privacyOption];
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];
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 NSEnumerator *enumerator;
629 NSMenuItem *menuItem;
630 NSMenu *menu = [[NSMenu alloc] init];
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.
636 BOOL isPrimaryAccountMenu = (!accountMenu || (inAccountMenu == accountMenu));
638 if (isPrimaryAccountMenu) {
639 if ([popUp_accounts menu]) {
640 previouslySelectedAccount = [[popUp_accounts selectedItem] representedObject];
642 } else if (inAccountMenu == sheetAccountMenu) {
643 if ([popUp_sheetAccounts menu]) {
644 previouslySelectedAccount = [[popUp_sheetAccounts selectedItem] representedObject];
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)
652 action:@selector(selectedAllAccountItem:)
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
661 enumerator = [menuItems objectEnumerator];
662 while ((menuItem = [enumerator nextObject])) {
663 [menu addItem:menuItem];
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
672 [self selectAccount:previouslySelectedAccount];
675 [popUp_sheetAccounts setMenu:menu];
677 [self selectAccountInSheet:previouslySelectedAccount];
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)]);
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);
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)]);
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];
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];
733 } else if ([identifier isEqualToString:@"contact"]) {
734 return [contact formattedUID];
736 } else if ([identifier isEqualToString:@"account"]) {
737 return [[contact account] formattedUID];
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) {
750 dragItems = [inArray retain];
756 - (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
758 NSMutableArray *itemArray = [NSMutableArray array];
759 NSEnumerator *enumerator = [rows objectEnumerator];
761 while ((rowNumber = [enumerator nextObject])) {
762 [itemArray addObject:[listContents objectAtIndex:[rowNumber intValue]]];
765 return [self writeListObjects:itemArray toPasteboard:pboard];
768 - (BOOL)tableView:(NSTableView *)aTableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard*)pboard
770 NSMutableArray *itemArray = [NSMutableArray array];
773 unsigned int bufSize = [rowIndexes count];
774 unsigned int *buf = malloc(bufSize * sizeof(unsigned int));
777 NSRange range = NSMakeRange([rowIndexes firstIndex], ([rowIndexes lastIndex]-[rowIndexes firstIndex]) + 1);
778 [rowIndexes getIndexes:buf maxCount:bufSize inIndexRange:&range];
780 for (i = 0; i != bufSize; i++) {
781 if ((item = [listContents objectAtIndex:buf[i]])) {
782 [itemArray addObject:item];
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"]) {
797 NSMutableArray *dragItemsArray = [NSMutableArray array];
798 NSEnumerator *enumerator = [dragItems objectEnumerator];
799 AIListObject *listObject;
801 while ((listObject = [enumerator nextObject])) {
802 [dragItemsArray addObject:[listObject internalObjectID]];
805 [sender setPropertyList:dragItemsArray forType:@"AIListObjectUniqueIDs"];
810 - (NSDragOperation)tableView:(NSTableView*)tv
811 validateDrop:(id <NSDraggingInfo>)info
813 proposedDropOperation:(NSTableViewDropOperation)op
816 NSDragOperation dragOp = NSDragOperationCopy;
818 if ([info draggingSource] == table) {
819 dragOp = NSDragOperationMove;
821 [tv setDropRow:row dropOperation:NSTableViewDropAbove];
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];
837 } else if ([listObject isKindOfClass:[AIMetaContact class]]) {
838 enumerator = [[(AIMetaContact *)listObject listContacts] objectEnumerator];
839 while ((containedObject = [enumerator nextObject])) {
840 [self addListObjectToList:containedObject];
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];
851 - (BOOL)tableView:(NSTableView*)tv acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)op
857 if ([[[info draggingPasteboard] types] containsObject:@"AIListObjectUniqueIDs"]) {
858 NSArray *dragItemsUniqueIDs = [[info draggingPasteboard] propertyListForType:@"AIListObjectUniqueIDs"];
860 NSEnumerator *idEnumerator = [dragItemsUniqueIDs objectEnumerator];
861 while ((uniqueUID = [idEnumerator nextObject]))
862 [self addListObjectToList:[[adium contactController] existingListObjectWithUniqueID:uniqueUID]];