Prevent warning when NSDocument file is programmatically renamed
Asked Answered
W

3

6

My application allows the user to rename documents that are currently open. This is trivial, and works fine, with one really annoying bug I can't figure out. When a file is renamed, AppKit (kindly) warns the user the next time they try to save the document. The user says "OK" and everything continues as normal. This makes sense when something external to the application changed the document, but not when it was actually done by the document itself.

The code goes something like this:

-(void)renameDocumentTo:(NSString *)newName {
  NSURL *newURL = [[[self fileURL] URLByDeletingLastPathComponent]
                                   URLByAppendingPathComponent:newName];

  NSFileManager *fileManager = [NSFileManager defaultManager];
  [fileManager moveItemAtURL:[self fileURL] toURL:newURL];
  NSDictionary *attrs = [fileManager attributesForItemAtPath:[newURL path] error:NULL];

  [self setFileURL:newURL];
  [self setFileModificationDate:[attrs fileModificationDate]];
}

One would think that expressly setting the new URL and modification date on the document would be enough, but sadly it's not. Cocoa still generates the warning.

I've tried changing the order (setting the new URL on the document, THEN renaming the file) but this doesn't help.

I've also tried a fix suggested by a user on an old post over at CocoaDev:

[self performSelector:@selector(_resetMoveAndRenameSensing)];

Even this does not stop the warning however, and I'm guessing there has to be a proper way to do this using the documented API. How does Xcode handle things when a user clicks a file on the project tree and renames it to something else. It doesn't warn the user about the rename, since the user actually performed the rename.

What do I need to do?

Wallop answered 3/12, 2010 at 9:17 Comment(2)
I have started bounty for help with this. I'm really getting nowhere with it unfortunately. A simple test case is just to create a blank document app that opens a .txt file (or anything really), add a menu item whose action renames the opened file to something else (and updates the document objects with the new URL). I'm trying to circumvent the warning when try to save the file the first time after the rename.Wallop
Oh, and this needs to work if the document is currently edited. Saving to disk, closing the document, moving the file and then and re-opening it could have undesirable effects if unsaved changes exist.Wallop
F
3

There isn't much on this in the main docs. Instead, have a look at the 10.5 release notes: http://developer.apple.com/library/mac/#releasenotes/Cocoa/AppKitOlderNotes.html%23X10_5Notes under the heading "NSDocument Checking for Modified Files At Saving Time"

(In the case of Xcode, it has a long history and I wouldn't be surprised if if doesn't use NSDocument for files within the project)

It is worth noting that moving a file does not change its modification date, so calling -setFileModificationDate: is unlikely to have any effect.

So one possibility could be to bypass NSDocument's usual warning like so:

- (void)saveDocument:(id)sender;
{
    if (wasRenamed)
    {
        [self saveToURL:[self fileURL] ofType:[self fileType] forSaveOperation:NSSaveOperation delegate:nil didSaveSelector:nil contextInfo:NULL];
        wasRenamed = NO;
    }
    else
    {
        [super saveDocument:sender];
    }
}

Ideally you also need to check for the possibility of:

  1. Ask app to rename the doc
  2. Renamed file is then modified/moved by another app
  3. User goes to save the doc

At that point you want the usual warning sheet to come up. Could probably be accomplished by something like:

- (void)renameDocumentTo:(NSString *)newName
{
    // Do the rename

    [self setFileURL:newURL];
    wasRenamed = YES; // MUST happen after -setFileURL:
}

- (void)setFileURL:(NSURL *)absoluteURL;
{
    if (![absoluteURL isEqual:[self fileURL]]) wasRenamed = NO;
    [super setFileURL:absoluteURL];
}

- (void)setFileModificationDate:(NSDate *)modificationDate;
{
    if (![modificationDate isEqualToDate:[self fileModificationDate]]) wasRenamed = NO;
    [super setFileModificationDate:modificationDate];
}

Otherwise, your only other choice I can see is to call one of the standard save/write methods with some custom parameters that prompt your document subclass to move the current doc rather than actually save it. Would be trickier I think. Perhaps define your own NSSaveOperationType?

With this technique the doc system should understand that the rename was part of a save-like operation, but it would need quite a bit of experimentation to be sure.

Florentinoflorenza answered 10/12, 2010 at 12:38 Comment(1)
Good answer, thanks. I had already tried your second suggestion without success, though it probably can be forced to work, somehow. I think your first suggestion (overriding saveDocument: and using a transient wasRenamed flag) should work well. Just about to test it now.Wallop
R
1

Much inspired from @Mike's answer, I got the "moved to" message not to show up anymore by re-routing NSSaveOperation to NSSaveAsOperation. In my NSDocument subclass:

  • I overload saveDocumentWithDelegate:didSaveSelector:contextInfo: to determine the save URL and document type (assigning those to self); if the old fileURL exists, I move that to the new location
  • Inside saveDocumentWithDelegate:didSaveSelector:contextInfo: I redirect the call to [self saveToURL:self.fileURL ofType:self.fileType forSaveOperation:NSSaveAsOperation completionHandler: ...] instead of [super saveDocumentWithDelegate:didSaveSelector:contextInfo:]

This works for me.

Radiotelegram answered 11/12, 2015 at 7:45 Comment(0)
R
0

Isn't it possible to programmatically answer the question for the user? Or you can save immediately after renaming, this way a user gets every answer in one go.

I see that this problem is up and running for some time, so telling you to read the reference won't do any good i guess..

Hope i helped a little bit although it doesn't fix your problem directly

Ramunni answered 10/12, 2010 at 12:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.