Merged [14244]: Small but vital blocking fix from Kiel Gillard:
[adiumx.git] / Source / CBContactCountingDisplayPlugin.m
blob249ab88908309f42054fcb398c05f1ed3e96fbda
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 "AIContactController.h"
18 #import "AIMenuController.h"
19 #import "AIPreferenceController.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 /*!
39  * @class CBContactCountingDisplayPlugin
40  *
41  * @brief Component to handle displaying counts of contacts, both online and total, next to group names
42  *
43  * This componenet adds two menu items, "Count All Contacts" and "Count Online Contacts." Both default to being off.
44  * When on, these options display the appropriate count for an AIListGroup's contained objects.
45  */
46 @implementation CBContactCountingDisplayPlugin
48 /*!
49  * @brief Install
50  */
51 - (void)installPlugin
53     //register our defaults
54     [[adium preferenceController] registerDefaults:[NSDictionary dictionaryNamed:CONTACT_COUNTING_DISPLAY_DEFAULT_PREFS 
55                                                                                                                                                 forClass:[self class]] 
56                                                                                   forGroup:PREF_GROUP_CONTACT_LIST];
58     //init our menu items
59     menuItem_countOnlineObjects = [[NSMenuItem alloc] initWithTitle:COUNT_ONLINE_CONTACTS_TITLE 
60                                                                                                                  target:self 
61                                                                                                                  action:@selector(toggleMenuItem:)
62                                                                                                   keyEquivalent:@""];
63     [[adium menuController] addMenuItem:menuItem_countOnlineObjects toLocation:LOC_View_Toggles];               
65     menuItem_countAllObjects = [[NSMenuItem alloc] initWithTitle:COUNT_ALL_CONTACTS_TITLE
66                                                                                                                  target:self 
67                                                                                                                  action:@selector(toggleMenuItem:)
68                                                                                                   keyEquivalent:@""];
69         [[adium menuController] addMenuItem:menuItem_countAllObjects toLocation:LOC_View_Toggles];              
70     
71         //set up the prefs
72         countAllObjects = NO;
73         countOnlineObjects = NO;
74         
75         [[adium preferenceController] registerPreferenceObserver:self forGroup:PREF_GROUP_CONTACT_LIST];
78 /*!
79  * @brief Preferences changed
80  *
81  * PREF_GROUP_CONTACT_LIST preferences changed; update our counting display as necessary.
82  */
83 - (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
84                                                         object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
86         BOOL oldCountAllObjects = countAllObjects;
87         BOOL oldCountOnlineObjects = countOnlineObjects;
88         
89         countAllObjects = [[prefDict objectForKey:KEY_COUNT_ALL_CONTACTS] boolValue];
90         countOnlineObjects = [[prefDict objectForKey:KEY_COUNT_ONLINE_CONTACTS] boolValue];
91         
92         if ((countAllObjects && !oldCountAllObjects) || (countOnlineObjects && !oldCountOnlineObjects)){
93                 //One of the displays is on, but it was off before
94                 
95                 if (!oldCountAllObjects && !oldCountOnlineObjects){
96                         //Install our observer if we are now counting contacts in some form but weren't before
97                         //This will update all list objects.
98                         [[adium contactController] registerListObjectObserver:self];                            
99                 }else{
100                         //Refresh all
101                         [[adium contactController] updateAllListObjectsForObserver:self];
102                 }
103                 
104         }else if ((!countAllObjects && oldCountAllObjects) || (!countOnlineObjects && oldCountOnlineObjects)){
105                 //One of the displays is off, but it was on before
106                 
107                 //Refresh all
108                 [[adium contactController] updateAllListObjectsForObserver:self];
109                 
110                 if (!countAllObjects && !countOnlineObjects){
111                         //Remove our observer since we are now doing no counting
112                         [[adium contactController] unregisterListObjectObserver:self];
113                 }
114         }
116         if([menuItem_countAllObjects state] != countAllObjects) {
117                 [menuItem_countAllObjects setState:countAllObjects];
118         }
119         if([menuItem_countOnlineObjects state] != countOnlineObjects) {
120                 [menuItem_countOnlineObjects setState:countOnlineObjects];
121         }
125  * @brief Update the counts when a group changes its object count or a contact signs on or off
126  */
127 - (NSSet *)updateListObject:(AIListObject *)inObject keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
128 {    
129         NSSet           *modifiedAttributes = nil;
131         //We never update for an AIAccount object
132         if([inObject isKindOfClass:[AIAccount class]]) return nil;
134         /* We check against a nil inModifiedKeys so we can remove our Counting information from the display when the user
135          * toggles it off.
136          *
137          * We update for any group which isn't the root group when its contained objects count changes.
138          * We update a contact's containing group when its online state changes.
139          */     
140         if((inModifiedKeys == nil) ||
141            ((countOnlineObjects || countAllObjects) &&
142                 (([inObject isKindOfClass:[AIListGroup class]] && [inModifiedKeys containsObject:@"ObjectCount"] && ![[inObject UID] isEqualToString:ADIUM_ROOT_GROUP_NAME]) ||
143                  ([inObject isKindOfClass:[AIListContact class]] && [inModifiedKeys containsObject:@"Online"])))){
144                 
145                 //Obtain the group we want to work with -- for a contact, use its parent group.
146                 AIListGroup             *targetGroup = ([inObject isKindOfClass:[AIListContact class]] ? 
147                                                                                 [inObject parentGroup] :
148                                                                                 inObject);
150                 NSString                *countString = nil;
151                 int onlineObjects = 0, totalObjects = 0;
153                 //Obtain a count of online objects in this group
154                 if(countOnlineObjects){
155                         AIListObject    *containedObject;
156                         NSEnumerator    *enumerator;
157                         
158                         onlineObjects = 0;
159                         enumerator = [targetGroup objectEnumerator];
160                         while(containedObject = [enumerator nextObject]){
161                                 if([containedObject online]) onlineObjects++;
162                         }
163                 }
164                 
165                 //Obtain a count of all objects in this group
166                 if(countAllObjects){
167                         totalObjects = [[targetGroup statusObjectForKey:@"ObjectCount"] intValue];
168                 }
169         
170                 //Build a string to add to the right of the name which shows any information we just extracted
171                 if (countOnlineObjects && countAllObjects){
172                         countString = [NSString stringWithFormat:@" (%i/%i)", onlineObjects, totalObjects];
173                 }else if(countAllObjects){
174                         countString = [NSString stringWithFormat:@" (%i)", totalObjects];
175                 }else if(countOnlineObjects){
176                         countString = [NSString stringWithFormat:@" (%i)", onlineObjects];
177                 }
179                 if (countString){
180                         AIMutableOwnerArray *rightTextArray = [targetGroup displayArrayForKey:@"Right Text"];
181                         
182                         [rightTextArray setObject:countString withOwner:self priorityLevel:High_Priority];
183                         modifiedAttributes = [NSSet setWithObject:@"Right Text"];
184                 }else{
185                         AIMutableOwnerArray *rightTextArray = [targetGroup displayArrayForKey:@"Right Text" create:NO];
186                         
187                         //If there is a right text object now but there shouldn't be anymore, remove it
188                         if ([rightTextArray objectWithOwner:self]){
189                                 [rightTextArray setObject:nil withOwner:self priorityLevel:High_Priority];
190                                 modifiedAttributes = [NSSet setWithObject:@"Right Text"];
191                         }
192                 }
193         }
194         
195         return(modifiedAttributes);
199  * @brief User toggled one of our two menu items
200  */
201 - (void)toggleMenuItem:(id)sender
203     if(sender == menuItem_countOnlineObjects || sender == menuItem_countAllObjects) {
204                 BOOL    newState = ![sender state];
205                 
206                 //Toggle and set, which will call back on preferencesChanged: above
207         [sender setState:newState];
208         [[adium preferenceController] setPreference:[NSNumber numberWithBool:newState]
209                                                                                          forKey:(sender == menuItem_countAllObjects ?
210                                                                                                          KEY_COUNT_ALL_CONTACTS : 
211                                                                                                          KEY_COUNT_ONLINE_CONTACTS)
212                                                                                           group:PREF_GROUP_CONTACT_LIST];
213     }
217  * Uninstall
218  */
219 - (void)uninstallPlugin
221     //we are no longer an observer
222     [[adium notificationCenter] removeObserver:self];
223     [[adium contactController] unregisterListObjectObserver:self];
224         [[adium preferenceController] unregisterPreferenceObserver:self];
227 @end