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