libgaim.framework [386] which has extensive debugging in the upnp module to try to...
[adiumx.git] / Source / ESStatusPreferences.m
blobfe589622a09525d3ffff5baf6b17276736f019d8
1 //
2 //  ESStatusPreferences.m
3 //  Adium
4 //
5 //  Created by Evan Schoenberg on 2/26/05.
6 //  Copyright 2006 The Adium Team. All rights reserved.
7 //
9 #import "ESStatusPreferences.h"
10 #import "AIStatusController.h"
11 #import <Adium/AIAccountControllerProtocol.h>
12 #import "ESEditStatusGroupWindowController.h"
13 #import <Adium/AIAccount.h>
14 #import <Adium/AIEditStateWindowController.h>
15 #import <Adium/AIStatusMenu.h>
16 #import <Adium/AIStatusGroup.h>
17 #import <AIUtilities/AIImageTextCell.h>
18 #import <AIUtilities/AIAutoScrollView.h>
19 #import <AIUtilities/AIVerticallyCenteredTextCell.h>
20 #import <AIUtilities/AIOutlineViewAdditions.h>
21 #import <AIUtilities/AIImageAdditions.h>
22 #import "KFTypeSelectTableView.h"
24 #define STATE_DRAG_TYPE @"AIState"
26 @interface ESStatusPreferences (PRIVATE)
27 - (void)configureOtherControls;
28 - (void)configureAutoAwayStatusStatePopUp;
29 - (void)saveTimeValues;
30 - (void)_selectStatusWithUniqueID:(NSNumber *)uniqueID inPopUpButton:(NSPopUpButton *)inPopUpButton;
32 - (BOOL)addItemIfNeeded:(NSMenuItem *)menuItem toPopUpButton:(NSPopUpButton *)popUpButton alreadyShowingAnItem:(BOOL)alreadyShowing;
33 @end
35 @implementation ESStatusPreferences
37 /*!
38  * @brief Category
39  */
40 - (AIPreferenceCategory)category{
41     return AIPref_Status;
43 /*!
44  * @brief Label
45  */
46 - (NSString *)label{
47     return AILocalizedString(@"Status",nil);
50 /*!
51  * @brief Nib name
52  */
53 - (NSString *)nibName{
54     return @"StatusPreferences";
57 /*!
58  * @brief Configure the preference view
59  */
60 - (void)viewDidLoad
62         //Configure the controls
63         [self configureStateList];
65         //Manually size and position our buttons
66         {
67                 NSRect  newFrame, oldFrame;
68                 
69                 //Edit, right justified and far enough away from Remove that it can't conceivably overlap
70                 oldFrame = [button_editState frame];
71                 [button_editState setTitle:AILocalizedString(@"Edit",nil)];
72                 [button_editState sizeToFit];
73                 newFrame = [button_editState frame];
74                 if (newFrame.size.width < oldFrame.size.width) newFrame.size.width = oldFrame.size.width;
75                 if (newFrame.size.height < oldFrame.size.height) newFrame.size.height = oldFrame.size.height;
76                 newFrame.origin.x = oldFrame.origin.x + oldFrame.size.width - newFrame.size.width;
77                 [button_editState setFrame:newFrame];
79                 //Add Group
80                 oldFrame = [button_addGroup frame];
81                 [button_addGroup setTitle:AILocalizedString(@"Add Group",nil)];
82                 [button_addGroup sizeToFit];
83                 newFrame = [button_addGroup frame];
84                 if (newFrame.size.height < oldFrame.size.height) newFrame.size.height = oldFrame.size.height;
85                 [button_addGroup setFrame:newFrame];
86         }
87         
88         
89         /* Register as an observer of state array changes so we can refresh our list
90          * in response to changes. */
91         [[adium notificationCenter] addObserver:self
92                                                                    selector:@selector(stateArrayChanged:)
93                                                                            name:AIStatusStateArrayChangedNotification
94                                                                          object:nil];
95         [self stateArrayChanged:nil];
97         [self configureOtherControls];
101  * @brief Preference view is closing
102  */
103 - (void)viewWillClose
105         [self saveTimeValues];
106         [[adium notificationCenter] removeObserver:self];
110  * @brief Deallocate
111  */
112 - (void)dealloc
114         [super dealloc];
117 #pragma mark Status state list and controls
119 * @brief Configure the state list
121  * Configure the state list table view, setting up the custom table cells, padding, scroll view settings and other
122  * state list interface related setup.
123  */
124 - (void)configureStateList
126     AIImageTextCell                     *cell;
128         //Configure the table view
129         [outlineView_stateList setTarget:self];
130         [outlineView_stateList setDoubleAction:@selector(editState:)];
131         [outlineView_stateList setIntercellSpacing:NSMakeSize(4,4)];
132     [scrollView_stateList setAutohidesScrollers:YES];
133         
134         //Enable dragging of states
135         [outlineView_stateList registerForDraggedTypes:[NSArray arrayWithObject:STATE_DRAG_TYPE]];
136         
137     //Custom vertically-centered text cell for status state names
138     cell = [[AIVerticallyCenteredTextCell alloc] init];
139     [cell setFont:[NSFont systemFontOfSize:13]];
140     [[outlineView_stateList tableColumnWithIdentifier:@"name"] setDataCell:cell];
141         [cell release];
145  * @brief Update table control availability
147  * Updates table control availability based on the current state selection.  If no states are selected this method dims the
148  * edit and delete buttons since they require a selection to function.  The edit and delete buttons are also
149  * dimmed if the selected state is a built-in state.
150  */
151 - (void)updateTableControlAvailability
153 //      NSArray *selectedItems = [outlineView_stateList arrayOfSelectedItems];
154         NSIndexSet *selectedIndexes = [outlineView_stateList selectedRowIndexes];
155         int                     count = [selectedIndexes count];
157         [button_editState setEnabled:(count && 
158                                                                   ([[outlineView_stateList itemAtRow:[selectedIndexes firstIndex]] mutabilityType] == AIEditableStatusState))];
159         [button_deleteState setEnabled:count];
163  * @brief Invoked when the state array changes
165  * This method is invoked when the state array changes.  In response, we hold onto the new array and refresh our state
166  * list.
167  */
168 - (void)stateArrayChanged:(NSNotification *)notification
170         [outlineView_stateList reloadData];
171         [self updateTableControlAvailability];
172         
173         //Update the auto away status pop up as necessary
174         [self configureAutoAwayStatusStatePopUp];
177 //State Editing --------------------------------------------------------------------------------------------------------
178 #pragma mark State Editing
180 * @brief Edit the selected state
182  * Opens an edit state sheet for the selected state.  If the sheet is closed with success our
183  * customStatusState:changedTo: method will be invoked and we can save the changes
184  */
185 - (IBAction)editState:(id)sender
187         int                             selectedRow = [outlineView_stateList selectedRow];
188         AIStatusItem    *statusState = [outlineView_stateList itemAtRow:selectedRow];
189         
190         if (statusState) {
191                 if ([statusState isKindOfClass:[AIStatus class]]) {
192                         [AIEditStateWindowController editCustomState:(AIStatus *)statusState
193                                                                                                  forType:[statusState statusType]
194                                                                                           andAccount:nil
195                                                                                   withSaveOption:NO
196                                                                                                 onWindow:[[self view] window]
197                                                                                  notifyingTarget:self];
198                         
199                 } else if ([statusState isKindOfClass:[AIStatusGroup class]]) {
200                         [ESEditStatusGroupWindowController editStatusGroup:(AIStatusGroup *)statusState
201                                                                                                           onWindow:[[self view] window]
202                                                                                            notifyingTarget:self];                       
203                 }
204         }
208 * @brief State edited callback
210  * Invoked when the user successfully edits a state.  This method adds the new or updated state to Adium's state array.
211  */
212 - (void)customStatusState:(AIStatus *)originalState changedTo:(AIStatus *)newState forAccount:(AIAccount *)account
214         if (originalState) {
215                 /* As far the user was concerned, this was an edit.  The unique status ID should remain the same so that anything
216                  * depending upon this status will update to using it.  Furthermore, since this may be a copy of originalState
217                  * rather than the exact same object, we should update all accounts which are using this state to use the new copy
218                  */
219                 [newState setUniqueStatusID:[originalState uniqueStatusID]];
220                 
221                 NSEnumerator    *enumerator;
222                 AIAccount               *account;
223                 
224                 enumerator = [[[adium accountController] accounts] objectEnumerator];
225                 while ((account = [enumerator nextObject])) {
226                         if ([account statusState] == originalState) {
227                                 [account setStatusStateAndRemainOffline:newState];
228                                 
229                                 [account notifyOfChangedStatusSilently:YES];
230                         }
231                 }
233                 [[originalState containingStatusGroup] replaceExistingStatusState:originalState withStatusState:newState];
235                 [originalState setUniqueStatusID:nil];
237         } else {
238                 [[adium statusController] addStatusState:newState];
239         }
240         
241         [outlineView_stateList selectItemsInArray:[NSArray arrayWithObject:newState]];
242         [outlineView_stateList scrollRowToVisible:[outlineView_stateList rowForItem:newState]];
245 - (void)finishedSatusGroupEdit:(AIStatusGroup *)inStatusGroup
247         if (![inStatusGroup containingStatusGroup]) {
248                 //Add it if it's not already in a group
249                 [[[adium statusController] rootStateGroup] addStatusItem:inStatusGroup atIndex:-1];
251         } else {
252                 //Otherwise just save
253                 [[adium statusController] savedStatusesChanged];
254         }
256         [outlineView_stateList selectItemsInArray:[NSArray arrayWithObject:inStatusGroup]];
257         [outlineView_stateList scrollRowToVisible:[outlineView_stateList rowForItem:inStatusGroup]];
261  * @brief Delete the selected state
263  * Deletes the selected state from Adium's state array.
264  */
265 - (IBAction)deleteState:(id)sender
267         int                      selectedIndex = [outlineView_stateList selectedRow];
268         AIStatusItem *statusItem = [outlineView_stateList itemAtRow:selectedIndex];
270         if (statusItem) {
271                 //Confirm deletion of a status group with contents
272                 if ([statusItem isKindOfClass:[AIStatusGroup class]] &&
273                         [[(AIStatusGroup *)statusItem containedStatusItems] count]) {
274                         unsigned count = [[(AIStatusGroup *)statusItem containedStatusItems] count];
275                         NSString *message;
276                         
277                         if (count > 1) {
278                                 message = [NSString stringWithFormat:AILocalizedString(@"Are you sure you want to delete the group \"%@\" containing %i saved status items?",nil),
279                                         [statusItem title], count];
280                                 
281                         } else {
282                                 message = [NSString stringWithFormat:AILocalizedString(@"Are you sure you want to delete the group \"%@\" containing 1 saved status item?",nil),
283                                         [statusItem title], count];                             
284                         }
286                         //Warn if deleting a group containing status items
287                         NSBeginAlertSheet(AILocalizedString(@"Status Group Deletion Confirmation",nil),
288                                                           AILocalizedString(@"Delete", nil),
289                                                           AILocalizedString(@"Cancel", nil), nil,
290                                                           [[self view] window], self,
291                                                           @selector(sheetDidEnd:returnCode:contextInfo:), NULL,
292                                                           statusItem,
293                                                           message);
294                         
295                 } else {
296                         [[statusItem containingStatusGroup] removeStatusItem:statusItem];
297                 }
298         }
302  * @brief Confirmed a status item deletion operation
303  */
304 - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
306         if (returnCode == NSAlertDefaultReturn) {
307                 AIStatusItem *statusItem = (AIStatusItem *)contextInfo;
308                 [[statusItem containingStatusGroup] removeStatusItem:statusItem];
309         }       
313 * @brief Add a new state
315  * Creates a new state.  This is done by invoking an edit window without passing it a base state.  When the edit window
316  * returns successfully, it will invoke our customStatusState:changedTo: which adds the new state to Adium's state
317  * array.
318  */
319 - (IBAction)newState:(id)sender
321         [AIEditStateWindowController editCustomState:nil
322                                                                                  forType:AIAwayStatusType
323                                                                           andAccount:nil
324                                                                   withSaveOption:NO
325                                                                                 onWindow:[[self view] window]
326                                                                  notifyingTarget:self];
329 - (IBAction)addGroup:(id)sender
331         [ESEditStatusGroupWindowController editStatusGroup:nil
332                                                                                           onWindow:[[self view] window]
333                                                                            notifyingTarget:self];
336 //State List OutlinView Delegate --------------------------------------------------------------------------------------------
337 #pragma mark State List (OutlinView Delegate)
338 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
340         AIStatusGroup *statusGroup = (item ? item : [[adium statusController] rootStateGroup]);
341         
342         return [[statusGroup containedStatusItems] objectAtIndex:index];
345 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
347         AIStatusGroup *statusGroup = (item ? item : [[adium statusController] rootStateGroup]);
348         
349         return [[statusGroup containedStatusItems] count];      
352 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
357 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
359         NSString                *identifier = [tableColumn identifier];
360         
361         if ([identifier isEqualToString:@"icon"]) {
362                 return ([item respondsToSelector:@selector(icon)] ? [item icon] : nil);
363                 
364         } else if ([identifier isEqualToString:@"name"]) {
365                 NSImage *icon = ([item respondsToSelector:@selector(icon)] ? [item icon] : nil);
366                 
367                 if (icon) {
368                         NSMutableAttributedString *name;
370                         NSTextAttachment                *attachment;
371                         NSTextAttachmentCell    *cell;
372                         
373                         NSSize                                  iconSize = [icon size];
374                         
375                         if ((iconSize.width > 13) || (iconSize.height > 13)) {
376                                 icon = [icon imageByScalingToSize:NSMakeSize(13, 13)];
377                         }
379                         cell = [[[NSTextAttachmentCell alloc] init] autorelease];
380                         [cell setImage:icon];
381                         
382                         attachment = [[[NSTextAttachment alloc] init] autorelease];
383                         [attachment setAttachmentCell:cell];
384                         
385                         name = [[NSAttributedString attributedStringWithAttachment:attachment] mutableCopy];
386                         [name appendAttributedString:[[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" %@",([item title] ? [item title] : @"")]
387                                                                                                                                                   attributes:nil] autorelease]];
388                         return [name autorelease];
389                 } else {
390                         return [item title]; 
391                 }
392         }
393         
394         return nil;
397 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
399         return [item isKindOfClass:[AIStatusGroup class]];
403 * @brief Delete the selected row
404  */
405 - (void)outlineViewDeleteSelectedRows:(NSTableView *)tableView
407     [self deleteState:nil];
411 * @brief Selection change
412  */
413 - (void)outlineViewSelectionDidChange:(NSNotification *)notification
415         [self updateTableControlAvailability];
419 * @brief Drag start
420  */
421 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray*)items toPasteboard:(NSPasteboard*)pboard
423     draggingItems = [items retain];
424         
425     [pboard declareTypes:[NSArray arrayWithObject:STATE_DRAG_TYPE] owner:self];
426     [pboard setString:@"State" forType:STATE_DRAG_TYPE]; //Arbitrary state
428     return YES;
431 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
433     if (index == NSOutlineViewDropOnItemIndex && ![item isKindOfClass:[AIStatusGroup class]]) {
434                 AIStatusGroup *dropItem = [item containingStatusGroup];
435                 if (dropItem == [[adium statusController] rootStateGroup])
436                         dropItem = nil;
438                 [outlineView setDropItem:dropItem
439                                   dropChildIndex:[[[item containingStatusGroup] containedStatusItems] indexOfObjectIdenticalTo:item]];
440         }
441      
442         return NSDragOperationPrivate;
446 * @brief Drag complete
447  */
448 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
450     NSString    *avaliableType = [[info draggingPasteboard] availableTypeFromArray:[NSArray arrayWithObject:STATE_DRAG_TYPE]];
451     if ([avaliableType isEqualToString:STATE_DRAG_TYPE]) {              
452                 [[adium statusController] setDelayStatusMenuRebuilding:YES];
454                 if (!item) item = [[adium statusController] rootStateGroup];
456                 NSEnumerator *enumerator;
457                 AIStatusItem *statusItem;
458                 
459                 enumerator = [draggingItems objectEnumerator];
461                 while ((statusItem = [enumerator nextObject])) {
462                         if ([statusItem containingStatusGroup] == item) {
463                                 BOOL shouldIncrement = NO;
464                                 if ([[[statusItem containingStatusGroup] containedStatusItems] indexOfObject:statusItem] > index) {
465                                         shouldIncrement = YES;
466                                 }
467                                 
468                                 //Move the state and select it in the new location
469                                 [item moveStatusItem:statusItem toIndex:index];
470                                 
471                                 if (shouldIncrement) index++;
472                         } else {
473                                 //Don't let an object be moved into itself...
474                                 if (item != statusItem) {
475                                         [statusItem retain];
476                                         [[statusItem containingStatusGroup] removeStatusItem:statusItem];
477                                         [item addStatusItem:statusItem atIndex:index];
478                                         [statusItem release];
479                                         
480                                         index++;
481                                 }
482                         }
483                 }
485                 //Notify and reselect outside of the NSOutlineView callback
486                 [self performSelector:@selector(reselectDraggedItems:)
487                                    withObject:draggingItems
488                                    afterDelay:0];
490                 [draggingItems release]; draggingItems = nil;
492         return YES;
493     } else {
494         return NO;
495     }
498 - (void)reselectDraggedItems:(NSArray *)theDraggedItems
500         [[adium statusController] setDelayStatusMenuRebuilding:NO];
502         [outlineView_stateList selectItemsInArray:theDraggedItems];
503         [outlineView_stateList scrollRowToVisible:[outlineView_stateList rowForItem:[theDraggedItems objectAtIndex:0]]];
507 * @brief Set up KFTypeSelectTableView
509  * Only search the "name" column.
510  */
511 - (void)configureTypeSelectTableView:(KFTypeSelectTableView *)tableView
513     [tableView setSearchColumnIdentifiers:[NSSet setWithObject:@"name"]];
516 #pragma mark Other status-related controls
519  * @brief Configure initial values for idle, auto-away, etc., preferences.
520  */
522 - (void)configureOtherControls
524         NSDictionary    *prefDict = [[adium preferenceController] preferencesForGroup:PREF_GROUP_STATUS_PREFERENCES];
525         
526         [checkBox_idle setState:[[prefDict objectForKey:KEY_STATUS_REPORT_IDLE] boolValue]];
527         [textField_idleMinutes setDoubleValue:([[prefDict objectForKey:KEY_STATUS_REPORT_IDLE_INTERVAL] doubleValue] / 60.0)];
529         [checkBox_autoAway setState:[[prefDict objectForKey:KEY_STATUS_AUTO_AWAY] boolValue]];
530         [textField_autoAwayMinutes setDoubleValue:([[prefDict objectForKey:KEY_STATUS_AUTO_AWAY_INTERVAL] doubleValue] / 60.0)];
532         [checkBox_fastUserSwitching setState:[[prefDict objectForKey:KEY_STATUS_FUS] boolValue]];
534         [checkBox_showStatusWindow setState:[[prefDict objectForKey:KEY_STATUS_SHOW_STATUS_WINDOW] boolValue]];
536         [self configureControlDimming];
540  * @brief Configure the pop up of states for autoAway.
542  * Should be called by stateArrayChanged: both for initial set up and for updating when the states change.
543  */
544 - (void)configureAutoAwayStatusStatePopUp
546         NSMenu          *statusStatesMenu;
547         NSNumber        *targetUniqueStatusIDNumber;
549         statusStatesMenu = [AIStatusMenu staticStatusStatesMenuNotifyingTarget:self selector:@selector(changedAutoAwayStatus:)];
550         [popUp_autoAwayStatusState setMenu:statusStatesMenu];
551         
552         statusStatesMenu = [AIStatusMenu staticStatusStatesMenuNotifyingTarget:self selector:@selector(changedFastUserSwitchingStatus:)];       
553         [popUp_fastUserSwitchingStatusState setMenu:[[statusStatesMenu copy] autorelease]];
555         //Now select the proper state, or deselect all items if there is no chosen state or the chosen state doesn't exist
556         targetUniqueStatusIDNumber = [[adium preferenceController] preferenceForKey:KEY_STATUS_AUTO_AWAY_STATUS_STATE_ID
557                                                                                                                                                   group:PREF_GROUP_STATUS_PREFERENCES];
558         [self _selectStatusWithUniqueID:targetUniqueStatusIDNumber inPopUpButton:popUp_autoAwayStatusState];
559         
560         targetUniqueStatusIDNumber = [[adium preferenceController] preferenceForKey:KEY_STATUS_FUS_STATUS_STATE_ID
561                                                                                                                                                   group:PREF_GROUP_STATUS_PREFERENCES];
562         [self _selectStatusWithUniqueID:targetUniqueStatusIDNumber inPopUpButton:popUp_fastUserSwitchingStatusState];   
566  * @brief Add all items in inMenu to an array, returning the resulting array
568  * This method adds items deeply; that is, submenus and their contents are recursively included
570  * @param inMenu The menu to start from
571  * @param recursiveArray The array thus far; if nil an array will be created
573  * @result All the menu items in inMenu
574  */
575 - (NSMutableArray *)addItemsFromMenu:(NSMenu *)inMenu toArray:(NSMutableArray *)recursiveArray
577         NSArray                 *itemArray = [inMenu itemArray];
578         NSEnumerator    *enumerator;
579         NSMenuItem              *menuItem;
581         if (!recursiveArray) recursiveArray = [NSMutableArray array];
583         enumerator = [itemArray objectEnumerator];
584         while ((menuItem = [enumerator nextObject])) {
585                 [recursiveArray addObject:menuItem];
587                 if ([menuItem submenu]) {
588                         [self addItemsFromMenu:[menuItem submenu] toArray:recursiveArray];
589                 }
590         }
592         return recursiveArray;
596  * @brief Select a status with uniqueID in inPopUpButton
597  */
598 - (void)_selectStatusWithUniqueID:(NSNumber *)uniqueID inPopUpButton:(NSPopUpButton *)inPopUpButton
600         NSMenuItem      *menuItem = nil;
601         
602         if (uniqueID) {
603                 int                      targetUniqueStatusID= [uniqueID intValue];
604                 NSEnumerator *enumerator;
606                 enumerator = [[self addItemsFromMenu:[inPopUpButton menu] toArray:nil] objectEnumerator];
608                 while ((menuItem = [enumerator nextObject])) {
609                         AIStatusItem    *statusState;
610                         
611                         statusState = [[menuItem representedObject] objectForKey:@"AIStatus"];
613                         //Found the right status by matching its status ID to our preferred one
614                         if ([statusState preexistingUniqueStatusID] == targetUniqueStatusID) {
615                                 break;
616                         }
617                 }
618         }
620         if (menuItem) {
621                 [inPopUpButton selectItem:menuItem];
623                 //Add it if we weren't able to select it initially
624                 if (![inPopUpButton selectedItem]) {
625                         [self addItemIfNeeded:menuItem toPopUpButton:inPopUpButton alreadyShowingAnItem:NO];
626                         
627                         if (inPopUpButton == popUp_autoAwayStatusState) {
628                                 showingSubmenuItemInAutoAway = YES;
629                                 
630                         } else if (inPopUpButton == popUp_fastUserSwitchingStatusState) {
631                                 showingSubmenuItemInFastUserSwitching = YES;
632                                 
633                         }
634                 }
635         }
639  * @brief Configure control dimming for idle, auto-away, etc., preferences.
640  */
641 - (void)configureControlDimming
643         BOOL    idleControlsEnabled, autoAwayControlsEnabled;
645         idleControlsEnabled = ([checkBox_idle state] == NSOnState);
646         [textField_idleMinutes setEnabled:idleControlsEnabled];
647         [stepper_idleMinutes setEnabled:idleControlsEnabled];
648         
649         autoAwayControlsEnabled = ([checkBox_autoAway state] == NSOnState);
650         [popUp_autoAwayStatusState setEnabled:autoAwayControlsEnabled];
651         [textField_autoAwayMinutes setEnabled:autoAwayControlsEnabled];
652         [stepper_autoAwayMinutes setEnabled:autoAwayControlsEnabled];
653         
654         [popUp_fastUserSwitchingStatusState setEnabled:([checkBox_fastUserSwitching state] == NSOnState)];
658  * @brief Change preference
660  * Sent when controls are clicked
661  */
662 - (void)changePreference:(id)sender
664         if (sender == checkBox_idle) {
665                 [[adium preferenceController] setPreference:[NSNumber numberWithBool:[sender state]]
666                                                                                          forKey:KEY_STATUS_REPORT_IDLE
667                                                                                           group:PREF_GROUP_STATUS_PREFERENCES];
668                 [self configureControlDimming];
669                 
670         } else if (sender == checkBox_autoAway) {
671                 [[adium preferenceController] setPreference:[NSNumber numberWithBool:[sender state]]
672                                                                                          forKey:KEY_STATUS_AUTO_AWAY
673                                                                                           group:PREF_GROUP_STATUS_PREFERENCES];
674                 [self configureControlDimming];
675                 
676         } else if (sender == checkBox_showStatusWindow) {
677                 [[adium preferenceController] setPreference:[NSNumber numberWithBool:[sender state]]
678                                                                                          forKey:KEY_STATUS_SHOW_STATUS_WINDOW
679                                                                                           group:PREF_GROUP_STATUS_PREFERENCES];         
680                 
681         } else if (sender == checkBox_fastUserSwitching) {
682                 [[adium preferenceController] setPreference:[NSNumber numberWithBool:[sender state]]
683                                                                                          forKey:KEY_STATUS_FUS
684                                                                                           group:PREF_GROUP_STATUS_PREFERENCES];
685                 [self configureControlDimming];
686                 
687         }
691  * @brief If menuItem is not selectable in popUpButton, add it and select it
693  * Menu items located within submenus can't be directly selected. This method will add a spearator item and then the item itself
694  * to the bottom of popUpButton if needed.  alreadyShowing should be YES if a similarly set separate + item exists; it will be removed
695  * first.
697  * @result YES if the item was added to popUpButton.
698  */
699 - (BOOL)addItemIfNeeded:(NSMenuItem *)menuItem toPopUpButton:(NSPopUpButton *)popUpButton alreadyShowingAnItem:(BOOL)alreadyShowing
701         BOOL    nowShowing = NO;
702         NSMenu  *menu = [popUpButton menu];
704         [menuItem retain];
705         if (alreadyShowing) {
706                 int count = [menu numberOfItems];
707                 [menu removeItemAtIndex:--count];
708                 [menu removeItemAtIndex:--count];                       
709         }
710         
711         if ([popUpButton selectedItem] != menuItem) {
712                 NSMenuItem  *imitationMenuItem = [menuItem copy];
713                 
714                 [menu addItem:[NSMenuItem separatorItem]];
715                 [menu addItem:imitationMenuItem];
716                 
717                 [popUpButton selectItem:imitationMenuItem];
718                 [imitationMenuItem release];
719                 
720                 nowShowing = YES;
721         }       
722         [menuItem release];
723         
724         return nowShowing;
726 - (void)changedAutoAwayStatus:(id)sender
728         AIStatus        *statusState = [[sender representedObject] objectForKey:@"AIStatus"];
730         [[adium preferenceController] setPreference:[statusState uniqueStatusID]
731                                                                                  forKey:KEY_STATUS_AUTO_AWAY_STATUS_STATE_ID
732                                                                                   group:PREF_GROUP_STATUS_PREFERENCES];
734         showingSubmenuItemInAutoAway = [self addItemIfNeeded:sender
735                                                                                    toPopUpButton:popUp_autoAwayStatusState
736                                                                         alreadyShowingAnItem:showingSubmenuItemInAutoAway];
739 - (void)changedFastUserSwitchingStatus:(id)sender
741         AIStatus        *statusState = [[sender representedObject] objectForKey:@"AIStatus"];
743         [[adium preferenceController] setPreference:[statusState uniqueStatusID]
744                                                                                  forKey:KEY_STATUS_FUS_STATUS_STATE_ID
745                                                                                   group:PREF_GROUP_STATUS_PREFERENCES];
746         
747         showingSubmenuItemInFastUserSwitching = [self addItemIfNeeded:sender
748                                                                                                         toPopUpButton:popUp_fastUserSwitchingStatusState
749                                                                                          alreadyShowingAnItem:showingSubmenuItemInFastUserSwitching];
753  * @brief Control text did end editing
755  * In an attempt to get closer to a live-apply of preferences, save the preference when the
756  * text field loses focus.  See saveTimeValues for more information.
757  */
758 - (void)controlTextDidEndEditing:(NSNotification *)notification
760         [self saveTimeValues];
764  * @brief Save time text field values
766  * We can't get notified when the associated NSStepper is clicked, so we just save as requested.
767  * This method should be called before the view closes.
768  */
769 - (void)saveTimeValues
771         [[adium preferenceController] setPreference:[NSNumber numberWithDouble:([textField_idleMinutes doubleValue]*60.0)]
772                                                                                  forKey:KEY_STATUS_REPORT_IDLE_INTERVAL
773                                                                                   group:PREF_GROUP_STATUS_PREFERENCES];
775         [[adium preferenceController] setPreference:[NSNumber numberWithDouble:([textField_autoAwayMinutes doubleValue]*60.0)]
776                                                                                  forKey:KEY_STATUS_AUTO_AWAY_INTERVAL
777                                                                                   group:PREF_GROUP_STATUS_PREFERENCES];
780 @end