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