ListBoxes and Mouse Cases
rhjpires
In trying to understand how to program with panels and controls, I came across a spinet that allows one to shuffle (drag and drop) items within a list. Please, have a look at the code bellow. The case of double-clicking should be ignored. However, when I double-click an item, I receive the notice: " ** a wave read gave error: Index out of range for wave "testList"." and Igor replaces the value of the list I have doubled-clicked by the last value on that list. Can anyone explain why Igor is doing this?
#pragma rtGlobals=3 // Use modern global access method and strict wave access.
Function OpenDragNDropExample()
Make/O/T/N=4 root:testList = {"item 1", "item 2", "item 3", "item 4"}
Make/O/N=4 root:testSel
DoWindow/K Panel_DragNDrop
Execute/Q "Panel_DragNDrop()"
End
Window Panel_DragNDrop() : Panel
PauseUpdate; Silent 1 // building window...
NewPanel /W=(350,125,650,325) as "Drag List Items Example"
ListBox list0,pos={1,2},size={298,197},proc=ListBoxProc_DragNDropLB
ListBox list0,listWave=root:testList,selWave=root:testSel,mode= 1,selRow= 1
EndMacro
Function ListBoxProc_DragNDropLB(lba) : ListBoxControl
STRUCT WMListboxAction &lba
Variable row = lba.row
Variable col = lba.col
WAVE/T/Z listWave = lba.listWave
WAVE/Z selWave = lba.selWave
switch( lba.eventCode )
case -1: // control being killed
break
case 1: // mouse down
Variable/G V_MouseDownRow = row
break
case 2: // mouse up
if(row != V_MouseDownRow) // dragged?
NVAR V_MouseDownRow
String item = listWave[V_MouseDownRow]
DeletePoints V_MouseDownRow, 1, listWave // do swap
InsertPoints row, 1, listWave
listWave[row] = item
endif
KillVariables V_MouseDownRow // cleanup variable
break
case 3: // double click
break
case 4: // cell selection
case 5: // cell selection plus shift key
break
case 6: // begin edit
break
case 7: // finish edit
break
endswitch
return 0
End
Function OpenDragNDropExample()
Make/O/T/N=4 root:testList = {"item 1", "item 2", "item 3", "item 4"}
Make/O/N=4 root:testSel
DoWindow/K Panel_DragNDrop
Execute/Q "Panel_DragNDrop()"
End
Window Panel_DragNDrop() : Panel
PauseUpdate; Silent 1 // building window...
NewPanel /W=(350,125,650,325) as "Drag List Items Example"
ListBox list0,pos={1,2},size={298,197},proc=ListBoxProc_DragNDropLB
ListBox list0,listWave=root:testList,selWave=root:testSel,mode= 1,selRow= 1
EndMacro
Function ListBoxProc_DragNDropLB(lba) : ListBoxControl
STRUCT WMListboxAction &lba
Variable row = lba.row
Variable col = lba.col
WAVE/T/Z listWave = lba.listWave
WAVE/Z selWave = lba.selWave
switch( lba.eventCode )
case -1: // control being killed
break
case 1: // mouse down
Variable/G V_MouseDownRow = row
break
case 2: // mouse up
if(row != V_MouseDownRow) // dragged?
NVAR V_MouseDownRow
String item = listWave[V_MouseDownRow]
DeletePoints V_MouseDownRow, 1, listWave // do swap
InsertPoints row, 1, listWave
listWave[row] = item
endif
KillVariables V_MouseDownRow // cleanup variable
break
case 3: // double click
break
case 4: // cell selection
case 5: // cell selection plus shift key
break
case 6: // begin edit
break
case 7: // finish edit
break
endswitch
return 0
End
See the Debugger screenshot I've attached.
--Jim Prouty
Software Engineer, WaveMetrics, Inc.
October 19, 2015 at 10:13 am - Permalink
The second problem is that the first execution of case 2 kills your V_MouseDownRow global variable. So the second time case 2 is called, this variable is null. The check for row != null is true, so the following code executes and as Jim pointed out, row is greater than the last index in your wave. The default Igor behavior is to substitute the last value in a wave if the last index is exceeded.
Hence the error message and the reason row is replaced with the last value in the wave.
I've modified your list box proc somewhat and it seems to work for my limited testing.
As a final comment, you can learn a lot about how code works by judiciously sprinkling it with diagnostic print statements, also learning to use the debugger pays off quite handsomely.
Hope this helps.
STRUCT WMListboxAction &lba
Variable row = lba.row
Variable col = lba.col
WAVE/T/Z listWave = lba.listWave
WAVE/Z selWave = lba.selWave
switch( lba.eventCode )
case -1: // control being killed
break
case 1: // mouse down
Variable/G V_MouseDownRow = row
break
case 2: // mouse up
//quit if V_MouseDownRow isn't a string or numeric variable
//otherwise create a pointer to it.
if( exists( "V_MouseDownRow" ) == 2 )
NVAR V_MouseDownRow
else
break
endif
if(row != V_MouseDownRow) // dragged?
String item = listWave[V_MouseDownRow]
DeletePoints V_MouseDownRow, 1, listWave // do swap
InsertPoints row, 1, listWave
listWave[row] = item
endif
KillVariables V_MouseDownRow // cleanup variable
break
case 3: // double click
break
case 4: // cell selection
case 5: // cell selection plus shift key
break
case 6: // begin edit
break
case 7: // finish edit
break
endswitch
print "code 1: ", lba.eventcode
return 1
End
October 19, 2015 at 11:08 am - Permalink
Thanks for the suggestions also!
R.
October 19, 2015 at 11:34 am - Permalink