Jump to content

Replace Edit control context menu with your own


maillady
 Share

Recommended Posts

Hi,

This example demonstrates how to replace the default context menu in an edit control with your own

Uses _WinAPI_CallWindowProc, a good function that requires more examples :ILA:

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <Constants.au3>
#include <GuiMenu.au3>
#include <WinAPI.au3>
#include <GUIEdit.au3>

; modified from help example: _WinAPI_CallWindowProc

$hGui = GUICreate("Custom context menu for Edit Control example", 489, 354, -1, -1)
$editBox = GUICtrlCreateEdit("", 8, 40, 473, 305)
GUICtrlSetData(-1, "New text")
$checkBox = GUICtrlCreateCheckbox("Use our own context menu", 8, 8, 241, 17)
GUICtrlSetState($checkBox, $GUI_CHECKED)

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;!! subclass for $editBox
$w_editBoxProcNew = DllCallbackRegister("_MyWindowProc", "ptr", "hwnd;uint;long;ptr")
$w_editBoxProcOld = _WinAPI_SetWindowLong(GUICtrlGetHandle($editBox), $GWL_WNDPROC, DllCallbackGetPtr($w_editBoxProcNew))
_WinAPI_SetWindowLong(GUICtrlGetHandle($editBox), $GWL_WNDPROC, DllCallbackGetPtr($w_editBoxProcNew))

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;!! our context menu for $editBox
$DummyMenu = GUICtrlCreateDummy()
$ContextMenu = GUICtrlCreateContextMenu($DummyMenu)
$TestMenuItem = GUICtrlCreateMenuItem("Our Custom Context Menu", $ContextMenu)
GUICtrlSetState($TestMenuItem, $GUI_DEFBUTTON)
GUICtrlCreateMenuItem("", $ContextMenu)
$UndoMenuItem = GUICtrlCreateMenuItem("&Undo", $ContextMenu)
GUICtrlCreateMenuItem("", $ContextMenu)
$CutMenuItem = GUICtrlCreateMenuItem("&Cut", $ContextMenu)
$CopyMenuItem = GUICtrlCreateMenuItem("&Copy", $ContextMenu)
$PasteMenuItem = GUICtrlCreateMenuItem("&Paste", $ContextMenu)
$DeleteMenuItem = GUICtrlCreateMenuItem("&Delete", $ContextMenu)
GUICtrlCreateMenuItem("", $ContextMenu)
$SelectAllMenuItem = GUICtrlCreateMenuItem("&Select all", $ContextMenu)
GUICtrlCreateMenuItem("", $ContextMenu)
$InsertDateMenuItem = GUICtrlCreateMenuItem("&Insert Date and Time", $ContextMenu)

GUISetState(@SW_SHOW)

While 1
    $nMsg = GUIGetMsg()
    Switch $nMsg
        ; toggle our context menu on / off
        Case $checkBox
            If BitAND(GUICtrlRead($checkBox), $GUI_CHECKED) = $GUI_CHECKED Then
                _WinAPI_SetWindowLong(GUICtrlGetHandle($editBox), $GWL_WNDPROC, DllCallbackGetPtr($w_editBoxProcNew))
            Else
                _WinAPI_SetWindowLong(GUICtrlGetHandle($editBox), $GWL_WNDPROC, $w_editBoxProcOld)
            EndIf

        Case $GUI_EVENT_CLOSE
            ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            ;!! we must free the callback before exit !!
            _WinAPI_SetWindowLong(GUICtrlGetHandle($editBox), $GWL_WNDPROC, $w_editBoxProcOld)
            DllCallbackFree($w_editBoxProcNew)
            Exit
    EndSwitch
