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