Reputation: 12043
I have implemented State Restoration in my AppKit (Cocoa) app. That works.
However: If the user opens the app by double clicking a document file saved by the app, then I want the app not to restore the previous windows but only open the document that was opened in Finder.
The problem is that the state restoration appears to happen before the handlers for opening document files are invoked, so even if I had a way to suppress the state restoration, I have no knowledge of the pending openURL calls at that point, yet.
I understand that I could check the launch arguments, but that seems to be a rather dirty way. I'd think that I should be able to query the stored file paths/URLs from the app object somehow, but I can't find anything.
Or can I change the order of restoring documents vs. opening documents? I've so far found out that both happens deep inside [NSDocument restoreDocumentWindowWithIdentifier:state:completionHandler:]
, inside the completionHandler function. I could not find an overridable function below that where I'd have more control over this, though.
I implement State Restauration by overriding restoreStateWithCoder:
and encodeRestorableStateWithCoder:
in my NSDocument subclass.
Upvotes: 6
Views: 757
Reputation: 15589
Undocumented (= unsupported) feature: Restoring all documents when the app is launched by opening a document can be disabled. In applicationWillFinishLaunching
do
[NSUserDefaults.standardUserDefaults setBool:YES forKey:@"NSDiscardWindowsOnDocumentOpen"];
Upvotes: 2
Reputation: 8793
Can you maybe delay state restoration? Queue up all the data you get for restoring state, return an "everything fine" value, and then wait for 'oapp' Apple Event to restore it (or just clear the queue if an 'odoc' arrives).
Upvotes: -1
Reputation: 12043
My current solution is to intercept the odoc
AppleEvent, and if that's invoked, set a flag that causes [NSApp restoreWindowWithIdentifier:]
to return NO.
Here's the code inside AppDelegate.m
:
static AEEventHandlerUPP openDocHandler = NULL;
static SRefCon openDocRefcon;
static pascal short HandleOpenDocMessage (const AEDesc *theAppleEvent, AEDesc *reply, void* handlerRefcon)
{
// intercepts the "odoc" Event to check if we're about to open any files, and then continues with the original handler
NSAppleEventDescriptor *event = [NSAppleEventDescriptor.alloc initWithAEDescNoCopy:theAppleEvent];
NSURL *url = [NSURL URLWithString:[[event paramDescriptorForKeyword:keyDirectObject] stringValue]];
if (url) {
NSString *docType = [NSDocumentController.sharedDocumentController typeForContentsOfURL:url error:nil];
if ([docType isEqualToString:@"..."]) { // I can check for particular types here in case I can open several
[(Application*)NSApplication.sharedApplication setSuppressRestorationOfSearchWindows:YES];
}
}
return openDocHandler (theAppleEvent, reply, openDocRefcon);
}
- (void)applicationWillFinishLaunching:(NSNotification *)notification
{
AEGetEventHandler(kCoreEventClass, kAEOpen, &openDocHandler, &openDocRefcon, false);
AEInstallEventHandler(kCoreEventClass, kAEOpen, HandleOpenDocMessage, (__bridge SRefCon)(self), false);
...
Inside Application.h
:
@property (assign) BOOL suppressRestorationOfSearchWindows;
Inside Application.m
:
- (BOOL)restoreWindowWithIdentifier:(NSString *)identifier state:(NSCoder *)state completionHandler:(void (^)(NSWindow *, NSError *))completionHandler // override
{
if ([identifier isEqualToString:@"MyWindowType"] && self.suppressRestorationOfSearchWindows) {
// prevent window restoration on startup if the user launched this app by opening a file of my particular type
return NO;
} else {
// continue with regular state restoration
return [super restoreWindowWithIdentifier:identifier state:state completionHandler:completionHandler];
}
}
Feels like a hack, though.
Upvotes: 2