Transmit NSMenu commands to the current window? - cocoa

Newbie OS X developer here, although fairly experienced with iOS.
I am missing something basic about the way the top-level NSMenu interacts with the application. I want the File->Save command to go to the current window. So far I have only been able to receive NSMenu actions in the app delegate. Am I supposed to keep track of the active window there and invoke methods from the app delegate?

Firstly, it sounds like you need to read up on Mac menu handling, because there are a lot of things you need to know about in order to deal with menus correctly.
To answer your specific question, if a menu item has a target of nil, such as the Save menu item, then the menu handling system walks up the responder chain, starting from the currently active control or view (first responder), looking for an object that implements the action selector for that menu item.
If you don't understand how the responder chain works, you should read about that too, because it's fundamental to understanding how Mac apps work.
If you want your window controller to handle the ‑save: action when its window is the main window, then all you need to do is implement the ‑save: action in your window controller. Because the window controller is in the responder chain before the application delegate, its implementation of the method will be used.

Related

Overriding close action in OSX

How do I override the close button action to show a confirm dialog?
Would placing code in the app delegate's
applicationWillTerminate
be the correct place for such code?
I have an app using CloudKit.
It saves way too constantly, where it causes errors trying to save to iCloud.
So I'm trying to show a dialog to confirm if the user wants to sync before quitting.
If you want to block a window from closing, the NSWindow's delegate is where it's at. Be it either the AppDelegate (which is the default for most Xcode templates) or an NSWindowController if that's how you presented your window. The method you want to implement from NSWindowDelegate is -windowShouldClose: or -windowWillClose:. If you want to prevent the closing of the window, simply return NO from -windowShouldClose:.
If you want to stop the app from quitting, you should look into NSApplicationDelegate and override -applicationShouldTerminate: and return NSTerminateCancel to prevent the app from terminating.
In short, the "should" methods allow you to prevent the action from occurring, the "will" methods are just saying that it will happen and you can deal with it.

How to choose a specific instance of a custom class added to Interface Builder, or suggest a better organization

I'm writing a single-window, non-document-based app for Mac OSX. I'm coming from an iOS project where we avoided Interface Builder like the plague. I'm forcing myself to use it for the Mac project just for the experience.
My program's menu and its main window are in two different nibs. The window contains a toolbar. Of course many of the toolbar and menu items are representing the same function, so I want to route them through the same code. Initially I put all the handlers for the menu/toolbar in my application controller and wired them to the menu/toolbar using Interface Builder. Then I realized many of the operations affect the window and its contents, not the entire program, so I thought it more appropriate to put those methods in the main window controller.
To support this I added an NSObject to my window nib, changed its class to my window controller class, and wired those up.
Of course what I'm seeing now is that my app controller creates an instance of the window controller when it creates my one-and-only window, AND the nib for the menu creates another instance of the window controller when it builds the interface from the nib. That may or may not prove to be problematic, but it seems wasteful. Seems like everybody should be talking to the one-and-only window controller I create in applicationDidFinishLaunching.
So question 1 is "How do I tell my program to use the existing window controller when building the user interface from the nib?"
If the answer to question 1 is "You don't" then I'm looking for a suggested organizational pattern to follow. Do I put all the menu/toolbar handlers in the app controller and route from there? Or is there some other common way of doing this that is consistent with the zeitgeist of Cocoa?

Cocoa: Receiving dock icon click of already-running application

I'm writing an application with a main window that's displayed when the app starts. When the window is closed, I'd like the app to remain running (with a menu-bar menu), and if the user clicks on the dock icon again, I'd like the main window to be presented again.
I'm about 90% of the way there: my app properly keeps running after the main window is closed with Cmd-W, and since "Release When Closed" is unchecked, the window could be [makeKeyAndOrderFront:]-ed to show it again when the dock icon is clicked.
The only missing piece of this puzzle is intercepting the actual dock-icon click.
The other threads about this topic recommend implementing either applicationShouldHandleReopen:hasVisibleWindows: or applicationShouldOpenUntitledFile: in the window controller. I've done both, and neither one ever gets called.
Any other ideas?
The other threads about this topic recommend implementing either applicationShouldHandleReopen:hasVisibleWindows: or applicationShouldOpenUntitledFile: in the window controller.
That's only true if the window controller is the application's delegate. That is the object to which the application sends those messages.
I would not make a window controller the application's delegate, though. I typically make them two separate objects. Make one object specifically to be the application's delegate, and when that object receives the relevant delegate messages, send a message to your window controller telling it to do whatever it needs to do.
Actually, what I usually do in single-window apps is make the application's delegate create and own the window controller. You can respond to window closure by throwing away the WC, and respond to reopen by checking whether you have a WC and creating one (and thereby reopening the window) if you don't.
Use [NSApp setDelegate:self]; in awakeFromNib.

Can I know what called applicationShouldHandleReopen ?

I'm looking to differenciate a dock clic from a click on the app icon in the finder.
Can I know what called applicationShouldHandleReopen or is there another way to do it ?
applicationShouldHandleReopen:hasVisibleWindows: is sent to the application's delegate. Delegate messages are normally sent by the delegating object, which in this case would be the application object.
The application object sends that message to its delegate in order to handle the reopen-application Apple Event. So, to find the sender, install your own Apple Event handler for that event and get the sender from the event. (The sample code is in Pascal and uses Apple Event Manager, but you can translate it to Objective-C and NSAppleEventDescriptor.)
That said, what you're doing is very dubious from a UI perspective. Reopening is meant to do the same thing no matter which application is reopening you—and it is not limited to the Finder or the Dock. In the common case, it is literally the user trying to launch the app while it is already open.
It may make more sense to only do your “reopen” behavior when no windows are open. Cocoa's built-in document-based-apps support does this automatically; if you don't respond to applicationShouldHandleReopen:: or you return YES, the application tries to open a new document. You can perform the same check (it even tells you whether you have any windows open) and perform your desired behavior under the same condition.

Issue with Quit application menu in cocoa application

I have a cocoa application running on Mac OS 10.6.8
I am adding an entry to apple menu in the app, for quitting my application. The code is like this:
item = [menu addItemWithTitle: #"Quit Myapp" , NSLocalizedString(#"Quit", nil), applicationName] action:#selector(terminate:) keyEquivalent:#"q"];
[item setTarget:NSAPP];
Now, my problem is that when a modal dialog is opened using runModal of NSOpenPanel, this quit menu item is still enabled. Rest of the menu items are disabled as usual. I am not able to understand why.
If I change the above code so that menu item's target is not NSApp, but another cocoa object, then the problem disappears.
Could someone please let me know if it is a known issue. Is it wrong to set the NSApp as a menu item's target?
Why don't you use the Application Menu from Interface Builder?
I agree with the others that doing this is probably a bad idea. That said, set (or leave) the target as nil to target the responder chain. That will probably make it disable when a model window is up.
Update:
Hmm. Checking a new MainMenu NIB, I see that the Quit menu item does actually target the application object. So, that may not be the issue.
By the way, in the code snippets above, you're targeting NSAPP, whatever that is, rather than NSApp.
Also, the argument list to -addItemWithTitle:... is all messed up and makes no sense. You should clean up your question to reflect actual code.

Resources