[melonDS] add support for submenus
This commit is contained in:
parent
5bd78c1932
commit
476ce2e93a
1
ui.h
1
ui.h
|
@ -286,6 +286,7 @@ _UI_EXTERN uiMenuItem *uiMenuAppendCheckItem(uiMenu *m, const char *name);
|
||||||
_UI_EXTERN uiMenuItem *uiMenuAppendQuitItem(uiMenu *m);
|
_UI_EXTERN uiMenuItem *uiMenuAppendQuitItem(uiMenu *m);
|
||||||
_UI_EXTERN uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m);
|
_UI_EXTERN uiMenuItem *uiMenuAppendPreferencesItem(uiMenu *m);
|
||||||
_UI_EXTERN uiMenuItem *uiMenuAppendAboutItem(uiMenu *m);
|
_UI_EXTERN uiMenuItem *uiMenuAppendAboutItem(uiMenu *m);
|
||||||
|
_UI_EXTERN uiMenuItem *uiMenuAppendSubmenu(uiMenu *m, uiMenu* child);
|
||||||
_UI_EXTERN void uiMenuAppendSeparator(uiMenu *m);
|
_UI_EXTERN void uiMenuAppendSeparator(uiMenu *m);
|
||||||
_UI_EXTERN uiMenu *uiNewMenu(const char *name);
|
_UI_EXTERN uiMenu *uiNewMenu(const char *name);
|
||||||
|
|
||||||
|
|
90
unix/menu.c
90
unix/menu.c
|
@ -2,6 +2,7 @@
|
||||||
#include "uipriv_unix.h"
|
#include "uipriv_unix.h"
|
||||||
|
|
||||||
static GArray *menus = NULL;
|
static GArray *menus = NULL;
|
||||||
|
static guint nmenus = 0;
|
||||||
static gboolean menusFinalized = FALSE;
|
static gboolean menusFinalized = FALSE;
|
||||||
static gboolean hasQuit = FALSE;
|
static gboolean hasQuit = FALSE;
|
||||||
static gboolean hasPreferences = FALSE;
|
static gboolean hasPreferences = FALSE;
|
||||||
|
@ -10,6 +11,8 @@ static gboolean hasAbout = FALSE;
|
||||||
struct uiMenu {
|
struct uiMenu {
|
||||||
char *name;
|
char *name;
|
||||||
GArray *items; // []*uiMenuItem
|
GArray *items; // []*uiMenuItem
|
||||||
|
gboolean ischild;
|
||||||
|
guint id;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct uiMenuItem {
|
struct uiMenuItem {
|
||||||
|
@ -21,6 +24,7 @@ struct uiMenuItem {
|
||||||
gboolean disabled;
|
gboolean disabled;
|
||||||
gboolean checked;
|
gboolean checked;
|
||||||
GHashTable *windows; // map[GtkMenuItem]*menuItemWindow
|
GHashTable *windows; // map[GtkMenuItem]*menuItemWindow
|
||||||
|
uiMenu *popupchild;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct menuItemWindow {
|
struct menuItemWindow {
|
||||||
|
@ -35,6 +39,7 @@ enum {
|
||||||
typePreferences,
|
typePreferences,
|
||||||
typeAbout,
|
typeAbout,
|
||||||
typeSeparator,
|
typeSeparator,
|
||||||
|
typeSubmenu,
|
||||||
};
|
};
|
||||||
|
|
||||||
// we do NOT want programmatic updates to raise an ::activated signal
|
// we do NOT want programmatic updates to raise an ::activated signal
|
||||||
|
@ -179,6 +184,33 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name)
|
||||||
}
|
}
|
||||||
|
|
||||||
item->windows = g_hash_table_new(g_direct_hash, g_direct_equal);
|
item->windows = g_hash_table_new(g_direct_hash, g_direct_equal);
|
||||||
|
item->popupchild = NULL;
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
uiMenuItem *uiMenuAppendSubmenu(uiMenu *m, uiMenu* child)
|
||||||
|
{
|
||||||
|
uiMenuItem *item;
|
||||||
|
|
||||||
|
if (menusFinalized)
|
||||||
|
uiprivUserBug("You cannot create a new menu item after menus have been finalized.");
|
||||||
|
|
||||||
|
item = uiprivNew(uiMenuItem);
|
||||||
|
|
||||||
|
g_array_append_val(m->items, item);
|
||||||
|
|
||||||
|
item->type = typeSubmenu;
|
||||||
|
item->name = child->name;
|
||||||
|
|
||||||
|
uiMenuItemOnClicked(item, defaultOnClicked, NULL);
|
||||||
|
|
||||||
|
// checkme
|
||||||
|
item->gtype = GTK_TYPE_MENU_ITEM;
|
||||||
|
|
||||||
|
item->windows = g_hash_table_new(g_direct_hash, g_direct_equal);
|
||||||
|
item->popupchild = child;
|
||||||
|
child->ischild = TRUE;
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
@ -237,9 +269,12 @@ uiMenu *uiNewMenu(const char *name)
|
||||||
m = uiprivNew(uiMenu);
|
m = uiprivNew(uiMenu);
|
||||||
|
|
||||||
g_array_append_val(menus, m);
|
g_array_append_val(menus, m);
|
||||||
|
m->id = nmenus;
|
||||||
|
nmenus++;
|
||||||
|
|
||||||
m->name = g_strdup(name);
|
m->name = g_strdup(name);
|
||||||
m->items = g_array_new(FALSE, TRUE, sizeof (uiMenuItem *));
|
m->items = g_array_new(FALSE, TRUE, sizeof (uiMenuItem *));
|
||||||
|
m->ischild = FALSE;
|
||||||
|
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
@ -264,6 +299,18 @@ static void appendMenuItem(GtkMenuShell *submenu, uiMenuItem *item, uiWindow *w)
|
||||||
ww->w = w;
|
ww->w = w;
|
||||||
ww->signal = signal;
|
ww->signal = signal;
|
||||||
g_hash_table_insert(item->windows, menuitem, ww);
|
g_hash_table_insert(item->windows, menuitem, ww);
|
||||||
|
|
||||||
|
if (item->popupchild != NULL) {
|
||||||
|
int j;
|
||||||
|
uiMenu* m;
|
||||||
|
GtkWidget *submenu;
|
||||||
|
|
||||||
|
m = item->popupchild;
|
||||||
|
submenu = gtk_menu_new();
|
||||||
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
|
||||||
|
for (j = 0; j < m->items->len; j++)
|
||||||
|
appendMenuItem(GTK_MENU_SHELL(submenu), g_array_index(m->items, uiMenuItem *, j), w);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GtkWidget *uiprivMakeMenubar(uiWindow *w)
|
GtkWidget *uiprivMakeMenubar(uiWindow *w)
|
||||||
|
@ -281,6 +328,7 @@ GtkWidget *uiprivMakeMenubar(uiWindow *w)
|
||||||
if (menus != NULL)
|
if (menus != NULL)
|
||||||
for (i = 0; i < menus->len; i++) {
|
for (i = 0; i < menus->len; i++) {
|
||||||
m = g_array_index(menus, uiMenu *, i);
|
m = g_array_index(menus, uiMenu *, i);
|
||||||
|
if (m->ischild) continue;
|
||||||
menuitem = gtk_menu_item_new_with_label(m->name);
|
menuitem = gtk_menu_item_new_with_label(m->name);
|
||||||
submenu = gtk_menu_new();
|
submenu = gtk_menu_new();
|
||||||
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu);
|
||||||
|
@ -299,6 +347,8 @@ struct freeMenuItemData {
|
||||||
guint i;
|
guint i;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void freeMenu(GtkWidget *widget, gpointer data);
|
||||||
|
|
||||||
static void freeMenuItem(GtkWidget *widget, gpointer data)
|
static void freeMenuItem(GtkWidget *widget, gpointer data)
|
||||||
{
|
{
|
||||||
struct freeMenuItemData *fmi = (struct freeMenuItemData *) data;
|
struct freeMenuItemData *fmi = (struct freeMenuItemData *) data;
|
||||||
|
@ -306,6 +356,8 @@ static void freeMenuItem(GtkWidget *widget, gpointer data)
|
||||||
struct menuItemWindow *w;
|
struct menuItemWindow *w;
|
||||||
|
|
||||||
item = g_array_index(fmi->items, uiMenuItem *, fmi->i);
|
item = g_array_index(fmi->items, uiMenuItem *, fmi->i);
|
||||||
|
if (item->popupchild != NULL)
|
||||||
|
freeMenu(widget, &item->popupchild->id);
|
||||||
w = (struct menuItemWindow *) g_hash_table_lookup(item->windows, widget);
|
w = (struct menuItemWindow *) g_hash_table_lookup(item->windows, widget);
|
||||||
if (g_hash_table_remove(item->windows, widget) == FALSE)
|
if (g_hash_table_remove(item->windows, widget) == FALSE)
|
||||||
uiprivImplBug("GtkMenuItem %p not in menu item's item/window map", widget);
|
uiprivImplBug("GtkMenuItem %p not in menu item's item/window map", widget);
|
||||||
|
@ -339,28 +391,38 @@ void uiprivFreeMenubar(GtkWidget *mb)
|
||||||
// no need to worry about destroying any widgets; destruction of the window they're in will do it for us
|
// no need to worry about destroying any widgets; destruction of the window they're in will do it for us
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _freeMenu(uiMenu* m)
|
||||||
|
{
|
||||||
|
uiMenuItem *item;
|
||||||
|
guint j;
|
||||||
|
|
||||||
|
g_free(m->name);
|
||||||
|
for (j = 0; j < m->items->len; j++) {
|
||||||
|
item = g_array_index(m->items, uiMenuItem *, j);
|
||||||
|
if (item->popupchild != NULL)
|
||||||
|
_freeMenu(item->popupchild);
|
||||||
|
if (g_hash_table_size(item->windows) != 0)
|
||||||
|
// TODO is this really a userbug()?
|
||||||
|
uiprivImplBug("menu item %p (%s) still has uiWindows attached; did you forget to destroy some windows?", item, item->name);
|
||||||
|
if (item->type != typeSubmenu) g_free(item->name);
|
||||||
|
g_hash_table_destroy(item->windows);
|
||||||
|
uiprivFree(item);
|
||||||
|
}
|
||||||
|
g_array_free(m->items, TRUE);
|
||||||
|
uiprivFree(m);
|
||||||
|
}
|
||||||
|
|
||||||
void uiprivUninitMenus(void)
|
void uiprivUninitMenus(void)
|
||||||
{
|
{
|
||||||
uiMenu *m;
|
uiMenu *m;
|
||||||
uiMenuItem *item;
|
guint i;
|
||||||
guint i, j;
|
|
||||||
|
|
||||||
if (menus == NULL)
|
if (menus == NULL)
|
||||||
return;
|
return;
|
||||||
for (i = 0; i < menus->len; i++) {
|
for (i = 0; i < menus->len; i++) {
|
||||||
m = g_array_index(menus, uiMenu *, i);
|
m = g_array_index(menus, uiMenu *, i);
|
||||||
g_free(m->name);
|
if (m->ischild) continue;
|
||||||
for (j = 0; j < m->items->len; j++) {
|
_freeMenu(m);
|
||||||
item = g_array_index(m->items, uiMenuItem *, j);
|
|
||||||
if (g_hash_table_size(item->windows) != 0)
|
|
||||||
// TODO is this really a uiprivUserBug()?
|
|
||||||
uiprivImplBug("menu item %p (%s) still has uiWindows attached; did you forget to destroy some windows?", item, item->name);
|
|
||||||
g_free(item->name);
|
|
||||||
g_hash_table_destroy(item->windows);
|
|
||||||
uiprivFree(item);
|
|
||||||
}
|
|
||||||
g_array_free(m->items, TRUE);
|
|
||||||
uiprivFree(m);
|
|
||||||
}
|
}
|
||||||
g_array_free(menus, TRUE);
|
g_array_free(menus, TRUE);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,9 @@ static BOOL hasAbout = FALSE;
|
||||||
|
|
||||||
struct uiMenu {
|
struct uiMenu {
|
||||||
WCHAR *name;
|
WCHAR *name;
|
||||||
|
HMENU handle;
|
||||||
uiMenuItem **items;
|
uiMenuItem **items;
|
||||||
|
BOOL ischild;
|
||||||
size_t len;
|
size_t len;
|
||||||
size_t cap;
|
size_t cap;
|
||||||
};
|
};
|
||||||
|
@ -28,6 +30,7 @@ struct uiMenuItem {
|
||||||
BOOL disabled; // template for new instances; kept in sync with everything else
|
BOOL disabled; // template for new instances; kept in sync with everything else
|
||||||
BOOL checked;
|
BOOL checked;
|
||||||
HMENU *hmenus;
|
HMENU *hmenus;
|
||||||
|
uiMenu* popupchild;
|
||||||
size_t len;
|
size_t len;
|
||||||
size_t cap;
|
size_t cap;
|
||||||
};
|
};
|
||||||
|
@ -39,6 +42,7 @@ enum {
|
||||||
typePreferences,
|
typePreferences,
|
||||||
typeAbout,
|
typeAbout,
|
||||||
typeSeparator,
|
typeSeparator,
|
||||||
|
typeSubmenu,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define grow 32
|
#define grow 32
|
||||||
|
@ -140,6 +144,7 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name)
|
||||||
item->name = toUTF16(name);
|
item->name = toUTF16(name);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
item->popupchild = NULL;
|
||||||
|
|
||||||
if (item->type != typeSeparator) {
|
if (item->type != typeSeparator) {
|
||||||
item->id = curID;
|
item->id = curID;
|
||||||
|
@ -156,6 +161,34 @@ static uiMenuItem *newItem(uiMenu *m, int type, const char *name)
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uiMenuItem *uiMenuAppendSubmenu(uiMenu *m, uiMenu* child)
|
||||||
|
{
|
||||||
|
uiMenuItem *item;
|
||||||
|
|
||||||
|
if (menusFinalized)
|
||||||
|
uiprivUserBug("You can not create a new menu item after menus have been finalized.");
|
||||||
|
|
||||||
|
if (m->len >= m->cap) {
|
||||||
|
m->cap += grow;
|
||||||
|
m->items = (uiMenuItem **) uiprivRealloc(m->items, m->cap * sizeof (uiMenuItem *), "uiMenuitem *[]");
|
||||||
|
}
|
||||||
|
|
||||||
|
item = uiprivNew(uiMenuItem);
|
||||||
|
|
||||||
|
m->items[m->len] = item;
|
||||||
|
m->len++;
|
||||||
|
|
||||||
|
item->type = typeSubmenu;
|
||||||
|
item->name = child->name;
|
||||||
|
|
||||||
|
item->popupchild = child;
|
||||||
|
child->ischild = TRUE;
|
||||||
|
|
||||||
|
uiMenuItemOnClicked(item, defaultOnClicked, NULL);
|
||||||
|
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
uiMenuItem *uiMenuAppendItem(uiMenu *m, const char *name)
|
uiMenuItem *uiMenuAppendItem(uiMenu *m, const char *name)
|
||||||
{
|
{
|
||||||
return newItem(m, typeRegular, name);
|
return newItem(m, typeRegular, name);
|
||||||
|
@ -216,13 +249,17 @@ uiMenu *uiNewMenu(const char *name)
|
||||||
len++;
|
len++;
|
||||||
|
|
||||||
m->name = toUTF16(name);
|
m->name = toUTF16(name);
|
||||||
|
m->ischild = FALSE;
|
||||||
|
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static HMENU makeMenu(uiMenu *m);
|
||||||
|
|
||||||
static void appendMenuItem(HMENU menu, uiMenuItem *item)
|
static void appendMenuItem(HMENU menu, uiMenuItem *item)
|
||||||
{
|
{
|
||||||
UINT uFlags;
|
UINT uFlags;
|
||||||
|
UINT_PTR id = item->id;
|
||||||
|
|
||||||
uFlags = MF_SEPARATOR;
|
uFlags = MF_SEPARATOR;
|
||||||
if (item->type != typeSeparator) {
|
if (item->type != typeSeparator) {
|
||||||
|
@ -231,8 +268,12 @@ static void appendMenuItem(HMENU menu, uiMenuItem *item)
|
||||||
uFlags |= MF_DISABLED | MF_GRAYED;
|
uFlags |= MF_DISABLED | MF_GRAYED;
|
||||||
if (item->checked)
|
if (item->checked)
|
||||||
uFlags |= MF_CHECKED;
|
uFlags |= MF_CHECKED;
|
||||||
|
if (item->popupchild) {
|
||||||
|
uFlags |= MF_POPUP;
|
||||||
|
id = (UINT_PTR)makeMenu(item->popupchild);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (AppendMenuW(menu, uFlags, item->id, item->name) == 0)
|
if (AppendMenuW(menu, uFlags, id, item->name) == 0)
|
||||||
logLastError(L"error appending menu item");
|
logLastError(L"error appending menu item");
|
||||||
|
|
||||||
if (item->len >= item->cap) {
|
if (item->len >= item->cap) {
|
||||||
|
@ -251,6 +292,7 @@ static HMENU makeMenu(uiMenu *m)
|
||||||
menu = CreatePopupMenu();
|
menu = CreatePopupMenu();
|
||||||
if (menu == NULL)
|
if (menu == NULL)
|
||||||
logLastError(L"error creating menu");
|
logLastError(L"error creating menu");
|
||||||
|
m->handle = menu;
|
||||||
for (i = 0; i < m->len; i++)
|
for (i = 0; i < m->len; i++)
|
||||||
appendMenuItem(menu, m->items[i]);
|
appendMenuItem(menu, m->items[i]);
|
||||||
return menu;
|
return menu;
|
||||||
|
@ -269,6 +311,7 @@ HMENU makeMenubar(void)
|
||||||
logLastError(L"error creating menubar");
|
logLastError(L"error creating menubar");
|
||||||
|
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
|
if (menus[i]->ischild) continue;
|
||||||
menu = makeMenu(menus[i]);
|
menu = makeMenu(menus[i]);
|
||||||
if (AppendMenuW(menubar, MF_POPUP | MF_STRING, (UINT_PTR) menu, menus[i]->name) == 0)
|
if (AppendMenuW(menubar, MF_POPUP | MF_STRING, (UINT_PTR) menu, menus[i]->name) == 0)
|
||||||
logLastError(L"error appending menu to menubar");
|
logLastError(L"error appending menu to menubar");
|
||||||
|
@ -321,21 +364,27 @@ static void freeMenu(uiMenu *m, HMENU submenu)
|
||||||
item->hmenus[j] = item->hmenus[j + 1];
|
item->hmenus[j] = item->hmenus[j + 1];
|
||||||
item->hmenus[j] = NULL;
|
item->hmenus[j] = NULL;
|
||||||
item->len--;
|
item->len--;
|
||||||
|
|
||||||
|
if (item->popupchild)
|
||||||
|
freeMenu(item->popupchild, item->popupchild->handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void freeMenubar(HMENU menubar)
|
void freeMenubar(HMENU menubar)
|
||||||
{
|
{
|
||||||
size_t i;
|
size_t i;
|
||||||
|
size_t j = 0;
|
||||||
MENUITEMINFOW mi;
|
MENUITEMINFOW mi;
|
||||||
|
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
|
if (menus[i]->ischild) continue;
|
||||||
ZeroMemory(&mi, sizeof (MENUITEMINFOW));
|
ZeroMemory(&mi, sizeof (MENUITEMINFOW));
|
||||||
mi.cbSize = sizeof (MENUITEMINFOW);
|
mi.cbSize = sizeof (MENUITEMINFOW);
|
||||||
mi.fMask = MIIM_SUBMENU;
|
mi.fMask = MIIM_SUBMENU;
|
||||||
if (GetMenuItemInfoW(menubar, i, TRUE, &mi) == 0)
|
if (GetMenuItemInfoW(menubar, j, TRUE, &mi) == 0)
|
||||||
logLastError(L"error getting menu to delete item references from");
|
logLastError(L"error getting menu to delete item references from");
|
||||||
freeMenu(menus[i], mi.hSubMenu);
|
freeMenu(menus[i], mi.hSubMenu);
|
||||||
|
j++;
|
||||||
}
|
}
|
||||||
// no need to worry about destroying any menus; destruction of the window they're in will do it for us
|
// no need to worry about destroying any menus; destruction of the window they're in will do it for us
|
||||||
}
|
}
|
||||||
|
@ -354,7 +403,7 @@ void uninitMenus(void)
|
||||||
if (item->len != 0)
|
if (item->len != 0)
|
||||||
// LONGTERM uiprivUserBug()?
|
// LONGTERM uiprivUserBug()?
|
||||||
uiprivImplBug("menu item %p (%ws) still has uiWindows attached; did you forget to destroy some windows?", item, item->name);
|
uiprivImplBug("menu item %p (%ws) still has uiWindows attached; did you forget to destroy some windows?", item, item->name);
|
||||||
if (item->name != NULL)
|
if (item->type != typeSubmenu && item->name != NULL)
|
||||||
uiprivFree(item->name);
|
uiprivFree(item->name);
|
||||||
if (item->hmenus != NULL)
|
if (item->hmenus != NULL)
|
||||||
uiprivFree(item->hmenus);
|
uiprivFree(item->hmenus);
|
||||||
|
|
Loading…
Reference in New Issue