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