Document NSDocumentController, imrpove setting directory.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@14845 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Adam Fedor 2002-10-26 02:38:34 +00:00
parent 9f911dc5d8
commit 24653e2d38
6 changed files with 217 additions and 87 deletions

View file

@ -1,3 +1,24 @@
2002-10-25 Adam Fedor <fedor@gnu.org>
* 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 <mirko.viviani@rccr.cremona.it>
* configure.ac: fixed header/ldflags for freebsd.

View file

@ -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:)])
{

View file

@ -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])
{

View file

@ -33,7 +33,7 @@
#include <AppKit/NSMenuItem.h>
#include <AppKit/NSWorkspace.h>
#include <AppKit/NSDocumentFrameworkPrivate.h>
#include <Foundation/NSUserDefaults.h>
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;
}
/** <p>
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.
</p>
<p>
NSDocumentController also manages document types and the related
NSDocument subclasses that handle them. This information comes
from the custom info property list (<ApplicationName>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:
</p>
<list>
<item>NSDocumentClass - The name of the subclass</item>
<item>NSName - Short name of the document type</item>
<item>NSHumanReadableName - Longer document type name</item>
<item>NSUnixExtensions - Array of strings</item>
<item>NSDOSExtensions - Array of strings</item>
<item>NSIcon - Icon name for these documents</item>
<item>NSRole - Class is a Viewer or Editor</item>
</list>
<p>
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). .
</p>
<p>
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.
</p>
*/
@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;
}
/** </init>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

View file

@ -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

View file

@ -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
{