// // BPEmailDocument.m // Attempt2 // // Created by Bea on 10/05/05. // Copyright 2005 __MyCompanyName__. All rights reserved. // #import "BPEmailDocument.h" #import "BPPerson.h" #import "BPMailAppData.h" #import #import @interface NSForm (BPLinkedForm) - (void)mouseDown:(NSEvent *)theEvent; @end @implementation NSForm (BPLinkedForm) - (void)mouseDown:(NSEvent *)theEvent { int row, col; NSPoint mouseLoc = [self convertPoint:[theEvent locationInWindow] fromView:nil]; if ([self getRow:&row column:&col forPoint:mouseLoc]) { NSCell *cell = [self cellAtRow:row column:col]; NSFont *cellFont = [cell font]; if ([cell isEditable]) { [super mouseDown:theEvent]; } else { NSAttributedString *currentAttrStr = [cell attributedStringValue]; NSDictionary *attrDict = [currentAttrStr attributesAtIndex:0 effectiveRange:NULL]; NSAttributedString *attrStr = [[[NSAttributedString alloc] initWithString:[currentAttrStr string] attributes:attrDict] autorelease]; if ([attrDict objectForKey:NSLinkAttributeName] == nil) { [super mouseDown:theEvent]; } else { [super mouseDown:theEvent]; // doesn't work. doesn't tell me if I'm clicking in whitespace and not in the text. //NSPoint p = [[self window] convertBaseToScreen:[theEvent locationInWindow]]; //NSLog(@"[[self currentEditor] characterIndexForPoint:p] %d", [[self currentEditor] characterIndexForPoint:p]); //if ([[self currentEditor] characterIndexForPoint:p] == NSNotFound) // return; NSNotification *notif; NSDictionary *details; NSObject *doc = [[self delegate] document]; if ([doc conformsToProtocol:@protocol(BPDocument)]) { if ([[cell title] isEqual:FORM_TITLE_FROM]) { details = [NSDictionary dictionaryWithObjectsAndKeys: doc, @"Document", @"Author", @"Query", nil]; } else if ([[cell title] isEqual:FORM_TITLE_SUBJECT]) { details = [NSDictionary dictionaryWithObjectsAndKeys: doc, @"Document", @"Label", @"Query", nil]; } // post the notif to do a search notif = [NSNotification notificationWithName:@"Search" object:nil userInfo:details]; [[NSNotificationCenter defaultCenter] postNotification:notif]; // can call - (unsigned int)characterIndexForPoint:(NSPoint)thePoint // which is in protocol that NSTextView implements } } } [cell setFont:cellFont]; } } @end /*- (void)resetCursorRects { NSLog(@"resetCursorRects"); [super resetCursorRects]; NSArray *cells = [self cells]; NSCell *cell; int i; for (i=0; i<[cells count]; i++) { cell = [cells objectAtIndex:i]; if ([[cell title] isEqual:FORM_TITLE_FROM] || [[cell title] isEqual:FORM_TITLE_SUBJECT]) { int row, col; if ([self getRow:&row column:&col ofCell:cell]) { [self addCursorRect:[self cellFrameAtRow:row column:col] cursor:[NSCursor pointingHandCursor]]; } } } } */ // Encapsulates view data for an EMAIL document, extends BPDocumentView. @interface BPEmailDocumentView : BPDocumentView {} @end @implementation BPEmailDocumentView + (void)initialize { FORM_TITLE_FROM = @"From"; FORM_TITLE_TO = @"To"; FORM_TITLE_CC = @"CC"; FORM_TITLE_SUBJECT = @"Subject"; FORM_TITLE_DATE = @"Date"; dateFormatString = @"%e %b %y %I:%M%p"; formFont = [NSFont labelFontOfSize:12]; } - (void)linkString:(NSString *)str inCell:(NSCell *)cell { [cell setAllowsEditingTextAttributes:YES]; NSDictionary *d = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:NSSingleUnderlineStyle], NSUnderlineStyleAttributeName, [NSColor blueColor], NSForegroundColorAttributeName, self, NSLinkAttributeName, nil]; NSAttributedString *attrStr = [[[NSAttributedString alloc] initWithString:str attributes:d] autorelease]; [cell setAttributedStringValue:attrStr]; } // the 'isMicro' was hacked in to do the micro views - (NSForm *)headersAsMicro:(BOOL)isMicro { BPEmailDocument *doc = (BPEmailDocument *)document; // if doc is editable, use headers-forms even if turned off in preferences if (![[NSUserDefaults standardUserDefaults] boolForKey:@"MailStacker_headersAsForm"] && ![doc isEditable]) { return [[[NSForm alloc] initWithFrame:NSZeroRect] autorelease]; } NSForm *form = [[[NSForm alloc] initWithFrame:NSMakeRect(0,0,100,100)] autorelease]; [form setDelegate:self]; //NSLog(@"set delegate %@", [form delegate]); [form setTitleFont:formFont]; [form setTextFont:formFont]; NSSize cellSize = [form cellSize]; if (!isMicro) { // need bigger height for bezel on form cellSize.height *= 1.4; [form setInterlineSpacing:1]; } else { cellSize.height *= 1.1; [form setInterlineSpacing:0]; } [form setCellSize:cellSize]; // add form cells & set their values NSFormCell *cell; // add "From" (don't add if this is an email I'm writing - i.e. it's editable) if (![doc isEditable]) { cell = [form addEntry:FORM_TITLE_FROM]; [self linkString:[[doc author] summary] inCell:cell]; } //[form setTarget:self]; //[form setDoubleAction:@selector(clickedLink:)]; // add "Date" if (![doc isEditable]) { cell = [form addEntry:FORM_TITLE_DATE]; [cell setStringValue:[[doc date] descriptionWithCalendarFormat:dateFormatString timeZone:nil locale:nil]]; } // add "To" if ([doc isEditable] || (![doc isEditable] && !isMicro)) { cell = [form addEntry:FORM_TITLE_TO]; [cell setStringValue:[BPPerson summaryForPersons:[doc toRecipients]]]; } // add "CC" if there are any if (!isMicro && ([[doc ccRecipients] count] > 0 || [doc isEditable])) { cell = [form addEntry:FORM_TITLE_CC]; [cell setStringValue:[BPPerson summaryForPersons:[doc ccRecipients]]]; } // add "Subject" cell = [form addEntry:FORM_TITLE_SUBJECT]; [cell setStringValue:[doc label]]; [self linkString:[doc label] inCell:cell]; [cell setFont:[NSFont boldSystemFontOfSize:12]]; // set form attributes [form setScrollable:YES]; // set attributes for all cells int i; for (i=0; i<[[form cells] count]; i++) { cell = [[form cells] objectAtIndex:i]; if (![doc isEditable] || [[cell title] isEqual:FORM_TITLE_DATE]) { [cell setEditable:NO]; [cell setSelectable:YES]; } } // size the form [form sizeToFit]; // set form to auto-resize when its superview resizes [form setAutoresizingMask:(NSViewMinXMargin | NSViewWidthSizable | NSViewMaxXMargin | NSViewMaxYMargin)]; [form setBordered:NO]; [form setBezeled:!isMicro]; return form; } // override generation of "headers" view - (NSForm *)headersView { return [self headersAsMicro:NO]; } - (NSForm *)microHeadersView { return [self headersAsMicro:YES]; } - (void)saveHeaders:(NSControl *)control { if (![control isKindOfClass:[NSForm class]]) return; NSForm *headers = (NSForm *)control; NSFormCell *cell; int i; for (i=0; i<[[headers cells] count]; i++) { cell = [[headers cells] objectAtIndex:i]; if ([[cell title] isEqual:FORM_TITLE_TO]) { // get to NSArray *toPersons = [BPPerson personsWithFormattedAddressesString:[cell stringValue]]; [document setValue:toPersons forKey:@"toRecipients"]; } else if ([[cell title] isEqual:FORM_TITLE_CC]) { // get cc NSArray *ccPersons = [BPPerson personsWithFormattedAddressesString:[cell stringValue]]; [document setValue:ccPersons forKey:@"ccRecipients"]; } else if ([[cell title] isEqual:FORM_TITLE_SUBJECT]) { // get subject NSString *subject = [cell stringValue]; [document setValue:subject forKey:@"label"]; } } } @end // ---------------------------------------- // ---------------------------------------- @implementation BPEmailDocument - (id)initWithSender:(BPPerson *)sender date:(NSDate *)aDate to:(NSArray *)to replyTo:(BPPerson *)replyTo cc:(NSArray *)cc subject:(NSString *)emailSubject body:(NSString *)emailBody attachments:(NSArray *)emailAttachments { [super initWithAuthor:sender date:aDate label:emailSubject body:emailBody attachments:emailAttachments]; /* // store message [msg retain]; message = msg; */ // store to [to retain]; toRecipients = to; // store cc [cc retain]; ccRecipients = cc; replyToPerson = [replyTo retain]; contentAttrString = nil; return self; } - (id)initWithMessage:(Message *)msg { // get attributes BPPerson *sender = [BPPerson personWithFormattedAddress:(NSString *)[msg sender]]; BPPerson *replyTo = [BPPerson personWithFormattedAddress:(NSString *)[msg replyTo]]; NSArray *to = [BPMailAppData personsFromAddressees:[msg recipients]]; NSArray *cc = [BPMailAppData personsFromAddressees:[msg ccRecipients]]; NSArray *msgAttachments = [BPMailAppData attachmentsFromNSTextAttachments:[[msg messageBody] attachments]]; BPEmailDocument *email = [self initWithSender:sender date:[msg dateReceived] to:to replyTo:replyTo cc:cc subject:[msg subject] body:[[msg content] string] attachments:msgAttachments]; // set other attributes that are readable from the Message object // attributed string if ([[msg messageBody] isHTML]) { NSString *html = [[[msg messageBody] textHtmlPart] decodeText]; NSAttributedString *attrStr = [[NSAttributedString alloc] initWithString:html]; [email setContentAsHTML:attrStr]; } else { [email setContentAsAttributedString:[msg attributedString]]; } // colour if (![[msg color] isEqual:[NSColor whiteColor]]) [email setColourCode:[msg color]]; // flagged [email setFlagged:[msg isFlagged]]; // read [email setHasBeenRead:[msg isRead]]; return email; } + (id)emailDocumentWithMessage:(Message *)msg { return [[[BPEmailDocument alloc] initWithMessage:msg] autorelease]; } - (void)setContentAsAttributedString:(NSAttributedString *)attrString { [attrString retain]; [contentAttrString release]; contentAttrString = attrString; } - (NSAttributedString *)contentAsAttributedString { return contentAttrString; } - (void)setContentAsHTML:(NSAttributedString *)attrString { [attrString retain]; [contentHTML release]; contentHTML = attrString; } - (NSAttributedString *)contentAsHTML { return contentHTML; } - (NSArray *)toRecipients { return toRecipients; } - (NSArray *)ccRecipients { return ccRecipients; } - (BPPerson *)replyTo { return replyToPerson; } // override setupView to use BPEmailDocumentView instead of BPDocumentView for // generating views - (void)setupView { BPDocumentView *newView = [[[BPEmailDocumentView alloc] initWithDocument:self] retain]; [view release]; view = newView; } /* - (NSView *)freshContentView { BPEmailDocumentView *freshView = [[[BPEmailDocumentView alloc] initWithDocument:self] autorelease]; return [freshView contentView]; } */ - (void)setHasBeenRead:(BOOL)beenRead { hasBeenRead = beenRead; } - (BOOL)hasBeenRead { return hasBeenRead; } - (NSString *)labelWithoutPrefix { NSRange reRange = [label rangeOfString:@"re: " options:NSCaseInsensitiveSearch]; if (reRange.length != 0) return [label substringFromIndex:reRange.length]; NSRange fwRange = [label rangeOfString:@"fw: " options:NSCaseInsensitiveSearch]; if (fwRange.length != 0) return [label substringFromIndex:fwRange.length]; NSRange fwdRange = [label rangeOfString:@"fwd: " options:NSCaseInsensitiveSearch]; if (fwdRange.length != 0) return [label substringFromIndex:fwdRange.length]; return label; } - (NSString *)allPersonsSummaries { NSString *summarySoFar = [author summary]; BPPerson *p; int i; for (i=0; i<[toRecipients count]; i++) { summarySoFar = [summarySoFar stringByAppendingString:[[toRecipients objectAtIndex:i] summary]]; } for (i=0; i<[ccRecipients count]; i++) { summarySoFar = [summarySoFar stringByAppendingString:[[ccRecipients objectAtIndex:i] summary]]; } return summarySoFar; } - (BOOL)isDirectCorrespondenceWithPerson:(BPPerson *)person { if ([person isEqual:[BPPerson personThatsMe]]) { if ([toRecipients containsObject:person]) return YES; } else if ([author isEqual:person]) { if ([toRecipients containsObject:[BPPerson personThatsMe]]) return YES; } return NO; } - (NSString *)description { return [NSString stringWithFormat:@"from <%@> to <%@> subject <%@>", author, toRecipients, label]; } - (void)dealloc { NSLog(@"Destroying %@", self); [toRecipients release]; [ccRecipients release]; [replyToPerson release]; [contentAttrString release]; [contentHTML release]; [super dealloc]; } #pragma - #pragma archival - (void)encodeWithCoder:(NSCoder *)coder { [super encodeWithCoder:coder]; [coder encodeObject:toRecipients forKey:@"toRecipients"]; [coder encodeObject:ccRecipients forKey:@"ccRecipients"]; [coder encodeObject:replyToPerson forKey:@"replyToPerson"]; [coder encodeObject:contentAttrString forKey:@"contentAttrString"]; [coder encodeObject:contentHTML forKey:@"contentHTML"]; [coder encodeBool:hasBeenRead forKey:@"hasBeenRead"]; } - (id)initWithCoder:(NSCoder *)coder { [super initWithCoder:coder]; toRecipients = [[coder decodeObjectForKey:@"toRecipients"] retain]; ccRecipients = [[coder decodeObjectForKey:@"ccRecipients"] retain]; replyToPerson = [[coder decodeObjectForKey:@"replyToPerson"] retain]; [self setContentAsAttributedString:[coder decodeObjectForKey:@"contentAttrString"]]; [self setContentAsHTML:[coder decodeObjectForKey:@"contentHTML"]]; [self setHasBeenRead:[coder decodeBoolForKey:@"hasBeenRead"]]; return self; } @end