Jump to content

Lightweight Notepad Clone (incomplete)


Go to solution Solved by MattyD,

Recommended Posts

Posted (edited)

I've been working on a lightweight Notepad clone off and on, but haven't had time to complete it. I really like the classic win32 Notepad but needed dark mode.

Please keep in mind that this is not complete. If anyone wants to add features and help carry the project forward, please feel free.

Working so far:

  • Open text files
  • Toggle between dark mode and light mode
  • Zoom In, Zoom Out, and Restore Default Zoom
  • Enable/Disable Word Wrap
  • Statusbar that actively shows the line number and column

I still have to figure out how to do a Save As dialog like Notepad has where you get to select the text encoding. There is a lot missing.

The problem is that I have other projects that are higher priority than this Notepad clone project. So I am not 100% sure if or when I will get a chance to finish it. But I am always to have anyone collaborate on it. :)

 

Notepad-2026.09.28b.zip

Notepad-2026.09.29.zip

Notepad-2026.09.30.zip

Edited by WildByDesign
Updated release source
Posted

Hope you don't mind, but I've added a few bits and bobs for fun. (take or leave it :))

#include <FileConstants.au3>
#include <FontConstants.au3>
#include <MsgBoxConstants.au3>
#include <WinAPIGdi.au3>
#include <WindowsStylesConstants.au3>
#include <GUIConstantsEx.au3>
#include <EditConstants.au3>
#include <GuiStatusBar.au3>
#include <WinAPI.au3>
#include <WindowsConstants.au3>
#include "GUIDarkTheme.au3"
#include "GuiEditEx.au3"


;Virtual key - the definition probably exists in a standard include library somewhere, but I'm too lazy to find it atm.
;Used to monitor control-wheel zooming.
Global Const $MK_CONTROL = 0x0008

; DPI awareness - nit-picky, but no magic numbers!
DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext", "int_ptr", $DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)

;Or technically this is a bit better because the DPI_AWARENESS_CONTEXT type is a pseudo-handle - not a signed int.
;~ Global Const $DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = Ptr(-4)
;~ DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext", "handle", $DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)

;{+} is the key on the top row, which you need "shift" to get to.
;So we should use "=" for zooming in here, or {NUMPADADD} for the other "plus" key.
HotKeySet("^=", "_ZoomIn")
HotKeySet("^{NUMPADADD}", "_ZoomIn")
HotKeySet("^-", "_ZoomOut")
HotKeySet("^{NUMPADSUB}", "_ZoomOut")
HotKeySet("^0", "RestoreZoom")

Global $g_iBaseFontSize = 11, $g_sSelFontFamily = "Consolas"

Global $g_hGUI, $g_idEdit, $g_hEdit, $g_hStatus, $g_idWordWrap
Global $g_hSubclassCallback = 0

Notepad()

