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 //The user probably does not want the sound to continue playing (especially if it's long), so stop it.
71 NSString *soundPath = [[popUp_actionDetails selectedItem] representedObject];
72 [[adium soundController] stopPlayingSoundAtPath:soundPath];
74 [soundFileIcon release]; soundFileIcon = nil;
75 [super viewWillClose];
79 * @brief Configure for the action
81 - (void)configureForActionDetails:(NSDictionary *)inDetails listObject:(AIListObject *)inObject
83 NSString *selectedSound;
86 if (!inDetails) inDetails = [[adium preferenceController] preferenceForKey:KEY_DEFAULT_SOUND_DICT
87 group:PREF_GROUP_SOUNDS];
89 //If the user has a custom sound selected, we need to create an entry in the menu for it
90 selectedSound = [inDetails objectForKey:KEY_ALERT_SOUND_PATH];
92 if ([[popUp_actionDetails menu] indexOfItemWithRepresentedObject:selectedSound] == -1) {
93 [self addSound:selectedSound toMenu:[popUp_actionDetails menu]];
96 //Set the menu to its previous setting if the stored event matches
97 soundIndex = [popUp_actionDetails indexOfItemWithRepresentedObject:[inDetails objectForKey:KEY_ALERT_SOUND_PATH]];
98 if (soundIndex >= 0 && soundIndex < [popUp_actionDetails numberOfItems]) {
99 [popUp_actionDetails selectItemAtIndex:soundIndex];
103 [popUp_actionDetails selectItemAtIndex:-1];
108 * @brief Return our current configuration
110 - (NSDictionary *)actionDetails
112 NSString *soundPath = [[popUp_actionDetails selectedItem] representedObject];
113 NSDictionary *actionDetails = nil;
115 if (soundPath && [soundPath length]) {
116 actionDetails = [NSDictionary dictionaryWithObject:soundPath forKey:KEY_ALERT_SOUND_PATH];
119 //Save the preferred settings for future use as defaults
120 [[adium preferenceController] setPreference:actionDetails
121 forKey:KEY_DEFAULT_SOUND_DICT
122 group:PREF_GROUP_SOUNDS];
124 return actionDetails;
128 //Sound Menu -----------------------------------------------------------------------------------------------------------
129 #pragma mark Sound Menu
131 * @brief Builds and returns a sound list menu
133 * The menu is organized by sound set.
135 - (NSMenu *)soundListMenu
137 NSMenu *soundMenu = [[NSMenu alloc] init];
138 NSEnumerator *enumerator;
139 AISoundSet *soundSet;
140 NSMenuItem *menuItem;
142 //Add all soundsets to our menu
143 enumerator = [[[adium soundController] soundSets] objectEnumerator];
144 while ((soundSet = [enumerator nextObject])) {
145 NSString *soundSetName = nil;
146 NSArray *soundSetContents = nil;
147 NSEnumerator *soundEnumerator;
150 soundSetName = [soundSet name];
151 soundSetContents = [[soundSet sounds] allValues];
153 NSAssert1(soundSetName != nil, @"Sound set does not have a name: %@", soundSet);
155 if (soundSetContents && [soundSetContents count]) {
156 NSMenu *soundsetMenu = [[NSMenu allocWithZone:[NSMenu menuZone]] init];
158 //Add an item for the set
159 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:soundSetName
162 keyEquivalent:@""] autorelease];
164 //Add an item for each sound
165 soundEnumerator = [soundSetContents objectEnumerator];
166 while ((soundPath = [soundEnumerator nextObject])) {
167 [self addSound:soundPath toMenu:soundsetMenu];
170 [menuItem setSubmenu:soundsetMenu];
171 [soundsetMenu release];
173 [soundMenu addItem:menuItem];
177 //Add a divider between the sets and Other...
178 [soundMenu addItem:[NSMenuItem separatorItem]];
180 //Add the "Other..." item
181 menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:OTHER_ELLIPSIS
183 action:@selector(selectSound:)
184 keyEquivalent:@""] autorelease];
185 [soundMenu addItem:menuItem];
186 [soundMenu setAutoenablesItems:NO];
188 return [soundMenu autorelease];
192 * @brief Add a sound menu item to a menu
194 - (void)addSound:(NSString *)soundPath toMenu:(NSMenu *)soundMenu
196 NSString *soundTitle = [[soundPath lastPathComponent] stringByDeletingPathExtension];
197 NSMenuItem *menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:soundTitle
199 action:@selector(selectSound:)
200 keyEquivalent:@""] autorelease];
202 [menuItem setRepresentedObject:[soundPath stringByCollapsingBundlePath]];
203 [menuItem setImage:soundFileIcon];
204 [soundMenu addItem:menuItem];
208 * @brief Add a soundPath to the menu root if it is not yet present, then select it
210 * @param The soundPath, which should have a collapsed bundle path (to match menuItem represented objects)
212 - (void)addAndSelectSoundPath:(NSString *)soundPath
214 NSMenu *rootMenu = [popUp_actionDetails menu];
217 //Check for it currently being present in the root menu
218 menuIndex = [popUp_actionDetails indexOfItemWithRepresentedObject:soundPath];
219 if (menuIndex == -1) {
220 //Add it if it wasn't found
221 [self addSound:soundPath toMenu:rootMenu];
222 menuIndex = [popUp_actionDetails indexOfItemWithRepresentedObject:soundPath];
225 if (menuIndex != -1) {
226 [popUp_actionDetails selectItemAtIndex:menuIndex];
231 * @brief A sound was selected from a sound popUp menu
233 * Update our header and play the sound. If "Other..." is selected, allow selection of a file.
235 - (IBAction)selectSound:(id)sender
237 NSString *soundPath = [sender representedObject];
239 if (soundPath != nil && [soundPath length] != 0) {
240 [[adium soundController] playSoundAtPath:[soundPath stringByExpandingBundlePath]]; //Play the sound
242 //Update the menu and and the selection
243 [self addAndSelectSoundPath:soundPath];
245 [self detailsForHeaderChanged];
246 } else { //selected "Other..."
247 NSOpenPanel *openPanel = [NSOpenPanel openPanel];
250 beginSheetForDirectory:nil
252 types:[NSSound soundUnfilteredFileTypes] //allow all the sounds NSSound understands
253 modalForWindow:[view window]
255 didEndSelector:@selector(concludeOtherPanel:returnCode:contextInfo:)
262 * @brief Finish up the Other... panel
264 * Play the selected sound and update the menu.
266 - (void)concludeOtherPanel:(NSOpenPanel *)panel returnCode:(int)returnCode contextInfo:(void *)contextInfo
268 if (returnCode == NSOKButton) {
269 NSString *soundPath = [[panel filenames] objectAtIndex:0];
271 [[adium soundController] playSoundAtPath:soundPath]; //Play the sound
273 //Update the menu and and the selection
274 [self addAndSelectSoundPath:soundPath];
276 [self detailsForHeaderChanged];