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