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 "../../include/pdfwindow/PDFWindow.h" 8 #include "../../include/pdfwindow/PWL_Wnd.h" 9 #include "../../include/pdfwindow/PWL_EditCtrl.h" 10 #include "../../include/pdfwindow/PWL_Edit.h" 11 #include "../../include/pdfwindow/PWL_ListBox.h" 12 #include "../../include/pdfwindow/PWL_ComboBox.h" 13 #include "../../include/pdfwindow/PWL_Utils.h" 14 15 #define PWLCB_DEFAULTFONTSIZE 12.0f 16 17 #define IsFloatZero(f) ((f) < 0.0001 && (f) > -0.0001) 18 #define IsFloatBigger(fa,fb) ((fa) > (fb) && !IsFloatZero((fa) - (fb))) 19 #define IsFloatSmaller(fa,fb) ((fa) < (fb) && !IsFloatZero((fa) - (fb))) 20 #define IsFloatEqual(fa,fb) IsFloatZero((fa)-(fb)) 21 22 23 /* ---------------------------- CPWL_CBListBox ---------------------------- */ 24 25 FX_BOOL CPWL_CBListBox::OnLButtonUp(const CPDF_Point & point, FX_DWORD nFlag) 26 { 27 CPWL_Wnd::OnLButtonUp(point,nFlag); 28 29 if (m_bMouseDown) 30 { 31 ReleaseCapture(); 32 m_bMouseDown = FALSE; 33 34 if (this->ClientHitTest(point)) 35 { 36 if (CPWL_Wnd * pParent = GetParentWindow()) 37 { 38 pParent->OnNotify(this,PNM_LBUTTONUP,0,PWL_MAKEDWORD(point.x,point.y)); 39 } 40 41 FX_BOOL bExit = FALSE; 42 OnNotifySelChanged(FALSE,bExit, nFlag); 43 if (bExit) return FALSE; 44 } 45 } 46 47 return TRUE; 48 } 49 50 FX_BOOL CPWL_CBListBox::OnKeyDown(FX_WORD nChar, FX_BOOL & bExit, FX_DWORD nFlag) 51 { 52 if (!m_pList) return FALSE; 53 54 switch (nChar) 55 { 56 default: 57 return FALSE; 58 case FWL_VKEY_Up: 59 case FWL_VKEY_Down: 60 case FWL_VKEY_Home: 61 case FWL_VKEY_Left: 62 case FWL_VKEY_End: 63 case FWL_VKEY_Right: 64 break; 65 } 66 67 switch (nChar) 68 { 69 case FWL_VKEY_Up: 70 m_pList->OnVK_UP(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag)); 71 break; 72 case FWL_VKEY_Down: 73 m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag)); 74 break; 75 case FWL_VKEY_Home: 76 m_pList->OnVK_HOME(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag)); 77 break; 78 case FWL_VKEY_Left: 79 m_pList->OnVK_LEFT(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag)); 80 break; 81 case FWL_VKEY_End: 82 m_pList->OnVK_END(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag)); 83 break; 84 case FWL_VKEY_Right: 85 m_pList->OnVK_RIGHT(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag)); 86 break; 87 case FWL_VKEY_Delete: 88 break; 89 } 90 91 OnNotifySelChanged(TRUE,bExit, nFlag); 92 93 return TRUE; 94 } 95 96 FX_BOOL CPWL_CBListBox::OnChar(FX_WORD nChar, FX_BOOL & bExit, FX_DWORD nFlag) 97 { 98 if (!m_pList) return FALSE; 99 100 if (!m_pList->OnChar(nChar,IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag))) return FALSE; 101 102 if (CPWL_ComboBox* pComboBox = (CPWL_ComboBox*)GetParentWindow()) 103 { 104 pComboBox->SetSelectText(); 105 } 106 107 OnNotifySelChanged(TRUE,bExit,nFlag); 108 109 return TRUE; 110 } 111 112 /* ---------------------------- CPWL_CBButton ---------------------------- */ 113 114 void CPWL_CBButton::GetThisAppearanceStream(CFX_ByteTextBuf & sAppStream) 115 { 116 CPWL_Wnd::GetThisAppearanceStream(sAppStream); 117 118 CPDF_Rect rectWnd = CPWL_Wnd::GetWindowRect(); 119 120 if (IsVisible() && !rectWnd.IsEmpty()) 121 { 122 CFX_ByteTextBuf sButton; 123 124 CPDF_Point ptCenter = this->GetCenterPoint(); 125 126 CPDF_Point pt1(ptCenter.x - PWL_CBBUTTON_TRIANGLE_HALFLEN,ptCenter.y + PWL_CBBUTTON_TRIANGLE_HALFLEN * 0.5f); 127 CPDF_Point pt2(ptCenter.x + PWL_CBBUTTON_TRIANGLE_HALFLEN,ptCenter.y + PWL_CBBUTTON_TRIANGLE_HALFLEN * 0.5f); 128 CPDF_Point pt3(ptCenter.x,ptCenter.y - PWL_CBBUTTON_TRIANGLE_HALFLEN * 0.5f); 129 130 if (IsFloatBigger(rectWnd.right - rectWnd.left,PWL_CBBUTTON_TRIANGLE_HALFLEN * 2) 131 && 132 IsFloatBigger(rectWnd.top - rectWnd.bottom,PWL_CBBUTTON_TRIANGLE_HALFLEN) 133 ) 134 { 135 sButton << "0 g\n"; 136 sButton << pt1.x << " " << pt1.y << " m\n"; 137 sButton << pt2.x << " " << pt2.y << " l\n"; 138 sButton << pt3.x << " " << pt3.y << " l\n"; 139 sButton << pt1.x << " " << pt1.y << " l f\n"; 140 141 sAppStream << "q\n" << sButton << "Q\n"; 142 } 143 } 144 } 145 146 void CPWL_CBButton::DrawThisAppearance(CFX_RenderDevice* pDevice, CPDF_Matrix* pUser2Device) 147 { 148 CPWL_Wnd::DrawThisAppearance(pDevice,pUser2Device); 149 150 CPDF_Rect rectWnd = CPWL_Wnd::GetWindowRect(); 151 152 if (IsVisible() && !rectWnd.IsEmpty()) 153 { 154 CPDF_Point ptCenter = this->GetCenterPoint(); 155 156 CPDF_Point pt1(ptCenter.x - PWL_CBBUTTON_TRIANGLE_HALFLEN,ptCenter.y + PWL_CBBUTTON_TRIANGLE_HALFLEN * 0.5f); 157 CPDF_Point pt2(ptCenter.x + PWL_CBBUTTON_TRIANGLE_HALFLEN,ptCenter.y + PWL_CBBUTTON_TRIANGLE_HALFLEN * 0.5f); 158 CPDF_Point pt3(ptCenter.x,ptCenter.y - PWL_CBBUTTON_TRIANGLE_HALFLEN * 0.5f); 159 160 if (IsFloatBigger(rectWnd.right - rectWnd.left,PWL_CBBUTTON_TRIANGLE_HALFLEN * 2) 161 && 162 IsFloatBigger(rectWnd.top - rectWnd.bottom,PWL_CBBUTTON_TRIANGLE_HALFLEN) 163 ) 164 { 165 CFX_PathData path; 166 167 path.SetPointCount(4); 168 path.SetPoint(0, pt1.x, pt1.y, FXPT_MOVETO); 169 path.SetPoint(1, pt2.x, pt2.y, FXPT_LINETO); 170 path.SetPoint(2, pt3.x, pt3.y, FXPT_LINETO); 171 path.SetPoint(3, pt1.x, pt1.y, FXPT_LINETO); 172 173 pDevice->DrawPath(&path, pUser2Device, NULL, 174 CPWL_Utils::PWLColorToFXColor(PWL_DEFAULT_BLACKCOLOR,GetTransparency()), 175 0, FXFILL_ALTERNATE); 176 } 177 } 178 } 179 180 FX_BOOL CPWL_CBButton::OnLButtonDown(const CPDF_Point & point, FX_DWORD nFlag) 181 { 182 CPWL_Wnd::OnLButtonDown(point,nFlag); 183 184 SetCapture(); 185 186 if (CPWL_Wnd * pParent = GetParentWindow()) 187 { 188 pParent->OnNotify(this,PNM_LBUTTONDOWN,0,PWL_MAKEDWORD(point.x,point.y)); 189 } 190 191 return TRUE; 192 } 193 194 FX_BOOL CPWL_CBButton::OnLButtonUp(const CPDF_Point & point, FX_DWORD nFlag) 195 { 196 CPWL_Wnd::OnLButtonUp(point, nFlag); 197 198 ReleaseCapture(); 199 200 return TRUE; 201 } 202 203 /* ---------------------------- CPWL_ComboBox ---------------------------- */ 204 205 CPWL_ComboBox::CPWL_ComboBox() : m_pEdit(NULL), 206 m_pButton(NULL), 207 m_pList(NULL), 208 m_bPopup(FALSE), 209 m_nPopupWhere(0), 210 m_nSelectItem(-1), 211 m_pFillerNotify(NULL) 212 { 213 } 214 215 CFX_ByteString CPWL_ComboBox::GetClassName() const 216 { 217 return "CPWL_ComboBox"; 218 } 219 220 void CPWL_ComboBox::OnCreate(PWL_CREATEPARAM & cp) 221 { 222 cp.dwFlags &= ~PWS_HSCROLL; 223 cp.dwFlags &= ~PWS_VSCROLL; 224 } 225 226 void CPWL_ComboBox::SetFocus() 227 { 228 if (m_pEdit) 229 m_pEdit->SetFocus(); 230 } 231 232 void CPWL_ComboBox::KillFocus() 233 { 234 SetPopup(FALSE); 235 CPWL_Wnd::KillFocus(); 236 } 237 238 CFX_WideString CPWL_ComboBox::GetText() const 239 { 240 if (m_pEdit) 241 { 242 return m_pEdit->GetText(); 243 } 244 return CFX_WideString(); 245 } 246 247 void CPWL_ComboBox::SetText(FX_LPCWSTR text) 248 { 249 if (m_pEdit) 250 m_pEdit->SetText(text); 251 } 252 253 void CPWL_ComboBox::AddString(FX_LPCWSTR string) 254 { 255 if (m_pList) 256 m_pList->AddString(string); 257 } 258 259 FX_INT32 CPWL_ComboBox::GetSelect() const 260 { 261 return m_nSelectItem; 262 } 263 264 void CPWL_ComboBox::SetSelect(FX_INT32 nItemIndex) 265 { 266 if (m_pList) 267 m_pList->Select(nItemIndex); 268 269 m_pEdit->SetText(m_pList->GetText()); 270 271 m_nSelectItem = nItemIndex; 272 } 273 274 void CPWL_ComboBox::SetEditSel(FX_INT32 nStartChar,FX_INT32 nEndChar) 275 { 276 if (m_pEdit) 277 { 278 m_pEdit->SetSel(nStartChar,nEndChar); 279 } 280 } 281 282 void CPWL_ComboBox::GetEditSel(FX_INT32 & nStartChar, FX_INT32 & nEndChar) const 283 { 284 nStartChar = -1; 285 nEndChar = -1; 286 287 if (m_pEdit) 288 { 289 m_pEdit->GetSel(nStartChar,nEndChar); 290 } 291 } 292 293 void CPWL_ComboBox::Clear() 294 { 295 if (m_pEdit) 296 { 297 m_pEdit->Clear(); 298 } 299 } 300 301 void CPWL_ComboBox::CreateChildWnd(const PWL_CREATEPARAM & cp) 302 { 303 CreateEdit(cp); 304 CreateButton(cp); 305 CreateListBox(cp); 306 } 307 308 void CPWL_ComboBox::CreateEdit(const PWL_CREATEPARAM & cp) 309 { 310 if (!m_pEdit) 311 { 312 m_pEdit = new CPWL_CBEdit; 313 m_pEdit->AttachFFLData(m_pFormFiller); 314 315 PWL_CREATEPARAM ecp = cp; 316 ecp.pParentWnd = this; 317 ecp.dwFlags = PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PES_CENTER | PES_AUTOSCROLL | PES_UNDO; 318 319 if (HasFlag(PWS_AUTOFONTSIZE)) 320 ecp.dwFlags |= PWS_AUTOFONTSIZE; 321 322 if (!HasFlag(PCBS_ALLOWCUSTOMTEXT)) 323 ecp.dwFlags |= PWS_READONLY; 324 325 ecp.rcRectWnd = CPDF_Rect(0,0,0,0); 326 ecp.dwBorderWidth = 0; 327 ecp.nBorderStyle = PBS_SOLID; 328 329 m_pEdit->Create(ecp); 330 } 331 } 332 333 void CPWL_ComboBox::CreateButton(const PWL_CREATEPARAM & cp) 334 { 335 if (!m_pButton) 336 { 337 m_pButton = new CPWL_CBButton; 338 339 PWL_CREATEPARAM bcp = cp; 340 bcp.pParentWnd = this; 341 bcp.dwFlags = PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PWS_BACKGROUND; 342 bcp.sBackgroundColor = PWL_SCROLLBAR_BKCOLOR; 343 bcp.sBorderColor = PWL_DEFAULT_BLACKCOLOR; 344 bcp.dwBorderWidth = 2; 345 bcp.nBorderStyle = PBS_BEVELED; 346 bcp.eCursorType = FXCT_ARROW; 347 348 m_pButton->Create(bcp); 349 } 350 } 351 352 void CPWL_ComboBox::CreateListBox(const PWL_CREATEPARAM & cp) 353 { 354 if (!m_pList) 355 { 356 m_pList = new CPWL_CBListBox; 357 m_pList->AttachFFLData(m_pFormFiller); 358 PWL_CREATEPARAM lcp = cp; 359 lcp.pParentWnd = this; 360 lcp.dwFlags = PWS_CHILD | PWS_BORDER | PWS_BACKGROUND | PLBS_HOVERSEL | PWS_VSCROLL; 361 lcp.nBorderStyle = PBS_SOLID; 362 lcp.dwBorderWidth = 1; 363 lcp.eCursorType = FXCT_ARROW; 364 lcp.rcRectWnd = CPDF_Rect(0,0,0,0); 365 366 if (cp.dwFlags & PWS_AUTOFONTSIZE) 367 lcp.fFontSize = PWLCB_DEFAULTFONTSIZE; 368 else 369 lcp.fFontSize = cp.fFontSize; 370 371 if (cp.sBorderColor.nColorType == COLORTYPE_TRANSPARENT) 372 lcp.sBorderColor = PWL_DEFAULT_BLACKCOLOR; 373 374 if (cp.sBackgroundColor.nColorType == COLORTYPE_TRANSPARENT) 375 lcp.sBackgroundColor = PWL_DEFAULT_WHITECOLOR; 376 377 m_pList->Create(lcp); 378 } 379 } 380 381 void CPWL_ComboBox::RePosChildWnd() 382 { 383 CPDF_Rect rcClient = GetClientRect(); 384 385 if (m_bPopup) 386 { 387 CPDF_Rect rclient = GetClientRect(); 388 CPDF_Rect rcButton = rclient; 389 CPDF_Rect rcEdit = rcClient; 390 CPDF_Rect rcList = CPWL_Wnd::GetWindowRect(); 391 392 FX_FLOAT fOldWindowHeight = m_rcOldWindow.Height(); 393 FX_FLOAT fOldClientHeight = fOldWindowHeight - GetBorderWidth() * 2; 394 395 switch (m_nPopupWhere) 396 { 397 case 0: 398 rcButton.left = rcButton.right - PWL_COMBOBOX_BUTTON_WIDTH; 399 400 if (rcButton.left < rclient.left) 401 rcButton.left = rclient.left; 402 403 rcButton.bottom = rcButton.top - fOldClientHeight; 404 405 rcEdit.right = rcButton.left - 1.0f; 406 407 if (rcEdit.left < rclient.left) 408 rcEdit.left = rclient.left; 409 410 if (rcEdit.right < rcEdit.left) 411 rcEdit.right = rcEdit.left; 412 413 rcEdit.bottom = rcEdit.top - fOldClientHeight; 414 415 rcList.top -= fOldWindowHeight; 416 417 break; 418 case 1: 419 rcButton.left = rcButton.right - PWL_COMBOBOX_BUTTON_WIDTH; 420 421 if (rcButton.left < rclient.left) 422 rcButton.left = rclient.left; 423 424 rcButton.top = rcButton.bottom + fOldClientHeight; 425 426 rcEdit.right = rcButton.left - 1.0f; 427 428 if (rcEdit.left < rclient.left) 429 rcEdit.left = rclient.left; 430 431 if (rcEdit.right < rcEdit.left) 432 rcEdit.right = rcEdit.left; 433 434 rcEdit.top = rcEdit.bottom + fOldClientHeight; 435 436 rcList.bottom += fOldWindowHeight; 437 438 break; 439 } 440 441 if (m_pButton) 442 m_pButton->Move(rcButton,TRUE,FALSE); 443 444 if (m_pEdit) 445 m_pEdit->Move(rcEdit,TRUE,FALSE); 446 447 if (m_pList) 448 { 449 m_pList->SetVisible(TRUE); 450 m_pList->Move(rcList,TRUE,FALSE); 451 m_pList->ScrollToListItem(m_nSelectItem); 452 } 453 } 454 else 455 { 456 CPDF_Rect rcButton = rcClient; 457 458 rcButton.left = rcButton.right - PWL_COMBOBOX_BUTTON_WIDTH; 459 460 if (rcButton.left < rcClient.left) 461 rcButton.left = rcClient.left; 462 463 if (m_pButton) 464 m_pButton->Move(rcButton,TRUE,FALSE); 465 466 CPDF_Rect rcEdit = rcClient; 467 rcEdit.right = rcButton.left - 1.0f; 468 469 if (rcEdit.left < rcClient.left) 470 rcEdit.left = rcClient.left; 471 472 if (rcEdit.right < rcEdit.left) 473 rcEdit.right = rcEdit.left; 474 475 if (m_pEdit) 476 m_pEdit->Move(rcEdit,TRUE,FALSE); 477 478 if (m_pList) 479 m_pList->SetVisible(FALSE); 480 } 481 } 482 483 void CPWL_ComboBox::SelectAll() 484 { 485 if (m_pEdit && HasFlag(PCBS_ALLOWCUSTOMTEXT)) 486 m_pEdit->SelectAll(); 487 } 488 489 CPDF_Rect CPWL_ComboBox::GetFocusRect() const 490 { 491 return CPDF_Rect(); 492 } 493 494 void CPWL_ComboBox::SetPopup(FX_BOOL bPopup) 495 { 496 if (!m_pList) return; 497 if (bPopup == m_bPopup) return; 498 FX_FLOAT fListHeight = m_pList->GetContentRect().Height(); 499 if (!IsFloatBigger(fListHeight,0.0f)) return; 500 501 if (bPopup) 502 { 503 if (m_pFillerNotify) 504 { 505 FX_INT32 nWhere = 0; 506 FX_FLOAT fPopupRet = 0.0f; 507 FX_FLOAT fPopupMin = 0.0f; 508 if (m_pList->GetCount() > 3) 509 fPopupMin = m_pList->GetFirstHeight() * 3 + m_pList->GetBorderWidth() * 2; 510 FX_FLOAT fPopupMax = fListHeight + m_pList->GetBorderWidth() * 2; 511 m_pFillerNotify->QueryWherePopup(GetAttachedData(), fPopupMin,fPopupMax,nWhere,fPopupRet); 512 513 if (IsFloatBigger(fPopupRet,0.0f)) 514 { 515 m_bPopup = bPopup; 516 517 CPDF_Rect rcWindow = CPWL_Wnd::GetWindowRect(); 518 m_rcOldWindow = rcWindow; 519 switch (nWhere) 520 { 521 default: 522 case 0: 523 rcWindow.bottom -= fPopupRet; 524 break; 525 case 1: 526 rcWindow.top += fPopupRet; 527 break; 528 } 529 530 m_nPopupWhere = nWhere; 531 Move(rcWindow, TRUE, TRUE); 532 } 533 } 534 } 535 else 536 { 537 m_bPopup = bPopup; 538 Move(m_rcOldWindow, TRUE, TRUE); 539 } 540 } 541 542 FX_BOOL CPWL_ComboBox::OnKeyDown(FX_WORD nChar, FX_DWORD nFlag) 543 { 544 if (!m_pList) return FALSE; 545 if (!m_pEdit) return FALSE; 546 547 m_nSelectItem = -1; 548 549 switch (nChar) 550 { 551 case FWL_VKEY_Up: 552 if (m_pList->GetCurSel() > 0) 553 { 554 FX_BOOL bExit = FALSE; 555 if (m_pList->OnKeyDown(nChar,bExit,nFlag)) 556 { 557 if (bExit) return FALSE; 558 SetSelectText(); 559 } 560 } 561 return TRUE; 562 case FWL_VKEY_Down: 563 if (m_pList->GetCurSel() < m_pList->GetCount() - 1) 564 { 565 FX_BOOL bExit = FALSE; 566 if (m_pList->OnKeyDown(nChar,bExit,nFlag)) 567 { 568 if (bExit) return FALSE; 569 SetSelectText(); 570 } 571 } 572 return TRUE; 573 } 574 575 if (HasFlag(PCBS_ALLOWCUSTOMTEXT)) 576 return m_pEdit->OnKeyDown(nChar,nFlag); 577 else 578 return FALSE; 579 } 580 581 FX_BOOL CPWL_ComboBox::OnChar(FX_WORD nChar, FX_DWORD nFlag) 582 { 583 if (!m_pList) return FALSE; 584 if (!m_pEdit) return FALSE; 585 586 m_nSelectItem = -1; 587 FX_BOOL bExit = FALSE; 588 589 if (HasFlag(PCBS_ALLOWCUSTOMTEXT)) 590 { 591 return m_pEdit->OnChar(nChar,nFlag); 592 } 593 else 594 { 595 if (m_pList->OnChar(nChar,bExit,nFlag)) 596 { 597 return bExit; 598 } 599 else 600 return FALSE; 601 } 602 } 603 604 void CPWL_ComboBox::OnNotify(CPWL_Wnd* pWnd, FX_DWORD msg, FX_INTPTR wParam, FX_INTPTR lParam) 605 { 606 switch (msg) 607 { 608 case PNM_LBUTTONDOWN: 609 if (pWnd == m_pButton) 610 { 611 SetPopup(!m_bPopup); 612 return; 613 } 614 break; 615 case PNM_LBUTTONUP: 616 if (m_pEdit && m_pList) 617 { 618 if (pWnd == m_pList) 619 { 620 SetSelectText(); 621 SelectAll(); 622 m_pEdit->SetFocus(); 623 SetPopup(FALSE); 624 return; 625 } 626 } 627 } 628 629 CPWL_Wnd::OnNotify(pWnd,msg,wParam,lParam); 630 } 631 632 FX_BOOL CPWL_ComboBox::IsPopup() const 633 { 634 return m_bPopup; 635 } 636 637 void CPWL_ComboBox::SetSelectText() 638 { 639 CFX_WideString swText = m_pList->GetText(); 640 m_pEdit->SelectAll(); 641 m_pEdit->ReplaceSel(m_pList->GetText()); 642 m_pEdit->SelectAll(); 643 644 m_nSelectItem = m_pList->GetCurSel(); 645 } 646 647 FX_BOOL CPWL_ComboBox::IsModified() const 648 { 649 return m_pEdit->IsModified(); 650 } 651 652 void CPWL_ComboBox::SetFillerNotify(IPWL_Filler_Notify* pNotify) 653 { 654 m_pFillerNotify = pNotify; 655 656 if (m_pEdit) 657 m_pEdit->SetFillerNotify(pNotify); 658 659 if (m_pList) 660 m_pList->SetFillerNotify(pNotify); 661 } 662 663