Update the icon in the personal preferences if it's changed elsewhere (the contact...
[adiumx.git] / Source / AIContactAccountsPane.m
blobf3c999fe59d5ff8bc397c807b141667b6998ef12
1 /* 
2  * Adium is the legal property of its developers, whose names are listed in the copyright file included
3  * with this source distribution.
4  * 
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.
8  * 
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.
12  * 
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.
15  */
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;
34 @end
36 static NSComparisonResult compareContactsByTheirAccounts(id firstContact, id secondContact, void *context);
38 /*!
39  * @class AIContactAccountsPane
40  * @brief Accounts pane in the contact info window
41  *
42  * Provides a list of what accounts list a contact and in what group.
43  */
44 @implementation AIContactAccountsPane
46 //Preference pane properties
47 /*!
48  * @brief Category
49  */
50 - (AIContactInfoCategory)contactInfoCategory
52     return AIInfo_Accounts;
55 /*!
56  * @brief Nib name
57  */
58 - (NSString *)nibName
60     return @"ContactAccounts";
63 /*!
64  * @brief Configure the preference view
65  */
66 - (void)viewDidLoad
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
83                                                                          object:nil];
84         [self updateGroupList];
85         
86         //Observe account changes
87         [[adium notificationCenter] addObserver:self
88                                                                    selector:@selector(updateAccountList)
89                                                                            name:Account_ListChanged
90                                                                          object:nil];
91         [self updateAccountList];
94 /*!
95  * @brief Preference view is closing
96  */
97 - (void)viewWillClose
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
107  */
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) {
115                                 //Add the column.
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;
127                         }
128                 } else if(contactsColumnIsInAccountsTableView) {
129                         //Remove the column.
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;
140                 }
142                 //Switch to the new list object.
143                 [listObject release];
144                 listObject = [inObject retain];
146                 //Rebuild our account list.
147                 [self updateAccountList];
148         }
152  * @brief Update our list of accounts
153  */
154 - (void)updateAccountList
156         //Get the new accounts
157         [accounts release];
158         
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];
166                 
167         } else {
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];
177                 AIAccount *account;
178                 while ((account = [compatibleAccountsEnum nextObject])) {
179                         if ([contactController existingContactWithService:service account:account UID:UID]) {
180                                 [foundAccounts addObject:account];
181                                 [contactTimesN addObject:listObject];
182                         }
183                 }
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;
188         }
189         
190         //Refresh our table
191         [tableView_accounts reloadData];
195  * @brief Update our list of groups
196  */
197 - (void)updateGroupList
199         //Get the new groups
200         NSMenu          *groupMenu = [[adium contactController] menuOfAllGroupsInGroup:nil withTarget:self];
201         [[[tableView_accounts tableColumnWithIdentifier:@"group"] dataCell] setMenu:groupMenu];
202         
203         //Refresh our table
204         [tableView_accounts reloadData];
208 //Table View Data Sources ----------------------------------------------------------------------------------------------
209 #pragma mark TableView Data Sources
211  * @brief Number of table view rows
212  */
213 - (int)numberOfRowsInTableView:(NSTableView *)tableView
215         return [accounts count];
219  * @brief Table view object value
220  */
221 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row
223         id result = @"";
225         NSString                *identifier = [tableColumn identifier];
227         if ([identifier isEqualToString:@"account"]) {
228                 AIAccount               *account = [accounts objectAtIndex:row];
229                 NSString        *accountFormattedUID = [account formattedUID];
230                 
231                 if ([account online]) {
232                         result = accountFormattedUID;
233                         
234                 } else {
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];
239                 }
240                 
241         } else if ([identifier isEqualToString:@"contact"]) {
242                 AIListObject *contact = [contacts objectAtIndex:row];
243                 result = [contact formattedUID];
244         }
245         
246         return result;
250  * @brief Table view will display a cell
251  */
252 - (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(int)row
254         NSString                *identifier = [tableColumn identifier];
255         AIAccount               *account;
256         AIListContact   *exactContact;
257         BOOL                    accountOnline;
258                 
259         account =  [accounts objectAtIndex:row];
260         accountOnline = [account online];
262         exactContact = [contacts objectAtIndex:row];                            
264         //Disable cells for offline accounts
265         [cell setEnabled:accountOnline];
266         
267         //Select active group
268         if ([identifier isEqualToString:@"group"]) {
269                 if (accountOnline) {
270                         AIListGroup     *group;
271                         
272                         if ((group = [[adium contactController] remoteGroupForContact:exactContact])) {
273                                 [cell selectItemWithRepresentedObject:group];
274                         } else {
275                                 [cell selectItemAtIndex:0];                     
276                         }
277                 } else {
278                         [cell setTitle:AILocalizedString(@"(Unavailable)",nil)];
279                 }
280         }
281         
285  * @brief Empty.  This method is the target of our menus, and needed for menu validation.
286  */
287 - (void)selectGroup:(id)sender {};
290  * @brief Table view set object value
291  */
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;
297         
298         if ([identifier isEqualToString:@"group"]) {
299                 NSMenu          *menu = [[tableColumn dataCell] menu];
300                 int                     menuIndex = [object intValue];
301                 
302                 if (menuIndex >= 0 && menuIndex < [menu numberOfItems]) {
303                         AIListGroup     *group = [[menu itemAtIndex:menuIndex] representedObject];
304                         
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];
308                                 
309                         } else {
310                                 //Retrieve an AIListContact on this account
311                                 exactContact = [[adium contactController] existingContactWithService:[listObject service]
312                                                                                                                                                          account:account
313                                                                                                                                                                  UID:[listObject UID]];
314                         }
315                         
316                         if (group) {
317                                 if (group != [exactContact containingObject]) {
318                                         
319                                         if (exactContact && ([exactContact containingObject] ||
320                                                                                  [exactContact isKindOfClass:[AIMetaContact class]])) {
321                                                 //Move contact
322                                                 [[adium contactController] moveContact:exactContact intoObject:group];
323                                                 
324                                         } else {
325                                                 //Add contact
326                                                 if (!exactContact) {
327                                                         exactContact = [[adium contactController] contactWithService:[listObject service]
328                                                                                                                                                                  account:account
329                                                                                                                                                                          UID:[listObject UID]];
330                                                 }
331                                                 
332                                                 [[adium contactController] addContacts:[NSArray arrayWithObject:exactContact] 
333                                                                                                            toGroup:group];
334                                         }
335                                 }
336                         } else {
337                                 if (exactContact) {
338                                         //User selected not listed, so we'll remove that contact
339                                         [[adium contactController] removeListObjects:[NSArray arrayWithObject:exactContact]];
340                                 }
341                         }
342                 }
343         }
346 @end
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];
352         return result;