// OLEDragAndDropListBox.cpp : implementation file
//
#include "stdafx.h"
#include "OLEDragAndDropListBox.h"
#include ".\oledraganddroplistbox.h"
#define TID_SCROLLDOWN 100
#define TID_SCROLLUP 101
// COLEDragAndDropListBox
IMPLEMENT_DYNAMIC(COLEDragAndDropListBox, CListBox)
COLEDragAndDropListBox::COLEDragAndDropListBox()
: m_DraggedIndex(-1)
, m_DropIndex(-1)
, m_Interval(0)
, m_CanIntenalDrop(TRUE)
{
}
COLEDragAndDropListBox::~COLEDragAndDropListBox()
{
}
BEGIN_MESSAGE_MAP(COLEDragAndDropListBox, CListBox)
ON_WM_LBUTTONDOWN()
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONUP()
ON_WM_TIMER()
END_MESSAGE_MAP()
// COLEDragAndDropListBox message handlers
void COLEDragAndDropListBox::PreSubclassWindow()
{
__super::PreSubclassWindow();
VERIFY(Register(this) == TRUE);
//register our own Clipboard format
m_cfFormat = RegisterClipboardFormat("{2FCA1C31-D8F1-4f20-8051-B0CCF7B6FD0D}");
}
void COLEDragAndDropListBox::OnLButtonDown(UINT nFlags, CPoint point)
{
__super::OnLButtonDown(nFlags, point);
//keep track of the item that was clicked on
//WM_MOUSEMOVE message is going to use that to
//create the COleDataSource
m_Interval = 0;
m_DropIndex = LB_ERR;
m_DraggedIndex = LB_ERR;
BOOL Outside;
int Index = ItemFromPoint(point,Outside);
if (Index != LB_ERR && !Outside)
{
m_DraggedIndex = Index;
SetCurSel(Index);
}
}
void COLEDragAndDropListBox::OnMouseMove(UINT nFlags, CPoint point)
{
if (m_DraggedIndex != LB_ERR && (nFlags & MK_LBUTTON))
{
//create the COleDataSource, and attach the data to it
COleDataSource DataSource;
HGLOBAL hData = GetData(m_DraggedIndex);
if (hData)
{
DataSource.CacheGlobalData(m_cfFormat,hData);
//allow the user to drag it.
DROPEFFECT DropEffect = DataSource.DoDragDrop(GetDragItemEffects(m_DraggedIndex));
//if the user wanted to move the item then delete it
//Only do this if it was dragged to another window
//OnDrop handles deleting a moved item within the same window
if (DropEffect & DROPEFFECT_MOVE && m_DraggedIndex != LB_ERR)
{
RemoveItem(m_DraggedIndex);
}
m_DraggedIndex = LB_ERR;
GetParent()->SendMessage(WM_COMMAND,MAKEWPARAM(GetDlgCtrlID(),LBN_SELCHANGE),(LPARAM)CListBox::m_hWnd);
}
}
__super::OnMouseMove(nFlags, point);
}
void COLEDragAndDropListBox::OnLButtonUp(UINT nFlags, CPoint point)
{
//cancel everything
KillTimer(TID_SCROLLDOWN);
KillTimer(TID_SCROLLUP);
m_Interval = 0;
m_DropIndex = LB_ERR;
Invalidate();
__super::OnLButtonUp(nFlags, point);
}
DROPEFFECT COLEDragAndDropListBox::OnDragEnter(CWnd* pWnd,COleDataObject* pDataObject,DWORD dwKeyState,CPoint point)
{
if (DragOriginateInSameWindow() && !GetCanInternalDrop())
{
return DROPEFFECT_NONE;
}
//if the data is the kind we want
if (pDataObject->IsDataAvailable(m_cfFormat))
{
//bring window to top
ActivateWindow();
//draw the line where it would be inserted
DrawTheLines(GetItemAt(point));
//scroll if needed
DoTheScrolling(point);
//return how the user can drop the item
return GetDropItemEffects(pDataObject,dwKeyState);
}
return DROPEFFECT_NONE;
}
DROPEFFECT COLEDragAndDropListBox::OnDragOver(CWnd* pWnd,COleDataObject* pDataObject,DWORD dwKeyState,CPoint point)
{
if (DragOriginateInSameWindow() && !GetCanInternalDrop())
{
return DROPEFFECT_NONE;
}
//if the data is the kind we want
if (pDataObject->IsDataAvailable(m_cfFormat))
{
//draw the line where it would be inserted
DrawTheLines(GetItemAt(point));
//scroll if needed
DoTheScrolling(point);
//return how the user can drop the item
return GetDropItemEffects(pDataObject,dwKeyState);
}
return DROPEFFECT_NONE;
}
void COLEDragAndDropListBox::OnDragLeave(CWnd* pWnd)
{
//cancel everything
KillTimer(TID_SCROLLDOWN);
KillTimer(TID_SCROLLUP);
//Clear The line that was drawn
CDC *pDC = GetDC();
ClearOldLine(pDC,m_DropIndex);
ReleaseDC(pDC);
m_Interval = 0;
m_DropIndex = LB_ERR;
}
BOOL COLEDragAndDropListBox::OnDrop(CWnd* pWnd,COleDataObject* pDataObject,DROPEFFECT dropEffect,CPoint point)
{
BOOL Ret = FALSE;
if (DragOriginateInSameWindow() && !GetCanInternalDrop())
{
return FALSE;
}
//if it is a format that we handle
if (pDataObject->IsDataAvailable(m_cfFormat))
{
//get the index of where the user want's to insert item
int Index = GetItemAt(point);
HGLOBAL hGlobal = pDataObject->GetGlobalData(m_cfFormat);
//if the Drag was initiated in the same window as the drop and
//user wants to move the item then we have to handle the delete here
//because if the original item was at a higher index than the dropped index
//we would have to delete the dragged item first before we
//insert the new item
if (DragOriginateInSameWindow() && dropEffect == DROPEFFECT_MOVE)
{
if (m_DraggedIndex < Index)
{
Ret = DroppedAt(Index,hGlobal);
RemoveItem(m_DraggedIndex);
m_DraggedIndex = LB_ERR;
SetCurSel(Index-1);
}
else if (m_DraggedIndex > Index)
{
RemoveItem(m_DraggedIndex);
m_DraggedIndex = LB_ERR;
Ret = DroppedAt(Index,hGlobal);
}
}
else //simply drop the item in the desired index
{
Ret = DroppedAt(Index,hGlobal);
GetParent()->SendMessage(WM_COMMAND,MAKEWPARAM(GetDlgCtrlID(),LBN_SELCHANGE),(LPARAM)CListBox::m_hWnd);
}
}
m_DropIndex = LB_ERR;
return Ret;
}
BOOL COLEDragAndDropListBox::DragOriginateInSameWindow()
{
//if m_DraggedIndex is equal to LB_ERR then the dragging
//did not originate in this window
return m_DraggedIndex != LB_ERR;
}
//find the item under the point,
//returns -1 if it is passed the last item in the list
int COLEDragAndDropListBox::GetItemAt(CPoint Point)
{
BOOL Outside;
int Index = ItemFromPoint(Point,Outside);
if (Outside)
{
Index = -1;
}
return Index;
}
void COLEDragAndDropListBox::DrawTheLines(int Index)
{
CRect ClientRect;
GetClientRect(&ClientRect);
CDC *pDC = GetDC();
ClearOldLine(pDC,m_DropIndex);
m_DropIndex = Index;
DrawNewLine(pDC,m_DropIndex);
ReleaseDC(pDC);
}
void COLEDragAndDropListBox::ClearOldLine(CDC *pDC,int Index)
{
CRect ClientRect;
GetClientRect(&ClientRect);
CRect Rect;
int Width = 2;
CPen Pen(PS_SOLID,Width,RGB(255,255,255));
CPen *pOldPen = pDC->SelectObject(&Pen);
if (Index != LB_ERR)
{
GetItemRect(m_DropIndex,&Rect);
if (ClientRect.PtInRect(Rect.TopLeft()))
{
pDC->MoveTo(Rect.left,Rect.top+1);
pDC->LineTo(Rect.right-(Width/2),Rect.top+1);
}
}
else
{
GetItemRect(GetCount()-1,&Rect);
if (ClientRect.PtInRect(CPoint(0,Rect.bottom)))
{
pDC->MoveTo(Rect.left,Rect.bottom);
pDC->LineTo(Rect.right-(Width/2),Rect.bottom);
}
}
pDC->SelectObject(pOldPen);
}
void COLEDragAndDropListBox::DrawNewLine(CDC *pDC,int Index)
{
CRect ClientRect;
GetClientRect(&ClientRect);
int Width = 2;
CRect Rect;
CPen Pen(PS_SOLID,Width,RGB(0,0,0));
CPen *pOldPen = pDC->SelectObject(&Pen);
if (Index != LB_ERR)
{
GetItemRect(Index,&Rect);
if (ClientRect.PtInRect(Rect.TopLeft()))
{
pDC->MoveTo(Rect.left,Rect.top+1);
pDC->LineTo(Rect.right-(Width/2),Rect.top+1);
}
}
else
{
GetItemRect(GetCount()-1,&Rect);
if (ClientRect.PtInRect(CPoint(0,Rect.bottom)))
{
pDC->MoveTo(Rect.left,Rect.bottom);
pDC->LineTo(Rect.right-(Width/2),Rect.bottom);
}
}
pDC->SelectObject(pOldPen);
}
void COLEDragAndDropListBox::DoTheScrolling(CPoint Point)
{
CRect ClientRect;
GetClientRect(&ClientRect);
if (Point.y > ClientRect.Height()-10)
{
DWORD Interval = 250 / (1+ ((Point.y-ClientRect.Height())/GetItemHeight(0)));
if (Interval != m_Interval)
{
m_Interval = Interval;
SetTimer(TID_SCROLLDOWN,Interval,NULL);
OnTimer(TID_SCROLLDOWN);
}
}
else if (Point.y < 10)
{
DWORD Interval = 250 / (1+(abs(Point.y)/GetItemHeight(1)));
if (Interval != m_Interval)
{
m_Interval = Interval;
SetTimer(TID_SCROLLUP,Interval,NULL);
OnTimer(TID_SCROLLUP);
}
}
else
{
KillTimer(TID_SCROLLDOWN);
KillTimer(TID_SCROLLUP);
m_Interval = 0;
}
}
void COLEDragAndDropListBox::OnTimer(UINT nIDEvent)
{
if (nIDEvent == TID_SCROLLDOWN)
{
if (m_DropIndex != -1)
{
DrawTheLines(m_DropIndex+1);
SetTopIndex(GetTopIndex()+1);
}
else
{
KillTimer(nIDEvent);
m_Interval = 0;
}
}
else if (nIDEvent == TID_SCROLLUP)
{
if (GetTopIndex() != 0)
{
DrawTheLines(m_DropIndex-1);
SetTopIndex(GetTopIndex()-1);
}
else
{
KillTimer(nIDEvent);
m_Interval = 0;
}
}
__super::OnTimer(nIDEvent);
}
HGLOBAL COLEDragAndDropListBox::GetData(int ForIndex)
{
CString Text;
GetText(ForIndex,Text);
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE,Text.GetLength()+1);
char *pChar = (char *)GlobalLock(hGlobal);
strcpy(pChar,Text);
GlobalUnlock(hGlobal);
return hGlobal;
}
BOOL COLEDragAndDropListBox::DroppedAt(int InsertBefore,HGLOBAL hGlobal)
{
char * pChar = (char *)GlobalLock(hGlobal);
int Index = InsertString(InsertBefore,pChar);
SetCurSel(Index);
GlobalUnlock(hGlobal);
return TRUE;
}
DROPEFFECT COLEDragAndDropListBox::GetDropItemEffects(COleDataObject* /*pDataObject*/,DWORD dwKeyState)
{
if (DragOriginateInSameWindow())
{
if (dwKeyState & MK_CONTROL)
{
return DROPEFFECT_COPY;
}
return DROPEFFECT_MOVE;
}
if (dwKeyState & MK_SHIFT)
{
return DROPEFFECT_MOVE;
}
return DROPEFFECT_COPY;
}
DROPEFFECT COLEDragAndDropListBox::GetDragItemEffects(int Index)
{
return DROPEFFECT_COPY|DROPEFFECT_MOVE;
}
void COLEDragAndDropListBox::RemoveItem(int Index)
{
DeleteString(Index);
}
void COLEDragAndDropListBox::ActivateWindow()
{
CWnd *pWnd = GetParent();
if (pWnd)
{
pWnd->SetForegroundWindow();
pWnd->BringWindowToTop();
pWnd->SetActiveWindow();
}
}