Jump to content

Can I set a Right click to select item in tree then show context menu?


Go to solution Solved by Nine,

Recommended Posts

Posted (edited)

Simple as that really. I have a treeview set up in a settings gui. I would like to be able use a right click to auto select the item before proceeding with the context menu and selecting Delete.

Essentially I want to perform these actions on Right clicking a treeview item:

$GUI_EVENT_PRIMARYDOWN then $GUI_EVENT_SECONDARYUP all in one right click action.


I tried this in the handler, but it isn't working:

Case $g_AppMenuDeleteItem
            MouseClick("left")
            Sleep(50)
            _AppTab_DeleteSelected()

Also tried this:
 

; --- Right-click: select item under mouse before showing context menu ---
    If $msg = $GUI_EVENT_SECONDARYDOWN Then
        Local $cursor = GUIGetCursorInfo($hGUI)
        Local $ctrlPos = ControlGetPos($hGUI, "", $g_AppTreeView)
        If IsArray($cursor) And IsArray($ctrlPos) Then
            Local $relX = $cursor[0] - $ctrlPos[0]
            Local $relY = $cursor[1] - $ctrlPos[1]
            Local $item = _GUICtrlTreeView_HitTest($g_AppTreeView, $relX, $relY)
            If $item > 0 Then
                GUICtrlSendMsg($g_AppTreeView, $TVM_SELECTITEM, $TVGN_CARET, $item)
                $g_AppLastSelectedItem = $item
            EndIf
        EndIf
    EndIf

Can't get it to work. Is it even possible?

Thanks for your help. :)

Edited by sl23
Posted

Just tried moving the left click code to a function and calling it, but it refuses to change selection on richt click! there must be a way?
 

Func Tab_App_HandleEvents($msg) 
    Local $selectedItem = GUICtrlRead($g_AppTreeView)
    ; Handle buttons and context menu
    Switch $msg
        Case $g_SaveBtn
            If $g_SelectedIni <> "" And $g_SelectedSection <> "" Then
                _AppTab_SaveFields($g_SelectedIni, $g_SelectedSection)
            EndIf
        Case $g_IgnoreBtn
            _AppTab_IgnoreSelected()
        Case $g_AppMenuDeleteItem
            _AppTab_HandleSelectionChange($selectedItem)
;~          MouseClick("left")
;~          Sleep(50)
            _AppTab_DeleteSelected()
        Case $g_AppFields("SymLinkCreate")
            ; Enable/disable symlinks field based on checkbox
            If GUICtrlRead($g_AppFields("SymLinkCreate")) = $GUI_CHECKED Then
                GUICtrlSetState($g_AppsSymlinksEdit, $GUI_ENABLE)
            Else
                GUICtrlSetState($g_AppsSymlinksEdit, $GUI_DISABLE)
            EndIf
        Case $g_AppFields("Sandboxie")
            ; Enable/disable SandboxName field based on checkbox
            If GUICtrlRead($g_AppFields("Sandboxie")) = $GUI_CHECKED Then
                GUICtrlSetState($g_AppFields("SandboxName"), $GUI_ENABLE)
            Else
                GUICtrlSetState($g_AppFields("SandboxName"), $GUI_DISABLE)
            EndIf
    EndSwitch

    _AppTab_HandleSelectionChange($selectedItem)
;~     ; Only act if the selection changed
;~     If $selectedItem <> $g_AppLastSelectedItem Then
;~         ; If a category node is selected
;~         If $selectedItem <> 0 And $g_AppCategoryTreeItems.Exists($selectedItem) Then
;~             _AppTab_ClearFields()
;~             _AppTab_DisableFields()
;~             GUICtrlSetState($g_SaveBtn, $GUI_DISABLE)
;~             GUICtrlSetState($g_IgnoreBtn, $GUI_DISABLE)
;~             $g_SelectedIni = ""
;~             $g_SelectedSection = ""
;~         ; If last selected was a valid app item and now whitespace/node: disable buttons
;~         ElseIf $g_AppLastSelectedItem <> 0 And $g_AppTreeItemToSection.Exists($g_AppLastSelectedItem) _
;~             And ($selectedItem = 0 Or Not $g_AppTreeItemToSection.Exists($selectedItem)) Then
;~             _AppTab_ClearFields()
;~             _AppTab_Buttons_Disable()
;~             $g_SelectedIni = ""
;~             $g_SelectedSection = ""
;~         ; If selection changed to a valid app item: enable buttons
;~         ElseIf $selectedItem <> 0 And $g_AppTreeItemToSection.Exists($selectedItem) Then
;~             $g_SelectedSection = $g_AppTreeItemToSection.Item($selectedItem)
;~             $g_SelectedIni = $g_AppTreeItemToIni.Item($selectedItem)
;~             _AppTab_PopulateFields($g_SelectedIni, $g_SelectedSection)
;~             _AppTab_EnableFields()
;~             _AppTab_Buttons_Enable()
;~         ; If selection changed to whitespace/node, disable buttons
;~         Else
;~             _AppTab_ClearFields()
;~             _AppTab_Buttons_Disable()
;~             $g_SelectedIni = ""
;~             $g_SelectedSection = ""
;~         EndIf
;~         $g_AppLastSelectedItem = $selectedItem
;~     EndIf
EndFunc

Func _AppTab_HandleSelectionChange($selectedItem)
    If $selectedItem <> $g_AppLastSelectedItem Then
        If $selectedItem <> 0 And $g_AppCategoryTreeItems.Exists($selectedItem) Then
            _AppTab_ClearFields()
            _AppTab_DisableFields()
            GUICtrlSetState($g_SaveBtn, $GUI_DISABLE)
            GUICtrlSetState($g_IgnoreBtn, $GUI_DISABLE)
            $g_SelectedIni = ""
            $g_SelectedSection = ""
        ElseIf $g_AppLastSelectedItem <> 0 And $g_AppTreeItemToSection.Exists($g_AppLastSelectedItem) _
            And ($selectedItem = 0 Or Not $g_AppTreeItemToSection.Exists($selectedItem)) Then
            _AppTab_ClearFields()
            _AppTab_Buttons_Disable()
            $g_SelectedIni = ""
            $g_SelectedSection = ""
        ElseIf $selectedItem <> 0 And $g_AppTreeItemToSection.Exists($selectedItem) Then
            $g_SelectedSection = $g_AppTreeItemToSection.Item($selectedItem)
            $g_SelectedIni = $g_AppTreeItemToIni.Item($selectedItem)
            _AppTab_PopulateFields($g_SelectedIni, $g_SelectedSection)
            _AppTab_EnableFields()
            _AppTab_Buttons_Enable()
        Else
            _AppTab_ClearFields()
            _AppTab_Buttons_Disable()
            $g_SelectedIni = ""
            $g_SelectedSection = ""
        EndIf
        $g_AppLastSelectedItem = $selectedItem
    EndIf
EndFunc

 

Posted (edited)
Local $idTreeview = GUICtrlCreateTreeView(6, 6, 100, 150, BitOR($TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_DISABLEDRAGDROP, $TVS_SHOWSELALWAYS), $WS_EX_CLIENTEDGE)
Local $idButtoncontext = GUICtrlCreateContextMenu($idTreeview)
Local $idMenuDelete = GUICtrlCreateMenuItem("Dellete", $idButtoncontext)

 

Switch $msg
...
    Case $idMenuDelete
        Local $hSelection = _GUICtrlTreeView_GetSelection($idTreeView)
        ConsoleWrite("!Dellete Selection: " & $hSelection & @CRLF)
        ;_GUICtrlTreeView_Delete($idTreeView, $hSelection)

 

Edited by ioa747

I know that I know nothing

Posted (edited)

Thanks, but isn't that just a standard context menu creation for a TreeView?

I need it to select first then show the context menu for delete. I currently have the context menu and it works fine. Everything is working ok. I just needed to enhance the right click by first selecting the item with the right mouse button, then open the context menu.

is that possible?
Ah, sorry i posted while you were in the middle of updating your post!
Seems that is not what I wanted either actually. Thank you anyway.

