Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

assign bitmap to any item in any AHk menu


  • Please log in to reply
29 replies to this topic
shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005
Work with context menu:

Menu, menuEmpty, Add

Menu, menuContext$Level2, Add, :menuEmpty
	Menu_AssignBitmap( "menuContext$Level2", 1, "coffee.bmp", true )
Menu, menuContext$Level2, Add, empty, :menuEmpty
Menu, menuContext$Level2, Add, world, :menuEmpty
	Menu_AssignBitmap( "menuContext$Level2", 3, "coffee.bmp", true )

Menu, menuContext, Add, empty 1, :menuEmpty
Menu, menuContext, Add, empty 2, :menuEmpty
	Menu, menuContext, Check, empty 2
	Menu_AssignBitmap( "menuContext", 2, "coffee.bmp", true, "coffee.bmp", false )
Menu, menuContext, Add, :menuContext$Level2

Gui, Show, x50 y50 w400 h200
return

GuiContextMenu:
	Menu, menuContext, Show
return

Work with Tray menu:

Menu, menuEmpty, Add

Menu, Tray, NoStandard
Menu, Tray, Add, Hello, :menuEmpty
	Menu_AssignBitmap( "Tray", 1, "coffee.bmp", true )
Menu, Tray, Add
Menu, Tray, Standard

Work with Window menu bar:

Menu, menuEmpty, Add

Menu, menuFile, Add, Hello, :menuEmpty
	Menu_AssignBitmap( "menuFile", 1, "coffee.bmp", false )

Menu, menuMain, Add, File, :menuFile

Gui, Menu, menuMain
Gui, Show, x50 y50 w400 h200

/*	p_menu				= "MenuName" (e.g., Tray, etc.)
	p_item				= 1, ...
	p_bm_unchecked,
	p_bm_checked		= path to bitmap/false
	p_unchecked_face,
	p_checked_face		= true/false (i.e., true = pixels with same color as first pixel are transparent)
*/
Menu_AssignBitmap( p_menu, p_item, p_bm_unchecked, p_unchecked_face=false, p_bm_checked=false, p_checked_face=false )
{
	static	h_menuDummy
	
	if h_menuDummy=
	{
		Menu, menuDummy, Add
		Menu, menuDummy, DeleteAll
		
		Gui, 99:Menu, menuDummy
		Gui, 99:Show, Hide, guiDummy

		old_DetectHiddenWindows := A_DetectHiddenWindows
		DetectHiddenWindows, on

		Process, Exist
		h_menuDummy := DllCall( "GetMenu", "uint", WinExist( "guiDummy ahk_class AutoHotkeyGUI ahk_pid " ErrorLevel ) )
		if ReportError( ErrorLevel or h_menuDummy = 0, "Menu_AssignBitmap: GetMenu", "h_menuDummy = " h_menuDummy )
			return, false

		DetectHiddenWindows, %old_DetectHiddenWindows%
		
		Gui, 99:Menu
		Gui, 99:Destroy
	}
	
	Menu, menuDummy, Add, :%p_menu%
	
	h_menu := DllCall( "GetSubMenu", "uint", h_menuDummy, "int", 0 )
	if ReportError( ErrorLevel or h_menu = 0, "Menu_AssignBitmap: GetSubMenu", "h_menu = " h_menu )
		return, false

	success := DllCall( "RemoveMenu", "uint", h_menuDummy, "uint", 0, "uint", 0x400 )
	if ReportError( ErrorLevel or ! success,  "Menu_AssignBitmap: RemoveMenu", "success = " success )
		return, false
	Menu, menuDummy, Delete, :%p_menu%
	
	if ( p_bm_unchecked )
	{
		hbm_unchecked := DllCall( "LoadImage"
									, "uint", 0
									, "str", p_bm_unchecked
									, "uint", 0									; IMAGE_BITMAP
									, "int", 0
									, "int", 0
									, "uint", 0x10|( 0x20*p_unchecked_face ) )	; LR_LOADFROMFILE|LR_LOADTRANSPARENT
		if ReportError( ErrorLevel or ! hbm_unchecked, "Menu_AssignBitmap: LoadImage: unchecked", "hbm_unchecked = " hbm_unchecked )
			return, false
	}

	if ( p_bm_checked )
	{
		hbm_checked := DllCall( "LoadImage"
									, "uint", 0
									, "str", p_bm_checked
									, "uint", 0
									, "int", 0
									, "int", 0
									, "uint", 0x10|( 0x20*p_checked_face ) )
		if ReportError( ErrorLevel or ! hbm_checked, "Menu_AssignBitmap: LoadImage: checked", "hbm_checked = " hbm_checked )
			return, false
	}

	success := DllCall( "SetMenuItemBitmaps"
							, "uint", h_menu
							, "uint", p_item-1
							, "uint", 0x400										; MF_BYPOSITION
							, "uint", hbm_unchecked
							, "uint", hbm_checked )
	if ReportError( ErrorLevel or ! success, "Menu_AssignBitmap: SetMenuItemBitmaps", "success = " success )
		return, false
	
	return, true
}

