Cocoa: NSPathControl NSOpenPanel, get notification when user clicked OK - cocoa

I have a NSPathControll set up in IB and I use this method when I set up the NSOpenPanel:
-(void)pathControl:(NSPathControl *)pathControl willDisplayOpenPanel:(NSOpenPanel *)openPanel
{
[openPanel setDelegate:self];
[openPanel setCanChooseDirectories:YES];
[openPanel setCanCreateDirectories:YES];
[openPanel setCanChooseFiles:NO];
[openPanel setPrompt:#"Choose"];
}
I would like to know when the user clicked the OK-button (in this case the 'Choose'-button).
If I use -(void)panel:(id)sender directoryDidChange:(NSString *)path I only get notified when the user double clicked on a folder.
Any ideas?
Thanks in advance!

I solved it by using:
-(BOOL)panel:(id)sender isValidFilename:(NSString *)filename
I was looking for a "panel:(id)sender didClose" or something similar.
/M

Related

force NSDocument to save after creation

In its documents, my application uses a lot of assets that are relative to the document path. So the document must be saved before assets can be added. How can I force-call a [NSDocument saveDocumentAs] ?
I managed to do parts of it : by creating my own document controller, and inside openUntitledDocumentAndDisplay: force a call like this :
- (id)openUntitledDocumentAndDisplay:(BOOL)displayDocument error:(NSError **)outError
{
NSDocument * res = [super openUntitledDocumentAndDisplay:displayDocument error:outError];
[res saveDocumentAs:self];
return res;
}
This forces the save dialog to appear, but unfortunately I can not check whether the user pressed cancel : the saveDocumentAs call is asynchronous and continues immediately !
Is there a way to fix this ?
I had a similar problem. By using:
saveDocumentWithDelegate:(id)delegate didSaveSelector:(SEL)didSaveSelector contextInfo:(void *)contextInfo
you can defer your processing (or not) until after the document save dialogue has completed. This means you can find out whether the user cancelled or not. You split your processing in two, do whatever preparation you need and put the rest (that depends upon a successful save) into another method. If you use something like:
[self saveDocumentWithDelegate:self didSaveSelector:#selector(actuallyDoIt: didSave: contextInfo:) contextInfo:nil];
The document will be saved but, critically, if it has not been saved before, the Save dialogue will appear so the user can input a file name. Once he/she has done that, or cancelled, your method actuallyDoIt: (or whatever) is invoked. The didSave: parameter tells you whether the save actually happened (essentially, did the user cancel) so you can either continue or offer an alert explaining politely to the user that nothing's going to happen until they save.
I have a similar thing in my application, in my case if the user tries to do something, I pull up a prompt to say 'This requires you to save the document first' with buttons to cancel or save.
If you want to absolutely force it, then instead of using saveDocumentAs, just display your own NSSavePanel. Run it modally, check the result, save the document with the result, and if this doesn;t go smoothly, call it again. You can check if the document is saved by looking for a valid value for it's file path.
NSSavePanel can run modally. Here is how it can/should look like.
- (id)openUntitledDocumentAndDisplay:(BOOL)displayDocument error:(NSError *__autoreleasing *)outError
{
Document *document;
NSSavePanel *panel = [NSSavePanel savePanel];
panel.prompt = #"Create";
NSInteger modalCode = [panel runModal];
if (modalCode == NSModalResponseOK) {
NSURL *URL = [panel URL];
NSError *docError;
document = [[Document alloc] initWithType:nil & docError];
[document saveToURL:URL ofType:nil forSaveOperation:NSSaveOperation completionHandler:^(NSError *error){
if(error) {
return nil;
}
[self addDocument:document];
[document makeWindowControllers];
if (displayDocument) {
[document showWindows];
}
}];
}
return document;
}
To sum up for reference:
Create custom nsdocumentsubclass in XIB (no XIB -> app did finish launching)
override openUntitledDocumentAndDisplay
(NSData *)dataOfType:(NSString *)typeName error:(NSError **)outError;

how do i reference the window associated with a nswindowcontroller

I have a subclassof NSWindowController with an associated xib file.
From my app delegate I display this using the following code:
if(!wc)
wc = [[NSWindowController alloc]initWithWindowNibName:#"MyNewWindowController"];
[wc showWindow:nil];
This displays the window. Now I want to reference that window in the new window controller but can't work out how. Specifically I have a button on the new window and I want to write something like:
- (IBAction)doStuffAndCloseWindow:(id)sender
{
[self doSomeStuff];
[*window* orderOut:nil];
}
I've tried creating a window variable (like the one created in appdelegate) but the compiler says my window variable is private.
So have do I declare and reference a window in my MyNewWindowController.m?
Thanks
That would be the 'window' method of NSWindowController. It's also a property that you can access via ".window".
So, in the first code snippet, that would be:
[wc window]
and in the second code snippet (assuming "doStuffAndCloseWindow" is part of your subclassed NSWindowController):
- (IBAction)doStuffAndCloseWindow:(id)sender
{
[self doSomeStuff];
[[self window] orderOut:nil];
}
Thanks to Michael, see above, first declare your subclassed NSwindowController thus
#property IBOutlet MyNewWindowController *wc;
Then in the implementation of the subclassed window controller, you can refer to the associated window with
[[self window] .....];
For example
[[self window] orderOut:self];

Sandbox NSOpenPanel Error 1000

I'm pretty much ready to release the first sandbox-enabled Mac application. The only entitlement that I need is User Selected File. The user clicks on a toolbar button to select one or more image files, which doesn't cause trouble. The user also clicks on a button to select a folder. When they do, the Xcode output window indicates the following error message. CGSSetIgnoresCycle: error 1000 setting or clearing window tags. If the user cancels the select-folder operation without selecting one, they get an additional error message on top of the first one. It says PSsetwindowlevel, error setting window level (1000). The application does not crash. Are these error messages things that I need to worry? If I ask Google, I don't get many search results. Anyway, the following code is used when the user clicks on a button to select a folder.
- (IBAction)system1Selected:(id)sender {
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setAllowsMultipleSelection:NO];
[panel setCanChooseDirectories:YES];
[panel setCanChooseFiles:NO];
NSString *currentpath = systempath1.stringValue;
if ([self fileExists:currentpath]) {
[panel setDirectoryURL:[NSURL fileURLWithPath:currentpath]];
} else {
[panel setDirectoryURL:[NSURL fileURLWithPath:[self filePathA]]];
}
if ([panel runModal] != NSFileHandlingPanelOKButton) {
//return nil;
} else {
NSURL *url = [[panel URLs] lastObject];
systempath1.stringValue = [url path];
}
}
Thank you for your advice.
This error has been there for a while in all my applications. It doesn't seem something you need to worry about. It disappears without changing anything and, probably, it depends on a bug of the NSOpenPanel (I didn't manage to get the same error using the NSSavePanel).
In my opinion, there's no need to investigate further.

Prompting the user to logon for encrypted documents

I have encrypted documents. After unarchiving each document, I need to prompt the user to enter the document password from a logon sheet. I have means to validate password entry against file contents (this part is done). If the password is incorrect the document shall be closed. If the password is correct the document window shall be presented with document contents.
When I attempt to load the logon sheet (via its controller) in the document's windowControllerDidLoadNib method I have unrecognized selector error as shown below:
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
[super windowControllerDidLoadNib:aController];
if (!newPasswordController){
newPasswordController = [[NewPasswordController alloc] init];
newPasswordSheet = [newPasswordController window];
}
[NSApp beginSheet:newPasswordSheet modalForWindow:[self window]
modalDelegate:self
didEndSelector:#selector(didNewPasswordEnd:returnCode:contextInfo:)
contextInfo:nil];
}
[_NSControllerObjectProxy copyWithZone:]: unrecognized selector sent to instance
The method [NewPasswordController init] is implemented as follows:
-(id)init
{
self = [super initWithWindowNibName:#"NewPassword"];
if (self) {
}
return self;
}
where the logon sheet nib file is called NewPassword.
I wonder what went wrong. What is the best way to solve this problem?
Aside from unrecognized selector issue (which I left with Apple) I managed to address the original issue Prompting the user to logon for encrypted documents and here is the solution.
Note every document is password protected and they could potentially respond to different passwords (so the issue is not 'password protected application' but 'password protected documents').
Simply inject the following code when we are about to read document contents to pop up an application modal window to verify the document password:
- (BOOL)readFromData:(NSData *)data
ofType:(NSString *)typeName
error:(NSError **)outError
{
PasswordController *passwordController = [[PasswordController alloc] init];
NSWindow *passwordSheet = [passwordController window];
NSApplication* app = [NSApplication sharedApplication];
NSInteger iret = [app runModalForWindow:passwordSheet];
NSLog(#"password dialog returned = %ld", iret);
if (iret != 0)
{
[app stop:self];
return NO;
}
[passwordController release];
...
You may also pop up another kind of window when the document is saved the first time in dataOfType, forcing the user to set the document's password.
This issue is answered now.
The issue unrecognized selector was resolved after I constructed the xib file from scratch. This indicates a serious problem though concerning IB in XCode 4, as properties of the sheet and steps taken to create bindings, key-payths etc seemed identical in both cases. Something I have done during interface construction from IB caused a corruption in the xib file in my first attempt.
I'll leave it to forum administration to delete or keep this issue. If it is value to anyone I'll file a bug report with Apple (see below)

NSMenu doesn't start tracking

I have a little cocoa app which usually operates in the background (as agent). Sometimes I'd like to be able to popup a contextmenu (no window or s.th. visible at this time).
As I'm only targetting Snow Leopard I tried this:
if (windows) {
NSMenu *theMenu = [[[NSMenu alloc] initWithTitle:#"test"] autorelease];
[theMenu setShowsStateColumn:NO];
[theMenu setAutoenablesItems:NO];
for (id item in windows) {
NSString *labelText = #"some text";
NSMenuItem *theMenuItem = [[[NSMenuItem alloc] initWithTitle:labelText
action:#selector(menuItemSelected:)
keyEquivalent:#""] autorelease];
[theMenuItem setTarget:self];
[theMenuItem setRepresentedObject:item];
[theMenuItem setEnabled:YES];
[theMenuItem setImage:icon];
[theMenu addItem:theMenuItem];
}
[theMenu popUpMenuPositioningItem:nil atLocation:[NSEvent mouseLocation] inView:nil];
}
The menu popsup perfectly but if I hover the items with the mouse cursor they don't highlight and I can't click them.
The menuItemSelected: method looks just like this:
-(IBAction)menuItemSelected:(id)sender {
}
Any idea what I'm doing wrong?
I suspect that the windowing system doesn't consider your application to be active, and thus doesn't send mouse events to the menu you've created.
As an experiment, try creating a dummy window before popping up the menu. I'd create an NSPanel, possibly with style NSNonActivatingPanelMask. makeKeyAndOrderFront: your window/panel, then pop up the menu and see what happens.
If this works, I'd stick with the approach and hide the window.

Resources