William Bowling is sharing code with you
Bitbucket is a code hosting site. Unlimited public and private repositories. Free for small teams.
Don't show this again1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 | //
// ESAwayStatusWindowController.m
// Adium
//
// Created by Evan Schoenberg on 4/12/05.
// Copyright 2006 The Adium Team. All rights reserved.
//
#import "ESAwayStatusWindowController.h"
#import <Adium/AIAccountControllerProtocol.h>
#import <Adium/AIStatusControllerProtocol.h>
#import <Adium/AIAccount.h>
#import <Adium/AIStatus.h>
#import <Adium/AIStatusIcons.h>
#import <Adium/AIServiceIcons.h>
#import <AIUtilities/AIApplicationAdditions.h>
#import <AIUtilities/AIArrayAdditions.h>
#import <AIUtilities/AIAttributedStringAdditions.h>
#import <AIUtilities/AIImageTextCell.h>
#import <AIUtilities/AITableViewAdditions.h>
#define AWAY_STATUS_WINDOW_NIB @"AwayStatusWindow"
#define KEY_AWAY_STATUS_WINDOW_FRAME @"Away Status Window Frame"
@interface ESAwayStatusWindowController ()
- (void)localizeButtons;
- (void)configureStatusWindow;
- (NSAttributedString *)attributedStatusTitleForStatus:(AIStatus *)statusState withIcon:(NSImage *)statusIcon;
- (NSArray *)awayAccounts;
- (void)setupMultistatusTable;
@end
/*!
* @class ESAwayStatusWindowController
* @brief Window controller for the status window which optionally shows when one or more accounts are away or invisible
*/
@implementation ESAwayStatusWindowController
static ESAwayStatusWindowController *sharedInstance = nil;
static BOOL alwaysOnTop = NO;
static BOOL hideInBackground = NO;
/*!
* @brief Update the visibility of the status window
*
* Opens or closes the window if necessary.
*
* If shouldBeVisibile is YES and the window is already visible, updates its contents to reflect the current status.
* If shouldBeVisible is NO and the window is already not visibile, no action is taken.
*/
+ (void)updateStatusWindowWithVisibility:(BOOL)shouldBeVisible
{
if (shouldBeVisible) {
if (sharedInstance) {
//Update the window's configuration
[sharedInstance configureStatusWindow];
} else {
//Create a new shared instance, which will be configured automatically once the window loads
sharedInstance = [[self alloc] initWithWindowNibName:AWAY_STATUS_WINDOW_NIB];
[sharedInstance showWindow:nil];
}
} else {
if (sharedInstance) {
//If the window is current visible, close it
[sharedInstance closeWindow:nil];
}
}
}
+ (void)setAlwaysOnTop:(BOOL)flag
{
alwaysOnTop = flag;
if (sharedInstance) {
//Update any open window
[[sharedInstance window] setLevel:(alwaysOnTop ? NSFloatingWindowLevel : NSNormalWindowLevel)];
}
}
+ (void)setHideInBackground:(BOOL)flag
{
hideInBackground = flag;
if (sharedInstance) {
//Update any open window
[[sharedInstance window] setHidesOnDeactivate:hideInBackground];
}
}
/*!
* @brief Window size and position autosave name
*/
- (NSString *)adiumFrameAutosaveName
{
return KEY_AWAY_STATUS_WINDOW_FRAME;
}
/*!
* @brief Window loaded
*/
- (void)windowDidLoad
{
//Call super first so we get our placement before performing autosizing
[super windowDidLoad];
[[self window] setLevel:(alwaysOnTop ? NSFloatingWindowLevel : NSNormalWindowLevel)];
[[self window] setHidesOnDeactivate:hideInBackground];
/* Set a more reasonable minimum size after the window is sized using our nib's specification.
* NSPanel behaves oddly with minimum size... it seems to increase the nib-specified minimum by 11.
*/
[[self window] setMinSize:NSMakeSize([[self window] minSize].width, 80)];
//Setup the textviews
[textView_singleStatus setHorizontallyResizable:NO];
[textView_singleStatus setVerticallyResizable:YES];
[textView_singleStatus setDrawsBackground:NO];
[textView_singleStatus setMinSize:NSZeroSize];
[[textView_singleStatus enclosingScrollView] setDrawsBackground:NO];
[self localizeButtons];
[self setupMultistatusTable];
[self configureStatusWindow];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(statusIconSetChanged:)
name:AIStatusIconSetDidChangeNotification
object:nil];
}
/*!
* @brief Window will close
*
* Release and clear the reference to our shared instance
*/
- (void)windowWillClose:(id)sender
{
[super windowWillClose:sender];
/* Hack of the day. The table view crashes when the window is released out from under it after it has reloaded data because
* it thinks it needs display. It thinks that because we are animating the window's resizing process. We could do animate:NO
* in configureStatusWindow, but that wouldn't be as pretty.
*/
[tableView_multiStatus setDataSource:nil];
//Clean up and release the shared instance
[sharedInstance autorelease]; sharedInstance = nil;
}
/*!
* @brief Deallocate
*/
- (void)dealloc
{
[_awayAccounts release]; _awayAccounts = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
/*!
* @brief Configure status window for the current account status(es)
*/
- (void)configureStatusWindow
{
NSWindow *window = [self window];
BOOL allOnlineAccountsAreUnvailable;
AIStatusType activeUnvailableStatusType;
NSString *activeUnvailableStatusName = nil;
NSSet *relevantStatuses;
NSRect frame = [window frame];
NSInteger newHeight;
[window setTitle:AILocalizedString(@"Current Status",nil)];
[_awayAccounts release]; _awayAccounts = nil;
relevantStatuses = [adium.statusController activeUnavailableStatusesAndType:&activeUnvailableStatusType
withName:&activeUnvailableStatusName
allOnlineAccountsAreUnvailable:&allOnlineAccountsAreUnvailable];
if (allOnlineAccountsAreUnvailable && ([relevantStatuses count] == 1)) {
//Show the single status tab if all online accounts are unavailable and they are all in the same status state
NSImage *statusIcon;
NSAttributedString *statusTitle;
statusIcon = [AIStatusIcons statusIconForStatusName:activeUnvailableStatusName
statusType:activeUnvailableStatusType
iconType:AIStatusIconTab
direction:AIIconNormal];
statusTitle = [self attributedStatusTitleForStatus:[relevantStatuses anyObject]
withIcon:statusIcon];
[[textView_singleStatus textStorage] setAttributedString:statusTitle];
newHeight = [statusTitle heightWithWidth:[textView_singleStatus frame].size.width] + 65;
frame.origin.y -= (newHeight - frame.size.height);
frame.size.height = newHeight;
//Select the right tab view item
[tabView_configuration selectTabViewItemWithIdentifier:@"singlestatus"];
} else {
/* Show the multistatus tableview tab if accounts are in different states, which includes the case of only one
* away state being in use but not all online accounts currently making use of it.
*/
NSInteger requiredHeight;
_awayAccounts = [[self awayAccounts] retain];
[tableView_multiStatus reloadData];
requiredHeight = (([tableView_multiStatus rowHeight] + [tableView_multiStatus intercellSpacing].height) *
[_awayAccounts count]);
newHeight = requiredHeight + 65;
frame.origin.y -= (newHeight - frame.size.height);
frame.size.height = newHeight;
/* Multiple statuses */
[tabView_configuration selectTabViewItemWithIdentifier:@"multistatus"];
}
//Perform the window resizing as needed
[window setFrame:frame display:YES animate:YES];
}
/*!
* @brief Return the attributed status title for a status
*
* This method puts statusIcon into an NSTextAttachment and prefixes statusState's status message or title with it.
*/
- (NSAttributedString *)attributedStatusTitleForStatus:(AIStatus *)statusState withIcon:(NSImage *)statusIcon
{
NSMutableAttributedString *statusTitle;
NSTextAttachment *attachment;
NSTextAttachmentCell *cell;
NSAttributedString *statusMessage;
if ((statusMessage = statusState.statusMessage) &&
([statusMessage length])) {
//Use the status message if it is set
statusTitle = [statusMessage mutableCopy];
[[statusTitle mutableString] insertString:@" "
atIndex:0];
} else {
//If it isn't, use the title
NSDictionary *attributesDict;
attributesDict = [NSDictionary dictionaryWithObject:[NSFont systemFontOfSize:0]
forKey:NSFontAttributeName];
statusTitle = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@" %@",[statusState title]]
attributes:attributesDict];
}
//Insert the image at the beginning
cell = [[NSTextAttachmentCell alloc] init];
[cell setImage:statusIcon];
attachment = [[NSTextAttachment alloc] init];
[attachment setAttachmentCell:cell];
[cell release];
[statusTitle insertAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]
atIndex:0];
[attachment release];
return [statusTitle autorelease];
}
/*!
* @brief Return an array of all away accounts
*/
- (NSArray *)awayAccounts
{
NSMutableArray *awayAccounts = [NSMutableArray array];
for (AIAccount *account in adium.accountController.accounts) {
if (account.online || [account boolValueForProperty:@"Connecting"]) {
AIStatus *statusState = account.statusState;
if (statusState.statusType != AIAvailableStatusType) {
[awayAccounts addObject:account];
}
}
}
return awayAccounts;
}
/*!
* @brief Return from away
*/
- (IBAction)returnFromAway:(id)sender
{
NSTabViewItem *selectedTabViewItem = [tabView_configuration selectedTabViewItem];
AIStatus *availableStatusState = [adium.statusController defaultInitialStatusState];
[self retain];
if ([[selectedTabViewItem identifier] isEqualToString:@"singlestatus"]) {
//Put all accounts in the Available status state
//We can perform this on all accounts without fear of bringing them online;
//Those that are offline will remain offline since -setActiveStatusState considers this.
[adium.statusController setActiveStatusState:availableStatusState];
} else {
//Multistatus
NSArray *selectedAccounts;
selectedAccounts = [[tableView_multiStatus selectedItemsFromArray:_awayAccounts] copy];
if ([selectedAccounts count]) {
//Apply the available status state to only the selected accounts
[adium.statusController applyState:availableStatusState
toAccounts:selectedAccounts];
} else {
//No selection: Put all accounts in the Available status state
//Like above, we can just call -setActiveStatusState and it will handle all accounts.
[adium.statusController setActiveStatusState:availableStatusState];
}
[selectedAccounts release];
}
[self release];
}
/*!
* @brief Perform initial setup for the multistatus table
*/
- (void)setupMultistatusTable
{
[[tableView_multiStatus tableColumnWithIdentifier:@"status"] setDataCell:[[[AIImageTextCell alloc] init] autorelease]];
}
#pragma mark Multiservice table view datasource
/*!
* @brief Number of rows in the table
*/
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
return [_awayAccounts count];
}
/*!
* @brief Table values
*
* Object value is the account's formatted UID
*/
- (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
AIAccount *account = [_awayAccounts objectAtIndex:row];
return account.formattedUID;
}
/*!
* @brief Will display a cell
*
* Set the image (status icon) and substring (status title) before display. Cell is an AIImageTextCell.
*/
- (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
AIAccount *account = [_awayAccounts objectAtIndex:row];
[cell setImage:[AIStatusIcons statusIconForListObject:account
type:AIServiceIconSmall
direction:AIIconNormal]];
[cell setSubString:[account.statusState title]];
}
- (void)localizeButtons
{
[button_return setLocalizedString:AILocalizedStringFromTableInBundle(@"Return",
@"Buttons",
[NSBundle bundleForClass:[self class]],
"Button to return from away in the away status window")];
}
- (void)statusIconSetChanged:(NSNotification *)inNotification
{
[self configureStatusWindow];
}
@end
|