ReportError( p_condition, p_title, p_extra )
{
	if p_condition
		MsgBox,
			( LTrim
				[Error] %p_title%
				EL = %ErrorLevel%, LE = %A_LastError%
				
				%p_extra%
			)
	
	return, p_condition
}


polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
Thats great! We don't have to wait for in-built support now :)

I found one issue, bitmaps with magenta backgrounds are shown to have the magenta colour. It's supposed to be negated and treated as a transparent secion right?

shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005

Thats great!


Thanks. It was a fun challenge to resolve.

one issue, bitmaps with magenta backgrounds are shown to have the magenta colour. It's supposed to be negated and treated as a transparent secion right?


Why?

Do you have the following in mind:

LR_LOADTRANSPARENT [=0x20]
Retrieves the color value of the first pixel in the image and replaces the corresponding entry in the color table with the default window color (COLOR_WINDOW). All pixels in the image that use that entry become the default window color. This value applies only to images that have corresponding color tables.
Do not use this option if you are loading a bitmap with a color depth greater than 8bpp.

If fuLoad includes both the LR_LOADTRANSPARENT and LR_LOADMAP3DCOLORS values, LRLOADTRANSPARENT takes precedence. However, the color table entry is replaced with COLOR_3DFACE rather than COLOR_WINDOW.



polyethene
  • Members
  • 5519 posts
  • Last active: May 17 2015 06:39 AM
  • Joined: 26 Oct 2012
That's the one. I'm not sure how I'd impliment it though.

shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005
Titan:

Change each of the lines:

, "uint", 0x10 ) ; LR_LOADFROMFILE
to

, "uint", 0x10|0x20 ) ; LR_LOADFROMFILE|LR_LOADTRANSPARENT

SKAN
  • Administrators
  • 9115 posts
  • Last active:
  • Joined: 26 Dec 2005
"Splendid Job" Shimanov :D

