Plugins/Dock Icon Badging/AIDockBadger.m
changeset 3670 add0c83648a5
parent 3669 7905c08bc4d8
child 3671 855cbc039a80
equal deleted inserted replaced
3669:7905c08bc4d8 3670:add0c83648a5
     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  */
       
    16 
       
    17 #import "AIDockBadger.h"
       
    18 #import <Adium/AIChatControllerProtocol.h>
       
    19 #import <Adium/AIContactControllerProtocol.h>
       
    20 #import <Adium/AIContentControllerProtocol.h>
       
    21 #import <Adium/AIStatusControllerProtocol.h>
       
    22 #import "AIDockController.h"
       
    23 #import <Adium/AIInterfaceControllerProtocol.h>
       
    24 #import <Adium/AIContactAlertsControllerProtocol.h>
       
    25 #import <AIUtilities/AIColorAdditions.h>
       
    26 #import <AIUtilities/AIDictionaryAdditions.h>
       
    27 #import <AIUtilities/AIParagraphStyleAdditions.h>
       
    28 #import <AIUtilities/AIArrayAdditions.h>
       
    29 #import <AIUtilities/AIImageAdditions.h>
       
    30 #import <Adium/AIAbstractListController.h>
       
    31 #import <Adium/AIAccount.h>
       
    32 #import <Adium/AIChat.h>
       
    33 #import <Adium/AIIconState.h>
       
    34 
       
    35 @interface AIDockBadger ()
       
    36 - (void)removeOverlay;
       
    37 - (void)_setOverlay;
       
    38 @end
       
    39 
       
    40 @implementation AIDockBadger
       
    41 
       
    42 #pragma mark Birth and death
       
    43 
       
    44 /*!
       
    45  * @brief Install
       
    46  */
       
    47 - (void)installPlugin
       
    48 {
       
    49 	overlayState = nil;
       
    50 
       
    51 	//Register our default preferences
       
    52     [adium.preferenceController registerDefaults:[NSDictionary dictionaryNamed:@"BadgerDefaults"
       
    53 																		forClass:[self class]] 
       
    54 										  forGroup:PREF_GROUP_APPEARANCE];
       
    55 
       
    56 	//Observe pref changes
       
    57 	[adium.preferenceController registerPreferenceObserver:self forGroup:PREF_GROUP_APPEARANCE];
       
    58 	
       
    59 	// Register as an observer of the status preferences for unread conversation count
       
    60 	[adium.preferenceController registerPreferenceObserver:self
       
    61 													forGroup:PREF_GROUP_STATUS_PREFERENCES];
       
    62 }
       
    63 
       
    64 /*!
       
    65  * @brief Uninstall
       
    66  */
       
    67 - (void)uninstallPlugin
       
    68 {
       
    69 	[adium.chatController unregisterChatObserver:self];
       
    70 	[[NSNotificationCenter defaultCenter] removeObserver:self];
       
    71 	[adium.preferenceController unregisterPreferenceObserver:self];
       
    72 }
       
    73 
       
    74 #pragma mark Signals to update
       
    75 
       
    76 /*!
       
    77  * @brief Update our overlay when a chat updates with a relevant key
       
    78  */
       
    79 - (NSSet *)updateChat:(AIChat *)inChat keys:(NSSet *)inModifiedKeys silent:(BOOL)silent
       
    80 {
       
    81 	if (!inModifiedKeys || [inModifiedKeys containsObject:KEY_UNVIEWED_CONTENT]) {
       
    82 		[self performSelector:@selector(_setOverlay)
       
    83 				   withObject:nil
       
    84 				   afterDelay:0];
       
    85 	}
       
    86 	
       
    87 	return nil;
       
    88 }
       
    89 
       
    90 /*!
       
    91  * @brief Update our overlay when a chat closes
       
    92  */
       
    93 - (void)chatClosed:(NSNotification *)notification
       
    94 {	
       
    95 	[self performSelector:@selector(_setOverlay)
       
    96 			   withObject:nil
       
    97 			   afterDelay:0];
       
    98 }
       
    99 
       
   100 #pragma mark Preference observing
       
   101 - (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
       
   102 							object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
       
   103 {
       
   104 	if ([group isEqualToString:PREF_GROUP_APPEARANCE] && (!key || [key isEqualToString:KEY_BADGE_DOCK_ICON])) {
       
   105 		BOOL	newShouldBadge = [[prefDict objectForKey:KEY_BADGE_DOCK_ICON] boolValue];
       
   106 		if (newShouldBadge != shouldBadge) {
       
   107 			shouldBadge = newShouldBadge;
       
   108 			
       
   109 			if (shouldBadge) {
       
   110 				//Register as a chat observer (for unviewed content). If there is any unviewed content, our overlay will be set.
       
   111 				[adium.chatController registerChatObserver:self];
       
   112 				
       
   113 				[[NSNotificationCenter defaultCenter] addObserver:self
       
   114 											   selector:@selector(chatClosed:)
       
   115 												   name:Chat_WillClose
       
   116 												 object:nil];
       
   117 			} else {
       
   118 				//Remove any existing overlay
       
   119 				[self removeOverlay];
       
   120 				
       
   121 				//Stop observing
       
   122 				[adium.chatController unregisterChatObserver:self];
       
   123 				[[NSNotificationCenter defaultCenter] removeObserver:self];
       
   124 			}
       
   125 		}
       
   126 	}
       
   127 	
       
   128 	if ([group isEqualToString:PREF_GROUP_STATUS_PREFERENCES]) {
       
   129 		showConversationCount = [[prefDict objectForKey:KEY_STATUS_CONVERSATION_COUNT] boolValue];
       
   130 		
       
   131 		[self _setOverlay];
       
   132 	}
       
   133 }	
       
   134 
       
   135 #pragma mark Work methods
       
   136 
       
   137 - (NSImage *)numberedBadge:(NSInteger)count
       
   138 {
       
   139 	if(!badgeTwoDigits) {
       
   140 		badgeTwoDigits = [[NSImage imageNamed:@"newContentTwoDigits"] retain];
       
   141 		badgeThreeDigits = [[NSImage imageNamed:@"newContentThreeDigits"] retain];
       
   142 	}
       
   143 
       
   144 	NSImage		*badge = nil, *badgeToComposite = nil;
       
   145 	NSString	*numString = nil;
       
   146 
       
   147 	//999 unread messages should be enough for anyone
       
   148 	if (count >= 1000) {
       
   149 		count = 999;
       
   150 	}
       
   151 
       
   152 	badgeToComposite = ((count < 10) ? badgeTwoDigits : badgeThreeDigits);
       
   153 	numString = [[NSNumber numberWithInteger:count] description];
       
   154 
       
   155 	NSRect rect = { NSZeroPoint, [badgeToComposite size] };
       
   156 	NSFont *font = [NSFont fontWithName:@"Helvetica-Bold" size:24];
       
   157 	
       
   158 	if (!font) font = [NSFont systemFontOfSize:24];
       
   159 	
       
   160 	NSDictionary *atts = [NSDictionary dictionaryWithObjectsAndKeys:
       
   161 		[NSColor whiteColor], NSForegroundColorAttributeName,
       
   162 		font, NSFontAttributeName,
       
   163 		nil];
       
   164 	
       
   165 	NSSize numSize = [numString sizeWithAttributes:atts];
       
   166 	rect.origin.x = (rect.size.width / 2) - (numSize.width / 2);
       
   167 	rect.origin.y = (rect.size.height / 2) - (numSize.height / 2);
       
   168 
       
   169 	badge = [[NSImage alloc] initWithSize:rect.size];
       
   170 	[badge setFlipped:YES];
       
   171 	[badge lockFocus];
       
   172 	[badgeToComposite compositeToPoint:NSMakePoint(0, rect.size.height) operation:NSCompositeSourceOver];
       
   173 
       
   174 	[numString drawInRect:rect
       
   175 		   withAttributes:atts];
       
   176 	
       
   177 	[badge unlockFocus];
       
   178 		
       
   179 	return [badge autorelease];
       
   180 }
       
   181 
       
   182 /*!
       
   183  * @brief Remove any existing dock overlay
       
   184  */
       
   185 - (void)removeOverlay
       
   186 {
       
   187 	if (overlayState) {
       
   188 		[adium.dockController removeIconStateNamed:@"UnviewedContentCount"];
       
   189 		[overlayState release]; overlayState = nil;
       
   190 	}
       
   191 }
       
   192 
       
   193 /*!
       
   194  * @brief Update our overlay to the current unviewed content count
       
   195  */
       
   196 - (void)_setOverlay
       
   197 {
       
   198 	NSInteger contentCount = (showConversationCount ?
       
   199 					   [adium.chatController unviewedConversationCount] : [adium.chatController unviewedContentCount]);
       
   200 
       
   201 	if (contentCount != lastUnviewedContentCount) {
       
   202 		//Remove & release the current overlay state
       
   203 		[self removeOverlay];
       
   204 
       
   205 		//Create & set the new overlay state
       
   206 		if (contentCount > 0) {
       
   207 			//Set the state
       
   208 			overlayState = [[AIIconState alloc] initWithImage:[self numberedBadge:contentCount] 
       
   209 													  overlay:YES];
       
   210 			[adium.dockController setIconState:overlayState named:@"UnviewedContentCount"];
       
   211 		}
       
   212 
       
   213 		lastUnviewedContentCount = contentCount;
       
   214 	}
       
   215 }
       
   216 
       
   217 @end