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