WEnd

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;!! process our context menu messages
Func MenuItem($iSelected)
    Switch $iSelected
        Case $TestMenuItem
            _GUICtrlEdit_AppendText($editBox, @CRLF & "Our Custom Context Menu")
        Case $UndoMenuItem
            _GUICtrlEdit_Undo($editBox)
        Case $CutMenuItem
            MenuItem($CopyMenuItem)
            MenuItem($DeleteMenuItem)
        Case $CopyMenuItem
            Local $aSel, $sText
            $aSel = _GUICtrlEdit_GetSel($editBox)
            If ($aSel[0] <> $aSel[1]) Then
                $sText = StringMid(_GUICtrlEdit_GetText($editBox), $aSel[0] + 1, ($aSel[1] - $aSel[0]) + 1)
                ClipPut($sText)
            EndIf
        Case $PasteMenuItem
            Local $sText = ClipGet()
            If (Not @error) Then _GUICtrlEdit_ReplaceSel($editBox, $sText, True)
        Case $DeleteMenuItem
            _GUICtrlEdit_ReplaceSel($editBox, "", True)
        Case $SelectAllMenuItem
            _GUICtrlEdit_SetSel($editBox, 0, -1)
        Case $InsertDateMenuItem
            _GUICtrlEdit_ReplaceSel($editBox, @CRLF & @YEAR &"/"& @MON &"/"& @MDAY &" "& @HOUR &":" & @MIN &":" & @SEC)
    EndSwitch
EndFunc   ;==>ShowMenu

; auxilary function to update menu item states, not required
Func UpdateMenuStates()
    Local $aSel = _GUICtrlEdit_GetSel($editBox)
    Local $iLen = _GUICtrlEdit_GetTextLen($editBox)
    _GUICtrlEnabled($UndoMenuItem, _GUICtrlEdit_CanUndo($editBox))
    _GUICtrlEnabled($CutMenuItem, ($aSel[0]<>$aSel[1]))
    _GUICtrlEnabled($CopyMenuItem, ($aSel[0]<>$aSel[1]))
    _GUICtrlEnabled($PasteMenuItem, (StringLen(ClipGet())>0))
    _GUICtrlEnabled($DeleteMenuItem, ($aSel[0]<>$aSel[1]))
    _GUICtrlEnabled($SelectAllMenuItem, ($iLen>0) And ($iLen<>$aSel[0]+$aSel[1]))
EndFunc   ;==>SetMenuTexts

; auxilary function to enable / disable controls, not required
Func _GUICtrlEnabled($CtrlID, $fTrue = True)
    If $fTrue Then
        GUICtrlSetState($CtrlID, $GUI_ENABLE)
    Else
        GUICtrlSetState($CtrlID, $GUI_DISABLE)
    EndIf
EndFunc

;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
;!! our window subclass for $editBox
Func _MyWindowProc($hWnd, $uiMsg, $wParam, $lParam)
    Switch $uiMsg
        Case $WM_CONTEXTMENU
            If $hWnd = GUICtrlGetHandle($editBox) Then
                ; show our context menu
                UpdateMenuStates()
                Local $iSelected = _GUICtrlMenu_TrackPopupMenu(GUICtrlGetHandle($ContextMenu), $hWnd, -1, -1, 1, 1, 2)
                MenuItem($iSelected)
                Return 0
            EndIf
    EndSwitch
    ;pass the unhandled messages to default WindowProc
    Return _WinAPI_CallWindowProc($w_editBoxProcOld, $hWnd, $uiMsg, $wParam, $lParam)
EndFunc   ;==>_MyWindowProc

Have a good day

Edited by maillady
Link to comment
Share on other sites

Excellent demonstration of example, killing the following sentence:

"Note: You can not create context menus for controls That Already have context menus system, ie input or edit controls."

The final must be in the following sequence:

Case $GUI_EVENT_CLOSE
            ;!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            ;!! we must free the callback before exit !!
            _WinAPI_SetWindowLong(GUICtrlGetHandle($editBox), $GWL_WNDPROC, $w_editBoxProcOld)
            DllCallbackFree($w_editBoxProcNew)
            Exit

***** stars!

Regards,

João Carlos.

Edited by JScript

http://forum.autoitbrasil.com/ (AutoIt v3 Brazil!!!)

Somewhere Out ThereJames Ingram

somewh10.png

dropbo10.pngDownload Dropbox - Simplify your life!
Your virtual HD wherever you go, anywhere!

