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 22CPWL_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 34CPWL_EditCtrl::~CPWL_EditCtrl() 35{ 36 IFX_Edit::DelEdit(m_pEdit); 37} 38 39void CPWL_EditCtrl::OnCreate(PWL_CREATEPARAM & cp) 40{ 41 cp.eCursorType = FXCT_VBEAM; 42} 43 44void 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 53FX_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 65void 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 79void CPWL_EditCtrl::RePosChildWnd() 80{ 81 m_pEdit->SetPlateRect(GetClientRect()); 82} 83 84void 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 136void CPWL_EditCtrl::CreateChildWnd(const PWL_CREATEPARAM & cp) 137{ 138 if (!IsReadOnly()) 139 CreateEditCaret(cp); 140} 141 142void 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 160void CPWL_EditCtrl::SetFontSize(FX_FLOAT fFontSize) 161{ 162 m_pEdit->SetFontSize(fFontSize); 163} 164 165FX_FLOAT CPWL_EditCtrl::GetFontSize() const 166{ 167 return m_pEdit->GetFontSize(); 168} 169 170FX_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 248FX_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 325FX_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 343FX_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 360FX_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 370CPDF_Rect CPWL_EditCtrl::GetContentRect() const 371{ 372 return m_pEdit->GetContentRect(); 373} 374 375void 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 388void 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 412void 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 421void 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 432FX_BOOL CPWL_EditCtrl::IsModified() const 433{ 434 return m_pEdit->IsModified(); 435} 436 437CFX_WideString CPWL_EditCtrl::GetText() const 438{ 439 return m_pEdit->GetText(); 440} 441 442void CPWL_EditCtrl::SetSel(FX_INT32 nStartChar,FX_INT32 nEndChar) 443{ 444 m_pEdit->SetSel(nStartChar, nEndChar); 445} 446 447void CPWL_EditCtrl::GetSel(FX_INT32 & nStartChar, FX_INT32 & nEndChar ) const 448{ 449 m_pEdit->GetSel(nStartChar, nEndChar); 450} 451 452void CPWL_EditCtrl::Clear() 453{ 454 if (!IsReadOnly()) 455 m_pEdit->Clear(); 456} 457 458void CPWL_EditCtrl::SelectAll() 459{ 460 m_pEdit->SelectAll(); 461} 462 463void CPWL_EditCtrl::Paint() 464{ 465 if (m_pEdit) 466 m_pEdit->Paint(); 467} 468 469void CPWL_EditCtrl::EnableRefresh(FX_BOOL bRefresh) 470{ 471 if (m_pEdit) 472 m_pEdit->EnableRefresh(bRefresh); 473} 474 475FX_INT32 CPWL_EditCtrl::GetCaret() const 476{ 477 if (m_pEdit) 478 return m_pEdit->GetCaret(); 479 480 return -1; 481} 482 483void CPWL_EditCtrl::SetCaret(FX_INT32 nPos) 484{ 485 if (m_pEdit) 486 m_pEdit->SetCaret(nPos); 487} 488 489FX_INT32 CPWL_EditCtrl::GetTotalWords() const 490{ 491 if (m_pEdit) 492 return m_pEdit->GetTotalWords(); 493 494 return 0; 495} 496 497void CPWL_EditCtrl::SetScrollPos(const CPDF_Point& point) 498{ 499 if (m_pEdit) 500 m_pEdit->SetScrollPos(point); 501} 502 503CPDF_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 511CPDF_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 539FX_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 564void CPWL_EditCtrl::SetText(FX_LPCWSTR csText) 565{ 566 m_pEdit->SetText(csText); 567} 568 569void CPWL_EditCtrl::CopyText() 570{ 571} 572 573void CPWL_EditCtrl::PasteText() 574{ 575} 576 577void CPWL_EditCtrl::CutText() 578{ 579} 580 581void CPWL_EditCtrl::ShowVScrollBar(FX_BOOL bShow) 582{ 583} 584 585void CPWL_EditCtrl::InsertText(FX_LPCWSTR csText) 586{ 587 if (!IsReadOnly()) 588 m_pEdit->InsertText(csText); 589} 590 591void CPWL_EditCtrl::InsertWord(FX_WORD word, FX_INT32 nCharset) 592{ 593 if (!IsReadOnly()) 594 m_pEdit->InsertWord(word, nCharset); 595} 596 597void CPWL_EditCtrl::InsertReturn() 598{ 599 if (!IsReadOnly()) 600 m_pEdit->InsertReturn(); 601} 602 603void CPWL_EditCtrl::Delete() 604{ 605 if (!IsReadOnly()) 606 m_pEdit->Delete(); 607} 608 609void CPWL_EditCtrl::Backspace() 610{ 611 if (!IsReadOnly()) 612 m_pEdit->Backspace(); 613} 614 615FX_BOOL CPWL_EditCtrl::CanUndo() const 616{ 617 return !IsReadOnly() && m_pEdit->CanUndo(); 618} 619 620FX_BOOL CPWL_EditCtrl::CanRedo() const 621{ 622 return !IsReadOnly() && m_pEdit->CanRedo(); 623} 624 625void CPWL_EditCtrl::Redo() 626{ 627 if (CanRedo()) 628 m_pEdit->Redo(); 629} 630 631void CPWL_EditCtrl::Undo() 632{ 633 if (CanUndo()) 634 m_pEdit->Undo(); 635} 636 637void 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 664void 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 670void 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 680void CPWL_EditCtrl::IOnCaretChange(const CPVT_SecProps & secProps, const CPVT_WordProps & wordProps) 681{ 682} 683 684void 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 695void CPWL_EditCtrl::IOnInvalidateRect(CPDF_Rect * pRect) 696{ 697 this->InvalidateRect(pRect); 698} 699 700FX_INT32 CPWL_EditCtrl::GetCharSet() const 701{ 702 if (m_nCharSet < 0) 703 return DEFAULT_CHARSET; 704 else 705 return m_nCharSet; 706} 707 708void 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 714CFX_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 721void CPWL_EditCtrl::SetReadyToInput() 722{ 723 if (m_bMouseDown) 724 { 725 ReleaseCapture(); 726 m_bMouseDown = FALSE; 727 } 728} 729