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_impl.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/ptr_util.h"
17 #include "third_party/base/stl_util.h"
18 
Item()19 CPWL_ListCtrl::Item::Item() : m_pEdit(pdfium::MakeUnique<CPWL_EditImpl>()) {
20   m_pEdit->SetAlignmentV(1, true);
21   m_pEdit->Initialize();
22 }
23 
24 CPWL_ListCtrl::Item::~Item() = default;
25 
SetFontMap(IPVT_FontMap * pFontMap)26 void CPWL_ListCtrl::Item::SetFontMap(IPVT_FontMap* pFontMap) {
27   m_pEdit->SetFontMap(pFontMap);
28 }
29 
SetText(const WideString & text)30 void CPWL_ListCtrl::Item::SetText(const WideString& text) {
31   m_pEdit->SetText(text);
32 }
33 
SetFontSize(float fFontSize)34 void CPWL_ListCtrl::Item::SetFontSize(float fFontSize) {
35   m_pEdit->SetFontSize(fFontSize);
36 }
37 
GetItemHeight() const38 float CPWL_ListCtrl::Item::GetItemHeight() const {
39   return m_pEdit->GetContentRect().Height();
40 }
41 
GetFirstChar() const42 uint16_t CPWL_ListCtrl::Item::GetFirstChar() const {
43   CPVT_Word word;
44   CPWL_EditImpl_Iterator* pIterator = m_pEdit->GetIterator();
45   pIterator->SetAt(1);
46   pIterator->GetWord(word);
47   return word.Word;
48 }
49 
GetText() const50 WideString CPWL_ListCtrl::Item::GetText() const {
51   return m_pEdit->GetText();
52 }
53 
CPLST_Select()54 CPLST_Select::CPLST_Select() {}
55 
~CPLST_Select()56 CPLST_Select::~CPLST_Select() {}
57 
Add(int32_t nItemIndex)58 void CPLST_Select::Add(int32_t nItemIndex) {
59   m_Items[nItemIndex] = SELECTING;
60 }
61 
Add(int32_t nBeginIndex,int32_t nEndIndex)62 void CPLST_Select::Add(int32_t nBeginIndex, int32_t nEndIndex) {
63   if (nBeginIndex > nEndIndex)
64     std::swap(nBeginIndex, nEndIndex);
65 
66   for (int32_t i = nBeginIndex; i <= nEndIndex; ++i)
67     Add(i);
68 }
69 
Sub(int32_t nItemIndex)70 void CPLST_Select::Sub(int32_t nItemIndex) {
71   auto it = m_Items.find(nItemIndex);
72   if (it != m_Items.end())
73     it->second = DESELECTING;
74 }
75 
Sub(int32_t nBeginIndex,int32_t nEndIndex)76 void CPLST_Select::Sub(int32_t nBeginIndex, int32_t nEndIndex) {
77   if (nBeginIndex > nEndIndex)
78     std::swap(nBeginIndex, nEndIndex);
79 
80   for (int32_t i = nBeginIndex; i <= nEndIndex; ++i)
81     Sub(i);
82 }
83 
DeselectAll()84 void CPLST_Select::DeselectAll() {
85   for (auto& item : m_Items)
86     item.second = DESELECTING;
87 }
88 
Done()89 void CPLST_Select::Done() {
90   auto it = m_Items.begin();
91   while (it != m_Items.end()) {
92     if (it->second == DESELECTING)
93       it = m_Items.erase(it);
94     else
95       (it++)->second = NORMAL;
96   }
97 }
98 
CPWL_ListCtrl()99 CPWL_ListCtrl::CPWL_ListCtrl()
100     : m_pNotify(nullptr),
101       m_bNotifyFlag(false),
102       m_nSelItem(-1),
103       m_nFootIndex(-1),
104       m_bCtrlSel(false),
105       m_nCaretIndex(-1),
106       m_fFontSize(0.0f),
107       m_pFontMap(nullptr),
108       m_bMultiple(false) {}
109 
~CPWL_ListCtrl()110 CPWL_ListCtrl::~CPWL_ListCtrl() {
111   Clear();
112 }
113 
InToOut(const CFX_PointF & point) const114 CFX_PointF CPWL_ListCtrl::InToOut(const CFX_PointF& point) const {
115   CFX_FloatRect rcPlate = m_rcPlate;
116   return CFX_PointF(point.x - (m_ptScrollPos.x - rcPlate.left),
117                     point.y - (m_ptScrollPos.y - rcPlate.top));
118 }
119 
OutToIn(const CFX_PointF & point) const120 CFX_PointF CPWL_ListCtrl::OutToIn(const CFX_PointF& point) const {
121   CFX_FloatRect rcPlate = m_rcPlate;
122   return CFX_PointF(point.x + (m_ptScrollPos.x - rcPlate.left),
123                     point.y + (m_ptScrollPos.y - rcPlate.top));
124 }
125 
InToOut(const CFX_FloatRect & rect) const126 CFX_FloatRect CPWL_ListCtrl::InToOut(const CFX_FloatRect& rect) const {
127   CFX_PointF ptLeftBottom = InToOut(CFX_PointF(rect.left, rect.bottom));
128   CFX_PointF ptRightTop = InToOut(CFX_PointF(rect.right, rect.top));
129   return CFX_FloatRect(ptLeftBottom.x, ptLeftBottom.y, ptRightTop.x,
130                        ptRightTop.y);
131 }
132 
OutToIn(const CFX_FloatRect & rect) const133 CFX_FloatRect CPWL_ListCtrl::OutToIn(const CFX_FloatRect& rect) const {
134   CFX_PointF ptLeftBottom = OutToIn(CFX_PointF(rect.left, rect.bottom));
135   CFX_PointF ptRightTop = OutToIn(CFX_PointF(rect.right, rect.top));
136   return CFX_FloatRect(ptLeftBottom.x, ptLeftBottom.y, ptRightTop.x,
137                        ptRightTop.y);
138 }
139 
InnerToOuter(const CFX_PointF & point) const140 CFX_PointF CPWL_ListCtrl::InnerToOuter(const CFX_PointF& point) const {
141   return CFX_PointF(point.x + GetBTPoint().x, GetBTPoint().y - point.y);
142 }
143 
OuterToInner(const CFX_PointF & point) const144 CFX_PointF CPWL_ListCtrl::OuterToInner(const CFX_PointF& point) const {
145   return CFX_PointF(point.x - GetBTPoint().x, GetBTPoint().y - point.y);
146 }
147 
InnerToOuter(const CFX_FloatRect & rect) const148 CFX_FloatRect CPWL_ListCtrl::InnerToOuter(const CFX_FloatRect& rect) const {
149   CFX_PointF ptLeftTop = InnerToOuter(CFX_PointF(rect.left, rect.top));
150   CFX_PointF ptRightBottom = InnerToOuter(CFX_PointF(rect.right, rect.bottom));
151   return CFX_FloatRect(ptLeftTop.x, ptRightBottom.y, ptRightBottom.x,
152                        ptLeftTop.y);
153 }
154 
OuterToInner(const CFX_FloatRect & rect) const155 CFX_FloatRect CPWL_ListCtrl::OuterToInner(const CFX_FloatRect& rect) const {
156   CFX_PointF ptLeftTop = OuterToInner(CFX_PointF(rect.left, rect.top));
157   CFX_PointF ptRightBottom = OuterToInner(CFX_PointF(rect.right, rect.bottom));
158   return CFX_FloatRect(ptLeftTop.x, ptRightBottom.y, ptRightBottom.x,
159                        ptLeftTop.y);
160 }
161 
OnMouseDown(const CFX_PointF & point,bool bShift,bool bCtrl)162 void CPWL_ListCtrl::OnMouseDown(const CFX_PointF& point,
163                                 bool bShift,
164                                 bool bCtrl) {
165   int32_t nHitIndex = GetItemIndex(point);
166 
167   if (IsMultipleSel()) {
168     if (bCtrl) {
169       if (IsItemSelected(nHitIndex)) {
170         m_aSelItems.Sub(nHitIndex);
171         SelectItems();
172         m_bCtrlSel = false;
173       } else {
174         m_aSelItems.Add(nHitIndex);
175         SelectItems();
176         m_bCtrlSel = true;
177       }
178 
179       m_nFootIndex = nHitIndex;
180     } else if (bShift) {
181       m_aSelItems.DeselectAll();
182       m_aSelItems.Add(m_nFootIndex, nHitIndex);
183       SelectItems();
184     } else {
185       m_aSelItems.DeselectAll();
186       m_aSelItems.Add(nHitIndex);
187       SelectItems();
188 
189       m_nFootIndex = nHitIndex;
190     }
191 
192     SetCaret(nHitIndex);
193   } else {
194     SetSingleSelect(nHitIndex);
195   }
196 
197   if (!IsItemVisible(nHitIndex))
198     ScrollToListItem(nHitIndex);
199 }
200 
OnMouseMove(const CFX_PointF & point,bool bShift,bool bCtrl)201 void CPWL_ListCtrl::OnMouseMove(const CFX_PointF& point,
202                                 bool bShift,
203                                 bool bCtrl) {
204   int32_t nHitIndex = GetItemIndex(point);
205 
206   if (IsMultipleSel()) {
207     if (bCtrl) {
208       if (m_bCtrlSel)
209         m_aSelItems.Add(m_nFootIndex, nHitIndex);
210       else
211         m_aSelItems.Sub(m_nFootIndex, nHitIndex);
212 
213       SelectItems();
214     } else {
215       m_aSelItems.DeselectAll();
216       m_aSelItems.Add(m_nFootIndex, nHitIndex);
217       SelectItems();
218     }
219 
220     SetCaret(nHitIndex);
221   } else {
222     SetSingleSelect(nHitIndex);
223   }
224 
225   if (!IsItemVisible(nHitIndex))
226     ScrollToListItem(nHitIndex);
227 }
228 
OnVK(int32_t nItemIndex,bool bShift,bool bCtrl)229 void CPWL_ListCtrl::OnVK(int32_t nItemIndex, bool bShift, bool bCtrl) {
230   if (IsMultipleSel()) {
231     if (nItemIndex >= 0 && nItemIndex < GetCount()) {
232       if (bCtrl) {
233       } else if (bShift) {
234         m_aSelItems.DeselectAll();
235         m_aSelItems.Add(m_nFootIndex, nItemIndex);
236         SelectItems();
237       } else {
238         m_aSelItems.DeselectAll();
239         m_aSelItems.Add(nItemIndex);
240         SelectItems();
241         m_nFootIndex = nItemIndex;
242       }
243 
244       SetCaret(nItemIndex);
245     }
246   } else {
247     SetSingleSelect(nItemIndex);
248   }
249 
250   if (!IsItemVisible(nItemIndex))
251     ScrollToListItem(nItemIndex);
252 }
253 
OnVK_UP(bool bShift,bool bCtrl)254 void CPWL_ListCtrl::OnVK_UP(bool bShift, bool bCtrl) {
255   OnVK(IsMultipleSel() ? GetCaret() - 1 : GetSelect() - 1, bShift, bCtrl);
256 }
257 
OnVK_DOWN(bool bShift,bool bCtrl)258 void CPWL_ListCtrl::OnVK_DOWN(bool bShift, bool bCtrl) {
259   OnVK(IsMultipleSel() ? GetCaret() + 1 : GetSelect() + 1, bShift, bCtrl);
260 }
261 
OnVK_LEFT(bool bShift,bool bCtrl)262 void CPWL_ListCtrl::OnVK_LEFT(bool bShift, bool bCtrl) {
263   OnVK(0, bShift, bCtrl);
264 }
265 
OnVK_RIGHT(bool bShift,bool bCtrl)266 void CPWL_ListCtrl::OnVK_RIGHT(bool bShift, bool bCtrl) {
267   OnVK(GetCount() - 1, bShift, bCtrl);
268 }
269 
OnVK_HOME(bool bShift,bool bCtrl)270 void CPWL_ListCtrl::OnVK_HOME(bool bShift, bool bCtrl) {
271   OnVK(0, bShift, bCtrl);
272 }
273 
OnVK_END(bool bShift,bool bCtrl)274 void CPWL_ListCtrl::OnVK_END(bool bShift, bool bCtrl) {
275   OnVK(GetCount() - 1, bShift, bCtrl);
276 }
277 
OnChar(uint16_t nChar,bool bShift,bool bCtrl)278 bool CPWL_ListCtrl::OnChar(uint16_t nChar, bool bShift, bool bCtrl) {
279   int32_t nIndex = GetLastSelected();
280   int32_t nFindIndex = FindNext(nIndex, nChar);
281 
282   if (nFindIndex != nIndex) {
283     OnVK(nFindIndex, bShift, bCtrl);
284     return true;
285   }
286   return false;
287 }
288 
SetPlateRect(const CFX_FloatRect & rect)289 void CPWL_ListCtrl::SetPlateRect(const CFX_FloatRect& rect) {
290   m_rcPlate = rect;
291   m_ptScrollPos.x = rect.left;
292   SetScrollPos(CFX_PointF(rect.left, rect.top));
293   ReArrange(0);
294   InvalidateItem(-1);
295 }
296 
GetItemRect(int32_t nIndex) const297 CFX_FloatRect CPWL_ListCtrl::GetItemRect(int32_t nIndex) const {
298   return InToOut(GetItemRectInternal(nIndex));
299 }
300 
GetItemRectInternal(int32_t nIndex) const301 CFX_FloatRect CPWL_ListCtrl::GetItemRectInternal(int32_t nIndex) const {
302   if (!IsValid(nIndex))
303     return CFX_FloatRect();
304 
305   CFX_FloatRect rcItem = m_ListItems[nIndex]->GetRect();
306   rcItem.left = 0.0f;
307   rcItem.right = m_rcPlate.Width();
308   return InnerToOuter(rcItem);
309 }
310 
AddString(const WideString & str)311 void CPWL_ListCtrl::AddString(const WideString& str) {
312   AddItem(str);
313   ReArrange(GetCount() - 1);
314 }
315 
SetMultipleSelect(int32_t nItemIndex,bool bSelected)316 void CPWL_ListCtrl::SetMultipleSelect(int32_t nItemIndex, bool bSelected) {
317   if (!IsValid(nItemIndex))
318     return;
319 
320   if (bSelected != IsItemSelected(nItemIndex)) {
321     if (bSelected) {
322       SetItemSelect(nItemIndex, true);
323       InvalidateItem(nItemIndex);
324     } else {
325       SetItemSelect(nItemIndex, false);
326       InvalidateItem(nItemIndex);
327     }
328   }
329 }
330 
SetSingleSelect(int32_t nItemIndex)331 void CPWL_ListCtrl::SetSingleSelect(int32_t nItemIndex) {
332   if (!IsValid(nItemIndex))
333     return;
334 
335   if (m_nSelItem != nItemIndex) {
336     if (m_nSelItem >= 0) {
337       SetItemSelect(m_nSelItem, false);
338       InvalidateItem(m_nSelItem);
339     }
340 
341     SetItemSelect(nItemIndex, true);
342     InvalidateItem(nItemIndex);
343     m_nSelItem = nItemIndex;
344   }
345 }
346 
SetCaret(int32_t nItemIndex)347 void CPWL_ListCtrl::SetCaret(int32_t nItemIndex) {
348   if (!IsValid(nItemIndex))
349     return;
350 
351   if (IsMultipleSel()) {
352     int32_t nOldIndex = m_nCaretIndex;
353 
354     if (nOldIndex != nItemIndex) {
355       m_nCaretIndex = nItemIndex;
356       InvalidateItem(nOldIndex);
357       InvalidateItem(nItemIndex);
358     }
359   }
360 }
361 
InvalidateItem(int32_t nItemIndex)362 void CPWL_ListCtrl::InvalidateItem(int32_t nItemIndex) {
363   if (m_pNotify) {
364     if (nItemIndex == -1) {
365       if (!m_bNotifyFlag) {
366         m_bNotifyFlag = true;
367         CFX_FloatRect rcRefresh = m_rcPlate;
368         m_pNotify->IOnInvalidateRect(&rcRefresh);
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 
380         m_pNotify->IOnInvalidateRect(&rcRefresh);
381         m_bNotifyFlag = false;
382       }
383     }
384   }
385 }
386 
SelectItems()387 void CPWL_ListCtrl::SelectItems() {
388   for (const auto& item : m_aSelItems) {
389     if (item.second != CPLST_Select::NORMAL)
390       SetMultipleSelect(item.first, item.second == CPLST_Select::SELECTING);
391   }
392   m_aSelItems.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_aSelItems.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 (IsFloatSmaller(rcItemCtrl.bottom, rcPlate.bottom)) {
433     if (IsFloatSmaller(rcItemCtrl.top, rcPlate.top)) {
434       SetScrollPosY(rcItem.bottom + rcPlate.Height());
435     }
436   } else if (IsFloatBigger(rcItemCtrl.top, rcPlate.top)) {
437     if (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->IOnSetScrollInfoY(rcPlate.bottom, rcPlate.top,
451                                    rcContent.bottom, rcContent.top,
452                                    GetFirstHeight(), 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 (!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 (IsFloatSmaller(fy - rcPlate.Height(), rcContent.bottom)) {
471         fy = rcContent.bottom + rcPlate.Height();
472       } else if (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->IOnSetScrollPosY(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   SetContentRect(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 
Clear()528 void CPWL_ListCtrl::Clear() {
529   m_ListItems.clear();
530   InvalidateItem(-1);
531 }
532 
Cancel()533 void CPWL_ListCtrl::Cancel() {
534   m_aSelItems.DeselectAll();
535 }
536 
GetItemIndex(const CFX_PointF & point) const537 int32_t CPWL_ListCtrl::GetItemIndex(const CFX_PointF& point) const {
538   CFX_PointF pt = OuterToInner(OutToIn(point));
539   bool bFirst = true;
540   bool bLast = true;
541   for (const auto& pListItem : m_ListItems) {
542     CFX_FloatRect rcListItem = pListItem->GetRect();
543     if (IsFloatBigger(pt.y, rcListItem.top))
544       bFirst = false;
545     if (IsFloatSmaller(pt.y, rcListItem.bottom))
546       bLast = false;
547     if (pt.y >= rcListItem.top && pt.y < rcListItem.bottom)
548       return &pListItem - &m_ListItems.front();
549   }
550   if (bFirst)
551     return 0;
552   if (bLast)
553     return GetCount() - 1;
554   return -1;
555 }
556 
GetText() const557 WideString CPWL_ListCtrl::GetText() const {
558   if (IsMultipleSel())
559     return GetItemText(m_nCaretIndex);
560   return GetItemText(m_nSelItem);
561 }
562 
AddItem(const WideString & str)563 void CPWL_ListCtrl::AddItem(const WideString& str) {
564   auto pListItem = pdfium::MakeUnique<Item>();
565   pListItem->SetFontMap(m_pFontMap.Get());
566   pListItem->SetFontSize(m_fFontSize);
567   pListItem->SetText(str);
568   m_ListItems.push_back(std::move(pListItem));
569 }
570 
GetItemEdit(int32_t nIndex) const571 CPWL_EditImpl* CPWL_ListCtrl::GetItemEdit(int32_t nIndex) const {
572   if (!IsValid(nIndex))
573     return nullptr;
574   return m_ListItems[nIndex]->GetEdit();
575 }
576 
GetCount() const577 int32_t CPWL_ListCtrl::GetCount() const {
578   return pdfium::CollectionSize<int32_t>(m_ListItems);
579 }
580 
GetFirstHeight() const581 float CPWL_ListCtrl::GetFirstHeight() const {
582   if (m_ListItems.empty())
583     return 1.0f;
584   return m_ListItems.front()->GetItemHeight();
585 }
586 
GetFirstSelected() const587 int32_t CPWL_ListCtrl::GetFirstSelected() const {
588   int32_t i = 0;
589   for (const auto& pListItem : m_ListItems) {
590     if (pListItem->IsSelected())
591       return i;
592     ++i;
593   }
594   return -1;
595 }
596 
GetLastSelected() const597 int32_t CPWL_ListCtrl::GetLastSelected() const {
598   for (auto iter = m_ListItems.rbegin(); iter != m_ListItems.rend(); ++iter) {
599     if ((*iter)->IsSelected())
600       return &*iter - &m_ListItems.front();
601   }
602   return -1;
603 }
604 
FindNext(int32_t nIndex,wchar_t nChar) const605 int32_t CPWL_ListCtrl::FindNext(int32_t nIndex, wchar_t nChar) const {
606   int32_t nCircleIndex = nIndex;
607   int32_t sz = GetCount();
608   for (int32_t i = 0; i < sz; i++) {
609     nCircleIndex++;
610     if (nCircleIndex >= sz)
611       nCircleIndex = 0;
612 
613     if (Item* pListItem = m_ListItems[nCircleIndex].get()) {
614       if (FXSYS_towupper(pListItem->GetFirstChar()) == FXSYS_towupper(nChar))
615         return nCircleIndex;
616     }
617   }
618 
619   return nCircleIndex;
620 }
621 
IsItemSelected(int32_t nIndex) const622 bool CPWL_ListCtrl::IsItemSelected(int32_t nIndex) const {
623   return IsValid(nIndex) && m_ListItems[nIndex]->IsSelected();
624 }
625 
SetItemSelect(int32_t nIndex,bool bSelected)626 void CPWL_ListCtrl::SetItemSelect(int32_t nIndex, bool bSelected) {
627   if (IsValid(nIndex))
628     m_ListItems[nIndex]->SetSelect(bSelected);
629 }
630 
IsValid(int32_t nItemIndex) const631 bool CPWL_ListCtrl::IsValid(int32_t nItemIndex) const {
632   return pdfium::IndexInBounds(m_ListItems, nItemIndex);
633 }
634 
GetItemText(int32_t nIndex) const635 WideString CPWL_ListCtrl::GetItemText(int32_t nIndex) const {
636   if (IsValid(nIndex))
637     return m_ListItems[nIndex]->GetText();
638   return WideString();
639 }
640