Link to comment
Share on other sites

I made something similar nearly 2 years ago.

I deactivated unnecessary things for demonstration.

I will have a look at your solution. Thanks for sharing!

;Universal Variable Table
;2010 Nov 25

;~ #include "libnodave.au3"
#Include 
#include 
#include 
#include 
#include 
#include 
#Include 

;~ Opt("GUIResizeMode", 1)
Opt("GUIOnEventMode", 1)

Global $sTitle = "UniVarTab für SIMATIC S7-300 und S7-400 über TCP/IP"
Global $Ini = @ScriptDir & "UniVarTab.ini"
Global $hProc = DllCallBackRegister("_WindowProc", "int", "hwnd;uint;wparam;lparam")
Global $OriginalWindowProc
Global $aGridLines[1]
Global $Socket_SPS, $di_SPS, $dc_SPS
Global $LastID

FileInstall("UniVarTab.ini", $Ini, 0)

Global $aSPS = IniReadSection($Ini, "SPS")

Global $hGui = GUICreate($sTitle, 800, 605)
GUISetOnEvent(-3, "_Exit")
GUIRegisterMsg(0x4E, "_WM_NOTIFY")

Global $hCombo = GUICtrlCreateCombo("", 10, 5, 200, 60, 0x3)
For $i = 1 To $aSPS[0][0]
    GUICtrlSetData(-1, $aSPS[$i][0], $aSPS[1][0])
Next

Global $hBtnConnect = GUICtrlCreateButton("Verbinden", 220, 5, 100, 20)
GUICtrlSetOnEvent(-1, "_Connect")

Global $hBtnView = GUICtrlCreateCheckbox("Beobachten", 330, 5, 100, 20, 0x1000)
GUICtrlSetOnEvent(-1, "_View")
GUICtrlSetState(-1, 128)

Global $hInput = GUICtrlCreateInput("", 0, 0, 0, 0, 0x280)
GUICtrlSetState(-1, $GUI_HIDE)

Global $HeaderX = 10, $HeaderY = 40

GUICtrlCreateLabel("", $HeaderX, $HeaderY, 30, 20, -1, 0x1)

Global $hHeader = _GUICtrlHeader_Create($hGui)
_GUICtrlHeader_SetUnicodeFormat($hHeader, True)

Global $aHeaderWidth[6] = [30, 120, 340, 100, 100, 90]

_GUICtrlHeader_AddItem($hHeader, "", $aHeaderWidth[0])
_GUICtrlHeader_AddItem($hHeader, "Operand", $aHeaderWidth[1])
_GUICtrlHeader_AddItem($hHeader, "Kommentar", $aHeaderWidth[2])
_GUICtrlHeader_AddItem($hHeader, "Anzeigeformat", $aHeaderWidth[3])
_GUICtrlHeader_AddItem($hHeader, "Statuswert", $aHeaderWidth[4])
_GUICtrlHeader_AddItem($hHeader, "Steuerwert", $aHeaderWidth[5])

ControlMove($sTitle, "", "SysHeader321", $HeaderX, $HeaderY, 780)

Global $aHeaderPos[6]
For $i = 0 To 5
    $aHeaderPos[$i] = _GUICtrlHeader_GetItemRect($hHeader, $i)
Next

Local $hDummy = GUICtrlCreateDummy()
Global $FormatContext = GUICtrlCreateContextMenu($hDummy)
Global $aMenu[5][2] = [["Binär"], ["Bool"], ["Dezimal"], ["Hexadezimal"], ["Gleitpunkt"]]
For $i = 0 To UBound($aMenu) - 1
    $aMenu[$i][1] = GUICtrlCreateMenuItem($aMenu[$i][0], $FormatContext)
    GUICtrlSetOnEvent(-1, "_WriteFormat")
Next

$aGridLines[0] = _NewGridLine()

GUISetState()

While 1
    Sleep(20000)
WEnd

Func _View()
    If GUICtrlRead(@GUI_CtrlId) = 1 Then
;~         AdlibRegister("_GetData", 1000)
;~         _GetData()
    Else
