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 "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;
54 @implementation ESGlobalEventsPreferencesPlugin
58 NSString *activeEventSet;
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.
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"]];
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
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
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;
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,
130 * @brief Apply a sound set
132 - (void)applySoundSet:(AISoundSet *)soundSet
134 NSDictionary *sounds = [soundSet sounds];
135 NSEnumerator *enumerator;
138 [[adium preferenceController] delayPreferenceChangedNotifications:YES];
140 //Clear out old global sound alerts
141 [[adium contactAlertsController] removeAllGlobalAlertsWithActionID:SOUND_ALERT_IDENTIFIER];
143 AILog(@"Applying sound set %@",soundSet);
146 enumerator = [sounds keyEnumerator];
147 while ((key = [enumerator nextObject])) {
148 NSDictionary *soundAlert = [ESGlobalEventsPreferencesPlugin soundAlertForKey:key
149 inSoundsDict:sounds];
151 [[adium contactAlertsController] addGlobalAlert:soundAlert];
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;
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"];
197 - (void)_activateSet:(NSArray *)setArray withActionID:(NSString *)actionID alertGenerationSelector:(SEL)selector
199 NSEnumerator *enumerator;
200 NSDictionary *dictionary;
202 //Clear out old global dock behavior alerts
203 [[adium contactAlertsController] removeAllGlobalAlertsWithActionID:actionID];
206 enumerator = [setArray objectEnumerator];
207 while ((dictionary = [enumerator nextObject])) {
208 [[adium contactAlertsController] addGlobalAlert:[self performSelector:selector
209 withObject:dictionary]];
213 - (void)setEventPreset:(NSDictionary *)eventPreset
215 [[adium contactAlertsController] setAllGlobalAlerts:[eventPreset objectForKey:@"Events"]];
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.
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)];
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;
237 nextOrderIndex = (nextOrderIndexNumber ? [nextOrderIndexNumber floatValue] : 1.0);
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
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];
261 //If we don't have a name at this point, simply assign one
263 name = NEW_PRESET_NAME;
265 //Make sure we're not using a name which is already in use
266 if ([storedEventPresets objectForKey:name]) {
268 name = [NEW_PRESET_NAME stringByAppendingFormat:@" (%i)",i];
270 while ([storedEventPresets objectForKey:name] != nil) {
272 name = [NEW_PRESET_NAME stringByAppendingFormat:@" (%i)",i];
276 NSAssert(name != nil, @"name is nil");
277 [eventPreset setObject:name
278 forKey:KEY_EVENT_SET_NAME];
281 NSAssert(eventPreset != nil, @"eventPreset is nil");
282 [storedEventPresets setObject:eventPreset
285 [[adium preferenceController] setPreference:storedEventPresets
286 forKey:KEY_STORED_EVENT_PRESETS
287 group:PREF_GROUP_EVENT_PRESETS];
291 * @brief Delete an event preset
293 - (void)deleteEventPreset:(NSDictionary *)eventPreset
295 [storedEventPresets removeObjectForKey:[eventPreset objectForKey:KEY_EVENT_SET_NAME]];
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];
317 if (orderIndexA > orderIndexB) {
318 return NSOrderedDescending;
319 } else if (orderIndexA < orderIndexB) {
320 return NSOrderedAscending;
322 return [[eventPresetA objectForKey:KEY_EVENT_SET_NAME] caseInsensitiveCompare:[eventPresetB objectForKey:KEY_EVENT_SET_NAME]];
326 - (NSArray *)storedEventPresetsArray
328 return [[storedEventPresets allValues] sortedArrayUsingFunction:eventPresetsSort
332 - (NSArray *)builtInEventPresetsArray
334 return [[builtInEventPresets allValues] sortedArrayUsingFunction:eventPresetsSort