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