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 <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;
52 * @class ESUserIconHandlingPlugin
53 * @brief User icon handling component
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.
58 @implementation ESUserIconHandlingPlugin
65 //Register our observers
66 [[adium notificationCenter] addObserver:self
67 selector:@selector(listObjectAttributesChanged:)
68 name:ListObject_AttributesChanged
71 [self registerToolbarItem];
77 - (void)uninstallPlugin
79 [[adium contactController] unregisterListObjectObserver:self];
80 [[adium preferenceController] unregisterPreferenceObserver:self];
83 //Needs some [self updateToolbarItemForObject:inObject];
86 * @brief List object attributes changes
88 * A plugin, or this plugin, modified the display array for the object; ensure our cache is up to date.
90 - (void)listObjectAttributesChanged:(NSNotification *)notification
92 AIListObject *inObject = [notification object];
93 NSSet *keys = [[notification userInfo] objectForKey:@"Keys"];
95 if (inObject && [keys containsObject:KEY_USER_ICON]) {
96 [self updateToolbarItemForObject:inObject];
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.
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
120 [[NSNotificationCenter defaultCenter] addObserver:self
121 selector:@selector(toolbarDidRemoveItem:)
122 name:NSToolbarDidRemoveItemNotification
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)
131 settingSelector:@selector(setView:)
133 action:@selector(dummyAction:)
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]];
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
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;
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:@""
169 action:@selector(dummyAction:)
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"
187 [toolbarItems addObject:item];
189 [self performSelector:@selector(toolbarDidAddItem:)
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.
200 if (![validatedItems containsObject:item]) {
201 NSEnumerator *enumerator = [[NSApp windows] objectEnumerator];
203 NSToolbar *thisItemsToolbar = [item toolbar];
205 //Look at each window to find the toolbar we are in
206 while ((window = [enumerator nextObject])) {
207 if ([window toolbar] == thisItemsToolbar) break;
211 [self _updateToolbarItem:item
212 forChat:[[adium interfaceController] activeChatInWindow:window]];
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.
224 - (void)toolbarDidRemoveItem: (NSNotification *)notification
226 NSToolbarItem *item = [[notification userInfo] objectForKey:@"item"];
227 if ([toolbarItems containsObject:item]) {
229 [toolbarItems removeObject:item];
230 [validatedItems removeObject:item];
232 if ([toolbarItems count] == 0) {
233 [[adium notificationCenter] removeObserver:self
234 name:@"AIChatDidBecomeVisible"
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
247 - (void)chatDidBecomeVisible:(NSNotification *)notification
249 [self _updateToolbarIconOfChat:[notification object]
250 inWindow:[[notification userInfo] objectForKey:@"NSWindow"]];
253 - (void)updateToolbarItemForObject:(AIListObject *)inObject
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
268 - (void)_updateToolbarItem:(NSToolbarItem *)item forChat:(AIChat *)chat
270 AIListContact *listContact;
273 if ((listContact = [[chat listObject] parentContact]) && ![chat isGroupChat]) {
274 image = [listContact userIcon];
276 //Use the serviceIcon if no image can be found
277 if (!image) image = [AIServiceIcons serviceIconForObject:listContact
278 type:AIServiceIconLarge
279 direction:AIIconNormal];
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];
288 [(AIImageButton *)[item view] setImage:image];
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
299 - (void)_updateToolbarIconOfChat:(AIChat *)chat inWindow:(NSWindow *)window
301 NSToolbar *toolbar = [window toolbar];
302 NSEnumerator *enumerator = [[toolbar items] objectEnumerator];
305 while ((item = [enumerator nextObject])) {
306 if ([[item itemIdentifier] isEqualToString:@"UserIcon"]) {
307 [self _updateToolbarItem:item forChat:chat];
314 * @brief Empty action for menu item validation purposes
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.
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