2 * Adium is the legal property of its developers, whose names are listed in the copyright file included
3 * with this source distribution.
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11 * Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 #import <Adium/AIAccountControllerProtocol.h>
18 #import "AIContactAccountsPane.h"
19 #import <Adium/AIContactControllerProtocol.h>
20 #import <AIUtilities/AIAlternatingRowTableView.h>
21 #import <AIUtilities/AIMenuAdditions.h>
22 #import <AIUtilities/AIArrayAdditions.h>
23 #import <AIUtilities/AIPopUpButtonAdditions.h>
24 #import <Adium/AIAccount.h>
25 #import <Adium/AIListContact.h>
26 #import <Adium/AIListObject.h>
27 #import <Adium/AIListGroup.h>
28 #import "AILocalizationTextField.h"
29 #import <Adium/AIMetaContact.h>
31 @interface AIContactAccountsPane (PRIVATE)
32 - (void)updateAccountList;
33 - (void)updateGroupList;
36 static NSComparisonResult compareContactsByTheirAccounts(id firstContact, id secondContact, void *context);
39 * @class AIContactAccountsPane
40 * @brief Accounts pane in the contact info window
42 * Provides a list of what accounts list a contact and in what group.
44 @implementation AIContactAccountsPane
46 //Preference pane properties
50 - (AIContactInfoCategory)contactInfoCategory
52 return AIInfo_Accounts;
60 return @"ContactAccounts";
64 * @brief Configure the preference view
68 [label_listedOnTheFollowingOfYourAccounts setLocalizedString:AILocalizedString(@"Listed on the following of your accounts:",nil)];
70 //Configure Table view
71 [tableView_accounts setUsesAlternatingRowBackgroundColors:YES];
72 [tableView_accounts setAcceptsFirstMouse:YES];
74 [[[tableView_accounts tableColumnWithIdentifier:@"account"] headerCell] setTitle:AILocalizedString(@"On Account",nil)];
75 [[[tableView_accounts tableColumnWithIdentifier:@"contact"] headerCell] setTitle:AILocalizedString(@"Individual Contact",nil)];
76 [[[tableView_accounts tableColumnWithIdentifier:@"group"] headerCell] setTitle:AILocalizedString(@"In Group",nil)];
77 contactsColumnIsInAccountsTableView = YES; //It's in the table view in the nib.
79 //Observe contact list changes
80 [[adium notificationCenter] addObserver:self
81 selector:@selector(updateGroupList)
82 name:Contact_ListChanged
84 [self updateGroupList];
86 //Observe account changes
87 [[adium notificationCenter] addObserver:self
88 selector:@selector(updateAccountList)
89 name:Account_ListChanged
91 [self updateAccountList];
95 * @brief Preference view is closing
99 [accounts release]; accounts = nil;
100 [contacts release]; contacts = nil;
101 [listObject release]; listObject = nil;
102 [[adium notificationCenter] removeObserver:self];
106 * @brief Configure the pane for a list object
108 - (void)configureForListObject:(AIListObject *)inObject
110 if (listObject != inObject) {
111 //Update the table view to have or not have the "Individual Contact" column, as appropriate.
112 //It should have the column when our list object is a metacontact.
113 if ([inObject isKindOfClass:[AIMetaContact class]]) {
114 if (!contactsColumnIsInAccountsTableView) {
116 [tableView_accounts addTableColumn:tableColumn_contacts];
117 //It was added as last; move to the middle.
118 [tableView_accounts moveColumn:2 toColumn:1];
119 //Set all of the table view's columns to be the same width.
120 float columnWidth = [tableView_accounts frame].size.width / 3.0;
121 [[tableView_accounts tableColumns] setValue:[NSNumber numberWithFloat:columnWidth] forKey:@"width"];
122 [tableView_accounts tile];
123 //We don't need it retained anymore.
124 [tableColumn_contacts release];
126 contactsColumnIsInAccountsTableView = YES;
128 } else if(contactsColumnIsInAccountsTableView) {
130 //Note that the column is in the table view in the nib, so it is in the table view before we have been configured for the first time.
131 //And be sure to retain it before removing it from the view.
132 [tableColumn_contacts retain];
133 [tableView_accounts removeTableColumn:tableColumn_contacts];
134 //Set both of the table view's columns to be the same width.
135 float columnWidth = [tableView_accounts frame].size.width / 2.0;
136 [[tableView_accounts tableColumns] setValue:[NSNumber numberWithFloat:columnWidth] forKey:@"width"];
137 [tableView_accounts tile];
139 contactsColumnIsInAccountsTableView = NO;
142 //Switch to the new list object.
143 [listObject release];
144 listObject = [inObject retain];
146 //Rebuild our account list.
147 [self updateAccountList];
152 * @brief Update our list of accounts
154 - (void)updateAccountList
156 //Get the new accounts
159 if ([listObject isKindOfClass:[AIMetaContact class]]) {
160 //Get all contacts of the metacontact.
161 //Sort them by account.
162 //Get the account of each contact.
163 //Finally, uniquify the accounts through a set.
164 contacts = [[[(AIMetaContact *)listObject listContacts] sortedArrayUsingFunction:compareContactsByTheirAccounts context:NULL] retain];
165 accounts = [[contacts valueForKey:@"account"] retain];
168 //Build a list of all accounts (compatible with the service of the input contact) that have the input contact's UID on their contact list.
169 AIService *service = [listObject service];
170 NSString *UID = [listObject UID];
171 NSArray *compatibleAccounts = [[adium accountController] accountsCompatibleWithService:service];
172 NSMutableArray *foundAccounts = [[NSMutableArray alloc] initWithCapacity:[compatibleAccounts count]];
173 NSMutableArray *contactTimesN = [[NSMutableArray alloc] initWithCapacity:[compatibleAccounts count]];
175 id <AIContactController> contactController = [adium contactController];
176 NSEnumerator *compatibleAccountsEnum = [compatibleAccounts objectEnumerator];
178 while ((account = [compatibleAccountsEnum nextObject])) {
179 if ([contactController existingContactWithService:service account:account UID:UID]) {
180 [foundAccounts addObject:account];
181 [contactTimesN addObject:listObject];
185 //These accounts were created with alloc/init. Borrow those implicit retains, and make them our retains of the arrays.
186 accounts = foundAccounts;
187 contacts = contactTimesN;
191 [tableView_accounts reloadData];
195 * @brief Update our list of groups
197 - (void)updateGroupList
200 NSMenu *groupMenu = [[adium contactController] menuOfAllGroupsInGroup:nil withTarget:self];
201 [[[tableView_accounts tableColumnWithIdentifier:@"group"] dataCell] setMenu:groupMenu];
204 [tableView_accounts reloadData];
208 //Table View Data Sources ----------------------------------------------------------------------------------------------
209 #pragma mark TableView Data Sources
211 * @brief Number of table view rows
213 - (int)numberOfRowsInTableView:(NSTableView *)tableView
215 return [accounts count];
219 * @brief Table view object value
221 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row
225 NSString *identifier = [tableColumn identifier];
227 if ([identifier isEqualToString:@"account"]) {
228 AIAccount *account = [accounts objectAtIndex:row];
229 NSString *accountFormattedUID = [account formattedUID];
231 if ([account online]) {
232 result = accountFormattedUID;
235 //Gray the names of offline accounts
236 NSDictionary *attributes = [NSDictionary dictionaryWithObject:[NSColor grayColor] forKey:NSForegroundColorAttributeName];
237 NSAttributedString *string = [[NSAttributedString alloc] initWithString:accountFormattedUID attributes:attributes];
238 result = [string autorelease];
241 } else if ([identifier isEqualToString:@"contact"]) {
242 AIListObject *contact = [contacts objectAtIndex:row];
243 result = [contact formattedUID];
250 * @brief Table view will display a cell
252 - (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(int)row
254 NSString *identifier = [tableColumn identifier];
256 AIListContact *exactContact;
259 account = [accounts objectAtIndex:row];
260 accountOnline = [account online];
262 exactContact = [contacts objectAtIndex:row];
264 //Disable cells for offline accounts
265 [cell setEnabled:accountOnline];
267 //Select active group
268 if ([identifier isEqualToString:@"group"]) {
272 if ((group = [[adium contactController] remoteGroupForContact:exactContact])) {
273 [cell selectItemWithRepresentedObject:group];
275 [cell selectItemAtIndex:0];
278 [cell setTitle:AILocalizedString(@"(Unavailable)",nil)];
285 * @brief Empty. This method is the target of our menus, and needed for menu validation.
287 - (void)selectGroup:(id)sender {};
290 * @brief Table view set object value
292 - (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row
294 NSString *identifier = [tableColumn identifier];
295 AIAccount *account = [accounts objectAtIndex:row];
296 AIListContact *exactContact;
298 if ([identifier isEqualToString:@"group"]) {
299 NSMenu *menu = [[tableColumn dataCell] menu];
300 int menuIndex = [object intValue];
302 if (menuIndex >= 0 && menuIndex < [menu numberOfItems]) {
303 AIListGroup *group = [[menu itemAtIndex:menuIndex] representedObject];
305 if ([listObject isKindOfClass:[AIMetaContact class]]) {
306 //If we're dealing with a metaContact, make sure it's the topmost one
307 exactContact = [(AIMetaContact *)listObject parentContact];
310 //Retrieve an AIListContact on this account
311 exactContact = [[adium contactController] existingContactWithService:[listObject service]
313 UID:[listObject UID]];
317 if (group != [exactContact containingObject]) {
319 if (exactContact && ([exactContact containingObject] ||
320 [exactContact isKindOfClass:[AIMetaContact class]])) {
322 [[adium contactController] moveContact:exactContact intoObject:group];
327 exactContact = [[adium contactController] contactWithService:[listObject service]
329 UID:[listObject UID]];
332 [[adium contactController] addContacts:[NSArray arrayWithObject:exactContact]
338 //User selected not listed, so we'll remove that contact
339 [[adium contactController] removeListObjects:[NSArray arrayWithObject:exactContact]];
348 static NSComparisonResult compareContactsByTheirAccounts(id firstContact, id secondContact, void *context) {
349 NSComparisonResult result = [[firstContact account] compare:[secondContact account]];
350 //If they have the same account, sort the contacts themselves within the account.
351 if(result == NSOrderedSame) result = [firstContact compare:secondContact];