Leaked source code of windows server 2003
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.

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