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.

5973 lines
140 KiB

  1. /*
  2. * @doc INTERNAL
  3. *
  4. * @module LBHOST.CPP -- Text Host for CreateWindow() Rich Edit
  5. * List Box Control |
  6. * Implements CLstBxWinHost message
  7. *
  8. * Original Author:
  9. * Jerry Kim
  10. *
  11. * History: <nl>
  12. * 12/15/97 - v-jerrki Created
  13. *
  14. * Set tabs every four (4) columns
  15. *
  16. * Copyright (c) 1997-2000 Microsoft Corporation. All rights reserved.
  17. */
  18. #include "_common.h"
  19. #ifndef NOLISTCOMBOBOXES
  20. #include "_host.h"
  21. #include "imm.h"
  22. #include "_format.h"
  23. #include "_edit.h"
  24. #include "_cfpf.h"
  25. #include "_cbhost.h"
  26. ASSERTDATA
  27. // REListbox scroll notification
  28. #define LBN_PRESCROLL 0x04000
  29. #define LBN_POSTSCROLL 0x08000
  30. // special define to set VSCROLL topindex to index directly
  31. #define SB_SETINDEX 0x0fff0
  32. #ifdef DEBUG
  33. const UINT db_rgLBUnsupportedStyle[] = {
  34. LBS_MULTICOLUMN,
  35. LBS_NODATA,
  36. LBS_NOREDRAW,
  37. LBS_NOSEL,
  38. 0
  39. };
  40. const UINT db_rgLBUnsupportedMsg[] = {
  41. LB_GETLOCALE,
  42. LB_SETLOCALE,
  43. // LB_INITSTORAGE,
  44. LB_ITEMFROMPOINT,
  45. LB_SETANCHORINDEX,
  46. LB_SETCOLUMNWIDTH,
  47. LB_ADDFILE,
  48. LB_DIR,
  49. EM_GETLIMITTEXT,
  50. EM_POSFROMCHAR,
  51. EM_CHARFROMPOS,
  52. EM_SCROLLCARET,
  53. EM_CANPASTE,
  54. EM_DISPLAYBAND,
  55. EM_EXGETSEL,
  56. EM_EXLIMITTEXT,
  57. EM_EXLINEFROMCHAR,
  58. EM_EXSETSEL,
  59. EM_FINDTEXT,
  60. EM_FORMATRANGE,
  61. EM_GETEVENTMASK,
  62. EM_GETOLEINTERFACE,
  63. EM_GETPARAFORMAT,
  64. EM_GETSELTEXT,
  65. EM_HIDESELECTION,
  66. EM_PASTESPECIAL,
  67. EM_REQUESTRESIZE,
  68. EM_SELECTIONTYPE,
  69. EM_SETBKGNDCOLOR,
  70. EM_SETEVENTMASK,
  71. EM_SETOLECALLBACK,
  72. EM_SETTARGETDEVICE,
  73. EM_STREAMIN,
  74. EM_STREAMOUT,
  75. EM_GETTEXTRANGE,
  76. EM_FINDWORDBREAK,
  77. EM_SETOPTIONS,
  78. EM_GETOPTIONS,
  79. EM_FINDTEXTEX,
  80. #ifdef _WIN32
  81. EM_GETWORDBREAKPROCEX,
  82. EM_SETWORDBREAKPROCEX,
  83. #endif
  84. /* Richedit v2.0 messages */
  85. EM_SETUNDOLIMIT,
  86. EM_REDO,
  87. EM_CANREDO,
  88. EM_GETUNDONAME,
  89. EM_GETREDONAME,
  90. EM_STOPGROUPTYPING,
  91. EM_SETTEXTMODE,
  92. EM_GETTEXTMODE,
  93. EM_AUTOURLDETECT,
  94. EM_GETAUTOURLDETECT,
  95. EM_SHOWSCROLLBAR,
  96. /* East Asia specific messages */
  97. EM_SETPUNCTUATION,
  98. EM_GETPUNCTUATION,
  99. EM_SETWORDWRAPMODE,
  100. EM_GETWORDWRAPMODE,
  101. EM_SETIMECOLOR,
  102. EM_GETIMECOLOR,
  103. EM_CONVPOSITION,
  104. EM_SETLANGOPTIONS,
  105. EM_GETLANGOPTIONS,
  106. EM_GETIMECOMPMODE,
  107. EM_FINDTEXTW,
  108. EM_FINDTEXTEXW,
  109. /* RE3.0 FE messages */
  110. EM_RECONVERSION,
  111. EM_SETIMEMODEBIAS,
  112. EM_GETIMEMODEBIAS,
  113. /* Extended edit style specific messages */
  114. 0
  115. };
  116. // Checks if the style is in the passed in array
  117. BOOL LBCheckStyle(UINT msg, const UINT* rg)
  118. {
  119. for (int i = 0; rg[i]; i++)
  120. if (rg[i] & msg)
  121. {
  122. char Buffer[128];
  123. sprintf(Buffer, "Unsupported style recieved 0x0%lx", rg[i]);
  124. AssertSz(FALSE, Buffer);
  125. return TRUE;
  126. }
  127. return FALSE;
  128. }
  129. // Checks if the msg is in the passed in array
  130. BOOL LBCheckMessage(UINT msg, const UINT* rg)
  131. {
  132. for (int i = 0; rg[i]; i++)
  133. if (rg[i] == msg)
  134. {
  135. char Buffer[128];
  136. sprintf(Buffer, "Unsupported message recieved 0x0%lx", msg);
  137. AssertSz(FALSE, Buffer);
  138. return TRUE;
  139. }
  140. return FALSE;
  141. }
  142. #define CHECKSTYLE(msg) LBCheckStyle(msg, db_rgLBUnsupportedStyle)
  143. #define CHECKMESSAGE(msg) LBCheckMessage(msg, db_rgLBUnsupportedMsg)
  144. #else
  145. #define CHECKSTYLE(msg)
  146. #define CHECKMESSAGE(msg)
  147. #endif
  148. // internal listbox messages
  149. #define LB_KEYDOWN WM_USER+1
  150. // UNDONE:
  151. // Should this go into _w32sys.h??
  152. #ifndef CSTR_LESS_THAN
  153. //
  154. // Compare String Return Values.
  155. //
  156. #define CSTR_LESS_THAN 1 // string 1 less than string 2
  157. #define CSTR_EQUAL 2 // string 1 equal to string 2
  158. #define CSTR_GREATER_THAN 3 // string 1 greater than string
  159. #endif
  160. // UNDONE : LOCALIZATION
  161. // these vary by country/region! For US they are VK_OEM_2 VK_OEM_5.
  162. // Change lboxctl2.c MapVirtualKey to character - and fix the spelling?
  163. #define VERKEY_SLASH 0xBF /* Vertual key for '/' character */
  164. #define VERKEY_BACKSLASH 0xDC /* Vertual key for '\' character */
  165. // Used for Listbox notifications
  166. #define LBNOTIFY_CANCEL 1
  167. #define LBNOTIFY_SELCHANGE 2
  168. #define LBNOTIFY_DBLCLK 4
  169. // Used for LBSetSelection
  170. #define LBSEL_SELECT 1
  171. #define LBSEL_NEWANCHOR 2
  172. #define LBSEL_NEWCURSOR 4
  173. #define LBSEL_RESET 8
  174. #define LBSEL_HIGHLIGHTONLY 16
  175. #define LBSEL_DEFAULT (LBSEL_SELECT | LBSEL_NEWANCHOR | LBSEL_NEWCURSOR | LBSEL_RESET)
  176. // Used for keyboard and mouse messages
  177. #define LBKEY_NONE 0
  178. #define LBKEY_SHIFT 1
  179. #define LBKEY_CONTROL 2
  180. #define LBKEY_SHIFTCONTROL 3
  181. extern const TCHAR szCR[];
  182. // Size of allocated string
  183. #define LBSEARCH_MAXSIZE 256
  184. // Helper function in edit.cpp
  185. LONG GetECDefaultHeightAndWidth(
  186. ITextServices *pts,
  187. HDC hdc,
  188. LONG lZoomNumerator,
  189. LONG lZoomDenominator,
  190. LONG yPixelsPerInch,
  191. LONG *pxAveWidth,
  192. LONG *pxOverhang,
  193. LONG *pxUnderhang);
  194. // helper function for compare string. This function checks for null strings
  195. // because CStrIn doesn't like initializing string with zero length
  196. int CompareStringWrapper(
  197. LCID Locale, // locale identifier
  198. DWORD dwCmpFlags, // comparison-style options
  199. LPCWSTR lpString1, // pointer to first string
  200. int cch1, // size, in bytes or characters, of first string
  201. LPCWSTR lpString2, // pointer to second string
  202. int cch2 // size, in bytes or characters, of second string
  203. )
  204. {
  205. // check if one of the 2 strings is 0-length if so then
  206. // no need to proceed the one with the 0-length is the less
  207. if (!cch1 || !cch2)
  208. {
  209. if (cch1 < cch2)
  210. return CSTR_LESS_THAN;
  211. else if (cch1 > cch2)
  212. return CSTR_GREATER_THAN;
  213. return CSTR_EQUAL;
  214. }
  215. return CompareString(Locale, dwCmpFlags, lpString1, cch1, lpString2, cch2);
  216. }
  217. template<class CLbData> CLbData
  218. CDynamicArray<CLbData>::_sDummy = {0, 0};
  219. //////////////////////////// System Window Procs ////////////////////////////
  220. /*
  221. * RichListBoxWndProc (hwnd, msg, wparam, lparam)
  222. *
  223. * @mfunc
  224. * Handle window messages pertinent to the host and pass others on to
  225. * text services.
  226. *
  227. * @rdesc
  228. * LRESULT = (code processed) ? 0 : 1
  229. */
  230. extern "C" LRESULT CALLBACK RichListBoxWndProc(
  231. HWND hwnd,
  232. UINT msg,
  233. WPARAM wparam,
  234. LPARAM lparam)
  235. {
  236. TRACEBEGINPARAM(TRCSUBSYSHOST, TRCSCOPEINTERN, "RichListBoxWndProc", msg);
  237. LRESULT lres = 0;
  238. HRESULT hr;
  239. CLstBxWinHost *phost = (CLstBxWinHost *) GetWindowLongPtr(hwnd, ibPed);
  240. BOOL fRecalcHeight = FALSE;
  241. #ifdef DEBUG
  242. Tracef(TRCSEVINFO, "hwnd %lx, msg %lx, wparam %lx, lparam %lx", hwnd, msg, wparam, lparam);
  243. #endif // DEBUG
  244. switch(msg)
  245. {
  246. case WM_NCCREATE:
  247. return CLstBxWinHost::OnNCCreate(hwnd, (CREATESTRUCT *)lparam);
  248. case WM_CREATE:
  249. // We may be on a system with no WM_NCCREATE (e.g. WINCE)
  250. if (!phost)
  251. {
  252. (void) CLstBxWinHost::OnNCCreate(hwnd, (CREATESTRUCT *) lparam);
  253. phost = (CLstBxWinHost *) GetWindowLongPtr(hwnd, ibPed);
  254. }
  255. break;
  256. case WM_DESTROY:
  257. if(phost)
  258. CLstBxWinHost::OnNCDestroy(phost);
  259. return 0;
  260. }
  261. if (!phost)
  262. return ::DefWindowProc(hwnd, msg, wparam, lparam);
  263. // in certain out-of-memory situations, clients may try to re-enter us
  264. // with calls. Just bail on the call if we don't have a text services
  265. // pointer.
  266. if(!phost->_pserv)
  267. return 0;
  268. // stabilize ourselves
  269. phost->AddRef();
  270. CHECKMESSAGE(msg);
  271. long nTemp = 0;
  272. switch(msg)
  273. {
  274. ///////////////////////Painting. Messages///////////////////////////////
  275. case WM_NCPAINT:
  276. lres = ::DefWindowProc(hwnd, msg, wparam, lparam);
  277. phost->OnSysColorChange();
  278. break;
  279. case WM_PRINTCLIENT:
  280. case WM_PAINT:
  281. {
  282. PAINTSTRUCT ps;
  283. RECT rc;
  284. HPALETTE hpalOld = NULL;
  285. HDC hdc;
  286. RECT rcClient;
  287. BOOL fErase = TRUE;
  288. //RAID 6964: WM_PRINTCLIENT should not call BeginPaint. If a HDC is passed
  289. //down in the wparam, use it instead of calling BeginPaint.
  290. if (!wparam)
  291. {
  292. hdc = BeginPaint(hwnd, &ps);
  293. fErase = ps.fErase;
  294. }
  295. else
  296. hdc = (HDC) wparam;
  297. // Since we are using the CS_PARENTDC style, make sure
  298. // the clip region is limited to our client window.
  299. GetClientRect(hwnd, &rcClient);
  300. // Set up the palette for drawing our data
  301. if(phost->_hpal)
  302. {
  303. hpalOld = SelectPalette(hdc, phost->_hpal, TRUE);
  304. RealizePalette(hdc);
  305. }
  306. SaveDC(hdc);
  307. IntersectClipRect(hdc, rcClient.left, rcClient.top, rcClient.right,
  308. rcClient.bottom);
  309. if (!phost->_fOwnerDraw)
  310. {
  311. phost->_pserv->TxDraw(
  312. DVASPECT_CONTENT, // Draw Aspect
  313. -1, // Lindex
  314. NULL, // Info for drawing optimazation
  315. NULL, // target device information
  316. hdc, // Draw device HDC
  317. NULL, // Target device HDC
  318. (const RECTL *) &rcClient,// Bounding client rectangle
  319. NULL, // Clipping rectangle for metafiles
  320. &ps.rcPaint, // Update rectangle
  321. NULL, // Call back function
  322. NULL, // Call back parameter
  323. TXTVIEW_ACTIVE); // What view - the active one!
  324. }
  325. else if (phost->LbEnableDraw())
  326. {
  327. // Owner draw
  328. int nViewsize = phost->GetViewSize();
  329. int nCount = phost->GetCount();
  330. int nTopidx = phost->GetTopIndex();
  331. if (!phost->_fOwnerDrawVar)
  332. {
  333. // notify each visible item and then the one which has the focus
  334. int nBottom = min(nCount, nTopidx + nViewsize);
  335. if (nBottom >= nCount || !phost->IsItemViewable(nBottom))
  336. nBottom--;
  337. for (int i = nTopidx; i <= nBottom; i++)
  338. {
  339. // get Rect of region and see if it intersects
  340. phost->LbGetItemRect(i, &rc);
  341. if (IntersectRect(&rc, &rc, &ps.rcPaint))
  342. {
  343. //first erase the background and notify parent to draw
  344. if (fErase)
  345. FillRect(hdc, &rc, (HBRUSH)(COLOR_WINDOW + 1));
  346. phost->LbDrawItemNotify(hdc, i, ODA_DRAWENTIRE, phost->IsSelected(i) ? ODS_SELECTED : 0);
  347. }
  348. }
  349. // Now draw onto the area where drawing may not have been done or erased
  350. if (fErase)
  351. {
  352. int nDiff = nCount - nTopidx;
  353. if (nDiff < nViewsize ||
  354. (phost->_fNoIntegralHeight && nDiff == nViewsize))
  355. {
  356. rc = rcClient;
  357. if (nDiff < 0)
  358. nDiff *= -1; // lets be positive
  359. rc.top = nDiff * phost->GetItemHeight();
  360. if (IntersectRect(&rc, &rc, &ps.rcPaint))
  361. FillRect(hdc, &rc, (HBRUSH)(COLOR_WINDOW + 1));
  362. }
  363. }
  364. }
  365. else
  366. {
  367. // Owner draw with variable height case
  368. rc = rcClient;
  369. rc.left = 0;
  370. rc.bottom = rc.top;
  371. for (int i = nTopidx; i < nCount && rc.bottom < rcClient.bottom; i++)
  372. {
  373. RECT rcIntersect;
  374. rc.top = rc.bottom;
  375. rc.bottom = rc.top + phost->_rgData[i]._uHeight;
  376. if (IntersectRect(&rcIntersect, &rc, &ps.rcPaint))
  377. {
  378. //first erase the background and notify parent to draw
  379. if (fErase)
  380. FillRect(hdc, &rcIntersect, (HBRUSH)(COLOR_WINDOW + 1));
  381. phost->LbDrawItemNotify(hdc, i, ODA_DRAWENTIRE, phost->IsSelected(i) ? ODS_SELECTED : 0);
  382. }
  383. }
  384. if (fErase)
  385. {
  386. // Now draw onto the area where drawing may not have been done or erased
  387. if (rc.bottom < rcClient.bottom)
  388. {
  389. rc.top = rc.bottom;
  390. rc.bottom = rcClient.bottom;
  391. if (IntersectRect(&rc, &rc, &ps.rcPaint))
  392. FillRect(hdc, &rc, (HBRUSH)(COLOR_WINDOW + 1));
  393. }
  394. }
  395. }
  396. }
  397. // Restore palette if there is one
  398. #ifndef NOPALETTE
  399. if(hpalOld)
  400. SelectPalette(hdc, hpalOld, TRUE);
  401. #endif
  402. RestoreDC(hdc, -1);
  403. // NOTE: Bug #5431
  404. // This bug could be fixed by replacing the hDC to NULL
  405. // The hdc can be clipped from BeginPaint API. So just pass in NULL
  406. // when drawing focus rect
  407. phost->SetCursor(hdc, phost->GetCursor(), FALSE);
  408. if (!wparam)
  409. EndPaint(hwnd, &ps);
  410. }
  411. break;
  412. /////////////////////////Mouse Messages/////////////////////////////////
  413. case WM_RBUTTONDOWN:
  414. case WM_RBUTTONDBLCLK:
  415. case WM_MBUTTONDBLCLK:
  416. case WM_MBUTTONDOWN:
  417. break;
  418. case WM_LBUTTONDBLCLK:
  419. phost->_fDblClick = 1;
  420. /* Fall through case */
  421. case WM_LBUTTONDOWN:
  422. if (!phost->_fFocus)
  423. SetFocus(hwnd);
  424. phost->OnLButtonDown(wparam, lparam);
  425. break;
  426. case WM_MOUSEMOVE:
  427. if (!phost->GetCapture())
  428. break;
  429. phost->OnMouseMove(wparam, lparam);
  430. break;
  431. case WM_LBUTTONUP:
  432. if (!phost->GetCapture())
  433. break;
  434. phost->OnLButtonUp(wparam, lparam, LBN_SELCHANGE);
  435. break;
  436. case WM_MOUSEWHEEL:
  437. if (wparam & (MK_SHIFT | MK_CONTROL))
  438. goto defwndproc;
  439. lres = phost->OnMouseWheel(wparam, lparam);
  440. break;
  441. ///////////////////////KeyBoard Messages////////////////////////////////
  442. case WM_KEYDOWN:
  443. phost->OnKeyDown(LOWORD(wparam), lparam, 0);
  444. break;
  445. case WM_CHAR:
  446. if (W32->OnWin9x() || phost->_fANSIwindow)
  447. {
  448. CW32System::WM_CHAR_INFO wmci;
  449. wmci._fAccumulate = phost->_fAccumulateDBC != 0;
  450. W32->AnsiFilter( msg, wparam, lparam, (void *) &wmci );
  451. if (wmci._fLeadByte)
  452. {
  453. phost->_fAccumulateDBC = TRUE;
  454. phost->_chLeadByte = wparam << 8;
  455. goto Exit; // Wait for trail byte
  456. }
  457. else if (wmci._fTrailByte)
  458. {
  459. // UNDONE:
  460. // Need to see what we should do in WM_IME_CHAR
  461. wparam = phost->_chLeadByte | wparam;
  462. phost->_fAccumulateDBC = FALSE;
  463. phost->_chLeadByte = 0;
  464. msg = WM_IME_CHAR;
  465. goto serv;
  466. }
  467. else if (wmci._fIMEChar)
  468. {
  469. msg = WM_IME_CHAR;
  470. goto serv;
  471. }
  472. else if (wmci._fIMEChar)
  473. {
  474. msg = WM_IME_CHAR;
  475. goto serv;
  476. }
  477. }
  478. phost->OnChar(LOWORD(wparam), lparam);
  479. break;
  480. case WM_TIMER:
  481. if (phost->OnTimer(wparam, lparam))
  482. goto serv;
  483. break;
  484. case LBCB_TRACKING:
  485. phost->OnCBTracking(wparam, lparam);
  486. break;
  487. //UNDONE:
  488. // Messages should be ordered from most often called --> least often called
  489. //
  490. case LB_GETITEMRECT:
  491. Assert(lparam);
  492. lres = -1;
  493. if (((wparam < (unsigned)phost->GetCount()) &&
  494. phost->IsItemViewable((long)wparam)) || wparam == (unsigned int)-1 ||
  495. wparam == 0 && phost->GetCount() == 0)
  496. lres = phost->LbGetItemRect(wparam, (RECT*)lparam);
  497. break;
  498. ///////////////////////ListBox Messages/////////////////////////////////
  499. case LB_GETITEMDATA:
  500. if ((unsigned)phost->GetCount() <= wparam)
  501. lres = LB_ERR;
  502. else
  503. lres = phost->GetData(wparam);
  504. break;
  505. case LB_SETITEMDATA:
  506. lres = LB_ERR;
  507. if ((int)wparam >= -1 && (int)wparam < phost->GetCount())
  508. {
  509. // if index is -1 this means all the dataItems are set
  510. // to the value
  511. lres = 1;
  512. if (wparam == -1)
  513. phost->LbSetItemData(0, phost->GetCount() - 1, lparam);
  514. else
  515. phost->LbSetItemData(wparam, wparam, lparam);
  516. }
  517. break;
  518. case LB_GETSELCOUNT:
  519. if (lparam != NULL || wparam != 0)
  520. {
  521. lres = LB_ERR;
  522. break;
  523. }
  524. // FALL through case
  525. case LB_GETSELITEMS:
  526. // retrieves all the selected items in the list
  527. lres = LB_ERR;
  528. if (!phost->IsSingleSelection())
  529. {
  530. int j = 0;
  531. for (int i = 0; i < phost->GetCount(); i++)
  532. {
  533. if (phost->IsSelected(i))
  534. {
  535. if (lparam)
  536. {
  537. if (j < (int)wparam)
  538. ((int*)lparam)[j] = i;
  539. else
  540. break; // exced the buffer size
  541. }
  542. j++;
  543. }
  544. }
  545. lres = j;
  546. }
  547. break;
  548. case LB_GETSEL:
  549. // return the select state of the passed in index
  550. lres = LB_ERR;
  551. if ((int)wparam >= 0 && (int)wparam < phost->GetCount())
  552. lres = phost->IsSelected((long)wparam);
  553. break;
  554. case LB_GETCURSEL:
  555. // Get the current selection
  556. lres = phost->LbGetCurSel();
  557. break;
  558. case LB_GETTEXTLEN:
  559. // Retieves the text at the requested index
  560. lres = LB_ERR;
  561. if (wparam < (unsigned)phost->GetCount())
  562. lres = phost->GetString(wparam, (PWCHAR)NULL);
  563. break;
  564. case LB_GETTEXT:
  565. // Retieves the text at the requested index
  566. lres = LB_ERR;
  567. if ((int)lparam != NULL && (int)wparam >= 0 && (int)wparam < phost->GetCount())
  568. lres = phost->GetString(wparam, (PWCHAR)lparam);
  569. break;
  570. case LB_RESETCONTENT:
  571. // Reset the contents
  572. lres = phost->LbDeleteString(0, phost->GetCount() - 1);
  573. break;
  574. case LB_DELETESTRING:
  575. // Delete requested item
  576. lres = phost->LbDeleteString(wparam, wparam);
  577. break;
  578. case LB_ADDSTRING:
  579. lres = phost->LbInsertString((phost->_fSort) ? -2 : -1, (LPCTSTR)lparam);
  580. break;
  581. case LB_INSERTSTRING:
  582. lres = LB_ERR;
  583. if (wparam <= (unsigned long)phost->GetCount() || (signed int)wparam == -1 || wparam == 0)
  584. lres = phost->LbInsertString(wparam, (LPCTSTR)lparam);
  585. break;
  586. case LB_GETCOUNT:
  587. // retrieve the count
  588. lres = phost->GetCount();
  589. break;
  590. case LB_GETTOPINDEX:
  591. // Just return the top index
  592. lres = phost->GetTopIndex();
  593. break;
  594. case LB_GETCARETINDEX:
  595. lres = phost->GetCursor();
  596. break;
  597. case LB_GETANCHORINDEX:
  598. lres = phost->GetAnchor();
  599. break;
  600. case LB_FINDSTRINGEXACT:
  601. // For NT compatibility
  602. wparam++;
  603. // Find and select the item matching the string text
  604. if ((int)wparam >= phost->GetCount() || (int)wparam < 0)
  605. wparam = 0;
  606. lres = phost->LbFindString(wparam, (LPCTSTR)lparam, TRUE);
  607. if (0 <= lres)
  608. break;
  609. lres = LB_ERR;
  610. break;
  611. case LB_FINDSTRING:
  612. // For NT compatibility
  613. wparam++;
  614. // Find and select the item matching the string text
  615. if (wparam >= (unsigned)phost->GetCount())
  616. wparam = 0;
  617. lres = phost->LbFindString(wparam, (LPCTSTR)lparam, FALSE);
  618. if (0 > lres)
  619. lres = LB_ERR;
  620. break;
  621. case LB_SELECTSTRING:
  622. if (phost->IsSingleSelection())
  623. {
  624. // For NT compatibility
  625. wparam++;
  626. // Find and select the item matching the string text
  627. if ((int)wparam >= phost->GetCount() || (int)wparam < 0)
  628. wparam = 0;
  629. lres = phost->LbFindString(wparam, (LPCTSTR)lparam, FALSE);
  630. if (0 <= lres)
  631. {
  632. // bug fix #5260 - need to move to selected item first
  633. // Unselect last item and select new one
  634. Assert(lres >= 0 && lres < phost->GetCount());
  635. if (phost->LbShowIndex(lres, FALSE) && phost->LbSetSelection(lres, lres, LBSEL_DEFAULT, lres, lres))
  636. {
  637. #ifndef NOACCESSIBILITY
  638. phost->_dwWinEvent = EVENT_OBJECT_FOCUS;
  639. phost->_fNotifyWinEvt = TRUE;
  640. phost->TxNotify(phost->_dwWinEvent, NULL);
  641. phost->_dwWinEvent = EVENT_OBJECT_SELECTION;
  642. phost->_fNotifyWinEvt = TRUE;
  643. phost->TxNotify(phost->_dwWinEvent, NULL);
  644. #endif
  645. break;
  646. }
  647. }
  648. }
  649. // If failed then let it fall through to the LB_ERR
  650. lres = LB_ERR;
  651. break;
  652. case LB_SETSEL:
  653. // We only update the GetAnchor() and _nCursor if we are selecting an item
  654. if (!phost->IsSingleSelection())
  655. {
  656. // We need to return zero to mimic system listbox
  657. if (!phost->GetCount())
  658. break;
  659. //bug fix #4265
  660. int nStart = lparam;
  661. int nEnd = lparam;
  662. int nAnCur = lparam;
  663. if (lparam == (unsigned long)-1)
  664. {
  665. nAnCur = phost->GetCursor();
  666. nStart = 0;
  667. nEnd = phost->GetCount() - 1;
  668. }
  669. if (phost->LbSetSelection(nStart, nEnd, (BOOL)wparam ?
  670. LBSEL_SELECT | LBSEL_NEWANCHOR | LBSEL_NEWCURSOR : 0, nAnCur, nAnCur))
  671. {
  672. if (wparam && lparam != ((unsigned long)-1) && nAnCur >= 0 && nAnCur < phost->GetCount()
  673. && !phost->IsItemViewable(nAnCur)) // Is selected item in view?
  674. phost->LbShowIndex(nAnCur, FALSE); // scroll to the selected item
  675. #ifndef NOACCESSIBILITY
  676. if (lparam == (unsigned long)-1)
  677. {
  678. phost->_dwWinEvent = EVENT_OBJECT_SELECTIONWITHIN;
  679. }
  680. else if (wparam)
  681. {
  682. phost->_dwWinEvent = EVENT_OBJECT_FOCUS;
  683. phost->_fNotifyWinEvt = TRUE;
  684. phost->TxNotify(phost->_dwWinEvent, NULL);
  685. phost->_dwWinEvent = EVENT_OBJECT_SELECTION;
  686. }
  687. else
  688. {
  689. phost->_nAccessibleIdx = lparam + 1;
  690. phost->_dwWinEvent = EVENT_OBJECT_SELECTIONREMOVE;
  691. }
  692. phost->_fNotifyWinEvt = TRUE;
  693. phost->TxNotify(phost->_dwWinEvent, NULL);
  694. #endif
  695. break;
  696. }
  697. }
  698. // We only get here if error occurs or list box is a singel sel Listbox
  699. lres = LB_ERR;
  700. break;
  701. case LB_SELITEMRANGEEX:
  702. // For this message we need to munge the messages a little bit so it
  703. // conforms with LB_SETITEMRANGE
  704. if ((int)lparam > (int)wparam)
  705. {
  706. nTemp = MAKELONG(wparam, lparam);
  707. wparam = 1;
  708. lparam = nTemp;
  709. }
  710. else
  711. {
  712. nTemp = MAKELONG(lparam, wparam);
  713. wparam = 0;
  714. lparam = nTemp;
  715. }
  716. /* Fall through case */
  717. case LB_SELITEMRANGE:
  718. // We have to make sure the range is valid
  719. if (LOWORD(lparam) >= phost->GetCount())
  720. {
  721. if (HIWORD(lparam) >= phost->GetCount())
  722. //nothing to do so exit without error
  723. break;
  724. lparam = MAKELONG(HIWORD(lparam), phost->GetCount() - 1);
  725. }
  726. else if (HIWORD(lparam) > LOWORD(lparam))
  727. {
  728. // NT swaps the start and end value if start > end
  729. lparam = MAKELONG(LOWORD(lparam), HIWORD(lparam) < phost->GetCount() ?
  730. HIWORD(lparam) : phost->GetCount()-1);
  731. }
  732. // Item range messages do not effect the GetAnchor() nor the _nCursor
  733. if (!phost->IsSingleSelection() && phost->LbSetSelection(HIWORD(lparam),
  734. LOWORD(lparam), (wparam) ? LBSEL_SELECT : 0, 0, 0))
  735. {
  736. #ifndef NOACCESSIBILITY
  737. phost->_dwWinEvent = EVENT_OBJECT_SELECTIONWITHIN;
  738. phost->_fNotifyWinEvt = TRUE;
  739. phost->TxNotify(phost->_dwWinEvent, NULL);
  740. #endif
  741. break;
  742. }
  743. // We only get here if error occurs or list box is a singel sel Listbox
  744. lres = LB_ERR;
  745. break;
  746. case LB_SETCURSEL:
  747. // Only single selection list box can call this!!
  748. if (phost->IsSingleSelection())
  749. {
  750. // -1 should return LB_ERR and turn off any selection
  751. // special flag indicating no items should be selected
  752. if (wparam == (unsigned)-1)
  753. {
  754. // turn-off any selections
  755. int nCurrentCursor = phost->GetCursor();
  756. phost->LbSetSelection(phost->GetCursor(), phost->GetCursor(), LBSEL_RESET, 0, 0);
  757. phost->SetCursor(NULL, -1, phost->_fFocus);
  758. #ifndef NOACCESSIBILITY
  759. if (nCurrentCursor != -1)
  760. {
  761. phost->_dwWinEvent = EVENT_OBJECT_FOCUS;
  762. phost->_nAccessibleIdx = nCurrentCursor + 1;
  763. phost->_fNotifyWinEvt = TRUE;
  764. phost->TxNotify(phost->_dwWinEvent, NULL);
  765. phost->_dwWinEvent = EVENT_OBJECT_SELECTIONREMOVE;
  766. phost->_nAccessibleIdx = nCurrentCursor + 1;
  767. phost->_fNotifyWinEvt = TRUE;
  768. phost->TxNotify(phost->_dwWinEvent, NULL);
  769. }
  770. #endif
  771. }
  772. else if (wparam < (unsigned)(phost->GetCount()))
  773. {
  774. if ((int)wparam == phost->GetCursor() && phost->IsSelected((int)wparam) &&
  775. phost->IsItemViewable((signed)wparam) ||
  776. phost->LbShowIndex(wparam, FALSE) /* bug fix #5260 - need to move to selected item first */
  777. && phost->LbSetSelection(wparam, wparam, LBSEL_DEFAULT, wparam, wparam))
  778. {
  779. lres = (unsigned)wparam;
  780. #ifndef NOACCESSIBILITY
  781. phost->_dwWinEvent = EVENT_OBJECT_FOCUS;
  782. phost->_fNotifyWinEvt = TRUE;
  783. phost->TxNotify(phost->_dwWinEvent, NULL);
  784. phost->_dwWinEvent = EVENT_OBJECT_SELECTION;
  785. phost->_fNotifyWinEvt = TRUE;
  786. phost->TxNotify(phost->_dwWinEvent, NULL);
  787. #endif
  788. break;
  789. }
  790. }
  791. }
  792. // If failed then let it fall through to the LB_ERR
  793. lres = LB_ERR;
  794. break;
  795. case LB_SETTOPINDEX:
  796. // Set the top index
  797. if ((!phost->GetCount() && !wparam) || phost->LbSetTopIndex(wparam) >= 0)
  798. break;
  799. // We get here if something went wrong
  800. lres = LB_ERR;
  801. break;
  802. case LB_SETITEMHEIGHT:
  803. if (!phost->LbSetItemHeight(wparam, lparam))
  804. lres = LB_ERR;
  805. break;
  806. case LB_GETITEMHEIGHT:
  807. lres = LB_ERR;
  808. if ((unsigned)phost->GetCount() > wparam || wparam == 0 || wparam == (unsigned)-1)
  809. {
  810. if (phost->_fOwnerDrawVar)
  811. {
  812. if ((unsigned)phost->GetCount() > wparam)
  813. lres = phost->_rgData[wparam]._uHeight;
  814. }
  815. else
  816. lres = phost->GetItemHeight();
  817. }
  818. break;
  819. case LB_SETCARETINDEX:
  820. if (((phost->GetCursor() == -1) || (!phost->IsSingleSelection()) &&
  821. (phost->GetCount() > (INT)wparam)))
  822. {
  823. /*
  824. * Set's the Cursor to the wParam
  825. * if lParam, then don't scroll if partially visible
  826. * else scroll into view if not fully visible
  827. */
  828. if (!phost->IsItemViewable(wparam) || lparam)
  829. {
  830. phost->LbShowIndex(wparam, FALSE);
  831. phost->SetCursor(NULL, wparam, TRUE);
  832. }
  833. lres = 0;
  834. }
  835. else
  836. return LB_ERR;
  837. break;
  838. case EM_SETTEXTEX:
  839. lres = LB_ERR;
  840. if (lparam)
  841. lres = phost->LbBatchInsert((WCHAR*)lparam);
  842. break;
  843. ////////////////////////Windows Messages////////////////////////////////
  844. case WM_VSCROLL:
  845. phost->OnVScroll(wparam, lparam);
  846. break;
  847. case WM_CAPTURECHANGED:
  848. lres = phost->OnCaptureChanged(wparam, lparam);
  849. if (!lres)
  850. break;
  851. goto serv;
  852. case WM_KILLFOCUS:
  853. lres = 1;
  854. phost->_fFocus = 0;
  855. phost->SetCursor(NULL, phost->GetCursor(), TRUE); // force the removal of focus rect
  856. phost->InitSearch();
  857. phost->InitWheelDelta();
  858. if (phost->_fLstType == CLstBxWinHost::kCombo)
  859. phost->OnCBTracking(LBCBM_END, 0); //this will internally release the mouse capture
  860. phost->TxNotify(LBN_KILLFOCUS, NULL);
  861. break;
  862. case WM_SETFOCUS:
  863. lres = 1;
  864. phost->_fFocus = 1;
  865. phost->SetCursor(NULL, (phost->GetCursor() < 0) ? -2 : phost->GetCursor(),
  866. FALSE); // force the displaying of the focus rect
  867. phost->TxNotify(LBN_SETFOCUS, NULL);
  868. #ifndef NOACCESSIBILITY
  869. if (!phost->_fDisabled && phost->GetCursor() != -1)
  870. {
  871. phost->_dwWinEvent = EVENT_OBJECT_FOCUS;
  872. phost->_fNotifyWinEvt = TRUE;
  873. phost->TxNotify(phost->_dwWinEvent, NULL);
  874. }
  875. #endif
  876. break;
  877. case WM_SETREDRAW:
  878. lres = phost->OnSetRedraw(wparam);
  879. break;
  880. case WM_HSCROLL:
  881. phost->OnHScroll(wparam, lparam);
  882. break;
  883. case WM_SETCURSOR:
  884. // Only set cursor when over us rather than a child; this
  885. // helps prevent us from fighting it out with an inplace child
  886. if((HWND)wparam == hwnd)
  887. {
  888. if(!(lres = ::DefWindowProc(hwnd, msg, wparam, lparam)))
  889. lres = phost->OnSetCursor();
  890. }
  891. break;
  892. case WM_CREATE:
  893. lres = phost->OnCreate((CREATESTRUCT*)lparam);
  894. break;
  895. case WM_GETDLGCODE:
  896. phost->_fInDialogBox = TRUE;
  897. lres |= DLGC_WANTARROWS | DLGC_WANTCHARS;
  898. break;
  899. ////////////////////////System setting messages/////////////////////
  900. case WM_SETTINGCHANGE:
  901. phost->OnSettingChange(wparam, lparam);
  902. // Fall thru
  903. case WM_SYSCOLORCHANGE:
  904. phost->OnSysColorChange();
  905. // Need to update the edit controls colors!!!!
  906. goto serv; // Notify text services that
  907. // system colors have changed
  908. case EM_SETPALETTE:
  909. // Application is setting a palette for us to use.
  910. phost->_hpal = (HPALETTE) wparam;
  911. // Invalidate the window & repaint to reflect the new palette.
  912. InvalidateRect(hwnd, NULL, FALSE);
  913. break;
  914. /////////////////////////Misc. Messages/////////////////////////////////
  915. case WM_ENABLE:
  916. if(!wparam ^ phost->_fDisabled)
  917. {
  918. // Stated of window changed so invalidate it so it will
  919. // get redrawn.
  920. InvalidateRect(phost->_hwnd, NULL, FALSE);
  921. phost->SetScrollBarsForWmEnable(wparam);
  922. }
  923. phost->_fDisabled = !wparam; // Set disabled flag
  924. InvalidateRect(hwnd, NULL, FALSE);
  925. lres = 0;
  926. break;
  927. case WM_STYLECHANGING:
  928. // Just pass this one to the default window proc
  929. goto defwndproc;
  930. break;
  931. case WM_STYLECHANGED:
  932. // FUTURE:
  933. // We should support style changes after the control has been created
  934. // to be more compatible with the system controls
  935. //
  936. // For now, we only interested in GWL_EXSTYLE Transparent mode changed.
  937. // This is to fix Bug 753 since Window95 is not passing us
  938. // the WS_EX_TRANSPARENT.
  939. //
  940. lres = 1;
  941. if(GWL_EXSTYLE == wparam)
  942. {
  943. LPSTYLESTRUCT lpss = (LPSTYLESTRUCT) lparam;
  944. if(phost->IsTransparentMode() != (BOOL)(lpss->styleNew & WS_EX_TRANSPARENT))
  945. {
  946. phost->_dwExStyle = lpss->styleNew;
  947. ((CTxtEdit *)phost->_pserv)->OnTxBackStyleChange(TRUE);
  948. // Return 0 to indicate we have handled this message
  949. lres = 0;
  950. }
  951. }
  952. break;
  953. case WM_SIZE:
  954. // Check if we have to recalculate the height of the listbox
  955. // Note if window is resized we will receive another WM_SIZE message
  956. // upon which the RecalcHeight will fail and we will proceed
  957. // normally
  958. if (phost->RecalcHeight(LOWORD(lparam), HIWORD(lparam)))
  959. break;
  960. phost->_pserv->TxSendMessage(msg, wparam, lparam, &lres);
  961. lres = phost->OnSize(hwnd, wparam, (int)LOWORD(lparam), (int)HIWORD(lparam));
  962. break;
  963. case WM_WINDOWPOSCHANGING:
  964. lres = ::DefWindowProc(hwnd, msg, wparam, lparam);
  965. if(phost->TxGetEffects() == TXTEFFECT_SUNKEN || phost->IsCustomLook())
  966. phost->OnSunkenWindowPosChanging(hwnd, (WINDOWPOS *) lparam);
  967. break;
  968. case WM_SHOWWINDOW:
  969. if ((phost->GetViewSize() == 0 || phost->_fLstType == CLstBxWinHost::kCombo) && wparam == 1)
  970. {
  971. // we need to do this because if we are part of a combo box
  972. // we won't get the size message because listbox may not be visible at time of sizing
  973. RECT rc;
  974. GetClientRect(hwnd, &rc);
  975. phost->_fVisible = 1;
  976. phost->RecalcHeight(rc.right, rc.bottom);
  977. //Since we may not get the WM_SIZE message for combo boxes we need to
  978. // do this in WM_SHOWWINDOW: bug fix #4080
  979. if (phost->_fLstType == CLstBxWinHost::kCombo)
  980. {
  981. phost->_pserv->TxSendMessage(WM_SIZE, SIZE_RESTORED, MAKELONG(rc.right, rc.bottom), NULL);
  982. phost->OnSize(hwnd, SIZE_RESTORED, rc.right, rc.bottom);
  983. }
  984. }
  985. hr = phost->OnTxVisibleChange((BOOL)wparam);
  986. break;
  987. case LB_SETTABSTOPS:
  988. msg = EM_SETTABSTOPS;
  989. goto serv;
  990. case WM_ERASEBKGND:
  991. // We will erase the background in WM_PAINT. For owner LB, we want
  992. // to check PAINTSTRUCT fErase flag before we erase the background.
  993. // Thus, if client (ie PPT) sub-class us and have handled the WM_ERASEBKGND,
  994. // we don't want to erase the background.
  995. lres = phost->_fOwnerDraw ? 0 : 1;
  996. break;
  997. case EM_SETPARAFORMAT:
  998. fRecalcHeight = TRUE;
  999. wparam = SPF_SETDEFAULT;
  1000. goto serv;
  1001. case EM_SETCHARFORMAT:
  1002. fRecalcHeight = TRUE;
  1003. wparam = SCF_ALL; //wparam for this message should always be SCF_ALL
  1004. goto serv;
  1005. case EM_SETEDITSTYLE:
  1006. lres = phost->OnSetEditStyle(wparam, lparam);
  1007. break;
  1008. case WM_GETTEXT:
  1009. GETTEXTEX gt;
  1010. if (W32->OnWin9x() || phost->_fANSIwindow)
  1011. W32->AnsiFilter( msg, wparam, lparam, (void *) &gt );
  1012. goto serv;
  1013. case WM_GETTEXTLENGTH:
  1014. GETTEXTLENGTHEX gtl;
  1015. if (W32->OnWin9x() || phost->_fANSIwindow)
  1016. W32->AnsiFilter( msg, wparam, lparam, (void *) &gtl );
  1017. goto serv;
  1018. case LB_GETHORIZONTALEXTENT:
  1019. lres = phost->GetHorzExtent();
  1020. break;
  1021. case LB_SETHORIZONTALEXTENT:
  1022. if (phost->_fHorzScroll)
  1023. {
  1024. LONG lHorzExtentLocal = (LONG)wparam;
  1025. if (lHorzExtentLocal < 0)
  1026. lHorzExtentLocal = 0;
  1027. if (phost->GetHorzExtent() != lHorzExtentLocal)
  1028. {
  1029. phost->SetHorzExtent(lHorzExtentLocal);
  1030. fRecalcHeight = TRUE;
  1031. }
  1032. }
  1033. break;
  1034. case WM_SETFONT:
  1035. if (wparam)
  1036. fRecalcHeight = TRUE;
  1037. goto serv;
  1038. case WM_SETTEXT:
  1039. // We don't handle WM_SETTEXT, pass onto defWindowPorc to set
  1040. // the title if this came from SetWindowText()
  1041. Tracef(TRCSEVWARN, "Unexpected WM_SETTEXT for REListbox");
  1042. goto defwndproc;
  1043. #ifndef NOACCESSIBILITY
  1044. case WM_GETOBJECT:
  1045. IUnknown* punk;
  1046. phost->QueryInterface(IID_IUnknown, (void**)&punk);
  1047. Assert(punk);
  1048. lres = W32->LResultFromObject(IID_IUnknown, wparam, (LPUNKNOWN)punk);
  1049. AssertSz(!FAILED((HRESULT)lres), "WM_GETOBJECT message FAILED\n");
  1050. punk->Release();
  1051. break;
  1052. #endif
  1053. default:
  1054. serv:
  1055. hr = phost->_pserv->TxSendMessage(msg, wparam, lparam, &lres);
  1056. if(hr == S_FALSE)
  1057. {
  1058. defwndproc:
  1059. // Message was not processed by text services so send it
  1060. // to the default window proc.
  1061. lres = ::DefWindowProc(hwnd, msg, wparam, lparam);
  1062. }
  1063. }
  1064. // Special border processing. The inset changes based on the size of the
  1065. // defautl character set. So if we got a message that changes the default
  1066. // character set, we need to update the inset.
  1067. if (fRecalcHeight)
  1068. {
  1069. // need to recalculate the height of each item
  1070. phost->ResizeInset();
  1071. // need to resize the window to update internal window variables
  1072. RECT rc;
  1073. GetClientRect(phost->_hwnd, &rc);
  1074. phost->RecalcHeight(rc.right, rc.bottom);
  1075. if (WM_SETFONT == msg)
  1076. phost->ResetItemColor();
  1077. }
  1078. Exit:
  1079. phost->Release();
  1080. return lres;
  1081. }
  1082. //////////////// CTxtWinHost Creation/Initialization/Destruction ///////////////////////
  1083. #ifndef NOACCESSIBILITY
  1084. /*
  1085. * CLstBxWinHost::QueryInterface (REFIID, void)
  1086. *
  1087. * @mfunc
  1088. * QI for IID_IAccessible
  1089. *
  1090. */
  1091. HRESULT CLstBxWinHost::QueryInterface(
  1092. REFIID riid,
  1093. void **ppv)
  1094. {
  1095. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CTxtWinHost::QueryInterface");
  1096. if(riid == IID_IAccessible)
  1097. *ppv = (IAccessible*)this;
  1098. else if (riid == IID_IDispatch)
  1099. *ppv = (IDispatch*)(IAccessible*)this;
  1100. else if (IsEqualIID(riid, IID_IUnknown))
  1101. *ppv = (IUnknown*)(IAccessible*)this;
  1102. else
  1103. return CTxtWinHost::QueryInterface(riid, ppv);
  1104. AddRef();
  1105. return NOERROR;
  1106. }
  1107. #endif
  1108. /*
  1109. * CLstBxWinHost::OnNCCreate (hwnd, pcs)
  1110. *
  1111. * @mfunc
  1112. * Static global method to handle WM_NCCREATE message (see remain.c)
  1113. */
  1114. LRESULT CLstBxWinHost::OnNCCreate(
  1115. HWND hwnd,
  1116. const CREATESTRUCT *pcs)
  1117. {
  1118. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnNCCreate");
  1119. #if defined DEBUG && !defined(NOPALETTE)
  1120. GdiSetBatchLimit(1);
  1121. #endif
  1122. CLstBxWinHost *phost = new CLstBxWinHost();
  1123. Assert(phost);
  1124. if(!phost)
  1125. return 0;
  1126. if(!phost->Init(hwnd, pcs)) // Stores phost in associated
  1127. { // window data
  1128. Assert(FALSE);
  1129. phost->Shutdown();
  1130. delete phost;
  1131. return FALSE;
  1132. }
  1133. return TRUE;
  1134. }
  1135. /*
  1136. * CLstBxWinHost::OnNCDestroy (phost)
  1137. *
  1138. * @mfunc
  1139. * Static global method to handle WM_CREATE message
  1140. *
  1141. * @devnote
  1142. * phost ptr is stored in window data (GetWindowLong())
  1143. */
  1144. void CLstBxWinHost::OnNCDestroy(
  1145. CLstBxWinHost *phost)
  1146. {
  1147. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnNCDestroy");
  1148. CNotifyMgr *pnm = ((CTxtEdit*)(phost->_pserv))->GetNotifyMgr();
  1149. if(pnm)
  1150. pnm->Remove((ITxNotify *)phost);
  1151. phost->_fShutDown = 1;
  1152. // We need to send WM_DELETEITEM messages for owner draw list boxes
  1153. if (phost->_fOwnerDraw && phost->_nCount)
  1154. {
  1155. phost->LbDeleteItemNotify(0, phost->_nCount - 1);
  1156. }
  1157. if (phost->_pwszSearch)
  1158. delete phost->_pwszSearch;
  1159. // set the combobox's listbox hwnd pointer to null so combo box won't try
  1160. // to delete the window twice
  1161. if (phost->_pcbHost)
  1162. {
  1163. phost->_pcbHost->_hwndList = NULL;
  1164. phost->_pcbHost->Release();
  1165. }
  1166. phost->Shutdown();
  1167. phost->Release();
  1168. }
  1169. /*
  1170. * CLstBxWinHost::CLstBxWinHost()
  1171. *
  1172. * @mfunc
  1173. * constructor
  1174. */
  1175. CLstBxWinHost::CLstBxWinHost() : CTxtWinHost(), _nCount(0), _fSingleSel(0), _nidxSearch(0),
  1176. _pwszSearch(NULL), _pcbHost(NULL)
  1177. {
  1178. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::CTxtWinHost");
  1179. #ifndef NOACCESSIBILITY
  1180. _dwWinEvent = 0; // Win Event code (ACCESSIBILITY use)
  1181. _nAccessibleIdx = -1; // Index (ACCESSIBILITY use)
  1182. #endif
  1183. }
  1184. /*
  1185. * CLstBxWinHost::~CLstBxWinHost()
  1186. *
  1187. * @mfunc
  1188. * destructor
  1189. */
  1190. CLstBxWinHost::~CLstBxWinHost()
  1191. {
  1192. }
  1193. /*
  1194. * CLstBxWinHost::Init (hwnd, pcs)
  1195. *
  1196. * @mfunc
  1197. * Initialize this CLstBxWinHost
  1198. */
  1199. BOOL CLstBxWinHost::Init(
  1200. HWND hwnd, //@parm Window handle for this control
  1201. const CREATESTRUCT *pcs) //@parm Corresponding CREATESTRUCT
  1202. {
  1203. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::Init");
  1204. if(!pcs->lpszClass)
  1205. return FALSE;
  1206. // Set pointer back to CLstBxWinHost from the window
  1207. if(hwnd)
  1208. SetWindowLongPtr(hwnd, ibPed, (INT_PTR)this);
  1209. _hwnd = hwnd;
  1210. _fHidden = TRUE;
  1211. if(pcs)
  1212. {
  1213. _hwndParent = pcs->hwndParent;
  1214. _dwExStyle = pcs->dwExStyle;
  1215. _dwStyle = pcs->style;
  1216. CHECKSTYLE(_dwStyle);
  1217. // Internally WinNT defines a LBS_COMBOBOX to determine
  1218. // if the list box is part of a combo box. So we will use
  1219. // the same flag and value!!
  1220. if (_dwStyle & LBS_COMBOBOX)
  1221. {
  1222. AssertSz(pcs->hMenu == (HMENU)CB_LISTBOXID && pcs->lpCreateParams,
  1223. "invalid combo box parameters");
  1224. if (pcs->hMenu != (HMENU)CB_LISTBOXID || !pcs->lpCreateParams)
  1225. return -1;
  1226. _pcbHost = (CCmbBxWinHost*) pcs->lpCreateParams;
  1227. _pcbHost->AddRef();
  1228. _fLstType = kCombo;
  1229. _fSingleSel = 1;
  1230. }
  1231. else
  1232. {
  1233. // NOTE:
  1234. // The order in which we check the style flags immulate
  1235. // WinNT's order. So please verify with NT order before
  1236. // reaaranging order.
  1237. // determine the type of list box
  1238. //if (_dwStyle & LBS_NOSEL) //Not implemented but may be in the future
  1239. // _fLstType = kNoSel;
  1240. //else
  1241. _fSingleSel = 0;
  1242. if (_dwStyle & LBS_EXTENDEDSEL)
  1243. _fLstType = kExtended;
  1244. else if (_dwStyle & LBS_MULTIPLESEL)
  1245. _fLstType = kMultiple;
  1246. else
  1247. {
  1248. _fLstType = kSingle;
  1249. _fSingleSel = 1;
  1250. }
  1251. }
  1252. _fNotify = ((_dwStyle & LBS_NOTIFY) != 0);
  1253. if (!(_dwStyle & LBS_HASSTRINGS))
  1254. {
  1255. _dwStyle |= LBS_HASSTRINGS;
  1256. SetWindowLong(_hwnd, GWL_STYLE, _dwStyle);
  1257. }
  1258. _fDisableScroll = 0;
  1259. if (_dwStyle & LBS_DISABLENOSCROLL)
  1260. {
  1261. _fDisableScroll = 1;
  1262. // WARNING!!!
  1263. // ES_DISABLENOSCROLL is equivalent to LBS_NODATA
  1264. // Since we don'w support LBS_NODATA this should be
  1265. // fine. But in the event we do want to support this
  1266. // in the future we will have to override the
  1267. // TxGetScrollBars member function and return the
  1268. // proper window style
  1269. // set the equivalent ES style
  1270. _dwStyle |= ES_DISABLENOSCROLL;
  1271. }
  1272. _fNoIntegralHeight = !!(_dwStyle & LBS_NOINTEGRALHEIGHT);
  1273. _fOwnerDrawVar = 0;
  1274. _fOwnerDraw = !!(_dwStyle & LBS_OWNERDRAWFIXED);
  1275. if (_dwStyle & LBS_OWNERDRAWVARIABLE)
  1276. {
  1277. _fOwnerDraw = 1;
  1278. _fOwnerDrawVar = 1;
  1279. _fNoIntegralHeight = 1; // Force no intergal height - following System LB
  1280. }
  1281. _fIntegralHeightOld = _fNoIntegralHeight;
  1282. _fSort = !!(_dwStyle & LBS_SORT);
  1283. _fHorzScroll = !!(_dwStyle & WS_HSCROLL);
  1284. _fBorder = !!(_dwStyle & WS_BORDER);
  1285. if(_dwExStyle & WS_EX_CLIENTEDGE)
  1286. _fBorder = TRUE;
  1287. // handle default disabled
  1288. if(_dwStyle & WS_DISABLED)
  1289. _fDisabled = TRUE;
  1290. _fWantKBInput = !!(_dwStyle & LBS_WANTKEYBOARDINPUT);
  1291. _fHasStrings = !!(_dwStyle & LBS_HASSTRINGS);
  1292. }
  1293. DWORD dwStyleSaved = _dwStyle;
  1294. // get rid of all ES styles except ES_DISABLENOSCROLL
  1295. _dwStyle &= (~(0x0FFFFL) | ES_DISABLENOSCROLL);
  1296. // Create Text Services component
  1297. if(FAILED(CreateTextServices()))
  1298. return FALSE;
  1299. _dwStyle = dwStyleSaved;
  1300. _yInset = 0;
  1301. _xInset = 0; //_xWidthSys / 2;
  1302. // Shut-off the undo stack since listbox don't have undo's
  1303. ((CTxtEdit*)_pserv)->HandleSetUndoLimit(0);
  1304. // Set alignment
  1305. PARAFORMAT PF2;
  1306. PF2.dwMask = 0;
  1307. if(_dwExStyle & WS_EX_RIGHT)
  1308. {
  1309. PF2.dwMask |= PFM_ALIGNMENT;
  1310. PF2.wAlignment = PFA_RIGHT; // right or center-aligned
  1311. }
  1312. if(_dwExStyle & WS_EX_RTLREADING)
  1313. {
  1314. PF2.dwMask |= PFM_RTLPARA;
  1315. PF2.wEffects = PFE_RTLPARA; // RTL reading order
  1316. }
  1317. if (PF2.dwMask)
  1318. {
  1319. PF2.cbSize = sizeof(PARAFORMAT2);
  1320. // tell text services
  1321. _pserv->TxSendMessage(EM_SETPARAFORMAT, SPF_SETDEFAULT, (LPARAM)&PF2, NULL);
  1322. }
  1323. // Tell textservices to select the entire background & disable ime for listbox
  1324. _pserv->TxSendMessage(EM_SETEDITSTYLE, SES_EXTENDBACKCOLOR | SES_NOIME, SES_EXTENDBACKCOLOR | SES_NOIME, NULL);
  1325. // Tell textservices to turn-on auto font sizing
  1326. _pserv->TxSendMessage(EM_SETLANGOPTIONS, 0, IMF_AUTOFONT | IMF_AUTOFONTSIZEADJUST | IMF_UIFONTS, NULL);
  1327. // NOTE:
  1328. // It is important we call this after
  1329. // ITextServices is created because this function relies on certain
  1330. // variable initialization to be performed on the creation by ITextServices
  1331. // At this point the border flag is set and so is the pixels per inch
  1332. // so we can initalize the inset.
  1333. _rcViewInset.left = 0;
  1334. _rcViewInset.bottom = 0;
  1335. _rcViewInset.right = 0;
  1336. _rcViewInset.top = 0;
  1337. _fSetRedraw = 1;
  1338. return TRUE;
  1339. }
  1340. /*
  1341. * CLstBxWinHost::OnCreate (pcs)
  1342. *
  1343. * @mfunc
  1344. * Handle WM_CREATE message
  1345. *
  1346. * @rdesc
  1347. * LRESULT = -1 if failed to in-place activate; else 0
  1348. */
  1349. LRESULT CLstBxWinHost::OnCreate(
  1350. const CREATESTRUCT *pcs)
  1351. {
  1352. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnCreate");
  1353. RECT rcClient;
  1354. // sometimes, these values are -1 (from windows itself); just treat them
  1355. // as zero in that case
  1356. LONG cy = (pcs->cy < 0) ? 0 : pcs->cy;
  1357. LONG cx = (pcs->cx < 0) ? 0 : pcs->cx;
  1358. rcClient.top = pcs->y;
  1359. rcClient.bottom = rcClient.top + cy;
  1360. rcClient.left = pcs->x;
  1361. rcClient.right = rcClient.left + cx;
  1362. DWORD dwStyle = GetWindowLong(_hwnd, GWL_STYLE);
  1363. // init variables
  1364. UpdateSysColors();
  1365. _idCtrl = (UINT)(DWORD_PTR)pcs->hMenu;
  1366. _fKeyMaskSet = 0;
  1367. _fMouseMaskSet = 0;
  1368. _fScrollMaskSet = 0;
  1369. _nAnchor = _nCursor = -1;
  1370. _nOldCursor = -1;
  1371. _fMouseDown = 0;
  1372. _nTopIdx = 0;
  1373. _cpLastGetRange = 0;
  1374. _nIdxLastGetRange = 0;
  1375. _fSearching = 0;
  1376. _nyFont = _nyItem = 1;
  1377. _fNoResize = 1;
  1378. _stvidx = -1;
  1379. _lHorzExtent = 0;
  1380. InitWheelDelta();
  1381. // Hide all scrollbars to start unless the disable scroll flag
  1382. // is set
  1383. if(_hwnd && !_fDisableScroll)
  1384. {
  1385. SetScrollRange(_hwnd, SB_VERT, 0, 0, TRUE);
  1386. SetScrollRange(_hwnd, SB_HORZ, 0, 0, TRUE);
  1387. dwStyle &= ~(WS_VSCROLL | WS_HSCROLL);
  1388. SetWindowLong(_hwnd, GWL_STYLE, dwStyle);
  1389. }
  1390. // Notify Text Services that we are in place active
  1391. if(FAILED(_pserv->OnTxInPlaceActivate(&rcClient)))
  1392. return -1;
  1393. // Initially the font height is the item height
  1394. ResizeInset();
  1395. Assert(_yInset == 0); // _yInset should be zero since listbox's doesn't have yinsets
  1396. //We never want to display the selection or caret so tell textservice this
  1397. _pserv->TxSendMessage(EM_HIDESELECTION, TRUE, FALSE, NULL);
  1398. //Set the indents to 2 pixels like system listboxes
  1399. SetListIndent(2);
  1400. _fNoResize = 0;
  1401. _usIMEMode = ES_NOIME;
  1402. CNotifyMgr *pnm = ((CTxtEdit*)_pserv)->GetNotifyMgr();
  1403. if(pnm)
  1404. pnm->Add((ITxNotify *)this);
  1405. return 0;
  1406. }
  1407. /*
  1408. * CLstBxWinHost::SetListIndent(int)
  1409. *
  1410. * @mfunc
  1411. * Sets the left indent of a paragraph to the equivalent point value of nLeft, nLeft is
  1412. * given in device-coordinate pixels.
  1413. *
  1414. * @rdesc
  1415. * BOOL = Successful ? TRUE : FALSE
  1416. */
  1417. BOOL CLstBxWinHost::SetListIndent(
  1418. int nLeft)
  1419. {
  1420. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::SetListIndent");
  1421. LRESULT lres;
  1422. PARAFORMAT2 pf2;
  1423. // tranlate the nLeft pixel value to point value
  1424. long npt = MulDiv(nLeft, 1440, W32->GetXPerInchScreenDC());
  1425. //format message struct
  1426. pf2.cbSize = sizeof(PARAFORMAT2);
  1427. pf2.dwMask = PFM_STARTINDENT;
  1428. pf2.dxStartIndent = npt;
  1429. // indent first line
  1430. _pserv->TxSendMessage(EM_SETPARAFORMAT, SPF_SETDEFAULT, (LPARAM)&pf2, &lres);
  1431. return lres;
  1432. }
  1433. /////////////////////////////// Helper Functions //////////////////////////////////
  1434. /*
  1435. * CLstBxWinHost::FindString(long, LPCTSTR, BOOL)
  1436. *
  1437. * @mfunc
  1438. * This function checks a given index matches the search string
  1439. *
  1440. * @rdesc
  1441. * BOOL = Match ? TRUE : FALSE
  1442. */
  1443. BOOL CLstBxWinHost::FindString(
  1444. long idx,
  1445. LPCTSTR szSearch,
  1446. BOOL bExact)
  1447. {
  1448. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::FindString");
  1449. Assert(_nCount);
  1450. // allocate string buffer into stack
  1451. WCHAR sz[1024];
  1452. WCHAR *psz = sz;
  1453. int cch = wcslen(szSearch);
  1454. if ( (cch + 3 /* 2 paragraphs and a NULL*/) > 1024)
  1455. psz = new WCHAR[cch + 3 /* 2 paragraphs and a NULL*/];
  1456. Assert(psz);
  1457. if (psz == NULL)
  1458. {
  1459. TxNotify((unsigned long)LBN_ERRSPACE, NULL);
  1460. return FALSE;
  1461. }
  1462. // format the string the way we need it
  1463. wcscpy(psz, szSearch);
  1464. if (bExact)
  1465. wcscat(psz, szCR);
  1466. BOOL bMatch = FALSE;
  1467. ITextRange *pRange = NULL;
  1468. BSTR bstrQuery = SysAllocString(psz);
  1469. if (!bstrQuery)
  1470. goto CleanExit;
  1471. if (psz != sz)
  1472. delete [] psz;
  1473. // Set starting position for the search
  1474. long cp, cp2;
  1475. if (!GetRange(idx, idx, &pRange))
  1476. {
  1477. SysFreeString(bstrQuery);
  1478. return FALSE;
  1479. }
  1480. CHECKNOERROR(pRange->GetStart(&cp));
  1481. CHECKNOERROR(pRange->FindTextStart(bstrQuery, 0, FR_MATCHALEFHAMZA | FR_MATCHKASHIDA | FR_MATCHDIAC, NULL));
  1482. CHECKNOERROR(pRange->GetStart(&cp2));
  1483. bMatch = (cp == cp2);
  1484. CleanExit:
  1485. if (bstrQuery)
  1486. SysFreeString(bstrQuery);
  1487. if (pRange)
  1488. pRange->Release();
  1489. return bMatch;
  1490. }
  1491. /*
  1492. * CLstBxWinHost::MouseMoveHelper(int, BOOL)
  1493. *
  1494. * @mfunc
  1495. * Helper function for the OnMouseMove function. Performs
  1496. * the correct type of selection given an index to select
  1497. *
  1498. * @rdesc
  1499. * void
  1500. */
  1501. void CLstBxWinHost::MouseMoveHelper(
  1502. int idx,
  1503. BOOL bSelect)
  1504. {
  1505. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::MouseMoveHelper");
  1506. int ff = LBSEL_RESET | LBSEL_NEWCURSOR;
  1507. if (bSelect)
  1508. ff |= LBSEL_SELECT;
  1509. switch (_fLstType)
  1510. {
  1511. case kSingle:
  1512. case kCombo:
  1513. case kExtended: // perform the extended selection
  1514. if (LbSetSelection(_fLstType == kExtended ? _nAnchor : idx, idx, ff, idx, 0))
  1515. {
  1516. #ifndef NOACCESSIBILITY
  1517. _dwWinEvent = EVENT_OBJECT_FOCUS;
  1518. _fNotifyWinEvt = TRUE;
  1519. TxNotify(_dwWinEvent, NULL);
  1520. if (_fLstType == kCombo)
  1521. {
  1522. _dwWinEvent = bSelect ? EVENT_OBJECT_SELECTION : EVENT_OBJECT_SELECTIONREMOVE;
  1523. _fNotifyWinEvt = TRUE;
  1524. TxNotify(_dwWinEvent, NULL);
  1525. }
  1526. #endif
  1527. }
  1528. break;
  1529. case kMultiple:
  1530. // Just change the cursor position
  1531. SetCursor(NULL, idx, TRUE);
  1532. #ifndef NOACCESSIBILITY
  1533. _dwWinEvent = EVENT_OBJECT_FOCUS;
  1534. _fNotifyWinEvt = TRUE;
  1535. TxNotify(_dwWinEvent, NULL);
  1536. #endif
  1537. break;
  1538. }
  1539. }
  1540. /*
  1541. * CLstBxWinHost::ResizeInset
  1542. *
  1543. * @mfunc Recalculates rectangle for a font change.
  1544. *
  1545. * @rdesc None.
  1546. */
  1547. void CLstBxWinHost::ResizeInset()
  1548. {
  1549. // Create a DC
  1550. HDC hdc = GetDC(_hwnd);
  1551. // Get the inset information
  1552. LONG xAveCharWidth = 0;
  1553. LONG yCharHeight = GetECDefaultHeightAndWidth(_pserv, hdc, 1, 1,
  1554. W32->GetYPerInchScreenDC(), &xAveCharWidth, NULL, NULL);
  1555. ReleaseDC(_hwnd, hdc);
  1556. // update our internal font and item height information with the new font
  1557. if (_nyItem == _nyFont)
  1558. {
  1559. // We need to set the new font height before calling set item height
  1560. // so set item height will set exact height rather than space after
  1561. // for the default paragraph
  1562. _nyFont = yCharHeight;
  1563. SetItemsHeight(yCharHeight, TRUE);
  1564. }
  1565. else
  1566. _nyFont = yCharHeight;
  1567. }
  1568. /*
  1569. * CLstBxWinHost::RecalcHeight(int, int)
  1570. *
  1571. * @mfunc
  1572. * Resized the height so no partial text will be displayed
  1573. *
  1574. * @rdesc
  1575. * BOOL = window has been resized ? TRUE : FALSE
  1576. */
  1577. BOOL CLstBxWinHost::RecalcHeight(
  1578. int nWidth,
  1579. int nHeight)
  1580. {
  1581. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::RecalcHeight");
  1582. // NOTE: We should also exit if nWidth == 0 but PPT does some
  1583. // sizing tests which we cause it to fail because before we
  1584. // just exited when nWidth was 0. (bug fix #4196)
  1585. // Check if any resizing should be done in the first place
  1586. if (_fNoResize || !nHeight || IsIconic(_hwnd))
  1587. return FALSE;
  1588. // get # of viewable items
  1589. Assert(_yInset == 0);
  1590. _nViewSize = max(1, (nHeight / max(_nyItem, 1)));
  1591. // Calculate the viewport
  1592. _rcViewport.left = 0;//(_fBorder) ? _xInset : 0;
  1593. _rcViewport.bottom = nHeight;
  1594. _rcViewport.right = nWidth;
  1595. _rcViewport.top = 0;
  1596. // bug fix don't do anything if the height is smaller then our font height
  1597. if (nHeight <= _nyItem)
  1598. return FALSE;
  1599. if (_nyItem && (nHeight % _nyItem) && !_fNoIntegralHeight)
  1600. {
  1601. // we need to get the window rect before we can call SetWindowPos because
  1602. // we have to include the scrollbar if the scrollbar is visible
  1603. RECT rc;
  1604. ::GetWindowRect(_hwnd, &rc);
  1605. // instead of worrying about the dimensions of the client edge and stuff we
  1606. // figure-out the difference between the window size and the client size and add
  1607. // that to the end of calculating the new height
  1608. int nDiff = max(rc.bottom - rc.top - nHeight, 0);
  1609. nHeight = (_nViewSize * _nyItem) + nDiff;
  1610. // Resize the window
  1611. SetWindowPos(_hwnd, HWND_TOP, 0, 0, rc.right - rc.left, nHeight,
  1612. SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOSENDCHANGING);
  1613. return TRUE;
  1614. }
  1615. else
  1616. {
  1617. // bug fix #6011
  1618. // we need to force the display to update the width since it doesn't do it on
  1619. // WM_SIZE
  1620. _sWidth = nWidth;
  1621. _pserv->OnTxPropertyBitsChange(TXTBIT_EXTENTCHANGE, TXTBIT_EXTENTCHANGE);
  1622. // We may need to adjust the top index if suddenly the viewsize becomes larger
  1623. // and causes empty space to be displayed at the bottom
  1624. int idx = GetTopIndex();
  1625. if (!_fOwnerDrawVar)
  1626. {
  1627. if ((GetCount() - max(0, idx)) < _nViewSize)
  1628. idx = GetCount() - _nViewSize;
  1629. }
  1630. else
  1631. {
  1632. // Get top index for the last page
  1633. int iLastPageTopIdx = PageVarHeight(_nCount, FALSE);
  1634. if (iLastPageTopIdx < idx)
  1635. idx = iLastPageTopIdx;
  1636. }
  1637. //bug fix #4374
  1638. // We need to make sure our internal state is in sync so update the top index
  1639. // based on the new _nViewSize
  1640. SetTopViewableItem(max(0, idx));
  1641. }
  1642. return FALSE;
  1643. }
  1644. /*
  1645. * CLstBxWinHost::ResetItemColor( )
  1646. *
  1647. * @mfunc
  1648. * reset all the item colors when needed
  1649. *
  1650. */
  1651. void CLstBxWinHost::ResetItemColor()
  1652. {
  1653. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::ResetItemColor");
  1654. int nStart = 0;
  1655. int nEnd = 0;
  1656. // Don't do anything if there LB is empty or
  1657. // it is an owner draw LB
  1658. if (_nCount <= 0 || _fOwnerDraw)
  1659. return;
  1660. BOOL bSelection = _rgData.Get(0)._fSelected;
  1661. for (int i = 1; i < _nCount; i++)
  1662. {
  1663. if (_rgData.Get(i)._fSelected != (unsigned)bSelection)
  1664. {
  1665. // Update the colors only for selections
  1666. if (bSelection)
  1667. SetColors(_crSelFore, _crSelBack, nStart, nStart + nEnd);
  1668. // Update our cache to reflect the value of our current index
  1669. bSelection = _rgData.Get(i)._fSelected;
  1670. nStart = i;
  1671. nEnd = 0;
  1672. }
  1673. else
  1674. nEnd++;
  1675. }
  1676. // there was some left over so change the color for these
  1677. if (bSelection)
  1678. SetColors(_crSelFore, _crSelBack, nStart, nStart + nEnd);
  1679. }
  1680. /*
  1681. * CLstBxWinHost::SortInsertList(WCHAR* pszDst, WCHAR* pszSrc)
  1682. *
  1683. * @mfunc
  1684. * inserts a list of strings rather than one at a time with addstring
  1685. *
  1686. * @rdesc
  1687. * int = amount of strings inserted;
  1688. */
  1689. int CLstBxWinHost::SortInsertList(
  1690. WCHAR* pszDst,
  1691. WCHAR* pszSrc)
  1692. {
  1693. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::SortInsertList");
  1694. Assert(pszSrc != NULL);
  1695. Assert(pszDst != NULL);
  1696. const int ARRAY_DEFAULT = 256;
  1697. //calculate the amount of strings to be inserted
  1698. CHARSORTINFO rg[ARRAY_DEFAULT];
  1699. int nMax = ARRAY_DEFAULT;
  1700. int nLen = wcslen(pszSrc);
  1701. CHARSORTINFO* prg = rg;
  1702. memset(rg, 0, sizeof(rg));
  1703. //insert first item in list to head or array
  1704. prg[0].str = pszSrc;
  1705. int i = 1;
  1706. // go through store strings into array and replace <CR> with NULL
  1707. WCHAR* psz = nLen + pszSrc - 1; //start at end of list
  1708. int nSz = 0;
  1709. while (psz >= pszSrc)
  1710. {
  1711. if (*psz == *szCR)
  1712. {
  1713. // Check if we need to allocate memory since we hit the maximum amount
  1714. // allowed in array
  1715. if (i == nMax)
  1716. {
  1717. int nSize = nMax + ARRAY_DEFAULT;
  1718. CHARSORTINFO* prgTemp = new CHARSORTINFO[nSize];
  1719. // Check if memory allocation failed
  1720. Assert(prgTemp);
  1721. if (!prgTemp)
  1722. {
  1723. if (prg != rg)
  1724. delete [] prg;
  1725. TxNotify((unsigned long)LBN_ERRSPACE, NULL);
  1726. return LB_ERR;
  1727. }
  1728. // copy memory from 1 array to the next
  1729. memcpy(prgTemp, prg, sizeof(CHARSORTINFO) * nMax);
  1730. // delete any previously allocated memory
  1731. if (prg != rg)
  1732. delete [] prg;
  1733. // set pointers and max to new values
  1734. prg = prgTemp;
  1735. nMax = nSize;
  1736. }
  1737. // record position of string into array
  1738. prg[i].str = psz + 1;
  1739. prg[i].sz = nSz;
  1740. i++;
  1741. nSz = 0;
  1742. }
  1743. else
  1744. nSz++;
  1745. psz--;
  1746. }
  1747. prg[0].sz = nSz; // update the size of first index since we didn't do it before
  1748. i--; // set i to last valid index
  1749. //now sort the array of items
  1750. QSort(prg, 0, i);
  1751. //create string list with the newly sorted list
  1752. WCHAR* pszOut = pszDst;
  1753. for (int j = 0; j <= i; j++)
  1754. {
  1755. memcpy(pszOut, (prg + j)->str, (prg + j)->sz * sizeof(WCHAR));
  1756. pszOut = pszOut + (prg + j)->sz;
  1757. *pszOut++ = L'\r';
  1758. }
  1759. *(--pszOut) = L'\0';
  1760. // delete any previously allocated memory
  1761. if (prg != rg)
  1762. delete [] prg;
  1763. return ++i;
  1764. }
  1765. /*
  1766. * CLstBxWinHost::QSort(CHARSORTINFO rg[], int nStart, int nEnd)
  1767. *
  1768. * @mfunc
  1769. * recursively quick sorts a given list of strings
  1770. *
  1771. * @rdesc
  1772. * int = SHOULD ALWAYS RETURN TRUE;
  1773. */
  1774. int CLstBxWinHost::QSort(
  1775. CHARSORTINFO rg[],
  1776. int nStart,
  1777. int nEnd)
  1778. {
  1779. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::QSort");
  1780. // it's important these values are what they are since we use < and >
  1781. Assert(CSTR_LESS_THAN == 1);
  1782. Assert(CSTR_EQUAL == 2);
  1783. Assert(CSTR_GREATER_THAN == 3);
  1784. if (nStart >= nEnd)
  1785. return TRUE;
  1786. // for statisical efficiency lets use the item in the middle of the array for
  1787. // the sentinal
  1788. int mid = (nStart + nEnd) / 2;
  1789. CHARSORTINFO tmp = rg[mid];
  1790. rg[mid] = rg[nEnd];
  1791. rg[nEnd] = tmp;
  1792. int x = nStart;
  1793. int y = nEnd - 1;
  1794. WCHAR* psz = rg[nEnd].str;
  1795. int nSz = rg[nEnd].sz;
  1796. for(;;)
  1797. {
  1798. while ((x < nEnd) && CompareStringWrapper(LOCALE_USER_DEFAULT, NORM_IGNORECASE, rg[x].str, rg[x].sz,
  1799. psz, nSz) == CSTR_LESS_THAN)
  1800. x++;
  1801. while ((y > x) && CompareStringWrapper(LOCALE_USER_DEFAULT, NORM_IGNORECASE, rg[y].str, rg[y].sz,
  1802. psz, nSz) == CSTR_GREATER_THAN)
  1803. y--;
  1804. // swap elements
  1805. if (x >= y)
  1806. break;
  1807. //if we got here then we need to swap the indexes
  1808. tmp = rg[x];
  1809. rg[x] = rg[y];
  1810. rg[y] = tmp;
  1811. // move to next index
  1812. x++;
  1813. y--;
  1814. }
  1815. tmp = rg[x];
  1816. rg[x] = rg[nEnd];
  1817. rg[nEnd] = tmp;
  1818. QSort(rg, nStart, x - 1);
  1819. QSort(rg, x + 1, nEnd);
  1820. return TRUE;
  1821. }
  1822. /*
  1823. * CLstBxWinHost::CompareIndex(LPCTSTR, int)
  1824. *
  1825. * @mfunc
  1826. * Recursive function which returns the insertion index of a sorted list
  1827. *
  1828. * @rdesc
  1829. * int = position to insert string
  1830. */
  1831. int CLstBxWinHost::CompareIndex(
  1832. LPCTSTR szInsert,
  1833. int nIndex)
  1834. {
  1835. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::CompareIndex");
  1836. Assert(0 <= nIndex && nIndex < _nCount);
  1837. // Get the string at a given index
  1838. // compare the string verses the index
  1839. ITextRange* pRange;
  1840. if (!GetRange(nIndex, nIndex, &pRange))
  1841. return -1;
  1842. // Exclude the paragraph character at the end
  1843. if (NOERROR != pRange->MoveEnd(tomCharacter, -1, NULL))
  1844. {
  1845. pRange->Release();
  1846. return -1;
  1847. }
  1848. // we need to get the locale for the comparison
  1849. // we will just use the locale of the string we want to compare with
  1850. ITextFont* pFont;
  1851. if (NOERROR != pRange->GetFont(&pFont))
  1852. {
  1853. pRange->Release();
  1854. return -1;
  1855. }
  1856. BSTR bstr;
  1857. int nRet;
  1858. CHECKNOERROR(pRange->GetText(&bstr));
  1859. if (!bstr)
  1860. nRet = CSTR_GREATER_THAN;
  1861. else if (!szInsert || !*szInsert)
  1862. nRet = CSTR_LESS_THAN;
  1863. else
  1864. {
  1865. nRet = CompareStringWrapper(LOCALE_USER_DEFAULT, NORM_IGNORECASE, szInsert, wcslen(szInsert),
  1866. bstr, wcslen(bstr));
  1867. SysFreeString(bstr);
  1868. }
  1869. pFont->Release();
  1870. pRange->Release();
  1871. return nRet;
  1872. CleanExit:
  1873. Assert(FALSE);
  1874. pFont->Release();
  1875. pRange->Release();
  1876. return -1;
  1877. }
  1878. /*
  1879. * CLstBxWinHost::GetSortedPosition(LPCTSTR, int, int)
  1880. *
  1881. * @mfunc
  1882. * Recursive function which returns the insertion index of a sorted list
  1883. *
  1884. * @rdesc
  1885. * int = position to insert string
  1886. */
  1887. int CLstBxWinHost::GetSortedPosition(
  1888. LPCTSTR szInsert,
  1889. int nStart,
  1890. int nEnd)
  1891. {
  1892. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::GetSortedPosition");
  1893. Assert(nStart <= nEnd);
  1894. // Start at the middle of the list
  1895. int nBisect = (nStart + nEnd) / 2;
  1896. int fResult = CompareIndex(szInsert, nBisect);
  1897. if (fResult == CSTR_LESS_THAN)
  1898. {
  1899. if (nStart == nBisect)
  1900. return nBisect;
  1901. else
  1902. return GetSortedPosition(szInsert, nStart, nBisect - 1); // [nStart, nBisect)
  1903. }
  1904. else if (fResult == CSTR_GREATER_THAN)
  1905. {
  1906. if (nEnd == nBisect)
  1907. return nBisect + 1;
  1908. else
  1909. return GetSortedPosition(szInsert, nBisect + 1, nEnd); // (nBisect, nStart]
  1910. }
  1911. else /*fResult == 0 (found match)*/
  1912. return nBisect;
  1913. }
  1914. /*
  1915. * CLstBxWinHost::SetScrollInfo
  1916. *
  1917. * @mfunc Set scrolling information for the scroll bar.
  1918. */
  1919. void CLstBxWinHost::SetScrollInfo(
  1920. INT fnBar, //@parm Specifies scroll bar to be updated
  1921. BOOL fRedraw) //@parm whether redraw is necessary
  1922. {
  1923. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::SetScrollInfo");
  1924. Assert(_pserv);
  1925. // Set up the basic structure for the call
  1926. SCROLLINFO si;
  1927. si.cbSize = sizeof(SCROLLINFO);
  1928. si.fMask = SIF_ALL;
  1929. // Call back to the control to get the parameters
  1930. if(fnBar == SB_VERT)
  1931. {
  1932. // Bug Fix #4913
  1933. // if the scrollbar is disabled and count is less than the view size
  1934. // then there is nothing to do so just exit out
  1935. BOOL fGreaterThanView;
  1936. if (_fOwnerDrawVar)
  1937. SumVarHeight(0, GetCount(), &fGreaterThanView);
  1938. else
  1939. fGreaterThanView = (GetCount() > _nViewSize);
  1940. if (!fGreaterThanView)
  1941. {
  1942. if (_fDisableScroll)
  1943. {
  1944. // Since listboxes changes height according to its content textservice
  1945. // might of turned-on the scrollbar during an insert string. Make sure
  1946. // the scrollbar is disabled
  1947. TxEnableScrollBar(SB_VERT, ESB_DISABLE_BOTH);
  1948. }
  1949. else
  1950. TxShowScrollBar(SB_VERT, FALSE);
  1951. return;
  1952. }
  1953. else
  1954. TxEnableScrollBar(SB_VERT, _fDisabled ? ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
  1955. RECT rc;
  1956. TxGetClientRect(&rc);
  1957. // For owner draw cases we have to set the scroll positioning
  1958. // ourselves
  1959. if (_fOwnerDraw)
  1960. {
  1961. Assert(GetCount() >= 0);
  1962. si.nMin = 0;
  1963. if (!_fOwnerDrawVar)
  1964. {
  1965. // We don't do anything here if
  1966. // item height is smaller than font height
  1967. if (_nyItem < _nyFont)
  1968. {
  1969. if (!_fDisableScroll)
  1970. TxShowScrollBar(SB_VERT, FALSE);
  1971. return;
  1972. }
  1973. si.nMax = _nyItem * GetCount();
  1974. si.nPos = _nyItem * max(GetTopIndex(), 0);
  1975. }
  1976. else
  1977. {
  1978. int iTopIdx = max(GetTopIndex(), 0);
  1979. si.nPos = SumVarHeight(0, iTopIdx);
  1980. si.nMax = SumVarHeight(iTopIdx, GetCount()) + si.nPos;
  1981. }
  1982. }
  1983. else
  1984. _pserv->TxGetVScroll((LONG *) &si.nMin, (LONG *) &si.nMax,
  1985. (LONG *) &si.nPos, (LONG *) &si.nPage, NULL);
  1986. // need to take care of cases where items are partially exposed
  1987. if (si.nMax)
  1988. {
  1989. si.nPage = rc.bottom; //our scrollbar range is based on pixels so just use the
  1990. //height of the window for the page size
  1991. if (!_fOwnerDrawVar)
  1992. si.nMax += (rc.bottom % _nyItem);
  1993. // We need to decrement the max by one so maximum scroll pos will match
  1994. // what the listbox should be the maximum value
  1995. si.nMax--;
  1996. }
  1997. }
  1998. else
  1999. _pserv->TxGetHScroll((LONG *) &si.nMin, (LONG *) &si.nMax,
  2000. (LONG *) &si.nPos, (LONG *) &si.nPage, NULL);
  2001. // Do the call
  2002. ::SetScrollInfo(_hwnd, fnBar, &si, fRedraw);
  2003. }
  2004. /*
  2005. * CLstBxWinHost::TxGetScrollBars (pdwScrollBar)
  2006. *
  2007. * @mfunc
  2008. * Get Text Host's scroll bars supported.
  2009. *
  2010. * @rdesc
  2011. * HRESULT = S_OK
  2012. *
  2013. * @comm
  2014. * <p pdwScrollBar> is filled with a boolean combination of the
  2015. * window styles related to scroll bars. Specifically, these are:
  2016. *
  2017. * WS_VSCROLL <nl>
  2018. * WS_HSCROLL <nl>
  2019. * ES_AUTOVSCROLL <nl>
  2020. * ES_AUTOHSCROLL <nl>
  2021. * ES_DISABLENOSCROLL <nl>
  2022. */
  2023. HRESULT CLstBxWinHost::TxGetScrollBars(
  2024. DWORD *pdwScrollBar) //@parm Where to put scrollbar information
  2025. {
  2026. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CLstBxWinHost::TxGetScrollBars");
  2027. *pdwScrollBar = _dwStyle & (WS_VSCROLL | WS_HSCROLL | ((_fDisableScroll) ? ES_DISABLENOSCROLL : 0));
  2028. return NOERROR;
  2029. }
  2030. /*
  2031. * CLstBxWinHost::TxGetHorzExtent (plHorzExtent)
  2032. *
  2033. * @mfunc
  2034. * Get Text Host's horizontal extent
  2035. *
  2036. * @rdesc
  2037. * HRESULT = S_OK
  2038. *
  2039. */
  2040. HRESULT CLstBxWinHost::TxGetHorzExtent (
  2041. LONG *plHorzExtent)
  2042. {
  2043. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CLstBxWinHost::TxGetHorzExtent");
  2044. *plHorzExtent = _lHorzExtent;
  2045. return S_OK;
  2046. }
  2047. /*
  2048. * CLstBxWinHost::TxGetEffects()
  2049. *
  2050. * @mfunc
  2051. * Indicates if a sunken window effect should be drawn
  2052. *
  2053. * @rdesc
  2054. * HRESULT = (_fBorder) ? TXTEFFECT_SUNKEN : TXTEFFECT_NONE
  2055. */
  2056. TXTEFFECT CLstBxWinHost::TxGetEffects() const
  2057. {
  2058. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::TxGetEffects");
  2059. return (_fBorder) ? TXTEFFECT_SUNKEN : TXTEFFECT_NONE;
  2060. }
  2061. /*
  2062. * CLstBxWinHost::TxNotify (iNotify, pv)
  2063. *
  2064. * @mfunc
  2065. * Notify Text Host of various events. Note that there are
  2066. * two basic categories of events, "direct" events and
  2067. * "delayed" events. All listbox notifications are post-action
  2068. *
  2069. *
  2070. * @rdesc
  2071. * S_OK - call succeeded <nl>
  2072. * S_FALSE -- success, but do some different action
  2073. * depending on the event type (see below).
  2074. *
  2075. * @comm
  2076. * The notification events are the same as the notification
  2077. * messages sent to the parent window of a listbox window.
  2078. *
  2079. * <LBN_DBLCLK> user double-clicks an item in teh list box
  2080. *
  2081. * <LBN_ERRSPCAE> The list box cannot allocate enough memory to
  2082. * fulfill a request
  2083. *
  2084. * <LBN_KILLFOCUS> The list box loses the keyboard focus
  2085. *
  2086. * <LBN_CANCEL> The user cancels te selection of an item in the list
  2087. * box
  2088. *
  2089. * <LBN_SELCHANGE> The selection in a list box is about to change
  2090. *
  2091. * <LBN_SETFOCUS> The list box receives the keyboard focus
  2092. *
  2093. */
  2094. HRESULT CLstBxWinHost::TxNotify(
  2095. DWORD iNotify, //@parm Event to notify host of. One of the
  2096. // EN_XXX values from Win32, e.g., EN_CHANGE
  2097. void *pv) //@parm In-only parameter with extra data. Type
  2098. // dependent on <p iNotify>
  2099. {
  2100. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CLstBxWinHost::TxNotify");
  2101. HRESULT hr = NOERROR;
  2102. // Filter-out all the messages except Listbox notification messages
  2103. // If _fNotifyWinEvt is true, we only need to do NotifyWinEvent
  2104. if (_fNotify && !_fNotifyWinEvt) // Notify parent?
  2105. {
  2106. Assert(_hwndParent);
  2107. switch (iNotify)
  2108. {
  2109. case LBN_DBLCLK:
  2110. case LBN_ERRSPACE:
  2111. case LBN_KILLFOCUS:
  2112. case LBN_SELCANCEL:
  2113. case LBN_SELCHANGE:
  2114. case LBN_SETFOCUS:
  2115. case LBN_PRESCROLL:
  2116. case LBN_POSTSCROLL:
  2117. hr = SendMessage(_hwndParent, WM_COMMAND,
  2118. GET_WM_COMMAND_MPS(_idCtrl, _hwnd, iNotify));
  2119. }
  2120. }
  2121. _fNotifyWinEvt = 0;
  2122. #ifndef NOACCESSIBILITY
  2123. DWORD dwLocalWinEvent = _dwWinEvent;
  2124. int nLocalIdx = _nAccessibleIdx;
  2125. _dwWinEvent = 0;
  2126. if (nLocalIdx == -1)
  2127. nLocalIdx = _nCursor+1;
  2128. _nAccessibleIdx = -1;
  2129. if (iNotify == LBN_SELCHANGE || dwLocalWinEvent)
  2130. W32->NotifyWinEvent(dwLocalWinEvent ? dwLocalWinEvent : EVENT_OBJECT_SELECTION, _hwnd, _idCtrl, nLocalIdx);
  2131. #endif
  2132. return hr;
  2133. }
  2134. /*
  2135. * CLstBxWinHost::TxGetPropertyBits(DWORD, DWORD *)
  2136. *
  2137. * @mfunc
  2138. * returns the proper style. This is a way to fool the edit
  2139. * control to behave the way we want it to
  2140. *
  2141. * @rdesc
  2142. * HRESULT = always NOERROR
  2143. */
  2144. HRESULT CLstBxWinHost::TxGetPropertyBits(
  2145. DWORD dwMask,
  2146. DWORD *pdwBits)
  2147. {
  2148. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::TxGetPropertyBits");
  2149. // Note: the rich edit host will never set TXTBIT_SHOWACCELERATOR or
  2150. // TXTBIT_SAVESELECTION. Those are currently only used by forms^3 host.
  2151. // This host is always rich text.
  2152. *pdwBits = (TXTBIT_RICHTEXT | TXTBIT_MULTILINE | TXTBIT_HIDESELECTION |
  2153. TXTBIT_DISABLEDRAG | TXTBIT_USECURRENTBKG) & dwMask;
  2154. return NOERROR;
  2155. }
  2156. /*
  2157. * CLstBxWinHost::TxShowScrollBar (nBar, fShow)
  2158. *
  2159. * @mfunc
  2160. * Shows or Hides scroll bar in Text Host window
  2161. *
  2162. * @rdesc
  2163. * TRUE on success, FALSE otherwise
  2164. *
  2165. * @comm
  2166. * This method is only valid when the control is in-place active;
  2167. * calls while inactive may fail.
  2168. */
  2169. BOOL CLstBxWinHost::TxShowScrollBar(
  2170. INT nBar, //@parm Specifies scroll bar(s) to be shown or hidden
  2171. BOOL fShow) //@parm Specifies whether scroll bar is shown or hidden
  2172. {
  2173. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CLstBxWinHost::TxShowScrollBar");
  2174. Assert(fShow == TRUE || fShow == FALSE);
  2175. if (SB_VERT == nBar)
  2176. {
  2177. // There maybe cases where the item height is smaller than the font size
  2178. // which means the notifications from ITextServices is wrong because
  2179. // it uses the wrong line height. We will use the following case
  2180. // 1a) if _nyItem >= _nyFont OR
  2181. // 1b) if window style is LBS_DISABLESCROLL OR
  2182. // 1c) We are showing the scrollbar w/ current count greater than viewsize OR
  2183. // 1d) We are hiding the scrollbar w/ current count <= viewsize
  2184. if (_fDisableScroll || !_fOwnerDrawVar && (_nyItem >= _nyFont || fShow == (GetCount() > _nViewSize)))
  2185. return CTxtWinHost::TxShowScrollBar(nBar, fShow);
  2186. if (_fOwnerDrawVar)
  2187. {
  2188. BOOL fGreaterThanView;
  2189. SumVarHeight(0, GetCount(), &fGreaterThanView);
  2190. if (fShow == fGreaterThanView)
  2191. return CTxtWinHost::TxShowScrollBar(nBar, fShow);
  2192. }
  2193. }
  2194. else
  2195. {
  2196. if (fShow) // When showing Horz scrollbar,
  2197. _fNoIntegralHeight = TRUE; // turn on _fNoIntegralHeight.
  2198. else
  2199. _fNoIntegralHeight = _fIntegralHeightOld; // Reset to previous setting
  2200. return CTxtWinHost::TxShowScrollBar(nBar, fShow);
  2201. }
  2202. return FALSE;
  2203. }
  2204. /*
  2205. * CLstBxWinHost::TxEnableScrollBar (fuSBFlags, fuArrowflags)
  2206. *
  2207. * @mfunc
  2208. * Enables or disables one or both scroll bar arrows
  2209. * in Text Host window.
  2210. *
  2211. * @rdesc
  2212. * If the arrows are enabled or disabled as specified, the return
  2213. * value is TRUE. If the arrows are already in the requested state or an
  2214. * error occurs, the return value is FALSE.
  2215. *
  2216. * @comm
  2217. * This method is only valid when the control is in-place active;
  2218. * calls while inactive may fail.
  2219. */
  2220. BOOL CLstBxWinHost::TxEnableScrollBar (
  2221. INT fuSBFlags, //@parm Specifies scroll bar type
  2222. INT fuArrowflags) //@parm Specifies whether and which scroll bar arrows
  2223. // are enabled or disabled
  2224. {
  2225. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEEXTERN, "CLstBxWinHost::TxEnableScrollBar");
  2226. // There may be cases where the item height is smaller than the font size
  2227. // which means the notifications from ITextServices is wrong. We have to perform
  2228. // some manual checking for owner draw listboxes. The following cases will be valid
  2229. // 1. If the listbox is NOT owner draw
  2230. // 2. If the message is to disable the control
  2231. // 3. If the count is greater than the viewsize
  2232. if (!_fOwnerDraw || ESB_ENABLE_BOTH != fuArrowflags)
  2233. return CTxtWinHost::TxEnableScrollBar(fuSBFlags, fuArrowflags);
  2234. BOOL fGreaterThanView = GetCount() > _nViewSize;
  2235. if (_fOwnerDrawVar)
  2236. SumVarHeight(0, GetCount(), &fGreaterThanView);
  2237. if (fGreaterThanView)
  2238. return CTxtWinHost::TxEnableScrollBar(fuSBFlags, fuArrowflags);
  2239. return FALSE;
  2240. }
  2241. /*
  2242. * CLstBxWinHost::SetItemsHeight(int, BOOL)
  2243. *
  2244. * @mfunc
  2245. * Sets the items height for all items
  2246. *
  2247. * @rdesc
  2248. * int = number of paragraphs whose fontsize has been changed
  2249. */
  2250. int CLstBxWinHost::SetItemsHeight(
  2251. int nHeight,
  2252. BOOL bUseExact)
  2253. {
  2254. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::SetItemsHeight");
  2255. if (_fOwnerDrawVar)
  2256. return 1;
  2257. // Calculate the new size in points
  2258. long nptNew = MulDiv(nHeight, 1440, W32->GetYPerInchScreenDC());
  2259. long nptMin = MulDiv(_nyFont, 1440, W32->GetYPerInchScreenDC());
  2260. // NOTE:
  2261. // This diverges from what the system list box does but there isn't a way
  2262. // to set the height of a item to smaller than what the richedit will allow and
  2263. // is not ownerdraw. If it is owner draw make sure our height is not zero
  2264. if (((nptNew < nptMin && !_fOwnerDraw) || nHeight <= 0) && !bUseExact)
  2265. nptNew = nptMin;
  2266. // Start setting the new height
  2267. Freeze();
  2268. long nPt;
  2269. PARAFORMAT2 pf2;
  2270. pf2.cbSize = sizeof(PARAFORMAT2);
  2271. if (bUseExact)
  2272. {
  2273. pf2.dwMask = PFM_LINESPACING;
  2274. pf2.bLineSpacingRule = 4;
  2275. pf2.dyLineSpacing = nPt = nptNew;
  2276. }
  2277. else
  2278. {
  2279. pf2.dwMask = PFM_SPACEAFTER;
  2280. pf2.dySpaceAfter = max(nptNew - nptMin, 0);
  2281. nPt = pf2.dySpaceAfter + nptMin;
  2282. }
  2283. // Set the default paragraph format
  2284. LRESULT lr;
  2285. _pserv->TxSendMessage(EM_SETPARAFORMAT, SPF_SETDEFAULT, (WPARAM)&pf2, &lr);
  2286. // set the item height
  2287. if (lr)
  2288. _nyItem = (_fOwnerDraw && nHeight > 0) ? nHeight :
  2289. MulDiv(nPt, W32->GetYPerInchScreenDC(), 1440);
  2290. Unfreeze();
  2291. return lr;
  2292. }
  2293. /*
  2294. * CLstBxWinHost::UpdateSysColors()
  2295. *
  2296. * @mfunc
  2297. * update the system colors in the event they changed or for initialization
  2298. * purposes
  2299. *
  2300. * @rdesc
  2301. * <none>
  2302. */
  2303. void CLstBxWinHost::UpdateSysColors()
  2304. {
  2305. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::UpdateSysColors");
  2306. // Update the system colors
  2307. _crDefBack = ::GetSysColor(COLOR_WINDOW);
  2308. _crSelBack = ::GetSysColor(COLOR_HIGHLIGHT);
  2309. _crDefFore = ::GetSysColor(COLOR_WINDOWTEXT);
  2310. _crSelFore = ::GetSysColor(COLOR_HIGHLIGHTTEXT);
  2311. }
  2312. /*
  2313. * CLstBxWinHost::SetCursor(HDC, int, BOOL)
  2314. *
  2315. * @mfunc
  2316. * Sets the cursor position, if it's valid and draws the focus rectangle if
  2317. * the control has focus. The BOOL is used to determine if the previous
  2318. * cursor drawing needs to be removed
  2319. *
  2320. * @rdesc
  2321. * <none>
  2322. */
  2323. void CLstBxWinHost::SetCursor(
  2324. HDC hdc,
  2325. int idx,
  2326. BOOL bErase)
  2327. {
  2328. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::SetCursor");
  2329. Assert(idx >= -2 && idx < _nCount);
  2330. // Get the hdc if it wasn't passed in
  2331. BOOL bReleaseDC = (hdc == NULL);
  2332. if (bReleaseDC)
  2333. hdc = TxGetDC();
  2334. Assert(hdc);
  2335. RECT rc;
  2336. // don't draw outside the client rect draw the rectangle
  2337. TxGetClientRect(&rc);
  2338. IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
  2339. // Check if we have to remove the previous position
  2340. if ((idx != _nCursor && _fFocus && idx >= -1) || bErase)
  2341. {
  2342. if (LbEnableDraw())
  2343. {
  2344. if (_fOwnerDraw)
  2345. LbDrawItemNotify(hdc, _nCursor, ODA_FOCUS, (IsSelected(max(_nCursor, 0)) ? ODS_SELECTED : 0));
  2346. else if (IsItemViewable(max(0, _nCursor)))
  2347. {
  2348. LbGetItemRect(max(_nCursor, 0), &rc);
  2349. ::DrawFocusRect(hdc, &rc);
  2350. }
  2351. }
  2352. }
  2353. // special flag meaning to set the cursor to the top index
  2354. // if there are items in the listbox
  2355. if (idx == -2)
  2356. {
  2357. if (GetCount())
  2358. {
  2359. idx = max(_nCursor, 0);
  2360. if (!IsItemViewable(idx))
  2361. idx = GetTopIndex();
  2362. }
  2363. else
  2364. idx = -1;
  2365. }
  2366. _nCursor = idx;
  2367. // Only draw the focus rect if the cursor item is
  2368. // visible in the list box
  2369. if (_fFocus && LbEnableDraw())
  2370. {
  2371. if (_fOwnerDraw)
  2372. LbDrawItemNotify(hdc, max(0, _nCursor), ODA_FOCUS, ODS_FOCUS | (IsSelected(max(0, _nCursor)) ? ODS_SELECTED : 0));
  2373. else if (IsItemViewable(max(0, idx)))
  2374. {
  2375. // Now draw the rectangle
  2376. LbGetItemRect(max(0,_nCursor), &rc);
  2377. ::DrawFocusRect(hdc, &rc);
  2378. }
  2379. }
  2380. if (bReleaseDC)
  2381. TxReleaseDC(hdc);
  2382. }
  2383. /*
  2384. * CLstBxWinHost::InitSearch()
  2385. *
  2386. * @mfunc
  2387. * Sets the array to its initial state
  2388. *
  2389. * @rdesc
  2390. * <none>
  2391. */
  2392. void CLstBxWinHost::InitSearch()
  2393. {
  2394. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::InitSearch");
  2395. _fSearching = 0;
  2396. _nidxSearch = 0;
  2397. if (_pwszSearch)
  2398. *_pwszSearch = 0;
  2399. }
  2400. /*
  2401. * CLstBxWinHost::PointInRect(const POINT*)
  2402. *
  2403. * @mfunc
  2404. * Determines if the given point is inside the listbox windows rect
  2405. * The point parameter should be in client coordinates.
  2406. *
  2407. * @rdesc
  2408. * BOOL = inside listbox window rectangle ? TRUE : FALSE
  2409. */
  2410. BOOL CLstBxWinHost::PointInRect(
  2411. const POINT * ppt)
  2412. {
  2413. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::PointInRect");
  2414. Assert(ppt);
  2415. RECT rc;
  2416. ::GetClientRect(_hwnd, &rc);
  2417. return PtInRect(&rc, *ppt);
  2418. }
  2419. /*
  2420. * CLstBxWinHost::GetItemFromPoint(POINT*)
  2421. *
  2422. * @mfunc
  2423. * Retrieves the nearest viewable item from a passed in point.
  2424. * The point should be in client coordinates.
  2425. *
  2426. * @rdesc
  2427. * int = item which is closest to the given in point, -1 if there
  2428. * are no items in the list box
  2429. */
  2430. int CLstBxWinHost::GetItemFromPoint(
  2431. const POINT * ppt)
  2432. {
  2433. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::GetItemFromPoint");
  2434. // perform error checking first
  2435. if (_nCount == 0)
  2436. return -1;
  2437. int y = (signed short)ppt->y;
  2438. // make sure y is in a valid range
  2439. if (y < _rcViewport.top)
  2440. y = 0;
  2441. else if (y > _rcViewport.bottom)
  2442. y = _rcViewport.bottom - 1;
  2443. //need to factor in the possibility an item may not fit entirely into the window view
  2444. int idx;
  2445. if (_fOwnerDrawVar)
  2446. {
  2447. int iHeightCurrent = 0;
  2448. int iHeight;
  2449. for (idx = GetTopIndex(); idx < _nCount; idx++)
  2450. {
  2451. iHeight = iHeightCurrent + _rgData[idx]._uHeight;
  2452. if (iHeightCurrent <= y && y <= iHeight)
  2453. break; // Found it
  2454. iHeightCurrent = iHeight;
  2455. }
  2456. }
  2457. else
  2458. {
  2459. Assert(_nyItem);
  2460. idx = GetTopIndex() + (int)(max(0,(y - 1)) / max(1,_nyItem));
  2461. }
  2462. Assert(IsItemViewable(idx));
  2463. return (idx < _nCount ? idx : _nCount - 1);
  2464. }
  2465. /*
  2466. * CLstBxWinHost::ResetContent()
  2467. *
  2468. * @mfunc
  2469. * Deselects all the items in the list box
  2470. *
  2471. * @rdesc
  2472. * BOOL = If everything went fine ? TRUE : FALSE
  2473. */
  2474. BOOL CLstBxWinHost::ResetContent()
  2475. {
  2476. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::ResetContent");
  2477. Assert(_fOwnerDraw == 0);
  2478. // lets try to be smart about reseting the colors by only select a range
  2479. // from the first selection found to the last selection found
  2480. int nStart = _nCount - 1;
  2481. int nEnd = -1;
  2482. for (int i = 0; i < _nCount; i++)
  2483. {
  2484. if (_rgData[i]._fSelected)
  2485. {
  2486. _rgData[i]._fSelected = 0;
  2487. if (nStart > i)
  2488. nStart = i;
  2489. if (nEnd < i)
  2490. nEnd = i;
  2491. }
  2492. }
  2493. Assert(nStart <= nEnd || ((nStart == _nCount - 1) && (nEnd == -1)));
  2494. if (nStart > nEnd)
  2495. return TRUE;
  2496. return (_nCount > 0) ? SetColors((unsigned)tomAutoColor, (unsigned)tomAutoColor, nStart, nEnd) : FALSE;
  2497. }
  2498. /*
  2499. * CLstBxWinHost::GetString(long, PWCHAR)
  2500. *
  2501. * @mfunc
  2502. * Retrieve the string at the requested index. PWSTR can be null
  2503. * if only the text length is requires
  2504. *
  2505. * @rdesc
  2506. * long = successful ? length of string : -1
  2507. */
  2508. long CLstBxWinHost::GetString(
  2509. long nIdx,
  2510. PWCHAR szOut)
  2511. {
  2512. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::GetString");
  2513. Assert(0 <= nIdx && nIdx < _nCount);
  2514. if (nIdx < 0 || _nCount <= nIdx)
  2515. return -1;
  2516. long l = -1;
  2517. long lStart;
  2518. long lEnd;
  2519. ITextRange* pRange;
  2520. BSTR bstr;
  2521. if (!GetRange(nIdx, nIdx, &pRange))
  2522. return -1;
  2523. // Need to move one character to the left to unselect the paragraph marker.
  2524. Assert(pRange);
  2525. CHECKNOERROR(pRange->MoveEnd(tomCharacter, -1, &lEnd));
  2526. CHECKNOERROR(pRange->GetStart(&lStart));
  2527. CHECKNOERROR(pRange->GetEnd(&lEnd));
  2528. // Get the string
  2529. if (szOut)
  2530. {
  2531. if (_dwStyle & LBS_HASSTRINGS)
  2532. {
  2533. CHECKNOERROR(pRange->GetText(&bstr));
  2534. if (bstr)
  2535. {
  2536. wcscpy(szOut, bstr);
  2537. SysFreeString(bstr);
  2538. }
  2539. else
  2540. wcscpy(szOut, L""); // we got an empty string!
  2541. }
  2542. else
  2543. (*(LPARAM *)szOut) = GetData(nIdx);
  2544. }
  2545. l = lEnd - lStart;
  2546. CleanExit:
  2547. pRange->Release();
  2548. return l;
  2549. }
  2550. /*
  2551. * CLstBxWinHost::InsertString(long, LPCTSTR)
  2552. *
  2553. * @mfunc
  2554. * Insert the string at the requested location. If the
  2555. * requested index is larger than _nCount then the function
  2556. * will fail. The string is inserted with CR appended to
  2557. * to the front and back of the string
  2558. *
  2559. * @rdesc
  2560. * BOOL = successfully inserted ? TRUE : FALSE
  2561. */
  2562. BOOL CLstBxWinHost::InsertString(
  2563. long nIdx,
  2564. LPCTSTR szInsert)
  2565. {
  2566. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::InsertString");
  2567. Assert(szInsert);
  2568. Assert(0 <= nIdx && nIdx <= _nCount);
  2569. // allocate string buffer into stack
  2570. WCHAR sz[1024];
  2571. WCHAR *psz = sz;
  2572. if ( (wcslen(szInsert) + 3 /* 2 paragraphs and a NULL*/) > 1024)
  2573. psz = new WCHAR[wcslen(szInsert) + 3 /* 2 paragraphs and a NULL*/];
  2574. Assert(psz);
  2575. if (psz == NULL)
  2576. {
  2577. TxNotify((unsigned long)LBN_ERRSPACE, NULL);
  2578. return FALSE;
  2579. }
  2580. *psz = NULL;
  2581. if (nIdx == _nCount && _nCount)
  2582. wcscpy(psz, szCR);
  2583. // copy string and add <CR> at the end
  2584. wcscat(psz, szInsert);
  2585. // don't add the carriage return if the entry point is the end
  2586. if (nIdx < _nCount)
  2587. wcscat(psz, szCR);
  2588. BOOL bRet = FALSE;
  2589. ITextRange * pRange = NULL;
  2590. int fFocus = _fFocus;
  2591. long idx = nIdx;
  2592. BSTR bstr = SysAllocString(psz);
  2593. if (!bstr)
  2594. goto CleanExit;
  2595. Assert(bstr);
  2596. if (psz != sz)
  2597. delete [] psz;
  2598. // Set the range to the point where we want to insert the string
  2599. // make sure the requested range is a valid one
  2600. if (nIdx == _nCount)
  2601. idx = max(idx - 1, 0);
  2602. if (!GetRange(idx, idx, &pRange))
  2603. {
  2604. SysFreeString(bstr);
  2605. return FALSE;
  2606. }
  2607. // Collapse the range to the start if insertion is in the middle or top
  2608. // of list, collapse range to the end if we are inserting at the end of the list
  2609. CHECKNOERROR(pRange->Collapse((idx == nIdx)));
  2610. // Need to assume the item was successfully added because during SetText TxEnable(show)Scrollbar
  2611. // gets called which looks at the count to determine if we should display the scroll bar
  2612. _nCount++;
  2613. //bug fix #5411
  2614. // Check if we have focus, if so we need to remove the focus rect first and update the cursor positions
  2615. _fFocus = 0;
  2616. SetCursor(NULL, (idx > GetCursor() || GetCursor() < 0) ? GetCursor() : GetCursor() + 1, fFocus);
  2617. _fFocus = fFocus;
  2618. //For fix height cases where the item height is less than the font we need to manually
  2619. //enable the scrollbar if we need the scrollbar and the scrollbar is disabled.
  2620. BOOL fSetupScrollBar;
  2621. fSetupScrollBar = FALSE;
  2622. if (!_fOwnerDrawVar && _nCount - 1 == _nViewSize &&
  2623. ((_nyItem < _nyFont && _fDisableScroll) || _fOwnerDraw))
  2624. {
  2625. fSetupScrollBar = TRUE;
  2626. TxEnableScrollBar(SB_VERT, _fDisabled ? ESB_DISABLE_BOTH : ESB_ENABLE_BOTH);
  2627. }
  2628. #ifdef _DEBUG
  2629. if (bstr && wcslen(bstr))
  2630. Assert(FALSE);
  2631. #endif
  2632. if (NOERROR != (pRange->SetText(bstr)))
  2633. {
  2634. // NOTE: SetText could return S_FALSE which means it has added some characters but not all.
  2635. // We do want to clean up the text in such case.
  2636. pRange->SetText(NULL); // Cleanup text that may have been added.
  2637. _nCount--;
  2638. //Unsuccessful in adding the string so disable the scrollbar if we enabled it
  2639. if (fSetupScrollBar)
  2640. TxEnableScrollBar(SB_VERT, ESB_DISABLE_BOTH);
  2641. TxNotify((unsigned long)LBN_ERRSPACE, NULL);
  2642. goto CleanExit;
  2643. }
  2644. //We need to update the top index after a string is inserted
  2645. if (idx < GetTopIndex())
  2646. _nTopIdx++;
  2647. bRet = TRUE;
  2648. CleanExit:
  2649. if (bstr)
  2650. SysFreeString(bstr);
  2651. if (pRange)
  2652. pRange->Release();
  2653. return bRet;
  2654. }
  2655. /*
  2656. * BOOL CLstBxWinHost::RemoveString(long, long)
  2657. *
  2658. * @mfunc
  2659. * Prevents TOM from drawing
  2660. *
  2661. * @rdesc
  2662. * BOOL = Successful ? TRUE : FALSE
  2663. */
  2664. BOOL CLstBxWinHost::RemoveString(
  2665. long nStart,
  2666. long nEnd)
  2667. {
  2668. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::RemoveString");
  2669. Assert(nStart <= nEnd);
  2670. Assert(nStart < _nCount && nEnd < _nCount);
  2671. // Remove item from richedit
  2672. Freeze();
  2673. ITextRange* pRange;
  2674. if (!GetRange(nStart, nEnd, &pRange))
  2675. {
  2676. Unfreeze();
  2677. return FALSE;
  2678. }
  2679. long l;
  2680. // Since we can't erase the last paragraph marker we will erase
  2681. // the paragraph marker before the item if it's not the first item
  2682. HRESULT hr;
  2683. if (nStart != 0)
  2684. {
  2685. hr = pRange->MoveStart(tomCharacter, -1, &l);
  2686. Assert(hr == NOERROR);
  2687. hr = pRange->MoveEnd(tomCharacter, -1, &l);
  2688. Assert(hr == NOERROR);
  2689. }
  2690. if (NOERROR != pRange->Delete(tomCharacter, 0, &l) && _nCount > 1)
  2691. {
  2692. Unfreeze();
  2693. pRange->Release();
  2694. return FALSE;
  2695. }
  2696. pRange->Release();
  2697. int nOldCt = _nCount;
  2698. _nCount -= (nEnd - nStart) + 1;
  2699. // Because we delete the paragraph preceeding the item
  2700. // rather than following the item we need to update
  2701. // the paragraph which followed the item. bug fix #4074
  2702. long nFmtPara = max(nStart -1, 0);
  2703. if (!_fOwnerDraw && (IsSelected(nEnd) != IsSelected(nFmtPara) || _nCount == 0))
  2704. {
  2705. DWORD dwFore = (unsigned)tomAutoColor;
  2706. DWORD dwBack = (unsigned)tomAutoColor;
  2707. if (IsSelected(nFmtPara) && _nCount)
  2708. {
  2709. dwFore = _crSelFore;
  2710. dwBack = _crSelBack;
  2711. }
  2712. SetColors(dwFore, dwBack, nFmtPara, nFmtPara);
  2713. }
  2714. // update our internal listbox records
  2715. int j = nEnd + 1;
  2716. for(int i = nStart; j < nOldCt; i++, j++)
  2717. {
  2718. _rgData[i]._fSelected = _rgData.Get(j)._fSelected;
  2719. _rgData[i]._lparamData = _rgData.Get(j)._lparamData;
  2720. _rgData[i]._uHeight = _rgData.Get(j)._uHeight;
  2721. }
  2722. //bug fix #5397
  2723. //we need to reset the internal array containing information
  2724. //about previous items
  2725. while (--j >= _nCount)
  2726. {
  2727. _rgData[j]._fSelected = 0;
  2728. _rgData[j]._lparamData = 0;
  2729. _rgData[i]._uHeight = 0;
  2730. }
  2731. if (_nCount > 0)
  2732. {
  2733. // update the cursor
  2734. if (nStart <= _nCursor)
  2735. _nCursor--;
  2736. _nCursor = min(_nCursor, _nCount - 1);
  2737. if (_fLstType == kExtended)
  2738. {
  2739. if (_nCursor < 0)
  2740. {
  2741. _nOldCursor = min(_nAnchor, _nCount - 1);
  2742. _nAnchor = -1;
  2743. }
  2744. else if (_nAnchor >= 0)
  2745. {
  2746. if (nStart <= _nAnchor && _nAnchor <= nEnd)
  2747. {
  2748. // Store the old anchor for future use
  2749. _nOldCursor = min(_nAnchor, _nCount - 1);
  2750. _nAnchor = -1;
  2751. }
  2752. }
  2753. }
  2754. if (_fOwnerDraw)
  2755. {
  2756. RECT rcStart;
  2757. RECT rcEnd;
  2758. LbGetItemRect(nStart, &rcStart);
  2759. LbGetItemRect(nEnd, &rcEnd);
  2760. rcStart.bottom = rcEnd.bottom;
  2761. if (IntersectRect(&rcStart, &rcStart, &_rcViewport))
  2762. {
  2763. // the list will get bumped up so we need to redraw
  2764. // everything from the top to the bottom
  2765. rcStart.bottom = _rcViewport.bottom;
  2766. ::InvalidateRect(_hwnd, &rcStart, FALSE);
  2767. }
  2768. }
  2769. }
  2770. else
  2771. {
  2772. SetTopViewableItem(0);
  2773. _nAnchor = -1;
  2774. _nCursor = -1;
  2775. }
  2776. //For fix height cases where the item height is less than the font we need to manually
  2777. //enable the scrollbar if we need the scrollbar and the scrollbar is disabled.
  2778. if (!_fOwnerDrawVar)
  2779. {
  2780. if ((_nyItem < _nyFont) && (_fDisableScroll) &&
  2781. (_nCount <= _nViewSize) && (nOldCt > _nViewSize))
  2782. TxEnableScrollBar(SB_VERT, ESB_DISABLE_BOTH);
  2783. }
  2784. LbDeleteItemNotify(nStart, nEnd);
  2785. Assert(GetTopIndex() >= 0);
  2786. if (_nCount)
  2787. LbShowIndex(min(GetTopIndex(), _nCount - 1), FALSE);
  2788. Unfreeze();
  2789. return TRUE;
  2790. }
  2791. /*
  2792. * CLstBxWinHost::Freeze()
  2793. *
  2794. * @mfunc
  2795. * Prevents TOM from drawing
  2796. *
  2797. * @rdesc
  2798. * freeze count
  2799. */
  2800. long CLstBxWinHost::Freeze()
  2801. {
  2802. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::Freeze");
  2803. long l;
  2804. ((CTxtEdit*)_pserv)->Freeze(&l);
  2805. return l;
  2806. }
  2807. /*
  2808. * inline CLstBxWinHost::FreezeCount()
  2809. *
  2810. * @mfunc
  2811. * Returns the current freeze count
  2812. *
  2813. * @rdesc
  2814. * <none>
  2815. */
  2816. short CLstBxWinHost::FreezeCount() const
  2817. {
  2818. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::GetFreezeCount");
  2819. return ((CTxtEdit*)_pserv)->GetFreezeCount();
  2820. }
  2821. /*
  2822. * CLstBxWinHost::Unfreeze(long *)
  2823. *
  2824. * @mfunc
  2825. * Allows TOM to update itself
  2826. *
  2827. * @rdesc
  2828. * freeze count
  2829. */
  2830. long CLstBxWinHost::Unfreeze()
  2831. {
  2832. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::Unfreeze");
  2833. long l;
  2834. ((CTxtEdit*)_pserv)->Unfreeze(&l);
  2835. // HACK ALERT!
  2836. // When ITextRange::ScrollIntoView starts caching the scroll position
  2837. // in cases where the display is frozen the following code can be removed
  2838. // We could have failed in ITextRange::ScrollIntoView
  2839. // Check if we did and try calling it again
  2840. if (!l && _stvidx >= 0)
  2841. {
  2842. ScrollToView(_stvidx);
  2843. _stvidx = -1;
  2844. }
  2845. return l;
  2846. }
  2847. /*
  2848. * CLstBxWinHost::ScrollToView(long)
  2849. *
  2850. * @mfunc
  2851. * Sets the given index to be at the top of
  2852. * the viewable window space
  2853. *
  2854. * @rdesc
  2855. * BOOL = if function succeeded ? TRUE : FALSE
  2856. */
  2857. BOOL CLstBxWinHost::ScrollToView(
  2858. long nTop)
  2859. {
  2860. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::SetTopViewableItem");
  2861. //Get the range which contains the item desired
  2862. BOOL bVal = FALSE;
  2863. ITextRange* pRange = NULL;
  2864. if (!GetRange(nTop, nTop, &pRange))
  2865. return bVal;
  2866. Assert(pRange);
  2867. CHECKNOERROR(pRange->Collapse(1));
  2868. CHECKNOERROR(pRange->ScrollIntoView(tomStart + /* TA_STARTOFLINE */ 32768));
  2869. bVal = TRUE;
  2870. CleanExit:
  2871. pRange->Release();
  2872. // HACK ALERT!
  2873. // When ITextRange::ScrollIntoView starts caching the scroll position
  2874. // in cases where the display is frozen the following code can be removed
  2875. //if we failed record the index we failed to scroll to
  2876. if (!bVal && FreezeCount())
  2877. _stvidx = nTop;
  2878. return bVal;
  2879. }
  2880. /*
  2881. * CLstBxWinHost::SetTopViewableItem(long)
  2882. *
  2883. * @mfunc
  2884. * Sets the given index to be at the top of
  2885. * the viewable window space
  2886. *
  2887. * @rdesc
  2888. * BOOL = if function succeeded ? TRUE : FALSE
  2889. */
  2890. BOOL CLstBxWinHost::SetTopViewableItem(
  2891. long nTop)
  2892. {
  2893. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::SetTopViewableItem");
  2894. // if we don't have any items in the list box then just set the topindex to
  2895. // zero
  2896. if (_nCount == 0)
  2897. {
  2898. Assert(nTop == 0);
  2899. _nTopIdx = 0;
  2900. return TRUE;
  2901. }
  2902. // don't do anything if the requested top index is greater
  2903. // then the amount of items in the list box
  2904. Assert(nTop < _nCount);
  2905. if (nTop >= _nCount)
  2906. return FALSE;
  2907. // Don't do this if it's ownerdraw
  2908. if (!_fOwnerDraw)
  2909. {
  2910. // Since we erase and draw the focus rect here
  2911. // cache the focus rect info and don't bother with the
  2912. // focus rect stuff until later
  2913. int fFocus = _fFocus;
  2914. _fFocus = 0;
  2915. if (fFocus && IsItemViewable(GetCursor()))
  2916. SetCursor(NULL, GetCursor(), TRUE);
  2917. //Get the range which contains the item desired
  2918. long nOldIdx = _nTopIdx;
  2919. _nTopIdx = nTop;
  2920. if (!ScrollToView(nTop))
  2921. {
  2922. // HACK ALERT!
  2923. // When ITextRange::ScrollIntoView starts caching the scroll position
  2924. // in cases where the display is frozen the following code can be removed
  2925. if (_stvidx >= 0)
  2926. return TRUE;
  2927. // Something went wrong and we weren't able to display the index requested
  2928. // reset top index
  2929. _nTopIdx = nOldIdx;
  2930. }
  2931. // Note:
  2932. // If the cursor was not viewable then we don't attempt
  2933. // to display the focus rect because we never erased it
  2934. _fFocus = fFocus;
  2935. if (_fFocus & IsItemViewable(GetCursor()))
  2936. {
  2937. // Now we need to redraw the focus rect which we erased
  2938. SetCursor(NULL, GetCursor(), FALSE);
  2939. }
  2940. }
  2941. else
  2942. {
  2943. int dy = (_nTopIdx - nTop) * _nyItem;
  2944. if (_fOwnerDrawVar)
  2945. {
  2946. if (_nTopIdx > nTop)
  2947. dy = SumVarHeight(nTop, _nTopIdx);
  2948. else
  2949. dy = -SumVarHeight(_nTopIdx, nTop);
  2950. }
  2951. RECT rc;
  2952. TxGetClientRect(&rc);
  2953. _nTopIdx = nTop;
  2954. _fSetScroll = 0;
  2955. if (_fSetRedraw)
  2956. {
  2957. if (((CTxtEdit *)_pserv)->_fLBScrollNotify)
  2958. TxNotify(LBN_PRESCROLL, NULL);
  2959. TxScrollWindowEx(0, dy, NULL, &rc, NULL, NULL,
  2960. SW_INVALIDATE | SW_ERASE | SW_SCROLLCHILDREN);
  2961. if (((CTxtEdit *)_pserv)->_fLBScrollNotify)
  2962. TxNotify(LBN_POSTSCROLL, NULL);
  2963. if (_dwStyle & WS_VSCROLL)
  2964. SetScrollInfo(SB_VERT, TRUE); // we update the scrollbar manually if we are in ownerdraw mode
  2965. UpdateWindow(_hwnd);
  2966. }
  2967. else
  2968. _fSetScroll = 1;
  2969. }
  2970. return TRUE;
  2971. }
  2972. /*
  2973. * CLstBxWinHost::GetRange(long, long, ITextRange**)
  2974. *
  2975. * @mfunc
  2976. * Sets the range given the top and bottom index
  2977. * by storing the range into ITextRange
  2978. *
  2979. * @rdesc
  2980. * BOOL = if function succeeded ? TRUE : FALSE
  2981. */
  2982. BOOL CLstBxWinHost::GetRange(
  2983. long nTop,
  2984. long nBottom,
  2985. ITextRange** ppRange)
  2986. {
  2987. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::GetRange");
  2988. // do some error checking
  2989. if (nTop < 0 || nTop > _nCount || nBottom < 0 || nBottom > _nCount)
  2990. return FALSE;
  2991. Assert(ppRange);
  2992. if (NOERROR != ((CTxtEdit*)_pserv)->Range(0, 0, ppRange))
  2993. {
  2994. Assert(FALSE);
  2995. return FALSE;
  2996. }
  2997. Assert(*ppRange);
  2998. if (_nIdxLastGetRange && nTop >= _nIdxLastGetRange)
  2999. {
  3000. long Count;
  3001. CHECKNOERROR((*ppRange)->SetRange(_cpLastGetRange, _cpLastGetRange));
  3002. if (nTop > _nIdxLastGetRange)
  3003. CHECKNOERROR((*ppRange)->Move(tomParagraph, nTop - _nIdxLastGetRange, &Count));
  3004. CHECKNOERROR((*ppRange)->MoveEnd(tomParagraph, 1, &Count));
  3005. }
  3006. else
  3007. {
  3008. CHECKNOERROR((*ppRange)->SetIndex(tomParagraph, nTop + 1, 1));
  3009. }
  3010. if (nBottom > nTop)
  3011. {
  3012. long l;
  3013. CHECKNOERROR((*ppRange)->MoveEnd(tomParagraph, nBottom - nTop, &l));
  3014. }
  3015. if (nTop)
  3016. {
  3017. _nIdxLastGetRange = nTop;
  3018. CHECKNOERROR((*ppRange)->GetStart(&_cpLastGetRange));
  3019. }
  3020. return TRUE;
  3021. CleanExit:
  3022. Assert(FALSE);
  3023. (*ppRange)->Release();
  3024. *ppRange = NULL;
  3025. _nIdxLastGetRange = 0;
  3026. _cpLastGetRange = 0;
  3027. return FALSE;
  3028. }
  3029. /*
  3030. * CLstBxWinHost::SetColors(DWORD, DWORD, long, long)
  3031. *
  3032. * @mfunc
  3033. * Sets the background color for the givin range of paragraphs. This
  3034. * only operates in terms of paragraphs.
  3035. *
  3036. * @rdesc
  3037. * BOOL = if function succeeded in changing different color
  3038. */
  3039. BOOL CLstBxWinHost::SetColors(
  3040. DWORD dwFgColor,
  3041. DWORD dwBgColor,
  3042. long nParaStart,
  3043. long nParaEnd)
  3044. {
  3045. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::SetColors");
  3046. Assert(_fOwnerDraw == 0);
  3047. //Get the range of the index
  3048. ITextRange* pRange;
  3049. if (!GetRange(nParaStart, nParaEnd, &pRange))
  3050. return FALSE;
  3051. BOOL bRet = FALSE;
  3052. ITextFont* pFont;
  3053. // Set the background and forground color
  3054. if (NOERROR != pRange->GetFont(&pFont))
  3055. {
  3056. pRange->Release();
  3057. return FALSE;
  3058. }
  3059. Assert(pFont);
  3060. CHECKNOERROR(pFont->SetBackColor(dwBgColor));
  3061. CHECKNOERROR(pFont->SetForeColor(dwFgColor));
  3062. bRet = TRUE;
  3063. CleanExit:
  3064. // Release pointers
  3065. pFont->Release();
  3066. pRange->Release();
  3067. return bRet;
  3068. }
  3069. ///////////////////////////// Message Map Functions ////////////////////////////////
  3070. /*
  3071. * void CLstBxWinHost::OnSetCursor()
  3072. *
  3073. * @mfunc
  3074. * Handles the WM_SETCURSOR message.
  3075. *
  3076. * @rdesc
  3077. * LRESULT = return value after message is processed
  3078. */
  3079. LRESULT CLstBxWinHost::OnSetCursor()
  3080. {
  3081. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnSetCursor");
  3082. // Just make sure the cursor is an arrow if it's over us
  3083. TxSetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW)), NULL);
  3084. return 1;
  3085. }
  3086. /*
  3087. * void CLstBxWinHost::OnSetRedraw(WAPRAM)
  3088. *
  3089. * @mfunc
  3090. * Handles the WM_SETREDRAW message.
  3091. *
  3092. * @rdesc
  3093. * LRESULT = return value after message is processed
  3094. */
  3095. LRESULT CLstBxWinHost::OnSetRedraw(
  3096. WPARAM wparam)
  3097. {
  3098. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnSetRedraw");
  3099. long lCount = 0;
  3100. BOOL fSetRedraw = (wparam == TRUE);
  3101. if (fSetRedraw != (BOOL)_fSetRedraw)
  3102. {
  3103. _fSetRedraw = fSetRedraw;
  3104. if (fSetRedraw)
  3105. lCount = Unfreeze(); // Turn on display
  3106. else
  3107. lCount = Freeze(); // Turn off display
  3108. }
  3109. if (fSetRedraw && lCount == 0)
  3110. {
  3111. if (_fSetScroll)
  3112. {
  3113. _fSetScroll = 0;
  3114. if (_dwStyle & WS_VSCROLL)
  3115. SetScrollInfo(SB_VERT, TRUE);
  3116. }
  3117. OnSunkenWindowPosChanging(_hwnd, NULL); // Erase frame/scrollbars as well
  3118. }
  3119. return 1;
  3120. }
  3121. /*
  3122. * void CLstBxWinHost::OnSysColorChange()
  3123. *
  3124. * @mfunc
  3125. * Handles the WM_SYSCOLORCHANGE message.
  3126. *
  3127. */
  3128. void CLstBxWinHost::OnSysColorChange()
  3129. {
  3130. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnSysColorChange");
  3131. if (!_fOwnerDraw)
  3132. {
  3133. // set the new colors
  3134. COLORREF crDefBack = _crDefBack;
  3135. COLORREF crDefFore = _crDefFore;
  3136. COLORREF crSelBack = _crSelBack;
  3137. COLORREF crSelFore = _crSelFore;
  3138. // update colors
  3139. UpdateSysColors();
  3140. // optimization check; don't do anything if there are no elements
  3141. if (_nCount <= 0)
  3142. return;
  3143. // Only update the list box if colors changed
  3144. if (crDefBack != _crDefBack || crDefFore != _crDefFore ||
  3145. crSelBack != _crSelBack || crSelFore != _crSelFore)
  3146. {
  3147. //Bug fix #4847
  3148. // notify parent first
  3149. CTxtWinHost::OnSysColorChange();
  3150. ResetItemColor();
  3151. }
  3152. }
  3153. }
  3154. /*
  3155. * CLstBxWinHost::OnSettingChange()
  3156. *
  3157. * @mfunc
  3158. * forwards the WM_SETTINGCHANGE message to RECombobox
  3159. *
  3160. */
  3161. void CLstBxWinHost::OnSettingChange(
  3162. WPARAM wparam,
  3163. LPARAM lparam)
  3164. {
  3165. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnSettingChange");
  3166. if (_pcbHost)
  3167. SendMessage(_hwndParent, WM_SETTINGCHANGE, wparam, lparam); // Forward this message to cb host
  3168. }
  3169. /*
  3170. * LRESULT CLstBxWinHost::OnChar(WORD, DWORD)
  3171. *
  3172. * @mfunc
  3173. * Handles the WM_CHAR message.
  3174. *
  3175. * @rdesc
  3176. * LRESULT = return value after message is processed
  3177. */
  3178. LRESULT CLstBxWinHost::OnChar(
  3179. WORD vKey,
  3180. DWORD lparam)
  3181. {
  3182. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnChar");
  3183. // don't do anything if list box is empty or in the middle of
  3184. // a mouse down
  3185. if (_fMouseDown || _nCount == 0)
  3186. return 0;
  3187. BOOL fControl = (GetKeyState(VK_CONTROL) < 0);
  3188. int nSel = -1;
  3189. int nRes;
  3190. if (_fWantKBInput && _fOwnerDraw && !_fHasStrings)
  3191. {
  3192. nRes = SendMessage(_hwndParent, WM_CHARTOITEM, MAKELONG(vKey, _nCursor), (LPARAM)_hwnd);
  3193. if (nRes < 0)
  3194. return 1;
  3195. goto SELECT_SEL;
  3196. }
  3197. switch (vKey)
  3198. {
  3199. case VK_ESCAPE:
  3200. InitSearch();
  3201. return 0;
  3202. case VK_BACK:
  3203. if (_pwszSearch && _nidxSearch)
  3204. {
  3205. if (_nidxSearch > 0)
  3206. _nidxSearch--;
  3207. _pwszSearch[_nidxSearch] = NULL;
  3208. break; // we break out of case because we still want to perform the search
  3209. }
  3210. return 0;
  3211. case VK_SPACE:
  3212. if (_fLstType == kMultiple)
  3213. return 0;
  3214. /* Fall through case */
  3215. default:
  3216. // convert CTRL+char to char
  3217. if (fControl && vKey < 0x20)
  3218. vKey += 0x40;
  3219. // don't go beyond the search array size
  3220. if (_nidxSearch >= LBSEARCH_MAXSIZE)
  3221. {
  3222. ((CTxtEdit*)_pserv)->Beep();
  3223. return 0;
  3224. }
  3225. // allocate string if not already allocated
  3226. if (_pwszSearch == NULL)
  3227. _pwszSearch = new WCHAR[LBSEARCH_MAXSIZE];
  3228. // error checking
  3229. if (_pwszSearch == NULL)
  3230. {
  3231. ((CTxtEdit*)_pserv)->Beep();
  3232. Assert(FALSE && "Unable to allocate search string");
  3233. return 0;
  3234. }
  3235. // put the input character into string array
  3236. _pwszSearch[_nidxSearch++] = (WCHAR)vKey;
  3237. _pwszSearch[_nidxSearch] = NULL;
  3238. }
  3239. if (_fSort)
  3240. {
  3241. nSel = (_fSearching) ? _nCursor + 1 : 0;
  3242. // Start the search for a string
  3243. TxSetTimer(ID_LB_SEARCH, ID_LB_SEARCH_DEFAULT);
  3244. _fSearching = 1;
  3245. }
  3246. else
  3247. {
  3248. _nidxSearch = 0;
  3249. nSel = _nCursor + 1;
  3250. }
  3251. // Make sure our index isn't more than the items we have
  3252. if (nSel >= _nCount)
  3253. nSel = 0;
  3254. nRes = LbFindString(nSel, _pwszSearch, FALSE);
  3255. if (nRes < 0)
  3256. {
  3257. if (_pwszSearch)
  3258. {
  3259. if (_nidxSearch > 0)
  3260. _nidxSearch--;
  3261. if (_nidxSearch == 1 && _pwszSearch[0] == _pwszSearch[1])
  3262. {
  3263. _pwszSearch[1] = NULL;
  3264. nRes = LbFindString(nSel, _pwszSearch, FALSE);
  3265. }
  3266. }
  3267. }
  3268. SELECT_SEL:
  3269. // If a matching string is found then select it
  3270. if (nRes >= 0)
  3271. OnKeyDown(nRes, 0, 1);
  3272. // If Hi-Ansi need to send a wm_syskeyup message to ITextServices to
  3273. // stabalize the state
  3274. if (0x80 <= vKey && vKey <= 0xFF && !HIWORD(GetKeyState(VK_MENU)))
  3275. {
  3276. LRESULT lres;
  3277. _pserv->TxSendMessage(WM_SYSKEYUP, VK_MENU, 0xC0000000, &lres);
  3278. }
  3279. return 0;
  3280. }
  3281. /*
  3282. * LRESULT CLstBxWinHost::OnKeyDown(WPARAM, LPARAM, INT)
  3283. *
  3284. * @mfunc
  3285. * Handles the WM_KEYDOWN message. The BOOL ff is used as a flag for calls
  3286. * made internally and not responsive to the WM_KEYDOWN message. Since this
  3287. * function is used for other things, ie helper to dealing with the WM_CHAR message.
  3288. *
  3289. * @rdesc
  3290. * LRESULT = return value after message is processed
  3291. */
  3292. LRESULT CLstBxWinHost::OnKeyDown(
  3293. WPARAM vKey,
  3294. LPARAM lparam,
  3295. int ff)
  3296. {
  3297. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnKeyDown");
  3298. // Ignore keyboard input if we are in the middle of a mouse down deal or
  3299. // if there are no items in the listbox. Note that we let F4's go
  3300. // through for combo boxes so that the use can pop up and down empty
  3301. // combo boxes.
  3302. if (_fMouseDown || (_nCount == 0 && vKey != VK_F4))
  3303. return 1;
  3304. // Check if the shift key is down for Extended listbox style only
  3305. int ffShift = 0;
  3306. if (_fLstType == kExtended)
  3307. ffShift = HIWORD(GetKeyState(VK_SHIFT));
  3308. // Special case!
  3309. // Check if this function is called as a helper
  3310. int nSel = (ff) ? vKey : -1;
  3311. TxKillTimer(ID_LB_CAPTURE);
  3312. if (_fWantKBInput && ff == 0) // Need to notify the parent the key was pressed
  3313. nSel = SendMessage(_hwndParent, WM_VKEYTOITEM, MAKELONG(vKey, _nCursor), (LPARAM)_hwnd);
  3314. // if the parent returns -2 then we don't do anything and immediately exit out
  3315. // if the parent returns >=0 then we just jump to that index
  3316. // else we just continue with the default procedure.
  3317. if (nSel == -2)
  3318. return 1;
  3319. else if (nSel >= 0)
  3320. goto SKIP_DEFAULT;
  3321. if (nSel < 0)
  3322. {
  3323. // Need to set the selection so find the new selection
  3324. // based on the virtual key pressed
  3325. switch (vKey)
  3326. {
  3327. // UNDONE: Later, not language independent!!!
  3328. // Need to find-out how NT5.0 determines the slash issue??
  3329. case VERKEY_BACKSLASH:
  3330. // Deselect everything if we are in extended mode
  3331. if (HIWORD(GetKeyState(VK_CONTROL)) && _fLstType == kExtended)
  3332. {
  3333. // NOTE:
  3334. // Winnt loses the anchor and performing a shift+<vkey>
  3335. // doesn't select any items. Instead, it just moves the
  3336. // cursor w/o selecting the current cursor
  3337. _nAnchor = -1;
  3338. LbSetSelection(_nCursor, _nCursor, LBSEL_RESET | LBSEL_SELECT, 0, 0);
  3339. TxNotify(LBN_SELCHANGE, NULL);
  3340. }
  3341. return 1;
  3342. case VK_DIVIDE:
  3343. case VERKEY_SLASH:
  3344. // Select everything if we are in extended mode
  3345. if (HIWORD(GetKeyState(VK_CONTROL)) && _fLstType == kExtended)
  3346. {
  3347. // NOTE:
  3348. // Winnt behaves as we expect. In other words the anchor
  3349. // isn't changed and neither is the cursor
  3350. LbSetSelection(0, _nCount - 1, LBSEL_SELECT, 0, 0);
  3351. TxNotify(LBN_SELCHANGE, NULL);
  3352. }
  3353. return 1;
  3354. case VK_SPACE:
  3355. // just get out if there is nothing to select
  3356. if (_nCursor < 0 && !GetCount())
  3357. return 1;
  3358. // Just select current item
  3359. nSel = _nCursor;
  3360. break;
  3361. case VK_PRIOR:
  3362. // move the cursor up enough so the current item which the cursor
  3363. // is pointing to is at the bottom and the new cursor position is at the top
  3364. if (_fOwnerDrawVar)
  3365. nSel = PageVarHeight(_nCursor, FALSE);
  3366. else
  3367. nSel = _nCursor - _nViewSize + 1;
  3368. if (nSel < 0)
  3369. nSel = 0;
  3370. break;
  3371. case VK_NEXT:
  3372. // move the cursor down enough so the current item which the cursor
  3373. // is point is at the top and the new cursor position is at the bottom
  3374. if (_fOwnerDrawVar)
  3375. nSel = PageVarHeight(_nCursor, TRUE);
  3376. else
  3377. nSel = _nCursor + _nViewSize - 1;
  3378. if (nSel >= _nCount)
  3379. nSel = _nCount - 1;
  3380. break;
  3381. case VK_HOME:
  3382. // move to the top of the list
  3383. nSel = 0;
  3384. break;
  3385. case VK_END:
  3386. // move to the bottom of the list
  3387. nSel = _nCount - 1;
  3388. break;
  3389. case VK_LEFT:
  3390. case VK_UP:
  3391. nSel = (_nCursor > 0) ? _nCursor - 1 : 0;
  3392. break;
  3393. case VK_RIGHT:
  3394. case VK_DOWN:
  3395. nSel = (_nCursor < _nCount - 1) ? _nCursor + 1 : _nCount - 1;
  3396. break;
  3397. case VK_RETURN:
  3398. case VK_F4:
  3399. case VK_ESCAPE:
  3400. if (_fLstType == kCombo)
  3401. {
  3402. Assert(_pcbHost);
  3403. int nCursor = (vKey == VK_RETURN) ? GetCursor() : _nOldCursor;
  3404. _pcbHost->SetSelectionInfo(vKey == VK_RETURN, nCursor);
  3405. LbSetSelection(nCursor, nCursor, LBSEL_RESET |
  3406. ((nCursor == -1) ? 0 : LBSEL_NEWCURSOR | LBSEL_SELECT), nCursor, nCursor);
  3407. OnCBTracking(LBCBM_END, 0); // we need to do this because we may have some extra messages
  3408. // in our message queue which can change the selections
  3409. SendMessage(_hwndParent, LBCB_TRACKING, 0, 0);
  3410. }
  3411. // NOTE:
  3412. // We differ from Winnt here in that we expect the
  3413. // combobox window handler to do all the positioning and
  3414. // showing of the list box. So when we get this message
  3415. // and we are part of a combobox we should notify the
  3416. // combobox and in turn the combobox should immediately close us.
  3417. //return 1;
  3418. //case VK_F8: // not suppported
  3419. // We need to return this to pserv to process these keys
  3420. /*
  3421. case VK_MENU:
  3422. case VK_CONTROL:
  3423. case VK_SHIFT:
  3424. return 1;
  3425. */
  3426. default:
  3427. return 1;
  3428. }
  3429. }
  3430. // There can be cases where nSel = -1; _nCursor = -1 && _nViewSize = 1
  3431. // make sure the selection index is valid
  3432. if (nSel < 0)
  3433. nSel = 0;
  3434. SKIP_DEFAULT:
  3435. // Should the cursor be set at the top or bottom of the list box??
  3436. BOOL bTop = (_nCursor > nSel) ? TRUE : FALSE;
  3437. Freeze();
  3438. if (_fLstType == kMultiple)
  3439. {
  3440. if (vKey == VK_SPACE)
  3441. {
  3442. BOOL fSel = IsSelected(nSel);
  3443. if (LbSetSelection(nSel, nSel, LBSEL_NEWCURSOR | (IsSelected(nSel) ? 0 : LBSEL_SELECT), nSel, 0))
  3444. {
  3445. _nAnchor = nSel;
  3446. #ifndef NOACCESSIBILITY
  3447. _dwWinEvent = EVENT_OBJECT_FOCUS;
  3448. _fNotifyWinEvt = TRUE;
  3449. TxNotify(_dwWinEvent, NULL);
  3450. if (fSel)
  3451. _dwWinEvent = EVENT_OBJECT_SELECTIONREMOVE;
  3452. #endif
  3453. }
  3454. }
  3455. else
  3456. {
  3457. SetCursor(NULL, nSel, TRUE);
  3458. #ifndef NOACCESSIBILITY
  3459. _dwWinEvent = EVENT_OBJECT_FOCUS;
  3460. _fNotifyWinEvt = TRUE;
  3461. TxNotify(_dwWinEvent, NULL);
  3462. #endif
  3463. }
  3464. }
  3465. else
  3466. {
  3467. if (ffShift && _fLstType == kExtended)
  3468. {
  3469. // Set the anchor if it already isn't set
  3470. _nOldCursor = -1;
  3471. if (_nAnchor < 0)
  3472. _nAnchor = nSel;
  3473. LbSetSelection(_nAnchor, nSel, LBSEL_RESET | LBSEL_SELECT | LBSEL_NEWCURSOR, nSel, 0);
  3474. #ifndef NOACCESSIBILITY
  3475. _dwWinEvent = EVENT_OBJECT_FOCUS;
  3476. _fNotifyWinEvt = TRUE;
  3477. TxNotify(_dwWinEvent, NULL);
  3478. _dwWinEvent = EVENT_OBJECT_SELECTIONWITHIN;
  3479. _fNotifyWinEvt = TRUE;
  3480. TxNotify(_dwWinEvent, NULL);
  3481. #endif
  3482. }
  3483. else
  3484. {
  3485. // if the selected item is already selected then
  3486. // just exit out
  3487. if (_nCursor == nSel && IsSelected(_nCursor))
  3488. {
  3489. Unfreeze();
  3490. return 1;
  3491. }
  3492. LbSetSelection(nSel, nSel, LBSEL_DEFAULT, nSel, nSel);
  3493. #ifndef NOACCESSIBILITY
  3494. _dwWinEvent = EVENT_OBJECT_FOCUS;
  3495. _fNotifyWinEvt = TRUE;
  3496. TxNotify(_dwWinEvent, NULL);
  3497. #endif
  3498. }
  3499. }
  3500. // LbShowIndex eventually calls ScrollToView which fails if display is frozen
  3501. Unfreeze();
  3502. // Make sure the selection is visible
  3503. LbShowIndex(nSel, bTop);
  3504. // key presses qualify as ok selections so we have to update the old cursor position
  3505. TxNotify(LBN_SELCHANGE, NULL);
  3506. _nOldCursor = _nCursor;
  3507. return 1;
  3508. }
  3509. /*
  3510. * LRESULT CLstBxWinHost::OnTimer(WPARAM, LPARAM)
  3511. *
  3512. * @mfunc
  3513. * Handles the WM_TIMER message
  3514. *
  3515. * @rdesc
  3516. * LRESULT = return value after message is processed
  3517. */
  3518. LRESULT CLstBxWinHost::OnTimer(
  3519. WPARAM wparam,
  3520. LPARAM lparam)
  3521. {
  3522. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnTimer");
  3523. // Check which timer we have
  3524. switch (wparam)
  3525. {
  3526. case ID_LB_CAPTURE:
  3527. // for mouse movements let mousemove handler deal with it
  3528. if (_fCapture)
  3529. {
  3530. POINT pt;
  3531. ::GetCursorPos(&pt);
  3532. // Must convert to client coordinates to mimic the mousemove call
  3533. TxScreenToClient(&pt);
  3534. OnMouseMove(0, MAKELONG(pt.x, pt.y));
  3535. }
  3536. break;
  3537. case ID_LB_SEARCH:
  3538. // for type search. If we get here means > 2 seconds elapsed before last
  3539. // character was typed in so reset type search and kill the timer
  3540. InitSearch();
  3541. TxKillTimer(ID_LB_SEARCH);
  3542. break;
  3543. default:
  3544. return 1;
  3545. }
  3546. return 0;
  3547. }
  3548. /*
  3549. * LRESULT CLstBxWinHost::OnVScroll(WPARAM, LPARAM)
  3550. *
  3551. * @mfunc
  3552. * Handles the WM_VSCROLL message
  3553. *
  3554. * @rdesc
  3555. * LRESULT = return value after message is processed
  3556. */
  3557. LRESULT CLstBxWinHost::OnVScroll(
  3558. WPARAM wparam,
  3559. LPARAM lparam)
  3560. {
  3561. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnVScroll");
  3562. if (_fOwnerDrawVar)
  3563. {
  3564. BOOL fGreaterThanView;
  3565. SumVarHeight(0, _nCount, &fGreaterThanView);
  3566. if (!fGreaterThanView) // If less than current view size
  3567. return 0; // nothing to scroll
  3568. }
  3569. else if (_nCount <= _nViewSize)
  3570. return 0;
  3571. int nCmd = LOWORD(wparam);
  3572. int nIdx = 0;
  3573. switch (nCmd)
  3574. {
  3575. case SB_TOP:
  3576. nIdx = 0;
  3577. break;
  3578. case SB_BOTTOM:
  3579. if (_fOwnerDrawVar)
  3580. nIdx = PageVarHeight(_nCount, FALSE) + 1;
  3581. else
  3582. nIdx = _nCount - _nViewSize;
  3583. if (nIdx < 0)
  3584. nIdx = 0;
  3585. if (nIdx >= _nCount)
  3586. nIdx = _nCount - 1;
  3587. break;
  3588. case SB_LINEDOWN:
  3589. nIdx = GetTopIndex() + 1;
  3590. break;
  3591. case SB_LINEUP:
  3592. nIdx = GetTopIndex() - 1;
  3593. if (nIdx < 0)
  3594. nIdx = 0;
  3595. break;
  3596. case SB_PAGEDOWN:
  3597. if (_fOwnerDrawVar)
  3598. nIdx = PageVarHeight(GetTopIndex(), TRUE);
  3599. else
  3600. {
  3601. nIdx = GetTopIndex() + _nViewSize;
  3602. if (nIdx > (_nCount - _nViewSize))
  3603. nIdx = _nCount - _nViewSize;
  3604. }
  3605. break;
  3606. case SB_PAGEUP:
  3607. if (_fOwnerDrawVar)
  3608. nIdx = PageVarHeight(GetTopIndex(), FALSE);
  3609. else
  3610. nIdx = GetTopIndex() - _nViewSize;
  3611. if (nIdx < 0)
  3612. nIdx = 0;
  3613. break;
  3614. case SB_THUMBPOSITION:
  3615. case SB_THUMBTRACK:
  3616. // NOTE:
  3617. // if the list box is expected to hold more that 0xffff items
  3618. // then we need to modify this code to call GetScrollInfo.
  3619. if (_fOwnerDrawVar)
  3620. nIdx = GetIdxFromHeight(HIWORD(wparam));
  3621. else
  3622. nIdx = HIWORD(wparam) / _nyItem;
  3623. break;
  3624. case SB_SETINDEX:
  3625. // Internal case for setting the index directly.
  3626. nIdx = HIWORD(wparam);
  3627. break;
  3628. // Don't need to do anything for this case
  3629. case SB_ENDSCROLL:
  3630. return 0;
  3631. }
  3632. LbSetTopIndex(nIdx);
  3633. return 0;
  3634. }
  3635. /*
  3636. * LRESULT CLstBxWinHost::OnHScroll(WPARAM, LPARAM)
  3637. *
  3638. * @mfunc
  3639. * Handles the WM_HSCROLL message
  3640. *
  3641. * @rdesc
  3642. * LRESULT = return value after message is processed
  3643. */
  3644. LRESULT CLstBxWinHost::OnHScroll(
  3645. WPARAM wparam,
  3646. LPARAM lparam)
  3647. {
  3648. BOOL fRedrawCursor = FALSE;
  3649. BOOL fFocus = _fFocus;
  3650. LRESULT lres = 0;
  3651. int nCmd = LOWORD(wparam);
  3652. if (nCmd == SB_LINEDOWN || nCmd == SB_PAGEDOWN)
  3653. {
  3654. LONG lMax, lPos, lPage;
  3655. _pserv->TxGetHScroll(NULL, &lMax, &lPos, &lPage, NULL);
  3656. if (lPos + lPage >= lMax)
  3657. return 0;
  3658. }
  3659. else if (nCmd == SB_ENDSCROLL)
  3660. return 0; // Do nothing for this case
  3661. if (!_fOwnerDraw && fFocus && IsItemViewable(GetCursor()))
  3662. {
  3663. fRedrawCursor = TRUE;
  3664. _fFocus = 0;
  3665. SetCursor(NULL, GetCursor(), TRUE); // force the removal of focus rect
  3666. }
  3667. _pserv->TxSendMessage(WM_HSCROLL, wparam, lparam, &lres);
  3668. if (fRedrawCursor)
  3669. {
  3670. _fFocus = fFocus;
  3671. SetCursor(NULL, GetCursor(), FALSE); // Redraw the focus rect which we erased
  3672. }
  3673. return lres;
  3674. }
  3675. /*
  3676. * LRESULT CLstBxWinHost::OnCaptureChanged(WPARAM, LPARAM)
  3677. *
  3678. * @mfunc
  3679. * Handles the WM_CAPTURECHANGED message
  3680. *
  3681. * @rdesc
  3682. * LRESULT = return value after message is processed
  3683. */
  3684. LRESULT CLstBxWinHost::OnCaptureChanged(
  3685. WPARAM wparam,
  3686. LPARAM lparam)
  3687. {
  3688. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnCaptureChanged");
  3689. if (_fCapture)
  3690. {
  3691. POINT pt;
  3692. ::GetCursorPos(&pt);
  3693. ::ScreenToClient(_hwnd, &pt);
  3694. // prevent us from trying to release capture since we don't have
  3695. // it anyways by set flag and killing timer
  3696. _fCapture = 0;
  3697. TxKillTimer(ID_LB_CAPTURE);
  3698. OnLButtonUp(0, MAKELONG(pt.y, pt.x), LBN_SELCANCEL);
  3699. }
  3700. return 0;
  3701. }
  3702. //FUTURE:
  3703. // Do we need to support ReadModeHelper?
  3704. /*
  3705. * LRESULT CLstBxWinHost::OnMouseWheel(WPARAM, LPARAM)
  3706. *
  3707. * @mfunc
  3708. * Handles the WM_MOUSEWHEEL message
  3709. *
  3710. * @rdesc
  3711. * LRESULT = return value after message is processed
  3712. */
  3713. LRESULT CLstBxWinHost::OnMouseWheel(
  3714. WPARAM wparam,
  3715. LPARAM lparam)
  3716. {
  3717. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnMouseWheel");
  3718. // we don't to any zooms or anything of the sort
  3719. if ((wparam & MK_CONTROL) == MK_CONTROL)
  3720. return 1;
  3721. // Check if the scroll is ok w/ the listbox requirements
  3722. LRESULT lReturn = 1;
  3723. short delta = (short)(HIWORD(wparam));
  3724. _cWheelDelta -= delta;
  3725. if ((abs(_cWheelDelta) >= WHEEL_DELTA) && (_dwStyle & WS_VSCROLL ))
  3726. {
  3727. BOOL fGreaterThanView = _nCount > _nViewSize;
  3728. if (_fOwnerDrawVar)
  3729. SumVarHeight(0, _nCount, &fGreaterThanView);
  3730. if (!fGreaterThanView) // Smaller than current view size
  3731. return lReturn; // no need to scroll
  3732. // shut-off timer for right now
  3733. TxKillTimer(ID_LB_CAPTURE);
  3734. Assert(delta != 0);
  3735. int nlines = W32->GetRollerLineScrollCount();
  3736. if (nlines == -1)
  3737. {
  3738. OnVScroll(MAKELONG((delta < 0) ? SB_PAGEUP : SB_PAGEDOWN, 0), 0);
  3739. }
  3740. else
  3741. {
  3742. int nIdx;
  3743. //Calculate the number of lines to scroll
  3744. nlines *= _cWheelDelta/WHEEL_DELTA;
  3745. if (!_fOwnerDrawVar)
  3746. {
  3747. //Perform some bounds checking
  3748. nlines = min(_nViewSize - 1, nlines);
  3749. nIdx = max(0, nlines + GetTopIndex());
  3750. nIdx = min(nIdx, _nCount - _nViewSize);
  3751. }
  3752. else
  3753. {
  3754. int idxNextPage = PageVarHeight(GetTopIndex(), TRUE);
  3755. if (nlines > idxNextPage - GetTopIndex())
  3756. nIdx = idxNextPage;
  3757. else
  3758. nIdx = max(0, nlines + GetTopIndex());
  3759. }
  3760. if (nIdx != GetTopIndex())
  3761. OnVScroll(MAKELONG(SB_SETINDEX, nIdx), 0);
  3762. }
  3763. _cWheelDelta %= WHEEL_DELTA;
  3764. }
  3765. return lReturn;
  3766. }
  3767. /*
  3768. * LRESULT CLstBxWinHost::OnLButtonUp(WPARAM, LPARAM, int)
  3769. *
  3770. * @mfunc
  3771. * Handles the WM_LBUTTONUP and WM_CAPTURECHANGED message
  3772. *
  3773. * @rdesc
  3774. * LRESULT = return value after message is processed
  3775. */
  3776. LRESULT CLstBxWinHost::OnLButtonUp(
  3777. WPARAM wparam,
  3778. LPARAM lparam,
  3779. int ff)
  3780. {
  3781. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnLButtonUp");
  3782. // if mouse wasn't down then exit out
  3783. if (!_fMouseDown)
  3784. return 0;
  3785. _fMouseDown = 0;
  3786. POINT pt;
  3787. POINTSTOPOINT(pt, lparam);
  3788. if (_fLstType == kCombo)
  3789. {
  3790. Assert(_fCapture);
  3791. // Check if user clicked outside the list box
  3792. // if so this signifies the user cancelled and we
  3793. // should send a message to the parentwindow
  3794. if (!PointInRect(&pt))
  3795. {
  3796. //User didn't click in listbox so reselect old item
  3797. LbSetSelection(_nOldCursor, _nOldCursor, LBSEL_DEFAULT, _nOldCursor, _nOldCursor);
  3798. ff = 0;
  3799. }
  3800. else
  3801. ff = LBN_SELCHANGE; //item changed so notify parent
  3802. _pcbHost->SetSelectionInfo(ff == LBN_SELCHANGE, GetCursor());
  3803. OnCBTracking(LBCBM_END, 0);
  3804. ::PostMessage(_hwndParent, LBCB_TRACKING, LBCBM_END, 0);
  3805. }
  3806. else
  3807. {
  3808. // Kill any initializations done by mouse down...
  3809. _fMouseDown = 0;
  3810. _nOldCursor = -1;
  3811. }
  3812. if (_fCapture)
  3813. {
  3814. TxKillTimer(ID_LB_CAPTURE);
  3815. _fCapture = 0;
  3816. TxSetCapture(FALSE);
  3817. }
  3818. if (ff)
  3819. {
  3820. #ifndef NOACCESSIBILITY
  3821. if (ff == LBN_SELCHANGE)
  3822. {
  3823. _dwWinEvent = EVENT_OBJECT_FOCUS;
  3824. _fNotifyWinEvt = TRUE;
  3825. TxNotify(_dwWinEvent, NULL);
  3826. if (!IsSelected(_nCursor))
  3827. {
  3828. _dwWinEvent = EVENT_OBJECT_SELECTIONREMOVE;
  3829. }
  3830. }
  3831. #endif
  3832. // Send notification if a notification exists
  3833. TxNotify(ff, NULL);
  3834. }
  3835. return 1;
  3836. }
  3837. /*
  3838. * LRESULT CLstBxWinHost::OnMouseMove(WPARAM, LPARAM)
  3839. *
  3840. * @mfunc
  3841. * Handles the WM_MOUSEMOVE message and possibly the
  3842. * WM_TIMER message for tracking mouse movements
  3843. *
  3844. * @rdesc
  3845. * LRESULT = return value after message is processed
  3846. */
  3847. LRESULT CLstBxWinHost::OnMouseMove(
  3848. WPARAM wparam,
  3849. LPARAM lparam)
  3850. {
  3851. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnMouseMove");
  3852. // bug fix #4998
  3853. // Check if previous mouse position is the same as current, if it is
  3854. // then this is probably a bogus message from PPT.
  3855. POINT pt;
  3856. POINTSTOPOINT(pt, lparam);
  3857. if (_nPrevMousePos == lparam && PtInRect(&_rcViewport, pt))
  3858. return 0;
  3859. _nPrevMousePos = lparam;
  3860. // This routine will only start the autoscrolling of the listbox
  3861. // The autoscrolling is done using a timer where the and the elapsed
  3862. // time is determined by how far the mouse is from the top and bottom
  3863. // of the listbox. The farther from the listbox the faster the timer
  3864. // will be. This function relies on the timer to scroll and select
  3865. // items.
  3866. // We get here if mouse cursor is in the list box.
  3867. int idx = GetItemFromPoint(&pt);
  3868. // We only do the following if mouse is down.
  3869. if (_fMouseDown)
  3870. {
  3871. int y = (short)pt.y;
  3872. if (y < 0 || y > _rcViewport.bottom - 1)
  3873. {
  3874. // calculate the new timer settings
  3875. int dist = y < 0 ? -y : (y - _rcViewport.bottom + 1);
  3876. int nTimer = ID_LB_CAPTURE_DEFAULT - (int)((WORD)dist << 4);
  3877. // Scroll up or down depending on the mouse pos relative
  3878. // to the list box
  3879. idx = (y <= 0) ? max(0, idx - 1) : min(_nCount - 1, idx + 1);
  3880. if (idx >= 0 && idx < _nCount)
  3881. {
  3882. // The ordering of this is VERY important to prevent screen
  3883. // flashing...
  3884. if (idx != _nCursor)
  3885. MouseMoveHelper(idx, (_fLstType == kCombo) ? FALSE : TRUE);
  3886. OnVScroll(MAKELONG((y < 0) ? SB_LINEUP : SB_LINEDOWN, 0), 0);
  3887. }
  3888. // reset timer
  3889. TxSetTimer(ID_LB_CAPTURE, (5 > nTimer) ? 5 : nTimer);
  3890. return 0;
  3891. }
  3892. // Don't select if we are part of a combo box and mouse is outside client area
  3893. else if (_fLstType == kCombo && (pt.x < 0 || pt.x > _rcViewport.right - 1))
  3894. return 0;
  3895. }
  3896. else if (!PointInRect(&pt))
  3897. return 0;
  3898. if (idx != _nCursor || (_fLstType == kCombo && idx >= 0 && !IsSelected(idx)))
  3899. {
  3900. // Prevent flashing by not redrawing if index
  3901. // didn't change
  3902. Assert(idx >= 0);
  3903. MouseMoveHelper(idx, TRUE);
  3904. }
  3905. return 0;
  3906. }
  3907. /*
  3908. * LRESULT CLstBxWinHost::OnLButtonDown(WPARAM, LPARAM)
  3909. *
  3910. * @mfunc
  3911. * Handles the WM_LBUTTONDOWN message
  3912. *
  3913. * @rdesc
  3914. * LRESULT = return value after message is processed
  3915. */
  3916. LRESULT CLstBxWinHost::OnLButtonDown(
  3917. WPARAM wparam,
  3918. LPARAM lparam)
  3919. {
  3920. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnLButtonDown");
  3921. POINT pt;
  3922. POINTSTOPOINT(pt, lparam);
  3923. if (_fCapture)
  3924. {
  3925. // Need to check if the listbox is part of a combobox, if so
  3926. // then we need to notify the parent class.
  3927. if (_fLstType == kCombo)
  3928. {
  3929. // Need to perform the following
  3930. // - check if click is within client area of combo box if not then
  3931. // behave as if user cancelled
  3932. if (!PointInRect(&pt))
  3933. {
  3934. // reset our double click flag because we could be double clicking on the scrollbar
  3935. _fDblClick = 0;
  3936. // check if the scroll bar was clicked
  3937. // mouse message won't get posted unless we release it
  3938. // for a short while
  3939. TxClientToScreen(&pt);
  3940. LRESULT lHit = SendMessage(_hwnd, WM_NCHITTEST, 0, MAKELONG(pt.x, pt.y));
  3941. // check if user clicked on the scrollbar
  3942. if (HTVSCROLL == lHit || HTHSCROLL == lHit)
  3943. {
  3944. if (_fCapture)
  3945. {
  3946. _fCapture = 0;
  3947. TxSetCapture(FALSE);
  3948. }
  3949. SendMessage(_hwnd, WM_NCLBUTTONDOWN, lHit, MAKELONG(pt.x, pt.y));
  3950. TxSetCapture(TRUE);
  3951. _fCapture = 1;
  3952. }
  3953. else if (HTGROWBOX != lHit)
  3954. {
  3955. // if user didn't click the scrollbar then notify parent and stop
  3956. // tracking else just get out
  3957. Assert(_pcbHost);
  3958. _pcbHost->SetSelectionInfo(FALSE, _nOldCursor);
  3959. LbSetSelection(_nOldCursor, _nOldCursor, LBSEL_RESET |
  3960. ((_nOldCursor == -1) ? 0 : LBSEL_NEWCURSOR | LBSEL_SELECT),
  3961. _nOldCursor, _nOldCursor);
  3962. OnCBTracking(LBCBM_END, 0);
  3963. SendMessage(_hwndParent, LBCB_TRACKING, 0, 0);
  3964. }
  3965. return 0;
  3966. }
  3967. }
  3968. }
  3969. int idx = GetItemFromPoint(&pt);
  3970. if (idx <= -1)
  3971. {
  3972. _fDblClick = 0;
  3973. return 0;
  3974. }
  3975. _fMouseDown = 1;
  3976. // if the message was a double click message than don't need to go
  3977. // any further just fake a mouseup message to get back to a normal
  3978. // state
  3979. if (_fDblClick)
  3980. {
  3981. _fDblClick = 0;
  3982. OnLButtonUp(wparam, lparam, LBN_DBLCLK);
  3983. return 0;
  3984. }
  3985. // Set the timer in case the user scrolls outside the listbox
  3986. if (!_fCapture)
  3987. {
  3988. TxSetCapture(TRUE);
  3989. _fCapture = 1;
  3990. TxSetTimer(ID_LB_CAPTURE, ID_LB_CAPTURE_DEFAULT);
  3991. }
  3992. int ffVirtKey = LBKEY_NONE;
  3993. if (_fLstType == kExtended)
  3994. {
  3995. if (HIWORD(GetKeyState(VK_SHIFT)))
  3996. ffVirtKey |= LBKEY_SHIFT;
  3997. if (HIWORD(GetKeyState(VK_CONTROL)))
  3998. ffVirtKey |= LBKEY_CONTROL;
  3999. }
  4000. int ff = 0;
  4001. int i = 0;
  4002. int nStart = idx;
  4003. int nEnd = idx;
  4004. int nAnchor = _nAnchor;
  4005. switch (ffVirtKey)
  4006. {
  4007. case LBKEY_NONE:
  4008. // This case accounts for listbox styles with kSingle, kMultiple, and
  4009. // kExtended w/ no keys pressed
  4010. if (_fLstType == kMultiple)
  4011. {
  4012. ff = (IsSelected(idx) ? 0 : LBSEL_SELECT) | LBSEL_NEWANCHOR | LBSEL_NEWCURSOR;
  4013. }
  4014. else
  4015. {
  4016. // keep a copy of the old cursor position around for combo cancells
  4017. ff = LBSEL_DEFAULT;
  4018. }
  4019. nAnchor = idx;
  4020. break;
  4021. case LBKEY_SHIFT:
  4022. // Now select all the items between the anchor and the current selection
  4023. // The problem is LbSetSelection expects the first index to be less then
  4024. // or equal to the second index so we have to manage the Anchor and index
  4025. // ourselves..
  4026. ff = LBSEL_SELECT | LBSEL_RESET | LBSEL_NEWCURSOR;
  4027. i = !(IsSelected(_nAnchor));
  4028. if (_nAnchor == -1)
  4029. {
  4030. ff |= LBSEL_NEWANCHOR;
  4031. nAnchor = idx;
  4032. }
  4033. else if (_nAnchor > idx)
  4034. {
  4035. nEnd = _nAnchor - i;
  4036. }
  4037. else if (_nAnchor < idx)
  4038. {
  4039. nEnd = _nAnchor + i;
  4040. }
  4041. else if (i) // _nAnchor == idx && idx IS selected
  4042. {
  4043. ff = LBSEL_RESET;
  4044. nStart = 0;
  4045. nEnd = 0;
  4046. }
  4047. break;
  4048. case LBKEY_CONTROL:
  4049. // Toggle the selected item and set the new anchor and cursor
  4050. // positions
  4051. ff = LBSEL_NEWCURSOR | LBSEL_NEWANCHOR | (IsSelected(idx) ? 0 : LBSEL_SELECT);
  4052. nAnchor = idx;
  4053. break;
  4054. case LBKEY_SHIFTCONTROL:
  4055. // De-select any items between the cursor and the anchor (excluding the anchor)
  4056. // and select or de-select the new items between the anchor and the cursor
  4057. // Set the anchor if it already isn't set
  4058. if (_nAnchor == -1)
  4059. _nAnchor = (_nOldCursor >= 0) ? _nOldCursor : idx;
  4060. // Just deselect all items between the cursor and the anchor
  4061. if (_nCursor != _nAnchor)
  4062. {
  4063. // remove selection from old cursor position to the current anchor position
  4064. LbSetSelection(_nCursor, (_nCursor > _nAnchor) ? _nAnchor + 1 : _nAnchor - 1, 0, 0, 0);
  4065. }
  4066. // Check if we used a temporary anchor if so then set the anchor to
  4067. // idx because we don't want the temporary anchor to be the actual anchor
  4068. if (_nOldCursor >= 0)
  4069. {
  4070. _nOldCursor = -1;
  4071. _nAnchor = idx;
  4072. }
  4073. // Set the state of all items between the new Cursor (idx) and
  4074. // the anchor to the state of the anchor
  4075. ff = LBSEL_NEWCURSOR | (IsSelected(_nAnchor) ? LBSEL_SELECT : 0);
  4076. nEnd = _nAnchor;
  4077. break;
  4078. default:
  4079. Assert(FALSE && "Should not be here!!");
  4080. }
  4081. if (LbSetSelection(nStart, nEnd, ff, idx, nAnchor))
  4082. {
  4083. #ifndef NOACCESSIBILITY
  4084. _dwWinEvent = EVENT_OBJECT_FOCUS;
  4085. _fNotifyWinEvt = TRUE;
  4086. TxNotify(_dwWinEvent, NULL);
  4087. #endif
  4088. }
  4089. return 0;
  4090. }
  4091. /////////////////////////// ComboBox Helper Functions //////////////////////////////
  4092. /*
  4093. * void CLstBxWinHost::OnCBTracking(WPARAM, LPARAM)
  4094. *
  4095. * @mfunc
  4096. * This should be only called by the combo box. This is a general message used
  4097. * to determine the state the listbox should be in
  4098. *
  4099. * @rdesc
  4100. * void
  4101. */
  4102. void CLstBxWinHost::OnCBTracking(
  4103. WPARAM wparam,
  4104. LPARAM lparam)
  4105. {
  4106. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnCBTracking");
  4107. Assert(_pcbHost);
  4108. Assert(_hwndParent);
  4109. switch (wparam)
  4110. {
  4111. // lparam = Set focus to listbox
  4112. case LBCBM_PREPARE:
  4113. Assert(IsWindowVisible(_hwnd));
  4114. _fMouseDown = FALSE;
  4115. if (lparam & LBCBM_PREPARE_SAVECURSOR)
  4116. _nOldCursor = GetCursor();
  4117. if (lparam & LBCBM_PREPARE_SETFOCUS)
  4118. {
  4119. _fFocus = 1;
  4120. TxSetFocus();
  4121. }
  4122. InitWheelDelta();
  4123. break;
  4124. // lparam = mouse is down
  4125. case LBCBM_START:
  4126. Assert(IsWindowVisible(_hwnd));
  4127. _fMouseDown = !!lparam;
  4128. TxSetCapture(TRUE);
  4129. _fCapture = 1;
  4130. break;
  4131. // lparam = Keep capture
  4132. case LBCBM_END:
  4133. TxKillTimer(ID_LB_CAPTURE);
  4134. _fFocus = 0;
  4135. if (_fCapture)
  4136. {
  4137. _fCapture = FALSE;
  4138. TxSetCapture(FALSE);
  4139. }
  4140. break;
  4141. default:
  4142. AssertSz(FALSE, "ALERT: Custom message being used by someone else");
  4143. }
  4144. }
  4145. /////////////////////////////// ListBox Functions //////////////////////////////////
  4146. /*
  4147. * void CLstBxWinHost::LbDeleteItemNotify(int, int)
  4148. *
  4149. * @mfunc
  4150. * Sends message to the parent an item has been deleted. This function should be
  4151. * called whenever the LB_DELETESTRING message is recieved or if the listbox is
  4152. * being destroyed and the listbox is owner draw
  4153. *
  4154. * @rdesc
  4155. * void
  4156. */
  4157. void CLstBxWinHost::LbDeleteItemNotify(
  4158. int nStart,
  4159. int nEnd)
  4160. {
  4161. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::LbDeleteItemNotify");
  4162. // Initialize structure
  4163. DELETEITEMSTRUCT ds;
  4164. ds.CtlType = ODT_LISTBOX;
  4165. ds.CtlID = _idCtrl;
  4166. ds.hwndItem = _hwnd;
  4167. for(int i = nStart; i <= nEnd; i++)
  4168. {
  4169. // We do this just in case the user decides to change
  4170. // the structure
  4171. ds.itemData = GetData(i);
  4172. ds.itemID = i;
  4173. SendMessage(_hwndParent, WM_DELETEITEM, _idCtrl, (LPARAM)&ds);
  4174. }
  4175. }
  4176. /*
  4177. * void CLstBxWinHost::LbDrawItemNotify(HDC, int, UINT, UINT)
  4178. *
  4179. * @mfunc
  4180. * This fills the draw item struct with some constant data for the given
  4181. * item. The caller will only have to modify a small part of this data
  4182. * for specific needs.
  4183. *
  4184. * @rdesc
  4185. * void
  4186. */
  4187. void CLstBxWinHost::LbDrawItemNotify(
  4188. HDC hdc,
  4189. int nIdx,
  4190. UINT itemAction,
  4191. UINT itemState)
  4192. {
  4193. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::LbDrawItemNotify");
  4194. // Only send the message if the item is viewable and no freeze is on
  4195. if (!IsItemViewable(nIdx) || !LbEnableDraw())
  4196. return;
  4197. //Fill the DRAWITEMSTRUCT with the unchanging constants
  4198. DRAWITEMSTRUCT dis;
  4199. dis.CtlType = ODT_LISTBOX;
  4200. dis.CtlID = _idCtrl;
  4201. // Use -1 if an invalid item number is being used. This is so that the app
  4202. // can detect if it should draw the caret (which indicates the lb has the
  4203. // focus) in an empty listbox
  4204. dis.itemID = (UINT)(nIdx < _nCount ? nIdx : -1);
  4205. dis.itemAction = itemAction;
  4206. dis.hwndItem = _hwnd;
  4207. dis.hDC = hdc;
  4208. dis.itemState = itemState |
  4209. (UINT)(_fDisabled ? ODS_DISABLED : 0);
  4210. // Set the app supplied data
  4211. if (_nCount == 0)
  4212. {
  4213. // If no items, just use 0 for data. This is so that we
  4214. // can display a caret when there are no items in the listbox.
  4215. dis.itemData = 0L;
  4216. }
  4217. else
  4218. {
  4219. Assert(nIdx < _nCount);
  4220. dis.itemData = GetData(nIdx);
  4221. }
  4222. LbGetItemRect(nIdx, &(dis.rcItem));
  4223. /*
  4224. * Set the window origin to the horizontal scroll position. This is so that
  4225. * text can always be drawn at 0,0 and the view region will only start at
  4226. * the horizontal scroll offset. We pass this as wparam
  4227. */
  4228. SendMessage(_hwndParent, WM_DRAWITEM, _idCtrl, (LPARAM)&dis);
  4229. }
  4230. /*
  4231. * LRESULT CLstBxWinHost::OnSetEditStyle(WPARAM, LPARAM)
  4232. *
  4233. * @mfunc
  4234. * Check if we need to do custom look for ListBox
  4235. *
  4236. * @rdesc
  4237. * return value same as EM_GETEDITSTYLE
  4238. */
  4239. LRESULT CLstBxWinHost::OnSetEditStyle(
  4240. WPARAM wparam,
  4241. LPARAM lparam)
  4242. {
  4243. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::OnSetEditStyle");
  4244. LRESULT lres;
  4245. BOOL fCustomLookBefore = ((CTxtEdit *) _pserv)->_fCustomLook;
  4246. BOOL fCustomLook;
  4247. HRESULT hr;
  4248. hr = _pserv->TxSendMessage(EM_SETEDITSTYLE, wparam, lparam, &lres);
  4249. fCustomLook = ((CTxtEdit *) _pserv)->_fCustomLook;
  4250. if (fCustomLook != fCustomLookBefore)
  4251. {
  4252. DWORD dwStyle = GetWindowLong(_hwnd, GWL_STYLE);
  4253. DWORD dwExStyle = GetWindowLong(_hwnd, GWL_EXSTYLE);
  4254. if (fCustomLook)
  4255. {
  4256. dwStyle |= WS_BORDER;
  4257. dwExStyle &= ~WS_EX_CLIENTEDGE;
  4258. }
  4259. else
  4260. {
  4261. dwStyle &= ~WS_BORDER;
  4262. dwExStyle |= WS_EX_CLIENTEDGE;
  4263. }
  4264. SetWindowLong(_hwnd, GWL_STYLE, dwStyle);
  4265. SetWindowLong(_hwnd, GWL_EXSTYLE, dwExStyle);
  4266. OnSunkenWindowPosChanging(_hwnd, NULL);
  4267. }
  4268. return lres;
  4269. }
  4270. /*
  4271. * LONG CLstBxWinHost::IsCustomLook()
  4272. *
  4273. * @mfunc
  4274. * return custom look setting for ListBox
  4275. *
  4276. * @rdesc
  4277. * return custom look setting for ListBox
  4278. */
  4279. BOOL CLstBxWinHost::IsCustomLook()
  4280. {
  4281. return ((CTxtEdit *) _pserv)->_fCustomLook;
  4282. }
  4283. /*
  4284. * BOOL CLstBxWinHost::LbSetItemHeight(WPARAM, LPARAM)
  4285. *
  4286. * @mfunc
  4287. * Sets the height of the items within the given range [0, _nCount -1]
  4288. *
  4289. * @rdesc
  4290. * BOOL = Successful ? TRUE : FALSE
  4291. */
  4292. BOOL CLstBxWinHost::LbSetItemHeight(
  4293. WPARAM wparam,
  4294. LPARAM lparam)
  4295. {
  4296. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::LbSetItemHeight");
  4297. int nHeight = (int)lparam;
  4298. BOOL retCode = FALSE;
  4299. // Set the height of the items if there are between [1,255] : bug fix #4783
  4300. if (nHeight < 256 && nHeight > 0)
  4301. {
  4302. if (_fOwnerDrawVar)
  4303. {
  4304. if ((unsigned)GetCount() > wparam)
  4305. retCode = SetVarItemHeight(wparam, nHeight);
  4306. }
  4307. else if (SetItemsHeight(nHeight, FALSE))
  4308. {
  4309. //bug fix #4214
  4310. //need to recalculate how many items are viewable, IN ITS ENTIRETY,
  4311. //using the current window size
  4312. RECT rc;
  4313. TxGetClientRect(&rc);
  4314. _nViewSize = max(rc.bottom / max(_nyItem, 1), 1);
  4315. retCode = TRUE;
  4316. }
  4317. if (retCode)
  4318. {
  4319. _fSetScroll = 0;
  4320. if (_fSetRedraw)
  4321. {
  4322. if (_dwStyle & WS_VSCROLL)
  4323. SetScrollInfo(SB_VERT, TRUE);
  4324. }
  4325. else
  4326. _fSetScroll = 1;
  4327. }
  4328. }
  4329. return retCode;
  4330. }
  4331. /*
  4332. * BOOL CLstBxWinHost::LbGetItemRect(int, RECT*)
  4333. *
  4334. * @mfunc
  4335. * Returns the rectangle coordinates of a requested index
  4336. * The coordinates will be in client coordinates
  4337. *
  4338. * @rdesc
  4339. * BOOL = Successful ? TRUE : FALSE
  4340. */
  4341. BOOL CLstBxWinHost::LbGetItemRect(
  4342. int idx,
  4343. RECT* prc)
  4344. {
  4345. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::LbGetItemRect");
  4346. Assert(prc);
  4347. Assert(idx >= -1);
  4348. #ifdef _DEBUG
  4349. if (_nCount > 0)
  4350. Assert(idx < _nCount);
  4351. else
  4352. Assert(idx == _nCount);
  4353. #endif //_DEBUG
  4354. if (idx == -1)
  4355. idx = 0;
  4356. TxGetClientRect(prc);
  4357. prc->left = 0;
  4358. if (_fOwnerDrawVar)
  4359. {
  4360. LONG lTop = _rcViewport.top;
  4361. if (idx > _nCount)
  4362. idx = _nCount - 1;
  4363. else if (idx < 0)
  4364. idx = 0;
  4365. if (idx >= GetTopIndex())
  4366. {
  4367. for (int i = GetTopIndex(); i < idx; i++)
  4368. lTop += _rgData[i]._uHeight;
  4369. }
  4370. else
  4371. {
  4372. for (int i = idx; i < GetTopIndex(); i++)
  4373. lTop -= _rgData[i]._uHeight;
  4374. }
  4375. prc->top = lTop;
  4376. prc->bottom = lTop + _rgData[idx]._uHeight;
  4377. }
  4378. else
  4379. {
  4380. prc->top = (idx - GetTopIndex()) * _nyItem + _rcViewport.top;
  4381. prc->bottom = prc->top + _nyItem;
  4382. }
  4383. return TRUE;
  4384. }
  4385. /*
  4386. * BOOL CLstBxWinHost::LbSetItemData(long, long, LPARAM)
  4387. *
  4388. * @mfunc
  4389. * Given a range [nStart,nEnd] the data for these items
  4390. * will be set to nValue
  4391. * @rdesc
  4392. * void
  4393. */
  4394. void CLstBxWinHost::LbSetItemData(
  4395. long nStart,
  4396. long nEnd,
  4397. LPARAM nValue)
  4398. {
  4399. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::LbSetItemData");
  4400. Assert(nStart >= 0 && nStart < _nCount);
  4401. Assert(nEnd >= 0 && nEnd < _nCount);
  4402. Assert(nStart <= nEnd);
  4403. int nMin = min(nEnd + 1, _nCount);
  4404. for (int i = nStart; i < nMin; i++)
  4405. _rgData[i]._lparamData = nValue;
  4406. }
  4407. /*
  4408. * long CLstBxWinHost::LbDeleteString(long, long)
  4409. *
  4410. * @mfunc
  4411. * Delete the string at the requested range.
  4412. * @rdesc
  4413. * long = # of items in the list box. If failed -1
  4414. */
  4415. long CLstBxWinHost::LbDeleteString(
  4416. long nStart,
  4417. long nEnd)
  4418. {
  4419. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::LbDeleteString");
  4420. if ((nStart > nEnd) || (nStart < 0) || (nEnd >= _nCount))
  4421. return -1;
  4422. if (!RemoveString(nStart, nEnd))
  4423. return -1;
  4424. // set the top index to fill the window
  4425. LbSetTopIndex(max(nStart -1, 0));
  4426. #ifndef NOACCESSIBILITY
  4427. _dwWinEvent = EVENT_OBJECT_DESTROY;
  4428. _fNotifyWinEvt = TRUE;
  4429. TxNotify(_dwWinEvent, NULL);
  4430. #endif
  4431. return _nCount;
  4432. }
  4433. /*
  4434. * CLstBxWinHost::LbInsertString(long, LPCTSTR)
  4435. *
  4436. * @mfunc
  4437. * Insert the string at the requested index. If long >= 0 then the
  4438. * string insertion is at the requested index. If long == -2 insertion
  4439. * is at the position which the string would be alphabetically in order.
  4440. * If long == -1 then string is added to the bottom of the list
  4441. *
  4442. * @rdesc
  4443. * long = If inserted, the index (paragraph) which the string
  4444. * was inserted. If not inserted returns -1;
  4445. */
  4446. long CLstBxWinHost::LbInsertString(
  4447. long nIdx,
  4448. LPCTSTR szText)
  4449. {
  4450. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::LbInsertString");
  4451. Assert(nIdx >= -2);
  4452. Assert(szText);
  4453. if (nIdx == -2)
  4454. {
  4455. if (_nCount > 0)
  4456. nIdx = GetSortedPosition(szText, 0, _nCount - 1);
  4457. else
  4458. nIdx = 0; //nothing inside listbox
  4459. }
  4460. else if (nIdx == -1)
  4461. nIdx = GetCount(); // Insert string to the bottom of list if -1
  4462. if (InsertString(nIdx, szText))
  4463. {
  4464. // If the index was previously selected unselect the newly
  4465. // added item
  4466. for (int i = _nCount - 1; i > nIdx; i--)
  4467. {
  4468. _rgData[i]._fSelected = _rgData.Get(i - 1)._fSelected;
  4469. // bug fix #4916
  4470. _rgData[i]._lparamData = _rgData.Get(i - 1)._lparamData;
  4471. _rgData[i]._uHeight = _rgData.Get(i - 1)._uHeight;
  4472. }
  4473. _rgData[nIdx]._fSelected = 0;
  4474. _rgData[nIdx]._uHeight = 0;
  4475. _rgData[nIdx]._lparamData = 0; // Need to Initialize data back to zero
  4476. if (!_fOwnerDraw)
  4477. {
  4478. // if we inserted at the middle or top then check 1 index down to see if the item
  4479. // was selected, if we inserted at the bottom then check 1 index up to see if the item
  4480. // was selected. If the item was selected we need to change the colors to default
  4481. // because we inherit the color properties from the range which we inserted into
  4482. if (_nCount > 1)
  4483. {
  4484. if (((nIdx < _nCount - 1) && _rgData.Get(nIdx + 1)._fSelected) ||
  4485. (nIdx == (_nCount - 1) && _rgData.Get(nIdx - 1)._fSelected))
  4486. SetColors((unsigned)tomAutoColor, (unsigned)tomAutoColor, nIdx, nIdx);
  4487. }
  4488. }
  4489. else
  4490. {
  4491. if (_fOwnerDrawVar)
  4492. {
  4493. // Get item height for OwnerDrawFix listbox
  4494. MEASUREITEMSTRUCT measureItem;
  4495. measureItem.CtlType = ODT_LISTBOX;
  4496. measureItem.CtlID = _idCtrl;
  4497. measureItem.itemHeight = _nyFont;
  4498. measureItem.itemWidth = 0;
  4499. measureItem.itemData = (ULONG_PTR)szText;
  4500. measureItem.itemID = nIdx;
  4501. SendMessage(_hwndParent, WM_MEASUREITEM, _idCtrl, (LPARAM)&measureItem);
  4502. LbSetItemHeight(nIdx, measureItem.itemHeight);
  4503. }
  4504. // Force redraw of items if owner draw and new item is viewable
  4505. if (IsItemViewable(nIdx))
  4506. {
  4507. RECT rc;
  4508. LbGetItemRect(nIdx, &rc);
  4509. rc.bottom = _rcViewport.bottom;
  4510. InvalidateRect(_hwnd, &rc, FALSE);
  4511. }
  4512. }
  4513. #ifndef NOACCESSIBILITY
  4514. _dwWinEvent = EVENT_OBJECT_CREATE;
  4515. _fNotifyWinEvt = TRUE;
  4516. _nAccessibleIdx = nIdx + 1;
  4517. TxNotify(_dwWinEvent, NULL);
  4518. #endif
  4519. return nIdx;
  4520. }
  4521. else
  4522. {
  4523. TxNotify((unsigned long)LBN_ERRSPACE, NULL);
  4524. return -1;
  4525. }
  4526. }
  4527. /*
  4528. * CLstBxWinHost::LbFindString(long, LPCTSTR, BOOL)
  4529. *
  4530. * @mfunc
  4531. * Searches the story for a given string. The
  4532. * starting position will be determined by the index nStart.
  4533. * This routine expects the units to be in tomParagraph.
  4534. * If bExact is TRUE then the paragraph must match the BSTR.
  4535. *
  4536. * @rdesc
  4537. * long = If found, the index (paragraph) which the string
  4538. * was found in. If not found returns -1;
  4539. */
  4540. long CLstBxWinHost::LbFindString(
  4541. long nStart,
  4542. LPCTSTR szSearch,
  4543. BOOL bExact)
  4544. {
  4545. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::LbFindString");
  4546. Assert(szSearch);
  4547. Assert(nStart <= _nCount);
  4548. int nSize = wcslen(szSearch);
  4549. // If string is empty and not finding exact match then just return -1 like
  4550. // the system control. We don't have to worry about the exact match case
  4551. // because it will work properly
  4552. if (nStart >= _nCount || (nSize == 0 && !bExact))
  4553. return -1;
  4554. // allocate string buffer into stack
  4555. WCHAR sz[1024];
  4556. WCHAR *psz = sz;
  4557. if ((nSize + 3) > 1024)
  4558. psz = new WCHAR[nSize + 3 /* 2 paragraphs and a NULL*/];
  4559. Assert(psz);
  4560. if (psz == NULL)
  4561. {
  4562. TxNotify((unsigned long)LBN_ERRSPACE, NULL);
  4563. return FALSE;
  4564. }
  4565. // format the string the way we need it
  4566. wcscpy(psz, szCR);
  4567. wcscat(psz, szSearch);
  4568. if (bExact)
  4569. wcscat(psz, szCR);
  4570. long lRet = -1;
  4571. long l, cp;
  4572. ITextRange *pRange = NULL;
  4573. BSTR bstrQuery = SysAllocString(psz);
  4574. if(!bstrQuery)
  4575. goto CleanExit;
  4576. if (psz != sz)
  4577. delete [] psz;
  4578. // Set starting position for the search
  4579. if (!GetRange(nStart, _nCount - 1, &pRange))
  4580. {
  4581. SysFreeString(bstrQuery);
  4582. return lRet;
  4583. }
  4584. CHECKNOERROR(pRange->GetStart(&cp));
  4585. if (cp > 0)
  4586. {
  4587. // We need to use the paragraph marker from the previous
  4588. // paragraph when searching for a string
  4589. CHECKNOERROR(pRange->SetStart(--cp));
  4590. }
  4591. else
  4592. {
  4593. // Special case:
  4594. // Check if the first item matchs
  4595. if (FindString(0, szSearch, bExact))
  4596. {
  4597. lRet = 0;
  4598. goto CleanExit;
  4599. }
  4600. }
  4601. if (NOERROR != pRange->FindTextStart(bstrQuery, 0, FR_MATCHALEFHAMZA | FR_MATCHKASHIDA | FR_MATCHDIAC, &l))
  4602. {
  4603. // Didn't find the string...
  4604. if (nStart > 0)
  4605. {
  4606. if (!FindString(0, szSearch, bExact))
  4607. {
  4608. // Start the search from top of list to the point where
  4609. // we last started the search
  4610. CHECKNOERROR(pRange->SetRange(0, ++cp));
  4611. CHECKNOERROR(pRange->FindTextStart(bstrQuery, 0, 0, &l));
  4612. }
  4613. else
  4614. {
  4615. // First item was a match
  4616. lRet = 0;
  4617. goto CleanExit;
  4618. }
  4619. }
  4620. else
  4621. goto CleanExit;
  4622. }
  4623. // If we got down here then we have a match.
  4624. // Get the index and convert to listbox index
  4625. CHECKNOERROR(pRange->MoveStart(tomCharacter, 1, &l));
  4626. CHECKNOERROR(pRange->GetIndex(tomParagraph, &lRet));
  4627. lRet--; // index is 1 based so we need to changed it to zero based
  4628. CleanExit:
  4629. if (lRet != -1 && nSize == 1 && *szSearch == CR && _fOwnerDraw)
  4630. {
  4631. // Special case
  4632. if (GetString(lRet, sz) != 1 || sz[0] != *szSearch)
  4633. lRet = -1;
  4634. }
  4635. if (bstrQuery)
  4636. SysFreeString(bstrQuery);
  4637. if (pRange)
  4638. pRange->Release();
  4639. return lRet;
  4640. }
  4641. /*
  4642. * CLstBxWinHost::LbShowIndex(int, BOOL)
  4643. *
  4644. * @mfunc
  4645. * Makes sure the requested index is within the viewable space.
  4646. * In cases where the item is not in the viewable space bTop is
  4647. * used to determine the requested item should be at the top
  4648. * of the list else list box will scrolled enough to display the
  4649. * item.
  4650. * NOTE:
  4651. * There can be situations where bTop will fail. These
  4652. * situations occurr of the top index requested prevents the list
  4653. * box from being completely filled with items. For more info
  4654. * read the comments for LBSetTopIndex.
  4655. *
  4656. * @rdesc
  4657. * BOOL = Successfully displays the item ? TRUE : FALSE
  4658. */
  4659. BOOL CLstBxWinHost::LbShowIndex(
  4660. long nIdx,
  4661. BOOL bTop)
  4662. {
  4663. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::LbShowIndex");
  4664. // Make sure the requested item is within valid bounds
  4665. if (!(nIdx >= 0 && nIdx < _nCount))
  4666. return FALSE;
  4667. if (_fOwnerDrawVar)
  4668. {
  4669. if (nIdx >= GetTopIndex())
  4670. {
  4671. BOOL fGreaterThanView;
  4672. SumVarHeight(GetTopIndex(), nIdx+1, &fGreaterThanView);
  4673. if (!fGreaterThanView)
  4674. return TRUE; // Already visible
  4675. }
  4676. if (!bTop)
  4677. nIdx = PageVarHeight(nIdx, FALSE);
  4678. }
  4679. else
  4680. {
  4681. int delta = nIdx - GetTopIndex();
  4682. // If item is already visible then just return TRUE
  4683. if (0 <= delta && delta < _nViewSize)
  4684. return TRUE;
  4685. if ((delta) >= _nViewSize && !bTop && _nViewSize)
  4686. nIdx = nIdx - _nViewSize + 1;
  4687. }
  4688. return (LbSetTopIndex(nIdx) < 0) ? FALSE : TRUE;
  4689. }
  4690. /*
  4691. * CLstBxWinHost::LbSetTopIndex(long)
  4692. *
  4693. * @mfunc
  4694. * Tries to make the requested item the top index in the list box.
  4695. * If making the requested item the top index prevents the list box
  4696. * from using the viewable region to its fullest then and alternative
  4697. * top index will be used which will display the requested index
  4698. * but NOT as the top index. This ensures conformancy with the system
  4699. * list box and makes full use of the dislayable region.
  4700. *
  4701. * @rdesc
  4702. * long = returns the new top index if successful. If failed returns -1
  4703. */
  4704. long CLstBxWinHost::LbSetTopIndex(
  4705. long nIdx)
  4706. {
  4707. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::LbSetTopIndex");
  4708. // Make sure the requested item is within valid bounds
  4709. if (nIdx < 0 || nIdx >= _nCount)
  4710. return -1;
  4711. // Always try to display a full list of items in the list box
  4712. // This may mean we have to adjust the requested top index if
  4713. // the requested top index will leave blanks at the end of the
  4714. // viewable space
  4715. if (_fOwnerDrawVar)
  4716. {
  4717. // Get top index for the last page
  4718. int iLastPageTopIdx = PageVarHeight(_nCount, FALSE);
  4719. if (iLastPageTopIdx < nIdx)
  4720. nIdx = iLastPageTopIdx;
  4721. }
  4722. else if (_nCount - _nViewSize < nIdx)
  4723. nIdx = max(0, _nCount - _nViewSize);
  4724. // Just check to make sure we not already at the top
  4725. if (GetTopIndex() == nIdx)
  4726. return nIdx;
  4727. if (!SetTopViewableItem(nIdx))
  4728. nIdx = -1;
  4729. return nIdx;
  4730. }
  4731. /*
  4732. * CLstBxWinHost::LbBatchInsert(WCHAR* psz)
  4733. *
  4734. * @mfunc
  4735. * Inserts the given list of items into listbox. The listbox is reset prior to adding
  4736. * the items into the listbox
  4737. *
  4738. * @rdesc
  4739. * int = # of items in the listbox if successful else LB_ERR
  4740. */
  4741. int CLstBxWinHost::LbBatchInsert(
  4742. WCHAR* psz)
  4743. {
  4744. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::LbBatchInsert");
  4745. // make sure we get some sort of string
  4746. if (!psz)
  4747. return LB_ERR;
  4748. WCHAR* pszOut = psz;
  4749. LRESULT nRet = LB_ERR;
  4750. BSTR bstr = NULL;
  4751. ITextRange* pRange = NULL;
  4752. int nCount = 0;
  4753. if (_fSort)
  4754. {
  4755. pszOut = new WCHAR[wcslen(psz) + 1];
  4756. Assert(pszOut);
  4757. if (!pszOut)
  4758. {
  4759. TxNotify((unsigned long)LBN_ERRSPACE, NULL);
  4760. return LB_ERR;
  4761. }
  4762. nCount = SortInsertList(pszOut, psz);
  4763. if (nCount == LB_ERR)
  4764. goto CleanExit;
  4765. }
  4766. else
  4767. {
  4768. //bug fix #5130 we need to know how much we are going to insert
  4769. //prior to inserting because we may be getting showscrollbar message
  4770. //during insertion
  4771. WCHAR* pszTemp = psz;
  4772. while(*pszTemp)
  4773. {
  4774. if (*pszTemp == L'\r')
  4775. nCount++;
  4776. pszTemp++;
  4777. }
  4778. nCount++;
  4779. }
  4780. //clear listbox and insert new list into listbox
  4781. LbDeleteString(0, GetCount() - 1);
  4782. bstr = SysAllocString(pszOut);
  4783. if(!bstr)
  4784. goto CleanExit;
  4785. // Insert string into list
  4786. CHECKNOERROR(((CTxtEdit*)_pserv)->Range(0, 0, &pRange));
  4787. //bug fix #5130
  4788. // preset our _nCount for scrollbar purposes
  4789. _nCount = nCount;
  4790. CHECKNOERROR(pRange->SetText(bstr));
  4791. nRet = nCount;
  4792. CleanExit:
  4793. if (pszOut != psz)
  4794. delete [] pszOut;
  4795. if (bstr)
  4796. SysFreeString(bstr);
  4797. if (pRange)
  4798. pRange->Release();
  4799. return nRet;
  4800. }
  4801. /*
  4802. * CLstBxWinHost::LbSetSelection(long, long, int, long, long)
  4803. *
  4804. * @mfunc
  4805. * Given the range of nStart to nEnd set the selection state of each item
  4806. * This function will also update the anchor and cursor position
  4807. * if requested.
  4808. *
  4809. * @rdesc
  4810. * BOOL = If everything went fine ? TRUE : FALSE
  4811. */
  4812. BOOL CLstBxWinHost::LbSetSelection(
  4813. long nStart,
  4814. long nEnd,
  4815. int ffFlags,
  4816. long nCursor,
  4817. long nAnchor)
  4818. {
  4819. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::LbSetSelection");
  4820. if (!_fOwnerDraw)
  4821. {
  4822. Freeze();
  4823. // de-select all items
  4824. if ((ffFlags & LBSEL_RESET))
  4825. {
  4826. if (!ResetContent())
  4827. {
  4828. Unfreeze();
  4829. return FALSE;
  4830. }
  4831. // Reset, check if anything else needs to be done
  4832. // else just exit out
  4833. if (ffFlags == LBSEL_RESET)
  4834. {
  4835. Unfreeze();
  4836. return TRUE;
  4837. }
  4838. }
  4839. }
  4840. // NOTE:
  4841. // This should be one big critical section because we rely on certain
  4842. // member variables not changing during the process of this function
  4843. // Check if we are changing the selection and if we have focus
  4844. // if we do then we first need to xor out the focus rect from
  4845. // old cursor
  4846. RECT rc;
  4847. HDC hdc;
  4848. hdc = TxGetDC();
  4849. Assert(hdc);
  4850. // don't draw outside the client rect draw the rectangle
  4851. TxGetClientRect(&rc);
  4852. IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
  4853. if ((ffFlags & LBSEL_NEWCURSOR) && _fFocus && LbEnableDraw())
  4854. {
  4855. // If owner draw notify parentwindow
  4856. if (_fOwnerDraw)
  4857. LbDrawItemNotify(hdc, max(_nCursor, 0), ODA_FOCUS, IsSelected(_nCursor) ? ODS_SELECTED : 0);
  4858. else
  4859. {
  4860. LbGetItemRect(_nCursor, &rc);
  4861. ::DrawFocusRect(hdc, &rc);
  4862. }
  4863. }
  4864. // check if all item should be selected
  4865. if (nStart == -1 && nEnd == 0)
  4866. {
  4867. nStart = 0;
  4868. nEnd = _nCount - 1;
  4869. }
  4870. else if (nStart > nEnd)
  4871. {
  4872. // reshuffle so nStart is <= nEnd;
  4873. long temp = nEnd;
  4874. nEnd = nStart;
  4875. nStart = temp;
  4876. }
  4877. // Check for invalid values
  4878. if (nStart < -1 || nEnd >= _nCount)
  4879. {
  4880. if (!_fOwnerDraw)
  4881. Unfreeze();
  4882. // mimic system listbox behaviour
  4883. if (nEnd >= _nCount)
  4884. return FALSE;
  4885. else
  4886. return TRUE;
  4887. }
  4888. // Prepare the state we want to be in
  4889. unsigned int bState;
  4890. DWORD dwFore;
  4891. DWORD dwBack;
  4892. if (ffFlags & LBSEL_SELECT)
  4893. {
  4894. bState = ODS_SELECTED; //NOTE ODS_SELECTED must equal 1
  4895. dwFore = _crSelFore;
  4896. dwBack = _crSelBack;
  4897. if (_fSingleSel)
  4898. nEnd = nStart;
  4899. }
  4900. else
  4901. {
  4902. bState = 0;
  4903. dwFore = (unsigned)tomAutoColor;
  4904. dwBack = (unsigned)tomAutoColor;
  4905. }
  4906. // A little optimization check
  4907. // Checks to see if the state is really being changed if not then don't bother
  4908. // calling SetColor, works only when nStart == nEnd;
  4909. // The list box will not change the background color if nSame is true
  4910. int nSame = (nStart == nEnd && nStart != -1) ? (_rgData.Get(nStart)._fSelected == bState) : FALSE;
  4911. BOOL bRet = TRUE;
  4912. if (_fOwnerDraw)
  4913. {
  4914. if (ffFlags & LBSEL_RESET || !bState)
  4915. {
  4916. // There are cases where we don't necessarily reset all the items
  4917. // in the list but rather the range which was given. The following
  4918. // takes care of this case
  4919. int ff = ffFlags & LBSEL_RESET;
  4920. int i = (ff) ? 0 : nStart;
  4921. int nStop = (ff) ? _nCount : nEnd + 1;
  4922. for (; i < nStop; i++)
  4923. {
  4924. // Don't unselect an item which is going to be
  4925. // selected in the next for loop
  4926. if (!bState || (i < nStart || i > nEnd) &&
  4927. (_rgData.Get(i)._fSelected != 0))
  4928. {
  4929. // Only send a unselect message if the item
  4930. // is viewable
  4931. _rgData[i]._fSelected = 0;
  4932. if (IsItemViewable(i))
  4933. LbDrawItemNotify(hdc, i, ODA_SELECT, 0);
  4934. }
  4935. }
  4936. }
  4937. if (bState)
  4938. {
  4939. // We need to loop through and notify the parent
  4940. // The item has been deselected or selected
  4941. for (int i = max(0, nStart); i <= nEnd; i++)
  4942. {
  4943. if (_rgData.Get(i)._fSelected != 1)
  4944. {
  4945. _rgData[i]._fSelected = 1;
  4946. if (IsItemViewable(i))
  4947. LbDrawItemNotify(hdc, i, ODA_SELECT, ODS_SELECTED);
  4948. }
  4949. }
  4950. }
  4951. }
  4952. else if (!nSame)
  4953. {
  4954. // Update our internal records
  4955. for (int i = max(0, nStart); i <= nEnd; i++)
  4956. _rgData[i]._fSelected = bState;
  4957. bRet = SetColors(dwFore, dwBack, nStart, nEnd);
  4958. }
  4959. // Update the cursor and anchor positions
  4960. if (ffFlags & LBSEL_NEWANCHOR)
  4961. _nAnchor = nAnchor;
  4962. // Update the cursor position
  4963. if (ffFlags & LBSEL_NEWCURSOR)
  4964. _nCursor = nCursor;
  4965. // Draw the focus rect
  4966. if (_fFocus && LbEnableDraw())
  4967. {
  4968. if (_fOwnerDraw)
  4969. LbDrawItemNotify(hdc, _nCursor, ODA_FOCUS, ODS_FOCUS |
  4970. (IsSelected(_nCursor) ? ODS_SELECTED : 0));
  4971. else
  4972. {
  4973. LbGetItemRect(_nCursor, &rc);
  4974. ::DrawFocusRect(hdc, &rc);
  4975. }
  4976. }
  4977. TxReleaseDC(hdc);
  4978. // This will automatically update the window
  4979. if (!_fOwnerDraw)
  4980. {
  4981. Unfreeze();
  4982. // We need to do this because we are making so many changes
  4983. // ITextServices might get confused
  4984. ScrollToView(GetTopIndex());
  4985. }
  4986. return bRet;
  4987. }
  4988. /*
  4989. * BOOL CLstBxWinHost::IsItemViewable(int)
  4990. *
  4991. * @mfunc
  4992. * Helper to check if the idx is in current view
  4993. *
  4994. * @rdesc
  4995. * TRUE if it is viewable
  4996. */
  4997. BOOL CLstBxWinHost::IsItemViewable(
  4998. long idx)
  4999. {
  5000. if (idx < GetTopIndex())
  5001. return FALSE;
  5002. if (!_fOwnerDrawVar)
  5003. return ((idx - GetTopIndex()) * _nyItem < _rcViewport.bottom);
  5004. BOOL fGreateThanView;
  5005. SumVarHeight(GetTopIndex(), idx, &fGreateThanView);
  5006. return !fGreateThanView;
  5007. }
  5008. /*
  5009. * CLstBxWinHost::SumVarHeight(int, int, BOOL*)
  5010. *
  5011. * @mfunc
  5012. * Helper to sum the height from iStart to iEnd for a variable item
  5013. * height listbox. If pfGreaterThanView is not NULL, then set it to TRUE
  5014. * once the total height is bigger than current view size.
  5015. *
  5016. * @rdesc
  5017. * int = hights from iStart to iEnd
  5018. */
  5019. int CLstBxWinHost::SumVarHeight(
  5020. int iStart,
  5021. int iEnd,
  5022. BOOL *pfGreaterThanView)
  5023. {
  5024. RECT rc = {0};
  5025. int uHeightSum = 0;
  5026. Assert(_fOwnerDrawVar);
  5027. if (pfGreaterThanView)
  5028. {
  5029. *pfGreaterThanView = FALSE;
  5030. TxGetClientRect(&rc);
  5031. }
  5032. if (GetCount() <= 0)
  5033. return 0;
  5034. if (iStart < 0)
  5035. iStart = 0;
  5036. if (iEnd >= GetCount())
  5037. iEnd = GetCount();
  5038. Assert(iEnd >= iStart);
  5039. for (int nIdx = iStart; nIdx < iEnd; nIdx++)
  5040. {
  5041. uHeightSum += _rgData[nIdx]._uHeight;
  5042. if (pfGreaterThanView && uHeightSum > rc.bottom)
  5043. {
  5044. *pfGreaterThanView = TRUE;
  5045. break;
  5046. }
  5047. }
  5048. return uHeightSum;
  5049. }
  5050. /*
  5051. * int CLstBxWinHost::PageVarHeight(int, BOOL)
  5052. *
  5053. * @mfunc
  5054. * For variable height ownerdraw listboxes, calaculates the new iTop we must
  5055. * move to when paging (page up/down) through variable height listboxes.
  5056. *
  5057. * @rdesc
  5058. * int = new iTop
  5059. */
  5060. int CLstBxWinHost::PageVarHeight(
  5061. int startItem,
  5062. BOOL fPageForwardDirection)
  5063. {
  5064. int i;
  5065. int iHeight;
  5066. RECT rc;
  5067. Assert(_fOwnerDrawVar);
  5068. if (GetCount() <= 1)
  5069. return 0;
  5070. TxGetClientRect(&rc);
  5071. iHeight = rc.bottom;
  5072. i = startItem;
  5073. if (fPageForwardDirection)
  5074. {
  5075. while ((iHeight >= 0) && (i < GetCount()))
  5076. iHeight -= _rgData[i++]._uHeight;
  5077. return ((iHeight >= 0) ? GetCount() - 1 : max(i - 2, startItem + 1));
  5078. }
  5079. else
  5080. {
  5081. while ((iHeight >= 0) && (i >= 0))
  5082. iHeight -= _rgData[i--]._uHeight;
  5083. return ((iHeight >= 0) ? 0 : min(i + 2, startItem - 1));
  5084. }
  5085. }
  5086. /*
  5087. * BOOL CLstBxWinHost::SetVarItemHeight(int, int)
  5088. *
  5089. * @mfunc
  5090. * For variable height ownerdraw listboxes, setup the para height.
  5091. *
  5092. * @rdesc
  5093. * TRUE if setup new height
  5094. */
  5095. BOOL CLstBxWinHost::SetVarItemHeight(
  5096. int idx,
  5097. int iHeight)
  5098. {
  5099. BOOL retCode = FALSE;
  5100. ITextPara *pPara = NULL;
  5101. ITextRange *pRange = NULL;
  5102. Assert(_fOwnerDrawVar);
  5103. // Calculate the new size in points
  5104. long lptNew = MulDiv(iHeight, 1440, W32->GetYPerInchScreenDC());
  5105. if (GetRange(idx, idx, &pRange))
  5106. {
  5107. CHECKNOERROR(pRange->GetPara(&pPara));
  5108. CHECKNOERROR(pPara->SetLineSpacing(tomLineSpaceExactly, (float)lptNew));
  5109. _rgData[idx]._uHeight = iHeight;
  5110. retCode = TRUE;
  5111. }
  5112. CleanExit:
  5113. if (pPara)
  5114. pPara->Release();
  5115. if (pRange)
  5116. pRange->Release();
  5117. return retCode;
  5118. }
  5119. /*
  5120. * int CLstBxWinHost::GetIdxFromHeight(int)
  5121. *
  5122. * @mfunc
  5123. * Find the idx for the given iHeight from idx 0.
  5124. *
  5125. * @rdesc
  5126. * idx
  5127. */
  5128. int CLstBxWinHost::GetIdxFromHeight(
  5129. int iHeight)
  5130. {
  5131. int idx;
  5132. int iHeightSum = 0;
  5133. Assert(_fOwnerDrawVar);
  5134. for(idx=0; idx < _nCount; idx++)
  5135. {
  5136. if (iHeight < iHeightSum + (int)_rgData[idx]._uHeight)
  5137. {
  5138. if (iHeight != iHeightSum)
  5139. idx++; // partial item, get the next idx
  5140. break;
  5141. }
  5142. iHeightSum += _rgData[idx]._uHeight;
  5143. }
  5144. idx = min(idx, _nCount-1);
  5145. return idx;
  5146. }
  5147. /*
  5148. * LRESULT CLstBxWinHost::LbGetCurSel()
  5149. *
  5150. * @mfunc
  5151. * For Single-selection LB, returns the idnex of current selected item.
  5152. * For multiple-selection LB, returns the index of the item that has the
  5153. * focus rect.
  5154. *
  5155. * @rdesc
  5156. * current select idx
  5157. */
  5158. LRESULT CLstBxWinHost::LbGetCurSel()
  5159. {
  5160. TRACEBEGIN(TRCSUBSYSHOST, TRCSCOPEINTERN, "CLstBxWinHost::LbGetCurSel");
  5161. LRESULT lres;
  5162. if (!IsSingleSelection() || // multiple-selection LB or
  5163. IsSelected(_nCursor)) // _nCursor is selected
  5164. lres = _nCursor;
  5165. else
  5166. {
  5167. lres = LB_ERR;
  5168. // Check which item is selected in single-selection LB
  5169. for (int idx=0; idx < _nCount; idx++)
  5170. {
  5171. if (_rgData[idx]._fSelected)
  5172. {
  5173. lres = idx;
  5174. break;
  5175. }
  5176. }
  5177. }
  5178. return lres;
  5179. }
  5180. //
  5181. // ITxNotify
  5182. //
  5183. /*
  5184. * CLstBxWinHost::OnPostReplaceRange(cp, cchDel, cchNew, cpFormatMin, cpFormatMax, pNotifyData)
  5185. *
  5186. * @mfunc called after a change has been made to the backing store.
  5187. */
  5188. void CLstBxWinHost::OnPostReplaceRange(
  5189. LONG cp, //@parm cp where ReplaceRange starts ("cpMin")
  5190. LONG cchDel, //@parm Count of chars after cp that are deleted
  5191. LONG cchNew, //@parm Count of chars inserted after cp
  5192. LONG cpFormatMin, //@parm cpMin for a formatting change
  5193. LONG cpFormatMax, //@parm cpMost for a formatting change
  5194. NOTIFY_DATA *pNotifyData) //@parm special data to indicate changes
  5195. {
  5196. _cpLastGetRange = 0;
  5197. _nIdxLastGetRange = 0;
  5198. }
  5199. #endif // NOLISTCOMBOBOXES