Image in menu bar

Started by erezpaz, June 19, 2018, 06:50:07 AM

Previous topic - Next topic

erezpaz

Hi

I wonder if there is a way to insert a small image in the left side before text in the menu-bar dynamic dialog. Something like the attached.

Thanks

td

It is theoretically possible using DllCall but the process is a bit convoluted, may not actually work, and hardly seem worth the effort.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

erezpaz

Can you give me more details or point me where can I read about it? Thanks!!

td

Here is an embarrassingly poorly written script that demonstrates the basics while managing to do nothing particularly useful.

Code (winbatch) Select
;; Define UDFs.
gosub defineudps

MenuBitmapFormat=`WWWDLGED,6.2`

MenuBitmapCaption=`WIL Dialog 1`
MenuBitmapX=001
MenuBitmapY=001
MenuBitmapWidth=352
MenuBitmapHeight=241
MenuBitmapNumControls=005
MenuBitmapProcedure=`MenuBitmapProc`
MenuBitmapFont=`DEFAULT`
MenuBitmapTextColor=`DEFAULT`
MenuBitmapBackground=`DEFAULT,DEFAULT`
MenuBitmapConfig=0

MenuBitmap001=`095,226,032,011,PUSHBUTTON,"PushButton_OK",DEFAULT,"OK",1,10,@csDefButton,DEFAULT,DEFAULT,DEFAULT`
MenuBitmap002=`223,226,032,011,PUSHBUTTON,"PushButton_Cancel",DEFAULT,"Cancel",0,20,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MenuBitmap003=`000,000,000,000,MENUBAR,"Dialog_Bar"`
MenuBitmap004=`000,000,000,000,MENUITEM,"mbi1_MyDialog","Dialog_Bar","Bitmap",DEFAULT,10,DEFAULT`
MenuBitmap005=`000,000,000,000,MENUITEM,"mbi2_MenuBitmap","mbi1_MyDialog","Has One",DEFAULT,10,DEFAULT`

ButtonPushed=Dialog("MenuBitmap")

exit

:defineudps

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Simple way to create a dummy menu item bitmap for testing
;; purposes.
;;
;; Returns a Windows bitmap handle.
#definefunction CreateMenuBitmap()
    hUser32 = DllLoad('user32.dll')

    hwndDesktop = DllCall(hUser32, long:'GetDesktopWindow')
    hdcDesktop = DllCall(hUser32, long:'GetDC', long:hwndDesktop)

    ; Determine the required bitmap size.
    SM_CXMENUCHECK = 71
    SM_CYMENUCHECK = 72
    nX = WinMetrics(SM_CXMENUCHECK)
    nY = WinMetrics(SM_CYMENUCHECK)

    ; Create a monochrome bitmap and select it.
    hGdi32 = DllLoad('Gdi32.dll')
    hdcMem = DllCall(hGdi32, long:'CreateCompatibleDC', long:hdcDesktop)
    hbm = DllCall(hGdi32, long:'CreateBitmap',long:nX, long:nY, long:1, long:1, lpnull)
    hbmOld =  DllCall(hGdi32, long:'SelectObject',long:hdcMem, long:hbm)
    WHITENESS = 16711778 ; 0x00FF0062
    DllCall(hGdi32, long:'PatBlt', long:hdcMem, long:0, long:0, long:nX, long:nY, long:WHITENESS);

    ; Clean up.
    DllCall(hGdi32, long:'SelectObject', long:hdcMem, long:hbmOld)
    DllCall(hGdi32, long:'DeleteDC',long:hdcMem)
    DllCall(hUser32, long:'ReleaseDC',long:hwndDesktop, long:hdcDesktop)
    DllFree(hUser32)
    DllFree(hGdi32)
    return hbm
#endfunction

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Creates a structure descript string for use with DllStructAlloc.
;;
;; Returns a string description of the Wub 32 MENUITEMINFOW structure.
#DefineFunction GetMiiStructStr()
return $"
  uint::cbSize;
  uint::fMask;
  uint::fType;
  uint::fStat;
  uint::wID;
  HMENU::hSubMenu;
  HBITMAP::hbmpChecked;
  HBITMAP::hbmpUnchecked;
  DWORD_PTR::dwItemData;
  LPWSTR::dwTypeData;
  uint::cch;
  HBITMAP::hbmpItem;$"

#EndFunction

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; User defined dialog callback function
;;
;; Adds and destroys a menu item's bitmap.
#DefineFunction MenuBitmapProc(Bm_Handle,Bm_Event,Bm_Name,Bm_EventInfo,Bm_ChangeInfo)
   switch Bm_Event                                         
      case @deInit                                         
         DialogProcOptions(Bm_Handle, @dePbPush, 1)

         hUser32 = DllLoad('user32.dll')
         hMenuBar = DllCall(hUser32, long:'GetMenu', long:Bm_Handle)
         mii = DllStructAlloc(GetMiiStructStr())
         DllStructPoke(mii, 'cbSize', IntControl(98, mii, 1, 0, 0))
         MIIM_SUBMENU = 4
         DllStructPoke(mii, 'fMask', MIIM_SUBMENU)
         DllCall(hUser32, long:'GetMenuItemInfoW', long:hMenuBar, long:0, long:@True, lpstruct:mii)
         hmenuPopup = DllStructPeek(mii, 'hSubMenu')
         hbm = CreateMenuBitmap()
         MF_BYPOSITION = 1024  ;0x00000400L
         DllCall(hUser32, long:'SetMenuItemBitmaps',long:hmenuPopup, long:0, long:MF_BYPOSITION, long:hbm, long:hbm);
         ; aError = DllLastError()
         DllStructFree(mii)
         DllFree(hUser32)
         return(@retDefault)

      case @dePbPush

         ;  Destroy bitmaps as system won't.
         hUser32 = DllLoad('user32.dll')
         hMenuBar = DllCall(hUser32, long:'GetMenu', long:Bm_Handle)
         mii = DllStructAlloc(GetMiiStructStr())
         DllStructPoke(mii, 'cbSize', IntControl(98, mii, 1, 0, 0))
         MIIM_SUBMENU = 4
         DllStructPoke(mii, 'fMask', MIIM_SUBMENU)
         DllCall(hUser32, long:'GetMenuItemInfoW', long:hMenuBar, long:0, long:@True, lpstruct:mii)
         hmenuPopup = DllStructPeek(mii, 'hSubMenu')
         MIIM_CHECKMARKS = 8
         DllStructPoke(mii, 'fMask', MIIM_CHECKMARKS)
         DllCall(hUser32, long:'GetMenuItemInfoW', long:hmenuPopup, long:0, long:@True, lpstruct:mii)
         hGdi32 = DllLoad('Gdi32.dll')
         DllCall(hGdi32,long:'DeleteObject', long:DllStructPeek(mii, 'hbmpChecked'))
         DllCall(hGdi32,long:'DeleteObject', long:DllStructPeek(mii, 'hbmpUnchecked'))
         DllStructFree(mii)
         DllFree(hUser32)
         DllFree(hGdi32)
        break
;      case @deMiSelect                                     
;      case @deMiInit                                       
;      case @deClose                                       

   endswitch                                             
   return(@retDefault)
#EndFunction                                               

return


"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

The above example only scratches the surface.  You can also create bitmap menu items where you would need to draw the menu's text on the bitmap.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

JTaylor

Just out of curiosity...would my ComControl Extender be any use?  That is, the creation and use of an "Image List"?   

Jim

td

Here is another poorly written mostly useless example.  This time using a static image.

Code (winbatch) Select
;; Define UDFs.
gosub defineudps

MenuBitmapFormat=`WWWDLGED,6.2`

MenuBitmapCaption=`WIL Dialog 1`
MenuBitmapX=001
MenuBitmapY=001
MenuBitmapWidth=352
MenuBitmapHeight=241
MenuBitmapNumControls=005
MenuBitmapProcedure=`MenuBitmapProc`
MenuBitmapFont=`DEFAULT`
MenuBitmapTextColor=`DEFAULT`
MenuBitmapBackground=`DEFAULT,DEFAULT`
MenuBitmapConfig=0

MenuBitmap001=`095,226,032,011,PUSHBUTTON,"PushButton_OK",DEFAULT,"OK",1,10,@csDefButton,DEFAULT,DEFAULT,DEFAULT`
MenuBitmap002=`223,226,032,011,PUSHBUTTON,"PushButton_Cancel",DEFAULT,"Cancel",0,20,DEFAULT,DEFAULT,DEFAULT,DEFAULT`
MenuBitmap003=`000,000,000,000,MENUBAR,"Dialog_Bar"`
MenuBitmap004=`000,000,000,000,MENUITEM,"mbi1_MyDialog","Dialog_Bar","Bitmap",DEFAULT,10,DEFAULT`
MenuBitmap005=`000,000,000,000,MENUITEM,"mbi2_MenuBitmap","mbi1_MyDialog","Has One",DEFAULT,10,DEFAULT`

ButtonPushed=Dialog("MenuBitmap")

exit

:defineudps

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Simple way to create a dummy menu item bitmap for testing
;; purposes.
;;   
;; Returns a Windows bitmap handle.
#definefunction CreateMenuBitmap(_strFile)

   ; Determine the required bitmap size.
   SM_CXMENUCHECK = 71
   SM_CYMENUCHECK = 72
   nX = WinMetrics(SM_CXMENUCHECK)
   nY = WinMetrics(SM_CYMENUCHECK)

   IMAGE_BITMAP = 0
   LR_LOADFROMFILE = 16
   hBitmap = DllCall('user32.dll', long:'LoadImageA', lpnull, lpstr:_strFile, long:IMAGE_BITMAP, LONG:nX, long:nY, long:LR_LOADFROMFILE)
   return hBitmap
#endfunction

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Creates a MENUITEMINFOW structure with DllStructAlloc.
;;
;; Returns a structure handle to a Win2 MENUITEMINFOW structure.
#DefineFunction GetMiiStruct()
   strMii =  $"
   uint::cbSize;
   uint::fMask;
   uint::fType;
   uint::fStat;
   uint::wID;
   HMENU::hSubMenu;
   HBITMAP::hbmpChecked;
   HBITMAP::hbmpUnchecked;
   DWORD_PTR::dwItemData;
   LPWSTR::dwTypeData;
   uint::cch;
   HBITMAP::hbmpItem;$"

   mii = DllStructAlloc(strMii)
   DllStructPoke(mii, 'cbSize', IntControl(98, mii, 1, 0, 0)) ; Always set the size

   return mii
#EndFunction

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; User defined dialog callback function
;;
;; Adds and destroys a menu item's bitmap.
#DefineFunction MenuBitmapProc(Bm_Handle,Bm_Event,Bm_Name,Bm_EventInfo,Bm_ChangeInfo)
   switch Bm_Event                                         
      case @deInit                                         
         DialogProcOptions(Bm_Handle, @dePbPush, 1)

         hUser32 = DllLoad('user32.dll')
         hMenuBar = DllCall(hUser32, long:'GetMenu', long:Bm_Handle)
         mii = GetMiiStruct()
         MIIM_SUBMENU = 4
         DllStructPoke(mii, 'fMask', MIIM_SUBMENU)
         DllCall(hUser32, long:'GetMenuItemInfoW', long:hMenuBar, long:0, long:@True, lpstruct:mii)
         hmenuPopup = DllStructPeek(mii, 'hSubMenu')
         hbm = CreateMenuBitmap(DirScript():'WBPrgn.bmp')
         MIIM_BITMAP = 128 ;0x00000080
         DllStructPoke(mii, 'fMask', MIIM_BITMAP)
         DllStructPoke(mii, 'fType', 0)
         DllStructPoke(mii, 'hbmpItem', hbm)       
         MF_BYPOSITION = 1024  ;0x00000400L
         az = DllCall(hUser32, long:'SetMenuItemInfoW',long:hmenuPopup, long:0, long:MF_BYPOSITION, lpstruct:mii);
         ;aError = DllLastError()
         DllStructFree(mii)
         DllFree(hUser32)
         return(@retDefault)

      case @dePbPush

         ;  Destroy bitmaps as system won't.
         hUser32 = DllLoad('user32.dll')
         hMenuBar = DllCall(hUser32, long:'GetMenu', long:Bm_Handle)
         mii = GetMiiStruct()
         MIIM_SUBMENU = 4
         DllStructPoke(mii, 'fMask', MIIM_SUBMENU)
         DllCall(hUser32, long:'GetMenuItemInfoW', long:hMenuBar, long:0, long:@True, lpstruct:mii)
         hmenuPopup = DllStructPeek(mii, 'hSubMenu')
         MIIM_BITMAP = 128 ;0x00000080
         DllStructPoke(mii, 'fMask', MIIM_BITMAP)
         DllCall(hUser32, long:'GetMenuItemInfoW', long:hmenuPopup, long:0, long:@True, lpstruct:mii)
         hGdi32 = DllLoad('Gdi32.dll')
         DllCall(hGdi32,long:'DeleteObject', long:DllStructPeek(mii, 'hbmpItem'))
         DllStructFree(mii)
         DllFree(hUser32)
         DllFree(hGdi32)
        break
;      case @deMiSelect                                     
;      case @deMiInit                                       
;      case @deClose                                       

   endswitch                                             
   return(@retDefault)
#EndFunction                                               

return


Don't believe menu items can use image lists directly.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

erezpaz

Quote from: JTaylor on June 20, 2018, 07:11:32 AM
Just out of curiosity...would my ComControl Extender be any use?  That is, the creation and use of an "Image List"?   

Jim

Do you have any example for a solution base on the com control?

Also i tried it in Windows 10. I didn't see any image just blank. see image.

stanl

Quote from: erezpaz on June 21, 2018, 07:52:44 AM
Do you have any example for a solution base on the com control?

Also i tried it in Windows 10. I didn't see any image just blank. see image.

I posted a link from the Tech DB, but removed it. Back around 2006 we did a lot with images [pre-WB Menus], using MSCI control, Treeview, or a neat OCX called TopicList. All worked pretty well on XP, but you can't even find them to download anymore and it would be a little insane to try stuff like that in Win10. There is excellent C# code out there to do what you want, but it can't be leveraged into WB due to event processing. And I agree with what Tony said, lot a work maybe not really that satisfying.

As usual I post this as somewhat controversial and to be proven wrong.  ;D

JTaylor

No, because I had no idea if they can be used in this way.  Tony indicated that this is not an option :(

Jim


Quote from: erezpaz on June 21, 2018, 07:52:44 AM
Do you have any example for a solution base on the com control?

Also i tried it in Windows 10. I didn't see any image just blank. see image.

td

Quote from: erezpaz on June 21, 2018, 07:52:44 AM
Do you have any example for a solution base on the com control?

Also i tried it in Windows 10. I didn't see any image just blank. see image.


Whatever your problem is it is not because you are testing on Windows 10.  Both scripts I posted were written and worked on Windows 10.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

Completely forgot to add that the first script posted will not show a bitmap until you click because of the way checkbox images work in Win32.  However, this is not an issue with the second script.  If you want to understand why the first script works the way it does and how to make it fully functional, you will need to read Microsoft's online documentation for the menu functions used in the script.  Pay special attention to the documentation for the "SetMenuItemBitmaps" function.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

erezpaz

Hi

I was able to successfully create an image for the menu item but not to a context menu. Cant find the context menu handle to load in the dllcall:
hMenuBar = DllCall(hUser32, long:'GetMenu', long:WindowHandler)
I need the image to appeared to a context menu on top a report view contro (see image attched)l. Is there something i am missing?

Thanks

td

You are missing the fact that a context menu is not attached to a window until a user causes a context menu message to be sent to the dialog window procedure.  The user creates the context menu message by right-clicking the control, using a menu key, or using a shortcut key combination. 
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

td

Here is Tech DB article that illustrates how to create your own sorta like a context menu that you can modify in any way you choose.

http://techsupt.winbatch.com/webcgi/webbatch.exe?techsupt/nftechsupt.web+WinBatch/Dialog~Editor/Dialog~Editor~version~6.X+Dialogs~-~Menus~in~dialogs.txt
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

erezpaz

Cant i use in the dialog function:
      case @deMiSelect
         if ControlNum == "cmi5_ReportView_JobSelect" then...
to capture the context manue handle and pass it to the SetMenuItemImage function?

td

The  @deMiSelect notification is not sent until the menu is already displayed unless a hotkey is used to activate the command without using the menu at all.  When the @deMiInit  notification is sent, the menu has not yet been associated with the Window handle in such a way that the Windows menu API can obtain the menu handle.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

JTaylor

Maybe your previous post answered this but if not, would it be a difficult enhancement to have a "ContextMenuCreated" event which passed the context menu handle to the script so it could be used to add images to context menus?

Jim

Quote from: td on July 02, 2018, 08:17:15 AM
You are missing the fact that a context menu is not attached to a window until a user causes a context menu message to be sent to the dialog window procedure.  The user creates the context menu message by right-clicking the control, using a menu key, or using a shortcut key combination.

JTaylor

Possible?  Likely any time soon?

Jim

td

User feedback is always appreciated.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

JTaylor

Hmmmmmmmm....feedback as in "User's generally like answers to questions" or some other type?   ;-)

Jim

td

Not all questions are answerable at the time they are asked and some may never be answered.  I can remember trying to explain this to my children during their formative years.  I think it may have sunk in a little bit as they are chasing their curiosity and thirst for discovery through carriers in science.  But then I could be giving myself to much credit...
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade

erezpaz

 :o
Let go back to the question asked please...
Is there a way to do what Jim asked? How can we capture the handle of the context menu? Thanks!!

Quote from: JTaylor on July 03, 2018, 03:06:56 PM
Maybe your previous post answered this but if not, would it be a difficult enhancement to have a "ContextMenuCreated" event which passed the context menu handle to the script so it could be used to add images to context menus?

Jim

Quote from: td on July 02, 2018, 08:17:15 AM
You are missing the fact that a context menu is not attached to a window until a user causes a context menu message to be sent to the dialog window procedure.  The user creates the context menu message by right-clicking the control, using a menu key, or using a shortcut key combination.

td

Quote from: erezpaz on July 11, 2018, 01:36:37 AM
:o
Let go back to the question asked please...

There isn't much to add but if you insist.

Quote
Is there a way to do what Jim asked? How can we capture the handle of the context menu? Thanks!!

As has already been mentioned in previous posts in this topic, currently there is no way to access the context menu handles in scripts.   As to the future, prophecy is difficult particularly when it is about the future but also as previously mentioned, we appreciate the suggestion.
"No one who sees a peregrine falcon fly can ever forget the beauty and thrill of that flight."
  - Dr. Tom Cade