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/AIContactControllerProtocol.h>
18 #import "AIContactIdlePlugin.h"
19 #import <Adium/AIInterfaceControllerProtocol.h>
20 #import <AIUtilities/AIArrayAdditions.h>
21 #import <AIUtilities/AIDateFormatterAdditions.h>
22 #import <Adium/AIListObject.h>
23 #import <Adium/AIMetaContact.h>
25 #define IDLE_UPDATE_INTERVAL 60.0
27 @interface AIContactIdlePlugin (PRIVATE)
28 - (void)setIdleForObject:(AIListObject *)inObject silent:(BOOL)silent;
29 - (void)updateIdleObjectsTimer:(NSTimer *)inTimer;
33 * @class AIContactIdlePlugin
34 * @brief Contact idle time updating, and idle time tooltip component
36 @implementation AIContactIdlePlugin
43 idleObjectArray = nil;
45 //Install our tooltip entry
46 [[adium interfaceController] registerContactListTooltipEntry:self secondaryEntry:YES];
49 [[adium contactController] registerListObjectObserver:self];
55 - (void)uninstallPlugin
57 //Stop tracking all idle handles
58 [idleObjectTimer invalidate]; [idleObjectTimer release]; idleObjectTimer = nil;
59 [[adium contactController] unregisterListObjectObserver:self];
67 [idleObjectArray release]; idleObjectArray = nil;
73 * @brief Update list object
75 * When the idleSince status key changes, we start or stop tracking the object as appropriate.
76 * We track in order to have a simple number associated with the contact, updated once per minute, rather
77 * than calculating the time from IdleSince until Now whenever we want to display the idle time.
79 * Don't calculate an idle time for a metacontact; its "idle time" should be determined dynamically based on its contained contacts.
81 - (NSSet *)updateListObject:(AIListObject *)inObject keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
83 if ((inModifiedKeys == nil || [inModifiedKeys containsObject:@"IdleSince"]) &&
84 ![inObject isKindOfClass:[AIMetaContact class]]) {
86 if ([inObject statusObjectForKey:@"IdleSince"] != nil) {
88 if (!idleObjectArray) {
89 idleObjectArray = [[NSMutableArray alloc] init];
90 idleObjectTimer = [[NSTimer scheduledTimerWithTimeInterval:IDLE_UPDATE_INTERVAL
92 selector:@selector(updateIdleObjectsTimer:)
96 [idleObjectArray addObject:inObject];
98 //Set the correct idle value
99 [self setIdleForObject:inObject silent:silent];
102 if ([idleObjectArray containsObjectIdenticalTo:inObject]) {
103 //Stop tracking the handle
104 [idleObjectArray removeObject:inObject];
105 if ([idleObjectArray count] == 0) {
106 [idleObjectTimer invalidate]; [idleObjectTimer release]; idleObjectTimer = nil;
107 [idleObjectArray release]; idleObjectArray = nil;
110 //Set the correct idle value
111 [self setIdleForObject:inObject silent:silent];
120 * @brief Updates the idle duration of all idle contacts
122 - (void)updateIdleObjectsTimer:(NSTimer *)inTimer
124 NSEnumerator *enumerator;
125 AIListObject *object;
127 //There's actually no reason to re-sort in response to these status changes, but there is no way for us to
128 //let the Adium core know that. The best we can do is delay updates so only a single sort occurs
129 //of course, smart sorting controllers should be watching IdleSince, not Idle, since that's the important bit
130 [[adium contactController] delayListObjectNotifications];
132 //Update everyone's idle time
133 enumerator = [idleObjectArray objectEnumerator];
134 while ((object = [enumerator nextObject])) {
135 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
136 [self setIdleForObject:object silent:YES];
140 [[adium contactController] endListObjectNotificationsDelay];
144 * @brief Give a contact its correct idle value
146 - (void)setIdleForObject:(AIListObject *)inObject silent:(BOOL)silent
148 NSDate *idleSince = [inObject statusObjectForKey:@"IdleSince"];
149 NSNumber *idleNumber = nil;
151 if (idleSince) { //Set the handle's 'idle' value
152 int idle = -[idleSince timeIntervalSinceNow] / 60.0;
154 /* They are idle; a non-zero idle time is needed. We'll treat them as generically idle until this updates */
159 idleNumber = [NSNumber numberWithInt:idle];
162 [inObject setStatusObject:idleNumber
166 [inObject notifyOfChangedStatusSilently:silent];
170 //Tooltip entry ---------------------------------------------------------------------------------
171 #pragma mark Tooltip entry
174 * @brief Tooltip label
176 * @result A label, or nil if no tooltip entry should be shown
178 - (NSString *)labelForObject:(AIListObject *)inObject
180 int idle = [inObject integerStatusObjectForKey:@"Idle"];
181 NSString *entry = nil;
183 if ((idle > 599400) || (idle == -1)) { //Cap idle at 999 Hours (999*60*60 seconds)
184 entry = AILocalizedString(@"Idle",nil);
185 } else if (idle != 0) {
186 entry = AILocalizedString(@"Idle Time",nil);
193 * @brief Tooltip entry
195 * @result The tooltip entry, or nil if no tooltip should be shown
197 - (NSAttributedString *)entryForObject:(AIListObject *)inObject
199 int idleMinutes = [inObject integerStatusObjectForKey:@"Idle"];
200 NSAttributedString *entry = nil;
202 if ((idleMinutes > 599400) || (idleMinutes == -1)) { //Cap idle at 999 Hours (999*60 minutes)
203 entry = [[NSAttributedString alloc] initWithString:AILocalizedString(@"Yes",nil)];
205 } else if (idleMinutes != 0) {
206 entry = [[NSAttributedString alloc] initWithString:[NSDateFormatter stringForTimeInterval:(idleMinutes * 60.0)]];
209 return [entry autorelease];