Unescape the HREF attribute's text before passing it to NSURL which does not expect...
[adiumx.git] / Source / ESFastUserSwitchingSupportPlugin.m
blob61a379fcc9e936eb5e6fa8be76f703b3165d62d0
1 /* 
2  * Adium is the legal property of its developers, whose names are listed in the copyright file included
3  * with this source distribution.
4  * 
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.
8  * 
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.
12  * 
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.
15  */
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;
29 @end
31 extern NSString *NSWorkspaceSessionDidBecomeActiveNotification __attribute__((weak_import));
32 extern NSString *NSWorkspaceSessionDidResignActiveNotification __attribute__((weak_import));
34 /*!
35  * @class ESFastUserSwitchingSupportPlugin
36  * @brief Handle Fast User Switching and Screen Savers with a changed status and sound muting
37  *
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.
40  */
41 @implementation ESFastUserSwitchingSupportPlugin
43 /*!
44  * @brief Install plugin
45  */
46 - (void)installPlugin
48         NSNotificationCenter *workspaceCenter = [[NSWorkspace sharedWorkspace] notificationCenter];
49         [workspaceCenter addObserver:self
50                             selector:@selector(switchHandler:)
51                                 name:NSWorkspaceSessionDidBecomeActiveNotification
52                               object:nil];
54         [workspaceCenter addObserver:self
55                             selector:@selector(switchHandler:)
56                                 name:NSWorkspaceSessionDidResignActiveNotification
57                               object:nil];
59         [[NSDistributedNotificationCenter defaultCenter] addObserver:self
60                             selector:@selector(switchHandler:)
61                                 name:@"com.apple.screensaver.didstart"
62                               object:nil];
63         
64         [[NSDistributedNotificationCenter defaultCenter] addObserver:self
65                             selector:@selector(switchHandler:)
66                                 name:@"com.apple.screensaver.didstop"
67                               object:nil];
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];
74 /*!
75  * @brief Preferences changed
76  *
77  * Note whether we are supposed to change states on FUS or SS.
78  */
79 - (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
80                                                         object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
82         fastUserSwitchStatusID = [prefDict objectForKey:KEY_STATUS_FUS_STATUS_STATE_ID];
83         screenSaverStatusID    = [prefDict objectForKey:KEY_STATUS_SS_STATUS_STATE_ID];
84         
85         fastUserSwitchStatus = [[prefDict objectForKey:KEY_STATUS_FUS] boolValue];
86         screenSaverStatus = [[prefDict objectForKey:KEY_STATUS_SS] boolValue];
89 /*!
90  * @brief Uninstall plugin
91  */
92 - (void)uninstallPlugin
94         //Clear the fast switch away if we had it up before
95         [self switchHandler:nil];
97         [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
98         [[NSDistributedNotificationCenter defaultCenter] removeObserver:self];
99         [[adium preferenceController] unregisterPreferenceObserver:self];
102 - (void)dealloc
104         [previousStatusStateDict release];
105         [accountsToReconnect release];
106         
107         [super dealloc];
111  * @brief Handle a fast user switch or screen saver event
113  * Calling this with (notification == nil) is the same as when the user switches back.
115  * @param notification The notification has a name NSWorkspaceSessionDidResignActiveNotification when the user switches away and NSWorkspaceSessionDidBecomeActiveNotification when the user switches back.
116  */
117 -(void)switchHandler:(NSNotification*) notification
119         if (notification &&
120                 (([[notification name] isEqualToString:NSWorkspaceSessionDidResignActiveNotification] && fastUserSwitchStatus) ||
121                         ([[notification name] isEqualToString:@"com.apple.screensaver.didstart"] && screenSaverStatus))) {
122                 //Deactivation - go away
123                 //Go away if we aren't already away, noting the current status states for restoration later
124                 NSEnumerator    *enumerator;
125                 AIAccount               *account;
126                 AIStatusItem    *targetStatusState;
128                 if (!previousStatusStateDict) previousStatusStateDict = [[NSMutableDictionary alloc] init];
130                 if ([[notification name] isEqualToString:NSWorkspaceSessionDidResignActiveNotification])
131                         targetStatusState = [[adium statusController] statusStateWithUniqueStatusID:fastUserSwitchStatusID];
132                 else
133                         targetStatusState = [[adium statusController] statusStateWithUniqueStatusID:screenSaverStatusID];
134                 
135                 if ([targetStatusState isKindOfClass:[AIStatusGroup class]]) {
136                         targetStatusState = [(AIStatusGroup *)targetStatusState anyContainedStatus];
137                 }
138                 
139                 if (targetStatusState) {
140                         enumerator = [[[adium accountController] accounts] objectEnumerator];
141                         while ((account = [enumerator nextObject])) {
142                                 AIStatus        *currentStatusState = [account statusState];
143                                 if ([currentStatusState statusType] == AIAvailableStatusType) {
144                                         //Store the state the account is in at present
145                                         [previousStatusStateDict setObject:currentStatusState
146                                                                                                 forKey:[NSNumber numberWithUnsignedInt:[account hash]]];
148                                         if ([account online]) {
149                                                 //If online, set the state
150                                                 [account setStatusState:(AIStatus *)targetStatusState];
151                                                 
152                                                 //If we just brought the account offline, note that it will need to be reconnected later
153                                                 if ([targetStatusState statusType] == AIOfflineStatusType) {
154                                                         if (!accountsToReconnect) accountsToReconnect = [[NSMutableSet alloc] init];
155                                                         [accountsToReconnect addObject:account];
156                                                 }
157                                         } else {
158                                                 //If offline, set the state without coming online
159                                                 [account setStatusStateAndRemainOffline:(AIStatus *)targetStatusState];
160                                         }
161                                 }
162                         }
163                 }
165         } else if (!notification ||
166                            (([[notification name] isEqualToString:NSWorkspaceSessionDidBecomeActiveNotification] && fastUserSwitchStatus) ||
167                                 ([[notification name] isEqualToString:@"com.apple.screensaver.didstop"] && screenSaverStatus))) {
168                 //Activation - return from away
170                 //Remove the away status flag if we set it originally
171                 NSEnumerator    *enumerator;
172                 AIAccount               *account;
174                 enumerator = [[[adium accountController] accounts] objectEnumerator];
175                 while ((account = [enumerator nextObject])) {
176                         AIStatus                *targetStatusState;
177                         NSNumber                *accountHash = [NSNumber numberWithUnsignedInt:[account hash]];
179                         targetStatusState = [previousStatusStateDict objectForKey:accountHash];
180                         if (targetStatusState) {
181                                 if ([account online] || [accountsToReconnect containsObject:account]) {
182                                         //If online or needs to be reconnected, set the previous state, going online if necessary
183                                         [account setStatusState:targetStatusState];
184                                 } else {
185                                         //If offline, set the state without coming online
186                                         [account setStatusStateAndRemainOffline:targetStatusState];
187                                 }
188                         }
189                 }
191                 [previousStatusStateDict release]; previousStatusStateDict = nil;
192                 [accountsToReconnect release]; accountsToReconnect = nil;
193         }
196 @end