Implement NSDocument autosaving.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@27288 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
wlux 2008-12-14 16:39:15 +00:00
parent d570c4cbb9
commit a7d31f149d
5 changed files with 287 additions and 11 deletions

View file

@ -1,8 +1,24 @@
2008-12-14 Wolfgang Lux <wlux@uni-muenster.de> 2008-12-14 Wolfgang Lux <wolfgang.lux@gmail.com>
* Source/NSDocumentFrameworkPrivate.h:
* Source/NSApplication.m(-finishLaunching):
* Source/NSDocumentController.m (-setAutosavingDelay:,
-reopenDocumentForURL:withContentsOfURL:error:, -_autosaveDirectory,
-autosaveDocuments:, -reopenAutosavedDocuments,
-recordAutosavedDocument:):
* Source/NSDocument.m (-initForURL:withContentsOfURL:ofType:error,
-writeWithBackupToFile:ofType:saveOperation:,
-writeSafelyToURL:ofType:forSaveOperation:error:,
-revertDocumentToSaved:, close, -setAutosavedContentsFileURL:,
-autosaveDocumentsWithDelegate:didAutosaveSelector:contextInfo:,
-_removeAutosavedContentsFile):
Implement document autosaving.
2008-12-14 Wolfgang Lux <wolfgang.lux@gmail.com>
* Source/NSSavePanel.m (-browserDidScroll:): Perform column * Source/NSSavePanel.m (-browserDidScroll:): Perform column
validation whenever the browser is scrolled, since only the validation whenever the browser is scrolled, since only the
contents of the visible columns is validated. contents of the visible columns is validated.
2008-12-13 Fred Kiefer <FredKiefer@gmx.de> 2008-12-13 Fred Kiefer <FredKiefer@gmx.de>

View file

