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

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