Disable TreeGadget Item

Just starting out? Need help? Post your questions and find answers here.
User avatar
Martt
User
User
Posts: 15
Joined: Sat Dec 23, 2017 4:03 pm
Location: The Netherlands

Disable TreeGadget Item

Post by Martt »

In this older post http://www.purebasic.fr/english/viewtopic.php?p=210670 it is suggested that #TVN_ITEMCHANGING can be used to disable one or more treegadget items. But... I cannot get this thing to work. For some reason uStateNew allways seems to be #Null ?

The #NM_CUSTOMDRAW code is from Sparkie (thanks), and is used to delete some checkboxes. Works fine.

BTW: Looping through all treegadget items to find the itemnr in the #TVN_ITEMCHANGING code is the only way ??

Code: Select all

EnableExplicit

Enumeration
    #Window
    #Tree
    #Image1
    #Image2
EndEnumeration

; This one is not present in 5.62 ??
CompilerIf Not Defined(TVN_ITEMCHANGING, #PB_Constant)
    #TVN_ITEMCHANGING       = -417
CompilerEndIf

Declare Main()
Declare Callback_Window(HWnd.i, uMsg.i, WParam.i, LParam.i)

Procedure Main()

    Protected Exit.b
    Protected Event.i

    OpenWindow(#Window, 0, 0, 150, 300, "Tree Demo", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    SetWindowCallback(@Callback_Window(), #Window)

    ; Tree ItemData : 
    ; ItemData = #Null => No checkbox
    ; ItemData = pointer to a structure => Checkbox

    TreeGadget(#Tree, 10, 10, 130, 280, #PB_Tree_AlwaysShowSelection | #PB_Tree_CheckBoxes | #PB_Tree_ThreeState)
    
    AddGadgetItem(#Tree, 0, "Parent 1", ImageID(#Image1), 0)
    SetGadgetItemData(#Tree, 0, #Null)

    AddGadgetItem(#Tree, 1, "Child 1", ImageID(#Image2), 1)
    SetGadgetItemData(#Tree, 1, $ABBA1)
    AddGadgetItem(#Tree, 2, "Child 2", ImageID(#Image2), 1)
    SetGadgetItemData(#Tree, 2, $ABBA2)
    
    AddGadgetItem(#Tree, 3, "Parent 2", ImageID(#Image1), 0)
    SetGadgetItemData(#Tree, 0, #Null)

    AddGadgetItem(#Tree, 4, "Child 3", ImageID(#Image2), 1)
    SetGadgetItemData(#Tree, 4, $DEADBEEF1)
    AddGadgetItem(#Tree, 5, "Child 4", ImageID(#Image2), 1)
    SetGadgetItemData(#Tree, 5, $DEADBEEF2)

    SetGadgetItemState(#Tree, 0, #PB_Tree_Expanded | #PB_Tree_Selected)
    SetGadgetItemState(#Tree, 3, #PB_Tree_Expanded | #PB_Tree_Selected)

    Repeat
        Event = WaitWindowEvent()
    
        Select Event
            Case #PB_Event_CloseWindow
                Exit = #True
        EndSelect
    
    Until Exit
    
EndProcedure

Procedure Callback_Window(HWnd.i, uMsg.i, WParam.i, LParam.i)
    
    Protected *pNmhdr.NMHDR
    Protected *pNmtvcustomdraw.NMTVCUSTOMDRAW
    Protected *pNmtvitemchange.NMTVITEMCHANGE
    Protected Tv_Item.TV_ITEM
    Protected Gadget.i
    Protected ItemNr.i
    Protected ItemData.i
    Protected LoopCnt.i
    Protected HItem.i
    
    Select uMsg
        Case #WM_NOTIFY
            *pNmhdr = LParam
            
            Select *pNmhdr\code
                Case #NM_CUSTOMDRAW
                    ; for *all* treegadgets - every item that has a #Null as data, do not draw checkbox...
                    Gadget = GetDlgCtrlID_(*pNmhdr\hwndFrom)
                    If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Tree
                        *pNmtvcustomdraw = LParam
                        ItemNr = *pNmtvcustomdraw\nmcd\lItemlParam
                
                        ; #Null means no checkbox, so prevent the checkbox from displaying
                        ItemData = GetGadgetItemData(Gadget, ItemNr)
                        
                        If Not ItemData
                            Tv_Item\mask = #TVIF_HANDLE | #TVIF_STATE
                            Tv_Item\hItem = GadgetItemID(Gadget, ItemNr)
                            Tv_Item\state = 0
                            Tv_Item\stateMask = #TVIS_STATEIMAGEMASK
                    
                            SendMessage_(*pNmhdr\hwndFrom, #TVM_SETITEM, 0, @Tv_Item)
                        EndIf
                    EndIf
                    
                Case #TVN_ITEMCHANGING
                    ; for *all* treegadgets - every item that has a ??? as data, do not change checkbox...
                    Gadget = GetDlgCtrlID_(*pNmhdr\hwndFrom)
                    If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Tree
                    
                        *pNmtvitemchange = LParam
                        ; Only look for state changes...
                        If *pNmtvitemchange\uChanged = #TVIF_STATE And ((*pNmtvitemchange\uStateNew & #TVIS_STATEIMAGEMASK) <> (*pNmtvitemchange\uStateOld & #TVIS_STATEIMAGEMASK))
                            
                            ; Get the handle for the selected item and search the item *number*
                            HItem = SendMessage_(*pNmhdr\hwndFrom, #TVM_GETNEXTITEM, #TVGN_CARET, *pNmtvitemchange\hItem)
                            
                            For LoopCnt = 0 To CountGadgetItems(Gadget) - 1
                                If GadgetItemID(Gadget, LoopCnt) = HItem
                                    ItemNr = LoopCnt
                                    
                                    ;Debug "uStateOld = " + Str(((*pNmtvitemchange\uStateOld & #TVIS_STATEIMAGEMASK) >> 12) & 15)
                                    ;Debug "uStateNew = " + Str(((*pNmtvitemchange\uStateNew & #TVIS_STATEIMAGEMASK) >> 12) & 15)
                                    Debug "----------------"
                                    Debug "ItemNr = " + Str(ItemNr)
                                    Debug "uStateOld = " + Str(*pNmtvitemchange\uStateOld)
                                    Debug "uStateNew = " + Str(*pNmtvitemchange\uStateNew)
                                EndIf
                            Next
                            
                            ;If ItemNr = 2 : ProcedureReturn #True : EndIf
                        EndIf
                    EndIf
            EndSelect
    EndSelect
    
    ProcedureReturn #PB_ProcessPureBasicEvents

EndProcedure

UsePNGImageDecoder() 

LoadImage(#Image1, #PB_Compiler_Home + "examples\sources\Data\Toolbar\Open.png")
LoadImage(#Image2, #PB_Compiler_Home + "examples\sources\Data\Toolbar\Save.png")

Main()
End
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4636
Joined: Sun Apr 12, 2009 6:27 am

Re: Disable TreeGadget Item

Post by RASHAD »

Hi
Author : srod
Modified : RASHAD

- Items 0 & 3 disabled
- Added disable for DblClick
- Only subclass used

Code: Select all

Global oldTreeProc

Procedure TreeProc(hwnd, uMsg, wParam, lParam)
  Protected result, HitTest.TV_HITTESTINFO, item, tvitem.TV_ITEM
  Select uMsg  
     ;Here we prevent the checkbox of item 0 & 3 being altered.
      Case #WM_LBUTTONDOWN
        ;Determine which item was clicked etc.
          HitTest\pt\x = lParam&$FFFF
          HitTest\pt\y = (lParam>>16)&$FFFF
          If SendMessage_(hwnd, #TVM_HITTEST, 0, HitTest)
            ;Identify the zero-based item index of the item clicked.
              tvitem\mask = #TVIF_PARAM   
              tvitem\hItem = HitTest\hItem
              SendMessage_(hwnd, #TVM_GETITEM, 0, tvitem)
              item = tvitem\lParam
            If ((HitTest\flags <> #TVHT_ONITEMLABEL And HitTest\flags <> #TVHT_ONITEMSTATEICON) Or (item <> 0 And item <> 3)); Or (HitTest\flags <> #TVHT_ONITEMSTATEICON  Or (item <> 0 And item <> 3))
                result = CallWindowProc_(oldTreeProc, hwnd, uMsg, wParam, lParam) 
            EndIf             
          EndIf 
          
      Case #WM_LBUTTONDBLCLK
          result = 1
          
      Case #WM_KEYDOWN
        If wParam <> #VK_SPACE Or GetGadgetState(0) <> 0 Or GetGadgetState(0) <> 3
          result = CallWindowProc_(oldTreeProc, hwnd, uMsg, wParam, lParam)
        EndIf
        
      Default
        result = CallWindowProc_(oldTreeProc, hwnd, uMsg, wParam, lParam)
    EndSelect
  ProcedureReturn result
EndProcedure

If OpenWindow(0, 0, 0, 350, 200, "Test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  TreeGadget(0, 10, 10, 330, 180, #PB_Tree_CheckBoxes) 
    AddGadgetItem (0, -1, "Cannot alter checkbox")
    AddGadgetItem (0, -1, "Can alter checkbox but cannot select")
    AddGadgetItem(0, -1, "Anything goes!")
    AddGadgetItem(0, -1, "Anything goes!")
    AddGadgetItem(0, -1, "Anything goes!")
    AddGadgetItem(0, -1, "Anything goes!")

  oldTreeProc = SetWindowLongPtr_(GadgetID(0), #GWL_WNDPROC, @TreeProc())

  Repeat
    Select WaitWindowEvent()
      Case #PB_Event_CloseWindow
        Quit = 1
        
      Case #WM_KEYDOWN
        kk = EventwParam()
        If GetActiveGadget() = 0
          item = GetGadgetState(0) 
          If kk = 38 And (item = 0 Or item = 3)
            SetGadgetState(0,item - 1)
          ElseIf kk = 40 And (item = 0 Or item = 3)
            SetGadgetState(0,item + 1)
          EndIf
        EndIf
        
      Case #PB_Event_Gadget
        Select EventGadget()
          Case 0
            Debug item
        EndSelect
    EndSelect   
  Until Quit = 1 
EndIf 
Edit : Updated
Last edited by RASHAD on Wed Mar 21, 2018 5:51 pm, edited 1 time in total.
Egypt my love
User avatar
RSBasic
Moderator
Moderator
Posts: 1218
Joined: Thu Dec 31, 2009 11:05 pm
Location: Gernsbach (Germany)
Contact:

Re: Disable TreeGadget Item

Post by RSBasic »

@RASHAD
It's not working yet. You can press up and down with the arrow keys and press space key to enable.
Image
Image
User avatar
Martt
User
User
Posts: 15
Joined: Sat Dec 23, 2017 4:03 pm
Location: The Netherlands

Re: Disable TreeGadget Item

Post by Martt »

Thanks Rashad. The route you took did come to mind, mouse and keyboard. But after a lot of experimenting with EditorGadgets and that stuff, I abandoned the idea. Something always breaks or does not work 100% all the time. Very annoying.

But I did answer my own question. This puppy finally works:

Code: Select all

EnableExplicit

Enumeration
    #Window
    #Tree
    #Image1
    #Image2
EndEnumeration

; This one is not present in 5.62 ??
CompilerIf Not Defined(TVN_ITEMCHANGING, #PB_Constant)
    #TVN_ITEMCHANGING       = -417
CompilerEndIf

Structure StructNMHDR Align #PB_Structure_AlignC 
    hwndFrom.i
    idFrom.i
    code.l
;    PB_Alignment1.b[4]
EndStructure

Structure StructNMTVITEMCHANGE Align #PB_Structure_AlignC 
    hdr.StructNMHDR
;    hdr.NMHDR
    uChanged.l
    hItem.i
    uStateNew.l
    uStateOld.l
    lParam.i
EndStructure

Declare Main()
Declare Callback_Window(HWnd.i, uMsg.i, WParam.i, LParam.i)

Procedure Main()

    Protected Exit.b
    Protected Event.i
    Protected Gadget.i
    Protected ItemNr.i
    Protected State.i

    OpenWindow(#Window, 0, 0, 250, 300, "Tree Demo", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    SetWindowCallback(@Callback_Window(), #Window)

    ; Tree ItemData : 
    ; ItemData = #Null => No checkbox
    ; ItemData = pointer to a structure => Checkbox

    TreeGadget(#Tree, 10, 10, 230, 280, #PB_Tree_AlwaysShowSelection | #PB_Tree_CheckBoxes | #PB_Tree_ThreeState)
    
    AddGadgetItem(#Tree, 0, "Parent 1", ImageID(#Image1), 0)
    SetGadgetItemData(#Tree, 0, #Null)

    AddGadgetItem(#Tree, 1, "Unlocked 1", ImageID(#Image2), 1)
    SetGadgetItemData(#Tree, 1, $ABBA1)
    AddGadgetItem(#Tree, 2, "Unlocked 2", ImageID(#Image2), 1)
    SetGadgetItemData(#Tree, 2, $ABBA2)
    
    AddGadgetItem(#Tree, 3, "Parent 2", ImageID(#Image1), 0)
    SetGadgetItemData(#Tree, 0, #Null)

    AddGadgetItem(#Tree, 4, "Locked Unchecked", ImageID(#Image2), 1)
    SetGadgetItemData(#Tree, 4, $DEADBEEF1)
    AddGadgetItem(#Tree, 5, "Locked Checked", ImageID(#Image2), 1)
    SetGadgetItemData(#Tree, 5, $DEADBEEF2)
    AddGadgetItem(#Tree, 6, "Locked In Between", ImageID(#Image2), 1)
    SetGadgetItemData(#Tree, 6, $DEADBEEF3)
    
    ; Expand the parents
    SetGadgetItemState(#Tree, 0, #PB_Tree_Expanded | #PB_Tree_Selected)
    SetGadgetItemState(#Tree, 3, #PB_Tree_Expanded | #PB_Tree_Selected)
    
    ; Set the locked items to the right state
    SetGadgetItemState(#Tree, 4, #PB_Tree_Collapsed)
    SetGadgetItemState(#Tree, 5, #PB_Tree_Collapsed | #PB_Tree_Checked)
    SetGadgetItemState(#Tree, 6, #PB_Tree_Collapsed | #PB_Tree_Inbetween)
    
    Repeat
        Event = WaitWindowEvent()
    
        Select Event
            Case #PB_Event_CloseWindow
                Exit = #True
        EndSelect
    
    Until Exit
    
EndProcedure

Procedure Callback_Window(HWnd.i, uMsg.i, WParam.i, LParam.i)
    
    Protected *pNmhdr.StructNMHDR
    Protected *pNmtvcustomdraw.NMTVCUSTOMDRAW
    Protected *pNmtvitemchange.StructNMTVITEMCHANGE
    Protected Tv_Item.TV_ITEM
    Protected Gadget.i
    Protected ItemNr.i
    Protected ItemData.i
    Protected LoopCnt.i
    Protected uStateOld.i
    Protected uStateNew.i
    
    Select uMsg
        Case #WM_NOTIFY
            *pNmhdr = LParam
            
            Select *pNmhdr\code
                Case #NM_CUSTOMDRAW
                    ; for *all* treegadgets - every item that has a #Null as data, do not draw checkbox...
                    Gadget = GetDlgCtrlID_(*pNmhdr\hwndFrom)
                    If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Tree
                        *pNmtvcustomdraw = LParam
                        ItemNr = *pNmtvcustomdraw\nmcd\lItemlParam
                
                        ; #Null means no checkbox, so prevent the checkbox from displaying
                        ItemData = GetGadgetItemData(Gadget, ItemNr)
                        
                        If Not ItemData
                            Tv_Item\mask = #TVIF_HANDLE | #TVIF_STATE
                            Tv_Item\hItem = GadgetItemID(Gadget, ItemNr)
                            Tv_Item\state = 0
                            Tv_Item\stateMask = #TVIS_STATEIMAGEMASK
                    
                            SendMessage_(*pNmhdr\hwndFrom, #TVM_SETITEM, 0, @Tv_Item)
                        EndIf
                    EndIf
                    
                Case #TVN_ITEMCHANGING
                    ; for *all* treegadgets - every item that has a ??? in the structure field, do not change checkbox...
                    Gadget = GetDlgCtrlID_(*pNmhdr\hwndFrom)
                    If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Tree
                    
                        *pNmtvitemchange = LParam
                        ; Only look for state changes...
                        If *pNmtvitemchange\uChanged = #TVIF_STATE And ((*pNmtvitemchange\uStateNew & #TVIS_STATEIMAGEMASK) <> (*pNmtvitemchange\uStateOld & #TVIS_STATEIMAGEMASK))
                            
                            ; Search the itemnr (reverse GadgetItemID)
                            For LoopCnt = 0 To CountGadgetItems(Gadget) - 1
                                If GadgetItemID(Gadget, LoopCnt) = *pNmtvitemchange\hItem
                                    ItemNr = LoopCnt
                                    uStateOld = ((*pNmtvitemchange\uStateOld & #TVIS_STATEIMAGEMASK) >> 12)
                                    uStateNew = ((*pNmtvitemchange\uStateNew & #TVIS_STATEIMAGEMASK) >> 12)

                                    ; Image 1 is the unchecked box
                                    ; Image 2 is the checked box
                                    ; Image 3 is the in between box
                                    
                                    Debug "----------------"
                                    Debug "uStateOld = " + Str(uStateOld)
                                    Debug "uStateNew = " + Str(uStateNew)
                                    Debug "ItemNr = " + Str(ItemNr)
                                    
                                    ; Prevent items 4-6 from changing (normally done from a field in a structure (itemdata)
                                    If ItemNr = 4 And uStateNew <> 1 : ProcedureReturn #True : EndIf
                                    If ItemNr = 5 And uStateNew <> 2 : ProcedureReturn #True : EndIf
                                    If ItemNr = 6 And uStateNew <> 3 : ProcedureReturn #True : EndIf
                                EndIf
                            Next
                        EndIf
                    EndIf
            EndSelect
    EndSelect
    
    ProcedureReturn #PB_ProcessPureBasicEvents

EndProcedure

UsePNGImageDecoder() 

LoadImage(#Image1, #PB_Compiler_Home + "examples\sources\Data\Toolbar\Open.png")
LoadImage(#Image2, #PB_Compiler_Home + "examples\sources\Data\Toolbar\Save.png")

Main()
End
It seems there are problems with at least one structure. The Align C fixes it. Not sure about the padding in the original NMHDR structure. If left, I see the uchanged also in the padding of the StructNMTVITEMCHANGE ? Crazy stuff. Someone will understand. This works in x86 and x64 mode.

This is only a test program, but in my project I will use a structure with some fields to allow/disallow state changes and store the initial state.

Glad it works now :D :D
User avatar
mk-soft
Always Here
Always Here
Posts: 5335
Joined: Fri May 12, 2006 6:51 pm
Location: Germany

Re: Disable TreeGadget Item

Post by mk-soft »

Disable Item over TVIF_STATEEX not work to. Is only Gray

Second way ist SetGadgetState(#Tree, -1) by event change.

Code: Select all

EnableExplicit

Structure TV_ITEMEX
   mask.l
   PB_Alignment.b[4]
   hItem.i
   state.l
   stateMask.l
   *pszText
   cchTextMax.l
   iImage.l
   iSelectedImage.l
   cChildren.l
   lParam.i
   iIntegral.l
   uStateEx.l
   hwnd.i
   iExpandedImage.l
EndStructure

#TVIS_EX_DISABLED = $0002
#TVIF_STATEEX = $0100

Enumeration
  #Window
  #Tree
  #Image1
  #Image2
EndEnumeration

; This one is not present in 5.62 ??
CompilerIf Not Defined(TVN_ITEMCHANGING, #PB_Constant)
  #TVN_ITEMCHANGING       = -417
CompilerEndIf

Declare Main()
Declare Callback_Window(HWnd.i, uMsg.i, WParam.i, LParam.i)

Procedure Main()
  
  Protected Exit.b
  Protected Event.i
  Protected Item.i
  
  OpenWindow(#Window, 0, 0, 150, 300, "Tree Demo", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  SetWindowCallback(@Callback_Window(), #Window)
  
  ; Tree ItemData :
  ; ItemData = #Null => No checkbox
  ; ItemData = pointer to a structure => Checkbox
  
  TreeGadget(#Tree, 10, 10, 130, 280, #PB_Tree_AlwaysShowSelection | #PB_Tree_CheckBoxes | #PB_Tree_ThreeState)
  
  AddGadgetItem(#Tree, 0, "Parent 1", ImageID(#Image1), 0)
  SetGadgetItemData(#Tree, 0, #Null)
  
  AddGadgetItem(#Tree, 1, "Child 1", ImageID(#Image2), 1)
  SetGadgetItemData(#Tree, 1, $ABBA1)
  AddGadgetItem(#Tree, 2, "Child 2", ImageID(#Image2), 1)
  SetGadgetItemData(#Tree, 2, $ABBA2)
  
  AddGadgetItem(#Tree, 3, "Parent 2", ImageID(#Image1), 0)
  SetGadgetItemData(#Tree, 0, #Null)
  
  AddGadgetItem(#Tree, 4, "Child 3", ImageID(#Image2), 1)
  SetGadgetItemData(#Tree, 4, $DEADBEEF1)
  AddGadgetItem(#Tree, 5, "Child 4", ImageID(#Image2), 1)
  SetGadgetItemData(#Tree, 5, $DEADBEEF2)
  
  SetGadgetItemState(#Tree, 0, #PB_Tree_Expanded | #PB_Tree_Selected)
  SetGadgetItemState(#Tree, 3, #PB_Tree_Expanded | #PB_Tree_Selected)
  
  Repeat
    Event = WaitWindowEvent()
    
    Select Event
      Case #PB_Event_CloseWindow
        Exit = #True
      Case #PB_Event_Gadget
        Select EventGadget()
          Case #Tree
            Select EventType()
              Case #PB_EventType_Change
                item = GetGadgetState(#tree)
                Debug "Item " +Item 
                If GetGadgetItemData(#tree, item) = 0
                  ;SetGadgetState(#tree, -1)
                EndIf
            EndSelect
            
        EndSelect
    EndSelect
    
  Until Exit
  
EndProcedure

Procedure Callback_Window(HWnd.i, uMsg.i, WParam.i, LParam.i)
  
  Protected *pNmhdr.NMHDR
  Protected *pNmtvcustomdraw.NMTVCUSTOMDRAW
  Protected *pNmtvitemchange.NMTVITEMCHANGE
  Protected Tv_Item.Tv_ItemEx
  Protected Gadget.i
  Protected ItemNr.i
  Protected ItemData.i
  Protected LoopCnt.i
  Protected HItem.i
  
  Select uMsg
    Case #WM_NOTIFY
      *pNmhdr = LParam
      
      Select *pNmhdr\code
        Case #NM_CUSTOMDRAW
          ; for *all* treegadgets - every item that has a #Null as data, do not draw checkbox...
          Gadget = GetDlgCtrlID_(*pNmhdr\hwndFrom)
          If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Tree
            *pNmtvcustomdraw = LParam
            ItemNr = *pNmtvcustomdraw\nmcd\lItemlParam
            
            ; #Null means no checkbox, so prevent the checkbox from displaying
            ItemData = GetGadgetItemData(Gadget, ItemNr)
            
            If Not ItemData
              Tv_Item\mask = #TVIF_HANDLE | #TVIF_STATE | #TVIF_STATEEX
              Tv_Item\hItem = GadgetItemID(Gadget, ItemNr)
              Tv_Item\state = 0
              Tv_Item\stateMask = #TVIS_STATEIMAGEMASK
              ;
              Tv_Item\uStateEx = #TVIS_EX_DISABLED
              
              SendMessage_(*pNmhdr\hwndFrom, #TVM_SETITEM, 0, @Tv_Item)
            EndIf
          EndIf
          
        Case #TVN_ITEMCHANGING
          ; for *all* treegadgets - every item that has a ??? as data, do not change checkbox...
          Gadget = GetDlgCtrlID_(*pNmhdr\hwndFrom)
          If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Tree
            
            *pNmtvitemchange = LParam
            ; Only look for state changes...
            If *pNmtvitemchange\uChanged = #TVIF_STATE And ((*pNmtvitemchange\uStateNew & #TVIS_STATEIMAGEMASK) <> (*pNmtvitemchange\uStateOld & #TVIS_STATEIMAGEMASK))
              
              ; Get the handle for the selected item and search the item *number*
              HItem = SendMessage_(*pNmhdr\hwndFrom, #TVM_GETNEXTITEM, #TVGN_CARET, *pNmtvitemchange\hItem)
              
              For LoopCnt = 0 To CountGadgetItems(Gadget) - 1
                If GadgetItemID(Gadget, LoopCnt) = HItem
                  ItemNr = LoopCnt
                  
                  ;Debug "uStateOld = " + Str(((*pNmtvitemchange\uStateOld & #TVIS_STATEIMAGEMASK) >> 12) & 15)
                  ;Debug "uStateNew = " + Str(((*pNmtvitemchange\uStateNew & #TVIS_STATEIMAGEMASK) >> 12) & 15)
                  Debug "----------------"
                  Debug "ItemNr = " + Str(ItemNr)
                  Debug "uStateOld = " + Str(*pNmtvitemchange\uStateOld)
                  Debug "uStateNew = " + Str(*pNmtvitemchange\uStateNew)
                EndIf
              Next
              
              ;If ItemNr = 2 : ProcedureReturn #True : EndIf
            EndIf
          EndIf
      EndSelect
  EndSelect
  
  ProcedureReturn #PB_ProcessPureBasicEvents
  
EndProcedure

UsePNGImageDecoder()

LoadImage(#Image1, #PB_Compiler_Home + "examples\sources\Data\Toolbar\Open.png")
LoadImage(#Image2, #PB_Compiler_Home + "examples\sources\Data\Toolbar\Save.png")

Main()
End
My Projects ThreadToGUI / OOP-BaseClass / EventDesigner V3
PB v3.30 / v5.75 - OS Mac Mini OSX 10.xx - VM Window Pro / Linux Ubuntu
Downloads on my Webspace / OneDrive
User avatar
Martt
User
User
Posts: 15
Joined: Sat Dec 23, 2017 4:03 pm
Location: The Netherlands

Re: Disable TreeGadget Item

Post by Martt »

Yes, I found the pb event way. Works very well. I like the api way better, for my project anyway.

You can disable a item, but the user can still interact. It is only gray. This one does work:

Code: Select all

EnableExplicit

Enumeration
    #Window
    #Tree
    #Image1
    #Image2
EndEnumeration

; These ones are not present in 5.62
CompilerIf Not Defined(TVN_ITEMCHANGING, #PB_Constant)
    #TVN_ITEMCHANGING   = -417
    #TVIS_EX_DISABLED   = $0002
    #TVIF_STATEEX       = $0100
CompilerEndIf

Structure StructTVITEMEX Align #PB_Structure_AlignC 
    mask.l
    PB_Alignment1.b[4]
    hItem.i
    state.l
    stateMask.l
    *pszText
    cchTextMax.l
    iImage.l
    iSelectedImage.l
    cChildren.l
    lParam.i
    iIntegral.l
    uStateEx.l
    hwnd.i
    iExpandedImage.l
    iReserved.l
EndStructure

Structure StructNMHDR Align #PB_Structure_AlignC 
    hwndFrom.i
    idFrom.i
    code.l
;    PB_Alignment1.b[4]
EndStructure

Structure StructNMTVITEMCHANGE Align #PB_Structure_AlignC 
    hdr.StructNMHDR
;    hdr.NMHDR
    uChanged.l
    hItem.i
    uStateNew.l
    uStateOld.l
    lParam.i
EndStructure

Declare Main()
Declare Callback_Window(HWnd.i, uMsg.i, WParam.i, LParam.i)

Procedure Main()

    Protected Exit.b
    Protected Event.i
    Protected Gadget.i
    Protected ItemNr.i
    Protected State.i
    Protected Tv_ItemEx.StructTVITEMEX

    OpenWindow(#Window, 0, 0, 250, 300, "Tree Demo", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    SetWindowCallback(@Callback_Window(), #Window)

    ; Tree ItemData : 
    ; ItemData = #Null => No checkbox
    ; ItemData = pointer to a structure => Checkbox

    TreeGadget(#Tree, 10, 10, 230, 280, #PB_Tree_AlwaysShowSelection | #PB_Tree_CheckBoxes | #PB_Tree_ThreeState)
    
    AddGadgetItem(#Tree, 0, "Parent 1", ImageID(#Image1), 0)
    SetGadgetItemData(#Tree, 0, #Null)

    AddGadgetItem(#Tree, 1, "Unlocked 1", ImageID(#Image2), 1)
    SetGadgetItemData(#Tree, 1, $ABBA1)
    AddGadgetItem(#Tree, 2, "Unlocked 2", ImageID(#Image2), 1)
    SetGadgetItemData(#Tree, 2, $ABBA2)
    
    AddGadgetItem(#Tree, 3, "Parent 2", ImageID(#Image1), 0)
    SetGadgetItemData(#Tree, 0, #Null)

    AddGadgetItem(#Tree, 4, "Locked Unchecked", ImageID(#Image2), 1)
    SetGadgetItemData(#Tree, 4, $DEADBEEF1)
    AddGadgetItem(#Tree, 5, "Locked Checked", ImageID(#Image2), 1)
    SetGadgetItemData(#Tree, 5, $DEADBEEF2)
    AddGadgetItem(#Tree, 6, "Locked In Between", ImageID(#Image2), 1)
    SetGadgetItemData(#Tree, 6, $DEADBEEF3)
    AddGadgetItem(#Tree, 7, "Disabled", ImageID(#Image2), 1)
    SetGadgetItemData(#Tree, 7, $DEADBEEF4)
    
    ; Expand the parents
    SetGadgetItemState(#Tree, 0, #PB_Tree_Expanded | #PB_Tree_Selected)
    SetGadgetItemState(#Tree, 3, #PB_Tree_Expanded | #PB_Tree_Selected)
    
    ; Set the locked items to the right state
    SetGadgetItemState(#Tree, 4, #PB_Tree_Collapsed)
    SetGadgetItemState(#Tree, 5, #PB_Tree_Collapsed | #PB_Tree_Checked)
    SetGadgetItemState(#Tree, 6, #PB_Tree_Collapsed | #PB_Tree_Inbetween)
    
    ; Make item 7 disabled
    Tv_ItemEx\mask = #TVIF_HANDLE | #TVIF_STATEEX
    Tv_ItemEx\hItem = GadgetItemID(#Tree, 7)
    Tv_ItemEx\uStateEx = #TVIS_EX_DISABLED
                            
    SendMessage_(GadgetID(#Tree), #TVM_SETITEM, 0, @Tv_ItemEx)

    Repeat
        Event = WaitWindowEvent()
    
        Select Event
            Case #PB_Event_CloseWindow
                Exit = #True
        EndSelect
    
    Until Exit
    
EndProcedure

Procedure Callback_Window(HWnd.i, uMsg.i, WParam.i, LParam.i)
    
    Protected *pNmhdr.StructNMHDR
    Protected *pNmtvcustomdraw.NMTVCUSTOMDRAW
    Protected *pNmtvitemchange.StructNMTVITEMCHANGE
    Protected Tv_ItemEx.StructTVITEMEX
    Protected Gadget.i
    Protected ItemNr.i
    Protected ItemData.i
    Protected LoopCnt.i
    Protected uStateOld.i
    Protected uStateNew.i
    
    Select uMsg
        Case #WM_NOTIFY
            *pNmhdr = LParam
            
            Select *pNmhdr\code
                Case #NM_CUSTOMDRAW
                    ; for *all* treegadgets - every item that has a #Null as data, do not draw checkbox...
                    Gadget = GetDlgCtrlID_(*pNmhdr\hwndFrom)
                    If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Tree
                        *pNmtvcustomdraw = LParam
                        ItemNr = *pNmtvcustomdraw\nmcd\lItemlParam
                
                        ; #Null means no checkbox, so prevent the checkbox from displaying
                        ItemData = GetGadgetItemData(Gadget, ItemNr)
                        
                        If Not ItemData
                            Tv_ItemEx\mask = #TVIF_HANDLE | #TVIF_STATE
                            Tv_ItemEx\hItem = GadgetItemID(Gadget, ItemNr)
                            Tv_ItemEx\state = 0
                            Tv_ItemEx\stateMask = #TVIS_STATEIMAGEMASK
                            
                            SendMessage_(*pNmhdr\hwndFrom, #TVM_SETITEM, 0, @Tv_ItemEx)
                        EndIf
                    EndIf
                    
                Case #TVN_ITEMCHANGING
                    ; for *all* treegadgets - every item that has a ??? in the structure field, do not change checkbox...
                    Gadget = GetDlgCtrlID_(*pNmhdr\hwndFrom)
                    If IsGadget(Gadget) And GadgetType(Gadget) = #PB_GadgetType_Tree
                    
                        *pNmtvitemchange = LParam
                        ; Only look for state changes...
                        If *pNmtvitemchange\uChanged = #TVIF_STATE And ((*pNmtvitemchange\uStateNew & #TVIS_STATEIMAGEMASK) <> (*pNmtvitemchange\uStateOld & #TVIS_STATEIMAGEMASK))
                            
                            ; Search the itemnr (reverse GadgetItemID)
                            For LoopCnt = 0 To CountGadgetItems(Gadget) - 1
                                If GadgetItemID(Gadget, LoopCnt) = *pNmtvitemchange\hItem
                                    ItemNr = LoopCnt
                                    uStateOld = ((*pNmtvitemchange\uStateOld & #TVIS_STATEIMAGEMASK) >> 12)
                                    uStateNew = ((*pNmtvitemchange\uStateNew & #TVIS_STATEIMAGEMASK) >> 12)

                                    ; Image 1 is the unchecked box
                                    ; Image 2 is the checked box
                                    ; Image 3 is the in between box
                                    
                                    Debug "----------------"
                                    Debug "uStateOld = " + Str(uStateOld)
                                    Debug "uStateNew = " + Str(uStateNew)
                                    Debug "ItemNr = " + Str(ItemNr)
                                    
                                    ; Prevent items 4-7 from changing (normally done from a field in a structure (itemdata)
                                    If ItemNr = 4 And uStateNew <> 1 : ProcedureReturn #True : EndIf
                                    If ItemNr = 5 And uStateNew <> 2 : ProcedureReturn #True : EndIf
                                    If ItemNr = 6 And uStateNew <> 3 : ProcedureReturn #True : EndIf
                                    If ItemNr = 7 And uStateNew <> 1 : ProcedureReturn #True : EndIf
                                EndIf
                            Next
                        EndIf
                    EndIf
            EndSelect
    EndSelect
    
    ProcedureReturn #PB_ProcessPureBasicEvents

EndProcedure

UsePNGImageDecoder() 

LoadImage(#Image1, #PB_Compiler_Home + "examples\sources\Data\Toolbar\Open.png")
LoadImage(#Image2, #PB_Compiler_Home + "examples\sources\Data\Toolbar\Save.png")

Main()
End
It would be nice if someone could explain why all the padding (PB_Alignment) is in the structures. And AlignC = align to the biggest structure member ? How does that work for NMTVITEMCHANGE which has NMHDR as a member ? :?: Maybe a onetime explanation and then a sticky ?

Hmmm. The one CompilerIf is wrong, should be 3 times that. And a break should be added in the for/next loop. But that is small stuff.
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4636
Joined: Sun Apr 12, 2009 6:27 am

Re: Disable TreeGadget Item

Post by RASHAD »

Hi RSBasic
Previous post updated

And thanks for the catch :)
Egypt my love
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: Disable TreeGadget Item

Post by srod »

You don't need the ALIGN if you switch some of the structure fields from .l to .i. Perhaps this is how they should be defined in the relevant PB resident files? Tested with PBx86 and PBx64.
I may look like a mule, but I'm not a complete ass.
User avatar
Martt
User
User
Posts: 15
Joined: Sat Dec 23, 2017 4:03 pm
Location: The Netherlands

Re: Disable TreeGadget Item

Post by Martt »

Nothing is changed from .i to .l.

This works fine

Code: Select all

Structure StructNMTVITEMCHANGE Align #PB_Structure_AlignC 
    hdr.NMHDR
    uChanged.l
    hItem.i
    uStateNew.l
    uStateOld.l
    lParam.i
EndStructure
This does not work:

Code: Select all

Structure StructNMTVITEMCHANGE; Align #PB_Structure_AlignC 
    hdr.NMHDR
    uChanged.l
    hItem.i
    uStateNew.l
    uStateOld.l
    lParam.i
EndStructure
And this is the original structure:

Code: Select all

Structure NMTVITEMCHANGE
    hdr.NMHDR
    uChanged.l
    hItem.i
    uStateNew.l
    uStateOld.l
    lParam.i
EndStructure
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: Disable TreeGadget Item

Post by srod »

What I am saying is that the C-alignment of structure fields will insert padding in effect turning some of the .l fields to .i (in terms of addressing at least) and my question was thus to wonder if Purebasic has the wrong definition of the NMTVITEMCHANGE structure in its resident files.

The following works fine here :

Code: Select all

Structure StructNMTVITEMCHANGE
    hdr.NMHDR
    uChanged.i
    hItem.i
    uStateNew.i
    uStateOld.i
    lParam.i
EndStructure
I may look like a mule, but I'm not a complete ass.
User avatar
nco2k
Addict
Addict
Posts: 1344
Joined: Mon Sep 15, 2003 5:55 am

Re: Disable TreeGadget Item

Post by nco2k »

srod wrote:The following works fine here
its still wrong though. the size should be 56 byte on 64bit, not 64 byte:

Code: Select all

Structure NMTVITEMCHANGE
  hdr.NMHDR
  uChanged.l
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    PB_Alignment.b[4]
  CompilerEndIf
  hItem.i
  uStateNew.l
  uStateOld.l
  lParam.i
EndStructure
c ya,
nco2k
If OSVersion() = #PB_OS_Windows_ME : End : EndIf
User avatar
Martt
User
User
Posts: 15
Joined: Sat Dec 23, 2017 4:03 pm
Location: The Netherlands

Re: Disable TreeGadget Item

Post by Martt »

Code: Select all

Structure StructNMTVITEMCHANGE Align #PB_Structure_AlignC 
    hdr.NMHDR
    uChanged.l
    hItem.i
    uStateNew.l
    uStateOld.l
    lParam.i
EndStructure

Structure Struct2NMTVITEMCHANGE
    hdr.NMHDR
    uChanged.l
    PB_Alignment.b[4]
    hItem.i
    uStateNew.l
    uStateOld.l
    lParam.i
EndStructure

Debug "With ALignC"
Debug OffsetOf(StructNMTVITEMCHANGE\hdr)
Debug OffsetOf(StructNMTVITEMCHANGE\uChanged)
Debug OffsetOf(StructNMTVITEMCHANGE\hItem)
Debug OffsetOf(StructNMTVITEMCHANGE\uStateNew)
Debug OffsetOf(StructNMTVITEMCHANGE\uStateOld)
Debug OffsetOf(StructNMTVITEMCHANGE\lParam)

Debug #CRLF$ + "Without ALignC"
Debug OffsetOf(NMTVITEMCHANGE\hdr)
Debug OffsetOf(NMTVITEMCHANGE\uChanged)
Debug OffsetOf(NMTVITEMCHANGE\hItem)
Debug OffsetOf(NMTVITEMCHANGE\uStateNew)
Debug OffsetOf(NMTVITEMCHANGE\uStateOld)
Debug OffsetOf(NMTVITEMCHANGE\lParam)

Debug #CRLF$ + "With Padding"
Debug OffsetOf(Struct2NMTVITEMCHANGE\hdr)
Debug OffsetOf(Struct2NMTVITEMCHANGE\uChanged)
Debug OffsetOf(Struct2NMTVITEMCHANGE\hItem)
Debug OffsetOf(Struct2NMTVITEMCHANGE\uStateNew)
Debug OffsetOf(Struct2NMTVITEMCHANGE\uStateOld)
Debug OffsetOf(Struct2NMTVITEMCHANGE\lParam)
x64 is correct, but x86 is wrong. The CompilerIf from Nico corrects that. Weird.
User avatar
nco2k
Addict
Addict
Posts: 1344
Joined: Mon Sep 15, 2003 5:55 am

Re: Disable TreeGadget Item

Post by nco2k »

why is that weird? its simple math and logic. i only did manually what #PB_Structure_AlignC would have done automatically.

c ya,
nco2k
If OSVersion() = #PB_OS_Windows_ME : End : EndIf
srod
PureBasic Expert
PureBasic Expert
Posts: 10589
Joined: Wed Oct 29, 2003 4:35 pm
Location: Beyond the pale...

Re: Disable TreeGadget Item

Post by srod »

Yes that makes more sense than what I was doing. :)
I may look like a mule, but I'm not a complete ass.
User avatar
Martt
User
User
Posts: 15
Joined: Sat Dec 23, 2017 4:03 pm
Location: The Netherlands

Re: Disable TreeGadget Item

Post by Martt »

Ok. Thanks for explaining that. Thanks to all who helped.
Post Reply