diff --git a/ChangeLog b/ChangeLog index 71e1622ac..94bf26164 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2002-10-25 Adam Fedor + + * Source/NSApplication.m: Remove some NSDebugLogs. + + * Source/NSDocument.m + (-fileNameFromRunningSavePanelForSaveOperation:): Set dicectory to + the currentDirectory if no fileName is set. Localize. + * Source/NSDocumentController.m: Document. + (+sharedDocumentController): Proper allocation/init. Set ourselves + as the application delegate if there isn't one already. + (-init): Retrieve list of recent documents. + (-reviewUnsavedDocumentsWithAlertTitle:cancellable:) Save + current directory. Localize. + (-currentDirectory): Rewrite according to docs. + + * Source/NSOpenPanel.m (-runModalForTypes:): Use nil for + directory. + * Source/NSSavePanel.m (_setupForDirectory:file:): Use + current directory if nil, otherwise keep the old one. + (-runModal): Pass nil for directory. + 2002-10-25 Mirko Viviani * configure.ac: fixed header/ldflags for freebsd. diff --git a/Source/NSApplication.m b/Source/NSApplication.m index d1e415277..bb7e29ca7 100644 --- a/Source/NSApplication.m +++ b/Source/NSApplication.m @@ -461,7 +461,6 @@ static NSCell* tileCell = nil; GSStringDrawingDummyFunction(); - NSDebugLog(@"Initialize NSApplication class\n"); [self setVersion: 1]; /* Create the gui bundle we use to localize messages. */ @@ -607,8 +606,6 @@ static NSCell* tileCell = nil; */ NSApp = self; - NSDebugLog(@"Begin of NSApplication -init\n"); - /* Initialize the backend here. */ initialize_gnustep_backend(); @@ -808,7 +805,6 @@ static NSCell* tileCell = nil; - (void) dealloc { GSDisplayServer *srv = GSServerForWindow(_app_icon_window); - NSDebugLog(@"Freeing NSApplication\n"); [nc removeObserver: self]; @@ -865,8 +861,6 @@ static NSCell* tileCell = nil; [nc postNotificationName: NSApplicationWillBecomeActiveNotification object: self]; - NSDebugLog(@"activateIgnoringOtherApps start."); - _app_is_active = YES; for (i = 0; i < count; i++) @@ -898,8 +892,6 @@ static NSCell* tileCell = nil; [[self mainWindow] orderFront: self]; } - NSDebugLog(@"activateIgnoringOtherApps end."); - [nc postNotificationName: NSApplicationDidBecomeActiveNotification object: self]; } @@ -982,8 +974,6 @@ static NSCell* tileCell = nil; NSEvent *e; id distantFuture = [NSDate distantFuture]; /* Cache this, safe */ - NSDebugLog(@"NSApplication -run\n"); - if (_runLoopPool != nil) { [NSException raise: NSInternalInconsistencyException @@ -1041,8 +1031,6 @@ static NSCell* tileCell = nil; IF_NO_GC(_runLoopPool = [arpClass new]); [[NSUserDefaults standardUserDefaults] synchronize]; - NSDebugLog(@"NSApplication end of run loop\n"); - DESTROY (_runLoopPool); } @@ -2566,7 +2554,6 @@ delegate. then we ask the delegate if the app is to be terminated. */ if (wasMain && count == 0 && _app_is_running) { - NSDebugLog(@"asking delegate whether to terminate app..."); if ([_delegate respondsToSelector: @selector(applicationShouldTerminateAfterLastWindowClosed:)]) { diff --git a/Source/NSDocument.m b/Source/NSDocument.m index 5b7461b8b..a4ff0abb4 100644 --- a/Source/NSDocument.m +++ b/Source/NSDocument.m @@ -430,7 +430,8 @@ //FIXME if we have accessory -- store the desired save type somewhere. } -- (int)runModalSavePanel:(NSSavePanel *)savePanel withAccessoryView:(NSView *)accessoryView +- (int)runModalSavePanel:(NSSavePanel *)savePanel + withAccessoryView:(NSView *)accessoryView { [savePanel setAccessoryView:accessoryView]; return [savePanel runModal]; @@ -453,10 +454,14 @@ - (NSString *)fileNameFromRunningSavePanelForSaveOperation:(NSSaveOperationType)saveOperation { NSView *accessory = nil; - NSString *title = @"save"; + NSString *title; + NSString *directory; + NSArray *extensions; + NSDocumentController *controller; NSSavePanel *savePanel = [NSSavePanel savePanel]; - NSArray *extensions = [[NSDocumentController sharedDocumentController] - fileExtensionsFromType:[self fileType]]; + + controller = [NSDocumentController sharedDocumentController]; + extensions = [controller fileExtensionsFromType:[self fileType]]; if ([self shouldRunSavePanelWithAccessoryView]) { @@ -473,15 +478,22 @@ switch (saveOperation) { - case NSSaveOperation: title = _(@"Save"); break; case NSSaveAsOperation: title = _(@"Save As"); break; - case NSSaveToOperation: title = _(@"Save To"); break; - } + case NSSaveToOperation: title = _(@"Save To"); break; + case NSSaveOperation: + default: + title = _(@"Save"); + break; + } [savePanel setTitle:title]; + if ([self fileName]) - [savePanel setDirectory:[[self fileName] stringByDeletingLastPathComponent]]; + directory = [[self fileName] stringByDeletingLastPathComponent]; + else + directory = [controller currentDirectory]; + [savePanel setDirectory: directory]; if ([self runModalSavePanel:savePanel withAccessoryView:accessory]) { diff --git a/Source/NSDocumentController.m b/Source/NSDocumentController.m index b8187bdb2..1f236d757 100644 --- a/Source/NSDocumentController.m +++ b/Source/NSDocumentController.m @@ -33,7 +33,7 @@ #include #include #include - +#include static NSString *NSTypesKey = @"NSTypes"; static NSString *NSNameKey = @"NSName"; @@ -45,6 +45,11 @@ static NSString *NSDOSExtensionsKey = @"NSDOSExtensions"; //static NSString *NSMIMETypesKey = @"NSMIMETypes"; static NSString *NSDocumentClassKey = @"NSDocumentClass"; +static NSString *NSRecentDocuments = @"NSRecentDocuments"; +static NSString *NSDefaultOpenDirectory = @"NSDefaultOpenDirectory"; + +static NSDocumentController *sharedController = nil; + #define TYPE_INFO(name) TypeInfoForName(_types, name) static NSDictionary *TypeInfoForName (NSArray *types, NSString *typeName) @@ -63,42 +68,90 @@ static NSDictionary *TypeInfoForName (NSArray *types, NSString *typeName) return nil; } +/**

