2 * Adium is the legal property of its developers, whose names are listed in the copyright file included
3 * with this source distribution.
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the License,
7 * or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
10 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
11 * Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 #import <Adium/AIAccountControllerProtocol.h>
18 #import <Adium/AIPreferenceControllerProtocol.h>
19 #import "AISoundController.h"
20 #import "AIStatusController.h"
21 #import "ESFastUserSwitchingSupportPlugin.h"
22 #import <AIUtilities/AIAttributedStringAdditions.h>
23 #import <AIUtilities/AIApplicationAdditions.h>
24 #import <Adium/AIAccount.h>
25 #import <Adium/AIStatusGroup.h>
27 @interface ESFastUserSwitchingSupportPlugin (PRIVATE)
28 -(void)switchHandler:(NSNotification*) notification;
31 extern NSString *NSWorkspaceSessionDidBecomeActiveNotification __attribute__((weak_import));
32 extern NSString *NSWorkspaceSessionDidResignActiveNotification __attribute__((weak_import));
35 * @class ESFastUserSwitchingSupportPlugin
36 * @brief Handle Fast User Switching and Screen Savers with a changed status and sound muting
38 * When the Screen Saver activates, or another user logs in via Fast User Switching (OS X 10.3 and above),
39 * this plugin sets a status state if an away state is not already set.
41 @implementation ESFastUserSwitchingSupportPlugin
44 * @brief Install plugin
48 NSNotificationCenter *workspaceCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
49 [workspaceCenter addObserver:self
50 selector:@selector(switchHandler:)
51 name:NSWorkspaceSessionDidBecomeActiveNotification
54 [workspaceCenter addObserver:self
55 selector:@selector(switchHandler:)
56 name:NSWorkspaceSessionDidResignActiveNotification
59 [[NSDistributedNotificationCenter defaultCenter] addObserver:self
60 selector:@selector(switchHandler:)
61 name:@"com.apple.screensaver.didstart"
64 [[NSDistributedNotificationCenter defaultCenter] addObserver:self
65 selector:@selector(switchHandler:)
66 name:@"com.apple.screensaver.didstop"
69 //Observe preference changes for updating when and how we should automatically change our state
70 [[adium preferenceController] registerPreferenceObserver:self
71 forGroup:PREF_GROUP_STATUS_PREFERENCES];
75 * @brief Preferences changed
77 * Note whether we are supposed to change states on FUS or SS.
79 - (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
80 object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
82 [fastUserSwitchStatusID release];
83 fastUserSwitchStatusID = [[prefDict objectForKey:KEY_STATUS_FUS_STATUS_STATE_ID] retain];
84 [screenSaverStatusID release];
85 screenSaverStatusID = [[prefDict objectForKey:KEY_STATUS_SS_STATUS_STATE_ID] retain];
87 fastUserSwitchStatus = [[prefDict objectForKey:KEY_STATUS_FUS] boolValue];
88 screenSaverStatus = [[prefDict objectForKey:KEY_STATUS_SS] boolValue];
92 * @brief Uninstall plugin
94 - (void)uninstallPlugin
96 //Clear the fast switch away if we had it up before
97 [self switchHandler:nil];
99 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
100 [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
101 [[adium preferenceController] unregisterPreferenceObserver:self];
106 [previousStatusStateDict release];
107 [accountsToReconnect release];
109 [fastUserSwitchStatusID release];
110 [screenSaverStatusID release];
116 * @brief Handle a fast user switch or screen saver event
118 * Calling this with (notification == nil) is the same as when the user switches back.
120 * @param notification The notification has a name NSWorkspaceSessionDidResignActiveNotification when the user switches away and NSWorkspaceSessionDidBecomeActiveNotification when the user switches back.
122 -(void)switchHandler:(NSNotification*) notification
124 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
127 (([[notification name] isEqualToString:NSWorkspaceSessionDidResignActiveNotification] && fastUserSwitchStatus) ||
128 ([[notification name] isEqualToString:@"com.apple.screensaver.didstart"] && screenSaverStatus))) {
129 //Deactivation - go away
130 //Go away if we aren't already away, noting the current status states for restoration later
131 NSEnumerator *enumerator;
133 AIStatusItem *targetStatusState;
135 if (!previousStatusStateDict) previousStatusStateDict = [[NSMutableDictionary alloc] init];
137 if ([[notification name] isEqualToString:NSWorkspaceSessionDidResignActiveNotification])
138 targetStatusState = [[adium statusController] statusStateWithUniqueStatusID:fastUserSwitchStatusID];
140 targetStatusState = [[adium statusController] statusStateWithUniqueStatusID:screenSaverStatusID];
142 if ([targetStatusState isKindOfClass:[AIStatusGroup class]]) {
143 targetStatusState = [(AIStatusGroup *)targetStatusState anyContainedStatus];
146 if (targetStatusState) {
147 enumerator = [[[adium accountController] accounts] objectEnumerator];
148 while ((account = [enumerator nextObject])) {
149 AIStatus *currentStatusState = [account statusState];
150 if ([currentStatusState statusType] == AIAvailableStatusType) {
151 //Store the state the account is in at present
152 [previousStatusStateDict setObject:currentStatusState
153 forKey:[NSNumber numberWithUnsignedInt:[account hash]]];
155 if ([account online]) {
156 //If online, set the state
157 [account setStatusState:(AIStatus *)targetStatusState];
159 //If we just brought the account offline, note that it will need to be reconnected later
160 if ([targetStatusState statusType] == AIOfflineStatusType) {
161 if (!accountsToReconnect) accountsToReconnect = [[NSMutableSet alloc] init];
162 [accountsToReconnect addObject:account];
165 //If offline, set the state without coming online
166 [account setStatusStateAndRemainOffline:(AIStatus *)targetStatusState];
172 } else if (!notification ||
173 (([[notification name] isEqualToString:NSWorkspaceSessionDidBecomeActiveNotification] && fastUserSwitchStatus) ||
174 ([[notification name] isEqualToString:@"com.apple.screensaver.didstop"] && screenSaverStatus))) {
175 //Activation - return from away
177 //Remove the away status flag if we set it originally
178 NSEnumerator *enumerator;
181 enumerator = [[[adium accountController] accounts] objectEnumerator];
182 while ((account = [enumerator nextObject])) {
183 AIStatus *targetStatusState;
184 NSNumber *accountHash = [NSNumber numberWithUnsignedInt:[account hash]];
186 targetStatusState = [previousStatusStateDict objectForKey:accountHash];
187 if (targetStatusState) {
188 if ([account online] || [accountsToReconnect containsObject:account]) {
189 //If online or needs to be reconnected, set the previous state, going online if necessary
190 [account setStatusState:targetStatusState];
192 //If offline, set the state without coming online
193 [account setStatusStateAndRemainOffline:targetStatusState];
198 [previousStatusStateDict release]; previousStatusStateDict = nil;
199 [accountsToReconnect release]; accountsToReconnect = nil;