use winapi::shared::windef::{HMENU, HWND};
use winapi::shared::minwindef::UINT;
use super::base_helper::{CUSTOM_ID_BEGIN, to_utf16};
use crate::controls::ControlHandle;
use crate::{NwgError};
use std::{mem, ptr};
use std::sync::atomic::{AtomicU32, Ordering};
static MENU_ITEMS_ID: AtomicU32 = AtomicU32::new(CUSTOM_ID_BEGIN);
pub unsafe 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::{CreateMenu, CreatePopupMenu, GetMenu, SetMenu, DrawMenuBar, AppendMenuW};
use winapi::um::winuser::{MF_STRING, MF_POPUP};
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 = 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 = GetMenu(hwnd);
if menubar.is_null() {
menubar = CreateMenu();
use_menu_command(menubar);
SetMenu(hwnd, menubar);
}
if item {
menu = menubar;
item_id = MENU_ITEMS_ID.fetch_add(1, Ordering::SeqCst);
AppendMenuW(menubar, flags, item_id as usize, text.as_ptr());
} else {
parent_menu = menubar;
menu = CreateMenu();
if menu.is_null() {
return Err(NwgError::menu_create("Menu without parent"));
}
use_menu_command(menu);
AppendMenuW(menubar, flags, mem::transmute(menu), text.as_ptr());
}
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);
AppendMenuW(parent, flags, item_id as usize, text.as_ptr());
} else {
parent_menu = parent;
menu = CreateMenu();
if menu.is_null() {
return Err(NwgError::menu_create("Menu without parent"));
}
use_menu_command(menu);
AppendMenuW(parent, flags, mem::transmute(menu), text.as_ptr());
}
}
if item {
Ok(ControlHandle::MenuItem(menu, item_id))
} else {
Ok(ControlHandle::Menu(parent_menu, menu))
}
}
pub unsafe fn enable_menuitem(h: HMENU, pos: Option<UINT>, id: Option<UINT>, enabled: bool) {
use winapi::um::winuser::{MENUITEMINFOW, MIIM_STATE, MFS_DISABLED, MFS_ENABLED};
use winapi::um::winuser::{SetMenuItemInfoW, GetMenuItemCount};
use winapi::shared::minwindef::BOOL;
let use_position = id.is_none();
let choice = if use_position { pos } else { id };
let value = match choice {
Some(p) => p,
None => (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()
};
SetMenuItemInfoW(h, value, use_position as BOOL, &mut info);
}
pub unsafe fn is_menuitem_enabled(h: HMENU, pos: Option<UINT>, id: Option<UINT>) -> bool {
use winapi::um::winuser::{MENUITEMINFOW, MIIM_STATE, MFS_DISABLED};
use winapi::um::winuser::GetMenuItemInfoW;
use winapi::shared::minwindef::BOOL;
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()
};
GetMenuItemInfoW(h, value, use_position as BOOL, &mut info);
(info.fState & MFS_DISABLED) != MFS_DISABLED
}
pub unsafe 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 unsafe 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 unsafe 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
};
CheckMenuItem(parent_menu, id, MF_BYCOMMAND | check);
}
pub unsafe fn menu_item_checked(parent_menu: HMENU, id: u32) -> bool {
use winapi::um::winuser::{GetMenuState, MF_BYCOMMAND, MF_CHECKED};
GetMenuState(parent_menu, id, MF_BYCOMMAND) & MF_CHECKED == MF_CHECKED
}
unsafe fn build_hmenu_separator(menu: HMENU) -> ControlHandle {
use winapi::um::winuser::{GetMenuItemCount, SetMenuItemInfoW, AppendMenuW};
use winapi::um::winuser::{MENUITEMINFOW, MF_SEPARATOR, MIIM_ID};
use winapi::shared::minwindef::{BOOL};
let item_id = MENU_ITEMS_ID.fetch_add(1, Ordering::SeqCst);
AppendMenuW(menu, MF_SEPARATOR, 0, ptr::null());
let pos = 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()
};
SetMenuItemInfoW(menu, pos as UINT, true as BOOL, &mut info);
ControlHandle::MenuItem(menu, item_id)
}
unsafe fn use_menu_command(h: HMENU) {
use winapi::um::winuser::{MENUINFO, MNS_NOTIFYBYPOS, MIM_STYLE, SetMenuInfo};
use winapi::shared::minwindef::DWORD;
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
};
SetMenuInfo(h, &mut info);
}
pub unsafe fn menu_index_in_parent(parent: HMENU, menu: HMENU) -> UINT {
use winapi::um::winuser::{GetMenuItemCount, GetSubMenu};
let children_count = GetMenuItemCount(parent);
let mut sub_menu: HMENU;
for i in 0..children_count {
sub_menu = 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!")
}