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 / AIListObjectContentsPlugin.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 "AIListObjectContentsPlugin.h"
#import <Adium/AIContactControllerProtocol.h>
#import <Adium/AIInterfaceControllerProtocol.h>
#import <Adium/AIMenuControllerProtocol.h>
#import <Adium/AIListContact.h>
#import <Adium/AIListObject.h>
#import <Adium/AIListGroup.h>
#import <Adium/AIMetaContact.h>
#import <Adium/AIAbstractListController.h>
#import <Adium/AIServiceIcons.h>
#import <AIUtilities/AIImageDrawingAdditions.h>
#import <AIUtilities/AIMenuAdditions.h>
#import <Adium/AIStatusIcons.h>
#import <Adium/AIContactHidingController.h>

#define META_TOOLTIP_ICON_SIZE NSMakeSize(11,11)

#define EXPAND_CONTACT          AILocalizedString(@"Expand Combined Contact", nil)
#define COLLAPSE_CONTACT        AILocalizedString(@"Collapse Combined Contact", nil)

#define MAX_CONTACTS 20
#define MORE_CONTACTS_STRING AILocalizedString(@"%d others", @"Used to describe omitted contacts.\
                                                               The first parameter is the number of omitted contacts")

/*!
 * @class AIListObjectContentsPlugin
 * @brief Tooltip component: Show the contacts contained by metaContacts, with service and status state.
 */
@implementation AIListObjectContentsPlugin

/*!
 * @brief Install
 */
- (void)installPlugin
{
    //Install our tooltip entry
    [adium.interfaceController registerContactListTooltipEntry:self secondaryEntry:YES];
        
        contextualMenuItem = [[NSMenuItem alloc] initWithTitle:EXPAND_CONTACT
                                                                                                        target:self
                                                                                                        action:@selector(toggleMetaContactExpansion:)
                                                                                         keyEquivalent:@""];
        [adium.menuController addContextualMenuItem:contextualMenuItem
                                                                           toLocation:Context_Contact_ListAction];

        [[NSNotificationCenter defaultCenter] addObserver:self
                                                                   selector:@selector(inspectedObjectDidChange:)
                                                                           name:AIContactInfoInspectorDidChangeInspectedObject
                                                                         object:nil];
}

- (void)dealloc
{
        [contextualMenuItem release]; contextualMenuItem = nil;

        [super dealloc];
}

/*!
 * @brief Tooltip label
 *
 * @result A label, or nil if no tooltip entry should be shown
 */
- (NSString *)labelForObject:(AIListObject *)inObject
{
        if ([inObject conformsToProtocol:@protocol(AIContainingObject)]) {
                return AILocalizedString(@"Contacts",nil);
        }
        
        return nil;
}

/*!
 * @brief Tooltip entry
 *
 * @result The tooltip entry, or nil if no tooltip should be shown
 */
- (NSAttributedString *)entryForObject:(AIListObject *)inObject
{
    NSMutableAttributedString   *entry = nil;
        
        if ([inObject conformsToProtocol:@protocol(AIContainingObject)]) {
        id<AIContainingObject> containingObject = (id<AIContainingObject>)inObject;
                NSArray *listContacts = [containingObject uniqueContainedObjects];
                
                //Only display the contents of a meta if it has more than one contact within it.
                if ([inObject isKindOfClass:[AIListGroup class]] || [listContacts count] > 1) {
                        BOOL                    shouldAppendString = NO;
                        BOOL                    shouldAppendServiceIcon = ([inObject isKindOfClass:[AIMetaContact class]] && ![(AIMetaContact *)inObject containsOnlyOneService]);

                        entry = [[NSMutableAttributedString alloc] init];
            NSMutableString     *entryString = [entry mutableString];
                        
            NSUInteger count = 0;
                        for (AIListContact *contact in listContacts) {
                if ([inObject isKindOfClass:[AIListGroup class]] && 
                    ![[AIContactHidingController sharedController] visibilityOfListObject:contact inContainer:containingObject]) {
                    continue;
                }
                                
                                NSImage *statusIcon, *serviceIcon;
                                
                                if (shouldAppendString) {
                                        [entryString appendString:@"\r"];
                                } else {
                                        shouldAppendString = YES;
                                }
                                
                // If there are a lot of contacts, just stop.
                if (++count >= MAX_CONTACTS) {
                    [entryString appendString:[NSString stringWithFormat:MORE_CONTACTS_STRING, listContacts.count - MAX_CONTACTS]];
                    break;
                }                
                
                                statusIcon = [[AIStatusIcons statusIconForListObject:contact
                                                                                                                                type:AIStatusIconTab
                                                                                                                   direction:AIIconNormal] imageByScalingToSize:META_TOOLTIP_ICON_SIZE];
                                
                                if (statusIcon) {
                                        NSTextAttachment                *attachment;
                                        NSTextAttachmentCell    *cell;
                                                
                                        cell = [[NSTextAttachmentCell alloc] init];
                                        [cell setImage:statusIcon];
                                        
                                        attachment = [[NSTextAttachment alloc] init];
                                        [attachment setAttachmentCell:cell];
                                        [cell release];

                                        [entry appendAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]];
                                        [attachment release];

                                        [entryString appendString:@" "];
                                }
                                
                if ([inObject isKindOfClass:[AIMetaContact class]]) {
                    [entryString appendString:contact.formattedUID];
                } else if ([inObject isKindOfClass:[AIListGroup class]]) {
                    [entryString appendString:contact.displayName];
                }
                                
                                if (shouldAppendServiceIcon) {
                                        serviceIcon = [[AIServiceIcons serviceIconForObject:contact type:AIServiceIconSmall direction:AIIconNormal]
                                                                        imageByScalingToSize:META_TOOLTIP_ICON_SIZE];
                                        if (serviceIcon) {
                                                NSTextAttachment                *attachment;
                                                NSTextAttachmentCell    *cell;
                                                
                                                cell = [[NSTextAttachmentCell alloc] init];
                                                [cell setImage:serviceIcon];
                                                
                                                attachment = [[NSTextAttachment alloc] init];
                                                [attachment setAttachmentCell:cell];
                                                [cell release];

                                                [entryString appendString:@" "];
                                                [entry appendAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]];
                                                [attachment release];
                                        }
                                }
                        }
                }
        }
    
    return [entry autorelease];
}