@ -83,6 +83,7 @@
#include "GSGuiPrivate.h" #include "GSGuiPrivate.h"
#include "GNUstepGUI/GSInfoPanel.h" #include "GNUstepGUI/GSInfoPanel.h"
#include "GNUstepGUI/GSVersion.h" #include "GNUstepGUI/GSVersion.h"
#include "NSDocumentFrameworkPrivate.h"
/* The -gui thread. See the comment in initialize_gnustep_backend. */ /* The -gui thread. See the comment in initialize_gnustep_backend. */
NSThread *GSAppKitThread; NSThread *GSAppKitThread;
@ -921,6 +922,7 @@ static NSSize scaledIconSizeForSize(NSSize imageSize)
unsigned count; unsigned count;
unsigned i; unsigned i;
BOOL hadDuplicates = NO; BOOL hadDuplicates = NO;
BOOL didAutoreopen = NO;
NSImage *image = nil; NSImage *image = nil;
appIconFile = [infoDict objectForKey: @"NSIcon"]; appIconFile = [infoDict objectForKey: @"NSIcon"];
@ -1053,9 +1055,16 @@ static NSSize scaledIconSizeForSize(NSSize imageSize)
[self activateIgnoringOtherApps: YES]; [self activateIgnoringOtherApps: YES];
/* Instantiate the NSDocumentController if we are a doc-based app */ /*
* Instantiate the NSDocumentController if we are a doc-based app
* and eventually reopen all autosaved documents
*/
if ([NSDocumentController isDocumentBasedApplication]) if ([NSDocumentController isDocumentBasedApplication])
[NSDocumentController sharedDocumentController]; {
didAutoreopen =
[[NSDocumentController sharedDocumentController]
_reopenAutosavedDocuments];
}
/* /*
* Now check to see if we were launched with arguments asking to * Now check to see if we were launched with arguments asking to
@ -1075,7 +1084,7 @@ static NSSize scaledIconSizeForSize(NSSize imageSize)
[_listener application: self printFile: filePath]; [_listener application: self printFile: filePath];
[self terminate: self]; [self terminate: self];
} }
else if (![defs boolForKey: @"autolaunch"] else if (!didAutoreopen && ![defs boolForKey: @"autolaunch"]
&& [_delegate respondsToSelector: && [_delegate respondsToSelector:
@selector(applicationShouldOpenUntitledFile:)] @selector(applicationShouldOpenUntitledFile:)]
&& ([_delegate applicationShouldOpenUntitledFile: self]) && ([_delegate applicationShouldOpenUntitledFile: self])

View file

@ -140,13 +140,17 @@ withContentsOfURL: (NSURL *)url
self = [self initWithType: type error: error]; self = [self initWithType: type error: error];
if (self != nil) if (self != nil)
{ {
[self setFileType: type];
if (forUrl)
[self setFileURL: forUrl];
if ([self readFromURL: url if ([self readFromURL: url
ofType: type ofType: type
error: error]) error: error])
{ {
if (forUrl != nil) if (![url isEqual:forUrl])
{ {
[self setFileURL: forUrl]; [self setAutosavedContentsFileURL: url];
[self updateChangeCount: NSChangeReadOtherContents];
} }
} }
else else
@ -791,6 +795,7 @@ withContentsOfURL: (NSURL *)url
if (saveOp != NSSaveToOperation) if (saveOp != NSSaveToOperation)
{ {
[self _removeAutosavedContentsFile];
[self setFileName: fileName]; [self setFileName: fileName];
[self setFileType: fileType]; [self setFileType: fileType];
[self updateChangeCount: NSChangeCleared]; [self updateChangeCount: NSChangeCleared];
@ -877,8 +882,14 @@ withContentsOfURL: (NSURL *)url
error: error]; error: error];
// FIXME: Should set the file attributes // FIXME: Should set the file attributes
if (saveOp != NSSaveToOperation) if (saveOp == NSAutosaveOperation)
{ {
[self setAutosavedContentsFileURL: url];
[self updateChangeCount: NSChangeAutosaved];
}
else if (saveOp != NSSaveToOperation)
{
[self _removeAutosavedContentsFile];
[self setFileURL: url]; [self setFileURL: url];
[self setFileType: type]; [self setFileType: type];
[self updateChangeCount: NSChangeCleared]; [self updateChangeCount: NSChangeCleared];
@ -1486,6 +1497,7 @@ originalContentsURL: (NSURL *)orig
{ {
[self updateChangeCount: NSChangeCleared]; [self updateChangeCount: NSChangeCleared];
[[self undoManager] removeAllActions]; [[self undoManager] removeAllActions];
[self _removeAutosavedContentsFile];
} }
else else
{ {
@ -1515,6 +1527,7 @@ originalContentsURL: (NSURL *)orig
while (count-- > 0) while (count-- > 0)
[array[count] close]; [array[count] close];
} }
[self _removeAutosavedContentsFile];
[[NSDocumentController sharedDocumentController] removeDocument: self]; [[NSDocumentController sharedDocumentController] removeDocument: self];
} }
} }
@ -1621,14 +1634,40 @@ originalContentsURL: (NSURL *)orig
- (void)setAutosavedContentsFileURL: (NSURL *)url - (void)setAutosavedContentsFileURL: (NSURL *)url
{ {
ASSIGN(_autosaved_file_url, url); ASSIGN(_autosaved_file_url, url);
[[NSDocumentController sharedDocumentController]
_recordAutosavedDocument: self];
} }
- (void)autosaveDocumentWithDelegate: (id)delegate - (void)autosaveDocumentWithDelegate: (id)delegate
didAutosaveSelector: (SEL)didAutosaveSelector didAutosaveSelector: (SEL)didAutosaveSelector
contextInfo: (void *)context contextInfo: (void *)context
{ {
[self saveToURL: [self autosavedContentsFileURL] NSURL *url = [self autosavedContentsFileURL];
ofType: [self autosavingFileType] NSString *type = [self autosavingFileType];
NSArray *exts =
[[NSDocumentController sharedDocumentController]
fileExtensionsFromType: type];
NSString *ext = [exts count] ? (NSString *)[exts objectAtIndex: 0] : @"";
if (url == nil)
{
static NSString *processName = nil;
NSString *path;
if (!processName)
processName = [[[NSProcessInfo processInfo] processName] copy];
path = [[NSDocumentController sharedDocumentController]
_autosaveDirectory: YES];
path = [path stringByAppendingPathComponent:
[NSString stringWithFormat: @"%@-%d",
processName, _document_index]];
path = [path stringByAppendingPathExtension: ext];
url = [NSURL fileURLWithPath: path];
}
[self saveToURL: url
ofType: type
forSaveOperation: NSAutosaveOperation forSaveOperation: NSAutosaveOperation
delegate: delegate delegate: delegate
didSaveSelector: didAutosaveSelector didSaveSelector: didAutosaveSelector
@ -1678,6 +1717,20 @@ originalContentsURL: (NSURL *)orig
} }
} }
- (void)_removeAutosavedContentsFile
{
NSURL *url = [self autosavedContentsFileURL];
if (url)
{
NSString *path = [[url path] retain];
[self setAutosavedContentsFileURL: nil];
[[NSFileManager defaultManager] removeFileAtPath: path handler: nil];
[path release];
}
}
- (void) _changeWasDone: (NSNotification *)notification - (void) _changeWasDone: (NSNotification *)notification
{ {
/* Prevent a document from appearing unmodified after saving the /* Prevent a document from appearing unmodified after saving the

View file

@ -32,9 +32,11 @@
#include <Foundation/NSFileManager.h> #include <Foundation/NSFileManager.h>
#include <Foundation/NSNotification.h> #include <Foundation/NSNotification.h>
#include <Foundation/NSPathUtilities.h> #include <Foundation/NSPathUtilities.h>
#include <Foundation/NSProcessInfo.h>
#include <Foundation/NSString.h> #include <Foundation/NSString.h>
#include <Foundation/NSURL.h> #include <Foundation/NSURL.h>
#include <Foundation/NSUserDefaults.h> #include <Foundation/NSUserDefaults.h>
#include <Foundation/NSTimer.h>
#include "AppKit/NSDocumentController.h" #include "AppKit/NSDocumentController.h"
#include "AppKit/NSOpenPanel.h" #include "AppKit/NSOpenPanel.h"
@ -278,7 +280,24 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
- (void) setAutosavingDelay: (NSTimeInterval)autosavingDelay - (void) setAutosavingDelay: (NSTimeInterval)autosavingDelay
{ {
static NSTimer *autosavingTimer;
if (autosavingTimer)
{
[autosavingTimer invalidate];
DESTROY (autosavingTimer);
}
_autosavingDelay = autosavingDelay; _autosavingDelay = autosavingDelay;
if (autosavingDelay > 0)
{
autosavingTimer =
[NSTimer scheduledTimerWithTimeInterval: autosavingDelay
target: self
selector: @selector(_autosaveDocuments:)
userInfo: nil
repeats: YES];
RETAIN (autosavingTimer);
}
} }
- (id) makeUntitledDocumentOfType: (NSString *)type - (id) makeUntitledDocumentOfType: (NSString *)type
@ -572,7 +591,32 @@ static NSDictionary *TypeInfoForHumanReadableName (NSArray *types, NSString *typ
withContentsOfURL: (NSURL *)contents withContentsOfURL: (NSURL *)contents
error: (NSError **)err error: (NSError **)err
{ {
// FIXME if ([contents isFileURL])
{
NSString *type =
[self typeFromFileExtension: [[contents path] pathExtension]];
id document =
[self makeDocumentForURL: url
withContentsOfURL: contents
ofType: type
error: err];
if (document)
{
[self addDocument:document];
if ([self shouldCreateUI])
{
[document makeWindowControllers];
[document showWindows];
}
return YES;
}
}
else
{
// FIXME: set error
*err = nil;
}
return NO; return NO;
} }
@ -1200,5 +1244,152 @@ static NSString *NSViewerRole = @"Viewer";
return [self _displayNamesForTypes: return [self _displayNamesForTypes:
[self _editorTypesForClass: documentClass]]; [self _editorTypesForClass: documentClass]];
} }
static NSMapTable *autosavedDocuments;
static NSString *processName;
- (NSString *) _autosaveDirectory: (BOOL)create
{
NSArray *paths =
NSSearchPathForDirectoriesInDomains (NSLibraryDirectory,
NSUserDomainMask,
YES);
NSString *path = [paths objectAtIndex:0];
path = [path stringByAppendingPathComponent: @"Autosave"];
if (create)
{
BOOL isDir;
NSFileManager *fm = [NSFileManager defaultManager];
if ([fm fileExistsAtPath: path isDirectory: &isDir] == NO)
{
if (![fm createDirectoryAtPath: path attributes: nil])
return nil;
}
else if (isDir == NO)
{
if (![fm removeFileAtPath: path handler: nil] ||
![fm createDirectoryAtPath: path attributes: nil])
return nil;
}
}
return path;
}
- (void) _autosaveDocuments: (NSTimer *)timer
{
id document;
int i, n = [_documents count];
for (i = 0; i < n; i++)
{
document = [_documents objectAtIndex: i];
if ([document autosavingFileType] && [document hasUnautosavedChanges])
{
[document autosaveDocumentWithDelegate: nil
didAutosaveSelector: NULL
contextInfo: NULL];
}
}
}
- (BOOL) _reopenAutosavedDocuments
{
BOOL didOpen = NO;
if (!autosavedDocuments)
{
NSArray *autosaved;
NSString *path;
autosavedDocuments =
NSCreateMapTable (NSObjectMapKeyCallBacks,
NSObjectMapValueCallBacks,
1);
if (!processName)
processName = [[[NSProcessInfo processInfo] processName] copy];
path = [self _autosaveDirectory: NO];
path = [path stringByAppendingPathComponent: processName];
path = [path stringByAppendingPathExtension: @"plist"];
autosaved = [NSArray arrayWithContentsOfFile: path];
if (autosaved)
{
int i, n = [autosaved count];
NSFileManager *fm = [NSFileManager defaultManager];
for (i = 0; i < n; i++)
{
NSDictionary *dict = [autosaved objectAtIndex: i];
NSString *location = [dict objectForKey: @"Location"];
NSString *autosavedLoc = [dict objectForKey: @"AutosavedLocation"];
NSURL *url = location ? [NSURL URLWithString: location] : nil;
NSURL *autosavedURL =
autosavedLoc ? [NSURL URLWithString: autosavedLoc] : nil;
if (autosavedURL && [fm fileExistsAtPath: [autosavedURL path]])
{
NSError *err;
if ([self reopenDocumentForURL: url
withContentsOfURL: autosavedURL
error: &err])
didOpen = YES;
}
}
}
}
return didOpen;
}
- (void) _recordAutosavedDocument: (NSDocument *)document
{
BOOL changed = NO;
NSURL *url = [document autosavedContentsFileURL];
if (!autosavedDocuments)
autosavedDocuments =
NSCreateMapTable (NSObjectMapKeyCallBacks,
NSObjectMapValueCallBacks,
1);
if (!processName)
processName = [[[NSProcessInfo processInfo] processName] copy];
if (url)
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
if ([document fileURL])
[dict setObject: [document fileURL] forKey: @"Location"];
[dict setObject: url forKey: @"AutosavedLocation"];
NSMapInsert (autosavedDocuments, document, dict);
[dict release];
changed = YES;
}
else if (NSMapGet (autosavedDocuments, document))
{
NSMapRemove (autosavedDocuments, document);
changed = YES;
}
if (changed)
{
NSString *path = [self _autosaveDirectory: YES];
NSArray *autosaved = NSAllMapTableValues (autosavedDocuments);
NSFileManager *fm = [NSFileManager defaultManager];
path = [path stringByAppendingPathComponent: processName];
path = [path stringByAppendingPathExtension: @"plist"];
if ([autosaved count] == 0)
{
[fm removeFileAtPath: path handler: nil];
}
else
{
[autosaved writeToFile: path atomically:YES];
}
}
}
@end @end

View file

@ -32,6 +32,8 @@
#include <AppKit/NSDocumentController.h> #include <AppKit/NSDocumentController.h>
@class NSTimer;
@interface NSDocumentController (Private) @interface NSDocumentController (Private)
- (NSArray *)_editorAndViewerTypesForClass:(Class)documentClass; - (NSArray *)_editorAndViewerTypesForClass:(Class)documentClass;
- (NSArray *)_editorTypesForClass:(Class)fp12; - (NSArray *)_editorTypesForClass:(Class)fp12;
@ -39,6 +41,10 @@
- (NSString *)_nameForHumanReadableType: (NSString *)type; - (NSString *)_nameForHumanReadableType: (NSString *)type;
- (NSArray *)_displayNamesForTypes: (NSArray *)types; - (NSArray *)_displayNamesForTypes: (NSArray *)types;
- (NSArray *)_displayNamesForClass: (Class)documentClass; - (NSArray *)_displayNamesForClass: (Class)documentClass;
- (NSString *)_autosaveDirectory: (BOOL)create;
- (void)_autosaveDocuments: (NSTimer *)timer;
- (BOOL)_reopenAutosavedDocuments;
- (void)_recordAutosavedDocument: (NSDocument *)document;
@end @end
@ -47,6 +53,7 @@
@interface NSDocument (Private) @interface NSDocument (Private)
- (void)_removeWindowController:(NSWindowController *)controller; - (void)_removeWindowController:(NSWindowController *)controller;
- (NSWindow *)_transferWindowOwnership; - (NSWindow *)_transferWindowOwnership;
- (void)_removeAutosavedContentsFile;
@end @end