Unescape the HREF attribute's text before passing it to NSURL which does not expect...
[adiumx.git] / Source / AIAliasSupportPlugin.m
blob378db2bc46bc4123320248a55822d55eca79c1ab
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 "AIAliasSupportPlugin.h"
18 #import <Adium/AIContactControllerProtocol.h>
19 #import "AIContactInfoWindowController.h"
20 #import "AIContactListEditorPlugin.h"
21 #import <Adium/AIMenuControllerProtocol.h>
22 #import <Adium/AIPreferenceControllerProtocol.h>
23 #import <AIUtilities/AIDictionaryAdditions.h>
24 #import <AIUtilities/AIMenuAdditions.h>
25 #import <AIUtilities/AIMutableOwnerArray.h>
26 #import <Adium/AIListContact.h>
27 #import <Adium/AIListObject.h>
29 #define ALIASES_DEFAULT_PREFS           @"Alias Defaults"
30 #define DISPLAYFORMAT_DEFAULT_PREFS     @"Display Format Defaults"
32 #define CONTACT_NAME_MENU_TITLE         AILocalizedString(@"Contact Name Format",nil)
33 #define ALIAS                                           AILocalizedString(@"Alias",nil)
34 #define ALIAS_SCREENNAME                        AILocalizedString(@"Alias (User Name)",nil)
35 #define SCREENNAME_ALIAS                        AILocalizedString(@"User Name (Alias)",nil)
36 #define SCREENNAME                                      AILocalizedString(@"User Name",nil)
38 @interface AIAliasSupportPlugin (PRIVATE)
39 - (NSSet *)_applyAlias:(NSString *)inAlias toObject:(AIListObject *)inObject notify:(BOOL)notify;
40 - (NSMenu *)_contactNameMenu;
41 @end
43 /*!
44  * @class AIAliasSupportPlugin
45  * @brief Plugin to handle applying aliases to contacts
46  *
47  * This plugin applies aliases to contacts.  It also responsible for generating the "long display name"
48  * used in the contact list which may include some combination of alias and screen name.
49  */
50 @implementation AIAliasSupportPlugin
52 /*!
53  * @brief Install plugin
54  */
55 - (void)installPlugin
57     //Register our default preferences
58     [[adium preferenceController] registerDefaults:[NSDictionary dictionaryNamed:ALIASES_DEFAULT_PREFS
59                                                                                                                                                 forClass:[self class]]
60                                                                                   forGroup:PREF_GROUP_ALIASES];
61     [[adium preferenceController] registerDefaults:[NSDictionary dictionaryNamed:DISPLAYFORMAT_DEFAULT_PREFS
62                                                                                                                                                 forClass:[self class]]
63                                                                                   forGroup:PREF_GROUP_DISPLAYFORMAT];
64         
65         //Create the menu item
66         menuItem_contactName = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:CONTACT_NAME_MENU_TITLE
67                                                                                                                                                                  target:nil
68                                                                                                                                                                  action:nil
69                                                                                                                                                   keyEquivalent:@""] autorelease];
70         
71         //Add the menu item (which will have _contactNameMenu as its submenu)
72         [[adium menuController] addMenuItem:menuItem_contactName toLocation:LOC_View_Additions];
73         
74         menu_contactSubmenu = [[self _contactNameMenu] retain];
75         [menuItem_contactName setSubmenu:menu_contactSubmenu];
77     //Observe preferences changes
78     [[adium notificationCenter] addObserver:self
79                                                                    selector:@selector(applyAliasRequested:)
80                                                                            name:Contact_ApplyDisplayName
81                                                                          object:nil];
82         [[adium preferenceController] registerPreferenceObserver:self forGroup:PREF_GROUP_DISPLAYFORMAT];       
85 /*!
86  * @brief Uninstall plugin
87  */
88 - (void)uninstallPlugin
90     [[adium contactController] unregisterListObjectObserver:self];
91         [[adium preferenceController] unregisterPreferenceObserver:self];
92         [[adium notificationCenter] removeObserver:self];
95 /*!
96  * @brief Deallocate
97  */
98 - (void)dealloc
100         [menu_contactSubmenu release];
101         [super dealloc];
105  * @brief Change the format for the long display name used in the contact list
107  * @param sender An NSMenuItem which was clicked. Its tag should be an AIDisplayNameType.
108  */
109 -(IBAction)changeFormat:(id)sender
111         [[adium preferenceController] setPreference:[NSNumber numberWithInt:[sender tag]]
112                                                                                  forKey:@"Long Display Format"
113                                                                                   group:PREF_GROUP_DISPLAYFORMAT];
117  * @brief Update list object
119  * As contacts are created or a formattedUID is received, update their alias, display name, and long display name
120  */
121 - (NSSet *)updateListObject:(AIListObject *)inObject keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
123     if ((inModifiedKeys == nil) || ([inModifiedKeys containsObject:@"FormattedUID"])) {
124                 return [self _applyAlias:[inObject preferenceForKey:@"Alias"
125                                                                                                           group:PREF_GROUP_ALIASES 
126                                                                           ignoreInheritedValues:YES]
127                                                 toObject:inObject
128                                                   notify:NO];
129     }
130         
131         return nil;
135  * @brief Preferences changed. Our only preference is for the Long Display Name format
137  * Update the checked menu item since this is not done automatically.
138  * Update all list objects so we make use of the new long display format preference.
139  */
140 - (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
141                                                         object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
143         //Clear old checkmark
144         [[menu_contactSubmenu itemWithTag:displayFormat] setState:NSOffState];
145         
146         //Load new displayFormat
147         displayFormat = [[prefDict objectForKey:@"Long Display Format"] intValue]; 
148         
149         //Set new checkmark
150         [[menu_contactSubmenu itemWithTag:displayFormat] setState:NSOnState];
151         
152         if (firstTime) {
153                 //Register ourself as a handle observer
154                 [[adium contactController] registerListObjectObserver:self];
155         } else {
156                 //Update all existing contacts
157                 [[adium contactController] updateAllListObjectsForObserver:self];
159         }
163  * @brief Notification was posted to apply a specific alias
165  * This is used from elsewhere in Adium to request the alias of the object be updated. It's a bit ugly, really.
166  * The object of the notification is an AIListObject.
167  * The userInfo is a dictionary with an NSNumber on key @"Notify" indicating if a 'changed' notification should be sent out as a result.
168  *              If this is NO, it is equivalent to a 'silent' update.
169  * The user info dictionary also has the desired NSString alias on the key @"Alias".
170  *              If this is not specified, the object's preference is reloaded.
171  */
172 - (void)applyAliasRequested:(NSNotification *)notification
174         AIListObject    *object = [notification object];
175         NSDictionary    *userInfo = [notification userInfo];
176         
177         NSNumber                *shouldNotifyNumber = [userInfo objectForKey:@"Notify"];
178         
179         NSString                *alias = [userInfo objectForKey:@"Alias"];
180         if (!alias) {
181                 alias = [object preferenceForKey:@"Alias"
182                                                                    group:PREF_GROUP_ALIASES 
183                                    ignoreInheritedValues:YES];
184         }
185         
186         [self _applyAlias:alias
187                          toObject:object
188                            notify:(shouldNotifyNumber ? [shouldNotifyNumber boolValue] : NO)];
191 //Private ---------------------------------------------------------------------------------------
193  * @brief Apply an alias to an object
195  * This does not save any preferences.
197  * @param inAlias The alias to apply. 
198  * @param inObject The object to which the alias should be applied
199  * @param notify YES if a notification should be sent out after the alias is applied
200  */
201 - (NSSet *)_applyAlias:(NSString *)inAlias toObject:(AIListObject *)inObject notify:(BOOL)notify
203         NSSet                           *modifiedAttributes;
204     NSString                    *displayName = nil;
205     NSString                    *longDisplayName = nil;
206     NSString                    *formattedUID = nil;
207         
208         AIMutableOwnerArray *displayNameArray = [inObject displayArrayForKey:@"Display Name"];
209         
210         //Apply the alias
211         [[inObject displayArrayForKey:@"Adium Alias"] setObject:inAlias withOwner:self];
212         [displayNameArray setObject:inAlias withOwner:self priorityLevel:High_Priority]; // <---- Not setting the display array to null...
213         
214         //Get the displayName which is now active for the object
215         displayName = [displayNameArray objectValue];
216         
217     //Build and set the Long Display Name
218         if ([inObject isKindOfClass:[AIListContact class]]) {
219                 switch (displayFormat)
220                 {
221                         case AINameFormat_DisplayName:
222                                 longDisplayName = displayName;
223                                 break;
224                                 
225                         case AINameFormat_DisplayName_ScreenName:
226                                 formattedUID = [inObject formattedUID];
227                                 
228                                 if (!displayName || !formattedUID || [displayName isEqualToString:formattedUID]) {
229                                         longDisplayName = displayName;
230                                 } else {
231                                         longDisplayName = [NSString stringWithFormat:@"%@ (%@)",displayName,formattedUID];
232                                 }
233                                         break;
234                                 
235                         case AINameFormat_ScreenName_DisplayName:
236                                 formattedUID = [inObject formattedUID];
237                                 if (!displayName || !formattedUID || [displayName isEqualToString:formattedUID]) {
238                                         longDisplayName = displayName;
239                                 } else {
240                                         longDisplayName = [NSString stringWithFormat:@"%@ (%@)",formattedUID,displayName];
241                                 }
242                                         break;
243                                 
244                         case AINameFormat_ScreenName:
245                                 //??? - How should this be handled for metaContacts?  What if there are no aliases set?
246                                 formattedUID = [inObject formattedUID];
247                                 longDisplayName = (formattedUID ? formattedUID : displayName);
248                                 break;
249                                 
250                         default:
251                                 longDisplayName = nil;
252                                 break;
253                 }
254                 
255                 //Apply the Long Display Name
256                 [[inObject displayArrayForKey:@"Long Display Name"] setObject:longDisplayName withOwner:self];
257         }
258         
259         modifiedAttributes = [NSSet setWithObjects:@"Display Name", @"Long Display Name", @"Adium Alias", nil];
260         
261         //If notify is YES, send out a manual listObjectAttributesChanged notice; 
262         //if NO, the observer methods will be handling it
263         if (notify) {
264                 [[adium contactController] listObjectAttributesChanged:inObject
265                                                                                                   modifiedKeys:modifiedAttributes];
266         }
267         
268         return modifiedAttributes;
272  * @brief Generate the menu of long display name format choices
274  * @result The autoreleased menu
275  */
276 - (NSMenu *)_contactNameMenu
278         
279         NSMenu          *choicesMenu;
280         NSMenuItem  *menuItem;
281         
282         choicesMenu = [[[NSMenu allocWithZone:[NSMenu menuZone]] initWithTitle:@""] autorelease];
283         
284     menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:ALIAS
285                                                                                                                                          target:self
286                                                                                                                                          action:@selector(changeFormat:)
287                                                                                                                           keyEquivalent:@""] autorelease];
288     [menuItem setTag:AINameFormat_DisplayName];
289     [choicesMenu addItem:menuItem];
290         
291     menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:ALIAS_SCREENNAME
292                                                                                                                                          target:self
293                                                                                                                                          action:@selector(changeFormat:)
294                                                                                                                           keyEquivalent:@""] autorelease];
295     [menuItem setTag:AINameFormat_DisplayName_ScreenName];
296     [choicesMenu addItem:menuItem];
297         
298     menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:SCREENNAME_ALIAS
299                                                                                                                                          target:self
300                                                                                                                                          action:@selector(changeFormat:)
301                                                                                                                           keyEquivalent:@""] autorelease];
302     [menuItem setTag:AINameFormat_ScreenName_DisplayName];
303     [choicesMenu addItem:menuItem];
304         
305     menuItem = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:SCREENNAME
306                                                                                                                                          target:self
307                                                                                                                                          action:@selector(changeFormat:)
308                                                                                                                           keyEquivalent:@""] autorelease];
309     [menuItem setTag:AINameFormat_ScreenName];
310     [choicesMenu addItem:menuItem];
311         
312         return choicesMenu;
315 @end