xref: /aosp_15_r20/external/pdfium/fpdfsdk/pwl/cpwl_list_ctrl.cpp (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
1 // Copyright 2014 The PDFium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "fpdfsdk/pwl/cpwl_list_ctrl.h"
8 
9 #include <algorithm>
10 #include <utility>
11 
12 #include "core/fpdfdoc/cpvt_word.h"
13 #include "core/fxcrt/fx_extension.h"
14 #include "core/fxcrt/stl_util.h"
15 #include "fpdfsdk/pwl/cpwl_edit_impl.h"
16 #include "fpdfsdk/pwl/cpwl_list_box.h"
17 #include "third_party/base/numerics/safe_conversions.h"
18 
19 CPWL_ListCtrl::NotifyIface::~NotifyIface() = default;
20 
Item()21 CPWL_ListCtrl::Item::Item() : m_pEdit(std::make_unique<CPWL_EditImpl>()) {
22   m_pEdit->SetAlignmentV(1);
23   m_pEdit->Initialize();
24 }
25 
26 CPWL_ListCtrl::Item::~Item() = default;
27 
SetFontMap(IPVT_FontMap * pFontMap)28 void CPWL_ListCtrl::Item::SetFontMap(IPVT_FontMap* pFontMap) {
29   m_pEdit->SetFontMap(pFontMap);
30 }
31 
SetText(const WideString & text)32 void CPWL_ListCtrl::Item::SetText(const WideString& text) {
33   m_pEdit->SetText(text);
34   m_pEdit->Paint();
35 }
36 
SetFontSize(float fFontSize)37 void CPWL_ListCtrl::Item::SetFontSize(float fFontSize) {
38   m_pEdit->SetFontSize(fFontSize);
39   m_pEdit->Paint();
40 }
41 
GetItemHeight() const42 float CPWL_ListCtrl::Item::GetItemHeight() const {
43   return m_pEdit->GetContentRect().Height();
44 }
45 
GetFirstChar() const46 uint16_t CPWL_ListCtrl::Item::GetFirstChar() const {
47   CPVT_Word word;
48   CPWL_EditImpl::Iterator* pIterator = m_pEdit->GetIterator();
49   pIterator->SetAt(1);
50   pIterator->GetWord(word);
51   return word.Word;
52 }
53 
GetText() const54 WideString CPWL_ListCtrl::Item::GetText() const {
55   return m_pEdit->GetText();
56 }
57 
58 CPWL_ListCtrl::SelectState::SelectState() = default;
59 
60 CPWL_ListCtrl::SelectState::~SelectState() = default;
61 
Add(int32_t nItemIndex)62 void CPWL_ListCtrl::SelectState::Add(int32_t nItemIndex) {
63   m_Items[nItemIndex] = SELECTING;
64 }
65 
Add(int32_t nBeginIndex,int32_t nEndIndex)66 void CPWL_ListCtrl::SelectState::Add(int32_t nBeginIndex, int32_t nEndIndex) {
67   if (nBeginIndex > nEndIndex)
68     std::swap(nBeginIndex, nEndIndex);
69 
70   for (int32_t i = nBeginIndex; i <= nEndIndex; ++i)
71     Add(i);
72 }
73 
Sub(int32_t nItemIndex)74 void CPWL_ListCtrl::SelectState::Sub(int32_t nItemIndex) {
75   auto it = m_Items.find(nItemIndex);
76   if (it != m_Items.end())
77     it->second = DESELECTING;
78 }
79 
Sub(int32_t nBeginIndex,int32_t nEndIndex)80 void CPWL_ListCtrl::SelectState::Sub(int32_t nBeginIndex, int32_t nEndIndex) {
81   if (nBeginIndex > nEndIndex)
82     std::swap(nBeginIndex, nEndIndex);
83 
84   for (int32_t i = nBeginIndex; i <= nEndIndex; ++i)
85     Sub(i);
86 }
87 
DeselectAll()88 void CPWL_ListCtrl::SelectState::DeselectAll() {
89   for (auto& item : m_Items)
90     item.second = DESELECTING;
91 }
92 
Done()93 void CPWL_ListCtrl::SelectState::Done() {
94   auto it = m_Items.begin();
95   while (it != m_Items.end()) {
96     if (it->second == DESELECTING)
97       it = m_Items.erase(it);
98     else
99       (it++)->second = NORMAL;
100   }
101 }
102 
103 CPWL_ListCtrl::CPWL_ListCtrl() = default;
104 
~CPWL_ListCtrl()105 CPWL_ListCtrl::~CPWL_ListCtrl() {
106   m_ListItems.clear();
107   InvalidateItem(-1);
108 }
109 
InToOut(const CFX_PointF & point) const110 CFX_PointF CPWL_ListCtrl::InToOut(const CFX_PointF& point) const {
111   CFX_FloatRect rcPlate = m_rcPlate;
112   return CFX_PointF(point.x - (m_ptScrollPos.x - rcPlate.left),
113                     point.y - (m_ptScrollPos.y - rcPlate.top));
114 }
115 
OutToIn(const CFX_PointF & point) const116 CFX_PointF CPWL_ListCtrl::OutToIn(const CFX_PointF& point) const {
117   CFX_FloatRect rcPlate = m_rcPlate;
118   return CFX_PointF(point.x + (m_ptScrollPos.x - rcPlate.left),
119                     point.y + (m_ptScrollPos.y - rcPlate.top));
120 }
121 
InToOut(const CFX_FloatRect & rect) const122 CFX_FloatRect CPWL_ListCtrl::InToOut(const CFX_FloatRect& rect) const {
123   CFX_PointF ptLeftBottom = InToOut(CFX_PointF(rect.left, rect.bottom));
124   CFX_PointF ptRightTop = InToOut(CFX_PointF(rect.right, rect.top));
125   return CFX_FloatRect(ptLeftBottom.x, ptLeftBottom.y, ptRightTop.x,
126                        ptRightTop.y);
127 }
128 
OutToIn(const CFX_FloatRect & rect) const129 CFX_FloatRect CPWL_ListCtrl::OutToIn(const CFX_FloatRect& rect) const {
130   CFX_PointF ptLeftBottom = OutToIn(CFX_PointF(rect.left, rect.bottom));
131   CFX_PointF ptRightTop = OutToIn(CFX_PointF(rect.right, rect.top));
132   return CFX_FloatRect(ptLeftBottom.x, ptLeftBottom.y, ptRightTop.x,
133                        ptRightTop.y);
134 }
135 
InnerToOuter(const CFX_PointF & point) const136 CFX_PointF CPWL_ListCtrl::InnerToOuter(const CFX_PointF& point) const {
137   return CFX_PointF(point.x + GetBTPoint().x, GetBTPoint().y - point.y);
138 }
139 
OuterToInner(const CFX_PointF & point) const140 CFX_PointF CPWL_ListCtrl::OuterToInner(const CFX_PointF& point) const {
141   return CFX_PointF(point.x - GetBTPoint().x, GetBTPoint().y - point.y);
142 }
143 
InnerToOuter(const CFX_FloatRect & rect) const144 CFX_FloatRect CPWL_ListCtrl::InnerToOuter(const CFX_FloatRect& rect) const {
145   CFX_PointF ptLeftTop = InnerToOuter(CFX_PointF(rect.left, rect.top));
146   CFX_PointF ptRightBottom = InnerToOuter(CFX_PointF(rect.right, rect.bottom));
147   return CFX_FloatRect(ptLeftTop.x, ptRightBottom.y, ptRightBottom.x,
148                        ptLeftTop.y);
149 }
150 
OuterToInner(const CFX_FloatRect & rect) const151 CFX_FloatRect CPWL_ListCtrl::OuterToInner(const CFX_FloatRect& rect) const {
152   CFX_PointF ptLeftTop = OuterToInner(CFX_PointF(rect.left, rect.top));
153   CFX_PointF ptRightBottom = OuterToInner(CFX_PointF(rect.right, rect.bottom));
154   return CFX_FloatRect(ptLeftTop.x, ptRightBottom.y, ptRightBottom.x,
155                        ptLeftTop.y);
156 }
157 
OnMouseDown(const CFX_PointF & point,bool bShift,bool bCtrl)158 void CPWL_ListCtrl::OnMouseDown(const CFX_PointF& point,
159                                 bool bShift,
160                                 bool bCtrl) {
161   int32_t nHitIndex = GetItemIndex(point);
162 
163   if (IsMultipleSel()) {
164     if (bCtrl) {
165       if (IsItemSelected(nHitIndex)) {
166         m_SelectState.Sub(nHitIndex);
167         SelectItems();
168         m_bCtrlSel = false;
169       } else {
170         m_SelectState.Add(nHitIndex);
171         SelectItems();
172         m_bCtrlSel = true;
173       }
174 
175       m_nFootIndex = nHitIndex;
176     } else if (bShift) {
177       m_SelectState.DeselectAll();
178       m_SelectState.Add(m_nFootIndex, nHitIndex);
179       SelectItems();
180     } else {
181       m_SelectState.DeselectAll();
182       m_SelectState.Add(nHitIndex);
183       SelectItems();
184 
185       m_nFootIndex = nHitIndex;
186     }
187 
188     SetCaret(nHitIndex);
189   } else {
190     SetSingleSelect(nHitIndex);
191   }
192 
193   if (!IsItemVisible(nHitIndex))
194     ScrollToListItem(nHitIndex);
195 }
196 
OnMouseMove(const CFX_PointF & point,bool bShift,bool bCtrl)197 void CPWL_ListCtrl::OnMouseMove(const CFX_PointF& point,
198                                 bool bShift,
199                                 bool bCtrl) {
200   int32_t nHitIndex = GetItemIndex(point);
201 
202   if (IsMultipleSel()) {
203     if (bCtrl) {
204       if (m_bCtrlSel)
205         m_SelectState.Add(m_nFootIndex, nHitIndex);
206       else
207         m_SelectState.Sub(m_nFootIndex, nHitIndex);
208 
209       SelectItems();
210     } else {
211       m_SelectState.DeselectAll();
212       m_SelectState.Add(m_nFootIndex, nHitIndex);
213       SelectItems();
214     }
215 
216     SetCaret(nHitIndex);
217   } else {
218     SetSingleSelect(nHitIndex);
219   }
220 
221   if (!IsItemVisible(nHitIndex))
222     ScrollToListItem(nHitIndex);
223 }
224 
OnVK(int32_t nItemIndex,bool bShift,bool bCtrl)225 void CPWL_ListCtrl::OnVK(int32_t nItemIndex, bool bShift, bool bCtrl) {
226   if (IsMultipleSel()) {
227     if (nItemIndex >= 0 && nItemIndex < GetCount()) {
228       if (bCtrl) {
229       } else if (bShift) {
230         m_SelectState.DeselectAll();
231         m_SelectState.Add(m_nFootIndex, nItemIndex);
232         SelectItems();
233       } else {
234         m_SelectState.DeselectAll();
235         m_SelectState.Add(nItemIndex);
236         SelectItems();
237         m_nFootIndex = nItemIndex;
238       }
239 
240       SetCaret(nItemIndex);
241     }
242   } else {
243     SetSingleSelect(nItemIndex);
244   }
245 
246   if (!IsItemVisible(nItemIndex))
247     ScrollToListItem(nItemIndex);
248 }
249 
OnVK_UP(bool bShift,bool bCtrl)250 void CPWL_ListCtrl::OnVK_UP(bool bShift, bool bCtrl) {
251   OnVK(IsMultipleSel() ? GetCaret() - 1 : GetSelect() - 1, bShift, bCtrl);
252 }
253 
OnVK_DOWN(bool bShift,bool bCtrl)254 void CPWL_ListCtrl::OnVK_DOWN(bool bShift, bool bCtrl) {
255   OnVK(IsMultipleSel() ? GetCaret() + 1 : GetSelect() + 1, bShift, bCtrl);
256 }
257 
OnVK_LEFT(bool bShift,bool bCtrl)258 void CPWL_ListCtrl::OnVK_LEFT(bool bShift, bool bCtrl) {
259   OnVK(0, bShift, bCtrl);
260 }
261 
OnVK_RIGHT(bool bShift,bool bCtrl)262 void CPWL_ListCtrl::OnVK_RIGHT(bool bShift, bool bCtrl) {
263   OnVK(GetCount() - 1, bShift, bCtrl);
264 }
265 
OnVK_HOME(bool bShift,bool bCtrl)266 void CPWL_ListCtrl::OnVK_HOME(bool bShift, bool bCtrl) {
267   OnVK(0, bShift, bCtrl);
268 }
269 
OnVK_END(bool bShift,bool bCtrl)270 void CPWL_ListCtrl::OnVK_END(bool bShift, bool bCtrl) {
271   OnVK(GetCount() - 1, bShift, bCtrl);
272 }
273 
OnChar(uint16_t nChar,bool bShift,bool bCtrl)274 bool CPWL_ListCtrl::OnChar(uint16_t nChar, bool bShift, bool bCtrl) {
275   int32_t nIndex = GetLastSelected();
276   int32_t nFindIndex = FindNext(nIndex, nChar);
277 
278   if (nFindIndex != nIndex) {
279     OnVK(nFindIndex, bShift, bCtrl);
280     return true;
281   }
282   return false;
283 }
284 
SetPlateRect(const CFX_FloatRect & rect)285 void CPWL_ListCtrl::SetPlateRect(const CFX_FloatRect& rect) {
286   m_rcPlate = rect;
287   m_ptScrollPos.x = rect.left;
288   SetScrollPos(CFX_PointF(rect.left, rect.top));
289   ReArrange(0);
290   InvalidateItem(-1);
291 }
292 
GetItemRect(int32_t nIndex) const293 CFX_FloatRect CPWL_ListCtrl::GetItemRect(int32_t nIndex) const {
294   return InToOut(GetItemRectInternal(nIndex));
295 }
296 
GetItemRectInternal(int32_t nIndex) const297 CFX_FloatRect CPWL_ListCtrl::GetItemRectInternal(int32_t nIndex) const {
298   if (!IsValid(nIndex))
299     return CFX_FloatRect();
300 
301   CFX_FloatRect rcItem = m_ListItems[nIndex]->GetRect();
302   rcItem.left = 0.0f;
303   rcItem.right = m_rcPlate.Width();
304   return InnerToOuter(rcItem);
305 }
306 
AddString(const WideString & str)307 void CPWL_ListCtrl::AddString(const WideString& str) {
308   AddItem(str);
309   ReArrange(GetCount() - 1);
310 }
311 
SetMultipleSelect(int32_t nItemIndex,bool bSelected)312 void CPWL_ListCtrl::SetMultipleSelect(int32_t nItemIndex, bool bSelected) {
313   if (!IsValid(nItemIndex))
314     return;
315 
316   if (bSelected != IsItemSelected(nItemIndex)) {
317     if (bSelected) {
318       SetItemSelect(nItemIndex, true);
319       InvalidateItem(nItemIndex);
320     } else {
321       SetItemSelect(nItemIndex, false);
322       InvalidateItem(nItemIndex);
323     }
324   }
325 }
326 
SetSingleSelect(int32_t nItemIndex)327 void CPWL_ListCtrl::SetSingleSelect(int32_t nItemIndex) {
328   if (!IsValid(nItemIndex))
329     return;
330 
331   if (m_nSelItem != nItemIndex) {
332     if (m_nSelItem >= 0) {
333       SetItemSelect(m_nSelItem, false);
334       InvalidateItem(m_nSelItem);
335     }
336 
337     SetItemSelect(nItemIndex, true);
338     InvalidateItem(nItemIndex);
339     m_nSelItem = nItemIndex;
340   }
341 }
342 
SetCaret(int32_t nItemIndex)343 void CPWL_ListCtrl::SetCaret(int32_t nItemIndex) {
344   if (!IsValid(nItemIndex))
345     return;
346 
347   if (IsMultipleSel()) {
348     int32_t nOldIndex = m_nCaretIndex;
349 
350     if (nOldIndex != nItemIndex) {
351       m_nCaretIndex = nItemIndex;
352       InvalidateItem(nOldIndex);
353       InvalidateItem(nItemIndex);
354     }
355   }
356 }
357 
InvalidateItem(int32_t nItemIndex)358 void CPWL_ListCtrl::InvalidateItem(int32_t nItemIndex) {
359   if (!m_pNotify) {
360     return;
361   }
362   if (nItemIndex == -1) {
363     if (!m_bNotifyFlag) {
364       m_bNotifyFlag = true;
365       CFX_FloatRect rcRefresh = m_rcPlate;
366       if (!m_pNotify->OnInvalidateRect(rcRefresh)) {
367         m_pNotify = nullptr;  // Gone, dangling even.
368       }
369       m_bNotifyFlag = false;
370     }
371   } else {
372     if (!m_bNotifyFlag) {
373       m_bNotifyFlag = true;
374       CFX_FloatRect rcRefresh = GetItemRect(nItemIndex);
375       rcRefresh.left -= 1.0f;
376       rcRefresh.right += 1.0f;
377       rcRefresh.bottom -= 1.0f;
378       rcRefresh.top += 1.0f;
379       if (!m_pNotify->OnInvalidateRect(rcRefresh)) {
380         m_pNotify = nullptr;  // Gone, dangling even.
381       }
382       m_bNotifyFlag = false;
383     }
384   }
385 }
386 
SelectItems()387 void CPWL_ListCtrl::SelectItems() {
388   for (const auto& item : m_SelectState) {
389     if (item.second != SelectState::NORMAL)
390       SetMultipleSelect(item.first, item.second == SelectState::SELECTING);
391   }
392   m_SelectState.Done();
393 }
394 
Select(int32_t nItemIndex)395 void CPWL_ListCtrl::Select(int32_t nItemIndex) {
396   if (!IsValid(nItemIndex))
397     return;
398 
399   if (IsMultipleSel()) {
400     m_SelectState.Add(nItemIndex);
401     SelectItems();
402   } else {
403     SetSingleSelect(nItemIndex);
404   }
405 }
406 
Deselect(int32_t nItemIndex)407 void CPWL_ListCtrl::Deselect(int32_t nItemIndex) {
408   if (!IsItemSelected(nItemIndex))
409     return;
410 
411   SetMultipleSelect(nItemIndex, false);
412 
413   if (!IsMultipleSel())
414     m_nSelItem = -1;
415 }
416 
IsItemVisible(int32_t nItemIndex) const417 bool CPWL_ListCtrl::IsItemVisible(int32_t nItemIndex) const {
418   CFX_FloatRect rcPlate = m_rcPlate;
419   CFX_FloatRect rcItem = GetItemRect(nItemIndex);
420 
421   return rcItem.bottom >= rcPlate.bottom && rcItem.top <= rcPlate.top;
422 }
423 
ScrollToListItem(int32_t nItemIndex)424 void CPWL_ListCtrl::ScrollToListItem(int32_t nItemIndex) {
425   if (!IsValid(nItemIndex))
426     return;
427 
428   CFX_FloatRect rcPlate = m_rcPlate;
429   CFX_FloatRect rcItem = GetItemRectInternal(nItemIndex);
430   CFX_FloatRect rcItemCtrl = GetItemRect(nItemIndex);
431 
432   if (FXSYS_IsFloatSmaller(rcItemCtrl.bottom, rcPlate.bottom)) {
433     if (FXSYS_IsFloatSmaller(rcItemCtrl.top, rcPlate.top)) {
434       SetScrollPosY(rcItem.bottom + rcPlate.Height());
435     }
436   } else if (FXSYS_IsFloatBigger(rcItemCtrl.top, rcPlate.top)) {
437     if (FXSYS_IsFloatBigger(rcItemCtrl.bottom, rcPlate.bottom)) {
438       SetScrollPosY(rcItem.top);
439     }
440   }
441 }
442 
SetScrollInfo()443 void CPWL_ListCtrl::SetScrollInfo() {
444   if (m_pNotify) {
445     CFX_FloatRect rcPlate = m_rcPlate;
446     CFX_FloatRect rcContent = GetContentRectInternal();
447 
448     if (!m_bNotifyFlag) {
449       m_bNotifyFlag = true;
450       m_pNotify->OnSetScrollInfoY(rcPlate.bottom, rcPlate.top, rcContent.bottom,
451                                   rcContent.top, GetFirstHeight(),
452                                   rcPlate.Height());
453       m_bNotifyFlag = false;
454     }
455   }
456 }
457 
SetScrollPos(const CFX_PointF & point)458 void CPWL_ListCtrl::SetScrollPos(const CFX_PointF& point) {
459   SetScrollPosY(point.y);
460 }
461 
SetScrollPosY(float fy)462 void CPWL_ListCtrl::SetScrollPosY(float fy) {
463   if (!FXSYS_IsFloatEqual(m_ptScrollPos.y, fy)) {
464     CFX_FloatRect rcPlate = m_rcPlate;
465     CFX_FloatRect rcContent = GetContentRectInternal();
466 
467     if (rcPlate.Height() > rcContent.Height()) {
468       fy = rcPlate.top;
469     } else {
470       if (FXSYS_IsFloatSmaller(fy - rcPlate.Height(), rcContent.bottom)) {
471         fy = rcContent.bottom + rcPlate.Height();
472       } else if (FXSYS_IsFloatBigger(fy, rcContent.top)) {
473         fy = rcContent.top;
474       }
475     }
476 
477     m_ptScrollPos.y = fy;
478     InvalidateItem(-1);
479 
480     if (m_pNotify) {
481       if (!m_bNotifyFlag) {
482         m_bNotifyFlag = true;
483         m_pNotify->OnSetScrollPosY(fy);
484         m_bNotifyFlag = false;
485       }
486     }
487   }
488 }
489 
GetContentRectInternal() const490 CFX_FloatRect CPWL_ListCtrl::GetContentRectInternal() const {
491   return InnerToOuter(m_rcContent);
492 }
493 
GetContentRect() const494 CFX_FloatRect CPWL_ListCtrl::GetContentRect() const {
495   return InToOut(GetContentRectInternal());
496 }
497 
ReArrange(int32_t nItemIndex)498 void CPWL_ListCtrl::ReArrange(int32_t nItemIndex) {
499   float fPosY = 0.0f;
500   if (IsValid(nItemIndex - 1))
501     fPosY = m_ListItems[nItemIndex - 1]->GetRect().bottom;
502 
503   for (const auto& pListItem : m_ListItems) {
504     float fListItemHeight = pListItem->GetItemHeight();
505     pListItem->SetRect(
506         CFX_FloatRect(0.0f, fPosY + fListItemHeight, 0.0f, fPosY));
507     fPosY += fListItemHeight;
508   }
509   m_rcContent = CFX_FloatRect(0.0f, fPosY, 0.0f, 0.0f);
510   SetScrollInfo();
511 }
512 
SetTopItem(int32_t nIndex)513 void CPWL_ListCtrl::SetTopItem(int32_t nIndex) {
514   if (IsValid(nIndex)) {
515     CFX_FloatRect rcItem = GetItemRectInternal(nIndex);
516     SetScrollPosY(rcItem.top);
517   }
518 }
519 
GetTopItem() const520 int32_t CPWL_ListCtrl::GetTopItem() const {
521   int32_t nItemIndex = GetItemIndex(GetBTPoint());
522   if (!IsItemVisible(nItemIndex) && IsItemVisible(nItemIndex + 1))
523     nItemIndex += 1;
524 
525   return nItemIndex;
526 }
527 
GetItemIndex(const CFX_PointF & point) const528 int32_t CPWL_ListCtrl::GetItemIndex(const CFX_PointF& point) const {
529   CFX_PointF pt = OuterToInner(OutToIn(point));
530   bool bFirst = true;
531   bool bLast = true;
532   for (const auto& pListItem : m_ListItems) {
533     CFX_FloatRect rcListItem = pListItem->GetRect();
534     if (FXSYS_IsFloatBigger(pt.y, rcListItem.top))
535       bFirst = false;
536     if (FXSYS_IsFloatSmaller(pt.y, rcListItem.bottom))
537       bLast = false;
538     if (pt.y >= rcListItem.top && pt.y < rcListItem.bottom) {
539       return pdfium::base::checked_cast<int32_t>(&pListItem -
540                                                  &m_ListItems.front());
541     }
542   }
543   if (bFirst)
544     return 0;
545   if (bLast)
546     return GetCount() - 1;
547   return -1;
548 }
549 
GetText() const550 WideString CPWL_ListCtrl::GetText() const {
551   if (IsMultipleSel())
552     return GetItemText(m_nCaretIndex);
553   return GetItemText(m_nSelItem);
554 }
555 
AddItem(const WideString & str)556 void CPWL_ListCtrl::AddItem(const WideString& str) {
557   auto pListItem = std::make_unique<Item>();
558   pListItem->SetFontMap(m_pFontMap);
559   pListItem->SetFontSize(m_fFontSize);
560   pListItem->SetText(str);
561   m_ListItems.push_back(std::move(pListItem));
562 }
563 
GetItemEdit(int32_t nIndex) const564 CPWL_EditImpl* CPWL_ListCtrl::GetItemEdit(int32_t nIndex) const {
565   if (!IsValid(nIndex))
566     return nullptr;
567   return m_ListItems[nIndex]->GetEdit();
568 }
569 
GetCount() const570 int32_t CPWL_ListCtrl::GetCount() const {
571   return fxcrt::CollectionSize<int32_t>(m_ListItems);
572 }
573 
GetFirstHeight() const574 float CPWL_ListCtrl::GetFirstHeight() const {
575   if (m_ListItems.empty())
576     return 1.0f;
577   return m_ListItems.front()->GetItemHeight();
578 }
579 
GetFirstSelected() const580 int32_t CPWL_ListCtrl::GetFirstSelected() const {
581   int32_t i = 0;
582   for (const auto& pListItem : m_ListItems) {
583     if (pListItem->IsSelected())
584       return i;
585     ++i;
586   }
587   return -1;
588 }
589 
GetLastSelected() const590 int32_t CPWL_ListCtrl::GetLastSelected() const {
591   for (auto iter = m_ListItems.rbegin(); iter != m_ListItems.rend(); ++iter) {
592     if ((*iter)->IsSelected())
593       return pdfium::base::checked_cast<int32_t>(&*iter - &m_ListItems.front());
594   }
595   return -1;
596 }
597 
FindNext(int32_t nIndex,wchar_t nChar) const598 int32_t CPWL_ListCtrl::FindNext(int32_t nIndex, wchar_t nChar) const {
599   int32_t nCircleIndex = nIndex;
600   int32_t sz = GetCount();
601   for (int32_t i = 0; i < sz; i++) {
602     nCircleIndex++;
603     if (nCircleIndex >= sz)
604       nCircleIndex = 0;
605 
606     if (Item* pListItem = m_ListItems[nCircleIndex].get()) {
607       if (FXSYS_towupper(pListItem->GetFirstChar()) == FXSYS_towupper(nChar))
608         return nCircleIndex;
609     }
610   }
611 
612   return nCircleIndex;
613 }
614 
IsItemSelected(int32_t nIndex) const615 bool CPWL_ListCtrl::IsItemSelected(int32_t nIndex) const {
616   return IsValid(nIndex) && m_ListItems[nIndex]->IsSelected();
617 }
618 
SetItemSelect(int32_t nIndex,bool bSelected)619 void CPWL_ListCtrl::SetItemSelect(int32_t nIndex, bool bSelected) {
620   if (IsValid(nIndex))
621     m_ListItems[nIndex]->SetSelect(bSelected);
622 }
623 
IsValid(int32_t nItemIndex) const624 bool CPWL_ListCtrl::IsValid(int32_t nItemIndex) const {
625   return fxcrt::IndexInBounds(m_ListItems, nItemIndex);
626 }
627 
GetItemText(int32_t nIndex) const628 WideString CPWL_ListCtrl::GetItemText(int32_t nIndex) const {
629   if (IsValid(nIndex))
630     return m_ListItems[nIndex]->GetText();
631   return WideString();
632 }
633