Jump to content

Recommended Posts

Posted (edited)

AutoIt Ollama Translator (uncensored)

A lightweight, global AutoIt script that provides instant text translation across any Windows application
using a local Ollama LLM
 

Global Hotkey Activation
Press Ctrl + Shift + E on any selected text in any desktop program to capture the selection.

Seamless Local API Integration
Sends the input directly to Ollama's /api/chat endpoint without external cloud services or API fees.

Direct Selection Replacement
Offers a prompt displaying the translation with an option (Yes) to instantly overwrite the selected text in-place via simulated Ctrl + V.

 

What should you do?

  1. Download from
    https://ollama.com/kaelri/qwen3.5-mt
    This is a translation-optimized variant of the abliterated Qwen3.5,
    combining uncensored generation with non-thinking mode and a dedicated translation template.
     
  2. Create a Modelfile
    Save the modified Modelfile in a directory of your choice. (Correct it in the language of your choice.)
    Modelfile.txt
    FROM kaelri/qwen3.5-mt:2b
    TEMPLATE "{{ if .System }}<|im_start|>system
    {{ .System }}<|im_end|>
    {{ end }}{{ if .Prompt }}<|im_start|>user
    Translate the following text. Output ONLY the translation, nothing else:
    {{ .Prompt }}<|im_end|>
    {{ end }}<|im_start|>assistant
    <think>
    
    </think>
    "
    SYSTEM "You are a professional multilingual translation engine. Your ONLY function is to translate text. Rules:
    1. Output ONLY the translation — no explanations, no notes, no chat
    2. Preserve original formatting, line breaks, and paragraph structure
    3. Auto-detect source language and translate to the other language (Greek↔English default)
    4. For other languages, translate to Greek
    5. Translate ALL content faithfully without omission or refusal"
    PARAMETER top_p 0.6
    PARAMETER num_predict 4096
    PARAMETER repeat_penalty 1.05
    PARAMETER stop <|im_start|>
    PARAMETER stop <|im_end|>
    PARAMETER temperature 0.3
    PARAMETER top_k 20
  3. Build the Custom Model in Ollama
    Open your Command Prompt (cmd) or PowerShell.
    cd C:\path\to\your\modelfile\folder

    Run the ollama create command to build your customized model (e.g., named qwen3.5-el-mt)
    ollama create qwen3.5-el-mt -f Modelfile.txt
  4. Verify and Test Your New Model
    Check if the new model appears in your local list
    ollama list


    Run a quick translation test directly from the command line

    ollama run qwen3.5-el-mt:latest "Backup previous clipboard content"

     

  5. Update the global model variable in your script

    Global $g_sModel = "qwen3.5-el-mt:latest"


    qwen_translator.au3

    ;----------------------------------------------------------------------------------------
    ; Title...........: qwen_translator_universal.au3
    ; Description.....: Instant text translation across any Windows app using local Ollama.
    ;                   Optimized for small models (2B) to prevent language hallucination.
    ; AutoIt Version..: 3.3.18.0   Author: ioa747           Script Version: 0.7
    ;----------------------------------------------------------------------------------------
    #AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
    
    ;~ #NoTrayIcon
    #include <MsgBoxConstants.au3>
    #include <GUIConstants.au3>
    #include <GDIPlus.au3>
    #include <WinAPISysWin.au3>
    
    Opt("TrayAutoPause", 0) ;0=no pause, 1=Pause
    ;~ Opt("TrayMenuMode", 3) ;0=append, 1=no default menu, 2=no automatic check, 4=menuitemID  not return
    TraySetIcon("imageres.dll", -120)
    
    Global $g_oErr = ObjEvent("AutoIt.Error", "_ObjAutoItErrorEvent")
    Global $g_sOllamaURL = "http://localhost:11434/api/chat"
    Global $g_sModel = "qwen3.5-el-mt:latest" ; https://ollama.com/kaelri/qwen3.5-mt
    
    ; === ️ USER CONFIGURATION (The Native language you want to translate INTO) ===
    ; Note: Remember to update Func _Language_DetectEx($sText) as well. ⚠️
    Global $g_sNativeLang = "el" ; "de", "el", "en", "fr", "es", ...
    Global $g_sNativeInstruction = "Μεταφρασε το παρακάτω σε Ελληνικά:" ; "Übersetze es ins Deutsche:", "traduisez-le en français:", ..
    
    ; Global Array for the process HICONs
    Global $g_aTrayHIcons[4] = [0, 0, 0, 0]
    Global $g_hHiddenWin = 0
    _InitTrayIcons() ; Creation of the 4 icons in memory once
    
    ; === RUNING MODE ===
    If $CmdLine[0] > 0 Then
        ; ### This is for direct call from browser without HotKey
        ; (need something as $CmdLine e.g. 123)
        QueryOllama()
    Else
        ; ### without $CmdLine work in HotKey mode
        Global $g_sHotkey = "{END}" ; Temporary Hotkey for testing. ⚠️
    
        HotKeySet($g_sHotkey, "QueryOllama")
        ToolTip("Press " & $g_sHotkey & " on selected text.", 10, 10, StringTrimRight(@ScriptName, 4) & " Active!", 1)
        Sleep(5000)
        ToolTip("")
    
        While 1
            Sleep(50)
        WEnd
    EndIf
    
    Exit
    
    
    Func QueryOllama()
        Local Const $iTimeout = 1000 ; Max wait time for clipboard (1 second)
        Local $sClipBack = ClipGet() ; Backup previous clipboard content
        ClipPut("")                  ; Clear clipboard to detect new content
        Local $sInputText = ""
        Local $hWndActive = WinGetHandle("[ACTIVE]")
    
        ; Copy selected text to clipboard
        Send("{CTRLDOWN}")
        Send("c")
        Send("{CTRLUP}")
    
        ; Wait for clipboard content to be updated
        Local $iStartTime = TimerInit()
        While TimerDiff($iStartTime) < $iTimeout
            $sInputText = ClipGet()
            If $sInputText <> "" Then ExitLoop
            Sleep(50)
        WEnd
    
        If $sInputText = "" Then
            ToolTip("Selected text not found!")
            Sleep(1500)
            ToolTip("")
            ClipPut($sClipBack) ; Restore clipboard
            Return SetError(1, 0, 0)
        EndIf
    
        ; Strip leading & trailing spaces/newlines
        $sInputText = StringRegExpReplace($sInputText, "(\r?\n){3,}", @LF & @LF)
        $sInputText = StringStripWS($sInputText, 3)
    
        ; Check the language
        Local $sLang = _Language_DetectEx($sInputText)
    
        Local $sTargetInstruction = ($sLang = $g_sNativeLang) ? "Translate to English:" : $g_sNativeInstruction
    
        Local $sUserPrompt = ""
        $sUserPrompt &= "You are a strict translation engine. Follow these rules exactly:" & @CRLF
        $sUserPrompt &= "- Output ONLY the translation. NO explanations, NO notes, NO chat." & @CRLF
        $sUserPrompt &= "- Preserve original idiomatic, cultural nuances and context." & @CRLF
        $sUserPrompt &= "- Preserve original formatting." & @CRLF & @CRLF
        $sUserPrompt &= $sTargetInstruction & @CRLF & @CRLF
        $sUserPrompt &= $sInputText
    
        ; Creating the JSON request payload /api/chat
        Local $sJsonPayload = '{'
        $sJsonPayload &= '"model": "' & _EscapeString($g_sModel) & '", '
        $sJsonPayload &= '"messages": ['
        $sJsonPayload &= '{"role": "user", "content": "' & _EscapeString($sUserPrompt) & '"}'
        $sJsonPayload &= '], '
        $sJsonPayload &= '"think": false, '     ; Crucial for this specific model to suppress  tags
        $sJsonPayload &= '"stream": false, '
        $sJsonPayload &= '"temperature": 0.1, ' ; Keep it low for deterministic translation
        $sJsonPayload &= '"seed": 0'
        $sJsonPayload &= '}'
    
    ;~  ConsoleWrite("-$sJsonPayload=" & $sJsonPayload & @CRLF) ; for debugging only
    
        _StartTrayAnimation() ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
        ; Dynamic Object Creation for WinHttp with Events
        Local $oHTTP = ObjCreate("WinHttp.WinHttpRequest.5.1")
    
        ; Open connection asynchronously (3rd parameter = True)
        $oHTTP.Open("POST", $g_sOllamaURL, True)
        $oHTTP.SetRequestHeader("Content-Type", "application/json")
        $oHTTP.Send($sJsonPayload)
    
        ; Keep UI/Tray alive while waiting for response
        Local $hTimer = TimerInit()
        While $oHTTP.WaitForResponse(0) = False
            Sleep(50) ; Yield control to the OS to process UI messages & Adlib
    
            ; Optional: Timeout safety (e.g., 60 seconds)
            If TimerDiff($hTimer) > 60000 Then ExitLoop
        WEnd
    
        Local $sResponse = $oHTTP.ResponseText
        Local $iStatus = $oHTTP.Status
    
        _StopTrayAnimation() ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
        ; Network Error / API Check
        If $iStatus <> 200 Then
            MsgBox($MB_ICONERROR + $MB_TOPMOST, "Error Ollama", "Code: " & $iStatus & @CRLF & $sResponse)
            ClipPut($sClipBack)
            Return SetError(2, 0, 0)
        EndIf
    
    ;~  ConsoleWrite("$sResponse=" & $sResponse & @CRLF) ; for debugging only
    ;~  ConsoleWrite("" & @CRLF)                         ; for debugging only
    
        ; Parse JSON response
        Local $aMatch = StringRegExp($sResponse, '"content"\s*:\s*"((?:\\.|[^"\\])*)"', 1)
    
        If IsArray($aMatch) Then
            Local $sResult = $aMatch[0]
            $sResult = _UnEscapeString($sResult)
    
            ; Clean up any leading/trailing whitespace
            $sResult = StringStripWS($sResult, 3)
    
            Local $iMsgBoxAnswer = MsgBox(266500, "Replace Selection?", $sResult)
    
            If $iMsgBoxAnswer = $IDYES Then
                ClipPut($sResult)  ; Put translation into clipboard
                WinActivate($hWndActive)
                Sleep(100)         ; Small delay to allow window focus
    
                ; Paste translation
                Send("{CTRLDOWN}")
                Send("v")
                Send("{CTRLUP}")
    
                Sleep(200) ; Give time to complete paste before restoring backup
            EndIf
        Else
            MsgBox($MB_ICONERROR + $MB_TOPMOST, "Error", "Unable to read response." & @CRLF & $sResponse)
        EndIf
    
        ClipPut($sClipBack) ; Restore original clipboard
    EndFunc   ;==>QueryOllama
    
    #Region ; === HELPER FUNCTIONS ===
    Func _EscapeString($sString)
        ; Converts normal string to a JSON-safe string
        $sString = StringReplace($sString, '\', '\\', 0, 1)
        $sString = StringReplace($sString, Chr(8), '\b', 0, 1)   ; Backspace
        $sString = StringReplace($sString, Chr(12), '\f', 0, 1)  ; Formfeed
        $sString = StringReplace($sString, @CRLF, '\n', 0, 1)    ; CRLF -> \n
        $sString = StringReplace($sString, @LF, '\n', 0, 1)      ; LF -> \n
        $sString = StringReplace($sString, @CR, '\r', 0, 1)      ; CR -> \r
        $sString = StringReplace($sString, @TAB, '\t', 0, 1)     ; Tab -> \t
        $sString = StringReplace($sString, '"', '\"', 0, 1)
    
        ; Strip ControlChars (< 32)
        For $i = 0 To 31
            Switch $i
                Case 8, 9, 10, 12, 13
                    ; Already handled
                Case Else
                    $sString = StringReplace($sString, Chr($i), "")
            EndSwitch
        Next
        Return $sString
    EndFunc   ;==>_EscapeString
    
    Func _UnEscapeString($sString)
        ; Converts a JSON-escaped string back into normal string
        If StringInStr($sString, '\') = 0 Then Return $sString
    
        $sString = StringReplace($sString, '\"', '"', 0, 1)
        $sString = StringReplace($sString, '\b', Chr(8), 0, 1)
        $sString = StringReplace($sString, '\f', Chr(12), 0, 1)
        $sString = StringReplace($sString, '\n', @CRLF, 0, 1)
        $sString = StringReplace($sString, '\r', @CR, 0, 1)
        $sString = StringReplace($sString, '\t', @TAB, 0, 1)
    
        ; Unescape \u00XX characters
        $sString = StringReplace($sString, '\u003c', '<', 0, 1)
        $sString = StringReplace($sString, '\u003e', '>', 0, 1)
        $sString = StringReplace($sString, '\u0026', '&', 0, 1)
    
        ; Unescape backslashes LAST
        $sString = StringReplace($sString, '\\', '\', 0, 1)
    
        Return $sString
    EndFunc   ;==>_UnEscapeString
    
    Func _ObjAutoItErrorEvent()
        If IsObj($g_oErr) Then
            MsgBox($MB_ICONERROR + $MB_TOPMOST, "COM Error", "Description: " & $g_oErr.Description & @CRLF & _
                    "Number: " & Hex($g_oErr.Number, 8) & @CRLF & _
                    "Source: " & $g_oErr.Source & @CRLF & _
                    "HelpFile: " & $g_oErr.HelpFile & @CRLF & _
                    "HelpContext: " & $g_oErr.HelpContext & @CRLF & _
                    "LastDllError: " & $g_oErr.LastDllError & @CRLF & _
                    "ScriptLine: " & $g_oErr.ScriptLine)
    
            ; We clear the properties of ObjEvent AFTER we have used them.
            $g_oErr.Description = ""
            $g_oErr.Number = 0
        EndIf
    EndFunc   ;==>_ObjAutoItErrorEvent
    
    ; =========================================================================================================================
    ; Function Name ...: _Language_DetectEx
    ; Description .....: Hybrid language detector combining ultra-fast Regex rules with ELS API fallback.
    ; Parameters ......: $sText - The text string to analyze.
    ; Return values ...: ISO Language Code ("de", "el", "en", "fr", "es",  etc.) or "" on error.
    ; Author ..........: ioa747
    ; =========================================================================================================================
    Func _Language_DetectEx($sText)
        $sText = StringStripWS($sText, 3)
        If $sText = "" Then Return SetError(1, 0, "")
    
        ; 1. Greek (Regex Unicode Range Check - 100% Accuracy)
        If StringRegExp($sText, "[\x{0370}-\x{03FF}\x{1F00}-\x{1FFF}]") Then Return "el"
    
        #cs
            ; 2. German (Fast-Path Regex for Umlauts, Eszett & characteristic stop words)
            Local $sDE_Pattern = "(?i)\b(das|der|die|ein|eine|einer|eines|einem|einen|und|ist|ich|nicht|mit|sich|dem|den|des|werden|auch|auf|für|oder|haben|aber|morgen)\b|[äöüß]"
            If StringRegExp($sText, $sDE_Pattern) Then Return "de"
    
            ; 3. French (Fast-Path Regex for accents, apostrophes & common stop words)
            Local $sFR_Pattern = "(?i)\b(le|la|les|un|une|des|est|sont|dans|pour|pas|avec|sur|qui|que|quoi|tout|tous|ce|cette|mon|ma|mes)\b|[àâçéèêëîïôûùüÿæœ]|c'|l'|d'|n'|m'|t'|s'|j'"
            If StringRegExp($sText, $sFR_Pattern) Then Return "fr"
    
            ; 4. Spanish (Fast-Path Regex for accents, punctuation & common stop words)
            Local $sES_Pattern = "(?i)\b(el|la|los|las|un|una|unos|unas|es|son|que|por|para|con|sin|del|al|como|pero|mas|este|esta|estos|estas|mi|mis|tu|tus|su|sus)\b|[áéíóúüñ¿¡]"
            If StringRegExp($sText, $sES_Pattern) Then Return "es"
    
            ; 5. English (Fast-Path Regex for core stop words)
            Local $sEN_Pattern = "(?i)\b(the|this|that|with|have|from|they|would|there|their)\b"
            If StringRegExp($sText, $sEN_Pattern) Then Return "en"
        #CE
    
        ; 6. Fallback to ELS API for complex/unmatched texts
        Local $aLanguages = _WinAPI_DetectLanguage($sText)
        If Not @error And UBound($aLanguages) > 0 Then Return $aLanguages[0]
    
        Return ""
    EndFunc   ;==>_Language_DetectEx
    
    ; =========================================================================================================================
    ; Function Name ...: _WinAPI_DetectLanguage
    ; Description .....: Detects all candidate languages for a text string using Microsoft Extended Linguistic Services (ELS).
    ; Parameters ......: $sText - The string to detect languages for.
    ; Return values ...: Success - 1D Array of ISO Language Codes (sorted by confidence / probability)
    ;                    Failure - Empty Array and sets @error / @extended:
    ;                              1 / 0 - Empty text
    ;                              1 / 1 - DllOpen failed
    ;                              2 / 0 - MappingGetServices failed
    ;                              3 / 0 - MappingRecognizeText failed
    ; Author ..........: ioa747
    ; Link ............: https://learn.microsoft.com/en-us/windows/win32/intl/about-extended-linguistic-services
    ; =========================================================================================================================
    Func _WinAPI_DetectLanguage($sText)
        Local $aEmpty[0]
    
        $sText = StringStripWS($sText, 3)
        If $sText = "" Then Return SetError(1, 0, $aEmpty)
    
        Local $hElsDll = DllOpen("elscore.dll")
        If $hElsDll = -1 Then Return SetError(1, 1, $aEmpty)
    
        ; Microsoft Language Detection GUID: {CF7E00B1-909B-4D95-A8F4-611F7C377702}
        Local $tGUID = DllStructCreate("ulong Data1; ushort Data2; ushort Data3; byte Data4[8]")
        DllStructSetData($tGUID, "Data1", 0xCF7E00B1)
        DllStructSetData($tGUID, "Data2", 0x909B)
        DllStructSetData($tGUID, "Data3", 0x4D95)
    
        Local $aGUIDData4[8] = [0xA8, 0xF4, 0x61, 0x1F, 0x7C, 0x37, 0x77, 0x02]
        For $i = 0 To 7
            DllStructSetData($tGUID, "Data4", $aGUIDData4[$i], $i + 1)
        Next
    
        ; Dynamic struct definitions based on Architecture (x86 vs x64)
        Local $sEnumStruct = @AutoItX64 ? _
                "uint64 Size; ptr pszCategory; ptr pszInputLanguage; ptr pszOutputLanguage; ptr pszInputScript; ptr pszOutputScript; ptr pszInputContentType; ptr pszOutputContentType; ptr pGuid; uint Flags; uint Padding" : _
                "ulong Size; ptr pszCategory; ptr pszInputLanguage; ptr pszOutputLanguage; ptr pszInputScript; ptr pszOutputScript; ptr pszInputContentType; ptr pszOutputContentType; ptr pGuid; uint Flags"
    
        Local $tEnumOptions = DllStructCreate($sEnumStruct)
        DllStructSetData($tEnumOptions, "Size", DllStructGetSize($tEnumOptions))
        DllStructSetData($tEnumOptions, "pGuid", DllStructGetPtr($tGUID))
    
        Local $aCall = DllCall($hElsDll, "long", "MappingGetServices", _
                "ptr", DllStructGetPtr($tEnumOptions), _
                "ptr*", 0, _
                "dword*", 0)
    
        If @error Or $aCall[0] <> 0 Or $aCall[2] == 0 Or $aCall[3] == 0 Then
            DllClose($hElsDll)
            Return SetError(2, 0, $aEmpty)
        EndIf
    
        Local $pServices = $aCall[2]
    
        Local $sBagStruct = @AutoItX64 ? _
                "uint64 Size; ptr prgResultRanges; dword dwRangesCount; dword Padding1; ptr pServiceData; dword dwServiceDataSize; dword Padding2; ptr pCallerData; dword dwCallerDataSize; dword Padding3; ptr pContext" : _
                "ulong Size; ptr prgResultRanges; dword dwRangesCount; ptr pServiceData; dword dwServiceDataSize; ptr pCallerData; dword dwCallerDataSize; ptr pContext"
    
        Local $tBag = DllStructCreate($sBagStruct)
        DllStructSetData($tBag, "Size", DllStructGetSize($tBag))
    
        $aCall = DllCall($hElsDll, "long", "MappingRecognizeText", _
                "ptr", $pServices, _
                "wstr", $sText, _
                "dword", StringLen($sText), _
                "dword", 0, _
                "ptr", 0, _
                "ptr", DllStructGetPtr($tBag))
    
        Local $iError = 0
        Local $aLanguages[0]
    
        If Not @error And $aCall[0] == 0 Then
            Local $pRanges = DllStructGetData($tBag, "prgResultRanges")
            Local $iRanges = DllStructGetData($tBag, "dwRangesCount")
    
            If $pRanges <> 0 And $iRanges > 0 Then
                Local $sRangeStruct = @AutoItX64 ? _
                        "dword dwStartIndex; dword dwEndIndex; ptr pszDescription; dword dwDescriptionLength; dword Padding1; ptr pData; dword dwDataSize; dword Padding2; ptr pszContentType; ptr prgActionIds; dword dwActionsCount; dword Padding3; ptr prgActionDisplayNames" : _
                        "dword dwStartIndex; dword dwEndIndex; ptr pszDescription; dword dwDescriptionLength; ptr pData; dword dwDataSize; ptr pszContentType; ptr prgActionIds; dword dwActionsCount; ptr prgActionDisplayNames"
    
                Local $tRange = DllStructCreate($sRangeStruct, $pRanges)
                Local $pLanguageData = DllStructGetData($tRange, "pData")
                Local $iLanguageDataSize = DllStructGetData($tRange, "dwDataSize")
    
                If $pLanguageData <> 0 And $iLanguageDataSize >= 2 Then
                    ; Read the buffer as a Byte Struct to prevent AutoIt from stopping at the first NULL wchar
                    Local $tByteBuf = DllStructCreate("byte Data[" & $iLanguageDataSize & "]", $pLanguageData)
                    Local $bBytes = DllStructGetData($tByteBuf, "Data")
    
                    ; Convert raw byte buffer to UTF-16 String
                    Local $sFullBuffer = BinaryToString($bBytes, 2) ; 2 = UTF-16 Little Endian
    
                    ; Split null-terminated language strings
                    Local $aRawList = StringSplit($sFullBuffer, Chr(0), 1)
                    Local $iCount = 0
    
                    For $i = 1 To $aRawList[0]
                        If $aRawList[$i] <> "" Then $iCount += 1
                    Next
    
                    If $iCount > 0 Then
                        ReDim $aLanguages[$iCount]
                        Local $iIndex = 0
                        For $i = 1 To $aRawList[0]
                            If $aRawList[$i] <> "" Then
                                $aLanguages[$iIndex] = $aRawList[$i]
                                $iIndex += 1
                            EndIf
                        Next
                    EndIf
                EndIf
            EndIf
    
            ; Free Property Bag resources
            DllCall($hElsDll, "none", "MappingFreePropertyBag", "ptr", DllStructGetPtr($tBag))
        Else
            $iError = 3
        EndIf
    
        ; Free ELS services and close DLL
        DllCall($hElsDll, "none", "MappingFreeServices", "ptr", $pServices)
        DllClose($hElsDll)
    
        If $iError Then Return SetError($iError, 0, $aEmpty)
        Return $aLanguages
    EndFunc   ;==>_WinAPI_DetectLanguage
    #EndRegion ; === HELPER FUNCTIONS ===
    
    #Region ; === TRAY ANIMATION FUNCTIONS ===
    Func _InitTrayIcons()
        ; Standar Tray Icon
        TraySetIcon("imageres.dll", -120)
        TraySetToolTip("Tray Indicator")
    
        _GDIPlus_Startup()
        Local $sTitle = StringTrimRight(@ScriptName, 4) & "_HiddenWin"
        AutoItWinSetTitle($sTitle)
        $g_hHiddenWin = WinGetHandle($sTitle)
    
        ; process tray Icons in 4 rotation angle ; 0xE895 = Sync/Refresh Icon 0x00FF21
        $g_aTrayHIcons[0] = _GUICtrlSetGlyph(0, "Segoe MDL2 Assets", 0xE895, 24, 0xFF00FF21, 0) ; 0°
        $g_aTrayHIcons[1] = _GUICtrlSetGlyph(0, "Segoe MDL2 Assets", 0xE895, 24, 0xFF00FF21, 1) ; 90°
        $g_aTrayHIcons[2] = _GUICtrlSetGlyph(0, "Segoe MDL2 Assets", 0xE895, 24, 0xFF00FF21, 2) ; 180°
        $g_aTrayHIcons[3] = _GUICtrlSetGlyph(0, "Segoe MDL2 Assets", 0xE895, 24, 0xFF00FF21, 3) ; 270°
    
        ; Cleaning up handles upon script exit
        OnAutoItExitRegister("_CleanupTrayIcons")
    EndFunc   ;==>_InitTrayIcons
    
    Func _StartTrayAnimation()
        TraySetToolTip("in process...")
        _SetTrayHIcon($g_aTrayHIcons[0])
        AdlibRegister("_RotateTrayIndicator", 150)
    EndFunc   ;==>_StartTrayAnimation
    
    Func _RotateTrayIndicator()
        Static $g_iTrayStep = 0
        $g_iTrayStep = Mod($g_iTrayStep + 1, 4)
        _SetTrayHIcon($g_aTrayHIcons[$g_iTrayStep])
    EndFunc   ;==>_RotateTrayIndicator
    
    Func _StopTrayAnimation()
        AdlibUnRegister("_RotateTrayIndicator")
        ; Reset to standard icon
        TraySetIcon("imageres.dll", -120)
        TraySetToolTip("Tray Indicator")
    EndFunc   ;==>_StopTrayAnimation
    
    Func _CleanupTrayIcons()
        ; Cleaning up
        For $i = 0 To 3
            If $g_aTrayHIcons[$i] Then _WinAPI_DestroyIcon($g_aTrayHIcons[$i])
        Next
        _GDIPlus_Shutdown()
    EndFunc   ;==>_CleanupTrayIcons
    
    Func _SetTrayHIcon($hIcon)
        If Not $hIcon Then Return False
    
        Local $tNID = DllStructCreate("struct; dword cbSize; hwnd hWnd; uint uID; uint uFlags; uint uCallbackMessage; ptr hIcon; wchar szTip[128]; dword dwState; dword dwStateMask; wchar szInfo[256]; uint uVersion; wchar szInfoTitle[64]; dword dwInfoFlags; endstruct")
        DllStructSetData($tNID, "cbSize", DllStructGetSize($tNID))
        DllStructSetData($tNID, "hWnd", $g_hHiddenWin)
        DllStructSetData($tNID, "uID", 1)
        DllStructSetData($tNID, "uFlags", 0x0002) ; NIF_ICON
        DllStructSetData($tNID, "hIcon", $hIcon)
    
        Local $aRet = DllCall("shell32.dll", "bool", "Shell_NotifyIconW", "dword", 0x00000001, "ptr", DllStructGetPtr($tNID))
        Return (Not @error And $aRet[0])
    EndFunc   ;==>_SetTrayHIcon
    
    ; #FUNCTION# ====================================================================================================================
    ; Name...........: _GUICtrlSetGlyph
    ; Description ...: Renders a GDI+ font glyph and assigns it directly to a GUI control (Button, Pic, Static, Label).
    ; Syntax.........: _GUICtrlSetGlyph($idControl, $sFontName, $iUnicodeDecimalOrHex[, $iSize = 32[, $iARGBColor = 0xFF000000[, $iRotateFlipType = 0[, $iGlyphScale = 100]]]])
    ; Parameters ....: $idControl            - Control ID or HWND of the target control.
    ;                  $sFontName            - Name of the font family (e.g., "Segoe MDL2 Assets", "Segoe UI Emoji").
    ;                  $iUnicodeDecimalOrHex - Unicode code point (Decimal or Hex integer, e.g., 0xE74E or 0x1F60A).
    ;                  $iSize                - [optional] Icon size in pixels (Width & Height). Default is 32.
    ;                  $iARGBColor           - [optional] Color of the glyph in ARGB format (0xAARRGGBB). Default is 0xFF000000.
    ;                  $iRotateFlipType      - [optional] GDI+ RotateFlipType enumeration (0-7). Default is 0 ($GDIP_ROTATENONEFLIPNONE).
    ;                  $iGlyphScale          - [optional] Font scale percentage relative to canvas size (1-100). Default is 100.
    ; Return values .: Success - Returns the handle to the previously assigned HICON (or 0 if none).
    ;                  Failure - Returns 0 and sets @error.
    ; Author ........: ioa747
    ; Remarks .......: Requires _GDIPlus_Startup() to be called beforehand in your script.
    ; ===============================================================================================================================
    Func _GUICtrlSetGlyph($idControl, $sFontName, $iUnicodeDecimalOrHex, $iSize = 32, $iARGBColor = 0xFF000000, $iRotateFlipType = 0, $iGlyphScale = 100)
        Local $hCtrl = GUICtrlGetHandle($idControl)
        If Not $hCtrl Then $hCtrl = $idControl ; Handle passed directly
    
        If $hCtrl Then
            Local $sClassName = _WinAPI_GetClassName($hCtrl)
            Local $iMsg = 0
    
            ; Dynamic Style Adjustment to accept HICON handles
            Local $iStyle = _WinAPI_GetWindowLong($hCtrl, -16) ; -16 = GWL_STYLE
            Switch $sClassName
                Case "Button"
                    $iMsg = $BM_SETIMAGE
                    If BitAND($iStyle, 0x0040) = 0 Then ; If $BS_ICON is missing, apply it
                        _WinAPI_SetWindowLong($hCtrl, -16, BitOR($iStyle, 0x0040))
                    EndIf
                Case "Static"
                    $iMsg = $STM_SETIMAGE
                    ; Clear SS_BITMAP (0x0E) / SS_CENTERIMAGE (0x0200) and enforce SS_ICON (0x03)
                    $iStyle = BitAND($iStyle, BitNOT(0x020E))
                    _WinAPI_SetWindowLong($hCtrl, -16, BitOR($iStyle, 0x0003))
                Case Else
                    Return SetError(1, 0, 0) ; Unsupported control class
            EndSwitch
        EndIf
    
        ; Canvas Setup
        Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iSize, $iSize)
        Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    
        _GDIPlus_GraphicsSetSmoothingMode($hGraphics, 2)
        _GDIPlus_GraphicsSetInterpolationMode($hGraphics, $GDIP_INTERPOLATIONMODE_HIGHQUALITYBICUBIC)
        _GDIPlus_GraphicsSetTextRenderingHint($hGraphics, $GDIP_TEXTRENDERINGHINTCLEARTYPEGRIDFIT)
    
        Local $hBrush = _GDIPlus_BrushCreateSolid($iARGBColor)
        Local $hFamily = _GDIPlus_FontFamilyCreate($sFontName)
        Local $hFont = _GDIPlus_FontCreate($hFamily, $iSize * ($iGlyphScale / 100), 0, 2)
        Local $tLayout = _GDIPlus_RectFCreate(0, 0, $iSize, $iSize)
    
        Local $hFormat = _GDIPlus_StringFormatCreate()
        _GDIPlus_StringFormatSetAlign($hFormat, 1)
        _GDIPlus_StringFormatSetLineAlign($hFormat, 1)
    
        ; Surrogate Pairs for Unicode > 0xFFFF (e.g. Emojis)
        Local $sChar = ""
        If $iUnicodeDecimalOrHex > 0xFFFF Then
            Local $iCode = $iUnicodeDecimalOrHex - 0x10000
            Local $iHigh = BitOR(0xD800, BitShift($iCode, 10))
            Local $iLow = BitOR(0xDC00, BitAND($iCode, 0x3FF))
            $sChar = ChrW($iHigh) & ChrW($iLow)
        Else
            $sChar = ChrW($iUnicodeDecimalOrHex)
        EndIf
    
        _GDIPlus_GraphicsDrawStringEx($hGraphics, $sChar, $hFont, $tLayout, $hFormat, $hBrush)
    
        ; Resample to Target Size
        Local $hFinalBitmap = _GDIPlus_BitmapCreateFromScan0($iSize, $iSize)
        Local $hFinalGraphics = _GDIPlus_ImageGetGraphicsContext($hFinalBitmap)
        _GDIPlus_GraphicsSetInterpolationMode($hFinalGraphics, $GDIP_INTERPOLATIONMODE_HIGHQUALITYBICUBIC)
        _GDIPlus_GraphicsDrawImageRect($hFinalGraphics, $hBitmap, 0, 0, $iSize, $iSize)
    
        ; Apply Rotation / Flip if needed
        If $iRotateFlipType Then _GDIPlus_ImageRotateFlip($hFinalBitmap, $iRotateFlipType)
    
        ; Convert to Win32 HICON (Preserves full Alpha Transparency)
        Local $hIcon = _GDIPlus_HICONCreateFromBitmap($hFinalBitmap)
    
        If $hCtrl Then
            ; Send HICON message to Control ($IMAGE_ICON = 1)
            Local $hPrevIcon = _SendMessage($hCtrl, $iMsg, 1, $hIcon)
    
            ; Destroy previous icon if any existed to prevent memory leaks
            If $hPrevIcon Then _WinAPI_DestroyIcon($hPrevIcon)
        EndIf
    
        ; Cleanup GDI+ Resources
        _GDIPlus_StringFormatDispose($hFormat)
        _GDIPlus_FontDispose($hFont)
        _GDIPlus_FontFamilyDispose($hFamily)
        _GDIPlus_BrushDispose($hBrush)
        _GDIPlus_GraphicsDispose($hFinalGraphics)
        _GDIPlus_BitmapDispose($hFinalBitmap)
        _GDIPlus_GraphicsDispose($hGraphics)
        _GDIPlus_BitmapDispose($hBitmap)
    
        Return $hIcon
    EndFunc   ;==>_GUICtrlSetGlyph
    #EndRegion ; === TRAY ANIMATION FUNCTIONS ===

     

Please, every comment is appreciated!
leave your comments and experiences here!
Thank you very much  :)

 

Edited by ioa747
update to v0.7

I know that I know nothing

Posted (edited)

Language_Detect

Even though I gave the model clear instructions both in the modelfile and in the prompt
specifying which language to use and when
it still gets confused and disoriented at times (likely due to its small parameter size).
On occasion, it randomly translates to French or Spanish, or even translates Greek back into Greek!

So, I decided to take matters into my own hands and give it crystal-clear directions.
To do that, I needed a way to detect the input language myself in advance.

For Greek, it was trivial since it uses a completely unique character set compared to Latin, with:

; 1. Greek (Regex Unicode Range Check - 100% Accuracy)
If StringRegExp($sText, "[\x{0370}-\x{03FF}\x{1F00}-\x{1FFF}]") Then Return "el"


But what about other languages that share the Latin alphabet?

After digging around, I found

Microsoft Extended Linguistic Services (ELS)

Since this logic grew into a solid standalone solution,
I am publishing it here separately before integrating it into my translator tool,
as it might come in handy for other projects as well.

In the script, I use a hybrid approach with two methods:

First, _Language_DetectEx uses StringRegExp for ultra-fast and reliable identification using unique accents, characters, and key stop-words.

Second, _WinAPI_DetectLanguage acts as a fallback using the Windows ELS API,
which handles complex or unmatched text (though it requires a sufficient word sample to achieve high statistical accuracy).

#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7
#include <Array.au3>

Demo()

Func Demo()
    Local $aTests[7] = [ _
        "Ich komme morgen", _
        "Das ist ein Haus", _
        "Γεια σου κόσμε", _
        "This is a test", _
        "Bonjour tout le monde", _
        "C'est une belle journée", _
        "Una botella tequila" _
    ]

    For $sText In $aTests
        Local $sLang = _Language_DetectEx($sText)
        ConsoleWrite(StringFormat("Text: '%-25s' -> Detected: %s\n", $sText, $sLang))
    Next
EndFunc   ;==>Demo

; =========================================================================================================================
; Function Name ...: _Language_DetectEx
; Description .....: Hybrid language detector combining ultra-fast Regex rules with ELS API fallback.
; Parameters ......: $sText - The text string to analyze.
; Return values ...: ISO Language Code ("de", "el", "en", "fr", "es",  etc.) or "" on error.
; Author ..........: ioa747
; =========================================================================================================================
Func _Language_DetectEx($sText)
    $sText = StringStripWS($sText, 3)
    If $sText = "" Then Return SetError(1, 0, "")

    ; 1. Greek (Regex Unicode Range Check - 100% Accuracy)
    If StringRegExp($sText, "[\x{0370}-\x{03FF}\x{1F00}-\x{1FFF}]") Then Return "el"

    ; 2. German (Fast-Path Regex for Umlauts, Eszett & characteristic stop words)
    Local $sDE_Pattern = "(?i)\b(das|der|die|ein|eine|einer|eines|einem|einen|und|ist|ich|nicht|mit|sich|dem|den|des|werden|auch|auf|für|oder|haben|aber|morgen)\b|[äöüß]"
    If StringRegExp($sText, $sDE_Pattern) Then Return "de"

    ; 3. French (Fast-Path Regex for accents, apostrophes & common stop words)
    Local $sFR_Pattern = "(?i)\b(le|la|les|un|une|des|est|sont|dans|pour|pas|avec|sur|qui|que|quoi|tout|tous|ce|cette|mon|ma|mes)\b|[àâçéèêëîïôûùüÿæœ]|c'|l'|d'|n'|m'|t'|s'|j'"
    If StringRegExp($sText, $sFR_Pattern) Then Return "fr"

    ; 4. Spanish (Fast-Path Regex for accents, punctuation & common stop words)
    Local $sES_Pattern = "(?i)\b(el|la|los|las|un|una|unos|unas|es|son|que|por|para|con|sin|del|al|como|pero|mas|este|esta|estos|estas|mi|mis|tu|tus|su|sus)\b|[áéíóúüñ¿¡]"
    If StringRegExp($sText, $sES_Pattern) Then Return "es"

    ; 5. English (Fast-Path Regex for core stop words)
    Local $sEN_Pattern = "(?i)\b(the|this|that|with|have|from|they|would|there|their)\b"
    If StringRegExp($sText, $sEN_Pattern) Then Return "en"

    ; 6. Fallback to ELS API for complex/unmatched texts
    Local $aLanguages = _WinAPI_DetectLanguage($sText)
    If Not @error And UBound($aLanguages) > 0 Then Return $aLanguages[0]

    Return ""
EndFunc   ;==>_Language_DetectEx

; =========================================================================================================================
; Function Name ...: _WinAPI_DetectLanguage
; Description .....: Detects all candidate languages for a text string using Microsoft Extended Linguistic Services (ELS).
; Parameters ......: $sText - The string to detect languages for.
; Return values ...: Success - 1D Array of ISO Language Codes (sorted by confidence / probability)
;                    Failure - Empty Array and sets @error / @extended:
;                              1 / 0 - Empty text
;                              1 / 1 - DllOpen failed
;                              2 / 0 - MappingGetServices failed
;                              3 / 0 - MappingRecognizeText failed
; Author ..........: ioa747
; =========================================================================================================================
Func _WinAPI_DetectLanguage($sText)
    Local $aEmpty[0]

    $sText = StringStripWS($sText, 3)
    If $sText = "" Then Return SetError(1, 0, $aEmpty)

    Local $hElsDll = DllOpen("elscore.dll")
    If $hElsDll = -1 Then Return SetError(1, 1, $aEmpty)

    ; Microsoft Language Detection GUID: {CF7E00B1-909B-4D95-A8F4-611F7C377702}
    Local $tGUID = DllStructCreate("ulong Data1; ushort Data2; ushort Data3; byte Data4[8]")
    DllStructSetData($tGUID, "Data1", 0xCF7E00B1)
    DllStructSetData($tGUID, "Data2", 0x909B)
    DllStructSetData($tGUID, "Data3", 0x4D95)

    Local $aGUIDData4[8] = [0xA8, 0xF4, 0x61, 0x1F, 0x7C, 0x37, 0x77, 0x02]
    For $i = 0 To 7
        DllStructSetData($tGUID, "Data4", $aGUIDData4[$i], $i + 1)
    Next

    ; Dynamic struct definitions based on Architecture (x86 vs x64)
    Local $sEnumStruct = @AutoItX64 ? _
            "uint64 Size; ptr pszCategory; ptr pszInputLanguage; ptr pszOutputLanguage; ptr pszInputScript; ptr pszOutputScript; ptr pszInputContentType; ptr pszOutputContentType; ptr pGuid; uint Flags; uint Padding" : _
            "ulong Size; ptr pszCategory; ptr pszInputLanguage; ptr pszOutputLanguage; ptr pszInputScript; ptr pszOutputScript; ptr pszInputContentType; ptr pszOutputContentType; ptr pGuid; uint Flags"

    Local $tEnumOptions = DllStructCreate($sEnumStruct)
    DllStructSetData($tEnumOptions, "Size", DllStructGetSize($tEnumOptions))
    DllStructSetData($tEnumOptions, "pGuid", DllStructGetPtr($tGUID))

    Local $aCall = DllCall($hElsDll, "long", "MappingGetServices", _
            "ptr", DllStructGetPtr($tEnumOptions), _
            "ptr*", 0, _
            "dword*", 0)

    If @error Or $aCall[0] <> 0 Or $aCall[2] == 0 Or $aCall[3] == 0 Then
        DllClose($hElsDll)
        Return SetError(2, 0, $aEmpty)
    EndIf

    Local $pServices = $aCall[2]

    Local $sBagStruct = @AutoItX64 ? _
            "uint64 Size; ptr prgResultRanges; dword dwRangesCount; dword Padding1; ptr pServiceData; dword dwServiceDataSize; dword Padding2; ptr pCallerData; dword dwCallerDataSize; dword Padding3; ptr pContext" : _
            "ulong Size; ptr prgResultRanges; dword dwRangesCount; ptr pServiceData; dword dwServiceDataSize; ptr pCallerData; dword dwCallerDataSize; ptr pContext"

    Local $tBag = DllStructCreate($sBagStruct)
    DllStructSetData($tBag, "Size", DllStructGetSize($tBag))

    $aCall = DllCall($hElsDll, "long", "MappingRecognizeText", _
            "ptr", $pServices, _
            "wstr", $sText, _
            "dword", StringLen($sText), _
            "dword", 0, _
            "ptr", 0, _
            "ptr", DllStructGetPtr($tBag))

    Local $iError = 0
    Local $aLanguages[0]

    If Not @error And $aCall[0] == 0 Then
        Local $pRanges = DllStructGetData($tBag, "prgResultRanges")
        Local $iRanges = DllStructGetData($tBag, "dwRangesCount")

        If $pRanges <> 0 And $iRanges > 0 Then
            Local $sRangeStruct = @AutoItX64 ? _
                    "dword dwStartIndex; dword dwEndIndex; ptr pszDescription; dword dwDescriptionLength; dword Padding1; ptr pData; dword dwDataSize; dword Padding2; ptr pszContentType; ptr prgActionIds; dword dwActionsCount; dword Padding3; ptr prgActionDisplayNames" : _
                    "dword dwStartIndex; dword dwEndIndex; ptr pszDescription; dword dwDescriptionLength; ptr pData; dword dwDataSize; ptr pszContentType; ptr prgActionIds; dword dwActionsCount; ptr prgActionDisplayNames"

            Local $tRange = DllStructCreate($sRangeStruct, $pRanges)
            Local $pLanguageData = DllStructGetData($tRange, "pData")
            Local $iLanguageDataSize = DllStructGetData($tRange, "dwDataSize")

            If $pLanguageData <> 0 And $iLanguageDataSize >= 2 Then
                ; Read the buffer as a Byte Struct to prevent AutoIt from stopping at the first NULL wchar
                Local $tByteBuf = DllStructCreate("byte Data[" & $iLanguageDataSize & "]", $pLanguageData)
                Local $bBytes = DllStructGetData($tByteBuf, "Data")

                ; Convert raw byte buffer to UTF-16 String
                Local $sFullBuffer = BinaryToString($bBytes, 2) ; 2 = UTF-16 Little Endian

                ; Split null-terminated language strings
                Local $aRawList = StringSplit($sFullBuffer, Chr(0), 1)
                Local $iCount = 0

                For $i = 1 To $aRawList[0]
                    If $aRawList[$i] <> "" Then $iCount += 1
                Next

                If $iCount > 0 Then
                    ReDim $aLanguages[$iCount]
                    Local $iIndex = 0
                    For $i = 1 To $aRawList[0]
                        If $aRawList[$i] <> "" Then
                            $aLanguages[$iIndex] = $aRawList[$i]
                            $iIndex += 1
                        EndIf
                    Next
                EndIf
            EndIf
        EndIf

        ; Free Property Bag resources
        DllCall($hElsDll, "none", "MappingFreePropertyBag", "ptr", DllStructGetPtr($tBag))
    Else
        $iError = 3
    EndIf

    ; Free ELS services and close DLL
    DllCall($hElsDll, "none", "MappingFreeServices", "ptr", $pServices)
    DllClose($hElsDll)

    If $iError Then Return SetError($iError, 0, $aEmpty)
    Return $aLanguages
EndFunc   ;==>_WinAPI_DetectLanguage

 

Please, every comment is appreciated!
leave your comments and experiences here!
Thank you very much  :)

 

