[futurebasic] Re: [FB] Re: Resizing data browser

Message: < previous - next > : Reply : Subscribe : Cleanse
Home   : January 2004 : Group Archive : Group : All Groups

From: Alain Pastor <apastor@...>
Date: Wed, 14 Jan 2004 22:39:11 +0100
Bernie Wylde wrote:
> Thanks Alain
> 
Bernie,

I have toyed a bit to implement my suggestion about the drag & drop 
operation in the DataBrowser. Unfortunately, I'm stuck with fn 
BrowserSetItemRow that doesn't seem to work as I would expect. I have 
yet to figure out why. Anyway, to get you started with the drag & drop 
feature:

define three new vectors in the general settings:

list.procs[_AcceptDrag]  = @Fn MyAcceptDrag
list.procs[_AddDragItem] = @Fn MyAddDragItem
list.procs[_ReceiveDrag] = @Fn MyReceiveDrag

The DataBrowser will call the program on different occasions:
1 - when it is time to add an item in the drag operation (AddDragItem)
2 - when it needs to know if a drag is acceptable on a specific target 
(AcceptDrag)
3 - when it is time for the prog to retrieve and handle the dropped item 
(ReceiveDrag)


1 - Adding an identifier to the drag operation:

In the tiny example, it is rather simple, because the end-user can only 
drag a single item at a time. MyAddDragItem might look like this:

clear local fn MyAddDragItem( browser as ControlRef,        ¬
                            theDrag as DragReference,     ¬
                           inItemID as DataBrowserItemID, ¬
                        dragItemRef as .ItemReference )

dim as OSStatus            err
dim as DataBrowserItemID @ draggedItemID


draggedItemID = inItemID // must be in RAM

err = fn AddDragItemFlavor( theDrag, ¬
                    dragItemRef.nil&, ¬
                             _"item", ¬
                       draggedItemID, ¬
           sizeof(DataBrowserItemID), ¬
                                  0 )

End Fn = _true // for translucent dragging

The DataBrowser tells the program which item is being dragged 
(inItemID). We will add that itemID to the dragItemRef with a custom 
flavor that we will name _"item" for example. Actually, the data we must 
store is a long integer (DataBrowserItemID).

Note that we can easily store also the string referenced by the 
identifier being dragged. We would use a flavor of type _"TEXT", so that 
other applications like TeachText could deal with the dragged item too. 
we would have just to add the following:



dim as str255 ourString
ourString = str&(gWordListH, draggedItemID)
err = fn AddDragItemFlavor( theDrag, ¬
                    dragItemRef.nil&, ¬
                             _"TEXT", ¬
                         @ourString[1], ¬
                          ourString[0], ¬
                                    0 )

the _"TEXT" type is a stream of characters and not a Pascal string so we 
need to skip the length byte, hence @ourString[1].

2 - Accepting a drag:

The DataBrowser will ask the program if the drag is acceptable. In our 
case, we could accept the drag in any circumstances that means that we 
only have to return _true. However, we will refuse the drag if the item 
being dragged is the same as the item that is the target of the drag 
(the item would be dropped onto itself).

clear local fn MyAcceptDrag( browser as ControlRef,   ¬
                           theDrag as DragReference, ¬
                            itemID as DataBrowserItemID )
dim selItemH as handle
dim result   as boolean

result = _true // assume the drag is acceptable
selItemH = fn BrowserGetSelectedItems(gBrowser)
long if selItemH
select selItemH..4&
case itemID : result = _false
case else   : result = _true
end select
DisposeHandle(selItemH)
end if

End Fn = result

The item being dragged is the item selected in the list, therefore we 
will compare the target of the drag with the selected item, if they are 
the same, we return _false. Like I said in a previous post, we get the 
selection in a handle, but we must skip the first identifier (long 
integer) in that memory block because it is used by the DBFD library to 
store a possible parent identifier.

3 - Receiving the dropped item:

The DataBrowser tells us which item is the target of the drag and gives 
us the drag reference, so we just have to get the dragItemRef and 
extract from there the data of our specific flavor to retrieve the 
identifier that has been dropped.

clear local fn MyReceiveDrag( browser as ControlRef,      ¬
                            theDrag as DragReference,   ¬
                       targetItemID as DataBrowserItemID )

dim as OSStatus               err
dim as size                 @ dataSize
dim as ItemReference        @ dragItemRef
dim as UInt16               @ numItems
dim as DataBrowserItemID    @ droppedItemID

err   = fn GetDragItemReferenceNumber( theDrag, 1, dragItemRef )
long if err = _noErr

dataSize = sizeof(DataBrowserItemID)// must be in RAM
err   = fn GetFlavorData( theDrag, ¬
                       dragItemRef, ¬
                           _"item", ¬
                     droppedItemID, ¬
                          dataSize, ¬
                                 0)
Long If err = _noErr
dim targetRow as long
targetRow = Fn BrowserGetItemRow( browser, targetItemID )
Fn BrowserSetItemRow( browser,droppedItemID, targetRow )
//def debugnumber(droppedItemID)
//def debugnumber(targetRow)
end if
end if

end fn = ( err = _noErr )

My attempt shows that we can get the right row for the target as well as 
the right itemID that has been dropped, nevertheless BrowserSetItemRow 
fails to change the display in the list and returns _noErr. Maybe 
someone will have an idea as to why the DataBrowser does not update its 
list.

Until a solution is found, perhaps you can adopt another strategy from 
there. I have tried at my side to maintain a duplicate of the list 
handled by the DataBrowser, but it's a real pain in the butt.
I have declared a dynamic array (gIndex (_maxInt) as long) and stored 
the itemIDs sequentially. Like so:

long if gWordListH
dim i as long
for i = 1 to gWordListH..nil%
gIndex(i) = i
next
compress dynamic gIndex&
fn BrowserAddItems( gBrowser, gWordListH..nil%, #0 )

end if

Oddly enough, if the for/next loop is placed right after the addition of 
the items in the DataBrowser, the 10 or so first strings are not 
displayed in the list (double clicking on the empty cells showed the 
items were there), only Lord and maybe Apple know why. I'm puzzled 
because the two lists are not related.
Of course I had to change the code for the display, in MyGetSetItemData 
and in some other places. For example, I had to write things like this:

err = Fn BrowserSetString( itemData, str&(gWordListH,gIndex(itemID)) )

I believe that the handling of the drag & drop feature would be a 
nightmare compared to the simplicity above. I just tried the following 
in the receiveDrag fn

swap gIndex(targetRow),gIndex(itemRow)

but, no matter what I did to update the list in the DataBrowser 
(BrowserUpdate, DrawOneControl, InvalRect), nothing worked until I 
destroyed the list in the Browser and recreated it (like with child 
items in a hierarchical list):

fn DeleteAllItemsInBrowser( gBrowser )
fn BrowserAddItems( gBrowser, gWordListH..nil%, #0 )

Afterward the list is displayed like I want, i.e. with two items that 
have exchanged their respective position.

The DataBrowser seems to be a very strange land to explore.

Alain