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 "ESContactAlertsController.h"
19 #import <Adium/AIPreferenceControllerProtocol.h>
20 #import <Adium/AIListObject.h>
21 #import <AIUtilities/AIMenuAdditions.h>
22 #import <AIUtilities/AIImageAdditions.h>
24 @interface ESContactAlertsController (PRIVATE)
25 - (NSArray *)arrayOfMenuItemsForEventsWithTarget:(id)target forGlobalMenu:(BOOL)global;
27 - (NSMutableArray *)appendEventsForObject:(AIListObject *)listObject eventID:(NSString *)eventID toArray:(NSMutableArray *)events;
28 - (void)addMenuItemsForEventHandlers:(NSDictionary *)inEventHandlers toArray:(NSMutableArray *)menuItemArray withTarget:(id)target forGlobalMenu:(BOOL)global;
29 - (void)removeAllAlertsFromListObject:(AIListObject *)listObject;
32 @implementation ESContactAlertsController
34 static NSMutableDictionary *eventHandlersByGroup[EVENT_HANDLER_GROUP_COUNT];
35 static NSMutableDictionary *globalOnlyEventHandlersByGroup[EVENT_HANDLER_GROUP_COUNT];
38 * @brief Initialize before the class is used
42 static BOOL didInitialize = NO;
44 for (int i = 0; i < EVENT_HANDLER_GROUP_COUNT; i++) {
45 eventHandlersByGroup[i] = nil;
46 globalOnlyEventHandlersByGroup[i] = nil;
58 if ((self = [super init])) {
59 globalOnlyEventHandlers = [[NSMutableDictionary alloc] init];
60 eventHandlers = [[NSMutableDictionary alloc] init];
61 actionHandlers = [[NSMutableDictionary alloc] init];
67 - (void)controllerDidLoad
71 - (void)controllerWillClose
81 [globalOnlyEventHandlers release]; globalOnlyEventHandlers = nil;
82 [eventHandlers release]; eventHandlers = nil;
83 [actionHandlers release]; actionHandlers = nil;
89 //Events ---------------------------------------------------------------------------------------------------------------
93 * @brief Register an event
95 * An event must have a unique eventID. handler is responsible for providing information
96 * about the event, such as short and long descriptions. The group determines how the event will be displayed in the events
97 * preferences; events in the same group are displayed together.
99 * @param eventID Unique event ID
100 * @param handler The handler, which must conform to AIEventHandler
101 * @param inGroup The group
102 * @param global If YES, the event will only be displayed in the global Events preferences; if NO, the event is available for contacts and groups via Get Info, as well.
104 - (void)registerEventID:(NSString *)eventID
105 withHandler:(id <AIEventHandler>)handler
106 inGroup:(AIEventHandlerGroupType)inGroup
107 globalOnly:(BOOL)global
110 [globalOnlyEventHandlers setObject:handler forKey:eventID];
112 if (!globalOnlyEventHandlersByGroup[inGroup]) globalOnlyEventHandlersByGroup[inGroup] = [[NSMutableDictionary alloc] init];
113 [globalOnlyEventHandlersByGroup[inGroup] setObject:handler forKey:eventID];
116 [eventHandlers setObject:handler forKey:eventID];
118 if (!eventHandlersByGroup[inGroup]) eventHandlersByGroup[inGroup] = [[NSMutableDictionary alloc] init];
119 [eventHandlersByGroup[inGroup] setObject:handler forKey:eventID];
123 //Return all event IDs for groups/contacts
124 - (NSArray *)allEventIDs
126 return [[eventHandlers allKeys] arrayByAddingObjectsFromArray:[globalOnlyEventHandlers allKeys]];
129 - (NSString *)longDescriptionForEventID:(NSString *)eventID forListObject:(AIListObject *)listObject
131 id <AIEventHandler> handler;
133 handler = [eventHandlers objectForKey:eventID];
134 if (!handler) handler = [globalOnlyEventHandlers objectForKey:eventID];
136 return [handler longDescriptionForEventID:eventID forListObject:listObject];
140 * @brief Returns a menu of all events
142 * A menu item's represented object is the dictionary describing the event it represents
144 * @param target The target on which @selector(selectEvent:) will be called on selection.
145 * @param global If YES, the events listed will include global ones (such as Error Occurred) in addition to contact-specific ones.
146 * @result An NSMenu of the events
148 - (NSMenu *)menuOfEventsWithTarget:(id)target forGlobalMenu:(BOOL)global
150 NSEnumerator *enumerator;
155 menu = [[NSMenu allocWithZone:[NSMenu zone]] init];
156 [menu setAutoenablesItems:NO];
158 enumerator = [[self arrayOfMenuItemsForEventsWithTarget:target forGlobalMenu:global] objectEnumerator];
159 while ((item = [enumerator nextObject])) {
163 return [menu autorelease];
166 - (NSArray *)arrayOfMenuItemsForEventsWithTarget:(id)target forGlobalMenu:(BOOL)global
168 NSMutableArray *menuItemArray = [NSMutableArray array];
169 BOOL addedItems = NO;
172 for (i = 0; i < EVENT_HANDLER_GROUP_COUNT; i++) {
173 NSMutableArray *groupMenuItemArray;
175 //Create an array of menu items for this group
176 groupMenuItemArray = [NSMutableArray array];
178 [self addMenuItemsForEventHandlers:eventHandlersByGroup[i]
179 toArray:groupMenuItemArray
181 forGlobalMenu:global];
183 [self addMenuItemsForEventHandlers:globalOnlyEventHandlersByGroup[i]
184 toArray:groupMenuItemArray
186 forGlobalMenu:global];
189 if ([groupMenuItemArray count]) {
190 //Add a separator if we are adding a group and we have added before
192 [menuItemArray addObject:[NSMenuItem separatorItem]];
197 //Sort the array of menuItems alphabetically by title within this group
198 [groupMenuItemArray sortUsingSelector:@selector(titleCompare:)];
200 [menuItemArray addObjectsFromArray:groupMenuItemArray];
204 return menuItemArray;
207 - (void)addMenuItemsForEventHandlers:(NSDictionary *)inEventHandlers toArray:(NSMutableArray *)menuItemArray withTarget:(id)target forGlobalMenu:(BOOL)global
209 NSEnumerator *enumerator;
211 NSMenuItem *menuItem;
213 enumerator = [inEventHandlers keyEnumerator];
214 while ((eventID = [enumerator nextObject])) {
215 id <AIEventHandler> eventHandler = [inEventHandlers objectForKey:eventID];
217 menuItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:(global ?
218 [eventHandler globalShortDescriptionForEventID:eventID] :
219 [eventHandler shortDescriptionForEventID:eventID])
221 action:@selector(selectEvent:)
223 [menuItem setRepresentedObject:eventID];
224 [menuItemArray addObject:menuItem];
230 * @brief Sort event IDs by group and then by global short description
232 int eventIDSort(id objectA, id objectB, void *context) {
234 id <AIEventHandler> eventHandlerA;
235 id <AIEventHandler> eventHandlerB;
237 //Determine the group of the first event ID
238 for (groupA = 0; groupA < EVENT_HANDLER_GROUP_COUNT; groupA++) {
239 eventHandlerA = [eventHandlersByGroup[groupA] objectForKey:objectA];
240 if (!eventHandlerA) {
241 eventHandlerA = [globalOnlyEventHandlersByGroup[groupA] objectForKey:objectA];
244 if (eventHandlerA) break;
247 //Determine the group of the second ID
248 for (groupB = 0; groupB < EVENT_HANDLER_GROUP_COUNT; groupB++) {
249 eventHandlerB = [eventHandlersByGroup[groupB] objectForKey:objectB];
250 if (!eventHandlerB) {
251 eventHandlerB = [globalOnlyEventHandlersByGroup[groupB] objectForKey:objectB];
254 if (eventHandlerB) break;
257 if (groupA < groupB) {
258 return NSOrderedAscending;
260 } else if (groupB < groupA) {
261 return NSOrderedDescending;
264 NSString *descriptionA = [eventHandlerA globalShortDescriptionForEventID:objectA];
265 NSString *descriptionB = [eventHandlerA globalShortDescriptionForEventID:objectB];
267 return ([descriptionA caseInsensitiveCompare:descriptionB]);
272 * @brief Sort an array of event IDs
274 * @brief inArray The array of eventIDs to sort
275 * @return The array sorted by eventIDSort()
277 - (NSArray *)sortedArrayOfEventIDsFromArray:(NSArray *)inArray
279 return [inArray sortedArrayUsingFunction:eventIDSort context:NULL];
283 * @brief Return the image associated with an event
285 - (NSImage *)imageForEventID:(NSString *)eventID
287 id <AIEventHandler> eventHandler;
289 eventHandler = [eventHandlers objectForKey:eventID];
290 if (!eventHandler) eventHandler = [globalOnlyEventHandlers objectForKey:eventID];
292 return [eventHandler imageForEventID:eventID];
296 * @brief Generate an event, returning a set of the actionIDs which were performed.
298 * @param eventID The event which occurred
299 * @param listObject The object for which the event occurred
300 * @param userInfo Event-specific user info
301 * @param previouslyPerformedActionIDs If non-nil, a set of actionIDs which should be treated as if they had already been performed in this invocation.
303 * @result The set of actions which were performed, suitable for being passed back in for another event generation via previouslyPerformedActionIDs
305 - (NSSet *)generateEvent:(NSString *)eventID forListObject:(AIListObject *)listObject userInfo:(id)userInfo previouslyPerformedActionIDs:(NSSet *)previouslyPerformedActionIDs
307 NSArray *alerts = [self appendEventsForObject:listObject eventID:eventID toArray:nil];
308 NSMutableSet *performedActionIDs = nil;
310 if (alerts && [alerts count]) {
311 NSEnumerator *enumerator;
314 performedActionIDs = (previouslyPerformedActionIDs ?
315 [[previouslyPerformedActionIDs mutableCopy] autorelease]:
318 //We go from contact->group->root; a given action will only fire once for this event
319 enumerator = [alerts objectEnumerator];
321 //Process each alert (There may be more than one for an event)
322 while ((alert = [enumerator nextObject])) {
324 id <AIActionHandler> actionHandler;
326 actionID = [alert objectForKey:KEY_ACTION_ID];
327 actionHandler = [actionHandlers objectForKey:actionID];
329 if ((![performedActionIDs containsObject:actionID]) || ([actionHandler allowMultipleActionsWithID:actionID])) {
330 if ([actionHandler performActionID:actionID
331 forListObject:listObject
332 withDetails:[alert objectForKey:KEY_ACTION_DETAILS]
333 triggeringEventID:eventID
334 userInfo:userInfo]) {
336 //If this alert was a single-fire alert, we can delete it now
337 if ([[alert objectForKey:KEY_ONE_TIME_ALERT] intValue]) {
338 [self removeAlert:alert fromListObject:listObject];
341 //We don't want to perform this action again for this event
342 [performedActionIDs addObject:actionID];
348 [[adium notificationCenter] postNotificationName:eventID
352 /* If we generated a new perfromedActionIDs, return it. If we didn't, return the original
353 * previouslyPerformedActionIDs, which may also be nil or may be actionIDs performed on some previous invocation.
355 return (performedActionIDs ? performedActionIDs : previouslyPerformedActionIDs);
359 * @brief Append events for the passed object to the specified array.
361 * @param events The array of events so far. Create the array if passed nil.
362 * @param The object for which we'ere retrieving events. If nil, we retrieve the global preferences.
364 * This method is intended to be called recursively; it should generate an array which has alerts from:
365 * contact->metaContact->group->global preferences (skipping any which don't exist).
367 * @result An array which contains the object's own events followed by its containingObject's events.
369 - (NSMutableArray *)appendEventsForObject:(AIListObject *)listObject eventID:(NSString *)eventID toArray:(NSMutableArray *)events
373 //Add events for this object (replacing any inherited from the containing object so that this object takes precendence)
374 newEvents = [[[adium preferenceController] preferenceForKey:KEY_CONTACT_ALERTS
375 group:PREF_GROUP_CONTACT_ALERTS
376 objectIgnoringInheritance:listObject] objectForKey:eventID];
378 if (newEvents && [newEvents count]) {
379 if (!events) events = [NSMutableArray array];
380 [events addObjectsFromArray:newEvents];
383 //Get all events from the contanining object if we have an object
385 //If listObject doesn't have a containingObject, this will pass nil
386 events = [self appendEventsForObject:[listObject containingObject]
395 * @brief Return the default event ID for a new alert
397 - (NSString *)defaultEventID
399 NSString *defaultEventID = [[adium preferenceController] preferenceForKey:KEY_DEFAULT_EVENT_ID
400 group:PREF_GROUP_CONTACT_ALERTS];
401 if (![eventHandlers objectForKey:defaultEventID]) {
402 defaultEventID = [[eventHandlers allKeys] objectAtIndex:0];
405 return defaultEventID;
409 * @brief Find the eventID associated with an English name
411 * This exists for compatibility with old AdiumXtras...
413 - (NSString *)eventIDForEnglishDisplayName:(NSString *)displayName
415 NSEnumerator *enumerator;
418 enumerator = [eventHandlers keyEnumerator];
419 while ((eventID = [enumerator nextObject])) {
420 id <AIEventHandler> eventHandler = [eventHandlers objectForKey:eventID];
421 if ([[eventHandler englishGlobalShortDescriptionForEventID:eventID] isEqualToString:displayName]) {
426 enumerator = [globalOnlyEventHandlers keyEnumerator];
427 while ((eventID = [enumerator nextObject])) {
428 id <AIEventHandler> eventHandler = [globalOnlyEventHandlers objectForKey:eventID];
429 if ([[eventHandler englishGlobalShortDescriptionForEventID:eventID] isEqualToString:displayName]) {
438 * @brief Return a short description to describe eventID when considered globally
440 - (NSString *)globalShortDescriptionForEventID:(NSString *)eventID
442 id <AIEventHandler> eventHandler;
444 eventHandler = [eventHandlers objectForKey:eventID];
445 if (!eventHandler) eventHandler = [globalOnlyEventHandlers objectForKey:eventID];
448 return [eventHandler globalShortDescriptionForEventID:eventID];
455 * @brief Return a natural language, localized description for an event
457 * This will be suitable for display to the user such as in a message window or a Growl notification
459 * @param eventID The event
460 * @param listObject The object for which the event occurred
461 * @param userInfo Event-specific userInfo
462 * @param includeSubject If YES, the return value is a complete sentence. If NO, the return value is suitable for display after a name or other identifier.
463 * @result The natural language description
465 - (NSString *)naturalLanguageDescriptionForEventID:(NSString *)eventID
466 listObject:(AIListObject *)listObject
467 userInfo:(id)userInfo
468 includeSubject:(BOOL)includeSubject
470 id <AIEventHandler> eventHandler;
472 eventHandler = [eventHandlers objectForKey:eventID];
473 if (!eventHandler) eventHandler = [globalOnlyEventHandlers objectForKey:eventID];
476 return [eventHandler naturalLanguageDescriptionForEventID:eventID
477 listObject:listObject
479 includeSubject:includeSubject];
486 //Actions --------------------------------------------------------------------------------------------------------------
489 * @brief Register an actionID and its handler
491 * When an event occurs -- that is, when the event is generated via
492 * -[ESContactAlertsController generateEvent:forListObject:userInfo:] -- the handler for each action
493 * associated with that event within the appropriate list object's heirarchy (object -> containing group -> global)
494 * will be called as per the AIActionHandler protocol.
496 * @param actionID The actionID
497 * @param handler The handler, which must conform to the AIActionHandler protocol
499 - (void)registerActionID:(NSString *)actionID withHandler:(id <AIActionHandler>)handler
501 [actionHandlers setObject:handler forKey:actionID];
505 * @brief Return a dictionary whose keys are action IDs and whose objects are objects conforming to AIActionHandler
507 - (NSDictionary *)actionHandlers
509 return actionHandlers;
513 * @brief Returns a menu of all actions
515 * A menu item's represented object is the dictionary describing the action it represents
517 * @param target The target on which @selector(selectAction:) will be called on selection
518 * @result The NSMenu, which does not send validateMenuItem: messages
520 - (NSMenu *)menuOfActionsWithTarget:(id)target
522 NSEnumerator *enumerator;
524 NSMenuItem *menuItem;
526 NSMutableArray *menuItemArray;
529 menu = [[NSMenu alloc] init];
530 [menu setAutoenablesItems:NO];
532 menuItemArray = [[NSMutableArray alloc] init];
534 //Insert a menu item for each available action
535 enumerator = [actionHandlers keyEnumerator];
536 while ((actionID = [enumerator nextObject])) {
537 id <AIActionHandler> actionHandler = [actionHandlers objectForKey:actionID];
539 menuItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:[actionHandler shortDescriptionForActionID:actionID]
541 action:@selector(selectAction:)
543 [menuItem setRepresentedObject:actionID];
544 [menuItem setImage:[[actionHandler imageForActionID:actionID] imageByScalingForMenuItem]];
546 [menuItemArray addObject:menuItem];
550 //Sort the array of menuItems alphabetically by title
551 [menuItemArray sortUsingSelector:@selector(titleCompare:)];
553 enumerator = [menuItemArray objectEnumerator];
554 while ((menuItem = [enumerator nextObject])) {
555 [menu addItem:menuItem];
558 [menuItemArray release];
560 return [menu autorelease];
564 * @brief Return the default action ID for a new alert
566 - (NSString *)defaultActionID
568 NSString *defaultActionID = [[adium preferenceController] preferenceForKey:KEY_DEFAULT_ACTION_ID
569 group:PREF_GROUP_CONTACT_ALERTS];
570 if (![actionHandlers objectForKey:defaultActionID]) {
571 defaultActionID = [[actionHandlers allKeys] objectAtIndex:0];
574 return defaultActionID;
577 //Alerts ---------------------------------------------------------------------------------------------------------------
580 * @brief Returns an array of all the alerts of a given list object
582 * @param listObject The object
584 - (NSArray *)alertsForListObject:(AIListObject *)listObject
586 return [self alertsForListObject:listObject withEventID:nil actionID:nil];
590 * @brief Return an array of all alerts for a list object
592 * @param listObject The object, or nil for global
593 * @param eventID If specified, only return events matching eventID. If nil, don't filter based on events.
594 * @param actionID If specified, only return actions matching actionID. If nil, don't filter based on actionID.
596 - (NSArray *)alertsForListObject:(AIListObject *)listObject withEventID:(NSString *)eventID actionID:(NSString *)actionID
598 NSDictionary *contactAlerts = [[adium preferenceController] preferenceForKey:KEY_CONTACT_ALERTS
599 group:PREF_GROUP_CONTACT_ALERTS
600 objectIgnoringInheritance:listObject];
601 NSMutableArray *alertArray = [NSMutableArray array];
604 /* If we have an eventID, just look at the alerts for this eventID */
605 NSEnumerator *alertEnumerator;
608 alertEnumerator = [[contactAlerts objectForKey:eventID] objectEnumerator];
610 while ((alert = [alertEnumerator nextObject])) {
611 //If we don't have a specific actionID, or this one is right, add it
612 if (!actionID || [actionID isEqualToString:[alert objectForKey:KEY_ACTION_ID]]) {
613 [alertArray addObject:alert];
618 /* If we don't have an eventID, look at all alerts */
619 NSEnumerator *groupEnumerator;
622 //Flatten the alert dict into an array
623 groupEnumerator = [contactAlerts keyEnumerator];
624 while ((anEventID = [groupEnumerator nextObject])) {
625 NSEnumerator *alertEnumerator;
628 alertEnumerator = [[contactAlerts objectForKey:anEventID] objectEnumerator];
629 while ((alert = [alertEnumerator nextObject])) {
630 //If we don't have a specific actionID, or this one is right, add it
631 if (!actionID || [actionID isEqualToString:[alert objectForKey:KEY_ACTION_ID]]) {
632 [alertArray addObject:alert];
642 * @brief Add an alert (passed as a dictionary) to a list object
644 * @param newAlert The alert to add
645 * @param listObject The object to which to add, or nil for global
646 * @param setAsNewDefaults YES to make the type and details of newAlert be the new default for new alerts
648 - (void)addAlert:(NSDictionary *)newAlert toListObject:(AIListObject *)listObject setAsNewDefaults:(BOOL)setAsNewDefaults
650 NSString *newAlertEventID = [newAlert objectForKey:KEY_EVENT_ID];
651 NSMutableDictionary *contactAlerts;
652 NSMutableArray *eventArray;
654 [[adium preferenceController] delayPreferenceChangedNotifications:YES];
656 //Get the alerts for this list object
657 contactAlerts = [[[adium preferenceController] preferenceForKey:KEY_CONTACT_ALERTS
658 group:PREF_GROUP_CONTACT_ALERTS
659 objectIgnoringInheritance:listObject] mutableCopy];
660 if (!contactAlerts) contactAlerts = [[NSMutableDictionary alloc] init];
662 //Get the event array for the new alert, making a copy so we can modify it
663 eventArray = [[contactAlerts objectForKey:newAlertEventID] mutableCopy];
664 if (!eventArray) eventArray = [[NSMutableArray alloc] init];
666 //Avoid putting the exact same alert into the array twice
667 if ([eventArray indexOfObject:newAlert] == NSNotFound) {
669 [eventArray addObject:newAlert];
671 //Put the modified event array back into the contact alert dict, and save our changes
672 [contactAlerts setObject:eventArray forKey:newAlertEventID];
673 [[adium preferenceController] setPreference:contactAlerts
674 forKey:KEY_CONTACT_ALERTS
675 group:PREF_GROUP_CONTACT_ALERTS
679 //Update the default events if requested
680 if (setAsNewDefaults) {
681 [[adium preferenceController] setPreference:newAlertEventID
682 forKey:KEY_DEFAULT_EVENT_ID
683 group:PREF_GROUP_CONTACT_ALERTS];
684 [[adium preferenceController] setPreference:[newAlert objectForKey:KEY_ACTION_ID]
685 forKey:KEY_DEFAULT_ACTION_ID
686 group:PREF_GROUP_CONTACT_ALERTS];
690 [contactAlerts release];
691 [eventArray release];
693 [[adium preferenceController] delayPreferenceChangedNotifications:NO];
697 * @brief Add an alert at the global level
699 - (void)addGlobalAlert:(NSDictionary *)newAlert
701 [self addAlert:newAlert toListObject:nil setAsNewDefaults:NO];
705 * @brief Remove an alert from a listObject
707 * @param victimAlert The alert to remove; it will be tested against existing alerts using isEqual: so must be identical
708 * @param listObject The object (or nil, for global) from which to remove victimAlert
710 - (void)removeAlert:(NSDictionary *)victimAlert fromListObject:(AIListObject *)listObject
712 NSMutableDictionary *contactAlerts = [[[adium preferenceController] preferenceForKey:KEY_CONTACT_ALERTS
713 group:PREF_GROUP_CONTACT_ALERTS
714 objectIgnoringInheritance:listObject] mutableCopy];
715 NSString *victimEventID = [victimAlert objectForKey:KEY_EVENT_ID];
716 NSMutableArray *eventArray;
718 //Get the event array containing the victim alert, making a copy so we can modify it
719 eventArray = [[contactAlerts objectForKey:victimEventID] mutableCopy];
722 [eventArray removeObject:victimAlert];
724 //Put the modified event array back into the contact alert dict, and save our changes
725 if ([eventArray count]) {
726 [contactAlerts setObject:eventArray forKey:victimEventID];
728 [contactAlerts removeObjectForKey:victimEventID];
731 [[adium preferenceController] setPreference:contactAlerts
732 forKey:KEY_CONTACT_ALERTS
733 group:PREF_GROUP_CONTACT_ALERTS
735 [eventArray release];
736 [contactAlerts release];
740 * @brief Remove all alerts which are specifically applied to listObject
742 * This does not affect alerts set at higher (containing object, root) levels
744 - (void)removeAllAlertsFromListObject:(AIListObject *)listObject
746 [listObject setPreference:nil
747 forKey:KEY_CONTACT_ALERTS
748 group:PREF_GROUP_CONTACT_ALERTS];
752 * @brief Remove all global (root-level) alerts with a given action ID
754 - (void)removeAllGlobalAlertsWithActionID:(NSString *)actionID
756 NSDictionary *contactAlerts = [[adium preferenceController] preferenceForKey:KEY_CONTACT_ALERTS
757 group:PREF_GROUP_CONTACT_ALERTS];
758 NSMutableDictionary *newContactAlerts = [contactAlerts mutableCopy];
759 NSEnumerator *enumerator = [contactAlerts keyEnumerator];
760 NSString *victimEventID;
761 NSEnumerator *alertArrayEnumerator;
763 NSDictionary *alertDict;
765 //The contact alerts preference is a dictionary keyed by event. Each event key yields an array of dictionaries;
766 //each of these dictionaries represents an alert. We want to remove all dictionaries which represent alerts with
767 //the passed actionID
768 while ((victimEventID = [enumerator nextObject])) {
769 NSMutableArray *newEventArray = nil;
771 eventArray = [contactAlerts objectForKey:victimEventID];
773 //Enumerate each alert for this event
774 alertArrayEnumerator = [eventArray objectEnumerator];
775 while ((alertDict = [alertArrayEnumerator nextObject])) {
777 //We found an alertDict which needs to be removed
778 if ([[alertDict objectForKey:KEY_ACTION_ID] isEqualToString:actionID]) {
779 //If this is the first modification to the current eventArray, make a mutableCopy with which to work
780 if (!newEventArray) newEventArray = [eventArray mutableCopy];
781 [newEventArray removeObject:alertDict];
785 //newEventArray will only be non-nil if we made changes; now that we have enumerated this eventArray, save them
787 if ([newEventArray count]) {
788 [newContactAlerts setObject:newEventArray forKey:victimEventID];
790 [newContactAlerts removeObjectForKey:victimEventID];
794 [newEventArray release];
798 [[adium preferenceController] setPreference:newContactAlerts
799 forKey:KEY_CONTACT_ALERTS
800 group:PREF_GROUP_CONTACT_ALERTS];
801 [newContactAlerts release];
805 * @brief Remove all current global alerts and replace them with the alerts in allGlobalAlerts
807 * Used for setting a preset of events
809 - (void)setAllGlobalAlerts:(NSArray *)allGlobalAlerts
811 NSMutableDictionary *contactAlerts = [[NSMutableDictionary alloc] init];;
812 NSDictionary *eventDict;
813 NSEnumerator *enumerator;
815 [[adium preferenceController] delayPreferenceChangedNotifications:YES];
817 enumerator = [allGlobalAlerts objectEnumerator];
818 while ((eventDict = [enumerator nextObject])) {
819 NSMutableArray *eventArray;
820 NSString *eventID = [eventDict objectForKey:KEY_EVENT_ID];
822 /* Get the event array for this alert. Since we are creating the entire dictionary, we can be sure we are working
823 * with an NSMutableArray.
825 eventArray = [contactAlerts objectForKey:eventID];
826 if (!eventArray) eventArray = [NSMutableArray array];
829 [eventArray addObject:eventDict];
831 //Put the modified event array back into the contact alert dict
832 [contactAlerts setObject:eventArray forKey:eventID];
835 [[adium preferenceController] setPreference:contactAlerts
836 forKey:KEY_CONTACT_ALERTS
837 group:PREF_GROUP_CONTACT_ALERTS
839 [contactAlerts release];
841 [[adium preferenceController] delayPreferenceChangedNotifications:NO];
846 * @brief Move all contact alerts from oldObject to newObject
848 * This is useful when adding oldObject to the metaContact newObject so that any existing contact alerts for oldObject
849 * are applied at the contact-general level, displayed and handled properly for the new, combined contact.
851 * @param oldObject The object from which to move contact alerts
852 * @param newObject The object to which to we want to add the moved contact alerts
854 - (void)mergeAndMoveContactAlertsFromListObject:(AIListObject *)oldObject intoListObject:(AIListObject *)newObject
856 NSArray *oldAlerts = [self alertsForListObject:oldObject];
857 NSEnumerator *enumerator = [oldAlerts objectEnumerator];
858 NSDictionary *alertDict;
860 [[adium preferenceController] delayPreferenceChangedNotifications:YES];
862 //Add each alert to the target (addAlert:toListObject:setAsNewDefaults: will ensure identical alerts aren't added more than once)
863 while ((alertDict = [enumerator nextObject])) {
864 [self addAlert:alertDict toListObject:newObject setAsNewDefaults:NO];
867 //Remove the alerts from the originating list object
868 [self removeAllAlertsFromListObject:oldObject];
870 [[adium preferenceController] delayPreferenceChangedNotifications:NO];
875 * @brief Is the passed event a message event?
877 * Examples of messages events are "message sent" and "message received."
879 * @result YES if it is a message event
881 - (BOOL)isMessageEvent:(NSString *)eventID
883 return ([eventHandlersByGroup[AIMessageEventHandlerGroup] objectForKey:eventID] != nil ||
884 ([globalOnlyEventHandlersByGroup[AIMessageEventHandlerGroup] objectForKey:eventID] != nil));
888 * @brief Is the passed event a contact status event?
890 * Examples of messages events are "contact signed on" and "contact went away."
892 * @result YES if it is a contact status event
894 - (BOOL)isContactStatusEvent:(NSString *)eventID
896 return ([eventHandlersByGroup[AIContactsEventHandlerGroup] objectForKey:eventID] != nil ||
897 ([globalOnlyEventHandlersByGroup[AIContactsEventHandlerGroup] objectForKey:eventID] != nil));