CLV_ListView.c
上传用户:tuheem
上传日期:2007-05-01
资源大小:21889k
文件大小:104k
源码类别:

多媒体编程

开发平台:

Visual C++

  1. ////////////////////////////////////////////////////////////////////////////////
  2. #include "stdafx.h"
  3. #include "globals.h"
  4. #include "resource.h"
  5. ////////////////////////////////////////////////////////////////////////////////
  6. //
  7. typedef enum _CIe_WindowMode
  8. {
  9.     wmQuiescent,
  10.     wmHeader_Click,
  11.     wmHeader_ChangeWidth,
  12.     wmHeader_ChangeOrder,
  13.     wmHScrollbar_DragThumb,
  14.     wmHScrollbar_Scroll_Left,
  15.     wmHScrollbar_Scroll_Right,
  16.     wmHScrollbar_Page_Left,
  17.     wmHScrollbar_Page_Right,
  18.     wmVScrollbar_DragThumb,
  19.     wmVScrollbar_Scroll_Up,
  20.     wmVScrollbar_Scroll_Down,
  21.     wmVScrollbar_Page_Up,
  22.     wmVScrollbar_Page_Down,
  23.     wmList_Click,
  24.     wmList_Drag
  25. } CIe_WindowMode;
  26. //
  27. //
  28. //
  29. typedef struct _CIs_ListView_Column
  30. {
  31.     char* m_pColumnText;
  32.     int m_iColumnWidth;
  33.     DWORD m_dwFlags;
  34.     wp_GetItemText m_pfnTextAccessor;
  35.     wp_GetItemDrawColour m_pfnGetCustomDrawColour;
  36.     CPe_ListColumnAlign m_enAlign;
  37. } CIs_ListView_Column;
  38. //
  39. //
  40. //
  41. typedef struct _CIs_ListView_Item
  42. {
  43.     DWORD m_dwFlags;
  44.     const void* m_pItemData;
  45. } CIs_ListView_Item;
  46. //
  47. //
  48. //
  49. typedef struct _CIs_ListViewData
  50. {
  51.     HWND m_hWnd;
  52.     BOOL m_bInBatch;
  53.     int m_iBatchNesting;
  54.     BOOL m_bHasFocus;
  55.     unsigned int m_iItemHeight;
  56.     int m_iNumItemsOnPage;
  57.     RECT m_rClient;
  58.     RECT m_rHeader;
  59.     RECT m_rList;
  60.     RECT m_rScrollbar_Horiz;
  61.     RECT m_rScrollbar_Horiz_Thumb;
  62.     RECT m_rScrollbar_Vert;
  63.     RECT m_rScrollbar_Vert_Thumb;
  64.     // Columns
  65.     unsigned int m_iNumColumns;
  66.     CIs_ListView_Column* m_pColumns;
  67.     unsigned int* m_piColumnOrder;
  68.     // Items
  69.     CIs_ListView_Item* m_pItems;
  70.     int m_iNumItemsInBuffer;
  71.     int m_iNumItems;
  72.     // Selection, scroll & focus
  73.     int m_iXOrigin;
  74.     int m_iXScrollExtent;
  75.     BOOL m_bScrollBarVisible_Horiz;
  76.     BOOL m_bScrollBarVisible_Vert;
  77.     int m_iFirstVisibleItem;
  78.     int m_iFocusItem;
  79.     int m_iKeyboardAnchor;
  80.     // State
  81.     CIe_WindowMode m_enWindowMode;
  82.     unsigned int m_uiAutorepeatTimer;
  83.     BOOL m_bAutoRepeatFirst;
  84.     BOOL m_bMouseOverScrollbutton;
  85.     int m_iActiveHeaderCol;
  86.     int m_iClickedItem;
  87.     POINT m_ptMouseDown;
  88.     POINT m_ptMouseDown_OnHitItem;
  89.     DWORD m_dwMouseDown_Keys;
  90.     // Callback handlers
  91.     wp_DrawBackgroundRect m_hndlr_DrawBackgroundRect;
  92.     wp_HeaderChanged m_hndlr_HeaderChanged;
  93.     wp_ItemCallback m_hndlr_ItemSelected;
  94.     wp_ItemCallback m_hndlr_ItemAction;
  95.     wp_ItemCallback m_hndlr_ItemDrag;
  96.     wp_ItemSubCallback m_hndlr_ItemRightClick;
  97.     wp_ColHeaderClick m_hndlr_ColHeaderClick;
  98.     wp_UnhandledKeyPress m_hndlr_UnhandledKeyPress;
  99. } CIs_ListViewData;
  100. //
  101. ////////////////////////////////////////////////////////////////////////////////
  102. #define CPC_HEADERCOLLAPSETHRESHOLD 8
  103. #define CPC_HEADERDRAGDISTANCE 16
  104. #define CPC_BUFFERQUANTISATION 128
  105. #define CPC_HEADERDRAG_HTWIDTH 8
  106. #define CPC_HEADERDRAG_DEFAULTWIDTH 100
  107. #define CPC_SCROLLBAR_HORIZ_LINESIZE 10
  108. #define CPC_SCROLLBAR_HORIZ_PAGESIZE 100
  109. #define CPC_SCROLLBAR_MOUSEWHEELAMOUNT 5
  110. #define CPC_TIMERID_AUTOREPEAT 1
  111. #define CPC_LISTDRAGDISTANCE 4
  112. LRESULT CALLBACK exp_ListViewWindowProc(HWND hWnd, UINT uiMessage, WPARAM wParam, LPARAM lParam);
  113. #define CLC_COOLPLAYER_LISTVIEW_WINDOWCLASSNAME "CoolPlayer_ListView"
  114. ////////////////////////////////////////////////////////////////////////////////
  115. //
  116. //
  117. //
  118. CP_HLISTVIEW CLV_Create(HWND hWndParent, const int iX, const int iY, const int iWidth, const int iHeight)
  119. {
  120.     WNDCLASS wcPlaylist;
  121.     HWND hWndWindow;
  122.     CIs_ListViewData* pListData;
  123.     wcPlaylist.style = CS_DBLCLKS;
  124.     wcPlaylist.lpfnWndProc = exp_ListViewWindowProc;
  125.     wcPlaylist.cbClsExtra = 0;
  126.     wcPlaylist.cbWndExtra = 0;
  127.     wcPlaylist.hInstance = GetModuleHandle(NULL);
  128.     wcPlaylist.hIcon = NULL; // We will explicity set our icons (so that we have a nice small icon)
  129.     wcPlaylist.hCursor = NULL;
  130.     wcPlaylist.hbrBackground = (HBRUSH)GetStockObject(HOLLOW_BRUSH); // Prevent the system drawing white over our invaid rgn before we can paint
  131.     wcPlaylist.lpszMenuName = NULL;
  132.     wcPlaylist.lpszClassName = CLC_COOLPLAYER_LISTVIEW_WINDOWCLASSNAME;
  133.     RegisterClass(&wcPlaylist);
  134.     pListData = (CIs_ListViewData*)malloc(sizeof(CIs_ListViewData));
  135.     pListData->m_bInBatch = FALSE;
  136.     pListData->m_iBatchNesting = 0;
  137.     pListData->m_bHasFocus = FALSE;
  138.     pListData->m_iNumColumns = 0;
  139.     pListData->m_pColumns = NULL;
  140.     pListData->m_piColumnOrder = NULL;
  141.     pListData->m_enWindowMode = wmQuiescent;
  142.     pListData->m_uiAutorepeatTimer = 0;
  143.     pListData->m_pItems = NULL;
  144.     pListData->m_iNumItems = 0;
  145.     pListData->m_iNumItemsInBuffer = 0;
  146.     pListData->m_iFirstVisibleItem = 0;
  147.     pListData->m_iXOrigin = 0;
  148.     pListData->m_bScrollBarVisible_Horiz = FALSE;
  149.     pListData->m_iFocusItem = CPC_INVALIDITEM;
  150.     pListData->m_iKeyboardAnchor = CPC_INVALIDITEM;
  151.     // Handlers
  152.     pListData->m_hndlr_DrawBackgroundRect = NULL;
  153.     pListData->m_hndlr_HeaderChanged = NULL;
  154.     pListData->m_hndlr_ItemSelected = NULL;
  155.     pListData->m_hndlr_ItemAction = NULL;
  156.     pListData->m_hndlr_ItemDrag = NULL;
  157.     pListData->m_hndlr_ItemRightClick = NULL;
  158.     pListData->m_hndlr_ColHeaderClick = NULL;
  159.     pListData->m_hndlr_UnhandledKeyPress = NULL;
  160.     hWndWindow = CreateWindowEx(WS_EX_ACCEPTFILES,
  161.                                 CLC_COOLPLAYER_LISTVIEW_WINDOWCLASSNAME,
  162.                                 "",
  163.                                 WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE,
  164.                                 iX, iY, iWidth, iHeight, hWndParent,
  165.                                 NULL,
  166.                                 GetModuleHandle(NULL),
  167.                                 pListData);
  168.     return (CP_HLISTVIEW)pListData;
  169. }
  170. //
  171. //
  172. //
  173. HWND CLV_GetHWND(CP_HLISTVIEW _hListData)
  174. {
  175.     CIs_ListViewData* pListData = (CIs_ListViewData*)_hListData;
  176.     CP_CHECKOBJECT(pListData);
  177.     return pListData->m_hWnd;
  178. }
  179. //
  180. //
  181. //
  182. void CLV_EmptyItems(CIs_ListViewData* pListData)
  183. {
  184.     if(pListData->m_pItems == NULL)
  185.         return;
  186.     free(pListData->m_pItems);
  187.     pListData->m_pItems = NULL;
  188.     pListData->m_iNumItems = 0;
  189.     pListData->m_iNumItemsInBuffer = 0;
  190. }
  191. //
  192. //
  193. //
  194. void CLV_InvalidateWindow(CIs_ListViewData* pListData)
  195. {
  196.     InvalidateRect(pListData->m_hWnd, NULL, FALSE);
  197. }
  198. //
  199. //
  200. //
  201. int CLV_YOffsetToItem(CIs_ListViewData* pListData, const int iYOffset)
  202. {
  203.     return pListData->m_iFirstVisibleItem
  204.            + (int)floor( (float)(iYOffset-pListData->m_rList.top) / (float)pListData->m_iItemHeight);
  205. }
  206. //
  207. //
  208. //
  209. int CLV_GetListRect_Lines(CIs_ListViewData* pListData)
  210. {
  211.     return (int)floor( (float)(pListData->m_rList.bottom-pListData->m_rList.top) / (float)pListData->m_iItemHeight);
  212. }
  213. //
  214. //
  215. //
  216. void CLV_CleanupWindowData(CIs_ListViewData* pListData)
  217. {
  218.     // Free items
  219.     CLV_EmptyItems(pListData);
  220.     // Free columns
  221.     if(pListData->m_pColumns)
  222.     {
  223.         unsigned int iColumnIDX;
  224.         for(iColumnIDX = 0; iColumnIDX < pListData->m_iNumColumns; iColumnIDX++)
  225.         {
  226.             if(pListData->m_pColumns[iColumnIDX].m_pColumnText)
  227.                 free(pListData->m_pColumns[iColumnIDX].m_pColumnText);
  228.         }
  229.     }
  230.     free(pListData);
  231. }
  232. //
  233. //
  234. //
  235. void CLV_DrawText(CPs_DrawContext* pDC, const char* pcString, const RECT* _prTarget, const CPe_ListColumnAlign enAlign)
  236. {
  237.     RECT rDraw;
  238.     UINT uiFlags;
  239.     // Skip this draw if we are totally clipped
  240.     if(_prTarget->right < pDC->m_rClip.left
  241.             || _prTarget->bottom < pDC->m_rClip.top
  242.             || _prTarget->left > pDC->m_rClip.right
  243.             || _prTarget->top > pDC->m_rClip.bottom)
  244.     {
  245.         return;
  246.     }
  247.     rDraw = *_prTarget;
  248.     if(enAlign == lcaLeft)
  249.         uiFlags = DT_LEFT;
  250.     else if(enAlign == lcaCentre)
  251.         uiFlags = DT_CENTER;
  252.     else if(enAlign == lcaRight)
  253.     {
  254.         uiFlags = DT_RIGHT;
  255.         rDraw.right -= 5;
  256.         if(rDraw.right < rDraw.left)
  257.             rDraw.right = rDraw.left;
  258.     }
  259.     else
  260.         uiFlags = 0L;
  261.     OffsetRect(&rDraw, pDC->m_ptOffset.x, pDC->m_ptOffset.y);
  262.     DrawText(pDC->m_dcDraw, pcString, -1, &rDraw, DT_WORD_ELLIPSIS | DT_NOPREFIX | uiFlags);
  263. }
  264. //
  265. //
  266. //
  267. void CLV_UpdateScrollBars(CIs_ListViewData* pListData)
  268. {
  269.     unsigned int _iColIDX;
  270.     int iListRectWidth;
  271.     int iListRectHeight_Lines;
  272.     BOOL bCountedVScrollbar;
  273.     // Get the total width
  274.     pListData->m_iXScrollExtent = 0;
  275.     for(_iColIDX = 0; _iColIDX < pListData->m_iNumColumns; _iColIDX++)
  276.     {
  277.         unsigned int iColumnIDX = pListData->m_piColumnOrder[_iColIDX];
  278.         if(pListData->m_pColumns[iColumnIDX].m_dwFlags & CPLV_COLFLAG_HIDDEN)
  279.             continue;
  280.         pListData->m_iXScrollExtent += pListData->m_pColumns[iColumnIDX].m_iColumnWidth;
  281.     }
  282.     // Work out available width
  283.     iListRectWidth = pListData->m_rClient.right-pListData->m_rClient.left;
  284.     // - If we need a vertical scrollbar (at this point) - then take this into account
  285.     iListRectHeight_Lines = CLV_GetListRect_Lines(pListData);
  286.     if(iListRectHeight_Lines < pListData->m_iNumItems)
  287.     {
  288.         bCountedVScrollbar = TRUE;
  289.         pListData->m_rList.right = pListData->m_rClient.right - glb_pSkin->mpl_pVScrollBar_TrackUp->m_szSize.cx;
  290.         iListRectWidth -= glb_pSkin->mpl_pVScrollBar_TrackUp->m_szSize.cx;
  291.     }
  292.     else
  293.         bCountedVScrollbar = FALSE;
  294.     // No (horiz) scrollbar needed?
  295.     if(pListData->m_iXScrollExtent <= iListRectWidth)
  296.     {
  297.         pListData->m_rList.bottom = pListData->m_rClient.bottom;
  298.         if(pListData->m_bScrollBarVisible_Horiz == TRUE)
  299.             CLV_InvalidateWindow(pListData);
  300.         pListData->m_bScrollBarVisible_Horiz = FALSE;
  301.         pListData->m_iXOrigin = 0;
  302.         SetRect(&pListData->m_rScrollbar_Horiz, 0, 0, 0, 0);
  303.     }
  304.     else
  305.     {
  306.         int iTrackWidth;
  307.         int iTrackThumbWidth;
  308.         int iTrackThumbPos;
  309.         int iTrackThumbWidth_Min;
  310.         int iTrackThumbWidth_Max;
  311.         pListData->m_rList.bottom = pListData->m_rClient.bottom - glb_pSkin->mpl_pHScrollBar_TrackUp->m_szSize.cy;
  312.         // The presence of this scrollbar may require a vertical scrollbar - take this into account
  313.         if(bCountedVScrollbar == FALSE)
  314.         {
  315.             iListRectHeight_Lines = CLV_GetListRect_Lines(pListData);
  316.             if(iListRectHeight_Lines < pListData->m_iNumItems)
  317.             {
  318.                 bCountedVScrollbar = TRUE;
  319.                 pListData->m_rList.right = pListData->m_rClient.right - glb_pSkin->mpl_pVScrollBar_TrackUp->m_szSize.cx;
  320.                 iListRectWidth -= glb_pSkin->mpl_pVScrollBar_TrackUp->m_szSize.cx;
  321.             }
  322.         }
  323.         // Work out size of scroll track
  324.         iTrackWidth = iListRectWidth - (glb_pSkin->mpl_pHScrollBar_Left->m_pImage->m_szSize.cx
  325.                                         + glb_pSkin->mpl_pHScrollBar_Right->m_pImage->m_szSize.cx);
  326.         // Setup scrollbar
  327.         if(pListData->m_bScrollBarVisible_Horiz == FALSE)
  328.             CLV_InvalidateWindow(pListData);
  329.         pListData->m_bScrollBarVisible_Horiz = TRUE;
  330.         pListData->m_rScrollbar_Horiz = pListData->m_rList;
  331.         pListData->m_rScrollbar_Horiz.top = pListData->m_rList.bottom;
  332.         pListData->m_rScrollbar_Horiz.bottom = pListData->m_rClient.bottom;
  333.         // Limit scroll to fit into window
  334.         if( (pListData->m_iXOrigin + iListRectWidth) > pListData->m_iXScrollExtent)
  335.             pListData->m_iXOrigin = pListData->m_iXScrollExtent - iListRectWidth;
  336.         // Setup track rect
  337.         iTrackThumbWidth = (int)( (( (float)iListRectWidth / (float)pListData->m_iXScrollExtent ) * (float)iTrackWidth));
  338.         iTrackThumbWidth_Min = glb_pSkin->mpl_rHScrollBar_Track_Tile.left
  339.                                + (glb_pSkin->mpl_pHScrollBar_TrackUp->m_szSize.cx - glb_pSkin->mpl_rHScrollBar_Track_Tile.right);
  340.         iTrackThumbWidth_Max = (pListData->m_rScrollbar_Horiz.right - pListData->m_rScrollbar_Horiz.left);
  341.         if(iTrackThumbWidth < iTrackThumbWidth_Min)
  342.             iTrackThumbWidth = iTrackThumbWidth_Min;
  343.         if(iTrackThumbWidth > iTrackThumbWidth_Max)
  344.             iTrackThumbWidth = iTrackThumbWidth_Max;
  345.         pListData->m_rScrollbar_Horiz_Thumb = pListData->m_rScrollbar_Horiz;
  346.         iTrackThumbPos = (int)( (  ((float)pListData->m_iXOrigin / (float)(pListData->m_iXScrollExtent - iListRectWidth)) * (float)(iTrackWidth-iTrackThumbWidth)));
  347.         pListData->m_rScrollbar_Horiz_Thumb.left = iTrackThumbPos + glb_pSkin->mpl_pHScrollBar_Left->m_pImage->m_szSize.cx;
  348.         pListData->m_rScrollbar_Horiz_Thumb.right = pListData->m_rScrollbar_Horiz_Thumb.left + iTrackThumbWidth;
  349.         InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Horiz, FALSE);
  350.     }
  351.     // Vertical scrollbar
  352.     iListRectHeight_Lines = CLV_GetListRect_Lines(pListData);
  353.     if(pListData->m_iNumItems <= iListRectHeight_Lines)
  354.     {
  355.         pListData->m_rList.right = pListData->m_rClient.right;
  356.         pListData->m_rHeader.right = pListData->m_rList.right;
  357.         if(pListData->m_bScrollBarVisible_Vert == TRUE)
  358.             CLV_InvalidateWindow(pListData);
  359.         pListData->m_bScrollBarVisible_Vert = FALSE;
  360.         pListData->m_iFirstVisibleItem = 0;
  361.         SetRect(&pListData->m_rScrollbar_Vert, 0, 0, 0, 0);
  362.     }
  363.     else
  364.     {
  365.         int iTrackHeight;
  366.         int iTrackThumbHeight;
  367.         int iTrackThumbPos;
  368.         int iTrackThumbHeight_Min;
  369.         int iTrackThumbHeight_Max;
  370.         const int iListRectHeight = pListData->m_rList.bottom - pListData->m_rList.top;
  371.         // Work out size of scroll track
  372.         iTrackHeight = iListRectHeight - (glb_pSkin->mpl_pVScrollBar_Up->m_iStateHeight
  373.                                           + glb_pSkin->mpl_pVScrollBar_Down->m_iStateHeight);
  374.         // Setup scrollbar
  375.         pListData->m_rList.right = pListData->m_rClient.right - glb_pSkin->mpl_pVScrollBar_TrackUp->m_szSize.cx;
  376.         pListData->m_rHeader.right = pListData->m_rList.right;
  377.         if(pListData->m_bScrollBarVisible_Vert == FALSE)
  378.             CLV_InvalidateWindow(pListData);
  379.         pListData->m_bScrollBarVisible_Vert = TRUE;
  380.         pListData->m_rScrollbar_Vert = pListData->m_rList;
  381.         pListData->m_rScrollbar_Vert.left = pListData->m_rList.right;
  382.         pListData->m_rScrollbar_Vert.right = pListData->m_rClient.right;
  383.         // Limit scroll to fit into window
  384.         if( (pListData->m_iFirstVisibleItem + iListRectHeight_Lines) > pListData->m_iNumItems)
  385.             pListData->m_iFirstVisibleItem = pListData->m_iNumItems - iListRectHeight_Lines;
  386.         // Setup track rect
  387.         iTrackThumbHeight = (int)( (( (float)iListRectHeight_Lines / (float)pListData->m_iNumItems ) * (float)iTrackHeight));
  388.         iTrackThumbHeight_Min = glb_pSkin->mpl_rVScrollBar_Track_Tile.top
  389.                                 + (glb_pSkin->mpl_pVScrollBar_TrackUp->m_szSize.cy - glb_pSkin->mpl_rVScrollBar_Track_Tile.bottom);
  390.         iTrackThumbHeight_Max = (pListData->m_rScrollbar_Vert.bottom - pListData->m_rScrollbar_Vert.top);
  391.         if(iTrackThumbHeight < iTrackThumbHeight_Min)
  392.             iTrackThumbHeight = iTrackThumbHeight_Min;
  393.         if(iTrackThumbHeight > iTrackThumbHeight_Max)
  394.             iTrackThumbHeight = iTrackThumbHeight_Max;
  395.         pListData->m_rScrollbar_Vert_Thumb = pListData->m_rScrollbar_Vert;
  396.         iTrackThumbPos = (int)( (  ((float)pListData->m_iFirstVisibleItem / (float)(pListData->m_iNumItems - iListRectHeight_Lines)) * (float)(iTrackHeight-iTrackThumbHeight)));
  397.         pListData->m_rScrollbar_Vert_Thumb.top = iTrackThumbPos + pListData->m_rScrollbar_Vert.top + glb_pSkin->mpl_pVScrollBar_Up->m_iStateHeight;
  398.         pListData->m_rScrollbar_Vert_Thumb.bottom = pListData->m_rScrollbar_Vert_Thumb.top + iTrackThumbHeight;
  399.         InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Horiz, FALSE);
  400.     }
  401. }
  402. //
  403. //
  404. //
  405. void CLV_BeginBatch(CP_HLISTVIEW _hListData)
  406. {
  407.     CIs_ListViewData* pListData = (CIs_ListViewData*)_hListData;
  408.     CP_CHECKOBJECT(pListData);
  409.     pListData->m_bInBatch = TRUE;
  410.     pListData->m_iBatchNesting ++;
  411. }
  412. //
  413. //
  414. //
  415. void CLV_EndBatch(CP_HLISTVIEW _hListData)
  416. {
  417.     CIs_ListViewData* pListData = (CIs_ListViewData*)_hListData;
  418.     CP_CHECKOBJECT(pListData);
  419.     pListData->m_iBatchNesting--;
  420.     if(pListData->m_iBatchNesting == 0)
  421.     {
  422.         pListData->m_bInBatch = FALSE;
  423.         CLV_InvalidateWindow(pListData);
  424.         CLV_UpdateScrollBars(pListData);
  425.     }
  426. }
  427. //
  428. //
  429. //
  430. void CLV_Scroll_Horiz(CIs_ListViewData* pListData, const int iPixels)
  431. {
  432.     const int iListRectWidth = pListData->m_rList.right-pListData->m_rList.left;
  433.     int iNewXOrigin;
  434.     iNewXOrigin = pListData->m_iXOrigin + iPixels;
  435.     // Ensure scoll is in range
  436.     if(iNewXOrigin < 0)
  437.         iNewXOrigin = 0;
  438.     if( (iNewXOrigin + iListRectWidth) > pListData->m_iXScrollExtent)
  439.         iNewXOrigin = pListData->m_iXScrollExtent - iListRectWidth;
  440.     // Update only if we have changed the origin
  441.     if(iNewXOrigin != pListData->m_iXOrigin)
  442.     {
  443.         pListData->m_iXOrigin = iNewXOrigin;
  444.         // Update display
  445.         CLV_UpdateScrollBars(pListData);
  446.         CLV_InvalidateWindow(pListData);
  447.     }
  448. }
  449. //
  450. //
  451. //
  452. void CLV_Scroll_Vert(CIs_ListViewData* pListData, const int iLines)
  453. {
  454.     const int iListRectHeight_Lines = CLV_GetListRect_Lines(pListData);
  455.     int iNewFirstVisibleItem;
  456.     iNewFirstVisibleItem = pListData->m_iFirstVisibleItem + iLines;
  457.     // Ensure scoll is in range
  458.     if(iNewFirstVisibleItem < 0)
  459.         iNewFirstVisibleItem = 0;
  460.     if( (iNewFirstVisibleItem + iListRectHeight_Lines) > pListData->m_iNumItems)
  461.         iNewFirstVisibleItem = pListData->m_iNumItems - iListRectHeight_Lines;
  462.     // Update only if we have changed the first visible item
  463.     if(iNewFirstVisibleItem != pListData->m_iFirstVisibleItem)
  464.     {
  465.         pListData->m_iFirstVisibleItem = iNewFirstVisibleItem;
  466.         CLV_UpdateScrollBars(pListData);
  467.         CLV_InvalidateWindow(pListData);
  468.     }
  469. }
  470. //
  471. //
  472. //
  473. void CLV_UpdateWindowDims(CIs_ListViewData* pListData, const int iCX, const int iCY)
  474. {
  475.     // Work out window parts
  476.     pListData->m_rClient.top = 0;
  477.     pListData->m_rClient.left = 0;
  478.     pListData->m_rClient.right = iCX;
  479.     pListData->m_rClient.bottom = iCY;
  480.     pListData->m_rHeader.top = 0;
  481.     pListData->m_rHeader.left = 0;
  482.     pListData->m_rHeader.right = iCX;
  483.     pListData->m_rHeader.bottom = pListData->m_iItemHeight;
  484.     pListData->m_rList.top = pListData->m_rHeader.bottom;
  485.     pListData->m_rList.left = 0;
  486.     pListData->m_rList.right = iCX;
  487.     pListData->m_rList.bottom = iCY;
  488.     CLV_UpdateScrollBars(pListData);
  489.     pListData->m_iNumItemsOnPage = (int)floor( (float)(pListData->m_rList.bottom-pListData->m_rList.top) / (float)pListData->m_iItemHeight);
  490. }
  491. //
  492. //
  493. //
  494. void CLV_DrawBackgroundRect(CIs_ListViewData* pListData, CPs_DrawContext* pDC, const RECT* _prTarget)
  495. {
  496.     // Call the parent and get it to draw into this rect
  497.     RECT rDraw;
  498.     POINT ptParentOffset;
  499.     CPs_DrawContext drawcontext;
  500.     HRGN rgnClip;
  501.     // Skip this draw if there is no handler registered
  502.     if(!pListData->m_hndlr_DrawBackgroundRect)
  503.         return;
  504.     // Skip this draw if we are totally clipped
  505.     if(_prTarget->right < pDC->m_rClip.left
  506.             || _prTarget->bottom < pDC->m_rClip.top
  507.             || _prTarget->left > pDC->m_rClip.right
  508.             || _prTarget->top > pDC->m_rClip.bottom)
  509.     {
  510.         return;
  511.     }
  512.     // Get rect and clip relative to the parent's origin
  513.     IntersectRect(&rDraw, _prTarget, &pDC->m_rClip);
  514.     // Correct our draw context to bring it into our parent's domain
  515.     ptParentOffset.x = 0;
  516.     ptParentOffset.y = 0;
  517.     ClientToScreen(pListData->m_hWnd, &ptParentOffset);
  518.     ScreenToClient(GetParent(pListData->m_hWnd), &ptParentOffset);
  519.     drawcontext = *pDC;
  520.     drawcontext.m_ptOffset.x -= ptParentOffset.x;
  521.     drawcontext.m_ptOffset.y -= ptParentOffset.y;
  522.     drawcontext.m_rClip = rDraw;
  523.     OffsetRect(&drawcontext.m_rClip, ptParentOffset.x, ptParentOffset.y);
  524.     // Setup a GDI clip region
  525.     rgnClip = CreateRectRgn(rDraw.left + pDC->m_ptOffset.x, rDraw.top + pDC->m_ptOffset.y,
  526.                             rDraw.right + pDC->m_ptOffset.x, rDraw.bottom + pDC->m_ptOffset.y);
  527.     SelectClipRgn(drawcontext.m_dcDraw, rgnClip);
  528.     pListData->m_hndlr_DrawBackgroundRect(&drawcontext);
  529.     SelectClipRgn(drawcontext.m_dcDraw, NULL);
  530. }
  531. //
  532. //
  533. //
  534. void CLV_Handle_WM_PAINT(CIs_ListViewData* pListData)
  535. {
  536.     RECT rClient;
  537.     PAINTSTRUCT ps;
  538.     HDC dcPaint;
  539.     HBITMAP hbmSurface, hbmSurface_Old;
  540.     HFONT hfOld;
  541.     CPs_DrawContext drawcontext;
  542.     BOOL bAvoidFlicker;
  543.     // Do some debug checking
  544.     CP_ASSERT(pListData->m_iFocusItem == CPC_INVALIDITEM || (pListData->m_iFocusItem >= 0 && pListData->m_iFocusItem < pListData->m_iNumItems));
  545.     // Prepare for draw
  546.     dcPaint = BeginPaint(pListData->m_hWnd, &ps);
  547.     rClient = pListData->m_rClient;
  548.     GetClipBox(dcPaint, &drawcontext.m_rClip);
  549.     // Null clip rgn?
  550.     if(drawcontext.m_rClip.right == drawcontext.m_rClip.left
  551.             || drawcontext.m_rClip.top == drawcontext.m_rClip.bottom)
  552.     {
  553.         EndPaint(pListData->m_hWnd, &ps);
  554.         return;
  555.     }
  556.     bAvoidFlicker = TRUE;
  557.     if(bAvoidFlicker == TRUE)
  558.     {
  559.         hbmSurface = CreateCompatibleBitmap(dcPaint, drawcontext.m_rClip.right-drawcontext.m_rClip.left, drawcontext.m_rClip.bottom-drawcontext.m_rClip.top);
  560.         CP_ASSERT(hbmSurface);
  561.         drawcontext.m_dcDraw = CreateCompatibleDC(dcPaint);
  562.         CP_ASSERT(drawcontext.m_dcDraw);
  563.         hbmSurface_Old = (HBITMAP)SelectObject(drawcontext.m_dcDraw, hbmSurface);
  564.         drawcontext.m_ptOffset.x = -drawcontext.m_rClip.left;
  565.         drawcontext.m_ptOffset.y = -drawcontext.m_rClip.top;
  566.     }
  567.     else
  568.     {
  569.         hbmSurface = NULL;
  570.         drawcontext.m_dcDraw = dcPaint;
  571.         drawcontext.m_ptOffset.x = 0;
  572.         drawcontext.m_ptOffset.y = 0;
  573.         hbmSurface_Old = NULL;
  574.     }
  575.     // Draw header
  576.     hfOld = (HFONT)SelectObject(drawcontext.m_dcDraw, glb_pSkin->mpl_hfFont);
  577.     {
  578.         int iCursorX;
  579.         unsigned int _iColIDX;
  580.         RECT rHeaderItem;
  581.         // Draw header items
  582.         iCursorX = -pListData->m_iXOrigin;
  583.         SetTextColor(drawcontext.m_dcDraw, glb_pSkin->mpl_ListHeaderColour);
  584.         SetBkMode(drawcontext.m_dcDraw, TRANSPARENT);
  585.         for(_iColIDX = 0; _iColIDX < pListData->m_iNumColumns; _iColIDX++)
  586.         {
  587.             int iColumnIDX = pListData->m_piColumnOrder[_iColIDX];
  588.             int iTextOffset = 0;
  589.             int iNextCursorX;
  590.             if(pListData->m_pColumns[iColumnIDX].m_dwFlags & CPLV_COLFLAG_HIDDEN)
  591.                 continue;
  592.             rHeaderItem.left = iCursorX;
  593.             rHeaderItem.right = iCursorX + pListData->m_pColumns[iColumnIDX].m_iColumnWidth;
  594.             rHeaderItem.top = pListData->m_rHeader.top;
  595.             rHeaderItem.bottom = pListData->m_rHeader.bottom;
  596.             if(rHeaderItem.right > (pListData->m_rHeader.right - CPC_HEADERCOLLAPSETHRESHOLD))
  597.                 rHeaderItem.right = pListData->m_rHeader.right;
  598.             iNextCursorX = rHeaderItem.right;
  599.             // Draw this header item down - if it's the clicked or dragged header item
  600.             if( (pListData->m_enWindowMode == wmHeader_Click || pListData->m_enWindowMode == wmHeader_ChangeOrder)
  601.                     && iColumnIDX == pListData->m_iActiveHeaderCol)
  602.             {
  603.                 iTextOffset = 1;
  604.                 CPIG_TiledFill(&drawcontext, &rHeaderItem, &glb_pSkin->mpl_rListHeader_SourceTile, glb_pSkin->mpl_pListHeader_Down, CIC_TILEDFILOPTIONS_NONE);
  605.             }
  606.             else
  607.                 CPIG_TiledFill(&drawcontext, &rHeaderItem, &glb_pSkin->mpl_rListHeader_SourceTile, glb_pSkin->mpl_pListHeader_Up, CIC_TILEDFILOPTIONS_NONE);
  608.             rHeaderItem.left += glb_pSkin->mpl_rListHeader_SourceTile.left + iTextOffset;
  609.             rHeaderItem.right -= glb_pSkin->mpl_pListHeader_Down->m_szSize.cx - glb_pSkin->mpl_rListHeader_SourceTile.right;
  610.             rHeaderItem.top += iTextOffset;
  611.             if(pListData->m_pColumns[iColumnIDX].m_pColumnText)
  612.                 CLV_DrawText(&drawcontext, pListData->m_pColumns[iColumnIDX].m_pColumnText, &rHeaderItem, pListData->m_pColumns[iColumnIDX].m_enAlign);
  613.             iCursorX = iNextCursorX;
  614.             if(iCursorX == rClient.right)
  615.                 break;
  616.         }
  617.         // Draw a dummy last column
  618.         if(iCursorX < rClient.right)
  619.         {
  620.             rHeaderItem.left = iCursorX;
  621.             rHeaderItem.right = pListData->m_rHeader.right;
  622.             rHeaderItem.top = pListData->m_rHeader.top;
  623.             rHeaderItem.bottom = pListData->m_rHeader.top + pListData->m_iItemHeight;
  624.             CPIG_TiledFill(&drawcontext, &rHeaderItem, &glb_pSkin->mpl_rListHeader_SourceTile, glb_pSkin->mpl_pListHeader_Up, CIC_TILEDFILOPTIONS_NONE);
  625.         }
  626.     }
  627.     // Draw the (horiz) scrollbar
  628.     if(pListData->m_bScrollBarVisible_Horiz == TRUE)
  629.     {
  630.         RECT rHScrollBarTrack = pListData->m_rScrollbar_Horiz;
  631.         rHScrollBarTrack.left += glb_pSkin->mpl_pHScrollBar_Left->m_pImage->m_szSize.cx;
  632.         rHScrollBarTrack.right -= glb_pSkin->mpl_pHScrollBar_Right->m_pImage->m_szSize.cx;
  633.         // Draw buttons
  634.         CPIG_DrawStateImage( &drawcontext,
  635.                              pListData->m_rScrollbar_Horiz.left,
  636.                              rHScrollBarTrack.top,
  637.                              glb_pSkin->mpl_pHScrollBar_Left,
  638.                              (pListData->m_enWindowMode == wmHScrollbar_Scroll_Left && pListData->m_bMouseOverScrollbutton)
  639.                              ? igsActive : igsQuiescent);
  640.         CPIG_DrawStateImage( &drawcontext,
  641.                              rHScrollBarTrack.right,
  642.                              rHScrollBarTrack.top,
  643.                              glb_pSkin->mpl_pHScrollBar_Right,
  644.                              (pListData->m_enWindowMode == wmHScrollbar_Scroll_Right && pListData->m_bMouseOverScrollbutton)
  645.                              ? igsActive : igsQuiescent);
  646.         // Scrollbar background
  647.         CPIG_TiledFill(&drawcontext, &rHScrollBarTrack, &glb_pSkin->mpl_rHScrollBar_Bk_Tile, glb_pSkin->mpl_pHScrollBar_Bk, CIC_TILEDFILOPTIONS_NONE);
  648.         // Track
  649.         CPIG_TiledFill( &drawcontext,
  650.                         &pListData->m_rScrollbar_Horiz_Thumb,
  651.                         &glb_pSkin->mpl_rHScrollBar_Track_Tile,
  652.                         pListData->m_enWindowMode == wmHScrollbar_DragThumb ? glb_pSkin->mpl_pHScrollBar_TrackDn : glb_pSkin->mpl_pHScrollBar_TrackUp, CIC_TILEDFILOPTIONS_NONE);
  653.     }
  654.     // Draw the (vert) scrollbar
  655.     if(pListData->m_bScrollBarVisible_Vert == TRUE)
  656.     {
  657.         RECT rHeaderPad;
  658.         RECT rVScrollBarTrack = pListData->m_rScrollbar_Vert;
  659.         rVScrollBarTrack.top += glb_pSkin->mpl_pHScrollBar_Left->m_iStateHeight;
  660.         rVScrollBarTrack.bottom -= glb_pSkin->mpl_pHScrollBar_Right->m_iStateHeight;
  661.         // Draw buttons
  662.         CPIG_DrawStateImage( &drawcontext,
  663.                              rVScrollBarTrack.left, pListData->m_rScrollbar_Vert.top,
  664.                              glb_pSkin->mpl_pVScrollBar_Up,
  665.                              (pListData->m_enWindowMode == wmVScrollbar_Scroll_Up && pListData->m_bMouseOverScrollbutton)
  666.                              ? igsActive : igsQuiescent);
  667.         CPIG_DrawStateImage( &drawcontext,
  668.                              rVScrollBarTrack.left, rVScrollBarTrack.bottom,
  669.                              glb_pSkin->mpl_pVScrollBar_Down,
  670.                              (pListData->m_enWindowMode == wmVScrollbar_Scroll_Down && pListData->m_bMouseOverScrollbutton)
  671.                              ? igsActive : igsQuiescent);
  672.         // Scrollbar background
  673.         CPIG_TiledFill(&drawcontext, &rVScrollBarTrack, &glb_pSkin->mpl_rVScrollBar_Bk_Tile, glb_pSkin->mpl_pVScrollBar_Bk, CIC_TILEDFILOPTIONS_NONE);
  674.         // Track
  675.         CPIG_TiledFill( &drawcontext,
  676.                         &pListData->m_rScrollbar_Vert_Thumb,
  677.                         &glb_pSkin->mpl_rVScrollBar_Track_Tile,
  678.                         pListData->m_enWindowMode == wmVScrollbar_DragThumb ? glb_pSkin->mpl_pVScrollBar_TrackDn : glb_pSkin->mpl_pVScrollBar_TrackUp,
  679.                         CIC_TILEDFILOPTIONS_NONE);
  680.         // Draw some background above the scrollbar (and beyond the header area)
  681.         rHeaderPad = pListData->m_rHeader;
  682.         rHeaderPad.left = rHeaderPad.right;
  683.         rHeaderPad.right = pListData->m_rClient.right;
  684.         CLV_DrawBackgroundRect(pListData, &drawcontext, &rHeaderPad);
  685.     }
  686.     // Draw a background area in the gap between the 2 scrollbars
  687.     if(pListData->m_bScrollBarVisible_Horiz == TRUE && pListData->m_bScrollBarVisible_Vert == TRUE)
  688.     {
  689.         RECT rScrollbarGap;
  690.         rScrollbarGap = pListData->m_rClient;
  691.         rScrollbarGap.top = pListData->m_rScrollbar_Vert.bottom;
  692.         rScrollbarGap.left = pListData->m_rScrollbar_Horiz.right;
  693.         CLV_DrawBackgroundRect(pListData, &drawcontext, &rScrollbarGap);
  694.     }
  695.     // Draw the list
  696.     // - clip to list rect
  697.     {
  698.         RECT rList = pListData->m_rList;
  699.         OffsetRect(&rList, drawcontext.m_ptOffset.x, drawcontext.m_ptOffset.y);
  700.         IntersectClipRect(drawcontext.m_dcDraw, rList.left, rList.top, rList.right, rList.bottom);
  701.     }
  702.     // - draw background
  703.     CPIG_TiledFill(&drawcontext, &pListData->m_rList, &glb_pSkin->mpl_rListBackground_SourceTile, glb_pSkin->mpl_pListBackground, CIC_TILEDFILOPTIONS_NONE);
  704.     // Draw the selection
  705.     {
  706.         int iItemIDX;
  707.         int iFirstVisibleItem;
  708.         int iLastVisibleItem;
  709.         BOOL bInSelection;
  710.         RECT rSelection;
  711.         // Setup bounds
  712.         iFirstVisibleItem = pListData->m_iFirstVisibleItem - 1;
  713.         iLastVisibleItem = iFirstVisibleItem + ((pListData->m_rList.bottom-pListData->m_rList.top)/pListData->m_iItemHeight) + 1;
  714.         if(iFirstVisibleItem < 0)
  715.             iFirstVisibleItem = 0;
  716.         if(iLastVisibleItem >= pListData->m_iNumItems)
  717.             iLastVisibleItem = pListData->m_iNumItems-1;
  718.         // Part initialise selection rect
  719.         rSelection.left = -pListData->m_iXOrigin + glb_pSkin->mpl_rListBackground_SourceTile.left;
  720.         rSelection.right = (pListData->m_iXScrollExtent - pListData->m_iXOrigin) - (glb_pSkin->mpl_pListBackground->m_szSize.cx - glb_pSkin->mpl_rListBackground_SourceTile.right);
  721.         // Draw selection (grouping adjacent selected items)
  722.         bInSelection = FALSE;
  723.         for(iItemIDX = iFirstVisibleItem; iItemIDX <= iLastVisibleItem; iItemIDX++)
  724.         {
  725.             // Is this item selected?
  726.             if(pListData->m_pItems[iItemIDX].m_dwFlags & CPLV_ITEMFLAG_SELECTED)
  727.             {
  728.                 // Open a new selection (if we are not in one already)
  729.                 if(bInSelection == FALSE)
  730.                 {
  731.                     bInSelection = TRUE;
  732.                     rSelection.top = (iItemIDX + 1 - pListData->m_iFirstVisibleItem) * pListData->m_iItemHeight;
  733.                 }
  734.             }
  735.             else
  736.             {
  737.                 // Are we in a selection (if so complete it)
  738.                 if(bInSelection)
  739.                 {
  740.                     bInSelection = FALSE;
  741.                     rSelection.bottom = (iItemIDX + 1 - pListData->m_iFirstVisibleItem) * pListData->m_iItemHeight;
  742.                     CPIG_TiledFill(&drawcontext, &rSelection, &glb_pSkin->mpl_rSelection_Tile, glb_pSkin->mpl_pSelection, CIC_TILEDFILOPTIONS_NONE);
  743.                 }
  744.             }
  745.         }
  746.         // Close any open groups
  747.         if(bInSelection)
  748.         {
  749.             bInSelection = FALSE;
  750.             rSelection.bottom = ((iLastVisibleItem+2) - pListData->m_iFirstVisibleItem) * pListData->m_iItemHeight;
  751.             CPIG_TiledFill(&drawcontext, &rSelection, &glb_pSkin->mpl_rSelection_Tile, glb_pSkin->mpl_pSelection, CIC_TILEDFILOPTIONS_NONE);
  752.         }
  753.         // Draw focus item
  754.         if(pListData->m_iFocusItem != CPC_INVALIDITEM
  755.                 && pListData->m_iFocusItem >= iFirstVisibleItem && pListData->m_iFocusItem <= iLastVisibleItem
  756.                 && pListData->m_bHasFocus == TRUE)
  757.         {
  758.             RECT rFocus;
  759.             rFocus = rSelection;
  760.             rFocus.top = (pListData->m_iFocusItem + 1 - pListData->m_iFirstVisibleItem) * pListData->m_iItemHeight;
  761.             rFocus.bottom = (pListData->m_iFocusItem + 2 - pListData->m_iFirstVisibleItem) * pListData->m_iItemHeight;
  762.             CPIG_TiledFill(&drawcontext, &rFocus, &glb_pSkin->mpl_rFocus_Tile, glb_pSkin->mpl_pFocus, CIC_TILEDFILOPTIONS_NOCENTRE);
  763.         }
  764.     }
  765.     // Draw the items
  766.     {
  767.         int iCursorX;
  768.         unsigned int _iColIDX;
  769.         int iFirstVisibleItem;
  770.         int iLastVisibleItem;
  771.         RECT rItem;
  772.         iFirstVisibleItem = pListData->m_iFirstVisibleItem;
  773.         iLastVisibleItem = iFirstVisibleItem + ((pListData->m_rList.bottom-pListData->m_rList.top)/pListData->m_iItemHeight);
  774.         if(iLastVisibleItem >= pListData->m_iNumItems)
  775.             iLastVisibleItem = pListData->m_iNumItems-1;
  776.         // Draw items column by column
  777.         iCursorX = -pListData->m_iXOrigin;
  778.         SetBkMode(drawcontext.m_dcDraw, TRANSPARENT);
  779.         for(_iColIDX = 0; _iColIDX < pListData->m_iNumColumns; _iColIDX++)
  780.         {
  781.             int iColumnIDX = pListData->m_piColumnOrder[_iColIDX];
  782.             int iItemIDX;
  783.             int iNextCursorX;
  784.             if(pListData->m_pColumns[iColumnIDX].m_dwFlags & CPLV_COLFLAG_HIDDEN)
  785.                 continue;
  786.             rItem.left = iCursorX;
  787.             rItem.right = iCursorX + pListData->m_pColumns[iColumnIDX].m_iColumnWidth;
  788.             if(rItem.right > (pListData->m_rHeader.right - CPC_HEADERCOLLAPSETHRESHOLD))
  789.                 rItem.right = pListData->m_rHeader.right;
  790.             iNextCursorX = rItem.right;
  791.             // Line text up with header
  792.             rItem.left += glb_pSkin->mpl_rListHeader_SourceTile.left;
  793.             rItem.right -= glb_pSkin->mpl_pListHeader_Down->m_szSize.cx - glb_pSkin->mpl_rListHeader_SourceTile.right;
  794.             // Draw item text (if there is any and this column is inside the clip box)
  795.             if(pListData->m_pColumns[iColumnIDX].m_pfnTextAccessor
  796.                     && rItem.right > drawcontext.m_rClip.left
  797.                     && rItem.left < drawcontext.m_rClip.right)
  798.             {
  799.                 int iCursorY = pListData->m_rList.top;
  800.                 for(iItemIDX = iFirstVisibleItem; iItemIDX <= iLastVisibleItem; iItemIDX++)
  801.                 {
  802.                     // Set the text colour depending on whether we are selected
  803.                     const char* pcText = pListData->m_pColumns[iColumnIDX].m_pfnTextAccessor(pListData->m_pItems[iItemIDX].m_pItemData);
  804.                     if(pcText)
  805.                     {
  806.                         if(pListData->m_pItems[iItemIDX].m_dwFlags & CPLV_ITEMFLAG_SELECTED)
  807.                             SetTextColor(drawcontext.m_dcDraw, glb_pSkin->mpl_ListTextColour_Selected);
  808.                         else
  809.                         {
  810.                             if(pListData->m_pColumns[iColumnIDX].m_pfnGetCustomDrawColour)
  811.                             {
  812.                                 CPe_CustomDrawColour enDrawColour;
  813.                                 enDrawColour = pListData->m_pColumns[iColumnIDX].m_pfnGetCustomDrawColour(pListData->m_pItems[iItemIDX].m_pItemData);
  814.                                 if(enDrawColour == cdcNormal)
  815.                                     SetTextColor(drawcontext.m_dcDraw, glb_pSkin->mpl_ListTextColour);
  816.                                 else if(enDrawColour == cdcLowlighted)
  817.                                     SetTextColor(drawcontext.m_dcDraw, RGB(0,0,0));
  818.                                 else
  819.                                     SetTextColor(drawcontext.m_dcDraw, RGB(255,255,255));
  820.                             }
  821.                             else
  822.                                 SetTextColor(drawcontext.m_dcDraw, glb_pSkin->mpl_ListTextColour);
  823.                         }
  824.                         // Draw item's text
  825.                         rItem.top = iCursorY;
  826.                         rItem.bottom = iCursorY + pListData->m_iItemHeight;
  827.                         CLV_DrawText(&drawcontext, pcText, &rItem, pListData->m_pColumns[iColumnIDX].m_enAlign);
  828.                     }
  829.                     iCursorY += pListData->m_iItemHeight;
  830.                 }
  831.             }
  832.             iCursorX = iNextCursorX;
  833.             if(iCursorX == pListData->m_rList.right)
  834.                 break;
  835.         }
  836.     }
  837.     // Cleanup
  838.     SelectObject(drawcontext.m_dcDraw, hfOld);
  839.     if(hbmSurface)
  840.     {
  841.         BitBlt(dcPaint,
  842.                drawcontext.m_rClip.left, drawcontext.m_rClip.top,
  843.                drawcontext.m_rClip.right-drawcontext.m_rClip.left,
  844.                drawcontext.m_rClip.bottom-drawcontext.m_rClip.top,
  845.                drawcontext.m_dcDraw, 0, 0, SRCCOPY);
  846.         SelectObject(drawcontext.m_dcDraw, hbmSurface_Old);
  847.         DeleteDC(drawcontext.m_dcDraw);
  848.         DeleteObject(hbmSurface);
  849.     }
  850.     EndPaint(pListData->m_hWnd, &ps);
  851. }
  852. //
  853. //
  854. //
  855. void CLV_CalcItemHeight(CIs_ListViewData* pListData)
  856. {
  857.     HDC dcWindow;
  858.     SIZE szText;
  859.     HFONT hfOld;
  860.     dcWindow = GetDC(pListData->m_hWnd);
  861.     hfOld = (HFONT)SelectObject(dcWindow, glb_pSkin->mpl_hfFont);
  862.     GetTextExtentPoint(dcWindow, "Xy", 2, &szText);
  863.     pListData->m_iItemHeight = szText.cy;
  864.     SelectObject(dcWindow, hfOld);
  865.     ReleaseDC(pListData->m_hWnd, dcWindow);
  866. }
  867. //
  868. //
  869. //
  870. int CLV_HitTest_Header_X(CIs_ListViewData* pListData, const int iTestX)
  871. {
  872.     // We are in the header - which one?
  873.     int iCursorX;
  874.     int iLastCursorX;
  875.     unsigned int _iColIDX;
  876.     iCursorX = -pListData->m_iXOrigin;
  877.     iLastCursorX = 0;
  878.     for(_iColIDX = 0; _iColIDX < pListData->m_iNumColumns; _iColIDX++)
  879.     {
  880.         unsigned int iColumnIDX = pListData->m_piColumnOrder[_iColIDX];
  881.         if(pListData->m_pColumns[iColumnIDX].m_dwFlags & CPLV_COLFLAG_HIDDEN)
  882.             continue;
  883.         iLastCursorX = iCursorX;
  884.         iCursorX += pListData->m_pColumns[iColumnIDX].m_iColumnWidth;
  885.         if(iCursorX > (pListData->m_rHeader.right - CPC_HEADERCOLLAPSETHRESHOLD))
  886.             iCursorX = pListData->m_rHeader.right;
  887.         if(iTestX >= iLastCursorX && iTestX < iCursorX)
  888.             return iColumnIDX;
  889.     }
  890.     return CPC_INVALIDCOLUMN;
  891. }
  892. //
  893. //
  894. //
  895. int CLV_HitTest_HeaderSizer_X(CIs_ListViewData* pListData, const int iTestX)
  896. {
  897.     // We are in the header - are we near a drag boundry?
  898.     int iCursorX;
  899.     unsigned int _iColIDX;
  900.     iCursorX = -pListData->m_iXOrigin;
  901.     for(_iColIDX = 0; _iColIDX < pListData->m_iNumColumns; _iColIDX++)
  902.     {
  903.         unsigned int iColumnIDX = pListData->m_piColumnOrder[_iColIDX];
  904.         int iColSizer;
  905.         if(pListData->m_pColumns[iColumnIDX].m_dwFlags & CPLV_COLFLAG_HIDDEN)
  906.             continue;
  907.         iColSizer = iCursorX + pListData->m_pColumns[iColumnIDX].m_iColumnWidth;
  908.         if(iColSizer > (pListData->m_rHeader.right - CPC_HEADERCOLLAPSETHRESHOLD))
  909.             iColSizer = pListData->m_rHeader.right - CPC_HEADERCOLLAPSETHRESHOLD;
  910.         if(iTestX >= (iColSizer-CPC_HEADERDRAG_HTWIDTH) && iTestX <= (iColSizer+CPC_HEADERDRAG_HTWIDTH))
  911.         {
  912.             // If we cannot resize - return an invalid column
  913.             if(pListData->m_pColumns[iColumnIDX].m_dwFlags & CPLV_COLFLAG_LOCKRESIZE)
  914.                 return CPC_INVALIDCOLUMN;
  915.             return iColumnIDX;
  916.         }
  917.         iCursorX = iColSizer;
  918.     }
  919.     return CPC_INVALIDCOLUMN;
  920. }
  921. //
  922. //
  923. //
  924. void CLV_Handle_WM_MOUSEMOVE(CIs_ListViewData* pListData, const POINTS _ptCursor)
  925. {
  926.     POINT ptCursor;
  927.     // Init
  928.     ptCursor.x = _ptCursor.x;
  929.     ptCursor.y = _ptCursor.y;
  930.     if(pListData->m_enWindowMode == wmQuiescent)
  931.     {
  932.         // Set cursor according to where it is
  933.         if(PtInRect(&pListData->m_rHeader, ptCursor) == TRUE)
  934.         {
  935.             if(CLV_HitTest_HeaderSizer_X(pListData, ptCursor.x) == CPC_INVALIDCOLUMN)
  936.                 SetCursor(LoadCursor(NULL, IDC_ARROW)); // In a header
  937.             else
  938.                 SetCursor(LoadCursor(NULL, IDC_SIZEWE)); // Sizing
  939.         }
  940.         else
  941.             SetCursor(LoadCursor(NULL, IDC_ARROW));
  942.     }
  943.     else if(pListData->m_enWindowMode == wmHeader_Click)
  944.     {
  945.         // If the mouse has moved beyond our drag threshold then enter into header_orderchange mode
  946.         if(abs(ptCursor.x - pListData->m_ptMouseDown.x) > CPC_HEADERDRAGDISTANCE
  947.                 || abs(ptCursor.y - pListData->m_ptMouseDown.y) > CPC_HEADERDRAGDISTANCE)
  948.         {
  949.             pListData->m_enWindowMode = wmHeader_ChangeOrder;
  950.             InvalidateRect(pListData->m_hWnd, &pListData->m_rHeader, FALSE);
  951.         }
  952.     }
  953.     else if(pListData->m_enWindowMode == wmHeader_ChangeWidth)
  954.     {
  955.         // Update the new column width
  956.         // - get the start of this colunm
  957.         unsigned int _iColIDX;
  958.         int iCursorX;
  959.         RECT rInvalid;
  960.         DWORD dwNewFlags;
  961.         int iNewWidth;
  962.         // Find the start point of the active column
  963.         iCursorX = -pListData->m_iXOrigin;
  964.         for(_iColIDX = 0; _iColIDX < pListData->m_iNumColumns; _iColIDX++)
  965.         {
  966.             unsigned int iColumnIDX = pListData->m_piColumnOrder[_iColIDX];
  967.             if((int)iColumnIDX == pListData->m_iActiveHeaderCol)
  968.                 break;
  969.             if(pListData->m_pColumns[iColumnIDX].m_dwFlags & CPLV_COLFLAG_HIDDEN)
  970.                 continue;
  971.             iCursorX += pListData->m_pColumns[iColumnIDX].m_iColumnWidth;
  972.         }
  973.         // Invalidate only the area that needs it
  974.         rInvalid.top = 0;
  975.         rInvalid.bottom = pListData->m_rClient.bottom;
  976.         rInvalid.right = pListData->m_rClient.right;
  977.         rInvalid.left = iCursorX;
  978.         // Perform resize
  979.         dwNewFlags = pListData->m_pColumns[pListData->m_iActiveHeaderCol].m_dwFlags;
  980.         iNewWidth = pListData->m_pColumns[pListData->m_iActiveHeaderCol].m_iColumnWidth;
  981.         if(ptCursor.x > (iCursorX + CPC_HEADERDRAG_HTWIDTH) )
  982.         {
  983.             dwNewFlags &= (~CPLV_COLFLAG_HIDDEN);
  984.             iNewWidth = ptCursor.x - iCursorX;
  985.         }
  986.         else
  987.         {
  988.             if(dwNewFlags & CPLV_COLFLAG_NOHIDE)
  989.             {
  990.                 iNewWidth = CPC_HEADERDRAG_HTWIDTH;
  991.             }
  992.             else
  993.             {
  994.                 dwNewFlags |= CPLV_COLFLAG_HIDDEN;
  995.                 iNewWidth = CPC_HEADERDRAG_DEFAULTWIDTH;
  996.             }
  997.         }
  998.         // Update dispaly only if the width (or visible state) has changed
  999.         if(iNewWidth != pListData->m_pColumns[pListData->m_iActiveHeaderCol].m_iColumnWidth
  1000.                 || dwNewFlags != pListData->m_pColumns[pListData->m_iActiveHeaderCol].m_dwFlags)
  1001.         {
  1002.             pListData->m_pColumns[pListData->m_iActiveHeaderCol].m_dwFlags = dwNewFlags;
  1003.             pListData->m_pColumns[pListData->m_iActiveHeaderCol].m_iColumnWidth = iNewWidth;
  1004.             InvalidateRect(pListData->m_hWnd, &rInvalid, FALSE);
  1005.             CLV_UpdateScrollBars(pListData);
  1006.         }
  1007.     }
  1008.     else if(pListData->m_enWindowMode == wmHeader_ChangeOrder)
  1009.     {
  1010.         // Search for our drag rect - keep track of the threshold of the prev and next colunms
  1011.         unsigned int _iColIDX;
  1012.         int iXColStart_Prev = 0;
  1013.         int iXColEnd_Prev = 0;
  1014.         int iXColStart_Next = 0;
  1015.         int iXColEnd_Next = 0;
  1016.         int iPrevColIDX = CPC_INVALIDCOLUMN;
  1017.         int iNextColIDX = CPC_INVALIDCOLUMN;
  1018.         unsigned int iCurrentOrderIDX;
  1019.         int iActiveColumnWidth;
  1020.         int iCursorX;
  1021.         BOOL bFoundColumn;
  1022.         // Find prev and next colunms
  1023.         bFoundColumn = FALSE;
  1024.         iCursorX = -pListData->m_iXOrigin;
  1025.         iCurrentOrderIDX = 0;
  1026.         iActiveColumnWidth = 0;
  1027.         for(_iColIDX = 0; _iColIDX < pListData->m_iNumColumns; _iColIDX++)
  1028.         {
  1029.             int iColumnIDX = pListData->m_piColumnOrder[_iColIDX];
  1030.             if(pListData->m_pColumns[iColumnIDX].m_dwFlags & CPLV_COLFLAG_HIDDEN)
  1031.                 continue;
  1032.             // Update prev and next
  1033.             if(bFoundColumn == TRUE && iNextColIDX == CPC_INVALIDCOLUMN)
  1034.             {
  1035.                 iXColStart_Next = iCursorX;
  1036.                 iXColEnd_Next = iCursorX + pListData->m_pColumns[iColumnIDX].m_iColumnWidth;
  1037.                 iNextColIDX = iColumnIDX;
  1038.             }
  1039.             if(iColumnIDX == pListData->m_iActiveHeaderCol)
  1040.             {
  1041.                 iCurrentOrderIDX = _iColIDX;
  1042.                 iActiveColumnWidth = pListData->m_pColumns[iColumnIDX].m_iColumnWidth;
  1043.                 bFoundColumn = TRUE;
  1044.             }
  1045.             else if(bFoundColumn == FALSE)
  1046.             {
  1047.                 iPrevColIDX = iColumnIDX;
  1048.                 iXColStart_Prev = iCursorX;
  1049.                 iXColEnd_Prev = iCursorX + pListData->m_pColumns[iColumnIDX].m_iColumnWidth;
  1050.             }
  1051.             iCursorX += pListData->m_pColumns[iColumnIDX].m_iColumnWidth;
  1052.         }
  1053.         // If we have dragged our mouse beyond the prev threshold - swap the order of that with the current col
  1054.         // - Only swap if the mouse will remain on top of the active column
  1055.         if(iPrevColIDX != CPC_INVALIDCOLUMN
  1056.                 && ptCursor.x < iXColEnd_Prev
  1057.                 && ptCursor.x < (iXColStart_Prev+iActiveColumnWidth))
  1058.         {
  1059.             int iTemp;
  1060.             RECT rInvalid;
  1061.             iTemp = pListData->m_piColumnOrder[iCurrentOrderIDX];
  1062.             pListData->m_piColumnOrder[iCurrentOrderIDX] = pListData->m_piColumnOrder[iCurrentOrderIDX-1];
  1063.             pListData->m_piColumnOrder[iCurrentOrderIDX-1] = iTemp;
  1064.             // Invalidate only the area that needs it
  1065.             rInvalid.top = 0;
  1066.             rInvalid.bottom = pListData->m_rClient.bottom;
  1067.             rInvalid.left = iXColStart_Prev;
  1068.             rInvalid.right = iXColEnd_Prev + iActiveColumnWidth;
  1069.             InvalidateRect(pListData->m_hWnd, &rInvalid, FALSE);
  1070.             // Send notify
  1071.             if(pListData->m_hndlr_HeaderChanged)
  1072.                 pListData->m_hndlr_HeaderChanged(pListData);
  1073.         }
  1074.         // If we have dragged our mouse beyond the next threshold - swap the order of that with the current col
  1075.         // - Only swap if the mouse will remain on top of the active column
  1076.         else if(iNextColIDX != CPC_INVALIDCOLUMN
  1077.                 && ptCursor.x > iXColStart_Next
  1078.                 && ptCursor.x > (iXColEnd_Next-iActiveColumnWidth))
  1079.         {
  1080.             int iTemp;
  1081.             RECT rInvalid;
  1082.             iTemp = pListData->m_piColumnOrder[iCurrentOrderIDX];
  1083.             pListData->m_piColumnOrder[iCurrentOrderIDX] = pListData->m_piColumnOrder[iCurrentOrderIDX+1];
  1084.             pListData->m_piColumnOrder[iCurrentOrderIDX+1] = iTemp;
  1085.             // Invalidate only the area that needs it
  1086.             rInvalid.top = 0;
  1087.             rInvalid.bottom = pListData->m_rClient.bottom;
  1088.             rInvalid.left = iXColStart_Next - iActiveColumnWidth;
  1089.             rInvalid.right = iXColEnd_Next;
  1090.             InvalidateRect(pListData->m_hWnd, &rInvalid, FALSE);
  1091.             // Send notify
  1092.             if(pListData->m_hndlr_HeaderChanged)
  1093.                 pListData->m_hndlr_HeaderChanged(pListData);
  1094.         }
  1095.     }
  1096.     else if(pListData->m_enWindowMode == wmHScrollbar_DragThumb)
  1097.     {
  1098.         if(pListData->m_ptMouseDown.x != ptCursor.x)
  1099.         {
  1100.             const int iTrackWidth = (pListData->m_rScrollbar_Horiz.right - pListData->m_rScrollbar_Horiz.left)
  1101.                                     - (glb_pSkin->mpl_pHScrollBar_Left->m_pImage->m_szSize.cx
  1102.                                        + glb_pSkin->mpl_pHScrollBar_Right->m_pImage->m_szSize.cx);
  1103.             const int iTrackThumbWidth = pListData->m_rScrollbar_Horiz_Thumb.right - pListData->m_rScrollbar_Horiz_Thumb.left;
  1104.             const int iListRectWidth = pListData->m_rList.right - pListData->m_rList.left;
  1105.             int iNewTrackPos;
  1106.             int iNewXOrigin;
  1107.             // Work out new origin based on the track drag distance
  1108.             iNewTrackPos = ptCursor.x - pListData->m_ptMouseDown_OnHitItem.x - glb_pSkin->mpl_pHScrollBar_Left->m_pImage->m_szSize.cx;
  1109.             iNewXOrigin = (int)( (  ((float)iNewTrackPos * (float)(pListData->m_iXScrollExtent - iListRectWidth)) / (float)(iTrackWidth-iTrackThumbWidth)));
  1110.             if(iNewXOrigin < 0)
  1111.                 iNewXOrigin = 0;
  1112.             else if(iNewXOrigin > (pListData->m_iXScrollExtent-iListRectWidth))
  1113.                 iNewXOrigin = pListData->m_iXScrollExtent-iListRectWidth;
  1114.             // Update only if the origin has changed
  1115.             if(iNewXOrigin != pListData->m_iXOrigin)
  1116.             {
  1117.                 pListData->m_iXOrigin = iNewXOrigin;
  1118.                 CLV_UpdateScrollBars(pListData);
  1119.                 CLV_InvalidateWindow(pListData);
  1120.             }
  1121.         }
  1122.     }
  1123.     else if(pListData->m_enWindowMode == wmHScrollbar_Scroll_Left)
  1124.     {
  1125.         // We will draw the button up if the mouse isn't over it
  1126.         BOOL bNewButtonState;
  1127.         if(PtInRect(&pListData->m_rScrollbar_Horiz, ptCursor) == TRUE
  1128.                 && ptCursor.x < (pListData->m_rScrollbar_Horiz.left + glb_pSkin->mpl_pHScrollBar_Left->m_pImage->m_szSize.cx) )
  1129.         {
  1130.             bNewButtonState = TRUE;
  1131.         }
  1132.         else
  1133.             bNewButtonState = FALSE;
  1134.         // State changed?
  1135.         if(bNewButtonState != pListData->m_bMouseOverScrollbutton)
  1136.         {
  1137.             pListData->m_bMouseOverScrollbutton = bNewButtonState;
  1138.             InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Horiz, FALSE);
  1139.         }
  1140.     }
  1141.     else if(pListData->m_enWindowMode == wmHScrollbar_Scroll_Right)
  1142.     {
  1143.         // We draw the button up if the mouse isn't over it
  1144.         BOOL bNewButtonState;
  1145.         if(PtInRect(&pListData->m_rScrollbar_Horiz, ptCursor) == TRUE
  1146.                 && ptCursor.x > (pListData->m_rScrollbar_Horiz.right - glb_pSkin->mpl_pHScrollBar_Right->m_pImage->m_szSize.cx) )
  1147.         {
  1148.             bNewButtonState = TRUE;
  1149.         }
  1150.         else
  1151.             bNewButtonState = FALSE;
  1152.         // State changed?
  1153.         if(bNewButtonState != pListData->m_bMouseOverScrollbutton)
  1154.         {
  1155.             pListData->m_bMouseOverScrollbutton = bNewButtonState;
  1156.             InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Horiz, FALSE);
  1157.         }
  1158.     }
  1159.     else if(pListData->m_enWindowMode == wmVScrollbar_DragThumb)
  1160.     {
  1161.         if(pListData->m_ptMouseDown.y != ptCursor.y)
  1162.         {
  1163.             const int iListRectHeight = pListData->m_rList.bottom - pListData->m_rList.top;
  1164.             const int iTrackHeight = iListRectHeight - (glb_pSkin->mpl_pVScrollBar_Up->m_iStateHeight
  1165.                                      + glb_pSkin->mpl_pVScrollBar_Down->m_iStateHeight);
  1166.             const int iTrackThumbHeight = pListData->m_rScrollbar_Vert_Thumb.bottom - pListData->m_rScrollbar_Vert_Thumb.top;
  1167.             const int iListRectHeight_Lines = CLV_GetListRect_Lines(pListData);
  1168.             int iNewTrackPos;
  1169.             int iNewFirstVisibleItem;
  1170.             // Work out new origin based on the track drag distance
  1171.             iNewTrackPos = ptCursor.y - pListData->m_ptMouseDown_OnHitItem.y - (pListData->m_rScrollbar_Vert.top+glb_pSkin->mpl_pVScrollBar_Up->m_iStateHeight);
  1172.             iNewFirstVisibleItem = (int)( ( ((float)iNewTrackPos * (float)(pListData->m_iNumItems - iListRectHeight_Lines)) / (float)(iTrackHeight-iTrackThumbHeight)) );
  1173.             if(iNewFirstVisibleItem < 0)
  1174.                 iNewFirstVisibleItem = 0;
  1175.             if( (iNewFirstVisibleItem + iListRectHeight_Lines) > pListData->m_iNumItems)
  1176.                 iNewFirstVisibleItem = pListData->m_iNumItems - iListRectHeight_Lines;
  1177.             // Update only if the origin has changed
  1178.             if(iNewFirstVisibleItem != pListData->m_iFirstVisibleItem)
  1179.             {
  1180.                 pListData->m_iFirstVisibleItem = iNewFirstVisibleItem;
  1181.                 CLV_UpdateScrollBars(pListData);
  1182.                 CLV_InvalidateWindow(pListData);
  1183.             }
  1184.         }
  1185.     }
  1186.     else if(pListData->m_enWindowMode == wmVScrollbar_Scroll_Up)
  1187.     {
  1188.         // We will draw the button up if the mouse isn't over it
  1189.         BOOL bNewButtonState;
  1190.         if(PtInRect(&pListData->m_rScrollbar_Vert, ptCursor) == TRUE
  1191.                 && ptCursor.y < (pListData->m_rScrollbar_Vert.top + glb_pSkin->mpl_pVScrollBar_Up->m_iStateHeight) )
  1192.         {
  1193.             bNewButtonState = TRUE;
  1194.         }
  1195.         else
  1196.             bNewButtonState = FALSE;
  1197.         // State changed?
  1198.         if(bNewButtonState != pListData->m_bMouseOverScrollbutton)
  1199.         {
  1200.             pListData->m_bMouseOverScrollbutton = bNewButtonState;
  1201.             InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Vert, FALSE);
  1202.         }
  1203.     }
  1204.     else if(pListData->m_enWindowMode == wmVScrollbar_Scroll_Down)
  1205.     {
  1206.         // We draw the button up if the mouse isn't over it
  1207.         BOOL bNewButtonState;
  1208.         if(PtInRect(&pListData->m_rScrollbar_Vert, ptCursor) == TRUE
  1209.                 && ptCursor.y > (pListData->m_rScrollbar_Vert.bottom - glb_pSkin->mpl_pVScrollBar_Down->m_iStateHeight) )
  1210.         {
  1211.             bNewButtonState = TRUE;
  1212.         }
  1213.         else
  1214.             bNewButtonState = FALSE;
  1215.         // State changed?
  1216.         if(bNewButtonState != pListData->m_bMouseOverScrollbutton)
  1217.         {
  1218.             pListData->m_bMouseOverScrollbutton = bNewButtonState;
  1219.             InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Vert, FALSE);
  1220.         }
  1221.     }
  1222.     else if(pListData->m_enWindowMode == wmList_Click)
  1223.     {
  1224.         // If the mouse has moved beyond our drag threshold then enter into header_orderchange mode
  1225.         if( pListData->m_iFocusItem != CPC_INVALIDITEM
  1226.                 && ((abs(ptCursor.x - pListData->m_ptMouseDown.x) > CPC_LISTDRAGDISTANCE
  1227.                      || abs(ptCursor.y - pListData->m_ptMouseDown.y) > CPC_LISTDRAGDISTANCE) )
  1228.                 && (pListData->m_pItems[pListData->m_iFocusItem].m_dwFlags & CPLV_ITEMFLAG_SELECTED))
  1229.         {
  1230.             pListData->m_enWindowMode = wmList_Drag;
  1231.             if(pListData->m_hndlr_ItemDrag && pListData->m_iFocusItem != CPC_INVALIDITEM)
  1232.                 pListData->m_hndlr_ItemDrag(pListData, pListData->m_iFocusItem, pListData->m_pItems[pListData->m_iFocusItem].m_pItemData);
  1233.         }
  1234.     }
  1235.     else
  1236.         SetCursor(LoadCursor(NULL, IDC_ARROW));
  1237. }
  1238. //
  1239. //
  1240. //
  1241. void CLV_StartAutoRepeat(CIs_ListViewData* pListData)
  1242. {
  1243.     DWORD dwRepeatDelay;
  1244.     SystemParametersInfo(SPI_GETKEYBOARDDELAY, 0, &dwRepeatDelay, 0L);
  1245.     dwRepeatDelay = 250 + (dwRepeatDelay*250);
  1246.     pListData->m_bAutoRepeatFirst = TRUE;
  1247.     pListData->m_uiAutorepeatTimer = SetTimer(pListData->m_hWnd, CPC_TIMERID_AUTOREPEAT, dwRepeatDelay, NULL);
  1248. }
  1249. //
  1250. //
  1251. //
  1252. void CLV_Handle_WM_TIMER_AUTOREPEAT(CIs_ListViewData* pListData)
  1253. {
  1254.     POINT ptCursor;
  1255.     GetCursorPos(&ptCursor);
  1256.     ScreenToClient(pListData->m_hWnd, &ptCursor);
  1257.     // Are we autorepeating in the horizontal scrollbar?
  1258.     if(PtInRect(&pListData->m_rScrollbar_Horiz, ptCursor) == TRUE)
  1259.     {
  1260.         // In left button
  1261.         if(ptCursor.x < (pListData->m_rScrollbar_Horiz.left + glb_pSkin->mpl_pHScrollBar_Left->m_pImage->m_szSize.cx)
  1262.                 && pListData->m_enWindowMode == wmHScrollbar_Scroll_Left)
  1263.         {
  1264.             CLV_Scroll_Horiz(pListData, -CPC_SCROLLBAR_HORIZ_LINESIZE);
  1265.         }
  1266.         // In right button
  1267.         else if(ptCursor.x > (pListData->m_rScrollbar_Horiz.right - glb_pSkin->mpl_pHScrollBar_Right->m_pImage->m_szSize.cx)
  1268.                 && pListData->m_enWindowMode == wmHScrollbar_Scroll_Right)
  1269.         {
  1270.             CLV_Scroll_Horiz(pListData, CPC_SCROLLBAR_HORIZ_LINESIZE);
  1271.         }
  1272.         // In left page area
  1273.         else if(ptCursor.x < pListData->m_rScrollbar_Horiz_Thumb.left
  1274.                 && pListData->m_enWindowMode == wmHScrollbar_Page_Left)
  1275.         {
  1276.             CLV_Scroll_Horiz(pListData, -CPC_SCROLLBAR_HORIZ_PAGESIZE);
  1277.         }
  1278.         // In right page area
  1279.         else if(ptCursor.x > pListData->m_rScrollbar_Horiz_Thumb.right
  1280.                 && pListData->m_enWindowMode == wmHScrollbar_Page_Right)
  1281.         {
  1282.             CLV_Scroll_Horiz(pListData, CPC_SCROLLBAR_HORIZ_PAGESIZE);
  1283.         }
  1284.     }
  1285.     // Are we autorepeating in the vertical scrollbar?
  1286.     else if(PtInRect(&pListData->m_rScrollbar_Vert, ptCursor) == TRUE)
  1287.     {
  1288.         if(ptCursor.y < (pListData->m_rScrollbar_Vert.top + glb_pSkin->mpl_pVScrollBar_Up->m_iStateHeight)
  1289.                 && pListData->m_enWindowMode == wmVScrollbar_Scroll_Up)
  1290.         {
  1291.             CLV_Scroll_Vert(pListData, -1);
  1292.         }
  1293.         // In down button
  1294.         else if(ptCursor.y > (pListData->m_rScrollbar_Vert.bottom - glb_pSkin->mpl_pVScrollBar_Down->m_iStateHeight)
  1295.                 && pListData->m_enWindowMode == wmVScrollbar_Scroll_Down)
  1296.         {
  1297.             CLV_Scroll_Vert(pListData, 1);
  1298.         }
  1299.         // In top page area
  1300.         else if(ptCursor.y < pListData->m_rScrollbar_Vert_Thumb.top
  1301.                 && pListData->m_enWindowMode == wmVScrollbar_Page_Up)
  1302.         {
  1303.             CLV_Scroll_Vert(pListData, -pListData->m_iNumItemsOnPage);
  1304.         }
  1305.         // In bottom page area
  1306.         else if(ptCursor.y > pListData->m_rScrollbar_Vert_Thumb.bottom
  1307.                 && pListData->m_enWindowMode == wmVScrollbar_Page_Down)
  1308.         {
  1309.             CLV_Scroll_Vert(pListData, pListData->m_iNumItemsOnPage);
  1310.         }
  1311.     }
  1312.     // Do we need to update the timer (after the first autorepeat delay)
  1313.     if(pListData->m_bAutoRepeatFirst)
  1314.     {
  1315.         int iRepeatDelay;
  1316.         // Make the autorepeat the same as the keyboard autorepeat
  1317.         pListData->m_bAutoRepeatFirst = FALSE;
  1318.         SystemParametersInfo(SPI_GETKEYBOARDSPEED, 0, &iRepeatDelay, 0L);
  1319.         iRepeatDelay = 400 - (iRepeatDelay*12);
  1320.         if(iRepeatDelay < 0)
  1321.             iRepeatDelay = 10;
  1322.         pListData->m_uiAutorepeatTimer = SetTimer(pListData->m_hWnd, CPC_TIMERID_AUTOREPEAT, iRepeatDelay, NULL);
  1323.     }
  1324. }
  1325. //
  1326. //
  1327. //
  1328. void CLV_InvalidateItem(CP_HLISTVIEW _hListData, const int iItemIDX)
  1329. {
  1330.     CIs_ListViewData* pListData;
  1331.     RECT rItemRect;
  1332.     pListData = (CIs_ListViewData*)_hListData;
  1333.     CP_CHECKOBJECT(pListData);
  1334.     if(iItemIDX == CPC_INVALIDITEM)
  1335.         return;
  1336.     CP_ASSERT(iItemIDX >= 0);
  1337.     CP_ASSERT(iItemIDX < pListData->m_iNumItems);
  1338.     rItemRect.left = pListData->m_rList.left;
  1339.     rItemRect.right = pListData->m_rList.right;
  1340.     rItemRect.top = pListData->m_rList.top + ( (iItemIDX - pListData->m_iFirstVisibleItem) * pListData->m_iItemHeight);
  1341.     rItemRect.bottom = rItemRect.top + pListData->m_iItemHeight;
  1342.     // Handle the possiblity that the previous/next item may be selected
  1343.     rItemRect.top -= glb_pSkin->mpl_pSelection->m_szSize.cy - glb_pSkin->mpl_rSelection_Tile.bottom;
  1344.     rItemRect.bottom += glb_pSkin->mpl_rSelection_Tile.top;
  1345.     InvalidateRect(pListData->m_hWnd, &rItemRect, FALSE);
  1346. }
  1347. //
  1348. //
  1349. //
  1350. void CLV_ItemSelChange(CIs_ListViewData* pListData, int iItemIDX)
  1351. {
  1352.     if(iItemIDX == CPC_INVALIDITEM)
  1353.         return;
  1354.     CP_ASSERT(iItemIDX >= 0);
  1355.     CP_ASSERT(iItemIDX < pListData->m_iNumItems);
  1356.     if(pListData->m_hndlr_ItemSelected
  1357.             && pListData->m_pItems[iItemIDX].m_dwFlags & CPLV_ITEMFLAG_SELECTED)
  1358.     {
  1359.         pListData->m_hndlr_ItemSelected(pListData, iItemIDX, pListData->m_pItems[iItemIDX].m_pItemData);
  1360.     }
  1361. }
  1362. //
  1363. //
  1364. //
  1365. void CLV_ActionFocusedItem(CIs_ListViewData* pListData)
  1366. {
  1367.     if(pListData->m_iFocusItem == CPC_INVALIDITEM)
  1368.         return;
  1369.     CP_ASSERT(pListData->m_iFocusItem >= 0);
  1370.     CP_ASSERT(pListData->m_iFocusItem < pListData->m_iNumItems);
  1371.     CLV_ClearSelection(pListData);
  1372.     pListData->m_pItems[pListData->m_iFocusItem].m_dwFlags |= CPLV_ITEMFLAG_SELECTED;
  1373.     CLV_ItemSelChange(pListData, pListData->m_iFocusItem);
  1374.     CLV_InvalidateItem(pListData, pListData->m_iFocusItem);
  1375.     pListData->m_iFocusItem = pListData->m_iFocusItem;
  1376.     pListData->m_iKeyboardAnchor = pListData->m_iFocusItem;
  1377.     if(pListData->m_hndlr_ItemAction)
  1378.         pListData->m_hndlr_ItemAction(pListData, pListData->m_iFocusItem, pListData->m_pItems[pListData->m_iFocusItem].m_pItemData);
  1379. }
  1380. //
  1381. //
  1382. //
  1383. void CLV_SelectRange(CIs_ListViewData* pListData, const int _iFirst, const int _iLast)
  1384. {
  1385.     int iStartRange;
  1386.     int iEndRange;
  1387.     int iItemIDX;
  1388.     if(_iFirst > _iLast)
  1389.     {
  1390.         iStartRange = _iLast;
  1391.         iEndRange = _iFirst;
  1392.     }
  1393.     else
  1394.     {
  1395.         iStartRange = _iFirst;
  1396.         iEndRange = _iLast;
  1397.     }
  1398.     CP_ASSERT(iStartRange >= 0);
  1399.     CP_ASSERT(iStartRange < pListData->m_iNumItems);
  1400.     CP_ASSERT(iEndRange >= 0);
  1401.     CP_ASSERT(iEndRange < pListData->m_iNumItems);
  1402.     for(iItemIDX = iStartRange; iItemIDX <= iEndRange; iItemIDX++)
  1403.     {
  1404.         pListData->m_pItems[iItemIDX].m_dwFlags |= CPLV_ITEMFLAG_SELECTED;
  1405.         CLV_ItemSelChange(pListData, iItemIDX);
  1406.         CLV_InvalidateItem(pListData, iItemIDX);
  1407.     }
  1408. }
  1409. //
  1410. //
  1411. //
  1412. void CLV_Handle_WM_LBUTTONDOWN(CIs_ListViewData* pListData, const POINTS _ptCursor, const DWORD dwKeys)
  1413. {
  1414.     POINT ptCursor;
  1415.     // Init
  1416.     ptCursor.x = _ptCursor.x;
  1417.     ptCursor.y = _ptCursor.y;
  1418.     pListData->m_ptMouseDown = ptCursor;
  1419.     pListData->m_dwMouseDown_Keys = dwKeys;
  1420.     // Hit test cursor
  1421.     // - in header?
  1422.     if(PtInRect(&pListData->m_rHeader, ptCursor) == TRUE)
  1423.     {
  1424.         pListData->m_iActiveHeaderCol = CLV_HitTest_HeaderSizer_X(pListData, ptCursor.x);
  1425.         if(pListData->m_iActiveHeaderCol != CPC_INVALIDCOLUMN)
  1426.         {
  1427.             // Resizing header
  1428.             SetCapture(pListData->m_hWnd);
  1429.             pListData->m_enWindowMode = wmHeader_ChangeWidth;
  1430.         }
  1431.         else
  1432.         {
  1433.             pListData->m_iActiveHeaderCol = CLV_HitTest_Header_X(pListData, ptCursor.x);
  1434.             if(pListData->m_iActiveHeaderCol != CPC_INVALIDCOLUMN)
  1435.             {
  1436.                 // Resizing header
  1437.                 SetCapture(pListData->m_hWnd);
  1438.                 pListData->m_enWindowMode = wmHeader_Click;
  1439.                 InvalidateRect(pListData->m_hWnd, &pListData->m_rHeader, FALSE);
  1440.             }
  1441.         }
  1442.     }
  1443.     // - In HScrollbar?
  1444.     else if(PtInRect(&pListData->m_rScrollbar_Horiz, ptCursor) == TRUE)
  1445.     {
  1446.         // In Thumb?
  1447.         if(PtInRect(&pListData->m_rScrollbar_Horiz_Thumb, ptCursor) == TRUE)
  1448.         {
  1449.             // Dragging thumb
  1450.             SetCapture(pListData->m_hWnd);
  1451.             pListData->m_enWindowMode = wmHScrollbar_DragThumb;
  1452.             pListData->m_ptMouseDown_OnHitItem.x = ptCursor.x - pListData->m_rScrollbar_Horiz_Thumb.left;
  1453.             pListData->m_ptMouseDown_OnHitItem.y = ptCursor.y - pListData->m_rScrollbar_Horiz_Thumb.top;
  1454.             InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Horiz_Thumb, FALSE);
  1455.         }
  1456.         // In left button
  1457.         else if(ptCursor.x < (pListData->m_rScrollbar_Horiz.left + glb_pSkin->mpl_pHScrollBar_Left->m_pImage->m_szSize.cx) )
  1458.         {
  1459.             SetCapture(pListData->m_hWnd);
  1460.             pListData->m_enWindowMode = wmHScrollbar_Scroll_Left;
  1461.             CLV_Scroll_Horiz(pListData, -CPC_SCROLLBAR_HORIZ_LINESIZE);
  1462.             InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Horiz, FALSE);
  1463.             pListData->m_bMouseOverScrollbutton = TRUE;
  1464.             CLV_StartAutoRepeat(pListData);
  1465.         }
  1466.         // In right button
  1467.         else if(ptCursor.x > (pListData->m_rScrollbar_Horiz.right - glb_pSkin->mpl_pHScrollBar_Right->m_pImage->m_szSize.cx) )
  1468.         {
  1469.             SetCapture(pListData->m_hWnd);
  1470.             pListData->m_enWindowMode = wmHScrollbar_Scroll_Right;
  1471.             CLV_Scroll_Horiz(pListData, CPC_SCROLLBAR_HORIZ_LINESIZE);
  1472.             InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Horiz, FALSE);
  1473.             pListData->m_bMouseOverScrollbutton = TRUE;
  1474.             CLV_StartAutoRepeat(pListData);
  1475.         }
  1476.         // In left page area
  1477.         else if(ptCursor.x < pListData->m_rScrollbar_Horiz_Thumb.left)
  1478.         {
  1479.             SetCapture(pListData->m_hWnd);
  1480.             pListData->m_enWindowMode = wmHScrollbar_Page_Left;
  1481.             CLV_Scroll_Horiz(pListData, -CPC_SCROLLBAR_HORIZ_PAGESIZE);
  1482.             InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Horiz, FALSE);
  1483.             CLV_StartAutoRepeat(pListData);
  1484.         }
  1485.         // In right page area
  1486.         else if(ptCursor.x > pListData->m_rScrollbar_Horiz_Thumb.right)
  1487.         {
  1488.             SetCapture(pListData->m_hWnd);
  1489.             pListData->m_enWindowMode = wmHScrollbar_Page_Right;
  1490.             CLV_Scroll_Horiz(pListData, CPC_SCROLLBAR_HORIZ_PAGESIZE);
  1491.             InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Horiz, FALSE);
  1492.             CLV_StartAutoRepeat(pListData);
  1493.         }
  1494.     }
  1495.     // - In VScrollbar?
  1496.     else if(PtInRect(&pListData->m_rScrollbar_Vert, ptCursor) == TRUE)
  1497.     {
  1498.         // In Thumb?
  1499.         if(PtInRect(&pListData->m_rScrollbar_Vert_Thumb, ptCursor) == TRUE)
  1500.         {
  1501.             // Dragging thumb
  1502.             SetCapture(pListData->m_hWnd);
  1503.             pListData->m_enWindowMode = wmVScrollbar_DragThumb;
  1504.             pListData->m_ptMouseDown_OnHitItem.x = ptCursor.x - pListData->m_rScrollbar_Vert_Thumb.left;
  1505.             pListData->m_ptMouseDown_OnHitItem.y = ptCursor.y - pListData->m_rScrollbar_Vert_Thumb.top;
  1506.             InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Vert_Thumb, FALSE);
  1507.         }
  1508.         // In up button
  1509.         else if(ptCursor.y < (pListData->m_rScrollbar_Vert.top + glb_pSkin->mpl_pVScrollBar_Up->m_iStateHeight) )
  1510.         {
  1511.             SetCapture(pListData->m_hWnd);
  1512.             pListData->m_enWindowMode = wmVScrollbar_Scroll_Up;
  1513.             CLV_Scroll_Vert(pListData, -1);
  1514.             InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Vert, FALSE);
  1515.             pListData->m_bMouseOverScrollbutton = TRUE;
  1516.             CLV_StartAutoRepeat(pListData);
  1517.         }
  1518.         // In down button
  1519.         else if(ptCursor.y > (pListData->m_rScrollbar_Vert.bottom - glb_pSkin->mpl_pVScrollBar_Down->m_iStateHeight) )
  1520.         {
  1521.             SetCapture(pListData->m_hWnd);
  1522.             pListData->m_enWindowMode = wmVScrollbar_Scroll_Down;
  1523.             CLV_Scroll_Vert(pListData, 1);
  1524.             InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Vert, FALSE);
  1525.             pListData->m_bMouseOverScrollbutton = TRUE;
  1526.             CLV_StartAutoRepeat(pListData);
  1527.         }
  1528.         // In top page area
  1529.         else if(ptCursor.y < pListData->m_rScrollbar_Vert_Thumb.top)
  1530.         {
  1531.             SetCapture(pListData->m_hWnd);
  1532.             pListData->m_enWindowMode = wmVScrollbar_Page_Up;
  1533.             CLV_Scroll_Vert(pListData, -pListData->m_iNumItemsOnPage);
  1534.             InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Vert, FALSE);
  1535.             CLV_StartAutoRepeat(pListData);
  1536.         }
  1537.         // In bottom page area
  1538.         else if(ptCursor.y > pListData->m_rScrollbar_Vert_Thumb.bottom)
  1539.         {
  1540.             SetCapture(pListData->m_hWnd);
  1541.             pListData->m_enWindowMode = wmVScrollbar_Page_Down;
  1542.             CLV_Scroll_Vert(pListData, pListData->m_iNumItemsOnPage);
  1543.             InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Vert, FALSE);
  1544.             CLV_StartAutoRepeat(pListData);
  1545.         }
  1546.     }
  1547.     // In list
  1548.     if(PtInRect(&pListData->m_rList, ptCursor) == TRUE)
  1549.     {
  1550.         pListData->m_enWindowMode = wmList_Click;
  1551.         // Get the item that we have clicked
  1552.         pListData->m_iClickedItem = CLV_YOffsetToItem(pListData, ptCursor.y);
  1553.         if(pListData->m_iClickedItem < 0 || pListData->m_iClickedItem >= pListData->m_iNumItems)
  1554.             pListData->m_iClickedItem = CPC_INVALIDITEM;
  1555.         // If shift is down - select a range
  1556.         if( (dwKeys & MK_SHIFT))
  1557.         {
  1558.             int iCorrectedClickedItem;
  1559.             // If CTRL isn't down - clear the selection
  1560.             if((dwKeys & MK_CONTROL) == 0)
  1561.                 CLV_ClearSelection(pListData);
  1562.             iCorrectedClickedItem = pListData->m_iClickedItem;
  1563.             if(iCorrectedClickedItem >= pListData->m_iNumItems || iCorrectedClickedItem == CPC_INVALIDITEM)
  1564.                 iCorrectedClickedItem = pListData->m_iNumItems - 1;
  1565.             if(pListData->m_iKeyboardAnchor == CPC_INVALIDITEM)
  1566.                 pListData->m_iKeyboardAnchor = pListData->m_iFocusItem;
  1567.             if(pListData->m_iKeyboardAnchor == CPC_INVALIDITEM)
  1568.                 pListData->m_iKeyboardAnchor = iCorrectedClickedItem;
  1569.             CLV_SelectRange(pListData, pListData->m_iKeyboardAnchor, iCorrectedClickedItem);
  1570.             pListData->m_iFocusItem = iCorrectedClickedItem;
  1571.             CLV_InvalidateItem(pListData, pListData->m_iFocusItem);
  1572.         }
  1573.         else
  1574.         {
  1575.             CLV_InvalidateItem(pListData, pListData->m_iFocusItem);
  1576.             pListData->m_iFocusItem = pListData->m_iClickedItem;
  1577.             CLV_InvalidateItem(pListData, pListData->m_iFocusItem);
  1578.             pListData->m_iKeyboardAnchor = pListData->m_iFocusItem;
  1579.         }
  1580.         // ... further processing on mouse up
  1581.     }
  1582. }
  1583. //
  1584. //
  1585. //
  1586. void CLV_Handle_WM_LBUTTONDBLCLK(CIs_ListViewData* pListData, const POINTS _ptCursor)
  1587. {
  1588.     POINT ptCursor;
  1589.     // Init
  1590.     ptCursor.x = _ptCursor.x;
  1591.     ptCursor.y = _ptCursor.y;
  1592.     pListData->m_ptMouseDown = ptCursor;
  1593.     // Hit test cursor
  1594.     // - in header?
  1595.     if(PtInRect(&pListData->m_rHeader, ptCursor) == TRUE)
  1596.     {
  1597.         // A double click in the resize area?
  1598.         int iHitTestColIDX = CLV_HitTest_HeaderSizer_X(pListData, ptCursor.x);
  1599.         if(iHitTestColIDX != CPC_INVALIDCOLUMN)
  1600.         {
  1601.             int iMinWidth = CPC_HEADERDRAG_HTWIDTH<<1;
  1602.             int iItemIDX;
  1603.             HDC dcWindow;
  1604.             SIZE szText;
  1605.             HFONT hfOld;
  1606.             // Resize column according to it's contents
  1607.             dcWindow = GetDC(pListData->m_hWnd);
  1608.             hfOld = (HFONT)SelectObject(dcWindow, glb_pSkin->mpl_hfFont);
  1609.             // Ensure col is big enough for the header
  1610.             if(pListData->m_pColumns[iHitTestColIDX].m_pColumnText)
  1611.             {
  1612.                 GetTextExtentPoint(dcWindow,
  1613.                                    pListData->m_pColumns[iHitTestColIDX].m_pColumnText,
  1614.                                    strlen(pListData->m_pColumns[iHitTestColIDX].m_pColumnText),
  1615.                                    &szText);
  1616.                 szText.cx += glb_pSkin->mpl_rListHeader_SourceTile.left
  1617.                              + (glb_pSkin->mpl_pListHeader_Up->m_szSize.cx-glb_pSkin->mpl_rListHeader_SourceTile.right);
  1618.                 if(szText.cx > iMinWidth)
  1619.                     iMinWidth = szText.cx;
  1620.             }
  1621.             // Go through all the items getting their widths
  1622.             if(pListData->m_pColumns[iHitTestColIDX].m_pfnTextAccessor)
  1623.             {
  1624.                 for(iItemIDX = 0; iItemIDX < pListData->m_iNumItems; iItemIDX++)
  1625.                 {
  1626.                     const char* pcText = pListData->m_pColumns[iHitTestColIDX].m_pfnTextAccessor(pListData->m_pItems[iItemIDX].m_pItemData);
  1627.                     if(pcText)
  1628.                     {
  1629.                         GetTextExtentPoint(dcWindow,
  1630.                                            pcText, strlen(pcText),
  1631.                                            &szText);
  1632.                         szText.cx += glb_pSkin->mpl_rListHeader_SourceTile.left
  1633.                                      + (glb_pSkin->mpl_pListHeader_Down->m_szSize.cx - glb_pSkin->mpl_rListHeader_SourceTile.right);
  1634.                         if(szText.cx > iMinWidth)
  1635.                             iMinWidth = szText.cx;
  1636.                     }
  1637.                 }
  1638.             }
  1639.             SelectObject(dcWindow, hfOld);
  1640.             ReleaseDC(pListData->m_hWnd, dcWindow);
  1641.             // Update column width
  1642.             pListData->m_pColumns[iHitTestColIDX].m_iColumnWidth = iMinWidth;
  1643.             CLV_InvalidateWindow(pListData);
  1644.             CLV_UpdateScrollBars(pListData);
  1645.             SetCursor(LoadCursor(NULL, IDC_ARROW)); // In a header
  1646.             // Send notify
  1647.             if(pListData->m_hndlr_HeaderChanged)
  1648.                 pListData->m_hndlr_HeaderChanged(pListData);
  1649.         }
  1650.     }
  1651.     else if(PtInRect(&pListData->m_rList, ptCursor) == TRUE)
  1652.     {
  1653.         CLV_ActionFocusedItem(pListData);
  1654.     }
  1655. }
  1656. //
  1657. //
  1658. //
  1659. void CLV_Handle_WM_RBUTTONDOWN(CIs_ListViewData* pListData, const POINTS _ptCursor)
  1660. {
  1661.     POINT ptCursor;
  1662.     // Init
  1663.     ptCursor.x = _ptCursor.x;
  1664.     ptCursor.y = _ptCursor.y;
  1665.     // Hit test cursor
  1666.     // - in header?
  1667.     if(PtInRect(&pListData->m_rHeader, ptCursor) == TRUE)
  1668.     {
  1669.         HMENU hmPopup;
  1670.         POINT ptMouse_Screen;
  1671.         UINT uiMenuResult;
  1672.         unsigned int _iColIDX;
  1673.         // Show a popup menu of all the columns so that the visibility can be affected
  1674.         hmPopup = CreatePopupMenu();
  1675.         // Build menu
  1676.         for(_iColIDX = 0; _iColIDX < pListData->m_iNumColumns; _iColIDX++)
  1677.         {
  1678.             UINT uiMenuFlags;
  1679.             unsigned int iColumnIDX = pListData->m_piColumnOrder[_iColIDX];
  1680.             // Skip if there is no text on this column (or it's not hidable)
  1681.             if(pListData->m_pColumns[iColumnIDX].m_pColumnText == NULL
  1682.                     || pListData->m_pColumns[iColumnIDX].m_dwFlags & CPLV_COLFLAG_NOHIDE)
  1683.             {
  1684.                 continue;
  1685.             }
  1686.             if(pListData->m_pColumns[iColumnIDX].m_dwFlags & CPLV_COLFLAG_HIDDEN)
  1687.                 uiMenuFlags = 0;
  1688.             else
  1689.                 uiMenuFlags = MF_CHECKED;
  1690.             AppendMenu(hmPopup, MF_STRING | uiMenuFlags, iColumnIDX + 1, pListData->m_pColumns[iColumnIDX].m_pColumnText);
  1691.         }
  1692.         // Show menu
  1693.         ptMouse_Screen = ptCursor;
  1694.         ClientToScreen(pListData->m_hWnd, &ptMouse_Screen);
  1695.         uiMenuResult = TrackPopupMenu(hmPopup,
  1696.                                       TPM_NONOTIFY | TPM_RETURNCMD,
  1697.                                       ptMouse_Screen.x, ptMouse_Screen.y, 0,
  1698.                                       pListData->m_hWnd, NULL);
  1699.         // If there was a result - toggle the visible state of the item in question
  1700.         if(uiMenuResult != 0)
  1701.         {
  1702.             pListData->m_pColumns[uiMenuResult-1].m_dwFlags ^= CPLV_COLFLAG_HIDDEN;
  1703.             CLV_UpdateScrollBars(pListData);
  1704.             CLV_InvalidateWindow(pListData);
  1705.             // Send notify
  1706.             if(pListData->m_hndlr_HeaderChanged)
  1707.                 pListData->m_hndlr_HeaderChanged(pListData);
  1708.         }
  1709.         DestroyMenu(hmPopup);
  1710.     }
  1711.     else if(PtInRect(&pListData->m_rList, ptCursor) == TRUE)
  1712.     {
  1713.         int iItemIDX;
  1714.         int iColumnIDX;
  1715.         // Perform hit test
  1716.         iItemIDX = CLV_YOffsetToItem(pListData, ptCursor.y);
  1717.         iColumnIDX = CLV_HitTest_Header_X(pListData, ptCursor.x);
  1718.         if(iItemIDX < 0 || iItemIDX >= pListData->m_iNumItems)
  1719.             iItemIDX = CPC_INVALIDITEM;
  1720.         if(iItemIDX != CPC_INVALIDITEM && iColumnIDX != CPC_INVALIDCOLUMN)
  1721.         {
  1722.             if(pListData->m_iFocusItem != iItemIDX)
  1723.                 CLV_SetFocusItem(pListData, iItemIDX);
  1724.             // If the hit item isn't selected - clear selection (and select this item)
  1725.             if( (pListData->m_pItems[iItemIDX].m_dwFlags & CPLV_ITEMFLAG_SELECTED) == 0)
  1726.             {
  1727.                 CLV_ClearSelection(pListData);
  1728.                 pListData->m_pItems[iItemIDX].m_dwFlags |= CPLV_ITEMFLAG_SELECTED;
  1729.                 CLV_InvalidateItem(pListData, iItemIDX);
  1730.                 CLV_ItemSelChange(pListData, iItemIDX);
  1731.             }
  1732.             // Send notify
  1733.             if(pListData->m_hndlr_ItemRightClick)
  1734.                 pListData->m_hndlr_ItemRightClick(pListData, iItemIDX, iColumnIDX, pListData->m_pItems[iItemIDX].m_pItemData);
  1735.         }
  1736.         else
  1737.             CLV_ClearSelection(pListData);
  1738.     }
  1739. }
  1740. //
  1741. //
  1742. //
  1743. void CLV_Handle_WM_LBUTTONUP(CIs_ListViewData* pListData, const POINTS _ptCursor)
  1744. {
  1745.     if(pListData->m_enWindowMode != wmQuiescent)
  1746.         ReleaseCapture();
  1747.     // Invalidate header if we are in click or drag mode (because the header item should now be draw "up)
  1748.     if(pListData->m_enWindowMode == wmHeader_Click || pListData->m_enWindowMode == wmHeader_ChangeOrder)
  1749.         InvalidateRect(pListData->m_hWnd, &pListData->m_rHeader, FALSE);
  1750.     // Invalidate thumb if we were dragging it
  1751.     else if(pListData->m_enWindowMode == wmHScrollbar_DragThumb)
  1752.         InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Horiz_Thumb, FALSE);
  1753.     else if(pListData->m_enWindowMode == wmHScrollbar_Scroll_Left || pListData->m_enWindowMode == wmHScrollbar_Scroll_Right)
  1754.         InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Horiz, FALSE);
  1755.     else if(pListData->m_enWindowMode == wmVScrollbar_DragThumb)
  1756.         InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Vert_Thumb, FALSE);
  1757.     else if(pListData->m_enWindowMode == wmVScrollbar_Scroll_Up || pListData->m_enWindowMode == wmVScrollbar_Scroll_Down)
  1758.         InvalidateRect(pListData->m_hWnd, &pListData->m_rScrollbar_Vert, FALSE);
  1759.     // Clear the autorepeat timer if we need to
  1760.     if(pListData->m_uiAutorepeatTimer)
  1761.     {
  1762.         KillTimer(pListData->m_hWnd, pListData->m_uiAutorepeatTimer);
  1763.         pListData->m_uiAutorepeatTimer = 0;
  1764.     }
  1765.     // Send header change notify if we need to
  1766.     if(pListData->m_hndlr_HeaderChanged
  1767.             && (pListData->m_enWindowMode == wmHeader_ChangeOrder || pListData->m_enWindowMode == wmHeader_ChangeWidth) )
  1768.     {
  1769.         pListData->m_hndlr_HeaderChanged(pListData);
  1770.     }
  1771.     // Send col header click if we need to
  1772.     if(pListData->m_enWindowMode == wmHeader_Click && pListData->m_hndlr_ColHeaderClick)
  1773.         pListData->m_hndlr_ColHeaderClick(pListData, pListData->m_iActiveHeaderCol);
  1774.     // If a list item was clicked then update selection
  1775.     if(pListData->m_enWindowMode == wmList_Click
  1776.             && pListData->m_iClickedItem != CPC_INVALIDITEM
  1777.             && pListData->m_iClickedItem < pListData->m_iNumItems
  1778.             && (pListData->m_dwMouseDown_Keys & MK_SHIFT) == 0)
  1779.     {
  1780.         // If CTRL isn't down - clear the selection
  1781.         if((pListData->m_dwMouseDown_Keys & MK_CONTROL) == 0)
  1782.             CLV_ClearSelection(pListData);
  1783.         // Toggle the selection of the clicked item
  1784.         pListData->m_pItems[pListData->m_iClickedItem].m_dwFlags ^= CPLV_ITEMFLAG_SELECTED;
  1785.         CLV_ItemSelChange(pListData, pListData->m_iClickedItem);
  1786.         CLV_InvalidateItem(pListData, pListData->m_iClickedItem);
  1787.     }
  1788.     // Reset window mode
  1789.     pListData->m_enWindowMode = wmQuiescent;
  1790. }
  1791. //
  1792. //
  1793. //
  1794. void CLV_Handle_WM_KEYDOWN(CIs_ListViewData* pListData, const int iVKeyCode)
  1795. {
  1796.     const BOOL bAltIsDown = (GetAsyncKeyState(VK_MENU)  & 0x8000) ? TRUE : FALSE;
  1797.     const BOOL bCtrlIsDown = (GetAsyncKeyState(VK_CONTROL)  & 0x8000) ? TRUE : FALSE;
  1798.     const BOOL bShiftIsDown = (GetAsyncKeyState(VK_SHIFT)  & 0x8000) ? TRUE : FALSE;
  1799.     switch(iVKeyCode)
  1800.     {
  1801.     case VK_UP:
  1802.     case VK_DOWN:
  1803.     case VK_PRIOR:
  1804.     case VK_NEXT:
  1805.     case VK_HOME:
  1806.     case VK_END:
  1807.         if(bAltIsDown == FALSE)
  1808.         {
  1809.             if(pListData->m_iNumItems > 0)
  1810.             {
  1811.                 int iNewFocusItemIDX = pListData->m_iFocusItem;
  1812.                 if(iNewFocusItemIDX == CPC_INVALIDITEM)
  1813.                     iNewFocusItemIDX = 0;
  1814.                 if(iVKeyCode == VK_UP)
  1815.                     iNewFocusItemIDX--;
  1816.                 else if(iVKeyCode == VK_DOWN)
  1817.                     iNewFocusItemIDX++;
  1818.                 else if(iVKeyCode == VK_PRIOR)
  1819.                     iNewFocusItemIDX -= pListData->m_iNumItemsOnPage;
  1820.                 else if(iVKeyCode == VK_NEXT)
  1821.                     iNewFocusItemIDX += pListData->m_iNumItemsOnPage;
  1822.                 else if(iVKeyCode == VK_HOME)
  1823.                     iNewFocusItemIDX = 0;
  1824.                 else if(iVKeyCode == VK_END)
  1825.                     iNewFocusItemIDX = pListData->m_iNumItems-1;
  1826.                 if(iNewFocusItemIDX < 0)
  1827.                     iNewFocusItemIDX = 0;
  1828.                 else if(iNewFocusItemIDX >= pListData->m_iNumItems)
  1829.                     iNewFocusItemIDX = pListData->m_iNumItems - 1;
  1830.                 if(iNewFocusItemIDX != pListData->m_iFocusItem)
  1831.                 {
  1832.                     // Update selection
  1833.                     if(bCtrlIsDown == FALSE)
  1834.                     {
  1835.                         CLV_ClearSelection(pListData);
  1836.                         pListData->m_pItems[iNewFocusItemIDX].m_dwFlags |= CPLV_ITEMFLAG_SELECTED;
  1837.                         CLV_ItemSelChange(pListData, iNewFocusItemIDX);
  1838.                     }
  1839.                     if(bShiftIsDown)
  1840.                     {
  1841.                         if(pListData->m_iKeyboardAnchor == CPC_INVALIDITEM)
  1842.                         {
  1843.                             if(pListData->m_iFocusItem != CPC_INVALIDITEM)
  1844.                                 pListData->m_iKeyboardAnchor = pListData->m_iFocusItem;
  1845.                             else
  1846.                                 pListData->m_iKeyboardAnchor = iNewFocusItemIDX;
  1847.                         }
  1848.                         if(pListData->m_iFocusItem != iNewFocusItemIDX)
  1849.                             CLV_InvalidateItem(pListData, pListData->m_iFocusItem);
  1850.                         pListData->m_iFocusItem = iNewFocusItemIDX;
  1851.                         CLV_EnsureVisible(pListData, iNewFocusItemIDX);
  1852.                         CLV_SelectRange(pListData, pListData->m_iKeyboardAnchor, pListData->m_iFocusItem);
  1853.                     }
  1854.                     else
  1855.                     {
  1856.                         CLV_InvalidateItem(pListData, pListData->m_iFocusItem);
  1857.                         pListData->m_iFocusItem = iNewFocusItemIDX;
  1858.                         CLV_InvalidateItem(pListData, iNewFocusItemIDX);
  1859.                         CLV_EnsureVisible(pListData, iNewFocusItemIDX);
  1860.                         pListData->m_iKeyboardAnchor = pListData->m_iFocusItem;
  1861.                     }
  1862.                 }
  1863.                 return;
  1864.             }
  1865.         }
  1866.         break;
  1867.     case VK_SPACE:
  1868.         if(pListData->m_iFocusItem != CPC_INVALIDITEM)
  1869.         {
  1870.             if(bCtrlIsDown == TRUE)
  1871.                 pListData->m_pItems[pListData->m_iFocusItem].m_dwFlags ^= CPLV_ITEMFLAG_SELECTED;
  1872.             else
  1873.                 pListData->m_pItems[pListData->m_iFocusItem].m_dwFlags |= CPLV_ITEMFLAG_SELECTED;
  1874.             CLV_ItemSelChange(pListData, pListData->m_iFocusItem);
  1875.             CLV_InvalidateItem(pListData, pListData->m_iFocusItem);
  1876.             CLV_EnsureVisible(pListData, pListData->m_iFocusItem);
  1877.         }
  1878.         return;
  1879.     case 'A':
  1880.         if(bCtrlIsDown == TRUE && pListData->m_iNumItems > 0)
  1881.         {
  1882.             CLV_SelectRange(pListData, 0, pListData->m_iNumItems-1);
  1883.         }
  1884.         return;
  1885.     case VK_RETURN:
  1886.         CLV_ActionFocusedItem(pListData);
  1887.         return;
  1888.     }
  1889.     // Default processing to parent
  1890.     if(pListData->m_hndlr_UnhandledKeyPress)
  1891.         pListData->m_hndlr_UnhandledKeyPress(pListData, iVKeyCode, bAltIsDown, bCtrlIsDown, bShiftIsDown);
  1892. }
  1893. //
  1894. //
  1895. //
  1896. LRESULT CALLBACK exp_ListViewWindowProc(HWND hWnd, UINT uiMessage, WPARAM wParam, LPARAM lParam)
  1897. {
  1898.     CIs_ListViewData* pListData;
  1899.     // Get the window's data object
  1900.     if(uiMessage == WM_NCCREATE)
  1901.     {
  1902.         pListData = (CIs_ListViewData*)((CREATESTRUCT*)lParam)->lpCreateParams;
  1903.         pListData->m_hWnd = hWnd;
  1904.         SetWindowLong(hWnd, GWL_USERDATA, (LONG)pListData);
  1905.     }
  1906.     else
  1907.         pListData = (CIs_ListViewData*)GetWindowLong(hWnd, GWL_USERDATA);
  1908.     CP_CHECKOBJECT(pListData);
  1909.     // Message handlers
  1910.     switch(uiMessage)
  1911.     {
  1912.     case WM_CREATE:
  1913.         {
  1914.             CREATESTRUCT* pCS = (CREATESTRUCT*)lParam;
  1915.             CLV_CalcItemHeight(pListData);
  1916.             CLV_UpdateWindowDims(pListData, pCS->cx, pCS->cy);
  1917.         }
  1918.         break;
  1919.     case WM_NCDESTROY:
  1920.         CLV_CleanupWindowData(pListData);
  1921.         break;
  1922.     case WM_PAINT:
  1923.         CLV_Handle_WM_PAINT(pListData);
  1924.         return 0;
  1925.     case WM_NCCALCSIZE:
  1926.         // We do not wish to have any window area lost to captions etc (also prevent the system
  1927.         // from preserving our window contents during a resize
  1928.         return WVR_REDRAW;
  1929.     case WM_WINDOWPOSCHANGED:
  1930.         {
  1931.             const WINDOWPOS* pWP = (const WINDOWPOS*)lParam;
  1932.             CLV_UpdateWindowDims(pListData, pWP->cx, pWP->cy);
  1933.         }
  1934.         return 0;
  1935.     case WM_DROPFILES:
  1936.         return SendMessage(GetParent(pListData->m_hWnd), WM_DROPFILES, wParam, lParam);
  1937.     case WM_MOUSEMOVE:
  1938.         CLV_Handle_WM_MOUSEMOVE(pListData, MAKEPOINTS(lParam));
  1939.         return 0;
  1940.     case WM_SYSKEYDOWN:
  1941.     case WM_KEYDOWN:
  1942.         CLV_Handle_WM_KEYDOWN(pListData, (int)wParam);
  1943.         return 0;
  1944.     case WM_LBUTTONDOWN:
  1945.         SetFocus(pListData->m_hWnd);
  1946.         CLV_Handle_WM_LBUTTONDOWN(pListData, MAKEPOINTS(lParam), (DWORD)wParam);
  1947.         return 0;
  1948.     case WM_LBUTTONUP:
  1949.         CLV_Handle_WM_LBUTTONUP(pListData, MAKEPOINTS(lParam));
  1950.         return 0;
  1951.     case WM_LBUTTONDBLCLK:
  1952.         CLV_Handle_WM_LBUTTONDBLCLK(pListData, MAKEPOINTS(lParam));
  1953.         return 0;
  1954.     case WM_RBUTTONDOWN:
  1955.         SetFocus(pListData->m_hWnd);
  1956.         CLV_Handle_WM_RBUTTONDOWN(pListData, MAKEPOINTS(lParam));
  1957.         return 0;
  1958.     case WM_MOUSEWHEEL:
  1959.         if((short)HIWORD(wParam) > 0)
  1960.             CLV_Scroll_Vert(pListData, -CPC_SCROLLBAR_MOUSEWHEELAMOUNT);
  1961.         else
  1962.             CLV_Scroll_Vert(pListData, CPC_SCROLLBAR_MOUSEWHEELAMOUNT);
  1963.         return 0;
  1964.     case WM_SETFOCUS:
  1965.         pListData->m_bHasFocus = TRUE;
  1966.         if(pListData->m_iFocusItem != CPC_INVALIDITEM)
  1967.             CLV_InvalidateItem(pListData, pListData->m_iFocusItem);
  1968.         return 0;
  1969.     case WM_KILLFOCUS:
  1970.         pListData->m_bHasFocus = FALSE;
  1971.         if(pListData->m_iFocusItem != CPC_INVALIDITEM)
  1972.             CLV_InvalidateItem(pListData, pListData->m_iFocusItem);
  1973.         return 0;
  1974.     case WM_TIMER:
  1975.         if(wParam == pListData->m_uiAutorepeatTimer)
  1976.             CLV_Handle_WM_TIMER_AUTOREPEAT(pListData);
  1977.         return 0;
  1978.     }
  1979.     return DefWindowProc(hWnd, uiMessage, wParam, lParam);
  1980. }
  1981. //
  1982. //
  1983. //
  1984. void CLV_AddColumn(CP_HLISTVIEW _hListData, const char* pcTitle, const int iWidth, wp_GetItemText pfnItemTextAccessor, const DWORD dwFlags)
  1985. {
  1986.     CIs_ListViewData* pListData;
  1987.     CIs_ListView_Column* pNewColumn;
  1988.     unsigned int iNewColumnIDX;
  1989.     pListData = (CIs_ListViewData*)_hListData;
  1990.     CP_CHECKOBJECT(pListData);
  1991.     // Allocate a new column
  1992.     iNewColumnIDX = pListData->m_iNumColumns;
  1993.     pListData->m_iNumColumns++;
  1994.     pListData->m_pColumns = (CIs_ListView_Column*)realloc(pListData->m_pColumns, pListData->m_iNumColumns * sizeof(CIs_ListView_Column));
  1995.     pListData->m_piColumnOrder = (unsigned int*)realloc(pListData->m_piColumnOrder, pListData->m_iNumColumns * sizeof(unsigned int));
  1996.     pListData->m_piColumnOrder[iNewColumnIDX] = iNewColumnIDX;
  1997.     pNewColumn = pListData->m_pColumns + iNewColumnIDX;
  1998.     // Setup it's data
  1999.     STR_AllocSetString(&pNewColumn->m_pColumnText, pcTitle, FALSE);
  2000.     pNewColumn->m_iColumnWidth = iWidth;
  2001.     pNewColumn->m_dwFlags = dwFlags;
  2002.     pNewColumn->m_pfnTextAccessor = pfnItemTextAccessor;
  2003.     pNewColumn->m_pfnGetCustomDrawColour = NULL;
  2004.     pNewColumn->m_enAlign = lcaLeft;
  2005.     // Cleanup
  2006.     CLV_InvalidateWindow(pListData);
  2007.     CLV_UpdateScrollBars(pListData);
  2008. }
  2009. //
  2010. //
  2011. //
  2012. void CLV_SetColumnCustomDrawColour(CP_HLISTVIEW _hListData, const int iColumnIDX, wp_GetItemDrawColour pfnGetCustomDrawColour)
  2013. {
  2014.     CIs_ListViewData* pListData;
  2015.     pListData = (CIs_ListViewData*)_hListData;
  2016.     CP_CHECKOBJECT(pListData);
  2017.     pListData->m_pColumns[iColumnIDX].m_pfnGetCustomDrawColour = pfnGetCustomDrawColour;
  2018. }
  2019. //
  2020. //
  2021. //
  2022. void CLV_SetColumnAlign(CP_HLISTVIEW _hListData, const int iColumnIDX, const CPe_ListColumnAlign enNewAlign)
  2023. {
  2024.     CIs_ListViewData* pListData;
  2025.     pListData = (CIs_ListViewData*)_hListData;
  2026.     CP_CHECKOBJECT(pListData);
  2027.     pListData->m_pColumns[iColumnIDX].m_enAlign = enNewAlign;
  2028. }
  2029. //
  2030. //
  2031. //
  2032. int CLV_AddItem(CP_HLISTVIEW _hListData, const void* pvItemData)
  2033. {
  2034.     CIs_ListViewData* pListData;
  2035.     int iNewItemIDX;
  2036.     pListData = (CIs_ListViewData*)_hListData;
  2037.     CP_CHECKOBJECT(pListData);
  2038.     // Do we need a bigger buffer?
  2039.     if(pListData->m_iNumItemsInBuffer == pListData->m_iNumItems)
  2040.     {
  2041.         pListData->m_iNumItemsInBuffer += CPC_BUFFERQUANTISATION;
  2042.         pListData->m_pItems = (CIs_ListView_Item*)realloc(pListData->m_pItems, pListData->m_iNumItemsInBuffer * sizeof(CIs_ListView_Item));
  2043.     }
  2044.     // Actually add the item
  2045.     iNewItemIDX = pListData->m_iNumItems;
  2046.     pListData->m_iNumItems++;
  2047.     pListData->m_pItems[iNewItemIDX].m_dwFlags = CPLV_ITEMFLAG_NONE;
  2048.     pListData->m_pItems[iNewItemIDX].m_pItemData = pvItemData;
  2049.     // Update the display
  2050.     if(pListData->m_bInBatch == FALSE)
  2051.     {
  2052.         CLV_UpdateScrollBars(pListData);
  2053.         CLV_InvalidateWindow(pListData);
  2054.     }
  2055.     return iNewItemIDX;
  2056. }
  2057. //
  2058. //
  2059. //
  2060. void CLV_RemoveAllItems(CP_HLISTVIEW _hListData)
  2061. {
  2062.     CIs_ListViewData* pListData;
  2063.     pListData = (CIs_ListViewData*)_hListData;
  2064.     CP_CHECKOBJECT(pListData);
  2065.     CLV_EmptyItems(pListData);
  2066.     if(pListData->m_bInBatch == FALSE)
  2067.     {
  2068.         CLV_UpdateScrollBars(pListData);
  2069.         CLV_InvalidateWindow(pListData);
  2070.     }
  2071.     pListData->m_iFocusItem = CPC_INVALIDITEM;
  2072.     pListData->m_iKeyboardAnchor = CPC_INVALIDITEM;
  2073. }
  2074. //
  2075. //
  2076. //
  2077. int CLV_GetFocusItem(CP_HLISTVIEW _hListData)
  2078. {
  2079.     CIs_ListViewData* pListData;
  2080.     pListData = (CIs_ListViewData*)_hListData;
  2081.     CP_CHECKOBJECT(pListData);
  2082.     return pListData->m_iFocusItem;
  2083. }
  2084. //
  2085. //
  2086. //
  2087. void CLV_SetFocusItem(CP_HLISTVIEW _hListData, int iNewItemIDX)
  2088. {
  2089.     CIs_ListViewData* pListData;
  2090.     pListData = (CIs_ListViewData*)_hListData;
  2091.     CP_CHECKOBJECT(pListData);
  2092.     if(pListData->m_iFocusItem != CPC_INVALIDITEM)
  2093.         CLV_InvalidateItem(pListData, pListData->m_iFocusItem);
  2094.     pListData->m_iFocusItem = iNewItemIDX;
  2095.     if(pListData->m_iFocusItem != CPC_INVALIDITEM)
  2096.         CLV_InvalidateItem(pListData, pListData->m_iFocusItem);
  2097. }
  2098. //
  2099. //
  2100. //
  2101. void CLV_sethandler_DrawBackgroundRect(CP_HLISTVIEW _hListData, wp_DrawBackgroundRect pfnHandler)
  2102. {
  2103.     CIs_ListViewData* pListData;
  2104.     pListData = (CIs_ListViewData*)_hListData;
  2105.     CP_CHECKOBJECT(pListData);
  2106.     pListData->m_hndlr_DrawBackgroundRect = pfnHandler;
  2107. }
  2108. //
  2109. //
  2110. //
  2111. void CLV_sethandler_HeaderChanged(CP_HLISTVIEW _hListData, wp_HeaderChanged pfnHandler)
  2112. {
  2113.     CIs_ListViewData* pListData;
  2114.     pListData = (CIs_ListViewData*)_hListData;
  2115.     CP_CHECKOBJECT(pListData);
  2116.     pListData->m_hndlr_HeaderChanged = pfnHandler;
  2117. }
  2118. //
  2119. //
  2120. //
  2121. void CLV_sethandler_ItemSelected(CP_HLISTVIEW _hListData, wp_ItemCallback pfnHandler)
  2122. {
  2123.     CIs_ListViewData* pListData;
  2124.     pListData = (CIs_ListViewData*)_hListData;
  2125.     CP_CHECKOBJECT(pListData);
  2126.     pListData->m_hndlr_ItemSelected = pfnHandler;
  2127. }
  2128. //
  2129. //
  2130. //
  2131. void CLV_sethandler_ItemAction(CP_HLISTVIEW _hListData, wp_ItemCallback pfnHandler)
  2132. {
  2133.     CIs_ListViewData* pListData;
  2134.     pListData = (CIs_ListViewData*)_hListData;
  2135.     CP_CHECKOBJECT(pListData);
  2136.     pListData->m_hndlr_ItemAction = pfnHandler;
  2137. }
  2138. //
  2139. //
  2140. //
  2141. void CLV_sethandler_ItemDrag(CP_HLISTVIEW _hListData, wp_ItemCallback pfnHandler)
  2142. {
  2143.     CIs_ListViewData* pListData;
  2144.     pListData = (CIs_ListViewData*)_hListData;
  2145.     CP_CHECKOBJECT(pListData);
  2146.     pListData->m_hndlr_ItemDrag = pfnHandler;
  2147. }
  2148. //
  2149. //
  2150. //
  2151. void CLV_sethandler_ItemRightClick(CP_HLISTVIEW _hListData, wp_ItemSubCallback pfnHandler)
  2152. {
  2153.     CIs_ListViewData* pListData;
  2154.     pListData = (CIs_ListViewData*)_hListData;
  2155.     CP_CHECKOBJECT(pListData);
  2156.     pListData->m_hndlr_ItemRightClick = pfnHandler;
  2157. }
  2158. //
  2159. //
  2160. //
  2161. void CLV_sethandler_ColHeaderClick(CP_HLISTVIEW _hListData, wp_ColHeaderClick pfnHandler)
  2162. {
  2163.     CIs_ListViewData* pListData;
  2164.     pListData = (CIs_ListViewData*)_hListData;
  2165.     CP_CHECKOBJECT(pListData);
  2166.     pListData->m_hndlr_ColHeaderClick = pfnHandler;
  2167. }
  2168. //
  2169. //
  2170. //
  2171. void CLV_sethandler_UnhandledKeyPress(CP_HLISTVIEW _hListData, wp_UnhandledKeyPress pfnHandler)
  2172. {
  2173.     CIs_ListViewData* pListData;
  2174.     pListData = (CIs_ListViewData*)_hListData;
  2175.     CP_CHECKOBJECT(pListData);
  2176.     pListData->m_hndlr_UnhandledKeyPress = pfnHandler;
  2177. }
  2178. //
  2179. //
  2180. //
  2181. void CLV_GetColumnOrder(CP_HLISTVIEW _hListData, unsigned int* pOrder, const unsigned int iNumColumnsInArray)
  2182. {
  2183.     CIs_ListViewData* pListData;
  2184.     pListData = (CIs_ListViewData*)_hListData;
  2185.     CP_CHECKOBJECT(pListData);
  2186.     CP_ASSERT(iNumColumnsInArray == pListData->m_iNumColumns);
  2187.     memcpy(pOrder, pListData->m_piColumnOrder, pListData->m_iNumColumns * sizeof(*pListData->m_piColumnOrder) );
  2188. }
  2189. //
  2190. //
  2191. //
  2192. void CLV_SetColumnOrder(CP_HLISTVIEW _hListData, const unsigned int* pOrder, const unsigned int iNumColumnsInArray)
  2193. {
  2194.     CIs_ListViewData* pListData;
  2195.     pListData = (CIs_ListViewData*)_hListData;
  2196.     CP_CHECKOBJECT(pListData);
  2197.     CP_ASSERT(iNumColumnsInArray == pListData->m_iNumColumns);
  2198.     memcpy(pListData->m_piColumnOrder, pOrder, pListData->m_iNumColumns * sizeof(*pListData->m_piColumnOrder) );
  2199. }
  2200. //
  2201. //
  2202. //
  2203. void CLV_GetColumnVisibleState(CP_HLISTVIEW _hListData, BOOL* pStates, const unsigned int iNumColumnsInArray)
  2204. {
  2205.     CIs_ListViewData* pListData;
  2206.     unsigned int iColIDX;
  2207.     pListData = (CIs_ListViewData*)_hListData;
  2208.     CP_CHECKOBJECT(pListData);
  2209.     CP_ASSERT(iNumColumnsInArray == pListData->m_iNumColumns);
  2210.     for(iColIDX = 0; iColIDX < pListData->m_iNumColumns; iColIDX++)
  2211.         pStates[iColIDX] = (pListData->m_pColumns[iColIDX].m_dwFlags & CPLV_COLFLAG_HIDDEN) ? FALSE : TRUE;
  2212. }
  2213. //
  2214. //
  2215. //
  2216. void CLV_GetColumnWidths(CP_HLISTVIEW _hListData, int* pWidths, const unsigned int iNumColumnsInArray)
  2217. {
  2218.     CIs_ListViewData* pListData;
  2219.     unsigned int iColIDX;
  2220.     pListData = (CIs_ListViewData*)_hListData;
  2221.     CP_CHECKOBJECT(pListData);
  2222.     CP_ASSERT(iNumColumnsInArray == pListData->m_iNumColumns);
  2223.     for(iColIDX = 0; iColIDX < pListData->m_iNumColumns; iColIDX++)
  2224.         pWidths[iColIDX] = pListData->m_pColumns[iColIDX].m_iColumnWidth;
  2225. }
  2226. //
  2227. //
  2228. //
  2229. void CLV_ClearSelection(CP_HLISTVIEW _hListData)
  2230. {
  2231.     CIs_ListViewData* pListData;
  2232.     int iItemIDX;
  2233.     pListData = (CIs_ListViewData*)_hListData;
  2234.     CP_CHECKOBJECT(pListData);
  2235.     for(iItemIDX = 0; iItemIDX < pListData->m_iNumItems; iItemIDX++)
  2236.     {
  2237.         if(pListData->m_bInBatch == FALSE && pListData->m_pItems[iItemIDX].m_dwFlags & CPLV_ITEMFLAG_SELECTED)
  2238.             CLV_InvalidateItem(pListData, iItemIDX);
  2239.         pListData->m_pItems[iItemIDX].m_dwFlags &= ~CPLV_ITEMFLAG_SELECTED;
  2240.         CLV_ItemSelChange(pListData, iItemIDX);
  2241.     }
  2242. }
  2243. //
  2244. //
  2245. //
  2246. int CLV_GetNextSelectedItem(CP_HLISTVIEW _hListData, const int _iSearchStart)
  2247. {
  2248.     CIs_ListViewData* pListData;
  2249.     int iItemIDX;
  2250.     int iSearchStart;
  2251.     pListData = (CIs_ListViewData*)_hListData;
  2252.     CP_CHECKOBJECT(pListData);
  2253.     // Decide on where to start the search
  2254.     if(_iSearchStart == CPC_INVALIDITEM)
  2255.         iSearchStart = 0;
  2256.     else
  2257.         iSearchStart = _iSearchStart + 1;
  2258.     // Search array for selected items
  2259.     for(iItemIDX = iSearchStart; iItemIDX < pListData->m_iNumItems; iItemIDX++)
  2260.     {
  2261.         if(pListData->m_pItems[iItemIDX].m_dwFlags & CPLV_ITEMFLAG_SELECTED)
  2262.             return iItemIDX;
  2263.     }
  2264.     return CPC_INVALIDITEM;
  2265. }
  2266. //
  2267. //
  2268. //
  2269. int CLV_GetPrevSelectedItem(CP_HLISTVIEW _hListData, const int _iSearchStart)
  2270. {
  2271.     CIs_ListViewData* pListData;
  2272.     int iItemIDX;
  2273.     int iSearchStart;
  2274.     pListData = (CIs_ListViewData*)_hListData;
  2275.     CP_CHECKOBJECT(pListData);
  2276.     // Decide on where to start the search
  2277.     if(_iSearchStart == CPC_INVALIDITEM)
  2278.         iSearchStart = pListData->m_iNumItems-1;
  2279.     else
  2280.         iSearchStart = _iSearchStart - 1;
  2281.     // Search array for selected items
  2282.     for(iItemIDX = iSearchStart; iItemIDX >= 0; iItemIDX--)
  2283.     {
  2284.         if(pListData->m_pItems[iItemIDX].m_dwFlags & CPLV_ITEMFLAG_SELECTED)
  2285.             return iItemIDX;
  2286.     }
  2287.     return CPC_INVALIDITEM;
  2288. }
  2289. //
  2290. //
  2291. //
  2292. void CLV_SetItemSelected(CP_HLISTVIEW _hListData, const int iItemIDX, const BOOL bSelected)
  2293. {
  2294.     CIs_ListViewData* pListData;
  2295.     pListData = (CIs_ListViewData*)_hListData;
  2296.     CP_CHECKOBJECT(pListData);
  2297.     CP_ASSERT(iItemIDX >= 0);
  2298.     CP_ASSERT(iItemIDX < pListData->m_iNumItems);
  2299.     if(bSelected)
  2300.         pListData->m_pItems[iItemIDX].m_dwFlags |= CPLV_ITEMFLAG_SELECTED;
  2301.     else
  2302.         pListData->m_pItems[iItemIDX].m_dwFlags &= ~CPLV_ITEMFLAG_SELECTED;
  2303.     CLV_ItemSelChange(pListData, iItemIDX);
  2304.     if(pListData->m_bInBatch == FALSE)
  2305.         CLV_InvalidateItem(pListData, iItemIDX);
  2306. }
  2307. //
  2308. //
  2309. //
  2310. BOOL CLV_IsItemSelected(CP_HLISTVIEW _hListData, const int iItemIDX)
  2311. {
  2312.     CIs_ListViewData* pListData;
  2313.     pListData = (CIs_ListViewData*)_hListData;
  2314.     CP_CHECKOBJECT(pListData);
  2315.     CP_ASSERT(iItemIDX >= 0);
  2316.     CP_ASSERT(iItemIDX < pListData->m_iNumItems);
  2317.     if(pListData->m_pItems[iItemIDX].m_dwFlags & CPLV_ITEMFLAG_SELECTED)
  2318.         return TRUE;
  2319.     return FALSE;
  2320. }
  2321. //
  2322. //
  2323. //
  2324. void CLV_SetItemData(CP_HLISTVIEW _hListData, const int iItemIDX, const void* pvItemData)
  2325. {
  2326.     CIs_ListViewData* pListData;
  2327.     pListData = (CIs_ListViewData*)_hListData;
  2328.     CP_CHECKOBJECT(pListData);
  2329.     CP_ASSERT(iItemIDX >= 0);
  2330.     CP_ASSERT(iItemIDX < pListData->m_iNumItems);
  2331.     pListData->m_pItems[iItemIDX].m_pItemData = pvItemData;
  2332.     if(pListData->m_bInBatch == FALSE)
  2333.         CLV_InvalidateItem(pListData, iItemIDX);
  2334. }
  2335. //
  2336. //
  2337. //
  2338. const void* CLV_GetItemData(CP_HLISTVIEW _hListData, const int iItemIDX)
  2339. {
  2340.     CIs_ListViewData* pListData;
  2341.     pListData = (CIs_ListViewData*)_hListData;
  2342.     CP_CHECKOBJECT(pListData);
  2343.     CP_ASSERT(iItemIDX >= 0);
  2344.     CP_ASSERT(iItemIDX < pListData->m_iNumItems);
  2345.     return pListData->m_pItems[iItemIDX].m_pItemData;
  2346. }
  2347. //
  2348. //
  2349. //
  2350. void CLV_EnsureVisible(CP_HLISTVIEW _hListData, const int iItemIDX)
  2351. {
  2352.     CIs_ListViewData* pListData;
  2353.     pListData = (CIs_ListViewData*)_hListData;
  2354.     CP_CHECKOBJECT(pListData);
  2355.     CP_ASSERT(iItemIDX >= 0);
  2356.     CP_ASSERT(iItemIDX < pListData->m_iNumItems);
  2357.     if(iItemIDX < pListData->m_iFirstVisibleItem)
  2358.     {
  2359.         pListData->m_iFirstVisibleItem = iItemIDX;
  2360.         CLV_InvalidateWindow(pListData);
  2361.         CLV_UpdateScrollBars(pListData);
  2362.     }
  2363.     else
  2364.     {
  2365.         int iListRectHeight_CompleteLines;
  2366.         iListRectHeight_CompleteLines = CLV_GetListRect_Lines(pListData) - 1;
  2367.         if(iItemIDX > (pListData->m_iFirstVisibleItem + iListRectHeight_CompleteLines) )
  2368.         {
  2369.             pListData->m_iFirstVisibleItem = iItemIDX - iListRectHeight_CompleteLines;
  2370.             CLV_InvalidateWindow(pListData);
  2371.             CLV_UpdateScrollBars(pListData);
  2372.         }
  2373.     }
  2374. }
  2375. //
  2376. //
  2377. //
  2378. void CLV_SetItem(CP_HLISTVIEW _hListData, const int iItemIDX, const void* pvItemData)
  2379. {
  2380.     CIs_ListViewData* pListData;
  2381.     pListData = (CIs_ListViewData*)_hListData;
  2382.     CP_CHECKOBJECT(pListData);
  2383.     CP_ASSERT(iItemIDX >= 0);
  2384.     CP_ASSERT(iItemIDX < pListData->m_iNumItems);
  2385.     pListData->m_pItems[iItemIDX].m_pItemData = pvItemData;
  2386. }
  2387. //
  2388. //
  2389. //
  2390. void CLV_DeleteItem(CP_HLISTVIEW _hListData, const int iItemToDelete)
  2391. {
  2392.     CIs_ListViewData* pListData;
  2393.     int iItemIDX;
  2394.     pListData = (CIs_ListViewData*)_hListData;
  2395.     CP_CHECKOBJECT(pListData);
  2396.     CP_ASSERT(iItemToDelete >= 0);
  2397.     CP_ASSERT(iItemToDelete < pListData->m_iNumItems);
  2398.     // Shunt all items down one
  2399.     if(pListData->m_iNumItems == 1)
  2400.         CLV_RemoveAllItems(_hListData);
  2401.     else
  2402.     {
  2403.         if(iItemToDelete == pListData->m_iKeyboardAnchor)
  2404.             pListData->m_iKeyboardAnchor = CPC_INVALIDITEM;
  2405.         else if(iItemToDelete < pListData->m_iKeyboardAnchor)
  2406.             pListData->m_iKeyboardAnchor--;
  2407.         // Remove item (and shunt items down)
  2408.         pListData->m_iNumItems--;
  2409.         for(iItemIDX = iItemToDelete; iItemIDX < pListData->m_iNumItems; iItemIDX++)
  2410.             pListData->m_pItems[iItemIDX] = pListData->m_pItems[iItemIDX+1];
  2411.         // Ensure that the focus and anchor items are still valid
  2412.         if(pListData->m_iFocusItem >= pListData->m_iNumItems)
  2413.         {
  2414.             pListData->m_iFocusItem = pListData->m_iNumItems - 1;
  2415.             CLV_InvalidateItem(pListData, pListData->m_iFocusItem);
  2416.         }
  2417.         // Perform invalidation
  2418.         if(pListData->m_bInBatch == FALSE)
  2419.         {
  2420.             CLV_InvalidateWindow(pListData);
  2421.             CLV_UpdateScrollBars(pListData);
  2422.         }
  2423.     }
  2424. }
  2425. //
  2426. //
  2427. //
  2428. int CLV_GetItemCount(CP_HLISTVIEW _hListData)
  2429. {
  2430.     CIs_ListViewData* pListData;
  2431.     pListData = (CIs_ListViewData*)_hListData;
  2432.     CP_CHECKOBJECT(pListData);
  2433.     return pListData->m_iNumItems;
  2434. }
  2435. //
  2436. //
  2437. //
  2438. int CLV_GetNearestItem(CP_HLISTVIEW _hListData, const POINT* ptMouse)
  2439. {
  2440.     CIs_ListViewData* pListData;
  2441.     int iHitItemIDX;
  2442.     pListData = (CIs_ListViewData*)_hListData;
  2443.     CP_CHECKOBJECT(pListData);
  2444.     if(pListData->m_iNumItems == 0)
  2445.         return CPC_INVALIDITEM;
  2446.     iHitItemIDX = CLV_YOffsetToItem(pListData, ptMouse->y);
  2447.     if(iHitItemIDX < 0)
  2448.         iHitItemIDX = 0;
  2449.     if(iHitItemIDX >= pListData->m_iNumItems)
  2450.         iHitItemIDX = pListData->m_iNumItems-1;
  2451.     return iHitItemIDX;
  2452. }
  2453. //
  2454. //
  2455. //
  2456. void CLV_GetItemSubRect(CP_HLISTVIEW _hListData, RECT* pRect, const int iTargetItemIDX, const int iTargetColumnIDX)
  2457. {
  2458.     CIs_ListViewData* pListData;
  2459.     unsigned int _iColIDX;
  2460.     int iCursorX;
  2461.     pListData = (CIs_ListViewData*)_hListData;
  2462.     CP_CHECKOBJECT(pListData);
  2463.     CP_ASSERT(iTargetItemIDX >= 0);
  2464.     CP_ASSERT(iTargetItemIDX < pListData->m_iNumItems);
  2465.     CP_ASSERT(iTargetColumnIDX >= 0);
  2466.     CP_ASSERT(iTargetColumnIDX < (int)pListData->m_iNumColumns);
  2467.     // Work out top and bottom
  2468.     pRect->top = pListData->m_rList.top + ( (iTargetItemIDX - pListData->m_iFirstVisibleItem) * pListData->m_iItemHeight);
  2469.     pRect->bottom = pRect->top + pListData->m_iItemHeight;
  2470.     iCursorX = -pListData->m_iXOrigin;
  2471.     for(_iColIDX = 0; _iColIDX < pListData->m_iNumColumns; _iColIDX++)
  2472.     {
  2473.         int iColumnIDX = pListData->m_piColumnOrder[_iColIDX];
  2474.         if(pListData->m_pColumns[iColumnIDX].m_dwFlags & CPLV_COLFLAG_HIDDEN)
  2475.             continue;
  2476.         if(iColumnIDX == iTargetColumnIDX)
  2477.         {
  2478.             pRect->left = iCursorX;
  2479.             pRect->right = pRect->left + pListData->m_pColumns[iColumnIDX].m_iColumnWidth;
  2480.             break;
  2481.         }
  2482.         iCursorX += pListData->m_pColumns[iColumnIDX].m_iColumnWidth;
  2483.     }
  2484.     // Clip to the list rect
  2485.     if(pRect->left < pListData->m_rList.left)
  2486.         pRect->left = pListData->m_rList.left;
  2487.     if(pRect->right > pListData->m_rList.right)
  2488.         pRect->right = pListData->m_rList.right;
  2489.     if(pRect->top < pListData->m_rList.top)
  2490.         pRect->top = pListData->m_rList.top;
  2491.     if(pRect->bottom > pListData->m_rList.right)
  2492.         pRect->bottom = pListData->m_rList.bottom;
  2493. }
  2494. //
  2495. //
  2496. //
  2497. void CLV_InvalidateColumn(CP_HLISTVIEW _hListData, const int iInvalidColumnIDX)
  2498. {
  2499.     CIs_ListViewData* pListData;
  2500.     RECT rInvalid;
  2501.     unsigned int _iColIDX;
  2502.     int iCursorX;
  2503.     pListData = (CIs_ListViewData*)_hListData;
  2504.     CP_CHECKOBJECT(pListData);
  2505.     // Hidden?
  2506.     if(pListData->m_pColumns[iInvalidColumnIDX].m_dwFlags & CPLV_COLFLAG_HIDDEN)
  2507.         return;
  2508.     // Find the start point of the column
  2509.     iCursorX = -pListData->m_iXOrigin;
  2510.     for(_iColIDX = 0; _iColIDX < pListData->m_iNumColumns; _iColIDX++)
  2511.     {
  2512.         unsigned int iColumnIDX = pListData->m_piColumnOrder[_iColIDX];
  2513.         if((int)iColumnIDX == iInvalidColumnIDX)
  2514.             break;
  2515.         if(pListData->m_pColumns[iColumnIDX].m_dwFlags & CPLV_COLFLAG_HIDDEN)
  2516.             continue;
  2517.         iCursorX += pListData->m_pColumns[iColumnIDX].m_iColumnWidth;
  2518.     }
  2519.     rInvalid.top = 0;
  2520.     rInvalid.bottom = pListData->m_rClient.bottom;
  2521.     rInvalid.left = iCursorX;
  2522.     rInvalid.right = rInvalid.left + pListData->m_pColumns[iInvalidColumnIDX].m_iColumnWidth;
  2523.     InvalidateRect(pListData->m_hWnd, &rInvalid, FALSE);
  2524. }
  2525. //
  2526. //
  2527. //
  2528. void CLV_Invalidate(CP_HLISTVIEW _hListData)
  2529. {
  2530.     CIs_ListViewData* pListData;
  2531.     pListData = (CIs_ListViewData*)_hListData;
  2532.     CP_CHECKOBJECT(pListData);
  2533.     InvalidateRect(pListData->m_hWnd, NULL, FALSE);
  2534. }
  2535. //
  2536. //
  2537. //