IMECandidateCtrl.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "IMECandidateCtrl.h"
  19. #include "IMECandidate.h"
  20. #include "DialogBase.h"
  21. #include "StyleMgr.h"
  22. #include "MouseMgr.h"
  23. #define BORDER_WIDTH 2
  24. #define BORDER_HEIGHT 2
  25. /******************************************************************************
  26. *
  27. * NAME
  28. * IMECandidateCtrl::IMECandidateCtrl
  29. *
  30. * DESCRIPTION
  31. *
  32. * INPUTS
  33. * NONE
  34. *
  35. * RESULT
  36. * NONE
  37. *
  38. ******************************************************************************/
  39. IMECandidateCtrl::IMECandidateCtrl() :
  40. mFullRect(0,0,0,0),
  41. mCellSize(0,0),
  42. mCurrSel(-1),
  43. mScrollPos(0),
  44. mCellsPerPage(0),
  45. mCandidate(NULL)
  46. {
  47. StyleMgrClass::Assign_Font(&mTextRenderer, StyleMgrClass::FONT_LISTS);
  48. // Candidate control never wants to take focus
  49. Set_Wants_Focus(false);
  50. // Setup child scrollbar
  51. mScrollBarCtrl.Set_Wants_Focus(false);
  52. mScrollBarCtrl.Set_Small_BMP_Mode(true);
  53. mScrollBarCtrl.Set_Advise_Sink(this);
  54. }
  55. /******************************************************************************
  56. *
  57. * NAME
  58. * IMECandidateCtrl::~IMECandidateCtrl
  59. *
  60. * DESCRIPTION
  61. *
  62. * INPUTS
  63. * NONE
  64. *
  65. * RESULT
  66. * NONE
  67. *
  68. ******************************************************************************/
  69. IMECandidateCtrl::~IMECandidateCtrl()
  70. {
  71. }
  72. /******************************************************************************
  73. *
  74. * NAME
  75. * IMECandidateCtrl::Init
  76. *
  77. * DESCRIPTION
  78. *
  79. * INPUTS
  80. *
  81. * RESULT
  82. * NONE
  83. *
  84. ******************************************************************************/
  85. void IMECandidateCtrl::Init(IME::IMECandidate* candidate)
  86. {
  87. WWDEBUG_SAY(("IMECandidateCtrl: Init\n"));
  88. Reset();
  89. mCandidate = candidate;
  90. Changed(candidate);
  91. }
  92. /******************************************************************************
  93. *
  94. * NAME
  95. * IMECandidateCtrl::Changed
  96. *
  97. * DESCRIPTION
  98. *
  99. * INPUTS
  100. *
  101. * RESULT
  102. * NONE
  103. *
  104. ******************************************************************************/
  105. void IMECandidateCtrl::Changed(IME::IMECandidate* candidate)
  106. {
  107. WWDEBUG_SAY(("IMECandidateCtrl: Changed\n"));
  108. WWASSERT(mCandidate == candidate);
  109. if (candidate)
  110. {
  111. mScrollPos = candidate->GetPageStart();
  112. unsigned long candSel = candidate->GetSelection();
  113. if (candSel != (unsigned long)mCurrSel)
  114. {
  115. mCurrSel = candSel;
  116. UpdateScrollPos();
  117. }
  118. }
  119. Set_Dirty();
  120. }
  121. /******************************************************************************
  122. *
  123. * NAME
  124. * IMECandidateCtrl::Reset
  125. *
  126. * DESCRIPTION
  127. *
  128. * INPUTS
  129. * NONE
  130. *
  131. * RESULT
  132. * NONE
  133. *
  134. ******************************************************************************/
  135. void IMECandidateCtrl::Reset(void)
  136. {
  137. mCellSize.Set(0,0);
  138. mCurrSel = -1;
  139. mScrollPos = 0;
  140. mCellsPerPage = 0;
  141. mScrollBarCtrl.Show(false);
  142. mCandidate = NULL;
  143. }
  144. /******************************************************************************
  145. *
  146. * NAME
  147. * IMECandidateCtrl::CreateControlRenderer
  148. *
  149. * DESCRIPTION
  150. *
  151. * INPUTS
  152. * NONE
  153. *
  154. * RESULT
  155. * NONE
  156. *
  157. ******************************************************************************/
  158. void IMECandidateCtrl::CreateControlRenderer(void)
  159. {
  160. Render2DClass& renderer = mControlRenderer;
  161. // Configure this renderer
  162. renderer.Reset();
  163. renderer.Enable_Texturing(false);
  164. renderer.Set_Coordinate_Range(Render2DClass::Get_Screen_Resolution());
  165. renderer.Add_Quad(mFullRect, RGBA_TO_INT32(0, 0, 0, 170));
  166. // Determine which color to draw the outline in
  167. int color = StyleMgrClass::Get_Line_Color();
  168. int bkColor = StyleMgrClass::Get_Bk_Color();
  169. if (IsEnabled == false)
  170. {
  171. color = StyleMgrClass::Get_Disabled_Line_Color();
  172. bkColor = StyleMgrClass::Get_Disabled_Bk_Color();
  173. }
  174. // Draw the control outline
  175. RectClass rect = Rect;
  176. renderer.Add_Outline(rect, 1.0F, color);
  177. // Now draw the background
  178. rect.Right -= 1;
  179. rect.Bottom -= 1;
  180. renderer.Add_Quad(rect, bkColor);
  181. }
  182. /******************************************************************************
  183. *
  184. * NAME
  185. * IMECandidateCtrl::CreateTextRenderer
  186. *
  187. * DESCRIPTION
  188. *
  189. * INPUTS
  190. * NONE
  191. *
  192. * RESULT
  193. * NONE
  194. *
  195. ******************************************************************************/
  196. void IMECandidateCtrl::CreateTextRenderer(void)
  197. {
  198. mHilightRenderer.Reset();
  199. mHilightRenderer.Set_Coordinate_Range(Render2DClass::Get_Screen_Resolution());
  200. StyleMgrClass::Configure_Hilighter(&mHilightRenderer);
  201. mTextRenderer.Reset();
  202. if (mCandidate)
  203. {
  204. // Add each candidate to the list
  205. const unsigned int selIndexBias = (mCandidate->IsStartFrom1() ? 1 : 0);
  206. float currYPos = ClientRect.Top;
  207. const unsigned long candidateCount = mCandidate->GetCount();
  208. for (unsigned long index = mScrollPos; index < candidateCount; ++index)
  209. {
  210. // Build the rectangle we will draw the text into
  211. RectClass textRect;
  212. textRect.Left = ClientRect.Left;
  213. textRect.Top = currYPos;
  214. textRect.Right = (textRect.Left + mCellSize.X);
  215. textRect.Bottom = (textRect.Top + mCellSize.Y);
  216. // If this cell extends past the end of the control then stop
  217. if (textRect.Bottom > ClientRect.Bottom)
  218. {
  219. break;
  220. }
  221. // Draw the text
  222. const WCHAR* text = mCandidate->GetCandidate(index);
  223. WideStringClass entry(0, true);
  224. entry.Format(L"%d. %s", ((index - mScrollPos) + selIndexBias), text);
  225. StyleMgrClass::Render_Text(entry, &mTextRenderer, textRect, true, true);
  226. // Hilight this entry (if its the currently selected one)
  227. if ((int)index == mCurrSel)
  228. {
  229. StyleMgrClass::Render_Hilight(&mHilightRenderer, textRect);
  230. }
  231. currYPos += mCellSize.Y;
  232. }
  233. }
  234. }
  235. /******************************************************************************
  236. *
  237. * NAME
  238. * IMECandidateCtrl::Render
  239. *
  240. * DESCRIPTION
  241. *
  242. * INPUTS
  243. * NONE
  244. *
  245. * RESULT
  246. * NONE
  247. *
  248. ******************************************************************************/
  249. void IMECandidateCtrl::Render(void)
  250. {
  251. // Recreate the renderers (if necessary)
  252. if (IsDirty)
  253. {
  254. CreateControlRenderer();
  255. CreateTextRenderer();
  256. }
  257. mControlRenderer.Render();
  258. mTextRenderer.Render();
  259. mHilightRenderer.Render();
  260. DialogControlClass::Render();
  261. }
  262. /******************************************************************************
  263. *
  264. * NAME
  265. * IMECandidateCtrl::SetCurrSel
  266. *
  267. * DESCRIPTION
  268. *
  269. * INPUTS
  270. *
  271. * RESULT
  272. * NONE
  273. *
  274. ******************************************************************************/
  275. void IMECandidateCtrl::SetCurrSel(int index)
  276. {
  277. if (mCandidate)
  278. {
  279. if ((index != mCurrSel) && (index == -1) || ((index >= 0) && (unsigned long)index < mCandidate->GetCount()))
  280. {
  281. mCurrSel = index;
  282. Set_Dirty();
  283. }
  284. }
  285. }
  286. /******************************************************************************
  287. *
  288. * NAME
  289. * IMECandidateCtrl::EntryFromPos
  290. *
  291. * DESCRIPTION
  292. *
  293. * INPUTS
  294. *
  295. * RESULT
  296. *
  297. ******************************************************************************/
  298. int IMECandidateCtrl::EntryFromPos(const Vector2& mousePos)
  299. {
  300. if (mCandidate)
  301. {
  302. // Loop over all the entries in our current view
  303. float currYPos = ClientRect.Top;
  304. const unsigned long candidateCount = mCandidate->GetCount();
  305. for (unsigned long index = mScrollPos; index < candidateCount; ++index)
  306. {
  307. // Is ths mouse over this entry?
  308. if ((mousePos.Y >= currYPos && mousePos.Y <= (currYPos + mCellSize.Y))
  309. || mousePos.Y > ClientRect.Bottom)
  310. {
  311. return index;
  312. }
  313. currYPos += mCellSize.Y;
  314. }
  315. }
  316. return -1;
  317. }
  318. /******************************************************************************
  319. *
  320. * NAME
  321. * IMECandidateCtrl::UpdateScrollPos
  322. *
  323. * DESCRIPTION
  324. *
  325. * INPUTS
  326. * NONE
  327. *
  328. * RESULT
  329. * NONE
  330. *
  331. ******************************************************************************/
  332. void IMECandidateCtrl::UpdateScrollPos(void)
  333. {
  334. if (mCurrSel != -1)
  335. {
  336. unsigned int scrollPos = mScrollPos;
  337. if (mCurrSel < (int)scrollPos)
  338. {
  339. // Scroll up so the current selection is in view
  340. scrollPos = mCurrSel;
  341. Set_Dirty();
  342. }
  343. else if (mCurrSel >= (int)(scrollPos + mCellsPerPage))
  344. {
  345. // Scroll down so the current selection is in view
  346. scrollPos = max<unsigned int>((unsigned int)mCurrSel - (mCellsPerPage - 1), 0);
  347. Set_Dirty();
  348. }
  349. if (scrollPos != mScrollPos)
  350. {
  351. // Update the scrollbar
  352. mScrollBarCtrl.Set_Pos(mScrollPos, false);
  353. if (mCandidate)
  354. {
  355. mCandidate->SetPageStart(mScrollPos);
  356. }
  357. }
  358. }
  359. }
  360. /******************************************************************************
  361. *
  362. * NAME
  363. * IMECandidateCtrl::Update_Client_Rect
  364. *
  365. * DESCRIPTION
  366. *
  367. * INPUTS
  368. * NONE
  369. *
  370. * RESULT
  371. * NONE
  372. *
  373. ******************************************************************************/
  374. void IMECandidateCtrl::Update_Client_Rect(void)
  375. {
  376. WWDEBUG_SAY(("IMECandidateCtrl: Update_Client_Rect()\n"));
  377. Vector2 pageSize;
  378. CalculateCandidatePageExtent(pageSize, mCellSize);
  379. mCellsPerPage = (unsigned int)(pageSize.Y / mCellSize.Y);
  380. Rect.Right = (Rect.Left + (pageSize.X + (BORDER_WIDTH * 2.0f)));
  381. Rect.Bottom = (Rect.Top + (pageSize.Y + (BORDER_HEIGHT * 2.0f)));
  382. mFullRect = Rect;
  383. ClientRect = Rect;
  384. ClientRect.Inflate(Vector2(-BORDER_WIDTH, -BORDER_HEIGHT));
  385. if (mCandidate)
  386. {
  387. WWASSERT(mCandidate->GetPageSize() <= mCellsPerPage);
  388. // Do we need to show a scroll bar?
  389. const unsigned long candidateCount = mCandidate->GetCount();
  390. if ((unsigned long)mCellsPerPage < candidateCount)
  391. {
  392. // Position the scrollbar to the right of the list
  393. RectClass scrollRect;
  394. scrollRect.Left = mFullRect.Right;
  395. scrollRect.Top = mFullRect.Top;
  396. scrollRect.Right = -1;
  397. scrollRect.Bottom = mFullRect.Bottom;
  398. // Size the scroll bar
  399. mScrollBarCtrl.Set_Window_Rect(scrollRect);
  400. mScrollBarCtrl.Set_Page_Size(mCellsPerPage - 1);
  401. mScrollBarCtrl.Set_Range(0, (candidateCount - mCellsPerPage));
  402. mScrollBarCtrl.Show(true);
  403. const RectClass& rect = mScrollBarCtrl.Get_Window_Rect();
  404. mFullRect.Right = rect.Right;
  405. }
  406. }
  407. Set_Dirty();
  408. }
  409. /******************************************************************************
  410. *
  411. * NAME
  412. * IMECandidateCtrl::
  413. *
  414. * DESCRIPTION
  415. *
  416. * INPUTS
  417. *
  418. * RESULT
  419. * NONE
  420. *
  421. ******************************************************************************/
  422. void IMECandidateCtrl::CalculateCandidatePageExtent(Vector2& outExtent, Vector2& outCellSize)
  423. {
  424. if (mCandidate)
  425. {
  426. // Get the size of the widest candidate string.
  427. float maxCandWidth = 0.0f;
  428. const unsigned long candidateCount = mCandidate->GetCount();
  429. for (unsigned long index = 0; index < candidateCount; ++index)
  430. {
  431. // Get the extent of the current entry
  432. const WCHAR* text = mCandidate->GetCandidate(index);
  433. Vector2 textExtent = mTextRenderer.Get_Text_Extents(text);
  434. if (textExtent.X > maxCandWidth)
  435. {
  436. maxCandWidth = textExtent.X;
  437. }
  438. }
  439. Vector2 charSize = mTextRenderer.Get_Text_Extents(L"W");
  440. // The cell size is the maximum candidate size plus some spacing.
  441. outCellSize.X = (maxCandWidth + (charSize.X * 3.0f));
  442. outCellSize.Y = (charSize.Y * 1.25f);
  443. outExtent.X = outCellSize.X;
  444. outExtent.Y = (mCellSize.Y * (float)mCandidate->GetPageSize());
  445. }
  446. else
  447. {
  448. outExtent.X = 0.0f;
  449. outExtent.Y = 0.0f;
  450. outCellSize.X = 0.0f;
  451. outCellSize.Y = 0.0f;
  452. }
  453. }
  454. /******************************************************************************
  455. *
  456. * NAME
  457. * IMECandidateCtrl::On_Set_Cursor
  458. *
  459. * DESCRIPTION
  460. *
  461. * INPUTS
  462. *
  463. * RESULT
  464. * NONE
  465. *
  466. ******************************************************************************/
  467. void IMECandidateCtrl::On_Set_Cursor(const Vector2& mousePos)
  468. {
  469. // Change the mouse cursor if necessary
  470. if (ClientRect.Contains(mousePos))
  471. {
  472. MouseMgrClass::Set_Cursor(MouseMgrClass::CURSOR_ACTION);
  473. }
  474. }
  475. /******************************************************************************
  476. *
  477. * NAME
  478. * IMECandidateCtrl::On_LButton_Down
  479. *
  480. * DESCRIPTION
  481. *
  482. * INPUTS
  483. *
  484. * RESULT
  485. * NONE
  486. *
  487. ******************************************************************************/
  488. void IMECandidateCtrl::On_LButton_Down(const Vector2& mousePos)
  489. {
  490. SetCurrSel(EntryFromPos(mousePos));
  491. }
  492. /******************************************************************************
  493. *
  494. * NAME
  495. * IMECandidateCtrl::On_LButton_Up
  496. *
  497. * DESCRIPTION
  498. *
  499. * INPUTS
  500. *
  501. * RESULT
  502. * NONE
  503. *
  504. ******************************************************************************/
  505. void IMECandidateCtrl::On_LButton_Up(const Vector2& mousePos)
  506. {
  507. int sel = EntryFromPos(mousePos);
  508. SetCurrSel(sel);
  509. // Process candidate selection here
  510. if (mCandidate && (sel >= 0))
  511. {
  512. const wchar_t* string = mCandidate->GetCandidate(sel);
  513. WWDEBUG_SAY(("*** Selected Candidate: %d %04x\n", sel, *string));
  514. mCandidate->SelectCandidate((unsigned long)sel);
  515. }
  516. }
  517. /******************************************************************************
  518. *
  519. * NAME
  520. * IMECandidateCtrl::On_Add_To_Dialog
  521. *
  522. * DESCRIPTION
  523. *
  524. * INPUTS
  525. * NONE
  526. *
  527. * RESULT
  528. * NONE
  529. *
  530. ******************************************************************************/
  531. void IMECandidateCtrl::On_Add_To_Dialog(void)
  532. {
  533. Parent->Add_Control(&mScrollBarCtrl);
  534. }
  535. /******************************************************************************
  536. *
  537. * NAME
  538. * IMECandidateCtrl::On_Remove_From_Dialog
  539. *
  540. * DESCRIPTION
  541. *
  542. * INPUTS
  543. * NONE
  544. *
  545. * RESULT
  546. * NONE
  547. *
  548. ******************************************************************************/
  549. void IMECandidateCtrl::On_Remove_From_Dialog(void)
  550. {
  551. Parent->Remove_Control(&mScrollBarCtrl);
  552. }
  553. /******************************************************************************
  554. *
  555. * NAME
  556. * IMECandidateCtrl::On_VScroll
  557. *
  558. * DESCRIPTION
  559. *
  560. * INPUTS
  561. *
  562. * RESULT
  563. * NONE
  564. *
  565. ******************************************************************************/
  566. void IMECandidateCtrl::On_VScroll(ScrollBarCtrlClass*, int , int newPos)
  567. {
  568. mScrollPos = newPos;
  569. if (mCandidate)
  570. {
  571. mCandidate->SetPageStart(mScrollPos);
  572. }
  573. Set_Dirty();
  574. }