As you can see it is probably a niche thing? I have a tree view with category containing items. If I select a category then right click an item I am asked if I want to delete the category, which is a mistake and could be a major problem! So I wondered if it were possible to do the selction on right click before showing the context menu?

Edited by sl23
Posted (edited)

Here is a test code. in order to test correctly, follow these instructions to see the error in the handling of the selection:
1. Run the script and select the Category using left mouse button to open it.
2. Right click any node and select delete.

Notice that the message is asking if you want to delete the category, not the node you right clicked on!

So what I am asking is, can I basically perform a left-click action using the right mouse click?
This would then:
1. select the item.
2. Open the context menu.
3. selecting delete would then delete the correct item.

Test code:
 

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

$hGUI = GUICreate("TreeView Category Right Click Select Example", 300, 220)
$idTreeview = GUICtrlCreateTreeView(10, 10, 150, 180, BitOR($TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_SHOWSELALWAYS))
$idMenu = GUICtrlCreateContextMenu($idTreeview)
$idMenuDelete = GUICtrlCreateMenuItem("Delete", $idMenu)

; Add category node(s)
Global $aCategories[1]
$aCategories[0] = GUICtrlCreateTreeViewItem("Category 1", $idTreeview)

; Add child nodes under category
$idItem1 = GUICtrlCreateTreeViewItem("Node 1", $aCategories[0])
$idItem2 = GUICtrlCreateTreeViewItem("Node 2", $aCategories[0])
$idItem3 = GUICtrlCreateTreeViewItem("Node 3", $aCategories[0])

GUISetState()

