William Bowling is sharing code with you

Bitbucket is a code hosting site. Unlimited public and private repositories. Free for small teams.

Don't show this again

wbowling / adium (fork of adium / adium)

Fork of Adium for patches/improvements

Clone this repository (size: 338.7 MB): HTTPS / SSH
hg clone https://bitbucket.org/wbowling/adium
hg clone ssh://hg@bitbucket.org/wbowling/adium

adium / Source / ESUserIconHandlingPlugin.m

/* 
 * Adium is the legal property of its developers, whose names are listed in the copyright file included
 * with this source distribution.
 * 
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
 * General Public License as published by the Free Software Foundation; either version 2 of the License,
 * or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
 * Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along with this program; if not,
 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */

#import <Adium/AIChatControllerProtocol.h>
#import <Adium/AIContactControllerProtocol.h>
#import <Adium/AIContentControllerProtocol.h>
#import <Adium/AIInterfaceControllerProtocol.h>
#import <Adium/AIToolbarControllerProtocol.h>
#import "ESUserIconHandlingPlugin.h"
#import <AIUtilities/AIApplicationAdditions.h>
#import <AIUtilities/AIFileManagerAdditions.h>
#import <AIUtilities/AIMutableOwnerArray.h>
#import <AIUtilities/AIToolbarUtilities.h>
#import <AIUtilities/AIMenuAdditions.h>
#import <AIUtilities/AIImageAdditions.h>
#import <AIUtilities/AIImageButton.h>
#import <Adium/AIAccount.h>
#import <Adium/AIChat.h>
#import <Adium/AIListContact.h>
#import <Adium/AIListObject.h>
#import <Adium/AIServiceIcons.h>

#define TOOLBAR_ITEM_TAG        -999

@interface ESUserIconHandlingPlugin ()
- (void)registerToolbarItem;
- (void)_updateToolbarIconOfChat:(AIChat *)inChat inWindow:(NSWindow *)window;
- (void)_updateToolbarItem:(NSToolbarItem *)item forChat:(AIChat *)chat;
- (void)updateToolbarItemForObject:(AIListObject *)inObject;
@end

/*!
 * @class ESUserIconHandlingPlugin
 * @brief User icon handling component
 *
 * This component manages the Adium user icon cache.  It also provides a toolbar icon which shows the user icon
 * or service icon of the current chat in its window.
 */
@implementation ESUserIconHandlingPlugin

/*!
 * @brief Install
 */
- (void)installPlugin
{
        //Register our observers
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                                   selector:@selector(listObjectAttributesChanged:)
                                                                           name:ListObject_AttributesChanged
                                                                         object:nil];

        [self registerToolbarItem];
}

/*!
 * @brief Uninstall
 */
- (void)uninstallPlugin
{
        [adium.preferenceController unregisterPreferenceObserver:self];
}

//Needs some            [self updateToolbarItemForObject:inObject];

/*!
 * @brief List object attributes changes
 *
 * A plugin, or this plugin, modified the display array for the object; ensure our cache is up to date.
 */
- (void)listObjectAttributesChanged:(NSNotification *)notification
{
        AIListObject    *inObject = [notification object];
        NSSet                   *keys = [[notification userInfo] objectForKey:@"Keys"];
        
        if ([keys containsObject:KEY_USER_ICON]) {
                if (inObject) {
                        [self updateToolbarItemForObject:inObject];
                } else {
                        for (AIChat *chat in adium.interfaceController.openChats) {
                                NSWindow *window = [adium.interfaceController windowForChat:chat];
                                if (window) {
                                        [self _updateToolbarIconOfChat:chat
                                                                                  inWindow:window];
                                }
                        }
                }
                        
        }
}

#pragma mark Toolbar Item

/*!
 * @brief Register our toolbar item
 *
 * Our toolbar item shows an image for the current chat, displaying it full size/animating if clicked.
 */
