I would like to be able to disable the Mac’s Quit command when I have a “modalish” dialog up, such as a non-native file browser (in a GUI App). So far, this seems to be impossible. I’ve tried everything including hacking JUCE code.
(I am using the ApplicationCommandManager, MenuBarModel, etc.)
I’m talking about the Quit item that shows up at the bottom of the “Apple Menu” (next to the real apple icon menu) that gets put there by default when you use a global Mac Menu Bar, and has “Services”, “Hide” etc.
This seems to be hard-coded in at an extremely low level. Even if I comment out everything in the JUCE source code related to StandardApplicationCommandIDs::quit (in an attempt to remove the Quit command, just so I can see where it’s coming from) it still shows up. Has anyone found a way to disable this?
Overriding the systemRequestedQuit function of the App should block it, isn’t it?
void systemRequestedQuit() override
{
DBG ("?");
}
By the way I probably misread your question.
I don’t know the answer to your question, but why is this something you want to do? Seems very user unfriendly.
Yes, I can block it from executing. But I would rather disable the command so that the user cannot select it. Instead the possibility exists and he selects it, and then you must play the SytemAlert or something and do nothing.
Is it? It seems standard behaviour on a lot of MacOS software, when a modal is up, most menu commands (if not all) are disabled. For example, just checking now:
Photoshop: Open File dialog: All menu commands deactivated including EVERYTHING on the Apple Menu.
MS Word: Open File dialog: nearly all menu commands deactivated including Quit on the Apple Menu.
MS Excel: Open File dialog: nearly all menu commands deactivated including Quit on the Apple Menu.
FireFox: Open File dialog: entire Mac Menu bar disappears except for Apple Menu, and Quit is disabled.
I can also find some that don’t disable the Quit item… but since I’m porting an app that used to work that way, I wanted to be able to disable the Quit command.
1 Like
Interesting, it looks like a lot of apps do this indeed. But lots of apps also don’t. Apparently menu commands are automatically disabled when modal dialogs are shown, so it seems not all apps show the Open File dialog using [panel runModal]. IIRC modal loops were removed from JUCE 7 (or was it 6?).
Yes, it is not recommended to use modal anymore (#if JUCE_MODAL_LOOPS_PERMITTED
). And I’m actually not. In my app and plugins, I simulate modal by installing dialogs as a child component over the entire MainComponent, blocking interaction with the rest of it. I don’t use the ModalComponentManager.
And I have successfully created the behavior of disabling all menu commands when such a “modalish” dialog is up, with the exception of the Apple Menu.
This was fixed already (but I didn’t get a chance to test it personally, been a while since working on the codebase that needed it). It probably won’t help with the Quit item though.
1 Like
Yeah, I’ve been able to disable items I add, such as “Show About Box”, but no way to get at the Quit item. 
1 Like
Sounds like a legit feature request then. Something like virtual bool isQuitMenuItemEnabled() in MenuBarModel perhaps.
1 Like
Yes. In the meantime, I’m going to try to hack something together to see if I can make this happen. I’ve just now figured out that this is the code that creates the Apple Menu. Interestingly enough, it is installed as a SubMenu in the first menu that you provide. So for example, my MenuBarModel has a File Menu as the first menu, so this installs the AppleMenu as a submenu in the File Menu.
Problem is, I don’t speak Swift or Objective C or whatever this is.

static void createStandardAppMenu (NSMenu* menu, const String& appName, const PopupMenu* extraItems)
{
if (extraItems != nullptr && JuceMainMenuHandler::instance != nullptr && extraItems->getNumItems() > 0)
{
for (PopupMenu::MenuItemIterator iter (*extraItems); iter.next();)
JuceMainMenuHandler::instance->addMenuItem (iter, menu, 0, -1);
[menu addItem: [NSMenuItem separatorItem]];
}
// Services...
NSMenuItem* services = [[[NSMenuItem alloc] initWithTitle: translateMenuName ("Services")
action: nil keyEquivalent: nsEmptyString()] autorelease];
[menu addItem: services];
NSMenu* servicesMenu = [[[NSMenu alloc] initWithTitle: translateMenuName ("Services")] autorelease];
[menu setSubmenu: servicesMenu forItem: services];
[NSApp setServicesMenu: servicesMenu];
[menu addItem: [NSMenuItem separatorItem]];
createMenuItem (menu, TRANS("Hide") + String (" ") + appName, @selector (hide:), nsStringLiteral ("h"));
[createMenuItem (menu, TRANS("Hide Others"), @selector (hideOtherApplications:), nsStringLiteral ("h"))
setKeyEquivalentModifierMask: NSEventModifierFlagCommand | NSEventModifierFlagOption];
createMenuItem (menu, TRANS("Show All"), @selector (unhideAllApplications:), nsEmptyString());
[menu addItem: [NSMenuItem separatorItem]];
createMenuItem (menu, TRANS("Quit") + String (" ") + appName, @selector (terminate:), nsStringLiteral ("q"));
}
I think it’s Objective-C, I’m also clueless about it… 
Just a guess, but I think this might be a good place to stick a breakpoint and see if the Quit menu option can be determined there:
static BOOL validateMenuItem (id, SEL, NSMenuItem* item)
{
if (auto* juceItem = getPopupMenuItem (item))
return juceItem->isEnabled;
return YES;
}
Looks to me that what that does is if the item in question is coming from JUCE then it returns the enabled state, otherwise it’s returning YES (true?). Or instead of adding a breakpoint there you could have it return false (NO?) and see if that perma-disables the Quit option, at least that will show it’s kind of the right place to be looking?
Nice idea, but I put a return NO; at the top of that, and it disables every single item on every single popup EXCEPT the items on the Apple Menu (including Quit) that are not part of the PopupMenu additional items you pass in. 
So apparently this only is used for items that have a direct correspondence with items in a JUCE PopupMenu somewhere.
It’s worse than Objective-C, it’s Objective-C++ 
The createMenuItem function creates an NSMenuItem object. If you capture that in a variable somewhere, then you can do myMenuItem.enabled = NO; to disable it (assuming the NSMenu it sits in has its autoenablesItems property set to NO).
1 Like
Thanks, I figured that out a little bit ago. 
Actually, createMenuItem() already returns a pointer to the NSMenuItem. So I’m doing this at the end of createStandardAppMenu(), where the Quit command is added:
// old:
// createMenuItem (menu, TRANS("Quit") + String (" ") + appName, @selector (terminate:), nsStringLiteral ("q"));
// fix/hack:
[menu setAutoenablesItems: false];
NSMenuItem* item = createMenuItem (menu, TRANS("Quit") + String (" ") + appName, @selector (terminate:), nsStringLiteral ("q"));
if (auto* app = JUCEApplication::getInstance())
[item setEnabled: app->getEnableQuitCommand()];
Then, in the JuceApplication.h class, add these:
public:
void setEnableQuitCommand(bool state) { enableQuitCommand = state; }
bool getEnableQuitCommand() { return enableQuitCommand; }
private:
bool enableQuitCommand = true;
Then, when you put up a modal or whatever, you can:
app->setEnableQuitCommand(false);
rebuildAppleMenu(); // a function you should already have implemented
…and it works! So far - still testing and cleaning up.
Hot damn! I wrote some Objective-C++! 
2 Likes