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

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