Drag'nDrop manual sort for TreeGadget
Posted: Fri Aug 27, 2010 12:43 pm
Code: Select all
; Drag'nDrop manual sort for TreeGadget
; By Zapman
;
; This demo code shows how to implemente a drag'n drop ability for TreeGadget
; in order to allow the user to sort manually the gadget content by draging
; it elements.
;
Procedure GetFolderIconHandle()
*sfi.SHFILEINFO = AllocateMemory(SizeOf(SHFILEINFO))
If SHGetFileInfo_("aa.txt",#FILE_ATTRIBUTE_DIRECTORY,*sfi, SizeOf(SHFILEINFO), #SHGFI_ICON| #SHGFI_SMALLICON | #SHGFI_USEFILEATTRIBUTES)
hFolderIcon = *sfi\hIcon
EndIf
FreeMemory(*sfi)
ProcedureReturn hFolderIcon
EndProcedure
Procedure GetTreeItemIndexFromHandle(IHandle,TreeGadgetID)
; By Timo 'fr34k' Harter and Zapman
dItem = 0
hItem = SendMessage_(TreeGadgetID, #TVM_GETNEXTITEM, #TVGN_ROOT, 0)
While IHandle And hItem And hItem <> IHandle
hItem2 = SendMessage_(TreeGadgetID, #TVM_GETNEXTITEM, #TVGN_CHILD, hItem)
Repeat
If hItem2 = #Null: hItem2 = SendMessage_(TreeGadgetID, #TVM_GETNEXTITEM, #TVGN_NEXT, hItem): EndIf
If hItem2 = #Null: hItem = SendMessage_(TreeGadgetID, #TVM_GETNEXTITEM, #TVGN_PARENT, hItem): EndIf
Until hItem2 <> #Null
hItem = hItem2
dItem + 1
Wend
ProcedureReturn dItem
EndProcedure
;
Procedure HighLightTreeItem(TreeGadgetID,hItem,HighLight)
; By Timo 'fr34k' Harter and Zapman
If HighLight : HighLight = #TVIS_DROPHILITED : EndIf ; to be sure that te right value is used
; We'll send a #TVM_SETITEM message. We only need to change the
; item state, so #TVIF_STATE is enough for the mask.
pitem.TV_ITEM
pitem\mask = #TVIF_STATE
pitem\hItem = hItem
; 'stateMask' determines, what will be changed, and 'state' sets the actual value,
; so here, we set the TVIS_DROPHILITED state flag to HighLight.
pitem\state = HighLight
pitem\stateMask = #TVIS_DROPHILITED
Result = SendMessage_(TreeGadgetID, #TVM_SETITEM, 0, @pitem)
; a redraw is neccesary here to avoid problems.
RedrawWindow_(TreeGadgetID, 0, 0, #RDW_UPDATENOW)
ProcedureReturn Result
EndProcedure
;
Procedure GetTreeItemIconIndex(TreeGadget,hitem)
; by Zapman
tvitem.TV_ITEM\mask=#TVIF_IMAGE
tvitem\hitem=hitem
If SendMessage_(GadgetID(TreeGadget), #TVM_GETITEM,0,@tvitem) ; get the icon index from the item
ProcedureReturn tvitem\iImage
Else
ProcedureReturn -1
EndIf
EndProcedure
;
Procedure SetTreeItemIconIndex(TreeGadget,hitem,iconeIndex)
; by Zapman
tvitem.TV_ITEM\mask=#TVIF_IMAGE|#TVIF_SELECTEDIMAGE
tvitem\hitem=hitem
tvitem\iImage= iconeIndex
tvitem\iSelectedImage = iconeIndex
result = SendMessage_(GadgetID(TreeGadget), #TVM_SETITEM,0,@tvitem) ; set the icon index from the item
ProcedureReturn result
EndProcedure
;
Procedure InvertTreeGadgetItems(TreeGadget,p1,p2)
t1$=GetGadgetItemText(TreeGadget,p1)
t2$=GetGadgetItemText(TreeGadget,p2)
SetGadgetItemText(TreeGadget,p1,t2$)
SetGadgetItemText(TreeGadget,p2,t1$)
iconindex1 = GetTreeItemIconIndex(TreeGadget,GadgetItemID(TreeGadget,p1)); get the icon from the first item
iconindex2 = GetTreeItemIconIndex(TreeGadget,GadgetItemID(TreeGadget,p2)); get the icon from the second item
SetTreeItemIconIndex(TreeGadget,GadgetItemID(TreeGadget,p1),iconindex2); set the icon for the first item
SetTreeItemIconIndex(TreeGadget,GadgetItemID(TreeGadget,p2),iconindex1); set the icon for the second item
EndProcedure
;
;
Global DD_TreeGadget
Procedure DD_TreeCallback(Window, Message, wParam, lParam)
; By Timo 'fr34k' Harter for ListView Gadget
; Adapted to TreeView by Zapman
Shared OldTreeItem,TreeIsDraging,DraggedItemsList$,DragImageList
result = #PB_ProcessPureBasicEvents
; The first message we need is #TVN_BEGINDRAG. This is a so called
; Notification message. This means, that you don't get a message called
; #TVN_BEGINDRAG, but you get a #WM_NOTIFY message, with further information,
; that tells you, that it is #TVN_BEGINDRAG
If Message = #WM_NOTIFY
; The lparam paramenter contains a pointer to a NMHDR structure. There is
; the information about the Message
*nmhdr.NMHDR = lParam
; The 'hwndfrom' member tells you, which Gadget actually sent the message
; This must be always checked to be sure, it is the right gadget.
; We allow only our own Gadgets:
If *nmhdr\hwndfrom = GadgetID(DD_TreeGadget)
; The 'code' member contains the real message, in our case #TVN_BEGINDRAG
If *nmhdr\code = #TVN_BEGINDRAG
; Ok, for the #TVN_BEGINDRAG notification message, lParam is a pointer to a
; NMTREEVIEW Structure. You might think that this can't be, as lParam allready
; pointed to a NMHDR structure, but this is correct, as NMTREEVIEW has a
; NMHDR structure inside, it only adds more members to it.
*nmv.NMTREEVIEW = lParam
; The 'hItem' member contains the index of the source item, which we will need
; later.
TreeItemSource = *nmv\itemNew\hItem
OldTreeItem = TreeItemSource ; will be used to unhilite the item which was hilited
; By sending a #TVM_CREATEDRAGIMAGE message to the gadget, we create an ImageList
; that contains just one image, the drag&drop image. We will need the Imagelist WinAPI
; commands to work with that one later.
HighLightTreeItem(GadgetID(DD_TreeGadget),TreeItemSource,0)
HighLightTreeItem(GadgetID(DD_TreeGadget),GadgetItemID(DD_TreeGadget,GetGadgetState(DD_TreeGadget)),0)
DragImageList = SendMessage_(GadgetID(DD_TreeGadget), #TVM_CREATEDRAGIMAGE, 0,TreeItemSource)
;
; If the user is draging a node,
; we'll modify the image by adding all the items contained by the node
LImage = 400
HImage = 600
Pb_Image = CreateImage(#PB_Any,LImage,HImage)
Font1 = LoadFont(#PB_Any, "Tahoma" , 8)
StartDrawing(ImageOutput(Pb_Image))
DrawingFont(FontID(Font1))
Box(0, 0, LImage, HImage ,RGB(255,255,255))
ItemIndex = GetTreeItemIndexFromHandle(TreeItemSource,GadgetID(DD_TreeGadget))
ItemText$ = GetGadgetItemText(DD_TreeGadget,ItemIndex,0)
DraggedItemsList$ = Str(ItemIndex)+Chr(13)+ReplaceString(ItemText$,Chr(13),"")+Chr(13)+"0"+Chr(13)
DrawText(0,0,ItemText$,0,RGB(255,255,255))
dep = TextWidth(ItemText$)
posvert=0
hcar = 14
hItem = SendMessage_(GadgetID(DD_TreeGadget), #TVM_GETNEXTITEM, #TVGN_CHILD, TreeItemSource)
If hItem
level = 1
Repeat
posvert + hcar
ItemIndex = GetTreeItemIndexFromHandle(hItem,GadgetID(DD_TreeGadget))
ItemText$ = GetGadgetItemText(DD_TreeGadget,ItemIndex,0)
DraggedItemsList$ + Str(ItemIndex)+Chr(13)+ReplaceString(ItemText$,Chr(13),"")+Chr(13)+Str(level)+Chr(13)
DrawText(level*20,posvert,ItemText$,0,RGB(255,255,255))
nWidth = TextWidth(ItemText$)+(level*20)
If nWidth>dep ; calculate max text width for our draging image
dep = nWidth
EndIf
hItem2 = SendMessage_(GadgetID(DD_TreeGadget), #TVM_GETNEXTITEM, #TVGN_CHILD, hItem) ; is the next item a child ?
If hItem2
level + 1
hItem = hItem2
Else
hItem2 = SendMessage_(GadgetID(DD_TreeGadget), #TVM_GETNEXTITEM, #TVGN_NEXT, hItem) ; is it a same level item ?
If hItem2
hItem = hItem2
Else
hItem = SendMessage_(GadgetID(DD_TreeGadget), #TVM_GETNEXTITEM,#TVGN_PARENT, hItem) ; get the parent of our item
hItem = SendMessage_(GadgetID(DD_TreeGadget), #TVM_GETNEXTITEM, #TVGN_NEXT, hItem) ; and go to the next
level - 1
EndIf
EndIf
Until level = 0 Or hItem = 0
EndIf
DraggedItemsList$ + "Store_end" + Chr(13)
StopDrawing()
FreeFont(Font1)
ImageList_SetIconSize_(DragImageList,dep+5,posvert+hcar)
ImageList_AddMasked_(DragImageList,ImageID(Pb_Image),RGB(255,255,255))
FreeImage(Pb_Image)
If DragImageList
; This begins the drag&drop operation by displaying the image
If ImageList_BeginDrag_(DragImageList , 0, 0, 0)
; this command shows or hides the drag&drop image. Or course,
; we want to see it now :)
ImageList_DragShowNolock_(#True)
; this causes windows to send all mouse messages to our window,
; even if the mouse is outside of our window. This is neccesary,
; because we need to detect the mouseup event to abort the
; operation, even if the mouse is outside our window.
SetCapture_(GetParent_(GadgetID(DD_TreeGadget)))
; hide the standard cursor
ShowCursor_(#False)
; When processing the other events, we need to know whether something
; is being draged or not.
TreeIsDraging = #True
EndIf ; check for ImageList_BeginDrag_()
EndIf ; check for DragImageList
EndIf ; check for #TVN_BEGINDRAG
EndIf ; check for right gadget
; The next message we need is #WM_MOUSEMOVE, to update the mouse.
; But only if TreeIsDraging is #TRUE.
ElseIf Message = #WM_MOUSEMOVE And TreeIsDraging
; now we hide the image to avoid redrawn problems.
ImageList_DragShowNolock_(#False)
; the next thing we do is hilighting the target item in the target gadget.
GetWindowRect_(GadgetID(DD_TreeGadget),re.RECT)
GetCursorPos_(pt.POINT)
If PtInRect_(@re, pt\x | (pt\y<<32))
; What we need next is the Item, that is currently under the mouse cursor.
; the #TVM_HITTEST message does that test for us.
; here, we need coordinates relative to the top/left corner of our target
; Gadget, so we substract the Gadget position from our window coordinates
; When #EM_MOUSEMOVE is sent, the lParam parameter contains
; the x coordinate in the low-word and the y coordinate in the high-word, so we
; just use them here:
MouseX = lParam & $FFFF
MouseY = lParam >> 16
hittestinfo.TV_HITTESTINFO
hittestinfo\pt\x = MouseX - GadgetX(DD_TreeGadget)
hittestinfo\pt\y = MouseY - GadgetY(DD_TreeGadget)
; find the item, if none is under the cursor, we get -1, and the next
; SendMessage won't work, and nothing is hilighted.
TargetItem = SendMessage_(GadgetID(DD_TreeGadget), #TVM_HITTEST, 0, @hittestinfo)
If TargetItem<>-1 And TargetItem<>OldTreeItem
If OldTreeItem
HighLightTreeItem(GadgetID(DD_TreeGadget),OldTreeItem,0) ; Unhilighte the precedent hilighted item
EndIf
OldTreeItem = TargetItem
HighLightTreeItem(GadgetID(DD_TreeGadget),OldTreeItem,1)
EndIf
Else ; scroll treegadget if mouse is upper or lower than it
GetWindowRect_(GadgetID(DD_TreeGadget),re.RECT)
GetCursorPos_(@MP.POINT)
If MP\y < re\top
SendMessage_(GadgetID(DD_TreeGadget), #WM_VSCROLL, #SB_LINEUP, 0);
EndIf
If MP\y > re\bottom
SendMessage_(GadgetID(DD_TreeGadget), #WM_VSCROLL, #SB_LINEDOWN, 0);
EndIf
EndIf
; now we can show the drag image again:
ImageList_DragShowNolock_(#True)
; this moves the drag Image.
GetCursorPos_(@MP.Point)
ImageList_DragMove_(MP\x, MP\y-7)
; that's all to do while moving the mouse. Of course, there can be more checks added,
; and for example, a different cursor displayed, if you don't want the source to
; be draged to the current target and so on, but i think this is enough for a basic
; example.
; next is the end of the drag&drop operation... when the user releases the mouse.
ElseIf Message = #WM_LBUTTONUP And TreeIsDraging
; first, we release all we did, to unlock the mouse.
; mouse messages will be sent to other windows again
ReleaseCapture_()
; and drag&drop (remove the image)
ImageList_EndDrag_()
; free the image list
ImageList_Destroy_(DragImageList)
; show cursor again
ShowCursor_(#True)
; no draging in progress now.
TreeIsDraging = #False
; next, we unhilight the last target item
If OldTreeItem
HighLightTreeItem(GadgetID(DD_TreeGadget),OldTreeItem,0)
EndIf
GetWindowRect_(GadgetID(DD_TreeGadget),re.RECT)
GetCursorPos_(pt.POINT)
If PtInRect_(@re, pt\x | (pt\y<<32))
; now, move the items as demanded by the user:
;
; examine items at old positions
;
nbitems = 0
FIndex = -1
ct = 0
Repeat
ct + 1
LValue$ = StringField(DraggedItemsList$,ct,Chr(13))
If LValue$ <> "Store_end"
ItemIndex = Val(LValue$)
If FIndex = -1 : FIndex = ItemIndex: EndIf
ct + 2
nbitems + 1
EndIf
Until LValue$ = "Store_end"
;
; add items at new position
;
nPosition = GetTreeItemIndexFromHandle(OldTreeItem,GadgetID(DD_TreeGadget))+1
If nPosition>FIndex And nPosition<(FIndex+nbitems+1)
Debug "Trying to put a node inside its own content !!!!"
Else
mnPosition = nPosition
nLevel = GetGadgetItemAttribute(DD_TreeGadget,nPosition-1,#PB_Tree_SubLevel)
iconindex = GetTreeItemIconIndex(DD_TreeGadget,GadgetItemID(DD_TreeGadget,nPosition-1))
If iconindex And iconindex<>-1 ; get the icon from the target item
Debug "target is a folder" ; if there is one, we suppose that the item is a folder (a potential node)
nLevel + 1
EndIf
ct = 0
Repeat
ct + 1
LValue$ = StringField(DraggedItemsList$,ct,Chr(13))
If LValue$ <> "Store_end"
ItemIndex = Val(LValue$)
ct + 1
ItemText$ = StringField(DraggedItemsList$,ct,Chr(13))
ct + 1
level = Val(StringField(DraggedItemsList$,ct,Chr(13)))
If FIndex>= mnPosition
ItemIndex + nPosition - mnPosition
EndIf
iconindex = GetTreeItemIconIndex(DD_TreeGadget,GadgetItemID(DD_TreeGadget,ItemIndex)); get the icon from the old item
AddGadgetItem(DD_TreeGadget, nPosition, ItemText$,0,level+nLevel)
SetTreeItemIconIndex(DD_TreeGadget,GadgetItemID(DD_TreeGadget,nPosition),iconindex); set the icon for the new item
nPosition + 1
EndIf
Until LValue$ = "Store_end"
If FIndex>= mnPosition ; position has to be adjusted if items to delete are after added items
FIndex + nbitems
nPosition = mnPosition
Else
nPosition = mnPosition-nbitems
EndIf
RemoveGadgetItem(DD_TreeGadget,FIndex)
SetGadgetState(DD_TreeGadget,nPosition)
SetActiveGadget(DD_TreeGadget)
EndIf ; IsMouseOver(GadgetID(DD_TreeGadget))
EndIf ; If Message = #WM_LBUTTONUP And TreeIsDraging
EndIf ; check Message
ProcedureReturn Result
EndProcedure
;
Global hFolderIcon
Procedure Enable_DragnDrop_On_TreeGadget(Window,TreeGadget)
DD_TreeGadget = TreeGadget
hWndTV = GadgetID(TreeGadget)
Style = GetWindowLong_(hWndTV, #GWL_STYLE)
Style = Style & (~#TVS_DISABLEDRAGDROP)
SetWindowLong_(hWndTV, #GWL_STYLE, Style) ; enable drag'n drop for the gadget
;
hFolderIcon = GetFolderIconHandle() ; get a folder icon to use later in our list
;
SendMessage_(hWndTV, #TVM_GETIMAGELIST,#TVSIL_NORMAL,imageList)
If imageList = 0 ; The TreeGadget has no image list!! We need one, so we'll create one.
AddGadgetItem(TreeGadget, -1, "ToDelete", hFolderIcon,0) ; PB will automatically create an image list if we add an item with an icon
RemoveGadgetItem(TreeGadget, CountGadgetItems(TreeGadget)-1) ; OK, we can now delete this item
EndIf
;
SetWindowCallback(@DD_TreeCallback(),Window)
EndProcedure
; Window und Gadget...
winMain = OpenWindow(#PB_Any, 0, 0, 350, 260, "Drag'n Drop sorting with a TreeView", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
TreeGadget = TreeGadget(#PB_Any, 5, 5, 200, 250,#PB_Tree_AlwaysShowSelection)
Enable_DragnDrop_On_TreeGadget(winMain,TreeGadget)
ItemString = StringGadget(#PB_Any,220,5,120,20,"")
TextGadget = TextGadget(#PB_Any, 220, 50, 120, 250,"You can sort this treeview manually by draging items from one position to another one.")
PosVert = 160
BNewFolder = ButtonGadget(#PB_Any,220,PosVert,120,20,"New Folder")
PosVert + 25
BNewItem = ButtonGadget(#PB_Any,220,PosVert,120,20,"New Item")
PosVert + 25
BDelete = ButtonGadget(#PB_Any,220,PosVert,120,20,"Delete")
PosVert + 25
BSort = ButtonGadget(#PB_Any,220,PosVert,120,20,"Alphabetical Sort")
AddGadgetItem(TreeGadget, -1, "Item1"+Chr(13), 0,0)
AddGadgetItem(TreeGadget, -1, "Folder#1", hFolderIcon,0)
AddGadgetItem(TreeGadget, -1, "Item11",0,1)
AddGadgetItem(TreeGadget, -1, "Item12",0,1)
AddGadgetItem(TreeGadget, -1, "Folder#12",hFolderIcon,1)
AddGadgetItem(TreeGadget, -1, "Item121",0,2)
AddGadgetItem(TreeGadget, -1, "Item122",0,2)
AddGadgetItem(TreeGadget, -1, "Folder#2", hFolderIcon,0)
AddGadgetItem(TreeGadget, -1, "Itemz11",0,1)
AddGadgetItem(TreeGadget, -1, "zItem1a",0,1)
For ct = 1 To 20
AddGadgetItem(TreeGadget, -1, "Item"+Str(ct+1),0,0)
Next
SetGadgetState(TreeGadget,0)
SetGadgetText(ItemString,GetGadgetText(TreeGadget))
SendMessage_(GadgetID(ItemString), #EM_SETSEL, 0, Len(GetGadgetText(TreeGadget)))
SetActiveGadget(ItemString)
Repeat
Event = WaitWindowEvent()
If Event = #PB_Event_Gadget
If EventGadget() = TreeGadget
ItemText$ = GetGadgetItemText(TreeGadget,GetGadgetState(TreeGadget))
SetGadgetText(ItemString,ItemText$)
SendMessage_(GadgetID(ItemString), #EM_SETSEL, 0, Len(ItemText$))
EndIf
If EventGadget() = ItemString And EventType() = #PB_EventType_Change
SetGadgetItemText(TreeGadget,GetGadgetState(TreeGadget),GetGadgetText(ItemString))
ElseIf EventGadget() = BNewFolder Or EventGadget() = BNewItem
IIndex = GetGadgetState(TreeGadget)+1
If EventGadget() = BNewFolder
AddGadgetItem(TreeGadget,IIndex,"New folder",hFolderIcon)
Else
AddGadgetItem(TreeGadget,IIndex,"New item")
EndIf
SetGadgetState(TreeGadget,IIndex)
ItemText$ = GetGadgetItemText(TreeGadget,IIndex)
SetGadgetText(ItemString,ItemText$)
SendMessage_(GadgetID(ItemString), #EM_SETSEL, 0, Len(ItemText$))
SetActiveGadget(ItemString)
ElseIf EventGadget() = BDelete
IIndex = GetGadgetState(TreeGadget)
RemoveGadgetItem(TreeGadget, IIndex)
SetGadgetState(TreeGadget,IIndex)
SetGadgetText(ItemString,ItemText$)
SendMessage_(GadgetID(ItemString), #EM_SETSEL, 0, Len(ItemText$))
SetActiveGadget(TreeGadget)
ElseIf EventGadget() = BSort
; Windows API has a sort function for treeview (#TVM_SORTCHILDREN message)
; but we can't use it because the PureBasic element index is not sorted with the elements.
; So, we recreate a complete sort function for PB treegadget.
lastitem = CountGadgetItems(TreeGadget)-1
maxlevel = 0
For clevel = 0 To maxlevel
For i=0 To lastitem-1
nlevel = GetGadgetItemAttribute(TreeGadget,i,#PB_Tree_SubLevel)
If nlevel>maxlevel : maxlevel = nlevel : EndIf
If nlevel = clevel
min=i
For j=i+1 To lastitem
If GetGadgetItemText(TreeGadget,j)<GetGadgetItemText(TreeGadget,min) And GetGadgetItemAttribute(TreeGadget,j,#PB_Tree_SubLevel)=clevel
min=j
EndIf
If GetGadgetItemAttribute(TreeGadget,j,#PB_Tree_SubLevel)<nlevel
j = lastitem
EndIf
Next
If min<>i
For b = 1 To 2
storeitems$ = ""
If b = 1
posd = i
Else
posd = min
EndIf
; Store the node content
ct = 1
While GetGadgetItemAttribute(TreeGadget,posd+ct,#PB_Tree_SubLevel)>clevel
storeitems$ + GetGadgetItemText(TreeGadget,posd+ct)+Chr(13)+Str(GetGadgetItemAttribute(TreeGadget,posd+ct,#PB_Tree_SubLevel))+Chr(13)+Str(GetTreeItemIconIndex(TreeGadget,GadgetItemID(TreeGadget,posd+ct)))+Chr(13)
ct + 1
If b = 1 And min>i
min - 1
EndIf
If b = 2 And i>min
i - 1
EndIf
Wend
If b = 1
storeitems_i$ = storeitems$
Else
storeitems_min$ = storeitems$
EndIf
; Delete the node content
While GetGadgetItemAttribute(TreeGadget,posd+1,#PB_Tree_SubLevel)>clevel
RemoveGadgetItem(TreeGadget,posd+1)
Wend
Next
; recreate the node content at the right place
For b = 1 To 2
If b = 1
posi = min
storeitems$ = storeitems_i$
Else
posi = i
storeitems$ = storeitems_min$
EndIf
ct = 0
Repeat
ct + 1
itemtext$ = StringField(storeitems$,ct,Chr(13))
If itemtext$
ct + 1
level = Val(StringField(storeitems$,ct,Chr(13)))
ct + 1
iconindex = Val(StringField(storeitems$,ct,Chr(13)))
posi + 1
AddGadgetItem(TreeGadget,posi,itemtext$,0,level)
SetTreeItemIconIndex(TreeGadget,GadgetItemID(TreeGadget,posi),iconindex)
If b = 1 And i>min
i + 1
EndIf
If b = 2 And min>i
min + 1
EndIf
EndIf
Until itemtext$ = ""
Next
InvertTreeGadgetItems(TreeGadget,i,min)
EndIf
EndIf
Next
Next
SetGadgetState(TreeGadget,0)
SetGadgetText(ItemString,GetGadgetText(TreeGadget))
SendMessage_(GadgetID(ItemString), #EM_SETSEL, 0, Len(GetGadgetText(TreeGadget)))
SetActiveGadget(ItemString)
EndIf
EndIf
Until Event = #PB_Event_CloseWindow
End
; -------------------------------------------------------------------------