/* * cocoa_window.m * Copyright (C) 2017 Kovid Goyal * * Distributed under terms of the GPL3 license. */ #include "data-types.h" #include #include // Needed for _NSGetProgname #include #if (MAC_OS_X_VERSION_MAX_ALLOWED < 101200) #define NSWindowStyleMaskResizable NSResizableWindowMask #endif @interface MenuDispatcher : NSObject @end @implementation MenuDispatcher @end static NSObject* menu_dispatcher = NULL; static NSMenuItem* title_menu = NULL; static NSString* find_app_name(void) { size_t i; NSDictionary* infoDictionary = [[NSBundle mainBundle] infoDictionary]; // Keys to search for as potential application names NSString* name_keys[] = { @"CFBundleDisplayName", @"CFBundleName", @"CFBundleExecutable", }; for (i = 0; i < sizeof(name_keys) / sizeof(name_keys[0]); i++) { id name = [infoDictionary objectForKey:name_keys[i]]; if (name && [name isKindOfClass:[NSString class]] && ![name isEqualToString:@""]) { return name; } } char** progname = _NSGetProgname(); if (progname && *progname) return [NSString stringWithUTF8String:*progname]; // Really shouldn't get here return @"kitty"; } PyObject* cocoa_create_global_menu(PyObject UNUSED *_self) { if (menu_dispatcher != NULL) { Py_RETURN_NONE; } NSString* app_name = find_app_name(); menu_dispatcher = [[MenuDispatcher alloc] init]; NSMenu* bar = [[NSMenu alloc] init]; [NSApp setMainMenu:bar]; NSMenuItem* appMenuItem = [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; NSMenu* appMenu = [[NSMenu alloc] init]; [appMenuItem setSubmenu:appMenu]; [appMenu addItemWithTitle:[NSString stringWithFormat:@"About %@", app_name] action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; [appMenu addItem:[NSMenuItem separatorItem]]; [appMenu addItemWithTitle:[NSString stringWithFormat:@"Hide %@", app_name] action:@selector(hide:) keyEquivalent:@"h"]; [[appMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"] setKeyEquivalentModifierMask:NSAlternateKeyMask | NSCommandKeyMask]; [appMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; [appMenu addItem:[NSMenuItem separatorItem]]; NSMenu* servicesMenu = [[NSMenu alloc] init]; [NSApp setServicesMenu:servicesMenu]; [[appMenu addItemWithTitle:@"Services" action:NULL keyEquivalent:@""] setSubmenu:servicesMenu]; [servicesMenu release]; [appMenu addItem:[NSMenuItem separatorItem]]; [appMenu addItemWithTitle:[NSString stringWithFormat:@"Quit %@", app_name] action:@selector(terminate:) keyEquivalent:@"q"]; [appMenu release]; NSMenuItem* windowMenuItem = [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; NSMenu* windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; [windowMenuItem setSubmenu:windowMenu]; [windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; [windowMenu addItemWithTitle:@"Zoom" action:@selector(performZoom:) keyEquivalent:@""]; [windowMenu addItem:[NSMenuItem separatorItem]]; [windowMenu addItemWithTitle:@"Bring All to Front" action:@selector(arrangeInFront:) keyEquivalent:@""]; [windowMenu addItem:[NSMenuItem separatorItem]]; [[windowMenu addItemWithTitle:@"Enter Full Screen" action:@selector(toggleFullScreen:) keyEquivalent:@"f"] setKeyEquivalentModifierMask:NSControlKeyMask | NSCommandKeyMask]; [NSApp setWindowsMenu:windowMenu]; [windowMenu release]; [bar release]; Py_RETURN_NONE; } PyObject* cocoa_update_title(PyObject UNUSED *self, PyObject *pytitle) { NSString *title = [[NSString alloc] initWithUTF8String:PyUnicode_AsUTF8(pytitle)]; NSMenu *bar = [NSApp mainMenu]; if (title_menu != NULL) { [bar removeItem:title_menu]; } title_menu = [bar addItemWithTitle:@"" action:NULL keyEquivalent:@""]; NSMenu *m = [[NSMenu alloc] initWithTitle:[NSString stringWithFormat:@" :: %@", title]]; [title_menu setSubmenu:m]; [m release]; [title release]; Py_RETURN_NONE; } PyObject* cocoa_make_window_resizable(PyObject UNUSED *self, PyObject *window_id) { NSWindow *window = (NSWindow*)PyLong_AsVoidPtr(window_id); @try { [window setStyleMask: [window styleMask] | NSWindowStyleMaskResizable]; } @catch (NSException *e) { return PyErr_Format(PyExc_ValueError, "Failed to set style mask: %s: %s", [[e name] UTF8String], [[e reason] UTF8String]); } Py_RETURN_NONE; } PyObject* cocoa_get_lang(PyObject UNUSED *self) { NSString* locale = nil; NSString* lang_code = [[NSLocale currentLocale] objectForKey:NSLocaleLanguageCode]; NSString* country_code = [[NSLocale currentLocale] objectForKey:NSLocaleCountryCode]; if (lang_code && country_code) { locale = [NSString stringWithFormat:@"%@_%@", lang_code, country_code]; } else { locale = [[NSLocale currentLocale] localeIdentifier]; } if (!locale) { Py_RETURN_NONE; } return Py_BuildValue("s", [locale UTF8String]); } static PyMethodDef module_methods[] = { {"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""}, \ {"cocoa_make_window_resizable", (PyCFunction)cocoa_make_window_resizable, METH_O, ""}, \ {"cocoa_create_global_menu", (PyCFunction)cocoa_create_global_menu, METH_NOARGS, ""}, \ {"cocoa_update_title", (PyCFunction)cocoa_update_title, METH_O, ""}, {NULL, NULL, 0, NULL} /* Sentinel */ }; bool init_cocoa(PyObject *module) { if (PyModule_AddFunctions(module, module_methods) != 0) return false; return true; }