Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

3780 lines
107 KiB

  1. #include "ctlspriv.h"
  2. #include "image.h"
  3. #define CCHLABELMAX MAX_PATH // borrowed from listview.h
  4. #define HDDF_NOIMAGE 0x0001
  5. #define HDDF_NOEDGE 0x0002
  6. #define HDI_ALL95 0x001f
  7. #define TF_HEADER TF_LISTVIEW
  8. #define HD_EDITCHANGETIMER 0x100
  9. #define c_cxFilterBarEdge (1)
  10. #define c_cyFilterBarEdge (1)
  11. #define c_cxFilterImage (13)
  12. #define c_cyFilterImage (12)
  13. typedef struct
  14. {
  15. int x; // this is the x position of the RIGHT side (divider) of this item
  16. int cxy;
  17. int fmt;
  18. LPTSTR pszText;
  19. HBITMAP hbm;
  20. int iImage; // index of bitmap in imagelist
  21. LPARAM lParam;
  22. int xBm; // cached values
  23. int xText; // for implementing text and bitmap in header
  24. int cxTextAndBm;
  25. // information used for the filter contol
  26. UINT idOperator;
  27. UINT type;
  28. HD_TEXTFILTER textFilter;
  29. int intFilter;
  30. } HDI;
  31. typedef struct
  32. {
  33. CCONTROLINFO ci;
  34. UINT flags;
  35. int cxEllipses;
  36. int cxDividerSlop;
  37. int cyChar;
  38. HFONT hfont;
  39. HFONT hfontSortArrow;
  40. HIMAGELIST hFilterImage;
  41. HDSA hdsaHDI; // list of HDI's
  42. // tracking state info
  43. int iTrack;
  44. BITBOOL bTrackPress :1; // is the button pressed?
  45. BITBOOL fTrackSet:1;
  46. BITBOOL fOwnerDraw:1;
  47. BITBOOL fFocus:1;
  48. BITBOOL fFilterChangePending:1;
  49. UINT flagsTrack;
  50. int dxTrack; // the distance from the divider that the user started tracking
  51. int xTrack; // the current track position (or starting track position on a button drag)
  52. int xMinTrack; // the x of the end of the previous item (left limit)
  53. int xTrackOldWidth;
  54. HIMAGELIST himl; // handle to our image list
  55. HDSA hdsaOrder; // this is an index array of the hdsaHDI items.
  56. // this is the physical order of items
  57. int iHot ;
  58. HIMAGELIST himlDrag;
  59. int iNewOrder; // what's the new insertion point for a d/d?
  60. int iTextMargin; // The margin to place on either side of text or bitmaps
  61. int iBmMargin; // Normally, 3 * g_cxLabelMargin
  62. int iFocus; // focus object
  63. int iEdit; // editing object
  64. int iButtonDown;
  65. int iFilterChangeTimeout;
  66. HWND hwndEdit;
  67. WNDPROC pfnEditWndProc;
  68. int typeOld;
  69. LPTSTR pszFilterOld;
  70. int intFilterOld;
  71. HTHEME hTheme;
  72. } HD;
  73. LRESULT CALLBACK Header_WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  74. // Message handler functions
  75. BOOL Header_OnCreate(HD* phd, CREATESTRUCT* lpCreateStruct);
  76. void Header_OnNCDestroy(HD* phd);
  77. HIMAGELIST Header_OnSetImageList(HD* phd, HIMAGELIST himl);
  78. HIMAGELIST Header_OnGetImageList(HD* phd);
  79. void Header_OnPaint(HD* phd, HDC hdcIn);
  80. void Header_OnCommand(HD* phd, int id, HWND hwndCtl, UINT codeNotify);
  81. void Header_OnEnable(HD* phd, BOOL fEnable);
  82. UINT Header_OnGetDlgCode(HD* phd, MSG* lpmsg);
  83. void Header_OnLButtonDown(HD* phd, BOOL fDoubleClick, int x, int y, UINT keyFlags);
  84. BOOL Header_IsTracking(HD* phd);
  85. void Header_OnMouseMove(HD* phd, int x, int y, UINT keyFlags);
  86. void Header_OnLButtonUp(HD* phd, int x, int y, UINT keyFlags);
  87. void Header_OnSetFont(HD* plv, HFONT hfont, BOOL fRedraw);
  88. int Header_OnHitTest(HD* phd, HD_HITTESTINFO *phdht);
  89. HFONT Header_OnGetFont(HD* plv);
  90. HIMAGELIST Header_OnCreateDragImage(HD* phd, int i);
  91. BOOL Header_OnGetItemRect(HD* phd, int i, RECT* prc);
  92. void Header_Draw(HD* phd, HDC hdc, RECT* prcClip);
  93. void Header_InvalidateItem(HD* phd, int i, UINT uFlags );
  94. void Header_GetDividerRect(HD* phd, int i, LPRECT prc);
  95. LPARAM Header_OnSetHotDivider(HD* phd, BOOL fPos, LPARAM lParam);
  96. void Header_GetFilterRects(LPRECT prcItem, LPRECT prcHeader, LPRECT prcEdit, LPRECT prcButton);
  97. BOOL Header_BeginFilterEdit(HD* phd, int i);
  98. VOID Header_StopFilterEdit(HD* phd, BOOL fDiscardChanges);
  99. VOID Header_FilterChanged(HD* phd, BOOL fWait);
  100. VOID Header_OnFilterButton(HD* phd, INT i);
  101. LRESULT Header_OnClearFilter(HD* phd, INT i);
  102. // HDM_* Message handler functions
  103. int Header_OnInsertItem(HD* phd, int i, const HD_ITEM* pitem);
  104. BOOL Header_OnDeleteItem(HD* phd, int i);
  105. BOOL Header_OnGetItem(HD* phd, int i, HD_ITEM* pitem);
  106. BOOL Header_OnSetItem(HD* phd, int i, const HD_ITEM* pitem);
  107. BOOL Header_OnLayout(HD* phd, HD_LAYOUT* playout);
  108. BOOL Header_OnSetCursor(HD* phd, HWND hwndCursor, UINT codeHitTest, UINT msg);
  109. void Header_DrawDivider(HD* phd, int x);
  110. int Header_OnInsertItemA(HD* phd, int i, HD_ITEMA* pitem);
  111. BOOL Header_OnGetItemA(HD* phd, int i, HD_ITEMA* pitem);
  112. BOOL Header_OnSetItemA(HD* phd, int i, HD_ITEMA* pitem);
  113. void Header_EndDrag(HD* phd);
  114. BOOL Header_SendChange(HD* phd, int i, int code, const HD_ITEM* pitem);
  115. BOOL Header_Notify(HD* phd, int i, int iButton, int code);
  116. #define Header_GetItemPtr(phd, i) (HDI*)DSA_GetItemPtr((phd)->hdsaHDI, (i))
  117. #define Header_GetCount(phd) (DSA_GetItemCount((phd)->hdsaHDI))
  118. #define Header_IsFilter(phd) ((phd)->ci.style & HDS_FILTERBAR)
  119. #pragma code_seg(CODESEG_INIT)
  120. BOOL Header_Init(HINSTANCE hinst)
  121. {
  122. WNDCLASS wc;
  123. wc.lpfnWndProc = Header_WndProc;
  124. wc.hCursor = NULL; // we do WM_SETCURSOR handling
  125. wc.hIcon = NULL;
  126. wc.lpszMenuName = NULL;
  127. wc.hInstance = hinst;
  128. wc.lpszClassName = c_szHeaderClass;
  129. wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE+1);
  130. wc.style = CS_DBLCLKS | CS_GLOBALCLASS;
  131. wc.cbWndExtra = sizeof(HD*);
  132. wc.cbClsExtra = 0;
  133. if (!RegisterClass(&wc) && !GetClassInfo(hinst, c_szHeaderClass, &wc))
  134. return FALSE;
  135. return TRUE;
  136. }
  137. #pragma code_seg()
  138. // returns -1 if failed to find the item
  139. int Header_OnGetItemOrder(HD* phd, int i)
  140. {
  141. int iIndex;
  142. // if there's no hdsaOrder, then it's in index order
  143. if (phd->hdsaOrder) {
  144. int j;
  145. int iData;
  146. iIndex = -1;
  147. for (j = 0; j < DSA_GetItemCount(phd->hdsaOrder); j++) {
  148. DSA_GetItem(phd->hdsaOrder, j, &iData);
  149. if (iData == i) {
  150. iIndex = j;
  151. break;
  152. }
  153. }
  154. } else {
  155. iIndex = i;
  156. }
  157. return iIndex;
  158. }
  159. int Header_ItemOrderToIndex(HD* phd, int iOrder)
  160. {
  161. RIPMSG(iOrder < DSA_GetItemCount(phd->hdsaHDI), "HDM_ORDERTOINDEX: Invalid order %d", iOrder);
  162. if (phd->hdsaOrder) {
  163. ASSERT(DSA_GetItemCount(phd->hdsaHDI) == DSA_GetItemCount(phd->hdsaOrder));
  164. #ifdef DEBUG
  165. // DSA_GetItem will assert on an invalid index, so filter it out
  166. // so all we get is the RIP above.
  167. if (iOrder < DSA_GetItemCount(phd->hdsaOrder))
  168. #endif
  169. DSA_GetItem(phd->hdsaOrder, iOrder, &iOrder);
  170. }
  171. return iOrder;
  172. }
  173. HDI* Header_GetItemPtrByOrder(HD* phd, int iOrder)
  174. {
  175. int iIndex = Header_ItemOrderToIndex(phd, iOrder);
  176. return Header_GetItemPtr(phd, iIndex);
  177. }
  178. HDSA Header_InitOrderArray(HD* phd)
  179. {
  180. int i;
  181. if (!phd->hdsaOrder && !(phd->ci.style & HDS_OWNERDATA)) {
  182. // not initialized yet..
  183. // create an array with i to i mapping
  184. phd->hdsaOrder = DSA_Create(sizeof(int), 4);
  185. if (phd->hdsaOrder) {
  186. for (i = 0; i < Header_GetCount(phd); i++) {
  187. if (DSA_InsertItem(phd->hdsaOrder, i, &i) == -1) {
  188. // faild to add... bail
  189. DSA_Destroy(phd->hdsaOrder);
  190. phd->hdsaOrder = NULL;
  191. }
  192. }
  193. }
  194. }
  195. return phd->hdsaOrder;
  196. }
  197. // this moves all items starting from iIndex over by dx
  198. void Header_ShiftItems(HD* phd, int iOrder, int dx)
  199. {
  200. for(; iOrder < Header_GetCount(phd); iOrder++) {
  201. HDI* phdi = Header_GetItemPtrByOrder(phd, iOrder);
  202. phdi->x += dx;
  203. }
  204. }
  205. void Header_OnSetItemOrder(HD* phd, int iIndex, int iOrder)
  206. {
  207. if (iIndex < Header_GetCount(phd) &&
  208. iOrder < Header_GetCount(phd) &&
  209. Header_InitOrderArray(phd)) {
  210. int iCurOrder = Header_OnGetItemOrder(phd, iIndex);
  211. // only do work if the order is changing
  212. if (iOrder != iCurOrder) {
  213. // delete the current order location
  214. HDI* phdi = Header_GetItemPtr(phd, iIndex);
  215. HDI* phdiOld = Header_GetItemPtrByOrder(phd, iOrder);
  216. // stop editing the filter
  217. Header_StopFilterEdit(phd, FALSE);
  218. // remove iIndex from the current order
  219. // (slide stuff to the right down by our width)
  220. Header_ShiftItems(phd, iCurOrder + 1, -phdi->cxy);
  221. DSA_DeleteItem(phd->hdsaOrder, iCurOrder);
  222. // insert it into the order and slide everything else over
  223. // (slide stuff to the right of the new position up by our width)
  224. DSA_InsertItem(phd->hdsaOrder, iOrder, &iIndex);
  225. // set our right edge to where their left edge was
  226. Header_ShiftItems(phd, iOrder + 1, phdi->cxy);
  227. if (iOrder == 0) {
  228. phdi->x = phdi->cxy;
  229. } else {
  230. phdiOld = Header_GetItemPtrByOrder(phd, iOrder - 1);
  231. phdi->x = phdiOld->x + phdi->cxy;
  232. }
  233. RedrawWindow(phd->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  234. }
  235. }
  236. }
  237. void Header_SetHotItem(HD* phd, int i)
  238. {
  239. if (i != phd->iHot) {
  240. Header_InvalidateItem(phd, i, RDW_INVALIDATE);
  241. Header_InvalidateItem(phd, phd->iHot, RDW_INVALIDATE);
  242. phd->iHot = i;
  243. }
  244. }
  245. LRESULT Header_OnGetOrderArray(HD* phd, int iCount, LPINT lpi)
  246. {
  247. int i;
  248. if (Header_GetCount(phd) != iCount)
  249. return FALSE;
  250. for (i = 0; i < Header_GetCount(phd) ; i++) {
  251. lpi[i] = Header_ItemOrderToIndex(phd, i);
  252. }
  253. return TRUE;
  254. }
  255. LRESULT Header_OnSetOrderArray(HD* phd, int iCount, LPINT lpi)
  256. {
  257. int i;
  258. if (Header_GetCount(phd) != iCount)
  259. return FALSE;
  260. for (i = 0; i < Header_GetCount(phd); i++) {
  261. Header_OnSetItemOrder(phd, lpi[i], i);
  262. }
  263. NotifyWinEvent(EVENT_OBJECT_REORDER, phd->ci.hwnd, OBJID_CLIENT, 0);
  264. return TRUE;
  265. }
  266. BOOL HDDragFullWindows(HD* phd)
  267. {
  268. return (g_fDragFullWindows && (phd->ci.style & HDS_FULLDRAG));
  269. }
  270. LRESULT CALLBACK Header_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  271. {
  272. HD* phd = (HD*)GetWindowPtr(hwnd, 0);
  273. if (phd == NULL)
  274. {
  275. if (uMsg == WM_NCCREATE)
  276. {
  277. phd = (HD*)NearAlloc(sizeof(HD));
  278. if (phd == NULL)
  279. return 0L;
  280. phd->ci.hwnd = hwnd;
  281. phd->ci.hwndParent = ((LPCREATESTRUCT)lParam)->hwndParent;
  282. SetWindowPtr(hwnd, 0, phd);
  283. // fall through to call DefWindowProc
  284. }
  285. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  286. }
  287. else
  288. {
  289. if (uMsg == WM_THEMECHANGED)
  290. {
  291. if (phd->hTheme)
  292. CloseThemeData(phd->hTheme);
  293. phd->hTheme = OpenThemeData(phd->ci.hwnd, L"Header");
  294. InvalidateRect(phd->ci.hwnd, NULL, TRUE);
  295. return 0;
  296. }
  297. if (uMsg == WM_NCDESTROY)
  298. {
  299. Header_OnNCDestroy(phd);
  300. NearFree(phd);
  301. SetWindowInt(hwnd, 0, 0);
  302. return 0;
  303. }
  304. // if we loose capture, or the r-button goes down, or the user hits esc, then we abort the drag/resize
  305. if (uMsg == WM_CAPTURECHANGED ||
  306. uMsg == WM_RBUTTONDOWN || (GetKeyState(VK_ESCAPE) & 0x8000)) {
  307. if (phd->himlDrag) {
  308. // if this is the end of a drag,
  309. // notify the user.
  310. HDITEM item;
  311. item.mask = HDI_ORDER;
  312. item.iOrder = -1; // abort order changing
  313. Header_EndDrag(phd);
  314. Header_SendChange(phd, phd->iTrack, HDN_ENDDRAG, &item);
  315. } else if (phd->flagsTrack & (HHT_ONDIVIDER | HHT_ONDIVOPEN)) {
  316. HD_ITEM item;
  317. item.mask = HDI_WIDTH;
  318. item.cxy = phd->xTrackOldWidth;
  319. phd->flagsTrack = 0;
  320. KillTimer(phd->ci.hwnd, 1);
  321. CCReleaseCapture(&phd->ci);
  322. Header_SendChange(phd, phd->iTrack, HDN_ENDTRACK, &item);
  323. if (HDDragFullWindows(phd)) {
  324. // incase they changed something
  325. item.mask = HDI_WIDTH;
  326. item.cxy = phd->xTrackOldWidth;
  327. Header_OnSetItem(phd, phd->iTrack, &item);
  328. RedrawWindow(phd->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  329. } else {
  330. // Undraw the last divider we displayed
  331. Header_DrawDivider(phd, phd->xTrack);
  332. }
  333. }
  334. }
  335. if ((uMsg >= WM_MOUSEFIRST) && (uMsg <= WM_MOUSELAST) &&
  336. (phd->hTheme || phd->ci.style & HDS_HOTTRACK) && !phd->fTrackSet) {
  337. TRACKMOUSEEVENT tme;
  338. phd->fTrackSet = TRUE;
  339. tme.cbSize = sizeof(tme);
  340. tme.hwndTrack = phd->ci.hwnd;
  341. tme.dwFlags = TME_LEAVE;
  342. TrackMouseEvent(&tme);
  343. }
  344. // ROBUSTNESS: keep this switch within the if (phd) block
  345. //
  346. switch (uMsg)
  347. {
  348. HANDLE_MSG(phd, WM_CREATE, Header_OnCreate);
  349. HANDLE_MSG(phd, WM_SETCURSOR, Header_OnSetCursor);
  350. HANDLE_MSG(phd, WM_MOUSEMOVE, Header_OnMouseMove);
  351. HANDLE_MSG(phd, WM_LBUTTONDOWN, Header_OnLButtonDown);
  352. HANDLE_MSG(phd, WM_LBUTTONDBLCLK, Header_OnLButtonDown);
  353. HANDLE_MSG(phd, WM_LBUTTONUP, Header_OnLButtonUp);
  354. HANDLE_MSG(phd, WM_GETDLGCODE, Header_OnGetDlgCode);
  355. HANDLE_MSG(phd, WM_SETFONT, Header_OnSetFont);
  356. HANDLE_MSG(phd, WM_GETFONT, Header_OnGetFont);
  357. case WM_COMMAND:
  358. if ( (phd->iEdit>=0) && ((HWND)lParam == phd->hwndEdit) )
  359. {
  360. // when filtering we will receive notifications that the filter
  361. // has been edited, therefore lets send those down to the
  362. // parent.
  363. if ( HIWORD(wParam)==EN_CHANGE )
  364. {
  365. Header_FilterChanged(phd, TRUE);
  366. return(0);
  367. }
  368. }
  369. break;
  370. case WM_TIMER:
  371. if (wParam == HD_EDITCHANGETIMER)
  372. {
  373. Header_FilterChanged(phd, FALSE);
  374. return(0);
  375. }
  376. break;
  377. case WM_SETFOCUS:
  378. case WM_KILLFOCUS:
  379. // filter bar and not editing then take caret into edit first column
  380. if (Header_IsFilter(phd))
  381. {
  382. phd->fFocus = (uMsg==WM_SETFOCUS);
  383. Header_InvalidateItem(phd, Header_ItemOrderToIndex(phd, phd->iFocus), RDW_INVALIDATE);
  384. UpdateWindow(phd->ci.hwnd);
  385. return(0);
  386. }
  387. break;
  388. case WM_KEYDOWN:
  389. if ( phd->fFocus )
  390. {
  391. // handle the key events that the header control receives, when the filter
  392. // bar is displayed we then allow the user to enter filter mode and drop the
  393. // filter menu.
  394. //
  395. // F2 = enter filter mode
  396. // F4 = drop filter menu
  397. // -> = next column
  398. // <- = previous column
  399. if ( wParam == VK_F2 )
  400. {
  401. // start editing the currently focused column
  402. Header_BeginFilterEdit(phd, Header_ItemOrderToIndex(phd, phd->iFocus));
  403. //notify of navigation key usage
  404. CCNotifyNavigationKeyUsage(&(phd->ci), UISF_HIDEFOCUS);
  405. return 0L;
  406. }
  407. else if ( wParam == VK_F4 )
  408. {
  409. // drop the filter menu (this exits edit mode)
  410. Header_OnFilterButton(phd, Header_ItemOrderToIndex(phd, phd->iFocus));
  411. //notify of navigation key usage
  412. CCNotifyNavigationKeyUsage(&(phd->ci), UISF_HIDEFOCUS);
  413. return 0L;
  414. }
  415. else if ( (wParam == VK_LEFT)||(wParam == VK_RIGHT) )
  416. {
  417. INT iFocus = phd->iFocus;
  418. // move to previous or next column
  419. if ( wParam == VK_RIGHT )
  420. {
  421. phd->iFocus = (iFocus+1) % Header_GetCount(phd);
  422. }
  423. else
  424. {
  425. phd->iFocus = iFocus-1;
  426. if ( phd->iFocus < 0 )
  427. phd->iFocus = max(Header_GetCount(phd)-1, 0);
  428. }
  429. // did the focused column change? if so then update the control
  430. // as required.
  431. if ( iFocus != phd->iFocus )
  432. {
  433. Header_InvalidateItem(phd, Header_ItemOrderToIndex(phd, iFocus), RDW_INVALIDATE);
  434. Header_InvalidateItem(phd, Header_ItemOrderToIndex(phd, phd->iFocus), RDW_INVALIDATE);
  435. UpdateWindow(phd->ci.hwnd);
  436. }
  437. //notify of navigation key usage
  438. CCNotifyNavigationKeyUsage(&(phd->ci), UISF_HIDEFOCUS);
  439. return 0L;
  440. }
  441. }
  442. break;
  443. case WM_MOUSELEAVE:
  444. Header_SetHotItem(phd, -1);
  445. phd->fTrackSet = FALSE;
  446. break;
  447. case WM_ERASEBKGND:
  448. return 1;
  449. case WM_PRINTCLIENT:
  450. case WM_PAINT:
  451. Header_OnPaint(phd, (HDC)wParam);
  452. return(0);
  453. case WM_RBUTTONUP:
  454. if (CCSendNotify(&phd->ci, NM_RCLICK, NULL))
  455. return(0);
  456. break;
  457. case WM_STYLECHANGED:
  458. if (wParam == GWL_STYLE) {
  459. LPSTYLESTRUCT pss = (LPSTYLESTRUCT)lParam;
  460. phd->ci.style = pss->styleNew;
  461. // if the filter is changing then discard it if its active
  462. if ((pss->styleOld & HDS_FILTERBAR) != (pss->styleNew & HDS_FILTERBAR))
  463. Header_StopFilterEdit(phd, TRUE);
  464. // we don't cache our style so relay out and invaidate
  465. InvalidateRect(phd->ci.hwnd, NULL, TRUE);
  466. }
  467. return(0);
  468. case WM_UPDATEUISTATE:
  469. {
  470. DWORD dwUIStateMask = MAKEWPARAM(0xFFFF, UISF_HIDEFOCUS);
  471. if (CCOnUIState(&(phd->ci), WM_UPDATEUISTATE, wParam & dwUIStateMask, lParam) &&
  472. phd->iFocus < DSA_GetItemCount(phd->hdsaHDI))
  473. {
  474. Header_InvalidateItem(phd, Header_ItemOrderToIndex(phd, phd->iFocus), RDW_INVALIDATE);
  475. }
  476. break;
  477. }
  478. case WM_NOTIFYFORMAT:
  479. return CIHandleNotifyFormat(&phd->ci, lParam);
  480. case HDM_GETITEMCOUNT:
  481. return (LPARAM)(UINT)DSA_GetItemCount(phd->hdsaHDI);
  482. case HDM_INSERTITEM:
  483. return (LPARAM)Header_OnInsertItem(phd, (int)wParam, (const HD_ITEM*)lParam);
  484. case HDM_DELETEITEM:
  485. return (LPARAM)Header_OnDeleteItem(phd, (int)wParam);
  486. case HDM_GETITEM:
  487. return (LPARAM)Header_OnGetItem(phd, (int)wParam, (HD_ITEM*)lParam);
  488. case HDM_SETITEM:
  489. return (LPARAM)Header_OnSetItem(phd, (int)wParam, (const HD_ITEM*)lParam);
  490. case HDM_LAYOUT:
  491. return (LPARAM)Header_OnLayout(phd, (HD_LAYOUT*)lParam);
  492. case HDM_HITTEST:
  493. return (LPARAM)Header_OnHitTest(phd, (HD_HITTESTINFO *)lParam);
  494. case HDM_GETITEMRECT:
  495. return (LPARAM)Header_OnGetItemRect(phd, (int)wParam, (LPRECT)lParam);
  496. case HDM_SETIMAGELIST:
  497. return (LRESULT)(ULONG_PTR)Header_OnSetImageList(phd, (HIMAGELIST)lParam);
  498. case HDM_GETIMAGELIST:
  499. return (LRESULT)(ULONG_PTR)phd->himl;
  500. case HDM_INSERTITEMA:
  501. return (LPARAM)Header_OnInsertItemA(phd, (int)wParam, (HD_ITEMA*)lParam);
  502. case HDM_GETITEMA:
  503. return (LPARAM)Header_OnGetItemA(phd, (int)wParam, (HD_ITEMA*)lParam);
  504. case HDM_SETITEMA:
  505. return (LPARAM)Header_OnSetItemA(phd, (int)wParam, (HD_ITEMA*)lParam);
  506. case HDM_ORDERTOINDEX:
  507. return Header_ItemOrderToIndex(phd, (int)wParam);
  508. case HDM_CREATEDRAGIMAGE:
  509. return (LRESULT)Header_OnCreateDragImage(phd, Header_OnGetItemOrder(phd, (int)wParam));
  510. case HDM_SETORDERARRAY:
  511. return Header_OnSetOrderArray(phd, (int)wParam, (LPINT)lParam);
  512. case HDM_GETORDERARRAY:
  513. return Header_OnGetOrderArray(phd, (int)wParam, (LPINT)lParam);
  514. case HDM_SETHOTDIVIDER:
  515. return Header_OnSetHotDivider(phd, (int)wParam, lParam);
  516. case HDM_SETBITMAPMARGIN:
  517. phd->iBmMargin = (int)wParam;
  518. TraceMsg(TF_HEADER, "Setting bmMargin = %d",wParam);
  519. return TRUE;
  520. case HDM_GETBITMAPMARGIN:
  521. return phd->iBmMargin;
  522. case HDM_EDITFILTER:
  523. Header_StopFilterEdit(phd, (BOOL)LOWORD(lParam));
  524. return Header_BeginFilterEdit(phd, (int)wParam);
  525. case HDM_SETFILTERCHANGETIMEOUT:
  526. if ( lParam ) {
  527. int iOldTimeout = phd->iFilterChangeTimeout;
  528. phd->iFilterChangeTimeout = (int)lParam;
  529. return(iOldTimeout);
  530. }
  531. return(phd->iFilterChangeTimeout);
  532. case HDM_CLEARFILTER:
  533. return Header_OnClearFilter(phd, (int)wParam);
  534. case WM_GETOBJECT:
  535. if( lParam == OBJID_QUERYCLASSNAMEIDX )
  536. return MSAA_CLASSNAMEIDX_HEADER;
  537. break;
  538. default:
  539. {
  540. LRESULT lres;
  541. if (CCWndProc(&phd->ci, uMsg, wParam, lParam, &lres))
  542. return lres;
  543. }
  544. }
  545. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  546. }
  547. }
  548. BOOL Header_SendChange(HD* phd, int i, int code, const HD_ITEM* pitem)
  549. {
  550. NMHEADER nm;
  551. nm.iItem = i;
  552. nm.pitem = (HD_ITEM*)pitem;
  553. nm.iButton = 0;
  554. return !(BOOL)CCSendNotify(&phd->ci, code, &nm.hdr);
  555. }
  556. BOOL Header_Notify(HD* phd, int i, int iButton, int code)
  557. {
  558. NMHEADER nm;
  559. nm.iItem = i;
  560. nm.iButton = iButton;
  561. nm.pitem = NULL;
  562. return !(BOOL)CCSendNotify(&phd->ci, code, &nm.hdr);
  563. }
  564. void Header_NewFont(HD* phd, HFONT hfont)
  565. {
  566. HDC hdc;
  567. SIZE siz = {0};
  568. HRESULT hr = E_FAIL;
  569. HFONT hFontOld = NULL;
  570. int cy;
  571. TEXTMETRIC tm;
  572. HFONT hfontSortArrow = NULL;
  573. hdc = GetDC(HWND_DESKTOP);
  574. if (phd->hTheme)
  575. {
  576. RECT rc = {0};
  577. RECT rcBound = {0};
  578. hr = GetThemeTextExtent(phd->hTheme, hdc, HP_HEADERITEM, 0, TEXT("M"), -1, 0, &rcBound, &rc);
  579. siz.cx = RECTWIDTH(rc);
  580. siz.cy = RECTHEIGHT(rc);
  581. }
  582. if (FAILED(hr))
  583. {
  584. if (hfont)
  585. SelectFont(hdc, hfont);
  586. GetTextExtentPoint(hdc, c_szEllipses, CCHELLIPSES, &siz);
  587. }
  588. if (phd->hfont)
  589. hFontOld = (HFONT)SelectObject(hdc, phd->hfont);
  590. GetTextMetrics(hdc, &tm);
  591. if (hFontOld)
  592. SelectObject(hdc, hFontOld);
  593. // Set the font height (based on original USER code)
  594. cy = ((tm.tmHeight + tm.tmExternalLeading + GetSystemMetrics(SM_CYBORDER)) & 0xFFFE) - 1;
  595. // Create the marlett font, so we can paint the arrows.
  596. hfontSortArrow = CreateFont(cy, 0, 0, 0, 0, FALSE, FALSE, FALSE, DEFAULT_CHARSET, 0, 0, 0, 0,
  597. TEXT("Marlett"));
  598. phd->cxEllipses = siz.cx;
  599. phd->cyChar = siz.cy;
  600. phd->hfont = hfont;
  601. if (hfontSortArrow)
  602. {
  603. if (phd->hfontSortArrow) // Do we have one to free?
  604. {
  605. DeleteObject(phd->hfontSortArrow);
  606. }
  607. phd->hfontSortArrow = hfontSortArrow;
  608. }
  609. phd->ci.uiCodePage = GetCodePageForFont(hfont);
  610. ReleaseDC(HWND_DESKTOP, hdc);
  611. }
  612. BOOL Header_OnCreate(HD* phd, CREATESTRUCT* lpCreateStruct)
  613. {
  614. ASSERT(phd); // we are only called if phd is valid
  615. CIInitialize(&phd->ci, phd->ci.hwnd, (LPCREATESTRUCT)lpCreateStruct);
  616. phd->flags = 0;
  617. phd->hfont = NULL;
  618. phd->hFilterImage = NULL;
  619. phd->iNewOrder = -1;
  620. phd->iHot = -1;
  621. phd->iFocus = 0;
  622. phd->iEdit = -1;
  623. phd->iButtonDown = -1;
  624. phd->iFilterChangeTimeout = GetDoubleClickTime()*2;
  625. phd->hwndEdit = NULL;
  626. phd->hdsaHDI = DSA_Create(sizeof(HDI), 4);
  627. if (!phd->hdsaHDI)
  628. return FALSE;
  629. phd->cxDividerSlop = 8 * g_cxBorder;
  630. phd->hTheme = OpenThemeData(phd->ci.hwnd, L"Header");
  631. // Warning! ListView_RSetColumnWidth knows these values.
  632. phd->iTextMargin = 3 * g_cxLabelMargin;
  633. phd->iBmMargin = 3 * g_cxLabelMargin;
  634. // phd->himl = NULL;
  635. Header_NewFont(phd, NULL);
  636. return TRUE;
  637. }
  638. int Header_DestroyItemCallback(LPVOID p, LPVOID d)
  639. {
  640. HDI * phdi = (HDI*)p;
  641. if (phdi)
  642. {
  643. Str_Set(&phdi->pszText, NULL);
  644. if ( (phdi->type & HDFT_ISMASK)==HDFT_ISSTRING )
  645. Str_Set(&phdi->textFilter.pszText, NULL);
  646. }
  647. return 1;
  648. }
  649. void Header_OnNCDestroy(HD* phd)
  650. {
  651. // stop editing the filter
  652. if ( phd->hFilterImage )
  653. ImageList_Destroy(phd->hFilterImage);
  654. Header_StopFilterEdit(phd, TRUE);
  655. // We must walk through and destroy all of the string pointers that
  656. // are contained in the structures before we pass it off to the
  657. // DSA_Destroy function...
  658. DSA_DestroyCallback(phd->hdsaHDI, Header_DestroyItemCallback, 0);
  659. phd->hdsaHDI = NULL;
  660. if (phd->hdsaOrder)
  661. {
  662. DSA_Destroy(phd->hdsaOrder);
  663. phd->hdsaOrder = NULL;
  664. }
  665. if (phd->hTheme)
  666. CloseThemeData(phd->hTheme);
  667. if (phd->hfontSortArrow)
  668. DeleteObject(phd->hfontSortArrow);
  669. }
  670. HIMAGELIST Header_OnSetImageList(HD* phd, HIMAGELIST himl)
  671. {
  672. HIMAGELIST hImageOld = phd->himl;
  673. phd->himl = himl;
  674. return hImageOld;
  675. }
  676. void Header_OnPaint(HD* phd, HDC hdc)
  677. {
  678. PAINTSTRUCT ps;
  679. HDC hdcUse;
  680. CCDBUFFER cdd;
  681. if (!phd)
  682. return;
  683. if (hdc)
  684. {
  685. hdcUse = hdc;
  686. GetClientRect(phd->ci.hwnd, &ps.rcPaint);
  687. }
  688. else
  689. {
  690. hdcUse = BeginPaint(phd->ci.hwnd, &ps);
  691. }
  692. hdcUse = CCBeginDoubleBuffer(hdcUse, &ps.rcPaint, &cdd);
  693. Header_Draw(phd, hdcUse, &ps.rcPaint);
  694. CCEndDoubleBuffer(&cdd);
  695. if (!hdc)
  696. {
  697. EndPaint(phd->ci.hwnd, &ps);
  698. }
  699. }
  700. UINT Header_OnGetDlgCode(HD* phd, MSG* lpmsg)
  701. {
  702. return DLGC_WANTTAB | DLGC_WANTARROWS;
  703. }
  704. int Header_HitTest(HD* phd, int x, int y, UINT* pflags)
  705. {
  706. UINT flags = 0;
  707. POINT pt;
  708. RECT rc;
  709. HDI* phdi;
  710. int i;
  711. pt.x = x; pt.y = y;
  712. GetClientRect(phd->ci.hwnd, &rc);
  713. flags = 0;
  714. i = -1;
  715. if (x < rc.left)
  716. flags |= HHT_TOLEFT;
  717. else if (x >= rc.right)
  718. flags |= HHT_TORIGHT;
  719. if (y < rc.top)
  720. flags |= HHT_ABOVE;
  721. else if (y >= rc.bottom)
  722. flags |= HHT_BELOW;
  723. if (flags == 0)
  724. {
  725. int cItems = DSA_GetItemCount(phd->hdsaHDI);
  726. int xPrev = 0;
  727. BOOL fPrevZero = FALSE;
  728. int xItem;
  729. int cxSlop;
  730. //DebugMsg(DM_TRACE, "Hit Test begin");
  731. for (i = 0; i <= cItems; i++, phdi++, xPrev = xItem)
  732. {
  733. if (i == cItems)
  734. xItem = rc.right;
  735. else {
  736. phdi = Header_GetItemPtrByOrder(phd, i);
  737. xItem = phdi->x;
  738. }
  739. // DebugMsg(DM_TRACE, "x = %d xItem = %d xPrev = %d fPrevZero = %d", x, xItem, xPrev, xPrev == xItem);
  740. if (xItem == xPrev)
  741. {
  742. // Skip zero width items...
  743. //
  744. fPrevZero = TRUE;
  745. continue;
  746. }
  747. cxSlop = min((xItem - xPrev) / 4, phd->cxDividerSlop);
  748. if (x >= xPrev && x < xItem)
  749. {
  750. if ( Header_IsFilter(phd) )
  751. {
  752. RECT rcItem;
  753. RECT rcHeader, rcFilter, rcButton;
  754. rcItem.left = xPrev;
  755. rcItem.top = rc.top;
  756. rcItem.right = xItem;
  757. rcItem.bottom = rc.bottom ;
  758. Header_GetFilterRects(&rcItem, &rcHeader, &rcFilter, &rcButton);
  759. if ( y >= rcFilter.top )
  760. {
  761. if ( x >= rcFilter.right )
  762. {
  763. // hit check the entire button, forget about the divider
  764. // when over the filter glyph
  765. flags = HHT_ONFILTERBUTTON;
  766. break;
  767. }
  768. else
  769. {
  770. flags = HHT_ONFILTER;
  771. }
  772. }
  773. else if ( y < rcHeader.bottom )
  774. flags = HHT_ONHEADER;
  775. }
  776. else
  777. {
  778. flags = HHT_ONHEADER;
  779. }
  780. if (i > 0 && x < xPrev + cxSlop)
  781. {
  782. i--;
  783. flags = HHT_ONDIVIDER;
  784. if (fPrevZero && x > xPrev)
  785. {
  786. flags = HHT_ONDIVOPEN;
  787. }
  788. }
  789. else if (x >= xItem - cxSlop)
  790. {
  791. flags = HHT_ONDIVIDER;
  792. }
  793. break;
  794. }
  795. fPrevZero = FALSE;
  796. }
  797. if (i == cItems)
  798. {
  799. i = -1;
  800. flags = HHT_NOWHERE;
  801. } else {
  802. // now convert order index to real index
  803. i = Header_ItemOrderToIndex(phd, i);
  804. }
  805. }
  806. *pflags = flags;
  807. return i;
  808. }
  809. int Header_OnHitTest(HD* phd, HD_HITTESTINFO *phdht)
  810. {
  811. if (phdht && phd) {
  812. phdht->iItem = Header_HitTest(phd, phdht->pt.x, phdht->pt.y, &phdht->flags);
  813. return phdht->iItem;
  814. } else
  815. return -1;
  816. }
  817. BOOL Header_OnSetCursor(HD* phd, HWND hwndCursor, UINT codeHitTest, UINT msg)
  818. {
  819. POINT pt;
  820. UINT flags;
  821. LPCTSTR lpCur = MAKEINTRESOURCE(IDC_SIZEWE);
  822. HINSTANCE hinst = NULL;
  823. int iItem;
  824. int iDPI = CCGetScreenDPI();
  825. if (!phd)
  826. return FALSE;
  827. if (phd->ci.hwnd != hwndCursor || codeHitTest >= 0x8000)
  828. return FALSE;
  829. GetMessagePosClient(hwndCursor, &pt);
  830. iItem = Header_HitTest(phd, pt.x, pt.y, &flags);
  831. switch (flags)
  832. {
  833. case HHT_ONDIVIDER:
  834. if (iDPI <= 96)
  835. {
  836. lpCur = MAKEINTRESOURCE(IDC_DIVIDER);
  837. hinst = HINST_THISDLL;
  838. }
  839. break;
  840. case HHT_ONDIVOPEN:
  841. if (iDPI <= 96)
  842. {
  843. lpCur = MAKEINTRESOURCE(IDC_DIVOPEN);
  844. hinst = HINST_THISDLL;
  845. }
  846. break;
  847. case HHT_ONFILTER:
  848. {
  849. HDI* phdi = Header_GetItemPtrByOrder(phd, iItem);
  850. ASSERT(phdi);
  851. lpCur = IDC_ARROW; // default to the arrow
  852. switch ( phdi->type & HDFT_ISMASK )
  853. {
  854. case HDFT_ISSTRING:
  855. case HDFT_ISNUMBER:
  856. lpCur = IDC_IBEAM;
  857. break;
  858. default:
  859. // FEATURE: handle custom filters
  860. break;
  861. }
  862. break;
  863. }
  864. default:
  865. lpCur = IDC_ARROW;
  866. break;
  867. }
  868. SetCursor(LoadCursor(hinst, lpCur));
  869. return TRUE;
  870. }
  871. void Header_DrawDivider(HD* phd, int x)
  872. {
  873. RECT rc;
  874. HDC hdc = GetDC(phd->ci.hwnd);
  875. GetClientRect(phd->ci.hwnd, &rc);
  876. rc.left = x;
  877. rc.right = x + g_cxBorder;
  878. InvertRect(hdc, &rc);
  879. ReleaseDC(phd->ci.hwnd, hdc);
  880. }
  881. int Header_PinDividerPos(HD* phd, int x)
  882. {
  883. x += phd->dxTrack;
  884. if (x < phd->xMinTrack)
  885. x = phd->xMinTrack;
  886. return x;
  887. }
  888. void Header_OnLButtonDown(HD* phd, BOOL fDoubleClick, int x, int y, UINT keyFlags)
  889. {
  890. HD_ITEM hd;
  891. int i;
  892. UINT flags;
  893. if (!phd)
  894. return;
  895. Header_StopFilterEdit(phd, FALSE);
  896. i = Header_HitTest(phd, x, y, &flags);
  897. if (flags & (HHT_ONDIVIDER))
  898. {
  899. if (fDoubleClick) {
  900. Header_SendChange(phd, i, HDN_DIVIDERDBLCLICK, NULL);
  901. }
  902. }
  903. if ((flags & (HHT_ONDIVIDER | HHT_ONHEADER | HHT_ONDIVOPEN))
  904. && !fDoubleClick)
  905. {
  906. phd->iTrack = i;
  907. phd->flagsTrack = flags;
  908. phd->xTrack = x;
  909. SetCapture(phd->ci.hwnd);
  910. // this is just to get messages so we can
  911. // check for the escape key being hit
  912. SetTimer(phd->ci.hwnd, 1, 100, NULL);
  913. GetAsyncKeyState(VK_ESCAPE);
  914. }
  915. if (flags & (HHT_ONDIVIDER | HHT_ONDIVOPEN) &&
  916. !fDoubleClick)
  917. {
  918. //
  919. // We should first send out the HDN_BEGINTRACK notification
  920. //
  921. HDI * phdi;
  922. int iOrder = Header_OnGetItemOrder(phd, i);
  923. phdi = Header_GetItemPtr(phd, i);
  924. phd->xMinTrack = phdi->x - phdi->cxy;
  925. phd->xTrack = phdi->x;
  926. phd->dxTrack = phd->xTrack - x;
  927. phd->xTrackOldWidth = phdi->cxy;
  928. hd.mask = HDI_WIDTH;
  929. hd.cxy = phd->xTrackOldWidth;
  930. if (!Header_SendChange(phd, i, HDN_BEGINTRACK, &hd))
  931. {
  932. // They said no!
  933. phd->flagsTrack = 0;
  934. CCReleaseCapture(&phd->ci);
  935. KillTimer(phd->ci.hwnd, 1);
  936. return;
  937. }
  938. if (!HDDragFullWindows(phd)) {
  939. x = Header_PinDividerPos(phd, x);
  940. Header_DrawDivider(phd, x);
  941. }
  942. }
  943. else if ((flags & HHT_ONHEADER) && (phd->ci.style & HDS_BUTTONS))
  944. {
  945. if (fDoubleClick) {
  946. Header_SendChange(phd, i, HDN_ITEMDBLCLICK, NULL);
  947. } else {
  948. phd->bTrackPress = TRUE;
  949. Header_InvalidateItem(phd, phd->iTrack, RDW_INVALIDATE| RDW_ERASE);
  950. }
  951. }
  952. if ( flags & HHT_ONFILTER )
  953. {
  954. Header_BeginFilterEdit(phd, i);
  955. }
  956. if ( flags & HHT_ONFILTERBUTTON )
  957. {
  958. Header_OnFilterButton(phd, i);
  959. }
  960. }
  961. void Header_StartDrag(HD* phd, int i, int x, int y)
  962. {
  963. RECT rc;
  964. if ((phd->ci.style & HDS_DRAGDROP) &&
  965. Header_Notify(phd, i, MK_LBUTTON, HDN_BEGINDRAG)) {
  966. // clear the hot bit and
  967. // update before we do the BeginDrag so that the save bitmap won't
  968. // have the hot drawing on it.
  969. Header_SetHotItem(phd, -1);
  970. UpdateWindow(phd->ci.hwnd);
  971. phd->himlDrag = Header_OnCreateDragImage(phd, Header_OnGetItemOrder(phd,i));
  972. if (!phd->himlDrag)
  973. return;
  974. // find the delta between the start of the item and the cursor
  975. Header_OnGetItemRect(phd, i, &rc);
  976. phd->dxTrack = rc.left - x;
  977. ImageList_BeginDrag(phd->himlDrag, 0, 0, 0);
  978. ImageList_DragEnter(phd->ci.hwnd, x, 0);
  979. }
  980. }
  981. void Header_InvalidateDivider(HD* phd, int iItem)
  982. {
  983. RECT rc;
  984. Header_GetDividerRect(phd, iItem, &rc);
  985. InvalidateRect(phd->ci.hwnd, &rc, FALSE);
  986. }
  987. void _Header_SetHotDivider(HD* phd, int iNewOrder)
  988. {
  989. if (iNewOrder != phd->iNewOrder) {
  990. if (phd->himlDrag)
  991. ImageList_DragShowNolock(FALSE);
  992. Header_InvalidateDivider(phd, phd->iNewOrder);
  993. Header_InvalidateDivider(phd, iNewOrder);
  994. phd->iNewOrder = iNewOrder;
  995. UpdateWindow(phd->ci.hwnd);
  996. if (phd->himlDrag)
  997. ImageList_DragShowNolock(TRUE);
  998. }
  999. }
  1000. LPARAM Header_OnSetHotDivider(HD* phd, BOOL fPos, LPARAM lParam)
  1001. {
  1002. int iNewOrder = -1;
  1003. if (fPos) {
  1004. RECT rc;
  1005. int y = GET_Y_LPARAM(lParam);
  1006. int x = GET_X_LPARAM(lParam);
  1007. // this means that lParam is the cursor position (in client coordinates)
  1008. GetClientRect(phd->ci.hwnd, &rc);
  1009. InflateRect(&rc, 0, g_cyHScroll * 2);
  1010. // show only if the y point is reasonably close to the header
  1011. // (a la scrollbar)
  1012. if (y >= rc.top &&
  1013. y <= rc.bottom) {
  1014. //
  1015. // find out the new insertion point
  1016. //
  1017. if (x <= 0) {
  1018. iNewOrder = 0;
  1019. } else {
  1020. UINT flags;
  1021. int iIndex;
  1022. iIndex = Header_HitTest(phd, x, (rc.top + rc.bottom)/2, &flags);
  1023. // if we didn't find an item, see if it's on the far right
  1024. if (iIndex == -1) {
  1025. int iLast = Header_ItemOrderToIndex(phd, Header_GetCount(phd) -1);
  1026. if (Header_OnGetItemRect(phd, iLast, &rc)) {
  1027. if (x >= rc.right) {
  1028. iNewOrder = Header_GetCount(phd);
  1029. }
  1030. }
  1031. } else {
  1032. Header_OnGetItemRect(phd, iIndex, &rc);
  1033. iNewOrder= Header_OnGetItemOrder(phd, iIndex);
  1034. // if it was past the midpoint, the insertion point is the next one
  1035. if (x > ((rc.left + rc.right)/2)) {
  1036. // get the next item... translate to item order then back to index.
  1037. iNewOrder++;
  1038. }
  1039. }
  1040. }
  1041. }
  1042. } else {
  1043. iNewOrder = (int)lParam;
  1044. }
  1045. _Header_SetHotDivider(phd, iNewOrder);
  1046. return iNewOrder;
  1047. }
  1048. void Header_MoveDrag(HD* phd, int x, int y)
  1049. {
  1050. LPARAM iNewOrder = -1;
  1051. iNewOrder = Header_OnSetHotDivider(phd, TRUE, MAKELONG(x, y));
  1052. if (iNewOrder == -1) {
  1053. ImageList_DragShowNolock(FALSE);
  1054. } else {
  1055. ImageList_DragShowNolock(TRUE);
  1056. ImageList_DragMove(x + phd->dxTrack, 0);
  1057. }
  1058. }
  1059. void Header_EndDrag(HD* phd)
  1060. {
  1061. ImageList_EndDrag();
  1062. ImageList_Destroy(phd->himlDrag);
  1063. phd->himlDrag = NULL;
  1064. _Header_SetHotDivider(phd, -1);
  1065. }
  1066. // iOrder
  1067. void Header_GetDividerRect(HD* phd, int iOrder, LPRECT prc)
  1068. {
  1069. int iIndex;
  1070. BOOL fLeft;
  1071. if (iOrder == -1)
  1072. {
  1073. SetRectEmpty(prc);
  1074. return;
  1075. }
  1076. // if we're getting the divider slot of < N then
  1077. // it's the left of the rect of item i.
  1078. // otherwise it's the right of the last item.
  1079. if (iOrder < Header_GetCount(phd)) {
  1080. fLeft = TRUE;
  1081. } else {
  1082. fLeft = FALSE;
  1083. iOrder--;
  1084. }
  1085. iIndex = Header_ItemOrderToIndex(phd, iOrder);
  1086. Header_OnGetItemRect(phd, iIndex, prc);
  1087. if (fLeft) {
  1088. prc->right = prc->left;
  1089. } else {
  1090. prc->left = prc->right;
  1091. }
  1092. InflateRect(prc, g_cxBorder, 0);
  1093. }
  1094. void Header_OnMouseMove(HD* phd, int x, int y, UINT keyFlags)
  1095. {
  1096. UINT flags;
  1097. int i;
  1098. HD_ITEM hd;
  1099. if (!phd)
  1100. return;
  1101. // do the hot tracking
  1102. // but not if anything is ownerdraw or if we're in d/d mode
  1103. if ((phd->hTheme || phd->ci.style & HDS_HOTTRACK) && !phd->fOwnerDraw && !phd->himlDrag) {
  1104. // only do this if we're in button mode meaning you can actually click
  1105. if (phd->ci.style & HDS_BUTTONS) {
  1106. i = Header_HitTest(phd, x, y, &flags);
  1107. Header_SetHotItem(phd, i);
  1108. }
  1109. }
  1110. if (Header_IsTracking(phd))
  1111. {
  1112. if (phd->flagsTrack & (HHT_ONDIVIDER | HHT_ONDIVOPEN))
  1113. {
  1114. x = Header_PinDividerPos(phd, x);
  1115. //
  1116. // Let the Owner have a chance to update this.
  1117. //
  1118. hd.mask = HDI_WIDTH;
  1119. hd.cxy = x - phd->xMinTrack;
  1120. if (!HDDragFullWindows(phd) && !Header_SendChange(phd, phd->iTrack, HDN_TRACK, &hd))
  1121. {
  1122. // We need to cancel tracking
  1123. phd->flagsTrack = 0;
  1124. CCReleaseCapture(&phd->ci);
  1125. KillTimer(phd->ci.hwnd, 1);
  1126. // Undraw the last divider we displayed
  1127. Header_DrawDivider(phd, phd->xTrack);
  1128. return;
  1129. }
  1130. // We should update our x depending on what caller did
  1131. x = hd.cxy + phd->xMinTrack;
  1132. // if full window track is turned on, go ahead and set the width
  1133. if (HDDragFullWindows(phd)) {
  1134. HD_ITEM item;
  1135. item.mask = HDI_WIDTH;
  1136. item.cxy = hd.cxy;
  1137. DebugMsg(DM_TRACE, TEXT("Tracking header. item %d gets width %d... %d %d"), phd->iTrack, item.cxy, phd->xMinTrack, x);
  1138. // Let the owner have a chance to say yes.
  1139. Header_OnSetItem(phd, phd->iTrack, &item);
  1140. UpdateWindow(phd->ci.hwnd);
  1141. } else {
  1142. // do the cheezy old stuff
  1143. Header_DrawDivider(phd, phd->xTrack);
  1144. Header_DrawDivider(phd, x);
  1145. }
  1146. phd->xTrack = x;
  1147. }
  1148. else if (phd->flagsTrack & HHT_ONHEADER)
  1149. {
  1150. i = Header_HitTest(phd, x, y, &flags);
  1151. if (ABS(x - phd->xTrack) >
  1152. GetSystemMetrics(SM_CXDRAG)) {
  1153. if (!phd->himlDrag) {
  1154. Header_StartDrag(phd, i, phd->xTrack, y);
  1155. }
  1156. }
  1157. if (phd->himlDrag) {
  1158. Header_MoveDrag(phd, x, y);
  1159. } else {
  1160. // if pressing on button and it's not pressed, press it
  1161. if (flags & HHT_ONHEADER && i == phd->iTrack)
  1162. {
  1163. if ((!phd->bTrackPress) && (phd->ci.style & HDS_BUTTONS))
  1164. {
  1165. phd->bTrackPress = TRUE;
  1166. Header_InvalidateItem(phd, phd->iTrack, RDW_INVALIDATE| RDW_ERASE);
  1167. }
  1168. }
  1169. // tracked off of button. if pressed, pop it
  1170. else if ((phd->bTrackPress) && (phd->ci.style & HDS_BUTTONS))
  1171. {
  1172. phd->bTrackPress = FALSE;
  1173. Header_InvalidateItem(phd, phd->iTrack, RDW_INVALIDATE| RDW_ERASE);
  1174. }
  1175. }
  1176. }
  1177. }
  1178. }
  1179. void Header_OnLButtonUp(HD* phd, int x, int y, UINT keyFlags)
  1180. {
  1181. if (!phd)
  1182. return;
  1183. if (Header_IsTracking(phd))
  1184. {
  1185. if (phd->flagsTrack & (HHT_ONDIVIDER | HHT_ONDIVOPEN))
  1186. {
  1187. HD_ITEM item;
  1188. if (!HDDragFullWindows(phd)) {
  1189. Header_DrawDivider(phd, phd->xTrack);
  1190. }
  1191. item.mask = HDI_WIDTH;
  1192. item.cxy = phd->xTrack - phd->xMinTrack;
  1193. // Let the owner have a chance to say yes.
  1194. if (Header_SendChange(phd, phd->iTrack, HDN_ENDTRACK, &item))
  1195. Header_OnSetItem(phd, phd->iTrack, &item);
  1196. RedrawWindow(phd->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  1197. }
  1198. else if ((phd->flagsTrack & HHT_ONHEADER)
  1199. && (phd->bTrackPress || phd->himlDrag))
  1200. {
  1201. if (phd->himlDrag) {
  1202. // if this is the end of a drag,
  1203. // notify the user.
  1204. HDITEM item;
  1205. item.mask = HDI_ORDER;
  1206. item.iOrder = phd->iNewOrder;
  1207. if (item.iOrder > Header_OnGetItemOrder(phd, phd->iTrack)) {
  1208. // if the new order is greater than the old one,
  1209. // we subtract one because it's leaving the old place
  1210. // which decs the count by one.
  1211. item.iOrder--;
  1212. }
  1213. Header_EndDrag(phd);
  1214. if (Header_SendChange(phd, phd->iTrack, HDN_ENDDRAG, &item)) {
  1215. if (item.iOrder != -1) {
  1216. // all's well... change the item order
  1217. Header_OnSetItemOrder(phd, phd->iTrack, item.iOrder);
  1218. NotifyWinEvent(EVENT_OBJECT_REORDER, phd->ci.hwnd, OBJID_CLIENT, 0);
  1219. }
  1220. }
  1221. } else {
  1222. // Notify the owner that the item has been clicked
  1223. Header_Notify(phd, phd->iTrack, 0, HDN_ITEMCLICK);
  1224. }
  1225. phd->bTrackPress = FALSE;
  1226. Header_InvalidateItem(phd, phd->iTrack, RDW_INVALIDATE| RDW_ERASE);
  1227. }
  1228. phd->flagsTrack = 0;
  1229. CCReleaseCapture(&phd->ci);
  1230. KillTimer(phd->ci.hwnd, 1);
  1231. }
  1232. }
  1233. BOOL Header_IsTracking(HD* phd)
  1234. {
  1235. if (!phd->flagsTrack)
  1236. {
  1237. return FALSE;
  1238. } else if (GetCapture() != phd->ci.hwnd) {
  1239. phd->flagsTrack = 0;
  1240. return FALSE;
  1241. }
  1242. return TRUE;
  1243. }
  1244. void Header_OnSetFont(HD* phd, HFONT hfont, BOOL fRedraw)
  1245. {
  1246. if (!phd)
  1247. return;
  1248. if (hfont != phd->hfont)
  1249. {
  1250. Header_NewFont(phd, hfont);
  1251. if (fRedraw)
  1252. RedrawWindow(phd->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  1253. }
  1254. }
  1255. HFONT Header_OnGetFont(HD* phd)
  1256. {
  1257. if (!phd)
  1258. return NULL;
  1259. return phd->hfont;
  1260. }
  1261. //**********************************************************************
  1262. int Header_OnInsertItemA(HD* phd, int i, HD_ITEMA* pitem) {
  1263. LPWSTR pszW = NULL;
  1264. LPSTR pszC = NULL;
  1265. HD_TEXTFILTERW textFilterW;
  1266. LPHD_TEXTFILTERA ptextFilterA = NULL;
  1267. int iRet;
  1268. //HACK ALERT -- this code assumes that HD_ITEMA is exactly the same
  1269. // as HD_ITEMW except for the pointer to the string.
  1270. ASSERT(sizeof(HD_ITEMA) == sizeof(HD_ITEMW))
  1271. if (!pitem || !phd)
  1272. return -1;
  1273. if ((pitem->mask & HDI_TEXT) && (pitem->pszText != LPSTR_TEXTCALLBACKA) &&
  1274. (pitem->pszText != NULL)) {
  1275. pszC = pitem->pszText;
  1276. if ((pszW = ProduceWFromA(phd->ci.uiCodePage, pszC)) == NULL)
  1277. return -1;
  1278. pitem->pszText = (LPSTR)pszW;
  1279. }
  1280. if ( (pitem->mask & HDI_FILTER) &&
  1281. ((pitem->type & HDFT_ISMASK) == HDFT_ISSTRING) ) {
  1282. // pick up the filter if there is one for us to thunk
  1283. if ( pitem->pvFilter ) {
  1284. ptextFilterA = pitem->pvFilter;
  1285. ASSERT(ptextFilterA);
  1286. textFilterW.pszText = NULL;
  1287. textFilterW.cchTextMax = ptextFilterA->cchTextMax;
  1288. if ( !(pitem->type & HDFT_HASNOVALUE) ) {
  1289. textFilterW.pszText = ProduceWFromA(phd->ci.uiCodePage, ptextFilterA->pszText);
  1290. if ( !textFilterW.pszText ) {
  1291. if ( pszW )
  1292. FreeProducedString(pszW)
  1293. return -1;
  1294. }
  1295. }
  1296. pitem->pvFilter = &textFilterW;
  1297. }
  1298. }
  1299. iRet = Header_OnInsertItem(phd, i, (const HD_ITEM*) pitem);
  1300. if (pszW != NULL) {
  1301. pitem->pszText = pszC;
  1302. FreeProducedString(pszW);
  1303. }
  1304. if (ptextFilterA)
  1305. {
  1306. pitem->pvFilter = ptextFilterA;
  1307. FreeProducedString(textFilterW.pszText);
  1308. }
  1309. return iRet;
  1310. }
  1311. int Header_OnInsertItem(HD* phd, int i, const HD_ITEM* pitem)
  1312. {
  1313. HDI hdi = {0};
  1314. int x;
  1315. HDI* phdi;
  1316. int iOrder;
  1317. int cxy;
  1318. if (!pitem || !phd || i < 0)
  1319. return -1;
  1320. if (pitem->mask == 0)
  1321. return -1;
  1322. if (!DSA_ForceGrow(phd->hdsaHDI, 1))
  1323. {
  1324. return -1;
  1325. }
  1326. if (phd->hdsaOrder && !DSA_ForceGrow(phd->hdsaOrder, 1))
  1327. {
  1328. return -1;
  1329. }
  1330. cxy = pitem->cxy;
  1331. if (cxy < 0)
  1332. cxy = 0;
  1333. x = cxy;
  1334. if (i > DSA_GetItemCount(phd->hdsaHDI))
  1335. i = DSA_GetItemCount(phd->hdsaHDI);
  1336. // stop editing the filter
  1337. Header_StopFilterEdit(phd, FALSE);
  1338. iOrder = i;
  1339. // can't have order info if it's owner data
  1340. if (!(phd->ci.style & HDS_OWNERDATA))
  1341. {
  1342. // the iOrder field wasn't there in win95...
  1343. // so access it only if the bit is there.
  1344. if (pitem->mask & HDI_ORDER)
  1345. {
  1346. if ((pitem->iOrder != i) && (pitem->iOrder <= Header_GetCount(phd)))
  1347. {
  1348. if (Header_InitOrderArray(phd))
  1349. iOrder = pitem->iOrder;
  1350. }
  1351. }
  1352. }
  1353. if (iOrder > 0)
  1354. {
  1355. phdi = Header_GetItemPtrByOrder(phd, iOrder - 1);
  1356. if (phdi)
  1357. x += phdi->x;
  1358. }
  1359. // move everything else over
  1360. Header_ShiftItems(phd, iOrder, cxy);
  1361. if (phd->hdsaOrder)
  1362. {
  1363. int j;
  1364. int iIndex = -1;
  1365. // an index is added, all the current indices
  1366. // need to be incr by one
  1367. for (j = 0; j < DSA_GetItemCount(phd->hdsaOrder); j++)
  1368. {
  1369. DSA_GetItem(phd->hdsaOrder, j, &iIndex);
  1370. if (iIndex >= i)
  1371. {
  1372. iIndex++;
  1373. DSA_SetItem(phd->hdsaOrder, j, &iIndex);
  1374. }
  1375. }
  1376. DSA_InsertItem(phd->hdsaOrder, iOrder, &i);
  1377. }
  1378. hdi.x = x;
  1379. hdi.lParam = pitem->lParam;
  1380. hdi.fmt = pitem->fmt;
  1381. //hdi.pszText = NULL;
  1382. //hdi.iImage = 0;
  1383. hdi.cxy = cxy;
  1384. hdi.xText = hdi.xBm = RECOMPUTE;
  1385. hdi.type = HDFT_ISSTRING|HDFT_HASNOVALUE;
  1386. //hdi.textFilter.pszText = NULL;
  1387. hdi.textFilter.cchTextMax = MAX_PATH;
  1388. if ((pitem->mask & HDI_TEXT) && (pitem->pszText != NULL))
  1389. {
  1390. if (!Str_Set(&hdi.pszText, pitem->pszText))
  1391. return -1;
  1392. // Unless ownerdraw make sure the text bit is on!
  1393. if ((pitem->mask & HDF_OWNERDRAW) == 0)
  1394. hdi.fmt |= HDF_STRING;
  1395. }
  1396. else
  1397. {
  1398. hdi.fmt &= ~(HDF_STRING);
  1399. }
  1400. if ((pitem->mask & HDI_BITMAP) && (pitem->hbm != NULL))
  1401. {
  1402. hdi.hbm = pitem->hbm;
  1403. // Unless ownerdraw make sure the text bit is on!
  1404. if ((pitem->mask & HDF_OWNERDRAW) == 0)
  1405. hdi.fmt |= HDF_BITMAP;
  1406. }
  1407. else
  1408. {
  1409. hdi.hbm = NULL;
  1410. hdi.fmt &= ~(HDF_BITMAP);
  1411. }
  1412. if (pitem->mask & HDI_IMAGE)
  1413. {
  1414. hdi.iImage = pitem->iImage;
  1415. // Unless ownerdraw make sure the image bit is on!
  1416. if ((pitem->mask & HDF_OWNERDRAW) == 0)
  1417. hdi.fmt |= HDF_IMAGE;
  1418. }
  1419. if ( pitem->mask & HDI_FILTER )
  1420. {
  1421. // pick up the new filter, handling the case where the filter value is
  1422. // being discarded, and/or there is none
  1423. hdi.type = pitem->type;
  1424. switch ( hdi.type & HDFT_ISMASK )
  1425. {
  1426. case HDFT_ISSTRING:
  1427. {
  1428. if ( pitem->pvFilter )
  1429. {
  1430. LPHDTEXTFILTER ptextFilter = (LPHDTEXTFILTER)pitem->pvFilter;
  1431. ASSERT(ptextFilter);
  1432. if ( !(pitem->type & HDFT_HASNOVALUE) )
  1433. Str_Set(&hdi.textFilter.pszText, ptextFilter->pszText);
  1434. hdi.textFilter.cchTextMax = ptextFilter->cchTextMax;
  1435. }
  1436. break;
  1437. }
  1438. case HDFT_ISNUMBER:
  1439. {
  1440. if ( !(pitem->type & HDFT_HASNOVALUE) && pitem->pvFilter )
  1441. hdi.intFilter = *((int*)pitem->pvFilter);
  1442. break;
  1443. }
  1444. }
  1445. }
  1446. i = DSA_InsertItem(phd->hdsaHDI, i, &hdi);
  1447. if (i == -1)
  1448. {
  1449. // failed to add
  1450. Str_Set(&hdi.pszText, NULL);
  1451. if ( (hdi.type & HDFT_ISMASK) == HDFT_ISSTRING )
  1452. Str_Set(&hdi.textFilter.pszText, NULL);
  1453. }
  1454. else
  1455. {
  1456. RECT rc;
  1457. // succeeded! redraw
  1458. GetClientRect(phd->ci.hwnd, &rc);
  1459. rc.left = x - cxy;
  1460. RedrawWindow(phd->ci.hwnd, &rc, NULL, RDW_INVALIDATE | RDW_ERASE);
  1461. NotifyWinEvent(EVENT_OBJECT_CREATE, phd->ci.hwnd, OBJID_CLIENT, i+1);
  1462. }
  1463. return i;
  1464. }
  1465. BOOL Header_OnDeleteItem(HD* phd, int i)
  1466. {
  1467. HDI hdi;
  1468. RECT rc;
  1469. int iWidth;
  1470. int iOrder;
  1471. if (!phd)
  1472. return FALSE;
  1473. if (!DSA_GetItem(phd->hdsaHDI, i, &hdi))
  1474. return FALSE;
  1475. NotifyWinEvent(EVENT_OBJECT_DESTROY, phd->ci.hwnd, OBJID_CLIENT, i+1);
  1476. Header_StopFilterEdit(phd, FALSE);
  1477. phd->iFocus = 0;
  1478. GetClientRect(phd->ci.hwnd, &rc);
  1479. iWidth = rc.right;
  1480. Header_OnGetItemRect(phd, i, &rc);
  1481. InflateRect(&rc, g_cxBorder, g_cyBorder);
  1482. // move everything else over
  1483. iOrder = Header_OnGetItemOrder(phd, i);
  1484. Header_ShiftItems(phd, iOrder, -hdi.cxy);
  1485. if (!DSA_DeleteItem(phd->hdsaHDI, i))
  1486. return FALSE;
  1487. if (phd->hdsaOrder) {
  1488. int j;
  1489. int iIndex;
  1490. DSA_DeleteItem(phd->hdsaOrder, iOrder);
  1491. // an index is going away, all the current indices
  1492. // need to be decremented by one
  1493. for (j = 0; j < DSA_GetItemCount(phd->hdsaOrder); j++) {
  1494. DSA_GetItem(phd->hdsaOrder, j, &iIndex);
  1495. ASSERT(iIndex != i);
  1496. if (iIndex > i) {
  1497. iIndex--;
  1498. DSA_SetItem(phd->hdsaOrder, j, &iIndex);
  1499. }
  1500. }
  1501. }
  1502. Header_DestroyItemCallback(&hdi, NULL);
  1503. rc.right = iWidth;
  1504. InvalidateRect(phd->ci.hwnd, &rc, TRUE);
  1505. return TRUE;
  1506. }
  1507. BOOL Header_OnGetItemA(HD* phd, int i, HD_ITEMA* pitem) {
  1508. LPWSTR pszW = NULL;
  1509. LPSTR pszC = NULL;
  1510. HD_TEXTFILTERW textFilterW;
  1511. LPHD_TEXTFILTERA ptextFilterA = NULL;
  1512. BOOL fRet;
  1513. //HACK ALERT -- this code assumes that HD_ITEMA is exactly the same
  1514. // as HD_ITEMW except for the pointer to the string.
  1515. ASSERT(sizeof(HD_ITEMA) == sizeof(HD_ITEMW))
  1516. if (!pitem || !phd)
  1517. return FALSE;
  1518. if ((pitem->mask & HDI_TEXT) && (pitem->pszText != LPSTR_TEXTCALLBACKA) &&
  1519. (pitem->pszText != NULL)) {
  1520. pszC = pitem->pszText;
  1521. pszW = LocalAlloc(LMEM_FIXED, pitem->cchTextMax * sizeof(WCHAR));
  1522. if (pszW == NULL)
  1523. return FALSE;
  1524. pitem->pszText = (LPSTR)pszW;
  1525. }
  1526. if ( (pitem->mask & HDI_FILTER) &&
  1527. ((pitem->type & HDFT_ISMASK)==HDFT_ISSTRING) ) {
  1528. if ( pitem->pvFilter ) {
  1529. ptextFilterA = pitem->pvFilter;
  1530. ASSERT(ptextFilterA);
  1531. textFilterW.pszText = LocalAlloc(LMEM_FIXED, ptextFilterA->cchTextMax * sizeof(WCHAR));
  1532. textFilterW.cchTextMax = ptextFilterA->cchTextMax;
  1533. if ( !textFilterW.pszText ) {
  1534. if ( pszW )
  1535. LocalFree(pszW);
  1536. return FALSE;
  1537. }
  1538. pitem->pvFilter = &textFilterW;
  1539. }
  1540. }
  1541. fRet = Header_OnGetItem(phd, i, (HD_ITEM *) pitem);
  1542. if (pszW != NULL) {
  1543. ConvertWToAN(phd->ci.uiCodePage, pszC, pitem->cchTextMax, pszW, -1);
  1544. pitem->pszText = pszC;
  1545. LocalFree(pszW);
  1546. }
  1547. if (ptextFilterA)
  1548. {
  1549. ConvertWToAN(phd->ci.uiCodePage, ptextFilterA->pszText, ptextFilterA->cchTextMax,
  1550. textFilterW.pszText, -1);
  1551. pitem->pvFilter = ptextFilterA;
  1552. }
  1553. return fRet;
  1554. }
  1555. BOOL Header_OnGetItem(HD* phd, int i, HD_ITEM* pitem)
  1556. {
  1557. HDI* phdi;
  1558. UINT mask;
  1559. NMHDDISPINFO nm;
  1560. ASSERT(pitem);
  1561. if (!pitem || !phd)
  1562. return FALSE;
  1563. // Crappy hack to fix norton commander. MFC has a bug where it
  1564. // passes in stack trash (in addition to the desired bits) to HDM_GETITEM.
  1565. // Fix it here by stripping down to Win95 bits if more bits than the
  1566. // current valid bits are defined.
  1567. if (pitem->mask & ~HDI_ALL)
  1568. pitem->mask &= HDI_ALL95;
  1569. nm.mask = 0;
  1570. mask = pitem->mask;
  1571. #ifdef DEBUG
  1572. if (i < 0 || i >= Header_GetCount(phd))
  1573. {
  1574. RIPMSG(0, "HDM_GETITEM: Invalid item number %d", i);
  1575. return FALSE; // Return immediately so Header_GetItemPtr doesn't assert
  1576. }
  1577. #endif
  1578. phdi = Header_GetItemPtr(phd, i);
  1579. if (!phdi)
  1580. return FALSE;
  1581. if (mask & HDI_WIDTH)
  1582. {
  1583. pitem->cxy = phdi->cxy;
  1584. }
  1585. if (mask & HDI_FORMAT)
  1586. {
  1587. pitem->fmt = phdi->fmt;
  1588. }
  1589. if (mask & HDI_ORDER)
  1590. {
  1591. pitem->iOrder = Header_OnGetItemOrder(phd, i);
  1592. }
  1593. if (mask & HDI_LPARAM)
  1594. {
  1595. pitem->lParam = phdi->lParam;
  1596. }
  1597. if (mask & HDI_TEXT)
  1598. {
  1599. if (phdi->pszText != LPSTR_TEXTCALLBACK) {
  1600. // if pszText was NULL and you tried to retrieve it, we would bail
  1601. // and return FALSE, now we may return TRUE.
  1602. Str_GetPtr0(phdi->pszText, pitem->pszText, pitem->cchTextMax);
  1603. }
  1604. else {
  1605. // need to recalc the xText because they could keep changing it on us
  1606. phdi->xText = RECOMPUTE;
  1607. nm.mask |= HDI_TEXT;
  1608. }
  1609. }
  1610. if (mask & HDI_BITMAP)
  1611. pitem->hbm = phdi->hbm;
  1612. if (mask & HDI_IMAGE)
  1613. {
  1614. if (phdi->iImage == I_IMAGECALLBACK)
  1615. nm.mask |= HDI_IMAGE;
  1616. else
  1617. pitem->iImage = phdi->iImage;
  1618. }
  1619. if (mask & HDI_FILTER)
  1620. {
  1621. if (pitem->pvFilter)
  1622. {
  1623. if ((phdi->type & HDFT_ISMASK) != (pitem->type & HDFT_ISMASK))
  1624. return FALSE;
  1625. switch (phdi->type & HDFT_ISMASK)
  1626. {
  1627. case HDFT_ISSTRING:
  1628. {
  1629. LPHDTEXTFILTER ptextFilter = (LPHDTEXTFILTER)pitem->pvFilter;
  1630. ASSERT(ptextFilter);
  1631. if ( !Str_GetPtr(phdi->textFilter.pszText, ptextFilter->pszText, ptextFilter->cchTextMax) )
  1632. return FALSE;
  1633. ptextFilter->cchTextMax = phdi->textFilter.cchTextMax;
  1634. break;
  1635. }
  1636. case HDFT_ISNUMBER:
  1637. {
  1638. *((int*)pitem->pvFilter) = phdi->intFilter;
  1639. break;
  1640. }
  1641. default:
  1642. return FALSE;
  1643. }
  1644. }
  1645. pitem->type = phdi->type;
  1646. }
  1647. if (nm.mask) {
  1648. // just in case HDI_IMAGE is set and callback doesn't fill it in
  1649. // ... we'd rather have a -1 than watever garbage is on the stack
  1650. nm.iImage = -1;
  1651. nm.lParam = phdi->lParam;
  1652. if (nm.mask & HDI_TEXT) {
  1653. ASSERT(pitem->pszText);
  1654. nm.pszText = pitem->pszText;
  1655. nm.cchTextMax = pitem->cchTextMax;
  1656. // Make sure the buffer is zero terminated...
  1657. if (nm.cchTextMax)
  1658. *nm.pszText = 0;
  1659. }
  1660. CCSendNotify(&phd->ci, HDN_GETDISPINFO, &nm.hdr);
  1661. if (nm.mask & HDI_IMAGE)
  1662. pitem->iImage = nm.iImage;
  1663. if (nm.mask & HDI_TEXT)
  1664. pitem->pszText = CCReturnDispInfoText(nm.pszText, pitem->pszText, pitem->cchTextMax);
  1665. }
  1666. if (phdi && (nm.mask & HDI_DI_SETITEM)) {
  1667. if (nm.mask & HDI_IMAGE)
  1668. phdi->iImage = nm.iImage;
  1669. if (nm.mask & HDI_TEXT)
  1670. if (nm.pszText) {
  1671. ASSERT(phdi->pszText == LPSTR_TEXTCALLBACK);
  1672. Str_Set(&phdi->pszText, nm.pszText);
  1673. }
  1674. }
  1675. pitem->mask = mask;
  1676. return TRUE;
  1677. }
  1678. BOOL Header_OnSetItemA(HD* phd, int i, HD_ITEMA* pitem) {
  1679. LPWSTR pszW = NULL;
  1680. LPSTR pszC = NULL;
  1681. HD_TEXTFILTERW textFilterW;
  1682. LPHD_TEXTFILTERA ptextFilterA = NULL;
  1683. BOOL fRet;
  1684. //HACK ALERT -- this code assumes that HD_ITEMA is exactly the same
  1685. // as HD_ITEMW except for the pointer to the string.
  1686. ASSERT(sizeof(HD_ITEMA) == sizeof(HD_ITEMW));
  1687. if (!pitem || !phd)
  1688. return FALSE;
  1689. if ((pitem->mask & HDI_TEXT) && (pitem->pszText != LPSTR_TEXTCALLBACKA) &&
  1690. (pitem->pszText != NULL)) {
  1691. pszC = pitem->pszText;
  1692. if ((pszW = ProduceWFromA(phd->ci.uiCodePage, pszC)) == NULL)
  1693. return FALSE;
  1694. pitem->pszText = (LPSTR)pszW;
  1695. }
  1696. if ( (pitem->mask & HDI_FILTER) &&
  1697. ((pitem->type & HDFT_ISMASK) == HDFT_ISSTRING) )
  1698. {
  1699. if ( pitem->pvFilter )
  1700. {
  1701. ptextFilterA = pitem->pvFilter;
  1702. ASSERT(ptextFilterA);
  1703. textFilterW.pszText = NULL;
  1704. textFilterW.cchTextMax = ptextFilterA->cchTextMax;
  1705. if ( !(pitem->type & HDFT_HASNOVALUE) )
  1706. {
  1707. textFilterW.pszText = ProduceWFromA(phd->ci.uiCodePage, ptextFilterA->pszText);
  1708. if ( !textFilterW.pszText ) {
  1709. if ( pszW )
  1710. FreeProducedString(pszW)
  1711. return FALSE;
  1712. }
  1713. }
  1714. pitem->pvFilter = &textFilterW;
  1715. }
  1716. }
  1717. fRet = Header_OnSetItem(phd, i, (const HD_ITEM*) pitem);
  1718. if (pszW != NULL) {
  1719. pitem->pszText = pszC;
  1720. FreeProducedString(pszW);
  1721. }
  1722. if (ptextFilterA)
  1723. {
  1724. pitem->pvFilter = ptextFilterA;
  1725. FreeProducedString(textFilterW.pszText);
  1726. }
  1727. return fRet;
  1728. }
  1729. BOOL Header_OnSetItem(HD* phd, int i, const HD_ITEM* pitem)
  1730. {
  1731. HDI* phdi;
  1732. UINT mask;
  1733. int xOld;
  1734. BOOL fInvalidate = FALSE;
  1735. ASSERT(pitem);
  1736. if (!pitem || !phd)
  1737. return FALSE;
  1738. #ifdef DEBUG
  1739. if (i < 0 || i >= Header_GetCount(phd))
  1740. {
  1741. RIPMSG(0, "HDM_SETITEM: Invalid item number %d", i);
  1742. return FALSE; // Return immediately so Header_GetItemPtr doesn't assert
  1743. }
  1744. #endif
  1745. phdi = Header_GetItemPtr(phd, i);
  1746. if (!phdi)
  1747. return FALSE;
  1748. mask = pitem->mask;
  1749. if (mask == 0)
  1750. return TRUE;
  1751. // stop editing the filter
  1752. //Header_StopFilterEdit(phd, FALSE);
  1753. if (!Header_SendChange(phd, i, HDN_ITEMCHANGING, pitem))
  1754. return FALSE;
  1755. xOld = phdi->x;
  1756. if (mask & HDI_WIDTH)
  1757. {
  1758. RECT rcClip;
  1759. int iOrder;
  1760. int dx;
  1761. int cxy = pitem->cxy;
  1762. if (cxy < 0)
  1763. cxy = 0;
  1764. DebugMsg(DM_TRACE, TEXT("Header--SetWidth x=%d, cxyOld=%d, cxyNew=%d, dx=%d"),
  1765. phdi->x, phdi->cxy, cxy, (cxy-phdi->cxy));
  1766. dx = cxy - phdi->cxy;
  1767. phdi->cxy = cxy;
  1768. // scroll everything over
  1769. GetClientRect(phd->ci.hwnd, &rcClip);
  1770. rcClip.left = phdi->x; // we want to scroll the divider as well
  1771. // the scrolling rect needs to be the largest rect of the before
  1772. // and after. so if dx is negative, we want to enlarge the rect
  1773. if (dx < 0)
  1774. rcClip.left += dx;
  1775. iOrder = Header_OnGetItemOrder(phd, i);
  1776. Header_ShiftItems(phd, iOrder, dx);
  1777. phdi->xText = phdi->xBm = RECOMPUTE;
  1778. {
  1779. SMOOTHSCROLLINFO si = {
  1780. sizeof(si),
  1781. 0,
  1782. phd->ci.hwnd,
  1783. dx,
  1784. 0,
  1785. NULL,
  1786. &rcClip,
  1787. NULL,
  1788. NULL,
  1789. SW_ERASE | SW_INVALIDATE,
  1790. };
  1791. SmoothScrollWindow(&si);
  1792. }
  1793. UpdateWindow(phd->ci.hwnd);
  1794. // now invalidate this item itself
  1795. Header_OnGetItemRect(phd, i, &rcClip);
  1796. InvalidateRect(phd->ci.hwnd, &rcClip, TRUE);
  1797. }
  1798. if (mask & HDI_FORMAT) {
  1799. phdi->fmt = pitem->fmt;
  1800. phdi->xText = phdi->xBm = RECOMPUTE;
  1801. fInvalidate = TRUE;
  1802. }
  1803. if (mask & HDI_LPARAM)
  1804. phdi->lParam = pitem->lParam;
  1805. if (mask & HDI_TEXT)
  1806. {
  1807. if (!Str_Set(&phdi->pszText, pitem->pszText))
  1808. return FALSE;
  1809. phdi->xText = RECOMPUTE;
  1810. fInvalidate = TRUE;
  1811. }
  1812. if (mask & HDI_BITMAP)
  1813. {
  1814. phdi->hbm = pitem->hbm;
  1815. phdi->xBm = RECOMPUTE;
  1816. fInvalidate = TRUE;
  1817. }
  1818. if (mask & HDI_IMAGE)
  1819. {
  1820. phdi->iImage = pitem->iImage;
  1821. phdi->xBm = RECOMPUTE;
  1822. fInvalidate = TRUE;
  1823. }
  1824. if (mask & HDI_ORDER)
  1825. {
  1826. if (pitem->iOrder >= 0 && pitem->iOrder < Header_GetCount(phd))
  1827. {
  1828. Header_OnSetItemOrder(phd, i, pitem->iOrder);
  1829. NotifyWinEvent(EVENT_OBJECT_REORDER, phd->ci.hwnd, OBJID_CLIENT, 0);
  1830. }
  1831. }
  1832. if ( mask & HDI_FILTER )
  1833. {
  1834. if ( (phdi->type & HDFT_ISMASK) == HDFT_ISSTRING )
  1835. Str_Set(&phdi->textFilter.pszText, NULL);
  1836. // pick up the new filter, handling the case where the filter value is
  1837. // being discarded, and/or there is none
  1838. phdi->type = pitem->type;
  1839. switch ( phdi->type & HDFT_ISMASK )
  1840. {
  1841. case HDFT_ISSTRING:
  1842. {
  1843. if ( pitem->pvFilter )
  1844. {
  1845. LPHDTEXTFILTER ptextFilter = (LPHDTEXTFILTER)pitem->pvFilter;
  1846. ASSERT(ptextFilter);
  1847. if ( !(pitem->type & HDFT_HASNOVALUE) )
  1848. Str_Set(&phdi->textFilter.pszText, ptextFilter->pszText);
  1849. phdi->textFilter.cchTextMax = ptextFilter->cchTextMax;
  1850. }
  1851. break;
  1852. }
  1853. case HDFT_ISNUMBER:
  1854. {
  1855. if ( !(pitem->type & HDFT_HASNOVALUE) && pitem->pvFilter )
  1856. phdi->intFilter = *((int*)pitem->pvFilter);
  1857. break;
  1858. }
  1859. }
  1860. fInvalidate = TRUE;
  1861. }
  1862. Header_SendChange(phd, i, HDN_ITEMCHANGED, pitem);
  1863. if ( mask & HDI_FILTER )
  1864. Header_Notify(phd, i, 0, HDN_FILTERCHANGE); // send out a notify of change
  1865. if (fInvalidate) {
  1866. if (xOld == phdi->x) {
  1867. // no change in x
  1868. Header_InvalidateItem(phd, i, RDW_INVALIDATE| RDW_ERASE);
  1869. } else {
  1870. RECT rc;
  1871. GetClientRect(phd->ci.hwnd, &rc);
  1872. if (i > 0) {
  1873. HDI * phdiTemp;
  1874. phdiTemp = Header_GetItemPtrByOrder(phd, i - 1);
  1875. if (phdiTemp) {
  1876. rc.left = phdi->x;
  1877. }
  1878. }
  1879. RedrawWindow(phd->ci.hwnd, &rc, NULL, RDW_INVALIDATE | RDW_ERASE);
  1880. }
  1881. }
  1882. return TRUE;
  1883. }
  1884. // Compute layout for header bar, and leftover rectangle.
  1885. //
  1886. BOOL Header_OnLayout(HD* phd, HD_LAYOUT* playout)
  1887. {
  1888. int cyHeader;
  1889. WINDOWPOS* pwpos;
  1890. RECT* prc;
  1891. RIPMSG(playout != NULL, "HDM_LAYOUT: Invalid NULL pointer");
  1892. if (!playout || !phd)
  1893. return FALSE;
  1894. if (!(playout->pwpos && playout->prc))
  1895. return FALSE;
  1896. pwpos = playout->pwpos;
  1897. prc = playout->prc;
  1898. cyHeader = phd->cyChar + 2 * g_cyEdgeScaled;
  1899. // when filter bar is enabled then lets show that region
  1900. if ( Header_IsFilter(phd) )
  1901. cyHeader += phd->cyChar + (2*g_cyEdgeScaled) + c_cyFilterBarEdge;
  1902. // internal hack style for use with LVS_REPORT|LVS_NOCOLUMNHEADER! edh
  1903. if (phd->ci.style & HDS_HIDDEN)
  1904. cyHeader = 0;
  1905. pwpos->hwndInsertAfter = NULL;
  1906. pwpos->flags = SWP_NOZORDER | SWP_NOACTIVATE;
  1907. pwpos->x = prc->left;
  1908. pwpos->cx = prc->right - prc->left;
  1909. pwpos->y = prc->top;
  1910. pwpos->cy = cyHeader;
  1911. prc->top += cyHeader;
  1912. return TRUE;
  1913. }
  1914. BOOL Header_OnGetItemRect(HD* phd, int i, RECT* prc)
  1915. {
  1916. HDI* phdi;
  1917. phdi = Header_GetItemPtr(phd, i);
  1918. if (!phdi)
  1919. return FALSE;
  1920. GetClientRect(phd->ci.hwnd, prc);
  1921. prc->right = phdi->x;
  1922. prc->left = prc->right - phdi->cxy;
  1923. return TRUE;
  1924. }
  1925. void Header_InvalidateItem(HD* phd, int i, UINT uFlags)
  1926. {
  1927. RECT rc;
  1928. if (i != -1) {
  1929. Header_OnGetItemRect(phd, i, &rc);
  1930. InflateRect(&rc, g_cxBorder, g_cyBorder);
  1931. RedrawWindow(phd->ci.hwnd, &rc, NULL, uFlags);
  1932. }
  1933. }
  1934. int _Header_DrawBitmap(HDC hdc, HIMAGELIST himl, HD_ITEM* pitem,
  1935. RECT *prc, int fmt, UINT flags, LPRECT prcDrawn, int iMargin)
  1936. {
  1937. // This routine returns either the left of the image
  1938. // or the right of the image depending on the justification.
  1939. // This return value is used in order to properly tack on the
  1940. // bitmap when both the HDF_IMAGE and HDF_BITMAP flags are set.
  1941. RECT rc;
  1942. int xBitmap = 0;
  1943. int yBitmap = 0;
  1944. int cxBitmap;
  1945. int cyBitmap;
  1946. IMAGELISTDRAWPARAMS imldp;
  1947. HBITMAP hbmOld;
  1948. BITMAP bm;
  1949. HDC hdcMem;
  1950. int cxRc;
  1951. SetRectEmpty(prcDrawn);
  1952. if (IsRectEmpty(prc))
  1953. return prc->left;
  1954. rc = *prc;
  1955. rc.left += iMargin;
  1956. rc.right -= iMargin;
  1957. // rc.right -= g_cxEdge; // handle edge
  1958. if (rc.left >= rc.right)
  1959. return rc.left;
  1960. if (pitem->fmt & HDF_IMAGE)
  1961. ImageList_GetIconSize(himl, &cxBitmap, &cyBitmap);
  1962. else { // pitem->fmt & BITMAP
  1963. if (GetObject(pitem->hbm, sizeof(bm), &bm) != sizeof(bm))
  1964. return rc.left; // could not get the info about bitmap.
  1965. hdcMem = CreateCompatibleDC(hdc);
  1966. if (!hdcMem || ((hbmOld = SelectObject(hdcMem, pitem->hbm)) == ERROR))
  1967. return rc.left; // an error happened.
  1968. cxBitmap = bm.bmWidth;
  1969. cyBitmap = bm.bmHeight;
  1970. }
  1971. if (flags & SHDT_DEPRESSED)
  1972. OffsetRect(&rc, g_cxBorder, g_cyBorder);
  1973. // figure out all the formatting...
  1974. cxRc = rc.right - rc.left; // cache this value
  1975. if (fmt == HDF_LEFT)
  1976. {
  1977. if (cxBitmap > cxRc)
  1978. cxBitmap = cxRc;
  1979. }
  1980. else if (fmt == HDF_CENTER)
  1981. {
  1982. if (cxBitmap > cxRc)
  1983. {
  1984. xBitmap = (cxBitmap - cxRc) / 2;
  1985. cxBitmap = cxRc;
  1986. }
  1987. else
  1988. rc.left = (rc.left + rc.right - cxBitmap) / 2;
  1989. }
  1990. else // fmt == HDF_RIGHT
  1991. {
  1992. if (cxBitmap > cxRc)
  1993. {
  1994. xBitmap = cxBitmap - cxRc;
  1995. cxBitmap = cxRc;
  1996. }
  1997. else
  1998. rc.left = rc.right - cxBitmap;
  1999. }
  2000. // Now setup vertically
  2001. if (cyBitmap > (rc.bottom - rc.top))
  2002. {
  2003. yBitmap = (cyBitmap - (rc.bottom - rc.top)) / 2;
  2004. cyBitmap = rc.bottom - rc.top;
  2005. }
  2006. else
  2007. rc.top = (rc.bottom - rc.top - cyBitmap) / 2;
  2008. if (pitem->fmt & HDF_IMAGE) {
  2009. imldp.cbSize = sizeof(imldp);
  2010. imldp.himl = himl;
  2011. imldp.hdcDst = hdc;
  2012. imldp.i = pitem->iImage;
  2013. imldp.x = rc.left;
  2014. imldp.y = rc.top;
  2015. imldp.cx = cxBitmap;
  2016. imldp.cy = cyBitmap;
  2017. imldp.xBitmap= xBitmap;
  2018. imldp.yBitmap= yBitmap;
  2019. imldp.rgbBk = CLR_DEFAULT;
  2020. imldp.rgbFg = CLR_DEFAULT;
  2021. imldp.fStyle = ILD_NORMAL;
  2022. imldp.fState = 0;
  2023. ImageList_DrawIndirect(&imldp);
  2024. }
  2025. else { // pitem->fmt & HDF_BITMAP
  2026. TraceMsg(TF_HEADER, "h_db: BitBlt to (%d,%d) from (%d, %d)", rc.left, rc.top, xBitmap, yBitmap);
  2027. // Last but not least we will do the bitblt.
  2028. BitBlt(hdc, rc.left, rc.top, cxBitmap, cyBitmap,
  2029. hdcMem, xBitmap, yBitmap, SRCCOPY);
  2030. // Unselect our object from the DC
  2031. SelectObject(hdcMem, hbmOld);
  2032. // Also free any memory dcs we may have created
  2033. DeleteDC(hdcMem);
  2034. }
  2035. *prcDrawn = rc;
  2036. prcDrawn->bottom = rc.top + cyBitmap;
  2037. prcDrawn->right = rc.left + cxBitmap;
  2038. return ((pitem->fmt & HDF_RIGHT) ? rc.left : rc.left+cxBitmap);
  2039. }
  2040. void Header_DrawButtonEdges(HD* phd, HDC hdc, LPRECT prc, BOOL fItemSunken)
  2041. {
  2042. UINT uEdge;
  2043. UINT uBF;
  2044. if (phd->ci.style & HDS_BUTTONS)
  2045. {
  2046. if (fItemSunken) {
  2047. uEdge = EDGE_SUNKEN;
  2048. uBF = BF_RECT | BF_SOFT | BF_FLAT;
  2049. } else {
  2050. uEdge = EDGE_RAISED;
  2051. uBF = BF_RECT | BF_SOFT;
  2052. }
  2053. }
  2054. else
  2055. {
  2056. uEdge = EDGE_ETCHED;
  2057. if (phd->ci.style & WS_BORDER)
  2058. uBF = BF_RIGHT;
  2059. else
  2060. uBF = BF_BOTTOMRIGHT;
  2061. }
  2062. DrawEdge(hdc, prc, uEdge, uBF);
  2063. }
  2064. void Header_DrawFilterGlyph(HD* phd, HDC hdc, RECT* prc, BOOL fPressed)
  2065. {
  2066. UINT uEdge = BDR_RAISEDOUTER|BDR_RAISEDINNER;
  2067. UINT uBF = BF_RECT;
  2068. RECT rc = *prc;
  2069. if ( fPressed )
  2070. {
  2071. uEdge = EDGE_SUNKEN;
  2072. uBF = BF_RECT | BF_SOFT | BF_FLAT;
  2073. }
  2074. if ( !phd->hFilterImage )
  2075. {
  2076. phd->hFilterImage = ImageList_LoadBitmap(HINST_THISDLL, MAKEINTRESOURCE(IDB_FILTERIMAGE), c_cxFilterImage, 0, RGB(128, 0, 0));
  2077. if ( !phd->hFilterImage )
  2078. return;
  2079. }
  2080. DrawEdge(hdc, &rc, uEdge, uBF|BF_MIDDLE);
  2081. if (fPressed)
  2082. OffsetRect(&rc, g_cxBorder, g_cyBorder);
  2083. ImageList_Draw(phd->hFilterImage, 0, hdc,
  2084. rc.left+(((rc.right-rc.left)-c_cxFilterImage)/2),
  2085. rc.top+(((rc.bottom-rc.top)-c_cyFilterImage)/2),
  2086. ILD_NORMAL);
  2087. }
  2088. //
  2089. // Oh boy, here come the pictures.
  2090. //
  2091. // For a left-justified header item, the items are arranged like this.
  2092. //
  2093. // rcHeader.left rcHeader.right
  2094. // | iTextMargin iTextMargin |
  2095. // | ->| |<- ->| |<- |
  2096. // | | | | | |
  2097. // v |<--textSize-->| | v
  2098. // +----------------------------------------+
  2099. // | |BMPBMP| | |TEXTTEXTTEXT| | |
  2100. // +----------------------------------------+
  2101. // |<-bmSize->| | |
  2102. // | | | | | |
  2103. // ->| |<- ->| |<- | |
  2104. // iBmMargin iBmMargin | |
  2105. // | | |
  2106. // |<-------cxTextAndBm------->|
  2107. //
  2108. //
  2109. // For a right-justified header item, the items are arranged like this.
  2110. //
  2111. // rcHeader.left rcHeader.right
  2112. // | iBmMargin iBmMargin |
  2113. // | ->| |<- ->| |<- |
  2114. // | | | | | |
  2115. // v |<-bmSize->| v
  2116. // +----------------------------------------+
  2117. // | | |BMPBMP| | |TEXTTEXTTEXT| |
  2118. // +----------------------------------------+
  2119. // | |<---textSize--->|
  2120. // | | | | |
  2121. // | ->| |<- ->| |<-
  2122. // | iTextMargin iTextMargin
  2123. // | |
  2124. // |<-------cxTextAndBm------->|
  2125. //
  2126. // Obvious variations apply to center-justified, bitmap-on-right, etc.
  2127. // The point is that all the sizes are accounted for in the manner above.
  2128. // There are no gratuitous +1's or g_cxEdge's.
  2129. //
  2130. void Header_DrawItem(HD* phd, HDC hdc, int i, int iIndex, LPRECT prc, UINT uFlags)
  2131. {
  2132. RECT rcHeader;
  2133. RECT rcFilter, rcButton;
  2134. RECT rcText; // item text clipping rect
  2135. RECT rcBm; // item bitmap clipping rect
  2136. COLORREF clrText;
  2137. COLORREF clrBk;
  2138. DWORD dwRet = CDRF_DODEFAULT;
  2139. HDI* phdi; // pointer to current header item
  2140. BOOL fItemSunken;
  2141. HD_ITEM item; // used for text callback
  2142. BOOL fTracking = Header_IsTracking(phd);
  2143. UINT uDrawTextFlags;
  2144. NMCUSTOMDRAW nmcd;
  2145. TCHAR ach[CCHLABELMAX]; // used for text callback
  2146. HRGN hrgnClip = NULL;
  2147. HRESULT hr = E_FAIL;
  2148. int iStateId = HIS_NORMAL;
  2149. rcHeader = rcFilter = *prc; // private copies for us to dork
  2150. phdi = Header_GetItemPtrByOrder(phd,i);
  2151. fItemSunken = (fTracking && (phd->flagsTrack & HHT_ONHEADER) &&
  2152. (phd->iTrack == iIndex) && phd->bTrackPress);
  2153. if (fItemSunken)
  2154. {
  2155. iStateId = HIS_PRESSED;
  2156. }
  2157. else if (iIndex == phd->iHot)
  2158. {
  2159. iStateId = HIS_HOT;
  2160. }
  2161. // Note that SHDT_EXTRAMARGIN requires phd->iTextMargin >= 3*g_cxLabelMargin
  2162. uDrawTextFlags = SHDT_ELLIPSES | SHDT_EXTRAMARGIN | SHDT_CLIPPED;
  2163. if(fItemSunken)
  2164. uDrawTextFlags |= SHDT_DEPRESSED;
  2165. if (phdi->fmt & HDF_OWNERDRAW)
  2166. {
  2167. DRAWITEMSTRUCT dis;
  2168. phd->fOwnerDraw = TRUE;
  2169. dis.CtlType = ODT_HEADER;
  2170. dis.CtlID = GetWindowID(phd->ci.hwnd);
  2171. dis.itemID = iIndex;
  2172. dis.itemAction = ODA_DRAWENTIRE;
  2173. dis.itemState = (fItemSunken) ? ODS_SELECTED : 0;
  2174. dis.hwndItem = phd->ci.hwnd;
  2175. dis.hDC = hdc;
  2176. dis.rcItem = *prc;
  2177. dis.itemData = phdi->lParam;
  2178. // Now send it off to my parent...
  2179. if (SendMessage(phd->ci.hwndParent, WM_DRAWITEM, dis.CtlID,
  2180. (LPARAM)(DRAWITEMSTRUCT *)&dis))
  2181. goto DrawEdges; //Ick, but it works
  2182. }
  2183. else
  2184. {
  2185. nmcd.dwItemSpec = iIndex;
  2186. nmcd.hdc = hdc;
  2187. nmcd.rc = *prc;
  2188. nmcd.uItemState = (fItemSunken) ? CDIS_SELECTED : 0;
  2189. nmcd.lItemlParam = phdi->lParam;
  2190. if (!(CCGetUIState(&(phd->ci)) & UISF_HIDEFOCUS))
  2191. nmcd.uItemState |= CDIS_SHOWKEYBOARDCUES;
  2192. dwRet = CICustomDrawNotify(&phd->ci, CDDS_ITEMPREPAINT, &nmcd);
  2193. if (dwRet & CDRF_SKIPDEFAULT)
  2194. {
  2195. return;
  2196. }
  2197. }
  2198. // this is to fetch out any changes the caller might have changed
  2199. clrText = GetTextColor(hdc);
  2200. clrBk = GetBkColor(hdc);
  2201. //
  2202. // Now neet to handle the different combinatations of
  2203. // text, bitmaps, and images...
  2204. //
  2205. if ( Header_IsFilter(phd) )
  2206. Header_GetFilterRects(prc, &rcHeader, &rcFilter, &rcButton);
  2207. rcText = rcBm = rcHeader;
  2208. if (phdi->fmt & (HDF_STRING | HDF_IMAGE | HDF_BITMAP))
  2209. {
  2210. item.mask = HDI_TEXT | HDI_IMAGE | HDI_FORMAT | HDI_BITMAP;
  2211. item.pszText = ach;
  2212. item.cchTextMax = ARRAYSIZE(ach);
  2213. Header_OnGetItem(phd,iIndex,&item);
  2214. }
  2215. if (phd->hTheme)
  2216. {
  2217. GetThemeBackgroundContentRect(phd->hTheme, hdc, HP_HEADERITEM, iStateId, &rcHeader, &rcText);
  2218. rcBm = rcText;
  2219. }
  2220. //
  2221. // If we have a string and either an image or a bitmap...
  2222. //
  2223. if (phdi->fmt & HDF_STRING &&
  2224. (phdi->fmt & (HDF_BITMAP | HDF_IMAGE) ||
  2225. phdi->fmt & (HDF_SORTUP | HDF_SORTDOWN)))
  2226. {
  2227. // Begin Recompute
  2228. if (phdi->xText == RECOMPUTE ||
  2229. phdi->xBm == RECOMPUTE)
  2230. {
  2231. BITMAP bm; // used to calculate bitmap width
  2232. // calculate the placement of bitmap rect and text rect
  2233. SIZE textSize,bmSize; int dx;
  2234. // get total textwidth
  2235. if (phd->hTheme)
  2236. {
  2237. RECT rc = {0};
  2238. RECT rcBound = {0};
  2239. hr = GetThemeTextExtent(phd->hTheme, hdc, HP_HEADERITEM, iStateId, item.pszText, -1, 0, &rcBound, &rc);
  2240. textSize.cx = RECTWIDTH(rc);
  2241. textSize.cy = RECTHEIGHT(rc);
  2242. }
  2243. if (FAILED(hr))
  2244. {
  2245. GetTextExtentPoint(hdc,item.pszText,lstrlen(item.pszText),
  2246. &textSize);
  2247. }
  2248. TraceMsg(TF_HEADER, "h_di: GetTextExtentPoint returns %d", textSize.cx);
  2249. textSize.cx += 2 * phd->iTextMargin;
  2250. // get total bitmap width
  2251. if (phdi->fmt & HDF_IMAGE)
  2252. {
  2253. ImageList_GetIconSize(phd->himl,(LPINT)&bmSize.cx,(LPINT)&bmSize.cy);
  2254. }
  2255. else if (phdi->fmt & (HDF_SORTUP | HDF_SORTDOWN))
  2256. {
  2257. // Make the size of the arrow a square based on height.
  2258. bmSize.cx = textSize.cy + 2 * g_cxEdge;
  2259. bmSize.cy = textSize.cy;
  2260. }
  2261. else
  2262. {
  2263. // phdi->fmt & HDF_BITMAP
  2264. GetObject(phdi->hbm,sizeof(bm), &bm);
  2265. bmSize.cx = bm.bmWidth;
  2266. TraceMsg(TF_HEADER, "h_di: Bitmap size is %d", bmSize.cx);
  2267. }
  2268. bmSize.cx += 2 * phd->iBmMargin;
  2269. phdi->cxTextAndBm = bmSize.cx + textSize.cx;
  2270. // calculate how much extra space we have, if any.
  2271. dx = rcHeader.right-rcHeader.left - phdi->cxTextAndBm;
  2272. if (dx < 0)
  2273. {
  2274. dx = 0;
  2275. phdi->cxTextAndBm = rcHeader.right-rcHeader.left;
  2276. }
  2277. if (phdi->fmt & HDF_BITMAP_ON_RIGHT ||
  2278. phdi->fmt & (HDF_SORTUP | HDF_SORTDOWN)) // Sort arrows behave as if on right
  2279. {
  2280. switch (phdi->fmt & HDF_JUSTIFYMASK)
  2281. {
  2282. case HDF_LEFT:
  2283. phdi->xText = rcText.left;
  2284. break;
  2285. case HDF_RIGHT:
  2286. phdi->xText = rcText.right - phdi->cxTextAndBm;
  2287. break;
  2288. case HDF_CENTER:
  2289. phdi->xText = rcText.left + dx/2;
  2290. break;
  2291. }
  2292. // show as much of the bitmap as possible..
  2293. // if we start running out of room, scoot the bitmap
  2294. // back on.
  2295. if (dx == 0)
  2296. phdi->xBm = rcText.right - bmSize.cx;
  2297. else
  2298. phdi->xBm = phdi->xText + textSize.cx;
  2299. // clip the values
  2300. if (phdi->xBm < rcHeader.left)
  2301. phdi->xBm = rcHeader.left;
  2302. }
  2303. else
  2304. {
  2305. // BITMAP_ON_LEFT
  2306. switch (phdi->fmt & HDF_JUSTIFYMASK)
  2307. {
  2308. case HDF_LEFT:
  2309. phdi->xBm = rcBm.left;
  2310. break;
  2311. case HDF_RIGHT:
  2312. phdi->xBm = rcBm.right - phdi->cxTextAndBm;
  2313. break;
  2314. case HDF_CENTER:
  2315. phdi->xBm = rcBm.left + dx/2;
  2316. break;
  2317. }
  2318. phdi->xText = phdi->xBm + bmSize.cx;
  2319. // clip the values
  2320. if (phdi->xText > rcHeader.right)
  2321. phdi->xText = rcHeader.right;
  2322. }
  2323. // xBm and xText are now absolute coordinates..
  2324. // change them to item relative coordinates
  2325. phdi->xBm -= rcHeader.left;
  2326. phdi->xText -= rcHeader.left;
  2327. TraceMsg(TF_HEADER, "h_di: phdi->xBm = %d, phdi->xText=%d",phdi->xBm, phdi->xText );
  2328. }
  2329. // End Recompute
  2330. // calculate text and bitmap rectangles
  2331. rcBm.left = phdi->xBm + rcText.left;
  2332. rcText.left = phdi->xText + rcText.left;
  2333. if (phdi->fmt & HDF_BITMAP_ON_RIGHT ||
  2334. phdi->fmt & (HDF_SORTUP | HDF_SORTDOWN))
  2335. {
  2336. rcBm.right = rcText.left + phdi->cxTextAndBm;
  2337. rcText.right = rcBm.left;
  2338. }
  2339. else
  2340. {
  2341. // BITMAP_ON_LEFT
  2342. rcBm.right = rcText.left;
  2343. rcText.right = rcBm.left + phdi->cxTextAndBm;
  2344. }
  2345. }
  2346. if (phd->hTheme)
  2347. {
  2348. DrawThemeBackground(phd->hTheme, hdc, HP_HEADERITEM, iStateId, &rcHeader, 0);
  2349. }
  2350. if (phdi->fmt & (HDF_SORTUP | HDF_SORTDOWN))
  2351. {
  2352. BOOL fUpArrow = phdi->fmt & HDF_SORTUP;
  2353. if (phd->hfontSortArrow)
  2354. {
  2355. int cy;
  2356. TEXTMETRIC tm;
  2357. int bkMode = SetBkMode(hdc, TRANSPARENT);
  2358. COLORREF cr = SetTextColor(hdc, g_clrGrayText);
  2359. HFONT hFontOld = SelectObject(hdc, phd->hfontSortArrow);
  2360. GetTextMetrics(hdc, &tm);
  2361. // Set the font height (based on original USER code)
  2362. cy = ((tm.tmHeight + tm.tmExternalLeading + GetSystemMetrics(SM_CYBORDER)) & 0xFFFE) - 1;
  2363. ExtTextOut(hdc, rcBm.left, rcBm.top + (RECTHEIGHT(rcBm) - cy)/ 2, 0, &rcBm, fUpArrow? TEXT("5") : TEXT("6"), 1, NULL);
  2364. SetTextColor(hdc, cr);
  2365. SetBkMode(hdc, bkMode);
  2366. SelectObject(hdc, hFontOld);
  2367. }
  2368. }
  2369. else if (phdi->fmt & HDF_IMAGE ||
  2370. phdi->fmt & HDF_BITMAP) // If we have a bitmap and/or an image...
  2371. {
  2372. BOOL fDrawBoth = FALSE;
  2373. RECT rcDrawn;
  2374. HRGN hrgn1 = NULL, hrgn2 = NULL;
  2375. int temp; // used to determine placement of bitmap.
  2376. if (phdi->fmt & HDF_IMAGE &&
  2377. phdi->fmt & HDF_BITMAP)
  2378. {
  2379. // we have to do both
  2380. fDrawBoth = TRUE;
  2381. // first do just the image... turn off the bitmap bit
  2382. // HACK ALERT! -- Don't call _Header_DrawBitmap with
  2383. // both the bitmap and image flags on
  2384. // Draw the image...
  2385. item.fmt ^= HDF_BITMAP; // turn off bitmap bit
  2386. }
  2387. if (!(uFlags & HDDF_NOIMAGE))
  2388. {
  2389. TraceMsg(TF_HEADER, "h_di: about to draw bitmap at rcBm= (%d,%d,%d,%d)",
  2390. rcBm.left, rcBm.top, rcBm.right, rcBm.bottom );
  2391. temp = _Header_DrawBitmap(hdc, phd->himl, &item, &rcBm,
  2392. item.fmt & HDF_JUSTIFYMASK, uDrawTextFlags,
  2393. &rcDrawn, phd->iBmMargin);
  2394. hrgn1 = CreateRectRgnIndirect(&rcDrawn);
  2395. }
  2396. if (fDrawBoth)
  2397. {
  2398. // Tack on the bitmap...
  2399. // Always tack the bitmap on the right of the image and
  2400. // text unless we are right justified. then, tack it on
  2401. // left.
  2402. item.fmt ^= HDF_BITMAP; // turn on bitmap bit
  2403. item.fmt ^= HDF_IMAGE; // and turn off image bit
  2404. if (item.fmt & HDF_RIGHT)
  2405. {
  2406. rcBm.right = temp;
  2407. if (item.fmt & HDF_STRING)
  2408. {
  2409. rcBm.right = ((rcBm.left < rcText.left) ?
  2410. rcBm.left : rcText.left);
  2411. }
  2412. rcBm.left = rcHeader.left;
  2413. }
  2414. else
  2415. {
  2416. rcBm.left = temp;
  2417. if (item.fmt & HDF_STRING)
  2418. {
  2419. rcBm.left = ((rcBm.right > rcText.right) ? rcBm.right:rcText.right);
  2420. }
  2421. rcBm.right = rcHeader.right;
  2422. }
  2423. if (!(uFlags & HDDF_NOIMAGE))
  2424. {
  2425. _Header_DrawBitmap(hdc, phd->himl, &item, &rcBm,
  2426. item.fmt & HDF_RIGHT, uDrawTextFlags,
  2427. &rcDrawn, phd->iBmMargin);
  2428. hrgn2 = CreateRectRgnIndirect(&rcDrawn);
  2429. }
  2430. item.fmt ^= HDF_IMAGE; // turn on the image bit
  2431. }
  2432. // if there were any regions created, union them together
  2433. if(hrgn1 && hrgn2)
  2434. {
  2435. hrgnClip = CreateRectRgn(0,0,0,0);
  2436. CombineRgn(hrgnClip, hrgn1, hrgn2, RGN_OR);
  2437. DeleteObject(hrgn1);
  2438. DeleteObject(hrgn2);
  2439. }
  2440. else if (hrgn1)
  2441. {
  2442. hrgnClip = hrgn1;
  2443. hrgn1 = NULL;
  2444. }
  2445. else if (hrgn2)
  2446. {
  2447. hrgnClip = hrgn2;
  2448. hrgn2 = NULL;
  2449. }
  2450. // this only happens in the drag/drop case
  2451. if ((uFlags & HDDF_NOIMAGE) && !hrgnClip )
  2452. {
  2453. // this means we didn't draw the images, which means we
  2454. // don't have the rects for them,
  2455. // which means we need to create a dummy empty hrgnClip;
  2456. hrgnClip = CreateRectRgn(0,0,0,0);
  2457. }
  2458. SaveDC(hdc);
  2459. }
  2460. if (phdi->fmt & HDF_STRING)
  2461. {
  2462. if (item.fmt & HDF_RTLREADING)
  2463. {
  2464. uDrawTextFlags |= SHDT_RTLREADING;
  2465. }
  2466. if (phd->hTheme)
  2467. {
  2468. clrBk = CLR_NONE;
  2469. }
  2470. TraceMsg(TF_HEADER, "h_di: about to draw text rcText=(%d,%d,%d,%d)",
  2471. rcText.left, rcText.top, rcText.right, rcText.bottom );
  2472. SHThemeDrawText(phd->hTheme, hdc, HP_HEADERITEM, iStateId, item.pszText, &rcText,
  2473. item.fmt & HDF_JUSTIFYMASK,
  2474. uDrawTextFlags, phd->cyChar, phd->cxEllipses,
  2475. clrText, clrBk);
  2476. if (hrgnClip)
  2477. {
  2478. // if we're building a clipping region, add the text to it.
  2479. HRGN hrgnText;
  2480. hrgnText = CreateRectRgnIndirect(&rcText);
  2481. CombineRgn(hrgnClip, hrgnText, hrgnClip, RGN_OR);
  2482. DeleteObject(hrgnText);
  2483. }
  2484. }
  2485. if (Header_IsFilter(phd))
  2486. {
  2487. TCHAR szBuffer[32] = {'\0'};
  2488. LPTSTR pBuffer = szBuffer;
  2489. DWORD dwButtonState = DFCS_BUTTONPUSH;
  2490. uDrawTextFlags = SHDT_ELLIPSES | SHDT_EXTRAMARGIN | SHDT_CLIPPED;
  2491. if (item.fmt & HDF_RTLREADING)
  2492. uDrawTextFlags |= SHDT_RTLREADING;
  2493. if (phdi->type & HDFT_HASNOVALUE)
  2494. {
  2495. LocalizedLoadString(IDS_ENTERTEXTHERE, szBuffer, ARRAYSIZE(szBuffer));
  2496. clrText = g_clrGrayText;
  2497. }
  2498. else
  2499. {
  2500. clrText = g_clrWindowText;
  2501. switch (phdi->type & HDFT_ISMASK)
  2502. {
  2503. case HDFT_ISSTRING:
  2504. pBuffer = phdi->textFilter.pszText;
  2505. break;
  2506. case HDFT_ISNUMBER:
  2507. wsprintf(szBuffer, TEXT("%d"), phdi->intFilter);
  2508. break;
  2509. default:
  2510. ASSERT(FALSE);
  2511. break;
  2512. }
  2513. }
  2514. SHDrawText(hdc, pBuffer, &rcFilter,
  2515. 0, uDrawTextFlags,
  2516. phd->cyChar, phd->cxEllipses,
  2517. clrText, g_clrWindow);
  2518. PatBlt(hdc, rcFilter.left, rcFilter.bottom, rcFilter.right-rcFilter.left, c_cyFilterBarEdge, BLACKNESS);
  2519. Header_DrawFilterGlyph(phd, hdc, &rcButton, (i==phd->iButtonDown));
  2520. if (hrgnClip) {
  2521. // if we're building a clipping region, add the text to it.
  2522. HRGN hrgnFilter;
  2523. hrgnFilter = CreateRectRgn( rcFilter.left, rcButton.top, rcButton.right, rcButton.bottom );
  2524. CombineRgn(hrgnClip, hrgnFilter, hrgnClip, RGN_OR);
  2525. DeleteObject(hrgnFilter);
  2526. }
  2527. if ( phd->fFocus && (phd->iFocus == i) &&
  2528. !(CCGetUIState(&(phd->ci)) & UISF_HIDEFOCUS))
  2529. {
  2530. InflateRect(&rcFilter, -g_cxEdge/2, -g_cyEdge/2);
  2531. SetTextColor(hdc, g_clrWindowText);
  2532. DrawFocusRect(hdc, &rcFilter);
  2533. }
  2534. }
  2535. if (hrgnClip)
  2536. {
  2537. if (!phd->hTheme)
  2538. {
  2539. // hrgnClip is the union of everyplace we've drawn..
  2540. // we want just the opposite.. so xor it
  2541. HRGN hrgnAll = CreateRectRgnIndirect(&rcHeader);
  2542. if (hrgnAll)
  2543. {
  2544. HRGN hrgn = CreateRectRgn(0, 0,0,0);
  2545. if (hrgn)
  2546. {
  2547. CombineRgn(hrgn, hrgnAll, hrgnClip, RGN_XOR);
  2548. SelectClipRgn(hdc, hrgn);
  2549. ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcHeader, NULL, 0, NULL);
  2550. RestoreDC(hdc, -1);
  2551. DeleteObject(hrgn);
  2552. }
  2553. DeleteObject(hrgnAll);
  2554. }
  2555. }
  2556. DeleteObject(hrgnClip);
  2557. }
  2558. DrawEdges:
  2559. if (!phd->hTheme && (!(uFlags & HDDF_NOEDGE) &&
  2560. !(phd->ci.style & HDS_FLAT) ||
  2561. iIndex == phd->iHot))
  2562. {
  2563. Header_DrawButtonEdges(phd, hdc, &rcHeader, fItemSunken);
  2564. }
  2565. if (dwRet & CDRF_NOTIFYPOSTPAINT)
  2566. {
  2567. CICustomDrawNotify(&phd->ci, CDDS_ITEMPOSTPAINT, &nmcd);
  2568. }
  2569. }
  2570. void Header_Draw(HD* phd, HDC hdc, RECT* prcClip)
  2571. {
  2572. int i; // index of current header item
  2573. int cItems; // number of items in header
  2574. RECT rc = { 0 }; // item clipping rect
  2575. BOOL fTracking;
  2576. HFONT hfontOld = NULL;
  2577. HDC hdcMem = NULL;
  2578. int iIndex;
  2579. NMCUSTOMDRAW nmcd;
  2580. COLORREF clrText;
  2581. fTracking = Header_IsTracking(phd);
  2582. if (phd->hfont)
  2583. hfontOld = SelectFont(hdc, phd->hfont);
  2584. cItems = DSA_GetItemCount(phd->hdsaHDI);
  2585. FillRectClr(hdc, prcClip, GetSysColor(COLOR_BTNFACE));
  2586. nmcd.hdc = hdc;
  2587. nmcd.uItemState = 0;
  2588. nmcd.lItemlParam = 0;
  2589. nmcd.rc = *prcClip;
  2590. phd->ci.dwCustom = CICustomDrawNotify(&phd->ci, CDDS_PREPAINT, &nmcd);
  2591. for (i = 0 ; i < cItems; i++)
  2592. {
  2593. iIndex = Header_ItemOrderToIndex(phd, i);
  2594. Header_OnGetItemRect(phd, iIndex, &rc);
  2595. if (prcClip)
  2596. {
  2597. if (rc.right < prcClip->left)
  2598. continue;
  2599. if (rc.left >= prcClip->right)
  2600. break;
  2601. }
  2602. if (iIndex == phd->iHot) {
  2603. clrText = GetSysColor(COLOR_HOTLIGHT);
  2604. } else {
  2605. clrText = g_clrBtnText;
  2606. }
  2607. SetTextColor(hdc, clrText);
  2608. SetBkColor(hdc, g_clrBtnFace);
  2609. Header_DrawItem(phd, hdc, i, iIndex, &rc, 0);
  2610. }
  2611. if (i == cItems)
  2612. {
  2613. // we got through the loop... now we need to do the blank area on the right
  2614. rc.left = rc.right;
  2615. rc.right = 32000;
  2616. if (phd->hTheme)
  2617. {
  2618. DrawThemeBackground(phd->hTheme, hdc, 0, 0, &rc, 0);
  2619. }
  2620. else if (phd->ci.style & HDS_FLAT)
  2621. {
  2622. FillRectClr(hdc, &rc, g_clrBtnFace);
  2623. }
  2624. else
  2625. {
  2626. Header_DrawButtonEdges(phd, hdc, &rc, FALSE);
  2627. }
  2628. }
  2629. if (!HDDragFullWindows(phd) && fTracking && (phd->flagsTrack & (HHT_ONDIVIDER | HHT_ONDIVOPEN)))
  2630. Header_DrawDivider(phd, phd->xTrack);
  2631. // draw the hot divider
  2632. if (phd->iNewOrder != -1) {
  2633. RECT rc;
  2634. COLORREF clrHot = GetSysColor(COLOR_HOTLIGHT);
  2635. Header_GetDividerRect(phd, phd->iNewOrder, &rc);
  2636. FillRectClr(hdc, &rc, clrHot);
  2637. }
  2638. if (phd->ci.dwCustom & CDRF_NOTIFYPOSTPAINT) {
  2639. CICustomDrawNotify(&phd->ci, CDDS_POSTPAINT, &nmcd);
  2640. }
  2641. if (hfontOld)
  2642. SelectFont(hdc, hfontOld);
  2643. }
  2644. HIMAGELIST Header_OnCreateDragImage(HD* phd, int i)
  2645. {
  2646. HDC hdcMem;
  2647. RECT rc;
  2648. HBITMAP hbmImage = NULL;
  2649. HBITMAP hbmMask = NULL;
  2650. HFONT hfontOld = NULL;
  2651. HIMAGELIST himl = NULL;
  2652. HIMAGELIST himlDither = NULL;
  2653. HBITMAP hbmOld = NULL;
  2654. BOOL bMirroredWnd = (phd->ci.dwExStyle&RTL_MIRRORED_WINDOW);
  2655. int iIndex = Header_ItemOrderToIndex(phd, i);
  2656. // IEUNIX : Fixing crash in OE while dragging the message
  2657. // header.
  2658. if( !Header_OnGetItemRect(phd, iIndex, &rc) )
  2659. goto Bail;
  2660. // draw the header into this bitmap
  2661. OffsetRect(&rc, -rc.left, -rc.top);
  2662. if (!(hdcMem = CreateCompatibleDC(NULL)))
  2663. goto Bail;
  2664. if (!(hbmImage = CreateColorBitmap(rc.right, rc.bottom)))
  2665. goto Bail;
  2666. if (!(hbmMask = CreateMonoBitmap(rc.right, rc.bottom)))
  2667. goto Bail;
  2668. //
  2669. // Mirror the memory DC so that the transition from
  2670. // mirrored(memDC)->non-mirrored(imagelist DCs)->mirrored(screenDC)
  2671. // is consistent. [samera]
  2672. //
  2673. if (bMirroredWnd) {
  2674. SET_DC_RTL_MIRRORED(hdcMem);
  2675. }
  2676. if (phd->hfont)
  2677. hfontOld = SelectFont(hdcMem, phd->hfont);
  2678. if (!(himl = ImageList_Create(rc.right, rc.bottom, ILC_MASK, 1, 0)))
  2679. goto Bail;
  2680. if (!(himlDither = ImageList_Create(rc.right, rc.bottom, ILC_MASK, 1, 0)))
  2681. goto Bail;
  2682. // have the darker background
  2683. SetTextColor(hdcMem, g_clrBtnText);
  2684. SetBkColor(hdcMem, g_clrBtnShadow);
  2685. hbmOld = SelectObject(hdcMem, hbmImage);
  2686. Header_DrawItem(phd, hdcMem, i, iIndex, &rc, HDDF_NOEDGE);
  2687. //
  2688. // If the header is RTL mirrored, then
  2689. // mirror the Memory DC, so that when copying back
  2690. // we don't get any image-flipping. [samera]
  2691. //
  2692. if (bMirroredWnd)
  2693. MirrorBitmapInDC(hdcMem, hbmImage);
  2694. // fill the mask with all black
  2695. SelectObject(hdcMem, hbmMask);
  2696. PatBlt(hdcMem, 0, 0, rc.right, rc.bottom, BLACKNESS);
  2697. // put the image into an imagelist
  2698. SelectObject(hdcMem, hbmOld);
  2699. ImageList_SetBkColor(himl, CLR_NONE);
  2700. ImageList_Add(himl, hbmImage, hbmMask);
  2701. // have the darker background
  2702. // now put the text in undithered.
  2703. SetTextColor(hdcMem, g_clrBtnText);
  2704. SetBkColor(hdcMem, g_clrBtnShadow);
  2705. hbmOld = SelectObject(hdcMem, hbmImage);
  2706. Header_DrawItem(phd, hdcMem, i, iIndex, &rc, HDDF_NOIMAGE | HDDF_NOEDGE);
  2707. DrawEdge(hdcMem, &rc, EDGE_BUMP, BF_RECT | BF_FLAT);
  2708. //
  2709. // If the header is RTL mirrored, then
  2710. // mirror the Memory DC, so that when copying back
  2711. // we don't get any image-flipping. [samera]
  2712. //
  2713. if (bMirroredWnd)
  2714. MirrorBitmapInDC(hdcMem, hbmImage);
  2715. /*
  2716. // initialize this to transparent
  2717. SelectObject(hdcMem, hbmImage);
  2718. PatBlt(hdcMem, 0, 0, rc.right, rc.bottom, BLACKNESS);
  2719. SelectObject(hdcMem, hbmMask);
  2720. PatBlt(hdcMem, 0, 0, rc.right, rc.bottom, WHITENESS);
  2721. */
  2722. SelectObject(hdcMem, hbmOld);
  2723. ImageList_AddMasked(himlDither, hbmImage, g_clrBtnShadow);
  2724. // dither image into himlDithered
  2725. ImageList_CopyDitherImage(himlDither, 0, 0, 0,
  2726. himl, 0, 0);
  2727. Bail:
  2728. if (himl)
  2729. {
  2730. ImageList_Destroy(himl);
  2731. }
  2732. if (hdcMem)
  2733. {
  2734. if (hbmOld)
  2735. SelectObject(hdcMem, hbmOld);
  2736. if (hfontOld)
  2737. SelectFont(hdcMem, hfontOld);
  2738. DeleteObject(hdcMem);
  2739. }
  2740. if (hbmImage)
  2741. DeleteObject(hbmImage);
  2742. if (hbmMask)
  2743. DeleteObject(hbmMask);
  2744. return himlDither;
  2745. }
  2746. void Header_GetFilterRects(LPRECT prcItem, LPRECT prcHeader, LPRECT prcFilter, LPRECT prcButton)
  2747. {
  2748. INT cyFilter = ((prcItem->bottom-prcItem->top)-c_cyFilterBarEdge)/2;
  2749. *prcButton = *prcFilter = *prcHeader = *prcItem;
  2750. prcHeader->bottom = prcHeader->top + cyFilter;
  2751. prcButton->left = prcFilter->right = prcFilter->right -= (g_cxBorder*4)+c_cxFilterImage;
  2752. prcButton->top = prcFilter->top = prcHeader->bottom;
  2753. prcFilter->bottom = prcFilter->top + cyFilter;
  2754. }
  2755. //
  2756. // Subclass the edit control to ensure we get the keys we are interested in
  2757. //
  2758. LRESULT CALLBACK Header_EditWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  2759. {
  2760. HD* phd = (HD*)GetWindowPtr(GetParent(hwnd), 0);
  2761. ASSERT(phd);
  2762. switch (msg)
  2763. {
  2764. case WM_KILLFOCUS:
  2765. Header_StopFilterEdit(phd, FALSE);
  2766. return 0L;
  2767. case WM_KEYDOWN:
  2768. {
  2769. if (wParam == VK_RETURN)
  2770. {
  2771. Header_StopFilterEdit(phd, FALSE);
  2772. return 0L;
  2773. }
  2774. else if (wParam == VK_ESCAPE)
  2775. {
  2776. Header_StopFilterEdit(phd, TRUE);
  2777. return 0L;
  2778. }
  2779. else if (wParam == VK_F4 )
  2780. {
  2781. Header_OnFilterButton(phd, phd->iEdit);
  2782. return 0L;
  2783. }
  2784. break;
  2785. }
  2786. case WM_CHAR:
  2787. {
  2788. switch (wParam)
  2789. {
  2790. case VK_RETURN:
  2791. case VK_ESCAPE:
  2792. case VK_TAB:
  2793. return 0L; // eat these so we don't beep
  2794. }
  2795. //notify of navigation key usage
  2796. CCNotifyNavigationKeyUsage(&(phd->ci), UISF_HIDEFOCUS);
  2797. break;
  2798. }
  2799. case WM_GETDLGCODE:
  2800. return DLGC_WANTALLKEYS | DLGC_HASSETSEL; /* editing name, no dialog handling right now */
  2801. }
  2802. return CallWindowProc(phd->pfnEditWndProc, hwnd, msg, wParam, lParam);
  2803. }
  2804. //
  2805. // Begin to edit the given column, displaying the editor as required
  2806. //
  2807. BOOL Header_BeginFilterEdit(HD* phd, int i)
  2808. {
  2809. RECT rc, rcHeader, rcFilter, rcButton;
  2810. int iIndex = i;
  2811. int cxEdit, cyEdit;
  2812. TCHAR szBuffer[MAX_PATH];
  2813. LPTSTR pBuffer = szBuffer;
  2814. int cchBuffer = MAX_PATH;
  2815. UINT uFlags = WS_CLIPSIBLINGS|WS_VISIBLE|WS_CHILD|ES_AUTOHSCROLL;
  2816. HDI* phdi = Header_GetItemPtr(phd, i);
  2817. if ( !phdi || (i < 0) )
  2818. return FALSE; // yikes
  2819. // lets create an edit control that allows the user to
  2820. // modify the current filter, note that we first must
  2821. // format the data to be displayed in the control
  2822. Header_OnGetItemRect(phd, iIndex, &rc);
  2823. Header_GetFilterRects(&rc, &rcHeader, &rcFilter, &rcButton);
  2824. phd->typeOld = phdi->type; // keep the type field safe
  2825. switch (phdi->type & HDFT_ISMASK)
  2826. {
  2827. case HDFT_ISSTRING:
  2828. Str_Set(&phd->pszFilterOld, phdi->textFilter.pszText);
  2829. pBuffer = phdi->textFilter.pszText;
  2830. // This count does not include the terminating null
  2831. cchBuffer = phdi->textFilter.cchTextMax;
  2832. break;
  2833. case HDFT_ISNUMBER:
  2834. phd->intFilterOld = phdi->intFilter;
  2835. wsprintf(szBuffer, TEXT("%d"), phdi->intFilter);
  2836. cchBuffer = 11; // 10 digits, plus sign
  2837. uFlags |= ES_NUMBER;
  2838. break;
  2839. default:
  2840. return FALSE;
  2841. }
  2842. cxEdit = (rcFilter.right-rcFilter.left)-(g_cxLabelMargin*6);
  2843. cyEdit = (rcFilter.bottom-rcFilter.top)-(g_cyEdge*2);
  2844. phd->hwndEdit = CreateWindow(TEXT("EDIT"),
  2845. !(phdi->type & HDFT_HASNOVALUE) ? pBuffer:TEXT(""),
  2846. uFlags,
  2847. rcFilter.left+(g_cxLabelMargin*3),
  2848. rcFilter.top+g_cyEdge,
  2849. cxEdit, cyEdit,
  2850. phd->ci.hwnd,
  2851. NULL, HINST_THISDLL, NULL);
  2852. if ( phd->hwndEdit )
  2853. {
  2854. INT iOldFocus = phd->iFocus;
  2855. //
  2856. // Setup the edit mode for this object?
  2857. //
  2858. phd->iEdit = i; // now editing this column
  2859. phd->iFocus = Header_OnGetItemOrder(phd, i);
  2860. Header_OnGetItemRect(phd, Header_ItemOrderToIndex(phd, iOldFocus), &rc); // nb: iOldFocus
  2861. Header_GetFilterRects(&rc, &rcHeader, &rcFilter, &rcButton);
  2862. RedrawWindow(phd->ci.hwnd, &rcFilter, NULL, RDW_INVALIDATE | RDW_ERASE);
  2863. //
  2864. // Now subclass the edit control so we can trap the keystrokes we are interested in
  2865. //
  2866. phd->pfnEditWndProc = SubclassWindow(phd->hwndEdit, Header_EditWndProc);
  2867. ASSERT(phd->pfnEditWndProc);
  2868. Edit_LimitText(phd->hwndEdit, cchBuffer);
  2869. Edit_SetSel(phd->hwndEdit, 0, -1);
  2870. FORWARD_WM_SETFONT(phd->hwndEdit, phd->hfont, FALSE, SendMessage);
  2871. SetFocus(phd->hwndEdit);
  2872. }
  2873. return(phd->hwndEdit != NULL);
  2874. }
  2875. //
  2876. // Stop editing the fitler, discarding the change if we need to, otherwise
  2877. // the item has the correct information stored within it.
  2878. //
  2879. VOID Header_StopFilterEdit(HD* phd, BOOL fDiscardChanges)
  2880. {
  2881. if ( phd->iEdit >= 0 )
  2882. {
  2883. HDI* phdi = Header_GetItemPtr(phd, phd->iEdit);
  2884. HD_ITEM hdi;
  2885. HD_TEXTFILTER textFilter;
  2886. int intFilter;
  2887. ASSERT(phdi);
  2888. if ( fDiscardChanges )
  2889. {
  2890. hdi.mask = HDI_FILTER;
  2891. hdi.type = phd->typeOld;
  2892. switch (phdi->type & HDFT_ISMASK)
  2893. {
  2894. case HDFT_ISSTRING:
  2895. textFilter.pszText = phd->pszFilterOld;
  2896. textFilter.cchTextMax = phdi->textFilter.cchTextMax;
  2897. hdi.pvFilter = &textFilter;
  2898. break;
  2899. case HDFT_ISNUMBER:
  2900. intFilter = phd->intFilterOld;
  2901. hdi.pvFilter = &intFilter;
  2902. break;
  2903. }
  2904. Header_OnSetItem(phd, phd->iEdit, &hdi);
  2905. }
  2906. else
  2907. {
  2908. Header_FilterChanged(phd, FALSE); // ensure we flush the changes
  2909. }
  2910. if ( phd->hwndEdit )
  2911. {
  2912. SubclassWindow(phd->hwndEdit, phd->pfnEditWndProc);
  2913. DestroyWindow(phd->hwndEdit);
  2914. phd->hwndEdit = NULL;
  2915. }
  2916. phd->iEdit = -1;
  2917. phd->pszFilterOld = NULL;
  2918. }
  2919. }
  2920. //
  2921. // Send a filter change to the parent, either now or wait until the timeout
  2922. // expires.
  2923. //
  2924. VOID Header_FilterChanged(HD* phd, BOOL fWait)
  2925. {
  2926. if ( phd->iEdit < 0 )
  2927. return;
  2928. if ( fWait )
  2929. {
  2930. // defering the notify, therefore lets set the timer (killing any
  2931. // previous ones) and marking that we are waiting on it.
  2932. KillTimer(phd->ci.hwnd, HD_EDITCHANGETIMER);
  2933. SetTimer(phd->ci.hwnd, HD_EDITCHANGETIMER, phd->iFilterChangeTimeout, NULL);
  2934. phd->fFilterChangePending = TRUE;
  2935. }
  2936. else
  2937. {
  2938. HDI* phdi = Header_GetItemPtrByOrder(phd, phd->iEdit);
  2939. ASSERT(phdi);
  2940. // if we have a change notify pending then lets send it to
  2941. // the parent window, otherwise we just swallow it.
  2942. if ( phd->fFilterChangePending )
  2943. {
  2944. TCHAR szBuffer[MAX_PATH];
  2945. HD_ITEM hdi;
  2946. HD_TEXTFILTER textFilter;
  2947. int intFilter;
  2948. KillTimer(phd->ci.hwnd, HD_EDITCHANGETIMER);
  2949. phd->fFilterChangePending = FALSE;
  2950. hdi.mask = HDI_FILTER;
  2951. hdi.type = phdi->type & ~HDFT_HASNOVALUE;
  2952. if ( !GetWindowText(phd->hwndEdit, szBuffer, ARRAYSIZE(szBuffer)) )
  2953. hdi.type |= HDFT_HASNOVALUE;
  2954. switch (phdi->type & HDFT_ISMASK)
  2955. {
  2956. case HDFT_ISSTRING:
  2957. textFilter.pszText = szBuffer;
  2958. textFilter.cchTextMax = phdi->textFilter.cchTextMax;
  2959. hdi.pvFilter = &textFilter;
  2960. break;
  2961. case HDFT_ISNUMBER:
  2962. intFilter = StrToInt(szBuffer);
  2963. hdi.pvFilter = &intFilter;
  2964. break;
  2965. }
  2966. Header_OnSetItem(phd, phd->iEdit, &hdi);
  2967. }
  2968. }
  2969. }
  2970. //
  2971. // Handle the user displaying the filter menu
  2972. //
  2973. VOID Header_OnFilterButton(HD* phd, INT i)
  2974. {
  2975. NMHDFILTERBTNCLICK fbc;
  2976. RECT rc, rcHeader, rcFilter;
  2977. // filter button being depressed so depress it, then tell the user
  2978. // that it went down so they can display the UI they want, before
  2979. // we pop the button. if the notify returns TRUE then send
  2980. // a change notify around.
  2981. Header_StopFilterEdit(phd, FALSE);
  2982. ASSERT(phd->iButtonDown == -1);
  2983. phd->iButtonDown = i;
  2984. Header_InvalidateItem(phd, i, RDW_INVALIDATE);
  2985. UpdateWindow(phd->ci.hwnd);
  2986. ZeroMemory(&fbc, SIZEOF(fbc));
  2987. fbc.iItem = i;
  2988. // fbc.rc = { 0, 0, 0, 0 };
  2989. Header_OnGetItemRect(phd, i, &rc);
  2990. Header_GetFilterRects(&rc, &rcHeader, &rcFilter, &fbc.rc);
  2991. if ( CCSendNotify(&phd->ci, HDN_FILTERBTNCLICK, &fbc.hdr) )
  2992. Header_Notify(phd, i, 0, HDN_FILTERCHANGE);
  2993. phd->iButtonDown = -1;
  2994. Header_InvalidateItem(phd, i, RDW_INVALIDATE);
  2995. UpdateWindow(phd->ci.hwnd);
  2996. }
  2997. //
  2998. // Handle clearing the filter for the given item
  2999. //
  3000. LRESULT Header_OnClearFilter(HD* phd, INT i)
  3001. {
  3002. HDI* phdi;
  3003. HD_ITEM hdi;
  3004. INT iChanged = 0;
  3005. Header_StopFilterEdit(phd, FALSE);
  3006. if ( i == -1 )
  3007. {
  3008. //
  3009. // clear all filters by setting setting the HDFT_HASNOVALUEFLAG on all items
  3010. // remember to release the filter data. For each item we also send an item
  3011. // changing indicating that the filter is changing and then a item changed
  3012. // to indicat that we really did fix the value.
  3013. //
  3014. for ( i = 0 ; i < DSA_GetItemCount(phd->hdsaHDI); i++ )
  3015. {
  3016. phdi = Header_GetItemPtrByOrder(phd, i);
  3017. ASSERT(phdi);
  3018. if ( !(phdi->type & HDFT_HASNOVALUE) )
  3019. {
  3020. hdi.mask = HDI_FILTER;
  3021. hdi.type = phdi->type|HDFT_HASNOVALUE;
  3022. hdi.pvFilter = NULL;
  3023. if ( Header_SendChange(phd, i, HDN_ITEMCHANGING, &hdi) )
  3024. {
  3025. if ( (phdi->type & HDFT_ISMASK) == HDFT_ISSTRING )
  3026. Str_Set(&phdi->textFilter.pszText, NULL);
  3027. phdi->type |= HDFT_HASNOVALUE; // item is now empty
  3028. Header_SendChange(phd, i, HDN_ITEMCHANGED, &hdi);
  3029. iChanged++;
  3030. }
  3031. }
  3032. }
  3033. if ( iChanged )
  3034. {
  3035. //
  3036. // item == -1 indicating that we are cleared all filters, then invalidate
  3037. // the window so that the filter values are no longer visible
  3038. //
  3039. Header_Notify(phd, -1, 0, HDN_FILTERCHANGE); // send out a notify of change
  3040. RedrawWindow(phd->ci.hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  3041. }
  3042. }
  3043. else
  3044. {
  3045. if ( (i < 0) || (i > DSA_GetItemCount(phd->hdsaHDI)) )
  3046. return 0L;
  3047. phdi = Header_GetItemPtrByOrder(phd, i);
  3048. ASSERT(phdi);
  3049. if ( !(phdi->type & HDFT_HASNOVALUE) )
  3050. {
  3051. //
  3052. // clear a single filter by setting the HDFT_HASNOVALUE flag
  3053. //
  3054. hdi.mask = HDI_FILTER;
  3055. hdi.type = phdi->type|HDFT_HASNOVALUE;
  3056. hdi.pvFilter = NULL;
  3057. Header_OnSetItem(phd, i, &hdi);
  3058. }
  3059. }
  3060. return 1L;
  3061. }