Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Drag Drop ListBox..
#1
How do you drag drop ListBox items to rearrange the order of the items?
#2
This is for tree view control. For listbox would be difficult because it does not have some messages such as TVN_BEGINDRAG, TVM_HITTEST etc.

Drag, TvMoveItem and TvGetItemText can be used anywhere, not only with this dialog.


.qml   drag tree view item.qml (Size: 5.21 KB / Downloads: 363)

Function dlg_drag_test
Code:
Copy      Help
\Dialog_Editor
function# hDlg message wParam lParam
if(hDlg) goto messages

if(!ShowDialog("dlg_drag_test" &dlg_drag_test)) ret

;note: this dialog is Unicode and therefore received W versions of messages. But the function can work with ANSI dialogs too.

;BEGIN DIALOG
;1 "" 0x90C80A44 0x100 0 0 223 135 "Dialog"
;1 Button 0x54030001 0x4 120 116 48 14 "OK"
;2 Button 0x54030000 0x4 170 116 48 14 "Cancel"
;3 SysTreeView32 0x54030000 0x0 0 0 116 110 ""
;END DIALOG
;DIALOG EDITOR: "" 0x2030002 "" "" ""

ret
;messages
sel message
,case WM_INITDIALOG
,int i htv=id(3 hDlg)
,str s ss="one[]two[]three[]βββ"
,foreach(s ss) i+1; TvAdd htv 0 s i
,
,case WM_DESTROY
,case WM_COMMAND goto messages2
,case WM_NOTIFY goto messages3
ret
;messages2
sel wParam
,case IDOK
,;gett text of all
,htv=id(3 hDlg)
,int hi=SendMessage(htv TVM_GETNEXTITEM TVGN_ROOT 0)
,rep
,,if(!hi) break
,,TvGetItemText htv hi s
,,ss.addline(s)
,,hi=SendMessage(htv TVM_GETNEXTITEM TVGN_NEXT hi)
,out ss
,
,case IDCANCEL
ret 1
;messages3
NMHDR* nh=+lParam
sel nh.idFrom
,case 3
,NMTREEVIEW* nt=+nh
,if(nh.code=TVN_SELCHANGEDW or nh.code=TVN_SELCHANGED)
,,i=nt.itemNew.lParam ;;was set by TvAdd
,,out i
,else if(nh.code=TVN_BEGINDRAGW or nh.code=TVN_BEGINDRAG)
,,type TVDRAG49 htv hidrag
,,TVDRAG49 td.htv=nh.hwndFrom; td.hidrag=nt.itemNew.hItem
,,Drag(hDlg &drag_test_proc &td)
,,SendMessage td.htv TVM_SELECTITEM TVGN_DROPHILITE 0

Function drag_test_proc
Code:
Copy      Help
;/dlg_drag_test
function button TVDRAG49&td

TVHITTESTINFO ht
xm ht.pt; ScreenToClient(td.htv &ht.pt)
int hidrop=SendMessage(td.htv TVM_HITTEST 0 &ht)
int candrop=hidrop or ht.flags&TVHT_NOWHERE

if(button)
,SendMessage td.htv TVM_SELECTITEM TVGN_DROPHILITE 0
,if(!candrop) ret
,ret TvMoveItem(td.htv td.hidrag hidrop GetMod=2)
else
,SendMessage td.htv TVM_SELECTITEM TVGN_DROPHILITE hidrop
,if(!candrop) ret 3
,ret iif(GetMod=2 2 1)

;instead of TVM_SELECTITEM could use TVM_SETINSERTMARK, but then need more calculations

Function Drag
Code:
Copy      Help
;/
function hwnd proc param

;A simple drag function. Sets cursor, etc.
;Returns when dragging is finished.
;On drop, returns proc's return value. If cancelled, returns 0.

;hwnd - drag source window. It should be the window where drad/drop operation started. Can be child.
;proc:
,;Address of a function that will be called while dragging (many times) and when dropped.
,;It for example can reorder items in a list box.
,;It also sets cursor.
,;The function must be:
,
,;function button param
,
,;button - mouse button: 0 while dragging, 1 left up, 2 right up.
,;param - param passed to Drag().
,
,;On move, it must return cursor handle, or 0 if sets cursor itself, or 1-3 to use standard cursors: 1 move, 2 copy, 3 no operation.
,;On button up, it can return any value. Drag() returns it.


MSG m; int r; int mb

int hm=GetModuleHandle("ole32")
int cMove(LoadCursor(hm +2)) cCopy(LoadCursor(hm +3)) cNo(LoadCursor(hm +1))

