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_Edit.h" 8 9 #include <memory> 10 #include <vector> 11 12 #include "core/fpdfapi/font/cpdf_font.h" 13 #include "core/fpdfdoc/cpvt_word.h" 14 #include "core/fxcrt/fx_safe_types.h" 15 #include "core/fxcrt/fx_xml.h" 16 #include "core/fxge/cfx_graphstatedata.h" 17 #include "core/fxge/cfx_pathdata.h" 18 #include "core/fxge/cfx_renderdevice.h" 19 #include "core/fxge/fx_font.h" 20 #include "fpdfsdk/fxedit/fxet_edit.h" 21 #include "fpdfsdk/pdfwindow/PWL_Caret.h" 22 #include "fpdfsdk/pdfwindow/PWL_EditCtrl.h" 23 #include "fpdfsdk/pdfwindow/PWL_FontMap.h" 24 #include "fpdfsdk/pdfwindow/PWL_ScrollBar.h" 25 #include "fpdfsdk/pdfwindow/PWL_Utils.h" 26 #include "fpdfsdk/pdfwindow/PWL_Wnd.h" 27 #include "public/fpdf_fwlevent.h" 28 #include "third_party/base/stl_util.h" 29 30 CPWL_Edit::CPWL_Edit() 31 : m_pFillerNotify(nullptr), m_bFocus(false), m_pFormFiller(nullptr) {} 32 33 CPWL_Edit::~CPWL_Edit() { 34 ASSERT(m_bFocus == false); 35 } 36 37 CFX_ByteString CPWL_Edit::GetClassName() const { 38 return PWL_CLASSNAME_EDIT; 39 } 40 41 void CPWL_Edit::OnDestroy() {} 42 43 void CPWL_Edit::SetText(const CFX_WideString& csText) { 44 CFX_WideString swText = csText; 45 if (!HasFlag(PES_RICH)) { 46 m_pEdit->SetText(swText); 47 return; 48 } 49 50 CFX_ByteString sValue = CFX_ByteString::FromUnicode(swText); 51 std::unique_ptr<CXML_Element> pXML( 52 CXML_Element::Parse(sValue.c_str(), sValue.GetLength())); 53 if (!pXML) { 54 m_pEdit->SetText(swText); 55 return; 56 } 57 58 int32_t nCount = pXML->CountChildren(); 59 bool bFirst = true; 60 61 swText.clear(); 62 63 for (int32_t i = 0; i < nCount; i++) { 64 CXML_Element* pSubElement = pXML->GetElement(i); 65 if (!pSubElement) 66 continue; 67 68 CFX_ByteString tag = pSubElement->GetTagName(); 69 if (tag.EqualNoCase("p")) { 70 int nChild = pSubElement->CountChildren(); 71 CFX_WideString swSection; 72 for (int32_t j = 0; j < nChild; j++) 73 swSection += pSubElement->GetContent(j); 74 75 if (bFirst) 76 bFirst = false; 77 else 78 swText += FWL_VKEY_Return; 79 swText += swSection; 80 } 81 } 82 83 m_pEdit->SetText(swText); 84 } 85 86 void CPWL_Edit::RePosChildWnd() { 87 if (CPWL_ScrollBar* pVSB = GetVScrollBar()) { 88 CFX_FloatRect rcWindow = m_rcOldWindow; 89 CFX_FloatRect rcVScroll = 90 CFX_FloatRect(rcWindow.right, rcWindow.bottom, 91 rcWindow.right + PWL_SCROLLBAR_WIDTH, rcWindow.top); 92 pVSB->Move(rcVScroll, true, false); 93 } 94 95 if (m_pEditCaret && !HasFlag(PES_TEXTOVERFLOW)) 96 m_pEditCaret->SetClipRect(CPWL_Utils::InflateRect( 97 GetClientRect(), 1.0f)); // +1 for caret beside border 98 99 CPWL_EditCtrl::RePosChildWnd(); 100 } 101 102 CFX_FloatRect CPWL_Edit::GetClientRect() const { 103 CFX_FloatRect rcClient = CPWL_Utils::DeflateRect( 104 GetWindowRect(), (FX_FLOAT)(GetBorderWidth() + GetInnerBorderWidth())); 105 106 if (CPWL_ScrollBar* pVSB = GetVScrollBar()) { 107 if (pVSB->IsVisible()) { 108 rcClient.right -= PWL_SCROLLBAR_WIDTH; 109 } 110 } 111 112 return rcClient; 113 } 114 115 void CPWL_Edit::SetAlignFormatV(PWL_EDIT_ALIGNFORMAT_V nFormat, bool bPaint) { 116 m_pEdit->SetAlignmentV((int32_t)nFormat, bPaint); 117 } 118 119 bool CPWL_Edit::CanSelectAll() const { 120 return GetSelectWordRange() != m_pEdit->GetWholeWordRange(); 121 } 122 123 bool CPWL_Edit::CanClear() const { 124 return !IsReadOnly() && m_pEdit->IsSelected(); 125 } 126 127 bool CPWL_Edit::CanCopy() const { 128 return !HasFlag(PES_PASSWORD) && !HasFlag(PES_NOREAD) && 129 m_pEdit->IsSelected(); 130 } 131 132 bool CPWL_Edit::CanCut() const { 133 return CanCopy() && !IsReadOnly(); 134 } 135 void CPWL_Edit::CutText() { 136 if (!CanCut()) 137 return; 138 m_pEdit->Clear(); 139 } 140 141 void CPWL_Edit::OnCreated() { 142 CPWL_EditCtrl::OnCreated(); 143 144 if (CPWL_ScrollBar* pScroll = GetVScrollBar()) { 145 pScroll->RemoveFlag(PWS_AUTOTRANSPARENT); 146 pScroll->SetTransparency(255); 147 } 148 149 SetParamByFlag(); 150 151 m_rcOldWindow = GetWindowRect(); 152 153 m_pEdit->SetOprNotify(this); 154 m_pEdit->EnableOprNotify(true); 155 } 156 157 void CPWL_Edit::SetParamByFlag() { 158 if (HasFlag(PES_RIGHT)) { 159 m_pEdit->SetAlignmentH(2, false); 160 } else if (HasFlag(PES_MIDDLE)) { 161 m_pEdit->SetAlignmentH(1, false); 162 } else { 163 m_pEdit->SetAlignmentH(0, false); 164 } 165 166 if (HasFlag(PES_BOTTOM)) { 167 m_pEdit->SetAlignmentV(2, false); 168 } else if (HasFlag(PES_CENTER)) { 169 m_pEdit->SetAlignmentV(1, false); 170 } else { 171 m_pEdit->SetAlignmentV(0, false); 172 } 173 174 if (HasFlag(PES_PASSWORD)) { 175 m_pEdit->SetPasswordChar('*', false); 176 } 177 178 m_pEdit->SetMultiLine(HasFlag(PES_MULTILINE), false); 179 m_pEdit->SetAutoReturn(HasFlag(PES_AUTORETURN), false); 180 m_pEdit->SetAutoFontSize(HasFlag(PWS_AUTOFONTSIZE), false); 181 m_pEdit->SetAutoScroll(HasFlag(PES_AUTOSCROLL), false); 182 m_pEdit->EnableUndo(HasFlag(PES_UNDO)); 183 184 if (HasFlag(PES_TEXTOVERFLOW)) { 185 SetClipRect(CFX_FloatRect(0.0f, 0.0f, 0.0f, 0.0f)); 186 m_pEdit->SetTextOverflow(true, false); 187 } else { 188 if (m_pEditCaret) { 189 m_pEditCaret->SetClipRect(CPWL_Utils::InflateRect( 190 GetClientRect(), 1.0f)); // +1 for caret beside border 191 } 192 } 193 } 194 195 void CPWL_Edit::GetThisAppearanceStream(CFX_ByteTextBuf& sAppStream) { 196 CPWL_Wnd::GetThisAppearanceStream(sAppStream); 197 198 CFX_FloatRect rcClient = GetClientRect(); 199 CFX_ByteTextBuf sLine; 200 201 int32_t nCharArray = m_pEdit->GetCharArray(); 202 203 if (nCharArray > 0) { 204 switch (GetBorderStyle()) { 205 case BorderStyle::SOLID: { 206 sLine << "q\n" 207 << GetBorderWidth() << " w\n" 208 << CPWL_Utils::GetColorAppStream(GetBorderColor(), false) 209 .AsStringC() 210 << " 2 J 0 j\n"; 211 212 for (int32_t i = 1; i < nCharArray; i++) { 213 sLine << rcClient.left + 214 ((rcClient.right - rcClient.left) / nCharArray) * i 215 << " " << rcClient.bottom << " m\n" 216 << rcClient.left + 217 ((rcClient.right - rcClient.left) / nCharArray) * i 218 << " " << rcClient.top << " l S\n"; 219 } 220 221 sLine << "Q\n"; 222 break; 223 } 224 case BorderStyle::DASH: { 225 sLine << "q\n" 226 << GetBorderWidth() << " w\n" 227 << CPWL_Utils::GetColorAppStream(GetBorderColor(), false) 228 .AsStringC() 229 << " 2 J 0 j\n" 230 << "[" << GetBorderDash().nDash << " " << GetBorderDash().nGap 231 << "] " << GetBorderDash().nPhase << " d\n"; 232 233 for (int32_t i = 1; i < nCharArray; i++) { 234 sLine << rcClient.left + 235 ((rcClient.right - rcClient.left) / nCharArray) * i 236 << " " << rcClient.bottom << " m\n" 237 << rcClient.left + 238 ((rcClient.right - rcClient.left) / nCharArray) * i 239 << " " << rcClient.top << " l S\n"; 240 } 241 242 sLine << "Q\n"; 243 break; 244 } 245 default: 246 break; 247 } 248 } 249 250 sAppStream << sLine; 251 252 CFX_ByteTextBuf sText; 253 CFX_PointF ptOffset; 254 CPVT_WordRange wrWhole = m_pEdit->GetWholeWordRange(); 255 CPVT_WordRange wrSelect = GetSelectWordRange(); 256 CPVT_WordRange wrVisible = 257 HasFlag(PES_TEXTOVERFLOW) ? wrWhole : m_pEdit->GetVisibleWordRange(); 258 259 CPVT_WordRange wrSelBefore(wrWhole.BeginPos, wrSelect.BeginPos); 260 CPVT_WordRange wrSelAfter(wrSelect.EndPos, wrWhole.EndPos); 261 CPVT_WordRange wrTemp = 262 CPWL_Utils::OverlapWordRange(GetSelectWordRange(), wrVisible); 263 CFX_ByteString sEditSel = 264 CPWL_Utils::GetEditSelAppStream(m_pEdit.get(), ptOffset, &wrTemp); 265 266 if (sEditSel.GetLength() > 0) 267 sText << CPWL_Utils::GetColorAppStream(PWL_DEFAULT_SELBACKCOLOR).AsStringC() 268 << sEditSel.AsStringC(); 269 270 wrTemp = CPWL_Utils::OverlapWordRange(wrVisible, wrSelBefore); 271 CFX_ByteString sEditBefore = CPWL_Utils::GetEditAppStream( 272 m_pEdit.get(), ptOffset, &wrTemp, !HasFlag(PES_CHARARRAY), 273 m_pEdit->GetPasswordChar()); 274 275 if (sEditBefore.GetLength() > 0) 276 sText << "BT\n" 277 << CPWL_Utils::GetColorAppStream(GetTextColor()).AsStringC() 278 << sEditBefore.AsStringC() << "ET\n"; 279 280 wrTemp = CPWL_Utils::OverlapWordRange(wrVisible, wrSelect); 281 CFX_ByteString sEditMid = CPWL_Utils::GetEditAppStream( 282 m_pEdit.get(), ptOffset, &wrTemp, !HasFlag(PES_CHARARRAY), 283 m_pEdit->GetPasswordChar()); 284 285 if (sEditMid.GetLength() > 0) 286 sText << "BT\n" 287 << CPWL_Utils::GetColorAppStream(CPWL_Color(COLORTYPE_GRAY, 1)) 288 .AsStringC() 289 << sEditMid.AsStringC() << "ET\n"; 290 291 wrTemp = CPWL_Utils::OverlapWordRange(wrVisible, wrSelAfter); 292 CFX_ByteString sEditAfter = CPWL_Utils::GetEditAppStream( 293 m_pEdit.get(), ptOffset, &wrTemp, !HasFlag(PES_CHARARRAY), 294 m_pEdit->GetPasswordChar()); 295 296 if (sEditAfter.GetLength() > 0) 297 sText << "BT\n" 298 << CPWL_Utils::GetColorAppStream(GetTextColor()).AsStringC() 299 << sEditAfter.AsStringC() << "ET\n"; 300 301 if (sText.GetLength() > 0) { 302 CFX_FloatRect rect = GetClientRect(); 303 sAppStream << "q\n/Tx BMC\n"; 304 305 if (!HasFlag(PES_TEXTOVERFLOW)) 306 sAppStream << rect.left << " " << rect.bottom << " " 307 << rect.right - rect.left << " " << rect.top - rect.bottom 308 << " re W n\n"; 309 310 sAppStream << sText; 311 312 sAppStream << "EMC\nQ\n"; 313 } 314 } 315 316 void CPWL_Edit::DrawThisAppearance(CFX_RenderDevice* pDevice, 317 CFX_Matrix* pUser2Device) { 318 CPWL_Wnd::DrawThisAppearance(pDevice, pUser2Device); 319 320 CFX_FloatRect rcClient = GetClientRect(); 321 CFX_ByteTextBuf sLine; 322 323 int32_t nCharArray = m_pEdit->GetCharArray(); 324 FX_SAFE_INT32 nCharArraySafe = nCharArray; 325 nCharArraySafe -= 1; 326 nCharArraySafe *= 2; 327 328 if (nCharArray > 0 && nCharArraySafe.IsValid()) { 329 switch (GetBorderStyle()) { 330 case BorderStyle::SOLID: { 331 CFX_GraphStateData gsd; 332 gsd.m_LineWidth = (FX_FLOAT)GetBorderWidth(); 333 334 CFX_PathData path; 335 336 for (int32_t i = 0; i < nCharArray - 1; i++) { 337 path.AppendPoint( 338 CFX_PointF( 339 rcClient.left + 340 ((rcClient.right - rcClient.left) / nCharArray) * (i + 1), 341 rcClient.bottom), 342 FXPT_TYPE::MoveTo, false); 343 path.AppendPoint( 344 CFX_PointF( 345 rcClient.left + 346 ((rcClient.right - rcClient.left) / nCharArray) * (i + 1), 347 rcClient.top), 348 FXPT_TYPE::LineTo, false); 349 } 350 if (!path.GetPoints().empty()) { 351 pDevice->DrawPath(&path, pUser2Device, &gsd, 0, 352 GetBorderColor().ToFXColor(255), FXFILL_ALTERNATE); 353 } 354 break; 355 } 356 case BorderStyle::DASH: { 357 CFX_GraphStateData gsd; 358 gsd.m_LineWidth = (FX_FLOAT)GetBorderWidth(); 359 360 gsd.SetDashCount(2); 361 gsd.m_DashArray[0] = (FX_FLOAT)GetBorderDash().nDash; 362 gsd.m_DashArray[1] = (FX_FLOAT)GetBorderDash().nGap; 363 gsd.m_DashPhase = (FX_FLOAT)GetBorderDash().nPhase; 364 365 CFX_PathData path; 366 for (int32_t i = 0; i < nCharArray - 1; i++) { 367 path.AppendPoint( 368 CFX_PointF( 369 rcClient.left + 370 ((rcClient.right - rcClient.left) / nCharArray) * (i + 1), 371 rcClient.bottom), 372 FXPT_TYPE::MoveTo, false); 373 path.AppendPoint( 374 CFX_PointF( 375 rcClient.left + 376 ((rcClient.right - rcClient.left) / nCharArray) * (i + 1), 377 rcClient.top), 378 FXPT_TYPE::LineTo, false); 379 } 380 if (!path.GetPoints().empty()) { 381 pDevice->DrawPath(&path, pUser2Device, &gsd, 0, 382 GetBorderColor().ToFXColor(255), FXFILL_ALTERNATE); 383 } 384 break; 385 } 386 default: 387 break; 388 } 389 } 390 391 CFX_FloatRect rcClip; 392 CPVT_WordRange wrRange = m_pEdit->GetVisibleWordRange(); 393 CPVT_WordRange* pRange = nullptr; 394 if (!HasFlag(PES_TEXTOVERFLOW)) { 395 rcClip = GetClientRect(); 396 pRange = &wrRange; 397 } 398 399 CFX_SystemHandler* pSysHandler = GetSystemHandler(); 400 CFX_Edit::DrawEdit(pDevice, pUser2Device, m_pEdit.get(), 401 GetTextColor().ToFXColor(GetTransparency()), rcClip, 402 CFX_PointF(), pRange, pSysHandler, m_pFormFiller); 403 } 404 405 bool CPWL_Edit::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) { 406 CPWL_Wnd::OnLButtonDown(point, nFlag); 407 408 if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) { 409 if (m_bMouseDown) 410 InvalidateRect(); 411 412 m_bMouseDown = true; 413 SetCapture(); 414 415 m_pEdit->OnMouseDown(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)); 416 } 417 418 return true; 419 } 420 421 bool CPWL_Edit::OnLButtonDblClk(const CFX_PointF& point, uint32_t nFlag) { 422 CPWL_Wnd::OnLButtonDblClk(point, nFlag); 423 424 if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) { 425 m_pEdit->SelectAll(); 426 } 427 428 return true; 429 } 430 431 bool CPWL_Edit::OnRButtonUp(const CFX_PointF& point, uint32_t nFlag) { 432 if (m_bMouseDown) 433 return false; 434 435 CPWL_Wnd::OnRButtonUp(point, nFlag); 436 437 if (!HasFlag(PES_TEXTOVERFLOW) && !ClientHitTest(point)) 438 return true; 439 440 CFX_SystemHandler* pSH = GetSystemHandler(); 441 if (!pSH) 442 return false; 443 444 SetFocus(); 445 446 return false; 447 } 448 449 void CPWL_Edit::OnSetFocus() { 450 SetEditCaret(true); 451 if (!IsReadOnly()) { 452 if (IPWL_FocusHandler* pFocusHandler = GetFocusHandler()) 453 pFocusHandler->OnSetFocus(this); 454 } 455 m_bFocus = true; 456 } 457 458 void CPWL_Edit::OnKillFocus() { 459 ShowVScrollBar(false); 460 m_pEdit->SelectNone(); 461 SetCaret(false, CFX_PointF(), CFX_PointF()); 462 SetCharSet(FXFONT_ANSI_CHARSET); 463 m_bFocus = false; 464 } 465 466 void CPWL_Edit::SetCharSpace(FX_FLOAT fCharSpace) { 467 m_pEdit->SetCharSpace(fCharSpace); 468 } 469 470 CFX_ByteString CPWL_Edit::GetSelectAppearanceStream( 471 const CFX_PointF& ptOffset) const { 472 CPVT_WordRange wr = GetSelectWordRange(); 473 return CPWL_Utils::GetEditSelAppStream(m_pEdit.get(), ptOffset, &wr); 474 } 475 476 CPVT_WordRange CPWL_Edit::GetSelectWordRange() const { 477 if (m_pEdit->IsSelected()) { 478 int32_t nStart = -1; 479 int32_t nEnd = -1; 480 481 m_pEdit->GetSel(nStart, nEnd); 482 483 CPVT_WordPlace wpStart = m_pEdit->WordIndexToWordPlace(nStart); 484 CPVT_WordPlace wpEnd = m_pEdit->WordIndexToWordPlace(nEnd); 485 486 return CPVT_WordRange(wpStart, wpEnd); 487 } 488 489 return CPVT_WordRange(); 490 } 491 492 CFX_ByteString CPWL_Edit::GetTextAppearanceStream( 493 const CFX_PointF& ptOffset) const { 494 CFX_ByteTextBuf sRet; 495 CFX_ByteString sEdit = CPWL_Utils::GetEditAppStream(m_pEdit.get(), ptOffset); 496 if (sEdit.GetLength() > 0) { 497 sRet << "BT\n" 498 << CPWL_Utils::GetColorAppStream(GetTextColor()).AsStringC() 499 << sEdit.AsStringC() << "ET\n"; 500 } 501 return sRet.MakeString(); 502 } 503 504 CFX_ByteString CPWL_Edit::GetCaretAppearanceStream( 505 const CFX_PointF& ptOffset) const { 506 if (m_pEditCaret) 507 return m_pEditCaret->GetCaretAppearanceStream(ptOffset); 508 509 return CFX_ByteString(); 510 } 511 512 CFX_PointF CPWL_Edit::GetWordRightBottomPoint(const CPVT_WordPlace& wpWord) { 513 CFX_Edit_Iterator* pIterator = m_pEdit->GetIterator(); 514 CPVT_WordPlace wpOld = pIterator->GetAt(); 515 pIterator->SetAt(wpWord); 516 517 CFX_PointF pt; 518 CPVT_Word word; 519 if (pIterator->GetWord(word)) { 520 pt = CFX_PointF(word.ptWord.x + word.fWidth, word.ptWord.y + word.fDescent); 521 } 522 pIterator->SetAt(wpOld); 523 return pt; 524 } 525 526 bool CPWL_Edit::IsTextFull() const { 527 return m_pEdit->IsTextFull(); 528 } 529 530 FX_FLOAT CPWL_Edit::GetCharArrayAutoFontSize(CPDF_Font* pFont, 531 const CFX_FloatRect& rcPlate, 532 int32_t nCharArray) { 533 if (pFont && !pFont->IsStandardFont()) { 534 FX_RECT rcBBox; 535 pFont->GetFontBBox(rcBBox); 536 537 CFX_FloatRect rcCell = rcPlate; 538 FX_FLOAT xdiv = rcCell.Width() / nCharArray * 1000.0f / rcBBox.Width(); 539 FX_FLOAT ydiv = -rcCell.Height() * 1000.0f / rcBBox.Height(); 540 541 return xdiv < ydiv ? xdiv : ydiv; 542 } 543 544 return 0.0f; 545 } 546 547 void CPWL_Edit::SetCharArray(int32_t nCharArray) { 548 if (HasFlag(PES_CHARARRAY) && nCharArray > 0) { 549 m_pEdit->SetCharArray(nCharArray); 550 m_pEdit->SetTextOverflow(true, true); 551 552 if (HasFlag(PWS_AUTOFONTSIZE)) { 553 if (IPVT_FontMap* pFontMap = GetFontMap()) { 554 FX_FLOAT fFontSize = GetCharArrayAutoFontSize( 555 pFontMap->GetPDFFont(0), GetClientRect(), nCharArray); 556 if (fFontSize > 0.0f) { 557 m_pEdit->SetAutoFontSize(false, true); 558 m_pEdit->SetFontSize(fFontSize); 559 } 560 } 561 } 562 } 563 } 564 565 void CPWL_Edit::SetLimitChar(int32_t nLimitChar) { 566 m_pEdit->SetLimitChar(nLimitChar); 567 } 568 569 void CPWL_Edit::ReplaceSel(const CFX_WideString& wsText) { 570 m_pEdit->Clear(); 571 m_pEdit->InsertText(wsText, FXFONT_DEFAULT_CHARSET); 572 } 573 574 CFX_FloatRect CPWL_Edit::GetFocusRect() const { 575 return CFX_FloatRect(); 576 } 577 578 void CPWL_Edit::ShowVScrollBar(bool bShow) { 579 if (CPWL_ScrollBar* pScroll = GetVScrollBar()) { 580 if (bShow) { 581 if (!pScroll->IsVisible()) { 582 pScroll->SetVisible(true); 583 CFX_FloatRect rcWindow = GetWindowRect(); 584 m_rcOldWindow = rcWindow; 585 rcWindow.right += PWL_SCROLLBAR_WIDTH; 586 Move(rcWindow, true, true); 587 } 588 } else { 589 if (pScroll->IsVisible()) { 590 pScroll->SetVisible(false); 591 Move(m_rcOldWindow, true, true); 592 } 593 } 594 } 595 } 596 597 bool CPWL_Edit::IsVScrollBarVisible() const { 598 if (CPWL_ScrollBar* pScroll = GetVScrollBar()) { 599 return pScroll->IsVisible(); 600 } 601 602 return false; 603 } 604 605 bool CPWL_Edit::OnKeyDown(uint16_t nChar, uint32_t nFlag) { 606 if (m_bMouseDown) 607 return true; 608 609 if (nChar == FWL_VKEY_Delete) { 610 if (m_pFillerNotify) { 611 bool bRC = true; 612 bool bExit = false; 613 CFX_WideString strChange; 614 CFX_WideString strChangeEx; 615 616 int nSelStart = 0; 617 int nSelEnd = 0; 618 GetSel(nSelStart, nSelEnd); 619 620 if (nSelStart == nSelEnd) 621 nSelEnd = nSelStart + 1; 622 m_pFillerNotify->OnBeforeKeyStroke(GetAttachedData(), strChange, 623 strChangeEx, nSelStart, nSelEnd, true, 624 bRC, bExit, nFlag); 625 if (!bRC) 626 return false; 627 if (bExit) 628 return false; 629 } 630 } 631 632 bool bRet = CPWL_EditCtrl::OnKeyDown(nChar, nFlag); 633 634 // In case of implementation swallow the OnKeyDown event. 635 if (IsProceedtoOnChar(nChar, nFlag)) 636 return true; 637 638 return bRet; 639 } 640 641 /** 642 *In case of implementation swallow the OnKeyDown event. 643 *If the event is swallowed, implementation may do other unexpected things, which 644 *is not the control means to do. 645 */ 646 bool CPWL_Edit::IsProceedtoOnChar(uint16_t nKeyCode, uint32_t nFlag) { 647 bool bCtrl = IsCTRLpressed(nFlag); 648 bool bAlt = IsALTpressed(nFlag); 649 if (bCtrl && !bAlt) { 650 // hot keys for edit control. 651 switch (nKeyCode) { 652 case 'C': 653 case 'V': 654 case 'X': 655 case 'A': 656 case 'Z': 657 return true; 658 default: 659 break; 660 } 661 } 662 // control characters. 663 switch (nKeyCode) { 664 case FWL_VKEY_Escape: 665 case FWL_VKEY_Back: 666 case FWL_VKEY_Return: 667 case FWL_VKEY_Space: 668 return true; 669 default: 670 return false; 671 } 672 } 673 674 bool CPWL_Edit::OnChar(uint16_t nChar, uint32_t nFlag) { 675 if (m_bMouseDown) 676 return true; 677 678 bool bRC = true; 679 bool bExit = false; 680 681 if (!IsCTRLpressed(nFlag)) { 682 if (m_pFillerNotify) { 683 CFX_WideString swChange; 684 685 int nSelStart = 0; 686 int nSelEnd = 0; 687 GetSel(nSelStart, nSelEnd); 688 689 switch (nChar) { 690 case FWL_VKEY_Back: 691 if (nSelStart == nSelEnd) 692 nSelStart = nSelEnd - 1; 693 break; 694 case FWL_VKEY_Return: 695 break; 696 default: 697 swChange += nChar; 698 break; 699 } 700 701 CFX_WideString strChangeEx; 702 m_pFillerNotify->OnBeforeKeyStroke(GetAttachedData(), swChange, 703 strChangeEx, nSelStart, nSelEnd, true, 704 bRC, bExit, nFlag); 705 } 706 } 707 708 if (!bRC) 709 return true; 710 if (bExit) 711 return false; 712 713 if (IPVT_FontMap* pFontMap = GetFontMap()) { 714 int32_t nOldCharSet = GetCharSet(); 715 int32_t nNewCharSet = 716 pFontMap->CharSetFromUnicode(nChar, FXFONT_DEFAULT_CHARSET); 717 if (nOldCharSet != nNewCharSet) { 718 SetCharSet(nNewCharSet); 719 } 720 } 721 722 return CPWL_EditCtrl::OnChar(nChar, nFlag); 723 } 724 725 bool CPWL_Edit::OnMouseWheel(short zDelta, 726 const CFX_PointF& point, 727 uint32_t nFlag) { 728 if (HasFlag(PES_MULTILINE)) { 729 CFX_PointF ptScroll = GetScrollPos(); 730 731 if (zDelta > 0) { 732 ptScroll.y += GetFontSize(); 733 } else { 734 ptScroll.y -= GetFontSize(); 735 } 736 SetScrollPos(ptScroll); 737 738 return true; 739 } 740 741 return false; 742 } 743 744 void CPWL_Edit::OnInsertReturn(const CPVT_WordPlace& place, 745 const CPVT_WordPlace& oldplace) { 746 if (HasFlag(PES_SPELLCHECK)) { 747 m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), 748 GetLatinWordsRange(place))); 749 } 750 } 751 752 void CPWL_Edit::OnBackSpace(const CPVT_WordPlace& place, 753 const CPVT_WordPlace& oldplace) { 754 if (HasFlag(PES_SPELLCHECK)) { 755 m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), 756 GetLatinWordsRange(place))); 757 } 758 } 759 760 void CPWL_Edit::OnDelete(const CPVT_WordPlace& place, 761 const CPVT_WordPlace& oldplace) { 762 if (HasFlag(PES_SPELLCHECK)) { 763 m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), 764 GetLatinWordsRange(place))); 765 } 766 } 767 768 void CPWL_Edit::OnClear(const CPVT_WordPlace& place, 769 const CPVT_WordPlace& oldplace) { 770 if (HasFlag(PES_SPELLCHECK)) { 771 m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), 772 GetLatinWordsRange(place))); 773 } 774 } 775 776 void CPWL_Edit::OnInsertWord(const CPVT_WordPlace& place, 777 const CPVT_WordPlace& oldplace) { 778 if (HasFlag(PES_SPELLCHECK)) { 779 m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), 780 GetLatinWordsRange(place))); 781 } 782 } 783 784 void CPWL_Edit::OnInsertText(const CPVT_WordPlace& place, 785 const CPVT_WordPlace& oldplace) { 786 if (HasFlag(PES_SPELLCHECK)) { 787 m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace), 788 GetLatinWordsRange(place))); 789 } 790 } 791 792 CPVT_WordRange CPWL_Edit::CombineWordRange(const CPVT_WordRange& wr1, 793 const CPVT_WordRange& wr2) { 794 CPVT_WordRange wrRet; 795 796 if (wr1.BeginPos.WordCmp(wr2.BeginPos) < 0) { 797 wrRet.BeginPos = wr1.BeginPos; 798 } else { 799 wrRet.BeginPos = wr2.BeginPos; 800 } 801 802 if (wr1.EndPos.WordCmp(wr2.EndPos) < 0) { 803 wrRet.EndPos = wr2.EndPos; 804 } else { 805 wrRet.EndPos = wr1.EndPos; 806 } 807 808 return wrRet; 809 } 810 811 CPVT_WordRange CPWL_Edit::GetLatinWordsRange(const CFX_PointF& point) const { 812 return GetSameWordsRange(m_pEdit->SearchWordPlace(point), true, false); 813 } 814 815 CPVT_WordRange CPWL_Edit::GetLatinWordsRange( 816 const CPVT_WordPlace& place) const { 817 return GetSameWordsRange(place, true, false); 818 } 819 820 CPVT_WordRange CPWL_Edit::GetArabicWordsRange( 821 const CPVT_WordPlace& place) const { 822 return GetSameWordsRange(place, false, true); 823 } 824 825 #define PWL_ISARABICWORD(word) \ 826 ((word >= 0x0600 && word <= 0x06FF) || (word >= 0xFB50 && word <= 0xFEFC)) 827 828 CPVT_WordRange CPWL_Edit::GetSameWordsRange(const CPVT_WordPlace& place, 829 bool bLatin, 830 bool bArabic) const { 831 CPVT_WordRange range; 832 833 CFX_Edit_Iterator* pIterator = m_pEdit->GetIterator(); 834 CPVT_Word wordinfo; 835 CPVT_WordPlace wpStart(place), wpEnd(place); 836 pIterator->SetAt(place); 837 838 if (bLatin) { 839 while (pIterator->NextWord()) { 840 if (!pIterator->GetWord(wordinfo) || 841 !FX_EDIT_ISLATINWORD(wordinfo.Word)) { 842 break; 843 } 844 845 wpEnd = pIterator->GetAt(); 846 } 847 } else if (bArabic) { 848 while (pIterator->NextWord()) { 849 if (!pIterator->GetWord(wordinfo) || !PWL_ISARABICWORD(wordinfo.Word)) 850 break; 851 852 wpEnd = pIterator->GetAt(); 853 } 854 } 855 856 pIterator->SetAt(place); 857 858 if (bLatin) { 859 do { 860 if (!pIterator->GetWord(wordinfo) || 861 !FX_EDIT_ISLATINWORD(wordinfo.Word)) { 862 break; 863 } 864 865 wpStart = pIterator->GetAt(); 866 } while (pIterator->PrevWord()); 867 } else if (bArabic) { 868 do { 869 if (!pIterator->GetWord(wordinfo) || !PWL_ISARABICWORD(wordinfo.Word)) 870 break; 871 872 wpStart = pIterator->GetAt(); 873 } while (pIterator->PrevWord()); 874 } 875 876 range.Set(wpStart, wpEnd); 877 return range; 878 } 879