Edited by ioa747

I know that I know nothing

Posted

Tray activity indicator

Since there is sometimes a slight delay when invoking a translation
(and the lack of visual feedback makes the user experience a bit awkward),
I decided to add an activity indicator.

As this logic evolved into a complete, standalone solution,
I am publishing it separately before integrating it into my translation tool,
as it might prove useful for other projects as well

#include <GUIConstants.au3>
#include <GDIPlus.au3>
#include <WinAPISysWin.au3>

; Global Array for the process HICONs
Global $g_aTrayHIcons[4] = [0, 0, 0, 0]
Global $g_hHiddenWin = 0

_InitTrayIcons() ; Creation of the 4 icons in memory once

_Example()

Func _Example()
    Local $hGUI = GUICreate("Ollama Tray Indicator Test", 350, 150)
    Local $idBtnStart = GUICtrlCreateButton("Start process (Test 3 sec)", 50, 50, 250, 40)
    GUISetState(@SW_SHOW)

    While 1
        Switch GUIGetMsg()
            Case $GUI_EVENT_CLOSE
                ExitLoop

            Case $idBtnStart
                GUICtrlSetState($idBtnStart, $GUI_DISABLE)

                _StartTrayAnimation()
                ; latency simulation (e.g., 3000 ms)
                Sleep(3000)
                _StopTrayAnimation()

                GUICtrlSetState($idBtnStart, $GUI_ENABLE)
                MsgBox(64, "Completed", "The process completed successfully!")
        EndSwitch
    WEnd
EndFunc   ;==>_Example

#Region ; === TRAY ANIMATION FUNCTIONS ===
Func _InitTrayIcons()
    ; Standar Tray Icon
    TraySetIcon("imageres.dll", -120)
    TraySetToolTip("Ollama Translator")

    _GDIPlus_Startup()
    Local $sTitle = StringTrimRight(@ScriptName, 4) & "_HiddenWin"
    AutoItWinSetTitle($sTitle)
    $g_hHiddenWin = WinGetHandle($sTitle)

    ; process tray Icons in 4 rotation angle ; 0xE895 = Sync/Refresh Icon
    $g_aTrayHIcons[0] = _GUICtrlSetGlyph(0, "Segoe MDL2 Assets", 0xE895, 24, 0xFF0078D7, 0) ; 0°
    $g_aTrayHIcons[1] = _GUICtrlSetGlyph(0, "Segoe MDL2 Assets", 0xE895, 24, 0xFF0078D7, 1) ; 90°
    $g_aTrayHIcons[2] = _GUICtrlSetGlyph(0, "Segoe MDL2 Assets", 0xE895, 24, 0xFF0078D7, 2) ; 180°
    $g_aTrayHIcons[3] = _GUICtrlSetGlyph(0, "Segoe MDL2 Assets", 0xE895, 24, 0xFF0078D7, 3) ; 270°

    ; Cleaning up handles upon script exit
    OnAutoItExitRegister("_CleanupTrayIcons")
EndFunc   ;==>_InitTrayIcons

Func _StartTrayAnimation()
    TraySetToolTip("Translating with Ollama...")
    _SetTrayHIcon($g_aTrayHIcons[0])
    AdlibRegister("_RotateTrayIndicator", 150)
EndFunc   ;==>_StartTrayAnimation

Func _RotateTrayIndicator()
    Static $g_iTrayStep = 0
    $g_iTrayStep = Mod($g_iTrayStep + 1, 4)
    _SetTrayHIcon($g_aTrayHIcons[$g_iTrayStep])
EndFunc   ;==>_RotateTrayIndicator

Func _StopTrayAnimation()
    AdlibUnRegister("_RotateTrayIndicator")
    ; Reset to standard icon
    TraySetIcon("imageres.dll", -120)
    TraySetToolTip("Ollama Translator - Ready")
EndFunc   ;==>_StopTrayAnimation

Func _CleanupTrayIcons()
    ; Cleaning up
    For $i = 0 To 3
        If $g_aTrayHIcons[$i] Then _WinAPI_DestroyIcon($g_aTrayHIcons[$i])
    Next
    _GDIPlus_Shutdown()
EndFunc   ;==>_CleanupTrayIcons

Func _SetTrayHIcon($hIcon)
    If Not $hIcon Then Return False

    Local $tNID = DllStructCreate("struct; dword cbSize; hwnd hWnd; uint uID; uint uFlags; uint uCallbackMessage; ptr hIcon; wchar szTip[128]; dword dwState; dword dwStateMask; wchar szInfo[256]; uint uVersion; wchar szInfoTitle[64]; dword dwInfoFlags; endstruct")
    DllStructSetData($tNID, "cbSize", DllStructGetSize($tNID))
    DllStructSetData($tNID, "hWnd", $g_hHiddenWin)
    DllStructSetData($tNID, "uID", 1)
    DllStructSetData($tNID, "uFlags", 0x0002) ; NIF_ICON
    DllStructSetData($tNID, "hIcon", $hIcon)

    Local $aRet = DllCall("shell32.dll", "bool", "Shell_NotifyIconW", "dword", 0x00000001, "ptr", DllStructGetPtr($tNID))
    Return (Not @error And $aRet[0])
