animeaime
Joined: 04 Nov 2008 Posts: 1046
|
Posted: Sat Jan 10, 2009 11:45 am Post subject: Menu DllCalls |
|
|
I need some help on how to rearrange menus items. Since AHK didn't include the ability, I thought I should figure it out. Below is the rough draft of my idea. I plan to add this to my menu wrapper that I'm working on, and wanted to ask the most important question of all - is it worth doing it this way (versus erasing the whole menu and rebuilding).
It isn't too hard to rebuild a menu based on how my wrapper is set up, but I was wondering, presuming I can get it working, is this way "better" (more efficient - computational wise; not my work effort).
Now, below are only observations, I dare not look at the actual AHK code (probably way over my head).
1) The menus are stored in an "array" which AHK keeps internal. This is what is used to see if the menu item exists. This observation is because although the item is clearly in the tray menu, AHK doesn't know that since AHK didn't put it there.
2) If you delete the original entry, the label won't work. I tried to remove the item from my "temp menu" after its creation, but the label no longer works. This is because when you use delete, AHK removes the gosub from the "array" and even though the same item is elsewhere, AHK doesn't know this. This problem doesn't occur with submenus because there is no gosub.
Now, here are my thoughts.
1) Provided I can keep track of the "original" menu item, then there should be minimal memory wasted for keeping a copy (since it should merely be a pointer - due to the same item handle)
2) Similarly, provided I can keep track of the "original" menu item, then there should be no problem deleting the original when the copy is deleted.
Now, the question, is it worth doing. Now, I don't mean my effort, I mean is it more efficient? Instead of deleting the entire menu and recreating it, it would only move items around (and keep a copy on a different menu).
| Code: | #SingleInstance Force
#NoEnv
Menu, Item1, add, Item1->Item1, Item1
Menu, tray, NoStandard
;since 4 > menu size, added to end
InsertMenuItem("tray", 4, "Item3", "Item3")
InsertMenuItem("tray", 1, "Item1", ":Item1")
InsertMenuItem("tray", 2, "Item2", "Item2")
;causes weird behavior
;deletes the item added using InsertMenuItem
;it must use the item handle and checks for it
;Menu, Tray, add, Temp, :Item1
;Menu, Tray, delete, Temp
Menu, Tray, add
Menu, Tray, add, MyMenu, % ":animeaime_Tray"
Menu, Tray, add
Menu, Tray, add, Exit, ExitProgram
;will cause an error
;Menu, tray, delete, Item2
;Menu, tray, delete, Item3
;since it's actually stored here
;(deleting the menu item deletes the gosub on click too)
;Menu, animeaime_Tray, delete, Item2
;Menu, animeaime_Tray, delete, Item3
return
Item1:
{
MsgBox, % "You can still see this even though the menu was added using"
. " insertMenu."
return
}
Item2:
{
MsgBox, % "Weird way to do it, but it works!"
return
}
Item3:
{
MsgBox, % "Somewhere over the rainbow"
return
}
ExitProgram:
ExitApp
InsertMenu(MenuHandle, Position, Flags, ItemHandle, DisplayText)
{
;-1 on position is to change it to zero-index
return DllCall("InsertMenu", "int", MenuHandle, "int", Position - 1
, "int", Flags, "int", ItemHandle, "int", &DisplayText)
}
InsertMenuItem(MenuName, Position, DisplayText, MenuLabel)
{
;0x400 - MF_ByPosition
;0x10 - MF_Popup
;it works... but hopefully there is a better way
MenuHandle := getMenuHandle(MenuName)
if (subStr(MenuLabel, 1, 1) = ":")
{
;it's a submenu
SubMenuName := subStr(MenuLabel, 2)
InsertMenu(MenuHandle, Position, 0x410
, getMenuHandle(SubMenuName), DisplayText)
}
else
{
;it's an item
ThisMenu = AnimeAime_%MenuName%
Menu, %ThisMenu%, add, %DisplayText%, %MenuLabel%
hAA := getMenuHandle(ThisMenu)
ItemHandle := getMenuItemHandle(hAA, getMenuSize(hAA))
InsertMenu(MenuHandle, Position, 0x400, ItemHandle, DisplayText)
}
}
getMenuHandle(menu_name)
{
;copied directly from Lexikos' MenuIcons suite
static h_menuDummy
; v2.2: Check for !h_menuDummy instead of h_menuDummy="" in case init failed last time.
If !h_menuDummy
{
Menu, menuDummy, Add
Menu, menuDummy, DeleteAll
Gui, 99:Menu, menuDummy
; v2.2: Use LastFound method instead of window title. [Thanks animeaime.]
Gui, 99:+LastFound
h_menuDummy := DllCall("GetMenu", "uint", WinExist())
Gui, 99:Menu
Gui, 99:Destroy
; v2.2: Return only after cleaning up. [Thanks animeaime.]
if !h_menuDummy
return 0
}
Menu, menuDummy, Add, :%menu_name%
h_menu := DllCall( "GetSubMenu", "uint", h_menuDummy, "int", 0 )
DllCall( "RemoveMenu", "uint", h_menuDummy, "uint", 0, "uint", 0x400 )
Menu, menuDummy, Delete, :%menu_name%
return h_menu
}
getMenuItemHandle(MenuNameOrHandle, Position)
{
If MenuNameOrHandle is integer
{
MenuHandle := MenuNameOrHandle
}
else
{
MenuHandle := getMenuHandle(MenuNameOrHandle)
}
;-1 on position is to change it to zero-index
return DllCall("GetMenuItemID", "int", MenuHandle, "int", Position - 1)
}
getMenuSize(MenuNameOrHandle)
{
If MenuNameOrHandle is integer
MenuHandle := MenuNameOrHandle
else
MenuHandle := getMenuHandle(MenuNameOrHandle)
return DllCall("GetMenuItemCount", "int", MenuHandle)
} |
Now, my second thought is to not use AHK menus at all (except when creating new menus), and add the menu items using the above technique. Currently, my menu wrapper stores the menulabels for an item already, so this won't cause any problems in that avenue. Instead, I need some help on how to detect a click on the menu (so that I can use a gosub to go to the right label).
Now, the big problems with this technique would be
1) I would need to use dynamic labels
2) I would want a way to send an error (like it does now) if the label is not valid. I know isLabel will allow me to see if it is a label, but I'm not sure how to send an error (other than adding a "temp item" to a "temp menu" using the label, and then deleting it).
3) There is "some" overhead when creating an empty menu (to get the menu handle to use)
| Code: | Menu, SomeMenu, add
Menu, SomeMenu, deleteAll
|
Or, I could use createMenu. Do I need to use destory menu if it's added to the tray menu (or a submenu there of)? Create menu's docs say that "Resources associated with a menu that is assigned to a window are freed automatically. If the menu is not assigned to a window, an application must free system resources associated with the menu before closing. An application frees menu resources by calling the DestroyMenu function." Does the tray menu count as a window?
Just looking for some feedback. Any comments/critiques on the code are also very much welcome. _________________ As always, if you have any further questions, don't hesitate to ask.
Add OOP to your scripts via the Class Library. Check out my scripts. |
|