While 1
    $msg = GUIGetMsg()
    
    ; --- Hit-test for right mouse button down ---
    If $msg = $GUI_EVENT_SECONDARYDOWN Then
        $aCursor = GUIGetCursorInfo($hGUI)
        $aPos = ControlGetPos($hGUI, "", $idTreeview)
        If IsArray($aCursor) And IsArray($aPos) Then
            $relX = $aCursor[0] - $aPos[0]
            $relY = $aCursor[1] - $aPos[1]
            $itemID = _GUICtrlTreeView_HitTest($idTreeview, $relX, $relY)
            If $itemID > 0 Then
                GUICtrlSendMsg($idTreeview, 0x110B, 0x9, $itemID) ; Select the node under mouse
            EndIf
        EndIf
    EndIf
    
    Switch $msg
        Case $idMenuDelete
            $hSelection = _GUICtrlTreeView_GetSelection($idTreeview)
            ; Get the text of the selected node for confirmation
            Local $sNodeText = _GUICtrlTreeView_GetText($idTreeview, $hSelection)
            ; Check if selection is a category node (parent)
            Local $isCategory = False
            For $i = 0 To UBound($aCategories) - 1
                If $hSelection = $aCategories[$i] Then
                    $isCategory = True
                    ExitLoop
                EndIf
            Next
            If $isCategory Then
                ; Confirm category deletion
                Local $answer = MsgBox(36, "Delete Category", "Are you sure you want to delete the CATEGORY: """ & $sNodeText & """ and all its items?")
                If $answer = 6 Then ; Yes
                    _GUICtrlTreeView_Delete($idTreeview, $hSelection)
                EndIf
            ElseIf $hSelection <> 0 Then
                ; Confirm node deletion
                Local $answer = MsgBox(36, "Delete Node", "Are you sure you want to delete the NODE: """ & $sNodeText & """?")
                If $answer = 6 Then ; Yes
                    _GUICtrlTreeView_Delete($idTreeview, $hSelection)
                EndIf
            EndIf
        Case $GUI_EVENT_CLOSE
            ExitLoop
    EndSwitch
WEnd

GUIDelete()

Why is it that this doesn't work: MouseClick($MOUSE_CLICK_LEFT)

Edited by sl23
  • Solution
Posted (edited)

Here my take on it :

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

Opt("MustDeclareVars", True)

Global $itemID

Example()

Func Example()
  Local $hGUI = GUICreate("TreeView Category Right Click Select Example", 300, 220)
  Local $idTreeview = GUICtrlCreateTreeView(10, 10, 150, 180, BitOR($TVS_HASBUTTONS, $TVS_HASLINES, $TVS_LINESATROOT, $TVS_SHOWSELALWAYS))
  Local $idMenu = GUICtrlCreateContextMenu($idTreeview)
  Local $idMenuDelete = GUICtrlCreateMenuItem("Delete", $idMenu)

  ; Add category node(s)
  Local $aCategories[1]
  $aCategories[0] = GUICtrlCreateTreeViewItem("Category 1", $idTreeview)

  ; Add child nodes under category
  Local $idItem1 = GUICtrlCreateTreeViewItem("Node 1", $aCategories[0])
  Local $idItem2 = GUICtrlCreateTreeViewItem("Node 2", $aCategories[0])
  Local $idItem3 = GUICtrlCreateTreeViewItem("Node 3", $aCategories[0])

  GUISetState()
  GUIRegisterMsg($WM_NOTIFY, WM_NOTIFY)

  Local $sNodeText, $isCategory, $answer

  While True
    Switch GUIGetMsg()
      Case $GUI_EVENT_CLOSE
        ExitLoop
      Case $idMenuDelete
        $sNodeText = _GUICtrlTreeView_GetText($idTreeview, $itemID)
        ; Check if selection is a category node (parent)
        $isCategory = False
        For $i = 0 To UBound($aCategories) - 1
          If $itemID = _GUICtrlTreeView_GetItemHandle($idTreeview, $aCategories[$i]) Then
            $isCategory = True
            ExitLoop
          EndIf
        Next
        If $isCategory Then
          ; Confirm category deletion
          $answer = MsgBox(36, "Delete Category", "Are you sure you want to delete the CATEGORY: """ & $sNodeText & """ and all its items?")
        Else
          ; Confirm node deletion
          $answer = MsgBox(36, "Delete Node", "Are you sure you want to delete the NODE: """ & $sNodeText & """?")
        EndIf
        If $answer = 6 Then _GUICtrlTreeView_Delete($idTreeview, $itemID) ; Yes
    EndSwitch
  WEnd
EndFunc   ;==>Example

Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
  Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
  If $tNMHDR.code <> $NM_RCLICK Then Return $GUI_RUNDEFMSG
  Local $cursor = GUIGetCursorInfo($hWnd)
  Local $ctrlPos = ControlGetPos($hWnd, "", $tNMHDR.idFrom)

  Local $relX = $cursor[0] - $ctrlPos[0]
  Local $relY = $cursor[1] - $ctrlPos[1]
  $itemID = _GUICtrlTreeView_HitTestItem($wParam, $relX, $relY)
  If Not $itemID Then Return 1
  Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_NOTIFY

 

Edited by Nine
Posted

That test script does exactly what I was hoping, but for the life of me, I just cannot get it running correctly in my script! It either doesn't work, or it breaks some other functionality!
Here's the before script:

; Tab_Apps.au3

#include-once
#include <GuiTreeView.au3>
#include <File.au3>
#include <Array.au3>

Global $g_CategoryIniDir = @ScriptDir & "\App"
Global $g_AppTreeView, $g_AppFields, $g_IniFiles, $g_AppSections, $g_SelectedIni, $g_SelectedSection
Global $g_SaveBtn, $g_IgnoreBtn, $g_RefreshBtn
Global $g_AppTreeItemToSection = ObjCreate("Scripting.Dictionary")
Global $g_AppTreeItemToIni = ObjCreate("Scripting.Dictionary")
Global $g_AppLastSelectedItem = 0
Global $guiW, $guiH, $listviewX, $listviewY, $btnW, $btnH, $footer_gap, $hGUI
Global $g_AppCategoryTreeItems = ObjCreate("Scripting.Dictionary")
Global $g_AppsSymlinksEdit
Global Const $ES_MULTILINE = 0x0004
Global Const $ES_WANTRETURN = 0x1000
Global $g_AppTreeViewMenu, $g_AppMenuDeleteItem

Func Tab_Apps_Create($hGUI, $hTab)
    GUICtrlCreateTabItem("Apps")
    Local $x = 290

    ; Right side fields container
    $g_AppFields = ObjCreate("Scripting.Dictionary")
    Local $fields = ["ButtonText", "RunFile", "RunAsAdmin", "WorkDir", "Arguments", "SingleInstance", "Sandboxie", "SandboxName", "Category", "Fave", "Hide", "SymLinkCreate"]
    For $i = 0 To UBound($fields) - 1
        GUICtrlCreateLabel($fields[$i] & ":", $x, 60 + $i * 27, 70, 18)
        If StringInStr("RunAsAdmin SingleInstance Sandboxie SymLinkCreate Fave Hide", $fields[$i]) Then
            $g_AppFields($fields[$i]) = GUICtrlCreateCheckbox("", $x + 90, 56 + $i * 27, 18, 18)
        Else
            $g_AppFields($fields[$i]) = GUICtrlCreateInput("", $x + 90, 56 + $i * 27, 250, 18)
        EndIf
    Next

    ; Add Symlinks multiline edit field below regular fields
    Local $symlinkY = 60 + UBound($fields) * 27
    GUICtrlCreateLabel("Symlinks:", $x, $symlinkY, 70, 18)
    $g_AppsSymlinksEdit = GUICtrlCreateEdit("", $x + 90, $symlinkY, 250, 130, BitOR($ES_MULTILINE, $ES_WANTRETURN))

    $g_AppTreeView = GUICtrlCreateTreeView($listviewX, $listviewY, $x + 90, $guiH-155, BitOR($TVS_HASBUTTONS, $TVS_LINESATROOT, $TVS_SHOWSELALWAYS))
    $g_IgnoreBtn = GUICtrlCreateButton("🚫", $x + 10, 440, $btnH, $btnH)
    GUICtrlSetTip($g_IgnoreBtn, "Add to Ignore List and Delete.")
    $g_SaveBtn = GUICtrlCreateButton("💾", $x + 40, 440, $btnH, $btnH)
    GUICtrlSetTip($g_SaveBtn, "Save before selecting items in Tree.")
    $g_RefreshBtn = GUICtrlCreateButton("🔄", $x + 70, 440, $btnH, $btnH)
    GUICtrlSetTip($g_RefreshBtn, "Reload GUI to update Tree")

    ; Create context menu for TreeView and delete item
    $g_AppTreeViewMenu = GUICtrlCreateContextMenu($g_AppTreeView)
    $g_AppMenuDeleteItem = GUICtrlCreateMenuItem("Delete", $g_AppTreeViewMenu)

    ; Disable all buttons on startup
    _AppTab_Buttons_Disable()
    _AppTab_LoadIniFilesAndApps()
    _AppTab_DisableFields()
EndFunc

Func _AppTab_LoadIniFilesAndApps()
    Global $g_AppSections
    $g_AppSections = ObjCreate("Scripting.Dictionary")
    $g_AppTreeItemToSection = ObjCreate("Scripting.Dictionary")
    $g_AppTreeItemToIni = ObjCreate("Scripting.Dictionary")
    $g_AppCategoryTreeItems = ObjCreate("Scripting.Dictionary")
    $g_IniFiles = _FileListToArray($g_CategoryIniDir, "*.ini", 1)
    If @error Or Not IsArray($g_IniFiles) Then Return

    ; Clear previous tree nodes
    GUICtrlDelete($g_AppTreeView)
    $g_AppTreeView = GUICtrlCreateTreeView($listviewX, $listviewY, 250, $guiH-155, BitOR($TVS_HASBUTTONS, $TVS_LINESATROOT, $TVS_SHOWSELALWAYS))

    ; Re-create context menu after deletion
    $g_AppTreeViewMenu = GUICtrlCreateContextMenu($g_AppTreeView)
    $g_AppMenuDeleteItem = GUICtrlCreateMenuItem("Delete", $g_AppTreeViewMenu)

    Local $favourites = ObjCreate("Scripting.Dictionary")
    Local $categories = ObjCreate("Scripting.Dictionary")
    For $f = 1 To $g_IniFiles[0]
        Local $file = $g_IniFiles[$f]
        Local $fileLower = StringLower($file)
        If $fileLower = "settings.ini" Or $fileLower = "ignorelist.ini" Then ContinueLoop

        Local $iniFile = $g_CategoryIniDir & "\" & $file
        Local $sections = IniReadSectionNames($iniFile)
        If Not IsArray($sections) Then ContinueLoop
        For $i = 1 To $sections[0]
            Local $appName = $sections[$i]
            Local $category = IniRead($iniFile, $appName, "Category", "")
            If $category = "" Then $category = "Uncategorized"
            Local $hide = IniRead($iniFile, $appName, "Hide", "0")
            Local $fave = IniRead($iniFile, $appName, "Fave", "0")

            If $fave = "1" Then
                $favourites.Add($appName, $iniFile)
            EndIf

            If Not $categories.Exists($category) Then
                $categories.Add($category, ObjCreate("Scripting.Dictionary"))
            EndIf
            $categories.Item($category).Add($appName, $iniFile)
        Next
    Next

    ; Add Favourites node (alphabetical)
    If $favourites.Count > 0 Then
        Local $favNode = GUICtrlCreateTreeViewItem("Favourites", $g_AppTreeView)
        ; Collect and sort favourite app names
        Local $faveNames[0]
        For $appName In $favourites.Keys
            _ArrayAdd($faveNames, $appName)
        Next
        _ArraySort($faveNames)
        For $i = 0 To UBound($faveNames) - 1
            Local $appName = $faveNames[$i]
            Local $iniFile = $favourites.Item($appName)
            Local $hide = IniRead($iniFile, $appName, "Hide", "0")
            Local $buttonText = IniRead($iniFile, $appName, "ButtonText", $appName)
            Local $label = $buttonText
            If $hide = "1" Then $label = "(H) " & $buttonText
            Local $appNode = GUICtrlCreateTreeViewItem($label, $favNode)
            If Not $g_AppSections.Exists($appName) Then
                $g_AppSections.Add($appName, $iniFile)
            Else
                $g_AppSections.Item($appName) = $iniFile ; Or update, or skip
            EndIf
            $g_AppTreeItemToSection.Add($appNode, $appName)
            $g_AppTreeItemToIni.Add($appNode, $iniFile)
        Next
    EndIf

    ; Add categories (alphabetical)
    Local $catNames[0]
    For $category In $categories.Keys
        _ArrayAdd($catNames, $category)
    Next
    _ArraySort($catNames)
    For $i = 0 To UBound($catNames) - 1
        Local $category = $catNames[$i]
        Local $catNode = GUICtrlCreateTreeViewItem($category, $g_AppTreeView)
        $g_AppCategoryTreeItems.Add($catNode, $category)
        ; Collect and sort app names in this category
        Local $appNames[0]
        For $appName In $categories.Item($category).Keys
            _ArrayAdd($appNames, $appName)
        Next
        _ArraySort($appNames)
        For $j = 0 To UBound($appNames) - 1
            Local $appName = $appNames[$j]
            Local $iniFile = $categories.Item($category).Item($appName)
            Local $hide = IniRead($iniFile, $appName, "Hide", "0")
            Local $fave = IniRead($iniFile, $appName, "Fave", "0")
            ; If Fave=1, already added above
            If $fave = "1" Then ContinueLoop
            Local $buttonText = IniRead($iniFile, $appName, "ButtonText", $appName)
            Local $label = $buttonText
            If $hide = "1" Then $label = "(H) " & $buttonText
            Local $appNode = GUICtrlCreateTreeViewItem($label, $catNode)
            If Not $g_AppSections.Exists($appName) Then
                $g_AppSections.Add($appName, $iniFile)
            Else
                $g_AppSections.Item($appName) = $iniFile ; Or update, or skip
            EndIf
            $g_AppTreeItemToSection.Add($appNode, $appName)
            $g_AppTreeItemToIni.Add($appNode, $iniFile)
        Next
    Next
EndFunc

; Enable all buttons (Ignore, Save)
Func _AppTab_Buttons_Enable()
    GUICtrlSetState($g_IgnoreBtn, $GUI_ENABLE)
    GUICtrlSetState($g_SaveBtn, $GUI_ENABLE)
EndFunc

; Disable all buttons (Ignore, Save)
Func _AppTab_Buttons_Disable()
    GUICtrlSetState($g_IgnoreBtn, $GUI_DISABLE)
    GUICtrlSetState($g_SaveBtn, $GUI_DISABLE)
EndFunc

Func Tab_App_HandleEvents($msg)
    ; Handle buttons and context menu
    Switch $msg
        Case $g_SaveBtn
            If $g_SelectedIni <> "" And $g_SelectedSection <> "" Then
                _AppTab_SaveFields($g_SelectedIni, $g_SelectedSection)
;~              _AppTab_LoadIniFilesAndApps()
            EndIf
        Case $g_IgnoreBtn
            _AppTab_IgnoreSelected()
        Case $g_RefreshBtn
            GUIDelete($hGUI)
            ShowSettingsGUI()
        Case $g_AppMenuDeleteItem
            _AppTab_DeleteSelected()
        Case $g_AppFields("SymLinkCreate")
            ; Enable/disable symlinks field based on checkbox
            If GUICtrlRead($g_AppFields("SymLinkCreate")) = $GUI_CHECKED Then
                GUICtrlSetState($g_AppsSymlinksEdit, $GUI_ENABLE)
            Else
                GUICtrlSetState($g_AppsSymlinksEdit, $GUI_DISABLE)
            EndIf
        Case $g_AppFields("Sandboxie")
            ; Enable/disable SandboxName field based on checkbox
            If GUICtrlRead($g_AppFields("Sandboxie")) = $GUI_CHECKED Then
                GUICtrlSetState($g_AppFields("SandboxName"), $GUI_ENABLE)
            Else
                GUICtrlSetState($g_AppFields("SandboxName"), $GUI_DISABLE)
            EndIf
    EndSwitch

    Local $selectedItem = GUICtrlRead($g_AppTreeView)
    ; Only act if the selection changed
    If $selectedItem <> $g_AppLastSelectedItem Then
        ; If a category node is selected
        If $selectedItem <> 0 And $g_AppCategoryTreeItems.Exists($selectedItem) Then
            _AppTab_ClearFields()
            _AppTab_DisableFields()
            GUICtrlSetState($g_SaveBtn, $GUI_DISABLE)
            GUICtrlSetState($g_IgnoreBtn, $GUI_DISABLE)
            $g_SelectedIni = ""
            $g_SelectedSection = ""
        ; If last selected was a valid app item and now whitespace/node: disable buttons
        ElseIf $g_AppLastSelectedItem <> 0 And $g_AppTreeItemToSection.Exists($g_AppLastSelectedItem) _
            And ($selectedItem = 0 Or Not $g_AppTreeItemToSection.Exists($selectedItem)) Then
            _AppTab_ClearFields()
            _AppTab_Buttons_Disable()
            $g_SelectedIni = ""
            $g_SelectedSection = ""
        ; If selection changed to a valid app item: enable buttons
        ElseIf $selectedItem <> 0 And $g_AppTreeItemToSection.Exists($selectedItem) Then
            $g_SelectedSection = $g_AppTreeItemToSection.Item($selectedItem)
            $g_SelectedIni = $g_AppTreeItemToIni.Item($selectedItem)
            _AppTab_PopulateFields($g_SelectedIni, $g_SelectedSection)
            _AppTab_EnableFields()
            _AppTab_Buttons_Enable()
        ; If selection changed to whitespace/node, disable buttons
        Else
            _AppTab_ClearFields()
            _AppTab_Buttons_Disable()
            $g_SelectedIni = ""
            $g_SelectedSection = ""
        EndIf
        $g_AppLastSelectedItem = $selectedItem
    EndIf
EndFunc

Func _AppTab_PopulateFields($iniFile, $section)
    For $key In $g_AppFields.Keys()
        Local $val = IniRead($iniFile, $section, $key, "")
        If StringInStr("RunAsAdmin SingleInstance Sandboxie SymLinkCreate Fave Hide", $key) Then
            GUICtrlSetState($g_AppFields($key), ($val = "1") ? $GUI_CHECKED : $GUI_UNCHECKED)
        Else
            GUICtrlSetData($g_AppFields($key), $val)
        EndIf
        GUICtrlSetState($g_AppFields($key), $GUI_ENABLE)
    Next
    ; Populate symlinks multiline edit
    Local $symlinks = ""
    Local $idx = 1
    While 1
        Local $val = IniRead($iniFile, $section, "Symlink" & $idx, "")
        If $val = "" Then ExitLoop
        $symlinks &= $val & @CRLF
        $idx += 1
    WEnd
    GUICtrlSetData($g_AppsSymlinksEdit, StringTrimRight($symlinks, 2))
    ; Enable/disable symlinks field according to SymLinkCreate checkbox
    If GUICtrlRead($g_AppFields("SymLinkCreate")) = $GUI_CHECKED Then
        GUICtrlSetState($g_AppsSymlinksEdit, $GUI_ENABLE)
    Else
        GUICtrlSetState($g_AppsSymlinksEdit, $GUI_DISABLE)
    EndIf
    ; Enable/disable SandboxName field according to Sandboxie checkbox
    If GUICtrlRead($g_AppFields("Sandboxie")) = $GUI_CHECKED Then
        GUICtrlSetState($g_AppFields("SandboxName"), $GUI_ENABLE)
    Else
        GUICtrlSetState($g_AppFields("SandboxName"), $GUI_DISABLE)
    EndIf
EndFunc

Func _AppTab_SaveFields($iniFile, $section)
    For $key In $g_AppFields.Keys()
        If StringInStr("RunAsAdmin SingleInstance Sandboxie SymLinkCreate Fave Hide", $key) Then
            Local $v = (GUICtrlRead($g_AppFields($key)) = $GUI_CHECKED) ? "1" : "0"
            IniWrite($iniFile, $section, $key, $v)
        Else
            IniWrite($iniFile, $section, $key, GUICtrlRead($g_AppFields($key)))
        EndIf
        GUICtrlSetState($g_AppFields($key), $GUI_DISABLE)
    Next
    ; Save symlinks from multiline edit as Symlink1=..., Symlink2=..., ...
    Local $symlinksText = GUICtrlRead($g_AppsSymlinksEdit)
    Local $symlinksArr = StringSplit(StringStripCR($symlinksText), @LF)
    ; First, delete any old SymlinkX keys
    Local $idx = 1
    While 1
        Local $exists = IniRead($iniFile, $section, "Symlink" & $idx, "")
        If $exists = "" Then ExitLoop
        IniDelete($iniFile, $section, "Symlink" & $idx)
        $idx += 1
    WEnd
    ; Write new symlinks
    If $symlinksArr[0] > 0 Then
        For $i = 1 To $symlinksArr[0]
            If StringStripWS($symlinksArr[$i], 3) <> "" Then
                IniWrite($iniFile, $section, "Symlink" & $i, $symlinksArr[$i])
            EndIf
        Next
    EndIf
    GUICtrlSetState($g_AppsSymlinksEdit, $GUI_DISABLE)
    ; Disable SandboxName field after save
    GUICtrlSetState($g_AppFields("SandboxName"), $GUI_DISABLE)
EndFunc

Func _AppTab_ClearFields()
    For $key In $g_AppFields.Keys()
        If StringInStr("RunAsAdmin SingleInstance Sandboxie SymLinkCreate Fave Hide", $key) Then
            GUICtrlSetState($g_AppFields($key), $GUI_UNCHECKED)
        Else
            GUICtrlSetData($g_AppFields($key), "")
        EndIf
        GUICtrlSetState($g_AppFields($key), $GUI_ENABLE)
    Next
    GUICtrlSetData($g_AppsSymlinksEdit, "")
    ; Enable/disable Symlinks field according to SymLinkCreate checkbox
    If GUICtrlRead($g_AppFields("SymLinkCreate")) = $GUI_CHECKED Then
        GUICtrlSetState($g_AppsSymlinksEdit, $GUI_ENABLE)
    Else
        GUICtrlSetState($g_AppsSymlinksEdit, $GUI_DISABLE)
    EndIf
    ; Enable/disable SandboxName field according to Sandboxie checkbox
    If GUICtrlRead($g_AppFields("Sandboxie")) = $GUI_CHECKED Then
        GUICtrlSetState($g_AppFields("SandboxName"), $GUI_ENABLE)
    Else
        GUICtrlSetState($g_AppFields("SandboxName"), $GUI_DISABLE)
    EndIf
EndFunc

Func _AppTab_EnableFields()
    For $key In $g_AppFields.Keys()
        GUICtrlSetState($g_AppFields($key), $GUI_ENABLE)
    Next
    ; Enable/disable symlinks field according to SymLinkCreate
    If GUICtrlRead($g_AppFields("SymLinkCreate")) = $GUI_CHECKED Then
        GUICtrlSetState($g_AppsSymlinksEdit, $GUI_ENABLE)
    Else
        GUICtrlSetState($g_AppsSymlinksEdit, $GUI_DISABLE)
    EndIf
    ; Enable/disable SandboxName field according to Sandboxie checkbox
    If GUICtrlRead($g_AppFields("Sandboxie")) = $GUI_CHECKED Then
        GUICtrlSetState($g_AppFields("SandboxName"), $GUI_ENABLE)
    Else
        GUICtrlSetState($g_AppFields("SandboxName"), $GUI_DISABLE)
    EndIf
EndFunc

Func _AppTab_DisableFields()
    For $key In $g_AppFields.Keys()
        GUICtrlSetState($g_AppFields($key), $GUI_DISABLE)
    Next
    GUICtrlSetState($g_AppsSymlinksEdit, $GUI_DISABLE)
    GUICtrlSetState($g_AppFields("SandboxName"), $GUI_DISABLE)
EndFunc

Func _AppTab_DeleteSelected($skipConfirm = False)
    Local $selectedItem = GUICtrlRead($g_AppTreeView)
    If $selectedItem = 0 Then Return

    ; --- CATEGORY NODE DELETION ---
    If $g_AppCategoryTreeItems.Exists($selectedItem) Then
        Local $category = $g_AppCategoryTreeItems.Item($selectedItem)
        Local $iniFile = $g_CategoryIniDir & "\" & $category & ".ini"
        ; SAFEGUARD: Never delete Settings.ini or IgnoreList.ini
        If StringInStr(StringLower($iniFile), "settings.ini") Or StringInStr(StringLower($iniFile), "ignorelist.ini") Then Return

        If Not $skipConfirm Then
            Local $answer = MsgBox(1 + 32, "Delete Category", _
                "Are you sure you want to delete the entire category '" & $category & "'?" & @CRLF & _
                "This will remove the INI file (" & $category & ".ini) and all its apps." & @CRLF & _
                "This cannot be undone.", 0)
            If $answer <> 1 Then Return
        EndIf

        If FileExists($iniFile) Then FileDelete($iniFile)
        GUICtrlDelete($selectedItem)
        $g_AppCategoryTreeItems.Remove($selectedItem)
        $g_SelectedIni = ""
        $g_SelectedSection = ""
        $g_AppLastSelectedItem = 0
        _AppTab_ClearFields()
        _AppTab_Buttons_Disable()
        Return
    EndIf

    ; --- APP NODE DELETION ---
    If Not $g_AppTreeItemToSection.Exists($selectedItem) Then Return

    Local $appName = $g_AppTreeItemToSection.Item($selectedItem)
    Local $iniFile = $g_AppSections.Item($appName)
    ; SAFEGUARD: Never delete from Settings.ini
    If StringInStr(StringLower($iniFile), "settings.ini") Then Return

    If Not $skipConfirm Then
        Local $answer = MsgBox(1 + 32, "Delete App", "Are you sure you want to delete '" & $appName & "'?" & @CRLF & "This cannot be undone.", 0)
        If $answer <> 1 Then Return
    EndIf

    IniDelete($iniFile, $appName)
    GUICtrlDelete($selectedItem)
    $g_AppSections.Remove($appName)
    $g_AppTreeItemToSection.Remove($selectedItem)
    $g_AppTreeItemToIni.Remove($selectedItem)
    _AppTab_ClearFields()
    $g_SelectedIni = ""
    $g_SelectedSection = ""
    $g_AppLastSelectedItem = 0
    _AppTab_Buttons_Disable()
EndFunc

Func _AppTab_IgnoreSelected()
    Local $selectedItem = GUICtrlRead($g_AppTreeView)
    If $selectedItem = 0 Or Not $g_AppTreeItemToSection.Exists($selectedItem) Then Return

    Local $appName = $g_AppTreeItemToSection.Item($selectedItem)
    Local $iniFile = $g_AppSections.Item($appName)
    ; SAFEGUARD: Never ignore from Settings.ini
    If StringInStr(StringLower($iniFile), "settings.ini") Then Return

    ; Get the RunFile (path) to ignore
    Local $ignorePath = IniRead($iniFile, $appName, "RunFile", "")
    If $ignorePath = "" Then Return

    ; --- Custom Ignore Confirmation ---
    Local $answer = MsgBox(1 + 32, "Ignore App", _
        "Confirm adding '" & $appName & "' (" & $ignorePath & ") to the Ignore List and delete from App list." & @CRLF & _
        @CRLF & _
        "You can restore ignored apps via the Ignore tab.", 0)
    If $answer <> 1 Then Return

    ; Add the path to IgnoreList.ini if not already present
    Local $ignoreIni = @ScriptDir & "\App\IgnoreList.ini"
    Local $ignoreArr = FileReadToArray($ignoreIni)
    Local $exists = False
    If IsArray($ignoreArr) Then
        For $i = 0 To UBound($ignoreArr) - 1
            If StringStripWS($ignoreArr[$i], 3) = $ignorePath Then
                $exists = True
                ExitLoop
            EndIf
        Next
    EndIf
    If Not $exists Then
        Local $hFile = FileOpen($ignoreIni, $FO_APPEND)
        If $hFile <> -1 Then
            FileWriteLine($hFile, $ignorePath)
            FileClose($hFile)
        EndIf
    EndIf

    ; Delete from Apps tab (calls delete logic)
    _AppTab_DeleteSelected(True)
    ; Update Ignore tab view
    GUICtrlSetState($g_IgnoreEdit, $GUI_ENABLE)
    _Ignore_EditPopulate()
    GUICtrlSetState($g_IgnoreEdit, $GUI_DISABLE)
EndFunc

Here's the after script:

; Tab_Apps.au3

#include-once
#include <GuiTreeView.au3>
#include <File.au3>
#include <Array.au3>

Global $g_CategoryIniDir = @ScriptDir & "\App"
Global $g_AppTreeView, $g_AppFields, $g_IniFiles, $g_AppSections, $g_SelectedIni, $g_SelectedSection
Global $g_SaveBtn, $g_IgnoreBtn, $g_RefreshBtn
Global $g_AppTreeItemToSection = ObjCreate("Scripting.Dictionary")
Global $g_AppTreeItemToIni = ObjCreate("Scripting.Dictionary")
Global $g_AppLastSelectedItem = 0
Global $guiW, $guiH, $listviewX, $listviewY, $btnW, $btnH, $footer_gap, $hGUI
Global $g_AppCategoryTreeItems = ObjCreate("Scripting.Dictionary")
Global $g_AppsSymlinksEdit
Global Const $ES_MULTILINE = 0x0004
Global Const $ES_WANTRETURN = 0x1000
Global $g_AppTreeViewMenu, $g_AppMenuDeleteItem

Func Tab_Apps_Create($hGUI, $hTab)
    GUICtrlCreateTabItem("Apps")
    Local $x = 290

    ; Right side fields container
    $g_AppFields = ObjCreate("Scripting.Dictionary")
    Local $fields = ["ButtonText", "RunFile", "RunAsAdmin", "WorkDir", "Arguments", "SingleInstance", "Sandboxie", "SandboxName", "Category", "Fave", "Hide", "SymLinkCreate"]
    For $i = 0 To UBound($fields) - 1
        GUICtrlCreateLabel($fields[$i] & ":", $x, 60 + $i * 27, 70, 18)
        If StringInStr("RunAsAdmin SingleInstance Sandboxie SymLinkCreate Fave Hide", $fields[$i]) Then
            $g_AppFields($fields[$i]) = GUICtrlCreateCheckbox("", $x + 90, 56 + $i * 27, 18, 18)
        Else
            $g_AppFields($fields[$i]) = GUICtrlCreateInput("", $x + 90, 56 + $i * 27, 250, 18)
        EndIf
    Next

    ; Add Symlinks multiline edit field below regular fields
    Local $symlinkY = 60 + UBound($fields) * 27
    GUICtrlCreateLabel("Symlinks:", $x, $symlinkY, 70, 18)
    $g_AppsSymlinksEdit = GUICtrlCreateEdit("", $x + 90, $symlinkY, 250, 130, BitOR($ES_MULTILINE, $ES_WANTRETURN))

    $g_AppTreeView = GUICtrlCreateTreeView($listviewX, $listviewY, $x + 90, $guiH-155, BitOR($TVS_HASBUTTONS, $TVS_LINESATROOT, $TVS_SHOWSELALWAYS))
    $g_IgnoreBtn = GUICtrlCreateButton("🚫", $x + 10, 440, $btnH, $btnH)
    GUICtrlSetTip($g_IgnoreBtn, "Add to Ignore List and Delete.")
    $g_SaveBtn = GUICtrlCreateButton("💾", $x + 40, 440, $btnH, $btnH)
    GUICtrlSetTip($g_SaveBtn, "Save before selecting items in Tree.")
    $g_RefreshBtn = GUICtrlCreateButton("🔄", $x + 70, 440, $btnH, $btnH)
    GUICtrlSetTip($g_RefreshBtn, "Reload GUI to update Tree")

    ; Create context menu for TreeView and delete item
    $g_AppTreeViewMenu = GUICtrlCreateContextMenu($g_AppTreeView)
    $g_AppMenuDeleteItem = GUICtrlCreateMenuItem("Delete", $g_AppTreeViewMenu)

    ; Disable all buttons on startup
    GUIRegisterMsg($WM_NOTIFY, Tab_Apps_WM_NOTIFY)
    _AppTab_Buttons_Disable()
    _AppTab_LoadIniFilesAndApps()
    _AppTab_DisableFields()
EndFunc

Func _AppTab_LoadIniFilesAndApps()
    Global $g_AppSections
    $g_AppSections = ObjCreate("Scripting.Dictionary")
    $g_AppTreeItemToSection = ObjCreate("Scripting.Dictionary")
    $g_AppTreeItemToIni = ObjCreate("Scripting.Dictionary")
    $g_AppCategoryTreeItems = ObjCreate("Scripting.Dictionary")
    $g_IniFiles = _FileListToArray($g_CategoryIniDir, "*.ini", 1)
    If @error Or Not IsArray($g_IniFiles) Then Return

    ; Clear previous tree nodes
    GUICtrlDelete($g_AppTreeView)
    $g_AppTreeView = GUICtrlCreateTreeView($listviewX, $listviewY, 250, $guiH-155, BitOR($TVS_HASBUTTONS, $TVS_LINESATROOT, $TVS_SHOWSELALWAYS))

    ; Re-create context menu after deletion
    $g_AppTreeViewMenu = GUICtrlCreateContextMenu($g_AppTreeView)
    $g_AppMenuDeleteItem = GUICtrlCreateMenuItem("Delete", $g_AppTreeViewMenu)

    Local $favourites = ObjCreate("Scripting.Dictionary")
    Local $categories = ObjCreate("Scripting.Dictionary")
    For $f = 1 To $g_IniFiles[0]
        Local $file = $g_IniFiles[$f]
        Local $fileLower = StringLower($file)
        If $fileLower = "settings.ini" Or $fileLower = "ignorelist.ini" Then ContinueLoop

        Local $iniFile = $g_CategoryIniDir & "\" & $file
        Local $sections = IniReadSectionNames($iniFile)
        If Not IsArray($sections) Then ContinueLoop
        For $i = 1 To $sections[0]
            Local $appName = $sections[$i]
            Local $category = IniRead($iniFile, $appName, "Category", "")
            If $category = "" Then $category = "Uncategorized"
            Local $hide = IniRead($iniFile, $appName, "Hide", "0")
            Local $fave = IniRead($iniFile, $appName, "Fave", "0")

            If $fave = "1" Then
                $favourites.Add($appName, $iniFile)
            EndIf

            If Not $categories.Exists($category) Then
                $categories.Add($category, ObjCreate("Scripting.Dictionary"))
            EndIf
            $categories.Item($category).Add($appName, $iniFile)
        Next
    Next

    ; Add Favourites node (alphabetical)
    If $favourites.Count > 0 Then
        Local $favNode = GUICtrlCreateTreeViewItem("Favourites", $g_AppTreeView)
        ; Collect and sort favourite app names
        Local $faveNames[0]
        For $appName In $favourites.Keys
            _ArrayAdd($faveNames, $appName)
        Next
        _ArraySort($faveNames)
        For $i = 0 To UBound($faveNames) - 1
            Local $appName = $faveNames[$i]
            Local $iniFile = $favourites.Item($appName)
            Local $hide = IniRead($iniFile, $appName, "Hide", "0")
            Local $buttonText = IniRead($iniFile, $appName, "ButtonText", $appName)
            Local $label = $buttonText
            If $hide = "1" Then $label = "(H) " & $buttonText
            Local $appNode = GUICtrlCreateTreeViewItem($label, $favNode)
            If Not $g_AppSections.Exists($appName) Then
                $g_AppSections.Add($appName, $iniFile)
            Else
                $g_AppSections.Item($appName) = $iniFile ; Or update, or skip
            EndIf
            $g_AppTreeItemToSection.Add($appNode, $appName)
            $g_AppTreeItemToIni.Add($appNode, $iniFile)
        Next
    EndIf

    ; Add categories (alphabetical)
    Local $catNames[0]
    For $category In $categories.Keys
        _ArrayAdd($catNames, $category)
    Next
    _ArraySort($catNames)
    For $i = 0 To UBound($catNames) - 1
        Local $category = $catNames[$i]
        Local $catNode = GUICtrlCreateTreeViewItem($category, $g_AppTreeView)
        $g_AppCategoryTreeItems.Add($catNode, $category)
        ; Collect and sort app names in this category
        Local $appNames[0]
        For $appName In $categories.Item($category).Keys
            _ArrayAdd($appNames, $appName)
        Next
        _ArraySort($appNames)
        For $j = 0 To UBound($appNames) - 1
            Local $appName = $appNames[$j]
            Local $iniFile = $categories.Item($category).Item($appName)
            Local $hide = IniRead($iniFile, $appName, "Hide", "0")
            Local $fave = IniRead($iniFile, $appName, "Fave", "0")
            ; If Fave=1, already added above
            If $fave = "1" Then ContinueLoop
            Local $buttonText = IniRead($iniFile, $appName, "ButtonText", $appName)
            Local $label = $buttonText
            If $hide = "1" Then $label = "(H) " & $buttonText
            Local $appNode = GUICtrlCreateTreeViewItem($label, $catNode)
            If Not $g_AppSections.Exists($appName) Then
                $g_AppSections.Add($appName, $iniFile)
            Else
                $g_AppSections.Item($appName) = $iniFile ; Or update, or skip
            EndIf
            $g_AppTreeItemToSection.Add($appNode, $appName)
            $g_AppTreeItemToIni.Add($appNode, $iniFile)
        Next
    Next
EndFunc

; Enable all buttons (Ignore, Save)
Func _AppTab_Buttons_Enable()
    GUICtrlSetState($g_IgnoreBtn, $GUI_ENABLE)
    GUICtrlSetState($g_SaveBtn, $GUI_ENABLE)
EndFunc

; Disable all buttons (Ignore, Save)
Func _AppTab_Buttons_Disable()
    GUICtrlSetState($g_IgnoreBtn, $GUI_DISABLE)
    GUICtrlSetState($g_SaveBtn, $GUI_DISABLE)
EndFunc

Func Tab_App_HandleEvents($msg)
    ; Handle buttons and context menu
    Switch $msg
        Case $g_SaveBtn
            If $g_SelectedIni <> "" And $g_SelectedSection <> "" Then
                _AppTab_SaveFields($g_SelectedIni, $g_SelectedSection)
;~              _AppTab_LoadIniFilesAndApps()
            EndIf
        Case $g_IgnoreBtn
            _AppTab_IgnoreSelected()
        Case $g_RefreshBtn
            GUIDelete($hGUI)
            ShowSettingsGUI()
        Case $g_AppMenuDeleteItem
            _AppTab_DeleteSelected()
        Case $g_AppFields("SymLinkCreate")
            ; Enable/disable symlinks field based on checkbox
            If GUICtrlRead($g_AppFields("SymLinkCreate")) = $GUI_CHECKED Then
                GUICtrlSetState($g_AppsSymlinksEdit, $GUI_ENABLE)
            Else
                GUICtrlSetState($g_AppsSymlinksEdit, $GUI_DISABLE)
            EndIf
        Case $g_AppFields("Sandboxie")
            ; Enable/disable SandboxName field based on checkbox
            If GUICtrlRead($g_AppFields("Sandboxie")) = $GUI_CHECKED Then
                GUICtrlSetState($g_AppFields("SandboxName"), $GUI_ENABLE)
            Else
                GUICtrlSetState($g_AppFields("SandboxName"), $GUI_DISABLE)
            EndIf
    EndSwitch

    Local $selectedItem = GUICtrlRead($g_AppTreeView)
    ; Only act if the selection changed
    If $selectedItem <> $g_AppLastSelectedItem Then
        ; If a category node is selected
        If $selectedItem <> 0 And $g_AppCategoryTreeItems.Exists($selectedItem) Then
            _AppTab_ClearFields()
            _AppTab_DisableFields()
            GUICtrlSetState($g_SaveBtn, $GUI_DISABLE)
            GUICtrlSetState($g_IgnoreBtn, $GUI_DISABLE)
            $g_SelectedIni = ""
            $g_SelectedSection = ""
        ; If last selected was a valid app item and now whitespace/node: disable buttons
        ElseIf $g_AppLastSelectedItem <> 0 And $g_AppTreeItemToSection.Exists($g_AppLastSelectedItem) _
            And ($selectedItem = 0 Or Not $g_AppTreeItemToSection.Exists($selectedItem)) Then
            _AppTab_ClearFields()
            _AppTab_Buttons_Disable()
            $g_SelectedIni = ""
            $g_SelectedSection = ""
        ; If selection changed to a valid app item: enable buttons
        ElseIf $selectedItem <> 0 And $g_AppTreeItemToSection.Exists($selectedItem) Then
            $g_SelectedSection = $g_AppTreeItemToSection.Item($selectedItem)
            $g_SelectedIni = $g_AppTreeItemToIni.Item($selectedItem)
            _AppTab_PopulateFields($g_SelectedIni, $g_SelectedSection)
            _AppTab_EnableFields()
            _AppTab_Buttons_Enable()
        ; If selection changed to whitespace/node, disable buttons
        Else
            _AppTab_ClearFields()
            _AppTab_Buttons_Disable()
            $g_SelectedIni = ""
            $g_SelectedSection = ""
        EndIf
        $g_AppLastSelectedItem = $selectedItem
    EndIf
EndFunc

Func _AppTab_PopulateFields($iniFile, $section)
    For $key In $g_AppFields.Keys()
        Local $val = IniRead($iniFile, $section, $key, "")
        If StringInStr("RunAsAdmin SingleInstance Sandboxie SymLinkCreate Fave Hide", $key) Then
            GUICtrlSetState($g_AppFields($key), ($val = "1") ? $GUI_CHECKED : $GUI_UNCHECKED)
        Else
            GUICtrlSetData($g_AppFields($key), $val)
        EndIf
        GUICtrlSetState($g_AppFields($key), $GUI_ENABLE)
    Next
    ; Populate symlinks multiline edit
    Local $symlinks = ""
    Local $idx = 1
    While 1
        Local $val = IniRead($iniFile, $section, "Symlink" & $idx, "")
        If $val = "" Then ExitLoop
        $symlinks &= $val & @CRLF
        $idx += 1
    WEnd
    GUICtrlSetData($g_AppsSymlinksEdit, StringTrimRight($symlinks, 2))
    ; Enable/disable symlinks field according to SymLinkCreate checkbox
    If GUICtrlRead($g_AppFields("SymLinkCreate")) = $GUI_CHECKED Then
        GUICtrlSetState($g_AppsSymlinksEdit, $GUI_ENABLE)
    Else
        GUICtrlSetState($g_AppsSymlinksEdit, $GUI_DISABLE)
    EndIf
    ; Enable/disable SandboxName field according to Sandboxie checkbox
    If GUICtrlRead($g_AppFields("Sandboxie")) = $GUI_CHECKED Then
        GUICtrlSetState($g_AppFields("SandboxName"), $GUI_ENABLE)
    Else
        GUICtrlSetState($g_AppFields("SandboxName"), $GUI_DISABLE)
    EndIf
EndFunc

Func _AppTab_SaveFields($iniFile, $section)
    For $key In $g_AppFields.Keys()
        If StringInStr("RunAsAdmin SingleInstance Sandboxie SymLinkCreate Fave Hide", $key) Then
            Local $v = (GUICtrlRead($g_AppFields($key)) = $GUI_CHECKED) ? "1" : "0"
            IniWrite($iniFile, $section, $key, $v)
        Else
            IniWrite($iniFile, $section, $key, GUICtrlRead($g_AppFields($key)))
        EndIf
        GUICtrlSetState($g_AppFields($key), $GUI_DISABLE)
    Next
    ; Save symlinks from multiline edit as Symlink1=..., Symlink2=..., ...
    Local $symlinksText = GUICtrlRead($g_AppsSymlinksEdit)
    Local $symlinksArr = StringSplit(StringStripCR($symlinksText), @LF)
    ; First, delete any old SymlinkX keys
    Local $idx = 1
    While 1
        Local $exists = IniRead($iniFile, $section, "Symlink" & $idx, "")
        If $exists = "" Then ExitLoop
        IniDelete($iniFile, $section, "Symlink" & $idx)
        $idx += 1
    WEnd
    ; Write new symlinks
    If $symlinksArr[0] > 0 Then
        For $i = 1 To $symlinksArr[0]
            If StringStripWS($symlinksArr[$i], 3) <> "" Then
                IniWrite($iniFile, $section, "Symlink" & $i, $symlinksArr[$i])
            EndIf
        Next
    EndIf
    GUICtrlSetState($g_AppsSymlinksEdit, $GUI_DISABLE)
    ; Disable SandboxName field after save
    GUICtrlSetState($g_AppFields("SandboxName"), $GUI_DISABLE)
EndFunc

Func _AppTab_ClearFields()
    For $key In $g_AppFields.Keys()
        If StringInStr("RunAsAdmin SingleInstance Sandboxie SymLinkCreate Fave Hide", $key) Then
            GUICtrlSetState($g_AppFields($key), $GUI_UNCHECKED)
        Else
            GUICtrlSetData($g_AppFields($key), "")
        EndIf
        GUICtrlSetState($g_AppFields($key), $GUI_ENABLE)
    Next
    GUICtrlSetData($g_AppsSymlinksEdit, "")
    ; Enable/disable Symlinks field according to SymLinkCreate checkbox
    If GUICtrlRead($g_AppFields("SymLinkCreate")) = $GUI_CHECKED Then
        GUICtrlSetState($g_AppsSymlinksEdit, $GUI_ENABLE)
    Else
        GUICtrlSetState($g_AppsSymlinksEdit, $GUI_DISABLE)
    EndIf
    ; Enable/disable SandboxName field according to Sandboxie checkbox
    If GUICtrlRead($g_AppFields("Sandboxie")) = $GUI_CHECKED Then
        GUICtrlSetState($g_AppFields("SandboxName"), $GUI_ENABLE)
    Else
        GUICtrlSetState($g_AppFields("SandboxName"), $GUI_DISABLE)
    EndIf
EndFunc

Func _AppTab_EnableFields()
    For $key In $g_AppFields.Keys()
        GUICtrlSetState($g_AppFields($key), $GUI_ENABLE)
    Next
    ; Enable/disable symlinks field according to SymLinkCreate
    If GUICtrlRead($g_AppFields("SymLinkCreate")) = $GUI_CHECKED Then
        GUICtrlSetState($g_AppsSymlinksEdit, $GUI_ENABLE)
    Else
        GUICtrlSetState($g_AppsSymlinksEdit, $GUI_DISABLE)
    EndIf
    ; Enable/disable SandboxName field according to Sandboxie checkbox
    If GUICtrlRead($g_AppFields("Sandboxie")) = $GUI_CHECKED Then
        GUICtrlSetState($g_AppFields("SandboxName"), $GUI_ENABLE)
    Else
        GUICtrlSetState($g_AppFields("SandboxName"), $GUI_DISABLE)
    EndIf
EndFunc

Func _AppTab_DisableFields()
    For $key In $g_AppFields.Keys()
        GUICtrlSetState($g_AppFields($key), $GUI_DISABLE)
    Next
    GUICtrlSetState($g_AppsSymlinksEdit, $GUI_DISABLE)
    GUICtrlSetState($g_AppFields("SandboxName"), $GUI_DISABLE)
EndFunc

Func _AppTab_DeleteSelected($skipConfirm = False)
    Local $selectedItem = GUICtrlRead($g_AppTreeView)
    If $selectedItem = 0 Then Return

    ; --- CATEGORY NODE DELETION ---
    If $g_AppCategoryTreeItems.Exists($selectedItem) Then
        Local $category = $g_AppCategoryTreeItems.Item($selectedItem)
        Local $iniFile = $g_CategoryIniDir & "\" & $category & ".ini"
        ; SAFEGUARD: Never delete Settings.ini or IgnoreList.ini
        If StringInStr(StringLower($iniFile), "settings.ini") Or StringInStr(StringLower($iniFile), "ignorelist.ini") Then Return

        If Not $skipConfirm Then
            Local $answer = MsgBox(1 + 32, "Delete Category", _
                "Are you sure you want to delete the entire category '" & $category & "'?" & @CRLF & _
                "This will remove the INI file (" & $category & ".ini) and all its apps." & @CRLF & _
                "This cannot be undone.", 0)
            If $answer <> 1 Then Return
        EndIf

        If FileExists($iniFile) Then FileDelete($iniFile)
        GUICtrlDelete($selectedItem)
        $g_AppCategoryTreeItems.Remove($selectedItem)
        $g_SelectedIni = ""
        $g_SelectedSection = ""
        $g_AppLastSelectedItem = 0
        _AppTab_ClearFields()
        _AppTab_Buttons_Disable()
        Return
    EndIf

    ; --- APP NODE DELETION ---
    If Not $g_AppTreeItemToSection.Exists($selectedItem) Then Return

    Local $appName = $g_AppTreeItemToSection.Item($selectedItem)
    Local $iniFile = $g_AppSections.Item($appName)
    ; SAFEGUARD: Never delete from Settings.ini
    If StringInStr(StringLower($iniFile), "settings.ini") Then Return

    If Not $skipConfirm Then
        Local $answer = MsgBox(1 + 32, "Delete App", "Are you sure you want to delete '" & $appName & "'?" & @CRLF & "This cannot be undone.", 0)
        If $answer <> 1 Then Return
    EndIf

    IniDelete($iniFile, $appName)
    GUICtrlDelete($selectedItem)
    $g_AppSections.Remove($appName)
    $g_AppTreeItemToSection.Remove($selectedItem)
    $g_AppTreeItemToIni.Remove($selectedItem)
    _AppTab_ClearFields()
    $g_SelectedIni = ""
    $g_SelectedSection = ""
    $g_AppLastSelectedItem = 0
    _AppTab_Buttons_Disable()
EndFunc

Func _AppTab_IgnoreSelected()
    Local $selectedItem = GUICtrlRead($g_AppTreeView)
    If $selectedItem = 0 Or Not $g_AppTreeItemToSection.Exists($selectedItem) Then Return

    Local $appName = $g_AppTreeItemToSection.Item($selectedItem)
    Local $iniFile = $g_AppSections.Item($appName)
    ; SAFEGUARD: Never ignore from Settings.ini
    If StringInStr(StringLower($iniFile), "settings.ini") Then Return

    ; Get the RunFile (path) to ignore
    Local $ignorePath = IniRead($iniFile, $appName, "RunFile", "")
    If $ignorePath = "" Then Return

    ; --- Custom Ignore Confirmation ---
    Local $answer = MsgBox(1 + 32, "Ignore App", _
        "Confirm adding '" & $appName & "' (" & $ignorePath & ") to the Ignore List and delete from App list." & @CRLF & _
        @CRLF & _
        "You can restore ignored apps via the Ignore tab.", 0)
    If $answer <> 1 Then Return

    ; Add the path to IgnoreList.ini if not already present
    Local $ignoreIni = @ScriptDir & "\App\IgnoreList.ini"
    Local $ignoreArr = FileReadToArray($ignoreIni)
    Local $exists = False
    If IsArray($ignoreArr) Then
        For $i = 0 To UBound($ignoreArr) - 1
            If StringStripWS($ignoreArr[$i], 3) = $ignorePath Then
                $exists = True
                ExitLoop
            EndIf
        Next
    EndIf
    If Not $exists Then
        Local $hFile = FileOpen($ignoreIni, $FO_APPEND)
        If $hFile <> -1 Then
            FileWriteLine($hFile, $ignorePath)
            FileClose($hFile)
        EndIf
    EndIf

    ; Delete from Apps tab (calls delete logic)
    _AppTab_DeleteSelected(True)
    ; Update Ignore tab view
    GUICtrlSetState($g_IgnoreEdit, $GUI_ENABLE)
    _Ignore_EditPopulate()
    GUICtrlSetState($g_IgnoreEdit, $GUI_DISABLE)
EndFunc

Func Tab_Apps_WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
    Local $tNMHDR = DllStructCreate($tagNMHDR, $lParam)
    If $tNMHDR.idFrom <> $g_AppTreeView Then Return $GUI_RUNDEFMSG
    If $tNMHDR.code <> $NM_RCLICK Then Return $GUI_RUNDEFMSG

    ; Find which item was right-clicked
    Local $cursor = GUIGetCursorInfo($hWnd)
    Local $ctrlPos = ControlGetPos($hWnd, "", $tNMHDR.idFrom)
    Local $relX = $cursor[0] - $ctrlPos[0]
    Local $relY = $cursor[1] - $ctrlPos[1]
    Local $itemID = _GUICtrlTreeView_HitTestItem($wParam, $relX, $relY)
    If Not $itemID Then Return 1 ; block menu if nothing

    ; Select the item (this is all you need)
    _GUICtrlTreeView_SelectItem($wParam, $itemID)
    ; DO NOT call any event handler here
    Return $GUI_RUNDEFMSG
EndFunc

Any idea what I am doing wrong?

Basically, the premise is that there is a treeview on the left hand side of the gui, clicking items inside the categories populates the fields and checkboxes on the right. Which is working as intended. But I am wanting the right click to move the selection to the mouse position just as your test script does.

So in plain terms, your test script:
1. Left click to select node 1, item selected.
2. Right click node 3 to select and show right click context menu.
3. Select Delete, node 3 is deleted.

But I can't get that behaviour in my own script! This is what is happening in mine:
1. Left click to select node 1, item selected. Correct.
2. Right click node 3 to select and show right click context menu. Correct.
3. Select Delete, node 1 is deleted. Incorrect

Thanks for your help.

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