Jump to content

Trying to create a treeview structured with separate icons for list items


Go to solution Solved by Nine,

Recommended Posts

Posted (edited)

Is this possible?

In essence, I am trying to create a menu with categories, these categories use standard system folder icons.
But the leaf nodes are using the same icon and despite every attempt, I can't get the treeview to show folder icons for categories and a second icon for leaf nodes. Everything has the same icon, the folder.

Is there a way around this? Or is it a limitation of AutoIT?
Thanks for your help. 🙂

Edited by sl23
  • sl23 changed the title to Trying to create a treeview structured with separate icons for list items
Posted

Here's a screenshot to explain:
2025-08-13_12_17_28.jpg.b5f22bf56aad6291c55211e2dc49a139.jpg

As you can see, it is using the same folder icon for the Snes9X leaf node. It should be using either it's own icon, or even a predefined icon, I tried both methods and many more, but nothing is showing the apps icon. I'm completely stuck!

Any ideas on how to do this? Simplest possible please, still trying to learn how this works ;)

Appreciate any advice.

Posted (edited)

Ok, sorry, I'm not too clear on what that meant.
This is what I currently have:
 

#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <GuiTreeView.au3>
#include <GuiImageList.au3>
#include <Array.au3>
#include <Misc.au3>
#include <StaticConstants.au3>
#include <WinAPIIcons.au3>
#include <WinAPIShellEx.au3>
#include "PortableUtils.au3"

Global Const $RUN_AS_ADMIN = 0x00000080
Global $INI_FILE = @ScriptDir & "\Settings.ini"

Global $Theme, $isDark, $bgColor, $buttonColor, $buttonTextColor, $buttonBorder, $listviewFontColor
Global $selectionColor = 0x3399FF ; Default highlight (blue)
Global $guiWidth, $guiHeight, $maxMenuHeight, $hGUI
Global $navMenu, $navReload, $navScan, $navOpenSettings, $helpMenu, $helpSyntax, $helpEnv, $helpShortcuts, $helpAbout
Global $stopSandboxieMenu, $stopSandboxieItem
Global $TitleText
Global $CloseMenuOnClick = 0
Global $buttonFontSize = 10
Global $buttonFontType = "Segoe UI"

Global $userVars = ObjCreate("Scripting.Dictionary")
Global $appsData[1][10]
Global $appsSections[1]
Global $SandboxieConfigured = False
Global $SandboxiePath = ""
Global $SandboxieServiceName = "SbieSvc"

Global $treeviewID
Global $appNodeMap = ObjCreate("Scripting.Dictionary")
Global $categoryNodeSet = ObjCreate("Scripting.Dictionary")

Func GetBestIconIndex($hImageList, $filepath)
    Local $iconIdx = -1
    If FileExists($filepath) And StringRegExp($filepath, "\.(exe|dll|ico)$") Then
        For $i = 0 To 5
            $iconIdx = _GUIImageList_AddIcon($hImageList, $filepath, $i)
            If $iconIdx <> -1 Then Return $iconIdx
        Next
    EndIf
    Return 1 ; Use leaf/app icon as fallback for leaf nodes
EndFunc

Func RGB2Dec($rgbString)
    Local $a = StringSplit($rgbString, ",")
    If $a[0] <> 3 Then Return 0x3399FF ; fallback blue
    Return BitOR(($a[1] * 0x10000), ($a[2] * 0x100), $a[3])
EndFunc

Func IsDarkMode()
    Local $reg = RegRead("HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Themes\Personalize", "AppsUseLightTheme")
    If @error Then Return False
    Return $reg = 0
EndFunc

Func SetTheme()
    $Theme = IniRead($INI_FILE, "GLOBAL", "Theme", "")
    If $Theme = "" Then
        $isDark = IsDarkMode()
    Else
        $isDark = (StringLower($Theme) = "dark")
    EndIf
    If $isDark Then
        $bgColor = 0x202020
        $buttonColor = 0x2D2D2D
        $buttonTextColor = 0xFFFFFF
        $buttonBorder = 0x404040
        $listviewFontColor = 0xFFFFFF
    Else
        $bgColor = 0xF0F0F0
        $buttonColor = 0xFFFFFF
        $buttonTextColor = 0x000000
        $buttonBorder = 0xC0C0C0
        $listviewFontColor = 0x222222
    EndIf
    ; Read highlight color from INI (RGB, e.g. 255,128,128)
    Local $selCol = IniRead($INI_FILE, "GLOBAL", "SelectionColor", "")
    If $selCol <> "" Then
        If StringRegExp($selCol, "^\d{1,3},\d{1,3},\d{1,3}$") Then
            $selectionColor = RGB2Dec($selCol)
        ElseIf StringLeft($selCol, 2) = "0x" Then
            $selectionColor = Dec(StringReplace($selCol, "0x", ""))
        Else
            $selectionColor = 0x3399FF ; fallback blue
        EndIf
    Else
        $selectionColor = 0x3399FF ; Default blue
    EndIf
