Clean up and completion of NSDocumentController.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@27415 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
fredkiefer 2008-12-25 13:21:22 +00:00
parent 4a710daca9
commit 6cfee381c1
4 changed files with 572 additions and 314 deletions

View file

@ -1,3 +1,13 @@
2008-12-25 Fred Kiefer <FredKiefer@gmx.de>
* Headers/AppKit/NSMenuItem.h: Declare to implement interface
NSValidatedUserInterfaceItem.
* Headers/AppKit/NSDocumentController.h: Change to use NSUInteger and
NSInteger.
* Source/NSDocumentController.m: Implement all Cocoa methods of this
class and clean up the rest of the implementation.
Partly using code by Nikolaus Schaller <hns@computer.org>.
2008-12-21 Adam Fedor <fedor@gnu.org>
* Version 0.16.0

View file

@ -48,7 +48,7 @@
@class NSOpenPanel;
@class NSWindow;
@interface NSDocumentController : NSObject
@interface NSDocumentController : NSObject <NSCoding>
{
@private
NSMutableArray *_documents;
@ -111,7 +111,7 @@
- (IBAction)newDocument:(id)sender;
- (IBAction)clearRecentDocuments:(id)sender;
#if OS_API_VERSION(MAC_OS_X_VERSION_10_4, GS_API_LATEST)
- (unsigned int) maximumRecentDocumentCount;
- (NSUInteger) maximumRecentDocumentCount;
#endif
/*" Recent Documents "*/
@ -122,7 +122,7 @@
/*" Open panel "*/
- (NSArray *)URLsFromRunningOpenPanel;
- (NSArray *)fileNamesFromRunningOpenPanel;
- (int)runModalOpenPanel:(NSOpenPanel *)openPanel forTypes:(NSArray *)openableFileExtensions;
- (NSInteger)runModalOpenPanel:(NSOpenPanel *)openPanel forTypes:(NSArray *)openableFileExtensions;
/*" Document management "*/
- (void)addDocument:(NSDocument *)document;

View file

@ -35,6 +35,7 @@
#import <GNUstepBase/GSVersionMacros.h>
#include <Foundation/NSObject.h>
#include <AppKit/NSUserInterfaceValidation.h>
@class NSAttributedString;
@class NSString;
@ -367,7 +368,7 @@
@end
@interface NSMenuItem : NSObject <NSMenuItem>
@interface NSMenuItem : NSObject <NSMenuItem, NSValidatedUserInterfaceItem>
{
NSMenu *_menu;
NSString *_title;

View file

@ -41,6 +41,7 @@
#include "AppKit/NSDocumentController.h"
#include "AppKit/NSOpenPanel.h"
#include "AppKit/NSApplication.h"
#include "AppKit/NSMenu.h"
#include "AppKit/NSMenuItem.h"
#include "AppKit/NSWorkspace.h"
#include "NSDocumentFrameworkPrivate.h"
@ -61,8 +62,14 @@ static NSString *CFBundleTypeExtensions = @"CFBundleTypeExtensions";
static NSString *CFBundleTypeName = @"CFBundleTypeName";
static NSString *CFBundleTypeRole = @"CFBundleTypeRole";
// FIXME: Looks like this was changed to @"NSRecentDocumentRecords"
static NSString *NSRecentDocuments = @"NSRecentDocuments";
static NSString *NSEditorRole = @"Editor";
static NSString *NSViewerRole = @"Viewer";
//static NSString *NSNoRole = @"None";
//static NSString *NSShellRole = @"Shell";
static NSDocumentController *sharedController = nil;
#define TYPE_INFO(name) TypeInfoForName(_types, name)
@ -108,6 +115,14 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
return nil;
}
@interface NSDocumentController (RecentsMenu)
- (NSMenu *) _recentMenu;
- (void) _updateOpenRecentMenu;
- (IBAction) _openRecentDocument: (id)sender;
@end
/** <p>
NSDocumentController is a class that controls a set of NSDocuments
for an application. As an application delegate, it responds to the
@ -159,7 +174,8 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
{
if (sharedController == nil)
{
sharedController = [[self alloc] init];
// -init sets sharedController
[[self alloc] init];
}
return sharedController;
@ -256,12 +272,13 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
IMP meth2;
meth1 = [self methodForSelector: sel];
meth2 = [[NSDocument class] instanceMethodForSelector: sel];
meth2 = [[NSDocumentController class] instanceMethodForSelector: sel];
return (meth1 != meth2);
}
#define OVERRIDDEN(sel) [self _hasOverridden: @selector(sel)]
//#define OVERRIDDEN(sel) [self _hasOverridden: @selector(sel)]
#define OVERRIDDEN(sel) ([self methodForSelector: @selector(sel)] != [[NSDocumentController class] instanceMethodForSelector: @selector(sel)])
- (BOOL) shouldCreateUI
{
@ -331,6 +348,13 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
error: (NSError **)err
{
Class documentClass = [self documentClassForType: type];
if (documentClass == nil)
{
// FIXME: Set err
return nil;
}
return AUTORELEASE([[documentClass alloc] initForURL: url
withContentsOfURL: contents
ofType: type
@ -340,20 +364,40 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
- (id) makeDocumentWithContentsOfURL: (NSURL *)url
ofType: (NSString *)type
error: (NSError **)err
{
if (OVERRIDDEN(makeDocumentWithContentsOfFile:ofType:) && [url isFileURL])
{
return [self makeDocumentWithContentsOfFile: [url path] ofType: type];
}
else
{
Class documentClass = [self documentClassForType: type];
if (documentClass == nil)
{
// FIXME: Set err
return nil;
}
return AUTORELEASE([[documentClass alloc] initWithContentsOfURL: url
ofType: type
error: err]);
}
}
- (id) makeUntitledDocumentOfType: (NSString *)type
error: (NSError **)err
{
if (OVERRIDDEN(makeUntitledDocumentOfType:))
{
return [self makeUntitledDocumentOfType: type];
}
else
{
Class documentClass = [self documentClassForType: type];
return AUTORELEASE([[documentClass alloc] initWithType: type
error: err]);
}
}
- (BOOL) presentError: (NSError *)error
{
@ -383,19 +427,32 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
- (NSString*) defaultType
{
NSString *defaultName = nil;
if ([_types count] == 0)
{
return nil; // raise exception?
}
int i, count = [_types count];
defaultName = [(NSDictionary*)[_types objectAtIndex: 0] objectForKey: NSNameKey];
for (i = 0; i < count; i++)
{
NSDictionary *typeInfo = (NSDictionary*)[_types objectAtIndex: i];
NSString *role;
role = [typeInfo objectForKey: NSRoleKey];
if (role == nil)
role = [typeInfo objectForKey: CFBundleTypeRole];
if ([role isEqual: NSEditorRole])
{
defaultName = [typeInfo objectForKey: NSNameKey];
if (defaultName == nil)
{
defaultName = [(NSDictionary*)[_types objectAtIndex: 0] objectForKey: CFBundleTypeName];
defaultName = [typeInfo objectForKey: CFBundleTypeName];
}
return defaultName;
}
}
// none found
return nil;
}
- (void) addDocument: (NSDocument *)document
{
@ -477,9 +534,7 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
if (document == nil)
{
// Should we only do this if [url isFileURL] is YES?
NSString *type = [self typeFromFileExtension:
[[url path] pathExtension]];
NSString *type = [self typeForContentsOfURL: url error: NULL];
document = [self makeDocumentWithContentsOfURL: url ofType: type];
@ -543,12 +598,10 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
display: (BOOL)display
error: (NSError **)err
{
if (OVERRIDDEN(openDocumentWithContentsOfFile:display:))
if (OVERRIDDEN(openDocumentWithContentsOfFile:display:) && [url isFileURL])
{
NSString *fileName;
fileName = [url path];
return [self openDocumentWithContentsOfFile: fileName display: display];
return [self openDocumentWithContentsOfFile: [url path]
display: display];
}
else
{
@ -556,11 +609,16 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
if (document == nil)
{
// Should we only do this if [url isFileURL] is YES?
NSString *type = [self typeFromFileExtension:
[[url path] pathExtension]];
NSString *type = [self typeForContentsOfURL: url error: err];
document = [self makeDocumentWithContentsOfURL: url ofType: type error: err];
if (type == nil)
{
return nil;
}
document = [self makeDocumentWithContentsOfURL: url
ofType: type
error: err];
if (document == nil)
{
@ -591,12 +649,15 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
withContentsOfURL: (NSURL *)contents
error: (NSError **)err
{
if ([contents isFileURL])
NSString *type = [self typeForContentsOfURL: contents error: err];
id document;
if (type == nil)
{
NSString *type =
[self typeFromFileExtension: [[contents path] pathExtension]];
id document =
[self makeDocumentForURL: url
return NO;
}
document = [self makeDocumentForURL: url
withContentsOfURL: contents
ofType: type
error: err];
@ -610,12 +671,6 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
}
return YES;
}
}
else
{
// FIXME: set error
*err = nil;
}
return NO;
}
@ -623,6 +678,7 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
- (NSOpenPanel *) _setupOpenPanel
{
NSOpenPanel *openPanel = [NSOpenPanel openPanel];
[openPanel setDirectory: [self currentDirectory]];
[openPanel setAllowsMultipleSelection: YES];
return openPanel;
@ -631,7 +687,7 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
/** Invokes [NSOpenPanel-runModalForTypes:] with the NSOpenPanel
object openPanel, and passes the openableFileExtensions file types
*/
- (int) runModalOpenPanel: (NSOpenPanel *)openPanel
- (NSInteger) runModalOpenPanel: (NSOpenPanel *)openPanel
forTypes: (NSArray *)openableFileExtensions
{
return [openPanel runModalForTypes: openableFileExtensions];
@ -645,6 +701,7 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
for (i = 0; i < count; i++)
{
NSDictionary *typeInfo = [_types objectAtIndex: i];
[array addObjectsFromArray: [typeInfo objectForKey: NSUnixExtensionsKey]];
[array addObjectsFromArray: [typeInfo objectForKey: NSDOSExtensionsKey]];
[array addObjectsFromArray: [typeInfo objectForKey: CFBundleTypeExtensions]];
@ -687,7 +744,6 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
return nil;
}
- (IBAction) saveAllDocuments: (id)sender
{
NSDocument *document;
@ -702,23 +758,30 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
}
}
- (IBAction) openDocument: (id)sender
{
NSEnumerator *fileEnum;
NSString *filename;
NSError *err = nil;
NSEnumerator *urlEnum;
NSURL *url;
fileEnum = [[self fileNamesFromRunningOpenPanel] objectEnumerator];
while ((filename = [fileEnum nextObject]))
urlEnum = [[self URLsFromRunningOpenPanel] objectEnumerator];
while ((url = [urlEnum nextObject]))
{
[self openDocumentWithContentsOfFile: filename display: YES];
[self openDocumentWithContentsOfURL: url display: YES error: &err];
if (err && ![self presentError: err])
{
break;
}
}
}
- (IBAction) newDocument: (id)sender
{
[self openUntitledDocumentOfType: [self defaultType] display: YES];
NSError *err = nil;
[self openUntitledDocumentAndDisplay: YES error: &err];
if (err)
[self presentError: err];
}
@ -748,11 +811,59 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
return YES;
}
// FIXME
static BOOL _shouldClose = YES;
- (void) _document: (NSDocument *)doc
shouldClose: (BOOL)shouldClose
contextInfo: (void *)contextInfo
{
_shouldClose = shouldClose;
}
- (void)closeAllDocumentsWithDelegate:(id)delegate
didCloseAllSelector:(SEL)didAllCloseSelector
contextInfo:(void *)contextInfo
{
//FIXME
int count;
BOOL closeAll = YES;
count = [_documents count];
if (count > 0)
{
NSDocument *array[count];
[_documents getObjects: array];
while (count-- > 0)
{
NSDocument *document = array[count];
// Initialize to known state
_shouldClose = YES;
[document canCloseDocumentWithDelegate: self
shouldCloseSelector:
@selector(_document:shouldClose:contextInfo:)
contextInfo: contextInfo];
if (_shouldClose)
{
[document close];
}
else
{
closeAll = NO;
}
}
}
if (delegate != nil && didAllCloseSelector != NULL)
{
void (*meth)(id, SEL, id, BOOL, void*);
meth = (void (*)(id, SEL, id, BOOL, void*))[delegate methodForSelector:
didAllCloseSelector];
if (meth)
meth(delegate, didAllCloseSelector, self, closeAll, contextInfo);
}
}
/** If there are any unsaved documents, this method displays an alert
@ -800,7 +911,34 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
didReviewAllSelector: (SEL)didReviewAllSelector
contextInfo: (void *)contextInfo
{
NSString *cancelString = (cancellable)? ((NSString *)_(@"Cancel")) : ((NSString *)nil);
int result = YES;
if (![self hasEditedDocuments])
{
if (delegate != nil && didReviewAllSelector != NULL)
{
void (*meth)(id, SEL, id, BOOL, void*);
meth = (void (*)(id, SEL, id, BOOL, void*))[delegate methodForSelector:
didReviewAllSelector];
if (meth)
meth(delegate, didReviewAllSelector, self, result, contextInfo);
}
return;
}
// FIXME
NSBeginAlertSheet(title,
_(@"Review Unsaved"),
cancelString,
_(@"Quit Anyway"),
nil,
delegate,
didReviewAllSelector,
contextInfo,
_(@"You have unsaved documents"),
nil);
}
@ -845,7 +983,6 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
[self reviewUnsavedDocumentsWithAlertTitle: _(@"Power Off") cancellable: NO];
}
/** Returns an array of all open documents */
- (NSArray *) documents
{
@ -958,12 +1095,9 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
- (id) documentForURL: (NSURL *)url
{
if (OVERRIDDEN(documentForFileName:))
if (OVERRIDDEN(documentForFileName:) && [url isFileURL])
{
NSString *fileName;
fileName = [url path];
return [self documentForFileName: fileName];
return [self documentForFileName: [url path]];
}
else
{
@ -985,23 +1119,30 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
- (BOOL) validateMenuItem: (NSMenuItem *)anItem
{
if ([anItem action] == @selector(saveAllDocuments:))
return [self validateUserInterfaceItem: anItem];
}
- (BOOL) validateUserInterfaceItem: (id <NSValidatedUserInterfaceItem>)anItem
{
if (sel_eq([anItem action], @selector(saveAllDocuments:)))
{
return [self hasEditedDocuments];
}
return YES;
}
- (BOOL)validateUserInterfaceItem:(id <NSValidatedUserInterfaceItem>)anItem
{
// FIXME
return YES;
}
- (NSString *) displayNameForType: (NSString *)type
{
// FIXME: Is this needed?
NSString *name = [TYPE_INFO(type) objectForKey: NSHumanReadableNameKey];
if (!name)
{
name = [[NSBundle mainBundle] localizedStringForKey: type
value: type
table: @"InfoPlist"];
}
return name ? name : type;
}
@ -1021,6 +1162,7 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
containsObject: fileExtension])
{
NSString *type = [typeInfo objectForKey: NSNameKey];
if(type == nil)
{
type = [typeInfo objectForKey: CFBundleTypeName];
@ -1034,26 +1176,21 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
- (NSString *) typeForContentsOfURL: (NSURL *)url error: (NSError **)err
{
// FIXME
NSString *extension;
extension = [[url path] pathExtension];
return [self typeFromFileExtension: extension];
// FIXME: open connection and get response to determine mine/type
// Should we only do this if [url isFileURL] is YES?
return [self typeFromFileExtension: [[url path] pathExtension]];
}
- (NSArray *) fileExtensionsFromType: (NSString *)type
{
NSDictionary *typeInfo = TYPE_INFO(type);
NSArray *unixExtensions = [typeInfo objectForKey: NSUnixExtensionsKey];
NSArray *dosExtensions = [typeInfo objectForKey: NSDOSExtensionsKey];
NSArray *cfFileExtensions = [typeInfo objectForKey: CFBundleTypeExtensions];
NSMutableArray *array = [NSMutableArray arrayWithCapacity: 3];
if (!dosExtensions && !unixExtensions) return cfFileExtensions;
if (!dosExtensions) return unixExtensions;
if (!unixExtensions) return dosExtensions;
[array addObjectsFromArray: [typeInfo objectForKey: NSUnixExtensionsKey]];
[array addObjectsFromArray: [typeInfo objectForKey: NSDOSExtensionsKey]];
[array addObjectsFromArray: [typeInfo objectForKey: CFBundleTypeExtensions]];
return [[unixExtensions arrayByAddingObjectsFromArray: dosExtensions]
arrayByAddingObjectsFromArray: cfFileExtensions];
return array;
}
- (Class) documentClassForType: (NSString *)type
@ -1095,18 +1232,19 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
[_recent_documents removeAllObjects];
[[NSUserDefaults standardUserDefaults]
setObject: _recent_documents forKey: NSRecentDocuments];
[self _updateOpenRecentMenu];
}
// The number of remembered recent documents
- (unsigned int) maximumRecentDocumentCount
- (NSUInteger) maximumRecentDocumentCount
{
// FIXME: Should come from user defaults
return 5;
}
- (void) noteNewRecentDocument: (NSDocument *)aDocument
{
NSString *fileName = [aDocument fileName];
NSURL *anURL = [NSURL fileURLWithPath: fileName];
NSURL *anURL = [aDocument fileURL];
if (anURL != nil)
[self noteNewRecentDocumentURL: anURL];
@ -1129,6 +1267,7 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
[_recent_documents addObject: anURL];
// Save the changed list
a = [_recent_documents mutableCopy];
index = [a count];
while (index-- > 0)
@ -1139,6 +1278,7 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
[[NSUserDefaults standardUserDefaults]
setObject: a forKey: NSRecentDocuments];
RELEASE(a);
[self _updateOpenRecentMenu];
}
- (NSArray *) recentDocumentURLs
@ -1146,12 +1286,23 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
return _recent_documents;
}
//
// NSCoding protocol
//
- (void) encodeWithCoder: (NSCoder*)aCoder
{
// FIXME
}
- (id) initWithCoder: (NSCoder*)aDecoder
{
// FIXME
return self;
}
@end
@implementation NSDocumentController (Private)
static NSString *NSEditorRole = @"Editor";
static NSString *NSViewerRole = @"Viewer";
//static NSString *NSNoRole = @"None";
- (NSArray *) _editorAndViewerTypesForClass: (Class)documentClass
{
@ -1177,6 +1328,7 @@ static NSString *NSViewerRole = @"Viewer";
|| [role isEqual: NSViewerRole]))
{
NSString *name = [typeInfo objectForKey: NSNameKey];
if(name == nil)
{
name = [typeInfo objectForKey: CFBundleTypeName];
@ -1200,10 +1352,17 @@ static NSString *NSViewerRole = @"Viewer";
NSString *className = [typeInfo objectForKey: NSDocumentClassKey];
NSString *role = [typeInfo objectForKey: NSRoleKey];
// if the standard one isn't filled... check the CF key.
if (role == nil)
{
role = [typeInfo objectForKey: CFBundleTypeRole];
}
if ([docClassName isEqualToString: className] &&
(role == nil || [role isEqual: NSEditorRole]))
{
NSString *name = [typeInfo objectForKey: NSNameKey];
if(name == nil)
{
name = [typeInfo objectForKey: CFBundleTypeName];
@ -1393,3 +1552,91 @@ static NSString *processName;
}
@end
@implementation NSDocumentController (RecentsMenu)
- (NSMenu *) _recentMenu
{
// FIXME
return nil;
}
// should be handled by making us the delegate of the recent's menu
- (void) _updateOpenRecentMenu
{
NSMenu *recentMenu;
int i;
recentMenu = [self _recentMenu];
if (!recentMenu)
{
return;
}
// don't update (to keep Clear List status consistent)
[recentMenu setAutoenablesItems: NO];
[recentMenu setMenuChangedMessagesEnabled: NO];
while ([recentMenu numberOfItems] > 0)
[recentMenu removeItemAtIndex: 0]; // remove them all
for (i = [_recent_documents count]; i >= -2; i--)
{
// add all items incl. a Clear List item if needed
NSMenuItem *item;
if (i == -1)
{
if ([_recent_documents count] == 0)
continue; // skip if menu is empty
item = (NSMenuItem *) [NSMenuItem separatorItem];
// will release...
RETAIN(item);
}
else if (i == -2)
{
item = [[NSMenuItem alloc] initWithTitle: _(@"Clear List")
action: @selector(clearRecentDocuments:)
keyEquivalent: nil];
// disable for empty list
[item setEnabled: [_recent_documents count] > 0];
}
else
{
// standard item
NSURL *u = [_recent_documents objectAtIndex: i]; // get URL
if ([u isFileURL])
item = [[NSMenuItem alloc] initWithTitle: [[u path] lastPathComponent]
action: @selector(_openRecentDocument:)
keyEquivalent:nil];
else
item = [[NSMenuItem alloc] initWithTitle: [u relativeString]
action: @selector(_openRecentDocument:)
keyEquivalent:nil];
[item setTag: i];
}
[item setTarget: self];
[recentMenu addItem: item];
RELEASE(item);
}
[recentMenu setMenuChangedMessagesEnabled: YES];
}
- (IBAction) _openRecentDocument: (id)sender
{
// action to open recent document by tag index
NSURL *url;
int idx = [sender tag];
if (idx < 0 || idx >= [_recent_documents count])
{
// something went wrong, ignore
[self _updateOpenRecentMenu];
return;
}
url = (NSURL *)[_recent_documents objectAtIndex: idx];
[self openDocumentWithContentsOfURL: url display: YES];
}
@end