Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2011 lines
58 KiB

  1. /*++
  2. Copyright (c) 1993-2000 Microsoft Corporation
  3. Module Name:
  4. Clb.c
  5. Abstract:
  6. This file contains support for the ColumnListBox (clb.dll) custom control.
  7. Author:
  8. David J. Gilman (davegi) 05-Feb-1993
  9. Environment:
  10. User Mode
  11. --*/
  12. #include "clb.h"
  13. #include <commctrl.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <string.h>
  17. #include <tchar.h> // _tcstok routines...
  18. #include <strsafe.h>
  19. //
  20. // Clb's module handle.
  21. //
  22. HINSTANCE
  23. _hModule;
  24. //
  25. // Child IDs for the header and listbox controls.
  26. //
  27. #define ID_HEADER ( 0x1234 )
  28. #define ID_LISTBOX ( 0xABCD )
  29. //
  30. // Separator used to parse headings.
  31. //
  32. #define HEADING_SEPARATOR L";"
  33. //
  34. // Valid styles for each part of the Clb.
  35. //
  36. #define CLBS_CLB ( \
  37. 0 \
  38. | CLBS_BORDER \
  39. | LBS_OWNERDRAWFIXED \
  40. | WS_VISIBLE \
  41. | WS_DISABLED \
  42. | WS_GROUP \
  43. | WS_TABSTOP \
  44. | WS_CHILD \
  45. )
  46. #define CLBS_HEADER ( \
  47. 0 \
  48. | WS_VISIBLE \
  49. | CLBS_POPOUT_HEADINGS \
  50. | CLBS_SPRINGY_COLUMNS \
  51. )
  52. #define CLBS_LIST_BOX ( \
  53. 0 \
  54. | WS_VISIBLE \
  55. | CLBS_NOTIFY \
  56. | CLBS_SORT \
  57. | CLBS_DISABLENOSCROLL \
  58. | CLBS_VSCROLL \
  59. )
  60. //
  61. // Window procedure for the CLB.
  62. //
  63. LRESULT
  64. ClbWndProc(
  65. IN HWND hWnd,
  66. IN UINT message,
  67. IN WPARAM wParam,
  68. IN LPARAM lParam
  69. );
  70. //
  71. // Per CLB window information.
  72. //
  73. // hWndHeader - hWnd for header control.
  74. // hWndListBox - hWnd for listbox control.
  75. // hFontListBox - hFont for the list box control.
  76. // HeaderHeight - height of the header window.
  77. // Columns - number of columns in CLB.
  78. // Headings - raw (semi-colon separated) column headings.
  79. // Right - array of right edge coordinates.
  80. //
  81. typedef
  82. struct
  83. _CLB_INFO {
  84. DECLARE_SIGNATURE
  85. HWND hWndHeader;
  86. HWND hWndListBox;
  87. HFONT hFontListBox;
  88. DWORD HeaderHeight;
  89. DWORD Columns;
  90. WCHAR Headings[ MAX_PATH ];
  91. LPLONG Right;
  92. } CLB_INFO, *LPCLB_INFO;
  93. //
  94. // Helper macros to save and restore per Clb window information.
  95. //
  96. #define SaveClbInfo( p ) \
  97. SetWindowLongPtr( hWnd, 0, ( LONG_PTR )( p ))
  98. #define RestoreClbInfo( h ) \
  99. ( LPCLB_INFO ) GetWindowLongPtr(( h ), 0 )
  100. //
  101. // Structures to support drawing and ersaing the drag line.
  102. //
  103. typedef
  104. struct
  105. _LINE_POINTS {
  106. POINT Src;
  107. POINT Dst;
  108. } LINE_POINT, *LPLINE_POINT;
  109. typedef
  110. struct
  111. _DRAW_ERASE_LINE {
  112. LINE_POINT Erase;
  113. LINE_POINT Draw;
  114. } DRAW_ERASE_LINE, *LPDRAW_ERASE_LINE;
  115. BOOL
  116. DrawLine(
  117. IN HDC hDC,
  118. IN LPDRAW_ERASE_LINE DrawEraseLine
  119. )
  120. /*++
  121. Routine Description:
  122. DrawLine draws the Draw line in the supplied DrawEraseLine structure
  123. and then sets up that line so that EraseLine will erase it.
  124. Arguments:
  125. hDC - Supplies a handle to the DC where the line should be
  126. drawn.
  127. DrawEraseLine - Supplies a pointer to a DRAW_ERASE_LINE structure that
  128. conatins the coordinates for the line to be drawn.
  129. Return Value:
  130. BOOL - Returns TRUE if the line was succesfully drawn.
  131. --*/
  132. {
  133. BOOL Success;
  134. DbgHandleAssert( hDC );
  135. DbgPointerAssert( DrawEraseLine );
  136. Success = Polyline( hDC, ( CONST LPPOINT ) &DrawEraseLine->Draw, 2 );
  137. DbgAssert( Success );
  138. DrawEraseLine->Erase = DrawEraseLine->Draw;
  139. return Success;
  140. }
  141. BOOL
  142. EraseLine(
  143. IN HDC hDC,
  144. IN LPDRAW_ERASE_LINE DrawEraseLine
  145. )
  146. /*++
  147. Routine Description:
  148. EraseLine erasess the Erase line in the supplied DrawEraseLine structure.
  149. The EraseLine is set by the DrawLine routine.
  150. Arguments:
  151. hDC - Supplies a handle to the DC where the line should
  152. be erased.
  153. DrawEraseLine - Supplies a pointer to a DRAW_ERASE_LINE structure that
  154. conatins the coordinates for the line to be erased.
  155. Return Value:
  156. BOOL - Returns TRUE if the line was succesfully erased.
  157. --*/
  158. {
  159. BOOL Success;
  160. DbgHandleAssert( hDC );
  161. DbgPointerAssert( DrawEraseLine );
  162. Success = Polyline( hDC, ( CONST LPPOINT ) &DrawEraseLine->Erase, 2 );
  163. DbgAssert( Success );
  164. return Success;
  165. }
  166. BOOL
  167. RedrawVerticalLine(
  168. IN HDC hDC,
  169. IN LONG x,
  170. IN LPDRAW_ERASE_LINE DrawEraseLine
  171. )
  172. /*++
  173. Routine Description:
  174. RedrawVerticalLine erases the old line and redraws a new one at the
  175. supplied x position. It is merely a warpper for DrawLine and EraseLine.
  176. Arguments:
  177. hDC - Supplies a handle to the DC where the line should
  178. be erased.
  179. x - Supplies the new x coordinate where the line should
  180. be drawn.
  181. DrawEraseLine - Supplies a pointer to a DRAW_ERASE_LINE structure that
  182. conatins the coordinates for the line to be erased.
  183. Return Value:
  184. BOOL - Returns TRUE if the line was succesfully erased.
  185. --*/
  186. {
  187. BOOL Success;
  188. DbgHandleAssert( hDC );
  189. DbgPointerAssert( DrawEraseLine );
  190. DrawEraseLine->Draw.Src.x = x;
  191. DrawEraseLine->Draw.Dst.x = x;
  192. Success = EraseLine( hDC, DrawEraseLine );
  193. DbgAssert( Success );
  194. Success = DrawLine( hDC, DrawEraseLine );
  195. DbgAssert( Success );
  196. return Success;
  197. }
  198. BOOL
  199. ClbEntryPoint(
  200. IN HINSTANCE hInstanceDll,
  201. IN DWORD Reason,
  202. IN LPVOID Reserved
  203. )
  204. /*++
  205. Routine Description:
  206. This function registers the ColumnListBox class as a global class for
  207. any process that attaches to clb.dll.
  208. Arguments:
  209. Standard DLL entry parameters.
  210. Return Value:
  211. BOOL - Returns TRUE if the class was succesfully registered.
  212. --*/
  213. {
  214. BOOL Success;
  215. static
  216. DWORD AttachedProcesses = 0;
  217. switch ( Reason ) {
  218. case DLL_PROCESS_ATTACH:
  219. {
  220. WNDCLASS Wc;
  221. //
  222. // If this is the first process attaching to Clb, register the
  223. // window class.
  224. //
  225. if ( AttachedProcesses == 0 ) {
  226. //
  227. // Remember the module handle.
  228. //
  229. _hModule = hInstanceDll;
  230. //
  231. // Make sure that the Common Controls (comctl32.dll) Dll
  232. // is loaded.
  233. //
  234. InitCommonControls( );
  235. Wc.style = CS_GLOBALCLASS | CS_OWNDC;
  236. Wc.lpfnWndProc = ClbWndProc;
  237. Wc.cbClsExtra = 0;
  238. Wc.cbWndExtra = sizeof( LPCLB_INFO );
  239. Wc.hInstance = hInstanceDll;
  240. Wc.hIcon = NULL;
  241. Wc.hCursor = LoadCursor( NULL, IDC_ARROW );
  242. Wc.hbrBackground = NULL;
  243. Wc.lpszMenuName = NULL;
  244. Wc.lpszClassName = CLB_CLASS_NAME;
  245. //
  246. // If the class couldn't be registered, fail the linkage.
  247. //
  248. if (!RegisterClass(&Wc))
  249. {
  250. return FALSE;
  251. }
  252. }
  253. //
  254. // Either the class was just succesfully registered or it was
  255. // registered by a prior process attachment, eother way increment
  256. // the count of attached processes.
  257. //
  258. AttachedProcesses++;
  259. return TRUE;
  260. }
  261. case DLL_PROCESS_DETACH:
  262. {
  263. DbgAssert( AttachedProcesses > 0 );
  264. AttachedProcesses--;
  265. if ( AttachedProcesses == 0 ) {
  266. Success = UnregisterClass( CLB_CLASS_NAME, hInstanceDll );
  267. DbgAssert( Success );
  268. }
  269. break;
  270. }
  271. }
  272. return TRUE;
  273. }
  274. BOOL
  275. ClbAddData(
  276. IN HWND hWnd,
  277. IN int ControlId,
  278. IN LPCLB_ROW ClbRow
  279. )
  280. /*++
  281. Routine Description:
  282. ClbAddData adds a new row of data to the Clb control's List Box.
  283. Arguments:
  284. hWnd - Supplies the window handle for the parent window.
  285. ControlId - Supplies the control id for this Clb for the supplied hWnd.
  286. ClbRow - Supplies a pointer to a CLB_ROW object which contains user
  287. define per row data along with an array of CLB_STRINGs.
  288. Return Value:
  289. BOOL - Returns TRUE if the data was successfully added.
  290. --*/
  291. {
  292. LPCLB_INFO ClbInfo;
  293. LRESULT LbErr;
  294. DWORD i;
  295. HWND hWndClb;
  296. LPCLB_ROW TempRow;
  297. //
  298. // Validate arguments.
  299. //
  300. DbgHandleAssert( hWnd );
  301. DbgPointerAssert( ClbRow );
  302. //
  303. // Retrieve information for this ColumnListBox.
  304. //
  305. hWndClb = GetDlgItem( hWnd, ControlId );
  306. DbgHandleAssert( hWndClb );
  307. if (hWndClb == NULL)
  308. return FALSE;
  309. ClbInfo = RestoreClbInfo( hWndClb );
  310. DbgPointerAssert( ClbInfo );
  311. if (ClbInfo == NULL)
  312. return FALSE;
  313. DbgAssert( CheckSignature( ClbInfo ));
  314. //
  315. // Validate the count of strings.
  316. //
  317. DbgAssert( ClbRow->Count == ClbInfo->Columns );
  318. //
  319. // Capture the CLB_ROW object.
  320. //
  321. TempRow = AllocateObject( CLB_ROW, 1 );
  322. DbgPointerAssert( TempRow );
  323. if (TempRow == NULL)
  324. return FALSE;
  325. DbgAssert(sizeof(*TempRow) == sizeof(*ClbRow));
  326. CopyMemory( TempRow, ClbRow, sizeof(CLB_ROW) );
  327. //
  328. // Capture the strings.
  329. //
  330. TempRow->Strings = AllocateObject( CLB_STRING, ClbInfo->Columns );
  331. DbgPointerAssert( TempRow->Strings );
  332. if (TempRow->Strings == NULL)
  333. return FALSE;
  334. for ( i = 0; i < ClbInfo->Columns; i++ )
  335. {
  336. //
  337. // Copy the header.
  338. //
  339. CopyMemory(
  340. &TempRow->Strings[ i ],
  341. &ClbRow->Strings[ i ],
  342. sizeof( CLB_STRING )
  343. );
  344. //
  345. // Copy the string.
  346. //
  347. TempRow->Strings[ i ].String = _wcsdup( ClbRow->Strings[ i ].String );
  348. }
  349. //
  350. // Store the CLB_ROW object in the listbox.
  351. //
  352. LbErr = SendMessage(
  353. ClbInfo->hWndListBox,
  354. LB_ADDSTRING,
  355. 0,
  356. ( LPARAM ) TempRow
  357. );
  358. DbgAssert(( LbErr != LB_ERR ) && ( LbErr != LB_ERRSPACE ));
  359. return TRUE;
  360. }
  361. BOOL
  362. GetCharMetrics(
  363. IN HDC hDC,
  364. IN LPLONG CharWidth,
  365. IN LPLONG CharHeight
  366. )
  367. /*++
  368. Routine Description:
  369. Return the width and height of a character.
  370. Arguments:
  371. hDC - Supplies a handle to the DC where the characters are to be
  372. displayed.
  373. CharWidth - Supplies a pointer where the character width is returned.
  374. CharHeight - Supplies a pointer where the character height is returned.
  375. Return Value:
  376. BOOL - Returns TRUE if the character height and width are returned.
  377. --*/
  378. {
  379. BOOL Success;
  380. TEXTMETRICW TextMetric;
  381. DbgHandleAssert( hDC );
  382. DbgPointerAssert( CharWidth );
  383. DbgPointerAssert( CharHeight );
  384. //
  385. // Attempt to retrieve the text metrics for the supplied DC.
  386. //
  387. Success = GetTextMetricsW( hDC, &TextMetric );
  388. DbgAssert( Success );
  389. if( Success ) {
  390. //
  391. // Compute the character width and height.
  392. //
  393. *CharWidth = TextMetric.tmAveCharWidth;
  394. *CharHeight = TextMetric.tmHeight
  395. + TextMetric.tmExternalLeading;
  396. }
  397. return Success;
  398. }
  399. BOOL
  400. ClbSetColumnWidths(
  401. IN HWND hWnd,
  402. IN int ControlId,
  403. IN LPDWORD Widths
  404. )
  405. /*++
  406. Routine Description:
  407. ClbSetColumnWidths sets the width of each column based on the supplied
  408. widths in characters. Note that the column on the far right extends to
  409. the edge of the Clb.
  410. Arguments:
  411. hWnd - Supplies the window handle for the parent window.
  412. ControlId - Supplies the control id for this Clb for the supplied hWnd.
  413. Widths - Supplies an array of widths, one less then the number of
  414. columns, in characters.
  415. Return Value:
  416. BOOL - Returns TRUE if the widths were successfully adjusted.
  417. --*/
  418. {
  419. BOOL Success;
  420. DWORD Columns;
  421. LPCLB_INFO ClbInfo;
  422. HWND hWndClb;
  423. LONG CharWidth;
  424. LONG CharHeight;
  425. DWORD i;
  426. LPLONG WidthsInPixels;
  427. LONG TotalPixels;
  428. HDC hDCClientHeader;
  429. HD_ITEM hdi;
  430. UINT iRight;
  431. //
  432. // Validate arguments.
  433. //
  434. DbgHandleAssert( hWnd );
  435. DbgPointerAssert( Widths );
  436. //
  437. // Retrieve information for this ColumnListBox.
  438. //
  439. hWndClb = GetDlgItem( hWnd, ControlId );
  440. DbgHandleAssert( hWndClb );
  441. if (hWndClb == NULL)
  442. return FALSE;
  443. ClbInfo = RestoreClbInfo( hWndClb );
  444. DbgPointerAssert( ClbInfo );
  445. if (ClbInfo == NULL)
  446. return FALSE;
  447. DbgAssert( CheckSignature( ClbInfo ));
  448. //
  449. // Get thd HDC for the header.
  450. //
  451. hDCClientHeader = GetDC( ClbInfo->hWndHeader );
  452. DbgHandleAssert( hDCClientHeader );
  453. if (hDCClientHeader == NULL)
  454. return FALSE;
  455. //
  456. // Get the width of a character.
  457. //
  458. Success = GetCharMetrics( hDCClientHeader, &CharWidth, &CharHeight );
  459. DbgAssert( Success );
  460. //
  461. // Release the DC for the header.
  462. //
  463. Success = ReleaseDC( ClbInfo->hWndHeader, hDCClientHeader );
  464. DbgAssert( Success );
  465. //
  466. // Allocate an array of pixel widths, one for each column.
  467. //
  468. WidthsInPixels = AllocateObject( LONG, ClbInfo->Columns );
  469. DbgPointerAssert( WidthsInPixels );
  470. if (WidthsInPixels == NULL)
  471. return FALSE;
  472. //
  473. // Compute the width of each column (not including the rightmost) in pixels,
  474. // and the total number of pixels used by these columns.
  475. //
  476. TotalPixels = 0;
  477. for ( i = 0; i < ClbInfo->Columns - 1; i++ )
  478. {
  479. WidthsInPixels[ i ] = Widths[ i ] * CharWidth;
  480. TotalPixels += WidthsInPixels[ i ];
  481. }
  482. //
  483. // The caller did not specify the width of the rightmost column.
  484. //
  485. if ( Widths[ i ] == -1 ) {
  486. RECT Rect;
  487. //
  488. // Set the width of the rightmost column to the remainder of the width
  489. // of the header window.
  490. //
  491. Success = GetClientRect(
  492. ClbInfo->hWndHeader,
  493. &Rect
  494. );
  495. DbgAssert( Success );
  496. WidthsInPixels[ i ] = ( Rect.right - Rect.left ) - TotalPixels;
  497. } else {
  498. //
  499. // Set the width of the rightmost column to the value supplied
  500. // by the caller.
  501. //
  502. WidthsInPixels[ i ] = Widths[ i ] * CharWidth;
  503. }
  504. //
  505. // Tell the header window the width of each column.
  506. //
  507. hdi.mask = HDI_WIDTH;
  508. for ( i = 0; i < ClbInfo->Columns - 1; i++ ) {
  509. hdi.cxy = WidthsInPixels[i];
  510. Success = Header_SetItem(ClbInfo->hWndHeader, i, &hdi);
  511. DbgAssert( Success );
  512. }
  513. //
  514. // Calc the array of right edges.
  515. //
  516. iRight = 0;
  517. for ( i = 0; i < ClbInfo->Columns - 1; i++ ) {
  518. iRight += WidthsInPixels[i];
  519. ClbInfo->Right[i] = iRight;
  520. }
  521. //
  522. // Free the array of pixel widths.
  523. //
  524. Success = FreeObject( WidthsInPixels );
  525. DbgAssert( Success );
  526. return TRUE;
  527. }
  528. BOOL
  529. AdjustClbHeadings(
  530. IN HWND hWnd,
  531. IN LPCLB_INFO ClbInfo,
  532. IN LPCWSTR Headings OPTIONAL
  533. )
  534. /*++
  535. Routine Description:
  536. AdjustClbHeadings adjust the number of columns, the widths an header text
  537. bbased on the optional Headings parameter. If Headings is NULL then the
  538. column widths are adjusted based on the old headings and the current size
  539. of the Clb. If Headings are supplied then they consist of ';' separated
  540. strings, each of which is a column heading. The number of columns and their
  541. widths is then computed based on these new headings.
  542. Arguments:
  543. hWnd - Supplies a window handle for this Clb.
  544. ClbInfo - Supplies a pointer the CLB_INFO structure for this Clb.
  545. Headings - Supplies an optional pointer to a ';' separated series of
  546. column header strings.
  547. Return Value:
  548. BOOL - Returns TRUE if the adjustment was succesfully made.
  549. --*/
  550. {
  551. BOOL Success;
  552. DWORD Columns;
  553. DWORD ColumnWidth;
  554. DWORD i;
  555. TCHAR Buffer[ MAX_PATH ];
  556. LPCWSTR Heading;
  557. RECT ClientRectHeader;
  558. HD_ITEM hdi;
  559. UINT iCount, j, iRight;
  560. DbgPointerAssert( ClbInfo );
  561. DbgAssert( ! (( ClbInfo->Columns == 0 ) && ( Headings == NULL )));
  562. //
  563. // If the user supplied headings, compute the new number of columns.
  564. //
  565. if ( ARGUMENT_PRESENT( Headings )) {
  566. //
  567. // Initialize the column counter.
  568. //
  569. Columns = 0;
  570. //
  571. // Make a copy of the new headings in the Clb object.
  572. //
  573. StringCchCopyW(ClbInfo->Headings, ARRAYSIZE(ClbInfo->Headings), Headings);
  574. //
  575. // Make a copy of the heading string so that it can be tokenized.
  576. // i.e. wcstok destroys the string.
  577. //
  578. StringCchCopy(Buffer, ARRAYSIZE(Buffer), Headings);
  579. //
  580. // Grab the first token (heading).
  581. //
  582. Heading = _tcstok( Buffer, HEADING_SEPARATOR );
  583. //
  584. // For each heading...
  585. //
  586. while ( Heading != NULL ) {
  587. //
  588. // Increment the number of columns.
  589. //
  590. Columns++;
  591. //
  592. // Get the next heading.
  593. //
  594. Heading = _tcstok( NULL, HEADING_SEPARATOR );
  595. }
  596. } else {
  597. //
  598. // Same number of Columns as before.
  599. //
  600. Columns = ClbInfo->Columns;
  601. }
  602. //
  603. // If the number of columns in the Clb is zero (i.e. this is the first
  604. // time it is being initialized) allocate the right edge array. Otherwise
  605. // reallocate the existing array if the number of columns has changed.
  606. //
  607. if ( ClbInfo->Columns == 0 )
  608. {
  609. ClbInfo->Right = AllocateObject( LONG, Columns );
  610. DbgPointerAssert( ClbInfo->Right );
  611. }
  612. else if ( Columns != ClbInfo->Columns )
  613. {
  614. //
  615. // If ReallocateObject, i.e, LocalReAlloc fails, it keeps ClbInfo->Right
  616. // as it is, and returns NULL. Ensure that this memory is freed properly,
  617. // in case of failure...
  618. //
  619. LONG * plTemp = ReallocateObject( LONG, ClbInfo->Right, Columns );
  620. DbgPointerAssert(plTemp);
  621. if (!plTemp)
  622. {
  623. FreeObject(ClbInfo->Right);
  624. }
  625. ClbInfo->Right = plTemp;
  626. }
  627. if (ClbInfo->Right == NULL)
  628. return FALSE;
  629. //
  630. // Update the number of columns in the Clb (note this may be the same
  631. // number as before).
  632. //
  633. ClbInfo->Columns = Columns;
  634. //
  635. // Compute the default column width by dividing the available space by the
  636. // number of columns.
  637. //
  638. Success = GetClientRect( ClbInfo->hWndHeader, &ClientRectHeader );
  639. DbgAssert( Success );
  640. ColumnWidth = ( ClientRectHeader.right - ClientRectHeader.left )
  641. / ClbInfo->Columns;
  642. //
  643. // Initialize the array of right edges to the width of each column.
  644. //
  645. for ( i = 0; i < ClbInfo->Columns; i++ ) {
  646. ClbInfo->Right[ i ] = ColumnWidth;
  647. }
  648. //
  649. // Update the existing header items
  650. //
  651. iCount = Header_GetItemCount(ClbInfo->hWndHeader);
  652. j = 0;
  653. hdi.mask = HDI_WIDTH;
  654. while ((j < iCount) && (j < Columns)) {
  655. hdi.cxy = ClbInfo->Right[j];
  656. Header_SetItem (ClbInfo->hWndHeader, j, &hdi);
  657. j++;
  658. }
  659. //
  660. // Add new header items if necessary.
  661. //
  662. hdi.mask = HDI_WIDTH;
  663. for (; j < Columns; j++) {
  664. hdi.cxy = ClbInfo->Right[j];
  665. Header_InsertItem (ClbInfo->hWndHeader, j, &hdi);
  666. }
  667. //
  668. // Query the header for the array of right edges.
  669. //
  670. iRight = 0;
  671. for ( i = 0; i < ClbInfo->Columns - 1; i++ ) {
  672. iRight += ClbInfo->Right[i];
  673. ClbInfo->Right[i] = iRight;
  674. }
  675. ClbInfo->Right[i] = ClientRectHeader.right;
  676. //
  677. // Copy and parse the headings so that each column's heading
  678. // can be set. These can be new or old headings.
  679. //
  680. StringCchCopy(Buffer, ARRAYSIZE(Buffer), ClbInfo->Headings);
  681. Heading = _tcstok( Buffer, HEADING_SEPARATOR );
  682. hdi.mask = HDI_TEXT | HDI_FORMAT;
  683. hdi.fmt = HDF_STRING;
  684. for ( i = 0; i < ClbInfo->Columns; i++ ) {
  685. hdi.pszText = (LPTSTR)Heading;
  686. Header_SetItem (ClbInfo->hWndHeader, i, &hdi);
  687. Heading = _tcstok( NULL, HEADING_SEPARATOR );
  688. }
  689. return TRUE;
  690. }
  691. BOOL
  692. CreateHeader(
  693. IN HWND hWnd,
  694. IN LPCLB_INFO ClbInfo,
  695. IN LPCREATESTRUCT lpcs
  696. )
  697. /*++
  698. Routine Description:
  699. Create the header portion of the Clb.
  700. Arguments:
  701. hWnd - Supplies a window handle for the parent (i.e. Clb) window.
  702. ClbInfo - Supplies a pointer the CLB_INFO structure for this Clb.
  703. lpcs - Supplies a pointer to a CREATESTRUCT structure.
  704. Return Value:
  705. BOOL - Returns TRUE if the header portion of the Clb was
  706. succesfully created.
  707. --*/
  708. {
  709. BOOL Success;
  710. RECT WindowRectHeader, rcParent;
  711. HD_LAYOUT hdl;
  712. WINDOWPOS wp;
  713. DbgHandleAssert( hWnd );
  714. DbgPointerAssert( ClbInfo );
  715. DbgPointerAssert( lpcs );
  716. //
  717. // Create the header window using the appropriate supplied styles,
  718. // augmented by additional styles needed by Clb, relative to the upper
  719. // left corner of the Clb and with a default height.
  720. // The width is adjusted in the WM_SIZE message handler.
  721. //
  722. ClbInfo->hWndHeader = CreateWindow(
  723. WC_HEADER,
  724. NULL,
  725. (lpcs->style & CLBS_HEADER) | WS_CHILD,
  726. 0,
  727. 0,
  728. 0,
  729. CW_USEDEFAULT,
  730. hWnd,
  731. ( HMENU ) ID_HEADER,
  732. NULL,
  733. NULL
  734. );
  735. DbgHandleAssert( ClbInfo->hWndHeader );
  736. if (ClbInfo->hWndHeader == NULL)
  737. return FALSE;
  738. //
  739. // Compute and save the height of the header window. This is used to
  740. // position the list box.
  741. //
  742. GetClientRect(hWnd, &rcParent);
  743. hdl.prc = &rcParent;
  744. hdl.pwpos = &wp;
  745. SendMessage(ClbInfo->hWndHeader, HDM_LAYOUT, 0, (LPARAM)&hdl);
  746. SetWindowPos(ClbInfo->hWndHeader, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags);
  747. ClbInfo->HeaderHeight = wp.cy;
  748. return TRUE;
  749. }
  750. BOOL
  751. CreateListBox(
  752. IN HWND hWnd,
  753. IN LPCLB_INFO ClbInfo,
  754. IN LPCREATESTRUCT lpcs
  755. )
  756. /*++
  757. Routine Description:
  758. Create the list box portion of the Clb.
  759. Arguments:
  760. hWnd - Supplies a window handle for the parent (i.e. Clb) window.
  761. ClbInfo - Supplies a pointer the CLB_INFO structure for this Clb.
  762. lpcs - Supplies a pointer to a CREATESTRUCT structure.
  763. Return Value:
  764. BOOL - Returns TRUE if the list box portion of the Clb was
  765. succesfully created.
  766. --*/
  767. {
  768. BOOL Success;
  769. LOGFONT LogFont;
  770. HDC hDCClientListBox;
  771. CHARSETINFO csi;
  772. DWORD dw = GetACP();
  773. if (!TranslateCharsetInfo(&dw, &csi, TCI_SRCCODEPAGE))
  774. csi.ciCharset = ANSI_CHARSET;
  775. DbgHandleAssert( hWnd );
  776. DbgPointerAssert( ClbInfo );
  777. DbgPointerAssert( lpcs );
  778. //
  779. //
  780. // Create the list box using the appropriate supplied styles,
  781. // augmented by additional styles needed by Clb, relative to the lower left
  782. // corner of the header window plus one. This additional row is reserved so
  783. // that a border can be drawn between the header and the list box. The size
  784. // is adjusted in the WM_SIZE message handler.
  785. //
  786. ClbInfo->hWndListBox = CreateWindow(
  787. L"LISTBOX",
  788. NULL,
  789. (lpcs->style & CLBS_LIST_BOX) | LBS_NOINTEGRALHEIGHT | LBS_OWNERDRAWFIXED | WS_CHILD,
  790. 0,
  791. ClbInfo->HeaderHeight + 3,
  792. 0,
  793. 0,
  794. hWnd,
  795. ( HMENU ) ID_LISTBOX,
  796. NULL,
  797. NULL
  798. );
  799. DbgHandleAssert( ClbInfo->hWndListBox );
  800. if (ClbInfo->hWndListBox == NULL)
  801. return FALSE;
  802. //
  803. // Get thd HDC for the list box.
  804. //
  805. hDCClientListBox = GetDC( ClbInfo->hWndListBox );
  806. DbgHandleAssert( hDCClientListBox );
  807. if (hDCClientListBox == NULL)
  808. return FALSE;
  809. //
  810. // Set the default font for the list box to MS Shell Dlg.
  811. //
  812. LogFont.lfHeight = MulDiv(
  813. -9,
  814. GetDeviceCaps(
  815. hDCClientListBox,
  816. LOGPIXELSY
  817. )
  818. ,72
  819. );
  820. LogFont.lfWidth = 0;
  821. LogFont.lfEscapement = 0;
  822. LogFont.lfOrientation = 0;
  823. LogFont.lfWeight = FW_NORMAL;
  824. LogFont.lfItalic = FALSE;
  825. LogFont.lfUnderline = FALSE;
  826. LogFont.lfStrikeOut = FALSE;
  827. LogFont.lfCharSet = (BYTE)csi.ciCharset;
  828. LogFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
  829. LogFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
  830. LogFont.lfQuality = DEFAULT_QUALITY;
  831. LogFont.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
  832. StringCchCopy(LogFont.lfFaceName, ARRAYSIZE(LogFont.lfFaceName), TEXT("MS Shell Dlg"));
  833. ClbInfo->hFontListBox = CreateFontIndirect( &LogFont );
  834. DbgHandleAssert( ClbInfo->hFontListBox );
  835. if (ClbInfo->hFontListBox == NULL)
  836. return FALSE;
  837. SendMessage(
  838. ClbInfo->hWndListBox,
  839. WM_SETFONT,
  840. ( WPARAM ) ClbInfo->hFontListBox,
  841. MAKELPARAM( FALSE, 0 )
  842. );
  843. //
  844. // Release the DC for the list box.
  845. //
  846. Success = ReleaseDC( ClbInfo->hWndListBox, hDCClientListBox );
  847. DbgAssert( Success );
  848. return TRUE;
  849. }
  850. LRESULT
  851. ClbWndProc(
  852. IN HWND hWnd,
  853. IN UINT message,
  854. IN WPARAM wParam,
  855. IN LPARAM lParam
  856. )
  857. /*++
  858. Routine Description:
  859. This function is the window procedure for the Clb custom control.
  860. Arguments:
  861. Standard window procedure parameters.
  862. Return Value:
  863. LRESULT - dependent on the supplied message.
  864. --*/
  865. {
  866. BOOL Success;
  867. LPCLB_INFO ClbInfo;
  868. if ( message == WM_NCCREATE ) {
  869. LONG Long;
  870. //
  871. // Save the original styles.
  872. //
  873. Long = SetWindowLong(hWnd, GWLP_USERDATA,(( LPCREATESTRUCT ) lParam )->style);
  874. DbgAssert( Long == 0 );
  875. //
  876. // Get rid of any styles that are uninteresting to the Clb.
  877. //
  878. SetWindowLong(
  879. hWnd,
  880. GWL_STYLE,
  881. (( LPCREATESTRUCT ) lParam )->style
  882. & CLBS_CLB
  883. );
  884. return TRUE;
  885. }
  886. if ( message == WM_CREATE ) {
  887. //
  888. // Assert that there is no prior per window information associated
  889. // with this Clb.
  890. //
  891. DbgAssert( RestoreClbInfo( hWnd ) == NULL );
  892. //
  893. // Restore the original styles.
  894. //
  895. (( LPCREATESTRUCT ) lParam )->style = GetWindowLong(
  896. hWnd,
  897. GWLP_USERDATA
  898. );
  899. //
  900. // Allocate a CLB_INFO object for this Clb and initialize the Clb
  901. // relevant fields.
  902. //
  903. ClbInfo = AllocateObject( CLB_INFO, 1 );
  904. DbgPointerAssert( ClbInfo );
  905. if (ClbInfo == NULL)
  906. return FALSE;
  907. //
  908. // Set the number of columns to zero so that remainder of creation
  909. // understands the state of the Clb.
  910. //
  911. ClbInfo->Columns = 0;
  912. //
  913. // Create the header portion of the Clb.
  914. //
  915. Success = CreateHeader( hWnd, ClbInfo, ( LPCREATESTRUCT ) lParam );
  916. DbgAssert( Success );
  917. //
  918. // Create the list box portion of the Clb.
  919. //
  920. Success = CreateListBox( hWnd, ClbInfo, ( LPCREATESTRUCT ) lParam );
  921. DbgAssert( Success );
  922. //
  923. // Adjust the column number, widths based on the heading text.
  924. //
  925. Success = AdjustClbHeadings( hWnd, ClbInfo, (( LPCREATESTRUCT ) lParam )->lpszName );
  926. DbgAssert( Success );
  927. //
  928. // Everything was succesfully created so set the Clb's signature
  929. // and save it away as part of the per window data.
  930. //
  931. SetSignature( ClbInfo );
  932. SaveClbInfo( ClbInfo );
  933. return 0;
  934. }
  935. //
  936. // Get the ClbInfo object for this Clb and make sure that its already
  937. // been created i.e. WM_CREATE was already executed and thereby initialized
  938. // and saved a ClbInfo object.
  939. //
  940. ClbInfo = RestoreClbInfo( hWnd );
  941. if ( ClbInfo != NULL ) {
  942. //
  943. // Validate that this really is a ClbInfo object.
  944. //
  945. DbgAssert( CheckSignature( ClbInfo ));
  946. switch ( message ) {
  947. case WM_DESTROY:
  948. {
  949. //
  950. // Delete the font used in the list box.
  951. //
  952. Success = DeleteObject( ClbInfo->hFontListBox );
  953. DbgAssert( Success );
  954. //
  955. // Delete the array of right habd edges.
  956. //
  957. Success = FreeObject( ClbInfo->Right );
  958. DbgAssert( Success );
  959. //
  960. // Delete the CLB_INFO object for this window.
  961. //
  962. Success = FreeObject( ClbInfo );
  963. DbgAssert( Success );
  964. SaveClbInfo ( ClbInfo );
  965. return 0;
  966. }
  967. case WM_PAINT:
  968. {
  969. PAINTSTRUCT ps;
  970. RECT Rect;
  971. POINT Points[ 2 ];
  972. HDC hDC;
  973. HPEN hPen;
  974. hDC = BeginPaint( hWnd, &ps );
  975. DbgAssert( hDC == ps.hdc );
  976. Success = GetClientRect( hWnd, &Rect );
  977. DbgAssert( Success );
  978. Points[ 0 ].x = 0;
  979. Points[ 0 ].y = ClbInfo->HeaderHeight + 1;
  980. Points[ 1 ].x = Rect.right - Rect.left;
  981. Points[ 1 ].y = ClbInfo->HeaderHeight + 1;
  982. hPen = GetStockObject( BLACK_PEN );
  983. DbgHandleAssert( hPen );
  984. hPen = SelectObject( hDC, hPen );
  985. Success = Polyline( hDC, Points, NumberOfEntries( Points ));
  986. DbgAssert( Success );
  987. hPen = SelectObject( hDC, hPen );
  988. if (hPen) {
  989. Success = DeleteObject( hPen );
  990. DbgAssert( Success );
  991. }
  992. Success = EndPaint( hWnd, &ps );
  993. DbgAssert( Success );
  994. return 0;
  995. }
  996. case WM_COMMAND:
  997. switch ( LOWORD( wParam )) {
  998. case ID_LISTBOX:
  999. switch ( HIWORD( wParam )) {
  1000. case LBN_DBLCLK:
  1001. case LBN_KILLFOCUS:
  1002. case LBN_SELCHANGE:
  1003. {
  1004. //
  1005. // These messages come to ClbWndProc because it is the parent
  1006. // of the list box, but they are really intended for the parent
  1007. // of the Clb.
  1008. //
  1009. HWND hWndParent;
  1010. //
  1011. // Forward the message to the Clb's parent if it has a parent.
  1012. //
  1013. hWndParent = GetParent( hWnd );
  1014. DbgHandleAssert( hWndParent );
  1015. if ( hWndParent != NULL ) {
  1016. //
  1017. // Replace the control id and handle with the Clb's.
  1018. //
  1019. *((WORD *)(&wParam)) = (WORD)GetDlgCtrlID( hWnd );
  1020. DbgAssert( LOWORD( wParam ) != 0 );
  1021. lParam = ( LPARAM ) hWnd;
  1022. //
  1023. // Forward the message...
  1024. //
  1025. return SendMessage( hWndParent, message, wParam, lParam );
  1026. }
  1027. }
  1028. }
  1029. break;
  1030. }
  1031. break;
  1032. //
  1033. // Forward to listbox.
  1034. //
  1035. case LB_GETCURSEL:
  1036. case LB_SETCURSEL:
  1037. case LB_FINDSTRING:
  1038. case LB_GETITEMDATA:
  1039. case LB_RESETCONTENT:
  1040. case WM_CHAR:
  1041. case WM_GETDLGCODE:
  1042. case WM_KILLFOCUS:
  1043. return SendMessage( ClbInfo->hWndListBox, message, wParam, lParam );
  1044. case WM_SETFOCUS:
  1045. {
  1046. SetFocus( ClbInfo->hWndListBox );
  1047. return 0;
  1048. }
  1049. case WM_COMPAREITEM:
  1050. {
  1051. //
  1052. // This message comes to ClbWndProc because it is the parent
  1053. // of the list box, but is really intended for the parent
  1054. // of the Clb.
  1055. //
  1056. HWND hWndParent;
  1057. //
  1058. // Forward the message to the Clb's parent if it has a parent.
  1059. //
  1060. hWndParent = GetParent( hWnd );
  1061. DbgHandleAssert( hWndParent );
  1062. if ( hWndParent != NULL ) {
  1063. int ControlId;
  1064. LPCOMPAREITEMSTRUCT lpcis;
  1065. lpcis = ( LPCOMPAREITEMSTRUCT ) lParam;
  1066. ControlId = GetDlgCtrlID( hWnd );
  1067. DbgAssert( ControlId != 0 );
  1068. //
  1069. // Modify the COMPAREITEMSTRUCT so that it refers to the Clb.
  1070. //
  1071. lpcis->CtlID = ControlId;
  1072. lpcis->hwndItem = hWnd;
  1073. //
  1074. // Forward the message...
  1075. //
  1076. return SendMessage( hWndParent, message, ( WPARAM ) ControlId, lParam );
  1077. }
  1078. break;
  1079. }
  1080. case WM_DELETEITEM:
  1081. {
  1082. LPDELETEITEMSTRUCT lpditms;
  1083. LPCLB_ROW ClbRow;
  1084. DWORD i;
  1085. DbgAssert( wParam == ID_LISTBOX );
  1086. //
  1087. // Retrieve the pointer to the DELETEITEMSTRUCT.
  1088. //
  1089. lpditms = ( LPDELETEITEMSTRUCT ) lParam;
  1090. DbgAssert(( lpditms->CtlType == ODT_LISTBOX )
  1091. &&( lpditms->CtlID == ID_LISTBOX ));
  1092. //
  1093. // If there is no data, just return.
  1094. //
  1095. if ( lpditms->itemData == 0 ) {
  1096. return TRUE;
  1097. }
  1098. //
  1099. // Retrieve the CLB_ROW object for this row.
  1100. //
  1101. ClbRow = ( LPCLB_ROW ) lpditms->itemData;
  1102. //
  1103. // For each column delete the string.
  1104. //
  1105. for ( i = 0; i < ClbInfo->Columns; i++ ) {
  1106. //
  1107. // Strings were copied with _tcsdup so they must be
  1108. // freed with free( ).
  1109. //
  1110. free( ClbRow->Strings[ i ].String );
  1111. }
  1112. //
  1113. // Free the CLB_STRING object.
  1114. //
  1115. Success = FreeObject( ClbRow->Strings );
  1116. DbgAssert( Success );
  1117. //
  1118. // Free the CLB_ROW object.
  1119. //
  1120. Success = FreeObject( ClbRow );
  1121. DbgAssert( Success );
  1122. return TRUE;
  1123. }
  1124. case WM_DRAWITEM:
  1125. {
  1126. LPDRAWITEMSTRUCT lpdis;
  1127. BOOL DrawFocus;
  1128. DbgAssert( wParam == ID_LISTBOX );
  1129. //
  1130. // Retrieve the pointer to the DRAWITEMSTRUCT.
  1131. //
  1132. lpdis = ( LPDRAWITEMSTRUCT ) lParam;
  1133. DbgAssert(( lpdis->CtlType == ODT_LISTBOX )
  1134. &&( lpdis->CtlID == ID_LISTBOX ));
  1135. //
  1136. // If there is no data, just return.
  1137. //
  1138. if ( lpdis->itemData == 0 ) {
  1139. return TRUE;
  1140. }
  1141. if ( lpdis->itemAction & ( ODA_DRAWENTIRE | ODA_SELECT )) {
  1142. DWORD i;
  1143. LPCLB_ROW ClbRow;
  1144. COLORREF TextColor;
  1145. COLORREF BkColor;
  1146. //
  1147. // Retrieve the CLB_ROW object for this row.
  1148. //
  1149. ClbRow = ( LPCLB_ROW ) lpdis->itemData;
  1150. //
  1151. // If the item is selected, set the selection colors.
  1152. //
  1153. if ( lpdis->itemState & ODS_SELECTED ) {
  1154. BkColor = COLOR_HIGHLIGHT;
  1155. TextColor = COLOR_HIGHLIGHTTEXT;
  1156. } else {
  1157. BkColor = COLOR_WINDOW;
  1158. TextColor = COLOR_WINDOWTEXT;
  1159. }
  1160. BkColor = GetSysColor( BkColor );
  1161. TextColor = GetSysColor( TextColor );
  1162. BkColor = SetBkColor( lpdis->hDC, BkColor );
  1163. DbgAssert( BkColor != CLR_INVALID );
  1164. TextColor = SetTextColor( lpdis->hDC, TextColor );
  1165. DbgAssert( TextColor != CLR_INVALID );
  1166. //
  1167. // For each column display the text.
  1168. //
  1169. for ( i = 0; i < ClbInfo->Columns; i++ ) {
  1170. RECT ClipOpaqueRect;
  1171. int x;
  1172. int Left;
  1173. UINT GdiErr;
  1174. //
  1175. // Depending on the format, adjust the alignment reference
  1176. // point (x) and the clipping rectangles left edge so that
  1177. // there are five pixels between each column.
  1178. //
  1179. switch ( ClbRow->Strings[ i ].Format ) {
  1180. case CLB_LEFT:
  1181. if ( i == 0 ) {
  1182. x = 2;
  1183. } else {
  1184. x = ClbInfo->Right[ i - 1 ] + 2;
  1185. }
  1186. Left = x - 2;
  1187. break;
  1188. case CLB_RIGHT:
  1189. if ( i == 0 ) {
  1190. Left = 0;
  1191. } else {
  1192. Left = ClbInfo->Right[ i - 1 ];
  1193. }
  1194. x = ClbInfo->Right[ i ] - 3;
  1195. break;
  1196. default:
  1197. DbgAssert( FALSE );
  1198. }
  1199. //
  1200. // Set the format for this column.
  1201. //
  1202. GdiErr = SetTextAlign( lpdis->hDC, ClbRow->Strings[ i ].Format | TA_TOP );
  1203. DbgAssert( GdiErr != GDI_ERROR );
  1204. //
  1205. // Clip each string to its column width less two pixels
  1206. // (for asthetics).
  1207. //
  1208. Success = SetRect(
  1209. &ClipOpaqueRect,
  1210. Left,
  1211. lpdis->rcItem.top,
  1212. ClbInfo->Right[ i ],
  1213. lpdis->rcItem.bottom
  1214. );
  1215. DbgAssert( Success );
  1216. Success = ExtTextOut(
  1217. lpdis->hDC,
  1218. x,
  1219. lpdis->rcItem.top,
  1220. ETO_CLIPPED
  1221. | ETO_OPAQUE,
  1222. &ClipOpaqueRect,
  1223. ClbRow->Strings[ i ].String,
  1224. ClbRow->Strings[ i ].Length,
  1225. NULL
  1226. );
  1227. DbgAssert( Success );
  1228. //
  1229. // If the item has the focus, draw the focus rectangle.
  1230. //
  1231. DrawFocus = lpdis->itemState & ODS_FOCUS;
  1232. }
  1233. } else {
  1234. //
  1235. // If the Clb has the focus, display a focus rectangle
  1236. // around the selected item.
  1237. //
  1238. DrawFocus = lpdis->itemAction & ODA_FOCUS;
  1239. }
  1240. //
  1241. // If needed, toggle the focus rectangle.
  1242. //
  1243. if ( DrawFocus ) {
  1244. Success = DrawFocusRect( lpdis->hDC, &lpdis->rcItem );
  1245. DbgAssert( Success );
  1246. }
  1247. return TRUE;
  1248. }
  1249. case WM_NOTIFY:
  1250. {
  1251. HD_NOTIFY * lpNot;
  1252. HD_ITEM *pHDI;
  1253. lpNot = (HD_NOTIFY *)lParam;
  1254. pHDI = lpNot->pitem;
  1255. switch ( lpNot->hdr.code) {
  1256. static
  1257. DRAW_ERASE_LINE DrawEraseLine;
  1258. static
  1259. HPEN hPen;
  1260. static
  1261. HDC hDCClientListBox;
  1262. HD_ITEM hdi;
  1263. UINT iRight;
  1264. UINT i;
  1265. RECT ClientRectHeader;
  1266. case HDN_BEGINTRACK:
  1267. {
  1268. RECT ClientRectListBox;
  1269. //
  1270. // Get thd HDC for the list box.
  1271. //
  1272. hDCClientListBox = GetDC( ClbInfo->hWndListBox );
  1273. DbgHandleAssert( hDCClientListBox );
  1274. if (hDCClientListBox == NULL)
  1275. return FALSE;
  1276. //
  1277. // Create the pen used to display the drag position and
  1278. // select it into the in list box client area DC. Also set
  1279. // the ROP2 code so that drawing with the pen twice in the
  1280. // same place will erase it. This is what allows the
  1281. // line to drag.
  1282. //
  1283. hPen = CreatePen( PS_DOT, 1, RGB( 255, 255, 255 ));
  1284. DbgHandleAssert( hPen );
  1285. hPen = SelectObject( hDCClientListBox, hPen );
  1286. SetROP2( hDCClientListBox, R2_XORPEN );
  1287. //
  1288. // Set up the DRAW_ERASE_LINE structure so that the drag line is
  1289. // drawn from the top to the bottom of the list box at the
  1290. // current drag position.
  1291. //
  1292. Success = GetClientRect(
  1293. ClbInfo->hWndListBox,
  1294. &ClientRectListBox
  1295. );
  1296. DbgAssert( Success );
  1297. //
  1298. // Draw the initial drag line from the top to the bottom
  1299. // of the list box equivalent with the header edge grabbed
  1300. // by the user.
  1301. //
  1302. DrawEraseLine.Draw.Src.x = ClbInfo->Right[ pHDI->cxy ];
  1303. DrawEraseLine.Draw.Src.y = 0;
  1304. DrawEraseLine.Draw.Dst.x = ClbInfo->Right[ pHDI->cxy ];
  1305. DrawEraseLine.Draw.Dst.y = ClientRectListBox.bottom
  1306. - ClientRectListBox.top;
  1307. Success = DrawLine( hDCClientListBox, &DrawEraseLine );
  1308. DbgAssert( Success );
  1309. return 0;
  1310. }
  1311. case HDN_TRACK:
  1312. {
  1313. //DWORD Columns;
  1314. //
  1315. // Get new drag position.
  1316. //
  1317. iRight = 0;
  1318. hdi.mask = HDI_WIDTH;
  1319. for ( i = 0; i < ClbInfo->Columns - 1; i++ ) {
  1320. if (i != (UINT)lpNot->iItem) {
  1321. Header_GetItem(ClbInfo->hWndHeader, i, &hdi);
  1322. } else {
  1323. hdi.cxy = pHDI->cxy;
  1324. }
  1325. iRight += hdi.cxy;
  1326. ClbInfo->Right[i] = iRight;
  1327. }
  1328. GetClientRect( ClbInfo->hWndHeader, &ClientRectHeader );
  1329. ClbInfo->Right[i] = ClientRectHeader.right;
  1330. //
  1331. // Erase the old line and draw the new one at the new
  1332. // drag position.
  1333. //
  1334. Success = RedrawVerticalLine(
  1335. hDCClientListBox,
  1336. ClbInfo->Right[lpNot->iItem],
  1337. &DrawEraseLine
  1338. );
  1339. DbgAssert( Success );
  1340. return 0;
  1341. }
  1342. case HDN_ENDTRACK:
  1343. //
  1344. // Replace the old pen and delete the one created
  1345. // during HBN_BEGINDRAG.
  1346. //
  1347. hPen = SelectObject( hDCClientListBox, hPen );
  1348. if (hPen) {
  1349. Success = DeleteObject( hPen );
  1350. DbgAssert( Success );
  1351. }
  1352. //
  1353. // Release the DC for the list box.
  1354. //
  1355. Success = ReleaseDC( ClbInfo->hWndListBox, hDCClientListBox );
  1356. DbgAssert( Success );
  1357. Success = RedrawWindow(
  1358. hWnd,
  1359. NULL,
  1360. NULL,
  1361. RDW_ERASE
  1362. | RDW_INVALIDATE
  1363. | RDW_UPDATENOW
  1364. | RDW_ALLCHILDREN
  1365. );
  1366. DbgAssert( Success );
  1367. return 0;
  1368. }
  1369. break;
  1370. }
  1371. case WM_SETTEXT:
  1372. //
  1373. // Adjust the column number and widths based on the heading text.
  1374. //
  1375. Success = AdjustClbHeadings( hWnd, ClbInfo, ( LPCWSTR ) lParam );
  1376. DbgAssert( Success );
  1377. return Success;
  1378. case WM_SIZE:
  1379. {
  1380. HDWP hDWP;
  1381. LONG Width;
  1382. LONG Height;
  1383. LONG Style;
  1384. LONG VScrollWidth;
  1385. Width = LOWORD( lParam );
  1386. Height = HIWORD( lParam );
  1387. hDWP = BeginDeferWindowPos( 2 );
  1388. DbgHandleAssert( hDWP );
  1389. if (hDWP == NULL)
  1390. return FALSE;
  1391. //
  1392. // Retrieve the list box's styles.
  1393. //
  1394. Style = GetWindowLong( ClbInfo->hWndListBox, GWL_STYLE );
  1395. //
  1396. // If the list box has a vertical scroll bar compute its
  1397. // width so that the header window's width can be adjusted
  1398. // appropriately.
  1399. //
  1400. VScrollWidth = ( Style & WS_VSCROLL )
  1401. ? GetSystemMetrics( SM_CXVSCROLL )
  1402. + ( GetSystemMetrics( SM_CXBORDER ) * 2 )
  1403. : 0;
  1404. //
  1405. // Size the header window to the width of the Clb and its
  1406. // default / original height.
  1407. //
  1408. hDWP = DeferWindowPos(
  1409. hDWP,
  1410. ClbInfo->hWndHeader,
  1411. NULL,
  1412. 0,
  1413. 0,
  1414. Width - VScrollWidth,
  1415. ClbInfo->HeaderHeight,
  1416. SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER
  1417. );
  1418. DbgHandleAssert( hDWP );
  1419. if (hDWP == NULL)
  1420. return FALSE;
  1421. //
  1422. // If the list box has a vertical scroll bar, bump the width
  1423. // and height by two so that its border overwrites the Clb
  1424. // border. This eliminates a double border (and a gap) between
  1425. // the right and bottom edges of the scroll bar and the Clb.
  1426. //
  1427. if ( Style & WS_VSCROLL ) {
  1428. Height += 2;
  1429. Width += 2;
  1430. }
  1431. //
  1432. // Size the list box so that it is the size of the Clb less
  1433. // the height of the header window less the height of the
  1434. // border.
  1435. //
  1436. hDWP = DeferWindowPos(
  1437. hDWP,
  1438. ClbInfo->hWndListBox,
  1439. NULL,
  1440. 0,
  1441. 0,
  1442. Width,
  1443. Height - ClbInfo->HeaderHeight - 3,
  1444. SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER
  1445. );
  1446. DbgHandleAssert( hDWP );
  1447. if (hDWP == NULL)
  1448. return FALSE;
  1449. Success = EndDeferWindowPos( hDWP );
  1450. DbgAssert( Success );
  1451. break;
  1452. }
  1453. }
  1454. }
  1455. return DefWindowProc( hWnd, message, wParam, lParam );
  1456. }