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 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 46 CFWL_ListBox::~CFWL_ListBox() {} 47 48 FWL_Type CFWL_ListBox::GetClassID() const { 49 return FWL_Type::ListBox; 50 } 51 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 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 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 122 void CFWL_ListBox::SetThemeProvider(IFWL_ThemeProvider* pThemeProvider) { 123 if (pThemeProvider) 124 m_pProperties->m_pThemeProvider = pThemeProvider; 125 } 126 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 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 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 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 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 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 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 242 bool CFWL_ListBox::IsMultiSelection() const { 243 return m_pProperties->m_dwStyleExes & FWL_STYLEEXT_LTB_MultiSelection; 244 } 245 246 bool CFWL_ListBox::IsItemSelected(CFWL_ListItem* pItem) { 247 return pItem && (pItem->GetStates() & FWL_ITEMSTATE_LTB_Selected) != 0; 248 } 249 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 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 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 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 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 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 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 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 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 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 585 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 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 613 FX_FLOAT CFWL_ListBox::GetScrollWidth() { 614 IFWL_ThemeProvider* theme = GetAvailableTheme(); 615 return theme ? theme->GetScrollBarWidth() : 0.0f; 616 } 617 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 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 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 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 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 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 718 void CFWL_ListBox::OnDrawWidget(CFX_Graphics* pGraphics, 719 const CFX_Matrix* pMatrix) { 720 DrawWidget(pGraphics, pMatrix); 721 } 722 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 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 779 void CFWL_ListBox::OnLButtonUp(CFWL_MessageMouse* pMsg) { 780 if (!m_bLButtonDown) 781 return; 782 783 m_bLButtonDown = false; 784 SetGrab(false); 785 } 786 787 void CFWL_ListBox::OnMouseWheel(CFWL_MessageMouseWheel* pMsg) { 788 if (IsShowScrollBar(true)) 789 m_pVertScrollBar->GetDelegate()->OnProcessMessage(pMsg); 790 } 791 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 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 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 894 int32_t CFWL_ListBox::CountItems(const CFWL_Widget* pWidget) const { 895 return pdfium::CollectionSize<int32_t>(m_ItemArray); 896 } 897 898 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 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 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 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 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 942 void CFWL_ListBox::DeleteAll() { 943 m_ItemArray.clear(); 944 } 945