Merged [13632]: Patch from Gayle Laakmann to not append @gmail.com if @googlemail...
[adiumx.git] / Source / ESStatusPreferences.m
blobe433ff1d170ddca615a0311323b6477664b7e774
1 //
2 //  ESStatusPreferences.m
3 //  Adium
4 //
5 //  Created by Evan Schoenberg on 2/26/05.
6 //  Copyright 2005 The Adium Team. All rights reserved.
7 //
9 #import "ESStatusPreferences.h"
10 #import "AIStatusController.h"
11 #import "AIAccountController.h"
12 #import <Adium/AIAccount.h>
13 #import <Adium/AIEditStateWindowController.h>
14 #import <AIUtilities/AIImageTextCell.h>
15 #import <AIUtilities/AIAutoScrollView.h>
16 #import <AIUtilities/AIVerticallyCenteredTextCell.h>
17 #import "KFTypeSelectTableView.h"
19 #define STATE_DRAG_TYPE @"AIState"
21 @interface ESStatusPreferences (PRIVATE)
22 - (void)configureOtherControls;
23 - (void)configureAutoAwayStatusStatePopUp;
24 - (void)saveTimeValues;
25 - (void)_selectStatusWithUniqueID:(NSNumber *)uniqueID inPopUpButton:(NSPopUpButton *)inPopUpButton;
26 @end
28 @implementation ESStatusPreferences
30 /*!
31  * @brief Category
32  */
33 - (PREFERENCE_CATEGORY)category{
34     return(AIPref_Status);
36 /*!
37  * @brief Label
38  */
39 - (NSString *)label{
40     return(AILocalizedString(@"Status",nil));
43 /*!
44  * @brief Nib name
45  */
46 - (NSString *)nibName{
47     return(@"StatusPreferences");
50 /*!
51  * @brief Configure the preference view
52  */
53 - (void)viewDidLoad
55         //Configure the controls
56         [self configureStateList];
57         [button_editState setTitle:AILocalizedString(@"Edit",nil)];
58         
59         /* Register as an observer of state array changes so we can refresh our list
60          * in response to changes. */
61         [[adium notificationCenter] addObserver:self
62                                                                    selector:@selector(stateArrayChanged:)
63                                                                            name:AIStatusStateArrayChangedNotification
64                                                                          object:nil];
65         [self stateArrayChanged:nil];
67         [self configureOtherControls];
70 /*!
71  * @brief Preference view is closing
72  */
73 - (void)viewWillClose
75         [self saveTimeValues];
76         [[adium notificationCenter] removeObserver:self];
79 /*!
80  * @brief Deallocate
81  */
82 - (void)dealloc
84         [stateArray release];
86         [super dealloc];
89 #pragma mark Status state list and controls
90 /*!
91 * @brief Configure the state list
92  *
93  * Configure the state list table view, setting up the custom table cells, padding, scroll view settings and other
94  * state list interface related setup.
95  */
96 - (void)configureStateList
98     AIImageTextCell                     *cell;
100         //Configure the table view
101         [tableView_stateList setTarget:self];
102         [tableView_stateList setDoubleAction:@selector(editState:)];
103         [tableView_stateList setIntercellSpacing:NSMakeSize(4,4)];
104     [scrollView_stateList setAutoHideScrollBar:YES];
105         
106         //Enable dragging of states
107         [tableView_stateList registerForDraggedTypes:[NSArray arrayWithObject:STATE_DRAG_TYPE]];
108         
109     //Custom vertically-centered text cell for status state names
110     cell = [[AIVerticallyCenteredTextCell alloc] init];
111     [cell setFont:[NSFont systemFontOfSize:13]];
112     [[tableView_stateList tableColumnWithIdentifier:@"name"] setDataCell:cell];
113         [cell release];
117 * @brief Update table control availability
119  * Updates table control availability based on the current state selection.  If no states are selected this method dims the
120  * edit and delete buttons since they require a selection to function.  The edit and delete buttons are also
121  * dimmed if the selected state is a built-in state.
122  */
123 - (void)updateTableControlAvailability
125         int             selectedRow = [tableView_stateList selectedRow];
126         BOOL    shouldEnable;
127         
128         shouldEnable = ((selectedRow != -1) &&
129                                         ([[stateArray objectAtIndex:selectedRow] mutabilityType] == AIEditableStatusState));
131         [button_editState setEnabled:shouldEnable];
132         [button_deleteState setEnabled:shouldEnable];
136 * @brief Invoked when the state array changes
138  * This method is invoked when the state array changes.  In response, we hold onto the new array and refresh our state
139  * list.
140  */
141 - (void)stateArrayChanged:(NSNotification *)notification
143         [stateArray release];
144         stateArray = [[[adium statusController] stateArray] retain];
146         [tableView_stateList reloadData];
147         [self updateTableControlAvailability];
148         
149         //Update the auto away status pop up as necessary
150         [self configureAutoAwayStatusStatePopUp];
153 //State Editing --------------------------------------------------------------------------------------------------------
154 #pragma mark State Editing
156 * @brief Edit the selected state
158  * Opens an edit state sheet for the selected state.  If the sheet is closed with success our
159  * customStatusState:changedTo: method will be invoked and we can save the changes
160  */
161 - (IBAction)editState:(id)sender
163         int             selectedIndex = [tableView_stateList selectedRow];
164         
165         if(selectedIndex >= 0 && selectedIndex < [stateArray count]){
166                 AIStatus        *statusState = [stateArray objectAtIndex:selectedIndex];
167                 [AIEditStateWindowController editCustomState:statusState
168                                                                                          forType:[statusState statusType]
169                                                                                   andAccount:nil
170                                                                           withSaveOption:NO
171                                                                                         onWindow:[[self view] window]
172                                                                          notifyingTarget:self];
173         }
177 * @brief State edited callback
179  * Invoked when the user successfully edits a state.  This method adds the new or updated state to Adium's state array.
180  */
181 - (void)customStatusState:(AIStatus *)originalState changedTo:(AIStatus *)newState forAccount:(AIAccount *)account
183         if(originalState){
184                 /* As far the user was concerned, this was an edit.  The unique status ID should remain the same so that anything
185                  * depending upon this status will update to using it.  Furthermore, since this may be a copy of originalState
186                  * rather than the exact same object, we should update all accounts which are using this state to use the new copy
187                  */
188                 [newState setUniqueStatusID:[originalState uniqueStatusID]];
189                 
190                 NSEnumerator    *enumerator;
191                 AIAccount               *account;
192                 
193                 enumerator = [[[adium accountController] accountArray] objectEnumerator];
194                 while(account = [enumerator nextObject]){
195                         if([account statusState] == originalState){
196                                 [account setStatusStateAndRemainOffline:newState];
197                                 
198                                 [account notifyOfChangedStatusSilently:YES];
199                         }
200                 }
202                 [[adium statusController] replaceExistingStatusState:originalState withStatusState:newState];
204                 [originalState setUniqueStatusID:nil];
206         }else{
207                 [[adium statusController] addStatusState:newState];
208         }
212 * @brief Delete the selected state
214  * Deletes the selected state from Adium's state array.
215  */
216 - (IBAction)deleteState:(id)sender
218         int             selectedIndex = [tableView_stateList selectedRow];
219         
220         if(selectedIndex >= 0 && selectedIndex < [stateArray count]){
221                 [[adium statusController] removeStatusState:[stateArray objectAtIndex:selectedIndex]];
222         }
226 * @brief Add a new state
228  * Creates a new state.  This is done by invoking an edit window without passing it a base state.  When the edit window
229  * returns successfully, it will invoke our customStatusState:changedTo: which adds the new state to Adium's state
230  * array.
231  */
232 - (IBAction)newState:(id)sender
234         [AIEditStateWindowController editCustomState:nil
235                                                                                  forType:AIAwayStatusType
236                                                                           andAccount:nil
237                                                                   withSaveOption:NO
238                                                                                 onWindow:[[self view] window]
239                                                                  notifyingTarget:self];
243 //State List Table Delegate --------------------------------------------------------------------------------------------
244 #pragma mark State List (Table Delegate)
246 * @brief Number of rows
247  */
248 - (int)numberOfRowsInTableView:(NSTableView *)tableView
250         return([stateArray count]);
254 * @brief Table values
255  */
256 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row
258         NSString                *identifier = [tableColumn identifier];
259         AIStatus                *statusState = [stateArray objectAtIndex:row];
260         
261         if([identifier isEqualToString:@"icon"]){
262                 return([statusState icon]);
263                 
264         }else if([identifier isEqualToString:@"name"]){
265                 return([statusState title]); 
266                 
267         }
268         
269         return(nil);
273 * @brief Delete the selected row
274  */
275 - (void)tableViewDeleteSelectedRows:(NSTableView *)tableView
277     [self deleteState:nil];
281 * @brief Selection change
282  */
283 - (void)tableViewSelectionDidChange:(NSNotification *)notification
285         [self updateTableControlAvailability];
289 * @brief Drag start
290  */
291 - (BOOL)tableView:(NSTableView *)tv writeRows:(NSArray*)rows toPasteboard:(NSPasteboard*)pboard
293     tempDragState = [stateArray objectAtIndex:[[rows objectAtIndex:0] intValue]];
294         
295     [pboard declareTypes:[NSArray arrayWithObject:STATE_DRAG_TYPE] owner:self];
296     [pboard setString:@"State" forType:STATE_DRAG_TYPE]; //Arbitrary state
297     
298     return(YES);
302 * @brief Drag validate
303  */
304 - (NSDragOperation)tableView:(NSTableView*)tv validateDrop:(id <NSDraggingInfo>)info proposedRow:(int)row proposedDropOperation:(NSTableViewDropOperation)op
306     if(op == NSTableViewDropAbove && row != -1){
307         return(NSDragOperationPrivate);
308     }else{
309         return(NSDragOperationNone);
310     }
314 * @brief Drag complete
315  */
316 - (BOOL)tableView:(NSTableView*)tv acceptDrop:(id <NSDraggingInfo>)info row:(int)row dropOperation:(NSTableViewDropOperation)op
318     NSString    *avaliableType = [[info draggingPasteboard] availableTypeFromArray:[NSArray arrayWithObject:STATE_DRAG_TYPE]];
319         
320     if([avaliableType isEqualToString:STATE_DRAG_TYPE]){
321         int     newIndex;
322                 
323         //Move the state and select it in the new location
324         newIndex = [[adium statusController] moveStatusState:tempDragState toIndex:row];
325         [tableView_stateList selectRow:newIndex byExtendingSelection:NO];
326                 
327         return(YES);
328     }else{
329         return(NO);
330     }
334 * @brief Set up KFTypeSelectTableView
336  * Only search the "name" column.
337  */
338 - (void)configureTypeSelectTableView:(KFTypeSelectTableView *)tableView
340     [tableView setSearchColumnIdentifiers:[NSSet setWithObject:@"name"]];
343 #pragma mark Other status-related controls
346  * @brief Configure initial values for idle, auto-away, etc., preferences.
347  */
349 - (void)configureOtherControls
351         NSDictionary    *prefDict = [[adium preferenceController] preferencesForGroup:PREF_GROUP_STATUS_PREFERENCES];
352         
353         [checkBox_idle setState:[[prefDict objectForKey:KEY_STATUS_REPORT_IDLE] boolValue]];
354         [textField_idleMinutes setDoubleValue:([[prefDict objectForKey:KEY_STATUS_REPORT_IDLE_INTERVAL] doubleValue] / 60.0)];
356         [checkBox_autoAway setState:[[prefDict objectForKey:KEY_STATUS_AUTO_AWAY] boolValue]];
357         [textField_autoAwayMinutes setDoubleValue:([[prefDict objectForKey:KEY_STATUS_AUTO_AWAY_INTERVAL] doubleValue] / 60.0)];
359         [checkBox_fastUserSwitching setState:[[prefDict objectForKey:KEY_STATUS_FUS] boolValue]];
361         [checkBox_showStatusWindow setState:[[prefDict objectForKey:KEY_STATUS_SHOW_STATUS_WINDOW] boolValue]];
363         [self configureControlDimming];
367  * @brief Configure the pop up of states for autoAway.
369  * Should be called by stateArrayChanged: both for initial set up and for updating when the states change.
370  */
371 - (void)configureAutoAwayStatusStatePopUp
373         NSMenu          *statusStatesMenu;
374         NSNumber        *targetUniqueStatusIDNumber;
376         statusStatesMenu = [[adium statusController] statusStatesMenu];
377         [popUp_autoAwayStatusState setMenu:statusStatesMenu];
378         [popUp_fastUserSwitchingStatusState setMenu:[[statusStatesMenu copy] autorelease]];
380         //Now select the proper state, or deselect all items if there is no chosen state or the chosen state doesn't exist
381         targetUniqueStatusIDNumber = [[adium preferenceController] preferenceForKey:KEY_STATUS_ATUO_AWAY_STATUS_STATE_ID
382                                                                                                                                                   group:PREF_GROUP_STATUS_PREFERENCES];
383         [self _selectStatusWithUniqueID:targetUniqueStatusIDNumber inPopUpButton:popUp_autoAwayStatusState];
384         
385         targetUniqueStatusIDNumber = [[adium preferenceController] preferenceForKey:KEY_STATUS_FUS_STATUS_STATE_ID
386                                                                                                                                                   group:PREF_GROUP_STATUS_PREFERENCES];
387         [self _selectStatusWithUniqueID:targetUniqueStatusIDNumber inPopUpButton:popUp_fastUserSwitchingStatusState];   
390 - (void)_selectStatusWithUniqueID:(NSNumber *)uniqueID inPopUpButton:(NSPopUpButton *)inPopUpButton
392         int                     index = -1;
394         if(uniqueID){
395                 NSArray         *itemArray;
396                 int                     targetUniqueStatusID;
397                 unsigned        itemArrayCount, i;
398                 
399                 targetUniqueStatusID = [uniqueID intValue];
400                 
401                 itemArray = [inPopUpButton itemArray];
402                 itemArrayCount = [itemArray count];
403                 for(i = 0; i < itemArrayCount; i++){
404                         NSMenuItem      *menuItem = [itemArray objectAtIndex:i];
405                         AIStatus        *statusState = [[menuItem representedObject] objectForKey:@"AIStatus"];
406                         
407                         //Found the right status by matching its status ID to our preferred one
408                         if([statusState preexistingUniqueStatusID] == targetUniqueStatusID){
409                                 index = i;
410                                 break;
411                         }
412                 }
413         }
414         
415         [inPopUpButton selectItemAtIndex:index];
419  * @brief Configure control dimming for idle, auto-away, etc., preferences.
420  */
421 - (void)configureControlDimming
423         BOOL    idleControlsEnabled, autoAwayControlsEnabled;
425         idleControlsEnabled = ([checkBox_idle state] == NSOnState);
426         [textField_idleMinutes setEnabled:idleControlsEnabled];
427         [stepper_idleMinutes setEnabled:idleControlsEnabled];
428         
429         autoAwayControlsEnabled = ([checkBox_autoAway state] == NSOnState);
430         [popUp_autoAwayStatusState setEnabled:autoAwayControlsEnabled];
431         [textField_autoAwayMinutes setEnabled:autoAwayControlsEnabled];
432         [stepper_autoAwayMinutes setEnabled:autoAwayControlsEnabled];
433         
434         [popUp_fastUserSwitchingStatusState setEnabled:([checkBox_fastUserSwitching state] == NSOnState)];
438  * @brief Change preference
440  * Sent when controls are clicked
441  */
442 - (void)changePreference:(id)sender
444         if(sender == checkBox_idle){
445                 [[adium preferenceController] setPreference:[NSNumber numberWithBool:[sender state]]
446                                                                                          forKey:KEY_STATUS_REPORT_IDLE
447                                                                                           group:PREF_GROUP_STATUS_PREFERENCES];
448                 [self configureControlDimming];
449                 
450         }else if(sender == checkBox_autoAway){
451                 [[adium preferenceController] setPreference:[NSNumber numberWithBool:[sender state]]
452                                                                                          forKey:KEY_STATUS_AUTO_AWAY
453                                                                                           group:PREF_GROUP_STATUS_PREFERENCES];
454                 [self configureControlDimming];
455                 
456         }else if(sender == checkBox_showStatusWindow){
457                 [[adium preferenceController] setPreference:[NSNumber numberWithBool:[sender state]]
458                                                                                          forKey:KEY_STATUS_SHOW_STATUS_WINDOW
459                                                                                           group:PREF_GROUP_STATUS_PREFERENCES];         
460                 
461         }else if(sender == checkBox_fastUserSwitching){
462                 [[adium preferenceController] setPreference:[NSNumber numberWithBool:[sender state]]
463                                                                                          forKey:KEY_STATUS_FUS
464                                                                                           group:PREF_GROUP_STATUS_PREFERENCES];
465                 [self configureControlDimming];
466                 
467         }else if(sender == popUp_autoAwayStatusState){
468                 AIStatus        *statusState = [[[sender selectedItem] representedObject] objectForKey:@"AIStatus"];
470                 [[adium preferenceController] setPreference:[statusState uniqueStatusID]
471                                                                                          forKey:KEY_STATUS_ATUO_AWAY_STATUS_STATE_ID
472                                                                                           group:PREF_GROUP_STATUS_PREFERENCES];
473                 
474         }else if(sender == popUp_fastUserSwitchingStatusState){
475                 AIStatus        *statusState = [[[sender selectedItem] representedObject] objectForKey:@"AIStatus"];
476                 
477                 [[adium preferenceController] setPreference:[statusState uniqueStatusID]
478                                                                                          forKey:KEY_STATUS_FUS_STATUS_STATE_ID
479                                                                                           group:PREF_GROUP_STATUS_PREFERENCES];
480         }
484  * @brief Control text did end editing
486  * In an attempt to get closer to a live-apply of preferences, save the preference when the
487  * text field loses focus.  See saveTimeValues for more information.
488  */
489 - (void)controlTextDidEndEditing:(NSNotification *)notification
491         [self saveTimeValues];
495  * @brief Save time text field values
497  * We can't get notified when the associated NSStepper is clicked, so we just save as requested.
498  * This method should be called before the view closes.
499  */
500 - (void)saveTimeValues
502         [[adium preferenceController] setPreference:[NSNumber numberWithDouble:([textField_idleMinutes doubleValue]*60.0)]
503                                                                                  forKey:KEY_STATUS_REPORT_IDLE_INTERVAL
504                                                                                   group:PREF_GROUP_STATUS_PREFERENCES];
506         [[adium preferenceController] setPreference:[NSNumber numberWithDouble:([textField_autoAwayMinutes doubleValue]*60.0)]
507                                                                                  forKey:KEY_STATUS_AUTO_AWAY_INTERVAL
508                                                                                   group:PREF_GROUP_STATUS_PREFERENCES];
511 @end