use super::base_helper::{CUSTOM_ID_BEGIN, to_utf16};
use crate::NwgError;
use crate::controls::ControlHandle;
use std::sync::atomic::{AtomicU32, Ordering};
use std::{mem, ptr};
use winapi::shared::basetsd::UINT_PTR;
use winapi::shared::minwindef::UINT;
use winapi::shared::windef::{HMENU, HWND};
static MENU_ITEMS_ID: AtomicU32 = AtomicU32::new(CUSTOM_ID_BEGIN);
pub fn build_hmenu_control(
text: Option<String>,
item: bool,
separator: bool,
popup: bool,
hmenu: Option<HMENU>,
hwnd: Option<HWND>,
) -> Result<ControlHandle, NwgError> {
use winapi::um::winuser::{
AppendMenuW, CreateMenu, CreatePopupMenu, DrawMenuBar, GetMenu, SetMenu,
};
use winapi::um::winuser::{MF_POPUP, MF_STRING};
if separator {
if hmenu.is_none() {
return Err(NwgError::menu_create("Separator without parent"));
}
return Ok(build_hmenu_separator(hmenu.unwrap()));
}
if popup {
if hwnd.is_none() {
return Err(NwgError::menu_create("Popup menu without parent"));
}
let menu = unsafe { CreatePopupMenu() };
if menu.is_null() {
return Err(NwgError::menu_create("Popup menu creation failed"));
}
use_menu_command(menu);
return Ok(ControlHandle::PopMenu(hwnd.unwrap(), menu));
}
let mut parent_menu: HMENU = ptr::null_mut();
let mut menu: HMENU = ptr::null_mut();
let mut item_id = 0;
let mut flags = MF_STRING;
if !item {
flags |= MF_POPUP;
}
let text = to_utf16(text.unwrap_or("".to_string()).as_ref());
if hwnd.is_some() {
let hwnd = hwnd.unwrap();
let mut menubar = unsafe { GetMenu(hwnd) };
if menubar.is_null() {
menubar = unsafe { CreateMenu() };
use_menu_command(menubar);
unsafe { SetMenu(hwnd, menubar) };
}
if item {
menu = menubar;
item_id = MENU_ITEMS_ID.fetch_add(1, Ordering::SeqCst);
unsafe { AppendMenuW(menubar, flags, item_id as usize, text.as_ptr()) };
} else {
parent_menu = menubar;
menu = unsafe { CreateMenu() };
if menu.is_null() {
return Err(NwgError::menu_create("Menu without parent"));
}
use_menu_command(menu);
unsafe { AppendMenuW(menubar, flags, menu as UINT_PTR, text.as_ptr()) };
}
unsafe { DrawMenuBar(hwnd) };
} else if hmenu.is_some() {
let parent = hmenu.unwrap();
if item {
menu = parent;
item_id = MENU_ITEMS_ID.fetch_add(1, Ordering::SeqCst);
unsafe { AppendMenuW(parent, flags, item_id as usize, text.as_ptr()) };
} else {
parent_menu = parent;
menu = unsafe { CreateMenu() };
if menu.is_null() {
return Err(NwgError::menu_create("Menu without parent"));
}
use_menu_command(menu);
unsafe { AppendMenuW(parent, flags, menu as UINT_PTR, text.as_ptr()) };
}
}
if item {
Ok(ControlHandle::MenuItem(menu, item_id))
} else {
Ok(ControlHandle::Menu(parent_menu, menu))
}
}
pub fn enable_menuitem(h: HMENU, pos: Option<UINT>, id: Option<UINT>, enabled: bool) {
use winapi::shared::minwindef::BOOL;
use winapi::um::winuser::{GetMenuItemCount, SetMenuItemInfoW};
use winapi::um::winuser::{MENUITEMINFOW, MFS_DISABLED, MFS_ENABLED, MIIM_STATE};
let use_position = id.is_none();
let choice = if use_position { pos } else { id };
let value = match choice {
Some(p) => p,
None => unsafe { (GetMenuItemCount(h) - 1) as u32 },
};
let state = match enabled {
true => MFS_ENABLED,
false => MFS_DISABLED,
};
let mut info = MENUITEMINFOW {
cbSize: mem::size_of::<MENUITEMINFOW>() as UINT,
fMask: MIIM_STATE,
fType: 0,
fState: state,
wID: 0,
hSubMenu: ptr::null_mut(),
hbmpChecked: ptr::null_mut(),
hbmpUnchecked: ptr::null_mut(),
dwItemData: 0,
dwTypeData: ptr::null_mut(),
cch: 0,
hbmpItem: ptr::null_mut(),
};
unsafe { SetMenuItemInfoW(h, value, use_position as BOOL, &mut info) };
}
pub fn is_menuitem_enabled(h: HMENU, pos: Option<UINT>, id: Option<UINT>) -> bool {
use winapi::shared::minwindef::BOOL;
use winapi::um::winuser::GetMenuItemInfoW;
use winapi::um::winuser::{MENUITEMINFOW, MFS_DISABLED, MIIM_STATE};
if id.is_none() && pos.is_none() {
panic!("Both pos and id are None");
}
let use_position = id.is_none();
let choice = if use_position { pos } else { id };
let value = match choice {
Some(p) => p,
None => unreachable!(),
};
let mut info = MENUITEMINFOW {
cbSize: mem::size_of::<MENUITEMINFOW>() as UINT,
fMask: MIIM_STATE,
fType: 0,
fState: 0,
wID: 0,
hSubMenu: ptr::null_mut(),
hbmpChecked: ptr::null_mut(),
hbmpUnchecked: ptr::null_mut(),
dwItemData: 0,
dwTypeData: ptr::null_mut(),
cch: 0,
hbmpItem: ptr::null_mut(),
};
unsafe { GetMenuItemInfoW(h, value, use_position as BOOL, &mut info) };
(info.fState & MFS_DISABLED) != MFS_DISABLED
}
pub fn enable_menu(parent_menu: HMENU, menu: HMENU, e: bool) {
let menu_index = menu_index_in_parent(parent_menu, menu);
enable_menuitem(parent_menu, Some(menu_index), None, e);
}
pub fn is_menu_enabled(parent_menu: HMENU, menu: HMENU) -> bool {
let menu_index = menu_index_in_parent(parent_menu, menu);
is_menuitem_enabled(parent_menu, Some(menu_index), None)
}
pub fn check_menu_item(parent_menu: HMENU, id: u32, check: bool) {
use winapi::um::winuser::{CheckMenuItem, MF_BYCOMMAND, MF_CHECKED, MF_UNCHECKED};
let check = match check {
true => MF_CHECKED,
false => MF_UNCHECKED,
};
unsafe { CheckMenuItem(parent_menu, id, MF_BYCOMMAND | check) };
}
pub fn menu_item_checked(parent_menu: HMENU, id: u32) -> bool {
use winapi::um::winuser::{GetMenuState, MF_BYCOMMAND, MF_CHECKED};
unsafe { GetMenuState(parent_menu, id, MF_BYCOMMAND) & MF_CHECKED == MF_CHECKED }
}
fn build_hmenu_separator(menu: HMENU) -> ControlHandle {
use winapi::shared::minwindef::BOOL;
use winapi::um::winuser::{AppendMenuW, GetMenuItemCount, SetMenuItemInfoW};
use winapi::um::winuser::{MENUITEMINFOW, MF_SEPARATOR, MIIM_ID};
let item_id = MENU_ITEMS_ID.fetch_add(1, Ordering::SeqCst);
unsafe { AppendMenuW(menu, MF_SEPARATOR, 0, ptr::null()) };
let pos = unsafe { GetMenuItemCount(menu) - 1 };
let mut info = MENUITEMINFOW {
cbSize: mem::size_of::<MENUITEMINFOW>() as UINT,
fMask: MIIM_ID,
fType: 0,
fState: 0,
wID: item_id,
hSubMenu: ptr::null_mut(),
hbmpChecked: ptr::null_mut(),
hbmpUnchecked: ptr::null_mut(),
dwItemData: 0,
dwTypeData: ptr::null_mut(),
cch: 0,
hbmpItem: ptr::null_mut(),
};
unsafe { SetMenuItemInfoW(menu, pos as UINT, true as BOOL, &mut info) };
ControlHandle::MenuItem(menu, item_id)
}
fn use_menu_command(h: HMENU) {
use winapi::shared::minwindef::DWORD;
use winapi::um::winuser::{MENUINFO, MIM_STYLE, MNS_NOTIFYBYPOS, SetMenuInfo};
let mut info = MENUINFO {
cbSize: mem::size_of::<MENUINFO>() as DWORD,
fMask: MIM_STYLE,
dwStyle: MNS_NOTIFYBYPOS,
cyMax: 0,
hbrBack: ptr::null_mut(),
dwContextHelpID: 0,
dwMenuData: 0,
};
unsafe { SetMenuInfo(h, &mut info) };
}
pub fn menu_index_in_parent(parent: HMENU, menu: HMENU) -> UINT {
use winapi::um::winuser::{GetMenuItemCount, GetSubMenu};
let children_count = unsafe { GetMenuItemCount(parent) };
let mut sub_menu: HMENU;
for i in 0..children_count {
sub_menu = unsafe { GetSubMenu(parent, i as i32) };
if sub_menu.is_null() {
continue;
} else if sub_menu == menu {
return i as UINT;
}
}
panic!("Menu/MenuItem not found in parent!")
}