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(¶m);
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