EndFunc

Func ReadSettings()
    $buttonFontSize = Number(IniRead($INI_FILE, "GLOBAL", "ButtonFontSize", "10"))
    $buttonFontType = IniRead($INI_FILE, "GLOBAL", "ButtonFontType", "Segoe UI")
    $TitleText = IniRead($INI_FILE, "GLOBAL", "TitleText", "")
    $CloseMenuOnClick = Number(IniRead($INI_FILE, "GLOBAL", "CloseMenuOnClick", "0"))
    $SandboxieConfigured = False
    $SandboxiePath = IniRead($INI_FILE, "GLOBAL", "SandboxiePath", "")
    If $SandboxiePath <> "" And FileExists($SandboxiePath) Then
        $SandboxieConfigured = True
    EndIf
    $userVars.RemoveAll()
    Local $globalKeys = IniReadSection($INI_FILE, "GLOBAL")
    If @error Or Not IsArray($globalKeys) Then Return
    For $i = 1 To $globalKeys[0][0]
        If StringLower($globalKeys[$i][0]) <> "setpath" Then
            $userVars($globalKeys[$i][0]) = $globalKeys[$i][1]
        Else
            Local $parts = StringSplit($globalKeys[$i][1], "|")
            If $parts[0] = 2 Then
                $userVars($parts[1]) = $parts[2]
            EndIf
        EndIf
    Next
EndFunc

Func ExpandUserVars($str)
    Local $result = $str
    For $key In $userVars.Keys
        $result = StringReplace($result, "%" & $key & "%", $userVars($key))
    Next
    Return $result
EndFunc

Func ReadApps()
    Local $sections = IniReadSectionNames($INI_FILE)
    Local $count = 0
    For $i = 1 To $sections[0]
        If StringRegExp($sections[$i], "(?i)^BUTTON\d+$") Then
            $count += 1
        EndIf
    Next
    If $count = 0 Then
        ReDim $appsData[1][10]
        ReDim $appsSections[1]
        $appsData[0][0] = ""
        $appsSections[0] = ""
        Return
    EndIf
    ReDim $appsData[$count][10]
    ReDim $appsSections[$count]
    Local $idx = 0
    For $i = 1 To $sections[0]
        If StringRegExp($sections[$i], "(?i)^BUTTON\d+$") Then
            Local $dict = ReadButtonSettings($INI_FILE, $sections[$i])
            $appsSections[$idx] = $sections[$i]
            $appsData[$idx][0] = $dict.Exists("ButtonText") ? $dict.Item("ButtonText") : "Button " & $idx+1
            $appsData[$idx][1] = $dict.Exists("Category") ? StringStripWS($dict.Item("Category"), 3) : "" ; <--- changed: blank if no category
            $appsData[$idx][2] = $dict.Exists("RunFile") ? ExpandUserVars($dict.Item("RunFile")) : ""
            $appsData[$idx][3] = $dict.Exists("Arguments") ? ExpandUserVars($dict.Item("Arguments")) : ""
            $appsData[$idx][4] = $dict.Exists("SingleInstance") ? (StringLower(StringStripWS($dict.Item("SingleInstance"), 3)) = "true" Or $dict.Item("SingleInstance") = "1") : False
            $appsData[$idx][5] = $dict.Exists("RunAsAdmin") ? Number($dict.Item("RunAsAdmin")) : 0
            $appsData[$idx][6] = $dict.Exists("NetAccess") ? Number($dict.Item("NetAccess")) : 1
            $appsData[$idx][7] = $dict.Exists("Sandboxie") ? ExpandUserVars($dict.Item("Sandboxie")) : ""
            $appsData[$idx][8] = $dict.Exists("SandboxName") ? ExpandUserVars($dict.Item("SandboxName")) : ""
            $appsData[$idx][9] = $dict.Exists("FileAssocApp") ? ExpandUserVars($dict.Item("FileAssocApp")) : ""
            $idx += 1
        EndIf
    Next
EndFunc

