Moving items using drag and drop

At the window level, PowerBuilder provides functions and properties for dragging controls onto other controls. Within the TreeView, you can also let the user drag items onto other items. Users might drag items to sort them, move them to another branch, or put child items under a parent.

When you implement drag and drop as a way to move items, you decide whether the dragged item becomes a sibling or child of the target, whether the dragged item is moved or copied, and whether its children get moved with it.

There are several properties and events that you need to coordinate to implement drag and drop for items, as shown in Table 8-4.

Table 8-4: Drag-and-drop properties and events

Property or event

Setting or purpose

DragAuto property

TRUE or FALSE

If FALSE, you must call the Drag function in the BeginDrag event.

DisableDragDrop property

FALSE

DragIcon property

An appropriate icon or None!, which means the user drags an image of the item

BeginDrag event

Script for saving the handle of the dragged item and optionally preventing particular items from being dragged

DragWithin event

Script for highlighting drop targets

DragDrop event

Script for implementing the result of the drag operation

Example

The key to a successful drag-and-drop implementation is in the details. This section illustrates one way of moving items. In the example, the dragged item becomes a sibling of the drop target, inserted after it. All children of the item are moved with it and the original item is deleted.

A function called recursively moves the children, regardless of the number of levels. To prevent an endless loop, an item cannot become a child of itself. This means a drop target that is a child of the dragged item is not allowed.

BeginDrag event The script saves the handle of the dragged item in an instance variable:

ll_dragged_tvi_handle = handle

If you want to prevent some items from being dragged—such as items at a particular level—that code goes here too:

TreeViewItem tvi
This.GetItem(handle, tvi)
IF tvi.Level = 3 THEN This.Drag(Cancel!)

DragWithin event The script highlights the item under the cursor so the user can see each potential drop target. If only some items are drop targets, your script should check an item’s characteristics before highlighting it. In this example, you could check whether an item is a parent of the dragged item and highlight it only if it is not:

TreeViewItem tvi
This.GetItem(handle, tvi)
tvi.DropHighlighted = TRUE
This.SetItem(handle, tvi)

DragDrop event This script does all the work. It checks whether the item can be inserted at the selected location and inserts the dragged item in its new position—a sibling after the drop target. Then it calls a function that moves the children of the dragged item too:

TreeViewItem tvi_src, tvi_child
long h_parent, h_gparent, h_moved, h_child
integer rtn

// Get TreeViewItem for dragged item
This.GetItem(ll_dragged_tvi_handle, tvi_src)
// Don't allow moving an item into its own branch,
// that is, a child of itself
h_gparent = This.FindItem(ParentTreeItem!, handle)

DO WHILE h_gparent <> -1
   IF h_gparent = ll_dragged_tvi_handle THEN
      MessageBox("No Drag", &
      "Can't make an item a child of itself.")
      RETURN 0
   END IF
   
h_gparent=This.FindItem(ParentTreeItem!, h_gparent)
LOOP

// Get item parent for inserting
h_parent = This.FindItem(ParentTreeItem!, handle)

// Use 0 if no parent because target is at level 1
IF h_parent = -1 THEN h_parent = 0

// Insert item after drop target
h_moved = This.InsertItem(h_parent, handle, tvi_src)
IF h_moved = -1 THEN 
   MessageBox("No Dragging", "Could not move item.")
   RETURN 0
ELSE
   // Args: old parent, new parent
   rtn = uf_movechildren(ll_dragged_tvi_handle, &
      h_moved)

   / If all children are successfully moved,
   // delete original item
   IF rtn = 0 THEN
      This.DeleteItem(ll_dragged_tvi_handle)
   END IF

END IF

The DragDrop event script shown above calls the function uf_movechildren. The function calls itself recursively so that all the levels of children below the dragged item are moved:

// Function: uf_movechildren
// Arguments:
// oldparent - Handle of item whose children are
// being moved. Initially, the dragged item in its
// original position
//
// newparent - Handle of item to whom children are
// being moved. Initially, the dragged item in its
// new position.

long h_child, h_movedchild
TreeViewItem tvi

// Return -1 if any Insert action fails

// Are there any children?
h_child = tv_2.FindItem(ChildTreeItem!, oldparent)
IF h_child <> -1 THEN
   tv_2.GetItem(h_child, tvi)
   h_movedchild = tv_2.InsertItemLast(newparent, tvi)
   IF h_movedchild = -1 THEN RETURN -1

   // Move the children of the child that was found
   uf_movechildren(h_child, h_movedchild)

   // Check for more children at the original level
   h_child = tv_2.FindItem(NextTreeItem!, h_child)
   DO WHILE h_child <> -1
      tv_2.GetItem(h_child, tvi)
      h_movedchild= tv_2.InsertItemLast(newparent,tvi)
      IF h_movedchild = -1 THEN RETURN -1   
      uf_movechildren(h_child, h_movedchild)

      // Any more children at original level?
      h_child = tv_2.FindItem(NextTreeItem!, h_child)
   LOOP
END IF
RETURN 0 // Success, all children moved