Func Notepad()
    Local $aWinDims[2] = [760, 500]
    _ScaleVals($aWinDims)

    $g_hGUI = GUICreate("Notepad", $aWinDims[0], $aWinDims[1], -1, -1, $WS_OVERLAPPEDWINDOW)

    Local $idFileMenu = GUICtrlCreateMenu("&File")
    Local $idOpen = GUICtrlCreateMenuItem("&Open...", $idFileMenu)
    Local $idExit = GUICtrlCreateMenuItem("E&xit", $idFileMenu)
    Local $idEditMenu = GUICtrlCreateMenu("&Edit")
    Local $idFormatMenu = GUICtrlCreateMenu("&Format")
    Local $idViewMenu = GUICtrlCreateMenu("&View")
    Local $idHelpMenu = GUICtrlCreateMenu("&Help")

    $g_idWordWrap = GUICtrlCreateMenuItem('Word &Wrap', $idFormatMenu)
    GUICtrlSetState($g_idWordWrap, $GUI_CHECKED)

    Local $idZoomMenu = GUICtrlCreateMenu("&Zoom", $idViewMenu)
    Local $idZoomIn = GUICtrlCreateMenuItem('Zoom &In' & @TAB & "Ctrl + Plus", $idZoomMenu)
    Local $idZoomOut = GUICtrlCreateMenuItem('Zoom &Out' & @TAB & "Ctrl + Minus", $idZoomMenu)
    Local $idZoomRestore = GUICtrlCreateMenuItem('&Restore Default Zoom' & @TAB & "Ctrl + 0", $idZoomMenu)
    Local $idThemeChange = GUICtrlCreateMenuItem('Toggle &Theme', $idViewMenu)
    $g_hStatus = _GUICtrlStatusBar_Create($g_hGUI)

    ;Added $ES_WANTRETURN
    $g_idEdit = GUICtrlCreateEdit("", 0, 0, -1, -1, BitOR($ES_WANTRETURN, $ES_NOHIDESEL, $WS_VSCROLL))
    $g_hEdit = GUICtrlGetHandle(-1)
    _GuiCtrlEdit_SetIsZoomable($g_idEdit)
    _ReplaceFont($g_hEdit, 0, $g_sSelFontFamily, $g_iBaseFontSize)

    ; Subclass Edit control
    $g_hSubclassCallback = DllCallbackRegister("_EditSubclassProc", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
    _WinAPI_SetWindowSubclass($g_hEdit, DllCallbackGetPtr($g_hSubclassCallback), 0)

    ; Set status bar parts
    Local $aParts[] = [1,1,1,1,1] ;Lazy, let size handler fix the real size.
    _GUICtrlStatusBar_SetParts($g_hStatus, -1, $aParts)
    _GUICtrlStatusBar_SetText($g_hStatus, "", 0)
    UpdateLineAndColumn()
    UpdateStatusBarEOL()
    _GUICtrlStatusBar_SetText($g_hStatus, "UTF-8", 4)
    RestoreZoom() ;also sets status bar value.

    If $__DM_g_bUseDarkMode Then
        _GUIDarkTheme_ApplyDark($g_hGUI)
    Else
        _GUIDarkTheme_ApplyLight($g_hGUI)
    EndIf

    ; This tells GUIDarkTheme UDF not to draw border around Edit control or show accent color when control has focus
    _GUIDarkTheme_CtrlBorderSet(False, False)

    GUIRegisterMsg($WM_SIZE, "WM_SIZE")
    GUIRegisterMsg($WM_DPICHANGED, "WM_DPICHANGED")
    GUISetState()

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE, $idExit
                ExitLoop
            Case $idZoomIn
                _ZoomIn()
            Case $idZoomOut
                _ZoomOut()
            Case $idZoomRestore
                ;also using Ctrl+0 to restore zoom, so this has its own fn now
                RestoreZoom()
            Case $idThemeChange
                ;Redraw window in 1 go.
                _SendMessage($g_hGUI, $WM_SETREDRAW, False)
                _GUIDarkTheme_SwitchTheme($g_hGUI)
                _SendMessage($g_hGUI, $WM_SETREDRAW, True)
                _WinAPI_RedrawWindow($g_hGUI)

            Case $idOpen
                _OpenFile()

            Case $g_idWordWrap
                _WordWrap()
        EndSwitch
    WEnd
    GUIDelete()

    ; Clean up subclass before exiting ---- We're removing the subclass on WM_DESTROY, so we don't need to remove it here.
    DllCallbackFree($g_hSubclassCallback)
EndFunc   ;==>Notepad

Func _WordWrap()
    ;Probably overkill.. Copy everything from previous ctrl.
    ;  If we want to change pos later or something, we only need to update our code in 1 place.

    ;Position.
    Local $tWinRect = _WinAPI_GetWindowRect($g_hEdit)
    _WinAPI_MapWindowPoints(0, $g_hGUI, DllStructGetPtr($tWinRect), 2) ;convert coords, relative to parent.

    ;Styles.
    Local $iStyle = _WinAPI_GetWindowLong($g_hEdit, $GWL_STYLE)
    $iStyle = BitXOR($iStyle, $WS_HSCROLL) ;Flip $WS_HSCROLL bit
    Local $iWinExStyle = _WinAPI_GetWindowLong($g_hEdit, $GWL_EXSTYLE)

    ;Copy Font.
    Local $hFont = _GUICtrlEdit_GetFont($g_hEdit)
    Local $tLogFont = DllStructCreate($tagLOGFONT)
    _WinAPI_GetObject($hFont, DllStructGetSize($tLogFont), DllStructGetPtr($tLogFont))
    $hFont = _WinAPI_CreateFontIndirect($tLogFont)

    ;Copy Edit ext styles etc
    Local $iEdtExStyle = _SendMessage($g_hEdit, $EM_GETEXTENDEDSTYLE)
    Local $iZmNum, $iZmDen
    _GuiCtrlEdit_GetZoom($g_hEdit, $iZmNum, $iZmDen)
    Local $iEOL = _GuiCtrlEdit_GetEOL($g_hEdit)

    ;Text
    Local $sText = _GUICtrlEdit_GetText($g_hEdit)

    ;Created with CuiCtrlCreate, we should delete with GUICtrlDelete. (au3 may need to do an internal cleanup)
    GUICtrlDelete($g_idEdit)

    $g_idEdit = GUICtrlCreateEdit($sText, $tWinRect.Left, $tWinRect.Top, _
            ($tWinRect.Right - $tWinRect.Left), ($tWinRect.Bottom - $tWinRect.Top), _
            $iStyle, $iWinExStyle)

    $g_hEdit = GUICtrlGetHandle(-1)
    _WinAPI_SetWindowSubclass($g_hEdit, DllCallbackGetPtr($g_hSubclassCallback), 0)

    _GUICtrlEdit_SetFont($g_hEdit, $hFont, False)

    _SendMessage($g_hEdit, $EM_SETEXTENDEDSTYLE, $iEdtExStyle, $iEdtExStyle)
    _GuiCtrlEdit_SetZoom($g_hEdit, $iZmNum, $iZmDen)
    _GuiCtrlEdit_SetEOL($g_hEdit, $iEOL)

    Local $bEnableDarkTheme = $__DM_g_bUseDarkMode
    _GUIDarkTheme_GUICtrlSetDarkTheme($g_hGUI, $g_hEdit, $bEnableDarkTheme)

    ;Give feedback.
    $iStyle = _WinAPI_GetWindowLong($g_hEdit, $GWL_STYLE)
    GUICtrlSetState($g_idWordWrap, BitAND($iStyle, $WS_HSCROLL) ? $GUI_CHECKED : $GUI_UNCHECKED)
EndFunc   ;==>_WordWrap

Func _OpenFile()
    Local Const $sMessage = "Select a text file."
    ; Display an open dialog to select a list of file(s).
    Local $sFileOpenDialog = FileOpenDialog($sMessage, @DesktopDir, "All (*.*)|Text files (*.txt)", $FD_FILEMUSTEXIST)
    If @error Then
        ; Display the error message.
        MsgBox($MB_SYSTEMMODAL, "", "No file(s) were selected.")
    Else
        GUICtrlSetData($g_idEdit, "")
        GUICtrlSetData($g_idEdit, FileRead($sFileOpenDialog))
        DetectEOL()
    EndIf
EndFunc   ;==>_OpenFile

Func _ZoomIn()
    Local $iNum, $iDen
    _GUICtrlEdit_GetZoom($g_hEdit, $iNum, $iDen)

    ;converts to correct values even if orig denominator <> 10
    ;(a bit redundant as denominator shouldn't change from 10)

    $iNum = Int(10*($iNum / $iDen)) + 1
    $iDen = Int(10*($iDen / $iDen))

    _GuiCtrlEdit_SetZoom($g_idEdit, $iNum + 1, $iDen)
    UpdateStatusBarZoom()
EndFunc   ;==>_ZoomIn

Func _ZoomOut()
    Local $iNum, $iDen
    _GUICtrlEdit_GetZoom($g_hEdit, $iNum, $iDen)

    $iNum = 10*($iNum / $iDen) - 1
    $iDen = 10*($iDen / $iDen)

    _GuiCtrlEdit_SetZoom($g_idEdit, $iNum, $iDen)
    UpdateStatusBarZoom()
EndFunc   ;==>_ZoomOut

Func WM_DPICHANGED($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam
    _ReplaceFont($g_hEdit, _WinAPI_LoWord($wParam), $g_sSelFontFamily, $g_iBaseFontSize)
    Local $tRect = DllStructCreate($tagRECT, $lParam)
    _WinAPI_SetWindowPos($hWnd, 0, $tRect.Left, $tRect.Top, ($tRect.Right - $tRect.Left), ($tRect.Bottom - $tRect.Top), BitOR($SWP_NOACTIVATE, $SWP_NOZORDER))
EndFunc

Func WM_SIZE($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam

    If $hWnd = $g_hGUI Then
        Local $aSBarWidths[] = [0, 140, 50, 120, 120]
        _ScaleVals($aSBarWidths, $hWnd)

        Local $iW = _WinAPI_LoWord($lParam)
        Local $iH = _WinAPI_HiWord($lParam)

        $aSBarWidths[0] = $iW
        For $i = 1 To 4
            $aSBarWidths[0] -= $aSBarWidths[$i]
        Next
        _GUICtrlStatusBar_SetParts($g_hStatus, -1, $aSBarWidths)
        _GUICtrlStatusBar_Resize($g_hStatus)

        ;Override Au3 Docking behavior, so we can deal with dpi changes
        _WinAPI_MoveWindow($g_hEdit, 0, 0, $iW, $iH - _WinAPI_GetWindowHeight($g_hStatus))
        Return _WinAPI_DefWindowProcW($hWnd, $iMsg, $wParam, $lParam)
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>_WM_SIZE

Func UpdateLineAndColumn()
    ;TODO: Need to fix - line nums are increasing with word wrap.
    ; Line count should be based entirely on EOL char/s.

    Local $aSel = _GUICtrlEdit_GetSel($g_hEdit)
    Local $iCharPos = $aSel[0] ; $aSel[0] is the starting character index
    Local $iLine = _GUICtrlEdit_LineFromChar($g_hEdit, $iCharPos)
    Local $iLineStartIndex = _GUICtrlEdit_LineIndex($g_hEdit, $iLine)
    Local $iCol = $iCharPos - $iLineStartIndex

    _GUICtrlStatusBar_SetText($g_hStatus, "Ln " & ($iLine + 1) & ", Col " & ($iCol + 1), 1)
EndFunc   ;==>UpdateLineAndColumn

Func UpdateStatusBarZoom()
    Local $iNum, $iDen
    If _GUICtrlEdit_GetZoom($g_hEdit, $iNum, $iDen) Then
        _GUICtrlStatusBar_SetText($g_hStatus, StringFormat("%d%%", 100 * ($iNum / $iDen)), 2)
    EndIf
EndFunc   ;==>UpdateStatusBarZoom

Func DetectEOL()
    _GuiCtrlEdit_SetEOL($g_hEdit, $EC_ENDOFLINE_DETECTFROMCONTENT)
    UpdateStatusBarEOL()
EndFunc

Func UpdateStatusBarEOL()
    Local $sEOL
    Switch _GuiCtrlEdit_GetEOL($g_hEdit)
        Case 0, $EC_ENDOFLINE_CRLF
            $sEOL = "Windows (CRLF)"
        Case $EC_ENDOFLINE_CR
            $sEOL = "Macintosh (CR)"
        Case $EC_ENDOFLINE_LF
            $sEOL = "Unix (LF)"
    EndSwitch
    If $sEOL Then
        _GUICtrlStatusBar_SetText($g_hStatus, $sEOL, 3)
    EndIf
EndFunc

Func RestoreZoom()
    _GuiCtrlEdit_SetZoom($g_idEdit, 10, 10)
    UpdateStatusBarZoom()
EndFunc   ;==>RestoreZoom

Func _EditSubclassProc($hWnd, $uMsg, $wParam, $lParam, $uIdSubclass, $dwRefData)
    Switch $uMsg

        Case $WM_MOUSEWHEEL
            ;If Ctrl + Wheel, update zoom in status bar..
            If BitAND(_WinAPI_LoWord($wParam), $MK_CONTROL) Then
                ;Update zoom BEFORE getting new size.
                Local $iRet = _WinAPI_DefSubclassProc($hWnd, $uMsg, $wParam, $lParam)
                UpdateStatusBarZoom()
                Return $iRet
            EndIf

        Case $WM_LBUTTONUP
            UpdateLineAndColumn()

        Case $WM_KEYUP
            UpdateLineAndColumn()

        Case $WM_DESTROY
            _WinAPI_DeleteObject(_GUICtrlEdit_GetFont($g_hEdit))

            ;Same as what you had, but we may as well use the wrapped func.
            _WinAPI_RemoveWindowSubclass($hWnd, DllCallbackGetPtr($g_hSubclassCallback), $uIdSubclass)

    EndSwitch
    ; Pass message down to the default subclass handler chain
    Return _WinAPI_DefSubclassProc($hWnd, $uMsg, $wParam, $lParam)
EndFunc   ;==>_EditSubclassProc

Func _WinAPI_MapWindowPoints($hWndFom, $hWndTo, $pPoints, $iCount)
    Local $aCall = DllCall("User32.dll", "int", "MapWindowPoints", "hwnd", $hWndFom, "hwnd", $hWndTo, "ptr", $pPoints, "int", $iCount)
    If @error Then Return SetError(@error, @extended, $aCall)
    Return $aCall[0]
EndFunc   ;==>_WinAPI_MapWindowPoints

Func _WinAPI_GetDpiForSystem()
    Local $aCall = DllCall("User32.dll", "uint", "GetDpiForSystem")
    If @error Then Return SetError(@error, @extended, 0)
    Return $aCall[0]
EndFunc

Func _WinAPI_GetDpiForWindow($hWnd)
    Local $aCall = DllCall("User32.dll", "uint", "GetDpiForWindow", "hwnd", $hWnd)
    If @error Then Return SetError(@error, @extended, 0)
    Return $aCall[0]
EndFunc

Func _ScaleVals(ByRef $aValues, $hWnd = 0)
    Local $iDPI = _WinAPI_IsWindow($hWnd) ? _WinAPI_GetDpiForWindow($hWnd) : _WinAPI_GetDpiForSystem()
    If @error Then $iDPI = 96

    For $i = 0 To UBound($aValues)- 1
        $aValues[$i] = _WinAPI_MulDiv($aValues[$i], $iDPI, 96)
    Next
EndFunc

Func _ReplaceFont($hWnd, $iDPI, $sTypefaceName, $iSize, $iWeight = $FW_NORMAL, $bItalic = False, $bUnderline = False, $bStrikeOut = False)
    If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd)

    If $iDPI < 1 Then
        $iDPI = _WinAPI_IsWindow($hWnd) ? _WinAPI_GetDpiForWindow($hWnd) : _WinAPI_GetDpiForSystem()
        If @error Then $iDPI = 96
    EndIf
    Local $iHeight = - _WinAPI_MulDiv($iSize, $iDPI, 72)

    Local $hOldFont = _SendMessage($hWnd, $WM_GETFONT)
    Local $hNewFont = _WinAPI_CreateFont($iHeight, 0, 0, 0, $iWeight, $bItalic, $bUnderline, $bStrikeOut, _
            $DEFAULT_CHARSET, $OUT_DEFAULT_PRECIS, $CLIP_DEFAULT_PRECIS, $DEFAULT_QUALITY, BitOR($DEFAULT_PITCH, $FF_DONTCARE), $sTypefaceName)
    _SendMessage($hWnd, $WM_SETFONT, $hNewFont, 1)
    If $hOldFont Then _WinAPI_DeleteObject($hOldFont)
    Return $hNewFont
EndFunc

Func _GUICtrlEdit_SetFont($hWnd, $hFont, $bRedraw = True)
    If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd)
    If _WinAPI_IsClassName($hWnd, $WC_EDIT) Then Return _SendMessage($hWnd, $WM_SETFONT, $hFont, $bRedraw)
EndFunc

Func _GUICtrlEdit_GetFont($hWnd)
    If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd)
    If _WinAPI_IsClassName($hWnd, $WC_EDIT) Then Return _SendMessage($hWnd, $WM_GETFONT, 0, 0, 0, "wparam", "lparam", "handle")
EndFunc
Posted
6 hours ago, MattyD said:

Hope you don't mind, but I've added a few bits and bobs for fun. (take or leave it :))

Thank you so much for this well organized contribution. I will certainly accept all of it and update the first post with it, but I just want to clarify some things.

But first, I just wanted to mention that I really like the way that you've organized the code and the code comments and such. It has inspired me to continue doing some more work on this Notepad clone. I already know much of what I need to do (and how to do it), I'm just lacking time lately to implement some of what I want to do.

Case $idThemeChange
    ;Redraw window in 1 go.
    _SendMessage($g_hGUI, $WM_SETREDRAW, False)
    _GUIDarkTheme_SwitchTheme($g_hGUI)
    _SendMessage($g_hGUI, $WM_SETREDRAW, True)
    _WinAPI_RedrawWindow($g_hGUI)