Func CreateMenuBar()
    $navMenu = GUICtrlCreateMenu("Navigation")
    $navReload = GUICtrlCreateMenuItem("Reload", $navMenu)
    $navScan   = GUICtrlCreateMenuItem("Scan", $navMenu)
    $navOpenSettings = GUICtrlCreateMenuItem("Open Settings", $navMenu)
    $helpMenu  = GUICtrlCreateMenu("Help")
    $helpSyntax    = GUICtrlCreateMenuItem("Commandline Syntax", $helpMenu)
    $helpEnv       = GUICtrlCreateMenuItem("Environment Variables", $helpMenu)
    $helpShortcuts = GUICtrlCreateMenuItem("Generate Shortcuts", $helpMenu)
    $helpAbout     = GUICtrlCreateMenuItem("About", $helpMenu)
    $stopSandboxieItem = GUICtrlCreateMenuItem("Close Sandboxie", $navMenu)
    If Not $SandboxieConfigured Then
        GUICtrlSetState($stopSandboxieItem, $GUI_DISABLE)
    Else
        GUICtrlSetState($stopSandboxieItem, $GUI_ENABLE)
    EndIf
EndFunc

Func ShowGUI()
    $guiWidth = 520
    $guiHeight = 650
    $hGUI = GUICreate("PortableApps Menu", $guiWidth, $guiHeight, (@DesktopWidth - $guiWidth) / 2, (@DesktopHeight - $guiHeight) / 2, -1, $WS_EX_TOPMOST + $WS_EX_WINDOWEDGE)
    GUISetBkColor($bgColor, $hGUI)
    CreateMenuBar()
    If $TitleText <> "" Then
        Local $lbl = GUICtrlCreateLabel($TitleText, 0, 18, $guiWidth, 36, $SS_CENTER)
        GUICtrlSetFont($lbl, 15, 700, 0, "Segoe UI")
        GUICtrlSetColor($lbl, $buttonTextColor)
        GUICtrlSetBkColor($lbl, $GUI_BKCOLOR_TRANSPARENT)
    EndIf

    ; --- TreeView Image List with folder icon and leaf icon ---
    Local $hImageList = _GUIImageList_Create(16, 16, 5, 3)
    Local $folderIcon = _GUIImageList_AddIcon($hImageList, @SystemDir & "\shell32.dll", 4) ; index 0
    Local $leafIconPath = @ScriptDir & "\myappicon.ico"
    Local $leafIcon = -1
    If FileExists($leafIconPath) Then
        $leafIcon = _GUIImageList_AddIcon($hImageList, $leafIconPath, 0) ; index 1
    Else
        $leafIcon = _GUIImageList_AddIcon($hImageList, @SystemDir & "\shell32.dll", 4)
    EndIf

    ; --- Ensure TreeView uses HASLINES and HASBUTTONS styles for glyphs ---
    Local $treeviewStyle = BitOR($WS_BORDER, $WS_TABSTOP)
    Local $treeviewExStyle = 0
    $treeviewID = GUICtrlCreateTreeView(20, 70, $guiWidth-40, $guiHeight-110, $treeviewStyle, $treeviewExStyle)
    GUICtrlSetFont($treeviewID, $buttonFontSize, 400, 0, $buttonFontType)
    GUICtrlSetBkColor($treeviewID, $buttonColor)
    GUICtrlSetColor($treeviewID, $listviewFontColor)

    _GUICtrlTreeView_SetNormalImageList(GUICtrlGetHandle($treeviewID), $hImageList)

    ; --- This ensures the +/- glyphs are visible ---
    GUICtrlSendMsg($treeviewID, 0x1100+1, 0x002 + 0x004 + 0x001, 0) ; TVS_HASBUTTONS + TVS_HASLINES + TVS_LINESATROOT

    $appNodeMap.RemoveAll()
    $categoryNodeSet.RemoveAll()

    ; --- Alphabetically sort categories, SKIP blank category ---
    Local $categories[1] = [0]
    For $i = 0 To UBound($appsData)-1
        Local $category = $appsData[$i][1]
        If $category = "" Then ContinueLoop
        Local $found = False
        For $j = 1 To $categories[0]
            If $categories[$j] = $category Then
                $found = True
                ExitLoop
            EndIf
        Next
        If Not $found Then
            $categories[0] += 1
            ReDim $categories[$categories[0]+1]
            $categories[$categories[0]] = $category
        EndIf
    Next
    _ArraySort($categories, 1, 1, $categories[0])

    ; --- Create category nodes with folder icon ---
    Local $categoryDict = ObjCreate("Scripting.Dictionary")
    For $i = 1 To $categories[0]
        Local $catNode = GUICtrlCreateTreeViewItem($categories[$i], $treeviewID)
        _GUICtrlTreeView_SetImageIndex(GUICtrlGetHandle($treeviewID), $catNode, 0) ; folder icon
        $categoryDict($categories[$i]) = $catNode
        $categoryNodeSet($catNode) = True
    Next

    ; --- Add apps: if no category, add under root ---
    For $i = 0 To UBound($appsData)-1
        Local $category = $appsData[$i][1]
        Local $appNode
        If $category = "" Then
            $appNode = GUICtrlCreateTreeViewItem($appsData[$i][0], $treeviewID)
        Else
            Local $parentNode = $categoryDict($category)
            $appNode = GUICtrlCreateTreeViewItem($appsData[$i][0], $parentNode)
        EndIf
        _GUICtrlTreeView_SetImageIndex(GUICtrlGetHandle($treeviewID), $appNode, 1) ; custom icon for leaf node
        $appNodeMap($appNode) = $i
    Next

    _GUICtrlTreeView_SetInsertMarkColor(GUICtrlGetHandle($treeviewID), $selectionColor)
    GUISetState(@SW_SHOW, $hGUI)
