// // BPDocument.m // Attempt2 // // Created by Bea on 9/05/05. // Copyright 2005 __MyCompanyName__. All rights reserved. // #import "WebKit/WebKit.h" #import "BPDocument.h" #import "BPPerson.h" #import "BPAttachment.h" #import "BPAttachmentCell.h" #import "BPEmailDocument.h" #import "BPIconManager.h" // // Custom NSView for displaying the content view of a document. // This is the main view, which encloses the header and body views. // @interface BPDocumentContentView : NSView { // no instance variables } // no new methods @end @implementation BPDocumentContentView // override isFlipped - (BOOL)isFlipped { return YES; } @end // ---------------------------------------- // ---------------------------------------- @implementation BPDocumentView - (id)initWithDocument:(NSObject *)theDocument { [super init]; document = theDocument; return self; } - (NSObject *)document { return document; } - (NSForm *)headersView { NSForm *form = [[NSForm alloc] initWithFrame:NSZeroRect]; return [form autorelease]; } - (NSForm *)microHeadersView { return [self headersView]; } - (NSView *)bodyView { // frame textView to be a bit smaller than the scroll view NSTextView *textView = [[[NSTextView alloc] initWithFrame:NSMakeRect(10,0,180,200)] autorelease]; if ( ![[NSUserDefaults standardUserDefaults] boolForKey:@"MailStacker_headersAsForm"] && [document isKindOfClass:[BPEmailDocument class]] && [(BPEmailDocument *)document contentAsAttributedString] != nil) { // insert text [textView insertText:[(BPEmailDocument *)document contentAsAttributedString]]; } else if ([document isKindOfClass:[BPEmailDocument class]] && [(BPEmailDocument *)document contentAsHTML] != nil && [[NSUserDefaults standardUserDefaults] boolForKey:@"MailStacker_renderHTML"]) { /* @try { [cup fill]; } @catch (NSException *exception) { NSLog(@"main: Caught %@: %@", [exception name], [exception reason]); } @finally { [cup release]; } */ WebView *webView; //@try { webView = [[[WebView alloc] initWithFrame:NSMakeRect(10,0,180,200) frameName:nil groupName:nil] autorelease]; //NSLog(@"[[(BPEmailDocument *)document contentAsHTML] string] %@", [[(BPEmailDocument *)document contentAsHTML] string]); NSString *attrStringVal = [[(BPEmailDocument *)document contentAsHTML] string]; if (attrStringVal != nil) { // make copy in case loadHTMLString modifies it (as it's the backing string) NSString *html = [[attrStringVal copy] autorelease]; [[webView mainFrame] loadHTMLString:(NSString *)html baseURL:nil]; } /* } @catch (NSException *exception) { NSLog(@"Caught webview rendering error %@: %@", [exception name], [exception reason]); webView = [[[NSView alloc] initWithFrame:NSMakeRect(10,0,180,200)] autorelease]; } */ NSScrollView *scrollView = [[[NSScrollView alloc] initWithFrame:NSMakeRect(0,0,200,200)] autorelease]; [scrollView setDocumentView:webView]; [scrollView setHasVerticalScroller:YES]; // must set auto-resizing masks! so that it resizes properly [webView setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin | NSViewHeightSizable | NSViewMaxYMargin)]; [scrollView setAutoresizingMask:(NSViewMinXMargin | NSViewWidthSizable | NSViewHeightSizable | NSViewMaxXMargin | NSViewMaxYMargin)]; return scrollView; } else { // insert text [textView insertText:[document body]]; } //[textView insertText:[document body]]; // set attributes [textView setEditable:[document isEditable]]; [textView setTextContainerInset:NSMakeSize(10,10)]; // this needs to be after sizeToFit call [textView setAllowsUndo:YES]; NSScrollView *scrollView = [[[NSScrollView alloc] initWithFrame:NSMakeRect(0,0,200,200)] autorelease]; [scrollView setDocumentView:textView]; [scrollView setHasVerticalScroller:YES]; // must set auto-resizing masks! so that it resizes properly [textView setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin | NSViewHeightSizable | NSViewMaxYMargin)]; [scrollView setAutoresizingMask:(NSViewMinXMargin | NSViewWidthSizable | NSViewHeightSizable | NSViewMaxXMargin | NSViewMaxYMargin)]; // register for notifications when the body textview end editing [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(textDidEndEditing:) name:NSTextDidEndEditingNotification object:textView]; return scrollView; } - (NSView *)generateContentViewWithMicroHeaders:(BOOL)isMicro { NSForm *headers = isMicro ? [self microHeadersView] : [self headersView]; NSView *body = [self bodyView]; // place the body immediately below the headers NSPoint bodyOrigin = [body frame].origin; bodyOrigin.y += [headers frame].size.height + [headers frame].origin.y; [body setFrameOrigin:bodyOrigin]; int width = 200; int height = 200; int padding = 6; if (isMicro) padding = 2; NSView *contents = [[[BPDocumentContentView alloc] initWithFrame:NSMakeRect(0,0,width,height)] autorelease]; [contents setAutoresizingMask:(NSViewMinXMargin | NSViewWidthSizable | NSViewMaxXMargin | NSViewMinYMargin | NSViewHeightSizable | NSViewMaxYMargin)]; // size headers & body, fitting to contents frame rect (give or take a bit of padding) if (!NSEqualRects([headers frame], NSZeroRect)) [headers setFrame:NSMakeRect(padding/2, padding, width-padding, [headers frame].size.height + padding*2)]; [body setFrame:NSMakeRect(0, [headers frame].size.height, width, height - [headers frame].size.height)]; // add headers & body to contents [contents addSubview:headers]; [contents addSubview:body]; // register for notifications for when any headers form cell is finished edit [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(controlTextDidEndEditing:) name:NSControlTextDidEndEditingNotification object:headers]; return contents; } - (NSView *)generateContentView { return [self generateContentViewWithMicroHeaders:NO]; } - (NSView *)contentView { if (contentView == nil) { contentView = [[self generateContentView] retain]; } return contentView; } - (NSView *)generateMicroContentView { return [self generateContentViewWithMicroHeaders:YES]; } - (NSView *)microContentView { if (microContentView == nil) { microContentView = [[self generateMicroContentView] retain]; } return microContentView; } - (NSView *)generateAttachmentsView { if ([[document attachments] count] == 0) { // document has no attachments return [[[NSMatrix alloc] initWithFrame:NSZeroRect] autorelease]; } // create the matrix of attachment cells BPAttachmentCell *templateCell = [[[BPAttachmentCell alloc] init] autorelease]; NSMatrix *attachmentsMatrix = [[NSMatrix alloc] initWithFrame:NSMakeRect(0,0,0,0) mode:NSListModeMatrix prototype:templateCell numberOfRows:[[document attachments] count] numberOfColumns:1]; [attachmentsMatrix autorelease]; // set the image & caption (file name) for each cell BPAttachmentCell *cell; BPAttachment *attachment; NSSize minCellSize = NSZeroSize; int i; for (i=0; i<[[document attachments] count]; i++) { cell = [[attachmentsMatrix cells] objectAtIndex:i]; attachment = [[document attachments] objectAtIndex:i]; [cell setImage:[attachment icon]]; [cell setCaption:[NSString stringWithFormat:@"%@ (%@)", [attachment name], [attachment humanReadableSize]]]; if ([cell minSizeToFit].width > minCellSize.width) minCellSize = [cell minSizeToFit]; } // (must do matrix sizing stuff after the cells have been filled, I think) // set matrix width as the width of the widest cell in the matrix [attachmentsMatrix setCellSize:minCellSize]; //[attachmentsMatrix setCellSize:[BPAttachmentCell defaultSize]]; // size the matrix [attachmentsMatrix sizeToCells]; [attachmentsMatrix setAutosizesCells:YES]; // set target/action attributes [attachmentsMatrix setTarget:self]; [attachmentsMatrix setDoubleAction:@selector(openAttachment:)]; return attachmentsMatrix; } - (NSView *)attachmentsView { if (attachmentsView == nil) { attachmentsView = [[self generateAttachmentsView] retain]; } return attachmentsView; } - (void)refreshAttachmentsView { NSMatrix *newAttachmentsView = [[self generateAttachmentsView] retain]; [attachmentsView release]; attachmentsView = newAttachmentsView; } - (void)openAttachment:(id)sender { BPAttachment *attachmentToOpen = [[document attachments] objectAtIndex:[(NSMatrix *)sender selectedRow]]; [attachmentToOpen openInDefaultApp]; } - (void)saveHeaders:(NSControl *)control { // nothing to do, default documents have no headers right now } - (void)saveBody:(NSText *)textObject { // update the related attribute using key-value coding [document setValue:[textObject string] forKey:@"body"]; } // called when one of the form header cells FINISHES editing // (not called when value just changes) - (void)controlTextDidEndEditing:(NSNotification *)aNotification { // instead of finding out which header cell changed value, just update all // of them for now, it's easier and there's not much text in them anyway [self saveHeaders:[aNotification object]]; } // called when the body textview changed value - (void)textDidEndEditing:(NSNotification *)aNotification { [self saveBody:[aNotification object]]; } - (void)dealloc { NSLog(@"Destroying document"); [contentView release]; [attachmentsView release]; //[headers release]; //[body release]; [super dealloc]; } @end #pragma mark - // ---------------------------------------- // ---------------------------------------- @implementation BPDefaultDocument - (id)initWithAuthor:(BPPerson *)theAuthor date:(NSDate *)aDate label:(NSString *)docLabel body:(NSString *)docBody { return [self initWithAuthor:theAuthor date:date label:docLabel body:docBody attachments:nil]; } // Init - (id)initWithAuthor:(BPPerson *)theAuthor date:(NSDate *)dateCreated label:(NSString *)docLabel body:(NSString *)docBody attachments:(NSArray *)theAttachments { [super init]; isEditable = NO; // store given properties author = [theAuthor retain]; date = [dateCreated retain]; [self setLabel:docLabel]; [self setBody:docBody]; // init other properties isFlagged = NO; icon = nil; //icon = SOME DEFAULT ICON deadlineDate = nil; toDoDate = nil; if (theAttachments == nil) { attachments = [[NSMutableArray alloc] initWithCapacity:5]; } else { // mutableCopy so don't need to retain theAttachments attachments = [theAttachments mutableCopy]; } //inMicroView = ([[NSUserDefaults standardUserDefaults] boolForKey:@"MailStacker_startInMicroView"]); inMicroView = ([[NSUserDefaults standardUserDefaults] integerForKey:@"MailStacker_viewerType"] == 0); lastHeadersViewPref = [[NSUserDefaults standardUserDefaults] boolForKey:@"MailStacker_headersAsForm"]; lastHTMLViewPref = [[NSUserDefaults standardUserDefaults] boolForKey:@"MailStacker_renderHTML"]; return self; } // Returns author - (BPPerson *)author { return author; } // Returns creation date - (NSDate *)date { return date; } - (void)setDocumentGroup:(BPDocumentGroup *)group { documentGroup = group; } - (BPDocumentGroup *)documentGroup { return documentGroup; } - (void)setEditable:(BOOL)editable { isEditable = editable; } - (BOOL)isEditable { return isEditable; } // set/get label - (void)setLabel:(NSString *)theLabel { [theLabel retain]; [label release]; label = theLabel; } - (NSString *)label { return label; } - (void)setBody:(NSString *)theBody { [theBody retain]; [body release]; body = theBody; } - (NSString *)body { return body; } - (void)setFlagged:(BOOL)flag { isFlagged = flag; } - (BOOL)isFlagged { return isFlagged; } - (void)setColourCode:(NSColor *)colour { [colour retain]; [colourCode release]; colourCode = colour; } - (NSColor *)colourCode { return colourCode; } - (void)setDeadlineDate:(NSDate *)aDate { [aDate retain]; [deadlineDate release]; deadlineDate = aDate; } - (NSDate *)deadlineDate { return deadlineDate; } - (void)setToDoDate:(NSDate *)aDate { [aDate retain]; [toDoDate release]; toDoDate = aDate; } - (NSDate *)toDoDate { return toDoDate; } - (void)setIcon:(NSImage *)theIcon { [theIcon retain]; [icon release]; icon = theIcon; } - (NSImage *)icon { if (icon == nil) icon = [[BPIconManager iconForDocument:self] retain]; return icon; } // don't need to RETAIN cos we're adding straight into an array which calls // that on it anyway?? - (void)addAttachment:(BPAttachment *)attachment { if (![attachments containsObject:attachment]) { [attachments addObject:attachment]; if (view != nil) [view refreshAttachmentsView]; } } - (void)removeAttachment:(BPAttachment *)attachment { int index = [attachments indexOfObject:attachment]; if (index != NSNotFound) { [attachments removeObjectAtIndex:index]; if (view != nil) [view refreshAttachmentsView]; } } - (NSArray *)attachments { return attachments; } - (void)setupView { BPDocumentView *newView = [[[BPDocumentView alloc] initWithDocument:self] retain]; [view release]; view = newView; } - (void)beforeGetContentView { // if user prefs relating to display for doc view have changed, refresh the view BOOL headersViewPref = [[NSUserDefaults standardUserDefaults] boolForKey:@"MailStacker_headersAsForm"]; BOOL HTMLViewPref = [[NSUserDefaults standardUserDefaults] boolForKey:@"MailStacker_renderHTML"]; if (lastHeadersViewPref != headersViewPref || lastHTMLViewPref != HTMLViewPref) [self setupView]; lastHeadersViewPref = headersViewPref; lastHTMLViewPref = HTMLViewPref; if (view == nil) [self setupView]; } - (NSView *)contentView { [self beforeGetContentView]; return [view contentView]; } - (NSView *)microContentView { [self beforeGetContentView]; return [view microContentView]; } - (NSView *)attachmentsView { if (view == nil) [self setupView]; return [view attachmentsView]; } - (NSView *)freshContentView { if (view == nil) [self setupView]; return [view generateContentView]; //BPDocumentView *freshView = [[[BPDocumentView alloc] initWithDocument:self] autorelease]; //return [freshView contentView]; } - (NSView *)freshMicroContentView { if (view == nil) [self setupView]; return [view generateMicroContentView]; } - (NSView *)freshAttachmentsView { if (view == nil) [self setupView]; return [view generateAttachmentsView]; //BPDocumentView *freshView = [[[BPDocumentView alloc] initWithDocument:self] autorelease]; //return [freshView attachmentsView]; } - (void)setInMicroView:(BOOL)isInMicroView { inMicroView = isInMicroView; } - (BOOL)inMicroView { return inMicroView; } - (void)toggleView { inMicroView = !inMicroView; } - (void)setMarkAmount:(int)amount { markAmount = amount; } - (int)markAmount { return markAmount; } - (NSString *)allPersonsSummaries { return [author summary]; } - (BOOL)isDirectCorrespondenceWithPerson:(BPPerson *)person { return [author isEqual:person]; } - (NSString *)description { return [NSString stringWithFormat:@"BPDefaultDocument by '%@' %@ '%@'", author, date, label]; } - (void)dealloc { // don't try to log 'self' here in the dealloc msg because it crashes it! // Maybe cos there isn't really a 'self in this context cos it's a superclass? NSLog(@"Destroying BPDefaultDocument"); [attachments release]; [view release]; [author release]; [colourCode release]; [icon release]; [label release]; [body release]; [date release]; [deadlineDate release]; [toDoDate release]; [super dealloc]; // super dealloc must be last line of method } #pragma - #pragma archival - (void)encodeWithCoder:(NSCoder *)coder { // super doesn't implement NSCoding, so don't need to call super impl. [coder encodeObject:author forKey:@"author"]; [coder encodeBool:isEditable forKey:@"isEditable"]; [coder encodeBool:isFlagged forKey:@"isFlagged"]; [coder encodeObject:colourCode forKey:@"colourCode"]; //[coder encodeObject:icon forKey:@"icon"]; [coder encodeObject:label forKey:@"label"]; [coder encodeObject:body forKey:@"body"]; [coder encodeObject:date forKey:@"date"]; [coder encodeObject:deadlineDate forKey:@"deadlineDate"]; [coder encodeObject:toDoDate forKey:@"toDoDate"]; [coder encodeObject:attachments forKey:@"attachments"]; // don't save the 'view' instance variable, it will be dynamically generated // don't save the 'lastHeadersViewPref' instance variable, it's based on current user prefs //[coder encodeBool:inMicroView forKey:@"inMicroView"]; [coder encodeInt:markAmount forKey:@"markAmount"]; } - (id)initWithCoder:(NSCoder *)coder { [super init]; author = [[coder decodeObjectForKey:@"author"] retain]; [self setEditable:[coder decodeBoolForKey:@"isEditable"]]; [self setFlagged:[coder decodeBoolForKey:@"isFlagged"]]; [self setColourCode:[coder decodeObjectForKey:@"colourCode"]]; //[self setIcon:[coder decodeObjectForKey:@"icon"]]; [self setLabel:[coder decodeObjectForKey:@"label"]]; [self setBody:[coder decodeObjectForKey:@"body"]]; date = [[coder decodeObjectForKey:@"date"] retain]; [self setDeadlineDate:[coder decodeObjectForKey:@"deadlineDate"]]; [self setToDoDate:[coder decodeObjectForKey:@"toDoDate"]]; attachments = [[coder decodeObjectForKey:@"attachments"] retain]; lastHeadersViewPref = [[NSUserDefaults standardUserDefaults] boolForKey:@"MailStacker_headersAsForm"]; lastHTMLViewPref = [[NSUserDefaults standardUserDefaults] boolForKey:@"MailStacker_renderHTML"]; //[self setInMicroView:[coder decodeBoolForKey:@"inMicroView"]]; [self setMarkAmount:[coder decodeIntForKey:@"markAmount"]]; return self; } @end