// // BPDocumentViewer.m // Piles3 // // Created by Bea on 19/07/05. // Copyright 2005 __MyCompanyName__. All rights reserved. // #import "BPDocumentViewer.h" #import "BPDocument.h" #import "BPToolbarController.h" #import @implementation BPDocumentViewer - (id)initWithContentRect:(NSRect)contentRect drawerContentSize:(NSSize)drawerContentSize windowMask:(int)mask { // call super [super initWithContentRect:contentRect styleMask:mask backing:NSBackingStoreBuffered defer:YES]; // set up other stuff delegateToolbarClickSel = @selector(toolbarItemWasClicked:inViewer:); toolbarController = [[BPToolbarController alloc] initWithDelegate:self]; maxAlpha = 1.0; // make drawer (attach to right edge) NSSize size = [NSScrollView frameSizeForContentSize:drawerContentSize hasHorizontalScroller:NO hasVerticalScroller:YES borderType:NSNoBorder]; NSDrawer *drawer = [[[NSDrawer alloc] initWithContentSize:size preferredEdge:NSMaxXEdge] autorelease]; [drawer setParentWindow:self]; [drawer setLeadingOffset:0]; [drawer setTrailingOffset:0]; // make a scroll view for drawer NSScrollView *drawerScrollView = [[[NSScrollView alloc] initWithFrame:NSMakeRect(0,0,[drawer contentSize].width, [drawer contentSize].height)] autorelease]; [drawerScrollView setHasVerticalScroller:YES]; [drawer setContentView:drawerScrollView]; // open drawer initially [drawer open]; // set self as delegate for drawer, to receive drawer resize requests [drawer setDelegate:self]; return self; } - (id)initWithContentRect:(NSRect)contentRect drawerContentSize:(NSSize)drawerContentSize { // window mask specifying everything except metal texture unsigned int mask = NSTitledWindowMask | NSMiniaturizableWindowMask | NSClosableWindowMask | NSResizableWindowMask; return [self initWithContentRect:contentRect drawerContentSize:drawerContentSize windowMask:mask]; } - (void)autoFitDrawerView { NSDrawer *drawer = [[self drawers] objectAtIndex:0]; // assumes drawer has a scroll view // resize width of document's attachments view so it fills up the drawer frame NSScrollView *drawerScrollView = (NSScrollView *)[drawer contentView]; NSSize clipViewSize = [[drawerScrollView contentView] frame].size; NSSize currentSize = [[drawerScrollView documentView] frame].size; // resize drawer content proportionally float ratio = clipViewSize.width/currentSize.width; float height = currentSize.height * ratio; [[drawerScrollView documentView] setFrameSize:NSMakeSize(clipViewSize.width, height)]; } - (void)setDocument:(NSObject *)aDocument useFreshView:(BOOL)freshCopy useMicroView:(BOOL)isMicro { // store document [aDocument retain]; [document release]; document = aDocument; NSDrawer *drawer = [[self drawers] objectAtIndex:0]; NSScrollView *drawerScrollView = (NSScrollView *)[drawer contentView]; // set the content inside the main self window and the attachments drawer if (freshCopy) { if (isMicro) { [self setContentView:[aDocument freshMicroContentView]]; } else { [self setContentView:[aDocument freshContentView]]; } [drawerScrollView setDocumentView:[aDocument freshAttachmentsView]]; } else { if (isMicro) { [self setContentView:[aDocument microContentView]]; } else { [self setContentView:[aDocument contentView]]; } [drawerScrollView setDocumentView:[aDocument attachmentsView]]; } // if in micro view, remove scrollbars if (isMicro) { NSArray *subviews = [[self contentView] subviews]; NSScrollView *view; int i; for (i=0; i<[subviews count]; i++) { view = [subviews objectAtIndex:i]; if ([view isKindOfClass:[NSScrollView class]]) { [view setHasVerticalScroller:NO]; [view setHasHorizontalScroller:NO]; if ([[view documentView] isKindOfClass:[WebView class]]) { [[[[view documentView] mainFrame] frameView] setAllowsScrolling:NO]; } } } } // dynamically size drawer according to width of attachments view float attachViewWidth = [[drawerScrollView documentView] frame].size.width; NSSize size = [NSScrollView frameSizeForContentSize:NSMakeSize(attachViewWidth, 1) // height doesn't matter hasHorizontalScroller:NO hasVerticalScroller:YES borderType:NSNoBorder]; [drawer setContentSize:size]; // set the toolbar if (!isMicro) { [toolbarController updateForDocument:aDocument]; [self setToolbar:[toolbarController toolbar]]; [[self toolbar] setVisible:YES]; } [self activateAttachmentsDrawer]; } /* - (void)sendEvent:(NSEvent *)theEvent { NSLog(@"calling super with event %@", theEvent); [super sendEvent:theEvent]; NSLog(@"called super with event."); } */ - (void)refresh { if (document != nil) { // trigger the header fields to save their values, since this makes // them lose focus if they have it if ([self makeFirstResponder:self]) { /* All fields are now valid; it’s safe to use fieldEditor:forObject: to claim the field editor. */ } else { /* Force first responder to resign. */ [self endEditingFor:nil]; } [self setDocument:document useFreshView:YES useMicroView:[document inMicroView]]; } } - (NSObject *)document { return document; } - (NSDrawer *)attachmentsDrawer { return [[self drawers] objectAtIndex:0]; } - (void)activateAttachmentsDrawer { NSDrawer *drawer = [self attachmentsDrawer]; if ([[document attachments] count] > 0) { [drawer openOnEdge:[drawer preferredEdge]]; } else { [drawer close]; } } - (BOOL)mouseInside { if (![self isVisible]) return NO; NSRect mainWindowRect = [self frame]; NSDrawer *drawer = [[self drawers] objectAtIndex:0]; // calculate approximate frame rect for drawer. // remember the origin is in the bottom-left corner!! // Was unsuccessful in getting drawerViewRect's frame and converting the coordinates, etc. // this calculation of the drawerViewRect's origin doesn't take the drawer's // leading and trailing offsets into account. i.e. MAKE THESE OFFSETS ZERO! NSRect rectEnclosingTempViewer; if ([drawer state] == NSDrawerClosedState) { // drawer is closed, so only calculate according to main window rectEnclosingTempViewer = mainWindowRect; } else { // calculate hit test according to main window + drawer NSRect drawerViewRect; drawerViewRect.size = [drawer contentSize]; drawerViewRect.origin = mainWindowRect.origin; if ([drawer edge] == NSMinXEdge) { // drawer is on window's left edge drawerViewRect.origin.x =- drawerViewRect.size.width; } else if ([drawer edge] == NSMinYEdge) { // drawer is on window's bottom edge drawerViewRect.origin.y =- mainWindowRect.size.width; } else if ([drawer edge] == NSMaxXEdge) { // drawer is on window's right edge drawerViewRect.origin.x += mainWindowRect.size.width; } else if ([drawer edge] == NSMaxYEdge) { // drawer is on window's top edge drawerViewRect.origin.y += mainWindowRect.size.width; } // get a rectangle around the whole temp viewer (approximately) // I do this instead of testing separately whether the mouse is in the main // and drawer views cos there's a gap between them, and the mouse may fall // into the gap and then the window would just close // (but I suppose with the added trackingRectPadding, the gap may not be there) rectEnclosingTempViewer = NSUnionRect(mainWindowRect, drawerViewRect); } // NSLog(@"mainWindowRect%@; drawerViewRect%@; rectEnclosingTempViewer %@", // NSStringFromRect(mainWindowRect), NSStringFromRect(drawerViewRect), NSStringFromRect(rectEnclosingTempViewer)); // see if mouse is in the rect NSPoint mouseLoc = [NSEvent mouseLocation]; if (NSMouseInRect(mouseLoc, rectEnclosingTempViewer, NO)) return YES; return NO; } - (void)setMaxAlpha:(float)alpha { maxAlpha = alpha; } - (float)maxAlpha { return maxAlpha; } - (BOOL)windowShouldClose:(id)sender { // trigger the header fields to save their values, since this makes // them lose focus if they have it if ([self makeFirstResponder:self]) { /* All fields are now valid; it’s safe to use fieldEditor:forObject: to claim the field editor. */ } else { /* Force first responder to resign. */ [self endEditingFor:nil]; } if ([document isEditable] && [document documentGroup] == nil) { // document isn't in a group so it must be a new email/note that hasn't // been saved, so ask whether document should be saved (otherwise it // will be released and probably dealloced since no group is retaining it) NSAlert *alert = [[[NSAlert alloc] init] autorelease]; [alert setMessageText:@"Do you want to save changes to this document before closing?"]; [alert setInformativeText:@"If you don't save, your changes will be lost."]; [alert addButtonWithTitle:@"Save"]; [alert addButtonWithTitle:@"Cancel"]; [alert addButtonWithTitle:@"Don't Save"]; [alert beginSheetModalForWindow:self modalDelegate:self didEndSelector:@selector(alertDidEnd:returnCode:contextInfo:) contextInfo:@"SaveAlert"]; return NO; } return YES; } #pragma mark - delegate methods - - (void)alertDidEnd:(NSAlert *)alert returnCode:(int)returnCode contextInfo:(void *)contextInfo { if (contextInfo == @"SaveAlert") { if (returnCode == NSAlertFirstButtonReturn) { // clicked "save" SEL selector = @selector(clickedSaveAsDraftForDocument:inViewer:); if ([[self delegate] respondsToSelector:selector]) { [[self delegate] performSelector:selector withObject:document withObject:self]; [self close]; } } else if (returnCode == NSAlertThirdButtonReturn) { // clicked "don't save" [self close]; } } } // forwards the method call onto the delegate - (void)toolbarItemWasClicked:(NSToolbarItem *)toolbarItem { // trigger the header fields to save their values, since this makes // them lose focus if they have it if ([self makeFirstResponder:self]) { /* All fields are now valid; it’s safe to use fieldEditor:forObject: to claim the field editor. */ } else { /* Force first responder to resign. */ [self endEditingFor:nil]; } if ([[self delegate] respondsToSelector:delegateToolbarClickSel]) [[self delegate] performSelector:delegateToolbarClickSel withObject:toolbarItem withObject:self]; } - (NSSize)drawerWillResizeContents:(NSDrawer *)sender toSize:(NSSize)contentSize { if (sender == [[self drawers] objectAtIndex:0]) [self autoFitDrawerView]; return contentSize; } - (void)dealloc { NSLog(@"destroying %@", self); [document release]; [toolbarController release]; [super dealloc]; } @end