0

I have an application that manages different types of NSDocument subclasses (along with matching NSWindow subclasses).

For instance, it's possible that the app has one window of type A open, and two windows of type B.

Now, if a window of type B is active, and the user chooses "Close All" or hits cmd+option+W, all my app's windows are sent the close message.

But I only want all of the active window type's windows closed instead, i.e. only the two type B, not the type A window. How do I accomplish this?

I currently have no explicit menu entry for "Close All". Instead, macOS provides that automagically. If there perhaps a way to intercept a "closeAll" message? Can't find one, though.

Thomas Tempelmann
  • 11,045
  • 8
  • 74
  • 149

3 Answers3

1

AppKit will add the Close All menu item if there isn't one. Add an alternate menu item item with key equivalent cmd+option+W below the Close menu and connect it to your own action method.

Menu item in IB

Willeke
  • 14,578
  • 4
  • 19
  • 47
0

You might succeed with overriding your document's canClose(withDelegate:,shouldClose:,contextInfo:) to return whether a document should be closed.

If this doesn't behave the way you want, you can create a subclass of NSDocumentController (if you don't have one already). Details on how to do that vary, but usually you have main (menu) XIB or main Storyboard, which has a "Document Controller" object: set its class to your custom class.

Then override closeAllDocuments(withDelegate:,didCloseAllSelector:,contextInfo:) and implement your custom logic.

Note that you should detect whether your app is about to quit and then really do close all you documents (unless you really want to prevent the app quit, e.g. because a document is dirty).

DarkDust
  • 90,870
  • 19
  • 190
  • 224
  • 1st option isn't working because the contextInfo is opaque and I can't easily tell which document was frontmost. Would have to store some state for this, which I don't like to do due to unforeseeable side effects. Tried 2nd option, but the `closeAllDocumentsWithDelegate:didCloseAllSelector:contextInfo:` implementation is not getting invoked. – Thomas Tempelmann Aug 30 '22 at 10:36
0

After some digging I figured out where the auto-generated "Close All" menu item sends its action to: To the closeAll: selector of the application target:

Xcode property view of NSMenuItem

Thus my solution is to subclass NSApplication and implement the handler there, which then simply closes all windows that are of the same type (this assumes that I use specific subclasses for my different types of windows):

- (IBAction)closeAll:(id)sender {
    Class windowClass = self.keyWindow.class;
    for (NSWindow *w in self.windows) {
        if (windowClass == w.class) {
            [w performClose:sender];
        }
    }
}

Caution: If you adopt this pattern be aware that:

  1. The closeAll: selector is not documented nor mentioned in the header files, meaning that Apple might feel free to change in a future SDK, though I find that unlikely. It will probably not break anything if that happens, but instead your custom handler won't be called any more.

  2. The code simply tells all windows to close, ignoring the fact that one might reject to be closed, e.g. by user interaction. In that case you may want to stop the loop instead of continuing to close more windows (though I know of no easy way to accomplish that).

Thomas Tempelmann
  • 11,045
  • 8
  • 74
  • 149