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