- (void)registerToolbarItem
{
        AIImageButton   *button;
        NSToolbarItem   *toolbarItem;

        toolbarItems = [[NSMutableSet alloc] init];
        validatedItems = [[NSMutableSet alloc] init];

        //Toolbar item registration
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                                                         selector:@selector(toolbarWillAddItem:)
                                                                                                 name:NSToolbarWillAddItemNotification
                                                                                           object:nil];
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                                                         selector:@selector(toolbarDidRemoveItem:)
                                                                                                 name:NSToolbarDidRemoveItemNotification
                                                                                           object:nil];

        button = [[AIImageButton alloc] initWithFrame:NSMakeRect(0,0,32,32)];
        toolbarItem = [AIToolbarUtilities toolbarItemWithIdentifier:@"UserIcon"
                                                                                                                  label:AILocalizedString(@"Icon",nil)
                                                                                                   paletteLabel:AILocalizedString(@"Contact Icon",nil)
                                                                                                                toolTip:AILocalizedString(@"Show this contact's icon",nil)
                                                                                                                 target:self
                                                                                                settingSelector:@selector(setView:)
                                                                                                        itemContent:button
                                                                                                                 action:@selector(dummyAction:)
                                                                                                                   menu:nil];

        [toolbarItem setMinSize:NSMakeSize(32,32)];
        [toolbarItem setMaxSize:NSMakeSize(32,32)];
        [button setToolbarItem:toolbarItem];
        [button setImage:[NSImage imageNamed:@"userIconToolbar" forClass:[self class] loadLazily:YES]];
        [button release];

        //Register our toolbar item
        [adium.toolbarController registerToolbarItem:toolbarItem forToolbarType:@"MessageWindow"];
}

/*!
 * @brief After the toolbar has added the item we can set up the submenus
 */
- (void)toolbarWillAddItem:(NSNotification *)notification
{
        NSToolbarItem   *item = [[notification userInfo] objectForKey:@"item"];

        if ([[item itemIdentifier] isEqualToString:@"UserIcon"]) {

                [item setEnabled:YES];

                //Add menu to toolbar item (for text mode)
                NSMenuItem      *menuFormRepresentation, *blankMenuItem;
                NSMenu          *menu;

                menuFormRepresentation = [[[NSMenuItem allocWithZone:[NSMenu menuZone]] init] autorelease];

                menu = [[[NSMenu allocWithZone:[NSMenu menuZone]] init] autorelease];
                [menu setDelegate:self];
                [menu setAutoenablesItems:NO];

                blankMenuItem = [[NSMenuItem alloc] initWithTitle:@""
                                                                                                   target:self
                                                                                                   action:@selector(dummyAction:)
                                                                                        keyEquivalent:@""];
                [blankMenuItem setRepresentedObject:item];
                [blankMenuItem setEnabled:YES];
                [menu addItem:blankMenuItem];
                [blankMenuItem release];

                [menuFormRepresentation setSubmenu:menu];
                [menuFormRepresentation setTitle:[item label]];
                [item setMenuFormRepresentation:menuFormRepresentation];

                //If this is the first item added, start observing for chats becoming visible so we can update the icon
                if ([toolbarItems count] == 0) {
                        [[NSNotificationCenter defaultCenter] addObserver:self
                                                                                   selector:@selector(chatDidBecomeVisible:)
                                                                                           name:@"AIChatDidBecomeVisible"
                                                                                         object:nil];
                }

                [toolbarItems addObject:item];
                
                [self performSelector:@selector(toolbarDidAddItem:)
                                   withObject:item
                                   afterDelay:0];
        }
}

- (void)toolbarDidAddItem:(NSToolbarItem *)item
{
        /* Only need to take action if we haven't already validated the initial state of this item.
        * This will only be true when the toolbar is revealed for the first time having been hidden when window opened.
        */
        if (![validatedItems containsObject:item]) {
                NSEnumerator *enumerator = [[NSApp windows] objectEnumerator];
                NSWindow         *window;
                NSToolbar        *thisItemsToolbar = [item toolbar];
                
                //Look at each window to find the toolbar we are in
                while ((window = [enumerator nextObject])) {
                        if ([window toolbar] == thisItemsToolbar) break;
                }
                
                if (window) {
                        [self _updateToolbarItem:item
                                                         forChat:[adium.interfaceController activeChatInWindow:window]];
                }
        }
}