EndFunc

Func MainLoop()
    While 1
        Local $msg = GUIGetMsg()
        If $msg = $GUI_EVENT_CLOSE Then Exit
        If $msg = $navReload Then
            GUIDelete($hGUI)
            SetTheme()
            ReadSettings()
            ReadApps()
            ShowGUI()
            ContinueLoop
        ElseIf $msg = $navScan Then
            TopmostMsgBox(64, "Navigation", "Scan selected.")
        ElseIf $msg = $navOpenSettings Then
            ShellExecute($INI_FILE)
        ElseIf $msg = $helpSyntax Then
            TopmostMsgBox(64, "Help", "Commandline Syntax selected.")
        ElseIf $msg = $helpEnv Then
            ShowEnvVarListView()
        ElseIf $msg = $helpShortcuts Then
            TopmostMsgBox(64, "Help", "Generate Shortcuts selected.")
        ElseIf $msg = $helpAbout Then
            TopmostMsgBox(64, "About", "PortableApps Menu" & @CRLF & "Version 1.0")
        ElseIf $msg = $stopSandboxieItem Then
            If $SandboxieConfigured Then _StopSandboxieService()
        EndIf

        If $msg > 0 And $appNodeMap.Exists($msg) Then
            LaunchApp($appNodeMap($msg))
            If $CloseMenuOnClick Then
                GUIDelete($hGUI)
                Exit
            EndIf
        EndIf
    WEnd
EndFunc

Func TopmostMsgBox($flag, $title, $text)
    Local $w = 400, $h = 140
    If $title = "" Then $title = "(no title provided)"
    If $text = "" Then $text = "(no message text provided)"
    Local $gui = GUICreate($title, $w, $h, -1, -1, -1, $WS_EX_TOPMOST)
    GUISetBkColor(0xF0F0F0, $gui)
    Local $icon = 0
    If BitAND($flag, 16) Then
        $icon = GUICtrlCreateIcon(@SystemDir & "\shell32.dll", 49, 20, 20, 32, 32)
    ElseIf BitAND($flag, 48) Then
        $icon = GUICtrlCreateIcon(@SystemDir & "\shell32.dll", 44, 20, 20, 32, 32)
    ElseIf BitAND($flag, 64) Then
        $icon = GUICtrlCreateIcon(@SystemDir & "\shell32.dll", 221, 20, 20, 32, 32)
    Else
        $icon = GUICtrlCreateIcon(@SystemDir & "\shell32.dll", 154, 20, 20, 32, 32)
    EndIf

    Local $lbl = GUICtrlCreateLabel($text, 60, 20, $w-80, $h-60)
    Local $btn = GUICtrlCreateButton("OK", $w/2-40, $h-48, 80, 28)
    GUISetState(@SW_SHOW, $gui)

    While 1
        Local $msg = GUIGetMsg()
        If $msg = $btn Or $msg = $GUI_EVENT_CLOSE Then ExitLoop
        Sleep(10)
    WEnd
    GUIDelete($gui)
EndFunc

Func SetFirewallNetAccess($exePath, $enable)
    If $exePath = "" Then Return
    Local $ruleName = StringRegExpReplace($exePath, "^.*\\", "MenuApp_")
    If $enable Then
        RunWait('netsh advfirewall firewall delete rule name="' & $ruleName & '"', "", @SW_HIDE)
    Else
        RunWait('netsh advfirewall firewall add rule name="' & $ruleName & '" dir=out action=block program="' & $exePath & '" enable=yes', "", @SW_HIDE)
        RunWait('netsh advfirewall firewall add rule name="' & $ruleName & '" dir=in action=block program="' & $exePath & '" enable=yes', "", @SW_HIDE)
    EndIf
