pt_BR strings updates
[adiumx.git] / Source / ESGlobalEventsPreferences.m
blob86534ec1946934b16f5f3930ffa359b71559baea
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/ESContactAlertsViewController.h"
19 #import <Adium/AIContactAlertsControllerProtocol.h>
20 #import "ESGlobalEventsPreferences.h"
21 #import "ESGlobalEventsPreferencesPlugin.h"
22 #import <Adium/ESPresetManagementController.h>
23 #import <Adium/ESPresetNameSheetController.h>
24 #import <Adium/AISoundSet.h>
25 #import <AIUtilities/AIMenuAdditions.h>
26 #import <AIUtilities/AIPopUpButtonAdditions.h>
27 #import <AIUtilities/AIStringAdditions.h>
28 #import <AIUtilities/AIApplicationAdditions.h>
29 #import <AIUtilities/AIVariableHeightOutlineView.h>
30 #import <AIUtilities/AIVerticallyCenteredTextCell.h>
31 #import <AIUtilities/AIAttributedStringAdditions.h>
32 #import <AIUtilities/AIArrayAdditions.h>
33 #import <AIUtilities/AIImageAdditions.h>
34 #import <AIUtilities/AIImageTextCell.h>
36 #define PREF_GROUP_EVENT_PRESETS        @"Event Presets"
37 #define CUSTOM_TITLE                            AILocalizedString(@"Custom",nil)
38 #define COPY_IN_PARENTHESIS                     AILocalizedString(@"(Copy)","Copy, in parenthesis, as a noun indicating that the preceding item is a duplicate")
40 #define VOLUME_SOUND_PATH   [NSString pathWithComponents:[NSArray arrayWithObjects: \
41         @"/", @"System", @"Library", @"LoginPlugins", \
42         [@"BezelServices" stringByAppendingPathExtension:@"loginPlugin"], \
43         @"Contents", @"Resources", \
44         [@"volume" stringByAppendingPathExtension:@"aiff"], \
45         nil]]
47 @interface ESGlobalEventsPreferences (PRIVATE)
48 - (void)popUp:(NSPopUpButton *)inPopUp shouldShowCustom:(BOOL)showCustom;
49 - (void)xtrasChanged:(NSNotification *)notification;
50 - (void)contactAlertsDidChangeForActionID:(NSString *)actionID;
52 - (NSMenu *)showMenu;
53 - (NSMenu *)eventPresetsMenu;
55 - (IBAction)selectSoundSet:(id)sender;
56 - (NSMenu *)_soundSetMenu;
58 - (void)editEventsWithEventID:(NSString *)eventID;
60 - (NSString *)_localizedTitle:(NSString *)englishTitle;
62 - (void)saveCurrentEventPreset;
64 - (void)setAndConfigureEventPresetsMenu;
65 - (void)updateSoundSetSelection;
66 - (void)updateSoundSetSelectionForSoundSet:(AISoundSet *)soundSet;
67 @end
69 @implementation ESGlobalEventsPreferences
70 - (NSString *)paneIdentifier
72         return @"Events";
74 - (NSString *)paneName{ 
75     return AILocalizedString(@"Events", "Name of preferences and tab for specifying what Adium should do when events occur - for example, display a Growl alert when John signs on.");
77 /*!
78  * @brief Nib name
79  */
80 - (NSString *)nibName{
81     return @"GlobalEventsPreferences";
83 - (NSImage *)paneIcon
85         return [NSImage imageNamed:@"pref-events" forClass:[self class]];
88 - (BOOL)resizableHorizontally
90         return YES;
93 /*!
94  * @brief Configure the preference view
95  */
96 - (void)viewDidLoad
98         //Configure our global contact alerts view controller
99         [contactAlertsViewController setConfigureForGlobal:YES];
100         [contactAlertsViewController setDelegate:self];
101         [contactAlertsViewController setShowEventsInEditSheet:NO];
102         
103         //Observe for installation of new sound sets and set up the sound set menu
104         [[adium notificationCenter] addObserver:self
105                                                                    selector:@selector(xtrasChanged:)
106                                                                            name:AIXtrasDidChangeNotification
107                                                                          object:nil];
109         //This will build the sound set menu
110         [self xtrasChanged:nil];        
112         //Presets menu
113         [self setAndConfigureEventPresetsMenu];
115         //And event presets to update our presets menu
116         [[adium preferenceController] registerPreferenceObserver:self forGroup:PREF_GROUP_EVENT_PRESETS];
118         //Ensure the correct sound set is selected
119         [self updateSoundSetSelection];
120         
121         //Volume
122         [slider_volume setFloatValue:[[[adium preferenceController] preferenceForKey:KEY_SOUND_CUSTOM_VOLUME_LEVEL
123                                                                                                                                                    group:PREF_GROUP_SOUNDS] floatValue]];       
126 - (void)localizePane
128         [label_eventPreset setLocalizedString:AILocalizedString(@"Event preset:",nil)];
129         [label_soundSet setLocalizedString:AILocalizedString(@"Sound set:",nil)];
133  * @brief Preference view is closing
134  */
135 - (void)viewWillClose
137         [contactAlertsViewController viewWillClose];
138         [contactAlertsViewController release]; contactAlertsViewController = nil;
140         [[adium preferenceController] unregisterPreferenceObserver:self];
141     [[adium notificationCenter] removeObserver:self];
145  * @brief PREF_GROUP_CONTACT_ALERTS changed; update our summary data
146  */
147 - (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
148                                                         object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
150         if ([group isEqualToString:PREF_GROUP_EVENT_PRESETS]) {
151                 if (!key || [key isEqualToString:@"Event Presets"]) {
152                         //Update when the available event presets change
153                         [self setAndConfigureEventPresetsMenu];
154                 }
155         }
159  * @brief Set if a popup should have a "Custom" menu item
160  */
161 - (void)popUp:(NSPopUpButton *)inPopUp shouldShowCustom:(BOOL)showCustom
163         NSMenuItem      *lastItem = [inPopUp lastItem];
164         BOOL            customIsShowing = (lastItem && (![lastItem representedObject] &&
165                                                                                                 [[lastItem title] isEqualToString:CUSTOM_TITLE]));
166         if (showCustom && !customIsShowing) {
167                 //Add 'custom' then select it
168                 [[inPopUp menu] addItem:[NSMenuItem separatorItem]];
169                 [[inPopUp menu] addItemWithTitle:CUSTOM_TITLE
170                                                                   target:nil
171                                                                   action:nil
172                                                    keyEquivalent:@""];
173                 [inPopUp selectItem:[inPopUp lastItem]];
175         } else if (!showCustom && customIsShowing) {
176                 //If it currently has a 'custom' item listed, remove it and the separator above it
177                 [inPopUp removeItemAtIndex:([inPopUp numberOfItems]-1)];
178                 [inPopUp removeItemAtIndex:([inPopUp numberOfItems]-1)];
179         }
183  * @brief Update our soundset menu if a new sound set is instaled
184  */
185 - (void)xtrasChanged:(NSNotification *)notification
187         if (!notification || [[notification object] caseInsensitiveCompare:@"AdiumSoundset"] == NSOrderedSame) {                
188                 //Build the soundset menu
189                 [popUp_soundSet setMenu:[self _soundSetMenu]];          
190         }
193 #pragma mark Event presets
196  * @brief Buld and return the event presets menu
198  * The menu will have built in presets, a divider, user-set presets, a divider, and then the preset management item(s)
199  */
200 - (NSMenu *)eventPresetsMenu
202         NSMenu                  *eventPresetsMenu = [[NSMenu allocWithZone:[NSMenu zone]] init];
203         NSEnumerator    *enumerator;
204         NSDictionary    *eventPreset;
205         NSMenuItem              *menuItem;
206         
207         //Built in event presets
208         enumerator = [[plugin builtInEventPresetsArray] objectEnumerator];
209         while ((eventPreset = [enumerator nextObject])) {
210                 NSString                *name = [eventPreset objectForKey:@"Name"];
211                 
212                 //Add a menu item for the set
213                 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:[self _localizedTitle:name]
214                                                                                                                                                  target:self
215                                                                                                                                                  action:@selector(selectEventPreset:)
216                                                                                                                                   keyEquivalent:@""] autorelease];
217                 [menuItem setRepresentedObject:eventPreset];
218                 [eventPresetsMenu addItem:menuItem];
219         }
220         
221         NSArray *storedEventPresetsArray = [plugin storedEventPresetsArray];
222         
223         if ([storedEventPresetsArray count]) {
224                 [eventPresetsMenu addItem:[NSMenuItem separatorItem]];
225                 
226                 enumerator = [storedEventPresetsArray objectEnumerator];
227                 while ((eventPreset = [enumerator nextObject])) {
228                         NSString                *name = [eventPreset objectForKey:@"Name"];
229                         
230                         //Add a menu item for the set
231                         menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:name
232                                                                                                                                                          target:self
233                                                                                                                                                          action:@selector(selectEventPreset:)
234                                                                                                                                           keyEquivalent:@""] autorelease];
235                         [menuItem setRepresentedObject:eventPreset];
236                         [eventPresetsMenu addItem:menuItem];
237                 }
238         }
239         
240         //Edit Presets
241         [eventPresetsMenu addItem:[NSMenuItem separatorItem]];
243         menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:[AILocalizedString(@"Add New Preset",nil) stringByAppendingEllipsis]
244                                                                                                                                          target:self
245                                                                                                                                          action:@selector(addNewPreset:)
246                                                                                                                           keyEquivalent:@""] autorelease];
247         [eventPresetsMenu addItem:menuItem];
248         
249         menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:[AILocalizedString(@"Edit Presets",nil) stringByAppendingEllipsis]
250                                                                                                                                          target:self
251                                                                                                                                          action:@selector(editPresets:)
252                                                                                                                           keyEquivalent:@""] autorelease];
253         [eventPresetsMenu addItem:menuItem];
254                 
255         return [eventPresetsMenu autorelease];
258 - (void)selectActiveEventInPopUp
260         NSString        *activeEventSetName = [[adium preferenceController] preferenceForKey:KEY_ACTIVE_EVENT_SET
261                                                                                                                                                            group:PREF_GROUP_EVENT_PRESETS];
263         //First try to set the localized version
264         [popUp_eventPreset selectItemWithTitle:[self _localizedTitle:activeEventSetName]];
265         //If that fails, look for one exactly matching
266         if (![popUp_eventPreset selectedItem]) [popUp_eventPreset selectItemWithTitle:activeEventSetName];
267         //And if that fails, select the first item (something went wrong, we should at least have a selection)
268         if (![popUp_eventPreset selectedItem]) [popUp_eventPreset selectItemAtIndex:0]; 
271 - (void)setAndConfigureEventPresetsMenu
273         [popUp_eventPreset setMenu:[self eventPresetsMenu]];
274         [self selectActiveEventInPopUp];
278  * @brief Selected an event preset
280  * Pass it to the plugin, which will perform necessary changes to our contact alerts
281  */
282 - (void)selectEventPreset:(id)sender
284         NSDictionary    *eventPreset = [sender representedObject];
285         [plugin setEventPreset:eventPreset];
287         [self updateSoundSetSelection];
291  * Add a new preset
293  * Called by the "Add New preset..." menu item.  Functions the same as duplicate from the preset management, duplicating
294  * the current event set with a new name.
295  */
296 - (void)addNewPreset:(id)sender
298         NSString        *defaultName;
299         NSString        *explanatoryText;
300         
301         defaultName = [NSString stringWithFormat:@"%@ %@",
302                 [self _localizedTitle:[[adium preferenceController] preferenceForKey:KEY_ACTIVE_EVENT_SET
303                                                                                                                                            group:PREF_GROUP_EVENT_PRESETS]],
304                 COPY_IN_PARENTHESIS];
305         explanatoryText = AILocalizedString(@"Enter a unique name for this new event set.",nil);
307         [ESPresetNameSheetController showPresetNameSheetWithDefaultName:defaultName
308                                                                                                         explanatoryText:explanatoryText
309                                                                                                                    onWindow:[[self view] window]
310                                                                                                         notifyingTarget:self
311                                                                                                                    userInfo:nil];
313         //Get our event presets menu back to its proper selection
314         [self selectActiveEventInPopUp];
318  * @brief Manage presets
320  * Called by the "Edit Presets..." menu item
321  */
322 - (void)editPresets:(id)sender
324         [ESPresetManagementController managePresets:[plugin storedEventPresetsArray]
325                                                                          namedByKey:@"Name"
326                                                                            onWindow:[[self view] window]
327                                                                    withDelegate:self];
329         //Get our event presets menu back to its proper selection
330         [self selectActiveEventInPopUp];
333 - (BOOL)allowDeleteOfPreset:(NSDictionary *)preset
335         NSString        *name = [preset objectForKey:@"Name"];
336         NSString        *localizedTitle;
337         
338         localizedTitle = [self _localizedTitle:[[adium preferenceController] preferenceForKey:KEY_ACTIVE_EVENT_SET
339                                                                                                                                                                         group:PREF_GROUP_EVENT_PRESETS]];
340         //Don't allow the active preset to be deleted
341         return (![localizedTitle isEqualToString:name]);
344 - (NSArray *)renamePreset:(NSDictionary *)preset toName:(NSString *)newName inPresets:(NSArray *)presets renamedPreset:(id *)renamedPreset
346         NSString                                *oldPresetName = [preset objectForKey:@"Name"];
347         NSMutableDictionary             *newPreset = [preset mutableCopy];
348         NSString                                *localizedCurrentName = [self _localizedTitle:[[adium preferenceController] preferenceForKey:KEY_ACTIVE_EVENT_SET
349                                                                                                                                                                                                                                    group:PREF_GROUP_EVENT_PRESETS]];
350         [newPreset setObject:newName
351                                   forKey:@"Name"];
353         //Mark the newly created (but still functionally identical) event set as active if the old one was active
354         if ([localizedCurrentName isEqualToString:oldPresetName]) {
355                 [[adium preferenceController] setPreference:newName
356                                                                                          forKey:KEY_ACTIVE_EVENT_SET
357                                                                                           group:PREF_GROUP_EVENT_PRESETS];
358         }
359         
360         //Remove the original one from the array, and add the newly-renamed one
361         [plugin deleteEventPreset:preset];
362         [plugin saveEventPreset:newPreset];
363         
364         if (renamedPreset) *renamedPreset = newPreset;
366         //Return an updated presets array
367         return [plugin storedEventPresetsArray];
370 - (NSArray *)duplicatePreset:(NSDictionary *)preset inPresets:(NSArray *)presets createdDuplicate:(id *)duplicatePreset
372         NSMutableDictionary     *newEventPreset = [preset mutableCopy];
373         NSString                        *newName = [NSString stringWithFormat:@"%@ %@", [preset objectForKey:@"Name"], COPY_IN_PARENTHESIS];
374         [newEventPreset setObject:newName
375                                            forKey:@"Name"];
376         
377         //Remove the original preset's order index
378         [newEventPreset removeObjectForKey:@"OrderIndex"];
379         
380         //Now save the new preset
381         [plugin saveEventPreset:newEventPreset];
383         //Return the created duplicate by reference
384         if (duplicatePreset != NULL) *duplicatePreset = [[newEventPreset retain] autorelease];
385         
386         //Cleanup
387         [newEventPreset release];
389         //Return an updated presets array
390         return [plugin storedEventPresetsArray];
393 - (NSArray *)deletePreset:(NSDictionary *)preset inPresets:(NSArray *)presets
395         //Remove the preset
396         [plugin deleteEventPreset:preset];
397         
398         //Return an updated presets array
399         return [plugin storedEventPresetsArray];
402 - (NSArray *)movePreset:(NSDictionary *)preset toIndex:(int)index inPresets:(NSArray *)presets presetAfterMove:(id *)presetAfterMove
404         NSMutableDictionary     *newEventPreset = [preset mutableCopy];
405         float newOrderIndex;
406         if (index == 0) {               
407                 newOrderIndex = [[[presets objectAtIndex:0] objectForKey:@"OrderIndex"] floatValue] / 2.0;
409         } else if (index < [presets count]) {
410                 float above = [[[presets objectAtIndex:index-1] objectForKey:@"OrderIndex"] floatValue];
411                 float below = [[[presets objectAtIndex:index] objectForKey:@"OrderIndex"] floatValue];
412                 newOrderIndex = ((above + below) / 2.0);
414         } else {
415                 newOrderIndex = [plugin nextOrderIndex];
416         }
417         
418         [newEventPreset setObject:[NSNumber numberWithFloat:newOrderIndex]
419                                            forKey:@"OrderIndex"];
420                          
421         //Now save the new preset
422         [plugin saveEventPreset:newEventPreset];
423         if (presetAfterMove != NULL) *presetAfterMove = [[newEventPreset retain] autorelease];
424         [newEventPreset release];
426         //Return an updated presets array
427         return [plugin storedEventPresetsArray];
430 #pragma mark Contact alerts changed by user
431 - (void)contactAlertsViewController:(ESContactAlertsViewController *)inController
432                                            updatedAlert:(NSDictionary *)newAlert
433                                                    oldAlert:(NSDictionary *)oldAlert
434 {       
435         [self contactAlertsDidChangeForActionID:[newAlert objectForKey:KEY_ACTION_ID]];
438 - (void)contactAlertsViewController:(ESContactAlertsViewController *)inController
439                                            deletedAlert:(NSDictionary *)deletedAlert
441         [self contactAlertsDidChangeForActionID:[deletedAlert objectForKey:KEY_ACTION_ID]];     
445  * @brief Contact alerts were changed by the user
446  */
447 - (void)contactAlertsDidChangeForActionID:(NSString *)actionID
449         if (!actionID ||
450                 [actionID isEqualToString:SOUND_ALERT_IDENTIFIER]) {
451                 
452                 NSArray                 *alertsArray = [[adium contactAlertsController] alertsForListObject:nil
453                                                                                                                                                                 withEventID:nil
454                                                                                                                                                                    actionID:SOUND_ALERT_IDENTIFIER];
455                 NSMenuItem              *soundMenuItem;
456                 
457                 if (![alertsArray count]) {
458                         //We can select "None" if there are no sounds
459                         soundMenuItem = (NSMenuItem *)[popUp_soundSet itemWithTitle:@"None"];
461                 } else {
462                         /* Otherwise, check to see if we remain in our proper soundset.
463                          * Note that this won't detect if we return to a soundset, but that'd be an expensive search.
464                          */
465                         soundMenuItem = (NSMenuItem *)[popUp_soundSet selectedItem];
467                         AISoundSet              *soundSet = [soundMenuItem representedObject];
468                         NSEnumerator    *enumerator;
469                         NSString                *key;
470                         NSDictionary    *sounds = [soundSet sounds];
472                         if ([alertsArray count] && ![sounds count]) {
473                                 //If we have one or more sound alerts and there are no sounds in this sound set ("None" sound set), there's no matching soundSetMenuitem.
474                                 soundMenuItem = nil;
476                         } else {
477                                 //First, check to see if any sounds which are present within this sound set have been changed
478                                 enumerator = [sounds keyEnumerator];
479                                 while ((key = [enumerator nextObject])) {
480                                         NSDictionary *soundAlert = [ESGlobalEventsPreferencesPlugin soundAlertForKey:key
481                                                                                                                                                                         inSoundsDict:sounds];
482                                         if (![alertsArray containsObject:soundAlert]) {
483                                                 soundMenuItem = nil;
484                                                 break;
485                                         }
486                                 }
487                                 
488                                 //Next, see if any sounds not present within this sound set have been added
489                                 if (soundMenuItem) {
490                                         NSDictionary    *alertDict;
491                                         enumerator = [alertsArray objectEnumerator];
492                                         while ((alertDict = [enumerator nextObject])) {
493                                                 if ([[alertDict objectForKey:KEY_ACTION_ID] isEqualToString:SOUND_ALERT_IDENTIFIER]) {
494                                                         NSString *englishEvent = [[[AIObject sharedAdiumInstance] contactAlertsController] eventIDForEnglishDisplayName:key];
495                                                         /*
496                                                          * If the sounds dictionary has no action for this event, or it has one but
497                                                          * it is for a different sound than specified, the sound set has been changed
498                                                          */
499                                                         if (![sounds objectForKey:englishEvent] ||
500                                                                 ![[[alertDict objectForKey:KEY_ACTION_DETAILS] objectForKey:KEY_ALERT_SOUND_PATH] isEqualToString:[sounds objectForKey:englishEvent]]) {
501                                                                 soundMenuItem = nil;
502                                                                 break;
503                                                         }
504                                                 }
505                                         }
506                                 }
508                         }
509                 }
511                 [self selectSoundSet:([soundMenuItem representedObject] ? soundMenuItem : nil)];
513         } else {
514                 [self saveCurrentEventPreset];
515         }
518 #pragma mark Sound sets
520  * @brief Called when an item in the sound set popUp is selected.
522  * Also called after the user changes sounds manually, by -[ESGlobalEventsPreferences contactAlertsDidChangeForActionID].
523  */
524 - (IBAction)selectSoundSet:(id)sender
526         //Apply the sound set so its events are in the current alerts.
527         if (sender) {
528                 [plugin applySoundSet:[sender representedObject]];
529         }
531         /* Update the selection, which will select Custom as appropriate.  This must be done before saving the event
532          * preset so the menu is on the correct sound set to save.
533          */
534         [self updateSoundSetSelectionForSoundSet:[sender representedObject]];
536         /* Save the preset which is now updated to have the appropriate sounds; 
537          * in saving, the name of the soundset, or @"", will also be saved.
538          */
539         [self saveCurrentEventPreset];
543  * @brief Revert the event set to how it was before the last attempted operation
544  */
545 - (void)revertToSavedEventSet
547         NSDictionary            *eventPreset;
549         [self selectActiveEventInPopUp];
550         eventPreset = [[popUp_eventPreset selectedItem] representedObject];
552         [plugin setEventPreset:eventPreset];
553         
554         //Ensure the correct sound set is selected
555         [self updateSoundSetSelection];
559  * @brief Build and return the event set as it should be saved
560  */
561 - (NSMutableDictionary *)currentEventSetForSaving
563         NSDictionary            *eventPreset = [[popUp_eventPreset selectedItem] representedObject];
564         NSMutableDictionary     *currentEventSetForSaving = [[eventPreset mutableCopy] autorelease];
565         
566         //Set the sound set, which is just stored here for ease of preference pane display
567         NSString                        *soundSetName = [[[popUp_soundSet selectedItem] representedObject] name];
568         if (soundSetName) {
569                 [currentEventSetForSaving setObject:soundSetName
570                                                                          forKey:KEY_EVENT_SOUND_SET];
571         } else {
572                 [currentEventSetForSaving removeObjectForKey:KEY_EVENT_SOUND_SET];
573         }
574         
575         //Get and store the alerts array
576         NSArray                         *alertsArray = [[adium contactAlertsController] alertsForListObject:nil
577                                                                                                                                                                 withEventID:nil
578                                                                                                                                                                    actionID:nil];
579         [currentEventSetForSaving setObject:alertsArray forKey:@"Events"];
581         //Ensure this set doesn't claim to be built in.
582         [currentEventSetForSaving removeObjectForKey:@"Built In"];
583         
584         return currentEventSetForSaving;
587 #pragma mark Volume
588 //New value selected on the volume slider or chosen by clicking a volume icon
589 - (IBAction)selectVolume:(id)sender
591     float                       volume, oldVolume;
592         
593         if (sender == slider_volume) {
594                 volume = [slider_volume floatValue];
595         } else if (sender == button_maxvolume) {
596                 volume = [slider_volume maxValue];
597                 [slider_volume setDoubleValue:volume];
598         } else if (sender == button_minvolume) {
599                 volume = [slider_volume minValue];
600                 [slider_volume setDoubleValue:volume];
601         } else {
602                 volume = 0;
603         }
604         
605         NSNumber *oldVolumeValue = [[adium preferenceController] preferenceForKey:KEY_SOUND_CUSTOM_VOLUME_LEVEL
606                                                                                                                                                 group:PREF_GROUP_SOUNDS];
607         oldVolume = (oldVolumeValue ? [oldVolumeValue floatValue] : -1.0);
608         
609     //Volume
610     if (volume != oldVolume) {
611         [[adium preferenceController] setPreference:[NSNumber numberWithFloat:volume]
612                                              forKey:KEY_SOUND_CUSTOM_VOLUME_LEVEL
613                                               group:PREF_GROUP_SOUNDS];
614                 
615                 //Play a sample sound
616         [[adium soundController] playSoundAtPath:VOLUME_SOUND_PATH];
617     }
620 #pragma mark Preset saving
623  * @brief Save the current event preset
625  * Called after each event change to immediately update the current preset.
626  * If a built-in preset is currently selected, this method will prompt for a new name before saving.
627  */
628 - (void)saveCurrentEventPreset
630         NSDictionary            *eventPreset = [[popUp_eventPreset selectedItem] representedObject];
632         if ([eventPreset objectForKey:@"Built In"] && [[eventPreset objectForKey:@"Built In"] boolValue]) {
633                 /* Perform after a delay so that if we got here as a result of a sheet-based add or edit of an event
634                  * the sheet will close before we try to open a new one. */
635                 [self performSelector:@selector(showPresetCopySheet:)
636                                    withObject:[self _localizedTitle:[eventPreset objectForKey:@"Name"]]
637                                    afterDelay:0];
638         } else {        
639                 //Now save the current settings
640                 [plugin saveEventPreset:[self currentEventSetForSaving]];
641         }               
645  * @brief Show the sheet for naming the preset created by an attempt to modify a built-in set
647  * @param originalPresetName The name of the original set, used as a base for the new name.
648  */
649 - (void)showPresetCopySheet:(NSString *)originalPresetName
651         NSString        *defaultName;
652         NSString        *explanatoryText;
653         
654         defaultName = [NSString stringWithFormat:@"%@ %@", originalPresetName, COPY_IN_PARENTHESIS];
655         explanatoryText = AILocalizedString(@"You are editing a default event set.  Please enter a unique name for your modified set.",nil);
656         
657         [ESPresetNameSheetController showPresetNameSheetWithDefaultName:defaultName
658                                                                                                         explanatoryText:explanatoryText
659                                                                                                                    onWindow:[[self view] window]
660                                                                                                         notifyingTarget:self
661                                                                                                                    userInfo:nil];
664 - (BOOL)presetNameSheetController:(ESPresetNameSheetController *)controller
665                           shouldAcceptNewName:(NSString *)newName
666                                                  userInfo:(id)userInfo
668         return (![[[plugin builtInEventPresets] allKeys] containsObject:newName] &&
669                    ![[[plugin storedEventPresets] allKeys] containsObject:newName]);
671         
672 - (void)presetNameSheetControllerDidEnd:(ESPresetNameSheetController *)controller 
673                                                          returnCode:(ESPresetNameSheetReturnCode)returnCode
674                                                                 newName:(NSString *)newName
675                                                            userInfo:(id)userInfo
677         switch (returnCode) {
678                 case ESPresetNameSheetOkayReturn:
679                 {
680                         //XXX error if overwriting existing set?
681                         NSMutableDictionary     *newEventPreset = [self currentEventSetForSaving];
682                         [newEventPreset setObject:newName
683                                                            forKey:@"Name"];
684                         
685                         //Now save the current settings
686                         [plugin saveEventPreset:newEventPreset];
687                         
688                         //Presets menu
689                         [[adium preferenceController] setPreference:newName
690                                                                                                  forKey:KEY_ACTIVE_EVENT_SET
691                                                                                                   group:PREF_GROUP_EVENT_PRESETS];
692                         [popUp_eventPreset setMenu:[self eventPresetsMenu]];
693                         [popUp_eventPreset selectItemWithTitle:newName];
694                         
695                         break;
696                 }
697                 case ESPresetNameSheetCancelReturn:
698                 {
699                         [self revertToSavedEventSet];
700                         break;
701                 }
702         }
704                 
706  * @brief Called when the OK button on the preset copy sheet is pressed
708  * Save the current event set under the name specified by [textField_name stringValue].
709  * Set the name of the active event set to this new name, and ensure our menu is up to date.
711  * Also, close the sheet.
712  */
713 - (IBAction)selectedNameForPresetCopy:(id)sender
715         
718 - (void)updateSoundSetSelectionForSoundSet:(AISoundSet *)soundSet
720         if (soundSet) {
721                 [popUp_soundSet selectItemWithRepresentedObject:soundSet];
722                 
723                 [self popUp:popUp_soundSet shouldShowCustom:NO];
724                 
725         } else {
726                 [self popUp:popUp_soundSet shouldShowCustom:YES];
727         }
730 - (void)updateSoundSetSelection
732         NSEnumerator    *enumerator = [[[adium soundController] soundSets] objectEnumerator];
733     AISoundSet          *soundSet;
734         NSString                *name;
736         name = [[[popUp_eventPreset selectedItem] representedObject] objectForKey:KEY_EVENT_SOUND_SET];
737         name = [[name lastPathComponent] stringByDeletingPathExtension];
739     while ((soundSet = [enumerator nextObject])) {
740                 if ([[soundSet name] isEqualToString:name]) break;
741         }
743         [self updateSoundSetSelectionForSoundSet:soundSet];
746 #define NONE AILocalizedString(@"None",nil)
748  * @brief Build and return a menu of sound set choices
750  * The menu items have an action of -[self selectSoundSet:].
751  */
752 - (NSMenu *)_soundSetMenu
754     NSMenu                      *soundSetMenu = [[NSMenu alloc] init];
755     NSEnumerator        *enumerator = [[[adium soundController] soundSets] objectEnumerator];
756         NSMutableArray  *menuItemArray = [NSMutableArray array];
757     AISoundSet          *soundSet;
758     NSMenuItem          *menuItem, *noneMenuItem = nil;
760     while ((soundSet = [enumerator nextObject])) {
761                 menuItem = [[NSMenuItem alloc] initWithTitle:[self _localizedTitle:[soundSet name]]
762                                                                                           target:self
763                                                                                           action:@selector(selectSoundSet:)
764                                                                            keyEquivalent:@""
765                                                                    representedObject:soundSet];
766                 
767                 if ([[menuItem title] isEqualToString:NONE]) {
768                         noneMenuItem = menuItem;
770                 } else {
771                         [menuItemArray addObject:menuItem];
772                         [menuItem release];
773                 }
774         }
775         
776         [menuItemArray sortUsingSelector:@selector(titleCompare:)];
777         
778         enumerator = [menuItemArray objectEnumerator];
779         while ((menuItem = [enumerator nextObject])) {
780                 [soundSetMenu addItem:menuItem];
781         }
783         if (noneMenuItem) {
784                 [soundSetMenu addItem:[NSMenuItem separatorItem]];
785                 [soundSetMenu addItem:noneMenuItem];
786                 [noneMenuItem release];
787         }
788         
789     return [soundSetMenu autorelease];
792 #pragma mark Common menu methods
794  * @brief Localized a menu item title for global events preferences
796  * @result The equivalent localized title if available; otherwise, the passed English title
797  */
798 - (NSString *)_localizedTitle:(NSString *)englishTitle
800         NSString        *localizedTitle = nil;
801         
802         if ([englishTitle isEqualToString:@"None"])
803                 localizedTitle = NONE;
804         else if ([englishTitle isEqualToString:@"Default Notifications"])
805                 localizedTitle = AILocalizedString(@"Default Notifications",nil);
806         else if ([englishTitle isEqualToString:@"Visual Notifications"])
807                 localizedTitle = AILocalizedString(@"Visual Notifications",nil);
808         else if ([englishTitle isEqualToString:@"Audio Notifications"])
809                 localizedTitle = AILocalizedString(@"Audio Notifications",nil);
811         return (localizedTitle ? localizedTitle : englishTitle);
814 @end