;~         AdlibUnRegister("_GetData")
    EndIf
EndFunc

Func _Connect()
    Local $Index = GUICtrlSendMsg($hCombo, 0x147, 0, 0)
    Local $aParam = StringSplit($aSPS[$Index+1][1], ";", 2)
    If GUICtrlRead(@GUI_CtrlId) = "Verbinden" Then
;~         _daveTCPConnect($aParam[0], $Socket_SPS, $di_SPS, $dc_SPS, $aParam[1], $aParam[2], $aSPS[$Index+1][0])
        If @error Then
            MsgBox(16, "Error " & @error, "Verbindung zur SPS konnte nicht hergestellt werden.")
        Else
            GUICtrlSetState($hCombo, 128)
            GUICtrlSetState($hBtnView, 64)
            GUICtrlSetData(@GUI_CtrlId, "Trennen")
        EndIf
    Else
;~         $ViewMode = 0
;~         $ViewActive = 0
;~         _daveTCPDisconnect($Socket_SPS, $di_SPS, $dc_SPS)
        GUICtrlSetData(@GUI_CtrlId, "Verbinden")
        GUICtrlSetState($hBtnView, 128)
        GUICtrlSetState($hCombo, 64)
    EndIf
EndFunc

;~ Func _GetData()
;~ ConsoleWrite("Lesen" & @cr)
;~     Local $aTemp = $aGridLines[0]
;~     Local $sAdresse = GUICtrlRead($aTemp[1])
;~     Local $Area = _GetArea($sAdresse)
;~     Local $DB = _GetDB($sAdresse)
;~     Local $StartByte = _GetByte($sAdresse)
;~     _daveReadBytes($dc_SPS, $Area, $DB, $StartByte, 4)
;~     GUICtrlSetData($aTemp[4], _daveGetFloat($dc_SPS))
;~ EndFunc

;~ Func _GetDB($sAdresse)
;~     If StringLeft($sAdresse, 2)  'DB' Then Return 0
;~     Return StringTrimLeft(StringLeft($sAdresse, StringInStr($sAdresse, '.') -1), 2)
;~ EndFunc

;~ Func _AdresseGetBit($sAdresse)
;~     Return StringRight($sAdresse, 1)
;~ EndFunc

;~ Func _GetByte($sAdresse)
;~     Local $string = StringRegExp($sAdresse, '(d+).*d*z', 3)    ; Dank an Oscar von Autoit.de für diesen regulären Ausdruck
;~     Return $string[0]
;~ EndFunc

;~ Func _GetArea($sAdresse)
;~     Switch StringLeft($sAdresse, 1)
;~     Case 'D'
;~         Return $daveDB
;~     Case 'E', 'I'
;~         Return $daveInputs
;~     Case 'A', 'O'
;~         Return $daveOutputs
;~     Case 'M', 'F'
;~         Return $daveFlags
;~     Case 'T'
;~         Return $daveTimer
;~     Case 'Z'
;~         Return $daveCounter
;~     Case Else
;~         Return 0
;~     EndSwitch
;~ EndFunc

Func _WM_NOTIFY($hWnd, $iMsg, $iwParam, $ilParam)
    Local $hWndFrom, $iIDFrom, $iCode
    Local $tNMHDR, $tNMHEADER

    $tNMHDR = DllStructCreate($tagNMHDR, $ilParam)
    $hWndFrom = HWnd(DllStructGetData($tNMHDR, "hWndFrom"))
    $iIDFrom = DllStructGetData($tNMHDR, "IDFrom")
    $iCode = DllStructGetData($tNMHDR, "Code")
    $tNMHEADER = DllStructCreate($tagNMHEADER, $ilParam)
    Switch $hWndFrom
        Case $hHeader
            Switch $iCode
                Case -321
                    _Reset_HeaderWidth(DllStructGetData($tNMHEADER, "Item"))
            EndSwitch
    EndSwitch

    Return "GUI_RUNDEFMSG"
EndFunc   ;==>_WM_NOTIFY


