click here for a plain text version
#include-once
;
; Cor's Essential (Lite Version) v1.2.0
;
; My commonly-used AutoIt functions.

#include <GuiConstants.au3>
#include <Misc.au3>
#include <Date.au3>
#include <GuiToolTip.au3>
#include <WinAPIFiles.au3>
#include <InetConstants.au3>
#include <WinNet.au3>
; for clean ini..
#include <File.au3>

; Setup a few variables..
global const $LOG_LF            = @CRLF    ; for logs/saved text files
global const $MSG_LF            = @LF    ; for dialog boxes

; These make great AutoIt Booleans for sooooooo many reasons..
global const $ON                = $GUI_CHECKED            ; 1
global const $OFF                = $GUI_UNCHECKED        ; 4

global const $UNSET                = $GUI_INDETERMINATE    ; 2
global const $str_enabled        = "enabled"                ; For "Humanizing" those AutoIt Booleans.
global const $str_disabled        = "disabled"
global const $def_msg_time        = 2000

; Handy for Win10 GUI stuff..
global const $COLOR_LTGREY        = 0xD0D0D0

; So we can do GUICtrlSetData($ctrl$state[$some_ON/OFF_bool])
; global $state[5] = [0, $str_enabled, 0, 0, $str_disabled]
; Nice idea, but Human($pref) gets you the same result and is more easily understood.

; I use these variables meaningfully. Check them out!
; You can override these in your app, if required.
global $dump_file$debug_level = 10

global $my_name$my_domain$data_dir$ini_path
global $log_string$log_location
global $max_log_size = 0 ; 0 = no limit, or a number (in MB)

global $monitors_list[1][5]
$monitors_list[0][0] = 0
global $do_tooltips$tip_icon = @ScriptFullPath & ",0"$tip_style = 1, $tooltip_time = 10000

; For UpdateIniFile()..
global $file_array$new_array[1]

; Set to true during Send() operations [SendWait()]
global $am_sending

