On Tue, 2005-01-11 at 04:20, Jez White wrote:
Hi,

>From what I understand of dragging (which isn't a lot!) most of the 
functionality that you would need for this explorer example is present 
within Win32::GUI at the moment.

Basically, dragging is a very manual process - you have to do most of the 
work - and I think most of the methods to do this work is part of the 
imagelist control (BeginDrag, DragEnter,DragMove and EndDrag). You have to 
work out when the user starts the drag, when they finish, so on 

Hi All,

dragging, found this.... frightening but here goes...

On WM_LBUTTONDOWN


If dragging begins over an item, the list-view control selects and sets the focus to the item. Then it sends an LVN_BEGINDRAG notification message to the parent window. The parent window is responsible for actually carrying out the drag operation.

If dragging begins over the window background, the list-view control enters another modal message loop, enabling the user to form a rectangle by dragging the mouse. Items within the rectangle are selected.

On WM_RBUTTONDOWN

Processed the same way as the WM_LBUTTONDOWN message, except that the control sends an NM_RCLICK notification message (instead of NM_CLICK (list view)) and an LVN_BEGINRDRAG notification message (instead of LVN_BEGINDRAG). Note that the control processes the corresponding WM_RBUTTONUP message, and does not dispatch it. Applications thus cannot see this message, even by subclassing the control.


All the below codes are assumed to be in your MainWndProc() function.

When user keeps pressing the mouse button down, and moves his mouse a little, a notification message (LVN_BEGINDRAG) will be sent to your MainWndProc() via WM_NOTIFY. You should handle this message by:

  1. Create a drag-image for all selected items. ListView provides a function to create a single-row drag-image. We will use this function to create multiple drag-images for each row, and merge them together.

  2. Call ImageList_Begin to initialize a drag-and-drop, then ImageList_DragEnter to start a drag action. You must call SetCapture() to get the subsequent mouse message inside your application.





Example code segment:

case WM_NOTIFY:


switch (((LPNMHDR)lParam)->code) {


case LVN_BEGINDRAG:


// You can set your customized cursor here


p.x = 8;

p.y = 8;


// Ok, now we create a drag-image for all selected items

bFirst = TRUE;

iPos = ListView_GetNextItem(hListView, -1, LVNI_SELECTED);

while (iPos != -1) {

if (bFirst) {

// For the first selected item,

// we simply create a single-line drag image

hDragImageList = ListView_CreateDragImage(hListView, iPos, &p);

ImageList_GetImageInfo(hDragImageList, 0, &imf);

iHeight = imf.rcImage.bottom;

bFirst = FALSE;

}else {

// For the rest selected items,

// we create a single-line drag image, then

// append it to the bottom of the complete drag image

hOneImageList = ListView_CreateDragImage(hListView, iPos, &p);

hTempImageList = ImageList_Merge(hDragImageList,

0, hOneImageList, 0, 0, iHeight);

ImageList_Destroy(hDragImageList);

ImageList_Destroy(hOneImageList);

hDragImageList = hTempImageList;

ImageList_GetImageInfo(hDragImageList, 0, &imf);

iHeight = imf.rcImage.bottom;

}

iPos = ListView_GetNextItem(hListView, iPos, LVNI_SELECTED);

}


// Now we can initialize then start the drag action

ImageList_BeginDrag(hDragImageList, 0, 0, 0);


pt = ((NM_LISTVIEW*) ((LPNMHDR)lParam))->ptAction;

ClientToScreen(hListView, &pt);


ImageList_DragEnter(GetDesktopWindow(), pt.x, pt.y);


bDragging = TRUE;


// Don't forget to capture the mouse

SetCapture(hWndMain);


break;

Then we handle WM_MOUSEMOVE message to move the drag-image along the mouse movement. There is no need to explain the example code segment:

case WM_MOUSEMOVE:


if (!bDragging)

break;


p.x = LOWORD(lParam);

p.y = HIWORD(lParam);


ClientToScreen(hWndMain, &p);

ImageList_DragMove(p.x, p.y);

break;

OK, now we reach the final step. When user releases the mouse button, we will end the drag-and-drop action and do the rearrangement work.

  1. Call ImageList_DragLeave and Image_EndDrag to end the drag-and-drop process. Don't forget to release the mouse capture and destroy the drag image to free the memory it uses.

  2. Determine the item onto where the user drops the selected items. If the dropped item is selected, you should terminate.

  3. Move all the selected items to the dropped item's position by copying and deleting.





Here's the example code segment:

case WM_LBUTTONUP:


// End the drag-and-drop process

bDragging = FALSE;

ImageList_DragLeave(hListView);

ImageList_EndDrag();

ImageList_Destroy(hDragImageList);


ReleaseCapture();


// Determine the dropped item

lvhti.pt.x = LOWORD(lParam);

lvhti.pt.y = HIWORD(lParam);

ClientToScreen(hWndMain, &lvhti.pt);

ScreenToClient(hListView, &lvhti.pt);

ListView_HitTest(hListView, &lvhti);


// Out of the ListView?

if (lvhti.iItem == -1)

break;

// Not in an item?

if ((lvhti.flags & LVHT_ONITEMLABEL == 0) &&

(lvhti.flags & LVHT_ONITEMSTATEICON == 0))

break;


// Dropped item is selected?

lvi.iItem = lvhti.iItem;

lvi.iSubItem = 0;

lvi.mask = LVIF_STATE;

lvi.stateMask = LVIS_SELECTED;

ListView_GetItem(hListView, &lvi);


if (lvi.state & LVIS_SELECTED)

break;


// Rearrange the items

iPos = ListView_GetNextItem(hListView, -1, LVNI_SELECTED);

while (iPos != -1) {

// First, copy one item

lvi.iItem = iPos;

lvi.iSubItem = 0;

lvi.cchTextMax = MAX_TARGET_LEN;

lvi.pszText = buf;

lvi.stateMask = ~LVIS_SELECTED;

lvi.mask = LVIF_STATE | LVIF_IMAGE

| LVIF_INDENT | LVIF_PARAM | LVIF_TEXT;

ListView_GetItem(hListView, &lvi);

lvi.iItem = lvhti.iItem;

// Insert the main item

iRet = ListView_InsertItem(hListView, &lvi);

if (lvi.iItem < iPos)

lvhti.iItem++;

if (iRet <= iPos)

iPos++;

// Set the subitem text

for (i = 1; i < JOB_WIN_COLUMN_NUM; i++) {

ListView_GetItemText(hListView, iPos,

i, buf, MAX_TARGET_LEN);

ListView_SetItemText(hListView, iRet, i, buf);

}

// Delete from original position

ListView_DeleteItem(hListView, iPos);

iPos = ListView_GetNextItem(hListView, -1, LVNI_SELECTED);

}


break;

Here are variable definitions:

int iHeight;

static HIMAGELIST hDragImageList;

HIMAGELIST hOneImageList, hTempImageList;

LPNMHDR pnmhdr;

static BOOL bDragging;

LVHITTESTINFO lvhti;

BOOL bFirst;

IMAGEINFO imf;

And of course, hListView and hMainWnd are HWND variables.