diff --git a/new/test/main.c b/new/test/main.c index 62f40fb6..5fe612cd 100644 --- a/new/test/main.c +++ b/new/test/main.c @@ -37,7 +37,7 @@ int main(int argc, char *argv[]) return 1; } -// initMenus(); + initMenus(); w = newWindow("Main Window", 320, 240, 1); uiWindowOnClosing(w, onClosing, NULL); diff --git a/new/windows/menu.c b/new/windows/menu.c index bad737be..8c033e6c 100644 --- a/new/windows/menu.c +++ b/new/windows/menu.c @@ -1,8 +1,242 @@ // 24 april 2015 #include "uipriv_windows.h" +// TODO window destruction + +static struct menu *menus = NULL; +static uintmax_t len = 0; +static uintmax_t cap = 0; +static BOOL menusFinalized = FALSE; + +struct menu { + uiMenu m; + WCHAR *name; + struct menuItem *items; + uintmax_t len; + uintmax_t cap; + WORD curID; +}; + +struct menuItem { + uiMenuItem mi; + WCHAR *name; + int type; + WORD id; + void (*onClicked)(uiMenuItem *, uiWindow *, void *); + void *onClickedData; + BOOL disabled; // template for new instances; kept in sync with everything else + BOOL checked; + HWND *hwnds; + HMENU *hmenus; + uintmax_t len; + uintmax_t cap; +}; + +enum { + typeRegular, + typeCheckbox, + typeQuit, + typePreferences, + typeAbout, + typeSeparator, +}; + +#define grow 32 + +static void sync(struct menuItem *item) +{ + uintmax_t i; + MENUITEMINFOW mi; + + // TODO do we need to get the current state and amend it? + ZeroMemory(&mi, sizeof (MENUITEMINFOW)); + mi.cbSize = sizeof (MENUITEMINFOW); + mi.fMask = MIIM_STATE; + if (item->disabled) + mi.fState |= MFS_DISABLED; + if (item->checked) + mi.fState |= MFS_CHECKED; + + for (i = 0; i < item->len; i++) + if (SetMenuItemInfo(item->hmenus[i], item->id, FALSE, &mi) == 0) + logLastError("error synchronizing menu items in windows/menu.c sync()"); +} + +static void defaultOnClicked(uiMenuItem *item, uiWindow *w, void *data) +{ + // do nothing +} + +static void menuItemEnable(uiMenuItem *ii) +{ + struct menuItem *item = (struct menuItem *) ii; + + item->disabled = FALSE; + sync(item); +} + +static void menuItemDisable(uiMenuItem *ii) +{ + struct menuItem *item = (struct menuItem *) ii; + + item->disabled = TRUE; + sync(item); +} + +static void menuItemOnClicked(uiMenuItem *ii, void (*f)(uiMenuItem *, uiWindow *, void *), void *data) +{ + struct menuItem *item = (struct menuItem *) ii; + + item->onClicked = f; + item->onClickedData = data; +} + +static int menuItemChecked(uiMenuItem *ii) +{ + struct menuItem *item = (struct menuItem *) ii; + + return item->checked != FALSE; +} + +static void menuItemSetChecked(uiMenuItem *ii, int checked) +{ + struct menuItem *item = (struct menuItem *) ii; + + // use explicit values + item->checked = FALSE; + if (checked) + item->checked = TRUE; + sync(item); +} + +static uiMenuItem *newItem(struct menu *m, int type, const char *name) +{ + struct menuItem *item; + + if (menusFinalized) + complain("attempt to create a new menu item after menus have been finalized"); + + if (m->len >= m->cap) { + m->cap += grow; + m->items = (struct menuItem *) uiRealloc(m->items, m->cap * sizeof (struct menuItem), "struct menuItem[]"); + } + item = &(m->items[m->len]); + m->len++; + + item->type = type; + switch (item->type) { + case typeQuit: + item->name = toUTF16("Quit"); + break; + case typePreferences: + item->name = toUTF16("Preferences..."); + break; + case typeAbout: + item->name = toUTF16("About"); + break; + case typeSeparator: + break; + default: + item->name = toUTF16(name); + break; + } + + if (item->type != typeSeparator) { + item->id = m->curID; + m->curID++; + } + // TODO this shouldn't be necessary, but uiRealloc() doesn't yet zero out new bytes + else + item->id = 0; + + item->onClicked = defaultOnClicked; + + // TODO this shouldn't be necessary, but uiRealloc() doesn't yet zero out new bytes + item->disabled = FALSE; + item->checked = FALSE; + item->hwnds = NULL; + item->hmenus = NULL; + item->len = 0; + item->cap = 0; + + uiMenuItem(item)->Enable = menuItemEnable; + uiMenuItem(item)->Disable = menuItemDisable; + uiMenuItem(item)->OnClicked = menuItemOnClicked; + uiMenuItem(item)->Checked = menuItemChecked; + uiMenuItem(item)->SetChecked = menuItemSetChecked; + + return uiMenuItem(item); +} + +uiMenuItem *menuAppendItem(uiMenu *mm, const char *name) +{ + return newItem((struct menu *) mm, typeRegular, name); +} + +uiMenuItem *menuAppendCheckItem(uiMenu *mm, const char *name) +{ + return newItem((struct menu *) mm, typeCheckbox, name); +} + +uiMenuItem *menuAppendQuitItem(uiMenu *mm) +{ + // TODO check multiple quit items + // TODO conditionally add separator + newItem((struct menu *) mm, typeSeparator, NULL); + return newItem((struct menu *) mm, typeQuit, NULL); +} + +uiMenuItem *menuAppendPreferencesItem(uiMenu *mm) +{ + // TODO check multiple preferences items + // TODO conditionally add separator + newItem((struct menu *) mm, typeSeparator, NULL); + return newItem((struct menu *) mm, typePreferences, NULL); +} + +uiMenuItem *menuAppendAboutItem(uiMenu *mm) +{ + // TODO check multiple about items + // TODO conditionally add separator + newItem((struct menu *) mm, typeSeparator, NULL); + return newItem((struct menu *) mm, typeAbout, NULL); +} + +void menuAppendSeparator(uiMenu *mm) +{ + // TODO check multiple about items + newItem((struct menu *) mm, typeSeparator, NULL); +} + uiMenu *uiNewMenu(const char *name) { - // TODO - return NULL; + struct menu *m; + + if (menusFinalized) + complain("attempt to create a new menu after menus have been finalized"); + if (len >= cap) { + cap += grow; + menus = (struct menu *) uiRealloc(menus, cap * sizeof (struct menu), "struct menu[]"); + } + m = &menus[len]; + len++; + + m->name = toUTF16(name); + + // TODO this shouldn't be necessary, but uiRealloc() doesn't yet zero out new bytes + m->items = NULL; + m->len = 0; + m->cap = 0; + + // start somewhere safe + m->curID = 100; + + uiMenu(m)->AppendItem = menuAppendItem; + uiMenu(m)->AppendCheckItem = menuAppendCheckItem; + uiMenu(m)->AppendQuitItem = menuAppendQuitItem; + uiMenu(m)->AppendPreferencesItem = menuAppendPreferencesItem; + uiMenu(m)->AppendAboutItem = menuAppendAboutItem; + uiMenu(m)->AppendSeparator = menuAppendSeparator; + + return uiMenu(m); }