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