Libpurple.framework [484]: libpurple 2.2.0
[adiumx.git] / Source / ESStatusPreferences.m
blob2316f018c41260f384666c1935c6a8e49681ea0f
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 - (NSString *)paneIdentifier
39         return @"Status";
41 - (NSString *)paneName
43     return AILocalizedString(@"Status",nil);
45 - (NSImage *)paneIcon
47         return [NSImage imageNamed:@"pref-status" forClass:[self class]];
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:AILocalizedStringFromTable(@"Edit", @"Buttons", "Verb 'edit' on a button")];
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                 if (newFrame.size.width < oldFrame.size.width) newFrame.size.width = oldFrame.size.width;
86                 if (newFrame.size.width > oldFrame.size.width) newFrame.size.width += 8;
87                 [button_addGroup setFrame:newFrame];
88         }
89         
90         
91         /* Register as an observer of state array changes so we can refresh our list
92          * in response to changes. */
93         [[adium notificationCenter] addObserver:self
94                                                                    selector:@selector(stateArrayChanged:)
95                                                                            name:AIStatusStateArrayChangedNotification
96                                                                          object:nil];
97         [self stateArrayChanged:nil];
99         [self configureOtherControls];
103  * @brief Preference view is closing
104  */
105 - (void)viewWillClose
107         [self saveTimeValues];
108         [[adium notificationCenter] removeObserver:self];
112  * @brief Deallocate
113  */
114 - (void)dealloc
116         [super dealloc];
119 #pragma mark Status state list and controls
121 * @brief Configure the state list
123  * Configure the state list table view, setting up the custom table cells, padding, scroll view settings and other
124  * state list interface related setup.
125  */
126 - (void)configureStateList
128     AIImageTextCell                     *cell;
130         //Configure the table view
131         [outlineView_stateList setTarget:self];
132         [outlineView_stateList setDoubleAction:@selector(editState:)];
133         [outlineView_stateList setIntercellSpacing:NSMakeSize(4,4)];
134     [scrollView_stateList setAutohidesScrollers:YES];
135         
136         //Enable dragging of states
137         [outlineView_stateList registerForDraggedTypes:[NSArray arrayWithObject:STATE_DRAG_TYPE]];
138         
139     //Custom vertically-centered text cell for status state names
140     cell = [[AIVerticallyCenteredTextCell alloc] init];
141     [cell setFont:[NSFont systemFontOfSize:13]];
142     [[outlineView_stateList tableColumnWithIdentifier:@"name"] setDataCell:cell];
143         [cell release];
147  * @brief Update table control availability
149  * Updates table control availability based on the current state selection.  If no states are selected this method dims the
150  * edit and delete buttons since they require a selection to function.  The edit and delete buttons are also
151  * dimmed if the selected state is a built-in state.
152  */
153 - (void)updateTableControlAvailability
155 //      NSArray *selectedItems = [outlineView_stateList arrayOfSelectedItems];
156         NSIndexSet *selectedIndexes = [outlineView_stateList selectedRowIndexes];
157         int                     count = [selectedIndexes count];
159         [button_editState setEnabled:(count && 
160                                                                   ([[outlineView_stateList itemAtRow:[selectedIndexes firstIndex]] mutabilityType] == AIEditableStatusState))];
161         [button_deleteState setEnabled:count];
165  * @brief Invoked when the state array changes
167  * This method is invoked when the state array changes.  In response, we hold onto the new array and refresh our state
168  * list.
169  */
170 - (void)stateArrayChanged:(NSNotification *)notification
172         [outlineView_stateList reloadData];
173         [self updateTableControlAvailability];
174         
175         //Update the auto away status pop up as necessary
176         [self configureAutoAwayStatusStatePopUp];
179 //State Editing --------------------------------------------------------------------------------------------------------
180 #pragma mark State Editing
182 * @brief Edit the selected state
184  * Opens an edit state sheet for the selected state.  If the sheet is closed with success our
185  * customStatusState:changedTo: method will be invoked and we can save the changes
186  */
187 - (IBAction)editState:(id)sender
189         int                             selectedRow = [outlineView_stateList selectedRow];
190         AIStatusItem    *statusState = [outlineView_stateList itemAtRow:selectedRow];
191         
192         if (statusState) {
193                 if ([statusState isKindOfClass:[AIStatus class]]) {
194                         [AIEditStateWindowController editCustomState:(AIStatus *)statusState
195                                                                                                  forType:[statusState statusType]
196                                                                                           andAccount:nil
197                                                                                   withSaveOption:NO
198                                                                                                 onWindow:[[self view] window]
199                                                                                  notifyingTarget:self];
200                         
201                 } else if ([statusState isKindOfClass:[AIStatusGroup class]]) {
202                         [ESEditStatusGroupWindowController editStatusGroup:(AIStatusGroup *)statusState
203                                                                                                           onWindow:[[self view] window]
204                                                                                            notifyingTarget:self];                       
205                 }
206         }
210 * @brief State edited callback
212  * Invoked when the user successfully edits a state.  This method adds the new or updated state to Adium's state array.
213  */
214 - (void)customStatusState:(AIStatus *)originalState changedTo:(AIStatus *)newState forAccount:(AIAccount *)account
216         if (originalState) {
217                 /* As far the user was concerned, this was an edit.  The unique status ID should remain the same so that anything
218                  * depending upon this status will update to using it.  Furthermore, since this may be a copy of originalState
219                  * rather than the exact same object, we should update all accounts which are using this state to use the new copy
220                  */
221                 [newState setUniqueStatusID:[originalState uniqueStatusID]];
222                 
223                 NSEnumerator    *enumerator;
224                 AIAccount               *account;
225                 
226                 enumerator = [[[adium accountController] accounts] objectEnumerator];
227                 while ((account = [enumerator nextObject])) {
228                         if ([account statusState] == originalState) {
229                                 [account setStatusStateAndRemainOffline:newState];
230                                 
231                                 [account notifyOfChangedStatusSilently:YES];
232                         }
233                 }
235                 [[originalState containingStatusGroup] replaceExistingStatusState:originalState withStatusState:newState];
237                 [originalState setUniqueStatusID:nil];
239         } else {
240                 [[adium statusController] addStatusState:newState];
241         }
242         
243         [outlineView_stateList selectItemsInArray:[NSArray arrayWithObject:newState]];
244         [outlineView_stateList scrollRowToVisible:[outlineView_stateList rowForItem:newState]];
247 - (void)finishedStatusGroupEdit:(AIStatusGroup *)inStatusGroup
249         if (![inStatusGroup containingStatusGroup]) {
250                 //Add it if it's not already in a group
251                 [[[adium statusController] rootStateGroup] addStatusItem:inStatusGroup atIndex:-1];
253         } else {
254                 //Otherwise just save
255                 [[adium statusController] savedStatusesChanged];
256         }
258         [outlineView_stateList selectItemsInArray:[NSArray arrayWithObject:inStatusGroup]];
259         [outlineView_stateList scrollRowToVisible:[outlineView_stateList rowForItem:inStatusGroup]];
263  * @brief Delete the selected state
265  * Deletes the selected state from Adium's state array.
266  */
267 - (IBAction)deleteState:(id)sender
269         NSArray          *selectedItems = [outlineView_stateList arrayOfSelectedItems];
270         
271         if ([selectedItems count]) {
272                 //Confirm deletion of a status group with contents
273                 NSEnumerator *enumerator;
274                 AIStatusItem *statusItem;
275                 int                      numberOfItems = 0;
277                 enumerator = [selectedItems objectEnumerator];
278                 while ((statusItem = [enumerator nextObject])) {
279                         if ([statusItem isKindOfClass:[AIStatusGroup class]] &&
280                                 [[(AIStatusGroup *)statusItem flatStatusSet] count]) {
281                                 numberOfItems += [[(AIStatusGroup *)statusItem flatStatusSet] count];
282                         } else {
283                                 numberOfItems++;
284                         }
285                 }
287                 if (numberOfItems > 1) {
288                         NSString *message = [NSString stringWithFormat:AILocalizedString(@"Are you sure you want to delete %i saved status items?",nil),
289                                 numberOfItems];
290                         
291                         //Warn if deleting a group containing status items
292                         NSBeginAlertSheet(AILocalizedString(@"Status Deletion Confirmation",nil),
293                                                           AILocalizedString(@"Delete", nil),
294                                                           AILocalizedString(@"Cancel", nil), nil,
295                                                           [[self view] window], self,
296                                                           @selector(sheetDidEnd:returnCode:contextInfo:), NULL,
297                                                           [selectedItems retain],
298                                                           message);                     
299                 } else {
300                         //Use an enumerator because we could be deleting multiple empty groups
301                         enumerator = [selectedItems objectEnumerator];
302                         while ((statusItem = [enumerator nextObject])) {
303                                 [[statusItem containingStatusGroup] removeStatusItem:statusItem];
304                         }
305                 }
306         }
310  * @brief Confirmed a status item deletion operation
311  */
312 - (void)sheetDidEnd:(NSWindow *)sheet returnCode:(int)returnCode contextInfo:(void *)contextInfo
314         NSArray *selectedItems = (NSArray *)contextInfo;
315         if (returnCode == NSAlertDefaultReturn) {
316                 NSEnumerator *enumerator;
317                 AIStatusItem *statusItem;
319                 enumerator = [selectedItems objectEnumerator];
320                 while ((statusItem = [enumerator nextObject])) {
321                         [[statusItem containingStatusGroup] removeStatusItem:statusItem];
322                 }
323         }       
324         
325         [selectedItems release];
329 * @brief Add a new state
331  * Creates a new state.  This is done by invoking an edit window without passing it a base state.  When the edit window
332  * returns successfully, it will invoke our customStatusState:changedTo: which adds the new state to Adium's state
333  * array.
334  */
335 - (IBAction)newState:(id)sender
337         [AIEditStateWindowController editCustomState:nil
338                                                                                  forType:AIAwayStatusType
339                                                                           andAccount:nil
340                                                                   withSaveOption:NO
341                                                                                 onWindow:[[self view] window]
342                                                                  notifyingTarget:self];
345 - (IBAction)addGroup:(id)sender
347         [ESEditStatusGroupWindowController editStatusGroup:nil
348                                                                                           onWindow:[[self view] window]
349                                                                            notifyingTarget:self];
352 //State List OutlinView Delegate --------------------------------------------------------------------------------------------
353 #pragma mark State List (OutlinView Delegate)
354 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item
356         AIStatusGroup *statusGroup = (item ? item : [[adium statusController] rootStateGroup]);
357         
358         return [[statusGroup containedStatusItems] objectAtIndex:index];
361 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
363         AIStatusGroup *statusGroup = (item ? item : [[adium statusController] rootStateGroup]);
364         
365         return [[statusGroup containedStatusItems] count];      
368 - (void)outlineView:(NSOutlineView *)outlineView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn item:(id)item
373 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
375         NSString                *identifier = [tableColumn identifier];
376         
377         if ([identifier isEqualToString:@"icon"]) {
378                 return ([item respondsToSelector:@selector(icon)] ? [item icon] : nil);
379                 
380         } else if ([identifier isEqualToString:@"name"]) {
381                 NSImage *icon = ([item respondsToSelector:@selector(icon)] ? [item icon] : nil);
382                 
383                 if (icon) {
384                         NSMutableAttributedString *name;
386                         NSTextAttachment                *attachment;
387                         NSTextAttachmentCell    *cell;
388                         
389                         NSSize                                  iconSize = [icon size];
390                         
391                         if ((iconSize.width > 13) || (iconSize.height > 13)) {
392                                 icon = [icon imageByScalingToSize:NSMakeSize(13, 13)];
393                         }
395                         cell = [[[NSTextAttachmentCell alloc] init] autorelease];
396                         [cell setImage:icon];
397                         
398                         attachment = [[[NSTextAttachment alloc] init] autorelease];
399                         [attachment setAttachmentCell:cell];
400                         
401                         name = [[NSAttributedString attributedStringWithAttachment:attachment] mutableCopy];
402                         [name appendAttributedString:[[[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" %@",([item title] ? [item title] : @"")]
403                                                                                                                                                   attributes:nil] autorelease]];
404                         return [name autorelease];
405                 } else {
406                         return [item title]; 
407                 }
408         }
409         
410         return nil;
413 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
415         return [item isKindOfClass:[AIStatusGroup class]];
419 * @brief Delete the selected row
420  */
421 - (void)outlineViewDeleteSelectedRows:(NSTableView *)tableView
423     [self deleteState:nil];
427 * @brief Selection change
428  */
429 - (void)outlineViewSelectionDidChange:(NSNotification *)notification
431         [self updateTableControlAvailability];
435 * @brief Drag start
436  */
437 - (BOOL)outlineView:(NSOutlineView *)outlineView writeItems:(NSArray*)items toPasteboard:(NSPasteboard*)pboard
439     draggingItems = [items retain];
440         
441     [pboard declareTypes:[NSArray arrayWithObject:STATE_DRAG_TYPE] owner:self];
442     [pboard setString:@"State" forType:STATE_DRAG_TYPE]; //Arbitrary state
444     return YES;
447 - (NSDragOperation)outlineView:(NSOutlineView *)outlineView validateDrop:(id <NSDraggingInfo>)info proposedItem:(id)item proposedChildIndex:(int)index
449     if (index == NSOutlineViewDropOnItemIndex && ![item isKindOfClass:[AIStatusGroup class]]) {
450                 AIStatusGroup *dropItem = [item containingStatusGroup];
451                 if (dropItem == [[adium statusController] rootStateGroup])
452                         dropItem = nil;
454                 [outlineView setDropItem:dropItem
455                                   dropChildIndex:[[[item containingStatusGroup] containedStatusItems] indexOfObjectIdenticalTo:item]];
456         }
457      
458         return NSDragOperationPrivate;
462 * @brief Drag complete
463  */
464 - (BOOL)outlineView:(NSOutlineView *)outlineView acceptDrop:(id <NSDraggingInfo>)info item:(id)item childIndex:(int)index
466     NSString    *avaliableType = [[info draggingPasteboard] availableTypeFromArray:[NSArray arrayWithObject:STATE_DRAG_TYPE]];
467     if ([avaliableType isEqualToString:STATE_DRAG_TYPE]) {              
468                 [[adium statusController] setDelayStatusMenuRebuilding:YES];
470                 if (!item) item = [[adium statusController] rootStateGroup];
472                 NSEnumerator *enumerator;
473                 AIStatusItem *statusItem;
474                 
475                 enumerator = [draggingItems objectEnumerator];
477                 while ((statusItem = [enumerator nextObject])) {
478                         if ([statusItem containingStatusGroup] == item) {
479                                 BOOL shouldIncrement = NO;
480                                 if ([[[statusItem containingStatusGroup] containedStatusItems] indexOfObject:statusItem] > index) {
481                                         shouldIncrement = YES;
482                                 }
483                                 
484                                 //Move the state and select it in the new location
485                                 [item moveStatusItem:statusItem toIndex:index];
486                                 
487                                 if (shouldIncrement) index++;
488                         } else {
489                                 //Don't let an object be moved into itself...
490                                 if (item != statusItem) {
491                                         [statusItem retain];
492                                         [[statusItem containingStatusGroup] removeStatusItem:statusItem];
493                                         [item addStatusItem:statusItem atIndex:index];
494                                         [statusItem release];
495                                         
496                                         index++;
497                                 }
498                         }
499                 }
501                 //Notify and reselect outside of the NSOutlineView callback
502                 [self performSelector:@selector(reselectDraggedItems:)
503                                    withObject:draggingItems
504                                    afterDelay:0];
506                 [draggingItems release]; draggingItems = nil;
508         return YES;
509     } else {
510         return NO;
511     }
514 - (void)reselectDraggedItems:(NSArray *)theDraggedItems
516         [[adium statusController] setDelayStatusMenuRebuilding:NO];
518         [outlineView_stateList selectItemsInArray:theDraggedItems];
519         [outlineView_stateList scrollRowToVisible:[outlineView_stateList rowForItem:[theDraggedItems objectAtIndex:0]]];
523 * @brief Set up KFTypeSelectTableView
525  * Only search the "name" column.
526  */
527 - (void)configureTypeSelectTableView:(KFTypeSelectTableView *)tableView
529     [tableView setSearchColumnIdentifiers:[NSSet setWithObject:@"name"]];
532 #pragma mark Other status-related controls
535  * @brief Configure initial values for idle, auto-away, etc., preferences.
536  */
538 - (void)configureOtherControls
540         NSDictionary    *prefDict = [[adium preferenceController] preferencesForGroup:PREF_GROUP_STATUS_PREFERENCES];
541         
542         [checkBox_idle setState:[[prefDict objectForKey:KEY_STATUS_REPORT_IDLE] boolValue]];
543         [textField_idleMinutes setDoubleValue:([[prefDict objectForKey:KEY_STATUS_REPORT_IDLE_INTERVAL] doubleValue] / 60.0)];
545         [checkBox_autoAway setState:[[prefDict objectForKey:KEY_STATUS_AUTO_AWAY] boolValue]];
546         [textField_autoAwayMinutes setDoubleValue:([[prefDict objectForKey:KEY_STATUS_AUTO_AWAY_INTERVAL] doubleValue] / 60.0)];
548         [checkBox_fastUserSwitching setState:[[prefDict objectForKey:KEY_STATUS_FUS] boolValue]];
550         [checkBox_showStatusWindow setState:[[prefDict objectForKey:KEY_STATUS_SHOW_STATUS_WINDOW] boolValue]];
552         [self configureControlDimming];
556  * @brief Configure the pop up of states for autoAway.
558  * Should be called by stateArrayChanged: both for initial set up and for updating when the states change.
559  */
560 - (void)configureAutoAwayStatusStatePopUp
562         NSMenu          *statusStatesMenu;
563         NSNumber        *targetUniqueStatusIDNumber;
565         statusStatesMenu = [AIStatusMenu staticStatusStatesMenuNotifyingTarget:self selector:@selector(changedAutoAwayStatus:)];
566         [popUp_autoAwayStatusState setMenu:statusStatesMenu];
567         
568         statusStatesMenu = [AIStatusMenu staticStatusStatesMenuNotifyingTarget:self selector:@selector(changedFastUserSwitchingStatus:)];       
569         [popUp_fastUserSwitchingStatusState setMenu:[[statusStatesMenu copy] autorelease]];
571         //Now select the proper state, or deselect all items if there is no chosen state or the chosen state doesn't exist
572         targetUniqueStatusIDNumber = [[adium preferenceController] preferenceForKey:KEY_STATUS_AUTO_AWAY_STATUS_STATE_ID
573                                                                                                                                                   group:PREF_GROUP_STATUS_PREFERENCES];
574         [self _selectStatusWithUniqueID:targetUniqueStatusIDNumber inPopUpButton:popUp_autoAwayStatusState];
575         
576         targetUniqueStatusIDNumber = [[adium preferenceController] preferenceForKey:KEY_STATUS_FUS_STATUS_STATE_ID
577                                                                                                                                                   group:PREF_GROUP_STATUS_PREFERENCES];
578         [self _selectStatusWithUniqueID:targetUniqueStatusIDNumber inPopUpButton:popUp_fastUserSwitchingStatusState];   
582  * @brief Add all items in inMenu to an array, returning the resulting array
584  * This method adds items deeply; that is, submenus and their contents are recursively included
586  * @param inMenu The menu to start from
587  * @param recursiveArray The array thus far; if nil an array will be created
589  * @result All the menu items in inMenu
590  */
591 - (NSMutableArray *)addItemsFromMenu:(NSMenu *)inMenu toArray:(NSMutableArray *)recursiveArray
593         NSArray                 *itemArray = [inMenu itemArray];
594         NSEnumerator    *enumerator;
595         NSMenuItem              *menuItem;
597         if (!recursiveArray) recursiveArray = [NSMutableArray array];
599         enumerator = [itemArray objectEnumerator];
600         while ((menuItem = [enumerator nextObject])) {
601                 [recursiveArray addObject:menuItem];
603                 if ([menuItem submenu]) {
604                         [self addItemsFromMenu:[menuItem submenu] toArray:recursiveArray];
605                 }
606         }
608         return recursiveArray;
612  * @brief Select a status with uniqueID in inPopUpButton
613  */
614 - (void)_selectStatusWithUniqueID:(NSNumber *)uniqueID inPopUpButton:(NSPopUpButton *)inPopUpButton
616         NSMenuItem      *menuItem = nil;
617         
618         if (uniqueID) {
619                 int                      targetUniqueStatusID= [uniqueID intValue];
620                 NSEnumerator *enumerator;
622                 enumerator = [[self addItemsFromMenu:[inPopUpButton menu] toArray:nil] objectEnumerator];
624                 while ((menuItem = [enumerator nextObject])) {
625                         AIStatusItem    *statusState;
626                         
627                         statusState = [[menuItem representedObject] objectForKey:@"AIStatus"];
629                         //Found the right status by matching its status ID to our preferred one
630                         if ([statusState preexistingUniqueStatusID] == targetUniqueStatusID) {
631                                 break;
632                         }
633                 }
634         }
636         if (menuItem) {
637                 [inPopUpButton selectItem:menuItem];
639                 //Add it if we weren't able to select it initially
640                 if (![inPopUpButton selectedItem]) {
641                         [self addItemIfNeeded:menuItem toPopUpButton:inPopUpButton alreadyShowingAnItem:NO];
642                         
643                         if (inPopUpButton == popUp_autoAwayStatusState) {
644                                 showingSubmenuItemInAutoAway = YES;
645                                 
646                         } else if (inPopUpButton == popUp_fastUserSwitchingStatusState) {
647                                 showingSubmenuItemInFastUserSwitching = YES;
648                                 
649                         }
650                 }
651         }
655  * @brief Configure control dimming for idle, auto-away, etc., preferences.
656  */
657 - (void)configureControlDimming
659         BOOL    idleControlsEnabled, autoAwayControlsEnabled;
661         idleControlsEnabled = ([checkBox_idle state] == NSOnState);
662         [textField_idleMinutes setEnabled:idleControlsEnabled];
663         [stepper_idleMinutes setEnabled:idleControlsEnabled];
664         
665         autoAwayControlsEnabled = ([checkBox_autoAway state] == NSOnState);
666         [popUp_autoAwayStatusState setEnabled:autoAwayControlsEnabled];
667         [textField_autoAwayMinutes setEnabled:autoAwayControlsEnabled];
668         [stepper_autoAwayMinutes setEnabled:autoAwayControlsEnabled];
669         
670         [popUp_fastUserSwitchingStatusState setEnabled:([checkBox_fastUserSwitching state] == NSOnState)];
674  * @brief Change preference
676  * Sent when controls are clicked
677  */
678 - (void)changePreference:(id)sender
680         if (sender == checkBox_idle) {
681                 [[adium preferenceController] setPreference:[NSNumber numberWithBool:[sender state]]
682                                                                                          forKey:KEY_STATUS_REPORT_IDLE
683                                                                                           group:PREF_GROUP_STATUS_PREFERENCES];
684                 [self configureControlDimming];
685                 
686         } else if (sender == checkBox_autoAway) {
687                 [[adium preferenceController] setPreference:[NSNumber numberWithBool:[sender state]]
688                                                                                          forKey:KEY_STATUS_AUTO_AWAY
689                                                                                           group:PREF_GROUP_STATUS_PREFERENCES];
690                 [self configureControlDimming];
691                 
692         } else if (sender == checkBox_showStatusWindow) {
693                 [[adium preferenceController] setPreference:[NSNumber numberWithBool:[sender state]]
694                                                                                          forKey:KEY_STATUS_SHOW_STATUS_WINDOW
695                                                                                           group:PREF_GROUP_STATUS_PREFERENCES];         
696                 
697         } else if (sender == checkBox_fastUserSwitching) {
698                 [[adium preferenceController] setPreference:[NSNumber numberWithBool:[sender state]]
699                                                                                          forKey:KEY_STATUS_FUS
700                                                                                           group:PREF_GROUP_STATUS_PREFERENCES];
701                 [self configureControlDimming];
702                 
703         }
707  * @brief If menuItem is not selectable in popUpButton, add it and select it
709  * Menu items located within submenus can't be directly selected. This method will add a spearator item and then the item itself
710  * to the bottom of popUpButton if needed.  alreadyShowing should be YES if a similarly set separate + item exists; it will be removed
711  * first.
713  * @result YES if the item was added to popUpButton.
714  */
715 - (BOOL)addItemIfNeeded:(NSMenuItem *)menuItem toPopUpButton:(NSPopUpButton *)popUpButton alreadyShowingAnItem:(BOOL)alreadyShowing
717         BOOL    nowShowing = NO;
718         NSMenu  *menu = [popUpButton menu];
720         [menuItem retain];
721         if (alreadyShowing) {
722                 int count = [menu numberOfItems];
723                 [menu removeItemAtIndex:--count];
724                 [menu removeItemAtIndex:--count];                       
725         }
726         
727         if ([popUpButton selectedItem] != menuItem) {
728                 NSMenuItem  *imitationMenuItem = [menuItem copy];
729                 
730                 [menu addItem:[NSMenuItem separatorItem]];
731                 [menu addItem:imitationMenuItem];
732                 
733                 [popUpButton selectItem:imitationMenuItem];
734                 [imitationMenuItem release];
735                 
736                 nowShowing = YES;
737         }       
738         [menuItem release];
739         
740         return nowShowing;
742 - (void)changedAutoAwayStatus:(id)sender
744         AIStatus        *statusState = [[sender representedObject] objectForKey:@"AIStatus"];
746         [[adium preferenceController] setPreference:[statusState uniqueStatusID]
747                                                                                  forKey:KEY_STATUS_AUTO_AWAY_STATUS_STATE_ID
748                                                                                   group:PREF_GROUP_STATUS_PREFERENCES];
750         showingSubmenuItemInAutoAway = [self addItemIfNeeded:sender
751                                                                                    toPopUpButton:popUp_autoAwayStatusState
752                                                                         alreadyShowingAnItem:showingSubmenuItemInAutoAway];
755 - (void)changedFastUserSwitchingStatus:(id)sender
757         AIStatus        *statusState = [[sender representedObject] objectForKey:@"AIStatus"];
759         [[adium preferenceController] setPreference:[statusState uniqueStatusID]
760                                                                                  forKey:KEY_STATUS_FUS_STATUS_STATE_ID
761                                                                                   group:PREF_GROUP_STATUS_PREFERENCES];
762         
763         showingSubmenuItemInFastUserSwitching = [self addItemIfNeeded:sender
764                                                                                                         toPopUpButton:popUp_fastUserSwitchingStatusState
765                                                                                          alreadyShowingAnItem:showingSubmenuItemInFastUserSwitching];
769  * @brief Control text did end editing
771  * In an attempt to get closer to a live-apply of preferences, save the preference when the
772  * text field loses focus.  See saveTimeValues for more information.
773  */
774 - (void)controlTextDidEndEditing:(NSNotification *)notification
776         [self saveTimeValues];
780  * @brief Save time text field values
782  * We can't get notified when the associated NSStepper is clicked, so we just save as requested.
783  * This method should be called before the view closes.
784  */
785 - (void)saveTimeValues
787         [[adium preferenceController] setPreference:[NSNumber numberWithDouble:([textField_idleMinutes doubleValue]*60.0)]
788                                                                                  forKey:KEY_STATUS_REPORT_IDLE_INTERVAL
789                                                                                   group:PREF_GROUP_STATUS_PREFERENCES];
791         [[adium preferenceController] setPreference:[NSNumber numberWithDouble:([textField_autoAwayMinutes doubleValue]*60.0)]
792                                                                                  forKey:KEY_STATUS_AUTO_AWAY_INTERVAL
793                                                                                   group:PREF_GROUP_STATUS_PREFERENCES];
796 @end