Func _Reset_HeaderWidth($iIndex)
    _GUICtrlHeader_SetItemWidth($hHeader, $iIndex, $aHeaderWidth[$iIndex])
EndFunc

Func _WriteFormat()
    GUICtrlSetData($LastID, GUICtrlRead(@GUI_CtrlId, 1))
EndFunc

Func _NewGridLine()
    Local $aLabel[6], $aPos, $iTemp
    For $i = 0 To 5
        $aPos = $aHeaderPos[$i]
        Switch $i
            Case 0
                $aLabel[$i] = GUICtrlCreateLabel("*>", $aPos[0] + $HeaderX, $aPos[3] + $HeaderY, $aPos[2] - $aPos[0], $aPos[3] - $aPos[1], -1, 0x1)
                GUICtrlSetOnEvent(-1, "_AddNewGridLine")
            Case 4
                $aLabel[$i] = GUICtrlCreateLabel("", $aPos[0] + $HeaderX, $aPos[3] + $HeaderY, $aPos[2] - $aPos[0], $aPos[3] - $aPos[1], -1, 0x1)
                GUICtrlSetBkColor(-1, 0xcfcfcf)
            Case Else
                $aLabel[$i] = GUICtrlCreateEdit("", $aPos[0] + $HeaderX, $aPos[3] + $HeaderY, $aPos[2] - $aPos[0], $aPos[3] - $aPos[1], 0, 0x1)
        EndSwitch
        $iTemp = $aPos[3] - $aPos[1]
        $aPos[1] += $iTemp
        $aPos[3] += $iTemp
        $aHeaderPos[$i] = $aPos
    Next
    $OriginalWindowProc = _WinSubclass(GUICtrlGetHandle($aLabel[3]), DllCallbackGetPtr($hProc))
    Return $aLabel
EndFunc

Func _AddNewGridLine()
    If UBound($aGridLines) = 5 Then Return
    ReDim $aGridLines[UBound($aGridLines) + 1]
    $aGridLines[UBound($aGridLines) - 1] = _NewGridLine()
EndFunc

Func _Exit()
    GUISetState(@SW_HIDE, $hGui)
;~     _daveTCPDisconnect($Socket_SPS, $di_SPS, $dc_SPS)
;~     _DaveDeInit()
    _GUICtrlHeader_Destroy($hHeader)
    GUIDelete()
    DllCallBackFree($hProc)
    Exit
EndFunc

Func _ShowFormatMenu()
;~      _GUICtrlMenu_TrackPopupMenu(GUICtrlGetHandle($FormatContext), $hGui)
    DllCall("user32.dll", "int", "TrackPopupMenuEx", "hwnd", GUICtrlGetHandle($FormatContext), "int", 0, "int", MouseGetPos(0), "int", MouseGetPos(1), "hwnd", $hGui, "ptr", 0)
EndFunc

Func _WindowProc($hWnd, $uiMsg, $wParam, $lParam)
   ; Disable the context menu and show your own menu
    Local $aTemp
    Switch $uiMsg
        Case $WM_CONTEXTMENU
            For $i = 0 To UBound($aGridLines) - 1
                $aTemp = $aGridLines[$i]
                If $hWnd = GUICtrlGetHandle($aTemp[3]) Then
                    $LastID = $aTemp[3]
                    _ShowFormatMenu()
                    Return 0
                EndIf
            Next
    EndSwitch
    Return _WinAPI_CallWindowProc($OriginalWindowProc, $hWnd, $uiMsg, $wParam, $lParam)
EndFunc

Func _WinSubclass($hWnd, $lpNewWindowProc)
    Return _WinAPI_SetWindowLong($hWnd, $GWL_WNDPROC, $lpNewWindowProc)
EndFunc

Edit: Ini-File should look like this:

[SPS]
Name=IP;Rack;Slot
Edited by funkey

Programming today is a race between software engineers striving to
build bigger and better idiot-proof programs, and the Universe
trying to produce bigger and better idiots.
So far, the Universe is winning.

Link to comment
Share on other sites

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 account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...