William Bowling is sharing code with you

Bitbucket is a code hosting site. Unlimited public and private repositories. Free for small teams.

Don't show this again

wbowling / adium (fork of adium / adium)

Fork of Adium for patches/improvements

Clone this repository (size: 338.7 MB): HTTPS / SSH
hg clone https://bitbucket.org/wbowling/adium
hg clone ssh://hg@bitbucket.org/wbowling/adium

adium / Source / ESGlobalEventsPreferencesPlugin.m

/* 
 * Adium is the legal property of its developers, whose names are listed in the copyright file included
 * with this source distribution.
 * 
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
 * General Public License as published by the Free Software Foundation; either version 2 of the License,
 * or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
 * Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this program; if not,
 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#import "AISoundController.h"
#import <Adium/AIContactAlertsControllerProtocol.h>
#import "ESGlobalEventsPreferences.h"
#import "ESGlobalEventsPreferencesPlugin.h"
#import <AIUtilities/AIDictionaryAdditions.h>
#import <AIUtilities/AIStringAdditions.h>
#import <AIUtilities/AIArrayAdditions.h>
#import <Adium/AISoundSet.h>

#define NEW_PRESET_NAME                         AILocalizedString(@"New Event Set",nil)

#define KEY_STORED_EVENT_PRESETS        @"Event Presets"
#define KEY_EVENT_SET_NAME                      @"Name"
#define KEY_ORDER_INDEX                         @"OrderIndex"
#define KEY_NEXT_ORDER_INDEX            @"NextOrderIndex"

#define EVENT_SOUNDS_DEFAULT_PREFS      @"EventSoundDefaults"

@interface ESGlobalEventsPreferencesPlugin ()
- (void)activateDockBehaviorSet:(NSArray *)behaviorArray;
- (void)activateSpeechPreset:(NSArray *)presetArray;
- (void)activateGrowlPreset:(NSArray *)presetArray;
- (NSArray *)_behaviorForPreset:(NSString *)presetName inPresetArray:(NSArray *)presetArray;
- (void)_activateSet:(NSArray *)setArray withActionID:(NSString *)actionID alertGenerationSelector:(SEL)selector;
@end

@implementation ESGlobalEventsPreferencesPlugin

- (void)installPlugin
{
        NSString        *activeEventSet;
        
        builtInEventPresets = [[NSDictionary dictionaryNamed:@"BuiltInEventPresets" forClass:[self class]] retain];
        storedEventPresets = [[adium.preferenceController preferenceForKey:KEY_STORED_EVENT_PRESETS
                                                                                                                                   group:PREF_GROUP_EVENT_PRESETS] mutableCopy];
        if (!storedEventPresets) storedEventPresets = [[NSMutableDictionary alloc] init];

        /* If there is no active event set, or the active event set is not present in our built in or stored event sets
         * then we are in one of two conditions: either this is a first-launch, or the user has deleted the event preferences.
         * Either way, we want to set ourselves to the default notification set before proceeding.
         */
        activeEventSet = [adium.preferenceController preferenceForKey:KEY_ACTIVE_EVENT_SET
                                                                                                                          group:PREF_GROUP_EVENT_PRESETS];
        if (!activeEventSet || (![builtInEventPresets objectForKey:activeEventSet] &&
                                                   ![storedEventPresets objectForKey:activeEventSet])) {
                [self setEventPreset:[builtInEventPresets objectForKey:@"Default Notifications"]];              
        }

        [adium.preferenceController registerDefaults:[NSDictionary dictionaryNamed:EVENT_SOUNDS_DEFAULT_PREFS
                                                                                                                                                forClass:[self class]]
                                                                                  forGroup:PREF_GROUP_SOUNDS];

        //Install our preference view
    preferences = [[ESGlobalEventsPreferences preferencePaneForPlugin:self] retain];

        //Wait for Adium to finish launching before we perform further actions
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                                   selector:@selector(adiumFinishedLaunching:)
                                                                           name:AIApplicationDidFinishLoadingNotification
                                                                         object:nil];   
}

- (void)uninstallPlugin
{
    //Uninstall our observers
    [[NSNotificationCenter defaultCenter] removeObserver:preferences];
    [[NSNotificationCenter defaultCenter] removeObserver:preferences];
}

- (void)adiumFinishedLaunching:(NSNotification *)notification
{
        [[NSNotificationCenter defaultCenter] removeObserver:self
                                                                                  name:AIApplicationDidFinishLoadingNotification
                                                                                object:nil];
}


