1// This file is part of VSTGUI. It is subject to the license terms 2// in the LICENSE file found in the top-level directory of this 3// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE 4 5#import "../../../../lib/platform/mac/cocoa/cocoahelpers.h" 6#import "../../../include/iappdelegate.h" 7#import "../../../include/iapplication.h" 8#import "../../application.h" 9#import "../../genericalertbox.h" 10#import "../../shareduiresources.h" 11#import "../../window.h" 12#import "VSTGUICommand.h" 13#import "macasync.h" 14#import "maccommondirectories.h" 15#import "macpreference.h" 16#import "macutilities.h" 17#import "macwindow.h" 18#import <Cocoa/Cocoa.h> 19 20#if __has_feature(nullability) == 0 21static_assert (false, "Need newer clang compiler!"); 22#endif 23 24#define VSTGUI_STANDALONE_USE_GENERIC_ALERTBOX_ON_MACOS 0 25 26//------------------------------------------------------------------------ 27@interface VSTGUIApplicationDelegate : NSObject <NSApplicationDelegate> 28{ 29 VSTGUI::Standalone::Platform::Mac::MacPreference prefs; 30 VSTGUI::Standalone::Platform::Mac::CommonDirectories commonDirecories; 31} 32@property NSArray<NSString*>* _Nullable startupOpenFiles; 33@property BOOL hasFinishedLaunching; 34@property BOOL hasTriggeredSetupMainMenu; 35@end 36 37using namespace VSTGUI::Standalone; 38using VSTGUI::Standalone::Platform::Mac::IMacWindow; 39using VSTGUI::Standalone::Detail::IPlatformApplication; 40using VSTGUI::Standalone::Detail::CommandWithKey; 41using VSTGUI::Standalone::Detail::IPlatformWindowAccess; 42using CommandWithKeyList = VSTGUI::Standalone::Detail::IPlatformApplication::CommandWithKeyList; 43using VSTGUI::Standalone::Detail::PlatformCallbacks; 44 45//------------------------------------------------------------------------ 46static CommandWithKeyList getCommandList (const char* _Nonnull group) 47{ 48 for (auto& e : Detail::getApplicationPlatformAccess ()->getCommandList ()) 49 { 50 if (e.first == group) 51 return e.second; 52 } 53 return {}; 54} 55 56//------------------------------------------------------------------------ 57@implementation VSTGUIApplicationDelegate 58 59//------------------------------------------------------------------------ 60- (instancetype _Nonnull)init 61{ 62 self = [super init]; 63 if (self) 64 { 65 } 66 return self; 67} 68 69//------------------------------------------------------------------------ 70- (NSApplicationTerminateReply)applicationShouldTerminate:(nonnull NSApplication*)sender 71{ 72 if (Detail::getApplicationPlatformAccess ()->canQuit ()) 73 return NSTerminateNow; 74 return NSTerminateCancel; 75} 76 77//------------------------------------------------------------------------ 78- (IBAction)showAboutDialog:(nullable id)sender 79{ 80 if (IApplication::instance ().getDelegate ().hasAboutDialog ()) 81 IApplication::instance ().getDelegate ().showAboutDialog (); 82 else 83 [NSApp orderFrontStandardAboutPanel:sender]; 84} 85 86//------------------------------------------------------------------------ 87- (IBAction)showPreferenceDialog:(nullable id)sender 88{ 89 IApplication::instance ().getDelegate ().showPreferenceDialog (); 90} 91 92//------------------------------------------------------------------------ 93- (IBAction)processCommand:(nullable id)sender 94{ 95 if (VSTGUICommand* command = [sender representedObject]) 96 Detail::getApplicationPlatformAccess ()->handleCommand ([command command]); 97} 98 99//------------------------------------------------------------------------ 100- (void)showHelp:(nullable id)sender 101{ 102} 103 104//------------------------------------------------------------------------ 105- (BOOL)validateMenuItem:(nonnull NSMenuItem*)menuItem 106{ 107 if (menuItem.action == @selector (showPreferenceDialog:)) 108 { 109 if (!IApplication::instance ().getDelegate ().hasPreferenceDialog ()) 110 { 111 return NO; 112 } 113 return YES; 114 } 115 else if (menuItem.action == @selector (showAboutDialog:)) 116 { 117 return YES; 118 } 119 else if (VSTGUICommand* command = menuItem.representedObject) 120 { 121 return Detail::getApplicationPlatformAccess ()->canHandleCommand ([command command]); 122 } 123 return NO; 124} 125 126//------------------------------------------------------------------------ 127- (nonnull SEL)selectorForCommand:(const CommandWithKey&)command 128{ 129 if (command == Commands::CloseWindow) 130 return @selector (performClose:); 131 else if (command == Commands::Undo) 132 return @selector (undo); 133 else if (command == Commands::Redo) 134 return @selector (redo); 135 else if (command == Commands::Cut) 136 return @selector (cut:); 137 else if (command == Commands::Copy) 138 return @selector (copy:); 139 else if (command == Commands::Paste) 140 return @selector (paste:); 141 else if (command == Commands::Delete) 142 return @selector (delete:); 143 else if (command == Commands::SelectAll) 144 return @selector (selectAll:); 145 return @selector (processCommand:); 146} 147 148//------------------------------------------------------------------------ 149- (nonnull NSMenuItem*)createMenuItemFromCommand:(const CommandWithKey&)command 150{ 151 if (command.name == CommandName::MenuSeparator) 152 return [NSMenuItem separatorItem]; 153 154 NSMenuItem* item = [NSMenuItem new]; 155 item.title = stringFromUTF8String (command.name); 156 item.action = [self selectorForCommand:command]; 157 if (command.defaultKey) 158 { 159 item.keyEquivalent = 160 [NSString stringWithCharacters:reinterpret_cast<const unichar*> (&command.defaultKey) 161 length:1]; 162 } 163 VSTGUICommand* representedObject = [VSTGUICommand new]; 164 representedObject.cmd = command; 165 item.representedObject = representedObject; 166 return item; 167} 168 169//------------------------------------------------------------------------ 170- (nonnull NSString*)appName 171{ 172 NSDictionary* dict = [[NSBundle mainBundle] infoDictionary]; 173 return dict[(@"CFBundleName")]; 174} 175 176//------------------------------------------------------------------------ 177- (nonnull NSMenu*)createAppMenu 178{ 179 NSString* appName = [self appName]; 180 NSMenu* menu = [[NSMenu alloc] initWithTitle:appName]; 181 182 [menu addItemWithTitle:[NSLocalizedString (@"About ", "Menu Item") 183 stringByAppendingString:appName] 184 action:@selector (showAboutDialog:) 185 keyEquivalent:@""]; 186 [menu addItem:[NSMenuItem separatorItem]]; 187 188 [menu addItemWithTitle:NSLocalizedString (@"Preferences...", "Menu Item") 189 action:@selector (showPreferenceDialog:) 190 keyEquivalent:@","]; 191 192 [menu addItem:[NSMenuItem separatorItem]]; 193 194 auto commandList = getCommandList (CommandGroup::Application); 195 if (!commandList.empty ()) 196 { 197 for (auto& command : commandList) 198 { 199 if (command != Commands::About && command != Commands::Preferences && 200 command != Commands::Quit && command != Commands::Help) 201 { 202 [menu addItem:[self createMenuItemFromCommand:command]]; 203 } 204 } 205 } 206 207 [menu addItem:[NSMenuItem separatorItem]]; 208 [menu 209 addItemWithTitle:[NSLocalizedString (@"Hide ", "Menu Item") stringByAppendingString:appName] 210 action:@selector (hide:) 211 keyEquivalent:@"h"]; 212 [menu addItemWithTitle:NSLocalizedString (@"Hide Others", "Menu Item") 213 action:@selector (hideOtherApplications:) 214 keyEquivalent:@""]; 215 [menu addItemWithTitle:NSLocalizedString (@"Show All", "Menu Item") 216 action:@selector (unhideAllApplications:) 217 keyEquivalent:@""]; 218 [menu addItem:[NSMenuItem separatorItem]]; 219 [menu 220 addItemWithTitle:[NSLocalizedString (@"Quit ", "Menu Item") stringByAppendingString:appName] 221 action:@selector (terminate:) 222 keyEquivalent:@"q"]; 223 224 return menu; 225} 226 227//------------------------------------------------------------------------ 228- (void)fillMenu:(nonnull NSMenu*)menu fromCommandList:(const CommandWithKeyList&)commandList 229{ 230 for (auto& command : commandList) 231 { 232 [menu addItem:[self createMenuItemFromCommand:command]]; 233 } 234} 235 236//------------------------------------------------------------------------ 237- (nonnull NSMenu*)createWindowsMenu 238{ 239 NSMenu* menu = 240 [[NSMenu alloc] initWithTitle:NSLocalizedString (@"Window", "Window Menu Title")]; 241 [menu addItemWithTitle:NSLocalizedString (@"Minimize", "Menu Item in Window Menu") 242 action:@selector (performMiniaturize:) 243 keyEquivalent:@"m"]; 244 [menu addItemWithTitle:NSLocalizedString (@"Zoom", "Menu Item in Window Menu") 245 action:@selector (performZoom:) 246 keyEquivalent:@""]; 247 NSMenuItem* item = 248 [menu addItemWithTitle:NSLocalizedString (@"Fullscreen", "Menu Item in Window Menu") 249 action:@selector (toggleFullScreen:) 250 keyEquivalent:@"f"]; 251 item.keyEquivalentModifierMask = 252 MacEventModifier::CommandKeyMask | MacEventModifier::ControlKeyMask; 253 [menu addItem:[NSMenuItem separatorItem]]; 254 255#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12 && __clang_major__ >= 9 256 if (@available (macOS 10.12, *)) 257 { 258 item = [menu 259 addItemWithTitle:NSLocalizedString (@"Show Previous Tab", "Menu Item in Window Menu") 260 action:@selector (selectPreviousTab:) 261 keyEquivalent:@"\t"]; 262 item.keyEquivalentModifierMask = 263 MacEventModifier::ShiftKeyMask | MacEventModifier::ControlKeyMask; 264 item = 265 [menu addItemWithTitle:NSLocalizedString (@"Show Next Tab", "Menu Item in Window Menu") 266 action:@selector (selectNextTab:) 267 keyEquivalent:@"\t"]; 268 item.keyEquivalentModifierMask = MacEventModifier::ControlKeyMask; 269 [menu addItemWithTitle:NSLocalizedString (@"Move Tab To New Window", 270 "Menu Item in Window Menu") 271 action:@selector (moveTabToNewWindow:) 272 keyEquivalent:@""]; 273 [menu addItemWithTitle:NSLocalizedString (@"Merge All Windows", "Menu Item in Window Menu") 274 action:@selector (mergeAllWindows:) 275 keyEquivalent:@""]; 276 item = 277 [menu addItemWithTitle:NSLocalizedString (@"Show All Tabs", "Menu Item in Window Menu") 278 action:@selector (toggleTabOverview:) 279 keyEquivalent:@"\\"]; 280 item.keyEquivalentModifierMask = 281 MacEventModifier::ShiftKeyMask | MacEventModifier::CommandKeyMask; 282 283 [menu addItem:[NSMenuItem separatorItem]]; 284 } 285#endif 286 [menu addItemWithTitle:NSLocalizedString (@"Bring All to Front", "Menu Item in Window Menu") 287 action:@selector (arrangeInFront:) 288 keyEquivalent:@""]; 289 [menu addItem:[NSMenuItem separatorItem]]; 290 return menu; 291} 292 293//------------------------------------------------------------------------ 294- (nonnull NSMenu*)createHelpMenu 295{ 296 NSMenu* menu = [[NSMenu alloc] initWithTitle:NSLocalizedString (@"Help", "Help Menu Title")]; 297 return menu; 298} 299 300//------------------------------------------------------------------------ 301- (void)setupMainMenu 302{ 303 NSMenu* mainMenu = [NSApp mainMenu]; 304 NSMenuItem* appMenuItem = nil; 305 if (mainMenu == nil) 306 { 307 mainMenu = [NSMenu new]; 308 [NSApp setMainMenu:mainMenu]; 309 310 appMenuItem = [[NSMenuItem alloc] initWithTitle:@"App" action:nil keyEquivalent:@""]; 311 [mainMenu addItem:appMenuItem]; 312 313 NSMenuItem* item = 314 [[NSMenuItem alloc] initWithTitle:NSLocalizedString (@"Window", "Menu Name") 315 action:nullptr 316 keyEquivalent:@""]; 317 NSMenu* windowsMenu = [self createWindowsMenu]; 318 [NSApp setWindowsMenu:windowsMenu]; 319 item.submenu = windowsMenu; 320 [mainMenu addItem:item]; 321 322 item = [[NSMenuItem alloc] initWithTitle:NSLocalizedString (@"Help", "Menu Name") 323 action:nullptr 324 keyEquivalent:@""]; 325 NSMenu* helpMenu = [self createHelpMenu]; 326 [NSApp setHelpMenu:helpMenu]; 327 item.submenu = helpMenu; 328 [mainMenu addItem:item]; 329 } 330 else 331 { 332 appMenuItem = [mainMenu itemAtIndex:0]; 333 } 334 335 appMenuItem.submenu = [self createAppMenu]; 336 337 auto commandList = Detail::getApplicationPlatformAccess ()->getCommandList (); 338 for (auto& e : commandList) 339 { 340 if (e.first == CommandGroup::Window) 341 { 342 NSMenu* windowsMenu = [NSApp windowsMenu]; 343 for (auto& command : e.second) 344 { 345 NSString* title = stringFromUTF8String (command.name); 346 NSMenuItem* item = [windowsMenu itemWithTitle:title]; 347 if (!item) 348 [windowsMenu addItem:[self createMenuItemFromCommand:command]]; 349 } 350 } 351 else if (e.first == CommandGroup::Application) 352 { 353 for (auto& cmd : e.second) 354 { 355 if (cmd.name == CommandName::Help) 356 { 357 NSMenu* helpMenu = [NSApp helpMenu]; 358 NSMenuItem* item = [helpMenu itemWithTitle:stringFromUTF8String (cmd.name)]; 359 if (!item) 360 { 361 item = [self createMenuItemFromCommand:cmd]; 362 NSString* appName = [self appName]; 363 item.title = [appName 364 stringByAppendingString:NSLocalizedString (@" Help", "Menu Item")]; 365 [helpMenu addItem:item]; 366 } 367 } 368 } 369 } 370 else 371 { 372 NSString* title = stringFromUTF8String (e.first); 373 NSMenuItem* item = [mainMenu itemWithTitle:title]; 374 if (!item) 375 { 376 item = [[NSMenuItem alloc] initWithTitle:title action:nil keyEquivalent:@""]; 377 [mainMenu addItem:item]; 378 NSMenu* menu = [[NSMenu alloc] initWithTitle:title]; 379 item.submenu = menu; 380 } 381 else 382 [item.submenu removeAllItems]; 383 [self fillMenu:item.submenu fromCommandList:e.second]; 384 } 385 } 386 387 NSMenuItem* editMenu = [mainMenu itemWithTitle:NSLocalizedString (@"Edit", "Menu Name")]; 388 if (editMenu && editMenu.submenu) 389 { 390 NSMenuItem* showCharacterPanelItem = 391 [editMenu.submenu itemWithTitle:NSLocalizedString (@"Characters", "Menu Item")]; 392 if (showCharacterPanelItem == nil) 393 { 394 [editMenu.submenu addItem:[NSMenuItem separatorItem]]; 395 [editMenu.submenu 396 addItemWithTitle:NSLocalizedString (@"Emoji & Symbols", "Menu Item in Edit Menu") 397 action:@selector (orderFrontCharacterPalette:) 398 keyEquivalent:@""]; 399 } 400 } 401 402 NSMenuItem* debugMenu = [mainMenu itemWithTitle:@"Debug"]; 403 if (debugMenu && debugMenu.submenu) 404 { 405 if ([debugMenu.submenu itemWithTitle:@"Color Panel"] == nil) 406 { 407 [debugMenu.submenu addItem:[NSMenuItem separatorItem]]; 408 [debugMenu.submenu addItemWithTitle:@"Color Panel" 409 action:@selector (orderFrontColorPanel:) 410 keyEquivalent:@""]; 411 } 412 } 413 414 // move Windows menu to the end 415 NSMenuItem* windowsMenuItem = 416 [mainMenu itemWithTitle:NSLocalizedString (@"Window", "Menu Name")]; 417 [mainMenu removeItem:windowsMenuItem]; 418 [mainMenu addItem:windowsMenuItem]; 419 // move Help menu to the end 420 NSMenuItem* helpMenuItem = [mainMenu itemWithTitle:NSLocalizedString (@"Help", "Menu Name")]; 421 [mainMenu removeItem:helpMenuItem]; 422 [mainMenu addItem:helpMenuItem]; 423} 424 425//------------------------------------------------------------------------ 426- (void)triggerSetupMainMenu 427{ 428 if (self.hasTriggeredSetupMainMenu) 429 return; 430 self.hasTriggeredSetupMainMenu = YES; 431 Async::schedule (Async::mainQueue (), [self] () { 432 [self setupMainMenu]; 433 self.hasTriggeredSetupMainMenu = NO; 434 }); 435} 436 437#if !VSTGUI_STANDALONE_USE_GENERIC_ALERTBOX_ON_MACOS 438//------------------------------------------------------------------------ 439- (nonnull NSAlert*)createAlert:(const AlertBoxConfig&)config 440{ 441 NSAlert* alert = [NSAlert new]; 442 if (!config.headline.empty ()) 443 alert.messageText = stringFromUTF8String (config.headline); 444 if (!config.description.empty ()) 445 alert.informativeText = stringFromUTF8String (config.description); 446 [alert addButtonWithTitle:stringFromUTF8String (config.defaultButton)]; 447 if (!config.secondButton.empty ()) 448 [alert addButtonWithTitle:stringFromUTF8String (config.secondButton)]; 449 if (!config.thirdButton.empty ()) 450 [alert addButtonWithTitle:stringFromUTF8String (config.thirdButton)]; 451 return alert; 452} 453#endif 454 455//------------------------------------------------------------------------ 456- (AlertResult)showAlert:(const AlertBoxConfig&)config 457{ 458#if VSTGUI_STANDALONE_USE_GENERIC_ALERTBOX_ON_MACOS 459 AlertResult result = AlertResult::Error; 460 auto alertWindow = Detail::createAlertBox (config, [&] (AlertResult r) { 461 result = r; 462 [NSApp abortModal]; 463 }); 464 auto platformAlertWindow = VSTGUI::dynamicPtrCast<IPlatformWindowAccess> (alertWindow); 465 assert (platformAlertWindow); 466 auto macAlertWindow = 467 VSTGUI::staticPtrCast<IMacWindow> (platformAlertWindow->getPlatformWindow ()); 468 assert (macAlertWindow); 469 auto nsWindow = macAlertWindow->getNSWindow (); 470 macAlertWindow->center (); 471 macAlertWindow->show (); 472 [NSApp runModalForWindow:nsWindow]; 473 return result; 474#else 475 NSAlert* alert = [self createAlert:config]; 476 NSModalResponse response = [alert runModal]; 477 if (response == NSAlertSecondButtonReturn) 478 return AlertResult::SecondButton; 479 if (response == NSAlertThirdButtonReturn) 480 return AlertResult::ThirdButton; 481 return AlertResult::DefaultButton; 482#endif 483} 484 485//------------------------------------------------------------------------ 486- (void)showAlertForWindow:(const AlertBoxForWindowConfig&)config 487{ 488 auto platformWindowAccess = VSTGUI::dynamicPtrCast<IPlatformWindowAccess> (config.window); 489 if (!platformWindowAccess) 490 return; 491 auto macWindow = VSTGUI::staticPtrCast<IMacWindow> (platformWindowAccess->getPlatformWindow ()); 492 if (!macWindow) 493 return; 494 if (macWindow->isPopup ()) 495 { 496 auto result = [self showAlert:config]; 497 if (config.callback) 498 config.callback (result); 499 return; 500 } 501 502 auto callback = std::move (config.callback); 503 504#if VSTGUI_STANDALONE_USE_GENERIC_ALERTBOX_ON_MACOS 505 struct Params 506 { 507 NSWindow* sheet {nullptr}; 508 NSWindow* parent {nullptr}; 509 }; 510 511 auto params = std::make_shared<Params> (); 512 params->parent = macWindow->getNSWindow (); 513 auto parentWindow = config.window; 514 auto alertWindow = Detail::createAlertBox (config, [=] (AlertResult r) { 515 if (callback) 516 callback (r); 517 [params->parent endSheet:params->sheet]; 518 }); 519 auto platformAlertWindow = VSTGUI::dynamicPtrCast<IPlatformWindowAccess> (alertWindow); 520 assert (platformAlertWindow); 521 auto macAlertWindow = 522 VSTGUI::staticPtrCast<IMacWindow> (platformAlertWindow->getPlatformWindow ()); 523 assert (macAlertWindow); 524 params->sheet = macAlertWindow->getNSWindow (); 525 526 [params->parent beginSheet:params->sheet completionHandler:^(NSModalResponse returnCode) {}]; 527 528#else 529 530 NSAlert* alert = [self createAlert:config]; 531 [alert beginSheetModalForWindow:macWindow->getNSWindow () 532 completionHandler:^(NSModalResponse returnCode) { 533 if (callback) 534 { 535 AlertResult result = AlertResult::Error; 536 if (returnCode == NSAlertFirstButtonReturn) 537 result = AlertResult::DefaultButton; 538 else if (returnCode == NSAlertSecondButtonReturn) 539 result = AlertResult::SecondButton; 540 else if (returnCode == NSAlertThirdButtonReturn) 541 result = AlertResult::ThirdButton; 542 callback (result); 543 } 544 }]; 545#endif 546} 547 548//------------------------------------------------------------------------ 549- (BOOL)verifyInfoPlistEntries 550{ 551 NSDictionary* dict = [[NSBundle mainBundle] infoDictionary]; 552 const auto& appInfo = IApplication::instance ().getDelegate ().getInfo (); 553 NSString* infoPlistString = dict[(@"CFBundleName")]; 554 if (![stringFromUTF8String (appInfo.name) isEqualToString:infoPlistString]) 555 { 556 NSLog (@"Warning: CFBundleName is not equal to Application::Info::name"); 557 } 558 infoPlistString = dict[(@"CFBundleShortVersionString")]; 559 if (![stringFromUTF8String (appInfo.version) isEqualToString:infoPlistString]) 560 { 561 NSLog (@"Warning: CFBundleShortVersionString is not equal to Application::Info::version"); 562 } 563 infoPlistString = dict[(@"CFBundleIdentifier")]; 564 if (![stringFromUTF8String (appInfo.uri) isEqualToString:infoPlistString]) 565 { 566 NSLog (@"Warning: CFBundleIdentifier is not equal to Application::Info::uri"); 567 } 568 return YES; 569} 570 571//------------------------------------------------------------------------ 572- (void)applicationDidFinishLaunching:(nonnull NSNotification*)notification 573{ 574 if ([self verifyInfoPlistEntries] == NO) 575 { 576 [NSApp terminate:nil]; 577 return; 578 } 579 580 IApplication::CommandLineArguments cmdArgs; 581 NSArray* args = [[NSProcessInfo processInfo] arguments]; 582 cmdArgs.reserve ([args count]); 583 for (NSString* str in args) 584 { 585 cmdArgs.emplace_back ([str UTF8String]); 586 } 587 588 VSTGUIApplicationDelegate* Self = self; 589 PlatformCallbacks callbacks; 590 callbacks.quit = [] () { 591 [NSApp performSelector:@selector (terminate:) withObject:nil afterDelay:0]; 592 }; 593 callbacks.onCommandUpdate = [Self] () { [Self triggerSetupMainMenu]; }; 594 callbacks.showAlert = [Self] (const AlertBoxConfig& config) { return [Self showAlert:config]; }; 595 callbacks.showAlertForWindow = [Self] (const AlertBoxForWindowConfig& config) { 596 return [Self showAlertForWindow:config]; 597 }; 598 auto app = Detail::getApplicationPlatformAccess (); 599 vstgui_assert (app); 600 [self setupMainMenu]; 601 IPlatformApplication::OpenFilesList openFilesList; 602 if (auto filenames = self.startupOpenFiles) 603 { 604 openFilesList.reserve (filenames.count); 605 for (NSString* filename in filenames) 606 { 607 openFilesList.emplace_back ([filename UTF8String]); 608 } 609 self.startupOpenFiles = nil; 610 } 611 self.hasFinishedLaunching = YES; 612 app->init ({prefs, commonDirecories, std::move (cmdArgs), std::move (callbacks), 613 std::move (openFilesList)}); 614} 615 616//------------------------------------------------------------------------ 617- (void)applicationWillTerminate:(nonnull NSNotification*)notification 618{ 619 IApplication::instance ().getDelegate ().onQuit (); 620 for (NSWindow* window in [NSApp windows]) 621 { 622 [window close]; 623 } 624 Detail::cleanupSharedUIResources (); 625 Async::waitAllTasksDone (); 626} 627 628//------------------------------------------------------------------------ 629- (BOOL)openFilesInternal:(nonnull NSArray<NSString*>*)filenames 630{ 631 std::vector<VSTGUI::UTF8String> paths; 632 paths.reserve (filenames.count); 633 for (NSString* filename in filenames) 634 { 635 paths.emplace_back ([filename UTF8String]); 636 } 637 return IApplication::instance ().getDelegate ().openFiles (paths) ? YES : NO; 638} 639 640//------------------------------------------------------------------------ 641- (void)application:(nonnull NSApplication*)sender openFiles:(nonnull NSArray<NSString*>*)filenames 642{ 643 if (!self.hasFinishedLaunching) 644 { 645 self.startupOpenFiles = filenames; 646 return; 647 } 648 BOOL result = [self openFilesInternal:filenames]; 649 [sender replyToOpenOrPrint:result ? NSApplicationDelegateReplySuccess : 650 NSApplicationDelegateReplyFailure]; 651} 652 653@end 654 655//------------------------------------------------------------------------ 656namespace VSTGUI { void* _Nullable gBundleRef = nullptr; } 657 658//------------------------------------------------------------------------ 659int main (int argc, const char* _Nonnull* _Nonnull argv) 660{ 661 VSTGUI::gBundleRef = CFBundleGetMainBundle (); 662 VSTGUIApplicationDelegate* delegate = [VSTGUIApplicationDelegate new]; 663 [NSApplication sharedApplication].delegate = delegate; 664 return NSApplicationMain (argc, argv); 665} 666