SetCapture(hwnd);
rep
,if(GetCapture()!=hwnd || GetMessage(&m, 0, 0, 0)<=0) break
,if(m.message==WM_KEYDOWN && m.wParam==VK_ESCAPE) ReleaseCapture();

,mb=0;
,sel(m.message)
,,case WM_MOUSEMOVE:
,,r=call(proc 0 param)
,,if(r)
,,,if(r>=1 and r<=3) int* pc=&cMove; r=pc[r-1]
,,,SetCursor(r);
,,continue;
,,case WM_LBUTTONUP: mb=MK_LBUTTON
,,case WM_RBUTTONUP: mb=MK_RBUTTON
,
,if(mb)
,,ReleaseCapture();
,,ret call(proc mb param);

,DispatchMessage(&m);

Function TvMoveItem
Code:
Copy      Help
;/
function# htv hi hito copy

;Moves or copies treeview control item hi to the place of hito.
;If successful, returns new item handle (on move too, because the item must be deleted and new item created).
;Does not copy Vista-specific properties. If you use them, set them again.
;If hito is 0, moves/copies to the end.


if(hi=hito) ret hi

;get all properties of hi, except Vista properties
TVINSERTSTRUCTW is
TVITEMW& t=is.item
t.hItem=hi; t.mask=TVIF_IMAGE|TVIF_SELECTEDIMAGE|TVIF_PARAM|TVIF_STATE|TVIF_TEXT
t.stateMask=TVIS_BOLD|TVIS_CUT|TVIS_DROPHILITED|TVIS_EXPANDED|TVIS_EXPANDEDONCE|TVIS_EXPANDPARTIAL|TVIS_SELECTED|TVIS_OVERLAYMASK|TVIS_STATEIMAGEMASK|TVIS_USERMASK
BSTR b; t.pszText=b.alloc(300); t.cchTextMax=300
if(!SendMessage(htv TVM_GETITEMW 0 &t)) ret
int selected=t.state&TVIS_SELECTED

;insert new item with these properties
if(copy) t.mask&=~TVIF_STATE
else
,int hparent(GetParent(htv)) wp(SubclassWindow(hparent &DefWindowProcW)) ;;subclass parent window to prevent receiving select and delete messages

if(hito) is.hInsertAfter=SendMessage(htv TVM_GETNEXTITEM TVGN_PREVIOUS hito); if(!is.hInsertAfter) is.hInsertAfter=TVI_FIRST
else is.hInsertAfter=TVI_LAST

int hinew=SendMessage(htv TVM_INSERTITEMW 0 &is)

;delete hi
if(hinew and !copy)
,if(selected) SendMessage htv TVM_SELECTITEM TVGN_CARET hinew
,SendMessage(htv TVM_DELETEITEM 0 hi)

;unsubclass
if(wp) SubclassWindow(hparent wp)

ret hinew

Function TvGetItemText
Code:
Copy      Help
;/
function# htv hi str&s

TVITEMW t.mask=TVIF_TEXT; t.hItem=hi
BSTR b; t.pszText=b.alloc(300); t.cchTextMax=300
if(!SendMessage(htv TVM_GETITEMW 0 &t)) ret
s.ansi(b)
ret 1
#3
This is sweet...wow that took a lot more code than I would have ever imagined. Thanks a bunch.

The only problem is that now I have to figure out how to drag and drop files from an directory into listview...I had gotten it worked out with a listbox...I'll work on it!

Thanks Gintaras!
#4
Use TvAdd instead of LB_Add.

Function dlg_tv_drop_files
Code:
Copy      Help
\Dialog_Editor
function# hDlg message wParam lParam
if(hDlg) goto messages

if(!ShowDialog("dlg_tv_drop_files" &dlg_tv_drop_files)) ret

;BEGIN DIALOG
;0 "" 0x90C80A44 0x100 0 0 223 135 "Dialog"
;1 Button 0x54030001 0x4 120 116 48 14 "OK"
;2 Button 0x54030000 0x4 170 116 48 14 "Cancel"
;3 SysTreeView32 0x54030000 0x0 0 0 116 110 ""
;END DIALOG
;DIALOG EDITOR: "" 0x2030002 "" "" ""

