#import "AIDockNameOverlay.h" #import "AIDockController.h" #import <AIUtilities/AIParagraphStyleAdditions.h> #import <AIUtilities/AIImageAdditions.h> #import <Adium/AIAccount.h> #import <AIUtilities/AIArrayAdditions.h> #import <Adium/AIAbstractListController.h> #import <AIUtilities/AIColorAdditions.h> #import <AIUtilities/AIBezierPathAdditions.h> #define DOCK_OVERLAY_ALERT_SHORT AILocalizedString(@"Display name in the dock icon",nil) #define DOCK_OVERLAY_ALERT_LONG DOCK_OVERLAY_ALERT_SHORT @interface AIDockNameOverlay () - (void)flushPreferenceColorCache; - (void)removeDockOverlay:(NSTimer *)removeTimer; @implementation AIDockNameOverlay //Install our contact alert [adium.contactAlertsController registerActionID:DOCK_OVERLAY_ALERT_IDENTIFIER withHandler:self]; overlayObjectsArray = [[NSMutableArray alloc] init]; //Register as a contact observer (For signed on / signed off) [[AIContactObserverManager sharedManager] registerListObjectObserver:self]; [adium.chatController registerChatObserver:self]; [adium.preferenceController registerPreferenceObserver:self forGroup:PREF_GROUP_APPEARANCE]; [adium.preferenceController registerPreferenceObserver:self forGroup:PREF_GROUP_LIST_THEME]; - (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime if ([group isEqualToString:PREF_GROUP_LIST_THEME]) { //Grab colors from status coloring plugin's prefs [self flushPreferenceColorCache]; signedOffColor = [[[prefDict objectForKey:KEY_SIGNED_OFF_COLOR] representedColor] retain]; signedOnColor = [[[prefDict objectForKey:KEY_SIGNED_ON_COLOR] representedColor] retain]; unviewedContentColor = [[[prefDict objectForKey:KEY_UNVIEWED_COLOR] representedColor] retain]; backSignedOffColor = [[[prefDict objectForKey:KEY_LABEL_SIGNED_OFF_COLOR] representedColor] retain]; backSignedOnColor = [[[prefDict objectForKey:KEY_LABEL_SIGNED_ON_COLOR] representedColor] retain]; backUnviewedContentColor = [[[prefDict objectForKey:KEY_LABEL_UNVIEWED_COLOR] representedColor] retain]; - (void)flushPreferenceColorCache [signedOffColor release]; signedOffColor = nil; [signedOnColor release]; signedOnColor = nil; [unviewedContentColor release]; unviewedContentColor = nil; [backSignedOffColor release]; backSignedOffColor = nil; [backSignedOnColor release]; backSignedOnColor = nil; [backUnviewedContentColor release]; backUnviewedContentColor = nil; [adium.preferenceController unregisterPreferenceObserver:self]; [adium.chatController unregisterChatObserver:self]; * @brief Short description * @result A short localized description of the action - (NSString *)shortDescriptionForActionID:(NSString *)actionID return DOCK_OVERLAY_ALERT_SHORT; * @brief Long description * @result A longer localized description of the action which should take into account the details dictionary as appropraite. - (NSString *)longDescriptionForActionID:(NSString *)actionID withDetails:(NSDictionary *)details return DOCK_OVERLAY_ALERT_LONG; - (NSImage *)imageForActionID:(NSString *)actionID return [NSImage imageNamed:@"events-dock-alert" forClass:[self class]]; * @result An <tt>AIActionDetailsPane</tt> to use for configuring this action, or nil if no configuration is possible. - (AIActionDetailsPane *)detailsPaneForActionID:(NSString *)actionID * @brief Perform an action * @param actionID The ID of the action to perform * @param listObject The listObject associated with the event triggering the action. It may be nil * @param details If set by the details pane when the action was created, the details dictionary for this particular action * @param eventID The eventID which triggered this action * @param userInfo Additional information associated with the event; userInfo's type will vary with the actionID. - (BOOL)performActionID:(NSString *)actionID forListObject:(AIListObject *)listObject withDetails:(NSDictionary *)details triggeringEventID:(NSString *)eventID userInfo:(id)userInfo BOOL isMessageEvent = [adium.contactAlertsController isMessageEvent:eventID]; if ((chat = [userInfo objectForKey:@"AIChat"]) && (chat != adium.interfaceController.activeChat)) { if (![overlayObjectsArray containsObjectIdenticalTo:chat]) [overlayObjectsArray addObject:chat]; //Wait until the next run loop so that this event is done processing and our unviewed content count is correct [self performSelector:@selector(drawOverlay) //Clear any current timer for this object to have its overlay removed if ((removeTimer = [listObject valueForProperty:@"DockOverlayRemoveTimer"])) [removeTimer invalidate]; //Add a timer to remove this overlay removeTimer = [NSTimer scheduledTimerWithTimeInterval:5 selector:@selector(removeDockOverlay:) [listObject setValue:removeTimer forProperty:@"DockOverlayRemoveTimer" if (![overlayObjectsArray containsObjectIdenticalTo:listObject]) [overlayObjectsArray addObject:listObject]; * @brief Allow multiple actions? * If this method returns YES, every one of this action associated with the triggering event will be executed. * If this method returns NO, only the first will be. - (BOOL)allowMultipleActionsWithID:(NSString *)actionID * @brief When a chat no longer has unviewed content remove it from display - (NSSet *)updateChat:(AIChat *)inChat keys:(NSSet *)inModifiedKeys silent:(BOOL)silent if (inModifiedKeys == nil || [inModifiedKeys containsObject:KEY_UNVIEWED_CONTENT]) { if (![inChat unviewedContentCount]) { [overlayObjectsArray removeObjectIdenticalTo:inChat]; * @brief When an account signs on or off force an overlay update as its contacts statuses may have silently changed - (NSSet *)updateListObject:(AIListObject *)inObject keys:(NSSet *)inModifiedKeys silent:(BOOL)silent if ([inObject isKindOfClass:[AIAccount class]]) { if ([inModifiedKeys containsObject:@"isOnline"]) { for (AIListObject *listObject in [[overlayObjectsArray copy] autorelease]) { if (([listObject respondsToSelector:@selector(account)]) && ([(id)listObject account] == inObject) && ([overlayObjectsArray containsObjectIdenticalTo:listObject])) { [overlayObjectsArray removeObjectIdenticalTo:listObject]; if (madeChanges) [self drawOverlay]; - (void)removeDockOverlay:(NSTimer *)removeTimer AIListObject *inObject = [removeTimer userInfo]; [overlayObjectsArray removeObjectIdenticalTo:inObject]; forProperty:@"DockOverlayRemoveTimer" NSParagraphStyle *paragraphStyle; NSImage *image = [[NSImage alloc] initWithSize:NSMakeSize(128, 128)]; top = bottom + iconHeight; //Set up the string details font = [NSFont boldSystemFontOfSize:24.0f]; paragraphStyle = [NSParagraphStyle styleWithAlignment:NSCenterTextAlignment lineBreakMode:NSLineBreakByClipping]; [[NSColor clearColor] set]; NSRectFillUsingOperation(NSMakeRect(0, 0, 128, 128), NSCompositeCopy); //Draw overlays for each contact for (ESObjectWithProperties *object in [overlayObjectsArray reverseObjectEnumerator]) { CGFloat arcRadius, stringInset; NSColor *backColor = nil, *textColor = nil, *borderColor = nil; arcRadius = (iconHeight / 2.0f); stringInset = (iconHeight / 4.0f); pillRect = NSMakeRect(0, bottom, 127, iconHeight); path = [NSBezierPath bezierPathWithRoundedRect:pillRect radius:arcRadius]; [path setLineWidth:((iconHeight/2.0f) * 0.13333f)]; if ([object integerValueForProperty:KEY_UNVIEWED_CONTENT]) { //Unviewed backColor = backUnviewedContentColor; textColor = unviewedContentColor; } else if ([object boolValueForProperty:@"signedOn"]) { //Signed on backColor = backSignedOnColor; textColor = signedOnColor; } else if ([object boolValueForProperty:@"signedOff"]) { //Signed off backColor = backSignedOffColor; textColor = signedOffColor; backColor = [NSColor whiteColor]; textColor = [NSColor blackColor]; //Lighten/Darken the back color slightly if ([backColor colorIsDark]) { backColor = [backColor darkenBy:-0.15f]; borderColor = [backColor darkenBy:-0.3f]; backColor = [backColor darkenBy:0.15f]; borderColor = [backColor darkenBy:0.3f]; //Get the object's display name [object.displayName drawInRect:NSMakeRect(stringInset + 1, bottom - 1, 127 - (stringInset * 2), top - bottom) withAttributes:[NSDictionary dictionaryWithObjectsAndKeys:font, NSFontAttributeName, paragraphStyle, NSParagraphStyleAttributeName, textColor, NSForegroundColorAttributeName, nil]]; //Move up to the next pill top = bottom + iconHeight; [adium.dockController setOverlay:image];