French updates
[adiumx.git] / Source / ESContactAlertsController.m
blob965ee039239f148bb58a369230315ee734ab67e9
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 "AIPreferenceController.h"
18 #import "ESContactAlertsController.h"
19 #import <AIUtilities/AIMenuAdditions.h>
20 #import <AIUtilities/ESImageAdditions.h>
21 #import <Adium/AIListObject.h>
23 @interface ESContactAlertsController (PRIVATE)
24 - (NSMutableArray *)appendEventsForObject:(AIListObject *)listObject eventID:(NSString *)eventID toArray:(NSMutableArray *)events;
25 - (void)addMenuItemsForEventHandlers:(NSDictionary *)inEventHandlers toArray:(NSMutableArray *)menuItemArray withTarget:(id)target forGlobalMenu:(BOOL)global;
26 - (void)removeAllAlertsFromListObject:(AIListObject *)listObject;
27 @end
29 @implementation ESContactAlertsController
31 int eventMenuItemSort(id menuItemA, id menuItemB, void *context);
32 int actionMenuItemSort(id menuItemA, id menuItemB, void *context);
34 static  NSMutableDictionary             *eventHandlersByGroup[EVENT_HANDLER_GROUP_COUNT];
35 static  NSMutableDictionary             *globalOnlyEventHandlersByGroup[EVENT_HANDLER_GROUP_COUNT];
37 /*!
38  * @brief Initialize
39  */
40 + (void)initialize
42         static BOOL didInitialize = NO;
43         if(!didInitialize){
44                 int i;
45                 for(i = 0; i < EVENT_HANDLER_GROUP_COUNT; i++){
46                         eventHandlersByGroup[i] = nil;
47                         globalOnlyEventHandlersByGroup[i] = nil;
48                 }
50                 didInitialize = YES;
51         }
54 //init and close
55 - (void)initController
57         globalOnlyEventHandlers = [[NSMutableDictionary alloc] init];
58         eventHandlers = [[NSMutableDictionary alloc] init];
59         actionHandlers = [[NSMutableDictionary alloc] init];
62 - (void)closeController
64         
67 - (void)dealloc
69         [globalOnlyEventHandlers release]; globalOnlyEventHandlers = nil;
70         [eventHandlers release]; eventHandlers = nil;
71         [actionHandlers release]; actionHandlers = nil;
72         
73         [super dealloc];
77 //Events ---------------------------------------------------------------------------------------------------------------
78 #pragma mark Events
79 //Register a potential event
80 - (void)registerEventID:(NSString *)eventID
81                         withHandler:(id <AIEventHandler>)handler
82                                 inGroup:(AIEventHandlerGroupType)inGroup
83                          globalOnly:(BOOL)global
85         if (global){
86                 [globalOnlyEventHandlers setObject:handler forKey:eventID];
87                 
88                 if(!globalOnlyEventHandlersByGroup[inGroup]) globalOnlyEventHandlersByGroup[inGroup] = [[NSMutableDictionary alloc] init];
89                 [globalOnlyEventHandlersByGroup[inGroup] setObject:handler forKey:eventID];
90                 
91         }else{
92                 [eventHandlers setObject:handler forKey:eventID];
93                 
94                 if(!eventHandlersByGroup[inGroup]) eventHandlersByGroup[inGroup] = [[NSMutableDictionary alloc] init];
95                 [eventHandlersByGroup[inGroup] setObject:handler forKey:eventID];
96         }
99 //Return all event IDs for groups/contacts
100 - (NSArray *)allEventIDs
102         return([[eventHandlers allKeys] arrayByAddingObjectsFromArray:[globalOnlyEventHandlers allKeys]]);
105 - (NSString *)longDescriptionForEventID:(NSString *)eventID forListObject:(AIListObject *)listObject
107         id <AIEventHandler> handler;
108         
109         handler = [eventHandlers objectForKey:eventID];
110         if(!handler) handler = [globalOnlyEventHandlers objectForKey:eventID];
111         
112         return([handler longDescriptionForEventID:eventID forListObject:listObject]);
115 //Returns a menu of all events
116 //- Selector called on event selection is selectEvent:
117 //- A menu item's represented object is the dictionary describing the event it represents
118 - (NSMenu *)menuOfEventsWithTarget:(id)target forGlobalMenu:(BOOL)global
120         NSEnumerator            *enumerator;
121         NSMenuItem                      *item;
122         NSMenu                          *menu;
124         //Prepare our menu
125         menu = [[NSMenu allocWithZone:[NSMenu zone]] init];
126         [menu setAutoenablesItems:NO];
127         
128         enumerator = [[self arrayOfMenuItemsForEventsWithTarget:target forGlobalMenu:global] objectEnumerator];
129         while(item = [enumerator nextObject]){
130                 [menu addItem:item];
131         }
132         
133         return [menu autorelease];
136 - (NSArray *)arrayOfMenuItemsForEventsWithTarget:(id)target forGlobalMenu:(BOOL)global
138         NSMutableArray          *menuItemArray = [NSMutableArray array];
139         BOOL                            addedItems = NO;
140         int                                     i;
141         
142         for(i = 0; i < EVENT_HANDLER_GROUP_COUNT; i++){
143                 NSMutableArray          *groupMenuItemArray;
145                 //Create an array of menu items for this group
146                 groupMenuItemArray = [NSMutableArray array];
147                 
148                 [self addMenuItemsForEventHandlers:eventHandlersByGroup[i]
149                                                                    toArray:groupMenuItemArray
150                                                                 withTarget:target
151                                                          forGlobalMenu:global];
152                 if (global){
153                         [self addMenuItemsForEventHandlers:globalOnlyEventHandlersByGroup[i]
154                                                                            toArray:groupMenuItemArray
155                                                                         withTarget:target
156                                                                  forGlobalMenu:global];
157                 }
158                 
159                 if([groupMenuItemArray count]){
160                         //Add a separator if we are adding a group and we have added before
161                         if(addedItems){
162                                 [menuItemArray addObject:[NSMenuItem separatorItem]];
163                         }else{
164                                 addedItems = YES;
165                         }
166                         
167                         //Sort the array of menuItems alphabetically by title within this group
168                         [groupMenuItemArray sortUsingFunction:eventMenuItemSort context:nil];
169                         
170                         [menuItemArray addObjectsFromArray:groupMenuItemArray];
171                 }
172         }
173         
174         return(menuItemArray);
175 }       
177 - (void)addMenuItemsForEventHandlers:(NSDictionary *)inEventHandlers toArray:(NSMutableArray *)menuItemArray withTarget:(id)target forGlobalMenu:(BOOL)global
178 {       
179         NSEnumerator            *enumerator;
180         NSString                        *eventID;
181         NSMenuItem                      *menuItem;
182         
183         enumerator = [inEventHandlers keyEnumerator];
184         while((eventID = [enumerator nextObject])){
185                 id <AIEventHandler>     eventHandler = [inEventHandlers objectForKey:eventID];          
186                 
187         menuItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:(global ?
188                                                                                                                                                                 [eventHandler globalShortDescriptionForEventID:eventID] :
189                                                                                                                                                                 [eventHandler shortDescriptionForEventID:eventID])
190                                                                                                                                                 target:target 
191                                                                                                                                                 action:@selector(selectEvent:) 
192                                                                                                                                  keyEquivalent:@""];
193         [menuItem setRepresentedObject:eventID];
194                 [menuItemArray addObject:menuItem];
195                 [menuItem release];
196     }
199 int eventIDSort(id objectA, id objectB, void *context){
200         int                                     groupA, groupB;
201         id <AIEventHandler> eventHandlerA;
202         id <AIEventHandler> eventHandlerB;
203         
204         //Determine the group of each eventID
205         for(groupA = 0; groupA < EVENT_HANDLER_GROUP_COUNT; groupA++){
206                 eventHandlerA = [eventHandlersByGroup[groupA] objectForKey:objectA];
207                 if(!eventHandlerA){
208                         eventHandlerA = [globalOnlyEventHandlersByGroup[groupA] objectForKey:objectA];
209                 }
210                 
211                 if(eventHandlerA) break;
212         }
213         
214         //Determine the group of each eventID
215         for(groupB = 0; groupB < EVENT_HANDLER_GROUP_COUNT; groupB++){
216                 eventHandlerB = [eventHandlersByGroup[groupB] objectForKey:objectB];
217                 if(!eventHandlerB){
218                         eventHandlerB = [globalOnlyEventHandlersByGroup[groupB] objectForKey:objectB];
219                 }
220                 
221                 if(eventHandlerB) break;
222         }
223         
224         if(groupA < groupB){
225                 return NSOrderedAscending;
226                 
227         }else if(groupB < groupA){
228                 return NSOrderedDescending;
229                 
230         }else{
231                 NSString        *descriptionA = [eventHandlerA globalShortDescriptionForEventID:objectA];
232                 NSString        *descriptionB = [eventHandlerA globalShortDescriptionForEventID:objectB];
233                 
234                 return ([descriptionA caseInsensitiveCompare:descriptionB]);
235         }
238 - (NSArray *)sortedArrayOfEventIDsFromArray:(NSArray *)inArray
240         return([inArray sortedArrayUsingFunction:eventIDSort context:NULL]);
243 - (NSImage *)imageForEventID:(NSString *)eventID
245         id <AIEventHandler>     eventHandler;
246         
247         eventHandler = [eventHandlers objectForKey:eventID];            
248         if(!eventHandler) eventHandler = [globalOnlyEventHandlers objectForKey:eventID];
250         return([eventHandler imageForEventID:eventID]);
254  Generate an event, returning a set of the actionIDs which were performed.
255  If perviouslyPerformedActionIDs is non-nil, it indicates a set of actionIDs which should be treated as if
256         they had already been performed in this invocation.
258 - (NSSet *)generateEvent:(NSString *)eventID forListObject:(AIListObject *)listObject userInfo:(id)userInfo previouslyPerformedActionIDs:(NSSet *)previouslyPerformedActionIDs
260         NSArray                 *alerts = [self appendEventsForObject:listObject eventID:eventID toArray:nil];
261         NSMutableSet    *performedActionIDs = nil;
262         
263         if(alerts && [alerts count]){
264                 NSEnumerator            *enumerator;
265                 NSDictionary            *alert;
267                 performedActionIDs = (previouslyPerformedActionIDs ?
268                                                           [[previouslyPerformedActionIDs mutableCopy] autorelease]:
269                                                           [NSMutableSet set]);
270                 
271                 //We go from contact->group->root; a given action will only fire once for this event
272                 enumerator = [alerts objectEnumerator];
274                 //Process each alert (There may be more than one for an event)
275                 while(alert = [enumerator nextObject]){
276                         NSString        *actionID;
277                         id <AIActionHandler>    actionHandler;                  
278                         
279                         actionID = [alert objectForKey:KEY_ACTION_ID];
280                         actionHandler = [actionHandlers objectForKey:actionID];         
281                         
282                         if((![performedActionIDs containsObject:actionID]) || ([actionHandler allowMultipleActionsWithID:actionID])){
283                                 [actionHandler performActionID:actionID
284                                                                  forListObject:listObject
285                                                                    withDetails:[alert objectForKey:KEY_ACTION_DETAILS] 
286                                                          triggeringEventID:eventID
287                                                                           userInfo:userInfo];
288                                 
289                                 //If this alert was a single-fire alert, we can delete it now
290                                 if([[alert objectForKey:KEY_ONE_TIME_ALERT] intValue]){
291                                         [self removeAlert:alert fromListObject:listObject];
292                                 }
293                                 
294                                 //We don't want to perform this action again for this event
295                                 [performedActionIDs addObject:actionID];
296                         }
297                 }
298         }
299         
300         [[adium notificationCenter] postNotificationName:eventID
301                                                                                           object:listObject 
302                                                                                         userInfo:userInfo];
303         
304         /* If we generated a new perfromedActionIDs, return it.  If we didn't, return the original
305          * previouslyPerformedActionIDs, which may also be nil or may be actionIDs performed on some previous invocation.
306          */
307         return (performedActionIDs ? performedActionIDs : previouslyPerformedActionIDs);
311  Append events for the passed object to the specified array.
312         Create the array if passed nil.
313         Return an array which contains the object's own events followed by its containingObject's events.
314         If the object is nil, we retrieve the global preferences.
316  This method is intended to be called recursively; it should generate an array which has alerts from:
317         contact->metaContact->group->global preferences (skipping any which don't exist).
318  */
319 - (NSMutableArray *)appendEventsForObject:(AIListObject *)listObject eventID:(NSString *)eventID toArray:(NSMutableArray *)events
321         NSArray                 *newEvents;
323         // AILog(@"appendEventsForObject: %@ eventID: %@ toArray: %@",preferenceSource,eventID,events);
325         //Add events for this object (replacing any inherited from the containing object so that this object takes precendence)
326         newEvents = [[[adium preferenceController] preferenceForKey:KEY_CONTACT_ALERTS
327                                                                                                                   group:PREF_GROUP_CONTACT_ALERTS
328                                                                           objectIgnoringInheritance:listObject] objectForKey:eventID];
329         
330         if(newEvents && [newEvents count]){
331                 if(!events) events = [NSMutableArray array];
332                 [events addObjectsFromArray:newEvents];
333         }
335         //Get all events from the contanining object if we have an object
336         if(listObject){
337                 //If listObject doesn't have a containingObject, this will pass nil
338                 events = [self appendEventsForObject:[listObject containingObject]
339                                                                          eventID:eventID
340                                                                          toArray:events];
341         }
343         return(events);
346 - (NSString *)defaultEventID
348         NSString *defaultEventID = [[adium preferenceController] preferenceForKey:KEY_DEFAULT_EVENT_ID
349                                                                                                                                                 group:PREF_GROUP_CONTACT_ALERTS];
350         if (![eventHandlers objectForKey:defaultEventID]){
351                 defaultEventID = [[eventHandlers allKeys] objectAtIndex:0];
352         }
353         
354         return defaultEventID;
357 - (NSString *)eventIDForEnglishDisplayName:(NSString *)displayName
359         NSEnumerator            *enumerator;
360         NSString                        *eventID;
361         
362         enumerator = [eventHandlers keyEnumerator];
363         while((eventID = [enumerator nextObject])){
364                 id <AIEventHandler>     eventHandler = [eventHandlers objectForKey:eventID];            
365                 if ([[eventHandler englishGlobalShortDescriptionForEventID:eventID] isEqualToString:displayName]){
366                         return eventID;
367                 }
368         }
370         enumerator = [globalOnlyEventHandlers keyEnumerator];
371         while((eventID = [enumerator nextObject])){
372                 id <AIEventHandler>     eventHandler = [globalOnlyEventHandlers objectForKey:eventID];          
373                 if ([[eventHandler englishGlobalShortDescriptionForEventID:eventID] isEqualToString:displayName]){
374                         return eventID;
375                 }
376         }
377         
378         return nil;
381 - (NSString *)globalShortDescriptionForEventID:(NSString *)eventID
383         id <AIEventHandler>     eventHandler;
384         
385         eventHandler = [eventHandlers objectForKey:eventID];
386         if(!eventHandler) eventHandler = [globalOnlyEventHandlers objectForKey:eventID];
387         
388         if (eventHandler){
389                 return [eventHandler globalShortDescriptionForEventID:eventID];
390         }
391         
392         return @"";
395 - (NSString *)naturalLanguageDescriptionForEventID:(NSString *)eventID
396                                                                                 listObject:(AIListObject *)listObject
397                                                                                   userInfo:(id)userInfo
398                                                                         includeSubject:(BOOL)includeSubject
400         id <AIEventHandler>     eventHandler;
402         eventHandler = [eventHandlers objectForKey:eventID];
403         if(!eventHandler) eventHandler = [globalOnlyEventHandlers objectForKey:eventID];
405         if(eventHandler){
406                 return([eventHandler naturalLanguageDescriptionForEventID:eventID
407                                                                                                            listObject:listObject
408                                                                                                                  userInfo:userInfo
409                                                                                                    includeSubject:includeSubject]);
410         }
412         return @"";
415 int eventMenuItemSort(id menuItemA, id menuItemB, void *context){
416         return ([[menuItemA title] caseInsensitiveCompare:[menuItemB title]]);
420 //Actions --------------------------------------------------------------------------------------------------------------
421 #pragma mark Actions
422 - (void)registerActionID:(NSString *)actionID withHandler:(id <AIActionHandler>)handler
424         [actionHandlers setObject:handler forKey:actionID];
427 //Return all available actions
428 - (NSDictionary *)actionHandlers
430         return(actionHandlers);
433 //Returns a menu of all actions
434 //- Selector called on action selection is selectAction:
435 //- A menu item's represented object is the dictionary describing the action it represents
436 - (NSMenu *)menuOfActionsWithTarget:(id)target
438     NSEnumerator        *enumerator;
439     NSString            *actionID;
440         NSMenuItem              *menuItem;
441         NSMenu                  *menu;
442         NSMutableArray  *menuItemArray;
443         
444         //Prepare our menu
445         menu = [[NSMenu alloc] init];
446         [menu setAutoenablesItems:NO];
447         
448         menuItemArray = [[NSMutableArray alloc] init];
449         
450     //Insert a menu item for each available action
451         enumerator = [actionHandlers keyEnumerator];
452         while((actionID = [enumerator nextObject])){
453                 id <AIActionHandler> actionHandler = [actionHandlers objectForKey:actionID];            
454                 
455         menuItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:[actionHandler shortDescriptionForActionID:actionID]
456                                                                                                                                                 target:target 
457                                                                                                                                                 action:@selector(selectAction:) 
458                                                                                                                                  keyEquivalent:@""];
459         [menuItem setRepresentedObject:actionID];
460                 [menuItem setImage:[[actionHandler imageForActionID:actionID] imageByScalingToSize:NSMakeSize(16,16)]];
461                 
462         [menuItemArray addObject:menuItem];
463                 [menuItem release];
464     }
466         //Sort the array of menuItems alphabetically by title
467         [menuItemArray sortUsingFunction:actionMenuItemSort context:nil];
468         
469         enumerator = [menuItemArray objectEnumerator];
470         while(menuItem = [enumerator nextObject]){
471                 [menu addItem:menuItem];
472         }
473         
474         [menuItemArray release];
476         return [menu autorelease];
477 }       
479 - (NSString *)defaultActionID
481         NSString *defaultActionID = [[adium preferenceController] preferenceForKey:KEY_DEFAULT_ACTION_ID
482                                                                                                                                                  group:PREF_GROUP_CONTACT_ALERTS];
483         if (![actionHandlers objectForKey:defaultActionID]){
484                 defaultActionID = [[actionHandlers allKeys] objectAtIndex:0];
485         }
486         
487         return defaultActionID;
490 int actionMenuItemSort(id menuItemA, id menuItemB, void *context){
491         return ([[menuItemA title] caseInsensitiveCompare:[menuItemB title]]);
494 //Alerts ---------------------------------------------------------------------------------------------------------------
495 #pragma mark Alerts
496 //Returns an array of all the alerts of a given list object
497 - (NSArray *)alertsForListObject:(AIListObject *)listObject
499         return([self alertsForListObject:listObject withEventID:nil actionID:nil]);
502 - (NSArray *)alertsForListObject:(AIListObject *)listObject withEventID:(NSString *)eventID actionID:(NSString *)actionID
504         NSDictionary    *contactAlerts = [[adium preferenceController] preferenceForKey:KEY_CONTACT_ALERTS
505                                                                                                                                                           group:PREF_GROUP_CONTACT_ALERTS
506                                                                                                                   objectIgnoringInheritance:listObject];
507         NSMutableArray  *alertArray = [NSMutableArray array];
509         if(eventID){
510                 /* If we have an eventID, just look at the alerts for this eventID */
511                 NSEnumerator    *alertEnumerator;
512                 NSDictionary    *alert;
513                 
514                 alertEnumerator = [[contactAlerts objectForKey:eventID] objectEnumerator];
515                 
516                 while(alert = [alertEnumerator nextObject]){
517                         //If we don't have a specific actionID, or this one is right, add it
518                         if(!actionID || [actionID isEqualToString:[alert objectForKey:KEY_ACTION_ID]]){
519                                 [alertArray addObject:alert];
520                         }
521                 }
522                 
523         }else{
524                 /* If we don't have an eventID, look at all alerts */
525                 NSEnumerator    *groupEnumerator;
526                 NSString                *anEventID;
527                 
528                 //Flatten the alert dict into an array
529                 groupEnumerator = [contactAlerts keyEnumerator];
530                 while(anEventID = [groupEnumerator nextObject]){
531                         NSEnumerator    *alertEnumerator;
532                         NSDictionary    *alert;
533                         
534                         alertEnumerator = [[contactAlerts objectForKey:anEventID] objectEnumerator];
535                         while(alert = [alertEnumerator nextObject]){
536                                 //If we don't have a specific actionID, or this one is right, add it
537                                 if(!actionID || [actionID isEqualToString:[alert objectForKey:KEY_ACTION_ID]]){
538                                         [alertArray addObject:alert];
539                                 }
540                         }
541                 }       
542         }
543         
544         return(alertArray);     
547 //Add an alert (passed as a dictionary) to a list object
548 - (void)addAlert:(NSDictionary *)newAlert toListObject:(AIListObject *)listObject setAsNewDefaults:(BOOL)setAsNewDefaults
550         NSString                        *newAlertEventID = [newAlert objectForKey:KEY_EVENT_ID];
551         NSMutableDictionary     *contactAlerts;
552         NSMutableArray          *eventArray;
553         
554         [[adium preferenceController] delayPreferenceChangedNotifications:YES];
555         
556         //Get the alerts for this list object
557         contactAlerts = [[[adium preferenceController] preferenceForKey:KEY_CONTACT_ALERTS
558                                                                                                                           group:PREF_GROUP_CONTACT_ALERTS
559                                                                                   objectIgnoringInheritance:listObject] mutableCopy];
560         if(!contactAlerts) contactAlerts = [[NSMutableDictionary alloc] init];
561         
562         //Get the event array for the new alert, making a copy so we can modify it
563         eventArray = [[contactAlerts objectForKey:newAlertEventID] mutableCopy];
564         if(!eventArray) eventArray = [[NSMutableArray alloc] init];
565         
566         //Avoid putting the exact same alert into the array twice
567         if ([eventArray indexOfObject:newAlert] == NSNotFound){
568                 //Add the new alert
569                 [eventArray addObject:newAlert];
570                 
571                 //Put the modified event array back into the contact alert dict, and save our changes
572                 [contactAlerts setObject:eventArray forKey:newAlertEventID];
573                 [[adium preferenceController] setPreference:contactAlerts
574                                                                                          forKey:KEY_CONTACT_ALERTS
575                                                                                           group:PREF_GROUP_CONTACT_ALERTS
576                                                                                          object:listObject];    
577         }
579         //Update the default events if requested
580         if(setAsNewDefaults){
581                 [[adium preferenceController] setPreference:newAlertEventID
582                                                                                          forKey:KEY_DEFAULT_EVENT_ID
583                                                                                           group:PREF_GROUP_CONTACT_ALERTS];
584                 [[adium preferenceController] setPreference:[newAlert objectForKey:KEY_ACTION_ID]
585                                                                                          forKey:KEY_DEFAULT_ACTION_ID
586                                                                                           group:PREF_GROUP_CONTACT_ALERTS];     
587         }
588         
589         //Cleanup
590         [contactAlerts release];
591         [eventArray release];
592         
593         [[adium preferenceController] delayPreferenceChangedNotifications:NO];
596 - (void)addGlobalAlert:(NSDictionary *)newAlert
598         [self addAlert:newAlert toListObject:nil setAsNewDefaults:NO];
601 //Remove the alert (passed as a dictionary, must be an exact = match) form a list object
602 - (void)removeAlert:(NSDictionary *)victimAlert fromListObject:(AIListObject *)listObject
604         NSMutableDictionary     *contactAlerts = [[[adium preferenceController] preferenceForKey:KEY_CONTACT_ALERTS
605                                                                                                                                                                    group:PREF_GROUP_CONTACT_ALERTS
606                                                                                                                            objectIgnoringInheritance:listObject] mutableCopy];
607         NSString                        *victimEventID = [victimAlert objectForKey:KEY_EVENT_ID];
608         NSMutableArray          *eventArray;
609         
610         //Get the event array containing the victim alert, making a copy so we can modify it
611         eventArray = [[contactAlerts objectForKey:victimEventID] mutableCopy];
612         
613         //Remove the victim
614         [eventArray removeObject:victimAlert];
615         
616         //Put the modified event array back into the contact alert dict, and save our changes
617         if ([eventArray count]){
618                 [contactAlerts setObject:eventArray forKey:victimEventID];
619         }else{
620                 [contactAlerts removeObjectForKey:victimEventID];       
621         }
622         
623         [[adium preferenceController] setPreference:contactAlerts
624                                                                                  forKey:KEY_CONTACT_ALERTS
625                                                                                   group:PREF_GROUP_CONTACT_ALERTS
626                                                                                  object:listObject];
627         [eventArray release];
628         [contactAlerts release];
631 - (void)removeAllAlertsFromListObject:(AIListObject *)listObject
633         [listObject setPreference:nil
634                                            forKey:KEY_CONTACT_ALERTS
635                                                 group:PREF_GROUP_CONTACT_ALERTS];
638 - (void)removeAllGlobalAlertsWithActionID:(NSString *)actionID
640         NSDictionary            *contactAlerts = [[adium preferenceController] preferenceForKey:KEY_CONTACT_ALERTS 
641                                                                                                                                                                   group:PREF_GROUP_CONTACT_ALERTS];
642         NSMutableDictionary *newContactAlerts = [contactAlerts mutableCopy];
643         NSEnumerator            *enumerator = [contactAlerts keyEnumerator];
644         NSString                        *victimEventID;
645         NSEnumerator            *alertArrayEnumerator;
646         NSArray                         *eventArray;
647         NSDictionary            *alertDict;
648         
649         //The contact alerts preference is a dictionary keyed by event.  Each event key yields an array of dictionaries;
650         //each of these dictionaries represents an alert.  We want to remove all dictionaries which represent alerts with
651         //the passed actionID
652         while (victimEventID = [enumerator nextObject]){
653                 NSMutableArray  *newEventArray = nil;
654         
655                 eventArray = [contactAlerts objectForKey:victimEventID];
657                 //Enumerate each alert for this event
658                 alertArrayEnumerator = [eventArray objectEnumerator];
659                 while (alertDict = [alertArrayEnumerator nextObject]){
660                         
661                         //We found an alertDict which needs to be removed
662                         if ([[alertDict objectForKey:KEY_ACTION_ID] isEqualToString:actionID]){
663                                 //If this is the first modification to the current eventArray, make a mutableCopy with which to work
664                                 if (!newEventArray) newEventArray = [eventArray mutableCopy];
665                                 [newEventArray removeObject:alertDict];
666                         }
667                 }
668                 
669                 //newEventArray will only be non-nil if we made changes; now that we have enumerated this eventArray, save them
670                 if (newEventArray){
671                         if ([newEventArray count]){
672                                 [newContactAlerts setObject:newEventArray forKey:victimEventID];
673                         }else{
674                                 [newContactAlerts removeObjectForKey:victimEventID];    
675                         }
676                         
677                         //Clean up
678                         [newEventArray release];
679                 }
680         }
681         
682         [[adium preferenceController] setPreference:newContactAlerts
683                                                                                  forKey:KEY_CONTACT_ALERTS
684                                                                                   group:PREF_GROUP_CONTACT_ALERTS];
685         [newContactAlerts release];
689  * @brief Remove all current global alerts and replace them with the alerts in allGlobalAlerts
691  * Used for setting a preset of events
692  */
693 - (void)setAllGlobalAlerts:(NSArray *)allGlobalAlerts
695         NSMutableDictionary     *contactAlerts = [[NSMutableDictionary alloc] init];;
696         NSDictionary            *eventDict;
697         NSEnumerator            *enumerator;
698         
699         [[adium preferenceController] delayPreferenceChangedNotifications:YES];
700         
701         enumerator = [allGlobalAlerts objectEnumerator];
702         while(eventDict = [enumerator nextObject]){
703                 NSMutableArray          *eventArray;
704                 NSString                        *eventID = [eventDict objectForKey:KEY_EVENT_ID];
706                 /* Get the event array for this alert. Since we are creating the entire dictionary, we can be sure we are working
707                  * with an NSMutableArray.
708                  */
709                 eventArray = [contactAlerts objectForKey:eventID];
710                 if(!eventArray) eventArray = [NSMutableArray array];            
712                 //Add the new alert
713                 [eventArray addObject:eventDict];
714                 
715                 //Put the modified event array back into the contact alert dict
716                 [contactAlerts setObject:eventArray forKey:eventID];            
717         }
718         
719         [[adium preferenceController] setPreference:contactAlerts
720                                                                                  forKey:KEY_CONTACT_ALERTS
721                                                                                   group:PREF_GROUP_CONTACT_ALERTS
722                                                                                  object:nil];
723         [contactAlerts release];
725         [[adium preferenceController] delayPreferenceChangedNotifications:NO];
726         
729 - (void)mergeAndMoveContactAlertsFromListObject:(AIListObject *)oldObject intoListObject:(AIListObject *)newObject
731         NSArray                         *oldAlerts = [self alertsForListObject:oldObject];
732         NSEnumerator            *enumerator = [oldAlerts objectEnumerator];
733         NSDictionary            *alertDict;
734         
735         [[adium preferenceController] delayPreferenceChangedNotifications:YES];
736         
737         //Add each alert to the target (addAlert:toListObject:setAsNewDefaults: will ensure identical alerts aren't added more than once)
738         while (alertDict  = [enumerator nextObject]){
739                 [self addAlert:alertDict toListObject:newObject setAsNewDefaults:NO];
740         }
741         
742         //Remove the alerts from the originating list object
743         [self removeAllAlertsFromListObject:oldObject];
744         
745         [[adium preferenceController] delayPreferenceChangedNotifications:NO];
748 #pragma mark -
749 - (BOOL)isMessageEvent:(NSString *)eventID
751         return(([eventHandlersByGroup[AIMessageEventHandlerGroup] objectForKey:eventID] != nil) ||
752                    ([globalOnlyEventHandlersByGroup[AIMessageEventHandlerGroup] objectForKey:eventID] != nil));
755 @end