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 "AIEventSoundsPlugin.h"
18 #import <Adium/AIPreferenceControllerProtocol.h>
19 #import "AISoundController.h"
20 #import "ESEventSoundAlertDetailPane.h"
21 #import <AIUtilities/AIMenuAdditions.h>
22 #import <AIUtilities/AIStringAdditions.h>
23 #import <AIUtilities/AIImageAdditions.h>
24 #import <Adium/AISoundSet.h>
25 #import <Adium/AILocalizationTextField.h>
27 #define PLAY_A_SOUND AILocalizedString(@"Play a sound",nil)
28 #define KEY_DEFAULT_SOUND_DICT @"Default Sound Dict"
30 @interface ESEventSoundAlertDetailPane (PRIVATE)
31 - (NSMenu *)soundListMenu;
32 - (void)addSound:(NSString *)soundPath toMenu:(NSMenu *)soundMenu;
36 * @class ESEventSoundAlertDetailPane
37 * @brief Details pane for the Play Sound action
39 @implementation ESEventSoundAlertDetailPane
44 - (NSString *)nibName{
45 return @"EventSoundContactAlert";
49 * @brief Configure the detail view
53 [label_sound setLocalizedString:AILocalizedString(@"Sound:",nil)];
55 /* Loading and using the real file icons is slow, and all the sound files should have the same icons anyway. So
56 * we can cheat and load a sound icon from our bundle here (for all the menu items) for a nice speed boost. */
57 if (!soundFileIcon) soundFileIcon = [[NSImage imageNamed:@"SoundFileIcon" forClass:[self class]] retain];
59 //Prepare our sound menu
60 [popUp_actionDetails setMenu:[self soundListMenu]];
66 * @brief View will close
70 [soundFileIcon release]; soundFileIcon = nil;
71 [super viewWillClose];
75 * @brief Configure for the action
77 - (void)configureForActionDetails:(NSDictionary *)inDetails listObject:(AIListObject *)inObject
79 NSString *selectedSound;
82 if (!inDetails) inDetails = [[adium preferenceController] preferenceForKey:KEY_DEFAULT_SOUND_DICT
83 group:PREF_GROUP_SOUNDS];
85 //If the user has a custom sound selected, we need to create an entry in the menu for it
86 selectedSound = [inDetails objectForKey:KEY_ALERT_SOUND_PATH];
88 if ([[popUp_actionDetails menu] indexOfItemWithRepresentedObject:selectedSound] == -1) {
89 [self addSound:selectedSound toMenu:[popUp_actionDetails menu]];
92 //Set the menu to its previous setting if the stored event matches
93 soundIndex = [popUp_actionDetails indexOfItemWithRepresentedObject:[inDetails objectForKey:KEY_ALERT_SOUND_PATH]];
94 if (soundIndex >= 0 && soundIndex < [popUp_actionDetails numberOfItems]) {
95 [popUp_actionDetails selectItemAtIndex:soundIndex];
99 [popUp_actionDetails selectItemAtIndex:-1];
104 * @brief Return our current configuration
106 - (NSDictionary *)actionDetails
108 NSString *soundPath = [[popUp_actionDetails selectedItem] representedObject];
109 NSDictionary *actionDetails = nil;
111 if (soundPath && [soundPath length]) {
112 actionDetails = [NSDictionary dictionaryWithObject:soundPath forKey:KEY_ALERT_SOUND_PATH];
115 //Save the preferred settings for future use as defaults
116 [[adium preferenceController] setPreference:actionDetails
117 forKey:KEY_DEFAULT_SOUND_DICT
118 group:PREF_GROUP_SOUNDS];
120 return actionDetails;
124 //Sound Menu -----------------------------------------------------------------------------------------------------------
125 #pragma mark Sound Menu
127 * @brief Builds and returns a sound list menu
129 * The menu is organized by sound set.
131 - (NSMenu *)soundListMenu
133 NSMenu *soundMenu = [[NSMenu alloc] init];
134 NSEnumerator *enumerator;
135 AISoundSet *soundSet;
136 NSMenuItem *menuItem;
138 //Add all soundsets to our menu
139 enumerator = [[[adium soundController] soundSets] objectEnumerator];
140 while ((soundSet = [enumerator nextObject])) {
141 NSString *soundSetName = nil;
142 NSArray *soundSetContents = nil;
143 NSEnumerator *soundEnumerator;
146 soundSetName = [soundSet name];
147 soundSetContents = [[soundSet sounds] allValues];
149 NSAssert1(soundSetName != nil, @"Sound set does not have a name: %@", soundSet);
151 if (soundSetContents && [soundSetContents count]) {
152 NSMenu *soundsetMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
154 //Add an item for the set
155 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:soundSetName
158 keyEquivalent:@""] autorelease];
160 //Add an item for each sound
161 soundEnumerator = [soundSetContents objectEnumerator];
162 while ((soundPath = [soundEnumerator nextObject])) {
163 [self addSound:soundPath toMenu:soundsetMenu];
166 [menuItem setSubmenu:soundsetMenu];
167 [soundsetMenu release];
169 [soundMenu addItem:menuItem];
173 //Add a divider between the sets and Other...
174 [soundMenu addItem:[NSMenuItem separatorItem]];
176 //Add the "Other..." item
177 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:OTHER_ELLIPSIS
179 action:@selector(selectSound:)
180 keyEquivalent:@""] autorelease];
181 [soundMenu addItem:menuItem];
182 [soundMenu setAutoenablesItems:NO];
184 return [soundMenu autorelease];
188 * @brief Add a sound menu item to a menu
190 - (void)addSound:(NSString *)soundPath toMenu:(NSMenu *)soundMenu
192 NSString *soundTitle = [[soundPath lastPathComponent] stringByDeletingPathExtension];
193 NSMenuItem *menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:soundTitle
195 action:@selector(selectSound:)
196 keyEquivalent:@""] autorelease];
198 [menuItem setRepresentedObject:[soundPath stringByCollapsingBundlePath]];
199 [menuItem setImage:soundFileIcon];
200 [soundMenu addItem:menuItem];
204 * @brief Add a soundPath to the menu root if it is not yet present, then select it
206 * @param The soundPath, which should have a collapsed bundle path (to match menuItem represented objects)
208 - (void)addAndSelectSoundPath:(NSString *)soundPath
210 NSMenu *rootMenu = [popUp_actionDetails menu];
213 //Check for it currently being present in the root menu
214 menuIndex = [popUp_actionDetails indexOfItemWithRepresentedObject:soundPath];
215 if (menuIndex == -1) {
216 //Add it if it wasn't found
217 [self addSound:soundPath toMenu:rootMenu];
218 menuIndex = [popUp_actionDetails indexOfItemWithRepresentedObject:soundPath];
221 if (menuIndex != -1) {
222 [popUp_actionDetails selectItemAtIndex:menuIndex];
227 * @brief A sound was selected from a sound popUp menu
229 * Update our header and play the sound. If "Other..." is selected, allow selection of a file.
231 - (IBAction)selectSound:(id)sender
233 NSString *soundPath = [sender representedObject];
235 if (soundPath != nil && [soundPath length] != 0) {
236 [[adium soundController] playSoundAtPath:[soundPath stringByExpandingBundlePath]]; //Play the sound
238 //Update the menu and and the selection
239 [self addAndSelectSoundPath:soundPath];
241 [self detailsForHeaderChanged];
242 } else { //selected "Other..."
243 NSOpenPanel *openPanel = [NSOpenPanel openPanel];
246 beginSheetForDirectory:nil
248 types:[NSSound soundUnfilteredFileTypes] //allow all the sounds NSSound understands
249 modalForWindow:[view window]
251 didEndSelector:@selector(concludeOtherPanel:returnCode:contextInfo:)
258 * @brief Finish up the Other... panel
260 * Play the selected sound and update the menu.
262 - (void)concludeOtherPanel:(NSOpenPanel *)panel returnCode:(int)returnCode contextInfo:(void *)contextInfo
264 if (returnCode == NSOKButton) {
265 NSString *soundPath = [[panel filenames] objectAtIndex:0];
267 [[adium soundController] playSoundAtPath:soundPath]; //Play the sound
269 //Update the menu and and the selection
270 [self addAndSelectSoundPath:soundPath];
272 [self detailsForHeaderChanged];