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_ScrollBar.h" 11 #include "../../include/pdfwindow/PWL_Utils.h" 12 #include "../../include/pdfwindow/PWL_Caret.h" 13 #include "../../include/pdfwindow/PWL_FontMap.h" 14 15 #define IsFloatZero(f) ((f) < 0.0001 && (f) > -0.0001) 16 #define IsFloatBigger(fa,fb) ((fa) > (fb) && !IsFloatZero((fa) - (fb))) 17 #define IsFloatSmaller(fa,fb) ((fa) < (fb) && !IsFloatZero((fa) - (fb))) 18 #define IsFloatEqual(fa,fb) IsFloatZero((fa)-(fb)) 19 20 /* ---------------------------- CPWL_EditCtrl ------------------------------ */ 21 22 CPWL_EditCtrl::CPWL_EditCtrl() : 23 m_pEdit(NULL), 24 m_pEditCaret(NULL), 25 m_bMouseDown(FALSE), 26 m_pEditNotify(NULL), 27 m_nCharSet(DEFAULT_CHARSET), 28 m_nCodePage(0) 29 { 30 m_pEdit = IFX_Edit::NewEdit(); 31 ASSERT(m_pEdit != NULL); 32 } 33 34 CPWL_EditCtrl::~CPWL_EditCtrl() 35 { 36 IFX_Edit::DelEdit(m_pEdit); 37 } 38 39 void CPWL_EditCtrl::OnCreate(PWL_CREATEPARAM & cp) 40 { 41 cp.eCursorType = FXCT_VBEAM; 42 } 43 44 void CPWL_EditCtrl::OnCreated() 45 { 46 SetFontSize(this->GetCreationParam().fFontSize); 47 48 m_pEdit->SetFontMap(this->GetFontMap()); 49 m_pEdit->SetNotify(this); 50 m_pEdit->Initialize(); 51 } 52 53 FX_BOOL CPWL_EditCtrl::IsWndHorV() 54 { 55 CPDF_Matrix mt = GetWindowMatrix(); 56 CPDF_Point point1(0,1); 57 CPDF_Point point2(1,1); 58 59 mt.Transform(point1.x, point1.y); 60 mt.Transform(point2.x, point2.y); 61 62 return point2.y == point1.y; 63 } 64 65 void CPWL_EditCtrl::SetCursor() 66 { 67 if (IsValid()) 68 { 69 if (IFX_SystemHandler* pSH = GetSystemHandler()) 70 { 71 if (IsWndHorV()) 72 pSH->SetCursor(FXCT_VBEAM); 73 else 74 pSH->SetCursor(FXCT_HBEAM); 75 } 76 } 77 } 78 79 void CPWL_EditCtrl::RePosChildWnd() 80 { 81 m_pEdit->SetPlateRect(GetClientRect()); 82 } 83 84 void CPWL_EditCtrl::OnNotify(CPWL_Wnd* pWnd, FX_DWORD msg, FX_INTPTR wParam, FX_INTPTR lParam) 85 { 86 CPWL_Wnd::OnNotify(pWnd,msg,wParam,lParam); 87 88 switch (msg) 89 { 90 case PNM_SETSCROLLINFO: 91 switch (wParam) 92 { 93 case SBT_VSCROLL: 94 if (CPWL_Wnd * pChild = GetVScrollBar()) 95 { 96 pChild->OnNotify(pWnd,PNM_SETSCROLLINFO,wParam,lParam); 97 } 98 break; 99 } 100 break; 101 case PNM_SETSCROLLPOS: 102 switch (wParam) 103 { 104 case SBT_VSCROLL: 105 if (CPWL_Wnd * pChild = GetVScrollBar()) 106 { 107 pChild->OnNotify(pWnd,PNM_SETSCROLLPOS,wParam,lParam); 108 } 109 break; 110 } 111 break; 112 case PNM_SCROLLWINDOW: 113 { 114 FX_FLOAT fPos = *(FX_FLOAT*)lParam; 115 switch (wParam) 116 { 117 case SBT_VSCROLL: 118 m_pEdit->SetScrollPos(CPDF_Point(m_pEdit->GetScrollPos().x,fPos)); 119 break; 120 } 121 } 122 break; 123 case PNM_SETCARETINFO: 124 { 125 if (PWL_CARET_INFO * pCaretInfo = (PWL_CARET_INFO *)wParam) 126 { 127 this->SetCaret(pCaretInfo->bVisible, 128 pCaretInfo->ptHead, 129 pCaretInfo->ptFoot); 130 } 131 } 132 break; 133 } 134 } 135 136 void CPWL_EditCtrl::CreateChildWnd(const PWL_CREATEPARAM & cp) 137 { 138 if (!IsReadOnly()) 139 CreateEditCaret(cp); 140 } 141 142 void CPWL_EditCtrl::CreateEditCaret(const PWL_CREATEPARAM & cp) 143 { 144 if (!m_pEditCaret) 145 { 146 m_pEditCaret = new CPWL_Caret; 147 m_pEditCaret->SetInvalidRect(GetClientRect()); 148 149 PWL_CREATEPARAM ecp = cp; 150 ecp.pParentWnd = this; 151 ecp.dwFlags = PWS_CHILD | PWS_NOREFRESHCLIP; 152 ecp.dwBorderWidth = 0; 153 ecp.nBorderStyle = PBS_SOLID; 154 ecp.rcRectWnd = CPDF_Rect(0,0,0,0); 155 156 m_pEditCaret->Create(ecp); 157 } 158 } 159 160 void CPWL_EditCtrl::SetFontSize(FX_FLOAT fFontSize) 161 { 162 m_pEdit->SetFontSize(fFontSize); 163 } 164 165 FX_FLOAT CPWL_EditCtrl::GetFontSize() const 166 { 167 return m_pEdit->GetFontSize(); 168 } 169 170 FX_BOOL CPWL_EditCtrl::OnKeyDown(FX_WORD nChar, FX_DWORD nFlag) 171 { 172 if (m_bMouseDown) return TRUE; 173 174 FX_BOOL bRet = CPWL_Wnd::OnKeyDown(nChar,nFlag); 175 176 //FILTER 177 switch (nChar) 178 { 179 default: 180 return FALSE; 181 case FWL_VKEY_Delete: 182 case FWL_VKEY_Up: 183 case FWL_VKEY_Down: 184 case FWL_VKEY_Left: 185 case FWL_VKEY_Right: 186 case FWL_VKEY_Home: 187 case FWL_VKEY_End: 188 case FWL_VKEY_Insert: 189 case 'C': 190 case 'V': 191 case 'X': 192 case 'A': 193 case 'Z': 194 case 'c': 195 case 'v': 196 case 'x': 197 case 'a': 198 case 'z': 199 break; 200 } 201 202 if (nChar == FWL_VKEY_Delete) 203 { 204 if (m_pEdit->IsSelected()) 205 nChar = FWL_VKEY_Unknown; 206 } 207 208 switch (nChar) 209 { 210 case FWL_VKEY_Delete: 211 Delete(); 212 return TRUE; 213 case FWL_VKEY_Insert: 214 if (IsSHIFTpressed(nFlag)) 215 PasteText(); 216 return TRUE; 217 case FWL_VKEY_Up: 218 m_pEdit->OnVK_UP(IsSHIFTpressed(nFlag),FALSE); 219 return TRUE; 220 case FWL_VKEY_Down: 221 m_pEdit->OnVK_DOWN(IsSHIFTpressed(nFlag),FALSE); 222 return TRUE; 223 case FWL_VKEY_Left: 224 m_pEdit->OnVK_LEFT(IsSHIFTpressed(nFlag),FALSE); 225 return TRUE; 226 case FWL_VKEY_Right: 227 m_pEdit->OnVK_RIGHT(IsSHIFTpressed(nFlag),FALSE); 228 return TRUE; 229 case FWL_VKEY_Home: 230 m_pEdit->OnVK_HOME(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag)); 231 return TRUE; 232 case FWL_VKEY_End: 233 m_pEdit->OnVK_END(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag)); 234 return TRUE; 235 case FWL_VKEY_Unknown: 236 if (!IsSHIFTpressed(nFlag)) 237 Clear(); 238 else 239 CutText(); 240 return TRUE; 241 default: 242 break; 243 } 244 245 return bRet; 246 } 247 248 FX_BOOL CPWL_EditCtrl::OnChar(FX_WORD nChar, FX_DWORD nFlag) 249 { 250 if (m_bMouseDown) return TRUE; 251 252 CPWL_Wnd::OnChar(nChar,nFlag); 253 254 //FILTER 255 switch (nChar) 256 { 257 case 0x0A: 258 case 0x1B: 259 return FALSE; 260 default: 261 break; 262 } 263 264 FX_BOOL bCtrl = IsCTRLpressed(nFlag); 265 FX_BOOL bAlt = IsALTpressed(nFlag); 266 FX_BOOL bShift = IsSHIFTpressed(nFlag); 267 268 FX_WORD word = nChar; 269 270 if (bCtrl && !bAlt) 271 { 272 switch (nChar) 273 { 274 case 'C' - 'A' + 1: 275 this->CopyText(); 276 return TRUE; 277 case 'V' - 'A' + 1: 278 this->PasteText(); 279 return TRUE; 280 case 'X' - 'A' + 1: 281 this->CutText(); 282 return TRUE; 283 case 'A' - 'A' + 1: 284 this->SelectAll(); 285 return TRUE; 286 case 'Z' - 'A' + 1: 287 if (bShift) 288 Redo(); 289 else 290 Undo(); 291 return TRUE; 292 default: 293 if (nChar < 32) 294 return FALSE; 295 } 296 } 297 298 if (IsReadOnly()) return TRUE; 299 300 if (m_pEdit->IsSelected() && word == FWL_VKEY_Back) 301 word = FWL_VKEY_Unknown; 302 303 Clear(); 304 305 switch (word) 306 { 307 case FWL_VKEY_Back: 308 Backspace(); 309 break; 310 case FWL_VKEY_Return: 311 InsertReturn(); 312 break; 313 case FWL_VKEY_Unknown: 314 break; 315 default: 316 if (IsINSERTpressed(nFlag)) 317 Delete(); 318 InsertWord(word, this->GetCharSet()); 319 break; 320 } 321 322 return TRUE; 323 } 324 325 FX_BOOL CPWL_EditCtrl::OnLButtonDown(const CPDF_Point & point, FX_DWORD nFlag) 326 { 327 CPWL_Wnd::OnLButtonDown(point,nFlag); 328 329 if (ClientHitTest(point)) 330 { 331 if (m_bMouseDown) 332 this->InvalidateRect(); 333 334 m_bMouseDown = TRUE; 335 SetCapture(); 336 337 m_pEdit->OnMouseDown(point,IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag)); 338 } 339 340 return TRUE; 341 } 342 343 FX_BOOL CPWL_EditCtrl::OnLButtonUp(const CPDF_Point & point, FX_DWORD nFlag) 344 { 345 CPWL_Wnd::OnLButtonUp(point,nFlag); 346 347 if (m_bMouseDown) 348 { 349 //can receive keybord message 350 if (ClientHitTest(point) && !this->IsFocused()) 351 SetFocus(); 352 353 ReleaseCapture(); 354 m_bMouseDown = FALSE; 355 } 356 357 return TRUE; 358 } 359 360 FX_BOOL CPWL_EditCtrl::OnMouseMove(const CPDF_Point & point, FX_DWORD nFlag) 361 { 362 CPWL_Wnd::OnMouseMove(point,nFlag); 363 364 if (m_bMouseDown) 365 m_pEdit->OnMouseMove(point,FALSE,FALSE); 366 367 return TRUE; 368 } 369 370 CPDF_Rect CPWL_EditCtrl::GetContentRect() const 371 { 372 return m_pEdit->GetContentRect(); 373 } 374 375 void CPWL_EditCtrl::SetEditCaret(FX_BOOL bVisible) 376 { 377 CPDF_Point ptHead(0,0),ptFoot(0,0); 378 379 if (bVisible) 380 { 381 GetCaretInfo(ptHead,ptFoot); 382 } 383 384 CPVT_WordPlace wpTemp = m_pEdit->GetCaretWordPlace(); 385 this->IOnSetCaret(bVisible,ptHead,ptFoot,wpTemp); 386 } 387 388 void CPWL_EditCtrl::GetCaretInfo(CPDF_Point & ptHead, CPDF_Point & ptFoot) const 389 { 390 if (IFX_Edit_Iterator * pIterator = m_pEdit->GetIterator()) 391 { 392 pIterator->SetAt(m_pEdit->GetCaret()); 393 CPVT_Word word; 394 CPVT_Line line; 395 if (pIterator->GetWord(word)) 396 { 397 ptHead.x = word.ptWord.x + word.fWidth; 398 ptHead.y = word.ptWord.y + word.fAscent; 399 ptFoot.x = word.ptWord.x + word.fWidth; 400 ptFoot.y = word.ptWord.y + word.fDescent; 401 } 402 else if (pIterator->GetLine(line)) 403 { 404 ptHead.x = line.ptLine.x; 405 ptHead.y = line.ptLine.y + line.fLineAscent; 406 ptFoot.x = line.ptLine.x; 407 ptFoot.y = line.ptLine.y + line.fLineDescent; 408 } 409 } 410 } 411 412 void CPWL_EditCtrl::GetCaretPos(FX_INT32& x, FX_INT32& y) const 413 { 414 CPDF_Point ptHead(0,0), ptFoot(0,0); 415 416 GetCaretInfo(ptHead,ptFoot); 417 418 PWLtoWnd(ptHead, x, y); 419 } 420 421 void CPWL_EditCtrl::SetCaret(FX_BOOL bVisible, const CPDF_Point & ptHead, const CPDF_Point & ptFoot) 422 { 423 if (m_pEditCaret) 424 { 425 if (!IsFocused() || m_pEdit->IsSelected()) 426 bVisible = FALSE; 427 428 m_pEditCaret->SetCaret(bVisible, ptHead, ptFoot); 429 } 430 } 431 432 FX_BOOL CPWL_EditCtrl::IsModified() const 433 { 434 return m_pEdit->IsModified(); 435 } 436 437 CFX_WideString CPWL_EditCtrl::GetText() const 438 { 439 return m_pEdit->GetText(); 440 } 441 442 void CPWL_EditCtrl::SetSel(FX_INT32 nStartChar,FX_INT32 nEndChar) 443 { 444 m_pEdit->SetSel(nStartChar, nEndChar); 445 } 446 447 void CPWL_EditCtrl::GetSel(FX_INT32 & nStartChar, FX_INT32 & nEndChar ) const 448 { 449 m_pEdit->GetSel(nStartChar, nEndChar); 450 } 451 452 void CPWL_EditCtrl::Clear() 453 { 454 if (!IsReadOnly()) 455 m_pEdit->Clear(); 456 } 457 458 void CPWL_EditCtrl::SelectAll() 459 { 460 m_pEdit->SelectAll(); 461 } 462 463 void CPWL_EditCtrl::Paint() 464 { 465 if (m_pEdit) 466 m_pEdit->Paint(); 467 } 468 469 void CPWL_EditCtrl::EnableRefresh(FX_BOOL bRefresh) 470 { 471 if (m_pEdit) 472 m_pEdit->EnableRefresh(bRefresh); 473 } 474 475 FX_INT32 CPWL_EditCtrl::GetCaret() const 476 { 477 if (m_pEdit) 478 return m_pEdit->GetCaret(); 479 480 return -1; 481 } 482 483 void CPWL_EditCtrl::SetCaret(FX_INT32 nPos) 484 { 485 if (m_pEdit) 486 m_pEdit->SetCaret(nPos); 487 } 488 489 FX_INT32 CPWL_EditCtrl::GetTotalWords() const 490 { 491 if (m_pEdit) 492 return m_pEdit->GetTotalWords(); 493 494 return 0; 495 } 496 497 void CPWL_EditCtrl::SetScrollPos(const CPDF_Point& point) 498 { 499 if (m_pEdit) 500 m_pEdit->SetScrollPos(point); 501 } 502 503 CPDF_Point CPWL_EditCtrl::GetScrollPos() const 504 { 505 if (m_pEdit) 506 return m_pEdit->GetScrollPos(); 507 508 return CPDF_Point(0.0f, 0.0f); 509 } 510 511 CPDF_Font * CPWL_EditCtrl::GetCaretFont() const 512 { 513 FX_INT32 nFontIndex = 0; 514 515 if (IFX_Edit_Iterator * pIterator = m_pEdit->GetIterator()) 516 { 517 pIterator->SetAt(m_pEdit->GetCaret()); 518 CPVT_Word word; 519 CPVT_Section section; 520 if (pIterator->GetWord(word)) 521 { 522 nFontIndex = word.nFontIndex; 523 } 524 else if (HasFlag(PES_RICH)) 525 { 526 if (pIterator->GetSection(section)) 527 { 528 nFontIndex = section.WordProps.nFontIndex; 529 } 530 } 531 } 532 533 if (IFX_Edit_FontMap * pFontMap = GetFontMap()) 534 return pFontMap->GetPDFFont(nFontIndex); 535 else 536 return NULL; 537 } 538 539 FX_FLOAT CPWL_EditCtrl::GetCaretFontSize() const 540 { 541 FX_FLOAT fFontSize = GetFontSize(); 542 543 if (IFX_Edit_Iterator * pIterator = m_pEdit->GetIterator()) 544 { 545 pIterator->SetAt(m_pEdit->GetCaret()); 546 CPVT_Word word; 547 CPVT_Section section; 548 if (pIterator->GetWord(word)) 549 { 550 fFontSize = word.fFontSize; 551 } 552 else if (HasFlag(PES_RICH)) 553 { 554 if (pIterator->GetSection(section)) 555 { 556 fFontSize = section.WordProps.fFontSize; 557 } 558 } 559 } 560 561 return fFontSize; 562 } 563 564 void CPWL_EditCtrl::SetText(FX_LPCWSTR csText) 565 { 566 m_pEdit->SetText(csText); 567 } 568 569 void CPWL_EditCtrl::CopyText() 570 { 571 } 572 573 void CPWL_EditCtrl::PasteText() 574 { 575 } 576 577 void CPWL_EditCtrl::CutText() 578 { 579 } 580 581 void CPWL_EditCtrl::ShowVScrollBar(FX_BOOL bShow) 582 { 583 } 584 585 void CPWL_EditCtrl::InsertText(FX_LPCWSTR csText) 586 { 587 if (!IsReadOnly()) 588 m_pEdit->InsertText(csText); 589 } 590 591 void CPWL_EditCtrl::InsertWord(FX_WORD word, FX_INT32 nCharset) 592 { 593 if (!IsReadOnly()) 594 m_pEdit->InsertWord(word, nCharset); 595 } 596 597 void CPWL_EditCtrl::InsertReturn() 598 { 599 if (!IsReadOnly()) 600 m_pEdit->InsertReturn(); 601 } 602 603 void CPWL_EditCtrl::Delete() 604 { 605 if (!IsReadOnly()) 606 m_pEdit->Delete(); 607 } 608 609 void CPWL_EditCtrl::Backspace() 610 { 611 if (!IsReadOnly()) 612 m_pEdit->Backspace(); 613 } 614 615 FX_BOOL CPWL_EditCtrl::CanUndo() const 616 { 617 return !IsReadOnly() && m_pEdit->CanUndo(); 618 } 619 620 FX_BOOL CPWL_EditCtrl::CanRedo() const 621 { 622 return !IsReadOnly() && m_pEdit->CanRedo(); 623 } 624 625 void CPWL_EditCtrl::Redo() 626 { 627 if (CanRedo()) 628 m_pEdit->Redo(); 629 } 630 631 void CPWL_EditCtrl::Undo() 632 { 633 if (CanUndo()) 634 m_pEdit->Undo(); 635 } 636 637 void CPWL_EditCtrl::IOnSetScrollInfoY(FX_FLOAT fPlateMin, FX_FLOAT fPlateMax, 638 FX_FLOAT fContentMin, FX_FLOAT fContentMax, 639 FX_FLOAT fSmallStep, FX_FLOAT fBigStep) 640 { 641 PWL_SCROLL_INFO Info; 642 643 Info.fPlateWidth = fPlateMax - fPlateMin; 644 Info.fContentMin = fContentMin; 645 Info.fContentMax = fContentMax; 646 Info.fSmallStep = fSmallStep; 647 Info.fBigStep = fBigStep; 648 649 this->OnNotify(this,PNM_SETSCROLLINFO,SBT_VSCROLL,(FX_INTPTR)&Info); 650 651 // PWL_TRACE("set scroll info:%f\n",fContentMax - fContentMin); 652 653 if (IsFloatBigger(Info.fPlateWidth,Info.fContentMax-Info.fContentMin) 654 || IsFloatEqual(Info.fPlateWidth,Info.fContentMax-Info.fContentMin)) 655 { 656 this->ShowVScrollBar(FALSE); 657 } 658 else 659 { 660 this->ShowVScrollBar(TRUE); 661 } 662 } 663 664 void CPWL_EditCtrl::IOnSetScrollPosY(FX_FLOAT fy) 665 { 666 // PWL_TRACE("set scroll position:%f\n",fy); 667 this->OnNotify(this,PNM_SETSCROLLPOS,SBT_VSCROLL,(FX_INTPTR)&fy); 668 } 669 670 void CPWL_EditCtrl::IOnSetCaret(FX_BOOL bVisible, const CPDF_Point & ptHead, const CPDF_Point & ptFoot, const CPVT_WordPlace& place) 671 { 672 PWL_CARET_INFO cInfo; 673 cInfo.bVisible = bVisible; 674 cInfo.ptHead = ptHead; 675 cInfo.ptFoot = ptFoot; 676 677 this->OnNotify(this,PNM_SETCARETINFO,(FX_INTPTR)&cInfo,(FX_INTPTR)NULL); 678 } 679 680 void CPWL_EditCtrl::IOnCaretChange(const CPVT_SecProps & secProps, const CPVT_WordProps & wordProps) 681 { 682 } 683 684 void CPWL_EditCtrl::IOnContentChange(const CPDF_Rect& rcContent) 685 { 686 if (this->IsValid()) 687 { 688 if (m_pEditNotify) 689 { 690 m_pEditNotify->OnContentChange(rcContent); 691 } 692 } 693 } 694 695 void CPWL_EditCtrl::IOnInvalidateRect(CPDF_Rect * pRect) 696 { 697 this->InvalidateRect(pRect); 698 } 699 700 FX_INT32 CPWL_EditCtrl::GetCharSet() const 701 { 702 if (m_nCharSet < 0) 703 return DEFAULT_CHARSET; 704 else 705 return m_nCharSet; 706 } 707 708 void CPWL_EditCtrl::GetTextRange(const CPDF_Rect& rect, FX_INT32 & nStartChar, FX_INT32 & nEndChar) const 709 { 710 nStartChar = m_pEdit->WordPlaceToWordIndex(m_pEdit->SearchWordPlace(CPDF_Point(rect.left, rect.top))); 711 nEndChar = m_pEdit->WordPlaceToWordIndex(m_pEdit->SearchWordPlace(CPDF_Point(rect.right, rect.bottom))); 712 } 713 714 CFX_WideString CPWL_EditCtrl::GetText(FX_INT32 & nStartChar, FX_INT32 & nEndChar) const 715 { 716 CPVT_WordPlace wpStart = m_pEdit->WordIndexToWordPlace(nStartChar); 717 CPVT_WordPlace wpEnd = m_pEdit->WordIndexToWordPlace(nEndChar); 718 return m_pEdit->GetRangeText(CPVT_WordRange(wpStart, wpEnd)); 719 } 720 721 void CPWL_EditCtrl::SetReadyToInput() 722 { 723 if (m_bMouseDown) 724 { 725 ReleaseCapture(); 726 m_bMouseDown = FALSE; 727 } 728 } 729