From 51f714e0e4b6f8f4998d21c03bcac26d5e021181 Mon Sep 17 00:00:00 2001 From: Pietro Gagliardi Date: Sun, 16 Aug 2015 22:46:18 -0400 Subject: [PATCH] More migration. I think that's all the code; all that's left is the makefiles... --- redo/reredo/darwin/GNUmakeinc.mk | 49 ++++ redo/reredo/darwin/menu.m | 360 +++++++++++++++++++++++++++++ redo/reredo/darwin/uipriv_darwin.h | 66 ++++++ 3 files changed, 475 insertions(+) create mode 100644 redo/reredo/darwin/GNUmakeinc.mk create mode 100644 redo/reredo/darwin/menu.m create mode 100644 redo/reredo/darwin/uipriv_darwin.h diff --git a/redo/reredo/darwin/GNUmakeinc.mk b/redo/reredo/darwin/GNUmakeinc.mk new file mode 100644 index 00000000..2f05c822 --- /dev/null +++ b/redo/reredo/darwin/GNUmakeinc.mk @@ -0,0 +1,49 @@ +# 28 april 2015 + +osMFILES = \ + darwin/alloc.m \ + darwin/autolayout.m \ + darwin/box.m \ + darwin/button.m \ + darwin/checkbox.m \ + darwin/combobox.m \ + darwin/datetimepicker.m \ + darwin/entry.m \ + darwin/group.m \ + darwin/label.m \ + darwin/main.m \ + darwin/menu.m \ + darwin/progressbar.m \ + darwin/radiobuttons.m \ + darwin/separator.m \ + darwin/slider.m \ + darwin/spinbox.m \ + darwin/stddialogs.m \ + darwin/tab.m \ + darwin/text.m \ + darwin/util.m \ + darwin/window.m + +osHFILES = \ + darwin/uipriv_darwin.h + +osCFLAGS = \ + -D_UI_EXTERN='__attribute__((visibility("default"))) extern' \ + -fvisibility=hidden \ + -mmacosx-version-min=10.7 -DMACOSX_DEPLOYMENT_TARGET=10.7 +osLDFLAGS = \ + -fvisibility=hidden \ + -mmacosx-version-min=10.7 -lobjc -framework Foundation -framework AppKit + +# the gcc flags don't work with Apple's linker +# fortunately, we don't need any; Apple's linker warns about undefined symbols in -shared builds! +osLDWarnUndefinedFlags = + +osLIBSUFFIX = .dylib +osEXESUFFIX = + +ifeq ($(ARCH),386) + archmflag = -m32 +else + archmflag = -m64 +endif diff --git a/redo/reredo/darwin/menu.m b/redo/reredo/darwin/menu.m new file mode 100644 index 00000000..783cc1da --- /dev/null +++ b/redo/reredo/darwin/menu.m @@ -0,0 +1,360 @@ +// 28 april 2015 +#import "uipriv_darwin.h" + +static NSMutableArray *menus = nil; +static BOOL menusFinalized = NO; + +struct uiMenu { + NSMenu *menu; + NSMenuItem *item; + NSMutableArray *items; +}; + +struct uiMenuItem { + NSMenuItem *item; + int type; + BOOL disabled; + void (*onClicked)(uiMenuItem *, uiWindow *, void *); + void *onClickedData; +}; + +enum { + typeRegular, + typeCheckbox, + typeQuit, + typePreferences, + typeAbout, + typeSeparator, +}; + +@implementation menuManager + +- (id)init +{ + self = [super init]; + if (self) { + // TODO NSPointerFunctionsOpaquePersonality? + self->items = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsOpaqueMemory + valueOptions:NSPointerFunctionsOpaqueMemory]; + self->hasQuit = NO; + self->hasPreferences = NO; + self->hasAbout = NO; + } + return self; +} + +- (void)dealloc +{ + [self->items release]; + [super dealloc]; +} + +- (IBAction)onClicked:(id)sender +{ + uiMenuItem *item; + NSValue *v; + + v = (NSValue *) [self->items objectForKey:sender]; + item = (uiMenuItem *) [v pointerValue]; + if (item->type == typeCheckbox) + uiMenuItemSetChecked(uiMenuItem(item), !uiMenuItemChecked(uiMenuItem(item))); + // use the key window as the source of the menu event; it's the active window + (*(item->onClicked))(uiMenuItem(item), windowFromNSWindow([realNSApp() keyWindow]), item->onClickedData); +} + +- (IBAction)onQuitClicked:(id)sender +{ + if (shouldQuit()) + uiQuit(); +} + +- (void)register:(NSMenuItem *)item to:(uiMenuItem *)smi +{ + NSValue *v; + + switch (smi->type) { + case typeQuit: + if (self->hasQuit) + complain("attempt to add multiple Quit menu items"); + self->hasQuit = YES; + break; + case typePreferences: + if (self->hasPreferences) + complain("attempt to add multiple Preferences menu items"); + self->hasPreferences = YES; + break; + case typeAbout: + if (self->hasAbout) + complain("attempt to add multiple About menu items"); + self->hasAbout = YES; + break; + } + v = [NSValue valueWithPointer:smi]; + [self->items setObject:v forKey:item]; +} + +// on OS X there are two ways to handle menu items being enabled or disabled: automatically and manually +// unfortunately, the application menu requires automatic menu handling for the Hide, Hide Others, and Show All items to work correctly +// therefore, we have to handle enabling of the other options ourselves +- (BOOL)validateMenuItem:(NSMenuItem *)item +{ + uiMenuItem *smi; + NSValue *v; + + // disable the special items if they aren't present + if (item == self.quitItem && !self->hasQuit) + return NO; + if (item == self.preferencesItem && !self->hasPreferences) + return NO; + if (item == self.aboutItem && !self->hasAbout) + return NO; + // then poll the item's enabled/disabled state + v = (NSValue *) [self->items objectForKey:item]; + smi = (uiMenuItem *) [v pointerValue]; + return !smi->disabled; +} + +// Cocoa constructs the default application menu by hand for each program; that's what MainMenu.[nx]ib does +- (void)buildApplicationMenu:(NSMenu *)menubar +{ + NSString *appName; + NSMenuItem *appMenuItem; + NSMenu *appMenu; + NSMenuItem *item; + NSString *title; + NSMenu *servicesMenu; + + appName = [[NSProcessInfo processInfo] processName]; + appMenuItem = [[NSMenuItem alloc] initWithTitle:appName action:NULL keyEquivalent:@""]; + appMenu = [[NSMenu alloc] initWithTitle:appName]; + [appMenuItem setSubmenu:appMenu]; + [menubar addItem:appMenuItem]; + + // first is About + title = [@"About " stringByAppendingString:appName]; + item = [[NSMenuItem alloc] initWithTitle:title action:@selector(onClicked:) keyEquivalent:@""]; + [item setTarget:self]; + [appMenu addItem:item]; + self.aboutItem = item; + + [appMenu addItem:[NSMenuItem separatorItem]]; + + // next is Preferences + item = [[NSMenuItem alloc] initWithTitle:@"Preferences…" action:@selector(onClicked:) keyEquivalent:@","]; + [item setTarget:self]; + [appMenu addItem:item]; + self.preferencesItem = item; + + [appMenu addItem:[NSMenuItem separatorItem]]; + + // next is Services + item = [[NSMenuItem alloc] initWithTitle:@"Services" action:NULL keyEquivalent:@""]; + servicesMenu = [[NSMenu alloc] initWithTitle:@"Services"]; + [item setSubmenu:servicesMenu]; + [realNSApp() setServicesMenu:servicesMenu]; + [appMenu addItem:item]; + + [appMenu addItem:[NSMenuItem separatorItem]]; + + // next are the three hiding options + title = [@"Hide " stringByAppendingString:appName]; + item = [[NSMenuItem alloc] initWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; + // the .xib file says they go to -1 ("First Responder", which sounds wrong...) + // to do that, we simply leave the target as nil + [appMenu addItem:item]; + item = [[NSMenuItem alloc] initWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; + [item setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)]; + [appMenu addItem:item]; + item = [[NSMenuItem alloc] initWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; + [appMenu addItem:item]; + + [appMenu addItem:[NSMenuItem separatorItem]]; + + // and finally Quit + // DON'T use @selector(terminate:) as the action; we handle termination ourselves + title = [@"Quit " stringByAppendingString:appName]; + item = [[NSMenuItem alloc] initWithTitle:title action:@selector(onQuitClicked:) keyEquivalent:@"q"]; + [item setTarget:self]; + [appMenu addItem:item]; + self.quitItem = item; +} + +- (NSMenu *)makeMenubar +{ + NSMenu *menubar; + + menubar = [[NSMenu alloc] initWithTitle:@""]; + [self buildApplicationMenu:menubar]; + return menubar; +} + +@end + +static void defaultOnClicked(uiMenuItem *item, uiWindow *w, void *data) +{ + // do nothing +} + +void uiMenuItemEnable(uiMenuItem *item) +{ + item->disabled = NO; + // we don't need to explicitly update the menus here; they'll be updated the next time they're opened (thanks mikeash in irc.freenode.net/#macdev) +} + +void uiMenuItemDisable(uiMenuItem *item) +{ + item->disabled = YES; +} + +void uiMenuItemOnClicked(uiMenuItem *item, void (*f)(uiMenuItem *, uiWindow *, void *), void *data) +{ + if (item->type == typeQuit) + complain("attempt to call uiMenuItemOnClicked() on a Quit item; use uiOnShouldQuit() instead"); + item->onClicked = f; + item->onClickedData = data; +} + +int uiMenuItemChecked(uiMenuItem *item) +{ + return [item->item state] != NSOffState; +} + +void menuItemSetChecked(uiMenuItem *item, int checked) +{ + NSInteger state; + + state = NSOffState; + if ([item->item state] == NSOffState) + state = NSOnState; + [item->item setState:state]; +} + +static uiMenuItem *newItem(uiMenu *m, int type, const char *name) +{ + uiMenuItem *item; + + if (menusFinalized) + complain("attempt to create a new menu item after menus have been finalized"); + + item = uiNew(uiMenuItem); + uiTyped(item)->Type = uiTypeMenuItem(); + + item->type = type; + switch (item->type) { + case typeQuit: + item->item = appDelegate().menuManager.quitItem; + break; + case typePreferences: + item->item = appDelegate().menuManager.preferencesItem; + break; + case typeAbout: + item->item = appDelegate().menuManager.aboutItem; + break; + case typeSeparator: + item->item = [NSMenuItem separatorItem]; + [m->menu addItem:item->item]; + break; + default: + item->item = [[NSMenuItem alloc] initWithTitle:toNSString(name) action:@selector(onClicked:) keyEquivalent:@""]; + [item->item setTarget:appDelegate().menuManager]; + [m->menu addItem:item->item]; + break; + } + + [appDelegate().menuManager register:item->item to:item]; + item->onClicked = defaultOnClicked; + + [m->items addObject:[NSValue valueWithPointer:item]]; + + return item; +} + +uiMenuItem *uiMenuAppendItem(uiMenu *m, const char *name) +{ + return newItem(m, typeRegular, name); +} + +uiMenuItem *uiMenuAppendCheckItem(uiMenu *m, const char *name) +{ + return newItem(m, typeCheckbox, name); +} + +uiMenuItem *uiMenuAppendQuitItem(uiMenu *m) +{ + // duplicate check is in the register:to: selector + return newItem(m, typeQuit, NULL); +} + +uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m) +{ + // duplicate check is in the register:to: selector + return newItem(mm, typePreferences, NULL); +} + +uiMenuItem *uiMenuAppendAboutItem(uiMenu *m) +{ + // duplicate check is in the register:to: selector + return newItem(m, typeAbout, NULL); +} + +void uiMenuAppendSeparator(uiMenu *m) +{ + newItem(m, typeSeparator, NULL); +} + +uiMenu *uiNewMenu(const char *name) +{ + uiMenu *m; + + if (menusFinalized) + complain("attempt to create a new menu after menus have been finalized"); + if (menus == nil) + menus = [NSMutableArray new]; + + m = uiNew(uiMenu); + uiTyped(m)->Type = uiTypeMenu(); + + m->menu = [[NSMenu alloc] initWithTitle:toNSString(name)]; + // use automatic menu item enabling for all menus for consistency's sake + + m->item = [[NSMenuItem alloc] initWithTitle:toNSString(name) action:NULL keyEquivalent:@""]; + [m->item setSubmenu:m->menu]; + + m->items = [NSMutableArray new]; + + [[realNSApp() mainMenu] addItem:m->item]; + + [menus addObject:[NSValue valueWithPointer:m]]; + + return m; +} + +void finalizeMenus(void) +{ + menusFinalized = YES; +} + +void uninitMenus(void) +{ + if (menus == NULL) + return; + // don't worry about the actual NSMenus and NSMenuItems; they'll be freed when we clean up the NSApplication + [menus enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) { + NSValue *v; + uiMenu *m; + + v = (NSValue *) obj; + m = (uiMenu *) [v pointerValue]; + [m->items enumerateObjectsUsingBlock:^(id obj, NSUInteger index, BOOL *stop) { + NSValue *v; + uiMenuItem *mi; + + v = (NSValue *) obj; + mi = (uiMenuItem *) [v pointerValue]; + uiFree(mi); + }]; + [m->items release]; + uiFree(m); + }]; + [menus release]; +} diff --git a/redo/reredo/darwin/uipriv_darwin.h b/redo/reredo/darwin/uipriv_darwin.h new file mode 100644 index 00000000..0d59fac6 --- /dev/null +++ b/redo/reredo/darwin/uipriv_darwin.h @@ -0,0 +1,66 @@ +// 6 january 2015 +#define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_7 +#define MAC_OS_X_VERSION_MAX_ALLOWED MAC_OS_X_VERSION_10_7 +#import +#import "../ui.h" +#import "../ui_darwin.h" +#import "../uipriv.h" + +#define toNSString(str) [NSString stringWithUTF8String:(str)] +#define fromNSString(str) [(str) UTF8String] + +// These are based on measurements from Interface Builder. +// These seem to be based on Auto Layout constants, but I don't see an API that exposes these... +#define macXMargin 20 +#define macYMargin 20 + +// menu.m +@interface menuManager : NSObject { + // unfortunately NSMutableDictionary copies its keys, meaning we can't use it for pointers + NSMapTable *items; + BOOL hasQuit; + BOOL hasPreferences; + BOOL hasAbout; +} +@property (strong) NSMenuItem *quitItem; +@property (strong) NSMenuItem *preferencesItem; +@property (strong) NSMenuItem *aboutItem; +// NSMenuValidation is only informal +- (BOOL)validateMenuItem:(NSMenuItem *)item; +- (NSMenu *)makeMenubar; +@end +extern void finalizeMenus(void); +extern void uninitMenus(void); + +// init.m +@interface applicationClass : NSApplication +@end +// this is needed because NSApp is of type id, confusing clang +#define realNSApp() ((applicationClass *) NSApp) +@interface appDelegate : NSObject +@property (strong) menuManager *menuManager; +@end +#define appDelegate() ((appDelegate *) [realNSApp() delegate]) + +// util.m +extern void setStandardControlFont(NSControl *); +extern void disableAutocorrect(NSTextView *); + +// entry.m +extern void finishNewTextField(uiControl *, NSTextField *, BOOL); + +// window.m +extern uiWindow *windowFromNSWindow(NSWindow *); + +// alloc.m +extern void initAlloc(void); +extern void uninitAlloc(void); + +// bin.c +extern uiControl *newBin(void); +extern void binSetChild(uiControl *, uiControl *); +extern int binMargined(uiControl *); +extern void binSetMargined(uiControl *, int); + +// TODO +#define PUT_CODE_HERE 0