#import "uipriv_darwin.h"
struct uiMenu {
NSMenu *menu;
NSMenuItem *item;
};
struct uiMenuItem {
uiprivMenuItem *item;
int type;
BOOL disabled;
void (*onClicked)(uiMenuItem *, uiWindow *, void *);
void *onClickedData;
};
enum uiprivMenuItemType {
typeRegular,
typeCheckbox,
typeQuit,
typePreferences,
typeAbout,
};
@interface uiprivMenu : NSMenu {
@public
uiMenu *menu;
}
@end
@implementation uiprivMenu
- (id)initWithTitle:(NSString *)title uiMenu:(uiMenu *)m
{
self = [super initWithTitle:title];
if (self) {
self->menu = m;
}
return self;
}
@end
@implementation uiprivMenuItem
- (id)initWithTitle:(NSString *)title uiMenuItem:(uiMenuItem *)i
{
self = [super initWithTitle:title action:@selector(onClicked:) keyEquivalent:@""];
if (self) {
self->item = i;
[self setTarget:self];
}
return self;
}
- (IBAction)onClicked:(id)sender
{
if (self->item == NULL) {
uiprivImplBug("Clicked nonexistent uiMenuItem which should be impossible");
return;
}
switch (self->item->type) {
case typeQuit:
if (uiprivShouldQuit())
uiQuit();
return;
case typeCheckbox:
uiMenuItemSetChecked(self->item, !uiMenuItemChecked(self->item));
default:
(*(self->item->onClicked))(self->item, uiprivWindowFromNSWindow([uiprivNSApp() keyWindow]),
self->item->onClickedData);
break;
}
}
- (void)setUiMenuItem:(uiMenuItem *)i
{
self->item = i;
}
- (BOOL)validateMenuItem:(NSMenuItem *)menuItem
{
uiprivMenuItem *i = (uiprivMenuItem *)menuItem;
if (i->item == NULL)
return NO;
return !i->item->disabled;
}
@end
@implementation uiprivMenuManager
- (id)init
{
self = [super init];
if (self) {
self->hasQuit = NO;
self->hasPreferences = NO;
self->hasAbout = NO;
self->finalized = NO;
}
return self;
}
- (BOOL)finalized
{
return self->finalized;
}
- (void)finalize
{
self->finalized = YES;
}
- (void)dealloc
{
uiprivUninitMenus();
[super dealloc];
}
- (void)register:(enum uiprivMenuItemType)type
{
switch (type) {
case typeQuit:
if (self->hasQuit)
uiprivUserBug("You can't have multiple Quit menu items in one program.");
self->hasQuit = YES;
break;
case typePreferences:
if (self->hasPreferences)
uiprivUserBug("You can't have multiple Preferences menu items in one program.");
self->hasPreferences = YES;
break;
case typeAbout:
if (self->hasAbout)
uiprivUserBug("You can't have multiple About menu items in one program.");
self->hasAbout = YES;
break;
}
}
- (void)buildApplicationMenu:(NSMenu *)menubar
{
NSString *appName;
NSMenuItem *appMenuItem;
NSMenu *appMenu;
NSMenuItem *item;
uiprivMenuItem *pitem;
NSString *title;
NSMenu *servicesMenu;
appName = [[NSProcessInfo processInfo] processName];
appMenuItem = [[[NSMenuItem alloc] initWithTitle:appName action:NULL keyEquivalent:@""] autorelease];
appMenu = [[[NSMenu alloc] initWithTitle:appName] autorelease];
[appMenuItem setSubmenu:appMenu];
[menubar addItem:appMenuItem];
title = [@"About " stringByAppendingString:appName];
pitem = [[[uiprivMenuItem alloc] initWithTitle:title uiMenuItem:NULL] autorelease];
[appMenu addItem:pitem];
self.aboutItem = pitem;
[appMenu addItem:[NSMenuItem separatorItem]];
pitem = [[[uiprivMenuItem alloc] initWithTitle:@"Preferences\u2026" uiMenuItem:NULL] autorelease];
[appMenu addItem:pitem];
self.preferencesItem = pitem;
[appMenu addItem:[NSMenuItem separatorItem]];
item = [[[NSMenuItem alloc] initWithTitle:@"Services" action:NULL keyEquivalent:@""] autorelease];
servicesMenu = [[[NSMenu alloc] initWithTitle:@"Services"] autorelease];
[item setSubmenu:servicesMenu];
[uiprivNSApp() setServicesMenu:servicesMenu];
[appMenu addItem:item];
[appMenu addItem:[NSMenuItem separatorItem]];
title = [@"Hide " stringByAppendingString:appName];
item = [[[NSMenuItem alloc] initWithTitle:title action:@selector(hide:) keyEquivalent:@"h"] autorelease];
[appMenu addItem:item];
item = [[[NSMenuItem alloc] initWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"] autorelease];
[item setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask)];
[appMenu addItem:item];
item = [[[NSMenuItem alloc] initWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""] autorelease];
[appMenu addItem:item];
[appMenu addItem:[NSMenuItem separatorItem]];
title = [@"Quit " stringByAppendingString:appName];
pitem = [[[uiprivMenuItem alloc] initWithTitle:title uiMenuItem:NULL] autorelease];
[appMenu addItem:pitem];
self.quitItem = pitem;
}
- (NSMenu *)makeMenubar
{
NSMenu *menubar;
menubar = [[[NSMenu alloc] initWithTitle:@""] autorelease];
[self buildApplicationMenu:menubar];
return menubar;
}
@end
static void defaultOnClicked(uiMenuItem *item, uiWindow *w, void *data)
{
}
void uiMenuItemEnable(uiMenuItem *item)
{
item->disabled = NO;
}
void uiMenuItemDisable(uiMenuItem *item)
{
item->disabled = YES;
}
void uiMenuItemOnClicked(uiMenuItem *item, void (*f)(uiMenuItem *, uiWindow *, void *), void *data)
{
if (item->type == typeQuit)
uiprivUserBug("You can't 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 uiMenuItemSetChecked(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)
{
@autoreleasepool {
uiMenuItem *item;
if ([uiprivAppDelegate().menuManager finalized])
uiprivUserBug("You can't create a new menu item after menus have been finalized.");
item = uiprivNew(uiMenuItem);
item->type = type;
switch (item->type) {
case typeQuit:
item->item = uiprivAppDelegate().menuManager.quitItem;
[item->item setUiMenuItem:item];
break;
case typePreferences:
item->item = uiprivAppDelegate().menuManager.preferencesItem;
[item->item setUiMenuItem:item];
break;
case typeAbout:
item->item = uiprivAppDelegate().menuManager.aboutItem;
[item->item setUiMenuItem:item];
break;
default:
item->item = [[uiprivMenuItem alloc] initWithTitle:uiprivToNSString(name) uiMenuItem:item];
[m->menu addItem:item->item];
break;
}
[uiprivAppDelegate().menuManager register:item->type];
if (item->type != typeQuit)
uiMenuItemOnClicked(item, defaultOnClicked, NULL);
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)
{
return newItem(m, typeQuit, NULL);
}
uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m)
{
return newItem(m, typePreferences, NULL);
}
uiMenuItem *uiMenuAppendAboutItem(uiMenu *m)
{
return newItem(m, typeAbout, NULL);
}
void uiMenuAppendSeparator(uiMenu *m)
{
[m->menu addItem:[NSMenuItem separatorItem]];
}
uiMenu *uiNewMenu(const char *name)
{
@autoreleasepool {
uiMenu *m;
if ([uiprivAppDelegate().menuManager finalized])
uiprivUserBug("You can't create a new menu after menus have been finalized.");
m = uiprivNew(uiMenu);
m->menu = [[uiprivMenu alloc] initWithTitle:uiprivToNSString(name) uiMenu:m];
m->item = [[NSMenuItem alloc] initWithTitle:uiprivToNSString(name) action:NULL keyEquivalent:@""];
[m->item setSubmenu:m->menu];
[[uiprivNSApp() mainMenu] addItem:m->item];
return m;
} }
void uiprivFinalizeMenus(void)
{
[uiprivAppDelegate().menuManager finalize];
}
void uiprivUninitMenus(void)
{
NSMenuItem *mi;
NSMenu *sm;
NSMenuItem *smi;
for (mi in [[uiprivNSApp() mainMenu] itemArray]) {
if ([mi hasSubmenu]) {
sm = [mi submenu];
for (smi in [sm itemArray]) {
if ([smi isKindOfClass:[uiprivMenuItem class]]) {
uiprivMenuItem *x = (uiprivMenuItem *)smi;
if (x->item != NULL)
uiprivFree(x->item);
}
}
if ([sm isKindOfClass:[uiprivMenu class]]) {
uiprivMenu *x = (uiprivMenu *)sm;
uiprivFree(x->menu);
}
}
}
}