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 "AIAccountController.h"
18 #import "AIAccountListWindowController.h"
19 #import "AIContactController.h"
20 #import "AIStatusController.h"
21 #import "AIEditAccountWindowController.h"
22 #import <AIUtilities/AIAutoScrollView.h>
23 #import <AIUtilities/AIImageTextCell.h>
24 #import <AIUtilities/AIVerticallyCenteredTextCell.h>
25 #import <AIUtilities/ESImageAdditions.h>
26 #import <Adium/AIAccount.h>
27 #import <Adium/AIListObject.h>
28 #import <Adium/AIServiceIcons.h>
29 #import <Adium/AIStatusIcons.h>
30 #import "KFTypeSelectTableView.h"
32 #define ACCOUNT_DRAG_TYPE @"AIAccount" //ID for an account drag
34 #define NEW_ACCOUNT_DISPLAY_TEXT AILocalizedString(@"<New Account>",nil)
36 @interface AIAccountListPreferences (PRIVATE)
37 - (void)configureAccountList;
38 - (void)accountListChanged:(NSNotification *)notification;
42 * @class AIAccountListPreferences
43 * @brief Shows a list of accounts and provides for management of them
45 @implementation AIAccountListPreferences
48 * @brief Preference pane properties
50 - (PREFERENCE_CATEGORY)category{
51 return(AIPref_Accounts);
54 return(AILocalizedString(@"Accounts","Accounts preferences label"));
56 - (NSString *)nibName{
57 return(@"AccountListWindow");
61 * @brief Configure the view initially
65 //Configure the account list
66 [self configureAccountList];
67 [self updateAccountOverview];
69 //Build the 'add account' menu
70 NSMenu *serviceMenu = [[adium accountController] menuOfServicesWithTarget:self
73 format:AILocalizedString(@"%@",nil)];
74 [serviceMenu setAutoenablesItems:YES];
75 [button_newAccount setMenu:serviceMenu];
77 //Observe status icon pack changes
78 [[adium notificationCenter] addObserver:self
79 selector:@selector(statusIconsChanged:)
80 name:AIStatusIconSetDidChangeNotification
85 * @brief Perform actions before the view closes
89 [[adium contactController] unregisterListObjectObserver:self];
90 [[adium notificationCenter] removeObserver:self];
94 * @brief Account status changed.
96 * Disable the service menu and user name field for connected accounts
98 - (NSSet *)updateListObject:(AIListObject *)inObject keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
100 if([inObject isKindOfClass:[AIAccount class]]){
101 if([inModifiedKeys containsObject:@"Online"] ||
102 [inModifiedKeys containsObject:@"Connecting"] ||
103 [inModifiedKeys containsObject:@"Disconnecting"] ||
104 [inModifiedKeys containsObject:@"ConnectionProgressString"] ||
105 [inModifiedKeys containsObject:@"ConnectionProgressPercent"] ||
106 [inModifiedKeys containsObject:@"IdleSince"] ||
107 [inModifiedKeys containsObject:@"StatusState"]){
109 //Refresh this account in our list
110 int accountRow = [accountArray indexOfObject:inObject];
111 if(accountRow >= 0 && accountRow < [accountArray count]){
112 [tableView_accountList setNeedsDisplayInRect:[tableView_accountList rectOfRow:accountRow]];
115 //Update our account overview
116 [self updateAccountOverview];
123 //Actions --------------------------------------------------------------------------------------------------------------
126 * @brief Create a new account
128 * Called when a service type is selected from the Add menu
130 - (IBAction)selectServiceType:(id)sender
135 //Create the new account. Our list will automatically update in response to the account being created
136 account = [[adium accountController] newAccountAtIndex:-1 forService:[sender representedObject]];
138 //And then, we can select and edit the new account
139 accountRow = [accountArray indexOfObject:account];
140 [tableView_accountList selectRow:accountRow byExtendingSelection:NO];
141 [tableView_accountList scrollRowToVisible:accountRow];
143 [AIEditAccountWindowController editAccount:account
144 onWindow:[[self view] window]
149 * @brief Edit the currently selected account using <tt>AIEditAccountWindowController</tt>
151 - (IBAction)editAccount:(id)sender
153 int selectedRow = [tableView_accountList selectedRow];
154 if(selectedRow >= 0 && selectedRow < [accountArray count]){
155 [AIEditAccountWindowController editAccount:[accountArray objectAtIndex:selectedRow]
156 onWindow:[[self view] window]
162 * @brief Delete the selected account
164 * Prompts for confirmation first
166 - (IBAction)deleteAccount:(id)sender
168 int index = [tableView_accountList selectedRow];
171 AIAccount *targetAccount;
172 NSString *accountFormattedUID;
174 targetAccount = [accountArray objectAtIndex:index];
175 accountFormattedUID = [targetAccount formattedUID];
177 //Confirm before deleting
178 NSBeginAlertSheet(AILocalizedString(@"Delete Account",nil),
179 AILocalizedString(@"Delete",nil),
180 AILocalizedString(@"Cancel",nil),
181 @"",[[self view] window], self,
182 @selector(deleteAccountSheetDidEnd:returnCode:contextInfo:), nil, targetAccount,
183 AILocalizedString(@"Delete the account %@?",nil), ([accountFormattedUID length] ? accountFormattedUID : NEW_ACCOUNT_DISPLAY_TEXT));
188 * @brief Finish account deletion
190 * Called when the sheet is closed
192 * @param returnCode NSAlertDefaultReturn indicates the account should be deleted
194 - (void)deleteAccountSheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
196 AIAccount *targetAccount = contextInfo;
199 NSParameterAssert(targetAccount != nil);
200 NSParameterAssert([targetAccount isKindOfClass:[AIAccount class]]);
202 if(returnCode == NSAlertDefaultReturn){
204 index = [accountArray indexOfObject:targetAccount];
205 [[adium accountController] deleteAccount:targetAccount save:YES];
207 //If it was the last row, select the new last row (by default the selection will jump to the top, which is bad)
208 if(index >= [accountArray count]){
209 index = [accountArray count]-1;
210 [tableView_accountList selectRow:index byExtendingSelection:NO];
216 //Account List ---------------------------------------------------------------------------------------------------------
217 #pragma mark Account List
219 * @brief Configure the account list table
221 - (void)configureAccountList
223 AIImageTextCell *cell;
224 NSRect oldFrame, newFrame;
226 //Setup our edit button, keeping its right side in the same location
227 oldFrame = [button_editAccount frame];
228 [button_editAccount setTitle:AILocalizedString(@"Edit",nil)];
229 [button_editAccount sizeToFit];
230 newFrame = [button_editAccount frame];
231 if(newFrame.size.width < oldFrame.size.width) newFrame.size.width = oldFrame.size.width;
232 newFrame.origin.x = oldFrame.origin.x + oldFrame.size.width - newFrame.size.width;
233 [button_editAccount setFrame:newFrame];
235 //Configure our table view
236 [tableView_accountList setTarget:self];
237 [tableView_accountList setDoubleAction:@selector(editAccount:)];
238 [tableView_accountList setIntercellSpacing:NSMakeSize(4,4)];
239 [scrollView_accountList setAutoHideScrollBar:YES];
241 //Enable dragging of accounts
242 [tableView_accountList registerForDraggedTypes:[NSArray arrayWithObjects:ACCOUNT_DRAG_TYPE,nil]];
244 //Custom vertically-centered text cell for account names
245 cell = [[AIVerticallyCenteredTextCell alloc] init];
246 [cell setFont:[NSFont boldSystemFontOfSize:13]];
247 [[tableView_accountList tableColumnWithIdentifier:@"name"] setDataCell:cell];
250 cell = [[AIVerticallyCenteredTextCell alloc] init];
251 [cell setFont:[NSFont systemFontOfSize:13]];
252 [cell setAlignment:NSRightTextAlignment];
253 [[tableView_accountList tableColumnWithIdentifier:@"status"] setDataCell:cell];
256 //Observe changes to the account list
257 [[adium notificationCenter] addObserver:self
258 selector:@selector(accountListChanged:)
259 name:Account_ListChanged
261 [self accountListChanged:nil];
263 //Observe accounts so we can display accurate status
264 [[adium contactController] registerListObjectObserver:self];
268 * @brief Account list changed, refresh our table
270 - (void)accountListChanged:(NSNotification *)notification
272 //Update our list of accounts
273 [accountArray release];
274 accountArray = [[[adium accountController] accountArray] retain];
276 //Refresh the account table
277 [tableView_accountList reloadData];
278 [self updateControlAvailability];
279 [self updateAccountOverview];
283 * @brief Status icons changed, refresh our table
285 - (void)statusIconsChanged:(NSNotification *)notification
287 [tableView_accountList reloadData];
291 * @brief Update our account overview
293 * The overview indicates the total number of accounts and the number which are online.
295 - (void)updateAccountOverview
297 if([accountArray count] == 0){
298 [textField_overview setStringValue:AILocalizedString(@"Click the + to add a new account","Instructions on how to add an account when none are present")];
301 NSEnumerator *enumerator = [accountArray objectEnumerator];
305 //Count online accounts
306 while(account = [enumerator nextObject]){
307 if([[account statusObjectForKey:@"Online"] boolValue]) online++;
310 [textField_overview setStringValue:[NSString stringWithFormat:AILocalizedString(@"%i accounts, %i online","Overview of total and online accounts"), [accountArray count], online]];
315 * @brief Update control availability based on list selection
317 - (void)updateControlAvailability
319 BOOL selection = ([tableView_accountList selectedRow] != -1);
321 [button_editAccount setEnabled:selection];
322 [button_deleteAccount setEnabled:selection];
326 //Account List Table Delegate ------------------------------------------------------------------------------------------
327 #pragma mark Account List (Table Delegate)
329 * @brief Delete the selected row
331 - (void)tableViewDeleteSelectedRows:(NSTableView *)tableView
333 [self deleteAccount:nil];
337 * @brief Number of rows in the table
339 - (int)numberOfRowsInTableView:(NSTableView *)tableView
341 return([accountArray count]);
345 * @brief Table values
347 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row
349 NSString *identifier = [tableColumn identifier];
350 AIAccount *account = [accountArray objectAtIndex:row];
352 if([identifier isEqualToString:@"icon"]){
353 return([[account userIcon] imageByScalingToSize:NSMakeSize(28,28)]);
355 }else if([identifier isEqualToString:@"service"]){
356 return([[AIServiceIcons serviceIconForObject:account
357 type:AIServiceIconLarge
358 direction:AIIconNormal] imageByScalingToSize:NSMakeSize(24,24)]);
360 }else if([identifier isEqualToString:@"name"]){
361 return([[account formattedUID] length] ? [account formattedUID] : NEW_ACCOUNT_DISPLAY_TEXT);
363 }else if([identifier isEqualToString:@"status"]){
366 if([[account statusObjectForKey:@"Connecting"] boolValue]){
367 title = AILocalizedString(@"Connecting",nil);
368 }else if([[account statusObjectForKey:@"Disconnecting"] boolValue]){
369 title = AILocalizedString(@"Disconnecting",nil);
370 }else if([[account statusObjectForKey:@"Online"] boolValue]){
371 title = AILocalizedString(@"Online",nil);
373 title = STATUS_DESCRIPTION_OFFLINE;
378 }else if([identifier isEqualToString:@"statusicon"]){
380 return([AIStatusIcons statusIconForListObject:account type:AIStatusIconList direction:AIIconNormal]);
382 }else if([identifier isEqualToString:@"enabled"]){
390 * @brief Configure cells before display
392 - (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(int)row
394 NSString *identifier = [tableColumn identifier];
395 AIAccount *account = [accountArray objectAtIndex:row];
397 if([identifier isEqualToString:@"enabled"]){
398 BOOL online = [[account preferenceForKey:@"Online" group:GROUP_ACCOUNT_STATUS] boolValue];
399 [cell setState:(online ? NSOnState : NSOffState)];
401 }else if([identifier isEqualToString:@"status"]){
402 [cell setEnabled:([[account statusObjectForKey:@"Connecting"] boolValue] ||
403 [[account statusObjectForKey:@"Disconnecting"] boolValue] ||
404 [[account statusObjectForKey:@"Online"] boolValue])];
410 * @brief Handle a clicked active/inactive checkbox
412 * Checking the box both takes the account online and sets it to autoconnect. Unchecking it does the opposite.
414 - (void)tableView:(NSTableView *)tableView setObjectValue:(id)object forTableColumn:(NSTableColumn *)tableColumn row:(int)row
416 if([[tableColumn identifier] isEqualToString:@"enabled"]){
417 [[accountArray objectAtIndex:row] setPreference:object forKey:@"Online" group:GROUP_ACCOUNT_STATUS];
424 - (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
426 tempDragAccount = [accountArray objectAtIndex:[[rows objectAtIndex:0] intValue]];
428 [pboard declareTypes:[NSArray arrayWithObject:ACCOUNT_DRAG_TYPE] owner:self];
429 [pboard setString:@"Account" forType:ACCOUNT_DRAG_TYPE];
435 * @brief Drag validate
437 - (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)op
439 if(op == NSTableViewDropAbove && row != -1){
440 return(NSDragOperationPrivate);
442 return(NSDragOperationNone);
447 * @brief Drag complete
449 - (BOOL)tableView:(NSTableView*)tv acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)op
451 NSString *avaliableType = [[info draggingPasteboard] availableTypeFromArray:[NSArray arrayWithObject:ACCOUNT_DRAG_TYPE]];
453 if([avaliableType isEqualToString:@"AIAccount"]){
456 //Select the moved account
457 newIndex = [[adium accountController] moveAccount:tempDragAccount toIndex:row];
458 [tableView_accountList selectRow:newIndex byExtendingSelection:NO];
467 * @brief Selection change
469 - (void)tableViewSelectionDidChange:(NSNotification *)notification
471 [self updateControlAvailability];
475 * @brief Set up KFTypeSelectTableView
477 * Only search the "name" column.
479 - (void)configureTypeSelectTableView:(KFTypeSelectTableView *)tableView
481 [tableView setSearchColumnIdentifiers:[NSSet setWithObject:@"name"]];