/*!
 * @brief Toolbar removed an item.
 *
 * If the item is one of ours, stop tracking it.
 *
 * @param notification Notification with an @"item" userInfo key for an NSToolbarItem.
 */
- (void)toolbarDidRemoveItem: (NSNotification *)notification
{
        NSToolbarItem   *item = [[notification userInfo] objectForKey:@"item"];
        if ([toolbarItems containsObject:item]) {
                [item setView:nil];
                [toolbarItems removeObject:item];
                [validatedItems removeObject:item];

                if ([toolbarItems count] == 0) {
                        [[NSNotificationCenter defaultCenter] removeObserver:self
                                                                                                  name:@"AIChatDidBecomeVisible"
                                                                                                object:nil];
                }
        }
}

/*!
 * @brief A chat became visible in a window.
 *
 * Update the item with the @"UserIcon" identifier if necessary
 *
 * @param notification Notification with an AIChat object and an @"NSWindow" userInfo key
 */
- (void)chatDidBecomeVisible:(NSNotification *)notification
{
        [self _updateToolbarIconOfChat:[notification object]
                                                  inWindow:[[notification userInfo] objectForKey:@"NSWindow"]];
}

- (void)updateToolbarItemForObject:(AIListObject *)inObject
{
        AIChat          *chat;
        NSWindow        *window;

        //Update the icon in the toolbar for this contact if a chat is open and we have any toolbar items
        if (([toolbarItems count] > 0) &&
                [inObject isKindOfClass:[AIListContact class]] &&
                (chat = [adium.chatController existingChatWithContact:(AIListContact *)inObject]) &&
                (window = [adium.interfaceController windowForChat:chat])) {
                [self _updateToolbarIconOfChat:chat
                                                          inWindow:window];
        }
}

- (void)_updateToolbarItem:(NSToolbarItem *)item forChat:(AIChat *)chat
{
        AIListContact   *listContact;
        NSImage                 *image;
        
        if ((listContact = chat.listObject.parentContact) && !chat.isGroupChat) {
                image = [listContact userIcon];
                
                //Use the serviceIcon if no image can be found
                if (!image) image = [AIServiceIcons serviceIconForObject:listContact
                                                                                                                        type:AIServiceIconLarge
                                                                                                           direction:AIIconNormal];
        } else {
                //If we have no listObject or we have a name, we are a group chat and
                //should use the account's service icon
                image = [AIServiceIcons serviceIconForObject:chat.account
                                                                                                type:AIServiceIconLarge
                                                                                   direction:AIIconNormal];
        }
        
        [(AIImageButton *)[item view] setImage:image];
        
        [validatedItems addObject:item];
}

/*!
 * @brief Update the user image toolbar icon in a chat
 *
 * @param chat The chat for which to retrieve an image
 * @param window The window in which the chat resides
 */
- (void)_updateToolbarIconOfChat:(AIChat *)chat inWindow:(NSWindow *)window
{
        NSToolbar               *toolbar = [window toolbar];
        NSEnumerator    *enumerator = [[toolbar items] objectEnumerator];
        NSToolbarItem   *item;

        while ((item = [enumerator nextObject])) {
                if ([[item itemIdentifier] isEqualToString:@"UserIcon"]) {
                        [self _updateToolbarItem:item forChat:chat];
                        break;
                }
        }
}

/*!
 * @brief Empty action for menu item validation purposes
 */
- (IBAction)dummyAction:(id)sender{};

/*!
 * @brief Menu needs update
 *
 * Should only be called for a menu off one of our toolbar items in text-only mode, and only when that menu is about
 * to be displayed.
 */
- (void)menuNeedsUpdate:(NSMenu *)menu
{
        NSMenuItem *menuItem = [menu itemAtIndex:0];
        NSToolbarItem   *toolbarItem = [menuItem representedObject];

        [menuItem setImage:[[[(AIImageButton *)[toolbarItem view] image] copy] autorelease]];
}

- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
{
        return YES;
}

@end