Re: Using NSSavePanel to export to UTTypeFolder without a file extension.
Re: Using NSSavePanel to export to UTTypeFolder without a file extension.
- Subject: Re: Using NSSavePanel to export to UTTypeFolder without a file extension.
- From: Thomas Bunch <email@hidden>
- Date: Wed, 23 Jan 2013 12:02:58 -0800
Following up on my own question, NSSavePanel can be made to “work” under PowerBox with a UTI that has no file extension, but it does get upset with itself and fail an assertion when it elects to automatically turn on the "hide extension" checkbox in response to the user changing the file type. The exception is appended below. The default assertion handler just logs it and everything seems to work, though I do worry that we're not getting a proper reply to the request.
Updated source here: https://www.dropbox.com/s/qjgnpee644c5eo3/NSSavePanel-kUTTypeFolder.zip
I'll be filing a radar if bugreporter comes back up.
Highlighted changes since I last posted:
- I had neglected to set a code signing identity, so the example wasn't actually sandboxed before.
- added the com.apple.security.files.user-selected.read-write entitlement
- declared exportable formats as NSExportableTypes using real UTIs rather than the old school NSExportableAs wherein I was using CFBundleTypeMIMETypes completely wrong anyway.
Here is the assertion that fails. Should you wish to download and build the project the steps to reproduce the assertion are as follows:
- in Finder preferences, turn off "Show all filename extensions"
- build and run the project
- File -> Export…,
- switch the File Format. The hide extension checkbox will turn on. Turn it off.
- repeat the previous step as necessary. This seems to be a race condition and may take many tries.
2013-01-23 11:21:35.728 NSSavePanel-kUTTypeFolder[26816:303] *** Assertion failure in -[NSRemoteSavePanel connection:didReceiveRequest:], /SourceCache/RemoteViewServices/RemoteViewServices-80.5/NSRemoteSavePanel.m:1713
2013-01-23 11:21:35.730 NSSavePanel-kUTTypeFolder[26816:303] caught exception {
name = NSInternalInconsistencyException;
reason = "window allowed unexpectedly";
} during request {requestType: 15, connections: 0, ports: 0, arguments: {
NSRemotePanelRequestKVOStateDidChangeNewStateKey = 1;
NSRemotePanelRequestKVOStateDidChangePathKey = isExtensionHidden;
}} with call stack (
0 CoreFoundation 0x00007fff8a9e30a6 __exceptionPreprocess + 198
1 libobjc.A.dylib 0x00007fff8baca3f0 objc_exception_throw + 43
2 CoreFoundation 0x00007fff8a9e2ee8 +[NSException raise:format:arguments:] + 104
3 Foundation 0x00007fff86ae96a2 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 189
4 RemoteViewServices 0x00007fff85bf3dc1 -[NSRemoteSavePanel connection:didReceiveRequest:] + 131
5 RemoteViewServices 0x00007fff85bec73f __block_global_4 + 68
6 CoreFoundation 0x00007fff8a9a0272 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 18
7 CoreFoundation 0x00007fff8a960a4f __CFRunLoopDoBlocks + 255
8 CoreFoundation 0x00007fff8a985220 __CFRunLoopRun + 1904
9 CoreFoundation 0x00007fff8a9846b2 CFRunLoopRunSpecific + 290
10 AppKit 0x00007fff860fb14b -[NSCarbonMenuImpl performActionWithHighlightingForItemAtIndex:] + 150
11 AppKit 0x00007fff85de846f -[NSMenu _internalPerformActionForItemAtIndex:] + 36
12 AppKit 0x00007fff85de82f7 -[NSCarbonMenuImpl _carbonCommandProcessEvent:handlerCallRef:] + 135
13 AppKit 0x00007fff860f4245 NSSLMMenuEventHandler + 342
14 HIToolbox 0x00007fff8904af0a _ZL23DispatchEventToHandlersP14EventTargetRecP14OpaqueEventRefP14HandlerCallRec + 1206
15 HIToolbox 0x00007fff8904a3d9 _ZL30SendEventToEventTargetInternalP14OpaqueEventRefP20OpaqueEventTargetRefP14HandlerCallRec + 410
16 HIToolbox 0x00007fff890601bd SendEventToEventTarget + 40
17 HIToolbox 0x00007fff89096e89 _ZL18SendHICommandEventjPK9HICommandjjhPKvP20OpaqueEventTargetRefS5_PP14OpaqueEventRef + 443
18 HIToolbox 0x00007fff8903bc11 SendMenuCommandWithContextAndModifiers + 59
19 HIToolbox 0x00007fff8903bbc3 SendMenuItemSelectedEvent + 254
20 HIToolbox 0x00007fff8903ba4f _ZL19FinishMenuSelectionP13SelectionDataP10MenuResultS2_ + 94
21 HIToolbox 0x00007fff891b2009 _ZL19PopUpMenuSelectCoreP8MenuData5PointdS1_tjPK4RecttjS4_S4_PK10__CFStringPP13OpaqueMenuRefPt + 1673
22 HIToolbox 0x00007fff891b1924 _HandlePopUpMenuSelection7 + 629
23 AppKit 0x00007fff8617761b _NSSLMPopUpCarbonMenu3 + 3916
24 AppKit 0x00007fff864d8d2a _NSPopUpCarbonMenu3 + 39
25 AppKit 0x00007fff861765dc -[NSCarbonMenuImpl popUpMenu:atLocation:width:forView:withSelectedItem:withFont:withFlags:withOptions:] + 346
26 AppKit 0x00007fff8633a315 -[NSPopUpButtonCell trackMouse:inRect:ofView:untilMouseUp:] + 540
On Jan 17, 2013, at 5:17 PM, Thomas Bunch <email@hidden> wrote:
> All good thoughts, but hold the phone.
>
> I think I have a mishmash a lot of old deprecated info plist keys and outright misapprehensions in my Info.plist, especially under CFBundleDocumentTypes and UTExportedTypeDeclarations. I think the file extension swapping will behave correctly once I've gotten it fixed. I will post an update tomorrow.
>
> I don't think your number 2 will be workable, since we're sandboxed/PowerBoxed here. If the NSSavePanel gives me a security scoped URL to a give file system path, I may not be able to write to a variant of that path.
>
> -Tom
>
> On Jan 17, 2013, at 4:57 PM, Quincey Morris <email@hidden> wrote:
>
>> On Jan 17, 2013, at 16:20 , Thomas Bunch <email@hidden> wrote:
>>
>>> Yes, in fact, I do exactly this. It's kind of suboptimal, in that NSSavePanel will first give you a warning:
>>>
>>>
>>> “Foo.oplx” already exists. Do you want to replace it?” and so on… the user will probably reflexively accept that one.
>>>
>>> Then we check and see that you're asking to dump a folder of web stuff, that the folder exists, that its contents don't look like a bucket of HTML and we pop up a second “That seems like a really bad idea, really really blow that away?” sheet.
>>
>> Working backwards, it seems to me you really, really want to retain this last behavior in any situation where you're writing a folder to replace a folder. Even if the extensions behaved the way they should, you could easily get a situation where the user selected an entirely unrelated folder, and "reflexively" accepted the warning about that replacement.
>>
>> If that's so, the extension behavior is somewhat a red herring. When you're exporting multiple types via a single dialog, writing one of the types on top of one of the other types is probably something you should always check for.
>>
>> A couple of other thoughts:
>>
>> 1. Is there any value in configuring your save panel to hide the "show/hide extension" checkbox, and to always display extensions. That will, incidentally, ensure that the saved item will show its extension in the Finder, regardless of what the user normally does. You could then go ahead and use '.htmld' without ever misleading the user that it wasn't there.
>>
>> 2. You could probably arrange to keep the "htmld" behavior in the dialog, and rename the folder to remove the "htmld" at the end of the save. (You'd have to use a NSDocument "perform…" method to ensure you re-set the document's fileURL at the right time afterwards.) The drawback, of course, is that the un-extensioned folder may already exist, and you don't know what the user wants you to do about that. You *might* be able to mitigate that problem via the save panel delegate's validate method.
>>
>> 3. Is there any value in the idea of exporting folders of stuff (that is, exporting things that don't have a unique UTI) via a separate menu item that doesn't involve choosing a type in its save panel?
>>
>
> _______________________________________________
>
> Cocoa-dev mailing list (email@hidden)
>
> Please do not post admin requests or moderator comments to the list.
> Contact the moderators at cocoa-dev-admins(at)lists.apple.com
>
> Help/Unsubscribe/Update your Subscription:
>
> This email sent to email@hidden
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden