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
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_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
~CFWL_ListBox()45 CFWL_ListBox::~CFWL_ListBox() {}
46
GetClassID() const47 FWL_Type CFWL_ListBox::GetClassID() const {
48 return FWL_Type::ListBox;
49 }
50
Update()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
HitTest(const CFX_PointF & point)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
DrawWidget(CXFA_Graphics * pGraphics,const CFX_Matrix & matrix)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
SetThemeProvider(IFWL_ThemeProvider * pThemeProvider)119 void CFWL_ListBox::SetThemeProvider(IFWL_ThemeProvider* pThemeProvider) {
120 if (pThemeProvider)
121 m_pProperties->m_pThemeProvider = pThemeProvider;
122 }
123
CountSelItems()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
GetSelItem(int32_t nIndexSel)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
GetSelIndex(int32_t nIndex)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
SetSelItem(CFWL_ListItem * pItem,bool bSelect)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
GetListItem(CFWL_ListItem * pItem,uint32_t dwKeyCode)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
SetSelection(CFWL_ListItem * hStart,CFWL_ListItem * hEnd,bool bSelected)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
SetSelectionDirect(CFWL_ListItem * pItem,bool bSelect)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
IsMultiSelection() const239 bool CFWL_ListBox::IsMultiSelection() const {
240 return m_pProperties->m_dwStyleExes & FWL_STYLEEXT_LTB_MultiSelection;
241 }
242
IsItemSelected(CFWL_ListItem * pItem)243 bool CFWL_ListBox::IsItemSelected(CFWL_ListItem* pItem) {
244 return pItem && (pItem->GetStates() & FWL_ITEMSTATE_LTB_Selected) != 0;
245 }
246
ClearSelection()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
SelectAll()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
GetFocusedItem()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
SetFocusItem(CFWL_ListItem * pItem)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
GetItemAtPoint(const CFX_PointF & point)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
ScrollToVisible(CFWL_ListItem * pItem)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
DrawBkground(CXFA_Graphics * pGraphics,IFWL_ThemeProvider * pTheme,const CFX_Matrix * pMatrix)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(¶m);
373 }
374
DrawItems(CXFA_Graphics * pGraphics,IFWL_ThemeProvider * pTheme,const CFX_Matrix * pMatrix)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
DrawItem(CXFA_Graphics * pGraphics,IFWL_ThemeProvider * pTheme,CFWL_ListItem * pItem,int32_t Index,const CFX_RectF & rtItem,const CFX_Matrix * pMatrix)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
CalcSize(bool bAutoSize)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
UpdateItemSize(CFWL_ListItem * pItem,CFX_SizeF & size,float fWidth,float fItemHeight,bool bAutoSize) const581 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
GetMaxTextWidth()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
GetScrollWidth()609 float CFWL_ListBox::GetScrollWidth() {
610 IFWL_ThemeProvider* theme = GetAvailableTheme();
611 return theme ? theme->GetScrollBarWidth() : 0.0f;
612 }
613
CalcItemHeight()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
InitVerticalScrollBar()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
InitHorizontalScrollBar()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
IsShowScrollBar(bool bVert)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
OnProcessMessage(CFWL_Message * pMessage)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
OnProcessEvent(CFWL_Event * pEvent)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
OnDrawWidget(CXFA_Graphics * pGraphics,const CFX_Matrix & matrix)714 void CFWL_ListBox::OnDrawWidget(CXFA_Graphics* pGraphics,
715 const CFX_Matrix& matrix) {
716 DrawWidget(pGraphics, matrix);
717 }
718
OnFocusChanged(CFWL_Message * pMsg,bool bSet)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
OnLButtonDown(CFWL_MessageMouse * pMsg)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
OnLButtonUp(CFWL_MessageMouse * pMsg)775 void CFWL_ListBox::OnLButtonUp(CFWL_MessageMouse* pMsg) {
776 if (!m_bLButtonDown)
777 return;
778
779 m_bLButtonDown = false;
780 SetGrab(false);
781 }
782
OnMouseWheel(CFWL_MessageMouseWheel * pMsg)783 void CFWL_ListBox::OnMouseWheel(CFWL_MessageMouseWheel* pMsg) {
784 if (IsShowScrollBar(true))
785 m_pVertScrollBar->GetDelegate()->OnProcessMessage(pMsg);
786 }
787
OnKeyDown(CFWL_MessageKey * pMsg)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
OnVK(CFWL_ListItem * pItem,bool bShift,bool bCtrl)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
OnScroll(CFWL_ScrollBar * pScrollBar,CFWL_EventScroll::Code dwCode,float fPos)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
CountItems(const CFWL_Widget * pWidget) const890 int32_t CFWL_ListBox::CountItems(const CFWL_Widget* pWidget) const {
891 return pdfium::CollectionSize<int32_t>(m_ItemArray);
892 }
893
GetItem(const CFWL_Widget * pWidget,int32_t nIndex) const894 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
GetItemIndex(CFWL_Widget * pWidget,CFWL_ListItem * pItem)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
AddString(const WideStringView & wsAdd)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
RemoveAt(int32_t iIndex)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
DeleteString(CFWL_ListItem * pItem)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
DeleteAll()938 void CFWL_ListBox::DeleteAll() {
939 m_ItemArray.clear();
940 }
941