#include "osmenuitem_win.inl"
#include "osmenu_win.inl"
#include "osgui_win.inl"
#include "osimg.inl"
#include "../osmenuitem.h"
#include <core/heap.h>
#include <core/event.h>
#include <core/strings.h>
#include <sewer/bstd.h>
#include <sewer/cassert.h>
#include <sewer/ptr.h>
#include <sewer/unicode.h>
#if !defined(__WINDOWS__)
#error This file is only for Windows
#endif
static const char_t *i_KEY_TEXT[] =
{
"",
"A",
"S",
"D",
"F",
"H",
"G",
"Z",
"X",
"C",
"V",
"\\",
"B",
"Q",
"W",
"E",
"R",
"Y",
"T",
"1",
"2",
"3",
"4",
"6",
"5",
"9",
"7",
"8",
"0",
"}",
"O",
"U",
"{",
"I",
"P",
"Intro",
"L",
"J",
";",
"K",
"\'",
",",
"-",
"N",
"M",
".",
"Tab",
" ",
">",
"Back",
"Esc",
"F17",
".",
"*",
"+",
"Lock",
"/",
"Itro",
"-",
"F18",
"F19",
" ",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"F5",
"F6",
"F7",
"F3",
"F8",
"F9",
"F11",
"F13",
"F16",
"F14",
"F10",
"F12",
"F15",
"Up",
"Home",
" ",
"F4",
"Down",
"F2",
"End",
"F1",
"←",
"→",
"↓",
"↑"
};
struct _osmenuitem_t
{
OSMenu *menu;
OSMenu *sub_menu;
String *text;
uint16_t id;
bool_t visible;
bool_t enabled;
uint8_t state;
HBITMAP hbitmap;
vkey_t key;
uint32_t modifiers;
Listener *OnClick;
};
static ___INLINE OSMenuItem *i_create(const uint16_t id, const uint8_t state, const char_t *text)
{
OSMenuItem *item = heap_new0(OSMenuItem);
item->text = text != NULL ? str_c(text) : NULL;
item->id = id;
item->visible = TRUE;
item->enabled = TRUE;
item->state = state;
item->key = ENUM_MAX(vkey_t);
item->modifiers = UINT32_MAX;
return item;
}
OSMenuItem *osmenuitem_create(const uint32_t flags)
{
uint16_t id = _osgui_unique_child_id();
if (flags == ekMENU_ITEM)
{
return i_create(id, ekGUI_OFF, "");
}
else
{
cassert(flags == ekMENU_SEPARATOR);
return i_create(id, UINT8_MAX, NULL);
}
}
void osmenuitem_destroy(OSMenuItem **item)
{
cassert_no_null(item);
cassert_no_null(*item);
cassert((*item)->menu == NULL);
cassert((*item)->sub_menu == NULL);
listener_destroy(&(*item)->OnClick);
if ((*item)->hbitmap != NULL)
{
BOOL ok = DeleteObject((*item)->hbitmap);
cassert_unref(ok != 0, ok);
}
str_destopt(&(*item)->text);
heap_delete(item, OSMenuItem);
}
void osmenuitem_OnClick(OSMenuItem *item, Listener *listener)
{
cassert_no_null(item);
listener_update(&item->OnClick, listener);
}
static UINT i_item_type(const gui_state_t state)
{
switch (state)
{
case ekGUI_ON:
case ekGUI_OFF:
return MFT_STRING;
case ekGUI_MIXED:
return MFT_STRING | MFT_RADIOCHECK;
cassert_default();
}
return MFT_STRING;
}
static UINT i_item_state(const gui_state_t state, const bool_t enabled)
{
UINT ustate = 0;
if (enabled == TRUE)
ustate |= MFS_ENABLED;
else
ustate |= MFS_DISABLED;
switch (state)
{
case ekGUI_ON:
case ekGUI_MIXED:
ustate |= MFS_CHECKED;
break;
case ekGUI_OFF:
ustate |= MFS_UNCHECKED;
break;
cassert_default();
}
return ustate;
}
void osmenuitem_enabled(OSMenuItem *item, const bool_t enabled)
{
cassert_no_null(item);
cassert(item->state != UINT8_MAX);
if (item->enabled != enabled)
{
item->enabled = enabled;
if (item->menu != NULL && item->visible == TRUE)
{
HMENU hmenu = _osmenu_hmenu(item->menu);
if (hmenu != NULL)
{
MENUITEMINFO info;
BOOL ok = FALSE;
info.fMask = MIIM_STATE;
info.cbSize = sizeof(MENUITEMINFO);
info.fState = i_item_state((gui_state_t)item->state, item->enabled);
ok = SetMenuItemInfo(hmenu, item->id, FALSE, &info);
cassert_unref(ok == TRUE, ok);
_osmenu_hmenu_redraw(item->menu);
}
}
}
}
static void i_item_text(const char_t *text, const vkey_t key, const uint32_t modifiers, WCHAR *item_wtext, const uint32_t max_chars)
{
char_t item_text[512];
cassert_no_null(text);
cassert_no_null(item_text);
if (key != ENUM_MAX(vkey_t))
{
const char_t *mod = "";
const char_t *wkey = "";
if (modifiers & ekMKEY_CONTROL)
{
if (modifiers & ekMKEY_ALT)
{
if (modifiers & ekMKEY_SHIFT)
mod = "Ctrl+Alt+Shift+";
else
mod = "Ctrl+Alt+";
}
else
{
if (modifiers & ekMKEY_SHIFT)
mod = "Ctrl+Shift+";
else
mod = "Ctrl+";
}
}
else
{
if (modifiers & ekMKEY_ALT)
{
if (modifiers & ekMKEY_SHIFT)
mod = "Alt+Shift+";
else
mod = "Alt+";
}
else
{
if (modifiers & ekMKEY_SHIFT)
mod = "Shift+";
else
mod = "";
}
}
wkey = i_KEY_TEXT[key];
bstd_sprintf(item_text, sizeof(item_text), "%s\t%s%s", text, mod, wkey);
}
else
{
bstd_sprintf(item_text, sizeof(item_text), "%s", text);
}
unicode_convers(item_text, cast(item_wtext, char_t), ekUTF8, ekUTF16, sizeof(WCHAR) * max_chars);
}
void osmenuitem_visible(OSMenuItem *item, const bool_t visible)
{
cassert_no_null(item);
if (item->visible != visible)
{
item->visible = visible;
if (item->menu != NULL)
_osmenu_hmenu_recompute(item->menu);
}
}
void osmenuitem_text(OSMenuItem *item, const char_t *text)
{
cassert_no_null(item);
cassert(item->state != UINT8_MAX);
cassert_no_null(text);
str_upd(&item->text, text);
if (item->menu != NULL && item->visible == TRUE)
{
HMENU hmenu = _osmenu_hmenu(item->menu);
if (hmenu != NULL)
{
WCHAR item_text[512];
MENUITEMINFO info;
BOOL ok = FALSE;
i_item_text(tc(item->text), item->key, item->modifiers, item_text, 512);
info.cbSize = sizeof(MENUITEMINFO);
info.fMask = MIIM_STRING;
info.dwTypeData = item_text;
ok = SetMenuItemInfo(hmenu, item->id, FALSE, &info);
cassert_unref(ok == TRUE, ok);
}
}
}
void osmenuitem_image(OSMenuItem *item, const Image *image)
{
cassert_no_null(item);
if (item->hbitmap != NULL)
{
BOOL ok = DeleteObject(item->hbitmap);
cassert_unref(ok != 0, ok);
item->hbitmap = NULL;
}
if (image != NULL)
item->hbitmap = _osimg_hbitmap(image, 0);
if (item->menu != NULL && item->visible == TRUE)
{
HMENU hmenu = _osmenu_hmenu(item->menu);
if (hmenu != NULL)
{
MENUITEMINFO info;
BOOL ok = FALSE;
info.cbSize = sizeof(MENUITEMINFO);
info.fMask = MIIM_BITMAP;
info.hbmpItem = item->hbitmap;
ok = SetMenuItemInfo(hmenu, item->id, FALSE, &info);
cassert_unref(ok == TRUE, ok);
}
}
}
static BYTE i_fvirt(const uint32_t modifiers)
{
BYTE virt = FVIRTKEY;
if (modifiers & ekMKEY_CONTROL)
virt |= FCONTROL;
if (modifiers & ekMKEY_ALT)
virt |= FALT;
if (modifiers & ekMKEY_SHIFT)
virt |= FSHIFT;
return virt;
}
void osmenuitem_key(OSMenuItem *item, const vkey_t key, const uint32_t modifiers)
{
cassert_no_null(item);
cassert(item->state != UINT8_MAX);
if (item->key == ENUM_MAX(vkey_t))
{
if (key != ENUM_MAX(vkey_t))
_osgui_add_accelerator(i_fvirt(modifiers), kVIRTUAL_KEY[key], item->id, NULL);
}
else
{
if (key != ENUM_MAX(vkey_t))
_osgui_change_accelerator(i_fvirt(modifiers), kVIRTUAL_KEY[key], item->id);
else
_osgui_remove_accelerator(item->id);
}
item->key = key;
item->modifiers = modifiers;
if (item->menu != NULL && item->visible == TRUE)
{
HMENU hmenu = _osmenu_hmenu(item->menu);
if (hmenu != NULL)
{
WCHAR item_text[512];
MENUITEMINFO info;
BOOL ok = FALSE;
i_item_text(tc(item->text), item->key, item->modifiers, item_text, 512);
info.cbSize = sizeof(MENUITEMINFO);
info.fMask = MIIM_STRING;
info.dwTypeData = item_text;
ok = SetMenuItemInfo(hmenu, item->id, FALSE, &info);
cassert_unref(ok == TRUE, ok);
}
}
}
void osmenuitem_state(OSMenuItem *item, const gui_state_t state)
{
cassert_no_null(item);
cassert(item->state != UINT8_MAX);
if (item->state != (uint8_t)state)
{
item->state = state;
if (item->menu != NULL && item->visible == TRUE)
{
HMENU hmenu = _osmenu_hmenu(item->menu);
if (hmenu != NULL)
{
MENUITEMINFO info;
BOOL ok = FALSE;
info.fMask = MIIM_FTYPE | MIIM_STATE;
info.cbSize = sizeof(MENUITEMINFO);
info.fType = i_item_type((gui_state_t)item->state);
info.fState = i_item_state((gui_state_t)item->state, item->enabled);
ok = SetMenuItemInfo(hmenu, item->id, FALSE, &info);
cassert_unref(ok == TRUE, ok);
_osmenu_hmenu_redraw(item->menu);
}
}
}
}
void osmenuitem_submenu(OSMenuItem *item, OSMenu *menu)
{
cassert_no_null(item);
cassert(item->sub_menu == NULL);
item->sub_menu = menu;
_osmenu_attach_to_item(menu, item);
}
void osmenuitem_unset_submenu(OSMenuItem *item, OSMenu *menu)
{
cassert_no_null(item);
cassert(item->sub_menu == menu);
item->sub_menu = NULL;
_osmenu_detach_from_item(menu, item);
}
void _osmenuitem_append_to_hmenu(OSMenuItem *item, OSMenu *menu)
{
cassert_no_null(item);
cassert(item->menu == NULL);
item->menu = menu;
if (item->visible == TRUE)
{
HMENU hmenu = NULL;
MENUITEMINFO info;
BOOL ok = FALSE;
cassert_no_null(item);
hmenu = _osmenu_hmenu(item->menu);
cassert_no_null(hmenu);
info.cbSize = sizeof(MENUITEMINFO);
if (item->state != UINT8_MAX)
{
WCHAR item_text[512];
i_item_text(tc(item->text), item->key, item->modifiers, item_text, 512);
info.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_STATE | MIIM_DATA | MIIM_ID;
info.fType = i_item_type((gui_state_t)item->state);
info.fState = i_item_state((gui_state_t)item->state, item->enabled);
info.dwTypeData = item_text;
info.dwItemData = (ULONG_PTR)item;
info.wID = item->id;
if (item->hbitmap != NULL)
{
info.fMask |= MIIM_BITMAP;
info.hbmpItem = item->hbitmap;
}
if (item->sub_menu != NULL)
{
info.fMask |= MIIM_SUBMENU;
info.hSubMenu = _osmenu_hmenu(item->sub_menu);
cassert(info.hSubMenu != NULL);
}
}
else
{
info.fMask = MIIM_FTYPE | MIIM_ID;
info.fType = MFT_SEPARATOR;
info.wID = item->id;
}
ok = InsertMenuItem(hmenu, item->id, FALSE, &info);
cassert_unref(ok != 0, ok);
}
}
void _osmenuitem_unset_parent(OSMenuItem *item, OSMenu *menu)
{
cassert_no_null(item);
if (item->menu != NULL)
{
cassert_unref(item->menu == menu, menu);
item->menu = NULL;
}
}
static gui_state_t i_state(UINT type, UINT state)
{
if (type == (MFT_STRING | MFT_RADIOCHECK))
return ekGUI_MIXED;
cassert(type == MFT_STRING);
if ((state & MFS_CHECKED) == MFS_CHECKED)
return ekGUI_ON;
cassert((state & MFS_UNCHECKED) == MFS_UNCHECKED);
return ekGUI_OFF;
}
void _osmenuitem_click(OSMenuItem *item, UINT id, UINT type, UINT state)
{
cassert_no_null(item);
cassert_unref(item->id == id, id);
if (item->OnClick != NULL)
{
EvMenu params;
params.index = UINT32_MAX;
params.state = i_state(type, state);
params.text = NULL;
listener_event(item->OnClick, ekGUI_EVENT_MENU, item, ¶ms, NULL, OSMenuItem, EvMenu, void);
}
}