click here for a plain text version
#include-once

; corz.org essential (Lite Version) v1.2.1.2
;
; 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>
#include <String.au3>
#include <GuiEdit.au3>

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

; 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

; For "humanizing" AutoIt booleans (and for reporting/display purposes)..
global const $STR_ENABLED        = "enabled"
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
global $debug_level = 10
global $max_debug_log_size = 0 ; 0 = no limit, or a number (in MB). Set in ini prefs.

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
global $tip_icon = @ScriptFullPath & ",0"
global $tip_style = 1
global $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$use_log_viewer=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 $use_log_viewer 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..

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

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

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

; Open log file..
func OpenLogFile()
    ShellExecute($log_location)
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("
", @ScriptLineNumber, 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("
", @ScriptLineNumber, 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 = Centre the tip horizontally under the control
        And so on. See below this function for more details.

#ce
func GUICtrlSetTipOptional($control_ID$tip_text=default, $title=default, $icon=default, $options=default)

    if $do_tooltips = $OFF then return

    if $tip_text = default then $tip_text = "
"
    if $title = default then $title = "
Information:"
    if $icon = default then $icon = $tip_icon
    if $options = default then $options = $tip_style

    $options = BitOr($tip_style$TTF_SUBCLASS$TTF_IDISHWND$TTF_PARSELINKS)
    $options = $tip_style

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

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


    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)

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

    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 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("
", @ScriptLineNumber, 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("
", @ScriptLineNumber, 7);debug
 debug("
DeUserTokenizeString(" & $string & ")", @ScriptLineNumber, 7);debug
 debug_PrintArray($known_user_tokens, "
cel.au3 :: $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
    
; 2do : check changes to CRT() have not impacted ArrayJoin() with empty delimiter. TEST!
;

    ; Pop all the pieces back together again..
    debug_PrintArray($split_string, "
cel.au3 :: $split_string:", @ScriptLineNumber, 7);debug
    $string = ArrayJoin($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





; Fix potentially messed-up slashes..
func FixPathSlashes($string)
    if StringInStr($string, "
://") then return $string ; but not for URLs
    ; Watch out! We don't want to "fix" flags, like: del /f /q "C:\some/path\*.*"
    $string = StringRegExpReplace($string, "
(.+)([^ ])/", "\1\2\\")
    $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 a positive integer if any changes were made, the
; integer being the number of changes performed. Otherwise 0, 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 = 0, $char_num = StringLen($character)
    if StringRight($string$char_num) == $character then 
        while StringRight($string$char_num) == $character
            $ret += 1
            $string = StringTrimRight($string$char_num)
        wend
    endif
    return $ret
endfunc


; And for the left-hand side..
; The default character here is a space..
func CLT(byref $string$character="
 ")
    local $ret = 0, $char_num = StringLen($character)
    while StringLeft($string$char_num) == $character
        $ret += 1
        $string = StringTrimLeft($string$char_num)
    wend
    return $ret
endfunc


; And now both together..
; Note different default character.
; This double-function wrapper is mostly used for prefs lists.
;
; You can, however, send your own replacement character(s)..
;
; Either specify one character to use it for both left and right,
; or specify both left and right characters.
;
func CBT(byref $string$character_left=default, $character_right=default)
    if $character_left = default then $character_left = "
,"
    if $character_right = default then $character_right = $character_left
    local $ret = 0
    $ret += CLT($string$character_left)
    $ret += CRT($string$character_right)
    return $ret
endfunc






; Strip off any leading white space and pad with exact number of <$pad_string>.
; Used to separate parameters on the command-line (we add a space " ").
;
func PadLeft($string$pad_string="
 ", $number=1)
    $string = StringStripWS($string, 1)
    $string = _StringRepeat($pad_string$number) & $string
    return $string
endfunc




; Test if a file can be written to a location..
;
func TestFileWrite($tmpfile)
    local $t = FileWrite($tmpfile, "
")
    if $t <> 1 then return false
    FileDelete($tmpfile)
    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$chr="
~")
    $string = StringReplace($string, " ", "_")
    $string = StringReplace($string, "
|", $chr)
    $string = StringReplace($string, '"
', "'")
    $string = StringReplace($string":"$chr)
    $string = StringReplace($string"*"$chr)
    $string = StringReplace($string"/"$chr)
    $string = StringReplace($string"\"$chr)
    $string = StringReplace($string">"$chr)
    $string = StringReplace($string"<"$chr)
    $string = StringReplace($string"?"$chr)
    return $string
endfunc


; CleanPrefName()
;
; Make a string safe to use in an ini [section pref name]..
;
func CleanPrefName($string$translate_spaces=true)
    $string = CleanPath($string)
    if $translate_spaces then $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, "
\")
    local $bn_tmp = $parts[$parts[0]]
    CRT($bn_tmp, "
:")
    return $bn_tmp
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]]
    ; "." was not found - extensionless file, possibly in a path with a dot..
    if $e <> $some_name and not StringInStr($e, "
\") then
        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



; For use in logs, etc..
;
func DateTimeString($msec=false, $sec=true, $time=true)
    local $addm$adds
    if $time then $time = "
 @ " & @Hour & "." & @Min
    if $sec then $adds =  "
:" & @Sec
    if $msec then $addm = "
." & @Msec
    return @Year & "
-" & @Mon & "-" & @Mday & $time & $adds & $addm
endfunc


; For backup files, etc..
;
func FileDateStamp()
    return @Year & "
-" & @Mon & "-" & @Mday & "@" & @Hour & "." & @Min
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



func IsWild($string)
    if StringInStr($string, "
*") or StringInStr($string, "?") then return true
    return false
endfunc


; Check if a string is a directory path string and if so, add standard wildcards..
; If the supplied path is valid, return true, otherwise false.
;
func CheckDirWild(byref $pathstring)
    if IsDir($pathstring) and not IsWild($pathstring) then 
        $pathstring &= "
\*"
    endif
    local $testfiles = FileFindFirstFile($pathstring)
    if $testfiles = -1 then return false
    return true
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

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

;        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

        ; Find all subdirs in this directory and store them in a array..
        while true
            $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 much 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", "-", "maybe"
            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




; 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")
    debug("
IniWriteCheckBoxValue() : "&$wcbv_inifile&" : "&$wcbv_section&" : "&$wcbv_key&" : "&$wcbv_val, @ScriptLineNumber, 8);debug
    switch $wcbv_val
        case $GUI_CHECKED ; 1
            $wcbv_val = $tru_val
        case $GUI_UNCHECKED    ; 4
            $wcbv_val = $fal_val
        case $GUI_INDETERMINATE    ; 2
            $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 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


; 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("
", @ScriptLineNumber, 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($my_name & "
.ini", $ini_path)
    else
        ; Create a temporary default strings.ini..
        local $tmp_ini_path = @TempDir & "
\TEMP_.ini"
        FileInstall($my_name & "
.ini", $tmp_ini_path)
        local $existing_sections = IniReadSectionNames($tmp_ini_path)


        ; 2do.. for sections, e.g. 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, "
cel.au3 :: $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




; The AutoIt built-in function moves the "new" section to the bottom of the ini
; file, which is annoying. This Blunderbuss approach works well, and leaves
; the section order as-is, meaning menus and such that rely on section order (or
; at least use it) won't find themselves reshuffled..
;
; Usage: BetterIniRenameSection(<path of ini file>, <Old Name>, <New Name>)
;
func BetterIniRenameSection($inifile$old_name$new_name)

    debug("
", @ScriptLineNumber, 8);debug
    debug("
cel.au3 :: BetterIniRenameSection($inifile=" & $inifile & "$old_name=" & $old_name & "$new_name=" & $new_name & "):", @ScriptLineNumber, 8);debug

    local $existing_sections = IniReadSectionNames($inifile)
    if not InArray($existing_sections$old_name) then return SetError(1, 0, false)

    local $inifile_contents = FileRead($inifile)
    local $new_ini_string = StringReplace($inifile_contents, "
[" & $old_name & "]", "[" & $new_name & "]")

    local $temp_inifile = _WinAPI_GetTempFileName(@TempDir, "
cel")
    FileDelete($temp_inifile)
    FileWrite($temp_inifile$new_ini_string)

    ; Compare ini files for matching (other) section names..
    local $compare1 = ArrayJoin($existing_sections, "
|")
    $compare1 = StringReplace($compare1$old_name, "
|")

    local $new_sections = IniReadSectionNames($temp_inifile)
    local $compare2 = ArrayJoin($new_sections, "
|")
    $compare2 = StringReplace($compare2$new_name, "
|")

    if StringCompare($compare1$compare2) == 0 then
        BackupFile($inifile)
        FileMove($temp_inifile$inifile, 1)
        return true
    endif

    return SetError(2, 0, false)

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*. 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$match_case=false)
    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"
        switch $match_case
            case true
                if $ia_string == $ia_array[$i] then return $i
            case false
                if $ia_string = $ia_array[$i] then return $i
        endswitch
    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




; Send it a regular 1-D AutoIt array and get back a 2-D array, with
; the original array filling the first column, and a second, blank column
; for you to fill with "associated" data. See also Associative Array
; Functions, below.
func OneToTwoDArray($array)
    debug("
OneToTwoDArray()", @ScriptLineNumber, 9);debug
    local $new_array[1][2]
    $new_array[0][0] = $array[0]
    for $e = 1 to $array[0]
        redim $new_array[$e+1][2] ; an extra 1!
        $new_array[$e][0] = $array[$e]
        $new_array[$e][1] = "
"
        debug_PrintArray($new_array, "
cel.au3 :: 1D->2D Array:", @ScriptLineNumber, 9);debug
    next
    debug_PrintArray($new_array, "
cel.au3 :: 1D->2D RETURNING Array:", @ScriptLineNumber, 9);debug
    return $new_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


; Take a two-dimensional array and convert it to a one-dimensional array
; using the chosen column of data..
func TwoDCol2OneDArray($Array2D$col=0)
    debug("
TwoDCol2OneDArray()", @ScriptLineNumber, 9);debug
    debug_PrintArray($Array2D, "
cel.au3 :: $Array2D:", @ScriptLineNumber, 7);debug
    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]
        $array[$i] = $Array2D[$i][$col]
        debug_PrintArray($array, "
cel.au3 :: $array:", @ScriptLineNumber, 9);debug
    next
    debug_PrintArray($array, "
cel.au3 :: 2D->1D RETURNING Array:", @ScriptLineNumber, 9);debug
    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("
", @ScriptLineNumber, 7);debug
 debug("
ArrayRemove(" & $item & ")", @ScriptLineNumber, 7);debug

    if not IsArray($array) then return SetError(1, 0, false)
    debug_PrintArray($array, "
cel.au3 :: 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("
", @ScriptLineNumber, 7);debug
 debug("
ArrayRemove2D([" & $item & "," & $column & "])", @ScriptLineNumber, 7);debug

    if not IsArray($array) then return SetError(1, 0, false)
    debug_PrintArray($array, "
cel.au3 :: 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






; Improvements on the UDF-supplied versions; posted in the AutoIt forum..
;
; This is the fastest FileReadToArray, but uses the most memory for big files (x64: 4.5s/1.02GB on a 106MB file, 912515 lines)
;
#FUNCTION# ====================================================================================================================
; Name...........:    FileReadIntoArray
; Description ...:    Reads the specified file into an array.
; Syntax.........:    FileReadIntoArray($TmpFile, ByRef $aArray [, $iFlag])
; Parameters ....:    $TmpFile        - Full path and filename of the file to be read.
;                    $aArray            - The array in which to store the contents of the file.
;                    $iFlag            - Optional: (add the flags together for multiple operations):
;                    |$iFlag = 0 Return the array count in the [0] element (create 1-based array index - the Default)
;                    |$iFlag = 1 Don't return the array count (create 0-based array index)
;                    |$iFlag = 2 Don't return Line empty (ignores @CR & @LF & @CRLF)
;                    |$iFlag = 4 Don't return Line empty or lines containing only whitespace character (ignores @CRLF, " ", @CR, @TAB, @CR, @LF, etc.)
;                    |$iFlag = 8 Strip Line leading white space
; Return values .:    Success - Returns a 1
;                    Failure - Returns a 0
;                    @error  - 0 = No error.
;                    |1 = Error opening specified file
;                    |2 = Unable to Split the file
; Authors ........:    Jonathan Bennett, Valik, Jpm, Gary, guinness, DXRW4E, Cor
; ===============================================================================================================================
func FileReadIntoArray($TmpFile, byref $aArray$iFlag = 0)
    local $ArrayCount$RegExp = "
(?:\r\n|\n|\r)([^\r\n]*)"
    local $hFileOpen = FileOpen($TmpFile, 0)
    if $hFileOpen = -1 then return SetError(1, 0, 0)
    local $sFileRead = StringStripWS(FileRead($hFileOpen), 3)
    FileClose($hFileOpen)

    ; Flags..
    if not BitAND($iFlag, 1) then $ArrayCount = "
ArrayCount" & @LF
    if BitAND($iFlag, 2) then $RegExp = "
(?:\r\n|\n|\r)([^\r\n]+)"
    if BitAND($iFlag, 4) then $RegExp = "
s*(?:\r\n|\n|\r)([^\r\n]+)"
    if BitAND($iFlag, 8) then $RegExp = StringReplace($RegExp, "
)", ")h*", 1, 1)

    $aArray = StringRegExp(@LF & $ArrayCount & $sFileRead$RegExp, 3)

    if @error then
        if StringLen($sFileRead) then
            local $ret[2] = [1, $sFileRead]
            $aArray = $ret
        else
            return SetError(2, 0, 0)
        endif
    elseif $ArrayCount then
        $aArray[0] = UBound($aArray) - 1
    endif
    return 1
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, $round=true)

    debug("
SecondsToDHMS(sec) =>" & $sec & "<=", @ScriptLineNumber, 9);debug

    if $round then $sec = StringFormat("
%.02f", $sec)

    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

; ; Convert seconds to readable D/H/M/S time..
; ;
; func SecondsToDHMS($sec=0)

    ; if $sec < 0 then return -1
    ; select
        ; case  $sec < 61
            ; return StringFormat("%.02f", $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


; Convert newlines from preferences "\n" to actual Linefeeds, for use in logs, 
; console display, etc.. Also works the other way around.
;
; Conversion happens ByRef, so simply do..
;
;    ConvertNewlines($string)
;
func ConvertNewlines(ByRef $pref_string$reverse=false, $LF=$LOG_LF)
    if $reverse then
        $pref_string = StringReplace($pref_string$LF, "
\n")
    else
        $pref_string = StringReplace($pref_string, "
\n", $LF)
    endif
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("
", @ScriptLineNumber, 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("
", @ScriptLineNumber, 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("", @ScriptLineNumber, 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.
;
; If you are using the defaults ($ini_path/$my_name), you can leave out the ini prefs.
;
;
; 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!)
;
; In other words, best to put any control-specific text into the *prompt*.
;
func CorzFancyInputBox($title$prompt$default_text="
", $pass=default, $ib_w=default, $ib_h=default, $ib_x=default, $ib_y=default, _
            $timeout=default, $gui_ex=false, $style=default, $inipath=default, $sectionname=default, $sel1=default, $sel2=default)

    debug("
CorzFancyInputBox(title: " & $title & ", prompt: " & $prompt  & ", default_text: " & $default_text & ", pass: " & $pass  & ", ib_w: " & $ib_w  & ", ib_h: " & $ib_h  & ", ib_x: " & $ib_x  & ", ib_y: " & $ib_y  & ", timeout: " & $timeout  & ", gui_ex: " & $gui_ex  & ", style: " & $style  & ", inipath: " & $inipath  & ", sectionname: " & $sectionname  & ", sel1: " & $sel1   & ", sel2: " & $sel2 & ")...", @ScriptLineNumber, 7);debug


    local $previous_event_mode = AutoItSetOption("
GUIOnEventMode", 0)
    local $previous_coord_mode = AutoItSetOption("
GUICoordMode", 1)

    local $pref_name = CleanPrefName($title)

    if not $inipath or $inipath = default then $inipath = $ini_path
    if not $sectionname or $sectionname = default then $sectionname = $my_name
    if not $style or $style = default then $style = -1
    if not $pass or $pass = default then $pass = "
"

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

    $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 then $ib_x = -1
    if not $ib_y then $ib_y = -1
    if not $ib_h then $ib_h = 96
    if not $ib_w then $ib_w = @DesktopWidth/2

    debug("
$ib_x" & $ib_x, @ScriptLineNumber, 8);debug
    debug("
$ib_y" & $ib_y, @ScriptLineNumber, 8);debug
    debug("
$ib_h" & $ib_h, @ScriptLineNumber, 8);debug
    debug("
$ib_w" & $ib_w, @ScriptLineNumber, 8);debug

    if $timeout = default then $timeout = 0
    if $sel1 = default then $sel1 = 0
    if $sel2 = default then $sel2 = -1

    local $line = $prompt
    if StringInStr($prompt$MSG_LF) then
        ; Measure length of first line, up to the linebreak..
        $line = StringMid($line, 1, StringInStr($line$MSG_LF)-1)
    endif
    debug("
line to length-check: " & $line, @ScriptLineNumber, 8);debug

    local $line_length = StringLen($line)
    debug("
$line_length" & $line_length, @ScriptLineNumber, 8);debug

    local $min_w = $line_length * 7.2 ; MAGIC!
    debug("
$min_w" & $min_w, @ScriptLineNumber, 8);debug

    if $ib_w < $min_w then $ib_w = $min_w

    StringReplace($prompt$MSG_LF$MSG_LF)
    local $line_count = @Extended + 1
    debug("
$line_count" & $line_count, @ScriptLineNumber, 8);debug

    local $return = false
    local $error = 0
    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 $ib_GUI = GUICreate($title$ib_w$ib_h$ib_x$ib_y$style$exStyle$gui_ex)

    local $lab_prompt = GUICtrlCreateLabel($prompt, 10, 5, $ib_w-10, 16*$line_count)
    GUICtrlSetFont(-1, 10)

    local $but_OK = GUICtrlCreateButton("
OK", $ib_w-45, $ib_h-30, 40, 22, $BS_DEFPUSHBUTTON)
    GUICtrlSetState(-1, $GUI_ONTOP)
    GUICtrlSetTip(-1,    "
Save the setting." & $MSG_LF & _
                        "
When you click this button (and the input is not empty)," & $MSG_LF & _
                        "
the size and position of this dialog will be saved for future use.")

    local $but_Cancel = GUICtrlCreateButton("
Cancel", 5, $ib_h-30, 60, 22)
    GUICtrlSetState(-1, $GUI_ONTOP)
    GUICtrlSetTip(-1,    "
Cancel this dialog." & $MSG_LF & _
                        "
Do not save the input."  & $MSG_LF & _
                        "
Do not save dialog's size and position.")

    local $ib_font = IniRead($inipath$sectionname, "
inputbox_font", "Consolas")
    local $inp_MyID = GUICtrlCreateInput($default_text, 70, $ib_h-30, $ib_w-125, 22, $inputstyle)
    GUICtrlSetState(-1, $GUI_FOCUS)
    GUICtrlSetFont(-1, 10 , 400, "
", $ib_font)
    GUICtrlSetState(-1, $GUI_DROPACCEPTED)
    GUICtrlSetTip(-1, $prompt)

    GUICtrlSetResizing($lab_prompt$GUI_DOCKALL)
    GUICtrlSetResizing($but_OK$GUI_DOCKSTATEBAR+$GUI_DOCKWIDTH+$GUI_DOCKRIGHT)
    GUICtrlSetResizing($but_Cancel$GUI_DOCKSTATEBAR+$GUI_DOCKLEFT+$GUI_DOCKWIDTH)
    GUICtrlSetResizing($inp_MyID$GUI_DOCKSTATEBAR+$GUI_DOCKLEFT+$GUI_DOCKRIGHT)


    GUISetState(@SW_SHOW, $ib_GUI)
    _GUICtrlEdit_SetSel($inp_MyID$sel1$sel2)
    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
                local $my_width = $size_array[2]
                if $size_array[2] < $min_w then
                    WinMove($title, "
", default, default, $min_w$ib_h)
                    $my_width = $min_w
                endif
                if $size_array[3] <> $ib_h then WinMove($title, "
", default, default, $my_width$ib_h)
            case $GUI_EVENT_CLOSE$but_Cancel
                $return = false
                $error = 1
                exitloop
            case $but_OK
                $return = StringStripWS(GUICtrlRead($inp_MyID), 3)
                exitloop
        endswitch

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

    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", $previous_event_mode)
    AutoItSetOption("
GUICoordMode", $previous_coord_mode)
    GUIDelete($ib_GUI)


    if $return then
        ; Only write prefs if the user actually changed the position/dimensions from the defaults..
        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)
        endif
        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("
", @ScriptLineNumber, 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("
", @ScriptLineNumber, 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(25)
    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("
", @ScriptLineNumber, 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..
; See corz_essentials.au3 for more info
; 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$this_version$GUI=default)

    debug("
VersionCheckOnline(v)" & $this_version, @ScriptLineNumber, 6);debug

    if not @Compiled then return

    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
        local $do_checkver = CorzFancyInputBox("
Automatic Version Checking?", $v_name & _
                        "
 can check online for a new version of itself." & $MSG_LF & _
                        "
Enter the number of days between checks." & $MSG_LF & _
                        "
(enter 0 to disable version checking.)", "" , " M" , _
                        400, 130 , (@DesktopWidth/2)-200, (@DesktopHeight/2)-50, 0, $GUI)
        if $do_checkver = "
" then return false ; cancelled out of inputbox
        ; They enter "fuck off!", we write "0"
        IniWrite($my_ini$my_section, "
version_checking", Number($do_checkver))
        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=" & $this_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
        debug("
Version Check FAILED." & $errmsg, @ScriptLineNumber, 1);debug
        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($this_version$published_version)
    switch $vcomp
        case 0, 1    ; both equal (or $this_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 (set current position with $start). Optional.

func BackGroundDownload($dl_URL$timeout=default, $start=default, $save_file=default)

    if $timeout = default then $timeout = 3000
    if $start = default then $start = 0
    if $save_file = default then $save_file = false

    Local $c_time$TmpFile
    $TmpFile = _WinAPI_GetTempFileName(@TempDir)
    if $save_file then
        $TmpFile = $save_file
    else
    $TmpFile = _WinAPI_GetTempFileName(@TempDir)
    endif
    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()
    local $dmsg

    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)
            $dmsg = "
Failed. Connexion Timeout."
            ProgressSet(0, "
", $dmsg)
            debug($dmsg, @ScriptLineNumber, 1);debug
            return SetError(1, 2, $dmsg)    ; 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
            $dmsg = "
Failed. Suspect Firewall."
            ProgressSet(0, "
", $dmsg)
            debug($dmsg, @ScriptLineNumber, 1);debug
            return SetError(1, 3, $dmsg; 3 = check firewall
        else
            $dmsg = "
Failed. Suspect Server Error."
            debug($dmsg, @ScriptLineNumber, 1);debug
            ProgressSet(0, "
", $dmsg)
            return SetError(1, 1, $dmsg; 1 = server error / 404
        endif
    endif

    local $dl_data = FileRead($TmpFile)
    ProgressSet(0, "
", "Success!")

    if not $save_file then
        FileDelete($TmpFile)
        return $dl_data
    endif

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("
", @ScriptLineNumber, 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("
", @ScriptLineNumber, 7);debug
 debug("
GetCommandLine(" & $command_line & ")", @ScriptLineNumber, 7);debug
 debug_PrintArray($cmd_array$LOG_LF & "
cel.au3 :: (" & @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("
", @ScriptLineNumber, 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="
")

    debug("
Log extra: " & $log_extra, @ScriptLineNumber, 10 );debug

    if $dl_string = "
out" then
        if $log_string then
            if $dl_append = $ON then
                $dl_append = $FO_APPEND + $FO_CREATEPATH
            else
                $dl_append = $FO_OVERWRITE  + $FO_CREATEPATH
            endif


            local $my_log_file = FileOpen($log_location$dl_append)
            FileWriteLine($my_log_file$my_name & _
                "
 log:  " & DateTimeString() & ".. " & $LOG_LF & _
                "
--------------------------------------------------------------------------------" & $LOG_LF)
            if not $log_extra then
                FileWriteLine($my_log_file, "
command-line: " & $CmdLineRaw & $LOG_LF & $LOG_LF)
            endif
            FileWriteLine($my_log_file$log_extra)
            FileWriteLine($my_log_file$log_string & $LOG_LF)
            FileClose($my_log_file)
            $log_string = "
"
        endif
    else
        $log_string &= UnifyCRLF($dl_string) & $LOG_LF
    endif
    return $dl_string
endfunc







;;    Debugging Functions..



; debug()
;
; Provides quick debug report in your console/log..
;
; If your text editor can do it (probably), this is a great
; way to get debug output without halting the script execution..
;
; Newbies: you set your global $debug_level and bugs of that level
; and below are logged. Stuff you always want to see will have a low
; level, and stuff you rarely want to see (it's large, etc.) will
; have a higher level.
;
; Debug levels go from 1 to 10, though typically in my apps there is
; also level 11, where gobs of generally unnecessary output is
; produced. An occasionally useful nod to Spinal Tap, I suppose.
;
; NOTE: if you call debug() in a compiled script you will get
; the debug() output debug_dump()ed to a log, instead.
;
; The Au3Stripper Parameter "/rsln" ensures these line numbers are
; preserved in the compiled script, so debug output is fairly useful.
; A typical debug line might look like this:
;
; (1103/2899) : ==> $do_output: enabled
;
; The numbers in braces refer to (original script line / stripped
; script line). The one on the left is probably the most useful.
;

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

    if $level > $debug_level then return false

    ; This is unlikely to catch actual arrays under normal circumstances (there is a string prepended to input!)..
    if IsArray($d_string) then return debug_PrintArray($d_string, "
cel.au3 :: NOT A STRING!", $ln$level, 0, $max_size);debug
    if @Compiled then return debug_dump($d_string$ln$level, true, $max_size)

    local $pre = "
"

    ; We add the line numbers so that:
    ; a) We know where shit happens, and..
    ; b) if your text editor supports it, we can click those lines in the console output
    ; and go directly to that line in the script. If your text editor/IDE does not support
    ; this feature, upgrade your editor. The time you are wasting ...

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

    if $pre then $pre &= "
" & _NowCalcDate() & " @ " & _NowTime() & "::" & @Msec & " ][l:" & $level & "]" & @Tab
    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_debug_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 $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_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_debug_log_size$forcedump=false, $string=false)

    if $level > $debug_level then return false

    local $pre
    $pre = "
(" & $ln & "" & @Tab
    if $pre then $pre &= "
" & _NowCalcDate() & " @ " & _NowTime() & "::" & @Msec & " ][l:" & $level & "]" & @Tab
    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..
;
; 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_debug_log_size$forcedump=false, $string=false) $limit for compatability only
    if @Compiled and ($level > $debug_level) then return 0
    local $pre
    if $ln then $pre = "
(" & $ln & "" & @Tab
    if not IsArray($array) then return debug($pre & $tring & "
: NOT an array!" & $LOG_LF & _
                $pre & "
it's a string: " & "->" & $array & "<-" & $LOG_LF, @ScriptLineNumber, 5);debug
    ; 1-dimensional array sent..
    if UBound($array, 0) < 2 then return debug_PrintArray($array$tring$ln$level$limit$ln)

    if $pre then $pre &= "
" & _NowCalcDate() & " @ " & _NowTime() & "::" & @Msec & " ][l:" & $level & "]" & @Tab
    $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_debug_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)

    CheckLogSize($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_debug_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


; Provides a time-stamped backup of a file..
func BackupFile($file)
    local $backup = GetParent($file) & "
\[" & FileDateStamp() & "]_" & RemoveExtension(Basename($file)) & "." & GetExtension($file)
    local $ret = FileMove($file$backup)
    Run(@ComSpec & "
 /c " & 'compact.exe /C "' & $dump_file & '"', "", @SW_HIDE)
    return $ret
endfunc


; _IsPressed (is a key pressed?)
; Author(s):    ezzetabi and Jon / Valik
func ce_IsPressed($Key)
    local $kp = DllCall('user32.dll', "
int", "GetAsyncKeyState", "int", '0x' & $Key)
    if not @error and BitAND($kp[0], 0x8000) = 0x8000 then return 1
    return 0
endfunc



; 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
;
; This also makes a great part 1 of a 1-2 WriteArrayToFromFile type function.
; (see above for exactly that)
;
func ArrayJoin($array$join_str=$LOG_LF)
    debug_PrintArray($array, "
cel.au3 :: ArrayJoin():", @ScriptLineNumber, 8);debug
    debug("
$join_str" & $join_str, @ScriptLineNumber, 8);debug

    local $ret_str
    for $i = 1 to $array[0]
        if $array[$i] <> "
" then
            $ret_str &= $array[$i] & $join_str
        endif
    next
    CRT($ret_str$join_str)
    debug("
$ret_str" & $ret_str, @ScriptLineNumber, 8);debug

    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(25)
    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!