I like your usage of WM_SETREDRAW here. The only problem that I have noticed is that WM_SETREDRAW seems to prevent the painting of the 1 pixel white line below the menu bar in dark mode.

As a temporary fix, this seems to correct it and the Sleep(10) seems to be really needed:

Case $idThemeChange
    ;Redraw window in 1 go.
    _SendMessage($g_hGUI, $WM_SETREDRAW, False)
    _GUIDarkTheme_SwitchTheme($g_hGUI)
    _SendMessage($g_hGUI, $WM_SETREDRAW, True)
    _WinAPI_RedrawWindow($g_hGUI)
    Sleep(10)
    If $__DM_g_bUseDarkMode Then __GUIDarkMenu_PaintWhiteLine($g_hGUI)

This reminds me, I have been curious about learning more about (and using) _WinAPI_PostMessage with a custom window message and handler. I've thought about this recently with WM_DPICHANGED because there are a couple of controls that prefer a delayed measurement due to internal caching of text sizes. That way, I assume, it would run the custom message handler when the OS is completely finished with the current message. That may be beneficial to this WM_SETREDRAW situation as well where the painting of the white line seems to need a slight delay.

I should also mention, my _GUIDarkTheme_SwitchTheme() function is also using _WinAPI_LockWindowUpdate() in its helper function __DM_ThemeCleanupHelper(). The helper function gets called twice in _GUIDarkTheme_SwitchTheme() for the two different cases representing stages.

From my understanding, using _WinAPI_LockWindowUpdate() is not the best method. But it does not seem to affect the painting of the white line. Your WM_SETREDRAW method seems to be the method that is recommended by MS and is therefore better to use.

Should I get rid of my usage of _WinAPI_LockWindowUpdate?

Posted (edited)

I'm using the @MattyD version as a base for this Notepad script now since it is structured much better. I've added updating the text encoding to the statusbar part whenever a file is opened.

Func _OpenFile()
    Local Const $sMessage = "Select a text file."
    ; Display an open dialog to select a list of file(s).
    Local $sFileOpenDialog = FileOpenDialog($sMessage, @DesktopDir, "All (*.*)|Text files (*.txt)", $FD_FILEMUSTEXIST)
    If @error Then
        ; Display the error message.
        MsgBox($MB_SYSTEMMODAL, "", "No file(s) were selected.")
    Else
        GUICtrlSetData($g_idEdit, "")
        GUICtrlSetData($g_idEdit, FileRead($sFileOpenDialog))
        DetectEOL()
        UpdateStatusBarEncoding($sFileOpenDialog) ; <<< added
    EndIf
EndFunc   ;==>_OpenFile

Func UpdateStatusBarEncoding($sFile)
    Local $sText
    Local $iEncoding = FileGetEncoding($sFile, $FE_PARTIALFIRST_UTF8)
    Switch $iEncoding
        Case $FO_UTF16_LE
            $sText = 'UTF-16 Little Endian'
        Case $FO_UTF16_BE
            $sText = 'UTF-16 Big Endian'
        Case $FO_UTF8
            $sText = 'UTF-8 (with BOM)'
        Case $FO_UTF8_NOBOM
            $sText = 'UTF-8 (without BOM)'
        Case $FO_ANSI
            $sText = 'ANSI'
    EndSwitch
    _GUICtrlStatusBar_SetText($g_hStatus, $sText, 4)
EndFunc   ;==>UpdateStatusBarEncoding

It might even be interesting to be able to change the text encoding as well. We could add that to the menu but I think there are much more higher priority things that need to be added first.

Edited by WildByDesign
Posted
3 hours ago, WildByDesign said:

I will certainly accept all of it and update the first post with it

You should pick through it though, I was moving pretty quickly so there's probably errors.. This line in particular is dumb, 10*1 is always 10!

$iDen = Int(10*($iDen / $iDen))

 

3 hours ago, WildByDesign said:
If $__DM_g_bUseDarkMode Then __GUIDarkMenu_PaintWhiteLine($g_hGUI)

Ah ok, If you draw the line during the windows paint cycle you shouldn't have to draw it everywhere ad hoc... 

In your main window's subclass, handling $WM_NCPAINT would probably do it.  Call DefSubclass first, then paint your line. 

3 hours ago, WildByDesign said:

That way, I assume, it would run the custom message handler when the OS is completely finished with the current message

Yep exactly. SendMessage bypasses the queue, its synchronous - the message is processed and returned immediately.  Postmessage adds the msg to the queue to be processed later.  Because the queue dispatches messages one at a time, any message you post within a handler cannot be executed until after you return.

3 hours ago, WildByDesign said:

Should I get rid of my usage of _WinAPI_LockWindowUpdate?

Yeah probably. You can only lock one window at a time, so there's that caveat too.

The documentation says:

The purpose of the LockWindowUpdate function is to permit drag/drop feedback to be drawn over a window without interference from the window itself. The intent is that the window is locked when feedback is drawn and unlocked when feedback is complete. LockWindowUpdate is not intended for general-purpose suppression of window redraw. Use the WM_SETREDRAW message to disable redrawing of a particular window.
Posted
10 hours ago, MattyD said:

You should pick through it though, I was moving pretty quickly so there's probably errors.. This line in particular is dumb, 10*1 is always 10!

One thing that I can't quite understand is that Zoom In goes up 20% each click, while Zoom Out goes down 10% at a time. I would like to match Notepad at 10% zoom in/out. I can't really figure out by looking at the code in _ZoomIn() and _ZoomOut() functions where that 20% is coming from,

Posted

Just another silly error!

