Sunaj Posted March 21, 2007 Share Posted March 21, 2007 Hi, Basically: the submenu entry in bottom of the context menu acts erroneously and not so when on top - I think that there may be a problem with GUICtrlCreateMenu, discovered it while fiddling about with menutips. Please scroll down to the part of the code where the context menu is initialized (or search for "!PROBLEM DESCRIPTION!") to see detailed description of problem. If run/debugged in SciTE: a consolewrite has been added to provide details when moving mouse over menuitems. Could of course also be a problem with the menutip system but it seems more and more unlikely the more I look at it (which is where this posting comes in Cheers, Sunaj expandcollapse popup#include <GUIConstants.au3> #include <Array.au3> ; ################ ; MENU TIP RELATED ; ################ Global Const $WM_MENUSELECT = 0x011F, $WM_TIMER = 0x0113, $WM_ENTERIDLE = 0x0121, $WM_QUERYENDSESSION = 0x0011 Global Const $WM_COPYDATA = 0x4A, $WM_COPY = 0x0301, $WM_CHANGECBCHAIN = 0x030D, $WM_DRAWCLIPBOARD = 0x0308 Global $hWnd_NEXT_IN_CLIP_CHAIN, $bInChain ;flags to indicate app is in the notification chain. Global $USE_TOOLTIP = True Local $aCtrlArray[1], $aCtrlMsgArray[1], $ControlID Global $defaultstatus = "Ready" Global $status Global $MenuItemId Global $IDLECounter = 0 ; When 5 is reached tooltip is closed Global $EVENT ; for msg loop related to menutip Global $TIMERENABLED = False ; Flag set when SetTimer api is called Global $TIP_TIMER ; Timestamp holder for tooltip visiblilty Global Const $TIP_TIMER_ID = 999 ; Timer id for SetTimer api Global Const $TIPSHOW = 1 ; Event Global Const $TIPVISIBLE = 2 ; Event Global Const $TIP_TTL = 3333 ; how long to show tooltip Global Const $TIP_DELAY = 333 ; how long to wait to show tooltip Global Const $MSG_INTERVAL = 33 ; Interval Windows will use to send ; #################### ; WINDOW MESSAGE HOOKS ; #################### GUIRegisterMsg($WM_MENUSELECT, "MenuTipHandler") ; menu tip, change on move between menu items GUIRegisterMsg($WM_ENTERIDLE, "MenuTipHandler") ; to make context menues disappear when moving pointer off menu without closing it GUIRegisterMsg($WM_TIMER, "TimerCallBack") ; also menu tip related GUISetOnEvent($GUI_EVENT_CLOSE, "exit_program") ; exit on standard stuff $h_GUI = GUICreate("My GUI Context Menu", 300, 200) $contextmenu = GUICtrlCreateContextMenu() $button = GUICtrlCreateButton("OK", 100, 100, 70, 20) $buttoncontext = GUICtrlCreateContextMenu($button) $buttonitem = GUICtrlCreateMenuitem("About button", $buttoncontext) $fileitem1 = GUICtrlCreateMenuitem("Open1", $contextmenu) _AddCtrl($fileitem1, "test1") $fileitem2 = GUICtrlCreateMenuitem("Open2", $contextmenu) _AddCtrl($fileitem2, "test2") $fileitem3 = GUICtrlCreateMenuitem("Open3", $contextmenu) _AddCtrl($fileitem3, "test3") $fileitem4 = GUICtrlCreateMenuitem("Open4", $contextmenu) _AddCtrl($fileitem4, "test4") $fileitem5 = GUICtrlCreateMenuitem("Open5", $contextmenu) _AddCtrl($fileitem5, "test5") $saveitem = GUICtrlCreateMenuitem("Save", $contextmenu) GUICtrlCreateMenuitem("", $contextmenu) ; separator $infoitem = GUICtrlCreateMenuitem("Info", $contextmenu) $newsubmenu = GUICtrlCreateMenu("new", $contextmenu) ; (!PROBLEM DESCRIPTION!) if you run this in scite you can see in the console that this menu is given the same internal id (the wparam) as the "Open2" menuitem.. this does NOT happen if the submenu is moved to the top of the menu, maybe suggesting a possible problem with GUICtrlCreateMenu (since this kinda ghosting does not happen with GUICtrlCreateMenuitem...?) $textitem = GUICtrlCreateMenuitem("text", $newsubmenu) GUISetState() ; Run the GUI until the dialog is closed While 1 $msg = GUIGetMsg() If $msg = $GUI_EVENT_CLOSE Then ExitLoop WEnd Func exit_program() Exit EndFunc ;==>exit_program ; Show a menu in a given GUI window which belongs to a given GUI ctrl Func ShowMenu($hWnd, $CtrlID, $nContextID) Local $hMenu = GUICtrlGetHandle($nContextID) $arPos = ControlGetPos($hWnd, "", $CtrlID) Local $x = $arPos[0] Local $y = $arPos[1] + $arPos[3] ClientToScreen($hWnd, $x, $y) TrackPopupMenu($hWnd, $hMenu, $x, $y) EndFunc ;==>ShowMenu ; Convert the client (GUI) coordinates to screen (desktop) coordinates Func ClientToScreen($hWnd, ByRef $x, ByRef $y) Local $stPoint = DllStructCreate("int;int") DllStructSetData($stPoint, 1, $x) DllStructSetData($stPoint, 2, $y) DllCall("user32.dll", "int", "ClientToScreen", "hwnd", $hWnd, "ptr", DllStructGetPtr($stPoint)) $x = DllStructGetData($stPoint, 1) $y = DllStructGetData($stPoint, 2) ; release Struct not really needed as it is a local $stPoint = 0 EndFunc ;==>ClientToScreen ; Show at the given coordinates (x, y) the popup menu (hMenu) which belongs to a given GUI window (hWnd) Func TrackPopupMenu($hWnd, $hMenu, $x, $y) DllCall("user32.dll", "int", "TrackPopupMenuEx", "hwnd", $hMenu, "int", 0, "int", $x, "int", $y, "hwnd", $hWnd, "ptr", 0) EndFunc ;==>TrackPopupMenu Func EventLoop() Select Case $EVENT = $TIPSHOW If $IDLECounter < 11 Then ; this if/then fixes 'quick move over' problem with context menu (not truely on the global menu) If $TIP_TIMER Then If ((TimerDiff($TIP_TIMER) >= $TIP_DELAY) And ($MenuItemId > 0)) Then ShowMenuTip() EndIf EndIf Case $EVENT = $TIPVISIBLE If TimerDiff($TIP_TIMER) >= $TIP_TTL + $TIP_DELAY Then VoidMenuTip() EndSelect EndFunc ;==>EventLoop Func _AddCtrl($ControlID, $ControlMsg) _ArrayAdd($aCtrlArray, $ControlID) _ArrayAdd($aCtrlMsgArray, $ControlMsg) EndFunc ;==>_AddCtrl Func ShowMenuTip() For $x = 0 To UBound($aCtrlArray) - 1 If $MenuItemId = ($aCtrlArray[$x]) Then ToolTip($aCtrlMsgArray[$x]) $EVENT = $TIPVISIBLE $IDLECounter = 0 ExitLoop EndIf Next EndFunc ;==>ShowMenuTip Func VoidMenuTip() ToolTip("") $TIP_TIMER = 0 EndFunc ;==>VoidMenuTip Func StartTimer($hWndGUI, $TimerId, $Interval) If $TIMERENABLED = True Then StopTimer($hWndGUI, $TimerId) $retval = DllCall("User32.dll", "int", "SetTimer", "hwnd", $hWndGUI, "int", $TimerId, "int", $Interval, "int", 0) $TIMERENABLED = True EndFunc ;==>StartTimer Func StopTimer($hWndGUI, $TimerId) $retval = DllCall("User32.dll", "int", "KillTimer", "hwnd", $hWndGUI, "int", $TimerId) $TIMERENABLED = False EndFunc ;==>StopTimer Func TimerCallBack($hWndGUI, $MsgID, $WParam, $LParam) Local $TimerId = BitAND($WParam, 0xFFFF) If $TimerId = $TIP_TIMER_ID Then EventLoop() Return $GUI_RUNDEFMSG EndFunc ;==>TimerCallBack Func MenuTipHandler($hWndGUI, $MsgID, $WParam, $LParam) $IDLECounter += 1 If $IDLECounter > 4 Then ;make context tip disappear on move away ToolTip("") EndIf Local $id = BitAND($WParam, 0xFFFF) If $MsgID = $WM_MENUSELECT Then If $USE_TOOLTIP Then StartTimer($h_GUI, $TIP_TIMER_ID, $MSG_INTERVAL) $MenuItemId = $id ToolTip("") $IDLECounter = 0 If $MenuItemId > 0 Then ConsoleWrite("$WParam: " & $WParam & "$LParam: " & $LParam & @CRLF) $TIP_TIMER = TimerInit() $EVENT = $TIPSHOW EndIf EndIf EndIf Return $GUI_RUNDEFMSG EndFunc ;==>MenuTipHandler [list=1][*]Generic way to detect full path to default browser, List/ListView Events Using GuiRegisterMsg (detect doubleclick and much more)[*]Using dllcall for full control over fileopendialog, Make DirMove act somewhat normally (by circumventing it...)[*]Avoid problems with "&" (chr(38)) in code, Change desktop maximized area/workspace (fx to make deskbar type app)[*]Change focus behavior when buttons are clicked to work closer to 'standard windows' app[*](Context) Menus With Timed Tooltips, Fast Loops & Operators in AU3[*]Clipboard UDF, A clipboard change notification udf[/list] Link to comment Share on other sites More sharing options...
Holger Posted March 21, 2007 Share Posted March 21, 2007 (edited) Not a problem with GUICtrlCreateMenu() - a problem with getting the ID - a menuitem is not the same like a menu! From MSDN: wParam...If the selected item opens a drop-down menu or submenu, this parameter contains the index of the drop-down menu or submenu in the main menu, and the lParam parameter contains the handle to the main (clicked) menu... expandcollapse popup#include <GUIConstants.au3> #include <Array.au3> ; ################ ; MENU TIP RELATED ; ################ Global Const $WM_MENUSELECT = 0x011F, $WM_TIMER = 0x0113, $WM_ENTERIDLE = 0x0121, $WM_QUERYENDSESSION = 0x0011 Global Const $WM_COPYDATA = 0x4A, $WM_COPY = 0x0301, $WM_CHANGECBCHAIN = 0x030D, $WM_DRAWCLIPBOARD = 0x0308 Global $hWnd_NEXT_IN_CLIP_CHAIN, $bInChain ;flags to indicate app is in the notification chain. Global $USE_TOOLTIP = True Local $aCtrlArray[1], $aCtrlMsgArray[1], $ControlID Global $defaultstatus = "Ready" Global $status Global $MenuItemId Global $IDLECounter = 0 ; When 5 is reached tooltip is closed Global $EVENT ; for msg loop related to menutip Global $TIMERENABLED = False ; Flag set when SetTimer api is called Global $TIP_TIMER ; Timestamp holder for tooltip visiblilty Global Const $TIP_TIMER_ID = 999 ; Timer id for SetTimer api Global Const $TIPSHOW = 1 ; Event Global Const $TIPVISIBLE = 2 ; Event Global Const $TIP_TTL = 3333 ; how long to show tooltip Global Const $TIP_DELAY = 333 ; how long to wait to show tooltip Global Const $MSG_INTERVAL = 33 ; Interval Windows will use to send Global Const $MF_POPUP = 0x00000010 ; Drop-down menu or submenu Global Const $MIIM_ID = 0x00000002 ; Menu item ID ; #################### ; WINDOW MESSAGE HOOKS ; #################### GUIRegisterMsg($WM_MENUSELECT, "MenuTipHandler") ; menu tip, change on move between menu items GUIRegisterMsg($WM_ENTERIDLE, "MenuTipHandler") ; to make context menues disappear when moving pointer off menu without closing it GUIRegisterMsg($WM_TIMER, "TimerCallBack") ; also menu tip related GUISetOnEvent($GUI_EVENT_CLOSE, "exit_program") ; exit on standard stuff $h_GUI = GUICreate("My GUI Context Menu", 300, 200) $contextmenu = GUICtrlCreateContextMenu() $button = GUICtrlCreateButton("OK", 100, 100, 70, 20) $buttoncontext = GUICtrlCreateContextMenu($button) $buttonitem = GUICtrlCreateMenuitem("About button", $buttoncontext) $fileitem1 = GUICtrlCreateMenuitem("Open1", $contextmenu) _AddCtrl($fileitem1, "test1") $fileitem2 = GUICtrlCreateMenuitem("Open2", $contextmenu) _AddCtrl($fileitem2, "test2") $fileitem3 = GUICtrlCreateMenuitem("Open3", $contextmenu) _AddCtrl($fileitem3, "test3") $fileitem4 = GUICtrlCreateMenuitem("Open4", $contextmenu) _AddCtrl($fileitem4, "test4") $fileitem5 = GUICtrlCreateMenuitem("Open5", $contextmenu) _AddCtrl($fileitem5, "test5") $saveitem = GUICtrlCreateMenuitem("Save", $contextmenu) _AddCtrl($saveitem, "saveitem") GUICtrlCreateMenuitem("", $contextmenu) ; separator $infoitem = GUICtrlCreateMenuitem("Info", $contextmenu) _AddCtrl($infoitem, "infoitem") $newsubmenu = GUICtrlCreateMenu("new", $contextmenu) ; (!PROBLEM DESCRIPTION!) if you run this in scite you can see in the console that this menu is given the same internal id (the wparam) as the "Open2" menuitem.. this does NOT happen if the submenu is moved to the top of the menu, maybe suggesting a possible problem with GUICtrlCreateMenu (since this kinda ghosting does not happen with GUICtrlCreateMenuitem...?) _AddCtrl($newsubmenu, "newsubmenu") $textitem = GUICtrlCreateMenuitem("text", $newsubmenu) _AddCtrl($textitem, "textitem") GUISetState() ; Run the GUI until the dialog is closed While 1 $msg = GUIGetMsg() If $msg = $GUI_EVENT_CLOSE Then ExitLoop WEnd Func exit_program() Exit EndFunc ;==>exit_program ; Show a menu in a given GUI window which belongs to a given GUI ctrl Func ShowMenu($hWnd, $CtrlID, $nContextID) Local $hMenu = GUICtrlGetHandle($nContextID) $arPos = ControlGetPos($hWnd, "", $CtrlID) Local $x = $arPos[0] Local $y = $arPos[1] + $arPos[3] ClientToScreen($hWnd, $x, $y) TrackPopupMenu($hWnd, $hMenu, $x, $y) EndFunc ;==>ShowMenu ; Convert the client (GUI) coordinates to screen (desktop) coordinates Func ClientToScreen($hWnd, ByRef $x, ByRef $y) Local $stPoint = DllStructCreate("int;int") DllStructSetData($stPoint, 1, $x) DllStructSetData($stPoint, 2, $y) DllCall("user32.dll", "int", "ClientToScreen", "hwnd", $hWnd, "ptr", DllStructGetPtr($stPoint)) $x = DllStructGetData($stPoint, 1) $y = DllStructGetData($stPoint, 2) ; release Struct not really needed as it is a local $stPoint = 0 EndFunc ;==>ClientToScreen ; Show at the given coordinates (x, y) the popup menu (hMenu) which belongs to a given GUI window (hWnd) Func TrackPopupMenu($hWnd, $hMenu, $x, $y) DllCall("user32.dll", "int", "TrackPopupMenuEx", "hwnd", $hMenu, "int", 0, "int", $x, "int", $y, "hwnd", $hWnd, "ptr", 0) EndFunc ;==>TrackPopupMenu Func EventLoop() Select Case $EVENT = $TIPSHOW If $IDLECounter < 11 Then ; this if/then fixes 'quick move over' problem with context menu (not truely on the global menu) If $TIP_TIMER Then If ((TimerDiff($TIP_TIMER) >= $TIP_DELAY) And ($MenuItemId > 0)) Then ShowMenuTip() EndIf EndIf Case $EVENT = $TIPVISIBLE If TimerDiff($TIP_TIMER) >= $TIP_TTL + $TIP_DELAY Then VoidMenuTip() EndSelect EndFunc ;==>EventLoop Func _AddCtrl($ControlID, $ControlMsg) _ArrayAdd($aCtrlArray, $ControlID) _ArrayAdd($aCtrlMsgArray, $ControlMsg) EndFunc ;==>_AddCtrl Func ShowMenuTip() For $x = 0 To UBound($aCtrlArray) - 1 If $MenuItemId = ($aCtrlArray[$x]) Then ToolTip($aCtrlMsgArray[$x]) $EVENT = $TIPVISIBLE $IDLECounter = 0 ExitLoop EndIf Next EndFunc ;==>ShowMenuTip Func VoidMenuTip() ToolTip("") $TIP_TIMER = 0 EndFunc ;==>VoidMenuTip Func StartTimer($hWndGUI, $TimerId, $Interval) If $TIMERENABLED = True Then StopTimer($hWndGUI, $TimerId) $retval = DllCall("User32.dll", "int", "SetTimer", "hwnd", $hWndGUI, "int", $TimerId, "int", $Interval, "int", 0) $TIMERENABLED = True EndFunc ;==>StartTimer Func StopTimer($hWndGUI, $TimerId) $retval = DllCall("User32.dll", "int", "KillTimer", "hwnd", $hWndGUI, "int", $TimerId) $TIMERENABLED = False EndFunc ;==>StopTimer Func TimerCallBack($hWndGUI, $MsgID, $WParam, $LParam) Local $TimerId = BitAND($WParam, 0xFFFF) If $TimerId = $TIP_TIMER_ID Then EventLoop() Return $GUI_RUNDEFMSG EndFunc ;==>TimerCallBack Func MenuTipHandler($hWndGUI, $MsgID, $WParam, $LParam) $IDLECounter += 1 If $IDLECounter > 4 Then ;make context tip disappear on move away ToolTip("") EndIf Local $id = BitAND($WParam, 0xFFFF) If $MsgID = $WM_MENUSELECT Then Local $Flags = BitAnd(BitShift($wParam, 16), 0xFFFF) If BitAnd($Flags, $MF_POPUP) Then Local $stMII = DllStructCreate("uint;uint;uint;uint;uint;hwnd;hwnd;hwnd;ptr;ptr;uint;hwnd") DllStructSetData($stMII, 1, DllStructGetSize($stMII)) DllStructSetData($stMII, 2, $MIIM_ID) If GetMenuItemInfoA($LParam, $id, TRUE, DllStructGetPtr($stMII)) Then $id = DllStructGetData($stMII, 5) EndIf If $USE_TOOLTIP Then StartTimer($h_GUI, $TIP_TIMER_ID, $MSG_INTERVAL) $MenuItemId = $id ToolTip("") $IDLECounter = 0 If $MenuItemId > 0 Then ConsoleWrite("$WParam: " & $WParam & "$LParam: " & $LParam & @CRLF) $TIP_TIMER = TimerInit() $EVENT = $TIPSHOW EndIf EndIf EndIf Return $GUI_RUNDEFMSG EndFunc ;==>MenuTipHandler Func GetMenuItemInfoA($hMenu, $nItem, $bByPosition, $pMII) Local $bResult = DllCall("user32.dll", "int", "GetMenuItemInfo", _ "hwnd", $hMenu, _ "int", $nItem, _ "int", $bByPosition, _ "ptr", $pMII) Return $bResult[0] EndFunc Greets Holger Edited March 21, 2007 by Holger Old project:GUI/Tray menu with icons and colors Other old stuff:IconFileScanner, TriState/ThreeState GUI TreeView, GUI ContextMenu created out of a TreeView Link to comment Share on other sites More sharing options...
Sunaj Posted March 21, 2007 Author Share Posted March 21, 2007 (edited) Update: sorry i had not seen your superb work on the code - simply took it for granted that you just replied in text. Everything works as it should - thanks again! Ok, thanks for the clarification Holger. Do you have an idea/explanation as to the fact that the submenu is given a unique id if placed at the top of the menu and not so if its placed at the bottom - and, if yes, how do think I could get the submenu to have a unique id when placed at the bottom (is it possible to change the id..?)? Thanks for your help here, I realize it takes time to look into this subject!Greets,SunajNot a problem with GUICtrlCreateMenu() - a problem with getting the ID - a menuitem is not the same like a menu!From MSDN: wParam...If the selected item opens a drop-down menu or submenu, this parameter contains the index of the drop-down menu or submenu in the main menu, and the lParam parameter contains the handle to the main (clicked) menu... Edited March 22, 2007 by Sunaj [list=1][*]Generic way to detect full path to default browser, List/ListView Events Using GuiRegisterMsg (detect doubleclick and much more)[*]Using dllcall for full control over fileopendialog, Make DirMove act somewhat normally (by circumventing it...)[*]Avoid problems with "&" (chr(38)) in code, Change desktop maximized area/workspace (fx to make deskbar type app)[*]Change focus behavior when buttons are clicked to work closer to 'standard windows' app[*](Context) Menus With Timed Tooltips, Fast Loops & Operators in AU3[*]Clipboard UDF, A clipboard change notification udf[/list] Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now