ret
;messages
sel message
,case WM_INITDIALOG
,QmRegisterDropTarget(id(3 hDlg) hDlg 16)
,case WM_DESTROY
,case WM_COMMAND goto messages2
,case WM_QM_DRAGDROP
,QMDRAGDROPINFO& di=+lParam
,int htv=id(3 hDlg)
,str s
,foreach s di.files
,,if(dir(s 1)) ;;folder
,,,s+"\*"
,,,Dir d
,,,foreach(d s FE_Dir)
,,,,str sPath=d.FileName(1)
,,,,TvAdd(htv 0 sPath)
,,else ;;file
,,,TvAdd(htv 0 s)
ret
;messages2
sel wParam
,case IDOK
,case IDCANCEL
ret 1
#5
Everything works great so far...thanks Ginataras!

Just a couple more items to go...
1. Limiting the type of files to be drag/drop to just MP3 files.
2. Automatically updating an XML file with every change made in the SysTree.
3. Getting the MP3 files in the list to play with the "Play" in the order of the list.
4. AutoAdding to the playlist based on files in a directory.

and just a few more...
Of course I'll be working on all of this myself, but if anyone has examples, ideas, or answers that will help me out, they would be greatly appreciated!

Thanks,
Jimmy Vig
#6
This works...
Function dlg_drag_test
Code:
Copy      Help
,,if(!_s.endi(".mp3"))
,,,ret

but is there a way to get the acceptfile icon to only show up for mp3's...it's not necessary, but it would look a lot tighter.
#7
di.files is populated only on drop. To decide what cursor to show, we need files on drag enter. Function QMDRAGDROPFILES.GetFiles extracts file paths from the data object.

Member function QMDRAGDROPINFO.GetFiles
Code:
Copy      Help
function# ARRAY(str)&a [flags] ;;flags: 1 free memory (called on drop)

;Gets paths of file(s) being dragged or dropped.
;Returns the number of files.
;Does not support non-file objects, such as control panel items.


a=0
int i
for(i 0 formats.len) if(formats[i].cfFormat=CF_HDROP) goto g1
ret
;g1
#opt nowarnings 1
STGMEDIUM sm ;;warning because of composites in union
dataObj.GetData(&formats[i] &sm); err ret
if(sm.tymed!=TYMED_HGLOBAL) ret
BSTR b.alloc(300)
for i 0 DragQueryFileW(sm.hGlobal -1 0 0)
,DragQueryFileW(sm.hGlobal i b 300)
,a[].ansi(b)

if(flags&1) DragFinish sm.hGlobal
ret a.len

Function dlg_tv_drop_files
Code:
Copy      Help
\Dialog_Editor
function# hDlg message wParam lParam
if(hDlg) goto messages

if(!ShowDialog("dlg_tv_drop_files" &dlg_tv_drop_files 0 _hwndqm)) ret

;BEGIN DIALOG
;0 "" 0x90C80A44 0x100 0 0 223 135 "Dialog"
;1 Button 0x54030001 0x4 120 116 48 14 "OK"
;2 Button 0x54030000 0x4 170 116 48 14 "Cancel"
;3 SysTreeView32 0x54030000 0x0 0 0 116 110 ""
;END DIALOG
;DIALOG EDITOR: "" 0x2030002 "" "" ""

ret
;messages
sel message
,case WM_INITDIALOG
,QmRegisterDropTarget(id(3 hDlg) hDlg 1)
,
,case WM_QM_DRAGDROP ;;we receive this message on drag enter (flag 1) and on drop (always)
,QMDRAGDROPINFO& di=+lParam
,di.effect=0
,ARRAY(str) a amp3
,if(di.GetFiles(a wParam=3) and GetMp3Files(a amp3))
,,di.effect=4 ;;shortcut
,,if(wParam=3) ;;on drop
,,,int i htv=id(3 hDlg)
,,,for i 0 amp3.len
,,,,TvAdd(htv 0 amp3[i])
,
,ret DT_Ret(hDlg 1)
,
,case WM_DESTROY
,case WM_COMMAND goto messages2
ret
;messages2
sel wParam
,case IDOK
,case IDCANCEL
ret 1

Function GetMp3Files
Code:
Copy      Help
;/
function# ARRAY(str)&a ARRAY(str)&amp3

;Gets mp3 files from a.
;
;Returns the number of mp3 files.
;For folders, gets all mp3 files that are in the folders, including all subfolders.

;a - files of any type, including folders.
;amp3 - receives mp3 files that are in a.


amp3=0
str s; int i
for i 0 a.len
,s=a[i]
,if(dir(s 1)) ;;folder
,,s+"\*"
,,Dir d
,,foreach(d s FE_Dir 4)
,,,str sPath=d.FileName(1)
,,,if(sPath.endi(".mp3")) amp3[]=sPath
,else ;;file
,,if(s.endi(".mp3")) amp3[]=s
ret amp3.len


Forum Jump:


Users browsing this thread: 1 Guest(s)