Reverted ESFileTransfer in adium-0.8 to [14253]; we simply won't draw the arrow if...
[adiumx.git] / Source / ESAwayStatusWindowController.m
blob98bd2432a9163472867f689786f0d40f39e28bef
1 //
2 //  ESAwayStatusWindowController.m
3 //  Adium
4 //
5 //  Created by Evan Schoenberg on 4/12/05.
6 //  Copyright 2005 The Adium Team. All rights reserved.
7 //
9 #import "ESAwayStatusWindowController.h"
10 #import "AIAccountController.h"
11 #import "AIPreferenceController.h"
12 #import "AISoundController.h"
13 #import "AIStatusController.h"
14 #import <Adium/AIAccount.h>
15 #import <Adium/AIStatus.h>
16 #import <Adium/AIStatusIcons.h>
17 #import <Adium/AIServiceIcons.h>
18 #import <AIUtilities/AIArrayAdditions.h>
19 #import <AIUtilities/AIAttributedStringAdditions.h>
20 #import <AIUtilities/AIImageTextCell.h>
21 #import <AIUtilities/AITableViewAdditions.h>
22 #import <AIUtilities/CBApplicationAdditions.h>
25 #define AWAY_STATUS_WINDOW_NIB                                  @"AwayStatusWindow"
26 #define KEY_AWAY_STATUS_WINDOW_FRAME                    @"Away Status Window Frame"
28 @interface ESAwayStatusWindowController (PRIVATE)
29 - (void)localizeButtons;
30 - (void)configureStatusWindow;
31 - (void)configureMuteWhileAway;
32 - (NSAttributedString *)attributedStatusTitleForStatus:(AIStatus *)statusState withIcon:(NSImage *)statusIcon;
33 - (NSArray *)awayAccounts;
34 - (void)setupMultistatusTable;
35 @end
37 /*!
38  * @class ESAwayStatusWindowController
39  * @brief Window controller for the status window which optionally shows when one or more accounts are away or invisible
40  */
41 @implementation ESAwayStatusWindowController
43 static ESAwayStatusWindowController     *sharedInstance = nil;
45 /*!
46  * @brief Update the visibility of the status window
47  *
48  * Opens or closes the window if necessary.
49  *
50  * If shouldBeVisibile is YES and the window is already visible, updates its contents to reflect the current status.
51  * If shouldBeVisible is NO and the window is already not visibile, no action is taken.
52  */
53 + (void)updateStatusWindowWithVisibility:(BOOL)shouldBeVisible
55         if(shouldBeVisible){
56                 if(sharedInstance){
57                         //Update the window's configuration
58                         [sharedInstance configureStatusWindow];
59                 }else{
60                         //Create a new shared instance, which will be configured automatically once the window loads
61                         sharedInstance = [[self alloc] initWithWindowNibName:AWAY_STATUS_WINDOW_NIB];
62                         [sharedInstance showWindow:nil];
63                 }
64         
65         }else{
66                 if(sharedInstance){
67                         //If the window is current visible, close it
68                         [sharedInstance closeWindow:nil];
69                 }
70         }
73 /*!
74  * @brief Window size and position autosave name
75  */
76 - (NSString *)adiumFrameAutosaveName
78         return(KEY_AWAY_STATUS_WINDOW_FRAME);
81 /*!
82  * @brief Window loaded
83  */
84 - (void)windowDidLoad
86         //Call super first so we get our placement before performing autosizing
87         [super windowDidLoad];
88         
89         //Setup the textviews
90     [textView_singleStatus setHorizontallyResizable:NO];
91     [textView_singleStatus setVerticallyResizable:YES];
92     [textView_singleStatus setDrawsBackground:NO];
93         [textView_singleStatus setMinSize:NSZeroSize];
94     [scrollView_singleStatus setDrawsBackground:NO];
96         [self localizeButtons];
97         [self setupMultistatusTable];
98         [self configureMuteWhileAway];
100         [self configureStatusWindow];
104  * @brief Window will close
106  * Release and clear the reference to our shared instance
107  */
108 - (void)windowWillClose:(id)sender
110         //If we are muting while this window is open, remove the mute before closing
111         if([button_muteWhileAway state]){
112                 [[adium preferenceController] setPreference:nil
113                                                                                          forKey:KEY_SOUND_STATUS_MUTE
114                                                                                           group:PREF_GROUP_SOUNDS];
115         }
117         [super windowWillClose:sender];
119     //Clean up and release the shared instance
120     [sharedInstance autorelease]; sharedInstance = nil;
124  * @brief Deallocate
125  */
126 - (void)dealloc
128         [_awayAccounts release];
129         
130         [super dealloc];
134  * @brief Configure status window for the current account status(es)
135  */
136  - (void)configureStatusWindow
138         NSWindow                *window = [self window];
139         BOOL                    allOnlineAccountsAreUnvailable;
140         AIStatusType    activeUnvailableStatusType;
141         NSString                *activeUnvailableStatusName = nil;
142         NSSet                   *relevantStatuses;
143         NSRect                  frame = [window frame];
144         int                             newHeight;
145         
146         [window setTitle:AILocalizedString(@"Current Status",nil)];
147         [_awayAccounts release]; _awayAccounts = nil;
149         relevantStatuses = [[adium statusController] activeUnavailableStatusesAndType:&activeUnvailableStatusType 
150                                                                                                                                                  withName:&activeUnvailableStatusName
151                                                                                                    allOnlineAccountsAreUnvailable:&allOnlineAccountsAreUnvailable];
152         
153         if(allOnlineAccountsAreUnvailable && ([relevantStatuses count] == 1)){
154                 //Show the single status tab if all online accounts are unavailable and they are all in the same status state
155                 NSImage                         *statusIcon;
156                 NSAttributedString      *statusTitle;
158                 statusIcon = [AIStatusIcons statusIconForStatusName:activeUnvailableStatusName
159                                                                                                   statusType:activeUnvailableStatusType
160                                                                                                         iconType:AIStatusIconTab
161                                                                                                   direction:AIIconNormal];
162                 statusTitle = [self attributedStatusTitleForStatus:[relevantStatuses anyObject]
163                                                                                                   withIcon:statusIcon];
164                 
165                 [[textView_singleStatus textStorage] setAttributedString:statusTitle];
167                 newHeight = [statusTitle heightWithWidth:[textView_singleStatus frame].size.width] + 65;
168                 frame.origin.y -= (newHeight - frame.size.height);
169                 frame.size.height = newHeight;
170                         
171                 //Select the right tab view item
172                 [tabView_configuration selectTabViewItemWithIdentifier:@"singlestatus"];
173                 
174         }else{
175                 /* Show the multistatus tableview tab if accounts are in different states, which includes the case of only one
176                  * away state being in use but not all online accounts currently making use of it.
177                  */
178                 int                             requiredHeight;
180                 _awayAccounts = [[self awayAccounts] retain];
182                 [tableView_multiStatus reloadData];
184                 requiredHeight = (([tableView_multiStatus rowHeight] + [tableView_multiStatus intercellSpacing].height) *
185                                                   [_awayAccounts count]);
187                 newHeight = requiredHeight + 65;
188                 frame.origin.y -= (newHeight - frame.size.height);
189                 frame.size.height = newHeight;
191                 /* Multiple statuses */
192                 [tabView_configuration selectTabViewItemWithIdentifier:@"multistatus"];
193         }
195         //Perform the window resizing as needed
196         if ([NSApp isOnPantherOrBetter]){
197                 [window setFrame:frame display:YES animate:YES];
198         }else{
199                 [window setFrame:frame display:YES]; //animate:YES can crash in 10.2
200         }
204  * @brief Return the attributed status title for a status
206  * This method puts statusIcon into an NSTextAttachment and prefixes statusState's status message or title with it.
207  */
208 - (NSAttributedString *)attributedStatusTitleForStatus:(AIStatus *)statusState withIcon:(NSImage *)statusIcon
210         NSMutableAttributedString       *statusTitle;
211         NSTextAttachment                        *attachment;
212         NSTextAttachmentCell            *cell;
213         NSAttributedString                      *statusMessage;
214         
215         if((statusMessage = [statusState statusMessage]) &&
216            ([statusMessage length])){
217                 //Use the status message if it is set
218                 statusTitle = [statusMessage mutableCopy];
219                 [[statusTitle mutableString] insertString:@" "
220                                                                                   atIndex:0];
222         }else{
223                 //If it isn't, use the title
224                 NSDictionary                            *attributesDict;
226                 attributesDict = [NSDictionary dictionaryWithObject:[NSFont systemFontOfSize:0]
227                                                                                                          forKey:NSFontAttributeName];
229                 statusTitle = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@" %@",[statusState title]]
230                                                                                                                           attributes:attributesDict];
231         }
233         //Insert the image at the beginning
234         cell = [[NSTextAttachmentCell alloc] init];
235         [cell setImage:statusIcon];
237         attachment = [[NSTextAttachment alloc] init];
238         [attachment setAttachmentCell:cell];
239         [cell release];
241         [statusTitle insertAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]
242                                                                 atIndex:0];
243         [attachment release];
245         return [statusTitle autorelease];
249  * @brief Return an array of all away accounts
250  */
251 - (NSArray *)awayAccounts
252 {       
253         NSMutableArray  *awayAccounts = [NSMutableArray array]; 
254         NSEnumerator    *enumerator = [[[adium accountController] accountArray] objectEnumerator];
255         AIAccount               *account;
256         
257         while(account = [enumerator nextObject]){
258                 if([account online] || [account integerStatusObjectForKey:@"Connecting"]){
259                         AIStatus        *statusState = [account statusState];
260                         if([statusState statusType] != AIAvailableStatusType){
261                                 [awayAccounts addObject:account];
262                         }
263                 }
264         }
266         return awayAccounts;
270  * @brief Return from away
271  */
272 - (IBAction)returnFromAway:(id)sender
274         NSTabViewItem   *selectedTabViewItem = [tabView_configuration selectedTabViewItem];
275         AIStatus                *availableStatusState = [[adium statusController] defaultInitialStatusState];
276         
277         if([[selectedTabViewItem identifier] isEqualToString:@"singlestatus"]){
278                 //Put all accounts in the Available status state
279                 [[adium statusController] setActiveStatusState:availableStatusState];
281         }else{
282                 //Multistatus
283                 NSArray *selectedAccounts;
284                 
285                 selectedAccounts = [[tableView_multiStatus arrayOfSelectedItemsUsingSourceArray:_awayAccounts] copy];
286                 
287                 if([selectedAccounts count]){
288                         //Apply the available status state to only the selected accounts
289                         [[adium statusController] applyState:availableStatusState
290                                                                           toAccounts:selectedAccounts];
291                 }else{
292                         //No selection: Put all accounts in the Available status state
293                         [[adium statusController] setActiveStatusState:availableStatusState];                   
294                 }
296                 [selectedAccounts release];
297         }
301  * @brief Perform initial setup for the multistatus table
302  */
303 - (void)setupMultistatusTable
305         AIImageTextCell *imageTextCell;
306         
307         imageTextCell = [[AIImageTextCell alloc] init];
308         [imageTextCell setDrawsGradientHighlight:YES];
309         [[tableView_multiStatus tableColumnWithIdentifier:@"status"] setDataCell:imageTextCell];
310         [imageTextCell release];        
313 #pragma mark Multiservice table view datasource
315 * @brief Number of rows in the table
316  */
317 - (int)numberOfRowsInTableView:(NSTableView *)tableView
319         return([_awayAccounts count]);
323  * @brief Table values
325  * Object value is the account's formatted UID
326  */
327 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row
329         AIAccount       *account = [_awayAccounts objectAtIndex:row];
331         return([account formattedUID]);
335  * @brief Will display a cell
337  * Set the image (status icon) and substring (status title) before display.  Cell is an AIImageTextCell.
338  */
339 - (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(int)row
341         AIAccount       *account = [_awayAccounts objectAtIndex:row];
343         [cell setImage:[AIStatusIcons statusIconForListObject:account
344                                                                                                          type:AIServiceIconSmall
345                                                                                                 direction:AIIconNormal]];
346         [cell setSubString:[[account statusState] title]];
349 - (void)configureMuteWhileAway
351         NSNumber        *shouldMuteWhileWindowIsOpen = [[adium preferenceController] preferenceForKey:@"Mute While Away Status Window is Open"
352                                                                                                                                                                         group:PREF_GROUP_STATUS_PREFERENCES];
353         //Apply the mute to the sound controller by setting a preference for it
354         [[adium preferenceController] setPreference:shouldMuteWhileWindowIsOpen
355                                                                                  forKey:KEY_SOUND_STATUS_MUTE
356                                                                                   group:PREF_GROUP_SOUNDS];
357         [button_muteWhileAway setState:([shouldMuteWhileWindowIsOpen boolValue] ? NSOnState : NSOffState)];
360 - (IBAction)toggleMuteWhileAway:(id)sender
362         NSNumber        *shouldMuteWhileWindowIsOpen;
363         
364         shouldMuteWhileWindowIsOpen = ([sender state] ?
365                                                                    [NSNumber numberWithBool:YES] :
366                                                                    nil);
367         //Store for restoring here
368         [[adium preferenceController] setPreference:shouldMuteWhileWindowIsOpen
369                                                                                  forKey:@"Mute While Away Status Window is Open"
370                                                                                   group:PREF_GROUP_STATUS_PREFERENCES];
371         //And apply the mute to the sound controller by setting a preference for it
372         [[adium preferenceController] setPreference:shouldMuteWhileWindowIsOpen
373                                                                                  forKey:KEY_SOUND_STATUS_MUTE
374                                                                                   group:PREF_GROUP_SOUNDS];     
377 - (void)localizeButtons
379         [button_return setLocalizedString:AILocalizedStringFromTableInBundle(@"Return", 
380                                                                                                                                                  @"Buttons",
381                                                                                                                                                  [NSBundle bundleForClass:[self class]],
382                                                                                                                                                  "Button to return from away in the away status window")];
383         [button_muteWhileAway setLocalizedString:AILocalizedString(@"Mute While Away", "Mute sounds while away. Found in the away status window.")];
386 @end