Unescape the HREF attribute's text before passing it to NSURL which does not expect...
[adiumx.git] / Source / AIContactIdlePlugin.m
blob7d2dced4ddd96f42f78216c16758a609c8d17d6d
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/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;
30 @end
32 /*!
33  * @class AIContactIdlePlugin
34  * @brief Contact idle time updating, and idle time tooltip component
35  */
36 @implementation AIContactIdlePlugin
38 /*!
39  * @brief Install
40  */
41 - (void)installPlugin
43     idleObjectArray = nil;
45     //Install our tooltip entry
46     [[adium interfaceController] registerContactListTooltipEntry:self secondaryEntry:YES];
48     //
49     [[adium contactController] registerListObjectObserver:self];
52 /*!
53  * @brief Uninstall
54  */
55 - (void)uninstallPlugin
57     //Stop tracking all idle handles
58     [idleObjectTimer invalidate]; [idleObjectTimer release]; idleObjectTimer = nil;
59         [[adium contactController] unregisterListObjectObserver:self];
62 /*!
63  * @brief Deallocate
64  */
65 - (void)dealloc
67     [idleObjectArray release]; idleObjectArray = nil;
68         
69         [super dealloc];
72 /*!
73  * @brief Update list object
74  *
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.
78  *
79  * Don't calculate an idle time for a metacontact; its "idle time" should be determined dynamically based on its contained contacts.
80  */
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) {
87             //Track the handle
88             if (!idleObjectArray) {
89                 idleObjectArray = [[NSMutableArray alloc] init];
90                 idleObjectTimer = [[NSTimer scheduledTimerWithTimeInterval:IDLE_UPDATE_INTERVAL
91                                                                                                                                         target:self 
92                                                                                                                                   selector:@selector(updateIdleObjectsTimer:)
93                                                                                                                                   userInfo:nil 
94                                                                                                                                    repeats:YES] retain];
95             }
96             [idleObjectArray addObject:inObject];
97                         
98                         //Set the correct idle value
99                         [self setIdleForObject:inObject silent:silent];
101         } else {
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;
108                                 }
109                                 
110                                 //Set the correct idle value
111                                 [self setIdleForObject:inObject silent:silent];
112                         }
113         }
114     }
116     return nil;
118         
120  * @brief Updates the idle duration of all idle contacts
121  */
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];
137                 [pool release];
138     }
139         
140         [[adium contactController] endListObjectNotificationsDelay];
144  * @brief Give a contact its correct idle value
145  */
146 - (void)setIdleForObject:(AIListObject *)inObject silent:(BOOL)silent
148         NSDate          *idleSince = [inObject statusObjectForKey:@"IdleSince"];
149         NSNumber        *idleNumber = nil;
150         
151         if (idleSince) { //Set the handle's 'idle' value
152                 int     idle = -[idleSince timeIntervalSinceNow] / 60.0;
153                 
154                 /* They are idle; a non-zero idle time is needed.  We'll treat them as generically idle until this updates */
155                 if (idle == 0) {
156                         idle = -1;
157                 }
159                 idleNumber = [NSNumber numberWithInt:idle];
160         }
162         [inObject setStatusObject:idleNumber
163                                            forKey:@"Idle"
164                                            notify:NotifyLater];
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
177  */
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);
187         }
189         return entry;
193  * @brief Tooltip entry
195  * @result The tooltip entry, or nil if no tooltip should be shown
196  */
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)];
204                 
205     } else if (idleMinutes != 0) {
206                 entry = [[NSAttributedString alloc] initWithString:[NSDateFormatter stringForTimeInterval:(idleMinutes * 60.0)]];    
207         }
209     return [entry autorelease];
212 @end