Menu_AssignBitmap() function is terrific, I tried it with my script`s tray menu and it looks so lively now!

Thanks for posting it.
kWo4Lk1.png

AGU
  • Guests
  • Last active:
  • Joined: --
Hello shimanov,

thx for your script/function. Works great. I commented the script as far as I understood it and made some slight formating changes. I'm really trying to understand what the script is doing. :mrgreen:
Attention: Further text below scriptcode
Menu_AssignBitmap(p_menu, p_path, p_bm_unchecked=false, p_bm_checked=false)
  {
    ; Thanks to shimanov for this function
    ; Details under http://www.autohotkey.com/forum/viewtopic.php?p=44577
    
    ; p_menu          : menu name
    ; p_path          : number of menu entry (1st entry = "1", 2nd entry = "2", ... )
    ; p_bm_unchecked  : path to .bmp for unchecked menu entry
    ; p_bm_checked    : path to .bmp for checked   menu entry
    
    static   menu_count, menu_list, h_menuDummy
    
    If h_menuDummy=
      {
        menu_count = 0
        menu_list = |
      
        ; Save current 'DetectHiddenWindows' mode to reset it later
        Old_DetectHiddenWindows := A_DetectHiddenWindows
        DetectHiddenWindows, on
        
        ; Retrieve scripts PID
        Process, Exist
        pid_this := ErrorLevel
        
        ; Create menuDummy and assign to Gui99
        Menu, menuDummy, Add
        Menu, menuDummy, DeleteAll
        
        Gui, 99:Menu, menuDummy
      
        ; Retrieve menu handle        
        h_menuDummy := DllCall( "GetMenu", "uint", WinExist( "ahk_class AutoHotkeyGUI ahk_pid " pid_this ) )
      
        ; Remove menu bar 'menuDummy'
        Gui, 99:Menu
        
        ; Reset 'DetectHiddenWindows' mode to old setting
        DetectHiddenWindows, %Old_DetectHiddenWindows%
      }
    
    If ( ! InStr( menu_list, "|" p_menu ",", false ) )
      {
        menu_count++
        menu_list = %menu_list%%p_menu%,%menu_count%|
        
        Menu, menuDummy, Add, :%p_menu%
      }
    
    root := InStr( menu_list, ",", false, InStr( menu_list, "|" p_menu ",", false ) ) + 1
    StringMid, root, menu_list, root, InStr( menu_list, "|", false, root ) - root
    p_path = %root%|%p_path%
    
    StringSplit, path, p_path, |
    
    ; Assign above retrieved menu handle to 'h_menu'
    h_menu := h_menuDummy
    
    loop, % path0 - 1
      h_menu := DllCall("GetSubMenu", "uint", h_menu, "int", path%A_Index% - 1)
    
    ; Load bitmap for unchecked menu entries
    If (p_bm_unchecked)
      {
        hbm_unchecked := DllCall( "LoadImage"
                                , "uint", 0
                                , "str", p_bm_unchecked
                                , "uint", 0               ; IMAGE_BITMAP
                                , "int", 0
                                , "int", 0
                                , "uint", 0x10 )          ; LR_LOADFROMFILE
        
        If (ErrorLevel or ! hbm_unchecked)
          {
             MsgBox, [Menu_AssignBitmap] failed: EL = %ErrorLevel%
             Return, false
          }
      }
    
    ; Load bitmap for checked menu entries
    If (p_bm_checked)
      {
        hbm_checked := DllCall( "LoadImage"
                              , "uint", 0
                              , "str", p_bm_checked
                              , "uint", 0                 ; IMAGE_BITMAP
                              , "int", 0
                              , "int", 0
                              , "uint", 0x10 )            ; LR_LOADFROMFILE
      
        If (ErrorLevel or ! hbm_checked)
          {
             MsgBox, [Menu_AssignBitmap] failed: EL = %ErrorLevel%
             Return, false
          }
      }
    
    ; On success assign image to menu entry
    success := DllCall( "SetMenuItemBitmaps"
                      , "uint", h_menu
                      , "uint", path%path0%-1
                      , "uint", 0x400                     ; MF_BYPOSITION
                      , "uint", hbm_unchecked
                      , "uint", hbm_checked )

    If (ErrorLevel or ! success)
      {
        MsgBox, [Menu_AssignBitmap] failed: EL = %ErrorLevel%
        Return, false
      }
    
    Return, true
  }

However I've got some problems in understanding this little follwing passage. Just in case you like would you be so kind to write some explaining lines? Just for me that I can make some explaining comments.
If ( ! InStr( menu_list, "|" p_menu ",", false ) )
      {
        menu_count++
        menu_list = %menu_list%%p_menu%,%menu_count%|
        
        Menu, menuDummy, Add, :%p_menu%
      }
    
    root := InStr( menu_list, ",", false, InStr( menu_list, "|" p_menu ",", false ) ) + 1
    StringMid, root, menu_list, root, InStr( menu_list, "|", false, root ) - root
    p_path = %root%|%p_path%
    
    StringSplit, path, p_path, |
    
    ; Assign above retrieved menu handle to 'h_menu'
    h_menu := h_menuDummy
    
    loop, % path0 - 1
      h_menu := DllCall("GetSubMenu", "uint", h_menu, "int", path%A_Index% - 1)

Cheers
AGU

shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005

I commented the script


Thank you. I really hate adding explicit comments. It's like doing the work twice.

	if ( ! InStr( menu_list, "|" p_menu ",", false ) )
	{
		menu_count++
		menu_list = %menu_list%%p_menu%,%menu_count%|
		
		Menu, menuDummy, Add, :%p_menu%
	}
	
	root := InStr( menu_list, ",", false, InStr( menu_list, "|" p_menu ",", false ) )+1
	StringMid, root, menu_list, root, InStr( menu_list, "|", false, root )-root
	p_path = %root%|%p_path%
	
	StringSplit, path, p_path, |

"Menu, menuDummy, Add, :%p_menu%" is where the magic happens. It attaches menus to a menu with a known handle (i.e., menuDummy) and enables retrieving its associated handle via "GetSubMenu" -- pretty cool.

The rest of the code manages the menu_list variable, which contains the menu names and their item position in menuDummy.

	h_menu := h_menuDummy
	loop, % path0-1
		h_menu := DllCall( "GetSubMenu", "uint", h_menu, "int", path%A_Index%-1 )

Determines menu handle for the requested menu and path on each call. This is necessary, but insufficient, to accommodate a deleted menu/item.

shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005
changes:
* support transparent bitmaps (as suggested by Titan)

AGU
  • Guests
  • Last active:
  • Joined: --
Hello again.

p_path = relative to p_menu (e.g., 3|1|2 = level 1: item 3|level 2: item 1|level 3: item 2)

Do I understand right, that I can assign more than one bitmap at once?

Concerning the support for transparent bitmaps:
How does that work? I add a bitmap and the first pixel (x1 * y1) becomes the transparent color for the whole bitmap? The transparent color pixels get the same color as the menu background when I understand that right.

I used bitmaps with white background and didn't make use of LR_LOADTRANSPARENT. But the background gets transparent, too. Why? Is that because you should use monochrome bitmaps normally? MSDN said s.th about this and that by using monochrome bitmaps the white background gets transparent.

Another thing:
What's the max-size for these bitmaps. I realized that 16x16 bitmaps get cut when you add them to a context menu. 13x13px is the maxsize I could use for context menus. Is that normal? And why don't they just scale to the correct size instead of getting cut?

Cheers
AGU

shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005

Do I understand right, that I can assign more than one bitmap at once?


Two different bitmaps can be assigned to each item, one for unchecked and one for checked.

Concerning the support for transparent bitmaps


Refer to my earlier post containing a description of the LR_LOADTRANSPARENT flag.

What's the max-size for these bitmaps.


SysGet, w, 71
SysGet, h, 72

MsgBox, %w%, %h%


  • Guests
  • Last active:
  • Joined: --

Two different bitmaps can be assigned to each item, one for unchecked and one for checked.

Yeah I know. But I understand the following line in this way, that you assign these two bitmaps to three different items namely:

"level 1: item 3",
"level 2: item 1"
"level 3: item 2"

p_path = relative to p_menu (e.g., 3|1|2 = level 1: item 3|level 2: item 1|level 3: item 2)

I understand it in the way that you assign 3 * 2(per item) = 6 pictures at once. My question aims at if I understood that right.
So, did I? Can you, by using "|", assign bitmaps to multiple entries on different menu layers (submenus)?

Cheers
AGU

shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005

assign these two bitmaps to three different items namely:

"level 1: item 3",
"level 2: item 1"
"level 3: item 2"


Now I understand your question. The path allows specification of a single item located on a submenu n-levels removed from a top level menu.

There are two ways to specify an item:

Menu, < level 2 >, Add, item 1, ...

Menu, < level 1 >, Add, item 2, :< level 2 >

; direct
Menu_AssignBitmap( "< level 2 >", "1", ... )

; indirect
Menu_AssignBitmap( "< level 1 >", "2|1", ... )

The difference between the two method is purely stylistic.

Now that I think about it, the technique used to determine the handle of submenus, obviates the need for a path specification. I'll have to update the code to reflect this capability.

AGU
  • Guests
  • Last active:
  • Joined: --
Ahhh, thx now i got it.

Some short kind of documentation won't be that bad. ;)

Cheers
AGU

P.S. looking forward to your code update.

shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005
changes:
* simplified item specification method (removed indirect method)
Thanks to AGU for keeping me on my toes.