// // BPIconManager.m // Piles3 // // Created by Bea on 14/07/05. // Copyright 2005 __MyCompanyName__. All rights reserved. // #import "BPIconManager.h" #import "BPDocument.h" #import "BPNoteDocument.h" #import "BPEmailDocument.h" #import "BPPerson.h" #import "BPUtil.h" @implementation BPIconManager + (NSImage *)iconForFileType:(NSString *)fileType { // should return a cached icon if it's previously been fetched NSImage *img = [[NSWorkspace sharedWorkspace] iconForFileType:fileType]; return img; } + (void)initialize { cachedIcons = [[NSMutableDictionary alloc] initWithCapacity:20]; // load base, overlay icons and set image names for them ICON_PROP_BASE_ICON = [[NSNumber alloc] initWithShort:1]; ICON_PROP_SENDER = [[NSNumber alloc] initWithShort:2]; ICON_PROP_OVERLAY = [[NSNumber alloc] initWithShort:3]; ICON_PROP_ATTACHMENTS = [[NSNumber alloc] initWithShort:4]; // these are the names of the corresponding icons in the project "Resources" BASE_ICON_NOTE = @"note"; BASE_ICON_EMAIL_GENERAL_READ = @"email_general"; BASE_ICON_EMAIL_GENERAL_UNREAD = @"email_general_unread"; BASE_ICON_EMAIL_LISTSERV_READ = @"email_listserv"; BASE_ICON_EMAIL_LISTSERV_UNREAD = @"email_listserv_unread"; BASE_ICON_EMAIL_DRAFT = @"email_draft"; BASE_ICON_EMAIL_SENT = @"email_sent"; OVERLAY_REPLY = @"email_overlay_re"; OVERLAY_FORWARD = @"email_overlay_fwd"; PAPER_CLIP = @"overlay_paperclip"; } + (NSString *)baseIconNameForDocument:(NSObject *)document { BOOL isDraftOrSentEmail = ([document isKindOfClass:[BPEmailDocument class]] && [[document author] isEqual:[BPPerson personThatsMe]]); if ([document isKindOfClass:[BPNoteDocument class]]) { return BASE_ICON_NOTE; } else if (isDraftOrSentEmail) { if ([document isEditable]) return BASE_ICON_EMAIL_DRAFT; else return BASE_ICON_EMAIL_SENT; } // if we're here, document must be a received email // find out if this is a "listserv" email if (![document isKindOfClass:[BPEmailDocument class]]) { NSLog(@"baseIconNameForDocument error: %@ not BPEmailDocument", document); return; } BPEmailDocument *email = (BPEmailDocument *)document; BOOL iAmListedRecipient = ([[email toRecipients] containsObject:[BPPerson personThatsMe]] || [[email ccRecipients] containsObject:[BPPerson personThatsMe]]); if (!iAmListedRecipient && ![[email author] isInAddressBook]) { //NSLog(@"[[BPPerson personThatsMe] address] %@ %@", [[BPPerson personThatsMe] address], [email toRecipients]); if ([email hasBeenRead]) return BASE_ICON_EMAIL_LISTSERV_READ; else return BASE_ICON_EMAIL_LISTSERV_UNREAD; } else { if ([email hasBeenRead]) return BASE_ICON_EMAIL_GENERAL_READ; else return BASE_ICON_EMAIL_GENERAL_UNREAD; } } + (NSString *)overlayIconNameForDocument:(NSObject *)document { if (![document isKindOfClass:[BPEmailDocument class]]) return nil; NSRange replyPrefix = ([[document label] rangeOfString:@"Re:" options:NSCaseInsensitiveSearch]); if (replyPrefix.length != 0) return OVERLAY_REPLY; NSRange forwardPrefix = ([[document label] rangeOfString:@"Fw:" options:NSCaseInsensitiveSearch]); NSRange forwardPrefix2 = ([[document label] rangeOfString:@"Fwd:" options:NSCaseInsensitiveSearch]); if (forwardPrefix.length != 0 || forwardPrefix2.length != 0) return OVERLAY_FORWARD; return nil; } + (NSDictionary *)iconPropsForDocument:(NSObject *)document { NSMutableDictionary *iconProps = [NSMutableDictionary dictionaryWithCapacity:5]; // special properties if this is a received email if ([document isKindOfClass:[BPEmailDocument class]] && ![[document author] isEqual:[BPPerson personThatsMe]]) { NSString *senderIconName = [[document author] image] == nil? nil : [[[document author] image] name]; if (senderIconName != nil) { [iconProps setObject:senderIconName forKey:ICON_PROP_SENDER]; } } NSString *overlayIconName = [BPIconManager overlayIconNameForDocument:document]; if (overlayIconName != nil) { [iconProps setObject:overlayIconName forKey:ICON_PROP_OVERLAY]; } // set the base icon property [iconProps setObject:[BPIconManager baseIconNameForDocument:document] forKey:ICON_PROP_BASE_ICON]; // set the attachments property // if feature including attachments thumbnails as icons is implemented, use // the thumbnail as the image name instead of PAPER_CLIP if ([[document attachments] count] > 0) { [iconProps setObject:PAPER_CLIP forKey:ICON_PROP_ATTACHMENTS]; } return iconProps; } + (void)compositeImage:(NSImage *)sourceImg ontoImage:(NSImage *)destImg opacity:(float)opacity resize:(BOOL)resize fromTopLeft:(BOOL)fromTopLeft { NSRect sourceRect; sourceRect.origin = NSZeroPoint; sourceRect.size = [sourceImg size]; NSRect destRect; destRect.size = [sourceImg size]; if (resize) { float wantedWidth = [destImg size].width; if (destRect.size.width > wantedWidth) { destRect.size.width = [BPUtil scaleSize:destRect.size toWidth:wantedWidth].width; } /* float wantedHeight = [destImg size].height; if (destRect.size.height > wantedHeight) { //destRect.size = [BPUtil scaleSize:destRect.size toHeight:wantedHeight]; //destRect.size.width = [sourceImg size].width; } */ } destRect.origin.x = 0; destRect.origin.y = 0; if (fromTopLeft) { //if ([[sourceImg name] rangeOfString:@"http://"].length > 0) { // // don't draw favicons from top left if ([sourceImg size].width < [destImg size].width) { // centre the source image on the dest image destRect.origin.x = [destImg size].width/2 - [sourceImg size].width/2; destRect.origin.y = [destImg size].height/2 - [sourceImg size].height/2; } else { // so you start drawing from the top part of the image, not from the // bottom part (i.e. make sure you can see top part of image) sourceRect.origin.y = sourceRect.size.height - [destImg size].height; } } // composite the source image onto the dest image [destImg lockFocus]; [sourceImg drawInRect:destRect fromRect:sourceRect operation:NSCompositeSourceAtop fraction:opacity]; [destImg unlockFocus]; } + (NSImage *)buildIconFromIconProps:(NSDictionary *)props { //NSLog(@"iconProps %@", props); NSImage *baseIcon = [NSImage imageNamed:[props objectForKey:ICON_PROP_BASE_ICON]]; NSImage *icon = [[baseIcon copy] autorelease]; [icon setCacheDepthMatchesImageDepth:YES]; // otherwise won't encode base icon properly in KeyedArchiver if ([props objectForKey:ICON_PROP_SENDER] != nil) { // composite sender icon onto base icon [BPIconManager compositeImage:[NSImage imageNamed:(NSString *)[props objectForKey:ICON_PROP_SENDER]] ontoImage:icon opacity:1.0 resize:YES fromTopLeft:YES]; } if ([props objectForKey:ICON_PROP_OVERLAY] != nil) { // composite overlay icon onto base icon [BPIconManager compositeImage:[NSImage imageNamed:(NSString *)[props objectForKey:ICON_PROP_OVERLAY]] ontoImage:icon opacity:0.6 resize:NO fromTopLeft:NO]; } if ([props objectForKey:ICON_PROP_ATTACHMENTS] != nil) { NSImage *sourceImg = [NSImage imageNamed:(NSString *)[props objectForKey:ICON_PROP_ATTACHMENTS]]; NSRect sourceRect; sourceRect.origin = NSZeroPoint; sourceRect.size = [sourceImg size]; // draw paperclip at bottom-right corner of icon NSPoint drawPoint; drawPoint.x = [icon size].width - sourceRect.size.width; drawPoint.y = 0; // move paperclip to left a bit if it's the draft/sent icons if ([[props objectForKey:ICON_PROP_BASE_ICON] isEqual:BASE_ICON_EMAIL_DRAFT] || [[props objectForKey:ICON_PROP_BASE_ICON] isEqual:BASE_ICON_EMAIL_SENT]) { drawPoint.x -= 5; } [icon lockFocus]; [sourceImg drawAtPoint:drawPoint fromRect:sourceRect operation:NSCompositeSourceOver fraction:1.0]; [icon unlockFocus]; } if ([icon isValid]) { // cache icon so I can reuse it if another email/note has same properties [cachedIcons setObject:icon forKey:props]; return icon; } else { NSLog(@"Error: built icon is not valid"); return baseIcon; } } + (NSImage *)iconForDocument:(NSObject *)document { NSDictionary *iconProps = [BPIconManager iconPropsForDocument:document]; // probably can't just see if iconProps is in the cachedIcons keys because // that probably uses isEqual not isEqualToDictionary NSEnumerator *enumerator = [cachedIcons keyEnumerator]; NSDictionary *currentIconProps; while ((currentIconProps = [enumerator nextObject]) != nil) { if ([currentIconProps isEqualToDictionary:iconProps]) { return [cachedIcons objectForKey:iconProps]; } } // can't find a cached icon, build a new one return [BPIconManager buildIconFromIconProps:iconProps]; } @end