+ NSDocumentController is a class that controls a set of NSDocuments + for an application. As the application delegate, it responds to + the typical File Menu commands for opening and creating new + documents, and making sure all documents have been saved when an + application quits. It also registers itself for the + NSWorkspaceWillPowerOffNotification. +

+

+ NSDocumentController also manages document types and the related + NSDocument subclasses that handle them. This information comes + from the custom info property list (Info.plist) + loaded when NSDocumentController is initialized. The property list + contains an array of dictionarys with the key NSTypes. Each + dictionary contains a set of keys: +

+ + NSDocumentClass - The name of the subclass + NSName - Short name of the document type + NSHumanReadableName - Longer document type name + NSUnixExtensions - Array of strings + NSDOSExtensions - Array of strings + NSIcon - Icon name for these documents + NSRole - Class is a Viewer or Editor + +

+ You can also use NSDocumentController to get a list of all open + documents, the current document (The one whose window is Key) and + other information about these documents. It also remebers the most + recently opened documents (through the user default key + _GSRecentDocuments). . +

+

+ You can subclass NSDocumentController to customize the behavior of + certain aspects of the class, but it is very rare that you would + need to do this. +

+*/ @implementation NSDocumentController -+ (void)initialize -{ -} - -+ (id) documentController //private -{ - return [self sharedDocumentController]; -} - -+ (id) allocWithZone: (NSZone *)zone -{ - return [self sharedDocumentController]; -} - +/** Returns the shared instance of the document controller class. You + should always use this method to get the NSDocumentController. */ + (id) sharedDocumentController { - static id instance = nil; - - if (instance == nil) + if (sharedController == nil) { - instance = [[super allocWithZone:NULL] init]; + sharedController = [[self alloc] init]; + /* Need to verify this is the correct behavior. Set ourselves as + the app delegate if there isn't one already */ + if ([NSApp delegate] == nil) + [NSApp setDelegate: self]; } - return instance; + return sharedController; } +/** Initializes the document controller class. The first + instance of a document controller class that gets initialized + becomes the shared instance. + */ - init { NSDictionary *customDict = [[NSBundle mainBundle] infoDictionary]; ASSIGN (_types, [customDict objectForKey: NSTypesKey]); _documents = [[NSMutableArray alloc] init]; - // FIXME: Should fill this list from some stored values - _recentDocuments = [[NSMutableArray alloc] init]; + + /* Get list of recent documents */ + _recentDocuments = [[NSUserDefaults standardUserDefaults] + objectForKey: NSRecentDocuments]; + if (_recentDocuments) + { + int i, count; + _recentDocuments = [_recentDocuments mutableCopy]; + count = [_recentDocuments count]; + for (i = 0; i < count; i++) + { + NSURL *url; + url = [NSURL URLWithString: [_recentDocuments objectAtIndex: i]]; + [_recentDocuments replaceObjectAtIndex: i withObject: url]; + } + } + else + _recentDocuments = RETAIN([NSMutableArray array]); [self setShouldCreateUI:YES]; [[[NSWorkspace sharedWorkspace] notificationCenter] @@ -107,6 +160,8 @@ static NSDictionary *TypeInfoForName (NSArray *types, NSString *typeName) name: NSWorkspaceWillPowerOffNotification object: nil]; + if (sharedController == nil) + sharedController = self; return self; } @@ -268,6 +323,9 @@ static NSDictionary *TypeInfoForName (NSArray *types, NSString *typeName) return openPanel; } +/** Invokes [NSOpenPanel-runModelForTypes:] with the NSOpenPanel + object openPanel, and passes the openableFileExtensions file types +*/ - (int) runModalOpenPanel: (NSOpenPanel *)openPanel forTypes: (NSArray *)openableFileExtensions { @@ -289,6 +347,10 @@ static NSDictionary *TypeInfoForName (NSArray *types, NSString *typeName) return array; } +/** Uses -runModalOpenPanel:forTypes: to allow the user to select + files to open (after initializing the NSOpenPanel). Returns the + list of files that the user has selected. +*/ - (NSArray *) fileNamesFromRunningOpenPanel { NSArray *types = [self _openableFileExtensions]; @@ -302,6 +364,10 @@ static NSDictionary *TypeInfoForName (NSArray *types, NSString *typeName) return nil; } +/** Uses -runModalOpenPanel:forTypes: to allow the user to select + files to open (after initializing the NSOpenPanel). Returns the + list of files as URLs that the user has selected. +*/ - (NSArray *) URLsFromRunningOpenPanel { NSArray *types = [self _openableFileExtensions]; @@ -350,6 +416,10 @@ static NSDictionary *TypeInfoForName (NSArray *types, NSString *typeName) } +/** Iterates through all the open documents and asks each one in turn + if it can close using [NSDocument-canCloseDocument]. If the + document returns YES, then it is closed. +*/ - (BOOL) closeAllDocuments { NSDocument *document; @@ -372,23 +442,38 @@ static NSDictionary *TypeInfoForName (NSArray *types, NSString *typeName) didCloseAllSelector:(SEL)didAllCloseSelector contextInfo:(void *)contextInfo { -//FIXME + //FIXME } +/** If there are any unsaved documents, this method displays an alert + panel asking if the user wants to review the unsaved documents. If + the user agrees to review the documents, this method calls + -closeAllDocuments to close each document (prompting to save a + document if it is dirty). If cancellable is YES, then the user is + not allowed to cancel this request, otherwise this method will + return NO if the user presses the Cancel button. Otherwise returns + YES after all documents have been closed (or if there are no + unsaved documents.) +*/ - (BOOL) reviewUnsavedDocumentsWithAlertTitle: (NSString *)title cancellable: (BOOL)cancellable { - //FIXME -- localize. - NSString *cancelString = (cancellable)? @"Cancel" : nil; + NSString *cancelString = (cancellable)? _(@"Cancel") : nil; int result; + /* Probably as good a place as any to do this */ + [[NSUserDefaults standardUserDefaults] + setObject: [self currentDirectory] forKey: NSDefaultOpenDirectory]; + if (![self hasEditedDocuments]) { return YES; } - result = NSRunAlertPanel(title, @"You have unsaved documents.", - @"Review Unsaved", cancelString, @"Quit Anyways"); + result = NSRunAlertPanel(title, _(@"You have unsaved documents"), + _(@"Review Unsaved"), + cancelString, + _(@"Quit Anyways")); #define ReviewUnsaved NSAlertDefaultReturn #define Cancel NSAlertAlternateReturn @@ -399,7 +484,7 @@ static NSDictionary *TypeInfoForName (NSArray *types, NSString *typeName) case ReviewUnsaved: return [self closeAllDocuments]; case QuitAnyways: return YES; case Cancel: - default: return NO; + default: return NO; } } @@ -444,23 +529,26 @@ static NSDictionary *TypeInfoForName (NSArray *types, NSString *typeName) - (BOOL) applicationShouldTerminate: (NSApplication *)sender { - return [self reviewUnsavedDocumentsWithAlertTitle: @"Quit" + return [self reviewUnsavedDocumentsWithAlertTitle: _(@"Quit") cancellable: YES]; } #endif - (void) _workspaceWillPowerOff: (NSNotification *)notification { - // FIXME -- localize. - [self reviewUnsavedDocumentsWithAlertTitle: @"Power" cancellable: NO]; + [self reviewUnsavedDocumentsWithAlertTitle: _(@"Power Off") cancellable: NO]; } +/** Returns an array of all open documents */ - (NSArray *) documents { return _documents; } +/** Returns YES if any documents are "dirty", e.g. changes have been + made to the document that have not been saved to the disk +*/ - (BOOL) hasEditedDocuments { int i, count = [_documents count]; @@ -476,30 +564,43 @@ static NSDictionary *TypeInfoForName (NSArray *types, NSString *typeName) return NO; } +/** Returns the document whose window is the keyWindow */ - (id) currentDocument { return [self documentForWindow: [[NSApplication sharedApplication] mainWindow]]; } +/** Returns the current directory. This method first checks if there + is a current document using the -currentDocument method. If this + returns a document and the document has a filename, this method + returns the directory this file is located in. Otherwise it + returns the directory of the most recently opened document or + NSHomeDirectory() if no document has been opened before. +*/ - (NSString *) currentDirectory { NSFileManager *manager = [NSFileManager defaultManager]; - NSDocument *currentDocument = [self currentDocument]; - NSString *directory = [[currentDocument fileName] stringByDeletingLastPathComponent]; + NSDocument *document = [self currentDocument]; + NSString *directory; BOOL isDir = NO; - if (directory && - [manager fileExistsAtPath: directory isDirectory: &isDir] && isDir) + if (document == nil) + document = [[self documents] lastObject]; + directory = [[document fileName] stringByDeletingLastPathComponent]; + if (directory == nil) + directory = [[NSUserDefaults standardUserDefaults] + objectForKey: NSDefaultOpenDirectory]; + if (directory == nil + || [manager fileExistsAtPath: directory isDirectory: &isDir] == NO + || isDir == NO) { - return directory; + directory = NSHomeDirectory (); } - - //FIXME -- need to remember last opened directory, and return that here. - //Only return NSHomeDirectory if nothing's been opened yet. - return NSHomeDirectory (); + return directory; } +/** Returns the NSDocument class that controls window */ - (id) documentForWindow: (NSWindow *)window { id document; @@ -524,6 +625,9 @@ static NSDictionary *TypeInfoForName (NSArray *types, NSString *typeName) return document; } +/** Returns the NSDocument class that controls the document with the + name fileName. +*/ - (id) documentForFileName: (NSString *)fileName { int i, count = [_documents count]; @@ -604,6 +708,8 @@ static NSDictionary *TypeInfoForName (NSArray *types, NSString *typeName) - (IBAction) clearRecentDocuments: (id)sender { [_recentDocuments removeAllObjects]; + [[NSUserDefaults standardUserDefaults] + setObject: _recentDocuments forKey: NSRecentDocuments]; } // The number of remembered recent documents @@ -633,6 +739,8 @@ static NSDictionary *TypeInfoForName (NSArray *types, NSString *typeName) } [_recentDocuments addObject: anURL]; + [[NSUserDefaults standardUserDefaults] + setObject: _recentDocuments forKey: NSRecentDocuments]; } - (NSArray *) recentDocumentURLs diff --git a/Source/NSOpenPanel.m b/Source/NSOpenPanel.m index 1433a2d28..6b8c8feaf 100644 --- a/Source/NSOpenPanel.m +++ b/Source/NSOpenPanel.m @@ -406,18 +406,20 @@ static NSOpenPanel *_gs_gui_open_panel = nil; * Running the NSOpenPanel */ /** Displays the open panel in a modal session, filtering for - files that have the specified types */ + files that have the specified types +*/ - (int) runModalForTypes: (NSArray *)fileTypes { - return [self runModalForDirectory: @"" + return [self runModalForDirectory: nil file: @"" types: fileTypes]; } /** Displays the open panel in a modal session, with the directory path shown and file name (if any) selected. Files are filtered for the - specified types. The directory and filename can be empty strings - but must not be nil. */ + specified types. If the directory is nil, then the directory shown in + the opena panel is the last directory selected. +*/ - (int) runModalForDirectory: (NSString *)path file: (NSString *)name types: (NSArray *)fileTypes diff --git a/Source/NSSavePanel.m b/Source/NSSavePanel.m index fd209c39f..242d3ff38 100644 --- a/Source/NSSavePanel.m +++ b/Source/NSSavePanel.m @@ -424,12 +424,17 @@ selectCellWithString: (NSString*)title - (void) _setupForDirectory: (NSString *)path file: (NSString *)filename { - if (path == nil || filename == nil) - [NSException raise: NSInvalidArgumentException - format: @"NSSavePanel runModalForDirectory:file: " - @"does not accept nil arguments."]; - - ASSIGN (_directory, path); + if (path == nil) + { + if (_directory == nil) + ASSIGN(_directory, [_fm currentDirectoryPath]); + } + else + { + ASSIGN (_directory, path); + } + if (filename == nil) + filename = @""; ASSIGN (_fullFileName, [path stringByAppendingPathComponent: filename]); [_browser setPath: _fullFileName]; @@ -779,29 +784,24 @@ selectCellWithString: (NSString*)title /** * Shows the save panel for the user. This method invokes - * -runModalForDirectory:file: with empty strings for the directory and - * filename. Returns NSOKButton (if the user clicks the OK button) or + * -runModalForDirectory:file: with empty strings for the filename. + * Returns NSOKButton (if the user clicks the OK button) or * NSCancelButton (if the user clicks the Cancel button). */ - (int) runModal { - if (_directory) - return [self runModalForDirectory: _directory - file: @""]; - else - return [self runModalForDirectory: [_fm currentDirectoryPath] - file: @""]; + return [self runModalForDirectory: nil file: @""]; } /** - * Initializes the panel to the directory specified by path - * and, optionally, the file specified by filename, then - * displays it and begins its modal event loop; path and - * filename can be empty strings, but cannot be nil. The - * method invokes NSApplication:-runModalForWindow: method with - * self as the argument. Returns NSOKButton (if the user - * clicks the OK button) or NSCancelButton (if the user clicks - * the Cancel button). + * Initializes the panel to the directory specified by path and, + * optionally, the file specified by filename, then displays it and + * begins its modal event loop; path and filename can be empty + * strings. The method invokes [NSApplication:-runModalForWindow:] + * method with self as the argument. Returns NSOKButton (if the user + * clicks the OK button) or NSCancelButton (if the user clicks the + * Cancel button). If path is nil then the panel displays the last + * selected directory or as a last resort, the current working directory. */ - (int) runModalForDirectory: (NSString*)path file: (NSString*)filename {