diff options
Diffstat (limited to 'video/out/win32/menu.c')
-rw-r--r-- | video/out/win32/menu.c | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/video/out/win32/menu.c b/video/out/win32/menu.c new file mode 100644 index 0000000..25681e8 --- /dev/null +++ b/video/out/win32/menu.c @@ -0,0 +1,231 @@ +/* + * This file is part of mpv. + * + * mpv is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * mpv is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with mpv. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <windows.h> +#include <string.h> + +#include "libmpv/client.h" +#include "osdep/io.h" +#include "mpv_talloc.h" + +#include "menu.h" + +struct menu_ctx { + HMENU menu; + void *ta_data; // talloc context for MENUITEMINFOW.dwItemData +}; + +// append menu item to HMENU +static int append_menu(HMENU hmenu, UINT fMask, UINT fType, UINT fState, + wchar_t *title, HMENU submenu, void *data) +{ + static UINT id = WM_USER + 100; + MENUITEMINFOW mii = {0}; + + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_ID | fMask; + mii.wID = id++; + + if (fMask & MIIM_FTYPE) + mii.fType = fType; + if (fMask & MIIM_STATE) + mii.fState = fState; + if (fMask & MIIM_STRING) { + mii.dwTypeData = title; + mii.cch = wcslen(title); + } + if (fMask & MIIM_SUBMENU) + mii.hSubMenu = submenu; + if (fMask & MIIM_DATA) + mii.dwItemData = (ULONG_PTR)data; + + return InsertMenuItemW(hmenu, -1, TRUE, &mii) ? mii.wID : -1; +} + +// build fState for menu item creation +static int build_state(mpv_node *node) +{ + int fState = 0; + for (int i = 0; i < node->u.list->num; i++) { + mpv_node *item = &node->u.list->values[i]; + if (item->format != MPV_FORMAT_STRING) + continue; + + if (strcmp(item->u.string, "hidden") == 0) { + return -1; + } else if (strcmp(item->u.string, "checked") == 0) { + fState |= MFS_CHECKED; + } else if (strcmp(item->u.string, "disabled") == 0) { + fState |= MFS_DISABLED; + } + } + return fState; +} + +// build dwTypeData for menu item creation +static wchar_t *build_title(void *talloc_ctx, char *title, char *shortcut) +{ + if (shortcut && shortcut[0]) { + char *buf = talloc_asprintf(NULL, "%s\t%s", title, shortcut); + wchar_t *wbuf = mp_from_utf8(talloc_ctx, buf); + talloc_free(buf); + return wbuf; + } + return mp_from_utf8(talloc_ctx, title); +} + +// build HMENU from mpv node +// +// node structure: +// +// MPV_FORMAT_NODE_ARRAY +// MPV_FORMAT_NODE_MAP (menu item) +// "type" MPV_FORMAT_STRING +// "title" MPV_FORMAT_STRING +// "cmd" MPV_FORMAT_STRING +// "shortcut" MPV_FORMAT_STRING +// "state" MPV_FORMAT_NODE_ARRAY[MPV_FORMAT_STRING] +// "submenu" MPV_FORMAT_NODE_ARRAY[menu item] +static void build_menu(void *talloc_ctx, HMENU hmenu, struct mpv_node *node) +{ + if (node->format != MPV_FORMAT_NODE_ARRAY) + return; + + for (int i = 0; i < node->u.list->num; i++) { + mpv_node *item = &node->u.list->values[i]; + if (item->format != MPV_FORMAT_NODE_MAP) + continue; + + mpv_node_list *list = item->u.list; + + char *type = ""; + char *title = NULL; + char *cmd = NULL; + char *shortcut = NULL; + int fState = 0; + HMENU submenu = NULL; + + for (int j = 0; j < list->num; j++) { + char *key = list->keys[j]; + mpv_node *value = &list->values[j]; + + switch (value->format) { + case MPV_FORMAT_STRING: + if (strcmp(key, "title") == 0) { + title = value->u.string; + } else if (strcmp(key, "cmd") == 0) { + cmd = value->u.string; + } else if (strcmp(key, "type") == 0) { + type = value->u.string; + } else if (strcmp(key, "shortcut") == 0) { + shortcut = value->u.string; + } + break; + case MPV_FORMAT_NODE_ARRAY: + if (strcmp(key, "state") == 0) { + fState = build_state(value); + } else if (strcmp(key, "submenu") == 0) { + submenu = CreatePopupMenu(); + build_menu(talloc_ctx, submenu, value); + } + break; + default: + break; + } + } + + if (fState == -1) // hidden + continue; + + if (strcmp(type, "separator") == 0) { + append_menu(hmenu, MIIM_FTYPE, MFT_SEPARATOR, 0, NULL, NULL, NULL); + } else { + if (title == NULL || title[0] == '\0') + continue; + + UINT fMask = MIIM_STRING | MIIM_STATE; + bool grayed = false; + if (strcmp(type, "submenu") == 0) { + if (submenu == NULL) + submenu = CreatePopupMenu(); + fMask |= MIIM_SUBMENU; + grayed = GetMenuItemCount(submenu) == 0; + } else { + fMask |= MIIM_DATA; + grayed = cmd == NULL || cmd[0] == '\0' || cmd[0] == '#' || + strcmp(cmd, "ignore") == 0; + } + int id = append_menu(hmenu, fMask, 0, (UINT)fState, + build_title(talloc_ctx, title, shortcut), + submenu, talloc_strdup(talloc_ctx, cmd)); + if (id > 0 && grayed) + EnableMenuItem(hmenu, id, MF_BYCOMMAND | MF_GRAYED); + } + } +} + +struct menu_ctx *mp_win32_menu_init(void) +{ + struct menu_ctx *ctx = talloc_ptrtype(NULL, ctx); + ctx->menu = CreatePopupMenu(); + ctx->ta_data = talloc_new(ctx); + return ctx; +} + +void mp_win32_menu_uninit(struct menu_ctx *ctx) +{ + DestroyMenu(ctx->menu); + talloc_free(ctx); +} + +void mp_win32_menu_show(struct menu_ctx *ctx, HWND hwnd) +{ + POINT pt; + RECT rc; + + if (!GetCursorPos(&pt)) + return; + + GetClientRect(hwnd, &rc); + ScreenToClient(hwnd, &pt); + + if (!PtInRect(&rc, pt)) + return; + + ClientToScreen(hwnd, &pt); + TrackPopupMenuEx(ctx->menu, TPM_LEFTALIGN | TPM_LEFTBUTTON, pt.x, pt.y, + hwnd, NULL); +} + +void mp_win32_menu_update(struct menu_ctx *ctx, struct mpv_node *data) +{ + while (GetMenuItemCount(ctx->menu) > 0) + RemoveMenu(ctx->menu, 0, MF_BYPOSITION); + talloc_free_children(ctx->ta_data); + + build_menu(ctx->ta_data, ctx->menu, data); +} + +const char* mp_win32_menu_get_cmd(struct menu_ctx *ctx, UINT id) +{ + MENUITEMINFOW mii = {0}; + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_DATA; + + GetMenuItemInfoW(ctx->menu, id, FALSE, &mii); + return (const char *)mii.dwItemData; +} |