Page 1 of 1

D'n'D & Treegadget

Posted: Wed Aug 08, 2007 11:22 am
by Progi1984
Hi, I search a piece of code which explains me how can I use Drag and Drop with TreeGadget for sorting differents items...

Thank you in advance...

Posted: Wed Aug 08, 2007 7:17 pm
by eJan
Look at Freak's Codesnippets 'TreeDrag': http://freak.purearea.net/code/code.html

Posted: Thu Aug 09, 2007 1:38 am
by freak
It is actually much easier now with 4.10 and the Drag & Drop library.
Here is a short example (this code is fully crossplatform)

The Drag & Drop is actually the easiest part here. What needs a bit more code
is the actual moving of the old item to the new place, because we need to move
nodes including all their children.

Run the code and drag around the items. Items named "Directory" can contain
other items.

Code: Select all

; ------------------------------------------------------------------
;
;   Example for Drag & Drop inside a TreeGadget to reorder items.
;
;   Requires 4.10 beta or newer to run
;
; ------------------------------------------------------------------

#Tree = 0
#Window = 0

; If you want to do multiple drag&drop in different Gadgets of your program,
; you can use different values here so the events do not collide.
; (ie so the user cannot drag to the wrong gadget)
#PrivateType = 0

If OpenWindow(#Window, 0, 0, 300, 500, "TreeGadget Drag & Drop", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
  CreateGadgetList(WindowID(#Window))
  TreeGadget(#Tree, 10, 10, 280, 480)
  
  ; Add some items. We will be able to move items into the
  ; "Directory" ones.
  ;
  For i = 0 To 20
    If i % 5 = 0
      AddGadgetItem(#Tree, -1, "Directory" + Str(i), 0, 0)
    Else
      AddGadgetItem(#Tree, -1, "Item" + Str(i), 0, 0)
    EndIf
  Next i
  
  ; this enables dropping our private type with a move operation
  EnableGadgetDrop(#Tree, #PB_Drop_Private, #PB_Drag_Move, #PrivateType)
  
  Repeat
    Event = WaitWindowEvent()
    
    If Event = #PB_Event_Gadget
      ; 
      ; The DragStart event tells us that the user clicked on an item and
      ; wants to start draging it. We save this item for later use and
      ; start our private drag
      ;
      If EventGadget() = #Tree And EventType() = #PB_EventType_DragStart
        SourceItem = GetGadgetState(#Tree)
        DragPrivate(#PrivateType, #PB_Drag_Move)
      EndIf
    
    ElseIf Event = #PB_Event_GadgetDrop
      ;
      ; Here we get a drop event. Make sure it is on the right gadget and of right type,
      ; especially if you have multiple Drag & Drop stuff in your program.
      ;
      If EventGadget() = #Tree And EventDropType() = #PB_Drop_Private And EventDropPrivate() = #PrivateType
        TargetItem = GetGadgetState(#Tree)        
        
        ; nothing to do if source and target are equal
        ;
        If SourceItem <> TargetItem        
        
          ; Find out to which index and sublevel to move the item
          ;
          If TargetItem = -1
            ; if dropped on the empty area, append at the end
            TargetItem  = CountGadgetItems(#Tree)
            TargetLevel = 0
            
          ElseIf Left(GetGadgetItemText(#Tree, TargetItem), 4) = "Item"      
            ; if dropped on an "Item", move right after this item
            TargetLevel = GetGadgetItemAttribute(#Tree, TargetItem, #PB_Tree_SubLevel)
            TargetItem  + 1
          
          Else
            ; if dropped on a "Directory", move into the directory and to the end of it
            ; all this can be easily done by examining the sublevel
            TargetLevel = GetGadgetItemAttribute(#Tree, TargetItem, #PB_Tree_SubLevel) + 1
            TargetItem  + 1
            While GetGadgetItemAttribute(#Tree, TargetItem, #PB_Tree_SubLevel) >= TargetLevel
              TargetItem + 1
            Wend
            
          EndIf
          
          ; Find out how many children our source item has, as we want to
          ; move them all. If you do not allow moving of directory nodes, it gets 
          ; much simpler as there is only one delete + add then.
          ;
          ; childnodes are all directly following ones with a higher level
          ;
          SourceLevel = GetGadgetItemAttribute(#Tree, SourceItem, #PB_Tree_SubLevel)          
          ChildCount  = 0
          For i = SourceItem+1 To CountGadgetItems(#Tree)-1
            If GetGadgetItemAttribute(#Tree, i, #PB_Tree_SubLevel) > SourceLevel 
              ChildCount + 1
            Else
              Break
            EndIf
          Next i
          
          ; Note: because by adding new items, the index of our old ones changes,
          ;   we need to make a distinction here wether we move before or after our old item.
          ;   Note also that we cannot move an item into one of its childs, which we
          ;   prevent by this check as well.
          ;
          ; We copy first the source item and all children to the new location and then
          ; delete the source item.
          ;
          If TargetItem > SourceItem + ChildCount + 1
            ;
            ; The target index is higher than the source one, so the source items are not
            ; affected by adding the new items in this case...
            ;
            For i = 0 To ChildCount  
              ; copy everything here (also colors and GetGadgetItemData() etc if you use that)                
              Text$ = GetGadgetItemText(#Tree, SourceItem+i)              
              Level = GetGadgetItemAttribute(#Tree, SourceItem+i, #PB_Tree_SubLevel) - SourceLevel + TargetLevel
              AddGadgetItem(#Tree, TargetItem+i, Text$, 0, Level)              
            Next i
            
            ; We apply the state of each item AFTER all items are copied.
            ; This must be in a separate loop, as else the "expanded" state of the items is 
            ; not preserved, as the childs were not added yet in the above loop.
            For i = 0 To ChildCount
              SetGadgetItemState(#Tree, TargetItem+i, GetGadgetItemState(#Tree, SourceItem+i))
            Next i
            
            ; remove the source item. This automatically removes all children as well.
            RemoveGadgetItem(#Tree, SourceItem)
            
            ; select the target. Note that the index is now 'ChildCount+1' less
            ; because of the remove of the source which was before the target
            SetGadgetState(#Tree, TargetItem-ChildCount-1)
          
          ElseIf TargetItem <= SourceItem
            ; 
            ; Second case: Target is lower than source. This means that each time
            ; we add a target item, the source items index increases by 1 as well,
            ; this is why we read the source items with the "SourceItem+i*2"
            ;
            For i = 0 To ChildCount
              Text$ = GetGadgetItemText(#Tree, SourceItem+i*2)
              Level = GetGadgetItemAttribute(#Tree, SourceItem+i*2, #PB_Tree_SubLevel) - SourceLevel + TargetLevel
              AddGadgetItem(#Tree, TargetItem+i, Text$, 0, Level)
            Next i
            
            ; Loop for the states. Note that here the index of the sourceitems is 
            ; 'ChildCount+1' higher than before because of the added targets
            ;
            For i = 0 To ChildCount
              SetGadgetItemState(#Tree, TargetItem+i, GetGadgetItemState(#Tree, SourceItem+ChildCount+1+i))
            Next i            
            
            ; remove source and select target. Here the target index is not affected by the remove as it is lower
            RemoveGadgetItem(#Tree, SourceItem+ChildCount+1)          
            SetGadgetState(#Tree, TargetItem)
          
          EndIf
          
        EndIf      
      EndIf
    
    EndIf
    
  Until Event = #PB_Event_CloseWindow
EndIf

End

Posted: Thu Aug 09, 2007 7:45 am
by Progi1984
It's exactly the code what i need...

Very thank you dear Freak !

Posted: Fri Jun 06, 2008 10:36 pm
by Foz
Thanks Freak, you just saved me creating a new topic asking an already answered question :lol:

Posted: Sat Jun 07, 2008 12:06 am
by DoubleDutch
I've used this example loads of times now - it's a great little routine. :D

Re: D'n'D & Treegadget

Posted: Mon Jul 31, 2017 3:13 pm
by Klonk
Exactly what I searched for.
Many thanks.

BTW: Works also on PB 5.60 (Just CreateGadgetList(9 has to be removed)

Just one thing: Any idea how to get it dropped at the very first position?

The only thing i can think of is: always inserting draged stuff before the item you droped on. But this does not work with directories...

Re: D'n'D & Treegadget

Posted: Wed Aug 02, 2017 7:18 am
by Klonk
OK, one more thing is missing: Handling of item images.

BTW: Is there some way to obtain the ImageID of a Treeitem so it can be reused in Addgadgetitem?