//Called when the preferences change, reregister for the notifications
- (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
                                                        object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
{

}

#pragma mark Sound Sets
+ (NSDictionary *)soundAlertForKey:(NSString *)key inSoundsDict:(NSDictionary *)sounds
{
        NSDictionary    *soundAlert = nil;
        NSString                *event;

        if (key &&
                (event = [adium.contactAlertsController eventIDForEnglishDisplayName:key])) {
                soundAlert = [NSDictionary dictionaryWithObjectsAndKeys:event, KEY_EVENT_ID,
                        SOUND_ALERT_IDENTIFIER, KEY_ACTION_ID, 
                        [NSDictionary dictionaryWithObject:[sounds objectForKey:key] forKey: KEY_ALERT_SOUND_PATH], KEY_ACTION_DETAILS,
                        nil];
        }
        
        return soundAlert;
}

/*!
* @brief Apply a sound set
 */
- (void)applySoundSet:(AISoundSet *)soundSet
{
        [adium.preferenceController delayPreferenceChangedNotifications:YES];
        
        //Clear out old global sound alerts
        [adium.contactAlertsController removeAllGlobalAlertsWithActionID:SOUND_ALERT_IDENTIFIER];

        AILog(@"Applying sound set %@",soundSet);

        //
        NSDictionary *sounds = [soundSet sounds];
        for (NSString *key in sounds) {
                NSDictionary *soundAlert = [ESGlobalEventsPreferencesPlugin soundAlertForKey:key
                                                                                                                                                inSoundsDict:sounds];
                if (soundAlert) {
                        [adium.contactAlertsController addGlobalAlert:soundAlert];
                }
        }
        
        [adium.preferenceController delayPreferenceChangedNotifications:NO];
}


#pragma mark Dock behavior sets
- (void)activateDockBehaviorSet:(NSArray *)behaviorArray
{
        [self _activateSet:behaviorArray withActionID:DOCK_BEHAVIOR_ALERT_IDENTIFIER alertGenerationSelector:@selector(dockAlertFromPresetDictionary:)];
}

#pragma mark Speech presets
- (void)activateSpeechPreset:(NSArray *)presetArray
{
        [self _activateSet:presetArray
                  withActionID:SPEAK_EVENT_ALERT_IDENTIFIER
alertGenerationSelector:@selector(speechAlertFromDictionary:)];
}

#pragma mark Growl presets
- (void)activateGrowlPreset:(NSArray *)presetArray
{
        [self _activateSet:presetArray withActionID:GROWL_EVENT_ALERT_IDENTIFIER alertGenerationSelector:@selector(growlAlertFromDictionary:)];
}

#pragma mark All simple presets
//Returns the behavior for a preset
- (NSArray *)_behaviorForPreset:(NSString *)presetName inPresetArray:(NSArray *)presetArray
{
    NSDictionary        *set;
    
    //Search for the desired set
    for (set in presetArray) {
        if ([presetName isEqualToString:[set objectForKey:@"Name"]]) {
            return [set objectForKey:@"Behavior"];
        }
    }
    
    return nil;
}

- (void)_activateSet:(NSArray *)setArray withActionID:(NSString *)actionID alertGenerationSelector:(SEL)selector
{
        NSDictionary    *dictionary;
        
        //Clear out old global dock behavior alerts
        [adium.contactAlertsController removeAllGlobalAlertsWithActionID:actionID];
        
        //
        for (dictionary in setArray) {
                [adium.contactAlertsController addGlobalAlert:[self performSelector:selector
                                                                                                                                   withObject:dictionary]];
        }
}

- (void)setEventPreset:(NSDictionary *)eventPreset
{
        [adium.preferenceController delayPreferenceChangedNotifications:YES];

        [adium.contactAlertsController setAllGlobalAlerts:[eventPreset objectForKey:@"Events"]];
        
        /* For a built in set, we now should apply the sound set it specified. User-created sets already include the
         * soundset as individual events.
         */
        if ([eventPreset objectForKey:@"Built In"] && [[eventPreset objectForKey:@"Built In"] boolValue]) {
                NSString        *soundSet = [eventPreset objectForKey:KEY_EVENT_SOUND_SET];
                [self applySoundSet:(soundSet ? [AISoundSet soundSetWithContentsOfFile:[soundSet stringByExpandingBundlePath]] : nil)];
        }
        
        [adium.preferenceController delayPreferenceChangedNotifications:NO];

        //Set the name of the now-active event set, which includes sounds and all other events
        [adium.preferenceController setPreference:[eventPreset objectForKey:KEY_EVENT_SET_NAME]
                                                                                 forKey:KEY_ACTIVE_EVENT_SET
                                                                                  group:PREF_GROUP_EVENT_PRESETS];
}

- (CGFloat)nextOrderIndex
{
        NSNumber *nextOrderIndexNumber = [adium.preferenceController preferenceForKey:KEY_NEXT_ORDER_INDEX
                                                                                                                                                          group:PREF_GROUP_EVENT_PRESETS];
        CGFloat nextOrderIndex;
        
        nextOrderIndex = (nextOrderIndexNumber ? [nextOrderIndexNumber doubleValue] : 1.0);
        
        [adium.preferenceController setPreference:[NSNumber numberWithDouble:(nextOrderIndex + 1)]
                                                                                 forKey:KEY_NEXT_ORDER_INDEX
                                                                                  group:PREF_GROUP_EVENT_PRESETS];      

        return nextOrderIndex;
}

/*!
 * @brief Save an event preset
 *
 * This will assign an order index to the preset if necessary and then save it to the stored event presets dictionary.
 * If a preset with the same name exists, it will be overwritten
 */
- (void)saveEventPreset:(NSMutableDictionary *)eventPreset
{
        NSString        *name = [eventPreset objectForKey:KEY_EVENT_SET_NAME];
        //Assign the next order index to this preset if it doesn't have one yet
        if (![eventPreset objectForKey:KEY_ORDER_INDEX]) {
                [eventPreset setObject:[NSNumber numberWithDouble:[self nextOrderIndex]]
                                                forKey:KEY_ORDER_INDEX];
        }

        //If we don't have a name at this point, simply assign one
        if (!name) {
                name = NEW_PRESET_NAME;
                
                //Make sure we're not using a name which is already in use
                if ([storedEventPresets objectForKey:name]) {
                        NSUInteger i = 1;
                        name = [NEW_PRESET_NAME stringByAppendingFormat:@" (%lu)",i];
                        
                        while ([storedEventPresets objectForKey:name] != nil) {
                                i++;
                                name = [NEW_PRESET_NAME stringByAppendingFormat:@" (%lu)",i];
                        }
                }
                
                NSAssert(name != nil, @"name is nil");
                [eventPreset setObject:name
                                                forKey:KEY_EVENT_SET_NAME];
        }
        
        NSAssert(eventPreset != nil, @"eventPreset is nil");
        [storedEventPresets setObject:eventPreset
                                                   forKey:name];

        [adium.preferenceController setPreference:storedEventPresets
                                                                                 forKey:KEY_STORED_EVENT_PRESETS
                                                                                  group:PREF_GROUP_EVENT_PRESETS];
}

/*!
 * @brief Delete an event preset
 */
- (void)deleteEventPreset:(NSDictionary *)eventPreset
{
        [storedEventPresets removeObjectForKey:[eventPreset objectForKey:KEY_EVENT_SET_NAME]];
        
        [adium.preferenceController setPreference:storedEventPresets
                                                                                 forKey:KEY_STORED_EVENT_PRESETS
                                                                                  group:PREF_GROUP_EVENT_PRESETS];      
}

- (NSDictionary *)builtInEventPresets
{
        return builtInEventPresets;
}

- (NSDictionary *)storedEventPresets
{
        return storedEventPresets;
}

NSInteger eventPresetsSort(id eventPresetA, id eventPresetB, void *context)
{
        CGFloat orderIndexA = [[eventPresetA objectForKey:KEY_ORDER_INDEX] doubleValue];
        CGFloat orderIndexB = [[eventPresetB objectForKey:KEY_ORDER_INDEX] doubleValue];
        
        if (orderIndexA > orderIndexB) {
                return NSOrderedDescending;
        } else if (orderIndexA < orderIndexB) {
                return NSOrderedAscending;
        } else {
                return [[eventPresetA objectForKey:KEY_EVENT_SET_NAME] caseInsensitiveCompare:[eventPresetB objectForKey:KEY_EVENT_SET_NAME]];
        }
}

- (NSArray *)storedEventPresetsArray
{
        return [[storedEventPresets allValues] sortedArrayUsingFunction:eventPresetsSort
                                                                                                                         context:nil];
}

- (NSArray *)builtInEventPresetsArray
{
        return [[builtInEventPresets allValues] sortedArrayUsingFunction:eventPresetsSort
                                                                                                                         context:nil];
}

@end