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