You can add / remove items and submenus.
Items can be accesed by id or position.
Regular pb functions work to check, disable, bind, etc.
Tested on Win7, Win10, Linux Mint17. 64
Not tested on 32 bit but should work.
Maybe some mac guru can fill the gaps for mac os.
Code: Select all
;Dynamic menus.
;PB 5.62.
;Windows / Linux.
;- DeclareModule
DeclareModule guiMenu
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
#MIM_MENUDATA = $00000008
;- MENUINFO
Structure MENUINFO
cbSize.l
fMask.l
dwStyle.l
cyMax.i
hbrBack.i
dwContextHelpID.l
dwMenuData.i
EndStructure
CompilerElseIf #PB_Compiler_OS = #PB_OS_Linux
CompilerElseIf #PB_Compiler_OS = #PB_OS_MacOS
CompilerEndIf
;- ENUM ItemBy
Enumeration
#ItemById
#ItemByPos
EndEnumeration
;- DECLARES
Declare CreateMenuBar(win.i)
Declare CreateSubmenu()
Declare AppendMenuItem(menu.i, text.s, id.i, submenu.i = 0)
Declare RemoveMenuItem(menu.i, item.i, flags.i = #ItemById)
Declare InsertMenuItem(men.i, item.i, text.s, id.i, submenu.i = 0, flags.i = #ItemById)
Declare InsertSeparator(men.i, pos.i, id.i = -1, flags.i = #ItemById)
Declare AppendSeparator(men.i, id.i = -1)
Declare SetSubmenu(men.i, item.i, submenu.i, flags.i = #ItemById)
Declare GetSubMenu(men.i, item.i, flags.i = #ItemById)
Declare GetMenuItemCount(men.i)
EndDeclareModule
;- Module
Module guiMenu
EnableExplicit
CompilerIf #PB_Compiler_OS = #PB_OS_Linux
;Tags used to set and retrieve object data.
#TagMenuItemId = "miid"
#TagPbMenuHandle = "pbmenu"
ProcedureC linOnMenuItemActivate(menitem.i, dat.i)
PostEvent(#PB_Event_Menu, GetActiveWindow(), g_object_get_data_(menitem, #TagMenuItemId))
EndProcedure
;Gets the menuitem object from a menuitem id.
Procedure linGetMenuItemById(hmenu.i, id.i)
Define.GList *childs
Define.i menItem
*childs = gtk_container_get_children_(hmenu)
If *childs
*childs = g_list_first_(*childs)
While *childs
If g_object_get_data_(*childs\data, #TagMenuItemId) = id
menItem = *childs\data
Break
EndIf
*childs = *childs\next
Wend
g_list_free_(*childs)
EndIf
ProcedureReturn menItem
EndProcedure
;Gets the menuitem object from a menuitem position.
Procedure linGetMenuItemByPos(hmenu.i, pos.i)
Define.GList *childs
Define.i menItem
*childs = gtk_container_get_children_(hmenu)
If *childs
menItem = g_list_nth_data_(*childs, pos)
g_list_free_(*childs)
EndIf
ProcedureReturn menItem
EndProcedure
;Gets the menuitem object from item pos or id depending on flags.
Procedure linGetMenuItem(hmenu.i, item.i, flags.i)
If flags = #ItemById
ProcedureReturn linGetMenuItemById(hmenu, item)
ElseIf flags = #ItemByPos
ProcedureReturn linGetMenuItemByPos(hmenu, item)
EndIf
EndProcedure
;Gets the menuitem position from id.
Procedure linGetMenuItemPosById(hmenu.i, id.i)
Define.GList *childs
Define.i pos
Define.b found
found = #False
pos = 0
*childs = gtk_container_get_children_(hmenu)
If *childs
*childs = g_list_first_(*childs)
While *childs
If g_object_get_data_(*childs\data, #TagMenuItemId) = id
found = #True
Break
EndIf
*childs = *childs\next
pos = pos + 1
Wend
g_list_free_(*childs)
EndIf
If found
ProcedureReturn pos
Else
ProcedureReturn -1
EndIf
EndProcedure
Procedure linInsertMenuItem(hmen.i, menItem.i, item.i, id.i, pbsubmenu.i, flags.i)
g_object_set_data_(menItem, #TagMenuItemId, id)
If item = -1 And flags = #ItemByPos ;Append
gtk_menu_shell_append_(hmen, menItem)
Else ;Insert
If flags = #ItemById
item = linGetMenuItemPosById(hmen, item)
EndIf
gtk_menu_shell_insert_(hmen, menItem, item)
EndIf
g_signal_connect_(menItem, "activate", @linOnMenuItemActivate(), 0)
gtk_widget_show_(menItem)
If IsMenu(pbsubmenu)
gtk_menu_item_set_submenu_(menItem, MenuID(pbsubmenu))
EndIf
EndProcedure
CompilerEndIf
Procedure CreateMenuBar(win.i)
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Windows
Define.i menu
Define.MENUINFO mi
menu = CreateMenu(#PB_Any, WindowID(win))
If menu
SetMenu_(WindowID(win), MenuID(menu))
;Save pb window handle in menu data, used to redraw de menubar.
mi\cbSize = SizeOf(MENUINFO)
mi\fMask = #MIM_MENUDATA
mi\dwMenuData = win
SetMenuInfo_(MenuID(menu), mi)
ProcedureReturn menu
EndIf
CompilerCase #PB_OS_Linux
ProcedureReturn CreateMenu(#PB_Any, WindowID(win))
CompilerCase #PB_OS_MacOS
CompilerEndSelect
EndProcedure
Procedure CreateSubmenu()
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Windows
Define.i pbMenu
Define.MENUINFO mi
pbMenu = CreatePopupMenu(#PB_Any)
If pbMenu
;Save pbMenu handle so we can retrieve it later.
mi\cbSize = SizeOf(MENUINFO)
mi\fMask = #MIM_MENUDATA
mi\dwMenuData = pbMenu
SetMenuInfo_(MenuID(pbMenu), @mi)
ProcedureReturn pbMenu
EndIf
CompilerCase #PB_OS_Linux
Define.i menu
menu = CreatePopupMenu(#PB_Any)
If menu
;Save pbMenu handle so we can retrieve it later.
g_object_set_data_(MenuID(menu), #TagPbMenuHandle, menu)
ProcedureReturn menu
EndIf
CompilerCase #PB_OS_MacOS
CompilerEndSelect
EndProcedure
Procedure AppendMenuItem(menu.i, text.s, id.i, submenu.i = 0)
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Windows
Define.MENUINFO mi
If InsertMenuItem(menu, GetMenuItemCount_(MenuID(menu)), text, id, submenu, #ItemByPos)
;Get menu window and redraw menubar, only happens when menubar items are added.
mi\cbSize = SizeOf(MENUINFO)
mi\fMask = #MIM_MENUDATA
If GetMenuInfo_(MenuID(menu), @mi)
If IsWindow(mi\dwMenuData) ;Is a menubar.
DrawMenuBar_(WindowID(mi\dwMenuData))
EndIf
EndIf
EndIf
CompilerCase #PB_OS_Linux
linInsertMenuItem(MenuID(menu), gtk_menu_item_new_with_mnemonic_(text), -1, id, submenu, #ItemByPos)
CompilerCase #PB_OS_MacOS
CompilerEndSelect
EndProcedure
Procedure RemoveMenuItem(menu.i, item.i, flags.i = #ItemById)
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Windows
Define.i winflags
If flags = #ItemById
winflags = #MF_BYCOMMAND
ElseIf flags = #ItemByPos
winflags = #MF_BYPOSITION
Else
winflags = #MF_BYCOMMAND
EndIf
RemoveMenu_(MenuID(menu), item, winflags)
CompilerCase #PB_OS_Linux
Define.i menItem
menItem = linGetMenuItem(MenuID(menu), item, flags)
If menItem
gtk_widget_destroy_(menItem)
EndIf
CompilerCase #PB_OS_MacOS
CompilerEndSelect
EndProcedure
Procedure InsertSeparator(men.i, pos.i, id.i = -1, flags.i = #ItemById)
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Windows
Define.i winflags
If flags = #ItemById
winflags = winflags | #MF_BYCOMMAND
Else
winflags = winflags | #MF_BYPOSITION
EndIf
ProcedureReturn InsertMenu_(MenuID(men), pos, winflags | #MF_SEPARATOR, id, 0)
CompilerCase #PB_OS_Linux
linInsertMenuItem(MenuID(men), gtk_separator_menu_item_new_(), pos, id, 0, flags)
CompilerCase #PB_OS_MacOS
CompilerEndSelect
EndProcedure
Procedure AppendSeparator(men.i, id.i = -1)
ProcedureReturn InsertSeparator(men, -1, id, #ItemByPos)
EndProcedure
Procedure InsertMenuItem(men.i, item.i, text.s, id.i, submenu.i = 0, flags.i = #ItemById)
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Windows
Define.MENUITEMINFO mii
Define.b byPos
If flags = #ItemByPos
byPos = #True
Else
byPos = #False
EndIf
mii\cbSize = SizeOf(MENUITEMINFO)
mii\fMask = #MIIM_ID | #MIIM_STRING | #MIIM_SUBMENU
mii\wID = id
mii\dwTypeData = @text
If IsMenu(submenu)
mii\hSubMenu = MenuID(submenu)
EndIf
ProcedureReturn InsertMenuItem_(MenuID(men), item, byPos, @mii)
CompilerCase #PB_OS_Linux
linInsertMenuItem(men, gtk_menu_item_new_with_mnemonic_(text), item, id, submenu, flags)
CompilerCase #PB_OS_MacOS
CompilerEndSelect
EndProcedure
Procedure SetSubmenu(men.i, item.i, submenu.i, flags.i = #ItemById)
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Windows
Define.MENUITEMINFO mii
Define.b byPos
If flags = #ItemByPos
byPos = #True
Else
byPos = #False
EndIf
mii\cbSize = SizeOf(MENUITEMINFO)
mii\fMask = #MIIM_SUBMENU
If IsMenu(submenu)
mii\hSubMenu = MenuID(submenu)
EndIf
ProcedureReturn SetMenuItemInfo_(MenuID(men), item, byPos, @mii)
CompilerCase #PB_OS_Linux
Define.i menItem
menItem = linGetMenuItem(MenuID(men), item, flags)
If menItem
If submenu = 0
gtk_menu_item_set_submenu_(menItem, 0) ;Removes submenu
ElseIf IsMenu(submenu)
gtk_menu_item_set_submenu_(menItem, MenuID(submenu))
EndIf
EndIf
CompilerCase #PB_OS_MacOS
CompilerEndSelect
EndProcedure
Procedure GetSubMenu(men.i, item.i, flags.i = #ItemById)
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Windows
Define.MENUITEMINFO mii
Define.MENUINFO mi
Define.b byPos
If flags = #ItemByPos
byPos = #True
Else
byPos = #False
EndIf
mii\cbSize = SizeOf(MENUITEMINFO)
mii\fMask = #MIIM_SUBMENU
;Get submenu
If GetMenuItemInfo_(MenuID(men), item, byPos, @mii)
;Get submenu pbHandle
mi\cbSize = SizeOf(MENUINFO)
mi\fMask = #MIM_MENUDATA
If GetMenuInfo_(mii\hSubMenu, @mi)
ProcedureReturn mi\dwMenuData
EndIf
EndIf
CompilerCase #PB_OS_Linux
Define.i menItem, submenu
menItem = linGetMenuItem(MenuID(men), item, flags)
If menItem
submenu = gtk_menu_item_get_submenu_(menItem)
If submenu
ProcedureReturn g_object_get_data_(submenu, #TagPbMenuHandle)
EndIf
EndIf
CompilerCase #PB_OS_MacOS
CompilerEndSelect
EndProcedure
Procedure GetMenuItemCount(men.i)
CompilerSelect #PB_Compiler_OS
CompilerCase #PB_OS_Windows
ProcedureReturn GetMenuItemCount_(MenuID(men))
CompilerCase #PB_OS_Linux
Define.GList *childs
*childs = gtk_container_get_children_(MenuID(men))
If *childs
ProcedureReturn g_list_length_(*childs)
EndIf
CompilerCase #PB_OS_MacOS
CompilerEndSelect
EndProcedure
EndModule
;- TEST
CompilerIf #PB_Compiler_IsMainFile
EnableExplicit
Procedure menCallback()
Debug "mc"
EndProcedure
Enumeration 1
#ID_COPY
#ID_PASTE
#ID_OPEN
#ID_CLOSE
#ID_ADD_FILE_ITEM
#ID_ADD_TITLE_ITEM
#ID_REMOVE_COPY_ITEM
#ID_PASTE1
#ID_PASTE2
#ID_REMOVE_PASTE_MENU
#ID_CHECK_OPEN
EndEnumeration
Define.i ev, win, fileMenu, editMenu, topMenu, testMenu, pasteMenu
win = OpenWindow(#PB_Any, 100, 100, 400, 300, "Dynamic Menu", #PB_Window_SystemMenu)
topMenu = guiMenu::CreateMenuBar(win)
;Paste submenu
pasteMenu = guiMenu::CreateSubmenu()
guiMenu::AppendMenuItem(pasteMenu, "Paste1", #ID_PASTE1)
guiMenu::AppendMenuItem(pasteMenu, "Paste2", #ID_PASTE2)
;Edit submenu
editMenu = guiMenu::CreateSubmenu()
guiMenu::AppendMenuItem(editMenu, "Copy", #ID_COPY)
guiMenu::AppendMenuItem(editMenu, "Paste", #ID_PASTE, pasteMenu)
;File submenu
fileMenu = guiMenu::CreateSubmenu()
guiMenu::AppendMenuItem(fileMenu, "Open", #ID_OPEN)
guiMenu::AppendMenuItem(fileMenu, "Close", #ID_CLOSE)
;Test submenu
testMenu = guiMenu::CreateSubmenu()
guiMenu::AppendMenuItem(testMenu, "Add file item", #ID_ADD_FILE_ITEM)
guiMenu::AppendMenuItem(testMenu, "Add title item", #ID_ADD_TITLE_ITEM)
guiMenu::AppendMenuItem(testMenu, "Remove copy item", #ID_REMOVE_COPY_ITEM)
guiMenu::AppendMenuItem(testMenu, "Remove paste menu", #ID_REMOVE_PASTE_MENU)
guiMenu::AppendMenuItem(testMenu, "Check open", #ID_CHECK_OPEN)
;Top menu
guiMenu::AppendMenuItem(topMenu, "File", -1, fileMenu)
guiMenu::AppendMenuItem(topMenu, "Edit", -1, editMenu)
guiMenu::AppendMenuItem(topMenu, "Test", -1, testMenu)
BindMenuEvent(topMenu, #ID_OPEN, @mencallback())
Define.w cmd, id
Repeat
ev = WaitWindowEvent()
Select ev
Case #PB_Event_Menu
cmd = EventMenu()
Select cmd
Case #ID_ADD_FILE_ITEM
id = guiMenu::GetMenuItemCount(fileMenu) + 1
guiMenu::AppendMenuItem(fileMenu, "Item " + Str(id), id)
Case #ID_ADD_TITLE_ITEM
id = guiMenu::GetMenuItemCount(topMenu) + 1
guiMenu::AppendMenuItem(topMenu, "Item " + Str(id), -1)
Case #ID_REMOVE_COPY_ITEM
guiMenu::RemoveMenuItem(editMenu, #ID_COPY)
Case #ID_REMOVE_PASTE_MENU
guiMenu::SetSubmenu(editMenu, #ID_PASTE, 0)
Case #ID_CHECK_OPEN
SetMenuItemState(fileMenu, #ID_OPEN, #True)
EndSelect
EndSelect
Until ev = #PB_Event_CloseWindow
CompilerEndIf