AutoHotkey Homepage AutoHotkey Community
Let's help each other out
 
 FAQFAQ   SearchSearch   MemberlistMemberlist   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Menu DllCalls

 
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Ask for Help
View previous topic :: View next topic  
Author Message
animeaime



Joined: 04 Nov 2008
Posts: 1046

PostPosted: Sat Jan 10, 2009 11:45 am    Post subject: Menu DllCalls Reply with quote

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.
Back to top
View user's profile Send private message Send e-mail
Display posts from previous:   
Post new topic   Reply to topic    AutoHotkey Community Forum Index -> Ask for Help All times are GMT
Page 1 of 1

 
Jump to:  
You can post new topics in this forum
You can reply to topics in this forum


Powered by phpBB © 2001, 2005 phpBB Group