/* Copyright (c) 2006-2007 Christopher J. W. Lloyd Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #import #import #import #import #import #import #import #import @interface NSDocument(private) -(void)_setUntitledNumber:(int)number; @end @interface NSDocumentController(forward) -(void)_updateRecentDocumentsMenu; @end @interface NSMenu(private) -(NSMenu *)_menuWithName:(NSString *)name; @end @implementation NSDocumentController static NSDocumentController *shared=nil; +sharedDocumentController { if(shared==nil) shared = [[NSDocumentController alloc] init]; return shared; } -init { if(shared==nil) shared=self; _documents=[NSMutableArray new]; _fileTypes=[[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDocumentTypes"] retain]; _autosavingDelay=0; return self; } -(void)dealloc { [_documents release]; [_fileTypes release]; [super dealloc]; } -initWithCoder:(NSCoder *)coder { NSUnimplementedMethod(); return nil; } -(void)encodeWithCoder:(NSCoder *)coder { NSUnimplementedMethod(); } -(NSString *)defaultType { if([_fileTypes count]==0) return nil; return [(NSDictionary *)[_fileTypes objectAtIndex:0] objectForKey:@"CFBundleTypeName"]; } -(NSArray *)documentClassNames { NSMutableSet *result=[NSMutableSet set]; int i,count=[_fileTypes count]; for(i=0;i=0) if([[[names objectAtIndex:count] lowercaseString] isEqual:extension]) return [check objectForKey:@"CFBundleTypeName"]; } return nil; } -(void)setAutosavingDelay:(NSTimeInterval)value { _autosavingDelay=value; NSUnimplementedMethod(); } -(NSArray *)documents { return _documents; } -(void)addDocument:(NSDocument *)document { [_documents addObject:document]; } -(void)removeDocument:(NSDocument *)document { [_documents removeObjectIdenticalTo:document]; } -documentForURL:(NSURL *)url { int i,count=[_documents count]; for(i=0;i 0) { [[_documents lastObject] canCloseDocumentWithDelegate:self shouldCloseSelector:@selector(_closeDocumentsStartingWith:shouldClose:closeAllContext:) contextInfo:context]; return; } } // Inform our closeAllDocuments delegate of the results. id delegate = [context objectForKey:@"delegate"]; SEL selector = NSSelectorFromString([context objectForKey:@"selector"]); void * info = [[context objectForKey:@"contextInfo"] pointerValue]; [context release]; if ([delegate respondsToSelector:selector]) { void (*delegateMethod)(id, SEL, id, BOOL, void *); delegateMethod = (void (*)(id, SEL, id, BOOL, void *))[delegate methodForSelector:selector]; delegateMethod(delegate, selector, self, ([_documents count] == 0), info); } } -(void)closeAllDocumentsWithDelegate:delegate didCloseAllSelector:(SEL)selector contextInfo:(void *)info { NSDictionary * closeAllContext = [[NSDictionary alloc] initWithObjectsAndKeys: delegate, @"delegate", NSStringFromSelector(selector), @"selector", [NSValue valueWithPointer:info], @"contextInfo", nil]; [self _closeDocumentsStartingWith:nil shouldClose:YES closeAllContext:closeAllContext]; } -(void)reviewUnsavedDocumentsWithAlertTitle:(NSString *)title cancellable:(BOOL)cancellable delegate:delegate didReviewAllSelector:(SEL)selector info:(void *)info { NSUnimplementedMethod(); } -(NSError *)willPresentError:(NSError *)error { // do nothing return error; } -(BOOL)presentError:(NSError *)error { return [NSApp presentError:[self willPresentError:error]]; } -(void)presentError:(NSError *)error modalForWindow:(NSWindow *)window delegate:delegate didPresentSelector:(SEL)selector contextInfo:(void *)info { [NSApp presentError:[self willPresentError:error] modalForWindow:window delegate:delegate didPresentSelector:selector contextInfo:info]; } -(int)runModalOpenPanel:(NSOpenPanel *)openPanel forTypes:(NSArray *)extensions { int result = [openPanel runModalForDirectory:[self currentDirectory] file:nil types:extensions]; if (result) [[NSUserDefaults standardUserDefaults] setObject:[openPanel directory] forKey:@"NSNavLastRootDirectory"]; return result; } -(NSOpenPanel *)_runOpenPanel { NSOpenPanel *openPanel=[NSOpenPanel openPanel]; [openPanel setAllowsMultipleSelection:YES]; if([self runModalOpenPanel:openPanel forTypes:[self _allFileExtensions]]){ return openPanel; } return nil; } -(NSArray *)fileNamesFromRunningOpenPanel { return [[self _runOpenPanel] filenames]; } -(NSArray *)URLsFromRunningOpenPanel { return [[self _runOpenPanel] URLs]; } -(NSMutableArray *)_recentDocumentPaths { NSFileManager *fileManager = [NSFileManager defaultManager]; NSMutableArray *paths=[NSMutableArray arrayWithArray:[[NSUserDefaults standardUserDefaults] arrayForKey:@"NSRecentDocumentPaths"]]; int i,count=[paths count]; BOOL isDir; for (i=count-1;i>=0;i--) if (![fileManager fileExistsAtPath:[paths objectAtIndex:i] isDirectory:&isDir] || isDir) [paths removeObjectAtIndex:i]; if ([paths count]!=count) [[NSUserDefaults standardUserDefaults] setObject:paths forKey:@"NSRecentDocumentPaths"]; return paths; } -(void)_openRecentDocument:sender { NSArray *paths=[self _recentDocumentPaths]; NSMenuItem *item=sender; int tag=[item tag]; if(tag>=0 && tag<[paths count]){ NSError *error=nil; NSURL *url=[NSURL fileURLWithPath:[paths objectAtIndex:tag]]; [self openDocumentWithContentsOfURL:url display:YES error:&error]; } } -(void)_removeAllRecentDocumentsFromMenu:(NSMenu *)menu { int count=[[menu itemArray] count]; while(--count>=0){ NSMenuItem *check=[[menu itemArray] objectAtIndex:count]; if([check action]==@selector(_openRecentDocument:)){ [menu removeItemAtIndex:count]; } } } -(void)_updateRecentDocumentsMenu { NSMenu *menu=[[NSApp mainMenu] _menuWithName:@"_NSRecentDocumentsMenu"]; NSArray *array=[self _recentDocumentPaths]; int count=[array count]; [self _removeAllRecentDocumentsFromMenu:menu]; if([[menu itemArray] count]>0){ if([array count]==0){ if([[[menu itemArray] objectAtIndex:0] isSeparatorItem]) [menu removeItemAtIndex:0]; } else { if(![[[menu itemArray] objectAtIndex:0] isSeparatorItem]) [menu insertItem:[NSMenuItem separatorItem] atIndex:0]; } } while(--count>=0){ NSString *path=[array objectAtIndex:count]; NSMenuItem *item=[[[NSMenuItem alloc] initWithTitle:path action:@selector(_openRecentDocument:) keyEquivalent:nil] autorelease]; [item setTag:count]; [menu insertItem:item atIndex:0]; } } -(NSArray *)recentDocumentURLs { NSArray *paths=[self _recentDocumentPaths]; int i,count=[paths count]; NSMutableArray *result=[NSMutableArray arrayWithCapacity:count]; for(i=0;i[self maximumRecentDocumentCount]) [array removeLastObject]; [[NSUserDefaults standardUserDefaults] setObject:array forKey:@"NSRecentDocumentPaths"]; [self _updateRecentDocumentsMenu]; } -(void)noteNewRecentDocument:(NSDocument *)document { NSURL *url=[document fileURL]; if(url!=nil) [self noteNewRecentDocumentURL:url]; } -(void)clearRecentDocuments:sender { NSArray *array=[NSArray array]; [[NSUserDefaults standardUserDefaults] setObject:array forKey:@"NSRecentDocumentPaths"]; [self _updateRecentDocumentsMenu]; } -(void)newDocument:sender { NSString *type=[(NSDictionary *)[_fileTypes objectAtIndex:0] objectForKey:@"CFBundleTypeName"]; [self openUntitledDocumentOfType:type display:YES]; } -(void)openDocument:sender { NSArray *files=[self fileNamesFromRunningOpenPanel]; int i,count=[files count]; for(i=0;i=0){ NSDocument *document=[_documents objectAtIndex:count]; if([document isDocumentEdited]) [document saveDocument:sender]; } } static BOOL actionIsDocumentController(SEL selector){ if(selector==@selector(saveAllDocuments:)) return YES; if(selector==@selector(openDocument:)) return YES; if(selector==@selector(newDocument:)) return YES; if(selector==@selector(clearRecentDocuments:)) return YES; if(selector==@selector(_openRecentDocument:)) return YES; return NO; } -(BOOL)validateMenuItem:(NSMenuItem *)item { return (actionIsDocumentController([item action]) || [self respondsToSelector:[item action]]); } -(BOOL)validateUserInterfaceItem:(id )item { return (actionIsDocumentController([item action]) || [self respondsToSelector:[item action]]); } -(BOOL)application:sender openFile:(NSString *)path { NSError *error=nil; NSURL *url=[NSURL fileURLWithPath:path]; NSDocument *document=[self openDocumentWithContentsOfURL:url display:YES error:&error]; return (document!=nil)?YES:NO; } -(BOOL)closeAllDocuments { NSUnimplementedMethod(); return NO; } -(BOOL)reviewUnsavedDocumentsWithAlertTitle:(NSString *)title cancellable:(BOOL)cancellable { NSUnimplementedMethod(); return 0; } @end