#import "BPDocumentViewManager.h" #import "BPDocument.h" #import "BPAttachmentCell.h" #import "BPToolbarController.h" #import "BPDocumentViewer.h" #import "BPNoteDocument.h" #import "BPPilesDataController.h" #import "BPPilesDisplayController.h" #import "BPEmailDocument.h" #import "BPMailSender.h" #import "BPAttachment.h" #import "BPUtil.h" #import "BPNoteWriter.h" @implementation BPDocumentViewManager + (BPDocumentViewer *)createStandaloneDocumentViewer { BPDocumentViewer *viewer = [[BPDocumentViewer alloc] initWithContentRect:NSMakeRect(0,0,550,450) drawerContentSize:NSMakeSize(200, 1)]; return [viewer autorelease]; } + (BPDocumentViewer *)createMicroDocViewer { unsigned int mask = NSTexturedBackgroundWindowMask; //NSResizableWindowMask; // | NSTexturedBackgroundWindowMask; BPDocumentViewer *viewer = [[BPDocumentViewer alloc] initWithContentRect:NSMakeRect(0,0,230,125) drawerContentSize:NSMakeSize([BPAttachmentCell defaultSize].width, 1) windowMask:mask]; //[viewer setLevel:NSFloatingWindowLevel]; //[viewer setHasShadow:NO]; [viewer setBackgroundColor:[NSColor colorWithCalibratedRed:0.95 green:0.95 blue:0.95 alpha:1.0]]; [viewer setMaxAlpha:0.85]; [viewer setAlphaValue:[viewer maxAlpha]]; return [viewer autorelease]; } + (BPDocumentViewer *)createMediumDocViewer { //unsigned int mask = NSTitledWindowMask | NSClosableWindowMask | NSResizableWindowMask; unsigned int mask = NSClosableWindowMask | NSResizableWindowMask | NSTitledWindowMask; BPDocumentViewer *viewer = [[BPDocumentViewer alloc] initWithContentRect:NSMakeRect(0,0,440,350) drawerContentSize:NSMakeSize([BPAttachmentCell defaultSize].width, 1) windowMask:mask]; // this effectively makes the tempDocViewer show up above other // applications' main windows //[viewer setLevel:NSFloatingWindowLevel]; [viewer setMaxAlpha:0.85]; [viewer setAlphaValue:[viewer maxAlpha]]; return [viewer autorelease]; } - (id)init { [super init]; microDocViewer = [[BPDocumentViewManager createMicroDocViewer] retain]; mediumDocViewer = [[BPDocumentViewManager createMediumDocViewer] retain]; tempDocViewer = microDocViewer; [microDocViewer setDelegate:self]; // to be notified when temp doc viewer is "zoomed" [microDocViewer setReleasedWhenClosed:NO]; // i.e. just hide, don't close and release all data [mediumDocViewer setDelegate:self]; // to be notified when temp doc viewer is "zoomed" [mediumDocViewer setReleasedWhenClosed:NO]; // i.e. just hide, don't close and release all data tempDocViewerClosedSel = @selector(tempDocViewerClosed:); canHideTempDocViewerSel = @selector(canHideTempDocViewer:); return self; } - (void)awakeFromNib { // // } - (void)setDelegate:(id)theDelegate { delegate = theDelegate; } // --------------------------------------------------- #pragma mark - // --------------------------------------------------- - (void)openStandaloneViewerForDocument:(NSObject *)document { BPDocumentViewer *viewer = [[BPDocumentViewManager createStandaloneDocumentViewer] retain]; [viewer center]; // set self as delegate (for window resizing & toolbar clicks) [viewer setDelegate:self]; // Do freshCopy=YES because the temp doc viewer is using the document's // original content view already, and if this standalone viewer uses that // copy as well, it doesn't work (I guess a view can't belong to 2 windows // at once? well, the standalone just goes blank if you don't use a fresh copy) [viewer setDocument:document useFreshView:YES useMicroView:NO]; //[[viewer toolbar] setSizeMode:NSToolbarSizeModeRegular]; // show viewer [viewer makeKeyAndOrderFront:nil]; } - (NSWindow *)tempDocViewer { return tempDocViewer; } - (BOOL)tempDocViewerIsClosed { return ![tempDocViewer isVisible]; } - (void)setTempViewerToShowDocument:(NSObject *)document atTopLeftPoint:(NSPoint)point drawerEdge:(NSRectEdge)drawerEdge { [self interruptTempViewerFade]; if ([document inMicroView]) { if (tempDocViewer != microDocViewer) { [self hideTempViewerRegardless]; tempDocViewer = microDocViewer; } } else { if (tempDocViewer != mediumDocViewer) { [self hideTempViewerRegardless]; tempDocViewer = mediumDocViewer; } } // if already showing this document, return if ([tempDocViewer isVisible] && [tempDocViewer document] == document) return; // set details [tempDocViewer setFrameTopLeftPoint:point]; [[tempDocViewer attachmentsDrawer] setPreferredEdge:drawerEdge]; // put document contents into the window // Do freshCopy=NO because the temp doc viewer uses the same document view // everytime so that it doesn't generate a new view everytime you mouse over // to view a document. Each standalone viewer needs to generate a fresh copy though. // If document is editable, do fresh copy cos it might've been changed. BOOL doFreshCopy = ([document isEditable]); [tempDocViewer setDocument:document useFreshView:doFreshCopy useMicroView:[document inMicroView]]; //if (tempDocViewer == mediumDocViewer) { // [[tempDocViewer toolbar] setSizeMode:NSToolbarSizeModeSmall]; //} // show window just above main piles window [tempDocViewer orderWindow:NSWindowAbove relativeTo:[mainPilesDisplayWindow windowNumber]]; } - (BOOL)mouseInsideTempDocViewer { return [tempDocViewer mouseInside]; } - (BOOL)shouldHideTempDocViewer { // check so that you don't close the viewer window if mouse has moved back // out of the window onto an icon, etc. if (delegate != nil) { // normally you would have a NSWindow delegate and ask it if the window // should close, by calling the performClose method, but here you can't // because the tempDocViewer doesn't have a 'close' button so performClose // wouldn't call the window's delegate's windowShouldClose method (this seems silly) // So instead there's a custom "should I close this window" call. if ([delegate respondsToSelector:canHideTempDocViewerSel]) if (![delegate performSelector:canHideTempDocViewerSel withObject:self]) return NO; } // check whether the mouse is still in the temp doc viewer. if ([self mouseInsideTempDocViewer]) return NO; return YES; } // hides the viewer but does checks first to see if you should hide it - (void)hideTempViewer { /* disable hiding for now if (![self shouldHideTempDocViewer]) return; [self hideTempViewerRegardless]; */ if (![tempDocViewer isVisible]) return; if (![self shouldHideTempDocViewer]) return; [self stopFadingTimer]; [[tempDocViewer attachmentsDrawer] close]; // Set up our timer to periodically call the fade: method. windowFadeTimer = [[NSTimer scheduledTimerWithTimeInterval:0.07 target:self selector:@selector(fade:) userInfo:nil repeats:YES] retain]; } // hides the viewer - (void)hideTempViewerRegardless { //[tempDocViewer orderWindow:NSWindowBelow relativeTo:[mainPilesDisplayWindow windowNumber]]; // close window [tempDocViewer close]; //[tempViewerDrawer close]; // notify delegate if ([delegate respondsToSelector:tempDocViewerClosedSel]) [delegate performSelector:tempDocViewerClosedSel withObject:tempDocViewer]; } - (NSRect)tempViewerFrameRectForDocument:(NSObject *)document { NSRect frame; if ([document inMicroView]) frame = [microDocViewer frame]; else frame = [mediumDocViewer frame]; return frame; } // couldn't hook up 'close' menu item for some reason, so hack it this way - (IBAction)closeCurrentDocWindow:(id)sender { if ([currentKeyDocViewer isKeyWindow]) [currentKeyDocViewer performClose:nil]; } #pragma mark - - (void)clickedMarkItemForDocument:(NSObject *)document inViewer:(BPDocumentViewer *)viewer { [pilesDataController toggleFlagForDocument:document]; } - (void)clickedDeleteForDocument:(NSObject *)document inViewer:(BPDocumentViewer *)viewer { if ([document documentGroup] == nil) { // this must be a new note/email since it doesn't belong to a group // just put it to a group first before moving it to the trash [pilesDataController saveDocumentIntoAPile:document]; } /* // move document to the trash [pilesDataController deleteDocument:document moveToTrash:YES]; [viewer close]; // should move viewer to next icon in pile if there's more */ [pilesDisplayController trashDocument:document]; } - (void)clickedAttachForDocument:(NSObject *)document inViewer:(BPDocumentViewer *)viewer { NSOpenPanel *openPanel = [NSOpenPanel openPanel]; int result = [openPanel runModalForTypes:nil]; if (result == NSCancelButton) return; // make attachment object for each selected file and add attach it to the document NSFileManager *manager = [NSFileManager defaultManager]; NSDictionary *fileAttrs; BPAttachment *attachment; NSString *filename; int i=0; for (i=0; i<[[openPanel filenames] count]; i++) { filename = [[openPanel filenames] objectAtIndex:i]; fileAttrs = [manager fileAttributesAtPath:filename traverseLink:YES]; attachment = [[[BPAttachment alloc] initWithName:[filename lastPathComponent] icon:nil size:[[fileAttrs objectForKey:NSFileSize] intValue] data:[NSData dataWithContentsOfFile:filename] filePath:filename] autorelease]; [document addAttachment:attachment]; } // update to show new attachments [viewer refresh]; } - (void)clickedSaveAsDraftForDocument:(NSObject *)document inViewer:(BPDocumentViewer *)viewer { // take focus off any textfields that might have focus, to auto-update document values [viewer makeFirstResponder:nil]; if ([document documentGroup] == nil) { // document hasn't been saved before [pilesDataController saveDocumentIntoAPile:document]; } } #pragma mark - #pragma mark delegate methods - (void)toolbarItemWasClicked:(NSToolbarItem *)toolbarItem inViewer:(BPDocumentViewer *)viewer { NSString *itemIdentifier = [toolbarItem itemIdentifier]; NSObject *document = [viewer document]; NSLog(@"itemIdentifier: %@", itemIdentifier); // handle general ones e.g. delete first if ([itemIdentifier isEqual:@"markItem"]) { [self clickedMarkItemForDocument:document inViewer:viewer]; return; } else if ([itemIdentifier isEqual:@"delete"]) { [self clickedDeleteForDocument:document inViewer:viewer]; return; } else if ([itemIdentifier isEqual:@"attach"]) { [self clickedAttachForDocument:document inViewer:viewer]; return; } else if ([itemIdentifier isEqual:@"saveAsDraft"]) { [self clickedSaveAsDraftForDocument:document inViewer:viewer]; return; } if ([document isKindOfClass:[BPEmailDocument class]]) { if ([itemIdentifier isEqual:@"reply"] || [itemIdentifier isEqual:@"replyAll"]) { [mailSender writeReplyForEmail:(BPEmailDocument *)document replyAll:[itemIdentifier isEqual:@"replyAll"]]; } else if ([itemIdentifier isEqual:@"forward"]) { [mailSender writeForwardForEmail:(BPEmailDocument *)document]; } else if ([itemIdentifier isEqual:@"send"]) { [mailSender sendEmail:(BPEmailDocument *)document]; [viewer close]; } } else if ([document isKindOfClass:[BPNoteDocument class]]) { if ([itemIdentifier isEqual:@"sendAsEmail"]) { [viewer makeFirstResponder:nil]; // lose focus to auto-save note contents [noteWriter sendNoteAsEmail:(BPNoteDocument *)document]; } } } - (void)windowDidBecomeKey:(NSNotification *)aNotification { currentKeyDocViewer = [aNotification object]; } /* // NSWindow delegate method - called when "zooming" on window - (NSRect)windowWillUseStandardFrame:(NSWindow *)sender defaultFrame:(NSRect)defaultFrame { NSRect currentFrame = [sender frame]; currentFrame.size.width += 200; return currentFrame; } */ /* - (BOOL)windowShouldClose:(id)sender { // Set up our timer to periodically call the fade: method. NSTimer *windowFadeTimer = [[NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(fade:) userInfo:sender repeats:YES] retain]; // Don't close just yet. return NO; } */ #pragma mark - - (BOOL)tempDocViewerIsFading { //return [tempDocViewer alphaValue] < 1.0; return [tempDocViewer isVisible] && windowFadeTimer != nil; } - (void)stopFadingTimer { if (windowFadeTimer != nil) { [windowFadeTimer invalidate]; [windowFadeTimer release]; windowFadeTimer = nil; } } - (void)interruptTempViewerFade { if (![self tempDocViewerIsFading]) return; // stop the fading by destroying the timer [self stopFadingTimer]; // make window fully opaque again [tempDocViewer setAlphaValue:[tempDocViewer maxAlpha]]; //[[tempDocViewer attachmentsDrawer] openOnEdge:[[tempDocViewer attachmentsDrawer] preferredEdge]]; [tempDocViewer activateAttachmentsDrawer]; } - (void)fade:(NSTimer *)timer { // if mouse is inside the tempDocViewer, stop the fading by destroying the timer if ([self mouseInsideTempDocViewer]) { //if ([tempDocViewer isKeyWindow]) { [self interruptTempViewerFade]; return; } // fade the window... if ([tempDocViewer alphaValue] > 0.0) { // If window is still partially opaque, reduce its opacity. [tempDocViewer setAlphaValue:[tempDocViewer alphaValue] - 0.1]; } else { // Otherwise, if window is completely transparent, destroy the timer and close the window. [self stopFadingTimer]; //[window close]; // assuming this window is the tempDocViewer ... the fade should only // apply to tempDocViewer [self hideTempViewerRegardless]; // Make the window fully opaque again for next time. [tempDocViewer setAlphaValue:[tempDocViewer maxAlpha]]; } } - (void)dealloc { NSLog(@"dealloc %@", self); [microDocViewer release]; [mediumDocViewer release]; [super dealloc]; } @end