Libpurple.framework [484]: libpurple 2.2.0
[adiumx.git] / Source / CBContactCountingDisplayPlugin.m
blobebfe516b86a3a9030ad8265f355376b8844970e5
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 <Adium/AIMenuControllerProtocol.h>
19 #import <Adium/AIPreferenceControllerProtocol.h>
20 #import "CBContactCountingDisplayPlugin.h"
21 #import <AIUtilities/AIDictionaryAdditions.h>
22 #import <AIUtilities/AIMenuAdditions.h>
23 #import <AIUtilities/AIMutableOwnerArray.h>
24 #import <Adium/AIAccount.h>
25 #import <Adium/AIListContact.h>
26 #import <Adium/AIListObject.h>
27 #import <Adium/AIListGroup.h>
29 #define CONTACT_COUNTING_DISPLAY_DEFAULT_PREFS  @"ContactCountingDisplayDefaults"
31 #define COUNT_ONLINE_CONTACTS_TITLE                             AILocalizedString(@"Show Group Online Count", nil)
32 #define COUNT_ALL_CONTACTS_TITLE                                AILocalizedString(@"Show Group Total Count", nil)
34 #define PREF_GROUP_CONTACT_LIST                                 @"Contact List"
35 #define KEY_COUNT_ALL_CONTACTS                                  @"Count All Contacts"
36 #define KEY_COUNT_ONLINE_CONTACTS                               @"Count Online Contacts"
38 #define KEY_HIDE_CONTACT_LIST_GROUPS                    @"Hide Contact List Groups"
39 #define PREF_GROUP_CONTACT_LIST_DISPLAY                 @"Contact List Display"
41 /*!
42  * @class CBContactCountingDisplayPlugin
43  *
44  * @brief Component to handle displaying counts of contacts, both online and total, next to group names
45  *
46  * This componenet adds two menu items, "Count All Contacts" and "Count Online Contacts." Both default to being off.
47  * When on, these options display the appropriate count for an AIListGroup's contained objects.
48  */
49 @implementation CBContactCountingDisplayPlugin
51 /*!
52  * @brief Install
53  */
54 - (void)installPlugin
56     //register our defaults
57     [[adium preferenceController] registerDefaults:[NSDictionary dictionaryNamed:CONTACT_COUNTING_DISPLAY_DEFAULT_PREFS 
58                                                                                                                                                 forClass:[self class]] 
59                                                                                   forGroup:PREF_GROUP_CONTACT_LIST];
60         
61     //init our menu items
62     menuItem_countOnlineObjects = [[NSMenuItem alloc] initWithTitle:COUNT_ONLINE_CONTACTS_TITLE 
63                                                                                                                  target:self 
64                                                                                                                  action:@selector(toggleMenuItem:)
65                                                                                                   keyEquivalent:@""];
66     [[adium menuController] addMenuItem:menuItem_countOnlineObjects toLocation:LOC_View_Toggles];               
68     menuItem_countAllObjects = [[NSMenuItem alloc] initWithTitle:COUNT_ALL_CONTACTS_TITLE
69                                                                                                                  target:self 
70                                                                                                                  action:@selector(toggleMenuItem:)
71                                                                                                   keyEquivalent:@""];
72         [[adium menuController] addMenuItem:menuItem_countAllObjects toLocation:LOC_View_Toggles];              
73     
74         //set up the prefs
75         countAllObjects = NO;
76         countOnlineObjects = NO;
77         
78         [[adium preferenceController] registerPreferenceObserver:self forGroup:PREF_GROUP_CONTACT_LIST];
79         [[adium preferenceController] registerPreferenceObserver:self forGroup:PREF_GROUP_CONTACT_LIST_DISPLAY];
82 /*!
83  * @brief Preferences changed
84  *
85  * PREF_GROUP_CONTACT_LIST preferences changed; update our counting display as necessary.
86  */
87 - (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
88                                                         object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
90         if ([group isEqualToString:PREF_GROUP_CONTACT_LIST]) {
91                 BOOL oldCountAllObjects = countAllObjects;
92                 BOOL oldCountOnlineObjects = countOnlineObjects;
93                 
94                 countAllObjects = [[prefDict objectForKey:KEY_COUNT_ALL_CONTACTS] boolValue];
95                 countOnlineObjects = [[prefDict objectForKey:KEY_COUNT_ONLINE_CONTACTS] boolValue];
96                 
97                 if ((countAllObjects && !oldCountAllObjects) || (countOnlineObjects && !oldCountOnlineObjects)) {
98                         //One of the displays is on, but it was off before
99                         
100                         if (!oldCountAllObjects && !oldCountOnlineObjects) {
101                                 //Install our observer if we are now counting contacts in some form but weren't before
102                                 //This will update all list objects.
103                                 [[adium contactController] registerListObjectObserver:self];                            
104                         } else {
105                                 //Refresh all
106                                 [[adium contactController] updateAllListObjectsForObserver:self];
107                         }
108                         
109                 } else if ((!countAllObjects && oldCountAllObjects) || (!countOnlineObjects && oldCountOnlineObjects)) {
110                         //One of the displays is off, but it was on before
111                         
112                         //Refresh all
113                         [[adium contactController] updateAllListObjectsForObserver:self];
114                         
115                         if (!countAllObjects && !countOnlineObjects) {
116                                 //Remove our observer since we are now doing no counting
117                                 [[adium contactController] unregisterListObjectObserver:self];
118                         }
119                 }
120                 
121                 if ([menuItem_countAllObjects state] != countAllObjects) {
122                         [menuItem_countAllObjects setState:countAllObjects];
123                 }
124                 if ([menuItem_countOnlineObjects state] != countOnlineObjects) {
125                         [menuItem_countOnlineObjects setState:countOnlineObjects];
126                 }
128         } else if (([group isEqualToString:PREF_GROUP_CONTACT_LIST_DISPLAY]) &&
129                            (!key || [key isEqualToString:KEY_HIDE_CONTACT_LIST_GROUPS])) {              
130                 showingGroups = ![[prefDict objectForKey:KEY_HIDE_CONTACT_LIST_GROUPS] boolValue];
131         }
135  * @brief Update the counts when a group changes its object count or a contact signs on or off
136  */
137 - (NSSet *)updateListObject:(AIListObject *)inObject keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
138 {    
139         NSSet           *modifiedAttributes = nil;
141         //We never update for an AIAccount object
142         if ([inObject isKindOfClass:[AIAccount class]]) return nil;
144         /* We check against a nil inModifiedKeys so we can remove our Counting information from the display when the user
145          * toggles it off.
146          *
147          * We update for any group which isn't the root group when its contained objects count changes.
148          * We update a contact's containing group when its online state changes.
149          */     
150         if ((inModifiedKeys == nil) ||
151            ((countOnlineObjects || countAllObjects) &&
152                 (([inObject isKindOfClass:[AIListGroup class]] && [inModifiedKeys containsObject:@"ObjectCount"] && ![[inObject UID] isEqualToString:ADIUM_ROOT_GROUP_NAME]) ||
153                  ([inObject isKindOfClass:[AIListContact class]] && [inModifiedKeys containsObject:@"Online"])))) {
154                 
155                 /* Obtain the group we want to work with -- for a contact, use its parent group.
156                  *
157                  * Casting note: We already checked that it isn't an AIAccount. If it's an AIListContact, we get the parentGroup. Otherwise,
158                  * it's an AIListGroup and we use the object itself. There is probably a way to set this method up without this convoluted casting interplay.
159                  */
160                 AIListGroup             *targetGroup = ([inObject isKindOfClass:[AIListContact class]] ? 
161                                                                                 [(AIListContact *)inObject parentGroup] :
162                                                                                 (AIListGroup *)inObject);
164                 NSString                *countString = nil;
165                 int onlineObjects = 0, totalObjects = 0;
167                 //Obtain a count of online objects in this group
168                 if (countOnlineObjects) {
169                         AIListObject    *containedObject;
170                         NSEnumerator    *enumerator;
171                         
172                         onlineObjects = 0;
173                         enumerator = [[targetGroup containedObjects] objectEnumerator];
174                         while ((containedObject = [enumerator nextObject])) {
175                                 if ([containedObject online]) onlineObjects++;
176                         }
177                 }
178                 
179                 //Obtain a count of all objects in this group
180                 if (countAllObjects) {
181                         totalObjects = [[targetGroup statusObjectForKey:@"ObjectCount"] intValue];
182                 }
183         
184                 //Build a string to add to the right of the name which shows any information we just extracted
185                 if (countOnlineObjects && countAllObjects) {
186                         countString = [NSString stringWithFormat:AILocalizedString(@" (%i of %i)", /*comment*/ nil), onlineObjects, totalObjects];
187                 } else if (countAllObjects) {
188                         countString = [NSString stringWithFormat:@" (%i)", totalObjects];
189                 } else if (countOnlineObjects) {
190                         countString = [NSString stringWithFormat:@" (%i)", onlineObjects];
191                 }
193                 if (countString) {
194                         AIMutableOwnerArray *rightTextArray = [targetGroup displayArrayForKey:@"Right Text"];
195                         
196                         [rightTextArray setObject:countString withOwner:self priorityLevel:High_Priority];
197                         modifiedAttributes = [NSSet setWithObject:@"Right Text"];
198                 } else {
199                         AIMutableOwnerArray *rightTextArray = [targetGroup displayArrayForKey:@"Right Text" create:NO];
200                         
201                         //If there is a right text object now but there shouldn't be anymore, remove it
202                         if ([rightTextArray objectWithOwner:self]) {
203                                 [rightTextArray setObject:nil withOwner:self priorityLevel:High_Priority];
204                                 modifiedAttributes = [NSSet setWithObject:@"Right Text"];
205                         }
206                 }
207         }
208         
209         return modifiedAttributes;
213  * @brief User toggled one of our two menu items
214  */
215 - (void)toggleMenuItem:(id)sender
217     if ((sender == menuItem_countOnlineObjects) || (sender == menuItem_countAllObjects)) {
218                 BOOL    newState = ![sender state];
219                 
220                 //Toggle and set, which will call back on preferencesChanged: above
221         [sender setState:newState];
222         [[adium preferenceController] setPreference:[NSNumber numberWithBool:newState]
223                                                                                          forKey:(sender == menuItem_countAllObjects ?
224                                                                                                          KEY_COUNT_ALL_CONTACTS : 
225                                                                                                          KEY_COUNT_ONLINE_CONTACTS)
226                                                                                           group:PREF_GROUP_CONTACT_LIST];
227     }
230 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
232     if ((menuItem == menuItem_countOnlineObjects) || (menuItem == menuItem_countAllObjects)) {
233                 return showingGroups;
234         }
235         
236         return YES;
240  * Uninstall
241  */
242 - (void)uninstallPlugin
244     //we are no longer an observer
245     [[adium notificationCenter] removeObserver:self];
246     [[adium contactController] unregisterListObjectObserver:self];
247         [[adium preferenceController] unregisterPreferenceObserver:self];
250 @end