; Associative arrays in AutoIt? Hells yeah!
global $oMyError = ObjEvent("AutoIt.Error""AAError"; Initialize a COM error handler
; Initialize your array in your script.au3 ...
; global $associative_array
; AAInit($associative_array)

; ***    don't forget to..     AAWipe($associative_array)


; Enable the creation of a list of user token, for save/recall..
global $known_user_tokens[1][2]



; Constants for Special Folders in Windows..

Const $Internet_Explorer                        = 0x1
Const $Programs                                    = 0x2
Const $Control_Panel                            = 0x3
Const $Printers_and_Faxes                        = 0x4
Const $My_Documents                                = 0x5
Const $Favorites                                = 0x6
Const $Startup                                    = 0x7
Const $My_Recent_Documents                        = 0x8
Const $SendTo                                    = 0x9
Const $Recycle_Bin                                = 0xa
Const $Start_Menu                                = 0xb
Const $My_Music                                    = 0xd
Const $My_Videos                                = 0xe
Const $Desktop                                    = 0x10
Const $My_Computer                                = 0x11
Const $My_Network_Places                        = 0x12
Const $NetHood                                    = 0x13
Const $Fonts                                    = 0x14
Const $Templates                                = 0x15
Const $All_Users_Start_Menu                        = 0x16
Const $All_Users_Programs                        = 0x17
Const $All_Users_Startup                        = 0x18
Const $All_Users_Desktop                        = 0x19
Const $Application_Data                            = 0x1a
Const $PrintHood                                = 0x1b
Const $Local_Settings_Application_Data            = 0x1c
Const $All_Users_Favorites                        = 0x19
Const $Local_Settings_Temporary_Internet_Files    = 0x20
Const $Cookies                                    = 0x21
Const $Local_Settings_History                    = 0x22
Const $All_Users_Application_Data                = 0x23
Const $Windows                                    = 0x24
Const $System32                                    = 0x25
Const $Program_Files                            = 0x26
Const $My_Pictures                                = 0x27
Const $User_Profile                                = 0x28
Const $Common_Files                                = 0x2b
Const $All_Users_Templates                        = 0x2e
Const $Administrative_Tools                        = 0x2f
Const $Network_Connections                        = 0x31
Const $CD_Burning_Folder                        = 0x003b


; OpenSpecialFolder($Recycle_Bin) ..

func OpenSpecialFolder($Folder_ID)
    local $ShellObj = ObjCreate("Shell.Application")
    $ShellObj.Open($ShellObj.NameSpace($Folder_ID))
    $ShellObj = 0
endfunc

; func GetSpecialFolderPath($Folder_ID)
    $ShellObj = ObjCreate("Shell.Application")
    $objFolder = $ShellObj.NameSpace($Folder_ID)
    $objFolderItem = $objFolder.ParseName("Desktop")
    $ret  = $objFolder.GetDetailsOf($objFolderItem, 0)
    ; ; $ShellObj = 0
    ; return $ret
; endfunc


; Generic File & Folder Opening function.
;
; If it's a file, opens the given file in the user's editor.
; If it's a directory, opens in Explorer.
;
func OpenSomething($filepath$logfile=false)
debug("OpenSomething: =>" & $filepath & "<=" , @ScriptLineNumber, 7);debug

    if not FileExists($filepath) then
        debug($filepath & " not found!", @ScriptLineNumber, 1);debug
        return false
    endif
    if IsDir($filepath) then
        if not WinExists($filepath) then
            debug("Opening: =>" & $filepath & "<=" , @ScriptLineNumber, 7);debug
            Run("Explorer.exe """ & $filepath & '"')
            ;ShellExecute($filepath)
        else
            WinActivate($filepath)
        endif
    else
        local $user_editor = DeTokenizeString(IniRead($ini_path$my_name, "
editor", "notepad.exe"))
        if $logfile then
            local $log_viewer = DeTokenizeString(IniRead($ini_path$my_name, "
log_viewer", ""))
            if FileExists($log_viewer) then $user_editor = $log_viewer
        endif
        if not Run($user_editor & "
 " & $filepath) then
            debug("
ERROR! I couldn't run:" & '"' & $user_editor & '"', @ScriptLineNumber, 7);debug
            debug("
You need to check your ini settings! ", @ScriptLineNumber, 7);debug
        endif
    endif
endfunc

; Functions that use it..
; These are standard in all apps that use this library..

; Edit ini prefs..
func EditIniFile()
    OpenSomething($ini_path)
endfunc

; Open log file..
func OpenLogFile()
    OpenSomething($log_location, true)
endfunc

; Open debug dump file..
func OpenDumpFile()
    OpenSomething($dump_file, true)
endfunc

; User data dir..
func OpenDataDir()
    OpenSomething($data_dir)
endfunc



; Display a ToolTip message slightly up from the mouse position..
; pass $screen=true to place it instead at the centre-top of the screen.
func DisplayTooltipMessage($message$time=$def_msg_time$title=$my_name$screen=false) $def_msg_time = 2000

 debug($LOG_LF, "
", 7);debug
 debug("
DisplayTooltipMessage(" & $message & "," & $time  & "," & $title & ")", @ScriptLineNumber, 7);debug
 
    local $x$y
    if not $screen then
        local $mousepos = MouseGetPos()
        $x = $mousepos[0]
        $y = $mousepos[1]-16
    else
        $x = (@DesktopWidth/2)-StringLen($message)*3
        $y = 32
    endif
        
    ToolTip($message$x$y$title, 1)
    debug("
Displaying Message: =>" & $message & "<=" , @ScriptLineNumber, 7);debug
    AdLibRegister("
ClearMessage", $time)
    
endfunc

; Clears the above message.
func ClearMessage()
 debug($LOG_LF, "
", 7);debug
 debug("
ClearMessage()", @ScriptLineNumber, 7);debug
    ToolTip("
")
    AdLibUnRegister("
ClearMessage")
endfunc


#cs

Optional ToolTips

    Set a tooltip or don't, dependant on the GLOBAL $do_tooltips variable,
    which you should set first, generally when gathering global prefs.

    This is a drop-in replacement for GUICtrlSetTip() and will happily work in
    this simple state.

    $options: these numbers can be added together..

        1 = Display as Balloon Tip
        2 = Center the tip horizontally under the control

    And so on.
    See below this function for more details.

#ce

func GUICtrlSetTipOptional($control_ID$tip_text$title="
Information:", $icon=$tip_icon$options=$tip_style)

    if $do_tooltips = $OFF then return

    local $style = 0, $hicon$ret
    $control_ID = GUICtrlGetHandle($control_ID)

    if BitAND($options, 1) then $style = $TTS_BALLOON
    $style = BitOr($style$TTS_ALWAYSTIP$TTS_NOPREFIX)


    local $hToolTip = _GUIToolTip_Create(0, $style)

    $options = BitOr($options$TTF_SUBCLASS$TTF_IDISHWND)
    _GUIToolTip_AddTool($hToolTip, 0, $tip_text$control_ID, default, default, default, default, $options, 0)

    ; (400ms) Time the pointer must remain stationary within a tool's bounding rectangle before the window appears.
    _GUIToolTip_SetDelayTime($hToolTip$TTDT_INITIAL, 400)
    ; (5000ms) Time the ToolTip window remains visible if the pointer is stationary within a tool's bounding rectangle.
    _GUIToolTip_SetDelayTime($hToolTip$TTDT_AUTOPOP$tooltip_time)
    ; (150ms) Time it takes for subsequent ToolTip windows to appear as the pointer moves from one tool to another.
    _GUIToolTip_SetDelayTime($hToolTip$TTDT_RESHOW, 150)

    if not StringInStr($icon, "
.") then
        _GUIToolTip_SetTitle($hToolTip$title$icon)
    else
        $hicon = DllStructCreate("
ptr")
        $icon = StringSplit($icon, "
,")
        if $icon[0] > 1 then
            $ret = DllCall("
shell32.dll", "uint", "ExtractIconExW", "wstr", $icon[$icon[0] - 1], "int", -1 * (Int($icon[$icon[0]])), "ptr", 0, "ptr", DllStructGetPtr($hicon), "uint", 1)
            if not @Error and $ret[0] then
                $hicon = DllStructGetData($hicon, 1)
            else
                $hicon = 0
            endif
            _GUIToolTip_SetTitle($hToolTip$title$hicon)
            DllCall("
user32.dll", "none", "DestroyIcon", "handle", $hIcon)
        endif
    endif

    return $hToolTip
endfunc


#cs
[optional] Flags that control the ToolTip display:

    Global Const $TTF_IDISHWND = 0x00000001        - Indicates that $iID is a window or control handle, instead of the ID of the tool
    Global Const $TTF_CENTERTIP = 0x00000002    - Centers the tooltip below the control specified by $iID
    Global Const $TTF_RTLREADING = 0x00000004    - Indicates that text will be displayed in the opposite direction of the parent window (see remarks)
    Global Const $TTF_SUBCLASS = 0x00000010        - Indicates that the control should subclass the tool's window
    Global Const $TTF_TRACK = 0x00000020        - Positions the tooltip window next to the tool to which it corresponds
    Global Const $TTF_ABSOLUTE = 0x00000080        - Positions the window at the same coordinates provided by TTM_TRACKPOSITION. (see remarks)
    Global Const $TTF_TRANSPARENT = 0x00000100    - Causes the control to forward mouse messages to the parent window
    Global Const $TTF_PARSELINKS = 0x00001000    - Indicates that links in the control text should be displayed as links
    Global Const $TTF_DI_SETITEM = 0x00008000

    Default = BitOr($TTF_SUBCLASS$TTF_IDISHWND)

_GUIToolTip_Create STYLE

    $TTS_ALWAYSTIP (0x01)    - Indicates that the ToolTip control appears when the cursor is on a tool even if the ToolTip control's owner window is inactive.
                              Without this style, the ToolTip appears only when the tool's owner window is active.
    $TTS_NOPREFIX (0x02)    - Prevents the system from stripping the ampersand character from a string. Without this style the system automatically strips ampersand characters.
                              This allows an application to use the same string as both a menu item and as text in a ToolTip control.
    $TTS_NOANIMATE (0x10)    - Disables sliding ToolTip animation.
    $TTS_NOFADE (0x20)        - Disables fading ToolTip animation.
    $TTS_BALLOON (0x40)        - Indicates that the ToolTip control has the appearance of a cartoon "
balloon"
    $TTS_CLOSE (0x80)        - Displays a close icon so that the tooltip can be cancelled

    Default: $_TT_ghTTDefaultStyle = BitOr($TTS_ALWAYSTIP$TTS_NOPREFIX)

Icon idx..

 [optional] Set to one of the values below:.
     $TTI_NONE (0) - No icon [default]
     $TTI_INFO (1) - Information icon
     $TTI_WARNING (2) - Warning icon
     $TTI_ERROR (3) - Error Icon
     $TTI_INFO_LARGE (4) - Large Information Icon
     $TTI_WARNING_LARGE (5) - Large Warning Icon
     $TTI_ERROR_LARGE (6) - Large Error Icon
#ce



; Dynamic @Tokens..
;
;
; There are 10 path tokens, 8 time tokens and 2 dimension tokens, as well as one
; clipboard token, one date token and lastly, user-definable variable tokens.
;
; The usual error checking has been omitted here - let the user have some fun!
;
func DeTokenizeString($string$inipath=$ini_path$section=$my_name$pref="
date_format")

 debug($LOG_LF, "
", 7);debug
 debug("
DeTokenizeString(" & $string & ")", @ScriptLineNumber, 7);debug

    if not $string then return $string
    if not StringInStr($string, "
@") then return $string

    ; Replace user @tokens first (they may contain other tokens)
    $string = DeUserTokenizeString($string)
    ; If not, return the string now..
    if not StringInStr($string, "
@") then return $string

    ; Special @ClipBoard @Token..
    $string = StringReplace($string, "
@ClipBoard", ClipGet())

    ; Date/Time @tokens..
    $string = DeTokenizeTimeStrings($string)
    $string = ReplaceDateTimeToken($string)
    $string = ReplaceDateToken($string$inipath$section$pref)

    ; Path @tokens..
    ; We still replace these when $path=false
    $string = ReplacePathToken($string)

    debug("
DeTokenizeString() RETURNING: =>" & $string & "<=", @ScriptLineNumber, 7);debug
    return $string
endfunc

func DeTokenizeTimeStrings($string)
    $string = StringReplace($string, "
@MSec", @MSec)
    $string = StringReplace($string, "
@Sec", @Sec)
    $string = StringReplace($string, "
@Min", @Min)
    $string = StringReplace($string, "
@Hour", @Hour)
    $string = StringReplace($string, "
@MDay", @MDay)
    $string = StringReplace($string, "
@Mon", @Mon)
    $string = StringReplace($string, "
@Year", @Year)
    $string = StringReplace($string, "
@WDay", @WDay)
    $string = StringReplace($string, "
@YDay", @YDay)
    $string = StringReplace($string, "
@Time", _NowTime())

    return $string
endfunc

func ReplaceDateToken($string$inipath=$ini_path$section=$my_name$pref="
date_format")
    $string = StringReplace($string, "
@Date", GetUserDate($inipath$section$pref))
    return $string
endfunc


; DateTime0        >>        DateTime5



func ReplaceDateTimeToken($string)
    local $test = StringRegExp ($string, "
(@DateTime([0-5]))", $STR_REGEXPARRAYMATCH)
    if IsArray($test) and $test[0] and $test[1] then
        local $datetime = _DateTimeFormat(_NowCalc(), Int($test[1]))
        $string = StringReplace($string$test[0], $datetime)
    endif
    return $string
endfunc



; We may be inside DeTokenizeString() here..
func GetUserDate($inipath=$ini_path$section=$my_name$pref="
date_format")
    return DeTokenizeTimeStrings(IniRead($inipath$section$pref, @Year & "
-" & @Mon & "-" & @Mday))
endfunc


func ReplacePathToken($string)
    if $data_dir <> "
" then
        $string = StringReplace($string, "
@DataDir", $data_dir)
        $string = StringReplace($string, "
@ScriptsDir", $data_dir & "\Scripts")
    endif
    $string = StringReplace($string, "
@Documents", @MyDocumentsDir)
    $string = StringReplace($string, "
@MyDocuments", @MyDocumentsDir)
    $string = StringReplace($string, "
@MyDocumentsdir", @MyDocumentsDir)
    $string = StringReplace($string, "
@Desktop", @DesktopDir)
    $string = StringReplace($string, "
@TempDir", @TempDir)
    $string = StringReplace($string, "
@ThisApp", @ScriptFullPath)
    $string = StringReplace($string, "
@KeyBind", @ScriptFullPath)
    $string = StringReplace($string, "
@Me", @UserName)
    $string = StringReplace($string, "
@User", @UserName)
    $string = StringReplace($string, "
@UserName", @UserName)
    $string = StringReplace($string, "
@ConfigEditor", GetParent(@ScriptFullPath) & "\" & $my_name & "Config.exe")

    $string = StringReplace($string, "
@HomeDir", @HomeDrive & @HomePath)
    $string = StringReplace($string, "
@HomeDrive", @HomeDrive)
    $string = StringReplace($string, "
@HomePath", @HomePath)
    $string = StringReplace($string, "
@ProgramFiles", @ProgramFilesDir)
    $string = StringReplace($string, "
@UserDir", @UserProfileDir)
    $string = StringReplace($string, "
@UserProfileDir", @UserProfileDir)
    $string = StringReplace($string, "@outputfile", $outputfile)
    return $string
endfunc

;
; Debugging is like particles and waves. With a shitload of debugging lines, you
; can't read the code, but you can read the debug output OK. It's code or debug
; output, never both at once (unless you leave your debug output open and then
; remove the debug lines to study both, but hey! It's nice philosophy!).
;


; User @tokens.
;
; 16 AlphaNumeric Characters Maximum.
;
; User variables will be named "var_VARIABLE_NAME"
; They will be declared using Assign(), with the Global flag.
;
;2do.. dumb - doesn't know there are no user @tokens. or what @token are starndard @token
;2do - take this out of main Detokenize and call separately when required.
;
func DeUserTokenizeString($string)

 debug($LOG_LF, "
", 7);debug
 debug("
DeUserTokenizeString(" & $string & ")", @ScriptLineNumber, 7);debug
 debug_PrintArray($known_user_tokens, "
$known_user_tokens:", @ScriptLineNumber, 7);debug

    if not StringInStr($string, "
@") then return $string

    local $this_token$maybe_token
    local $split_string = StringSplit($string, "
@")

    for $i = 2 to $split_string[0] ; first element is /definitely/ not a @token.

        $this_token = "
"
        $maybe_token = $split_string[$i]

        debug("
$maybe_token: =>" & $maybe_token & "<=", @ScriptLineNumber, 7);debug

        ; Remove everything after the alphanumerics and underscores..
        $this_token = StringRegExpReplace($maybe_token, "
(\w+).*", "\1", 1)
        debug("
Test $this_token: =>" & $this_token & "<=", @ScriptLineNumber, 7);debug

        ; We got the length of the first Alphanumeric string.
        ; Now test if that is a valid @token..

        ; This @token assigned already (we double-check here)..
        if InArray2D($known_user_tokens$this_token, 0) and IsDeclared ("
var_" & $this_token) then

            debug("
SUCCESS!! Found USER Variable: =>@" & $this_token & "<=", @ScriptLineNumber, 7);debug
            ; Replace the @token with the user's variable value..
            local $token_val = Eval("
var_" & $this_token)
            debug("
Assign USER Value: " & $this_token & " => " & $token_val & "<=", @ScriptLineNumber, 7);debug
            $split_string[$i] = StringReplace($maybe_token$this_token$token_val)

        else ; no var of that name assigned., or empty ("@@")!!.
            debug("
No Such User Token as @" & $this_token & "!", @ScriptLineNumber, 7);debug
            ; Continue on, use it as-is (was) replacing the @ we took..
            $split_string[$i] = "
@" & $maybe_token

        endif

    next

    ; Pop all the pieces back together again..
    debug_PrintArray($split_string, "
$split_string:", @ScriptLineNumber, 7);debug
    $string = StringJoin($split_string, "
")
    debug("
DeUserTokenizeString() RETURNING: =>" & $string & "<=", @ScriptLineNumber, 7);debug

    return $string

endfunc


; Tokenize a string (create macro tokens).
; Start with longest paths..
;
func TokenizeString($string$path=true)
    if not $string then return $string
    if $path then $string = FixPathSlashes($string)
    if $data_dir <> "
" then
        $string = StringReplace($string$data_dir & "
\Scripts", "@ScriptsDir")
        $string = StringReplace($string$data_dir, "
@DataDir")
    endif
    $string = StringReplace($string, @DesktopDir, "
@Desktop")
    $string = StringReplace($string, @MyDocumentsDir, "
@MyDocuments")
    $string = StringReplace($string, @HomeDrive & @HomePath, "
@HomeDir")
    $string = StringReplace($string, @UserProfileDir, "
@UserDir")
    $string = StringReplace($string, @TempDir, "
@TempDir")
    $string = StringReplace($string, @ScriptFullPath, "
@ThisApp")
    $string = StringReplace($string, @ProgramFilesDir, "
@ProgramFiles")
    return $string
endfunc




; Currently Not In Use.

; Fix potentially messed-up slashes..
;2do - maybe check for ":" or "\\" , i.e. first ensure it /is/ a path.
func FixPathSlashes($string)

    ; we need to think about this function!
    return $string

    if StringInStr($string, "
://") then return $string ; not for URLs

    ; if not StringInStr($string, ":") and not  ....  then return $string

    $string = StringReplace($string, "
/", "\")
    $string = StringReplace($string, "
\\", "\")

    return $string
endfunc




; Conditional Right/Left Trim
;
; If the far-right character(s) is(are) such-and-such, remove it(them).
; Handy for normalizing path ends and other stuff.
;
; Because we need this sort of thing quite a lot..
;
;    if StringRight($string, 1) = "\" then $string = StringTrimRight($string, 1)
;
; Functions work on the string byref; you simply do:
;
;    CRT("my string")
;
; The idea of the names is; quick to type.
;
; These functions return true if any changes were made, otherwise false, so you
; can optionally set a variable from their return value..
;
;    $contains_modifier = CRT("my string")
;
; or use them in conditional statements..
;
;    if CRT("my string") then ...
;
; So..

func CRT(byref $string$character="
\")
    local $ret = false, $char_num = StringLen($character)
    if StringRight($string$char_num) == $character then
        $ret = true
        $string = StringTrimRight($string$char_num)
            if StringRight($string$char_num) == $character then return CRT($string$character)
    endif
    return $ret
endfunc

; And for the left-hand side..
func CLT(byref $string$character="
\")
    local $ret = false, $char_num = StringLen($character)
    if StringLeft($string$char_num) == $character then
        $ret = true
        $string = StringTrimLeft($string$char_num)
            if StringLeft($string$char_num) == $character then return CLT($string$character)
    endif
    return $ret
endfunc

; And now both together..
; Note different default character.
; This double-function wrapper is mostly used for prefs lists..
func CBT(byref $string$character="
,")
    CLT($string$character)
    CRT($string$character)
endfunc




; Test if a file can be written to a location..
func TestFileWrite($tmpfile)
    local $t = FileWrite($tmpfile, "
")
    if $t = -1 then return false
    return true
endfunc




; BubbleSort()
;
; A very basic, but very handy array sort.
; Designed for small lists; menus and such-like, where it excels.
; Purist can kiss my butt, this is simple, and elegant.
;
; NOTE: this is for "AutoIt Arrays", where the first value is
; the total number of elements. Basically, it will be left as-is.
; Also note, this is a case-insensitive search.
;
; To use..        BubbleSort($my_array)
;
func BubbleSort(byref $bs_array)
    for $i = uBound($bs_array)-1 to 1 step -1
        for $j = 2 to $i
            if $bs_array[$j-1] > $bs_array[$j] then
                local $temp = $bs_array[$j-1]
                $bs_array[$j-1] = $bs_array[$j]
                $bs_array[$j] = $temp
            endif
        next
    next
    return $bs_array
endfunc
;
; By the way, it should be noted that I have no idea why this works.
; Technically, using strings as numbers in AutoIt should implicitly call
; Number() and all "words" would be 0. But it does work, and works great!
; Hmmm.



; InsertionSort()
;
; Slightly different results, here.
; Simple algo, good for lists of numbers, and pretty fast, too..
;
func InsertionSort(byref $is_array)
    for $i = 1 to uBound($is_array)-1
        local $index = $is_array[$i]
        local $j = $i
        while $j > 1 and $is_array[$j-1] > $index
            $is_array[$j] = $is_array[$j-1]
            $j -= 1
        wend
        $is_array[$j] = $index
    next
    return $is_array
endfunc
; mail me if you want an AutoIt version of the QuickSort algo




; ShellSort()
; Invented by Donald Shell, 1959.
;
; More efficient than a BubbleSort.
; Good for repetative sorting of smaller lists.
; Neck-and-neck with InsertionSort in most of my tests.
;
; Note: This is designed for "AutoIt Arrays" (first element it the total).
;
func ShellSort(byref $some_array)

    local $increment = 1
    while $increment > 0

        for $i = 2 to uBound($some_array)-1
            local $j = $i
            local $temp = $some_array[$i]

            while $j >= $increment and $some_array[$j-$increment] > $temp
                $some_array[$j] = $some_array[$j-$increment]
                $j = $j - $increment
            wend
            $some_array[$j] = $temp
        next

        if $increment/2 <> 0 then ; ?
            $increment = int($increment/2)
        elseif $increment = 1 then
            $increment = 0
        else
            $increment = 1
        endif
    wend
    return $some_array
endfunc




; MaxMax()
;
; Evaluates two or three numbers and returns the highest.
; Because _Max($a,(_Max($b,$c)) is just plain ugly.
;
func MaxMax($num1$num2$num3=1.7E-308)
    if not IsNumber($num1) or not IsNumber($num2) or not IsNumber($num3) then return
    if $num1 > $num2 then
        if $num3 > $num1 then return $num3
        return $num1
    else
        if $num3 > $num2 then return $num3
        return $num2
    endif
endfunc



; MinMin()
;
; Evaluates two or three numbers and returns the smallest.
;
func MinMin($num1$num2$num3=1.7E+308)
    if not IsNumber($num1) or not IsNumber($num2) or not IsNumber($num3) then return
    if $num1 > $num2 then
        if $num3 < $num2 then return $num3
        return $num2
    else
        if $num3 < $num1 then return $num3
        return $num1
    endif
endfunc


; String format, a-la C..
;
func printf($format$var1$var2=-1, $var3=-1)
    if $var2=-1 then
        return StringFormat($format$var1)
    else
        return StringFormat($format$var1$var2$var3)
    endif
endfunc




; Repeats a given string a given number of times..
;
func StringRepeat($string$count)
    $count = Int($count)
    if not $count then return "
"
    if not StringLen($string) then return SetError(-1, 0, "
Invalid Parameters sent.")
    local $return
    while $count
        $return &= $string
        $count -= 1
    wend
    return $return
endfunc






; A pipe is the delimiter used in ListViews, to separate columns.
; Obviously, literal pipes in our ListViews would cause all sorts of issues.
;
; So, when adding them to the ListView, we translate them into this identical-
; looking Unicode character. And back again when pulling them out. Simple.
;
; This is all completely transparent to the user, unless they happen across
; these comments somehow, in which case the game is up and I'm sure they will be
; absolutely furious.
;
; On Hebrew systems it probably shows up as a Star of David and then the joke is
; on me.
;
; These two functions are for when, for some reason, we can't do..
;
;    AutoItSetOption("GUIDataSeparatorChar", "") ; A Record Separator, Chr(30).
;
; FYI, good ole ASCII provides..
;
;    Chr(28):        File separator
;    Chr(29):        Group separator
;
; ..in addition to the most appropriate separator for ListViews..
;
;    Chr(30):        Record separator
;
; By the way, if you /don't/ see a cute box with "RS" in it (or similar cute
; character), I can only assume you are using some piece-of-shit text editor not
; fit to view this code. In that case, go upgrade to something decent, eh!
; Notepad++ is free!    >>    https://notepad-plus-plus.org/
;
func Str2LV($string)
    return StringReplace($string, "
|", "ǀ")        ; ǀ <> | !        [U+01C0]
endfunc
func LV2Str($string)
    return StringReplace($string, "
ǀ", "|")
endfunc
;
; NOTE: If you grab a task from the ListView in your code and forget to use this
; translations, you will be in for a debugging nightmare trying to figure out
; why "ǀfoo" is not equal to "|foo" when, you know, they LOOK THE SAME ...




; CleanPath()
;
; Clean-up potentially problematic file path characters..
;
func CleanPath($string)
    $string = StringReplace($string, " ", "_")
    $string = StringReplace($string, "
|", "~")
    $string = StringReplace($string, '"
', "'")
    $string = StringReplace($string":""~")
    $string = StringReplace($string"*""~")
    $string = StringReplace($string"/""~")
    $string = StringReplace($string"\""~")
    $string = StringReplace($string">""~")
    $string = StringReplace($string"<""~")
    $string = StringReplace($string"?""~@")
    return $string
endfunc


; CleanPrefName()
;
; Make a string safe to use in an ini [section pref name]..
;
func CleanPrefName($string)
    $string = CleanPath($string)
    $string = StringReplace($string" ""_")
    $string = StringReplace($string, '"', "_")
    $string = StringReplace($string, "
'", "_")
    $string = StringReplace($string, "
=", "_")
    $string = StringReplace($string, "
[", "_")
    $string = StringReplace($string, "
]", "_")
    $string = StringReplace($string, "
(", "_")
    $string = StringReplace($string, "
)", "_")
    $string = StringReplace($string, "
.", "_")
    $string = StringReplace($string, "
,", "_")
    $string = StringReplace($string, "
-", "_")
    return $string
endfunc



; BaseName()
;
; Get the base name of a file from a full path..
; In other words; returns the path name without the folders part (ie. file name only)

func BaseName($bn_path)
    $bn_path = StringReplace($bn_path, "
/", "\")
    CRT($bn_path)
    local $parts = StringSplit($bn_path, "
\")
    return $parts[$parts[0]]
endfunc



; GetParent()
;
; Returns the parent directory of a given file or directory path..

func GetParent($gp_dir)
    local $gp_full_path = StringSplit($gp_dir, "
\") ; array
    return StringTrimRight($gp_dir, StringLen($gp_full_path[$gp_full_path[0]]) + 1) ; 1 = "\"
endfunc



; GetExtension()
;
; Returns the extension of a file name, e.g. "txt".
; If the file has no extension, returns a blank "" string.

func GetExtension($some_name)
    local $parts = StringSplit($some_name, "
.")
    local $e = $parts[$parts[0]]
    if $e <> $some_name and not StringInStr($e, "
\") then  ; "." was not found - extensionless file, possibly in a path with a dot
        return $e
    else
        return "
"
    endif
endfunc


; RemoveExtension()
;
; Removes the extension of a file name (including the dot ".")
; Requires the above functions.
; Also works for folders.
;
func RemoveExtension($some_name)
    local $add = 0
    if StringInStr(BaseName($some_name), "
.") then $add = 1 ; might not have an extension
    return StringTrimRight($some_name, StringLen(GetExtension($some_name)) + $add)
endfunc


; CleanName()
;
; Returns a file name minus the path AND the extension.
; Basically does two of the above functions, all-in-one..
;
func CleanName($some_name)
    return RemoveExtension(BaseName($some_name))
endfunc


; SimpleProper()
;
; Very simple version of StringProper
; Capitalizes the first letter of a given ASCII string.
;
func SimpleProper($string)
    local $first_chr = Asc(StringLeft($string, 1))
    if $first_chr < 97 or $first_chr > 122 then return $string
    $string = Chr($first_chr-32) & StringMid($string, 2)
    return $string
endfunc


; A2RBool()
;
; Convert my AutoIt boolean ($ON=1,$OFF=4) to regular boolean (on=1,off=0)
; Handy for converting prefs to functions that require regular normal booleans,
; e.g..
;
;     WinSetOnTop($GUI, "", A2RBool($always_on_top))
;
; Basically converts 4 into 0 and anything else into 1.
;
func A2RBool($autoit_boolean)
    if $autoit_boolean == $OFF then return 0
    return 1
endfunc


; MakeTheParents()
;
; If the parent directories of a file do not exist, create them..
;
func MakeTheParents(byref $file_path)
    if not FileExists(GetParent($file_path)) then
        DirCreate(GetParent($file_path))
    endif
endfunc


; MakeAFile()
;
; If a file does not exist, create it..
;
func MakeAFile(byref $file_path)
    if not FileExists($file_path) then
        local $target_file = FileOpen($file_path, 2)
        FileClose($target_file)
    endif
endfunc



#cs
 AppendFileData()

 Append the contents of $data_file to the end of $target_file..

 $flags are the usual FileOpen flags, which apply to opening $data_file.
 Obviously you don't want to send any WRITE flags. Nincompoop!
 But this is handy for forcing binary reading, or whatever..

 Useful flags:

    $FO_READ (0) = Read mode (default)    (Auto-Detects Encoding)
    $FO_BINARY (16) = Force binary (byte) data mode.
    $FO_UNICODE or $FO_UTF16_LE (32) = Use Unicode UTF16 Little Endian reading and writing mode.
    $FO_UTF16_BE (64) = Use Unicode UTF16 Big Endian reading and writing mode.
    $FO_UTF8 (128) = Use Unicode UTF8 (with BOM) reading and writing mode.
    $FO_UTF8_NOBOM (256) = Use Unicode UTF8 (without BOM) reading and writing mode.
    $FO_ANSI (512) = Use ANSI reading and writing mode.
    $FO_UTF16_LE_NOBOM (1024) = Use Unicode UTF16 Little Endian (without BOM) reading and writing mode.
    $FO_UTF16_BE_NOBOM (2048) = Use Unicode UTF16 Big Endian (without BOM) reading and writing mode.
    $FO_FULLFILE_DETECT (16384) = When opening for reading and no BOM is present, use the entire file to
                                  determine if it is UTF8 or UTF16. If this is not used then only the
                                  initial part of the file (up to 64KB) is checked for performance reasons.
#ce
func AppendFileData($target_file$data_file$flags=0)

    $data_file = FileOpen($data_file$flags)
    local $file_data = FileRead($data_file)
    FileClose($data_file)

    $target_file = FileOpen($target_file, 1)
    local $return = FileWrite($target_file$file_data)
    FileClose($target_file)

    return $return

endfunc




; GetWinDrive()
;
; Returns the parent DRIVE of a given path..
;
; "I:\temp\test\musk.mp3" >> returns: I
;
; Invalid (or UNC) paths return an empty string.
;
func GetWinDrive($gd_path)
    select
        case StringMid($gd_path, 2, 1) == "
:"
            return StringLeft($gd_path, 1)
        case else
            return "
"
    endselect
endfunc


; LnkToReal()
;
; Convert shortcut to its real target path.
; If link is invalid, returns false.
; If file path sent is not a .lnk file, returns the path that was sent.
; So you can safely insert it into any user's path choosing operation.
;
func LnkToReal($file)
    if GetExtension($file) = "
lnk" then
        local $lnk_arr = FileGetShortCut($file)
        if IsArray($lnk_arr) and FileExists($lnk_arr[0]) then return $lnk_arr[0]
    else
        return $file
    endif
    return false
endfunc





; DateToDayOfWeekISO all-in-one..
;
func DateToDayOfWeekISO($iYear$iMonth$iDay)
    local $i_aFactor$i_yFactor$i_mFactor$i_dFactor
    $i_aFactor = Int((14 - $iMonth) / 12)
    $i_yFactor = $iYear - $i_aFactor
    $i_mFactor = $iMonth + (12 * $i_aFactor) - 2
    $i_dFactor = Mod($iDay + $i_yFactor + Int($i_yFactor / 4) - Int($i_yFactor / 100) _
                                + Int($i_yFactor / 400) + Int((31 * $i_mFactor) / 12), 7)
    if $i_dFactor >= 1 then return $i_dFactor - 1
    return 6
endfunc



; FolderIsEmpty()
;
; Empty the folder is?
;
; Returns 0 is folder is not empty
; Returns 1 if the given folder path is empty
; Returns 2 if the path does not exist
;
func FolderIsEmpty($fie_folder)
    if not FileExists($fie_folder) then return 2
    local $ret = 0
    local $fie_search_files = FileFindFirstFile($fie_folder & "
\*.*")
    if $fie_search_files = -1 then ; No files! Good!
        if @error = 1 then
            $ret = 1
        else
            $ret = 2
        endif
    endif
    FileClose($fie_search_files)
    return $ret
endfunc




; IsDir()
;
; Is the given path a directory?

func IsDir($some_path)
    ; simple but slow..
    ; (although the fastest of all the methods I've tried - a lot)
    return StringInStr(FileGetAttrib($some_path), "
D") ;, 2 = slower!
endfunc




; ReadDir()
;
; Open a folder and return files with a particular $extension (no "." dot)
; as an array of file names. See RecurseDir() (below) for more functionality.
;
; NO Wild-Cards Allowed.
;
; Alternatively, supply full file name. Wilcards allowed.

func ReadDir($folder$extension$full_filename="
")

    ; AutoIt array handling is basic, to say the least!
    local $files[1]
    local $new_array[2]
    local $filename = "
*."
    if $full_filename then
        $filename = $full_filename
        $extension = "
"
    else
        CLT($extension, "
.")
    endif

    local $init_dir = $folder & "
\" & $filename & $extension

    ; create a "search handle" of all the *.$extension files in the folder
    local $search_files = FileFindFirstFile($init_dir)

    if $search_files = -1 then
        FileClose($search_files)
        $search_files = FileFindFirstFile($init_dir)
    endif
    local $i = 1
    do
        local $tmp = FileFindNextFile($search_files)
        $files[$i-1] = $tmp
        $i += 1 ; like php, same as $i = $i + 1
        redim $files[$i]
    until @Error

    FileClose($search_files; close the search handle

    ; this removes the extraneous/empty elements from the array
    $i = 2
    for $this_file in $files
        local $entry = StringStripWS($this_file, 3)
        if $entry then
            redim $new_array[$i]
            $new_array[0] = $i-1
            $new_array[$i-1] = $entry
            $i = $i + 1
        endif
    next

    ; wipe this array..
    $files = 0

    if $new_array[0] = "
" then return 0

    ; return the array of filenames..
    return $new_array

endfunc


;
; RecurseDir()    v2.3
;
; Recursively search a directory structure for files matching a pattern
; and return its files as an array of file paths, the first value being the
; total number of file paths in the array (a-la "AutoIt Array").
;
; I spent some time testing many different routines. _GetFileList, by Jos van
; der Zande, always gave me the best results, and nicely coded, but around ten
; times slower (52s) than Larry (and Beerman's) recursion routines (4.9s) when
; recursing my home folder.**
;
; ** 1000 folders, 4773 files, 702MB. [cpu:1.3GHz, 640MB RAM] at time of writing
;
; This function is based on _GetFileList, but after a few hacks and tweaks is now
; more than fifteen times faster than the original.** The results are still as good,
; but instead of 50+ seconds to recurse my home folder, it now takes 3.3 seconds,
; making it fastest and bestest of all the AutoIt recursion routines! *tic*
;
; Note: you can now supply multiple file masks (needed for backup). It makes a lot
; of sense to grab any other extensions while we are in a directory.
; The delimiter is a comma..
;
;        RecurseDir("C:\Program Files", "*.reg,*.ini,*.cfg")
;
; When searching for multiple masks the speed improvements are *staggering*, and
; logarithmic; basically multiply the number of masks. For instance, a backup of
; all the pref files in my Program Files folder is scanned, copied, zipped and
; completed in around a minute. pretty good, considering it's over 12GB; all
; tools, no big game installs, and ten file masks to search.
;
; The optional third parameter "$top_level_only" tells RecurseDir() to only
; return matches for files in the top level directory, no deeper. (true/false)
; see also ReadDir() above.
;
; You can also supply an optional fourth parameter which is a string; the path to
; a "dump file" to dump (log) the paths of all matched files; for debugging only,
; because it will slow things down some.
;
; In v2.2 you can also supply an optional fifth parameter, "$return_dirs" which
; will do exactly that, returning an AutoIt array of all the directories in the
; path, and *only* the directories. (true/false)
;
; The optional 6th parameter ($max) is the maximum limit for returned paths,
; which is normally 1,000,000 (one million). Bigger sizes use more memory, roughly
; 10MB per million (for the initial *empty* array - before it gets filled with
; values).  The absolute maximum size for an array is around 8,388,606, so don't
; go above that, or you will get errors. Also, ensure this value is never zero.
;
; This function gets used a lot in my apps.
;
; **    A lot to do with using the "&=" operator. Those wee differences mount up.
;        Probably that operator wasn't available when the code was first written.
;

;global $quit = false

func RecurseDir($dir$mask$top_level_only=false, $dump="
", $return_dirs=false, $mx=1000000)

    local $n_dirnames[$mx]    ; maximum number of directories which can be scanned
    local $n_dircount = 0    ; ^ could be set much higher, if required
    local $n_file
    local $n_search
    local $n_tfile
    local $file_array
    local $filenames
;    local $filecount
    local $dircount = 1

    ; if there was an "\" on the end of the given directory, remove that..
    CRT($dir)
    debug("
$dir: =>" & $dir & "<=", @ScriptLineNumber, 7);debug

    $n_dirnames[$dircount] = $dir

    if not FileExists($dir) then return 0

    while $dircount > $n_dircount ; keep on looping until all directories are scanned..

;        if $quit = 1 then return

        $n_dircount += 1
        $n_search = FileFindFirstFile($n_dirnames[$n_dircount] & "
\*.*")
        if @Error then debug("
@Error: =>" & @Error & "<=", @ScriptLineNumber, 7);debug
        debug("
$n_search: =>" & $n_search & "<=", @ScriptLineNumber, 7);debug

        while true  ; find all subdirs in this directory and store them in a array..
            $n_file = FileFindNextFile($n_search)
            if @Error then exitloop
            ; skip directory references..
            if $n_file = "
." or $n_file = ".." then continueloop

            $n_tfile = $n_dirnames[$n_dircount] & "
\" & $n_file

            ; if it's a directory, add it to the list of directories to be processed..
            if StringInStr(FileGetAttrib($n_tfile ), "
D") and not $top_level_only then
                $dircount += 1
                $n_dirnames[$dircount] = $n_tfile
            endif
        wend
        FileClose($n_search)

        ; multiple masks..
        if StringInStr($mask, "
,", 2) then
            local $mask_array = StringSplit($mask, "
,")
        else ; or else create a dummy array..
            local $mask_array[2] = [1, $mask]
        endif

        ; loop through the array of masks..
        for $mask_c = 1 to $mask_array[0]
            ; find all files that match this mask..
            $n_search = FileFindFirstFile($n_dirnames[$n_dircount] & "
\" & $mask_array[$mask_c] )
            if $n_search = -1 then continueloop

            while true
                $n_file = FileFindNextFile($n_search)
                if @Error then exitloop ; end of dir
                if $n_file = "
." or $n_file = ".." then continueloop

                $n_tfile = $n_dirnames[$n_dircount] & "
\" & $n_file
                if not StringInStr(FileGetAttrib( $n_tfile ), "
D") then
;                    $filecount += 1
                    $filenames &= $n_tfile & @LF
                endif
            wend
            FileClose($n_search)
        next
    wend

    ; flip to a string and back to remove extraneous entries
    ; this is quicker than redimming on every loop
    if $return_dirs then
        local $tmp_str = "
"
        local $i = 1
        while $n_dirnames[$i] <> "
"
            $tmp_str &= $n_dirnames[$i] & "
|"
            $i += 1
        wend
        $tmp_str = StringTrimRight($tmp_str, 1)
        $n_dirnames = StringSplit($tmp_str, "
|")
        return $n_dirnames
    endif

    $filenames = StringTrimRight($filenames, 1)
    if $filenames = "
" then return 0
    $file_array = StringSplit($filenames, @LF)

    ; dump results to a file..
    if $dump then
        local $dumpfile = FileOpen($dump, 2)
        FileWrite($dumpfile$filenames)
        FileClose($dumpfile)
    endif
    return($file_array)
endfunc



#cs

    IniReadCheckBoxValue()

    Slightly altered for BlobDrop and its "
tristate" checkboxes..

    This function "
transforms" an AutoIt or human unchecked value (4 or 0,
    respectively), into plain old 4, which AutoIt can understand. this
    function is intended as a drop-in replacement for the IniRead command,
    simply replace the function name. Of course, it's only useful when
    reading checkbox values, e.g..

        $big_switch = IniReadCheckBoxValue($ini_path$my_name, "
big_switch", $GUI_UNCHECKED)

    However I've gotten into the habit of using $GUI_CHECKED and
    $GUI_UNCHECKED as general booleans, which has proven to be very
    effective, especially when coupled with these two functions. I have them
    set to $ON and $OFF.


    global const $ON        = $GUI_CHECKED            ; 1
    global const $OFF        = $GUI_UNCHECKED        ; 4

    Why?

    Windows uses 1 as the value for checked checkboxes, and 4 as the value
    for unchecked checkboxes.* This makes passing the values directly in and
    out of ini files undesirable, because "
4" is not a logical value, and
    most humans would expect it to be 0 (zero). The following two functions
    act as interface between the program and the ini file, making sense of
    the "
human" equivalents. Other "human" values are also understood, just
    in case.


    Using these custom booleans enables us to pass values directly back-and-
    forth between user settings and GUI Controls and tray menu items DIRECTLY,
    e.g..

    TrayItemSetState($tray_menu$some_pref)
                                                                            #ce
func IniReadCheckBoxValue($rcbv_inifile$rcbv_section$rcbv_key$rcbv_default$three_state=false)

    ; IF key NOT FOUND, returns DEFAULT
    ; IF key BLANK (empty) returns INTERMEDIATE

    local $ircbv_val = IniRead($rcbv_inifile$rcbv_section$rcbv_key, "
---")

    if $ircbv_val = "
---" then $ircbv_val = $rcbv_default
    if $three_state and $ircbv_val = "
" then return $GUI_INDETERMINATE

    switch $ircbv_val
        case $GUI_UNCHECKED    ; 4
            return $GUI_UNCHECKED
        case "
false", "off", "no", "not", "nope", "nay", "nay!", "nah", "nah!", "no way!", "no sir!", "negative", "neg", "no!", "disable", "disabled"
            return $GUI_UNCHECKED
        case "
true", "on", "yes", "yes!", "yay", "yay!", "yup", "hell yes!", "indeed", "yes sir!", "yessir!", "affirmative", "cool", "enable", "enabled"
            return $GUI_CHECKED
        case "
2", 2, "unset", "-"
            return $GUI_INDETERMINATE
        case "
0"
            return $GUI_UNCHECKED
        case "
1", $GUI_CHECKED ; 1
            return $GUI_CHECKED
        case else ; some special value
            return $ircbv_val
    endswitch
endfunc

func ProcessReadHumanCheckBoxValue($some_string)
    switch $some_string
        case $OFF    ; 4
            return $OFF
        case "
false", "off", "n", "no", "not", "nope", "nay", "nay!", "nah", "nah!", "no way!", "no sir!", "negative", "neg", "no!"
            return $OFF
        case "
true", "on", "y", "yes", "yes!", "yay", "yay!", "yup", "hell yes!", "indeed", "yes sir!", "yessir!", "affirmative", "cool"
            return $ON
        case "
0"
            return $OFF
        case $ON ; 1
            return $ON
        case else
            ; one last try for positive! ..
            if StringLeft($some_string, 1) = "
y" then
                return $ON
            else
                return $OFF
            endif
    endswitch
endfunc


; IniWriteCheckBoxValue()
;
; This function transforms an AutoIt checkbox value into a 'human' unchecked value (4 into 0, basically)
; this is intended as a drop-in replacement for the IniWrite command, simply replace the function name.
; Of course, it's only useful when writing checkbox values that will be read by 'IniReadCheckBoxValue'
; above. Instead of 0, you can also write "no", "off", or whatever, by passing the optional 5th parameter..

func IniWriteCheckBoxValue($wcbv_inifile$wcbv_section$wcbv_key$wcbv_val$tru_val="
true", $fal_val="false")
    switch $wcbv_val
        case $GUI_CHECKED
            $wcbv_val = $tru_val
        case $GUI_UNCHECKED
            $wcbv_val = $fal_val
        case $GUI_INDETERMINATE
            $wcbv_val = "
"
    endswitch
    if $wcbv_val then
        IniWrite($wcbv_inifile$wcbv_section$wcbv_key$wcbv_val)
    else
        ; we delete empty values - then they are "unset" proper!
        IniDelete($wcbv_inifile$wcbv_section$wcbv_key)
    endif
endfunc

; The above functions are useful until you create a proper preferences interface for your application!
; actually, I tend to use $GUI_CHECKED and $GUI_UNCHECKED as general-purpose booleans these days.



; func ProcessWriteHumanCheckBoxValue($some_string)

    ; switch $some_string
        ; case $ON
            ; return "enabled"
        ; case $OFF
            ; return "disabled"
        ; case else
            ; return $some_string
    ; endswitch
; endfunc

; For display purposes..
func ProcessWriteHumanCheckBoxValue($some_string$on_str=$str_enabled$off_str=$str_disabled$indet_str="
unset")
    switch $some_string
        case $ON
            return $on_str
        case $OFF
            return $off_str
        case $GUI_INDETERMINATE, "
"
            return $indet_str
        case else
            return $some_string
    endswitch
endfunc

; Alias..
func Human($some_string$on_str=$str_enabled$off_str=$str_disabled,$indet_str="
unset")
    return ProcessWriteHumanCheckBoxValue($some_string$on_str$off_str,$indet_str)
endfunc




;
; IniKeyExists()
;
; Feed it a 2-dimensional array (as supplied by IniReadSeaction)
; if the key exists, IniKeyExists() returns its value; if not, it returns false.
; The search is case-insensitive, but otherwise must be an exact (not partial) match.
; sometimes handy.
;
func IniKeyExists(byref $search_array$search_string)
    if not IsArray($search_array) then
        return false
    endif
    for $i = 1 to $search_array[0][0]
        if $search_string = $search_array[$i][0] then
            return $search_array[$i][1]
        endif
    next
    return false
endfunc



; Simply creates an empty ini section you can later fill..
;
func CreateEmptyIniSection($inipath$inisection)
    IniWrite($inipath$inisection, "
", "")
    IniDelete($inipath$inisection, "
")
endfunc







#cs
    UpdateIniFile()

    Add any new prefs from our master ini file.

    Keep all user's old prefs (see forced_settings for exceptions).

    Optionally clean out all info comments from new ini file.

    Optionally copy certain sections "
RAW".

        UpdateIniFile([clean comments {BOOL}[, forced prefs {STRING(comma-delimited list)}[, Raw Section {STRING}]]])

    NOTE: This function isn't exactly 100% portable. Your default ini file MUST
    be:

        ./resources/Default.ini

    In other words a folder next to this source called "
resource" with
    "
Default.ini" inisde it. You must also insure that $ini_path and $my_name
    are correctly set.


    The first parameter controls whether nor not the updated ini file will
    contain all the comments from the master ini file. For new users, these
    are probably very handy. For old-hats, they are more likely annoying. So
    let the user disable them, if required. Send $ON or $OFF, e.g..


        local $clean_ini = IniReadCheckBoxValue($ini_path$my_name, "
clean_ini", $OFF)
        if not $installed_ini then UpdateIniFile($clean_ini)

    If you have settings in your master ini file that you MUST put in the
    user's ini, overwriting their own setting (CAREFUL!) add the names of
    those settings to $forced_settings, in a comma-delimited list, e.g..

        UpdateIniFile(default, "
commands,click_commands")

    The final parameter, $raw_section is the name of any sections you would
    like written (from the user's ini) as-is. This is useful for sections which
    have more than one preference with the exact same name, e.g..

        [Window Triggers]
        Downloads=WinMove(@DesktopWidth/5+4,@DesktopHeight/4-32,@DesktopWidth/1.25,@DesktopHeight/2+64)
        Chrome=^t
        Research=WinMove(0,12,@DesktopWidth/2,@DesktopHeight-20)
        Downloads={End}

    Using the normal IniWrite mechanism, the second "
Downloads" would overwrite
    the first, basically destroying the setting.

        UpdateIniFile($clean_ini, default, "
Window Triggers")
                                                                             #ce
; func UpdateIniFileOld(byref $new_version$clean_comments=$OFF$forced_settings="", $raw_section="")

    ; if $clean_comments = default then $clean_comments = $OFF
    ; if $forced_settings = default then $forced_settings = ""
    ; if $raw_section = default then $raw_section = ""

    ; debug("Update Ini File: " & $ini_path & $LOG_LF, @ScriptLineNumber, 7);debug

    ; local $old_version = IniRead($ini_path$my_name, "version", 0)
    ; local $vcomp = _VersionCompare($new_version$old_version)

    ; ; user's ini is up-to-date..
    ; switch $vcomp
        ; case 0, -1    ; both equal
            ; return false
    ; endswitch

    ; if $forced_settings then
        $forced_settings = StringSplit($forced_settings, ",")
    ; endif

    ; ; drop a temporary ini file somewhere..
    ; local $tmp_ini = @TempDir & "\" & $my_name & ".new.ini"
    ; FileInstall("./resources/Default.ini", $tmp_ini, 1)

    ; ; purge the new ini file of all comments..
    ; if $clean_comments = $ON then
        ; if _FileReadToArray($tmp_ini$file_array) then
            ; for $i = 1 to $file_array[0]
                ; local $line = StringStripWS($file_array[$i], 3)
                ; if StringLeft($line, 1) <> ";" then
                    ; if $line then ArrayAdd($new_array$line)
                ; endif
            ; next
            ; _FileWriteFromArray($tmp_ini$new_array, 1)
        ; endif
    ; endif

    ; ; delete all non-application prefs from the new (temp) ini
    ; local $existing_sections = IniReadSectionNames($tmp_ini)

    ; for $a = 1 to $existing_sections[0]
        ; if $existing_sections[$a] <> $my_name then IniDelete($tmp_ini$existing_sections[$a])
    ; next

    ; ; grab sections from user's old ini file..
    $existing_sections = IniReadSectionNames($ini_path)


    ; ; write user prefs to the temporary ini file.. (overwriting tmp_ini)
    ; for $a = 1 to $existing_sections[0]
        ; debug("Existing SECTION:: =>" & $existing_sections[$a] & "<=", @ScriptLineNumber, 7);debug

        ; local $section = IniReadSection($ini_path$existing_sections[$a])
        ; debug_PrintArray($section, "$section:", @ScriptLineNumber, 7);debug

        ; if IsArray($section) then

            ; ; We write this section on its own - as-is,
            ; ; to get any duplicated prefs (2+ commands for one window)
            ; if $raw_section and $existing_sections[$a] = $raw_section then
                ; IniWriteSection($tmp_ini$existing_sections[$a], $section)
                ; continueloop
            ; endif

            ; ; Regular ini section..
            ; for $b = 1 to $section[0][0]
                ; if not InArray($forced_settings$section[$b][0]) then
                    ; IniWrite($tmp_ini$existing_sections[$a], $section[$b][0], $section[$b][1])
                ; endif
            ; next

        ; endif

    ; next

    ; ; Backup/rename their existing app.ini to "[@date] app.ini"
    ; FileMove($ini_path, GetParent($ini_path) & "\[" & @Year & "-" & @Mon & "-" & @Mday & "@" & _
                            ; @Hour & "." & @Min & "]_v" & $old_version & "_" & $my_name & ".ini")

    ; ; move the newly created ini into place, and delete temp file..
    ; FileMove($tmp_ini$ini_path, 1)

    ; ; finally, update the version info in the ini file..
    ; IniWrite($ini_path$my_name, "version", $new_version)

; endfunc




; Update the users ini with any new settings (unless we force settings)
; Leave their old examples and comments intact.
;
func UpdateIniFile($new_version$clean_comments=$OFF$forced_settings="
", $no_new_prefs="")

 debug($LOG_LF, "
", 7);debug
 debug("
UpdateIniFile(" & $new_version & "," & $clean_comments  & "," & $forced_settings & ")", @ScriptLineNumber, 7);debug

    if $clean_comments = default then $clean_comments = $OFF
    if $forced_settings = default then $forced_settings = "
"
    if $no_new_prefs = default then $no_new_prefs = "
"

    debug("
Update Ini File: " & $ini_path & $LOG_LF, @ScriptLineNumber, 7);debug

    local $old_version = IniRead($ini_path$my_name, "
version", 0)
    local $vcomp = _VersionCompare($new_version$old_version)

    ; user's ini is up-to-date..
    switch $vcomp
        case 0, -1    ; both equal
            return false
    endswitch

    ; Backup/rename their existing app.ini to "[@date] app.ini"
    FileCopy($ini_path, GetParent($ini_path) & "
\[" & @Year & "-" & @Mon & "-" & @Mday & "@" & _
                                @Hour & "
." & @Min & "][v" & $old_version & "]" &  $my_name & ".ini")

    if $forced_settings then
        $forced_settings = StringSplit($forced_settings, "
,")
    endif

    local $wrote = 0

    if not FileExists($ini_path) then
        FileInstall("
./resources/Default.ini", $ini_path)
    else
        ; Create a temporary default strings.ini..
        local $tmp_ini_path = @TempDir & "
\TEMP_.ini"
        FileInstall("
./resources/Default.ini", $tmp_ini_path)
        local $existing_sections = IniReadSectionNames($tmp_ini_path)


        ; 2do.. for sections, eg. menus, it is not desirable to add default values onto
        ;        the end of their prefs. Need raw for this. But WITH comments.
        
        
        ; Interrogate user ini file for missing prefs and add default/new prefs as required..
        if IsArray($existing_sections) then

            for $i = 1 to $existing_sections[0]

                local $current_section = $existing_sections[$i]
                debug("
SECTION:: =>" & $current_section & "<=", @ScriptLineNumber, 7);debug

                if $current_section <> $no_new_prefs then 
                debug("
Writing prefs to:: =>" & $current_section & "<=", @ScriptLineNumber, 7);debug

                    local $this_section_data = IniReadSection($tmp_ini_path$current_section)
                    debug_PrintArray($this_section_data, "
$this_section_data:", @ScriptLineNumber, 7);debug

                    if IsArray($this_section_data) then

                        for $k = 1 to $this_section_data[0][0]

                            local $current_setting = $this_section_data[$k][0]
                            debug("
$current_setting: =>" & $current_setting & "<=", @ScriptLineNumber, 7);debug

                            local $user_value = IniRead($ini_path$current_section$current_setting, "
")
                            local $default_value = IniRead($tmp_ini_path$current_section$current_setting, "
")
                            debug("
$user_value: =>" & $user_value & "<=", @ScriptLineNumber, 7);debug
                            debug("
$default_value: =>" & $default_value & "<=", @ScriptLineNumber, 7);debug

                            ; If empty or gone, write the setting..
                            ; If it's different, leave it alone - they edited it.
                            ; unless it's a forced pref, in which case they get the (new) default value..
                            if not $user_value or InArray($forced_settings$current_setting) then
                                $wrote += 1
                                IniWrite($ini_path$current_section$current_setting$default_value)
                            endif
                        next
                    endif
                endif
            next
        endif
    endif

    FileDelete($tmp_ini_path)

    ; Purge the ini file of all comments..
    if $clean_comments = $ON then
        if _FileReadToArray($ini_path$file_array) then
            for $i = 1 to $file_array[0]
                local $line = StringStripWS($file_array[$i], 3)
                if StringLeft($line, 1) <> "
;" then
                    if $line then ArrayAdd($new_array$line)
                endif
            next
            local $file_handle = Fileopen($ini_path$FO_UNICODE+$FO_OVERWRITE)
            _FileWriteFromArray($file_handle$new_array, 1)
            FileClose($file_handle)
        endif
    endif

    ; Insert this flag in your defualt.ini for a neat way to check for a "new install"..
    IniDelete($ini_path$my_name, "
new_install")

    ; Finally, update the version info in the ini file..
    IniWrite($ini_path$my_name, "
version", $new_version)

    debug($ini_path & "
 updated. Wrote: " & $wrote & " ini settings", @ScriptLineNumber, 1);debug

endfunc






; This tiny function saves a HEAP of code..
;
func TogglePref(byref $variable)
    if $variable = $ON then
        $variable = $OFF
    else
        $variable = $ON
    endif
endfunc

; Save one of the above tray prefs to the ini and set the state of the tray to
; match the setting..
;
func SaveTrayPref($tray_control$pref_var$pref_name)
    IniWriteCheckBoxValue($ini_path$my_name$pref_name$pref_var)
    TrayItemSetState($tray_control$pref_var)
endfunc



; InArray()        [for single-dimension arrays]
;
; Feed it an AutoIt array and a string, returns true if $ia_string is one of the
$ia_array's *values*, false if not. Non-AutoIt arrays work fine, the first
; element is not relied upon, it is simply ignored.
;
; The search is case-insensitive, but must be an exact == (not partial) match.
; There's probably a real function for this these days. (Not yet!)
;
func InArray(byref $ia_array$ia_string)
    if not IsArray($ia_array) then return false
    local $ia_limit = UBound($ia_array) - 1
    for $i = 1 to $ia_limit ; not 0, which would return the total as a positive result if $ia_array[0]= e.g. "1"
        if $ia_string == $ia_array[$i] then return $i
    next
    return false
endfunc



; InArrayValue()        [for single-dimension arrays]
;
; Feed it an AutoIt (zero-indexed) array and a string, returns true if
$ia_string is inside one of the $ia_array's *values*. Non-AutoIt arrays work
; fine, the first element is not relied upon, it is simply ignored.
;
; The search is case-insensitive, and can be a partial) match.
; Returns the index of the first value containing the string.
;
func InArrayValue(byref $ia_array$ia_string)
    if not IsArray($ia_array) then return SetError(1, -8, false)
    local $ia_limit = UBound($ia_array) - 1
    for $i = 1 to $ia_limit
        if StringInStr($ia_array[$i], $ia_string) then return $i
    next
    return false
endfunc


; InArrayColumn()    [for single-dimension arrays, with columned indexes]
;
; Feed it an AutoIt array and a string, returns true if $ia_string
; is a column of one of the $ia_array's *values*. Non-AutoIt arrays work
; fine, the first element is not relied upon, it is simply ignored.
;
; Because AutoIt's array-handling is almost non-existant, and redimming
; amazingly slow, other methods need to be devised to work with indexed
; lists. A "column marker" is simply a delimiter within the value which
; can be used to further split a value into multiple values.
;
; This function is designed to find text (e.g. "path/name.ext") inside a
; value that may look like.. "path/name.ext|179|20080621114934".
;
; The search is case-insensitive, and must be an exact match for a
; particular column. Columns are numbered from 1, which is the value of
; the first column, and the correct match in the above example.
;
;    Success: Returns the index of the first value containing the string.
;    Fail: Returns false
;
func InArrayColumn(byref $ia_array$ia_string$col=1, $marker="
*")
    if not IsArray($ia_array) then return SetError(1, -8, false)
    local $ia_limit = UBound($ia_array) - 1
    for $i = 1 to $ia_limit
        local $columns = StringSplit($ia_array[$i], $marker$STR_ENTIRESPLIT)
        if $columns[$col] == $ia_string then return $i
    next
    return false
endfunc



; MakeDummyArray()
;
; We need an array, but only have a string, what to do? this function returns an
; "AutoIt array" with a single data element, that is, two elements; $dummy_array[1]
; containing your string, and $dummy_array[0] being the total number of data elements,
; AutoIt-style, which will always be 1, of course.
;
; Although this is simple enough to do in place, it's more readable to do use a function.
;
func MakeDummyArray($regular_string)
    local $dummy_array[2] = [1, $regular_string]
    return $dummy_array
endfunc



; TwoD2OneDArray()
;
; Convert a 2-dimensional array into a 1-dimensional array
; of all the *values* of the original 2D array..
;
; Pass true as the 2nd parameter to instead have it made from the /keys/.
;
; The 3rd parameter is any character or string of characters you with to strip
; (replace with "") from each key or value. Handy for trimming @Macros and such.
;
func TwoD2OneDArray(byref $Array2D$keys=false, $strip="
")
    if not IsArray($Array2D) then return false
    local $array[$Array2D[0][0]+1]
    $array[0] = $Array2D[0][0]
    for $i = 1 to $Array2D[0][0]
    
        if $keys then local $key = $Array2D[$i][0]
        local $value = $Array2D[$i][1]
        
        if $strip then 
            if $keys then $key = StringReplace($key$strip, "
")
            $value = StringReplace($value$strip, "
")
        endif
        
        if $keys then 
            $array[$i] = $key
        else
            $array[$i] = $value
        endif
    next
    return $array
endfunc



; Takes a 2-dimensional array as input and splits it into two arrays,
; the first, all the indexes, the second, all the values.
;
func TwoD22OneDArrays(byref $Array2D)
    if not IsArray($Array2D) then return false
    local $array[$Array2D[0][0]+1]
    local $array2[$Array2D[0][0]+1]
    $array[0] = $Array2D[0][0]
    $array2[0] = $Array2D[0][0]
    for $i = 1 to $Array2D[0][0]
        $array[$i] = $Array2D[$i][0]
        $array2[$i] = $Array2D[$i][1]
    next
    local $newarray[2] = [$array$array2]

    return $newarray
endfunc


#cs
    ArrayAdd()
    
    Add a single item to an AutoIt array..
    
    ArrayAdd() automatically increases the [0] index, but DOES NOT rely upon it. 

    Enable the third parameter to NOT add items which already exist in the 
    array. 

    Set the fourth parameter to true, and when an item is added which 
    already exists, the array is re-indexed, moving the entry to the END of 
    the array. In other words, it becomes the "
most recent entry". 

    
#ce
func ArrayAdd(byref $array$item$unique=false, $update=false)

    if not IsArray($array) then return
    local $size = UBound($array)

    if $unique and $update = false then
        if InArray($array$item) then return
    endif

    if $update then
        local $idx = InArray($array$item)
        if $idx then
            for $i = $idx to $size-2
                $array[$i] = $array[$i+1]
            next
            $size -= 1
        endif

    endif

    redim $array[$size+1]
    $array[$size] = $item
    $array[0] = $size

endfunc




#cs
    ArrayRemove()
    
    If $item is a number, that index is removed, otherwise the array is 
    searched for values matching {String} $item and that key is removed. 

    Remaining elements are moved down to fill the gap and the resulting 
    array is one element shorter. The [0] count element is updated with 
    the new total. 

    Returns true is changes were made, false otherwise. 

#ce
func ArrayRemove(byref $array$item)

 debug($LOG_LF, "
", 7);debug
 debug("
ArrayRemove(" & $item & ")", @ScriptLineNumber, 7);debug

    if not IsArray($array) then return SetError(1, 0, false)
    debug_PrintArray($array, "
Remove from this: $array:", @ScriptLineNumber, 7);debug

    local $match = false
    local $total_rows = UBound($array$UBOUND_ROWS)-1
    debug("
$total_rows: =>" & $total_rows & "<=", @ScriptLineNumber, 7);debug

    if IsNumber($item) then
        $match = $item
    else
        for $i = 1 to $total_rows

            debug("
$array[$i] =>" & $array[$i] & "<=", @ScriptLineNumber, 7);debug
            if $array[$i] == $item then
                $match = $i
                ; Stop here. Run through remaining rows..
                exitloop
            endif
        next
    endif
    
    if $match then
        for $k = $match to $total_rows-1
            ; Fill /this/ row with the /next/ row's values..
            debug("
$array[$k+1]: =>" & $array[$k+1] & "<=", @ScriptLineNumber, 7);debug
            $array[$k] = $array[$k+1]
        next
        Redim $array[$total_rows]
        $array[0] = $total_rows-1
        
        return true
        
    endif

endfunc

; An alias for the above function.
func ArrayDelete(byref $array$item)
    return ArrayRemove($array$item)
endfunc


#cs
    Very simple 2D array add.
    
    Param 1: The array to add to.
    Param 2: A pipe delimited list of values.
    
        ArrayAdd2D($my_array, "
foo|bar|sheep|shoe")
    
    If your list has less items than there are columns, the remaining 
    columns will be empty. If you list contains more items than there are 
    columns, the extra items will be ignored. It's okay to send blank 
    vaules. 
    
        ArrayAdd2D($my_array, "
||foo||bar")
    
    The index value $my_array[0][0] is automatically updated.
    
    Set the 3rd parameter to true to ensure no duplicate entries. Only the 
    first column is checked. 

    Set the 4th parameter to true to replace existing values, otherwise if a 
    key already exists, it will we left alone. 

    
        ArrayAdd2D($array, "
foo|bar|sheep|shoe", $unique$replace_existing)
        
#ce
func ArrayAdd2D(byref $array$items$unique=false, $replace=false, $delim="
|")

    if not IsArray($array) then return SetError(1, 0, false)
    $items = StringSplit($items$delim$STR_ENTIRESPLIT)
    debug("
$items[1]: =>" & $items[1] & "<=", @ScriptLineNumber, 7);debug

    local $exists = InArray2D($array$items[1])
    debug("
$exists: =>" & $exists & "<=", @ScriptLineNumber, 7);debug

    if $exists and $unique and not $replace then return SetError(2, 0, false)

    ; Column 1 is empty, forget matching!
    if not $items[1] then $exists = false

    local $rows
    if $exists and $replace then $rows = $exists
    local $columns = UBound($array$UBOUND_COLUMNS)

    if not $exists then
        $rows = UBound($array$UBOUND_ROWS)
        redim $array[$rows+1][$columns]
        $array[0][0] += 1
        debug("
$array[0][0]: =>" & $array[0][0] & "<=", @ScriptLineNumber, 7);debug

    endif

    ; Add the items to this row..
    for $i = 1 to $items[0]
        $array[$rows][$i-1] = $items[$i]
        if $i = $columns then exitloop
    next

    return true

endfunc



#cs

    ArrayRemove2D(byref $array$item$column)

    Remove the first row of a 2D array where $array[row][$column] matches
    $item.

    Supply the array to search, a string to search for (This is a
    CaSe-sEnsItiVe search), and the column to search in for that string.

    When a match is found, the entire row is removed and the remaining rows
    are re-indexed from the removal point and the array is one row shorter.

    The count [0][0] element is not relied upon when removing rows, but it
    is updated with the correct new total, which will usually be the old
    total minus one.
    
    Returns true if changes were made, otherwise false.

#ce
func ArrayRemove2D(byref $array$item$column)

 debug($LOG_LF, "
", 7);debug
 debug("
ArrayRemove2D([" & $item & "," & $column & "])", @ScriptLineNumber, 7);debug

    if not IsArray($array) then return SetError(1, 0, false)
    debug_PrintArray($array, "
Remove from: $array:", @ScriptLineNumber, 7);debug

    local $total_columns = UBound($array$UBOUND_COLUMNS)
    local $total_rows = UBound($array$UBOUND_ROWS)-1
    debug("
$total_columns: =>" & $total_columns & "<=", @ScriptLineNumber, 7);debug
    debug("
$total_rows: =>" & $total_rows & "<=", @ScriptLineNumber, 7);debug

    for $i = 1 to $total_rows

        debug("
$array[$i][$column]: =>" & $array[$i][$column] & "<=", @ScriptLineNumber, 7);debug
        if $array[$i][$column] == $item then

            ; Stop here. Run through remaining rows..
            for $k = $i to $total_rows-1
                ; Fill /this/ row with the /next/ row's values..
                for $m = 0 to $total_columns-1
                    debug("
$array[$k+1][$m]: =>" & $array[$k+1][$m] & "<=", @ScriptLineNumber, 7);debug
                    $array[$k][$m] = $array[$k+1][$m]
                next
            next
            Redim $array[$total_rows][$total_columns]
            $array[0][0] = $total_rows-1
            return true
        endif
    next

endfunc



#cs

    InArray2D($array$search_string$search_column)

    Very Simple 2D Array Search.
    Suppy:

        param 1: The 2D Array to search.
        param 2: The string to search for (CaSe-sEnsItiVe match).
        param 3: (optional) The column to search.
                 If this is omitted, all columns are searched.

    Returns the number of the row which contained the string as one of its
    column's values. Otherwise returns false.

    If you can suppply the search column, it will increase speed slightly.
    Column numbering begins at zero (0). Example..

        global $array[1][7]

        ArrayAdd2D($array, "
|apple|pear||orange")
        ArrayAdd2D($array, "
dog|cat|cow|pig|goat")
        ArrayAdd2D($array, "
||||nut|seed|")

        local $row = InArray2D($array, "
pig", 3)
        if $row then debug("
PIG IN THE DUNGEON!!!", @ScriptLineNumber, 7);debug

        Will print out "
PIG IN THE DUNGEON!!!"
        $row will equal 2.
#ce
func InArray2D(byref $array$string$search_column=false)

    if not IsArray($array) then return false

    local $rows = UBound($array$UBOUND_ROWS) - 1
    local $columns = UBound($array$UBOUND_COLUMNS)

    for $i = 1 to $rows
        if $search_column then
            if $string == $array[$i][$search_column] then return $i
        else
            for $k = 0 to $columns-1
                if $string == $array[$i][$k] then return $i
            next
        endif
    next
    return false
endfunc




; Take a comma-delimited list, usually an ini pref, check it's formatted okay
; and then convert to an array. It all happens ByRef.
;
func CommaDelimListToArray(byref $my_list)
    CBT($my_list, "
,")
    $my_list = StringSplit(StringReplace($my_list, "
,,", ","), ",") ; now an array
endfunc






; func ArrayAddRow(byref $array$row_data)
    ; local $size = UBound($array$UBOUND_COLUMNS)
; endfunc



; Associative Array Functions
;
; Before you start using your associative array, you need to initialize it. Put
; this code somewhere before you start using the array, probably at the top-ish
; of your script..
;
;     ; Initialize your array in your script.au3 ...
;     global $associative_array
;     AAInit($associative_array)
;
; Obviously, "$associative_array" will be your own array variable.
;
; You can initialize and use multiple associative arrays in your script.
;
; There is another essential line, to initialize a COM error handler, but that
; is already added to the top of THIS script, waiting..
;
;    global $oMyError = ObjEvent("AutoIt.Error", "AAError") ;
;
func AAInit(byref $dict_obj)
    $dict_obj = ObjCreate("
Scripting.Dictionary")
endfunc

; Adds a key and item pair to a Dictionary object.
func AAAdd(byref $dict_obj$key$val$unique=false)
    if $unique and AAExists($dict_obj$key) then return SetError(1, 1, -2)
    $dict_obj.Add($key$val)
    If @error Then return SetError(1, 1, -1)
endfunc

; Removes a key and item pair from a Dictionary object..
func AARemove(byref $dict_obj$key)
    $dict_obj.Remove($key)
    If @error Then return SetError(1, 1, -1)
endfunc

; Returns true if a specified key exists in the associative array, false if not..
func AAExists(byref $dict_obj$key)
    return $dict_obj.Exists($key)
endfunc

; Returns a value for a specified key in the associative array..
func AAGetItem(byref $dict_obj$key)
    return $dict_obj.Item($key)
endfunc

; Returns the total number of keys in the array..
func AACount(byref $dict_obj)
    return $dict_obj.Count
endfunc

; List all the "Key" => "Item" pairs in the array..
func AAList(byref $dict_obj$title="
Associative Array:", $level=1)
    if $level > $debug_level then return false
    debug($title & "
 [" & AACount($dict_obj) & " items] :=>", @ScriptLineNumber, 7);debug
    local $k = $dict_obj.Keys ; Get the keys
    ; local $a = $dict_obj.Items ; Get the items
    for $i = 0 to AACount($dict_obj) -1 ; Iterate the array
        debug($k[$i] & "
    ==>    " & AAGetItem($dict_obj$k[$i]))
    next
endfunc

; Wipe the array, obviously.
func AAWipe(byref $dict_obj)
    $dict_obj.RemoveAll()
endfunc

; Oh oh!
func AAError()
    Local $err = $oMyError.number
    If $err = 0 Then $err = -1
    SetError($err)
endfunc






#cs

    SaveComboSelection()

        SaveComboSelection( array{this combo box's special selection array}, string{value to store})

    Does what it says on the tin    (erm, also works for regular drop-downs!)

    If the user deletes an item, the previous item will be selected
    This makes multiple deltions easier, and more. This code is also
    more compact than checking for even a single "
previous" entry on
    each combo box individually. It's neater, too.

    To use, simply create a global "
AutoIt" array for the values,
    with the number of values you would like to remember (remember
    to set element 0 to the total)..

        global $previous_combo_selections[501] = [500] ; plenty!

    Make one for each combo.
    Rather than set a limit, we could use redim, but it's  slow.

    Anyway, when the user selects something, store that..

        SaveComboSelection($previous_combo_selections$current_preset)

    And when they delete an item, recall the previous item with..

        $previous_selection = GetComboSelection($previous_combo_selections_array$valid_names_array)

    Or use it directly..

        ControlCommand($GUI_foo, "
", $Control_ID, "SelectString", _
            GetComboSelection($previous_combo_selections_array$valid_names_array))

    If your deleted entry is still in the list at the point you need to get this information,
    simply repeat the command. Essentially, go "
back two"..

        ; this selects and removes the currently selected item..
        GetComboSelection($previous_combo_selections_array$valid_names_array)

        ; now the previous one..
        ControlCommand($GUI_foo, "
", $Control_ID, "SelectString", _
            GetComboSelection($previous_combo_selections_array$valid_names_array))

                                                                            #ce
func SaveComboSelection(byref $combo_array$value)

    for $i = 1 to $combo_array[0]
        if $combo_array[$i] = "
" then
            if $combo_array[$i-1] <> $value then
                $combo_array[$i] = $value
            endif
            exitloop ; store or not, we're outta here
        endif
    next

endfunc


; GetComboSelection()
;
; Get back the user's previous drop-down selection after a delete operation..
;
; We simply iterate the saved-combo strings array backwards, deleting all non-
; valid entries until the most recent valid entry is found. Then we delete it
; from the array and return the value.
;
; In the absence of a stored value, we return the "last" value in the names
; list. If deleting newly imported/created presets, this will get you the
; most recent addition, even when it hasn't been selected this session.
;
; It would be trivial to store this array in an ini file between launches, and
; keep a selection "history", if you require that.
;
;     $String = GetComboSelection( array{this combo box's special selection array}, array{list of current valid names in this combo})
;
func GetComboSelection(byref $combo_array, byref $names_list)

    local $new_val = "
"
    local $i = $combo_array[0]

    while $i > 0
        if $combo_array[$i] <> "
" then
            if not InArray($names_list$combo_array[$i]) then
                $combo_array[$i] = "
"
                $i -= 1
                continueloop
            endif
            $new_val = $combo_array[$i]
            $combo_array[$i] = "
"
            exitloop
        endif
        $i -= 1
    wend
    if $new_val = "
" then $new_val = $names_list[$names_list[0]]
    return $new_val
endfunc




; VisitUrl()
;
; Send the "default browser" to our URL..

; This uses the USER'S SYSTEM BROWSER! (eg. Firefox)

func VisitUrl($VisitURL="
http://corz.org/")
    debug("
VisitURL: =>" & $VisitURL & "<=" , @ScriptLineNumber, 7);debug
    ShellExecute($VisitURL)
    if @Error <> 0 then
        return true
    else
        return SetError (@Error, default , true)
    endif
endfunc


; ReplaceHTMLEntities()
;
;
; Probably rather slow, but, erm, fairly thorough..
;
; Note: unless you are using a proper international font, many of the entities
; below will appear as boxes, or empty. Worry not, everything is working fine.
;
; Updated with routine from Dhilip89
;
func ReplaceHTMLEntities($text)

    Local $Entity[65536]

    $Entity[34] = "
quot"
    $Entity[38] = "
amp"
    $Entity[39] = "
apos"
    $Entity[60] = "
lt"
    $Entity[62] = "
gt"
    $Entity[160] = "
nbsp"
    $Entity[161] = "
iexcl"
    $Entity[162] = "
cent"
    $Entity[163] = "
pound"
    $Entity[164] = "
curren"
    $Entity[165] = "
yen"
    $Entity[166] = "
brvbar"
    $Entity[167] = "
sect"
    $Entity[168] = "
uml"
    $Entity[169] = "
copy"
    $Entity[170] = "
ordf"
    $Entity[171] = "
laquo"
    $Entity[172] = "
not"
    $Entity[173] = "
shy"
    $Entity[174] = "
reg"
    $Entity[175] = "
macr"
    $Entity[176] = "
deg"
    $Entity[177] = "
plusmn"
    $Entity[178] = "
sup2"
    $Entity[179] = "
sup3"
    $Entity[180] = "
acute"
    $Entity[181] = "
micro"
    $Entity[182] = "
para"
    $Entity[183] = "
middot"
    $Entity[184] = "
cedil"
    $Entity[185] = "
sup1"
    $Entity[186] = "
ordm"
    $Entity[187] = "
raquo"
    $Entity[188] = "
frac14"
    $Entity[189] = "
frac12"
    $Entity[190] = "
frac34"
    $Entity[191] = "
iquest"
    $Entity[192] = "
Agrave"
    $Entity[193] = "
Aacute"
    $Entity[194] = "
Acirc"
    $Entity[195] = "
Atilde"
    $Entity[196] = "
Auml"
    $Entity[197] = "
Aring"
    $Entity[198] = "
AElig"
    $Entity[199] = "
Ccedil"
    $Entity[200] = "
Egrave"
    $Entity[201] = "
Eacute"
    $Entity[202] = "
Ecirc"
    $Entity[203] = "
Euml"
    $Entity[204] = "
Igrave"
    $Entity[205] = "
Iacute"
    $Entity[206] = "
Icirc"
    $Entity[207] = "
Iuml"
    $Entity[208] = "
ETH"
    $Entity[209] = "
Ntilde"
    $Entity[210] = "
Ograve"
    $Entity[211] = "
Oacute"
    $Entity[212] = "
Ocirc"
    $Entity[213] = "
Otilde"
    $Entity[214] = "
Ouml"
    $Entity[215] = "
times"
    $Entity[216] = "
Oslash"
    $Entity[217] = "
Ugrave"
    $Entity[218] = "
Uacute"
    $Entity[219] = "
Ucirc"
    $Entity[220] = "
Uuml"
    $Entity[221] = "
Yacute"
    $Entity[222] = "
THORN"
    $Entity[223] = "
szlig"
    $Entity[224] = "
agrave"
    $Entity[225] = "
aacute"
    $Entity[226] = "
acirc"
    $Entity[227] = "
atilde"
    $Entity[228] = "
auml"
    $Entity[229] = "
aring"
    $Entity[230] = "
aelig"
    $Entity[231] = "
ccedil"
    $Entity[232] = "
egrave"
    $Entity[233] = "
eacute"
    $Entity[234] = "
ecirc"
    $Entity[235] = "
euml"
    $Entity[236] = "
igrave"
    $Entity[237] = "
iacute"
    $Entity[238] = "
icirc"
    $Entity[239] = "
iuml"
    $Entity[240] = "
eth"
    $Entity[241] = "
ntilde"
    $Entity[242] = "
ograve"
    $Entity[243] = "
oacute"
    $Entity[244] = "
ocirc"
    $Entity[245] = "
otilde"
    $Entity[246] = "
ouml"
    $Entity[247] = "
divide"
    $Entity[248] = "
oslash"
    $Entity[249] = "
ugrave"
    $Entity[250] = "
uacute"
    $Entity[251] = "
ucirc"
    $Entity[252] = "
uuml"
    $Entity[253] = "
yacute"
    $Entity[254] = "
thorn"
    $Entity[255] = "
yuml"
    $Entity[338] = "
OElig"
    $Entity[339] = "
oelig"
    $Entity[352] = "
Scaron"
    $Entity[353] = "
scaron"
    $Entity[376] = "
Yuml"
    $Entity[402] = "
fnof"
    $Entity[710] = "
circ"
    $Entity[732] = "
tilde"
    $Entity[913] = "
Alpha"
    $Entity[914] = "
Beta"
    $Entity[915] = "
Gamma"
    $Entity[916] = "
Delta"
    $Entity[917] = "
Epsilon"
    $Entity[918] = "
Zeta"
    $Entity[919] = "
Eta"
    $Entity[920] = "
Theta"
    $Entity[921] = "
Iota"
    $Entity[922] = "
Kappa"
    $Entity[923] = "
Lambda"
    $Entity[924] = "
Mu"
    $Entity[925] = "
Nu"
    $Entity[926] = "
Xi"
    $Entity[927] = "
Omicron"
    $Entity[928] = "
Pi"
    $Entity[929] = "
Rho"
    $Entity[931] = "
Sigma"
    $Entity[932] = "
Tau"
    $Entity[933] = "
Upsilon"
    $Entity[934] = "
Phi"
    $Entity[935] = "
Chi"
    $Entity[936] = "
Psi"
    $Entity[937] = "
Omega"
    $Entity[945] = "
alpha"
    $Entity[946] = "
beta"
    $Entity[947] = "
gamma"
    $Entity[948] = "
delta"
    $Entity[949] = "
epsilon"
    $Entity[950] = "
zeta"
    $Entity[951] = "
eta"
    $Entity[952] = "
theta"
    $Entity[953] = "
iota"
    $Entity[954] = "
kappa"
    $Entity[955] = "
lambda"
    $Entity[956] = "
mu"
    $Entity[957] = "
nu"
    $Entity[958] = "
xi"
    $Entity[959] = "
omicron"
    $Entity[960] = "
pi"
    $Entity[961] = "
rho"
    $Entity[962] = "
sigmaf"
    $Entity[963] = "
sigma"
    $Entity[964] = "
tau"
    $Entity[965] = "
upsilon"
    $Entity[966] = "
phi"
    $Entity[967] = "
chi"
    $Entity[968] = "
psi"
    $Entity[969] = "
omega"
    $Entity[977] = "
thetasym"
    $Entity[978] = "
upsih"
    $Entity[982] = "
piv"
    $Entity[8194] = "
ensp"
    $Entity[8195] = "
emsp"
    $Entity[8201] = "
thinsp"
    $Entity[8204] = "
zwnj"
    $Entity[8205] = "
zwj"
    $Entity[8206] = "
lrm"
    $Entity[8207] = "
rlm"
    $Entity[8211] = "
ndash"
    $Entity[8212] = "
mdash"
    $Entity[8216] = "
lsquo"
    $Entity[8217] = "
rsquo"
    $Entity[8218] = "
sbquo"
    $Entity[8220] = "
ldquo"
    $Entity[8221] = "
rdquo"
    $Entity[8222] = "
bdquo"
    $Entity[8224] = "
dagger"
    $Entity[8225] = "
Dagger"
    $Entity[8226] = "
bull"
    $Entity[8230] = "
hellip"
    $Entity[8240] = "
permil"
    $Entity[8242] = "
prime"
    $Entity[8243] = "
Prime"
    $Entity[8249] = "
lsaquo"
    $Entity[8250] = "
rsaquo"
    $Entity[8254] = "
oline"
    $Entity[8260] = "
frasl"
    $Entity[8364] = "
euro"
    $Entity[8465] = "
image"
    $Entity[8472] = "
weierp"
    $Entity[8476] = "
real"
    $Entity[8482] = "
trade"
    $Entity[8501] = "
alefsym"
    $Entity[8592] = "
larr"
    $Entity[8593] = "
uarr"
    $Entity[8594] = "
rarr"
    $Entity[8595] = "
darr"
    $Entity[8596] = "
harr"
    $Entity[8629] = "
crarr"
    $Entity[8656] = "
lArr"
    $Entity[8657] = "
uArr"
    $Entity[8658] = "
rArr"
    $Entity[8659] = "
dArr"
    $Entity[8660] = "
hArr"
    $Entity[8704] = "
forall"
    $Entity[8706] = "
part"
    $Entity[8707] = "
exist"
    $Entity[8709] = "
empty"
    $Entity[8711] = "
nabla"
    $Entity[8712] = "
isin"
    $Entity[8713] = "
notin"
    $Entity[8715] = "
ni"
    $Entity[8719] = "
prod"
    $Entity[8721] = "
sum"
    $Entity[8722] = "
minus"
    $Entity[8727] = "
lowast"
    $Entity[8730] = "
radic"
    $Entity[8733] = "
prop"
    $Entity[8734] = "
infin"
    $Entity[8736] = "
ang"
    $Entity[8743] = "
and"
    $Entity[8744] = "
or"
    $Entity[8745] = "
cap"
    $Entity[8746] = "
cup"
    $Entity[8747] = "
int"
    $Entity[8756] = "
there4"
    $Entity[8764] = "
sim"
    $Entity[8773] = "
cong"
    $Entity[8776] = "
asymp"
    $Entity[8800] = "
ne"
    $Entity[8801] = "
equiv"
    $Entity[8804] = "
le"
    $Entity[8805] = "
ge"
    $Entity[8834] = "
sub"
    $Entity[8835] = "
sup"
    $Entity[8836] = "
nsub"
    $Entity[8838] = "
sube"
    $Entity[8839] = "
supe"
    $Entity[8853] = "
oplus"
    $Entity[8855] = "
otimes"
    $Entity[8869] = "
perp"
    $Entity[8901] = "
sdot"
    $Entity[8968] = "
lceil"
    $Entity[8969] = "
rceil"
    $Entity[8970] = "
lfloor"
    $Entity[8971] = "
rfloor"
    $Entity[9001] = "
lang"
    $Entity[9002] = "
rang"
    $Entity[9674] = "
loz"
    $Entity[9824] = "
spades"
    $Entity[9827] = "
clubs"
    $Entity[9829] = "
hearts"
    $Entity[9830] = "
diams"

    local $e1 = StringRegExp($text, '&#x(.*?);', 3)
    local $e2 = StringRegExp($text, '&#(.*?);', 3)
    local $e3 = StringRegExp($text, '&(.*?);', 3)
    for $i = 0 To UBound($e1) - 1 step 1
        $text = StringReplace($text, '&#x' & $e1[$i] & ';', ChrW(Dec($e1[$i])))
    next
    for $i = 0 To UBound($e2) - 1 step 1
        $text = StringReplace($text, '&#' & $e2[$i] & ';', ChrW($e2[$i]))
    next
    for $i = 0 To UBound($e3) - 1 step 1
        $text = StringReplace($text, '&' & $e3[$i] & ';', ChrW(_ArraySearch($Entity$e3[$i], 0, 0, 1)))
    next
    return $text
endfunc


func CreateHTMLEntities($string)
    local $len = StringLen($string)
    local $ret
    if not $len then return $string
    for $i = 1 to $len
        local $wchar = StringMid($string$i, 1)
        $ret &= "
&#" & AscW($wchar) & ";"
    next
    return $ret
endfunc


func EnCodeUrl($text)
    $text = StringReplace($text, "
 ", "%20")
    $text = StringReplace($text, "
!", "%21")
    $text = StringReplace($text, '"
', "%22")
    $text = StringReplace($text"#""%23")
    $text = StringReplace($text"$""%24")
    $text = StringReplace($text"%""%25")
    $text = StringReplace($text"&""%26")
    $text = StringReplace($text"'""%27")
    $text = StringReplace($text"(""%28")
    $text = StringReplace($text")""%29")
    $text = StringReplace($text"*""%2A")
    $text = StringReplace($text"+""%2B")
    $text = StringReplace($text",""%2C")
    $text = StringReplace($text"-""%2D")
    $text = StringReplace($text".""%2E")
    $text = StringReplace($text"/""%2F")
    $text = StringReplace($text"0""%30")
    $text = StringReplace($text"1""%31")
    $text = StringReplace($text"2""%32")
    $text = StringReplace($text"3""%33")
    $text = StringReplace($text"4""%34")
    $text = StringReplace($text"5""%35")
    $text = StringReplace($text"6""%36")
    $text = StringReplace($text"7""%37")
    $text = StringReplace($text"8""%38")
    $text = StringReplace($text"9""%39")
    $text = StringReplace($text":""%3A")
    $text = StringReplace($text";""%3B")
    $text = StringReplace($text"<""%3C")
    $text = StringReplace($text"=""%3D")
    $text = StringReplace($text">""%3E")
    $text = StringReplace($text"?""%3F")
    $text = StringReplace($text"@""%40")
    $text = StringReplace($text"A""%41")
    $text = StringReplace($text"B""%42")
    $text = StringReplace($text"C""%43")
    $text = StringReplace($text"D""%44")
    $text = StringReplace($text"E""%45")
    $text = StringReplace($text"F""%46")
    $text = StringReplace($text"G""%47")
    $text = StringReplace($text"H""%48")
    $text = StringReplace($text"I""%49")
    $text = StringReplace($text"J""%4A")
    $text = StringReplace($text"K""%4B")
    $text = StringReplace($text"L""%4C")
    $text = StringReplace($text"M""%4D")
    $text = StringReplace($text"N""%4E")
    $text = StringReplace($text"O""%4F")
    $text = StringReplace($text"P""%50")
    $text = StringReplace($text"Q""%51")
    $text = StringReplace($text"R""%52")
    $text = StringReplace($text"S""%53")
    $text = StringReplace($text"T""%54")
    $text = StringReplace($text"U""%55")
    $text = StringReplace($text"V""%56")
    $text = StringReplace($text"W""%57")
    $text = StringReplace($text"X""%58")
    $text = StringReplace($text"Y""%59")
    $text = StringReplace($text"Z""%5A")
    $text = StringReplace($text"[""%5B")
    $text = StringReplace($text"\""%5C")
    $text = StringReplace($text"]""%5D")
    $text = StringReplace($text"^""%5E")
    $text = StringReplace($text"_""%5F")
    $text = StringReplace($text"`""%60")
    $text = StringReplace($text"a""%61")
    $text = StringReplace($text"b""%62")
    $text = StringReplace($text"c""%63")
    $text = StringReplace($text"d""%64")
    $text = StringReplace($text"e""%65")
    $text = StringReplace($text"f""%66")
    $text = StringReplace($text"g""%67")
    $text = StringReplace($text"h""%68")
    $text = StringReplace($text"i""%69")
    $text = StringReplace($text"j""%6A")
    $text = StringReplace($text"k""%6B")
    $text = StringReplace($text"l""%6C")
    $text = StringReplace($text"m""%6D")
    $text = StringReplace($text"n""%6E")
    $text = StringReplace($text"o""%6F")
    $text = StringReplace($text"p""%70")
    $text = StringReplace($text"q""%71")
    $text = StringReplace($text"r""%72")
    $text = StringReplace($text"s""%73")
    $text = StringReplace($text"t""%74")
    $text = StringReplace($text"u""%75")
    $text = StringReplace($text"v""%76")
    $text = StringReplace($text"w""%77")
    $text = StringReplace($text"x""%78")
    $text = StringReplace($text"y""%79")
    $text = StringReplace($text"z""%7A")
    $text = StringReplace($text"{""%7B")
    $text = StringReplace($text"|""%7C")
    $text = StringReplace($text"}""%7D")
    $text = StringReplace($text"~""%7E")
    $text = StringReplace($text" ""%7F")
    $text = StringReplace($text"€""%80")
    $text = StringReplace($text" ""%81")
    $text = StringReplace($text"‚""%82")
    $text = StringReplace($text"ƒ""%83")
    $text = StringReplace($text"„""%84")
    $text = StringReplace($text"…""%85")
    $text = StringReplace($text"†""%86")
    $text = StringReplace($text"‡""%87")
    $text = StringReplace($text"ˆ""%88")
    $text = StringReplace($text"‰""%89")
    $text = StringReplace($text"Š""%8A")
    $text = StringReplace($text"‹""%8B")
    $text = StringReplace($text"Œ""%8C")
    $text = StringReplace($text" ""%8D")
    $text = StringReplace($text"Ž""%8E")
    $text = StringReplace($text" ""%8F")
    $text = StringReplace($text" ""%90")
    $text = StringReplace($text"‘""%91")
    $text = StringReplace($text"’""%92")
    $text = StringReplace($text"“""%93")
    $text = StringReplace($text"”""%94")
    $text = StringReplace($text"•""%95")
    $text = StringReplace($text"–""%96")
    $text = StringReplace($text"—""%97")
    $text = StringReplace($text"˜""%98")
    $text = StringReplace($text"™""%99")
    $text = StringReplace($text"š""%9A")
    $text = StringReplace($text"›""%9B")
    $text = StringReplace($text"œ""%9C")
    $text = StringReplace($text" ""%9D")
    $text = StringReplace($text"ž""%9E")
    $text = StringReplace($text"Ÿ""%9F")
    $text = StringReplace($text" ""%A0")
    $text = StringReplace($text"¡""%A1")
    $text = StringReplace($text"¢""%A2")
    $text = StringReplace($text"£""%A3")
    $text = StringReplace($text" ""%A4")
    $text = StringReplace($text"¥""%A5")
    $text = StringReplace($text"|""%A6")
    $text = StringReplace($text"§""%A7")
    $text = StringReplace($text"¨""%A8")
    $text = StringReplace($text"©""%A9")
    $text = StringReplace($text"ª""%AA")
    $text = StringReplace($text"«""%AB")
    $text = StringReplace($text"¬""%AC")
    $text = StringReplace($text"¯""%AD")
    $text = StringReplace($text"®""%AE")
    $text = StringReplace($text"¯""%AF")
    $text = StringReplace($text"°""%B0")
    $text = StringReplace($text"±""%B1")
    $text = StringReplace($text"²""%B2")
    $text = StringReplace($text"³""%B3")
    $text = StringReplace($text"´""%B4")
    $text = StringReplace($text"µ""%B5")
    $text = StringReplace($text"¶""%B6")
    $text = StringReplace($text"·""%B7")
    $text = StringReplace($text"¸""%B8")
    $text = StringReplace($text"¹""%B9")
    $text = StringReplace($text"º""%BA")
    $text = StringReplace($text"»""%BB")
    $text = StringReplace($text"¼""%BC")
    $text = StringReplace($text"½""%BD")
    $text = StringReplace($text"¾""%BE")
    $text = StringReplace($text"¿""%BF")
    $text = StringReplace($text"À""%C0")
    $text = StringReplace($text"Á""%C1")
    $text = StringReplace($text"Â""%C2")
    $text = StringReplace($text"Ã""%C3")
    $text = StringReplace($text"Ä""%C4")
    $text = StringReplace($text"Å""%C5")
    $text = StringReplace($text"Æ""%C6")
    $text = StringReplace($text"Ç""%C7")
    $text = StringReplace($text"È""%C8")
    $text = StringReplace($text"É""%C9")
    $text = StringReplace($text"Ê""%CA")
    $text = StringReplace($text"Ë""%CB")
    $text = StringReplace($text"Ì""%CC")
    $text = StringReplace($text"Í""%CD")
    $text = StringReplace($text"Î""%CE")
    $text = StringReplace($text"Ï""%CF")
    $text = StringReplace($text"Ð""%D0")
    $text = StringReplace($text"Ñ""%D1")
    $text = StringReplace($text"Ò""%D2")
    $text = StringReplace($text"Ó""%D3")
    $text = StringReplace($text"Ô""%D4")
    $text = StringReplace($text"Õ""%D5")
    $text = StringReplace($text"Ö""%D6")
    $text = StringReplace($text" ""%D7")
    $text = StringReplace($text"Ø""%D8")
    $text = StringReplace($text"Ù""%D9")
    $text = StringReplace($text"Ú""%DA")
    $text = StringReplace($text"Û""%DB")
    $text = StringReplace($text"Ü""%DC")
    $text = StringReplace($text"Ý""%DD")
    $text = StringReplace($text"Þ""%DE")
    $text = StringReplace($text"ß""%DF")
    $text = StringReplace($text"à""%E0")
    $text = StringReplace($text"á""%E1")
    $text = StringReplace($text"â""%E2")
    $text = StringReplace($text"ã""%E3")
    $text = StringReplace($text"ä""%E4")
    $text = StringReplace($text"å""%E5")
    $text = StringReplace($text"æ""%E6")
    $text = StringReplace($text"ç""%E7")
    $text = StringReplace($text"è""%E8")
    $text = StringReplace($text"é""%E9")
    $text = StringReplace($text"ê""%EA")
    $text = StringReplace($text"ë""%EB")
    $text = StringReplace($text"ì""%EC")
    $text = StringReplace($text"í""%ED")
    $text = StringReplace($text"î""%EE")
    $text = StringReplace($text"ï""%EF")
    $text = StringReplace($text"ð""%F0")
    $text = StringReplace($text"ñ""%F1")
    $text = StringReplace($text"ò""%F2")
    $text = StringReplace($text"ó""%F3")
    $text = StringReplace($text"ô""%F4")
    $text = StringReplace($text"õ""%F5")
    $text = StringReplace($text"ö""%F6")
    $text = StringReplace($text"÷""%F7")
    $text = StringReplace($text"ø""%F8")
    $text = StringReplace($text"ù""%F9")
    $text = StringReplace($text"ú""%FA")
    $text = StringReplace($text"û""%FB")
    $text = StringReplace($text"ü""%FC")
    $text = StringReplace($text"ý""%FD")
    $text = StringReplace($text"þ""%FE")
    $text = StringReplace($text"ÿ""%FF")
    return $text
endfunc


func DeCodeUrl($text)
    $text = StringReplace($text"%20"" ")
    $text = StringReplace($text"%21""!")
    $text = StringReplace($text"%22", '"')
    $text = StringReplace($text, "
%23", "#")
    $text = StringReplace($text, "
%24", "$")
    $text = StringReplace($text, "
%25", "%")
    $text = StringReplace($text, "
%26", "&")
    $text = StringReplace($text, "
%27", "'")
    $text = StringReplace($text, "
%28", "(")
    $text = StringReplace($text, "
%29", ")")
    $text = StringReplace($text, "
%2A", "*")
    $text = StringReplace($text, "
%2B", "+")
    $text = StringReplace($text, "
%2C", ",")
    $text = StringReplace($text, "
%2D", "-")
    $text = StringReplace($text, "
%2E", ".")
    $text = StringReplace($text, "
%2F", "/")
    $text = StringReplace($text, "
%30", "0")
    $text = StringReplace($text, "
%31", "1")
    $text = StringReplace($text, "
%32", "2")
    $text = StringReplace($text, "
%33", "3")
    $text = StringReplace($text, "
%34", "4")
    $text = StringReplace($text, "
%35", "5")
    $text = StringReplace($text, "
%36", "6")
    $text = StringReplace($text, "
%37", "7")
    $text = StringReplace($text, "
%38", "8")
    $text = StringReplace($text, "
%39", "9")
    $text = StringReplace($text, "
%3A", ":")
    $text = StringReplace($text, "
%3B", ";")
    $text = StringReplace($text, "
%3C", "<")
    $text = StringReplace($text, "
%3D", "=")
    $text = StringReplace($text, "
%3E", ">")
    $text = StringReplace($text, "
%3F", "?")
    $text = StringReplace($text, "
%40", "@")
    $text = StringReplace($text, "
%41", "A")
    $text = StringReplace($text, "
%42", "B")
    $text = StringReplace($text, "
%43", "C")
    $text = StringReplace($text, "
%44", "D")
    $text = StringReplace($text, "
%45", "E")
    $text = StringReplace($text, "
%46", "F")
    $text = StringReplace($text, "
%47", "G")
    $text = StringReplace($text, "
%48", "H")
    $text = StringReplace($text, "
%49", "I")
    $text = StringReplace($text, "
%4A", "J")
    $text = StringReplace($text, "
%4B", "K")
    $text = StringReplace($text, "
%4C", "L")
    $text = StringReplace($text, "
%4D", "M")
    $text = StringReplace($text, "
%4E", "N")
    $text = StringReplace($text, "
%4F", "O")
    $text = StringReplace($text, "
%50", "P")
    $text = StringReplace($text, "
%51", "Q")
    $text = StringReplace($text, "
%52", "R")
    $text = StringReplace($text, "
%53", "S")
    $text = StringReplace($text, "
%54", "T")
    $text = StringReplace($text, "
%55", "U")
    $text = StringReplace($text, "
%56", "V")
    $text = StringReplace($text, "
%57", "W")
    $text = StringReplace($text, "
%58", "X")
    $text = StringReplace($text, "
%59", "Y")
    $text = StringReplace($text, "
%5A", "Z")
    $text = StringReplace($text, "
%5B", "[")
    $text = StringReplace($text, "
%5C", "\")
    $text = StringReplace($text, "
%5D", "]")
    $text = StringReplace($text, "
%5E", "^")
    $text = StringReplace($text, "
%5F", "_")
    $text = StringReplace($text, "
%60", "`")
    $text = StringReplace($text, "
%61", "a")
    $text = StringReplace($text, "
%62", "b")
    $text = StringReplace($text, "
%63", "c")
    $text = StringReplace($text, "
%64", "d")
    $text = StringReplace($text, "
%65", "e")
    $text = StringReplace($text, "
%66", "f")
    $text = StringReplace($text, "
%67", "g")
    $text = StringReplace($text, "
%68", "h")
    $text = StringReplace($text, "
%69", "i")
    $text = StringReplace($text, "
%6A", "j")
    $text = StringReplace($text, "
%6B", "k")
    $text = StringReplace($text, "
%6C", "l")
    $text = StringReplace($text, "
%6D", "m")
    $text = StringReplace($text, "
%6E", "n")
    $text = StringReplace($text, "
%6F", "o")
    $text = StringReplace($text, "
%70", "p")
    $text = StringReplace($text, "
%71", "q")
    $text = StringReplace($text, "
%72", "r")
    $text = StringReplace($text, "
%73", "s")
    $text = StringReplace($text, "
%74", "t")
    $text = StringReplace($text, "
%75", "u")
    $text = StringReplace($text, "
%76", "v")
    $text = StringReplace($text, "
%77", "w")
    $text = StringReplace($text, "
%78", "x")
    $text = StringReplace($text, "
%79", "y")
    $text = StringReplace($text, "
%7A", "z")
    $text = StringReplace($text, "
%7B", "{")
    $text = StringReplace($text, "
%7C", "|")
    $text = StringReplace($text, "
%7D", "}")
    $text = StringReplace($text, "
%7E", "~")
    $text = StringReplace($text, "
%7F", " ")
    $text = StringReplace($text, "
%80", "")
    $text = StringReplace($text, "
%81", " ")
    $text = StringReplace($text, "
%82", "")
    $text = StringReplace($text, "
%83", "ƒ")
    $text = StringReplace($text, "
%84", "")
    $text = StringReplace($text, "
%85", "")
    $text = StringReplace($text, "
%86", "")
    $text = StringReplace($text, "
%87", "")
    $text = StringReplace($text, "
%88", "ˆ")
    $text = StringReplace($text, "
%89", "")
    $text = StringReplace($text, "
%8A", "Š")
    $text = StringReplace($text, "
%8B", "")
    $text = StringReplace($text, "
%8C", "Œ")
    $text = StringReplace($text, "
%8D", " ")
    $text = StringReplace($text, "
%8E", "Ž")
    $text = StringReplace($text, "
%8F", " ")
    $text = StringReplace($text, "
%90", " ")
    $text = StringReplace($text, "
%91", "")
    $text = StringReplace($text, "
%92", "")
    $text = StringReplace($text, "
%93", "")
    $text = StringReplace($text, "
%94", "")
    $text = StringReplace($text, "
%95", "")
    $text = StringReplace($text, "
%96", "")
    $text = StringReplace($text, "
%97", "")
    $text = StringReplace($text, "
%98", "˜")
    $text = StringReplace($text, "
%99", "")
    $text = StringReplace($text, "
%9A", "š")
    $text = StringReplace($text, "
%9B", "")
    $text = StringReplace($text, "
%9C", "œ")
    $text = StringReplace($text, "
%9D", " ")
    $text = StringReplace($text, "
%9E", "ž")
    $text = StringReplace($text, "
%9F", "Ÿ")
    $text = StringReplace($text, "
%A0", " ")
    $text = StringReplace($text, "
%A1", "¡")
    $text = StringReplace($text, "
%A2", "¢")
    $text = StringReplace($text, "
%A3", "£")
    $text = StringReplace($text, "
%A4", " ")
    $text = StringReplace($text, "
%A5", "¥")
    $text = StringReplace($text, "
%A6", "|")
    $text = StringReplace($text, "
%A7", "§")
    $text = StringReplace($text, "
%A8", "¨")
    $text = StringReplace($text, "
%A9", "©")
    $text = StringReplace($text, "
%AA", "ª")
    $text = StringReplace($text, "
%AB", "«")
    $text = StringReplace($text, "
%AC", "¬")
    $text = StringReplace($text, "
%AD", "¯")
    $text = StringReplace($text, "
%AE", "®")
    $text = StringReplace($text, "
%AF", "¯")
    $text = StringReplace($text, "
%B0", "°")
    $text = StringReplace($text, "
%B1", "±")
    $text = StringReplace($text, "
%B2", "²")
    $text = StringReplace($text, "
%B3", "³")
    $text = StringReplace($text, "
%B4", "´")
    $text = StringReplace($text, "
%B5", "µ")
    $text = StringReplace($text, "
%B6", "")
    $text = StringReplace($text, "
%B7", "·")
    $text = StringReplace($text, "
%B8", "¸")
    $text = StringReplace($text, "
%B9", "¹")
    $text = StringReplace($text, "
%BA", "º")
    $text = StringReplace($text, "
%BB", "»")
    $text = StringReplace($text, "
%BC", "¼")
    $text = StringReplace($text, "
%BD", "½")
    $text = StringReplace($text, "
%BE", "¾")
    $text = StringReplace($text, "
%BF", "¿")
    $text = StringReplace($text, "
%C0", "À")
    $text = StringReplace($text, "
%C1", "Á")
    $text = StringReplace($text, "
%C2", "Â")
    $text = StringReplace($text, "
%C3", "Ã")
    $text = StringReplace($text, "
%C4", "Ä")
    $text = StringReplace($text, "
%C5", "Å")
    $text = StringReplace($text, "
%C6", "Æ")
    $text = StringReplace($text, "
%C7", "Ç")
    $text = StringReplace($text, "
%C8", "È")
    $text = StringReplace($text, "
%C9", "É")
    $text = StringReplace($text, "
%CA", "Ê")
    $text = StringReplace($text, "
%CB", "Ë")
    $text = StringReplace($text, "
%CC", "Ì")
    $text = StringReplace($text, "
%CD", "Í")
    $text = StringReplace($text, "
%CE", "Î")
    $text = StringReplace($text, "
%CF", "Ï")
    $text = StringReplace($text, "
%D0", "Ð")
    $text = StringReplace($text, "
%D1", "Ñ")
    $text = StringReplace($text, "
%D2", "Ò")
    $text = StringReplace($text, "
%D3", "Ó")
    $text = StringReplace($text, "
%D4", "Ô")
    $text = StringReplace($text, "
%D5", "Õ")
    $text = StringReplace($text, "
%D6", "Ö")
    $text = StringReplace($text, "
%D7", " ")
    $text = StringReplace($text, "
%D8", "Ø")
    $text = StringReplace($text, "
%D9", "Ù")
    $text = StringReplace($text, "
%DA", "Ú")
    $text = StringReplace($text, "
%DB", "Û")
    $text = StringReplace($text, "
%DC", "Ü")
    $text = StringReplace($text, "
%DD", "Ý")
    $text = StringReplace($text, "
%DE", "Þ")
    $text = StringReplace($text, "
%DF", "ß")
    $text = StringReplace($text, "
%E0", "à")
    $text = StringReplace($text, "
%E1", "á")
    $text = StringReplace($text, "
%E2", "â")
    $text = StringReplace($text, "
%E3", "ã")
    $text = StringReplace($text, "
%E4", "ä")
    $text = StringReplace($text, "
%E5", "å")
    $text = StringReplace($text, "
%E6", "æ")
    $text = StringReplace($text, "
%E7", "ç")
    $text = StringReplace($text, "
%E8", "è")
    $text = StringReplace($text, "
%E9", "é")
    $text = StringReplace($text, "
%EA", "ê")
    $text = StringReplace($text, "
%EB", "ë")
    $text = StringReplace($text, "
%EC", "ì")
    $text = StringReplace($text, "
%ED", "í")
    $text = StringReplace($text, "
%EE", "î")
    $text = StringReplace($text, "
%EF", "ï")
    $text = StringReplace($text, "
%F0", "ð")
    $text = StringReplace($text, "
%F1", "ñ")
    $text = StringReplace($text, "
%F2", "ò")
    $text = StringReplace($text, "
%F3", "ó")
    $text = StringReplace($text, "
%F4", "ô")
    $text = StringReplace($text, "
%F5", "õ")
    $text = StringReplace($text, "
%F6", "ö")
    $text = StringReplace($text, "
%F7", "÷")
    $text = StringReplace($text, "
%F8", "ø")
    $text = StringReplace($text, "
%F9", "ù")
    $text = StringReplace($text, "
%FA", "ú")
    $text = StringReplace($text, "
%FB", "û")
    $text = StringReplace($text, "
%FC", "ü")
    $text = StringReplace($text, "
%FD", "ý")
    $text = StringReplace($text, "
%FE", "þ")
    $text = StringReplace($text, "
%FF", "ÿ")
    return $text
endfunc



; Strip HTML tags from a string..
;
func StripHTML($web_text$lf=$LOG_LF)
    ; strip out the HTML..
    $web_text = StringReplace($web_text, "
&nbsp;", " ")
    $web_text = StringRegExpReplace($web_text, '</?[^>]*?>', '')
    $web_text = StringReplace($web_text$lf & $lf$lf)
    return StringStripWS($web_text,3)
endfunc


; OrdAbbAppend()
;
; Used to append ordinal abbreviations onto numbers, for
; use in human-readable numeric strings, mainly dates.
;
; Feed it a number, OrdAbbAppend() returns that number,
; plus the appendment, as a string. e.g..
;
;    $foo = OrdAbbAppend(20)
;    ; $foo = "20th"
;
func OrdAbbAppend($d_str)
    if $d_str < 1 then
        SetError(1)
        return "
"
    endif
    local $appends[10]
    $appends[0] = "
th"
    $appends[1] = "
st"
    $appends[2] = "
nd"
    $appends[3] = "
rd"
    $appends[4] = "
th"
    $appends[5] = "
th"
    $appends[6] = "
th"
    $appends[7] = "
th"
    $appends[8] = "
th"
    $appends[9] = "
th"
    if StringMid($d_str, StringLen($d_str)-1, 1) == 1 then
        $appends[1] = "
th"
        $appends[2] = "
th"
        $appends[3] = "
th"
    endif
    return $d_str & $appends[StringRight($d_str, 1)]
endfunc



; Convert seconds to readable H/M/S time..
;
func SecondsToDHMS($sec=0)
  debug("
SecondsToDHMS(sec) =>" & $sec & "<=", @ScriptLineNumber, 9);debug
    if $sec < 0 then return -1
    select
        case  $sec < 61
            return $sec & "
 seconds"
        case $sec < 3601
            return StringFormat('%.01dm %.01ds', Mod(($sec / 60), 60), Mod($sec, 60))
        case $sec < 86401
            return StringFormat('%.01dh %.01dm %.01ds', Mod($sec / 3600, 24), Int(Mod(($sec / 60), 60)), Mod($sec, 60))
        case else
            return StringFormat('%.01dd %.01dh %.01dm %.01ds', Mod($sec / 86400, 7), Mod($sec / 3600, 24), Int(Mod(($sec / 60), 60)), Mod($sec, 60))
    endselect
endfunc


;
; UserTimeToUnitTime
;
; This is an emtremely limited function designed for specific back-end tasks.
; If you want to mess with time stuff, use the functions inside <Date.au3>.
;
; This converts "user" (Human) time to an integer unit of time.
; Input a string indicating a time, returns that time in some unit. Here's ms..
;
;    input        Returned Milliseconds
;    ------        ---------------------
;    1000    =>    1000
;    1000ms    =>    1000
;    1s        =>    1000
;    1m        =>    60000
;    1h        =>    3600000
;    1d        =>    86400000
;
; The second parameter controls the units which will be returned.
; By default it is milliseconds. You can also ask for s, m, h & d.
;
; You can specify "full info" (3rd parameter) which will have UserTimeToUnitTime
; return an array of two values, [0] = the final units, [1] = the user (human)
; unit they arrived in, e.g. "h".
;
func UserTimeToUnitTime($user_time$time_unit="
ms", $full_info=false)

    local $ret_array[2] = [0,"
ms"]

    if IsNumber($user_time) then
        if $full_info then
            $ret_array[0] = $user_time
            return $ret_array
        else
            return $user_time
        endif
    endif

    select
        case StringRight($user_time, 2) = "
ms"
            CRT($user_time, "
ms")
            ; leave number as-is

        case StringRight($user_time, 1) = "
s"
            CRT($user_time, "
s")
            $user_time *= 1000

        case StringRight($user_time, 1) = "
m"
            CRT($user_time, "
m")
            $user_time *= (1000*60)

        case StringRight($user_time, 1) = "
h"
            CRT($user_time, "
h")
            $user_time *= (1000*60*60)

        case StringRight($user_time, 1) = "
d"
            CRT($user_time, "
d")
            $user_time *= (1000*60*60*24)
    endselect


    switch $time_unit
        ; case "ms" ; also 1st for quickness
            $user_time = $user_time
        case "
s"
            $user_time = $user_time/1000
            $ret_array[1] = "
s"
        case "
m"
            $user_time = $user_time/1000/60
            $ret_array[1] = "
m"
        case "
h"
            $user_time = $user_time/1000/60/60
            $ret_array[1] = "
h"
        case "
d"
            $user_time = $user_time/1000/60/60/24
            $ret_array[1] = "
h"
    endswitch

    $ret_array[0] = Number($user_time)

    if $full_info then
        return $ret_array
    else
        return $user_time
    endif

endfunc



; 24h > Human Time > 24h..
;
; HourToHumanTime()
;
; Feed it a 24-hour format hour (e.g. "16").
; Returns a 1-dimensional array with two values..    [4, "pm"]
;
;    [0] = hour (integer in 12-hour clock format)
;    [1] = am/pm (string)
;
func HourToHumanTime($24hour)
    local $human_hour[2]
        $human_hour[0] = Number($24hour; 02 -> 2
        $human_hour[1] = "
am"
        if $24hour > 11 then $human_hour[1] = "
pm"
        if $24hour = 00 then $human_hour[0] = 12
        if $24hour > 12 then $human_hour[0] -= 12
        $human_hour[0] = int($human_hour[0])
    return $human_hour
endfunc


; And the reverse..
;
; Feed it the twelve-hour-clock format hour, and the am/pm string ("am" or "pm")
; Returns an integer (the hour, in 24 hour clock format, padded with a zero, if
; necessary), e.g..
;
;        HumanTimeToHour(3, "am") = 03
;        HumanTimeToHour(3, "pm") = 15
;
; I use two variables here because a) it's clearer, and b)
; I've been gagging to use the word "puter" in a variable for ages.
;
func HumanTimeToHour($12hour$am_pm)
    local $puter_hours
    switch $am_pm
        case "
am"
            if $12hour = 12 then
                $puter_hours = 0
            else
                $puter_hours = $12hour
            endif
        case "
pm"
            if $12hour = 12 then
                $puter_hours = 12
            else
                $puter_hours = $12hour + 12
            endif
    endswitch
    return StringFormat("
%02d", $puter_hours)
endfunc




; Convert a plain ASCII file to Unicode..
func MakeUnicodeFile($file)
    local $non_unicode_text = FileRead($file)
    ; only ASCII will produce the same length string as file the size..
    if FileGetSize($file) = StringLen($non_unicode_text) then
        local $unicode_file = FileOpen($file, 2 + 32) ; erase + UTF16
        FileWrite($unicode_file$non_unicode_text)
        return true
    endif
    ; or else something bad happened..
    return false
endfunc


; Normalize all line-endings
; to @CRLF, or whatever..

; this is the fastest, bestest, simplest method..
func UnifyCRLF($some_text$LF=@CRLF)
    return StringRegExpReplace($some_text, "
\R", $LF)
;    return StringRegExpReplace($string, '(*BSR_ANYCRLF)\R', @CRLF)
endfunc


; Feed it a number (which is always a MB value), returns value + " MB". If the
; number is > 1024 MB, it is (optionally) converted and returned + " GB", then
; " TB". 
func FormatMB($mb$round=0, $array=false)

    local $return$unit
    $mb = Number($mb)
    
    select 
    
        case $mb = 0 
            $return = 0
    
        case $mb < 1024  
            $return = Round($mb$round)
            $unit = "
MB"
    
        case $mb >= 1048576 
            $return = Round($mb / 1048576, $round)
            $unit = "
TB"
    
        case $mb >= 1024
            $return = Round($mb / 1024, $round)
            $unit = "
GB"
            
    endselect
    
    if $array then
        local $ret[2]
        $ret[0] = $mb
        $ret[1] = $unit
        return $ret
    endif
    return $return & "
 " & $unit
    
endfunc




func BytesToByteUnit($bytes_value$unit_required$round=2)

 debug($LOG_LF, "
", 7);debug
 debug("
BytesToByteUnit(" & $bytes_value & "," & $unit_required  & "," & $round & ")", @ScriptLineNumber, 7);debug

    local $return = $bytes_value
    switch $unit_required
    
        ;case "B"
            
        case "
KB"; Kilobyte
            $return = Round($bytes_value/1024, $round)
        
        case "
MB; Megabyte
            $return = Round($bytes_value/1048576, $round)
        
        case "
GB; Gigabyte
            $return = Round($bytes_value/1.073742e+009, $round)
        
        case "
TB; Terabyte
            $return = Round($bytes_value/1.099512e+012, $round)
        
        case "
PB; Petabyte
            $return = Round($bytes_value/1.1259e+015, $round)
            
        case "
EB; Exabyte
            $return = Round($bytes_value/1.152922e+018, $round)
    
        case "
ZB; Zettabyte
            $return = Round($bytes_value/1.180592e+021, $round)
    
        case "
YB; Yottabyte    (named after Yoda!)
            $return = Round($bytes_value/1.208926e+024, $round)
            
    endswitch
    debug("
Returning: =>" & $return & "<=", @ScriptLineNumber, 7);debug

    return $return
    
endfunc


; Create a random String.
; Returns a string of "random" upper and lower case letters to the specified length.
; Only letters are returned, no special characters.
func MakeRandomString($length=32, $seed=@Min*@Sec*@MSec)

 debug($LOG_LF, "
", 7);debug
 debug("
MakeRandomString(" & $seed & ")", @ScriptLineNumber, 7);debug

    SRandom($seed)
    local $a[$length]
    local $x = 0
    while $a[$length-1] = "
"
        local $tmp = Chr(Random(65, 122, 1))
        if StringIsAlpha($tmp) then
            $a[$x] = Asc($tmp)
            $x += 1
        endif
    wend

    $a = StringFromASCIIArray($a)
    debug("
MakeRandomString() returning: =>" & $a & "<=", @ScriptLineNumber, 7);debug
    return $a
endfunc

; local $first_done = false
; if not $params then $params = 12
; for $i = 1 to Random($random_name_lower_limit$params, 1)
    ; if not $first_done then
        $new_name &= Chr(Random(65, 90, 1))
        $first_done = true
    ; endif
    $new_name &= Chr(Random(97, 122, 1))
; next



; Create a random "X-String", which is hexadecimal digits (0-9, A-F)
; Works great, but could obviously be improved!
func MakeRandomXString($length=32, $seed=@Min*@Sec*@MSec)
    SRandom($seed)
    local $a[$length]
    local $x = 0
    while $a[$length-1] = "
"
        local $tmp = Chr(Random(48, 71, 1)) ; CAPITALS
        if StringIsXDigit($tmp) then
            $a[$x] = Asc($tmp)
            $x += 1
        endif
    wend
    $a = StringFromASCIIArray($a)
    debug("
MakeRandomXString() returning: =>" & $a & "<=", @ScriptLineNumber, 7);debug
    return $a
endfunc





; From Color-Pickin-Chooser..
;

; ConvertColorValue()
;
;    ConvertColorValue(RGB_color{HEX}, mode{TEXT}, add_prefix{BOOL}, index{INT} (0=return full 6 digits), lowercase{BOOL})
;
;    To get this..        Use any of these..
;    -------------        --------------------------------------------------------
;     RGB Integer:        "i", "RGB Integer", "RGB Int" or "int".
;     AutoIt RGB            "a", "Autoit RGB Hex", "Autoit RGB", "AutoIt Hex", or "AutoIt".
;     AutoIt BGR            "b", "Autoit BGR Hex", "Autoit BGR", "BGR Hex" or "bgr".
;     Delphi Hex            "d", "Delphi" or "Delphi Hex".
;     Visual C++ BGR        "v", "vc", "Visual C++ Hex", "Visual C++ BGR", "Visual C++", "Visual C++ BGR Hex" or "C++".
;     RGB Float            "RGB Float", "float" or "f".
;     HSL                    "h", "Hue/Sat/Lum", "HSL" or "h/s/l".
;     CMYK                "k", "cmyk" or "c/m/y/k".
;     Web Hex                "w", "Web Hex", "Web", or "Hex".

func ConvertColorValue($color$mode = "
web", $prefix = false, $index = 0, $lowercase = $off)
    if StringLeft($color, 1) = "
#" Then $color = StringTrimLeft($color, 1)
    local $pre = "
"
    switch $mode
        case "
i", "RGB Integer", "RGB Int", "int"
            switch $index
                case 0
                    $color = Dec(StringLeft($color, 2)) & "
," & Dec(StringMid($color, 3, 2)) & "," & Dec(StringRight($color, 2))
                case 1
                    return Dec(StringLeft($color, 2))
                case 2
                    return Dec(StringMid($color, 3, 2))
                case 3
                    return Dec(StringRight($color, 2))
            endswitch
        case "
a", "Autoit RGB Hex", "Autoit RGB", "AutoIt Hex", "AutoIt"
            $color = "
0x" & $color
        case "
b", "Autoit BGR Hex", "Autoit BGR", "BGR Hex", "bgr"
            $color = "
0x" & StringRight($color, 2) & StringMid($color, 3, 2) & StringLeft($color, 2)
        case "
d", "Delphi", "Delphi Hex"
            $color = "
00" & StringRight($color, 2) & StringMid($color, 3, 2) & StringLeft($color, 2)
            $pre = "
$"
        case "
v", "vc", "Visual C++ Hex", "Visual C++ BGR", "Visual C++", "Visual C++ BGR Hex", "C++"
            $color = "
0x00" & StringRight($color, 2) & StringMid($color, 3, 2) & StringLeft($color, 2)
        case "
RGB Float", "float", "f"
            local $r = Round((1 / 255) * Dec(StringLeft($color, 2)), 2)
            local $g = Round((1 / 255) * Dec(StringMid($color, 3, 2)), 2)
            local $b = Round((1 / 255) * Dec(StringRight($color, 2)), 2)
            $color = StringFormat("
%#.2f", $r) & "," & StringFormat("%#.2f", $g) & "," & StringFormat("%#.2f", $b)
        case "
h", "Hue/Sat/Lum", "HSL", "h/s/l"
            $color = RGBtoHSL($color, "
,", 100)
        case "
k", "cmyk", "c/m/y/k"
            if $prefix = 1 then
                $color = RGBtoCMYK($color, true)
            else
                $color = RGBtoCMYK($color)
            endif
        case "
w", "Web Hex", "Web", "Hex"
            $pre = "
#"
    endswitch
    if not $prefix or $prefix = 4 then $pre = "
"
    if $lowercase = $on then $color = StringLower($color)
    return $pre & $color
endfunc

Func RGBtoCMYK($rgb_color$norm = 0)
    local $rc_r = ConvertColorValue($rgb_color, "
i", 0, 1) / 255
    local $rc_g = ConvertColorValue($rgb_color, "
i", 0, 2) / 255
    local $rc_b = ConvertColorValue($rgb_color, "
i", 0, 3) / 255
    local $k = MinMin(1 - $rc_r, 1 - $rc_g, 1 - $rc_b)
    local $c = (1 - $rc_r - $k) / (1 - $k)
    local $m = (1 - $rc_g - $k) / (1 - $k)
    local $y = (1 - $rc_b - $k) / (1 - $k)
    if $norm then
        return Round($c * 100, 1) & "
," & Round($m * 100, 1) & "," & Round($y * 100, 1) & "," & Round($k * 100, 1)
    else
        return Round($c, 3) & "
," & Round($m, 3) & "," & Round($y, 3) & "," & Round($k, 3)
    endif
endfunc

func RGBtoHSL($rgb_color$idx = "
", $simple_array = False, $hsl_scale = 1)
    local $rh_r = ConvertColorValue($rgb_color, "
i", 0, 1) / 255
    local $rh_g = ConvertColorValue($rgb_color, "
i", 0, 2) / 255
    local $rh_b = ConvertColorValue($rgb_color, "
i", 0, 3) / 255
    local $rh_min = MinMin($rh_r$rh_g$rh_b)
    local $rh_max = MaxMax($rh_r$rh_g$rh_b)
    local $rh_delta = $rh_max - $rh_min
    if $idx <> 1 then
        local $rh_lightness = ($rh_min + $rh_max) / 2
        if $idx = 3 then return Round($rh_lightness * $hsl_scale, 2)
        local $rh_saturation = 0
        if $rh_lightness > 0 and $rh_lightness < 1 then
            If $rh_lightness < 0.5 then
                $rh_saturation = $rh_delta / (2 * $rh_lightness)
            else
                $rh_saturation = $rh_delta / (2 - 2 * $rh_lightness)
            endif
        endif
        If $idx = 2 then return Round($rh_saturation * $hsl_scale, 2)
    endif
    local $rh_hue = 0
    if $rh_delta > 0 then
        if $rh_max = $rh_r and $rh_max <> $rh_g then
            $rh_hue += ($rh_g - $rh_b) / $rh_delta
        endif
        if $rh_max = $rh_g and $rh_max <> $rh_b then
            $rh_hue += 2 + ($rh_b - $rh_r) / $rh_delta
        endif
        if $rh_max = $rh_b and $rh_max <> $rh_r then
            $rh_hue += 4 + ($rh_r - $rh_g) / $rh_delta
        endif
        $rh_hue *= 60
    endif
    if $rh_hue < 0 then $rh_hue += 360
    if $idx = 1 then return Round($rh_hue)
    local $do_string = true
    if not $idx then
        $idx = "
,"
        $do_string = false
    endif
    local $hsl_arr[3]
    $hsl_arr[0] = Round($rh_hue)
    $hsl_arr[1] = Round($rh_saturation * $hsl_scale, 2)
    $hsl_arr[2] = Round($rh_lightness * $hsl_scale, 2)
    local $hsl = $hsl_arr[0] & $idx & $hsl_arr[1] & $idx & $hsl_arr[2]
    if $do_string then return $hsl
    if $simple_array then return $hsl_arr
    return StringSplit($hsl$idx)
endfunc





; Send the $string after the Shift, Alt, Ctrl & Win (L or R) keys are released.
; Optionally, give a warning after 1 sec if any of those keys are still down.
$flag is the same as for Send().
func SendWait($string$flag=0, $warn="
You don't need to hold the modifier keys down for so long!")

 ; debug($LOG_LF, "", 7);debug
 ; debug("string(" & $string & "," & $flag  & "," & $warn & ")", @ScriptLineNumber, 7);debug

    $am_sending = true
    local $HKt = TimerInit()
    while _IsPressed("
10") or _IsPressed("11") or _IsPressed("12") or _IsPressed("5B") or _IsPressed("5C")
        if $warn <> "
" and TimerDiff($HKt) > 1000 then
            debug("
SendWait(HOLDING_KEYS_WARNING)", @ScriptLineNumber, 7);debug

            if IniRead($ini_path$my_name, "
holding_keys_warning", $OFF) = $OFF then
                MsgBox($MB_TOPMOST, "
Note..", $warn; 0x00040000
                IniWrite($ini_path$my_name, "
holding_keys_warning", $ON)
            endif
        endif
        Sleep(50)
    wend
    Send($string$flag)
    $am_sending = false
    return $string
endfunc



; Fancy Custom Input Box..    (accepts drag&drop of files)
;
; Returns:
;
;    Success:    The value input by the user.
;                @error contains the X coordinate of the window when closed
;                @extended contains the X coordinate of the window when closed
;
;                If you leave the width at the default (-1), CorzFancyInputBox will
;                save and restore its own width settings. Or else specify your own.
;
;    Failure:    Empty "" value.
;                @error contains a non-0 value..
;
;                1 = The Cancel/Close button was pushed.
;                2 = The Timeout time was reached.
;
; As well as being a drop-in replacement for InputBox, you can optionally supply
; the  full path to an ini file and section name to have CorzFancyInputBox save
; the x/y/width specifications for the inputbox, recall them the next time the
; user accesses the same inputbox.
;
; NOTE: CorzFancyInputBox() uses the title to create a preference name. If you
; send 100 separate titles for say, 100 buttons, you will create 100 preferences (x3!)
;
; So put any control-specific text into the prompt, instead.
;
;     local $edit_butt_name = CorzFancyInputBox("Custom Button Name.. ",    "Specify a name for the custom button.. " & _
;                $MSG_LF & "Enter a new name to create a new button.", $custom_buttons_array[$clicked_butt][0] , "" , _
;                                                                            350 , 117, default, default, 0, $ffeGUI)

;

func CorzFancyInputBox($title$prompt$default="
", $pass="", $ib_w=default, $ib_h=default, $ib_x=-1, $ib_y=-1, _
                        $timeout=0, $gui_ex=false, $style=-1, $inipath=$ini_path$sectionname=$my_name)
 debug($LOG_LF, "
", 7);debug
 debug("
CorzFancyInputBox(" & $title & "," & $prompt  & "," & $default & ","  & $pass & "," & $ib_w  & "," & $ib_h & ","  & $ib_x & "," & $ib_y  & "," & $timeout & "," & $gui_ex  & "," & $style  & "," & $inipath  & "," & $sectionname & ")", @ScriptLineNumber, 7);debug

    ; "Defaults"..
    if $default = default then $default = "
"
    if $pass = default then $pass = "
"
    if $timeout = default then $timeout = 0
    if $gui_ex = default then $gui_ex = false
    if $style = default then $style = -1
    if $inipath = default then $inipath = $ini_path
    if $sectionname = default then $sectionname =  $my_name
    if $ib_w = default then $ib_w = @DesktopWidth/2


    ; DialogOpen()

    local $last_event_mode = AutoItSetOption("
GUIOnEventMode", 0)
    local $coord = AutoItSetOption("
GUICoordMode", 1)

    local $pref_name = CleanPrefName($title)
    $ib_x = IniRead($inipath$sectionname, "
inputbox_" & $pref_name & "_x", $ib_x)
    $ib_y = IniRead($inipath$sectionname, "
inputbox_" & $pref_name & "_y", $ib_y)
    $ib_w = IniRead($inipath$sectionname, "
inputbox_" & $pref_name & "_width", $ib_w)

    if not $ib_x or $ib_x = default then $ib_x = -1
    if not $ib_y or $ib_y = default then $ib_y = -1
    if not $ib_h or $ib_h = default then $ib_h = 96
    if not $ib_w or $ib_w = default then $ib_w = @DesktopWidth/2

    debug("
$ib_x: =>" & $ib_x & "<=", @ScriptLineNumber, 7);debug
    debug("
$ib_y: =>" & $ib_y & "<=", @ScriptLineNumber, 7);debug
    debug("
$ib_h: =>" & $ib_h & "<=", @ScriptLineNumber, 7);debug
    debug("
$ib_w: =>" & $ib_w & "<=", @ScriptLineNumber, 7);debug

    local $min_w = $ib_w
    local $return = false, $error
    local $inputstyle = $ES_AUTOHSCROLL
    local $exStyle = BitOr($WS_EX_ACCEPTFILES$WS_EX_TOPMOST)

    if $style = -1 then $style = BitOr($WS_MINIMIZEBOX$WS_CAPTION$WS_POPUP$WS_SYSMENU$WS_SIZEBOX)
    if $pass <> 0 then $inputstyle = BitOr($ES_AUTOHSCROLL$ES_PASSWORD)

    ; make the dialog..
    local $cfib_GUI = GUICreate($title$ib_w$ib_h$ib_x$ib_y$style$exStyle$gui_ex)


    local $label_prompt = GUICtrlCreateLabel($prompt, 10, 5, $ib_w-30, $ib_h-70)
    GUICtrlSetFont(-1, 10)

    local $butt_OK = GUICtrlCreateButton("
OK", $ib_w-45, $ib_h-31, 40, 24, $BS_DEFPUSHBUTTON)
    GUICtrlSetState(-1, $GUI_ONTOP)

    local $butt_cancel = GUICtrlCreateButton("
Cancel", 5, $ib_h-31, 60, 24)
    GUICtrlSetState(-1, $GUI_ONTOP)

    local $input_MyID = GUICtrlCreateInput($default, 70, $ib_h-30, $ib_w-125, 22, $inputstyle)
    GUICtrlSetState(-1, $GUI_FOCUS)
    GUICtrlSetFont(-1, 10 , 400, "
", "Consolas", 10)
    GUICtrlSetState(-1, $GUI_DROPACCEPTED)


    GUICtrlSetResizing($label_prompt$GUI_DOCKALL)
    GUICtrlSetResizing($butt_OK$GUI_DOCKSTATEBAR+$GUI_DOCKWIDTH+$GUI_DOCKRIGHT)
    GUICtrlSetResizing($butt_cancel$GUI_DOCKSTATEBAR+$GUI_DOCKLEFT+$GUI_DOCKWIDTH)
    GUICtrlSetResizing($input_MyID$GUI_DOCKSTATEBAR+$GUI_DOCKLEFT+$GUI_DOCKRIGHT)

    GUISetState(@SW_SHOW, $cfib_GUI)
    WinMove($title, "
", default, default, $ib_w$ib_h)

    if $timeout <> 0 then local $dialog_begin = TimerInit()
    local $size_array

    while true
        switch GUIGetMsg()
            case $GUI_EVENT_RESIZED
                $size_array = WinGetPos($title)
                if not IsArray($size_array) then continueloop
                if $size_array[3] <> $ib_h then WinMove($title, "
", default, default, $size_array[2], $ib_h)
                if $size_array[2] < $min_w then WinMove($title, "
", default, default, $min_w$ib_h)
            case $GUI_EVENT_CLOSE$butt_cancel
                $return = false
                $error = 1
                exitloop
            case $butt_OK
                $return = StringStripWS(GUICtrlRead($input_MyID), 3)
                exitloop
        endswitch

        if $timeout <> 0 then
            if TimerDiff($dialog_begin)/1000 > $timeout then
                $return = false
                $error = 2
                exitloop
            endif
        endif
        Sleep(10)
    wend

    ; Return X & Y in @error & @extended..
    if $return then
        local $x_coord = -1, $y_coord = -1, $ib_width
        $size_array = WinGetPos($title)    ; 0-1-2-3:x-y-w-h
        if IsArray($size_array) then
            $x_coord = $size_array[0]
            $y_coord = $size_array[1]
            $ib_width = $size_array[2]
        endif
    endif

    AutoItSetOption("
GUIOnEventMode", $last_event_mode)
    AutoItSetOption("
GUICoordMode", $coord)
    GUIDelete($cfib_GUI)

    ; DialogClose()
    debug("
$return: =>" & $return & "<=", @ScriptLineNumber, 7);debug


    if $return then
        ; only write prefs if the user actually changed the position/dimensions..
        if $x_coord and $x_coord <> -1 then IniWrite($inipath$sectionname, "
inputbox_" & $pref_name & "_x", $x_coord)
        if $y_coord and $y_coord <> -1 then IniWrite($inipath$sectionname, "
inputbox_" & $pref_name & "_y", $y_coord)
        if $ib_width and $ib_width <> @DesktopWidth/2 then IniWrite($inipath$sectionname, "
inputbox_" & $pref_name & "_width", $ib_width)
        return $return
    endif
    return SetError($error, 0, "
")
endfunc




#cs
    CorzFancyMsgBox()

    A replacement for MsgBox, except with a checkbox for the user to never be
    asked about the issue again. It's basic, but does the job.
    
    Returns the number of the button the user clicked (1, 2, 3 or 4).

    You can set the usual $hwnd and styles (defaults should be fine).

    It will resize its height to fit the text sent (ish).

    You can have from one to four buttons, sending the text required for each
    button, or blank to produce no button. Obviously, if you want only two
    buttons, you set the value of the first two and leave the last two blank.

    If you set, for example, the value of the first and last buttons, leaving 2
    and 3 blank, you will get four buttons, two of which will be blank!

    You can also set which is the default button (1, 2, 3 or 4).

    CorzFancyMsgBox() will store the X & Y position of the dialog for future
    instances. Each dialog gets its own settings (the pref name being derived
    from the title of the message box).

    You can supply an ini path and section name to store the prefs, otherwise
    CorzFancyMsgBox() will use the app default ini and main app [name] section.

    If the user checks the "
Always perform the chosen action" box, the user's
    choice is saved to the ini file and in future CorzFancyMsgBox() will simply
    return the pre-saved value.

    You can also set an icon (full path, unless it's in your PATH) and icon
    index (note: most icons inside shell32.dll use -negative IDs)..

    Handy shell32.dll icons..

        ID            Icon
        -----        -------------
        24            question mark
        -211        question mark 3D
        -28/28        stop     (appliance)
        -220        stop    (no entry)
        -237        warning triangle
        -278        information (i)    old 2D                (default icon)
        -317        cog    (completely square, no transparency required!)
        -239        Recycle

    CorzFancyMsgBox() returns:

        The number of the button the user clicked (1, 2, 3 or 4).

        If the user clicked a button, @error and @extended are set to the final
        X and Y positions of the dialog, respectively. FYI.

        If the user cancels out of the dialog, @error will be set to 1 and the
        function will return 0 as the value.


    Examples:

    CorzFancyMsgBox("
This is a notice", "You never have to see this notice again")

    $debug = CorzFancyMsgBox("
Really Overwrite Files?", "Many triggers and tasks have been found to already exist in KeyBind.ini. Overwrite?" , 60, false, -1, $ini_path, "Test", "Yes", "No", "", "", 2, "shell32.dll", -237)
    debug("
$debug: =>" & $debug & "<=", @ScriptLineNumber, 7);debug

#ce
func CorzFancyMsgBox($title$text$timeout=0, $gui_ex=false, $style=-1, $inipath=$ini_path, _
                    $sectionname=$my_name$butt1_txt="
OK", $butt2_txt="", $butt3_txt="", $butt4_txt="", _
                    $default_butt=1, $icon_path="
shell32.dll", $icon_id=-278)

 debug($LOG_LF, "
", 7);debug
 debug("
CorzFancyMsgBox(" & $title & "," & $text  & "," & $gui_ex & "," & $style  & "," & $inipath & "," & $sectionname  & "," & $butt1_txt & "," & $butt2_txt  & "," & $butt3_txt & "," & $butt4_txt  & "," & $default_butt & "," & $icon_path  & "," & $icon_id & ")", @ScriptLineNumber, 7);debug

    local $pref_name = CleanPrefName($title)

    ; There may be a predetermined response..
    local $dont_bug_me = IniRead($inipath$sectionname$pref_name & "
_dont_bug_me", 0)
    debug("
$dont_bug_me: =>" & $dont_bug_me & "<=", @ScriptLineNumber, 7);debug
    if $dont_bug_me <> 0 then return $dont_bug_me    ; the number of the saved response

    if $timeout        = default then $timeout        = 0
    if $gui_ex        = default then $gui_ex        = false
    if $style        = default then $style        = -1
    if $inipath        = default then $inipath        = $ini_path
    if $butt1_txt    = default then $butt1_txt    = "
OK"
    if not $butt1_txt then $butt1_txt = "
OK"
    if $sectionname    = default then $sectionname    = $my_name

    local $dt_pushed
    local $dt_warn_count = 0

    local $error$extended
    local $last_event_mode = AutoItSetOption("
GUIOnEventMode", 0)
    local $last_coord_mode = AutoItSetOption("
GUICoordMode", 0) ; relative to last control


    ; work out some sizes and positions..

    if $style = -1 then $style = BitOr($WS_CAPTION$WS_SYSMENU$WS_SIZEBOX)
    local $exStyle = BitOr($WS_EX_TOPMOST$GUI_WS_EX_PARENTDRAG)

    local $dt_min_w = 400
    local $dt_min_h = 130
    local $dt_w
    local $dt_h = $dt_min_h

    ; Not very clever. Works well within our limited range.    (under 1000 characters)
    local $text_len = StringLen($text)
    debug("
$text_len: =>" & $text_len & "<=", @ScriptLineNumber, 7);debug

    local $size_array
    local $addin_height = 0
    while $addin_height < $text_len
        $dt_h += 22        ; add these pixels to the GUI height
        $addin_height += 50 ; for every extra this many characters text length (slightly magic!)
        $dt_min_h += 22
    wend

    local $butt_width
    local $dt_x = IniRead($inipath$sectionname, "
msgbox_" & $pref_name & "_x", -1)
    local $dt_y = IniRead($inipath$sectionname, "
msgbox_" & $pref_name & "_y", -1)
    $dt_w = IniRead($inipath$sectionname, "
msgbox_" & $pref_name & "_width", $dt_min_w)

    ; create the GUI..
    ;

    local $dt_GUI = GUICreate($title$dt_w$dt_h$dt_x$dt_y$style$exStyle$gui_ex)
    GUISetFont(-1, 10, 400, 0, "
Segoe UI", 5)
    GUISetBkColor(0xFFFFFF)

    ; icon_path
    local $icon_width = 0
    if $icon_path then $icon_width = 36


    ; CheckBox::
    ; Don't bug me again about this!!
    ;
    GUISetCoord(10, $dt_h-66)
    local $check_dont_bug = GUICtrlCreateCheckBox("
Always perform the chosen action.", 0, 0, 220, default, _
        Bitor($WS_TABSTOP$BS_AUTOCHECKBOX), BitOr($WS_EX_TOPMOST$WS_EX_TRANSPARENT$GUI_WS_EX_PARENTDRAG))
    GUICtrlSetFont(-1, 8.5, 400, 0, "
Segoe UI", 5)
    GUICtrlSetBkColor(-1, 0xFFFFFF)
    GUICtrlSetResizing(-1, $GUI_DOCKBOTTOM+$GUI_DOCKSIZE+$GUI_DOCKLEFT)


    ; the "text" label..
    ;
    GUISetCoord(15+$icon_width, 6)
    GUICtrlCreatelabel($text, 0, 0, $dt_w-30-$icon_width$dt_h-53, _
                            BitOr($SS_NOTIFY$SS_LEFT), $GUI_WS_EX_PARENTDRAG)
    GUICtrlSetBkColor(-1, 0xFFFFFF)
    ; GUICtrlSetBkColor(-1, 0xFF0000)
    GUICtrlSetResizing(-1, $GUI_DOCKBORDERS)
    GUICtrlSetFont(-1, 10, 400, 0, "
Segoe UI", 5)
    GUICtrlSetTip(-1, "
 You can click and drag the entire window around quickly from here ")


    ; icon        "shell32.dll"
    ;
    if $icon_path then
        GUISetCoord(10, 10)
        GUICtrlCreateIcon("
", "", 0, -2, 32, 32, $SS_ICON$WS_EX_TRANSPARENT)
        GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)
        GUICtrlSetTip(-1, "
 this is an image ")
        GUICtrlSetState(-1,$GUI_DISABLE)
        GUICtrlSetImage(-1, $icon_path$icon_id)
        GUICtrlSetResizing(-1, $GUI_DOCKSIZE+$GUI_DOCKLEFT+$GUI_DOCKTOP)

    endif

    ; The Stripe
    ; (F0F0F0)..
    ;
    GUISetCoord(50,$dt_h-43)
    $label_stripe = GUICtrlCreatelabel("", 0, 0, $dt_w$dt_h, 0, $GUI_WS_EX_PARENTDRAG)
    GUICtrlCreatelabel("
 ", 0, 0, $dt_w-100, 2, 0, $GUI_WS_EX_PARENTDRAG)
    GUICtrlSetBkColor(-1, 0xF0F0F0)
    GUICtrlSetResizing(-1, $GUI_DOCKBOTTOM+$GUI_DOCKLEFT+$GUI_DOCKRIGHT+$GUI_DOCKHEIGHT)


    ; Make The Buttons..
    ;
    local $buttons = 1
    if $butt2_txt then $buttons = 2
    if $butt3_txt then $buttons = 3
    if $butt4_txt then $buttons = 4
    local $buttons_array[$buttons+1][2] = [[$buttons]]
    local $buttonstyle = BitOr($BS_CENTER$BS_FLAT$BS_VCENTER$BS_NOTIFY$WS_TABSTOP)
    local $total_width = 0, $gap = 7
    local $button_top = $dt_h-33
    local $i = 1 ; [0] => Button ID, [1] => text of button
    while $i <= $buttons
        $buttons_array[$i][1] = Eval("
butt" & $i & "_txt") ; screw the manual! It works!
        $i += 1
    wend
    ; create buttons, as required..
    for $i = $buttons_array[0][0] to 1 step -1
        $butt_width = (StringLen($buttons_array[$i][1])*8)+20
        GUISetCoord($dt_w-$total_width-$butt_width-$gap$button_top)
        $buttons_array[$i][0] = GUICtrlCreateButton($buttons_array[$i][1], 0, 0, $butt_width, 25, $buttonstyle)
        GUICtrlSetFont(-1, 9, 400, 0, "
Segoe UI", 5)
        GUICtrlSetResizing(-1, $GUI_DOCKBOTTOM+$GUI_DOCKSIZE+$GUI_DOCKRIGHT)
        $total_width += $butt_width+$gap
        if $i = $default_butt then ControlFocus($dt_GUI, "
", $buttons_array[$i][0])
    next

    ; Another Dragable Area..
    ;
    GUISetCoord(0,$button_top-8)
    GUICtrlCreateLabel("
", 0, 0, $dt_w-$total_width-2, 50, 0, $GUI_WS_EX_PARENTDRAG)
    GUICtrlSetTip(-1, "
 Yes, you can click and drag the entire window around quickly from here, too ")
    GUICtrlSetResizing(-1, $GUI_DOCKBOTTOM+$GUI_DOCKSIZE+$GUI_DOCKLEFT+$GUI_DOCKRIGHT)


    ; Show the dialog..
    ;
    GUISetState(@SW_SHOW, $dt_GUI)
    WinMove($dt_GUI, "
", default, default, $dt_w$dt_h)

    local $msg

    while true

        $dt_pushed = 0

        $msg = GUIGetMsg()

        for $i = 1 to $buttons_array[0][0]
            if $msg = $buttons_array[$i][0] then
                $dt_pushed = $i
                exitloop
            endif
        next

        if $dt_pushed then
            exitloop
        else
            select

                case $msg = $GUI_EVENT_CLOSE
                    exitloop

                case $msg = $GUI_EVENT_RESIZED
                    $size_array = WinGetPos($dt_GUI)
                    if not IsArray($size_array) then return false
                    if $size_array[2] < $dt_min_w then WinMove($dt_GUI, "
", default, default, $dt_min_w$size_array[3])
                    if $size_array[3] < $dt_min_h then WinMove($dt_GUI, "
", default, default, $size_array[2], $dt_min_h)

                case $msg = $check_dont_bug

                    if GUICtrlread($check_dont_bug) = $ON then

                        $dt_warn_count += 1
                        local $message =     "
From now on you will no longer be notified and " & $my_name & _
                                            "
 will just get on with doing whatever YOU CHOOSE NEXT..."
                        if $dt_warn_count > 1 then $message = "
You have been warned!"
                        if $dt_warn_count > 3 then $message = "
Oh! Just DECIDE will you!"
                        MsgBox($MB_ICONWARNING+$MB_SYSTEMMODAL, "
This is PERMANENT!!", $message)

                    endif

            endselect

        endif

        Sleep(50)
    wend



    ; User clicked a button (didn't cancel out)
    ;
    if $dt_pushed = 0 then
        $error = 1    ; user cancelled out
    else
        debug("
CorzFancyMsgBox() User Clicked Button: " & $dt_pushed, @ScriptLineNumber, 7);debug
        debug("
CorzFancyMsgBox() User Chooses Option: " & $buttons_array[$dt_pushed][1], @ScriptLineNumber, 7);debug

        ; There may already be a response for this dialog..
        ; Re-use $dont_bug_me.
        $dont_bug_me = GUICtrlRead($check_dont_bug)

        if $dont_bug_me = $ON then
            IniWrite($inipath$sectionname$pref_name & "
_dont_bug_me", $dt_pushed)
        else
            ; If you are never seeing this dialog again you don't need this lot..
            $size_array = WinGetPos($dt_GUI)    ; 0-1-2-3:x-y-w-h
            if IsArray($size_array) then
                IniWrite($inipath$sectionname, "
msgbox_" & $pref_name & "_x", $size_array[0])
                IniWrite($inipath$sectionname, "
msgbox_" & $pref_name & "_y", $size_array[1])
                IniWrite($inipath$sectionname, "
msgbox_" & $pref_name & "_width", $size_array[2])
                $error = $size_array[0]
                $extended = $size_array[1]
            endif
        endif
    endif

    AutoItSetOption("
GUIOnEventMode", $last_event_mode)
    AutoItSetOption("
GUICoordMode", $last_coord_mode)
    GUIDelete($dt_GUI)

    return SetError($error$extended$dt_pushed)

endfunc




; Event-Mode version of above function..
;
func CorzFancyMsgBoxEM($title$text$timeout=0, $gui_ex=false, $style=-1, $inipath=$ini_path, _
                    $sectionname=$my_name$butt1_txt="
OK", $butt2_txt="", $butt3_txt="", $butt4_txt="", _
                    $default_butt=1, $icon_path="
shell32.dll", $icon_id=-278)

 debug($LOG_LF, "
", 7);debug
 debug("
CorzFancyMsgBoxEM(" & $title & "," & $text  & "," & $gui_ex & "," & $style  & "," & $inipath & "," & $sectionname  & "," & $butt1_txt & "," & $butt2_txt  & "," & $butt3_txt & "," & $butt4_txt  & "," & $default_butt & "," & $icon_path  & "," & $icon_id & ")", @ScriptLineNumber, 7);debug

    local $pref_name = CleanPrefName($title)

    ; There may be a predetermined response..
    local $dont_bug_me = IniRead($inipath$sectionname$pref_name & "
_dont_bug_me", 0)
    debug("
$dont_bug_me: =>" & $dont_bug_me & "<=", @ScriptLineNumber, 7);debug
    if $dont_bug_me <> 0 then return $dont_bug_me    ; the number of the saved response

    if $timeout        = default then $timeout        = 0
    if $gui_ex        = default then $gui_ex        = false
    if $style        = default then $style        = -1
    if $inipath        = default then $inipath        = $ini_path
    if $butt1_txt    = default then $butt1_txt    = "
OK"
    if not $butt1_txt then $butt1_txt = "
OK"
    if $sectionname    = default then $sectionname    = $my_name

    global $dt_open = true
    global $dt_pushed = 0
    global $dt_warn_count = 0

    local $error$extended
    local $last_event_mode = AutoItSetOption("
GUIOnEventMode", 1)
    local $last_coord_mode = AutoItSetOption("
GUICoordMode", 0) ; relative to last control


    ; work out some sizes and positions..

    if $style = -1 then $style = BitOr($WS_CAPTION$WS_SYSMENU$WS_SIZEBOX)
    local $exStyle = BitOr($WS_EX_TOPMOST$GUI_WS_EX_PARENTDRAG)

    global $dt_min_w = 400
    global $dt_min_h = 130
    local $dt_w
    local $dt_h = $dt_min_h

    ; Not very clever. Works well within our limited range.    (under 1000 characters)
    local $text_len = StringLen($text)
    debug("
$text_len: =>" & $text_len & "<=", @ScriptLineNumber, 7);debug

    local $addin_height = 0
    while $addin_height < $text_len
        $dt_h += 22        ; add these pixels to the GUI height
        $addin_height += 50 ; for every extra this many characters text length (slightly magic!)
        $dt_min_h += 22
    wend

    local $butt_width
    local $dt_x = IniRead($inipath$sectionname, "
msgbox_" & $pref_name & "_x", -1)
    local $dt_y = IniRead($inipath$sectionname, "
msgbox_" & $pref_name & "_y", -1)
    $dt_w = IniRead($inipath$sectionname, "
msgbox_" & $pref_name & "_width", $dt_min_w)

    ; create the GUI..
    ;

    global $dt_GUI = GUICreate($title$dt_w$dt_h$dt_x$dt_y$style$exStyle$gui_ex)
    GUISetFont(-1, 10, 400, 0, "
Segoe UI", 5)
    GUISetBkColor(0xFFFFFF)

    GUISetOnEvent($GUI_EVENT_CLOSE, "
cfmbCloseDialog", $dt_GUI)
    GUISetOnEvent($GUI_EVENT_RESIZED, "
cfmbResize", $dt_GUI)

    ; icon_path
    local $icon_width = 0
    if $icon_path then $icon_width = 36



    ; CheckBox::
    ; Don't bug me again about this!!
    ;
    GUISetCoord(10, $dt_h-66)
    global $check_dont_bug = GUICtrlCreateCheckBox("
Always perform the chosen action.", 0, 0, 220, default, _
        Bitor($WS_TABSTOP$BS_AUTOCHECKBOX), BitOr($WS_EX_TOPMOST$WS_EX_TRANSPARENT$GUI_WS_EX_PARENTDRAG))
    GUICtrlSetFont(-1, 8.5, 400, 0, "
Segoe UI", 5)
    GUICtrlSetBkColor(-1, 0xFFFFFF)
    GUICtrlSetResizing(-1, $GUI_DOCKBOTTOM+$GUI_DOCKSIZE+$GUI_DOCKLEFT)
    GUICtrlSetOnEvent(-1, "
cfmbWarnPermanent")


    ; the "text" label..
    ;
    GUISetCoord(15+$icon_width, 6)
    GUICtrlCreatelabel($text, 0, 0, $dt_w-30-$icon_width$dt_h-53, _
                            BitOr($SS_NOTIFY$SS_LEFT), $GUI_WS_EX_PARENTDRAG)
    GUICtrlSetBkColor(-1, 0xFFFFFF)
    ; GUICtrlSetBkColor(-1, 0xFF0000)
    GUICtrlSetResizing(-1, $GUI_DOCKBORDERS)
    GUICtrlSetFont(-1, 10, 400, 0, "
Segoe UI", 5)
    GUICtrlSetTip(-1, "
 You can click and drag the entire window around quickly from here ")


    ; icon        "shell32.dll"
    ;
    if $icon_path then
        GUISetCoord(10, 10)
        GUICtrlCreateIcon("
", "", 0, -2, 32, 32, $SS_ICON$WS_EX_TRANSPARENT)
        GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)
        GUICtrlSetTip(-1, "
 this is an image ")
        GUICtrlSetState(-1,$GUI_DISABLE)
        GUICtrlSetImage(-1, $icon_path$icon_id)
        GUICtrlSetResizing(-1, $GUI_DOCKSIZE+$GUI_DOCKLEFT+$GUI_DOCKTOP)

    endif

    ; The Stripe
    ; (F0F0F0)..
    ;
    GUISetCoord(50,$dt_h-43)
    $label_stripe = GUICtrlCreatelabel("", 0, 0, $dt_w$dt_h, 0, $GUI_WS_EX_PARENTDRAG)
    GUICtrlCreatelabel("
 ", 0, 0, $dt_w-100, 2, 0, $GUI_WS_EX_PARENTDRAG)
    GUICtrlSetBkColor(-1, 0xF0F0F0)
    GUICtrlSetResizing(-1, $GUI_DOCKBOTTOM+$GUI_DOCKLEFT+$GUI_DOCKRIGHT+$GUI_DOCKHEIGHT)


    ; Make The Buttons..
    ;
    local $buttons = 1
    if $butt2_txt then $buttons = 2
    if $butt3_txt then $buttons = 3
    if $butt4_txt then $buttons = 4
    global $buttons_array[$buttons+1][2] = [[$buttons]]
    local $buttonstyle = BitOr($BS_CENTER$BS_FLAT$BS_VCENTER$BS_NOTIFY$WS_TABSTOP)
    local $total_width = 0, $gap = 7
    local $button_top = $dt_h-33
    local $i = 1 ; [0] => Button ID, [1] => text of button
    while $i <= $buttons
        $buttons_array[$i][1] = Eval("
butt" & $i & "_txt") ; screw the manual! It works!
        $i += 1
    wend
    ; create buttons, as required..
    for $i = $buttons_array[0][0] to 1 step -1
        $butt_width = (StringLen($buttons_array[$i][1])*8)+20
        GUISetCoord($dt_w-$total_width-$butt_width-$gap$button_top)
        $buttons_array[$i][0] = GUICtrlCreateButton($buttons_array[$i][1], 0, 0, $butt_width, 25, $buttonstyle)
        GUICtrlSetFont(-1, 9, 400, 0, "
Segoe UI", 5)
        GUICtrlSetOnEvent(-1, "
cfmbPushButton")
        GUICtrlSetResizing(-1, $GUI_DOCKBOTTOM+$GUI_DOCKSIZE+$GUI_DOCKRIGHT)
        ;GUICtrlSetBkColor(-1, $GUI_BKCOLOR_TRANSPARENT)
        $total_width += $butt_width+$gap
        if $i = $default_butt then ControlFocus($dt_GUI, "
", $buttons_array[$i][0])
    next

    ; Another Dragable Area..
    ;
    GUISetCoord(0,$button_top-8)
    GUICtrlCreateLabel("
", 0, 0, $dt_w-$total_width-2, 50, 0, $GUI_WS_EX_PARENTDRAG)
    GUICtrlSetTip(-1, "
 Yes, you can click and drag the entire window around quickly from here, too ")
    GUICtrlSetResizing(-1, $GUI_DOCKBOTTOM+$GUI_DOCKSIZE+$GUI_DOCKLEFT+$GUI_DOCKRIGHT)


    ; Show the dialog..
    ;
    GUISetState(@SW_SHOW, $dt_GUI)
    WinMove($dt_GUI, "
", default, default, $dt_w$dt_h)

    while $dt_open
        Sleep(10)
    wend

    ; User clicked a button (didn't cancel out)
    ;
    if $dt_pushed = 0 then
        $error = 1    ; user cancelled out
    else
        debug("
CorzFancyMsgBox() User Clicked Button:: " & $dt_pushed, @ScriptLineNumber, 7);debug
        debug("
CorzFancyMsgBox() User Chooses Option:: " & $buttons_array[$dt_pushed][1], @ScriptLineNumber, 7);debug

        ; There may already be a response for this dialog..
        ; Re-use $dont_bug_me.
        $dont_bug_me = GUICtrlRead($check_dont_bug)

        if $dont_bug_me = $ON then
            IniWrite($inipath$sectionname$pref_name & "
_dont_bug_me", $dt_pushed)
        else
            ; If you are never seeing this dialog again you don't need this lot..
            local $size_array = WinGetPos($dt_GUI)    ; 0-1-2-3:x-y-w-h
            if IsArray($size_array) then
                IniWrite($inipath$sectionname, "
msgbox_" & $pref_name & "_x", $size_array[0])
                IniWrite($inipath$sectionname, "
msgbox_" & $pref_name & "_y", $size_array[1])
                IniWrite($inipath$sectionname, "
msgbox_" & $pref_name & "_width", $size_array[2])
                $error = $size_array[0]
                $extended = $size_array[1]
            endif
        endif
    endif

    AutoItSetOption("
GUIOnEventMode", $last_event_mode)
    AutoItSetOption("
GUICoordMode", $last_coord_mode)
    GUIDelete($dt_GUI)

    return SetError($error$extended$dt_pushed)

endfunc

func cfmbResize()
    local $size_array = WinGetPos($dt_GUI)
    if not IsArray($size_array) then return false
    if $size_array[2] < $dt_min_w then WinMove($dt_GUI, "
", default, default, $dt_min_w$size_array[3])
    if $size_array[3] < $dt_min_h then WinMove($dt_GUI, "
", default, default, $size_array[2], $dt_min_h)
endfunc

func cfmbPushButton()
 debug($LOG_LF, "
", 7);debug
 debug("
cfmbPushButton(" & @GUI_CtrlId & ")", @ScriptLineNumber, 7);debug
    for $i = 1 to $buttons_array[0][0]
        if @GUI_CtrlId = $buttons_array[$i][0] then
            $dt_pushed = $i
            cfmbCloseDialog()
        endif
    next
endfunc

func cfmbWarnPermanent()
    if GUICtrlread($check_dont_bug) = $OFF then return
    $dt_warn_count += 1
    local $message =     "
From now on you will no longer be notified and " & $my_name & _
                        "
 will just get on with doing whatever YOU CHOOSE NEXT..."
    if $dt_warn_count > 1 then $message = "
You have been warned!"
    if $dt_warn_count > 3 then $message = "
Oh! Just DECIDE will you!"
    MsgBox($MB_ICONWARNING+$MB_SYSTEMMODAL, "
This is PERMANENT!!", $message)
endfunc

func cfmbCloseDialog()
    $dt_open = false
endfunc








; Automatic Version Checking..
;
; requires..
;    #include <Date.au3>
;    #include <Misc.au3>
;    #include <WinAPIFiles.au3>
;    #include <InetConstants.au3>

func VersionCheckOnline($v_name$vck_url$dl_url$my_ini$my_section$current_version$GUI=default)
 debug($LOG_LF, "
", 7);debug
 debug("
VersionCheckOnline(" & $v_name & "," & $vck_url  & "," & $dl_url & "," & $my_ini  & "," & $my_section & "," & $current_version  & "," & $GUI & ")", @ScriptLineNumber, 7);debug

    local $version_checking = IniRead($my_ini$my_section, "
version_checking", "")

    if $version_checking == 0 then return false

    if $version_checking = -1 or $version_checking = "
" then    ; -1 at install time
        ;UnSetHotKeys()
        local $do_checkver = InputBox("
Automatic Version Checking?", $v_name & " can check online for a new version of itself." & $MSG_LF & _
                                "
Enter the number of days between checks (0 to disable version checking).  ", "" , " M" , _
                                                                                    400, 150 , (@DesktopWidth/2)-200, (@DesktopHeight/2)-50, 0, $GUI)
        ;SetHotKeys()
        if $do_checkver = "
" then return false ; cancelled out of inputbox
        IniWrite($my_ini$my_section, "
version_checking", Number($do_checkver))    ; they enter "fuck off!", we write "0"
        if $do_checkver = 0 then return false
    endif

    if $version_checking > 365 then $version_checking = 365

    if $version_checking > 0 or StringLeft($version_checking, 1) = "
a" then
        local $version_checked = IniRead($my_ini$my_section, "
version_checked", "2000/01/01 00:00:00")
        if StringLeft($version_checking, 1) <> "
a" then
            local $days_passed = _DateDiff("
D", $version_checked, _NowCalc())
            if $days_passed < $version_checking then return false
        endif
    endif

    local $daystr = "
every day"
    if $version_checking > 1 then $daystr = "
every " & $version_checking & " days"
    if StringLeft($version_checking, 1) = "
a" then $daystr = "always"

    ProgressOn("
Version Check (" & $daystr & ")" , "Checking for new version..")
    ProgressSet(25)

    $vck_url &= "
&frequency=" & $version_checking & "&current=" & $current_version

    local $published_version = BackGroundDownload($vck_url, 5000, 25)
    local $errmsg$ext_error = @Extended

    if @Error <> 0 or not $published_version then
        ProgressOff()
        switch $ext_error
            case 1
                $errmsg = $MSG_LF & "
It looks like " & $my_domain & " is down."
            case 2
                $errmsg = $MSG_LF & "
Connexion timed-out."
            case 3
                $errmsg = $MSG_LF & "
Check your firewall."
        endswitch
        MsgBox(16, "
Version Check FAILED!", "Could not determine version online." & $errmsg)
        return false
    endif

    ProgressSet(50)
    IniWrite($my_ini$my_section, "
version_checked", _NowCalc())
    ProgressSet(75)

    local $vcomp = _VersionCompare($current_version$published_version)
    switch $vcomp
        case 0, 1    ; both equal (or $current_version is greater!!!)
            ProgressSet(100)
            ProgressOff()
            return false
        case -1
            ProgressSet(100)
            local $version_response = MsgBox(4+48+262144, "
New Version Available", "A newer version of " & $v_name & " is available." & $MSG_LF _
                                                                                &  "
Would you like to visit the download page?")
            ProgressOff()
            switch $version_response

                case 6    ; yes - go to download page
                    ShellExecute($dl_url)
            endswitch
            return $published_version
    endswitch
endfunc



; Download a file from $dl_URL in the background, wait for $timeout milliseconds.
;
; This is designed to be used with VersionCheckOnline(), but could be used for any background download.
; Returns the raw file data.
; Abort, set @Error to non-0 and return false if file not received. Type of fail is in @Extended (1=server down, 2=timeout, 3=firewall blocked)
;
; NOTE: this function updates a progress dialog, so you better have one up! (set current position with $start)

func BackGroundDownload($dl_URL$timeout=5000, $start=0)
 debug($LOG_LF, "
", 7);debug
 debug("
BackGroundDownload(" & $dl_URL & "," & $timeout  & "," & $start & ")", @ScriptLineNumber, 7);debug

    Local $c_time$TmpFile = _WinAPI_GetTempFileName(@TempDir)
    local $DL = InetGet($dl_URL$TmpFile, ($INET_FORCERELOAD + $INET_FORCEBYPASS), $INET_DOWNLOADBACKGROUND)

    ; we could use a sleep step for calculating time, but using small steps (eg. 25) would throw the total out of whack
    local $BG_time = TimerInit()

    do
        Sleep(10)

        $c_time = Round(TimerDiff($BG_time))

        if Mod($c_time, 100) < 11 then    ; 1 larger than sleep time (above).
            if $c_time > 999 then
                ProgressSet($start+(($c_time*3)/1000), "
", "Checking: timeout in " & ($timeout-$c_time) & " ms")
            endif
        endif

        if $c_time >= $timeout then
            InetClose($DL)
            ProgressSet(0, "
", "Failed. Connexion Timeout.")
            return SetError(1, 2, false)    ; 2 = timeout
        endif

    until InetGetInfo($DL$INET_DOWNLOADCOMPLETE)

    local $success = InetGetInfo($DL$INET_DOWNLOADSUCCESS)
    InetClose($DL)

    if not $success then
        ; test server..
        if Ping($my_domain, 500) < 10 then
            ProgressSet(0, "
", "Failed. Suspect Firewall.")
            return SetError(1, 3, false) ; 3 = check firewall
        else
            ProgressSet(0, "
", "Failed. Suspect Server Unresponsive.")
            return SetError(1, 1, false) ; 1 = server down
        endif
    endif


    local $fData = FileRead($TmpFile)
    ProgressSet(0, "
", "Success. Got version " & $fData)
    FileDelete($TmpFile)

    return $fData

endfunc




; Feed it a path, if it is a full, valid path it is returned as-is.
; If it's a relative path, it is added to the datadir path and that full path is returned.
func SetRelPathToDataDir($some_path)
    if $some_path and StringLeft($some_path, 2) <> '\\' and StringMid($some_path, 2, 1) <> ':' then
        CLT($some_path)
        $some_path = $data_dir & "
\" & $some_path
        CRT($some_path)
    endif
    return $some_path
endfunc


; Fire up the user's default screensaver..
;
func StartScreenSaver()
    global $user32_dll = DllOpen("
user32.dll")
    local Const $WM_SYSCOMMAND = 0x112
    local Const $SC_SCREENSAVE = 0xF140
    local $gui = GUICreate("
",10,10,-100,-100)
    local $guiHandle = WinGetHandle($gui)
    DllCall($user32_dll, "
long", "SendMessageA", "long", $guiHandle, "long", $WM_SYSCOMMAND, "long", $SC_SCREENSAVE, "long", 0x0)
    DllClose($user32_dll)
    GUIDelete($gui)
endfunc


; Lock the WorkStation.
; Simple, effective..
;
func LockWorkStation()
    ShellExecute("
rundll32.exe", 'user32.dll,LockWorkStation') ; NOTE: CaSe is Crucial, here.
    if @Error then return SetError(@Error, @Extended, false)
    return true
endfunc





; Author:       xrxca (autoit at forums dot xrx dot ca) + wee bits by me
;
func _GetMonitors()
    $monitors_list[0][0] = 0  ;  Added so that the global array is reset if this is called multiple times
    local $handle = DllCallbackRegister("
_MonitorEnumProc", "int", "hwnd;hwnd;ptr;lparam")
    DllCall("
user32.dll", "int", "EnumDisplayMonitors", "hwnd", 0, "ptr", 0, "ptr", DllCallbackGetPtr($handle), "lparam", 0)
    DllCallbackFree($handle)
    local $i = 0
    for $i = 1 To $monitors_list[0][0]
        if $monitors_list[$i][1] < $monitors_list[0][1] then $monitors_list[0][1] = $monitors_list[$i][1]
        if $monitors_list[$i][2] < $monitors_list[0][2] then $monitors_list[0][2] = $monitors_list[$i][2]
        if $monitors_list[$i][3] > $monitors_list[0][3] then $monitors_list[0][3] = $monitors_list[$i][3]
        if $monitors_list[$i][4] > $monitors_list[0][4] then $monitors_list[0][4] = $monitors_list[$i][4]
    next
    return $monitors_list
endfunc   ;==>_GetMonitors
func _MonitorEnumProc($hMonitor$hDC$lRect$lParam)
#forceref $lParam$hDC
    local $Rect = DllStructCreate("
int left;int top;int right;int bottom", $lRect)
    $monitors_list[0][0] += 1
    redim $monitors_list[$monitors_list[0][0] + 1][5]
    $monitors_list[$monitors_list[0][0]][0] = $hMonitor
    $monitors_list[$monitors_list[0][0]][1] = DllStructGetData($Rect, "
left")
    $monitors_list[$monitors_list[0][0]][2] = DllStructGetData($Rect, "
top")
    $monitors_list[$monitors_list[0][0]][3] = DllStructGetData($Rect, "
right")
    $monitors_list[$monitors_list[0][0]][4] = DllStructGetData($Rect, "
bottom")
    return 1 ; Return 1 to continue enumeration
endfunc



; By ascend4nt (encapsulated by cor)
; Returns the handle of the owner window directly under the mouse..
func GetWindowUnderMouse($iX,$iY)
    local $stInt64,$aRet,$stPoint=DllStructCreate("
long;long")
    DllStructSetData($stPoint,1,$iX)
    DllStructSetData($stPoint,2,$iY)
    $stInt64=DllStructCreate("
int64",DllStructGetPtr($stPoint))
    $aRet=DllCall("
user32.dll","hwnd","WindowFromPoint","int64",DllStructGetData($stInt64,1))
    if @Error then return SetError(2,@Error,0)
    if $aRet[0]=0 Then return SetError(3,0,0)
    ; The above can return 'sub' windows, or control handles, so we get the owner window..
    local $aResult = DllCall("
user32.dll", "hwnd", "GetAncestor", "hwnd", $aRet[0], "uint", 2)
    if @Error then return SetError(@Error, @Extended, 0)
    return $aResult[0]
endfunc

; check wether a number is within a set range..
func NumberInRange($number$min$max)
    return ($number >= $min and $number <= $max);
endfunc




; Speak some text out loud..
;
; Rate is -10 to 10 or thereabouts.
; Volume 0 - 100, maybe.
; Voice is a substring of one of your available system voices (e.g. "Hazel").
;
func Speak($text$vol=50, $rate=0, $voice="
", $wait=false)
 debug($LOG_LF, "
", 7);debug
 debug("
Speak(" & $text & "," & $vol  & "," & $rate & "," & $wait & ")", @ScriptLineNumber, 7);debug

    ; Use a global variable so speech call is asynchronous. If you start a
    ; new speech command, it will cut-off the old one and begin immediately.
    global $o_speech = ObjCreate("
SAPI.SpVoice")
    if not IsObj($o_speech) then return false
    local $speak_flag

    if $wait then
        $speak_flag = 10    ; no asynch
    else
        $speak_flag = 11    ;8 = XML     1 = SVSFlagsAsync    2: purge (seems to do this, anyway)
    endif

debug("
Speak() --> DeTokenizeString -->" & $text & "<=", @ScriptLineNumber, 7);debug
    $text = DeTokenizeString($text)
    if FileExists($text) then
        $speak_flag += 4    ; will read file contents when given valid file path
    endif

    ; choose a voice..
    local $choose_voice = $o_speech.GetVoices().Item(0)
    if $voice then
        for $i = 0 to $o_speech.GetVoices().Count() - 1
            local $MS_voice = $o_speech.GetVoices().Item($i)
            if StringInStr($MS_voice.GetAttribute("
Name"), $voice) then
                $choose_voice = $MS_voice
                exitloop
            endif
        next
    endif

    ; speak the text..
    with $o_speech
        .Voice = $choose_voice
        .Rate = $rate
        .Volume = $vol
        .Speak($text$speak_flag); with XML + purge before speak
    endwith

    return true
    
endfunc


; Returns an array of available system voices..
; NOTE: Unless you hack your registry, you will probably only have a small
; selection of voices to play with. See KeyBind docs for how to expand this.
func GetSpeechVoices()
    local $o_speech = ObjCreate("
SAPI.SpVoice")
    if not IsObj($o_speech) then return -1
    local $this_voice
    local $voice_array[1]
    for $i = 0 to $o_speech.GetVoices().Count() - 1
        $this_voice = $o_speech.GetVoices().Item($i)
        ArrayAdd($voice_array$this_voice.GetAttribute("
Name"))
    next
    return $voice_array
endfunc




;
; Get the command-line arguments.
; Returns an array with two values..
;
;    $ret_array[0]    =>    switches
;    $ret_array[1]    =>    filename (if supplied)
;
;    Do:
;
;     local $cmd_array = $CmdLine
;     local $cmdline = GetCommandLine($CmdLineRaw$cmd_array)
;     global $switches = $cmdline[0]
;     global $inputfile = $cmdline[1]
;
;    Note, when running inside your IDE (not @Compiled) you may not get the same
;    results as you will when @Compiled, depending on your IDE. Test the compiled
;    version!
;
func GetCommandLine($command_line$cmd_array)
 debug($LOG_LF, "
", 7);debug
 debug("
GetCommandLine(" & $command_line & ")", @ScriptLineNumber, 7);debug
 debug_PrintArray($cmd_array$LOG_LF & "
(" & @ScriptLineNumber & "" & " $cmd_array");debug

    $command_line = StringStripWS($command_line, 3)

    local $ret_array[2]

    if $cmd_array[0] then

        for $i = 1 to $cmd_array[0]
            $cmd_array[$i] = StringStripWS($cmd_array[$i], 3)
        next

        ; file path supplied..
        if StringRight($command_line, 1) <> "
)" then
            $ret_array[1] = StringStripWS($cmd_array[$cmd_array[0]], 3)
            for $i = 1 to $cmd_array[0]
                if StringStripWS($cmd_array[$i], 3) <> $ret_array[1] then _
                    $ret_array[0] &= StringStripWS($cmd_array[$i], 3) & "
 "
            next
        else
            $ret_array[1] = "
"
            for $i = 1 to $cmd_array[0]
                $ret_array[0] &= StringStripWS($cmd_array[$i], 3) & "
 "
            next
        endif
    endif
    CRT($ret_array[0], "
 ")
    return $ret_array
endfunc



; UrlToText()
;
; basic HTML > text translation, for spoken web pages
; slow, and not terribly clever..
;
; feed it a URL or path to a local html file, returns the plain text, for speaking
; all formatting is removed, and "." dots are also added to break up big sections.
;
; you can send start and end points for the text stream; simply tag the words/phrases
; onto the end of the URL (or local file location) using an asterisk, e.g..
;
$myText = UrlToText('http://news.bbc.co.uk/**/st Updated*CONTACT US')
;
; which would get you the latest headlines from the BBC, ready for feeding to Sam, or Mike
; or Mary or whoever. If you omit the end point, e.g..
;
$myText = UrlToText('http://www.answers.com/topic/gal-pagos-tortoise-2*Synonyms')
;
; all the text from the start point to the end of the page will be returned. If you provide
; no start and end points, the text of the whole page is returned. Something to play with.
;
func UrlToText($url)
 debug($LOG_LF, "
", 7);debug
 debug("
UrlToText(" & $url & ")", @ScriptLineNumber, 7);debug

    local $start = "
"
    local $end = "
"
    local $handle = "
"

    if StringInStr($url, "
*", 0, -1) then
        local $url_parts = StringSplit($url, "
*")
        $url = $url_parts[1]
        if uBound($url_parts) > 2 then $start = $url_parts[2]
        if uBound($url_parts) > 3 then $end = $url_parts[3]
    endif

    if StringLeft($url, 4) = "
http" then
        InetGet($url, @TempDir & "
\UrlToText.tmp", 1)
        ; if no file or emp
        $handle = FileOpen(@TempDir & "
\UrlToText.tmp", 0)
    else
        $handle = FileOpen($url, 0)
    endif
    local $web_text = FileRead($handle)
    if StringInStr($web_text, "
<") then
        ; we'll break shit up for speaking..
        $web_text = StringRegExpReplace($web_text, '</(div|p|tr|table)>', '. ')
        ; strip out the HTML..
        $web_text = StringRegExpReplace($web_text, '\s', ' ')
        $web_text = StringRegExpReplace($web_text, '<(?i)head(.*?)</(?i)head>', '') ; best done individually..
        $web_text = StringRegExpReplace($web_text, '<(?i)script(.*?)</(?i)script>', '')
        $web_text = StringRegExpReplace($web_text, '<(?i)style(.*?)</(?i)style>', '')
        $web_text = StringRegExpReplace($web_text, '</?[^>]*?>', '')
        $web_text = ReplaceHTMLEntities($web_text)
    endif

    ; speak from..
    if $start then
        $web_text = StringTrimLeft($web_text, StringInStr($web_text$start)-1)
    endif

    ; speak to..
    local $strlen = StringLen($web_text)
    local $end_pos = StringInStr($web_text$end)
    if $end_pos = 0 then $end_pos = $strlen
    if $end then
        $web_text = StringTrimRight($web_text$strlen-$end_pos+1)
    endif
    FileClose($handle)
    FileDelete(@TempDir & "
\UrlToText.tmp")
    return StringStripWS($web_text, 4)
endfunc



; DoLog()

; Logs stuff..
;
; The optional second parameter tells DoLog to append, or not.
$dl_append will most likely receive the output from a checkbox..
; 1 (append) or 4 [or anything else] (create new log)
;
; To stop and close the log, send "out" as your log string
; the optional third parameter '$log_extra' goes at top of log,
; and, if required, you must send it along with your "out" command, eg..
;
; DoLog("out", 0, "command-line: " & $my_arguments & $LOG_LF & $LOG_LF)
;
;
; and now the function itself..

func DoLog($dl_string$dl_append=4, $log_extra="
")
    if $dl_string = "
out" then
        if $log_string then
            if $dl_append = 1 then
                $dl_append = 9
            else
                $dl_append = 10
            endif

            local $my_log_file = FileOpen($log_location$dl_append)
            FileWriteLine($my_log_file$my_name & "
 log @ "&@Year&"-"&@Mon&"-"&@Mday&@Hour&"."&@Min&"."&@Sec&".. " & $LOG_LF & _
                "
--------------------------------------------------------------------------------" & $LOG_LF)
            if not $log_extra then FileWriteLine($my_log_file, "
command-line: " & $CmdLineRaw & $LOG_LF & $LOG_LF)
            FileWriteLine($my_log_file$log_extra)
            FileWriteLine($my_log_file$log_string & $LOG_LF)
            FileClose($my_log_file)
            $log_string = "
"
            ;2do - put per-job logging here..
        endif
    else
        $log_string &= UnifyCRLF($dl_string) & $LOG_LF
    endif
    return $dl_string
endfunc




;;    Debugging Functions..



; debug()
;
; Provides quick debug report in your console..
;
; If your text editor can do it (probably), this is a great
; way to get debug output without halting the script execution..
;
; NOTE: if you call debug() in a compiled script you will get 
; the debug() output debug_dump()ed, instead.

func debug($d_string$ln=false, $level=1, $max_size=$max_log_size)

    if $level > $debug_level then return false
    if @Compiled then return debug_dump($d_string$ln$level, true, $max_size)

    ; This is unlikely to catch anything under normal circumstances (string prepended to input)..
    if IsArray($d_string) then return debug_PrintArray($d_string, "
NOT A STRING!", $ln$level, 0, $max_size);debug

    local $pre = "
"
    ; ; Editplus..
    ; if $ln then $pre = ".\" & @ScriptName & "(" & $ln & "): ==> "
    ; Notepad++
    if $ln <> -1 then $pre = "
(" & $ln & "" & @Tab
    ; if $ln then $pre = """" & @ScriptFullPath & """(" & $ln & "): ==> "

    if $pre then $pre &= "
" & _NowCalcDate() & " @ " & _NowTime() & "::" & @MSec & " ] "

    ConsoleWrite($pre & $d_string & $LOG_LF)
endfunc



; debug_dump()
;
; Like debug (), but to a file..

func debug_dump($d_string$ln=false, $level=1, $append=true, $max_size=$max_log_size)

    if $level > $debug_level then return false
    if IsArray($d_string) then return debug_dumpArray($d_string, "
$ARRAY:", $append$level$max_size)

    local $pre$code = 129 ; + UTF-8
    if $append = false then $code = 130
    ; if $ln and not @Compiled then $pre = "(" & $ln & ") : ==> "
    if $ln <> -1 then $pre = "
(" & $ln & ") : ==> "
    CheckLogSize($max_size)
    local $fileh = FileOpen($dump_file$code)
    FileWriteLine($fileh$pre & $d_string & $LOG_LF)
    FileClose ($fileh)
endfunc


; LogError()
;
; It should be "errer", really.
; Log an (usually, exit) error to a file
;
func debug_LogError($error_string)
    FileWriteLine(@ScriptDir & "
\error.log", "")
    FileWriteLine(@ScriptDir & "
\error.log", _
    "
--------------------------------------------------------------------------------")
    FileWriteLine(@ScriptDir & "
\error.log",  @Year & "/" & @Mon & "/" & @MDay _
                                            & "
  " & @Hour & ":" & @Min & ":" & @Sec )
    FileWriteLine(@ScriptDir & "
\error.log", "")
    FileWriteLine(@ScriptDir & "
\error.log", "command-line: " & $CmdLineRaw)
    FileWriteLine(@ScriptDir & "
\error.log", "")
    FileWriteLine(@ScriptDir & "
\error.log", $error_string)
endfunc



; debug_PrintArray()
;
; Debug output of an array, in the console
; (your text editor/IDE can probably display this for you, if you so desire)..

func debug_PrintArray(byref $array$tring="
array", $ln="", $level=1, $limit=0, $max_size=$max_log_size$forcedump=false, $string=false)

    if $level > $debug_level then return false

    local $pre
    ; if $ln and not @Compiled then $pre = "(" & $ln & ") : ==> "
    if $ln then $pre = "
(" & $ln & ") : ==> "
    if not IsArray($array) then return ConsoleWrite($LOG_LF & $pre & $tring & "
: NOT an array!" & _
                        $LOG_LF & $pre & "
it's a string: " & "->" & $array & "<-" & $LOG_LF & $LOG_LF)

    ; 2+dimensional array sent..
    if UBound($array, 0) > 1 then return debug_Print2DArray($array$tring$ln$level$limit$max_size$forcedump$string)

    local $pa_string = "
"
    local $count = 0
    for $element in $array
        $pa_string &= '[' & $count & '] : ' & $element & $LOG_LF
        $count += 1
        if $count = $limit then exitloop
    next

    if $string then return $LOG_LF & $pre & $tring & "
" & $LOG_LF & $pa_string & $LOG_LF
    if @Compiled or $forcedump then
        debug_dump($LOG_LF & $pre & $tring & "
" & $LOG_LF & $pa_string & $LOG_LF$ln$level, true, $max_size)
    else
        ConsoleWrite($LOG_LF & $pre & $tring & "
" & $LOG_LF & $pa_string & $LOG_LF)
    endif

endfunc


; Like debug_PrintArray, but will handle arrays with more than two columns..
;
; Like Debug (), the output will enable you to double-click to go straight to
; the line of code at least, in a decent text editor it will.
;
;
; For if you know what you should have, and want to see it.
; Only works for the kind of arrays returned by IniReadSection() and similar
; functions, or ones you created yourself, needs $array[0][0] to be meaningful..

func debug_Print2DArray(byref $array$tring="
array", $ln="", $level=1, $limit=0, $max_size=$max_log_size$forcedump=false, $string=false) $limit for compatability only

    if @Compiled and ($level > $debug_level) then return 0

    local $pre
    ; if $ln and not @Compiled then $pre = "(" & $ln & ") : ==> "
    if $ln then $pre = "
(" & $ln & ") : ==> "

    if not IsArray($array) then return ConsoleWrite($LOG_LF & $pre & $tring & "
: NOT an array!" & $LOG_LF & _
                                        $pre & "
it's a string: " & "->" & $array & "<-" & $LOG_LF; & $LOG_LF
    ; 1-dimensional array sent..
    if UBound($array, 0) < 2 then return debug_PrintArray($array$tring$ln$level$limit$ln)

    $limit += 1
    local $cols = UBound($array, 2)
    local $pa_string = "
"
    for $i = 0 to UBound($array, 1)-1
            $pa_string &= '[' & $i & ']    [0] = ' & $array[$i][0] & '    '
        for $j = 1 to $cols-1
            $pa_string &= '    [' & $j & '] = ' & $array[$i][$j] & '    '
        next
        $pa_string &= $LOG_LF
    next
    if $string then return $LOG_LF & $pre & $tring & "
" & $LOG_LF & $pa_string & $LOG_LF
    if @Compiled or $forcedump then 
        debug_dump($LOG_LF & $pre & $tring & "
" & $LOG_LF & $pa_string & $LOG_LF$ln$level, true, $max_size)
    else
        ConsoleWrite($LOG_LF & $pre & $tring & "
" & $LOG_LF & $pa_string & $LOG_LF)
    endif
endfunc




; debug_dumpArray()
; Debug output of an array, to a file..
;
func debug_dumpArray(byref $array$title="
array", $append=true, $level=1, $max_size=$max_log_size)

    if not IsArray($array) then return 0
    if $level > $debug_level then return false
    if UBound($array, 0) > 1 then return debug_Print2DArray($array$title, "
", $level, 0, $max_size)

    local $code = 129 ; + UTF-8
    if $append = false then $code = 130
    local $fileh = FileOpen($dump_file$code)
    local $da_string = $title & $LOG_LF
    local $count = 0
    for $element in $array
        $da_string &= '[' & $count & '] : ' & $element & $LOG_LF
        $count += 1
    next
    FileWrite($fileh$da_string)
    FileClose($fileh)
endfunc


; if the log reaches a preset size, backup and start again..
func CheckLogSize($max_size=$max_log_size)
    if $max_size <> 0 then
        if FileGetSize($dump_file) > ($max_size * 1048576) then    ; Bytes > MB = * 1048576 (1024*1024)
            BackupFile($dump_file)
        endif
    endif
endfunc


func BackupFile($file)
    local $backup = GetParent($file) & "
\[" & @Year & "-" & @Mon & "-" & @Mday & "@" & _
                    @Hour & "
." & @Min & "]_" & RemoveExtension(Basename($file)) & "." & GetExtension($file)
    local $ret = FileMove($file$backup)
    Run(@ComSpec & "
 /c " & 'compact.exe /C "' & $dump_file & '"', "", @SW_HIDE)
    return $ret
endfunc



; Experimantal..


; Is some window visible?
func WinVisible($GUI)
    return BitAnd(WinGetState($GUI), 2)
endfunc


; Just for fun, reverses a StringSplit - handy if you have split a string to
; manipulate and now want it back as a string..
; Pass it an AutioIt array, as returned by StringSplit
;
func StringJoin(byref $StringArray$join_str=$LOG_LF)
    local $ret_str
    for $i = 1 to $StringArray[0]
        $ret_str &= $StringArray[$i] & $join_str
    next
    return $ret_str
endfunc


; Return total available free memory..
func GetFreeMem()
    local $mem_stats = MemGetStats()
    local $free = $mem_stats[2] * 1024    ; convert to bytes
    return $free
endfunc


func GetFreeMemHuman()
    local $mem_stats = MemGetStats()
    local $free = $mem_stats[2] / 1024    ; convert to MB
    return FormatMB($free, 2)
endfunc


; close some running process..
func CloseProcess($process_name)
    SetError(0)
    local $closed = ProcessClose($process_name)
    if @Error then $closed = ProcessWaitClose($process_name)
    if @Error then $closed = Run(@ComSpec & "
 /c " & 'taskkill /F /T /IM ' & $process_name, "", @SW_HIDE)
    debug("
Closed " & $process_name & "" & $closed, @ScriptLineNumber, 7);debug
    return $closed
endfunc



; Cute little System Info Box..
; Designed to be included as an About Box Easter Egg.
;
func SysInfoBox()

    local $last_event_mode = AutoItSetOption("
GUIOnEventMode", 0)
    local $last_coord_mode = AutoItSetOption("
GUICoordMode", 0)

    local $si_x = IniRead($ini_path$my_name, "
si_x", -1)
    local $si_y = IniRead($ini_path$my_name, "
si_y", -1)
    local $si_w = IniRead($ini_path$my_name, "
si_w", 800)
    local $si_h = IniRead($ini_path$my_name, "
si_h", 388)
    if $si_w < 400 Then $si_w = 400
    local $label_width = 140
    local $info_width = $si_w - $label_width - 20
    ; local $path_adds = ""

    local $gui_sysinfo = GUICreate("
System Information", $si_w$si_h$si_x$si_y, _
            BitOR($WS_CLIPSIBLINGS$WS_CAPTION$WS_SYSMENU$WS_SIZEBOX), $WS_EX_TOPMOST)
    GUISetIcon(@AutoItExe, 0, $gui_sysinfo)

    GUISetCoord($si_w-33, 10)
    local $label_env = GUICtrlCreateLabel("
ENV", 0, 0, 30, 20)
    GUICtrlSetResizing(-1, $GUI_DOCKMENUBAR+$GUI_DOCKWIDTH+$GUI_DOCKRIGHT)
    GUICtrlSetTip(-1, "
 Click here to see this system's Environment Variables (aka. 'ENV VAR'). ")

    GUISetCoord(10, -10)
    local $computername = GUICtrlCreateLabel("
Computer Name:", 0, 20, $label_width)
    GUICtrlCreateLabel("
Memory:", $label_width + 220, 0, 60)

    GUISetCoord(10, 10)
    GUICtrlCreateLabel("
Current UserName:", 0, 20, $label_width)
    GUICtrlCreateLabel("
Operating System:", 0, 20, $label_width)
    GUICtrlCreateLabel("
System %" & "PATH%:", 0, 20, $label_width; In case AutoItSetOption("ExpandEnvStrings", 1)
    local $ip = GUICtrlCreateLabel("
Local IP Address:", 0, 20, $label_width)
    local $combo_info_drives = GUICtrlCreateCombo("
", 0, 22, 100, 200)
    local $my_drives = DriveGetDrive("
FIXED")
    for $i = 1 to $my_drives[0]
        GUICtrlSetData($combo_info_drives$my_drives[$i])
    next
    ControlCommand($gui_sysinfo, "
", $combo_info_drives, "SelectString", @HomeDrive)
    local $startupdir = GUICtrlCreateLabel("
Startup Directory:", 0, 28, $label_width)
    GUICtrlCreateLabel("
Windows Directory:", 0, 20, $label_width)
    GUICtrlCreateLabel("
System Folder:", 0, 20, $label_width)
    GUICtrlCreateLabel("
Desktop Directory:", 0, 20, $label_width)
    GUICtrlCreateLabel("
'My Documents' Directory:", 0, 20, $label_width)
    GUICtrlCreateLabel("
Program Files Directory:", 0, 20, $label_width)
    GUICtrlCreateLabel("
Start Menu Directory:", 0, 20, $label_width)
    GUICtrlCreateLabel("
Temporary File Directory:", 0, 20, $label_width)
    GUICtrlCreateLabel("
Desktop:", 0, 20, $label_width)
    GUICtrlCreateLabel("
Date [Y-M-D]:", 0, 20, $label_width)
    GUICtrlCreateLabel("
Time [H:M:S]:", 0, 20, $label_width)
    local $myini = GUICtrlCreateLabel("
My ini:", 0, 20, $label_width)

    GUISetCoord($label_width + 10, -12)
    local $current_drive = GUICtrlRead($combo_info_drives)
    local $inp_computername = GUICtrlCreateInput(@ComputerName, 0, 20, 200, 20)
    local $input_freemem = GUICtrlCreateInput(GetFreeMemHuman(), $label_width * 2, 0, 120, 20)

    for $i = $computername to $input_freemem
        GUICtrlSetResizing($i$GUI_DOCKALL)
        GUICtrlSetTip($i, "
 Click the labels to select all the input text. " & $msg_lf & " NOTE: Some update dynamically and will quickly deselect themselves! ")
    next

    GUISetCoord($label_width + 10, 10)
    local $input_username = GUICtrlCreateInput(@UserName, 0, 20, $info_width, 20)
    local $vbit = "
x86"
    if @AutoItX64 = 1 then $vbit = "
x64"
    local $os_string = StringReplace(@OSVersion, "
_", " ") & " (" & $vbit & "" & @OSServicePack & " [" & @OSType & "][cpu:" & StringLower(@CPUArch) & "]"
    GUICtrlCreateInput($os_string, 0, 20, $info_width, 20)
    GUICtrlCreateInput(EnvGet("
PATH"), 0, 20, $info_width, 20)
    GUICtrlCreateInput(@IPAddress1, 0, 20, $info_width, 20)
    local $input_drive_label = GUICtrlCreateInput(DriveGetLabel($current_drive & "
\"), 0, 23, 150, 20)
    local $input_totalspace = GUICtrlCreateInput(FormatMB(DriveSpaceTotal($current_drive & "
\"), 2), 155, 0, 100, 20)
    local $input_freespace = GUICtrlCreateInput(FormatMB(DriveSpaceFree($current_drive & "
\"), 2) & " Free", 105, 0, 150, 20)
    GUISetCoord($label_width + 10, 138)
    GUICtrlCreateInput(@StartupDir, 0, 0, $info_width, 20)
    GUICtrlCreateInput(@WindowsDir, 0, 20, $info_width, 20)
    GUICtrlCreateInput(@SystemDir, 0, 20, $info_width, 20)
    GUICtrlCreateInput(@DesktopDir, 0, 20, $info_width, 20)
    GUICtrlCreateInput(@MyDocumentsDir, 0, 20, $info_width, 20)
    GUICtrlCreateInput(@ProgramFilesDir, 0, 20, $info_width, 20)
    GUICtrlCreateInput(@StartMenuDir, 0, 20, $info_width, 20)
    GUICtrlCreateInput(@TempDir, 0, 20, $info_width, 20)
    GUICtrlCreateInput(@DesktopWidth & "
px" & " x " & @DesktopHeight & "px " & _
                        @DesktopDepth & "
bit [" & @DesktopRefresh & "Hz]", 0, 20, $info_width, 20)
    local $input_date = GUICtrlCreateInput(@YEAR & "
-" & @MON & "-" & @MDAY, 0, 20, $info_width, 20)
    local $input_time = GUICtrlCreateInput(@HOUR & "
:" & @MIN & ":" & @SEC, 0, 20, $info_width, 20)
    local $input_ini = GUICtrlCreateInput($ini_path, 0, 20, $info_width, 20)

    for $i = $input_username To $input_ini
        GUICtrlSetResizing($i$GUI_DOCKALL+$GUI_DOCKRIGHT)
    next

    for $i = $inp_computername To $input_ini
        GUICtrlSetFont($i, 8.5, 400, 0, "
Consolas", 5)
    next

    for $i = $input_drive_label To $input_freespace
        GUICtrlSetResizing($i$GUI_DOCKALL)
    next


    GUISetState()
    local $last_sec = @Sec
    local $link = false

    while true

        if @Sec<> $last_sec then
        
            GUICtrlSetData($input_time, @HOUR & "
:" & @MIN & ":" & @SEC)
            if @SEC < 2 and @HOUR = 0 and @MIN = 0 then
                GUICtrlSetData($input_date, @YEAR & "
-" & @MON & "-" & @MDAY)
            endif

            $current_drive = GUICtrlRead($combo_info_drives)
            GUICtrlSetData($input_drive_label, DriveGetLabel($current_drive & "
\"))
            
            local $capacity = DriveSpaceTotal($current_drive & "
\")
            local $free_space = DriveSpaceFree($current_drive & "
\")
            GUICtrlSetData($input_totalspace, FormatMB($capacity, 2))
            GUICtrlSetData($input_freespace, FormatMB($free_space, 2) & "
 Free")
            if $free_space < ($capacity / 10) then
                GUICtrlSetBkColor($input_freespace, 0xff0000)
            else
                GUICtrlSetBkColor($input_freespace, default)
            endif
            
            GUICtrlSetData($input_freemem, GetFreeMemHuman() & "
 Free")
            
            $last_sec = @Sec
        endif

        
        local $msg = GUIGetMsg()

        switch $msg

            case $label_env
                GUISetState(@SW_DISABLE, $gui_sysinfo)
                DisplayEnvVars()
                GUISetState(@SW_ENABLE, $gui_sysinfo)

            case $gui_event_close
                exitloop

            case $combo_info_drives
                $current_drive = GUICtrlRead($combo_info_drives)
                GUICtrlSetData($input_drive_label, DriveGetLabel($current_drive & "
\"))
                GUICtrlSetData($input_totalspace, FormatMB(DriveSpaceTotal($current_drive & "
\"), 2))
                GUICtrlSetData($input_freespace, FormatMB(DriveSpaceFree($current_drive & "
\"), 2) & " Free")

            case $computername to $ip
                GUICtrlSetState($msg + 19, $gui_focus)

            case $startupdir to $myini
                GUICtrlSetState($msg + 21, $gui_focus)

        endswitch

        ; Visual indication of clickable link..
        local $mouse = GUIGetCursorInfo($gui_sysinfo)
        if $mouse[4] <> $link then
            if $mouse[4] = $label_env and $link = false then
                $link = $mouse[4]
                GUICtrlSetColor($label_env, 0x008DFF)
                GUISetCursor(0, 1, $gui_sysinfo)
            else
                GUICtrlSetColor($label_env, default)
                GUISetCursor()
                $link = false
            endif
        endif

        Sleep(100)
    wend


    local $size_array = WinGetPos($gui_sysinfo)
    if IsArray($size_array) then
        if $si_x <> $size_array[0] and $size_array[0] >= 0 and $size_array[0] < (@DesktopWidth - 25) then IniWrite($ini_path$my_name, "
si_x", $size_array[0])
        if $si_y <> $size_array[1] and $size_array[1] >= 0 and $size_array[1] < (@DesktopHeight - 25) then IniWrite($ini_path$my_name, "
si_y", $size_array[1])
        $size_array = WinGetClientSize($gui_sysinfo)
        if IsArray($size_array) then
            if $size_array[0] <> $si_w and $size_array[0] >= 100 then IniWrite($ini_path$my_name, "
si_w", $size_array[0])
            if $size_array[1] <> $si_h and $size_array[1] >= 100 then IniWrite($ini_path$my_name, "
si_h", $size_array[1])
        endif
    endif

    GUIDelete($gui_sysinfo)
    AutoItSetOption("
GUIOnEventMode", $last_event_mode)
    AutoItSetOption("
GUICoordMode", $last_coord_mode)

endfunc


; GUI readout of the System Environment Variables..
;
func DisplayEnvVars()

    local $PID = Run(@ComSpec & "
 /c set", @SystemDir, @SW_HIDE, $STDOUT_CHILD)
    local $env_vars
    while true
        $env_vars &= StdoutRead($PID)
        if @Error then exitloop
    wend
    $env_vars = StringStripWS($env_vars, 3)

    DisplayText($env_vars, "
Environment Variables", 700, 600)

endfunc


; Simple GUI to display some text for the user.
;
; Perhaps a readme, or license agreement.
; With an option to copy the text to the ClipBoard.
;
func DisplayText($text$title="
For Reading..", $width=650, $height=$width$inipath=$ini_path$sectionname=$my_name)

    local $last_event_mode = AutoItSetOption("
GUIOnEventMode", 0)
    local $last_coord_mode = AutoItSetOption("
GUICoordMode", 0)

    ; local $height = $width
    local $GUI_styles = BitOR($WS_CLIPSIBLINGS$WS_CAPTION$WS_SYSMENU$WS_SIZEBOX)
    local $edit_styles = BitOr($ES_WANTRETURN$WS_VSCROLL$WS_HSCROLL$ES_AUTOHSCROLL$ES_MULTILINE$WS_TABSTOP)

    local $prefname = "
display_txt_" & CleanPrefName($title)
    local $dt_x = IniRead($inipath$sectionname$prefname & "
_x", -1)
    local $dt_y = IniRead($inipath$sectionname$prefname & "
_y", -1)
    
    local $GUI_txt_display = GUICreate($title$width$height$dt_x$dt_y$GUI_styles$WS_EX_TOPMOST)
    local $edit_env = GUICtrlCreateEdit($text, 5, 5, $width-10, $height-40, $edit_styles)
    
    
    local $butt_copy = GUICtrlCreateButton("
Copy To ClipBoard", $width-155, $height-35, 105, 25)
    local $butt_done = GUICtrlCreateButton("
OK", 110, 0, 35, 25)

    GUICtrlSetResizing($edit_env$GUI_DOCKBORDERS)
    GUICtrlSetResizing($butt_copy$GUI_DOCKSTATEBAR+$GUI_DOCKWIDTH+$GUI_DOCKRIGHT)
    GUICtrlSetResizing($butt_done$GUI_DOCKSTATEBAR+$GUI_DOCKWIDTH+$GUI_DOCKRIGHT)

    ControlFocus($GUI_txt_display, "
", $edit_env)
    Send("
{Right}")

    GUISetState(@SW_SHOW, $GUI_txt_display)

    while true
        switch GUIGetMsg()
            case $butt_done$gui_event_close
                exitloop
            case $butt_copy
                ClipPut($text)
                DisplayTooltipMessage("
Data Successfully Copied to the ClipBoard", 3000, "Success!", true)
        endswitch
        Sleep(10)
    wend
    
    local $size_array = WinGetPos($GUI_txt_display)
    if IsArray($size_array) then
        IniWrite($inipath$sectionname$prefname & "
_x", $size_array[0])
        IniWrite($inipath$sectionname$prefname & "
_y", $size_array[1])
    endif
    
    GUIDelete($GUI_txt_display)
    AutoItSetOption("
GUIOnEventMode", $last_event_mode)
    AutoItSetOption("
GUICoordMode", $last_coord_mode)

    return true
    
endfunc"
back to the source menu
test

Welcome to corz.org!

I'm always messing around with the back-end.. See a bug? Wait a minute and try again. Still see a bug? Mail Me!