EndFunc

Func LaunchApp($index)
    Local $appName = $appsData[$index][0]
    Local $appPath = $appsData[$index][2]
    Local $arguments = $appsData[$index][3]
    Local $singleInstance = $appsData[$index][4]
    Local $runAsAdmin = $appsData[$index][5]
    Local $netAccess = $appsData[$index][6]
    Local $sandboxiePath = $appsData[$index][7]
    Local $sandboxName = $appsData[$index][8]
    Local $fileAssocApp = $appsData[$index][9]

    If $appPath = "" Or Not FileExists($appPath) Then
        TopmostMsgBox(48, $appName, "No app found at this location:" & @CRLF & $appPath)
        Return
    EndIf

    If $singleInstance Then
        Local $exeName = StringRegExpReplace($appPath, "^.*\\", "")
        If ProcessExists($exeName) Then
            TopmostMsgBox(48, $appName, "The app is already running:" & @CRLF & $exeName)
            Return
        EndIf
    EndIf

    SetFirewallNetAccess($appPath, $netAccess)

    If $fileAssocApp <> "" Then
        If $runAsAdmin Then
            ShellExecute($fileAssocApp, '"' & $appPath & '"', "", "runas")
        Else
            ShellExecute($fileAssocApp, '"' & $appPath & '"')
        EndIf
        Return
    EndIf

    If $sandboxiePath <> "" Then
        If $sandboxiePath = "%SandboxiePath%" Then
            $sandboxiePath = $SandboxiePath
        EndIf
        If $sandboxName = "" Then $sandboxName = "DefaultBox"
        Local $cmd = '"' & $sandboxiePath & '" /box:' & $sandboxName & ' "' & $appPath & '"'
        _EnsureSandboxieService()
        Run($cmd, "", @SW_SHOW)
        Return
    EndIf

    If $runAsAdmin Then
        ShellExecute($appPath, $arguments, "", "runas")
    Else
        ShellExecute($appPath, $arguments)
    EndIf
EndFunc

Func ReadButtonSettings($iniFile, $section)
    Local $dict = ObjCreate("Scripting.Dictionary")
    Local $keys = IniReadSection($iniFile, $section)
    If @error Or Not IsArray($keys) Or UBound($keys) < 2 Then
        SetError(1, 0, 0)
        Return $dict
    EndIf
    For $i = 1 To $keys[0][0]
        $dict($keys[$i][0]) = $keys[$i][1]
    Next
    Return $dict
EndFunc

Func _IsServiceRunning($svcName)
    Local $svcStatus = Run(@ComSpec & " /c sc query " & $svcName, "", @SW_HIDE, $STDOUT_CHILD)
    Local $output = ""
    While 1
        $output &= StdoutRead($svcStatus)
        If @error Then ExitLoop
    WEnd
    Return StringInStr($output, "RUNNING") > 0
EndFunc

Func _EnsureSandboxieService()
    If Not _IsServiceRunning($SandboxieServiceName) Then
        RunWait(@ComSpec & " /c net start " & $SandboxieServiceName, "", @SW_HIDE)
    EndIf
EndFunc

Func _StopSandboxieService()
    If _IsServiceRunning($SandboxieServiceName) Then
        If TopmostMsgBox(4, "Sandboxie", "Close Sandboxie and Stop services and drivers?") = 6 Then
            RunWait(@ComSpec & " /c net stop " & $SandboxieServiceName, "", @SW_HIDE)
        EndIf
    Else
        TopmostMsgBox(64, "Sandboxie", "Sandboxie service is already stopped.")
    EndIf
    If ProcessExists("SandMan.exe") Then
        ProcessClose("SandMan.exe")
    EndIf
EndFunc

SetTheme()
ReadSettings()
ReadApps()
ShowGUI()
MainLoop()
; --- PortableUtils.au3 ---
; Advanced portable app launching utility functions for PascalCase INI settings

;~ Func ReadButtonSettings($iniPath, $sectionName)
;~     Local $settings = ObjCreate("Scripting.Dictionary")
;~     Local $keys = IniReadSection($iniPath, $sectionName)
;~     If IsArray($keys) Then
;~         For $i = 1 To $keys[0][0]
;~             $settings.Add($keys[$i][0], $keys[$i][1])
;~         Next
;~     EndIf
;~     Return $settings
;~ EndFunc