;original
Func _ZoomIn()
    Local $iNum, $iDen
    _GUICtrlEdit_GetZoom($g_hEdit, $iNum, $iDen)

    ;converts to correct values even if orig denominator <> 10
    ;(maybe a bit OTT as den shouldn't really chage from 10)

    $iNum = Int(10*($iNum / $iDen)) + 1 ;incrementing here.
    $iDen = Int(10*($iDen / $iDen))

    _GuiCtrlEdit_SetZoom($g_idEdit, $iNum + 1, $iDen) ;also incrementing here.
    UpdateStatusBarZoom()
EndFunc

;a bit better...
Func _ZoomIn()
    Local $iNum, $iDen
    _GUICtrlEdit_GetZoom($g_hEdit, $iNum, $iDen)
    $iNum = Round(10*($iNum / $iDen)) + 1

    _GuiCtrlEdit_SetZoom($g_idEdit, $iNum, 10)
    UpdateStatusBarZoom()
EndFunc
Posted

I've updated the source archive in the first post.

Notepad 2026-09-28:

  • Using @MattyD's structural changes and improvements as a base script
  • Added UpdateStatusBarEncoding function to updating text encoding on status bar
  • Added Status Bar to View menu to show/hide status bar
  • Added _StatusChange function to handle everything needed to showing/hiding status bar
    • Sends WM_SIZE message to trigger Edit control sizing changes immediately
  • Updated WM_SIZE handler function to account for hidden Edit control sizing

 

As always, if anyone wants to contribute and add some bits and pieces, please feel free. That is always welcome. If we do end up with multiple people working on this, maybe we can leave a comment such as "I am going to work on such and such feature". That way we don't end up with two people working on the same thing. Anyway, that is just a suggestion. :)

Posted

I just wanted to give a quick heads up that I am working on text change modification tracking right now. Specifically, I am adding a WM_COMMAND handler to check the file anytime a change has been made to the text that is currently in the Edit control. If a change has been made, it adds the asterisk to the title bar to indicate that there are changes needed to be saved. If the change gets reverted to the original text, the asterisk is removed. I also added the currently opened file name to the title bar.

Somehow it's already better than MS implementation because they don't remove the asterisk when you revert the change. So our change tracking will be better in that regard.

Posted

Notepad 2026-09-28b:

  • Added change indicator to title bar when save changes are pending
  • Added Save and Save As to File menu

I still need to add a warning or save as dialog for when a user tries to close the app when there are pending save changes. Same needs to be done if there are pending changes to save and a user goes to Open a new file.

If anyone has the time and wants to check over my _SaveFile() and _SaveFileAs() functions that would be great. I tested it quite a bit but there are always scenarios that I don't think of and I tried to rush as much as I could today.

Posted (edited)
...
Func _SaveFileAs_NextName($sInitFolder)
    Local $iNext = 0, $sExt = '.txt', $sName = 'textFile'
    Local $sPath = _WinAPI_ShellGetKnownFolderPath($sInitFolder)
    If @error Or $sPath = "" Then $sPath = @DesktopDir
    If Not FileExists($sPath & '\' & $sName & $sExt) Then Return $sName & $sExt
    Do
        $iNext += 1
        If Not FileExists($sPath & '\' & $sName & ' (' & $iNext & ')' & $sExt) Then Return $sName & ' (' & $iNext & ')' & $sExt
    Until 0
EndFunc   ;==>_SaveFileAs_NextName

Func _SaveFileAs()
    Local $sFileSaveDialog, $hFile, $sDrive = "", $sDir = "", $sFileName = "", $sExtension = ""
    If Not $g_sOpenFile Then
        $sFileName = _SaveFileAs_NextName("::{450D8FBA-AD25-11D0-98A8-0800361B1103}")
        $sFileSaveDialog = FileSaveDialog("Save As", "::{450D8FBA-AD25-11D0-98A8-0800361B1103}", "Text Documents (*.txt)|All (*.*)", $FD_PATHMUSTEXIST + $FD_PROMPTOVERWRITE, $sFileName)
        If Not @error Then
...

..also, there is a " - Notepad". About "Notapad" :D 

Edit: "Nota" is Spanish ( or latin, or italian ) for note :) 
Edit 2: The Germanic for note is notu. So if the play in words are not welcome ( lack of humor ) then maybe the other 🤔

Edited by argumentum
clarify

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting  image.gif.922e3a93535f431de08b31ee669cc446.gif
autoit_scripter_blue_userbar.png

Posted

I wasted some time doing a fancy "about box" today, but it does require the new dialogs UDF that's just been uploaded.

Change the text to whatever you like, its just sample text. :) and it still needs dark-mode-ifying.

#NoTrayIcon
#AutoIt3Wrapper_Au3Check_Parameters=-q -d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7

#include <BorderConstants.au3>
#include <FileConstants.au3>
#include <FontConstants.au3>
#include <MsgBoxConstants.au3>
#include <WinAPIGdi.au3>
#include <WindowsStylesConstants.au3>
#include <GUIConstantsEx.au3>
#include <EditConstants.au3>
#include <GuiStatusBar.au3>
#include <WindowsConstants.au3>
#include <File.au3>
#include "GUIDarkTheme.au3"
#include "GuiEditEx.au3"
#include "Dialogs.au3"

Global $__g_NotepadVer = "2026.09.28b"

;Virtual key - the definition probably exists in a standard include library somewhere, but I'm too lazy to find it atm.
;Used to monitor control-wheel zooming.
Global Const $MK_CONTROL = 0x0008

; DPI awareness - nit-picky, but no magic numbers!
DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext", "int_ptr", $DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)

;~ Global Const $DWLP_MSGRESULT = 0 ;Currently required - will be included into Dialogs UDF for the next release..
Global $g_hAboutDlgProc = DllCallbackRegister("AboutDlgProc", "int_ptr", "hwnd;uint;wparam;lparam")

;Or technically this is a bit better because the DPI_AWARENESS_CONTEXT type is a pseudo-handle - not a signed int.
;~ Global Const $DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = Ptr(-4)
;~ DllCall("User32.dll", "bool", "SetProcessDpiAwarenessContext", "handle", $DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)

;{+} is the key on the top row, which you need "shift" to get to.
;So we should use "=" for zooming in here, or {NUMPADADD} for the other "plus" key.
HotKeySet("^=", "_ZoomIn")
HotKeySet("^{NUMPADADD}", "_ZoomIn")
HotKeySet("^-", "_ZoomOut")
HotKeySet("^{NUMPADSUB}", "_ZoomOut")
HotKeySet("^0", "RestoreZoom")

Global $g_iBaseFontSize = 11, $g_sSelFontFamily = "Consolas"

Global $g_hGUI, $g_idEdit, $g_hEdit, $g_hStatus, $g_idWordWrap, $g_sText = "", $g_sOpenFile = 0
Global $g_sFileName = "Untitled"
Global $g_hSubclassCallback = 0

Global $idFileMenu, $idSave, $idSaveAs

Notepad()

Func Notepad()

    _Dialog_Startup()

    Local $hAboutBoxTemplate = _Dialog_NewTemplate("About Dialog", 160, 80, -1, -1, BitOR($WS_CLIPCHILDREN, $WS_BORDER, $WS_POPUP, $WS_SYSMENU, $DS_SETFONT), 0)
    _Dialog_SetFont($hAboutBoxTemplate, "Segoe UI Symbol", 10)
    _Dialog_AddButton($hAboutBoxTemplate, $IDCLOSE, "", 160-15, 5, 10, 10, BitOR($BS_OWNERDRAW, $BS_DEFPUSHBUTTON))

    Local $aWinDims[2] = [760, 500]
    _ScaleVals($aWinDims)

    $g_hGUI = GUICreate($g_sFileName & " - Notepad", $aWinDims[0], $aWinDims[1], -1, -1, $WS_OVERLAPPEDWINDOW)

    $idFileMenu = GUICtrlCreateMenu("&File")
    Local $idOpen = GUICtrlCreateMenuItem("&Open...", $idFileMenu)
    $idSave = GUICtrlCreateMenuItem("&Save", $idFileMenu)
    GUICtrlSetState($idSave, $GUI_DISABLE)
    $idSaveAs = GUICtrlCreateMenuItem("Save &As...", $idFileMenu)
    GUICtrlCreateMenuItem("", $idFileMenu)
    Local $idExit = GUICtrlCreateMenuItem("E&xit", $idFileMenu)
    Local $idEditMenu = GUICtrlCreateMenu("&Edit")
    Local $idFormatMenu = GUICtrlCreateMenu("&Format")
    Local $idViewMenu = GUICtrlCreateMenu("&View")
    Local $idHelpMenu = GUICtrlCreateMenu("&Help")

    $g_idWordWrap = GUICtrlCreateMenuItem('Word &Wrap', $idFormatMenu)
    GUICtrlSetState($g_idWordWrap, $GUI_CHECKED)

    Local $idThemeChange = GUICtrlCreateMenuItem('Toggle &Theme', $idViewMenu)
    Local $idZoomMenu = GUICtrlCreateMenu("&Zoom", $idViewMenu)
    Local $idZoomIn = GUICtrlCreateMenuItem('Zoom &In' & @TAB & "Ctrl + Plus", $idZoomMenu)
    Local $idZoomOut = GUICtrlCreateMenuItem('Zoom &Out' & @TAB & "Ctrl + Minus", $idZoomMenu)
    Local $idZoomRestore = GUICtrlCreateMenuItem('&Restore Default Zoom' & @TAB & "Ctrl + 0", $idZoomMenu)
    Local $idStatusShow = GUICtrlCreateMenuItem("Status Bar", $idViewMenu)
    Local $idAbout = GUICtrlCreateMenuItem("&About", $idHelpMenu)

    Local $idCut = GUICtrlCreateMenuItem('Cu&t' & @TAB & "Ctrl + X", $idEditMenu)
    Local $idCopy = GUICtrlCreateMenuItem('&Copy' & @TAB & "Ctrl + C", $idEditMenu)
    Local $idPaste = GUICtrlCreateMenuItem('&Paste ' & @TAB & "Ctrl + V", $idEditMenu)

    GUICtrlSetState($idStatusShow, $GUI_CHECKED)

    $g_hStatus = _GUICtrlStatusBar_Create($g_hGUI)

    ;Added $ES_WANTRETURN
    $g_idEdit = GUICtrlCreateEdit("", 0, 0, -1, -1, BitOR($ES_WANTRETURN, $ES_NOHIDESEL, $WS_VSCROLL))
    $g_hEdit = GUICtrlGetHandle(-1)
    _GuiCtrlEdit_SetIsZoomable($g_idEdit)
    _ReplaceFont($g_hEdit, 0, $g_sSelFontFamily, $g_iBaseFontSize)

    ; Subclass Edit control
    $g_hSubclassCallback = DllCallbackRegister("_EditSubclassProc", "lresult", "hwnd;uint;wparam;lparam;uint_ptr;dword_ptr")
    _WinAPI_SetWindowSubclass($g_hEdit, DllCallbackGetPtr($g_hSubclassCallback), 0)

    ; Set status bar parts
    Local $aParts[] = [1,1,1,1,1] ;Lazy, let size handler fix the real size.
    _GUICtrlStatusBar_SetParts($g_hStatus, -1, $aParts)
    _GUICtrlStatusBar_SetText($g_hStatus, "", 0)
    UpdateLineAndColumn()
    UpdateStatusBarEOL()
    _GUICtrlStatusBar_SetText($g_hStatus, "UTF-8", 4)
    RestoreZoom() ;also sets status bar value.

    If $__DM_g_bUseDarkMode Then
        _GUIDarkTheme_ApplyDark($g_hGUI)
    Else
        _GUIDarkTheme_ApplyLight($g_hGUI)
    EndIf

    ; This tells GUIDarkTheme UDF not to draw border around Edit control or show accent color when control has focus
    _GUIDarkTheme_CtrlBorderSet(False, False)

    GUIRegisterMsg($WM_SIZE, "WM_SIZE")
    GUIRegisterMsg($WM_COMMAND, "WM_COMMAND")
    GUIRegisterMsg($WM_DPICHANGED, "WM_DPICHANGED")
    GUISetState()

    While 1
        Switch GUIGetMsg()
            Case $idAbout
                _Dialog_LaunchModal($hAboutBoxTemplate, $g_hGUI, 0, $g_hAboutDlgProc)
            Case $GUI_EVENT_CLOSE, $idExit
                ExitLoop
            Case $idZoomIn
                _ZoomIn()
            Case $idZoomOut
                _ZoomOut()
            Case $idZoomRestore
                ;also using Ctrl+0 to restore zoom, so this has its own fn now
                RestoreZoom()
            Case $idThemeChange
                _GUIDarkTheme_SwitchTheme($g_hGUI)
            Case $idOpen
                _OpenFile()
            Case $g_idWordWrap
                _WordWrap()
            Case $idStatusShow
                _StatusChange($idStatusShow)
            Case $idSave
                _SaveFile()
            Case $idSaveAs
                _SaveFileAs()
            Case $idCut
                _GuiCtrlEdit_Cut($g_hEdit)
            Case $idCopy
                _GuiCtrlEdit_Copy($g_hEdit)
            Case $idPaste
                _GuiCtrlEdit_Paste($g_hEdit)

        EndSwitch
    WEnd
    GUIDelete()

    _Dialog_Shutdown()

    ; Clean up subclass before exiting ---- We're removing the subclass on WM_DESTROY, so we don't need to remove it here.
    DllCallbackFree($g_hSubclassCallback)
EndFunc   ;==>Notepad

Func _WordWrap()
    ;Probably overkill.. Copy everything from previous ctrl.
    ;  If we want to change pos later or something, we only need to update our code in 1 place.

    ;Position.
    Local $tWinRect = _WinAPI_GetWindowRect($g_hEdit)
    _WinAPI_MapWindowPoints(0, $g_hGUI, DllStructGetPtr($tWinRect), 2) ;convert coords, relative to parent.

    ;Styles.
    Local $iStyle = _WinAPI_GetWindowLong($g_hEdit, $GWL_STYLE)
    $iStyle = BitXOR($iStyle, $WS_HSCROLL) ;Flip $WS_HSCROLL bit
    Local $iWinExStyle = _WinAPI_GetWindowLong($g_hEdit, $GWL_EXSTYLE)

    ;Copy Font.
    Local $hFont = _GUICtrlEdit_GetFont($g_hEdit)
    Local $tLogFont = DllStructCreate($tagLOGFONT)
    _WinAPI_GetObject($hFont, DllStructGetSize($tLogFont), DllStructGetPtr($tLogFont))
    $hFont = _WinAPI_CreateFontIndirect($tLogFont)

    ;Copy Edit ext styles etc
    Local $iEdtExStyle = _SendMessage($g_hEdit, $EM_GETEXTENDEDSTYLE)
    Local $iZmNum, $iZmDen
    _GuiCtrlEdit_GetZoom($g_hEdit, $iZmNum, $iZmDen)
    Local $iEOL = _GuiCtrlEdit_GetEOL($g_hEdit)

    ;Text
    Local $sText = _GUICtrlEdit_GetText($g_hEdit)

    ;Created with CuiCtrlCreate, we should delete with GUICtrlDelete. (au3 may need to do an internal cleanup)
    GUICtrlDelete($g_idEdit)

    $g_idEdit = GUICtrlCreateEdit($sText, $tWinRect.Left, $tWinRect.Top, _
            ($tWinRect.Right - $tWinRect.Left), ($tWinRect.Bottom - $tWinRect.Top), _
            $iStyle, $iWinExStyle)

    $g_hEdit = GUICtrlGetHandle(-1)
    _WinAPI_SetWindowSubclass($g_hEdit, DllCallbackGetPtr($g_hSubclassCallback), 0)

    _GUICtrlEdit_SetFont($g_hEdit, $hFont, False)

    _SendMessage($g_hEdit, $EM_SETEXTENDEDSTYLE, $iEdtExStyle, $iEdtExStyle)
    _GuiCtrlEdit_SetZoom($g_hEdit, $iZmNum, $iZmDen)
    _GuiCtrlEdit_SetEOL($g_hEdit, $iEOL)

    Local $bEnableDarkTheme = $__DM_g_bUseDarkMode
    _GUIDarkTheme_GUICtrlSetDarkTheme($g_hGUI, $g_hEdit, $bEnableDarkTheme)

    ;Give feedback.
    $iStyle = _WinAPI_GetWindowLong($g_hEdit, $GWL_STYLE)
    GUICtrlSetState($g_idWordWrap, BitAND($iStyle, $WS_HSCROLL) ? $GUI_CHECKED : $GUI_UNCHECKED)
EndFunc   ;==>_WordWrap

Func UpdateStatusBarEncoding($sFile)
    Local $sText
    Local $iEncoding = FileGetEncoding($sFile, $FE_PARTIALFIRST_UTF8)
    Switch $iEncoding
        Case $FO_UTF16_LE
            $sText = 'UTF-16 Little Endian'
        Case $FO_UTF16_BE
            $sText = 'UTF-16 Big Endian'
        Case $FO_UTF8
            $sText = 'UTF-8 (with BOM)'
        Case $FO_UTF8_NOBOM
            $sText = 'UTF-8 (without BOM)'
        Case $FO_ANSI
            $sText = 'ANSI'
    EndSwitch
    _GUICtrlStatusBar_SetText($g_hStatus, $sText, 4)
EndFunc   ;==>UpdateStatusBarEncoding

Func _ZoomIn()
    Local $iNum, $iDen
    _GUICtrlEdit_GetZoom($g_hEdit, $iNum, $iDen)
    $iNum = Round(10*($iNum / $iDen)) + 1

    _GuiCtrlEdit_SetZoom($g_idEdit, $iNum, 10)
    UpdateStatusBarZoom()
EndFunc   ;==>_ZoomIn

Func _ZoomOut()
    Local $iNum, $iDen
    _GUICtrlEdit_GetZoom($g_hEdit, $iNum, $iDen)

    $iNum = 10*($iNum / $iDen) - 1
    $iDen = 10*($iDen / $iDen)

    _GuiCtrlEdit_SetZoom($g_idEdit, $iNum, $iDen)
    UpdateStatusBarZoom()
EndFunc   ;==>_ZoomOut

Func WM_DPICHANGED($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam
    _ReplaceFont($g_hEdit, _WinAPI_LoWord($wParam), $g_sSelFontFamily, $g_iBaseFontSize)
    Local $tRect = DllStructCreate($tagRECT, $lParam)
    _WinAPI_SetWindowPos($hWnd, 0, $tRect.Left, $tRect.Top, ($tRect.Right - $tRect.Left), ($tRect.Bottom - $tRect.Top), BitOR($SWP_NOACTIVATE, $SWP_NOZORDER))
EndFunc

Func WM_SIZE($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg, $wParam, $lParam

    If $hWnd = $g_hGUI Then
        Local $aSBarWidths[] = [0, 140, 50, 120, 140]
        _ScaleVals($aSBarWidths, $hWnd)

        Local $iW = _WinAPI_LoWord($lParam)
        Local $iH = _WinAPI_HiWord($lParam)

        $aSBarWidths[0] = $iW
        For $i = 1 To 4
            $aSBarWidths[0] -= $aSBarWidths[$i]
        Next
        _GUICtrlStatusBar_SetParts($g_hStatus, -1, $aSBarWidths)
        _GUICtrlStatusBar_Resize($g_hStatus)

        ;Override Au3 Docking behavior, so we can deal with dpi changes
        Local $bStatusShow = _WinAPI_IsWindowVisible($g_hStatus)
        _WinAPI_MoveWindow($g_hEdit, 0, 0, $iW, $iH - ($bStatusShow ? _WinAPI_GetWindowHeight($g_hStatus) : 0))

        Return _WinAPI_DefWindowProcW($hWnd, $iMsg, $wParam, $lParam)
    EndIf
    Return $GUI_RUNDEFMSG
EndFunc   ;==>_WM_SIZE

Func UpdateLineAndColumn()
    ;TODO: Need to fix - line nums are increasing with word wrap.
    ; Line count should be based entirely on EOL char/s.

    Local $aSel = _GUICtrlEdit_GetSel($g_hEdit)
    Local $iCharPos = $aSel[0] ; $aSel[0] is the starting character index
    Local $iLine = _GUICtrlEdit_LineFromChar($g_hEdit, $iCharPos)
    Local $iLineStartIndex = _GUICtrlEdit_LineIndex($g_hEdit, $iLine)
    Local $iCol = $iCharPos - $iLineStartIndex

    _GUICtrlStatusBar_SetText($g_hStatus, "Ln " & ($iLine + 1) & ", Col " & ($iCol + 1), 1)
EndFunc   ;==>UpdateLineAndColumn

Func UpdateStatusBarZoom()
    Local $iNum, $iDen
    If _GUICtrlEdit_GetZoom($g_hEdit, $iNum, $iDen) Then
        _GUICtrlStatusBar_SetText($g_hStatus, StringFormat("%d%%", 100 * ($iNum / $iDen)), 2)
    EndIf
EndFunc   ;==>UpdateStatusBarZoom

Func DetectEOL()
    _GuiCtrlEdit_SetEOL($g_hEdit, $EC_ENDOFLINE_DETECTFROMCONTENT)
    UpdateStatusBarEOL()
EndFunc

Func UpdateStatusBarEOL()
    Local $sEOL
    Switch _GuiCtrlEdit_GetEOL($g_hEdit)
        Case 0, $EC_ENDOFLINE_CRLF
            $sEOL = "Windows (CRLF)"
        Case $EC_ENDOFLINE_CR
            $sEOL = "Macintosh (CR)"
        Case $EC_ENDOFLINE_LF
            $sEOL = "Unix (LF)"
    EndSwitch
    If $sEOL Then
        _GUICtrlStatusBar_SetText($g_hStatus, $sEOL, 3)
    EndIf
EndFunc

Func RestoreZoom()
    _GuiCtrlEdit_SetZoom($g_idEdit, 10, 10)
    UpdateStatusBarZoom()
EndFunc   ;==>RestoreZoom

Func _EditSubclassProc($hWnd, $uMsg, $wParam, $lParam, $uIdSubclass, $dwRefData)
    #forceref $dwRefData
    Switch $uMsg

        Case $WM_MOUSEWHEEL
            ;If Ctrl + Wheel, update zoom in status bar..
            If BitAND(_WinAPI_LoWord($wParam), $MK_CONTROL) Then
                ;Update zoom BEFORE getting new size.
                Local $iRet = _WinAPI_DefSubclassProc($hWnd, $uMsg, $wParam, $lParam)
                UpdateStatusBarZoom()
                Return $iRet
            EndIf

        Case $WM_LBUTTONUP
            UpdateLineAndColumn()

        Case $WM_KEYUP
            UpdateLineAndColumn()

        Case $WM_DESTROY
            _WinAPI_DeleteObject(_GUICtrlEdit_GetFont($g_hEdit))

            ;Same as what you had, but we may as well use the wrapped func.
            _WinAPI_RemoveWindowSubclass($hWnd, DllCallbackGetPtr($g_hSubclassCallback), $uIdSubclass)

    EndSwitch
    ; Pass message down to the default subclass handler chain
    Return _WinAPI_DefSubclassProc($hWnd, $uMsg, $wParam, $lParam)
EndFunc   ;==>_EditSubclassProc

Func _WinAPI_MapWindowPoints($hWndFom, $hWndTo, $pPoints, $iCount)
    Local $aCall = DllCall("User32.dll", "int", "MapWindowPoints", "hwnd", $hWndFom, "hwnd", $hWndTo, "ptr", $pPoints, "int", $iCount)
    If @error Then Return SetError(@error, @extended, $aCall)
    Return $aCall[0]
EndFunc   ;==>_WinAPI_MapWindowPoints

Func _WinAPI_GetDpiForSystem()
    Local $aCall = DllCall("User32.dll", "uint", "GetDpiForSystem")
    If @error Then Return SetError(@error, @extended, 0)
    Return $aCall[0]
EndFunc

Func _WinAPI_GetDpiForWindow($hWnd)
    Local $aCall = DllCall("User32.dll", "uint", "GetDpiForWindow", "hwnd", $hWnd)
    If @error Then Return SetError(@error, @extended, 0)
    Return $aCall[0]
EndFunc

Func _ScaleVals(ByRef $aValues, $hWnd = 0)
    Local $iDPI = _WinAPI_IsWindow($hWnd) ? _WinAPI_GetDpiForWindow($hWnd) : _WinAPI_GetDpiForSystem()
    If @error Then $iDPI = 96

    For $i = 0 To UBound($aValues)- 1
        $aValues[$i] = _WinAPI_MulDiv($aValues[$i], $iDPI, 96)
    Next
EndFunc

Func _StatusChange($idStatusShow)
    Local $bStatusShow
    If $g_hStatus Then
        $bStatusShow = _WinAPI_IsWindowVisible($g_hStatus)
        Switch $bStatusShow
            Case 1
                _GUICtrlStatusBar_ShowHide($g_hStatus, @SW_HIDE)
                GUICtrlSetState($idStatusShow, $GUI_UNCHECKED)
            Case 0
                _GUICtrlStatusBar_ShowHide($g_hStatus, @SW_SHOW)
                GUICtrlSetState($idStatusShow, $GUI_CHECKED)
        EndSwitch

        ; Get current client area width and height
        Local $aClientSize = WinGetClientSize($g_hGUI)

        Local $iWidth = $aClientSize[0]
        Local $iHeight = $aClientSize[1]

        ; Combine width and height into LPARAM using _WinAPI_MakeLong
        Local $lParam = _WinAPI_MakeLong($iWidth, $iHeight)

        ; Trigger WM_SIZE so that Edit box resizes to changed status bar
        _SendMessage($g_hGUI, $WM_SIZE, 0, $lParam)
    EndIf
EndFunc

Func _ReplaceFont($hWnd, $iDPI, $sTypefaceName, $iSize, $iWeight = $FW_NORMAL, $bItalic = False, $bUnderline = False, $bStrikeOut = False)
    If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd)

    If $iDPI < 1 Then
        $iDPI = _WinAPI_IsWindow($hWnd) ? _WinAPI_GetDpiForWindow($hWnd) : _WinAPI_GetDpiForSystem()
        If @error Then $iDPI = 96
    EndIf
    Local $iHeight = - _WinAPI_MulDiv($iSize, $iDPI, 72)

    Local $hOldFont = _SendMessage($hWnd, $WM_GETFONT)
    Local $hNewFont = _WinAPI_CreateFont($iHeight, 0, 0, 0, $iWeight, $bItalic, $bUnderline, $bStrikeOut, _
            $DEFAULT_CHARSET, $OUT_DEFAULT_PRECIS, $CLIP_DEFAULT_PRECIS, $DEFAULT_QUALITY, BitOR($DEFAULT_PITCH, $FF_DONTCARE), $sTypefaceName)
    _SendMessage($hWnd, $WM_SETFONT, $hNewFont, 1)
    If $hOldFont Then _WinAPI_DeleteObject($hOldFont)
    Return $hNewFont
EndFunc

Func _GUICtrlEdit_SetFont($hWnd, $hFont, $bRedraw = True)
    If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd)
    If _WinAPI_IsClassName($hWnd, $WC_EDIT) Then Return _SendMessage($hWnd, $WM_SETFONT, $hFont, $bRedraw)
EndFunc

Func _GUICtrlEdit_GetFont($hWnd)
    If Not IsHWnd($hWnd) Then $hWnd = GUICtrlGetHandle($hWnd)
    If _WinAPI_IsClassName($hWnd, $WC_EDIT) Then Return _SendMessage($hWnd, $WM_GETFONT, 0, 0, 0, "wparam", "lparam", "handle")
EndFunc

Func _Notepad_Version()
    Return $__g_NotepadVer
EndFunc   ;==>_Notepad_Version

Func _OpenFile()
    Local Const $sMessage = "Select a text file."
    ; Display an open dialog to select a list of file(s).
    Local $sFileOpenDialog = FileOpenDialog($sMessage, @DesktopDir, "Text Documents (*.txt)|All (*.*)", $FD_FILEMUSTEXIST)
    If Not @error Then
        ; Clear previous text and Edit control
        $g_sText = ""
        GUICtrlSetData($g_idEdit, $g_sText)
        $g_sText = FileRead($sFileOpenDialog)
        $g_sOpenFile = $sFileOpenDialog
        GUICtrlSetData($g_idEdit, $g_sText)
        DetectEOL()
        UpdateStatusBarEncoding($sFileOpenDialog)
        Local $sDrive = "", $sDir = "", $sFileName = "", $sExtension = ""
        _PathSplit($sFileOpenDialog, $sDrive, $sDir, $sFileName, $sExtension)
        $g_sFileName = $sFileName & $sExtension
        WinSetTitle($g_hGUI, "", $g_sFileName & " - Notepad")
    EndIf
EndFunc   ;==>_OpenFile

Func _SaveFile()
    Local $hFile
    If Not $g_sOpenFile Then
        Local $sFileSaveDialog = FileSaveDialog("Save As", "::{450D8FBA-AD25-11D0-98A8-0800361B1103}", "Text Documents (*.txt)|All (*.*)", $FD_PATHMUSTEXIST + $FD_PROMPTOVERWRITE, "*.txt")
        If Not @error Then
            $hFile = FileOpen($sFileSaveDialog, $FO_OVERWRITE + $FO_UTF8)
            FileWrite($hFile, GUICtrlRead($g_idEdit))
            FileClose($hFile)
            $g_sOpenFile = $sFileSaveDialog
            Local $sDrive = "", $sDir = "", $sFileName = "", $sExtension = ""
            _PathSplit($sFileSaveDialog, $sDrive, $sDir, $sFileName, $sExtension)
            $g_sFileName = $sFileName & $sExtension
            WinSetTitle($g_hGUI, "", $g_sFileName & " - Notepad")
            GUICtrlSetState($idSave, $GUI_DISABLE)
            $g_sText = GUICtrlRead($g_idEdit)
        EndIf
    Else
        Local $iEncoding = FileGetEncoding($g_sOpenFile)
        If $iEncoding = -1 Then $iEncoding = 128
        ;FileMove($g_sOpenFile, $g_sOpenFile & "." & TimerInit()) ; backup ?
        $hFile = FileOpen($g_sOpenFile, $FO_OVERWRITE + $iEncoding)
        FileWrite($hFile, GUICtrlRead($g_idEdit))
        FileClose($hFile)
        WinSetTitle($g_hGUI, "", $g_sFileName & " - Notepad")
        GUICtrlSetState($idSave, $GUI_DISABLE)
        $g_sText = GUICtrlRead($g_idEdit)
    EndIf
EndFunc   ;==>_SaveFile

Func _SaveFileAs()
    Local $sFileSaveDialog, $hFile, $sDrive = "", $sDir = "", $sFileName = "", $sExtension = ""
    If Not $g_sOpenFile Then
        $sFileSaveDialog = FileSaveDialog("Save As", "::{450D8FBA-AD25-11D0-98A8-0800361B1103}", "Text Documents (*.txt)|All (*.*)", $FD_PATHMUSTEXIST + $FD_PROMPTOVERWRITE, "*.txt")
        If Not @error Then
            $hFile = FileOpen($sFileSaveDialog, $FO_OVERWRITE + $FO_UTF8)
            FileWrite($hFile, GUICtrlRead($g_idEdit))
            FileClose($hFile)
            $g_sOpenFile = $sFileSaveDialog
            _PathSplit($sFileSaveDialog, $sDrive, $sDir, $sFileName, $sExtension)
            $g_sFileName = $sFileName & $sExtension
            WinSetTitle($g_hGUI, "", $g_sFileName & " - Notepad")
            GUICtrlSetState($idSave, $GUI_DISABLE)
            $g_sText = GUICtrlRead($g_idEdit)
        EndIf
    Else
        Local $iEncoding = FileGetEncoding($g_sOpenFile)
        If $iEncoding = -1 Then $iEncoding = 128

        $sFileSaveDialog = FileSaveDialog("Save As", "::{450D8FBA-AD25-11D0-98A8-0800361B1103}", "Text Documents (*.txt)|All (*.*)", $FD_PATHMUSTEXIST + $FD_PROMPTOVERWRITE, $g_sFileName)
        If Not @error Then
            $hFile = FileOpen($sFileSaveDialog, $FO_OVERWRITE + $iEncoding)
            FileWrite($hFile, GUICtrlRead($g_idEdit))
            FileClose($hFile)
            $g_sOpenFile = $sFileSaveDialog
            _PathSplit($sFileSaveDialog, $sDrive, $sDir, $sFileName, $sExtension)
            $g_sFileName = $sFileName & $sExtension
            WinSetTitle($g_hGUI, "", $g_sFileName & " - Notepad")
            GUICtrlSetState($idSave, $GUI_DISABLE)
            $g_sText = GUICtrlRead($g_idEdit)
        EndIf
    EndIf
EndFunc   ;==>_SaveFileAs

Func WM_COMMAND($hWnd, $iMsg, $wParam, $lParam)
    #forceref $hWnd, $iMsg
    Local $iCode = BitShift($wParam, 16)
    Switch $lParam
        Case $g_hEdit
            Switch $iCode
                Case $EN_CHANGE
                    If GUICtrlRead($g_idEdit) <> $g_sText Then
                        WinSetTitle($hWnd, "", "*" & $g_sFileName & " - Notepad")
                        GUICtrlSetState($idSave, $GUI_ENABLE)
                        ; TODO: we will need to add warning/save dialog if user tries to close app or open new file
                    Else
                        WinSetTitle($hWnd, "", "Untitled - Notepad")
                        WinSetTitle($hWnd, "", $g_sFileName & " - Notepad")
                        GUICtrlSetState($idSave, $GUI_DISABLE)
                    EndIf
            EndSwitch
    EndSwitch

    Return $GUI_RUNDEFMSG
EndFunc  ;==>WM_COMMAND


Func AboutDlgProc($hWnd, $iMsg, $wParam, $lParam)
    Local Const $ID_SCROLL_TIMER = 101
    Local Const $aText = [ _
        "Notapad (Or Whatever)", _
        "Version " & $__g_NotepadVer, _
        "", _
        "Developed by:", _
        "The AutoIt Community", _
        "", _
        "UI Homage to:", _
        "Some Microsoft Developer!", _
        "", _
        StringFormat("(Sample) Copyleft %s 2026", ChrW(0xD83C) & ChrW(0xDD2F))]

    Local Static $iScrollY = 0
    Local Static $iRefreshInterval = 50
    Local Static $iTotalTextHeight = 0
    Local Static $iLineHeight = 20
    Local Static $hTitleFont

    ;Vars used with different msgs. just declared up here for now to shut au3check up!
    Local $hDC, $tPos

    Switch $iMsg
        Case $WM_INITDIALOG
            ;Position: Horz Centre, Vert Top 3rd
            $tPos = _WinAPI_GetWindowRect($hWnd)
            Local $hOwner = _WinAPI_GetWindow($hWnd, $GW_OWNER)
            Local $tOwnerPos = _WinAPI_GetWindowRect($hOwner)
            Local $tPoint = DllStructCreate($tagPOINT)
            $tPoint.X = (($tOwnerPos.Right - $tOwnerPos.Left) - ($tPos.Right - $tPos.Left))/2
            $tPoint.Y = (($tOwnerPos.Bottom - $tOwnerPos.Top) - ($tPos.Bottom - $tPos.Top))/3
            _WinAPI_MapWindowPoints($hOwner, 0, DllStructGetPtr($tPoint), 1)
            _WinAPI_SetWindowPos($hWnd, 0, $tPoint.X, $tPoint.Y, 0, 0, BitOR($SWP_NOSIZE, $SWP_NOZORDER, $SWP_NOACTIVATE))

            $iTotalTextHeight = UBound($aText) * $iLineHeight
            $iScrollY = $tPos.Bottom - $tPos.Top
            _WinAPI_SetTimer($hWnd, $ID_SCROLL_TIMER, $iRefreshInterval, 0)

            ;Copy the font to use for the first line, make it bold.
            Local $hDlgFont = _SendMessage($hWnd, $WM_GETFONT, 0, 0, 0, "wparam", "lparam", "handle")
            Local $tDlgLogFont = DllStructCreate($tagLOGFONT)
            _WinAPI_GetObject($hDlgFont, DllStructGetSize($tDlgLogFont), DllStructGetPtr($tDlgLogFont))
            $tDlgLogFont.Weight = $FW_BOLD
            $hTitleFont = _WinAPI_CreateFontIndirect($tDlgLogFont)

            Return True

        Case $WM_NCHITTEST
            Local $iHit = _WinAPI_DefWindowProcW($hWnd, $iMsg, $wParam, $lParam)
            _WinAPI_SetWindowLong($hWnd, $DWLP_MSGRESULT, ($iHit = $HTCLIENT) ? $HTCAPTION : $iHit)
            Return True

        Case $WM_TIMER
            If $wParam = $ID_SCROLL_TIMER Then
                $iScrollY -= 1
                If $iScrollY < (0 - $iTotalTextHeight) Then
                    $tPos = _WinAPI_GetWindowRect($hWnd)
                    $iScrollY = $tPos.Bottom - $tPos.Top
                EndIf
                _WinAPI_InvalidateRect($hWnd)
            EndIf

        Case $WM_DRAWITEM
            Local Const $tagDRAWITEMSTRUCT = "struct;uint CtlType;uint CtlID;uint itemID;uint itemAction;uint itemState;" & _
                    "hwnd hwndItem;ptr hDC;long Left;long Top;long Right;long Bottom;ulong_ptr itemData;endstruct"
            Local $tDrawItem = DllStructCreate($tagDRAWITEMSTRUCT, $lParam)
            $hDC = $tDrawItem.hDC
            Local $tRect = DllStructCreate($tagRect, DllStructGetPtr($tDrawItem, "Left"))

            If $tDrawItem.CtlID = $IDCLOSE Then
                _WinAPI_DrawEdge($hDC, $tRect, $EDGE_ETCHED, BitOR( $BF_RECT, $BF_FLAT))
                _WinAPI_InflateRect($tRect, -1, -1)
                _WinAPI_DrawText($hDC, "X", $tRect, BitOR($DT_CENTER, $DT_VCENTER))
            EndIf

            Return True

        Case $WM_ERASEBKGND
            Return True

        Case $WM_PAINT
            Local $tPS = DllStructCreate($tagPAINTSTRUCT)
            $hDC = _WinAPI_BeginPaint($hWnd, $tPS)

            ;Double Buffer.  paint to me and swap out.
            Local $tClRect = _WinAPI_GetClientRect($hWnd)
            Local $hMemDC = _WinAPI_CreateCompatibleDC($hDC)
            Local $hMemBMP = _WinAPI_CreateCompatibleBitmap($hDC, $tClRect.Right, $tClRect.Bottom)
            Local $hOldBmp = _WinAPI_SelectObject($hMemDC, $hMemBMP) ;old bmp = stock obj, 1x1 monochrome bmp.

            Local $hBKBrush = _WinAPI_GetSysColorBrush($COLOR_3DFACE)
            _WinAPI_FillRect($hMemDC, $tClRect, $hBKBrush)
            _WinAPI_SetBkMode($hMemDC, $TRANSPARENT)
            _WinAPI_SetTextColor($hMemDC, 0)
            Local $hFont = _SendMessage($hWnd, $WM_GETFONT, 0, 0, 0, "wparam", "lparam", "handle")
            _WinAPI_SelectObject($hMemDC, $hTitleFont)

            Local $tTxtRect = DllStructCreate($tagRect)
            Local $iCurY = $iScrollY
            For $i = 0 To UBound($aText) - 1
                If $i = 1 Then _WinAPI_SelectObject($hMemDC, $hFont)
                If ($iCurY + 20 > 0) And ($iCurY < $tClRect.Bottom) Then
                    $tTxtRect.Left = 0
                    $tTxtRect.Top = $iCurY
                    $tTxtRect.Right = $tClRect.Right
                    $tTxtRect.Bottom = $iCurY + $iLineHeight
                    _WinAPI_DrawText($hMemDC, $aText[$i], $tTxtRect, BitOR($DT_CENTER, $DT_NOPREFIX, $DT_NOCLIP))
                EndIf
                $iCurY += $iLineHeight
            Next

            _WinAPI_BitBlt($hDC, 0, 0, $tClRect.Right, $tClRect.Bottom, $hMemDC, 0, 0, $SRCCOPY)

            ;Put 1x1 bmp back so we can destroy real bmp buffer.
            ;(it needs to be released from dc)
            _WinAPI_SelectObject($hMemDC, $hOldBmp)
            _WinAPI_DeleteObject($hMemBMP)
            _WinAPI_DeleteDC($hMemDC)
            _WinAPI_EndPaint($hWnd, $tPS)

            Return True

        Case $WM_COMMAND
            Local $iNotifCode = _WinAPI_HiWord($wParam)
            Local $iItemId = _WinAPI_LoWord($wParam)

            If (Not $lParam) Or ($iNotifCode = $BN_CLICKED) Then
                Return _Dialog_EndDialog($hWnd, $iItemId)
            EndIf

        Case $WM_DESTROY
            _WinAPI_KillTimer($hWnd, $ID_SCROLL_TIMER)
            _WinAPI_DeleteObject($hTitleFont)
            Return True

    EndSwitch

    Return False
EndFunc

 

Posted (edited)

Notepad 2026-09-29:

  • Added _SaveFileAs_NextName function and _SaveFileAs changes as suggested by @argumentum
  • Added custom About dialog and other changes from @MattyD
  • Added Font... option in Format menu
    • This allows changing font family, size, colour, strikeout, underline, etc.
    • All of these change properly when DPI scaling is changed
  • Added Undo (remember that Undo is single-level undo in Edit controls)
  • Added HotKeys for Open, Save, and Save As
  • Fixed a dialog subclass crash in GUIDarkTheme UDF
    • This was affecting the ChooseFont dialog only
Edited by WildByDesign
Posted

@MattyD Do you have any idea how Microsoft adds the Encoding combobox to the Save As dialog in the classic win32 notepad.exe?

If it's too difficult to achieve getting an Encoding combo in the Save As dialog, possibly we can add an Encoding sub-menu in the menu bar to change encoding if anyone needs it.

Posted

I need to read upon it properly, but from a skim I'm guessing its this: 

https://learn.microsoft.com/en-us/windows/win32/api/commdlg/ns-commdlg-openfilenamew

If OFN_EXPLORER is set .... The hook procedure also receives messages for any additional controls that you defined by specifying a child dialog template.

If we inspect c:\windows\system32\en-us\notepad.exe.mui, we find something that I'm guessing is that "child dialog template".

15 DIALOGEX 30, 17, 300, 22
STYLE DS_SETFONT | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS
CAPTION ""
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
FONT 8, "MS Shell Dlg"
{
   CONTROL "&Encoding:", 259, STATIC, SS_LEFT | WS_CHILD | WS_VISIBLE, 68, 1, 40, 40 
   CONTROL "", 257, COMBOBOX, CBS_DROPDOWNLIST | WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_GROUP | WS_TABSTOP, 130, 0, 164, 100 
}
Posted

Notepad 2026-09-30:

  • Removed the border from statusbar with $SBT_NOBORDERS
  • Added Select All to Edit menu
  • Added a simple app icon for now from font glyph (until there is a better icon)
    • App icon scales with DPI changes and changes color with theme changes
  • Added app name variable $__g_AppName so that we can easily change the Notepad name
  • Fixed an issue in GUIDarkTheme UDF that was not showing the prefix underline in menu bar when Alt pressed
Posted (edited)
On 9/29/2026 at 10:41 PM, WildByDesign said:

Do you have any idea how Microsoft adds the Encoding combobox to the Save As dialog in the classic win32 notepad.exe?

Ok, so this kinda works, we can add the extra combo.  The OFN_EXPLORER flag forces us to use an old fileopen dialog template though, and that's broken with modern versions of explorer (example updated).  The method is probably good for Windows XP though :P

#include <WinAPI.au3>
#include <GuiConstants.au3>
#include <Dialogs.au3>

Global $g_hFOHookProc = DllCallbackRegister("FileOpenHook", "uint_ptr", "hwnd;uint;wparam;lparam")
Global $g_sDialogDll = "Dialog.dll"

CreateDialog()
DoFileOpen(0)
FileDelete($g_sDialogDll)

Func CreateDialog()

    Local $sResName = "DLG_NESTED"

    FileDelete($g_sDialogDll)

    _Dialog_Startup()
    _Dialog_CreateDll($g_sDialogDll)

    Local $hTemplate = _Dialog_NewTemplate("", 300, 22, 30, 17, BitOR($DS_SETFONT, $DS_CONTROL, $WS_CHILD, $WS_CLIPSIBLINGS), 0)
    _Dialog_SetFont($hTemplate, "MS Shell Dlg", 8)
    _Dialog_AddStatic($hTemplate, 259, "&Encoding", 68, 1, 40, 40, BitOR($SS_LEFT, $WS_CHILD, $WS_VISIBLE))
    _Dialog_AddComboBox($hTemplate, 257, 130, 0, 164, 100, BitOR($CBS_DROPDOWNLIST, $WS_CHILD, $WS_VISIBLE, $WS_VSCROLL, $WS_GROUP, $WS_TABSTOP))

    _Dialog_AddToResource($hTemplate, $sResName, $g_sDialogDll)

    _Dialog_Shutdown()
EndFunc


Func DoFileOpen($hParent)


    Local $g_sDialogDll = "Dialog.dll"
    Local $hModule = _WinAPI_LoadLibrary($g_sDialogDll)

    Local $aFilter[][] = [["Text Files", "*.txt"], ["All Files", "*.*"]]
    Local $sCustomFilter = "Custom Filter"

    Local $tFilter = DllStructCreate("byte[256]"), $tFilterEntry
    Local $pFilter = DllStructGetPtr($tFilter)
    Local $iEntry, $iNumDescChars, $iNumPattChars
    For $i = 0 To UBound($aFilter) - 1
        $iNumDescChars = StringLen($aFilter[$i][0]) + 1
        $iNumPattChars = StringLen($aFilter[$i][1]) + 1
        If (2*($iNumDescChars + $iNumPattChars + 1) + $iEntry) > DllStructGetSize($tFilter) Then ExitLoop ;Can't fit in buffer
        $tFilterEntry = DllStructCreate(StringFormat("wchar Desc[%d];wchar Patt[%d]", $iNumDescChars, $iNumPattChars),  Ptr($pFilter + $iEntry))
        $tFilterEntry.Desc = $aFilter[$i][0]
        $tFilterEntry.Patt = $aFilter[$i][1]
        $iEntry += DllStructGetSize($tFilterEntry)
    Next

    Local $sCustomFilterDesc = "Custom Filter", $sCustomFilterPattern = "*.*"
    Local Static $tCustomFilter = DllStructCreate("byte[64]")
    Local $tCustFilterEntry = DllStructCreate(StringFormat("wchar Desc[%d];wchar Patt[%d]", StringLen($sCustomFilterDesc) + 1, StringLen($sCustomFilterPattern) + 1))
    $tCustFilterEntry.Desc = $sCustomFilterDesc
    $tCustFilterEntry.Patt = $sCustomFilterPattern

    ;Also recieves filename, or multiple if FO is configured that way
    Local $tFileNameBuff = DllStructCreate(StringFormat("wchar filenamess[%d]", 256))
    $tFileNameBuff.filenames = "SuggestedFile.txt"

    ;Title name.ext of sel file
    Local $tFileTitle = DllStructCreate(StringFormat("wchar FileTile[%d]", 64))

    ;Suggested init dir.
    Local $sInitDir = @WorkingDir
    Local $tInitialDir = DllStructCreate(StringFormat("wchar dir[%d]", StringLen($sInitDir) + 1))
    $tInitialDir.dir = $sInitDir

    ;dlg title
    Local $sTitle = "Custom Dialog Title"
    Local $tTitle = DllStructCreate(StringFormat("wchar title[%d]", StringLen($sTitle) + 1))
    $tTitle.Title = $sTitle

    Local $sResName = "DLG_NESTED"
    Local $tResName = DllStructCreate(StringFormat("wchar Name[%d]", StringLen($sResName) + 1))
    $tResName.Name = $sResName

    Local $tOFN = DllStructCreate($tagOPENFILENAME)
    $tOFN.StructSize = DllStructGetSize($tOFN)
    $tOFN.hwndOwner = $hParent
    $tOFN.hInstance = $hModule
    $tOFN.lpstrFilter = $pFilter
    $tOFN.lpstrCustomFilter = DllStructGetPtr($tCustomFilter)
    $tOFN.nMaxCustFilter = DllStructGetSize($tCustomFilter)/2
    $tOFN.nFilterIndex = 0 ;0 = cust filter; 1-n = dropdown.
    $tOFN.lpstrFile = DllStructGetPtr($tFileNameBuff)
    $tOFN.nMaxFile = DllStructGetSize($tFileNameBuff)/2
    $tOFN.lpstrFileTitle = DllStructGetPtr($tFileTitle)
    $tOFN.nMaxFileTitle = DllStructGetSize($tFileTitle)/2
    $tOFN.lpstrInitialDir = DllStructGetPtr($tInitialDir)
    $tOFN.lpstrTitle = DllStructGetPtr($tTitle)
    $tOFN.Flags = BitOR($OFN_DONTADDTORECENT, $OFN_ENABLEHOOK, $OFN_EXPLORER, $OFN_ENABLETEMPLATE)
    $tOFN.nFileOffset = 0 ;pos of filename in lpstrFile (might conatin path..)
    $tOFN.nFileExtension = _WinAPI_StringLenW($tFileNameBuff)-3 ;pos of ext in filename.
    $tOFN.lCustData = 0 ;Passes to hook.
    $tOFN.lpfnHook = DllCallbackGetPtr($g_hFOHookProc)
    $tOFN.lpTemplateName = DllStructGetPtr($tResName) ;Template name in res
    $tOFN.FlagsEx = 0; $OFN_EX_NOPLACESBAR; opt OFN_EX_NOPLACESBAR to hide "favoites" "desktop" etc.


    Local $aCall = DllCall("Comdlg32.dll", "bool", "GetOpenFileNameW", "struct*", $tOFN)

    _WinAPI_FreeLibrary($hModule)
EndFunc


Func FileOpenHook($hWnd, $iMsg, $wParam, $lParam)

    Switch $iMsg
        Case $WM_INITDIALOG
            Local $hCombo = _WinAPI_GetDlgItem($hWnd, 257)
            _SendMessage($hCombo, $CB_ADDSTRING, 0, "Item 1", 0, "wparam", "wstr")
            _SendMessage($hCombo, $CB_ADDSTRING, 0, "Item 2", 0, "wparam", "wstr")
            _SendMessage($hCombo, $CB_ADDSTRING, 0, "Item 3", 0, "wparam", "wstr")
            _SendMessage($hCombo, $CB_SETCURSEL)
            Return False
    EndSwitch

    Return _WinAPI_CallNextHookEx(0, $iMsg, $wParam, $lParam)
EndFunc

The correct way of doing things is more like this:

#include <WinAPI.au3>
#include <GuiConstants.au3>

Global $sCLSID_FileOpenDialog = "{DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7}"
Global Const $sIID_IFileOpenDialog = "{d57c7288-d4ad-4768-be02-9d969532d960}"
Global Const $sIID_IFileDialogCustomize = "{e6fdd21a-163f-4975-9c8c-a69f1ba37034}"

Global Const $tagIFileOpenDialog = _
    "Show hresult(hwnd);" & _
    "SetFileTypes hresult(uint;ptr);" & _
    "SetFileTypeIndex hresult(uint);" & _
    "GetFileTypeIndex hresult(uint*);" & _
    "Advise hresult(ptr;dword*);" & _
    "Unadvise hresult(dword);" & _
    "SetOptions hresult(dword);" & _
    "GetOptions hresult(dword*);" & _
    "SetDefaultFolder hresult(ptr);" & _
    "SetFolder hresult(ptr);" & _
    "GetFolder hresult(ptr*);" & _
    "GetCurrentSelection hresult(ptr*);" & _
    "SetFileName hresult(wstr);" & _
    "GetFileName hresult(wstr*);" & _
    "SetTitle hresult(wstr);" & _
    "SetOkButtonLabel hresult(wstr);" & _
    "SetFileNameLabel hresult(wstr);" & _
    "GetResult hresult(ptr*);" & _
    "AddPlace hresult(ptr;dword);" & _
    "SetDefaultExtension hresult(wstr);" & _
    "Close hresult(hresult);" & _
    "SetClientGuid hresult(clsid);" & _
    "ClearClientData hresult();" & _
    "SetFilter hresult(ptr);" & _
    "GetResults hresult(ptr*);" & _
    "GetSelectedItems hresult(ptr*);"

Global Const $tagIFileDialogCustomize = _
    "EnableOpenDropDown hresult(dword);" & _
    "AddMenu hresult(dword;wstr);" & _
    "AddPushButton hresult(dword;wstr);" & _
    "AddComboBox hresult(dword);" & _
    "AddRadioButtonList hresult(dword);" & _
    "AddCheckButton hresult(dword;wstr;bool);" & _
    "AddEditBox hresult(dword;wstr);" & _
    "AddSeparator hresult(dword);" & _
    "AddText hresult(dword;wstr);" & _
    "SetControlLabel hresult(dword;wstr);" & _
    "GetControlState hresult(dword;dword*);" & _
    "SetControlState hresult(dword;dword);" & _
    "GetEditBoxText hresult(dword;ptr*);" & _
    "SetEditBoxText hresult(dword;wstr);" & _
    "GetCheckButtonState hresult(dword;bool*);" & _
    "SetCheckButtonState hresult(dword;bool);" & _
    "AddControlItem hresult(dword;dword;wstr);" & _
    "RemoveControlItem hresult(dword;dword);" & _
    "RemoveAllControlItems hresult(dword);" & _
    "GetControlItemState hresult(dword;dword;dword*);" & _
    "SetControlItemState hresult(dword;dword;dword);" & _
    "GetSelectedControlItem hresult(dword;dword*);" & _
    "SetSelectedControlItem hresult(dword;dword);" & _
    "StartVisualGroup hresult(dword;wstr);" & _
    "EndVisualGroup hresult();" & _
    "MakeProminent hresult(dword);" & _
    "SetControlItemText hresult(dword;dword;wstr);"

DoFileOpen(0)

Func DoFileOpen($hParent)
    Local $oFileOpen = ObjCreateInterface($sCLSID_FileOpenDialog, $sIID_IFileOpenDialog, $tagIFileOpenDialog)

    Local $pCustomFileOpen, $tIID_IFileDialogCustomize  = _WinAPI_GUIDFromString($sIID_IFileDialogCustomize)
    $oFileOpen.QueryInterface(DllStructGetPtr($tIID_IFileDialogCustomize), $pCustomFileOpen)
    Local $oCustomFileOpen = ObjCreateInterface(Ptr($pCustomFileOpen), $sIID_IFileDialogCustomize, $tagIFileDialogCustomize)

    Local Enum $idVisualGroup = 100, $idCombo, $idItem1, $idItem2, $idItem3

    $oCustomFileOpen.StartVisualGroup($idVisualGroup, "&Encoding:")
    $oCustomFileOpen.AddComboBox($idCombo)
    $oCustomFileOpen.AddControlItem($idCombo, $idItem1, "Item 1")
    $oCustomFileOpen.AddControlItem($idCombo, $idItem2, "Item 2")
    $oCustomFileOpen.AddControlItem($idCombo, $idItem3, "Item 3")
    $oCustomFileOpen.SetSelectedControlItem($idCombo, $idItem1)
    $oCustomFileOpen.EndVisualGroup()

    $oFileOpen.Show($hParent)

    Local $idSelItem, $sSelItem
    $oCustomFileOpen.GetSelectedControlItem($idCombo, $idSelItem)

    Switch $idSelItem
        Case $idItem1
            $sSelItem = "Item 1"

        Case $idItem2
            $sSelItem = "Item 2"

        Case $idItem3
            $sSelItem = "Item 3"

    EndSwitch

    MsgBox(0, "Selected Item", $sSelItem)

EndFunc
Edited by MattyD
Posted

@MattyD My familiarity with any Object-related is essentially equal to zero. It was easy to use AddControlItem to add the text encoding options to the combo and get the returns on those, especially since you've got everything set up so nicely. But the part that I'm not sure about is how to get a return from the File name box as well. 

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
×
×
  • Create New...