- (BOOL)shouldDisplayInContactInspector
{
        return YES;
}

#pragma mark Automatic temporary expansion
- (void)inspectedObjectDidChange:(NSNotification *)inNotification
{
        AIListObject *oldListObject = [[inNotification userInfo] objectForKey:KEY_PREVIOUS_INSPECTED_OBJECT];
        AIListObject *newListObject = [[inNotification userInfo] objectForKey:KEY_NEW_INSPECTED_OBJECT];

        if (oldListObject && [oldListObject isKindOfClass:[AIMetaContact class]] &&
                [oldListObject boolValueForProperty:@"TemporaryMetaContactExpansion"]) {
                [oldListObject setValue:nil
                                        forProperty:@"TemporaryMetaContactExpansion"
                                                 notify:NotifyNever];
                [[NSNotificationCenter defaultCenter] postNotificationName:AIPerformCollapseItemNotification
                                                                                                  object:oldListObject];
        }

        if (newListObject && [newListObject isKindOfClass:[AIMetaContact class]] &&
                ![(AIMetaContact *)newListObject isExpanded]) {
                [newListObject setValue:[NSNumber numberWithBool:YES]
                                        forProperty:@"TemporaryMetaContactExpansion"
                                                 notify:NotifyNever];
                [[NSNotificationCenter defaultCenter] postNotificationName:AIPerformExpandItemNotification
                                                                                                  object:newListObject];
        }
}

#pragma mark Menu
- (void)toggleMetaContactExpansion:(id)sender
{
        AIListObject *listObject = adium.menuController.currentContextMenuObject;
        if ([listObject isKindOfClass:[AIMetaContact class]]) {
                BOOL currentlyExpanded = [(AIMetaContact *)listObject isExpanded];
                
                if (currentlyExpanded) {
                        [[NSNotificationCenter defaultCenter] postNotificationName:AIPerformCollapseItemNotification
                                                                                                         object:listObject];
                } else {
                        [[NSNotificationCenter defaultCenter] postNotificationName:AIPerformExpandItemNotification
                                                                                                         object:listObject];
                }
        }
}

- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
{
        AIListObject *listObject = adium.menuController.currentContextMenuObject;
        return ([listObject isKindOfClass:[AIMetaContact class]] &&
                        [(AIMetaContact *)listObject uniqueContainedObjectsCount] > 1);
}

- (void)menu:(NSMenu *)menu needsUpdateForMenuItem:(NSMenuItem *)menuItem
{
        AIListObject *listObject = adium.menuController.currentContextMenuObject;
        if (menuItem == contextualMenuItem) {
                if ([listObject isKindOfClass:[AIMetaContact class]] &&
                        [(AIMetaContact *)listObject uniqueContainedObjectsCount] > 1) {
                        BOOL currentlyExpanded = [(AIMetaContact *)listObject isExpanded];
                        
                        if (currentlyExpanded) {
                                [menuItem setTitle:COLLAPSE_CONTACT];
                        } else {
                                [menuItem setTitle:EXPAND_CONTACT];                             
                        }
                } else {
                        [menuItem setTitle:EXPAND_CONTACT];
                }
        }
}

@end