draglist.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 9k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. #include "ctlspriv.h"
  2. #define DF_ACTUALLYDRAG 0x0001
  3. #define DF_DEFERRED 0x0002
  4. #define INITLINESPERSECOND 6
  5. #define VERTCHANGENUMLINES 25
  6. #define TIMERID 238
  7. #define TIMERLEN 50
  8. #define DX_INSERT 16
  9. #define DY_INSERT 16
  10. typedef struct {
  11.     HWND hwndDrag;
  12.     UINT uFlags;
  13. } DRAGPROP, *PDRAGPROP;
  14. UINT uDragListMsg = 0;
  15. #ifndef WINNT
  16. #pragma data_seg(DATASEG_READONLY)
  17. #endif
  18. const TCHAR szDragListMsgString[] = DRAGLISTMSGSTRING;
  19. #ifndef WINNT
  20. #pragma data_seg()
  21. #endif
  22. BOOL NEAR PASCAL PtInLBItem(HWND hLB, int nItem, POINT pt, int xInflate, int yInflate)
  23. {
  24.   RECT rc;
  25.   if (nItem < 0)
  26.       nItem = (int)SendMessage(hLB, LB_GETCURSEL, 0, 0L);
  27.   if (SendMessage(hLB, LB_GETITEMRECT, nItem, (LPARAM)(LPRECT)&rc) == LB_ERR)
  28.       return(FALSE);
  29.   InflateRect(&rc, xInflate, yInflate);
  30.   return(PtInRect(&rc, pt));
  31. }
  32. /*
  33.  * DragListSubclassProc
  34.  * --------------------
  35.  *
  36.  * Window procedure for subclassed list boxes
  37.  */
  38. LRESULT CALLBACK DragListSubclassProc(HWND hLB, UINT uMsg, WPARAM wParam,
  39.       LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
  40. {
  41.   PDRAGPROP pDragProp;
  42.   DRAGLISTINFO sNotify;
  43.   BOOL bDragging;
  44.   POINT pt;
  45.   pDragProp = (PDRAGPROP)dwRefData;
  46.   bDragging = pDragProp->hwndDrag == hLB;
  47.   switch (uMsg)
  48.     {
  49.       case WM_NCDESTROY:
  50.         if (bDragging)
  51.             SendMessage(hLB, WM_RBUTTONDOWN, 0, 0L); /* cancel drag */
  52.         RemoveWindowSubclass(hLB, DragListSubclassProc, 0);
  53.         if (pDragProp)
  54.             LocalFree((HLOCAL)pDragProp);
  55.         break;
  56.       case WM_LBUTTONDOWN:
  57.         {
  58.           int nItem;
  59.           if (bDragging) /* nested button-down */
  60.               SendMessage(hLB, WM_RBUTTONDOWN, 0, 0L); /* cancel drag */
  61.           SetFocus(hLB);
  62.           pt.x = GET_X_LPARAM(lParam);
  63.           pt.y = GET_Y_LPARAM(lParam);
  64.           ClientToScreen(hLB, &pt);
  65.           nItem = LBItemFromPt(hLB, pt, FALSE);
  66.           if (nItem >= 0)
  67.             {
  68.               SendMessage(hLB, LB_SETCURSEL, nItem, 0L);
  69.               if (GetWindowLong(hLB, GWL_STYLE) & LBS_NOTIFY)
  70.                   SendMessage(GetParent(hLB), WM_COMMAND,
  71.                               GET_WM_COMMAND_MPS(GetDlgCtrlID(hLB), hLB, LBN_SELCHANGE));
  72.               sNotify.uNotification = DL_BEGINDRAG;
  73.               goto QueryParent;
  74.             }
  75.           else
  76.               goto FakeDrag;
  77.         }
  78.       case WM_TIMER:
  79.         if (wParam != TIMERID)
  80.             break;
  81.         lParam = GetMessagePosClient(hLB, &pt);
  82.         // fall through
  83.       case WM_MOUSEMOVE:
  84. if (bDragging)
  85.   {
  86.     HWND hwndParent;
  87.     LRESULT lResult;
  88.     /* We may be just simulating a drag, but not actually doing
  89.      * anything.
  90.      */
  91.     if (!(pDragProp->uFlags&DF_ACTUALLYDRAG))
  92. return(0L);
  93.     /* We don't want to do any dragging until the user has dragged
  94.      * outside of the current selection.
  95.      */
  96.     if (pDragProp->uFlags & DF_DEFERRED)
  97.       {
  98.                 pt.x = GET_X_LPARAM(lParam);
  99.                 pt.y = GET_Y_LPARAM(lParam);
  100. if (PtInLBItem(hLB, -1, pt, 0, 4))
  101.     return 0;
  102. pDragProp->uFlags &= ~DF_DEFERRED;
  103.       }
  104.     sNotify.uNotification = DL_DRAGGING;
  105. QueryParent:
  106.     hwndParent = GetParent(hLB);
  107.     sNotify.hWnd = hLB;
  108.             sNotify.ptCursor.x = GET_X_LPARAM(lParam);
  109.             sNotify.ptCursor.y = GET_Y_LPARAM(lParam);
  110.     ClientToScreen(hLB, &sNotify.ptCursor);
  111.             lResult = SendMessage(hwndParent, uDragListMsg, GetDlgCtrlID(hLB),
  112.   (LPARAM)(LPDRAGLISTINFO)&sNotify);
  113.     if (uMsg == WM_LBUTTONDOWN)
  114.       {
  115. /* Some things may not be draggable
  116.  */
  117. if (lResult)
  118.   {
  119.     SetTimer(hLB, TIMERID, TIMERLEN, NULL);
  120.     pDragProp->uFlags = DF_DEFERRED | DF_ACTUALLYDRAG;
  121.   }
  122. else
  123.   {
  124. FakeDrag:
  125.     pDragProp->uFlags = 0;
  126.   }
  127. /* Set capture and change mouse cursor
  128.  */
  129. pDragProp->hwndDrag = hLB;
  130. SetCapture(hLB);
  131.       }
  132.     else
  133.       {
  134. switch (lResult)
  135.   {
  136.     case DL_STOPCURSOR:
  137.                       SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_NO)));
  138.       break;
  139.     case DL_COPYCURSOR:
  140.                       SetCursor(LoadCursor(HINST_THISDLL, MAKEINTRESOURCE(IDC_COPY)));
  141.       break;
  142.     case DL_MOVECURSOR:
  143.                       SetCursor(LoadCursor(HINST_THISDLL, MAKEINTRESOURCE(IDC_MOVE)));
  144.       break;
  145.     default:
  146.       break;
  147.   }
  148.       }
  149.     /* Don't call the def proc, since it may try to change the
  150.      * selection or set timers or things like that.
  151.      */
  152.     return(0L);
  153.   }
  154. break;
  155.       case  WM_RBUTTONDOWN:
  156.       case  WM_LBUTTONUP:
  157. /* if we are capturing mouse - release it and check for acceptable place
  158.  * where mouse is now to decide drop or not
  159.  */
  160. if (bDragging)
  161.   {
  162.     HWND hwndParent;
  163.     pDragProp->hwndDrag = NULL;
  164.     KillTimer(hLB, TIMERID);
  165.     ReleaseCapture();
  166.     SetCursor(LoadCursor(NULL, IDC_ARROW));
  167.     hwndParent = GetParent(hLB);
  168.     sNotify.uNotification = uMsg==WM_LBUTTONUP ? DL_DROPPED : DL_CANCELDRAG;
  169.     sNotify.hWnd = hLB;
  170.             sNotify.ptCursor.x = GET_X_LPARAM(lParam);
  171.             sNotify.ptCursor.y = GET_Y_LPARAM(lParam);
  172.     ClientToScreen(hLB, &sNotify.ptCursor);
  173.     SendMessage(hwndParent, uDragListMsg, GetDlgCtrlID(hLB),
  174.   (LPARAM)(LPDRAGLISTINFO)&sNotify);
  175.     /* We need to make sure to return 0 in case this is from a
  176.      * keyboard message.
  177.      */
  178.     return(0L);
  179.   }
  180. break;
  181.       case WM_GETDLGCODE:
  182. if (bDragging)
  183.           {
  184.             return (DefSubclassProc(hLB, uMsg, wParam, lParam) |
  185.                 DLGC_WANTMESSAGE);
  186.           }
  187. break;
  188.       case WM_KEYDOWN:
  189.         if (wParam == VK_ESCAPE)
  190.           {
  191.             SendMessage(hLB, WM_RBUTTONDOWN, 0, 0L);
  192.           }
  193.         // fall through
  194.       case WM_CHAR:
  195.       case WM_KEYUP:
  196. /* We don't want the listbox processing this if we are dragging.
  197.  */
  198. if (bDragging)
  199.     return(0L);
  200. break;
  201.       default:
  202. break;
  203.     }
  204.   return(DefSubclassProc(hLB, uMsg, wParam, lParam));
  205. }
  206. BOOL WINAPI MakeDragList(HWND hLB)
  207. {
  208.   PDRAGPROP pDragProp;
  209.   if (!uDragListMsg)
  210.       uDragListMsg = RegisterWindowMessage(szDragListMsgString);
  211.   /* Check that we have not already subclassed this window.
  212.    */
  213.   if (GetWindowSubclass(hLB, DragListSubclassProc, 0, NULL))
  214.       return(TRUE);
  215.   pDragProp = (PDRAGPROP)LocalAlloc(LPTR, sizeof(DRAGPROP));
  216.   if (!pDragProp)
  217.       return(FALSE);
  218.   if (!SetWindowSubclass(hLB, DragListSubclassProc, 0, (DWORD_PTR)pDragProp))
  219.   {
  220.       LocalFree((HLOCAL)pDragProp);
  221.       return(FALSE);
  222.   }
  223.   return(TRUE);
  224. }
  225. int WINAPI LBItemFromPt(HWND hLB, POINT pt, BOOL bAutoScroll)
  226. {
  227.   static LONG dwLastScroll = 0;
  228.   RECT rc;
  229.   DWORD dwNow;
  230.   int nItem;
  231.   WORD wScrollDelay, wActualDelay;
  232.   ScreenToClient(hLB, &pt);
  233.   GetClientRect(hLB, &rc);
  234.   nItem = (int)SendMessage(hLB, LB_GETTOPINDEX, 0, 0L);
  235.   /* Is the point in the LB client area?
  236.    */
  237.   if (PtInRect(&rc, pt))
  238.     {
  239.       /* Check each visible item in turn.
  240.        */
  241.       for ( ; ; ++nItem)
  242. {
  243.   if (SendMessage(hLB, LB_GETITEMRECT, nItem, (LPARAM)(LPRECT)&rc)
  244. == LB_ERR)
  245.       break;
  246.   if (PtInRect(&rc, pt))
  247.       return(nItem);
  248. }
  249.     }
  250.   else
  251.     {
  252.       /* If we want autoscroll and the point is directly above or below the
  253.        * LB, determine the direction and if it is time to scroll yet.
  254.        */
  255.       if (bAutoScroll && (UINT)pt.x<(UINT)rc.right)
  256. {
  257.   if (pt.y <= 0)
  258.     {
  259.       --nItem;
  260.     }
  261.   else
  262.     {
  263.       ++nItem;
  264.       pt.y = rc.bottom - pt.y;
  265.     }
  266.   wScrollDelay = (WORD)(1000 /
  267. (INITLINESPERSECOND - pt.y/VERTCHANGENUMLINES));
  268.   dwNow = GetTickCount();
  269.   wActualDelay = (WORD)(dwNow - dwLastScroll);
  270.   if (wActualDelay > wScrollDelay)
  271.     {
  272.       /* This will the actual number of scrolls per second to be
  273.        * much closer to the required number.
  274.        */
  275.       if (wActualDelay > wScrollDelay * 2)
  276.   dwLastScroll = dwNow;
  277.       else
  278.   dwLastScroll += wScrollDelay;
  279.       SendMessage(hLB, LB_SETTOPINDEX, nItem, 0L);
  280.     }
  281. }
  282.     }
  283.   return(-1);
  284. }
  285. void WINAPI DrawInsert(HWND hwndParent, HWND hLB, int nItem)
  286. {
  287.   static POINT ptLastInsert;
  288.   static int nLastInsert = -1;
  289.   RECT rc;
  290.   /* Erase the old mark if necessary
  291.    */
  292.   if (nLastInsert>=0 && nItem!=nLastInsert)
  293.     {
  294.       rc.left = ptLastInsert.x;
  295.       rc.top = ptLastInsert.y;
  296.       rc.right = rc.left + DX_INSERT;
  297.       rc.bottom = rc.top + DY_INSERT;
  298.       /* Need to update immediately in case the insert rects overlap.
  299.        */
  300.       InvalidateRect(hwndParent, &rc, TRUE);
  301.       UpdateWindow(hwndParent);
  302.       nLastInsert = -1;
  303.     }
  304.   /* Draw a new mark if necessary
  305.    */
  306.   if (nItem!=nLastInsert && nItem>=0)
  307.     {
  308.       HICON hInsert = NULL;
  309.       if (!hInsert)
  310.   hInsert = LoadIcon(HINST_THISDLL, MAKEINTRESOURCE(IDI_INSERT));
  311.       if (hInsert)
  312. {
  313.   HDC hDC;
  314.   GetWindowRect(hLB, &rc);
  315.   ScreenToClient(hLB, (LPPOINT)&rc);
  316.   ptLastInsert.x = rc.left - DX_INSERT;
  317.   SendMessage(hLB, LB_GETITEMRECT, nItem, (LPARAM)(LPRECT)&rc);
  318.   ptLastInsert.y = rc.top - DY_INSERT/2;
  319.   nLastInsert = nItem;
  320.   ClientToScreen(hLB, &ptLastInsert);
  321.   ScreenToClient(hwndParent, &ptLastInsert);
  322.   hDC = GetDC(hwndParent);
  323.   DrawIcon(hDC, ptLastInsert.x, ptLastInsert.y, hInsert);
  324.   ReleaseDC(hwndParent, hDC);
  325. }
  326.     }
  327. }