Func SetPortableEnvVars($settings)
    For $key In $settings.Keys()
        If StringLeft($key, 4) = "Env_" Then
            Local $envPath = $settings.Item($key)
            If StringLeft($envPath, 2) = ".\" Then
                DirCreate($envPath)
            EndIf
            EnvSet(StringTrimLeft($key, 4), $envPath)
        EndIf
    Next
EndFunc

Func SetupSymLinks($settings)
    For $key In $settings.Keys()
        If $key = "SymLink" Or StringRegExp($key, "^SymLink\d+$") Then
            Local $pair = StringSplit($settings.Item($key), "|")
            If $pair[0] = 2 Then
                Local $source = $pair[1]
                Local $dest = $pair[2]
                If FileExists($dest) Or DirExists($dest) Then
                    RunWait(@ComSpec & ' /c rmdir "' & $dest & '"', "", @SW_HIDE)
                    FileDelete($dest)
                EndIf
                DirCreate(StringLeft($dest, StringInStr($dest, "\", 0, -1) - 1))
                If DirExists($source) Then
                    RunWait(@ComSpec & ' /c mklink /J "' & $dest & '" "' & $source & '"', "", @SW_HIDE)
                ElseIf FileExists($source) Then
                    RunWait(@ComSpec & ' /c mklink /H "' & $dest & '" "' & $source & '"', "", @SW_HIDE)
                EndIf
            EndIf
        EndIf
    Next
EndFunc

Func RemoveSymLinks($settings)
    For $key In $settings.Keys()
        If $key = "SymLink" Or StringRegExp($key, "^SymLink\d+$") Then
            Local $pair = StringSplit($settings.Item($key), "|")
            If $pair[0] = 2 Then
                Local $dest = $pair[2]
                If DirExists($dest) Or FileExists($dest) Then
                    RunWait(@ComSpec & ' /c rmdir "' & $dest & '"', "", @SW_HIDE)
                    FileDelete($dest)
                EndIf
            EndIf
        EndIf
    Next
EndFunc

Func HandleRegistry($settings, $mode)
    If $settings.Exists("Reg_Key") Then
        Local $regKey = $settings.Item("Reg_Key")
        Local $backupFile = @ScriptDir & "\Data\RegBackup_" & StringReplace($regKey, "\", "_") & ".reg"
        If $mode = "restore" And FileExists($backupFile) Then
            RunWait(@ComSpec & ' /c reg import "' & $backupFile & '"', "", @SW_HIDE)
        ElseIf $mode = "backup" Then
            RunWait(@ComSpec & ' /c reg export "' & $regKey & '" "' & $backupFile & '" /y', "", @SW_HIDE)
        EndIf
    EndIf
EndFunc

Func DoCleanup($settings)
    For $key In $settings.Keys()
        If StringLeft($key, 7) = "Cleanup" Then
            Local $pattern = $settings.Item($key)
            If StringRegExp($pattern, "\*") Then
                FileDelete($pattern)
            ElseIf DirExists($pattern) Then
                DirRemove($pattern, 1)
            EndIf
        EndIf
    Next
EndFunc

Func ShowSplash($settings)
    If $settings.Exists("Splash") Then
        Local $img = $settings.Item("Splash")
        Local $wait = 1500
        If $settings.Exists("SplashWait") Then $wait = Number($settings.Item("SplashWait"))
        SplashImageOn("Launching...", $img, 400, 200)
        Sleep($wait)
        SplashOff()
    EndIf
EndFunc

Func LaunchSandboxed($exe, $args, $workdir, $settings)
    If $settings.Exists("Sandboxie") Then
        Local $sandboxiePath = $settings.Item("Sandboxie")
        Local $cmd = '"' & $sandboxiePath & '" /box:DefaultBox "' & $exe & '" ' & $args
        Run($cmd, $workdir)
        Return True
    EndIf
    Return False
EndFunc

Func MapDriveLetter($settings)
    If $settings.Exists("DriveMap") Then
        Local $parts = StringSplit($settings.Item("DriveMap"), " ")
        If $parts[0] = 2 Then
            Local $driveLetter = $parts[1]
            Local $targetPath = $parts[2]
            RunWait(@ComSpec & ' /c subst ' & $driveLetter & ' "' & $targetPath & '"', "", @SW_HIDE)
        EndIf
    EndIf
EndFunc

Func IsSingleInstance($settings)
    If $settings.Exists("SingleInstance") And StringLower($settings.Item("SingleInstance")) = "true" Then
        Local $exe = $settings.Item("Exe")
        If ProcessExists($exe) Then Return True
    EndIf
    Return False
EndFunc

Func LaunchPortableApp($settings)
    If IsSingleInstance($settings) Then
        MsgBox(48, "Already Running", "This app is already running.")
        Return
    EndIf

    ShowSplash($settings)
    MapDriveLetter($settings)
    SetPortableEnvVars($settings)
    SetupSymLinks($settings)
    HandleRegistry($settings, "restore")

    If Not $settings.Exists("Exe") Then
        MsgBox(16, "Error", "Missing 'Exe' setting in button section.")
        RemoveSymLinks($settings)
        Return
    EndIf
    Local $exe = $settings.Item("Exe")
    Local $workdir = @ScriptDir
    If $settings.Exists("WorkDir") Then $workdir = $settings.Item("WorkDir")
    Local $args = ""
    If $settings.Exists("Arguments") Then $args = $settings.Item("Arguments")

    If LaunchSandboxed($exe, $args, $workdir, $settings) Then
        HandleRegistry($settings, "backup")
        RemoveSymLinks($settings)
        DoCleanup($settings)
        Return
    EndIf

    Local $cmd = '"' & $exe & '"'
    If $args <> "" Then $cmd &= " " & $args
    Local $pid = Run($cmd, $workdir)
    If $pid Then
        While ProcessExists($pid)
            Sleep(500)
        WEnd
    EndIf

    HandleRegistry($settings, "backup")
    RemoveSymLinks($settings)
    DoCleanup($settings)
EndFunc

Thank you.

Edited by sl23
Posted

Ok, after checking the manual, I tried the example and got another mock up of the ShowGui function, but it refuses to show different icons:
Entire ShowGUI Function:
 

Func ShowGUI()
    $guiWidth = 520
    $guiHeight = 650
    $hGUI = GUICreate("PortableApps Menu", $guiWidth, $guiHeight, (@DesktopWidth - $guiWidth) / 2, (@DesktopHeight - $guiHeight) / 2, -1, $WS_EX_TOPMOST + $WS_EX_WINDOWEDGE)
    GUISetBkColor($bgColor, $hGUI)
    CreateMenuBar()
    If $TitleText <> "" Then
        Local $lbl = GUICtrlCreateLabel($TitleText, 0, 18, $guiWidth, 36, $SS_CENTER)
        GUICtrlSetFont($lbl, 15, 700, 0, "Segoe UI")
        GUICtrlSetColor($lbl, $buttonTextColor)
        GUICtrlSetBkColor($lbl, $GUI_BKCOLOR_TRANSPARENT)
    EndIf

    ; Create the TreeView control
    Local $iTreeStyle = BitOR($WS_BORDER, $WS_TABSTOP, 0x0001, 0x0002, 0x0004) ; TVS_HASBUTTONS, TVS_HASLINES, TVS_LINESATROOT
    $treeviewID = GUICtrlCreateTreeView(20, 70, $guiWidth-40, $guiHeight-110, $iTreeStyle)
    GUICtrlSetFont($treeviewID, $buttonFontSize, 400, 0, $buttonFontType)
    GUICtrlSetBkColor($treeviewID, $buttonColor)
    GUICtrlSetColor($treeviewID, $listviewFontColor)

    ; Create the image list for icons
    Local $hImageList = _GUIImageList_Create(16, 16, 5, 3)
    Local $idxFolder = _GUIImageList_AddIcon($hImageList, "shell32.dll", 4) ; folder icon
    Local $idxApp    = _GUIImageList_AddIcon($hImageList, "shell32.dll", 2)   ; exe icon

    _GUICtrlTreeView_SetNormalImageList(GUICtrlGetHandle($treeviewID), $hImageList)

    $appNodeMap.RemoveAll()
    $categoryNodeSet.RemoveAll()

    ; Build sorted list of categories (skip blanks)
    Local $categories[1] = [0]
    For $i = 0 To UBound($appsData)-1
        Local $category = $appsData[$i][1]
        If $category = "" Then ContinueLoop
        Local $found = False
        For $j = 1 To $categories[0]
            If $categories[$j] = $category Then
                $found = True
                ExitLoop
            EndIf
        Next
        If Not $found Then
            $categories[0] += 1
            ReDim $categories[$categories[0]+1]
            $categories[$categories[0]] = $category
        EndIf
    Next
    _ArraySort($categories, 1, 1, $categories[0])

    ; Create category nodes using GUICtrlCreateTreeViewItem, then set icon
    Local $categoryDict = ObjCreate("Scripting.Dictionary")
    For $i = 1 To $categories[0]
        ; Add category node at root
        Local $catNode = GUICtrlCreateTreeViewItem($categories[$i], $treeviewID)
        _GUICtrlTreeView_SetImageIndex(GUICtrlGetHandle($treeviewID), $catNode, $idxFolder)
        $categoryDict($categories[$i]) = $catNode
        $categoryNodeSet($catNode) = True
    Next

    ; Add apps: if no category, add under root; else, under category node, then set icon
    For $i = 0 To UBound($appsData)-1
        Local $category = $appsData[$i][1]
        Local $appNode
        If $category = "" Then
            $appNode = GUICtrlCreateTreeViewItem($appsData[$i][0], $treeviewID)
        Else
            Local $parentNode = $categoryDict($category)
            $appNode = GUICtrlCreateTreeViewItem($appsData[$i][0], $parentNode)
        EndIf
        _GUICtrlTreeView_SetImageIndex(GUICtrlGetHandle($treeviewID), $appNode, $idxApp)
        $appNodeMap($appNode) = $i
    Next

    _GUICtrlTreeView_SetInsertMarkColor(GUICtrlGetHandle($treeviewID), $selectionColor)
    GUISetState(@SW_SHOW, $hGUI)
EndFunc

Section I changed to use the part from the manual:
 

; Create the image list for icons
    Local $hImageList = _GUIImageList_Create(16, 16, 5, 3)
    Local $idxFolder = _GUIImageList_AddIcon($hImageList, "shell32.dll", 4) ; folder icon
    Local $idxApp    = _GUIImageList_AddIcon($hImageList, "shell32.dll", 2)   ; exe icon

But I only get folder icons! What am I doing wrong?

Posted

Here's a test script using it, but I'm still getting folder icons only:
 

#include <GUIConstantsEx.au3>
#include <GuiTreeView.au3>
#include <GuiImageList.au3>
#include <WindowsConstants.au3>

GUICreate("Icon Test", 300, 300)
Local $idTreeView = GUICtrlCreateTreeView(10, 10, 280, 280)
GUISetState(@SW_SHOW)

Local $hImageList = _GUIImageList_Create(16, 16, 5, 3)
Local $idxFolder = _GUIImageList_AddIcon($hImageList, "shell32.dll", 4)
Local $idxApp    = _GUIImageList_AddIcon($hImageList, "shell32.dll", 2)
_GUICtrlTreeView_SetNormalImageList(GUICtrlGetHandle($idTreeView), $hImageList)

Local $catID = GUICtrlCreateTreeViewItem("Category", $idTreeView)
Local $catHandle = _GUICtrlTreeView_GetItemHandle(GUICtrlGetHandle($idTreeView), $catID)
_GUICtrlTreeView_SetImageIndex(GUICtrlGetHandle($idTreeView), $catHandle, $idxFolder)

Local $appID = GUICtrlCreateTreeViewItem("App", $catID)
Local $appHandle = _GUICtrlTreeView_GetItemHandle(GUICtrlGetHandle($idTreeView), $appID)
_GUICtrlTreeView_SetImageIndex(GUICtrlGetHandle($idTreeView), $appHandle, $idxApp)

While 1
    If GUIGetMsg() = $GUI_EVENT_CLOSE Then ExitLoop
WEnd

 

Posted

Is it possible the shell32.dll has changed for windows 11 and the icon index is wrong?
But then, the app/exe only seems to respond to using whatever the first defined icon is, it seems you cannot set a second icon to be used?

Posted
#include <GUIConstants.au3>
#include <GuiTreeView.au3>
#include <GuiImageList.au3>

GUICreate("Icon Test", 300, 300)
Local $idTreeView = GUICtrlCreateTreeView(10, 10, 280, 280)
GUISetState()

Local $hImageList = _GUIImageList_Create(16, 16, 5, 3)
Local $idxFolder = _GUIImageList_AddIcon($hImageList, "shell32.dll", 4)
Local $idxApp    = _GUIImageList_AddIcon($hImageList, "shell32.dll", 2)
_GUICtrlTreeView_SetNormalImageList(GUICtrlGetHandle($idTreeView), $hImageList)

Local $h1 = _GUICtrlTreeView_Add($idTreeView, 0, "Category", $idxFolder)
_GUICtrlTreeView_AddChild($idTreeView, $h1, "App", $idxApp)
While 1
    If GUIGetMsg() = $GUI_EVENT_CLOSE Then ExitLoop
WEnd

Win10.

Posted

Omg! I spent nearly two days trying to get that right! Thank you. What is it that was wrong, is it something simple?

I am on Windows 11 24H2 and was getting just folders only!.

Posted

I have a problem that has originated from your code. When clicking an item it changes the icon from app icon to folder icon instead of launching the desired app, how do i fix that? Thank you.

  • Solution

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

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