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 <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"
42 * @class CBContactCountingDisplayPlugin
44 * @brief Component to handle displaying counts of contacts, both online and total, next to group names
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.
49 @implementation CBContactCountingDisplayPlugin
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];
62 menuItem_countOnlineObjects = [[NSMenuItem alloc] initWithTitle:COUNT_ONLINE_CONTACTS_TITLE
64 action:@selector(toggleMenuItem:)
66 [[adium menuController] addMenuItem:menuItem_countOnlineObjects toLocation:LOC_View_Toggles];
68 menuItem_countAllObjects = [[NSMenuItem alloc] initWithTitle:COUNT_ALL_CONTACTS_TITLE
70 action:@selector(toggleMenuItem:)
72 [[adium menuController] addMenuItem:menuItem_countAllObjects toLocation:LOC_View_Toggles];
76 countOnlineObjects = NO;
78 [[adium preferenceController] registerPreferenceObserver:self forGroup:PREF_GROUP_CONTACT_LIST];
79 [[adium preferenceController] registerPreferenceObserver:self forGroup:PREF_GROUP_CONTACT_LIST_DISPLAY];
83 * @brief Preferences changed
85 * PREF_GROUP_CONTACT_LIST preferences changed; update our counting display as necessary.
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;
94 countAllObjects = [[prefDict objectForKey:KEY_COUNT_ALL_CONTACTS] boolValue];
95 countOnlineObjects = [[prefDict objectForKey:KEY_COUNT_ONLINE_CONTACTS] boolValue];
97 if ((countAllObjects && !oldCountAllObjects) || (countOnlineObjects && !oldCountOnlineObjects)) {
98 //One of the displays is on, but it was off before
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];
106 [[adium contactController] updateAllListObjectsForObserver:self];
109 } else if ((!countAllObjects && oldCountAllObjects) || (!countOnlineObjects && oldCountOnlineObjects)) {
110 //One of the displays is off, but it was on before
113 [[adium contactController] updateAllListObjectsForObserver:self];
115 if (!countAllObjects && !countOnlineObjects) {
116 //Remove our observer since we are now doing no counting
117 [[adium contactController] unregisterListObjectObserver:self];
121 if ([menuItem_countAllObjects state] != countAllObjects) {
122 [menuItem_countAllObjects setState:countAllObjects];
124 if ([menuItem_countOnlineObjects state] != countOnlineObjects) {
125 [menuItem_countOnlineObjects setState:countOnlineObjects];
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];
135 * @brief Update the counts when a group changes its object count or a contact signs on or off
137 - (NSSet *)updateListObject:(AIListObject *)inObject keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
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
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.
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"])))) {
155 /* Obtain the group we want to work with -- for a contact, use its parent group.
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.
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;
173 enumerator = [[targetGroup containedObjects] objectEnumerator];
174 while ((containedObject = [enumerator nextObject])) {
175 if ([containedObject online]) onlineObjects++;
179 //Obtain a count of all objects in this group
180 if (countAllObjects) {
181 totalObjects = [[targetGroup statusObjectForKey:@"ObjectCount"] intValue];
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];
194 AIMutableOwnerArray *rightTextArray = [targetGroup displayArrayForKey:@"Right Text"];
196 [rightTextArray setObject:countString withOwner:self priorityLevel:High_Priority];
197 modifiedAttributes = [NSSet setWithObject:@"Right Text"];
199 AIMutableOwnerArray *rightTextArray = [targetGroup displayArrayForKey:@"Right Text" create:NO];
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"];
209 return modifiedAttributes;
213 * @brief User toggled one of our two menu items
215 - (void)toggleMenuItem:(id)sender
217 if ((sender == menuItem_countOnlineObjects) || (sender == menuItem_countAllObjects)) {
218 BOOL newState = ![sender state];
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];
230 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
232 if ((menuItem == menuItem_countOnlineObjects) || (menuItem == menuItem_countAllObjects)) {
233 return showingGroups;
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];