Updated Address Book scripts to work with new AppleScript API.
[adiumx.git] / Source / ESUserIconHandlingPlugin.m
blob49a3a0a7f6a90566001f8831fb4a7f5d03df68b2
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 <Adium/AIChatControllerProtocol.h>
18 #import <Adium/AIContactControllerProtocol.h>
19 #import <Adium/AIContentControllerProtocol.h>
20 #import <Adium/AIInterfaceControllerProtocol.h>
21 #import <Adium/AIPreferenceControllerProtocol.h>
22 #import <Adium/AIToolbarControllerProtocol.h>
23 #import "ESUserIconHandlingPlugin.h"
24 #import <AIUtilities/AIFileManagerAdditions.h>
25 #import <AIUtilities/AIMutableOwnerArray.h>
26 #import <AIUtilities/AIToolbarUtilities.h>
27 #import <AIUtilities/AIMenuAdditions.h>
28 #import <AIUtilities/AIImageAdditions.h>
29 #import <AIUtilities/AIImageButton.h>
30 #import <Adium/AIAccount.h>
31 #import <Adium/AIChat.h>
32 #import <Adium/AIListContact.h>
33 #import <Adium/AIListObject.h>
34 #import <Adium/AIServiceIcons.h>
36 #define TOOLBAR_ITEM_TAG        -999
38 @interface ESUserIconHandlingPlugin (PRIVATE)
39 - (BOOL)cacheAndSetUserIconFromPreferenceForListObject:(AIListObject *)inObject;
40 - (BOOL)_cacheUserIconData:(NSData *)inData forObject:(AIListObject *)inObject;
41 - (NSString *)_cachedImagePathForObject:(AIListObject *)inObject;
42 - (BOOL)destroyCacheForListObject:(AIListObject *)inObject;
43 - (void)registerToolbarItem;
45 - (void)_updateToolbarIconOfChat:(AIChat *)inChat inWindow:(NSWindow *)window;
46 - (void)_updateToolbarItem:(NSToolbarItem *)item forChat:(AIChat *)chat;
48 - (void)updateToolbarItemForObject:(AIListObject *)inObject;
49 @end
51 /*!
52  * @class ESUserIconHandlingPlugin
53  * @brief User icon handling component
54  *
55  * This component manages the Adium user icon cache.  It also provides a toolbar icon which shows the user icon
56  * or service icon of the current chat in its window.
57  */
58 @implementation ESUserIconHandlingPlugin
60 /*!
61  * @brief Install
62  */
63 - (void)installPlugin
65         //Register our observers
66         [[adium notificationCenter] addObserver:self
67                                                                    selector:@selector(listObjectAttributesChanged:)
68                                                                            name:ListObject_AttributesChanged
69                                                                          object:nil];
71         [self registerToolbarItem];
74 /*!
75  * @brief Uninstall
76  */
77 - (void)uninstallPlugin
79         [[adium contactController] unregisterListObjectObserver:self];
80         [[adium preferenceController] unregisterPreferenceObserver:self];
83 //Needs some            [self updateToolbarItemForObject:inObject];
85 /*!
86  * @brief List object attributes changes
87  *
88  * A plugin, or this plugin, modified the display array for the object; ensure our cache is up to date.
89  */
90 - (void)listObjectAttributesChanged:(NSNotification *)notification
92         AIListObject    *inObject = [notification object];
93         NSSet                   *keys = [[notification userInfo] objectForKey:@"Keys"];
94         
95         if (inObject && [keys containsObject:KEY_USER_ICON]) {
96                 [self updateToolbarItemForObject:inObject];
97         }
100 #pragma mark Toolbar Item
103  * @brief Register our toolbar item
105  * Our toolbar item shows an image for the current chat, displaying it full size/animating if clicked.
106  */
107 - (void)registerToolbarItem
109         AIImageButton   *button;
110         NSToolbarItem   *toolbarItem;
112         toolbarItems = [[NSMutableSet alloc] init];
113         validatedItems = [[NSMutableSet alloc] init];
115         //Toolbar item registration
116         [[NSNotificationCenter defaultCenter] addObserver:self
117                                                                                          selector:@selector(toolbarWillAddItem:)
118                                                                                                  name:NSToolbarWillAddItemNotification
119                                                                                            object:nil];
120         [[NSNotificationCenter defaultCenter] addObserver:self
121                                                                                          selector:@selector(toolbarDidRemoveItem:)
122                                                                                                  name:NSToolbarDidRemoveItemNotification
123                                                                                            object:nil];
125         button = [[AIImageButton alloc] initWithFrame:NSMakeRect(0,0,32,32)];
126         toolbarItem = [AIToolbarUtilities toolbarItemWithIdentifier:@"UserIcon"
127                                                                                                                   label:AILocalizedString(@"Icon",nil)
128                                                                                                    paletteLabel:AILocalizedString(@"Contact Icon",nil)
129                                                                                                                 toolTip:AILocalizedString(@"Show this contact's icon",nil)
130                                                                                                                  target:self
131                                                                                                 settingSelector:@selector(setView:)
132                                                                                                         itemContent:button
133                                                                                                                  action:@selector(dummyAction:)
134                                                                                                                    menu:nil];
136         [toolbarItem setMinSize:NSMakeSize(32,32)];
137         [toolbarItem setMaxSize:NSMakeSize(32,32)];
138         [button setToolbarItem:toolbarItem];
139         [button setImage:[NSImage imageNamed:@"userIconToolbar" forClass:[self class] loadLazily:YES]];
140         [button release];
142         //Register our toolbar item
143         [[adium toolbarController] registerToolbarItem:toolbarItem forToolbarType:@"MessageWindow"];
147  * @brief After the toolbar has added the item we can set up the submenus
148  */
149 - (void)toolbarWillAddItem:(NSNotification *)notification
151         NSToolbarItem   *item = [[notification userInfo] objectForKey:@"item"];
153         if ([[item itemIdentifier] isEqualToString:@"UserIcon"]) {
155                 [item setEnabled:YES];
157                 //Add menu to toolbar item (for text mode)
158                 NSMenuItem      *menuFormRepresentation, *blankMenuItem;
159                 NSMenu          *menu;
161                 menuFormRepresentation = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] init] autorelease];
163                 menu = [[[NSMenu allocWithZone:[NSMenu menuZone]] init] autorelease];
164                 [menu setDelegate:self];
165                 [menu setAutoenablesItems:NO];
167                 blankMenuItem = [[NSMenuItem alloc] initWithTitle:@""
168                                                                                                    target:self
169                                                                                                    action:@selector(dummyAction:)
170                                                                                         keyEquivalent:@""];
171                 [blankMenuItem setRepresentedObject:item];
172                 [blankMenuItem setEnabled:YES];
173                 [menu addItem:blankMenuItem];
175                 [menuFormRepresentation setSubmenu:menu];
176                 [menuFormRepresentation setTitle:[item label]];
177                 [item setMenuFormRepresentation:menuFormRepresentation];
179                 //If this is the first item added, start observing for chats becoming visible so we can update the icon
180                 if ([toolbarItems count] == 0) {
181                         [[adium notificationCenter] addObserver:self
182                                                                                    selector:@selector(chatDidBecomeVisible:)
183                                                                                            name:@"AIChatDidBecomeVisible"
184                                                                                          object:nil];
185                 }
187                 [toolbarItems addObject:item];
188                 
189                 [self performSelector:@selector(toolbarDidAddItem:)
190                                    withObject:item
191                                    afterDelay:0];
192         }
195 - (void)toolbarDidAddItem:(NSToolbarItem *)item
197         /* Only need to take action if we haven't already validated the initial state of this item.
198         * This will only be true when the toolbar is revealed for the first time having been hidden when window opened.
199         */
200         if (![validatedItems containsObject:item]) {
201                 NSEnumerator *enumerator = [[NSApp windows] objectEnumerator];
202                 NSWindow         *window;
203                 NSToolbar        *thisItemsToolbar = [item toolbar];
204                 
205                 //Look at each window to find the toolbar we are in
206                 while ((window = [enumerator nextObject])) {
207                         if ([window toolbar] == thisItemsToolbar) break;
208                 }
209                 
210                 if (window) {
211                         [self _updateToolbarItem:item
212                                                          forChat:[[adium interfaceController] activeChatInWindow:window]];
213                 }
214         }
218  * @brief Toolbar removed an item.
220  * If the item is one of ours, stop tracking it.
222  * @param notification Notification with an @"item" userInfo key for an NSToolbarItem.
223  */
224 - (void)toolbarDidRemoveItem: (NSNotification *)notification
226         NSToolbarItem   *item = [[notification userInfo] objectForKey:@"item"];
227         if ([toolbarItems containsObject:item]) {
228                 [item setView:nil];
229                 [toolbarItems removeObject:item];
230                 [validatedItems removeObject:item];
232                 if ([toolbarItems count] == 0) {
233                         [[adium notificationCenter] removeObserver:self
234                                                                                                   name:@"AIChatDidBecomeVisible"
235                                                                                                 object:nil];
236                 }
237         }
241  * @brief A chat became visible in a window.
243  * Update the item with the @"UserIcon" identifier if necessary
245  * @param notification Notification with an AIChat object and an @"NSWindow" userInfo key
246  */
247 - (void)chatDidBecomeVisible:(NSNotification *)notification
249         [self _updateToolbarIconOfChat:[notification object]
250                                                   inWindow:[[notification userInfo] objectForKey:@"NSWindow"]];
253 - (void)updateToolbarItemForObject:(AIListObject *)inObject
255         AIChat          *chat;
256         NSWindow        *window;
258         //Update the icon in the toolbar for this contact if a chat is open and we have any toolbar items
259         if (([toolbarItems count] > 0) &&
260                 [inObject isKindOfClass:[AIListContact class]] &&
261                 (chat = [[adium chatController] existingChatWithContact:(AIListContact *)inObject]) &&
262                 (window = [[adium interfaceController] windowForChat:chat])) {
263                 [self _updateToolbarIconOfChat:chat
264                                                           inWindow:window];
265         }
268 - (void)_updateToolbarItem:(NSToolbarItem *)item forChat:(AIChat *)chat
270         AIListContact   *listContact;
271         NSImage                 *image;
272         
273         if ((listContact = [[chat listObject] parentContact]) && ![chat isGroupChat]) {
274                 image = [listContact userIcon];
275                 
276                 //Use the serviceIcon if no image can be found
277                 if (!image) image = [AIServiceIcons serviceIconForObject:listContact
278                                                                                                                         type:AIServiceIconLarge
279                                                                                                            direction:AIIconNormal];
280         } else {
281                 //If we have no listObject or we have a name, we are a group chat and
282                 //should use the account's service icon
283                 image = [AIServiceIcons serviceIconForObject:[chat account]
284                                                                                                 type:AIServiceIconLarge
285                                                                                    direction:AIIconNormal];
286         }
287         
288         [(AIImageButton *)[item view] setImage:image];
289         
290         [validatedItems addObject:item];
294  * @brief Update the user image toolbar icon in a chat
296  * @param chat The chat for which to retrieve an image
297  * @param window The window in which the chat resides
298  */
299 - (void)_updateToolbarIconOfChat:(AIChat *)chat inWindow:(NSWindow *)window
301         NSToolbar               *toolbar = [window toolbar];
302         NSEnumerator    *enumerator = [[toolbar items] objectEnumerator];
303         NSToolbarItem   *item;
305         while ((item = [enumerator nextObject])) {
306                 if ([[item itemIdentifier] isEqualToString:@"UserIcon"]) {
307                         [self _updateToolbarItem:item forChat:chat];
308                         break;
309                 }
310         }
314  * @brief Empty action for menu item validation purposes
315  */
316 - (IBAction)dummyAction:(id)sender{};
319  * @brief Menu needs update
321  * Should only be called for a menu off one of our toolbar items in text-only mode, and only when that menu is about
322  * to be displayed. The menu should have two items. The first is added by the system; the second has no title and is
323  * our menu item for showing the image.
324  */
325 - (void)menuNeedsUpdate:(NSMenu *)menu
327         //The first item is a root item inserted by the system. The second item is the single item
328         NSMenuItem              *menuItem = [menu itemAtIndex:1];
329         NSToolbarItem   *toolbarItem = [menuItem representedObject];
331         [menuItem setImage:[[[(AIImageButton *)[toolbarItem view] image] copy] autorelease]];
334 - (BOOL)validateMenuItem:(NSMenuItem *)menuItem
336         return YES;
339 @end