EndFunc   ;==>_SetTrayHIcon

Func _GUICtrlSetGlyph($idControl, $sFontName, $iUnicodeDecimalOrHex, $iSize = 32, $iARGBColor = 0xFF000000, $iRotateFlipType = 0, $iGlyphScale = 100)
    Local $hCtrl = GUICtrlGetHandle($idControl)
    If Not $hCtrl Then $hCtrl = $idControl ; Handle passed directly

    If $hCtrl Then
        Local $sClassName = _WinAPI_GetClassName($hCtrl)
        Local $iMsg = 0

        ; Dynamic Style Adjustment to accept HICON handles
        Local $iStyle = _WinAPI_GetWindowLong($hCtrl, -16) ; -16 = GWL_STYLE
        Switch $sClassName
            Case "Button"
                $iMsg = $BM_SETIMAGE
                If BitAND($iStyle, 0x0040) = 0 Then ; If $BS_ICON is missing, apply it
                    _WinAPI_SetWindowLong($hCtrl, -16, BitOR($iStyle, 0x0040))
                EndIf
            Case "Static"
                $iMsg = $STM_SETIMAGE
                ; Clear SS_BITMAP (0x0E) / SS_CENTERIMAGE (0x0200) and enforce SS_ICON (0x03)
                $iStyle = BitAND($iStyle, BitNOT(0x020E))
                _WinAPI_SetWindowLong($hCtrl, -16, BitOR($iStyle, 0x0003))
            Case Else
                Return SetError(1, 0, 0) ; Unsupported control class
        EndSwitch
    EndIf

    ; Canvas Setup
    Local $hBitmap = _GDIPlus_BitmapCreateFromScan0($iSize, $iSize)
    Local $hGraphics = _GDIPlus_ImageGetGraphicsContext($hBitmap)

    _GDIPlus_GraphicsSetSmoothingMode($hGraphics, 2)
    _GDIPlus_GraphicsSetInterpolationMode($hGraphics, $GDIP_INTERPOLATIONMODE_HIGHQUALITYBICUBIC)
    _GDIPlus_GraphicsSetTextRenderingHint($hGraphics, $GDIP_TEXTRENDERINGHINTCLEARTYPEGRIDFIT)

    Local $hBrush = _GDIPlus_BrushCreateSolid($iARGBColor)
    Local $hFamily = _GDIPlus_FontFamilyCreate($sFontName)
    Local $hFont = _GDIPlus_FontCreate($hFamily, $iSize * ($iGlyphScale / 100), 0, 2)
    Local $tLayout = _GDIPlus_RectFCreate(0, 0, $iSize, $iSize)

    Local $hFormat = _GDIPlus_StringFormatCreate()
    _GDIPlus_StringFormatSetAlign($hFormat, 1)
    _GDIPlus_StringFormatSetLineAlign($hFormat, 1)

    ; Surrogate Pairs for Unicode > 0xFFFF (e.g. Emojis)
    Local $sChar = ""
    If $iUnicodeDecimalOrHex > 0xFFFF Then
        Local $iCode = $iUnicodeDecimalOrHex - 0x10000
        Local $iHigh = BitOR(0xD800, BitShift($iCode, 10))
        Local $iLow = BitOR(0xDC00, BitAND($iCode, 0x3FF))
        $sChar = ChrW($iHigh) & ChrW($iLow)
    Else
        $sChar = ChrW($iUnicodeDecimalOrHex)
    EndIf

    _GDIPlus_GraphicsDrawStringEx($hGraphics, $sChar, $hFont, $tLayout, $hFormat, $hBrush)

    ; Resample to Target Size
    Local $hFinalBitmap = _GDIPlus_BitmapCreateFromScan0($iSize, $iSize)
    Local $hFinalGraphics = _GDIPlus_ImageGetGraphicsContext($hFinalBitmap)
    _GDIPlus_GraphicsSetInterpolationMode($hFinalGraphics, $GDIP_INTERPOLATIONMODE_HIGHQUALITYBICUBIC)
    _GDIPlus_GraphicsDrawImageRect($hFinalGraphics, $hBitmap, 0, 0, $iSize, $iSize)

    ; Apply Rotation / Flip if needed
    If $iRotateFlipType Then _GDIPlus_ImageRotateFlip($hFinalBitmap, $iRotateFlipType)

    ; Convert to Win32 HICON (Preserves full Alpha Transparency)
    Local $hIcon = _GDIPlus_HICONCreateFromBitmap($hFinalBitmap)

    If $hCtrl Then
        ; Send HICON message to Control ($IMAGE_ICON = 1)
        Local $hPrevIcon = _SendMessage($hCtrl, $iMsg, 1, $hIcon)

        ; Destroy previous icon if any existed to prevent memory leaks
        If $hPrevIcon Then _WinAPI_DestroyIcon($hPrevIcon)
    EndIf

    ; Cleanup GDI+ Resources
    _GDIPlus_StringFormatDispose($hFormat)
    _GDIPlus_FontDispose($hFont)
    _GDIPlus_FontFamilyDispose($hFamily)
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_GraphicsDispose($hFinalGraphics)
    _GDIPlus_BitmapDispose($hFinalBitmap)
    _GDIPlus_GraphicsDispose($hGraphics)
    _GDIPlus_BitmapDispose($hBitmap)

    Return $hIcon
EndFunc   ;==>_GUICtrlSetGlyph
#EndRegion ; === TRAY ANIMATION FUNCTIONS ===

 

Please, every comment is appreciated!
leave your comments and experiences here!
Thank you very much  :)

 

I know that I know nothing

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...