Put NSAutoreleasePool usage around other distributed notification observer methods
[adiumx.git] / Source / ESGlobalEventsPreferencesPlugin.m
blobb9a793d3b1f2b3934ba0ba7b086f1b2099be2ce1
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 "AISoundController.h"
18 #import <Adium/AIContactAlertsControllerProtocol.h>
19 #import "ESGlobalEventsPreferences.h"
20 #import "ESGlobalEventsPreferencesPlugin.h"
21 #import <AIUtilities/AIDictionaryAdditions.h>
22 #import <AIUtilities/AIStringAdditions.h>
23 #import <AIUtilities/AIArrayAdditions.h>
24 #import <Adium/AISoundSet.h>
26 #define NEW_PRESET_NAME                         AILocalizedString(@"New Event Set",nil)
28 #define KEY_STORED_EVENT_PRESETS        @"Event Presets"
29 #define KEY_EVENT_SET_NAME                      @"Name"
30 #define KEY_ORDER_INDEX                         @"OrderIndex"
31 #define KEY_NEXT_ORDER_INDEX            @"NextOrderIndex"
33 #define EVENT_SOUNDS_DEFAULT_PREFS      @"EventSoundDefaults"
35 @interface ESGlobalEventsPreferencesPlugin (PRIVATE)
36 - (void)activateSoundSet:(NSArray *)soundSetArray;
38 - (void)activateDockBehaviorSet:(NSArray *)behaviorArray;
39 - (NSDictionary *)dockAlertFromPresetDictionary:(NSDictionary *)dictionary;
41 - (void)activateSpeechPreset:(NSArray *)presetArray;
42 - (NSDictionary *)speechAlertFromDictionary:(NSDictionary *)dictionary;
44 - (void)activateGrowlPreset:(NSArray *)presetArray;
45 - (NSDictionary *)growlAlertFromDictionary:(NSDictionary *)dictionary;
47 - (NSArray *)_behaviorForPreset:(NSString *)presetName inPresetArray:(NSArray *)presetArray;
49 - (void)_activateSet:(NSArray *)setArray withActionID:(NSString *)actionID alertGenerationSelector:(SEL)selector;
50 - (NSArray *)_availablePresetsFromArray:(NSArray *)presetsArray;
51 - (void)_updateActiveSetFromPresetsArray:(NSArray *)presetsArray withActionID:(NSString *)actionID alertGenerationSelector:(SEL)selector preferencesKey:(NSString *)prefKey preferencesGroup:(NSString *)prefGroup;
52 @end
54 @implementation ESGlobalEventsPreferencesPlugin
56 - (void)installPlugin
58         NSString        *activeEventSet;
59         
60         builtInEventPresets = [[NSDictionary dictionaryNamed:@"BuiltInEventPresets" forClass:[self class]] retain];
61         storedEventPresets = [[[adium preferenceController] preferenceForKey:KEY_STORED_EVENT_PRESETS
62                                                                                                                                    group:PREF_GROUP_EVENT_PRESETS] mutableCopy];
63         if (!storedEventPresets) storedEventPresets = [[NSMutableDictionary alloc] init];
65         /* If there is no active event set, or the active event set is not present in our built in or stored event sets
66          * then we are in one of two conditions: either this is a first-launch, or the user has deleted the event preferences.
67          * Either way, we want to set ourselves to the default notification set before proceeding.
68          */
69         activeEventSet = [[adium preferenceController] preferenceForKey:KEY_ACTIVE_EVENT_SET
70                                                                                                                           group:PREF_GROUP_EVENT_PRESETS];
71         if (!activeEventSet || (![builtInEventPresets objectForKey:activeEventSet] &&
72                                                    ![storedEventPresets objectForKey:activeEventSet])) {
73                 [self setEventPreset:[builtInEventPresets objectForKey:@"Default Notifications"]];              
74         }
76         [[adium preferenceController] registerDefaults:[NSDictionary dictionaryNamed:EVENT_SOUNDS_DEFAULT_PREFS
77                                                                                                                                                 forClass:[self class]]
78                                                                                   forGroup:PREF_GROUP_SOUNDS];
80         //Install our preference view
81     preferences = [[ESGlobalEventsPreferences preferencePaneForPlugin:self] retain];
83         //Wait for Adium to finish launching before we perform further actions
84         [[adium notificationCenter] addObserver:self
85                                                                    selector:@selector(adiumFinishedLaunching:)
86                                                                            name:AIApplicationDidFinishLoadingNotification
87                                                                          object:nil];   
90 - (void)uninstallPlugin
92     //Uninstall our observers
93     [[adium notificationCenter] removeObserver:preferences];
94     [[NSNotificationCenter defaultCenter] removeObserver:preferences];
97 - (void)adiumFinishedLaunching:(NSNotification *)notification
99         [[adium notificationCenter] removeObserver:self
100                                                                                   name:AIApplicationDidFinishLoadingNotification
101                                                                                 object:nil];
105 //Called when the preferences change, reregister for the notifications
106 - (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
107                                                         object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
112 #pragma mark Sound Sets
113 + (NSDictionary *)soundAlertForKey:(NSString *)key inSoundsDict:(NSDictionary *)sounds
115         NSDictionary    *soundAlert = nil;
116         NSString                *event;
118         if (key &&
119                 (event = [[[AIObject sharedAdiumInstance] contactAlertsController] eventIDForEnglishDisplayName:key])) {
120                 soundAlert = [NSDictionary dictionaryWithObjectsAndKeys:event, KEY_EVENT_ID,
121                         SOUND_ALERT_IDENTIFIER, KEY_ACTION_ID, 
122                         [NSDictionary dictionaryWithObject:[sounds objectForKey:key] forKey: KEY_ALERT_SOUND_PATH], KEY_ACTION_DETAILS,
123                         nil];
124         }
125         
126         return soundAlert;
130 * @brief Apply a sound set
131  */
132 - (void)applySoundSet:(AISoundSet *)soundSet
134         NSDictionary    *sounds = [soundSet sounds];
135         NSEnumerator    *enumerator;
136         NSString                *key;
137         
138         [[adium preferenceController] delayPreferenceChangedNotifications:YES];
139         
140         //Clear out old global sound alerts
141         [[adium contactAlertsController] removeAllGlobalAlertsWithActionID:SOUND_ALERT_IDENTIFIER];
143         AILog(@"Applying sound set %@",soundSet);
145         //
146         enumerator = [sounds keyEnumerator];
147         while ((key = [enumerator nextObject])) {
148                 NSDictionary *soundAlert = [ESGlobalEventsPreferencesPlugin soundAlertForKey:key
149                                                                                                                                                 inSoundsDict:sounds];
150                 if (soundAlert) {
151                         [[adium contactAlertsController] addGlobalAlert:soundAlert];
152                 }
153         }
154         
155         [[adium preferenceController] delayPreferenceChangedNotifications:NO];
159 #pragma mark Dock behavior sets
160 - (void)activateDockBehaviorSet:(NSArray *)behaviorArray
162         [self _activateSet:behaviorArray withActionID:DOCK_BEHAVIOR_ALERT_IDENTIFIER alertGenerationSelector:@selector(dockAlertFromPresetDictionary:)];
165 #pragma mark Speech presets
166 - (void)activateSpeechPreset:(NSArray *)presetArray
168         [self _activateSet:presetArray
169                   withActionID:SPEAK_EVENT_ALERT_IDENTIFIER
170 alertGenerationSelector:@selector(speechAlertFromDictionary:)];
173 #pragma mark Growl presets
174 - (void)activateGrowlPreset:(NSArray *)presetArray
176         [self _activateSet:presetArray withActionID:GROWL_EVENT_ALERT_IDENTIFIER alertGenerationSelector:@selector(growlAlertFromDictionary:)];
179 #pragma mark All simple presets
180 //Returns the behavior for a preset
181 - (NSArray *)_behaviorForPreset:(NSString *)presetName inPresetArray:(NSArray *)presetArray
183     NSEnumerator        *enumerator;
184     NSDictionary        *set;
185     
186     //Search for the desired set
187     enumerator = [presetArray objectEnumerator];
188     while ((set = [enumerator nextObject])) {
189         if ([presetName isEqualToString:[set objectForKey:@"Name"]]) {
190             return [set objectForKey:@"Behavior"];
191         }
192     }
193     
194     return nil;
197 - (void)_activateSet:(NSArray *)setArray withActionID:(NSString *)actionID alertGenerationSelector:(SEL)selector
199         NSEnumerator    *enumerator;
200         NSDictionary    *dictionary;
201         
202         //Clear out old global dock behavior alerts
203         [[adium contactAlertsController] removeAllGlobalAlertsWithActionID:actionID];
204         
205         //
206         enumerator = [setArray objectEnumerator];
207         while ((dictionary = [enumerator nextObject])) {
208                 [[adium contactAlertsController] addGlobalAlert:[self performSelector:selector
209                                                                                                                                    withObject:dictionary]];
210         }
213 - (void)setEventPreset:(NSDictionary *)eventPreset
215         [[adium contactAlertsController] setAllGlobalAlerts:[eventPreset objectForKey:@"Events"]];
216         
217         /* For a built in set, we now should apply the sound set it specified. User-created sets already include the
218          * soundset as individual events.
219          */
220         if ([eventPreset objectForKey:@"Built In"] && [[eventPreset objectForKey:@"Built In"] boolValue]) {
221                 NSString        *soundSet = [eventPreset objectForKey:KEY_EVENT_SOUND_SET];
222                 [self applySoundSet:(soundSet ? [AISoundSet soundSetWithContentsOfFile:[soundSet stringByExpandingBundlePath]] : nil)];
223         }
225         //Set the name of the now-active event set, which includes sounds and all other events
226         [[adium preferenceController] setPreference:[eventPreset objectForKey:KEY_EVENT_SET_NAME]
227                                                                                  forKey:KEY_ACTIVE_EVENT_SET
228                                                                                   group:PREF_GROUP_EVENT_PRESETS];
231 - (float)nextOrderIndex
233         NSNumber *nextOrderIndexNumber = [[adium preferenceController] preferenceForKey:KEY_NEXT_ORDER_INDEX
234                                                                                                                                                           group:PREF_GROUP_EVENT_PRESETS];
235         float   nextOrderIndex;
236         
237         nextOrderIndex = (nextOrderIndexNumber ? [nextOrderIndexNumber floatValue] : 1.0);
238         
239         [[adium preferenceController] setPreference:[NSNumber numberWithFloat:(nextOrderIndex + 1)]
240                                                                                  forKey:KEY_NEXT_ORDER_INDEX
241                                                                                   group:PREF_GROUP_EVENT_PRESETS];      
243         return nextOrderIndex;
247  * @brief Save an event preset
249  * This will assign an order index to the preset if necessary and then save it to the stored event presets dictionary.
250  * If a preset with the same name exists, it will be overwritten
251  */
252 - (void)saveEventPreset:(NSMutableDictionary *)eventPreset
254         NSString        *name = [eventPreset objectForKey:KEY_EVENT_SET_NAME];
255         //Assign the next order index to this preset if it doesn't have one yet
256         if (![eventPreset objectForKey:KEY_ORDER_INDEX]) {
257                 [eventPreset setObject:[NSNumber numberWithFloat:[self nextOrderIndex]]
258                                                 forKey:KEY_ORDER_INDEX];
259         }
261         //If we don't have a name at this point, simply assign one
262         if (!name) {
263                 name = NEW_PRESET_NAME;
264                 
265                 //Make sure we're not using a name which is already in use
266                 if ([storedEventPresets objectForKey:name]) {
267                         unsigned i = 1;
268                         name = [NEW_PRESET_NAME stringByAppendingFormat:@" (%i)",i];
269                         
270                         while ([storedEventPresets objectForKey:name] != nil) {
271                                 i++;
272                                 name = [NEW_PRESET_NAME stringByAppendingFormat:@" (%i)",i];
273                         }
274                 }
275                 
276                 NSAssert(name != nil, @"name is nil");
277                 [eventPreset setObject:name
278                                                 forKey:KEY_EVENT_SET_NAME];
279         }
280         
281         NSAssert(eventPreset != nil, @"eventPreset is nil");
282         [storedEventPresets setObject:eventPreset
283                                                    forKey:name];
285         [[adium preferenceController] setPreference:storedEventPresets
286                                                                                  forKey:KEY_STORED_EVENT_PRESETS
287                                                                                   group:PREF_GROUP_EVENT_PRESETS];
291  * @brief Delete an event preset
292  */
293 - (void)deleteEventPreset:(NSDictionary *)eventPreset
295         [storedEventPresets removeObjectForKey:[eventPreset objectForKey:KEY_EVENT_SET_NAME]];
296         
297         [[adium preferenceController] setPreference:storedEventPresets
298                                                                                  forKey:KEY_STORED_EVENT_PRESETS
299                                                                                   group:PREF_GROUP_EVENT_PRESETS];      
302 - (NSDictionary *)builtInEventPresets
304         return builtInEventPresets;
307 - (NSDictionary *)storedEventPresets
309         return storedEventPresets;
312 int eventPresetsSort(id eventPresetA, id eventPresetB, void *context)
314         float orderIndexA = [[eventPresetA objectForKey:KEY_ORDER_INDEX] floatValue];
315         float orderIndexB = [[eventPresetB objectForKey:KEY_ORDER_INDEX] floatValue];
316         
317         if (orderIndexA > orderIndexB) {
318                 return NSOrderedDescending;
319         } else if (orderIndexA < orderIndexB) {
320                 return NSOrderedAscending;
321         } else {
322                 return [[eventPresetA objectForKey:KEY_EVENT_SET_NAME] caseInsensitiveCompare:[eventPresetB objectForKey:KEY_EVENT_SET_NAME]];
323         }
326 - (NSArray *)storedEventPresetsArray
328         return [[storedEventPresets allValues] sortedArrayUsingFunction:eventPresetsSort
329                                                                                                                          context:nil];
332 - (NSArray *)builtInEventPresetsArray
334         return [[builtInEventPresets allValues] sortedArrayUsingFunction:eventPresetsSort
335                                                                                                                          context:nil];
338 @end