Home | History | Annotate | Download | only in fwl
      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 "xfa/fwl/cfwl_listbox.h"
      8 
      9 #include <algorithm>
     10 #include <memory>
     11 #include <utility>
     12 
     13 #include "third_party/base/ptr_util.h"
     14 #include "third_party/base/stl_util.h"
     15 #include "xfa/fde/cfde_textout.h"
     16 #include "xfa/fwl/cfwl_app.h"
     17 #include "xfa/fwl/cfwl_messagekey.h"
     18 #include "xfa/fwl/cfwl_messagemouse.h"
     19 #include "xfa/fwl/cfwl_messagemousewheel.h"
     20 #include "xfa/fwl/cfwl_themebackground.h"
     21 #include "xfa/fwl/cfwl_themepart.h"
     22 #include "xfa/fwl/cfwl_themetext.h"
     23 #include "xfa/fwl/ifwl_themeprovider.h"
     24 
     25 namespace {
     26 
     27 const int kItemTextMargin = 2;
     28 
     29 }  // namespace
     30 
     31 CFWL_ListBox::CFWL_ListBox(const CFWL_App* app,
     32                            std::unique_ptr<CFWL_WidgetProperties> properties,
     33                            CFWL_Widget* pOuter)
     34     : CFWL_Widget(app, std::move(properties), pOuter),
     35       m_iTTOAligns(FDE_TextAlignment::kTopLeft),
     36       m_hAnchor(nullptr),
     37       m_fScorllBarWidth(0),
     38       m_bLButtonDown(false),
     39       m_pScrollBarTP(nullptr) {
     40   m_rtClient.Reset();
     41   m_rtConent.Reset();
     42   m_rtStatic.Reset();
     43 }
     44 
     45 CFWL_ListBox::~CFWL_ListBox() {}
     46 
     47 FWL_Type CFWL_ListBox::GetClassID() const {
     48   return FWL_Type::ListBox;
     49 }
     50 
     51 void CFWL_ListBox::Update() {
     52   if (IsLocked())
     53     return;
     54   if (!m_pProperties->m_pThemeProvider)
     55     m_pProperties->m_pThemeProvider = GetAvailableTheme();
     56 
     57   switch (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_LTB_AlignMask) {
     58     case FWL_STYLEEXT_LTB_LeftAlign: {
     59       m_iTTOAligns = FDE_TextAlignment::kCenterLeft;
     60       break;
     61     }
     62     case FWL_STYLEEXT_LTB_RightAlign: {
     63       m_iTTOAligns = FDE_TextAlignment::kCenterRight;
     64       break;
     65     }
     66     case FWL_STYLEEXT_LTB_CenterAlign:
     67     default: {
     68       m_iTTOAligns = FDE_TextAlignment::kCenter;
     69       break;
     70     }
     71   }
     72   m_dwTTOStyles.single_line_ = true;
     73   m_fScorllBarWidth = GetScrollWidth();
     74   CalcSize(false);
     75 }
     76 
     77 FWL_WidgetHit CFWL_ListBox::HitTest(const CFX_PointF& point) {
     78   if (IsShowScrollBar(false)) {
     79     CFX_RectF rect = m_pHorzScrollBar->GetWidgetRect();
     80     if (rect.Contains(point))
     81       return FWL_WidgetHit::HScrollBar;
     82   }
     83   if (IsShowScrollBar(true)) {
     84     CFX_RectF rect = m_pVertScrollBar->GetWidgetRect();
     85     if (rect.Contains(point))
     86       return FWL_WidgetHit::VScrollBar;
     87   }
     88   if (m_rtClient.Contains(point))
     89     return FWL_WidgetHit::Client;
     90   return FWL_WidgetHit::Unknown;
     91 }
     92 
     93 void CFWL_ListBox::DrawWidget(CXFA_Graphics* pGraphics,
     94                               const CFX_Matrix& matrix) {
     95   if (!pGraphics)
     96     return;
     97   if (!m_pProperties->m_pThemeProvider)
     98     return;
     99 
    100   IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
    101   pGraphics->SaveGraphState();
    102   if (HasBorder())
    103     DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
    104 
    105   CFX_RectF rtClip(m_rtConent);
    106   if (IsShowScrollBar(false))
    107     rtClip.height -= m_fScorllBarWidth;
    108   if (IsShowScrollBar(true))
    109     rtClip.width -= m_fScorllBarWidth;
    110 
    111   pGraphics->SetClipRect(matrix.TransformRect(rtClip));
    112   if ((m_pProperties->m_dwStyles & FWL_WGTSTYLE_NoBackground) == 0)
    113     DrawBkground(pGraphics, pTheme, &matrix);
    114 
    115   DrawItems(pGraphics, pTheme, &matrix);
    116   pGraphics->RestoreGraphState();
    117 }
    118 
    119 void CFWL_ListBox::SetThemeProvider(IFWL_ThemeProvider* pThemeProvider) {
    120   if (pThemeProvider)
    121     m_pProperties->m_pThemeProvider = pThemeProvider;
    122 }
    123 
    124 int32_t CFWL_ListBox::CountSelItems() {
    125   int32_t iRet = 0;
    126   int32_t iCount = CountItems(this);
    127   for (int32_t i = 0; i < iCount; i++) {
    128     CFWL_ListItem* pItem = GetItem(this, i);
    129     if (!pItem)
    130       continue;
    131     if (pItem->GetStates() & FWL_ITEMSTATE_LTB_Selected)
    132       iRet++;
    133   }
    134   return iRet;
    135 }
    136 
    137 CFWL_ListItem* CFWL_ListBox::GetSelItem(int32_t nIndexSel) {
    138   int32_t idx = GetSelIndex(nIndexSel);
    139   if (idx < 0)
    140     return nullptr;
    141   return GetItem(this, idx);
    142 }
    143 
    144 int32_t CFWL_ListBox::GetSelIndex(int32_t nIndex) {
    145   int32_t index = 0;
    146   int32_t iCount = CountItems(this);
    147   for (int32_t i = 0; i < iCount; i++) {
    148     CFWL_ListItem* pItem = GetItem(this, i);
    149     if (!pItem)
    150       return -1;
    151     if (pItem->GetStates() & FWL_ITEMSTATE_LTB_Selected) {
    152       if (index == nIndex)
    153         return i;
    154       index++;
    155     }
    156   }
    157   return -1;
    158 }
    159 
    160 void CFWL_ListBox::SetSelItem(CFWL_ListItem* pItem, bool bSelect) {
    161   if (!pItem) {
    162     if (bSelect) {
    163       SelectAll();
    164     } else {
    165       ClearSelection();
    166       SetFocusItem(nullptr);
    167     }
    168     return;
    169   }
    170   if (IsMultiSelection())
    171     SetSelectionDirect(pItem, bSelect);
    172   else
    173     SetSelection(pItem, pItem, bSelect);
    174 }
    175 
    176 CFWL_ListItem* CFWL_ListBox::GetListItem(CFWL_ListItem* pItem,
    177                                          uint32_t dwKeyCode) {
    178   CFWL_ListItem* hRet = nullptr;
    179   switch (dwKeyCode) {
    180     case FWL_VKEY_Up:
    181     case FWL_VKEY_Down:
    182     case FWL_VKEY_Home:
    183     case FWL_VKEY_End: {
    184       const bool bUp = dwKeyCode == FWL_VKEY_Up;
    185       const bool bDown = dwKeyCode == FWL_VKEY_Down;
    186       const bool bHome = dwKeyCode == FWL_VKEY_Home;
    187       int32_t iDstItem = -1;
    188       if (bUp || bDown) {
    189         int32_t index = GetItemIndex(this, pItem);
    190         iDstItem = dwKeyCode == FWL_VKEY_Up ? index - 1 : index + 1;
    191       } else if (bHome) {
    192         iDstItem = 0;
    193       } else {
    194         int32_t iCount = CountItems(this);
    195         iDstItem = iCount - 1;
    196       }
    197       hRet = GetItem(this, iDstItem);
    198       break;
    199     }
    200     default:
    201       break;
    202   }
    203   return hRet;
    204 }
    205 
    206 void CFWL_ListBox::SetSelection(CFWL_ListItem* hStart,
    207                                 CFWL_ListItem* hEnd,
    208                                 bool bSelected) {
    209   int32_t iStart = GetItemIndex(this, hStart);
    210   int32_t iEnd = GetItemIndex(this, hEnd);
    211   if (iStart > iEnd) {
    212     int32_t iTemp = iStart;
    213     iStart = iEnd;
    214     iEnd = iTemp;
    215   }
    216   if (bSelected) {
    217     int32_t iCount = CountItems(this);
    218     for (int32_t i = 0; i < iCount; i++) {
    219       CFWL_ListItem* pItem = GetItem(this, i);
    220       SetSelectionDirect(pItem, false);
    221     }
    222   }
    223   for (; iStart <= iEnd; iStart++) {
    224     CFWL_ListItem* pItem = GetItem(this, iStart);
    225     SetSelectionDirect(pItem, bSelected);
    226   }
    227 }
    228 
    229 void CFWL_ListBox::SetSelectionDirect(CFWL_ListItem* pItem, bool bSelect) {
    230   if (!pItem)
    231     return;
    232 
    233   uint32_t dwOldStyle = pItem->GetStates();
    234   bSelect ? dwOldStyle |= FWL_ITEMSTATE_LTB_Selected
    235           : dwOldStyle &= ~FWL_ITEMSTATE_LTB_Selected;
    236   pItem->SetStates(dwOldStyle);
    237 }
    238 
    239 bool CFWL_ListBox::IsMultiSelection() const {
    240   return m_pProperties->m_dwStyleExes & FWL_STYLEEXT_LTB_MultiSelection;
    241 }
    242 
    243 bool CFWL_ListBox::IsItemSelected(CFWL_ListItem* pItem) {
    244   return pItem && (pItem->GetStates() & FWL_ITEMSTATE_LTB_Selected) != 0;
    245 }
    246 
    247 void CFWL_ListBox::ClearSelection() {
    248   bool bMulti = IsMultiSelection();
    249   int32_t iCount = CountItems(this);
    250   for (int32_t i = 0; i < iCount; i++) {
    251     CFWL_ListItem* pItem = GetItem(this, i);
    252     if (!pItem)
    253       continue;
    254     if (!(pItem->GetStates() & FWL_ITEMSTATE_LTB_Selected))
    255       continue;
    256     SetSelectionDirect(pItem, false);
    257     if (!bMulti)
    258       return;
    259   }
    260 }
    261 
    262 void CFWL_ListBox::SelectAll() {
    263   if (!IsMultiSelection())
    264     return;
    265 
    266   int32_t iCount = CountItems(this);
    267   if (iCount <= 0)
    268     return;
    269 
    270   CFWL_ListItem* pItemStart = GetItem(this, 0);
    271   CFWL_ListItem* pItemEnd = GetItem(this, iCount - 1);
    272   SetSelection(pItemStart, pItemEnd, false);
    273 }
    274 
    275 CFWL_ListItem* CFWL_ListBox::GetFocusedItem() {
    276   int32_t iCount = CountItems(this);
    277   for (int32_t i = 0; i < iCount; i++) {
    278     CFWL_ListItem* pItem = GetItem(this, i);
    279     if (!pItem)
    280       return nullptr;
    281     if (pItem->GetStates() & FWL_ITEMSTATE_LTB_Focused)
    282       return pItem;
    283   }
    284   return nullptr;
    285 }
    286 
    287 void CFWL_ListBox::SetFocusItem(CFWL_ListItem* pItem) {
    288   CFWL_ListItem* hFocus = GetFocusedItem();
    289   if (pItem == hFocus)
    290     return;
    291 
    292   if (hFocus) {
    293     uint32_t dwStyle = hFocus->GetStates();
    294     dwStyle &= ~FWL_ITEMSTATE_LTB_Focused;
    295     hFocus->SetStates(dwStyle);
    296   }
    297   if (pItem) {
    298     uint32_t dwStyle = pItem->GetStates();
    299     dwStyle |= FWL_ITEMSTATE_LTB_Focused;
    300     pItem->SetStates(dwStyle);
    301   }
    302 }
    303 
    304 CFWL_ListItem* CFWL_ListBox::GetItemAtPoint(const CFX_PointF& point) {
    305   CFX_PointF pos = point - m_rtConent.TopLeft();
    306   float fPosX = 0.0f;
    307   if (m_pHorzScrollBar)
    308     fPosX = m_pHorzScrollBar->GetPos();
    309 
    310   float fPosY = 0.0;
    311   if (m_pVertScrollBar)
    312     fPosY = m_pVertScrollBar->GetPos();
    313 
    314   int32_t nCount = CountItems(this);
    315   for (int32_t i = 0; i < nCount; i++) {
    316     CFWL_ListItem* pItem = GetItem(this, i);
    317     if (!pItem)
    318       continue;
    319 
    320     CFX_RectF rtItem = pItem->GetRect();
    321     rtItem.Offset(-fPosX, -fPosY);
    322     if (rtItem.Contains(pos))
    323       return pItem;
    324   }
    325   return nullptr;
    326 }
    327 
    328 bool CFWL_ListBox::ScrollToVisible(CFWL_ListItem* pItem) {
    329   if (!m_pVertScrollBar)
    330     return false;
    331 
    332   CFX_RectF rtItem = pItem ? pItem->GetRect() : CFX_RectF();
    333   bool bScroll = false;
    334   float fPosY = m_pVertScrollBar->GetPos();
    335   rtItem.Offset(0, -fPosY + m_rtConent.top);
    336   if (rtItem.top < m_rtConent.top) {
    337     fPosY += rtItem.top - m_rtConent.top;
    338     bScroll = true;
    339   } else if (rtItem.bottom() > m_rtConent.bottom()) {
    340     fPosY += rtItem.bottom() - m_rtConent.bottom();
    341     bScroll = true;
    342   }
    343   if (!bScroll)
    344     return false;
    345 
    346   m_pVertScrollBar->SetPos(fPosY);
    347   m_pVertScrollBar->SetTrackPos(fPosY);
    348   RepaintRect(m_rtClient);
    349   return true;
    350 }
    351 
    352 void CFWL_ListBox::DrawBkground(CXFA_Graphics* pGraphics,
    353                                 IFWL_ThemeProvider* pTheme,
    354                                 const CFX_Matrix* pMatrix) {
    355   if (!pGraphics)
    356     return;
    357   if (!pTheme)
    358     return;
    359 
    360   CFWL_ThemeBackground param;
    361   param.m_pWidget = this;
    362   param.m_iPart = CFWL_Part::Background;
    363   param.m_dwStates = 0;
    364   param.m_pGraphics = pGraphics;
    365   param.m_matrix.Concat(*pMatrix);
    366   param.m_rtPart = m_rtClient;
    367   if (IsShowScrollBar(false) && IsShowScrollBar(true))
    368     param.m_pData = &m_rtStatic;
    369   if (!IsEnabled())
    370     param.m_dwStates = CFWL_PartState_Disabled;
    371 
    372   pTheme->DrawBackground(&param);
    373 }
    374 
    375 void CFWL_ListBox::DrawItems(CXFA_Graphics* pGraphics,
    376                              IFWL_ThemeProvider* pTheme,
    377                              const CFX_Matrix* pMatrix) {
    378   float fPosX = 0.0f;
    379   if (m_pHorzScrollBar)
    380     fPosX = m_pHorzScrollBar->GetPos();
    381 
    382   float fPosY = 0.0f;
    383   if (m_pVertScrollBar)
    384     fPosY = m_pVertScrollBar->GetPos();
    385 
    386   CFX_RectF rtView(m_rtConent);
    387   if (m_pHorzScrollBar)
    388     rtView.height -= m_fScorllBarWidth;
    389   if (m_pVertScrollBar)
    390     rtView.width -= m_fScorllBarWidth;
    391 
    392   int32_t iCount = CountItems(this);
    393   for (int32_t i = 0; i < iCount; i++) {
    394     CFWL_ListItem* pItem = GetItem(this, i);
    395     if (!pItem)
    396       continue;
    397 
    398     CFX_RectF rtItem = pItem->GetRect();
    399     rtItem.Offset(m_rtConent.left - fPosX, m_rtConent.top - fPosY);
    400     if (rtItem.bottom() < m_rtConent.top)
    401       continue;
    402     if (rtItem.top >= m_rtConent.bottom())
    403       break;
    404     DrawItem(pGraphics, pTheme, pItem, i, rtItem, pMatrix);
    405   }
    406 }
    407 
    408 void CFWL_ListBox::DrawItem(CXFA_Graphics* pGraphics,
    409                             IFWL_ThemeProvider* pTheme,
    410                             CFWL_ListItem* pItem,
    411                             int32_t Index,
    412                             const CFX_RectF& rtItem,
    413                             const CFX_Matrix* pMatrix) {
    414   uint32_t dwItemStyles = pItem ? pItem->GetStates() : 0;
    415   uint32_t dwPartStates = CFWL_PartState_Normal;
    416   if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
    417     dwPartStates = CFWL_PartState_Disabled;
    418   else if (dwItemStyles & FWL_ITEMSTATE_LTB_Selected)
    419     dwPartStates = CFWL_PartState_Selected;
    420 
    421   if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused &&
    422       dwItemStyles & FWL_ITEMSTATE_LTB_Focused) {
    423     dwPartStates |= CFWL_PartState_Focused;
    424   }
    425 
    426   CFWL_ThemeBackground bg_param;
    427   bg_param.m_pWidget = this;
    428   bg_param.m_iPart = CFWL_Part::ListItem;
    429   bg_param.m_dwStates = dwPartStates;
    430   bg_param.m_pGraphics = pGraphics;
    431   bg_param.m_matrix.Concat(*pMatrix);
    432   bg_param.m_rtPart = rtItem;
    433   bg_param.m_bMaximize = true;
    434   CFX_RectF rtFocus(rtItem);
    435   bg_param.m_pData = &rtFocus;
    436   if (m_pVertScrollBar && !m_pHorzScrollBar &&
    437       (dwPartStates & CFWL_PartState_Focused)) {
    438     bg_param.m_rtPart.left += 1;
    439     bg_param.m_rtPart.width -= (m_fScorllBarWidth + 1);
    440     rtFocus.Deflate(0.5, 0.5, 1 + m_fScorllBarWidth, 1);
    441   }
    442   pTheme->DrawBackground(&bg_param);
    443 
    444   if (!pItem)
    445     return;
    446 
    447   WideString wsText = pItem->GetText();
    448   if (wsText.GetLength() <= 0)
    449     return;
    450 
    451   CFX_RectF rtText(rtItem);
    452   rtText.Deflate(kItemTextMargin, kItemTextMargin);
    453 
    454   CFWL_ThemeText textParam;
    455   textParam.m_pWidget = this;
    456   textParam.m_iPart = CFWL_Part::ListItem;
    457   textParam.m_dwStates = dwPartStates;
    458   textParam.m_pGraphics = pGraphics;
    459   textParam.m_matrix.Concat(*pMatrix);
    460   textParam.m_rtPart = rtText;
    461   textParam.m_wsText = wsText;
    462   textParam.m_dwTTOStyles = m_dwTTOStyles;
    463   textParam.m_iTTOAlign = m_iTTOAligns;
    464   textParam.m_bMaximize = true;
    465   pTheme->DrawText(&textParam);
    466 }
    467 
    468 CFX_SizeF CFWL_ListBox::CalcSize(bool bAutoSize) {
    469   if (!m_pProperties->m_pThemeProvider)
    470     return CFX_SizeF();
    471 
    472   m_rtClient = GetClientRect();
    473   m_rtConent = m_rtClient;
    474   CFX_RectF rtUIMargin;
    475   if (!m_pOuter) {
    476     CFWL_ThemePart part;
    477     part.m_pWidget = this;
    478     IFWL_ThemeProvider* theme = GetAvailableTheme();
    479     CFX_RectF pUIMargin = theme ? theme->GetUIMargin(&part) : CFX_RectF();
    480     m_rtConent.Deflate(pUIMargin.left, pUIMargin.top, pUIMargin.width,
    481                        pUIMargin.height);
    482   }
    483 
    484   float fWidth = GetMaxTextWidth();
    485   fWidth += 2 * kItemTextMargin;
    486   if (!bAutoSize) {
    487     float fActualWidth = m_rtClient.width - rtUIMargin.left - rtUIMargin.width;
    488     fWidth = std::max(fWidth, fActualWidth);
    489   }
    490   m_fItemHeight = CalcItemHeight();
    491 
    492   int32_t iCount = CountItems(this);
    493   CFX_SizeF fs;
    494   for (int32_t i = 0; i < iCount; i++) {
    495     CFWL_ListItem* htem = GetItem(this, i);
    496     UpdateItemSize(htem, fs, fWidth, m_fItemHeight, bAutoSize);
    497   }
    498   if (bAutoSize)
    499     return fs;
    500 
    501   float iHeight = m_rtClient.height;
    502   bool bShowVertScr = false;
    503   bool bShowHorzScr = false;
    504   if (!bShowVertScr && (m_pProperties->m_dwStyles & FWL_WGTSTYLE_VScroll))
    505     bShowVertScr = (fs.height > iHeight);
    506 
    507   CFX_SizeF szRange;
    508   if (bShowVertScr) {
    509     if (!m_pVertScrollBar)
    510       InitVerticalScrollBar();
    511 
    512     CFX_RectF rtScrollBar(m_rtClient.right() - m_fScorllBarWidth,
    513                           m_rtClient.top, m_fScorllBarWidth,
    514                           m_rtClient.height - 1);
    515     if (bShowHorzScr)
    516       rtScrollBar.height -= m_fScorllBarWidth;
    517 
    518     m_pVertScrollBar->SetWidgetRect(rtScrollBar);
    519     szRange.width = 0;
    520     szRange.height = std::max(fs.height - m_rtConent.height, m_fItemHeight);
    521 
    522     m_pVertScrollBar->SetRange(szRange.width, szRange.height);
    523     m_pVertScrollBar->SetPageSize(rtScrollBar.height * 9 / 10);
    524     m_pVertScrollBar->SetStepSize(m_fItemHeight);
    525 
    526     float fPos =
    527         pdfium::clamp(m_pVertScrollBar->GetPos(), 0.0f, szRange.height);
    528     m_pVertScrollBar->SetPos(fPos);
    529     m_pVertScrollBar->SetTrackPos(fPos);
    530     if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_LTB_ShowScrollBarFocus) ==
    531             0 ||
    532         (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused)) {
    533       m_pVertScrollBar->RemoveStates(FWL_WGTSTATE_Invisible);
    534     }
    535     m_pVertScrollBar->Update();
    536   } else if (m_pVertScrollBar) {
    537     m_pVertScrollBar->SetPos(0);
    538     m_pVertScrollBar->SetTrackPos(0);
    539     m_pVertScrollBar->SetStates(FWL_WGTSTATE_Invisible);
    540   }
    541   if (bShowHorzScr) {
    542     if (!m_pHorzScrollBar)
    543       InitHorizontalScrollBar();
    544 
    545     CFX_RectF rtScrollBar(m_rtClient.left,
    546                           m_rtClient.bottom() - m_fScorllBarWidth,
    547                           m_rtClient.width, m_fScorllBarWidth);
    548     if (bShowVertScr)
    549       rtScrollBar.width -= m_fScorllBarWidth;
    550 
    551     m_pHorzScrollBar->SetWidgetRect(rtScrollBar);
    552     szRange.width = 0;
    553     szRange.height = fs.width - rtScrollBar.width;
    554     m_pHorzScrollBar->SetRange(szRange.width, szRange.height);
    555     m_pHorzScrollBar->SetPageSize(fWidth * 9 / 10);
    556     m_pHorzScrollBar->SetStepSize(fWidth / 10);
    557 
    558     float fPos =
    559         pdfium::clamp(m_pHorzScrollBar->GetPos(), 0.0f, szRange.height);
    560     m_pHorzScrollBar->SetPos(fPos);
    561     m_pHorzScrollBar->SetTrackPos(fPos);
    562     if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_LTB_ShowScrollBarFocus) ==
    563             0 ||
    564         (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused)) {
    565       m_pHorzScrollBar->RemoveStates(FWL_WGTSTATE_Invisible);
    566     }
    567     m_pHorzScrollBar->Update();
    568   } else if (m_pHorzScrollBar) {
    569     m_pHorzScrollBar->SetPos(0);
    570     m_pHorzScrollBar->SetTrackPos(0);
    571     m_pHorzScrollBar->SetStates(FWL_WGTSTATE_Invisible);
    572   }
    573   if (bShowVertScr && bShowHorzScr) {
    574     m_rtStatic = CFX_RectF(m_rtClient.right() - m_fScorllBarWidth,
    575                            m_rtClient.bottom() - m_fScorllBarWidth,
    576                            m_fScorllBarWidth, m_fScorllBarWidth);
    577   }
    578   return fs;
    579 }
    580 
    581 void CFWL_ListBox::UpdateItemSize(CFWL_ListItem* pItem,
    582                                   CFX_SizeF& size,
    583                                   float fWidth,
    584                                   float fItemHeight,
    585                                   bool bAutoSize) const {
    586   if (!bAutoSize && pItem) {
    587     CFX_RectF rtItem(0, size.height, fWidth, fItemHeight);
    588     pItem->SetRect(rtItem);
    589   }
    590   size.width = fWidth;
    591   size.height += fItemHeight;
    592 }
    593 
    594 float CFWL_ListBox::GetMaxTextWidth() {
    595   float fRet = 0.0f;
    596   int32_t iCount = CountItems(this);
    597   for (int32_t i = 0; i < iCount; i++) {
    598     CFWL_ListItem* pItem = GetItem(this, i);
    599     if (!pItem)
    600       continue;
    601 
    602     CFX_SizeF sz =
    603         CalcTextSize(pItem->GetText(), m_pProperties->m_pThemeProvider, false);
    604     fRet = std::max(fRet, sz.width);
    605   }
    606   return fRet;
    607 }
    608 
    609 float CFWL_ListBox::GetScrollWidth() {
    610   IFWL_ThemeProvider* theme = GetAvailableTheme();
    611   return theme ? theme->GetScrollBarWidth() : 0.0f;
    612 }
    613 
    614 float CFWL_ListBox::CalcItemHeight() {
    615   IFWL_ThemeProvider* theme = GetAvailableTheme();
    616   CFWL_ThemePart part;
    617   part.m_pWidget = this;
    618   return (theme ? theme->GetFontSize(&part) : 20.0f) + 2 * kItemTextMargin;
    619 }
    620 
    621 void CFWL_ListBox::InitVerticalScrollBar() {
    622   if (m_pVertScrollBar)
    623     return;
    624 
    625   auto prop = pdfium::MakeUnique<CFWL_WidgetProperties>();
    626   prop->m_dwStyleExes = FWL_STYLEEXT_SCB_Vert;
    627   prop->m_dwStates = FWL_WGTSTATE_Invisible;
    628   prop->m_pParent = this;
    629   prop->m_pThemeProvider = m_pScrollBarTP;
    630   m_pVertScrollBar = pdfium::MakeUnique<CFWL_ScrollBar>(m_pOwnerApp.Get(),
    631                                                         std::move(prop), this);
    632 }
    633 
    634 void CFWL_ListBox::InitHorizontalScrollBar() {
    635   if (m_pHorzScrollBar)
    636     return;
    637 
    638   auto prop = pdfium::MakeUnique<CFWL_WidgetProperties>();
    639   prop->m_dwStyleExes = FWL_STYLEEXT_SCB_Horz;
    640   prop->m_dwStates = FWL_WGTSTATE_Invisible;
    641   prop->m_pParent = this;
    642   prop->m_pThemeProvider = m_pScrollBarTP;
    643   m_pHorzScrollBar = pdfium::MakeUnique<CFWL_ScrollBar>(m_pOwnerApp.Get(),
    644                                                         std::move(prop), this);
    645 }
    646 
    647 bool CFWL_ListBox::IsShowScrollBar(bool bVert) {
    648   CFWL_ScrollBar* pScrollbar =
    649       bVert ? m_pVertScrollBar.get() : m_pHorzScrollBar.get();
    650   if (!pScrollbar || (pScrollbar->GetStates() & FWL_WGTSTATE_Invisible))
    651     return false;
    652   return !(m_pProperties->m_dwStyleExes &
    653            FWL_STYLEEXT_LTB_ShowScrollBarFocus) ||
    654          (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused);
    655 }
    656 
    657 void CFWL_ListBox::OnProcessMessage(CFWL_Message* pMessage) {
    658   if (!pMessage)
    659     return;
    660   if (!IsEnabled())
    661     return;
    662 
    663   switch (pMessage->GetType()) {
    664     case CFWL_Message::Type::SetFocus:
    665       OnFocusChanged(pMessage, true);
    666       break;
    667     case CFWL_Message::Type::KillFocus:
    668       OnFocusChanged(pMessage, false);
    669       break;
    670     case CFWL_Message::Type::Mouse: {
    671       CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
    672       switch (pMsg->m_dwCmd) {
    673         case FWL_MouseCommand::LeftButtonDown:
    674           OnLButtonDown(pMsg);
    675           break;
    676         case FWL_MouseCommand::LeftButtonUp:
    677           OnLButtonUp(pMsg);
    678           break;
    679         default:
    680           break;
    681       }
    682       break;
    683     }
    684     case CFWL_Message::Type::MouseWheel:
    685       OnMouseWheel(static_cast<CFWL_MessageMouseWheel*>(pMessage));
    686       break;
    687     case CFWL_Message::Type::Key: {
    688       CFWL_MessageKey* pMsg = static_cast<CFWL_MessageKey*>(pMessage);
    689       if (pMsg->m_dwCmd == FWL_KeyCommand::KeyDown)
    690         OnKeyDown(pMsg);
    691       break;
    692     }
    693     default:
    694       break;
    695   }
    696   CFWL_Widget::OnProcessMessage(pMessage);
    697 }
    698 
    699 void CFWL_ListBox::OnProcessEvent(CFWL_Event* pEvent) {
    700   if (!pEvent)
    701     return;
    702   if (pEvent->GetType() != CFWL_Event::Type::Scroll)
    703     return;
    704 
    705   CFWL_Widget* pSrcTarget = pEvent->m_pSrcTarget;
    706   if ((pSrcTarget == m_pVertScrollBar.get() && m_pVertScrollBar) ||
    707       (pSrcTarget == m_pHorzScrollBar.get() && m_pHorzScrollBar)) {
    708     CFWL_EventScroll* pScrollEvent = static_cast<CFWL_EventScroll*>(pEvent);
    709     OnScroll(static_cast<CFWL_ScrollBar*>(pSrcTarget),
    710              pScrollEvent->m_iScrollCode, pScrollEvent->m_fPos);
    711   }
    712 }
    713 
    714 void CFWL_ListBox::OnDrawWidget(CXFA_Graphics* pGraphics,
    715                                 const CFX_Matrix& matrix) {
    716   DrawWidget(pGraphics, matrix);
    717 }
    718 
    719 void CFWL_ListBox::OnFocusChanged(CFWL_Message* pMsg, bool bSet) {
    720   if (GetStylesEx() & FWL_STYLEEXT_LTB_ShowScrollBarFocus) {
    721     if (m_pVertScrollBar) {
    722       if (bSet)
    723         m_pVertScrollBar->RemoveStates(FWL_WGTSTATE_Invisible);
    724       else
    725         m_pVertScrollBar->SetStates(FWL_WGTSTATE_Invisible);
    726     }
    727     if (m_pHorzScrollBar) {
    728       if (bSet)
    729         m_pHorzScrollBar->RemoveStates(FWL_WGTSTATE_Invisible);
    730       else
    731         m_pHorzScrollBar->SetStates(FWL_WGTSTATE_Invisible);
    732     }
    733   }
    734   if (bSet)
    735     m_pProperties->m_dwStates |= (FWL_WGTSTATE_Focused);
    736   else
    737     m_pProperties->m_dwStates &= ~(FWL_WGTSTATE_Focused);
    738 
    739   RepaintRect(m_rtClient);
    740 }
    741 
    742 void CFWL_ListBox::OnLButtonDown(CFWL_MessageMouse* pMsg) {
    743   m_bLButtonDown = true;
    744   if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0)
    745     SetFocus(true);
    746 
    747   CFWL_ListItem* pItem = GetItemAtPoint(pMsg->m_pos);
    748   if (!pItem)
    749     return;
    750 
    751   if (IsMultiSelection()) {
    752     if (pMsg->m_dwFlags & FWL_KEYFLAG_Ctrl) {
    753       bool bSelected = IsItemSelected(pItem);
    754       SetSelectionDirect(pItem, !bSelected);
    755       m_hAnchor = pItem;
    756     } else if (pMsg->m_dwFlags & FWL_KEYFLAG_Shift) {
    757       if (m_hAnchor)
    758         SetSelection(m_hAnchor, pItem, true);
    759       else
    760         SetSelectionDirect(pItem, true);
    761     } else {
    762       SetSelection(pItem, pItem, true);
    763       m_hAnchor = pItem;
    764     }
    765   } else {
    766     SetSelection(pItem, pItem, true);
    767   }
    768 
    769   SetFocusItem(pItem);
    770   ScrollToVisible(pItem);
    771   SetGrab(true);
    772   RepaintRect(m_rtClient);
    773 }
    774 
    775 void CFWL_ListBox::OnLButtonUp(CFWL_MessageMouse* pMsg) {
    776   if (!m_bLButtonDown)
    777     return;
    778 
    779   m_bLButtonDown = false;
    780   SetGrab(false);
    781 }
    782 
    783 void CFWL_ListBox::OnMouseWheel(CFWL_MessageMouseWheel* pMsg) {
    784   if (IsShowScrollBar(true))
    785     m_pVertScrollBar->GetDelegate()->OnProcessMessage(pMsg);
    786 }
    787 
    788 void CFWL_ListBox::OnKeyDown(CFWL_MessageKey* pMsg) {
    789   uint32_t dwKeyCode = pMsg->m_dwKeyCode;
    790   switch (dwKeyCode) {
    791     case FWL_VKEY_Tab:
    792     case FWL_VKEY_Up:
    793     case FWL_VKEY_Down:
    794     case FWL_VKEY_Home:
    795     case FWL_VKEY_End: {
    796       CFWL_ListItem* pItem = GetFocusedItem();
    797       pItem = GetListItem(pItem, dwKeyCode);
    798       bool bShift = !!(pMsg->m_dwFlags & FWL_KEYFLAG_Shift);
    799       bool bCtrl = !!(pMsg->m_dwFlags & FWL_KEYFLAG_Ctrl);
    800       OnVK(pItem, bShift, bCtrl);
    801       break;
    802     }
    803     default:
    804       break;
    805   }
    806 }
    807 
    808 void CFWL_ListBox::OnVK(CFWL_ListItem* pItem, bool bShift, bool bCtrl) {
    809   if (!pItem)
    810     return;
    811 
    812   if (IsMultiSelection()) {
    813     if (bCtrl) {
    814       // Do nothing.
    815     } else if (bShift) {
    816       if (m_hAnchor)
    817         SetSelection(m_hAnchor, pItem, true);
    818       else
    819         SetSelectionDirect(pItem, true);
    820     } else {
    821       SetSelection(pItem, pItem, true);
    822       m_hAnchor = pItem;
    823     }
    824   } else {
    825     SetSelection(pItem, pItem, true);
    826   }
    827 
    828   SetFocusItem(pItem);
    829   ScrollToVisible(pItem);
    830 
    831   RepaintRect(CFX_RectF(0, 0, m_pProperties->m_rtWidget.width,
    832                         m_pProperties->m_rtWidget.height));
    833 }
    834 
    835 bool CFWL_ListBox::OnScroll(CFWL_ScrollBar* pScrollBar,
    836                             CFWL_EventScroll::Code dwCode,
    837                             float fPos) {
    838   CFX_SizeF fs;
    839   pScrollBar->GetRange(&fs.width, &fs.height);
    840   float iCurPos = pScrollBar->GetPos();
    841   float fStep = pScrollBar->GetStepSize();
    842   switch (dwCode) {
    843     case CFWL_EventScroll::Code::Min: {
    844       fPos = fs.width;
    845       break;
    846     }
    847     case CFWL_EventScroll::Code::Max: {
    848       fPos = fs.height;
    849       break;
    850     }
    851     case CFWL_EventScroll::Code::StepBackward: {
    852       fPos -= fStep;
    853       if (fPos < fs.width + fStep / 2)
    854         fPos = fs.width;
    855       break;
    856     }
    857     case CFWL_EventScroll::Code::StepForward: {
    858       fPos += fStep;
    859       if (fPos > fs.height - fStep / 2)
    860         fPos = fs.height;
    861       break;
    862     }
    863     case CFWL_EventScroll::Code::PageBackward: {
    864       fPos -= pScrollBar->GetPageSize();
    865       if (fPos < fs.width)
    866         fPos = fs.width;
    867       break;
    868     }
    869     case CFWL_EventScroll::Code::PageForward: {
    870       fPos += pScrollBar->GetPageSize();
    871       if (fPos > fs.height)
    872         fPos = fs.height;
    873       break;
    874     }
    875     case CFWL_EventScroll::Code::Pos:
    876     case CFWL_EventScroll::Code::TrackPos:
    877     case CFWL_EventScroll::Code::None:
    878       break;
    879     case CFWL_EventScroll::Code::EndScroll:
    880       return false;
    881   }
    882   if (iCurPos != fPos) {
    883     pScrollBar->SetPos(fPos);
    884     pScrollBar->SetTrackPos(fPos);
    885     RepaintRect(m_rtClient);
    886   }
    887   return true;
    888 }
    889 
    890 int32_t CFWL_ListBox::CountItems(const CFWL_Widget* pWidget) const {
    891   return pdfium::CollectionSize<int32_t>(m_ItemArray);
    892 }
    893 
    894 CFWL_ListItem* CFWL_ListBox::GetItem(const CFWL_Widget* pWidget,
    895                                      int32_t nIndex) const {
    896   if (nIndex < 0 || nIndex >= CountItems(pWidget))
    897     return nullptr;
    898   return m_ItemArray[nIndex].get();
    899 }
    900 
    901 int32_t CFWL_ListBox::GetItemIndex(CFWL_Widget* pWidget, CFWL_ListItem* pItem) {
    902   auto it =
    903       std::find_if(m_ItemArray.begin(), m_ItemArray.end(),
    904                    [pItem](const std::unique_ptr<CFWL_ListItem>& candidate) {
    905                      return candidate.get() == pItem;
    906                    });
    907   return it != m_ItemArray.end() ? it - m_ItemArray.begin() : -1;
    908 }
    909 
    910 CFWL_ListItem* CFWL_ListBox::AddString(const WideStringView& wsAdd) {
    911   m_ItemArray.emplace_back(
    912       pdfium::MakeUnique<CFWL_ListItem>(WideString(wsAdd)));
    913   return m_ItemArray.back().get();
    914 }
    915 
    916 void CFWL_ListBox::RemoveAt(int32_t iIndex) {
    917   if (iIndex < 0 || static_cast<size_t>(iIndex) >= m_ItemArray.size())
    918     return;
    919   m_ItemArray.erase(m_ItemArray.begin() + iIndex);
    920 }
    921 
    922 void CFWL_ListBox::DeleteString(CFWL_ListItem* pItem) {
    923   int32_t nIndex = GetItemIndex(this, pItem);
    924   if (nIndex < 0 || static_cast<size_t>(nIndex) >= m_ItemArray.size())
    925     return;
    926 
    927   int32_t iSel = nIndex + 1;
    928   if (iSel >= CountItems(this))
    929     iSel = nIndex - 1;
    930   if (iSel >= 0) {
    931     if (CFWL_ListItem* item = GetItem(this, iSel))
    932       item->SetStates(item->GetStates() | FWL_ITEMSTATE_LTB_Selected);
    933   }
    934 
    935   m_ItemArray.erase(m_ItemArray.begin() + nIndex);
    936 }
    937 
    938 void CFWL_ListBox::DeleteAll() {
    939   m_ItemArray.clear();
    940 }
    941