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 "xfa/fwl/cfwl_edit.h" 8 9 #include <algorithm> 10 #include <memory> 11 #include <utility> 12 #include <vector> 13 14 #include "third_party/base/ptr_util.h" 15 #include "third_party/base/stl_util.h" 16 #include "xfa/fde/cfde_texteditengine.h" 17 #include "xfa/fde/cfde_textout.h" 18 #include "xfa/fgas/font/cfgas_gefont.h" 19 #include "xfa/fwl/cfwl_app.h" 20 #include "xfa/fwl/cfwl_caret.h" 21 #include "xfa/fwl/cfwl_event.h" 22 #include "xfa/fwl/cfwl_eventcheckword.h" 23 #include "xfa/fwl/cfwl_eventtextchanged.h" 24 #include "xfa/fwl/cfwl_eventvalidate.h" 25 #include "xfa/fwl/cfwl_messagekey.h" 26 #include "xfa/fwl/cfwl_messagemouse.h" 27 #include "xfa/fwl/cfwl_themebackground.h" 28 #include "xfa/fwl/cfwl_themepart.h" 29 #include "xfa/fwl/cfwl_widgetmgr.h" 30 #include "xfa/fwl/ifwl_themeprovider.h" 31 #include "xfa/fwl/theme/cfwl_utils.h" 32 #include "xfa/fxfa/cxfa_ffdoc.h" 33 #include "xfa/fxfa/cxfa_ffwidget.h" 34 #include "xfa/fxgraphics/cxfa_gepath.h" 35 36 namespace { 37 38 const int kEditMargin = 3; 39 40 #if (_FX_OS_ == _FX_OS_MACOSX_) 41 constexpr int kEditingModifier = FWL_KEYFLAG_Command; 42 #else 43 constexpr int kEditingModifier = FWL_KEYFLAG_Ctrl; 44 #endif 45 46 bool FxEditIsLatinWord(wchar_t c) { 47 return c == 0x2D || (c <= 0x005A && c >= 0x0041) || 48 (c <= 0x007A && c >= 0x0061) || (c <= 0x02AF && c >= 0x00C0) || 49 c == 0x0027; 50 } 51 52 void AddSquigglyPath(CXFA_GEPath* pPathData, 53 float fStartX, 54 float fEndX, 55 float fY, 56 float fStep) { 57 pPathData->MoveTo(CFX_PointF(fStartX, fY)); 58 int i = 1; 59 for (float fx = fStartX + fStep; fx < fEndX; fx += fStep, ++i) 60 pPathData->LineTo(CFX_PointF(fx, fY + (i & 1) * fStep)); 61 } 62 63 } // namespace 64 65 CFWL_Edit::CFWL_Edit(const CFWL_App* app, 66 std::unique_ptr<CFWL_WidgetProperties> properties, 67 CFWL_Widget* pOuter) 68 : CFWL_Widget(app, std::move(properties), pOuter), 69 m_fVAlignOffset(0.0f), 70 m_fScrollOffsetX(0.0f), 71 m_fScrollOffsetY(0.0f), 72 m_bLButtonDown(false), 73 m_CursorPosition(0), 74 m_nLimit(-1), 75 m_fFontSize(0), 76 m_bSetRange(false), 77 m_iMax(0xFFFFFFF) { 78 m_rtClient.Reset(); 79 m_rtEngine.Reset(); 80 m_rtStatic.Reset(); 81 82 InitCaret(); 83 m_EdtEngine.SetDelegate(this); 84 } 85 86 CFWL_Edit::~CFWL_Edit() { 87 if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) 88 HideCaret(nullptr); 89 } 90 91 FWL_Type CFWL_Edit::GetClassID() const { 92 return FWL_Type::Edit; 93 } 94 95 CFX_RectF CFWL_Edit::GetWidgetRect() { 96 CFX_RectF rect = m_pProperties->m_rtWidget; 97 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_OuterScrollbar) { 98 IFWL_ThemeProvider* theme = GetAvailableTheme(); 99 float scrollbarWidth = theme ? theme->GetScrollBarWidth() : 0.0f; 100 if (IsShowScrollBar(true)) { 101 rect.width += scrollbarWidth; 102 rect.width += kEditMargin; 103 } 104 if (IsShowScrollBar(false)) { 105 rect.height += scrollbarWidth; 106 rect.height += kEditMargin; 107 } 108 } 109 return rect; 110 } 111 112 CFX_RectF CFWL_Edit::GetAutosizedWidgetRect() { 113 CFX_RectF rect; 114 115 if (m_EdtEngine.GetLength() > 0) { 116 CFX_SizeF size = CalcTextSize( 117 m_EdtEngine.GetText(), m_pProperties->m_pThemeProvider, 118 !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_MultiLine)); 119 rect = CFX_RectF(0, 0, size); 120 } 121 122 InflateWidgetRect(rect); 123 return rect; 124 } 125 126 void CFWL_Edit::SetStates(uint32_t dwStates) { 127 if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Invisible) || 128 (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)) { 129 HideCaret(nullptr); 130 } 131 CFWL_Widget::SetStates(dwStates); 132 } 133 134 void CFWL_Edit::Update() { 135 if (IsLocked()) 136 return; 137 if (!m_pProperties->m_pThemeProvider) 138 m_pProperties->m_pThemeProvider = GetAvailableTheme(); 139 140 Layout(); 141 if (m_rtClient.IsEmpty()) 142 return; 143 144 UpdateEditEngine(); 145 UpdateVAlignment(); 146 UpdateScroll(); 147 InitCaret(); 148 } 149 150 FWL_WidgetHit CFWL_Edit::HitTest(const CFX_PointF& point) { 151 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_OuterScrollbar) { 152 if (IsShowScrollBar(true)) { 153 if (m_pVertScrollBar->GetWidgetRect().Contains(point)) 154 return FWL_WidgetHit::VScrollBar; 155 } 156 if (IsShowScrollBar(false)) { 157 if (m_pHorzScrollBar->GetWidgetRect().Contains(point)) 158 return FWL_WidgetHit::HScrollBar; 159 } 160 } 161 if (m_rtClient.Contains(point)) 162 return FWL_WidgetHit::Edit; 163 return FWL_WidgetHit::Unknown; 164 } 165 166 void CFWL_Edit::AddSpellCheckObj(CXFA_GEPath& PathData, 167 int32_t nStart, 168 int32_t nCount, 169 float fOffSetX, 170 float fOffSetY) { 171 float fStep = m_EdtEngine.GetFontSize() / 16.0f; 172 float font_ascent = m_EdtEngine.GetFontAscent(); 173 174 std::vector<CFX_RectF> rects = 175 m_EdtEngine.GetCharacterRectsInRange(nStart, nCount); 176 for (const auto& rect : rects) { 177 float fY = rect.top + font_ascent + fOffSetY; 178 float fStartX = rect.left + fOffSetX; 179 float fEndX = fStartX + rect.Width(); 180 181 AddSquigglyPath(&PathData, fStartX, fEndX, fY, fStep); 182 } 183 } 184 185 void CFWL_Edit::DrawSpellCheck(CXFA_Graphics* pGraphics, 186 const CFX_Matrix* pMatrix) { 187 pGraphics->SaveGraphState(); 188 if (pMatrix) 189 pGraphics->ConcatMatrix(pMatrix); 190 191 CFWL_EventCheckWord checkWordEvent(this); 192 ByteString sLatinWord; 193 CXFA_GEPath pathSpell; 194 int32_t nStart = 0; 195 float fOffSetX = m_rtEngine.left - m_fScrollOffsetX; 196 float fOffSetY = m_rtEngine.top - m_fScrollOffsetY + m_fVAlignOffset; 197 WideString wsSpell = GetText(); 198 int32_t nContentLen = wsSpell.GetLength(); 199 for (int i = 0; i < nContentLen; i++) { 200 if (FxEditIsLatinWord(wsSpell[i])) { 201 if (sLatinWord.IsEmpty()) 202 nStart = i; 203 sLatinWord += (char)wsSpell[i]; 204 continue; 205 } 206 checkWordEvent.bsWord = sLatinWord; 207 checkWordEvent.bCheckWord = true; 208 DispatchEvent(&checkWordEvent); 209 210 if (!sLatinWord.IsEmpty() && !checkWordEvent.bCheckWord) { 211 AddSpellCheckObj(pathSpell, nStart, sLatinWord.GetLength(), fOffSetX, 212 fOffSetY); 213 } 214 sLatinWord.clear(); 215 } 216 217 checkWordEvent.bsWord = sLatinWord; 218 checkWordEvent.bCheckWord = true; 219 DispatchEvent(&checkWordEvent); 220 221 if (!sLatinWord.IsEmpty() && !checkWordEvent.bCheckWord) { 222 AddSpellCheckObj(pathSpell, nStart, sLatinWord.GetLength(), fOffSetX, 223 fOffSetY); 224 } 225 if (!pathSpell.IsEmpty()) { 226 CFX_RectF rtClip = m_rtEngine; 227 CFX_Matrix mt(1, 0, 0, 1, fOffSetX, fOffSetY); 228 if (pMatrix) { 229 rtClip = pMatrix->TransformRect(rtClip); 230 mt.Concat(*pMatrix); 231 } 232 pGraphics->SetClipRect(rtClip); 233 pGraphics->SetStrokeColor(CXFA_GEColor(0xFFFF0000)); 234 pGraphics->SetLineWidth(0); 235 pGraphics->StrokePath(&pathSpell, nullptr); 236 } 237 pGraphics->RestoreGraphState(); 238 } 239 240 void CFWL_Edit::DrawWidget(CXFA_Graphics* pGraphics, const CFX_Matrix& matrix) { 241 if (!pGraphics) 242 return; 243 if (!m_pProperties->m_pThemeProvider) 244 return; 245 if (m_rtClient.IsEmpty()) 246 return; 247 248 IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider; 249 if (!m_pWidgetMgr->IsFormDisabled()) 250 DrawTextBk(pGraphics, pTheme, &matrix); 251 DrawContent(pGraphics, pTheme, &matrix); 252 253 if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) && 254 !(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_ReadOnly)) { 255 DrawSpellCheck(pGraphics, &matrix); 256 } 257 if (HasBorder()) 258 DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix); 259 } 260 261 void CFWL_Edit::SetThemeProvider(IFWL_ThemeProvider* pThemeProvider) { 262 if (!pThemeProvider) 263 return; 264 if (m_pHorzScrollBar) 265 m_pHorzScrollBar->SetThemeProvider(pThemeProvider); 266 if (m_pVertScrollBar) 267 m_pVertScrollBar->SetThemeProvider(pThemeProvider); 268 if (m_pCaret) 269 m_pCaret->SetThemeProvider(pThemeProvider); 270 m_pProperties->m_pThemeProvider = pThemeProvider; 271 } 272 273 void CFWL_Edit::SetText(const WideString& wsText) { 274 m_EdtEngine.Clear(); 275 m_EdtEngine.Insert(0, wsText); 276 } 277 278 int32_t CFWL_Edit::GetTextLength() const { 279 return m_EdtEngine.GetLength(); 280 } 281 282 WideString CFWL_Edit::GetText() const { 283 return m_EdtEngine.GetText(); 284 } 285 286 void CFWL_Edit::ClearText() { 287 m_EdtEngine.Clear(); 288 } 289 290 void CFWL_Edit::SelectAll() { 291 m_EdtEngine.SelectAll(); 292 } 293 294 bool CFWL_Edit::HasSelection() const { 295 return m_EdtEngine.HasSelection(); 296 } 297 298 std::pair<size_t, size_t> CFWL_Edit::GetSelection() const { 299 return m_EdtEngine.GetSelection(); 300 } 301 302 void CFWL_Edit::ClearSelection() { 303 return m_EdtEngine.ClearSelection(); 304 } 305 306 int32_t CFWL_Edit::GetLimit() const { 307 return m_nLimit; 308 } 309 310 void CFWL_Edit::SetLimit(int32_t nLimit) { 311 m_nLimit = nLimit; 312 313 if (m_nLimit > 0) { 314 m_EdtEngine.SetHasCharacterLimit(true); 315 m_EdtEngine.SetCharacterLimit(nLimit); 316 } else { 317 m_EdtEngine.SetHasCharacterLimit(false); 318 } 319 } 320 321 void CFWL_Edit::SetAliasChar(wchar_t wAlias) { 322 m_EdtEngine.SetAliasChar(wAlias); 323 } 324 325 Optional<WideString> CFWL_Edit::Copy() { 326 if (!m_EdtEngine.HasSelection()) 327 return {}; 328 329 return {m_EdtEngine.GetSelectedText()}; 330 } 331 332 Optional<WideString> CFWL_Edit::Cut() { 333 if (!m_EdtEngine.HasSelection()) 334 return {}; 335 336 return {m_EdtEngine.DeleteSelectedText()}; 337 } 338 339 bool CFWL_Edit::Paste(const WideString& wsPaste) { 340 if (m_EdtEngine.HasSelection()) 341 m_EdtEngine.ReplaceSelectedText(wsPaste); 342 else 343 m_EdtEngine.Insert(m_CursorPosition, wsPaste); 344 345 return true; 346 } 347 348 bool CFWL_Edit::Undo() { 349 return CanUndo() ? m_EdtEngine.Undo() : false; 350 } 351 352 bool CFWL_Edit::Redo() { 353 return CanRedo() ? m_EdtEngine.Redo() : false; 354 } 355 356 bool CFWL_Edit::CanUndo() { 357 return m_EdtEngine.CanUndo(); 358 } 359 360 bool CFWL_Edit::CanRedo() { 361 return m_EdtEngine.CanRedo(); 362 } 363 364 void CFWL_Edit::SetOuter(CFWL_Widget* pOuter) { 365 m_pOuter = pOuter; 366 } 367 368 void CFWL_Edit::NotifyTextFull() { 369 CFWL_Event evt(CFWL_Event::Type::TextFull, this); 370 DispatchEvent(&evt); 371 } 372 373 void CFWL_Edit::OnCaretChanged() { 374 if (m_rtEngine.IsEmpty()) 375 return; 376 if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0) 377 return; 378 379 bool bRepaintContent = UpdateOffset(); 380 UpdateCaret(); 381 CFX_RectF rtInvalid; 382 bool bRepaintScroll = false; 383 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_MultiLine) { 384 CFWL_ScrollBar* pScroll = UpdateScroll(); 385 if (pScroll) { 386 rtInvalid = pScroll->GetWidgetRect(); 387 bRepaintScroll = true; 388 } 389 } 390 if (bRepaintContent || bRepaintScroll) { 391 if (bRepaintContent) 392 rtInvalid.Union(m_rtEngine); 393 RepaintRect(rtInvalid); 394 } 395 } 396 397 void CFWL_Edit::OnTextChanged(const WideString& prevText) { 398 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_VAlignMask) 399 UpdateVAlignment(); 400 401 CFWL_EventTextChanged event(this); 402 event.wsPrevText = prevText; 403 DispatchEvent(&event); 404 405 LayoutScrollBar(); 406 RepaintRect(GetClientRect()); 407 } 408 409 void CFWL_Edit::OnSelChanged() { 410 RepaintRect(GetClientRect()); 411 } 412 413 bool CFWL_Edit::OnValidate(const WideString& wsText) { 414 CFWL_Widget* pDst = GetOuter(); 415 if (!pDst) 416 pDst = this; 417 418 CFWL_EventValidate event(this); 419 event.wsInsert = wsText; 420 event.bValidate = true; 421 DispatchEvent(&event); 422 return event.bValidate; 423 } 424 425 void CFWL_Edit::SetScrollOffset(float fScrollOffset) { 426 m_fScrollOffsetY = fScrollOffset; 427 } 428 429 void CFWL_Edit::DrawTextBk(CXFA_Graphics* pGraphics, 430 IFWL_ThemeProvider* pTheme, 431 const CFX_Matrix* pMatrix) { 432 CFWL_ThemeBackground param; 433 param.m_pWidget = this; 434 param.m_iPart = CFWL_Part::Background; 435 param.m_bStaticBackground = false; 436 param.m_dwStates = m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_ReadOnly 437 ? CFWL_PartState_ReadOnly 438 : CFWL_PartState_Normal; 439 uint32_t dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled); 440 if (dwStates) 441 param.m_dwStates = CFWL_PartState_Disabled; 442 param.m_pGraphics = pGraphics; 443 param.m_matrix = *pMatrix; 444 param.m_rtPart = m_rtClient; 445 pTheme->DrawBackground(¶m); 446 447 if (!IsShowScrollBar(true) || !IsShowScrollBar(false)) 448 return; 449 450 CFX_RectF rtScroll = m_pHorzScrollBar->GetWidgetRect(); 451 452 CFX_RectF rtStatic(m_rtClient.right() - rtScroll.height, 453 m_rtClient.bottom() - rtScroll.height, rtScroll.height, 454 rtScroll.height); 455 param.m_bStaticBackground = true; 456 param.m_bMaximize = true; 457 param.m_rtPart = rtStatic; 458 pTheme->DrawBackground(¶m); 459 } 460 461 void CFWL_Edit::DrawContent(CXFA_Graphics* pGraphics, 462 IFWL_ThemeProvider* pTheme, 463 const CFX_Matrix* pMatrix) { 464 pGraphics->SaveGraphState(); 465 466 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_CombText) 467 pGraphics->SaveGraphState(); 468 469 CFX_RectF rtClip = m_rtEngine; 470 float fOffSetX = m_rtEngine.left - m_fScrollOffsetX; 471 float fOffSetY = m_rtEngine.top - m_fScrollOffsetY + m_fVAlignOffset; 472 473 CFX_Matrix mt(1, 0, 0, 1, fOffSetX, fOffSetY); 474 if (pMatrix) { 475 rtClip = pMatrix->TransformRect(rtClip); 476 mt.Concat(*pMatrix); 477 } 478 479 bool bShowSel = !!(m_pProperties->m_dwStates & FWL_WGTSTATE_Focused); 480 if (bShowSel && m_EdtEngine.HasSelection()) { 481 size_t sel_start; 482 size_t count; 483 std::tie(sel_start, count) = m_EdtEngine.GetSelection(); 484 std::vector<CFX_RectF> rects = 485 m_EdtEngine.GetCharacterRectsInRange(sel_start, count); 486 487 CXFA_GEPath path; 488 for (auto& rect : rects) { 489 rect.left += fOffSetX; 490 rect.top += fOffSetY; 491 path.AddRectangle(rect.left, rect.top, rect.width, rect.height); 492 } 493 pGraphics->SetClipRect(rtClip); 494 495 CFWL_ThemeBackground param; 496 param.m_pGraphics = pGraphics; 497 param.m_matrix = *pMatrix; 498 param.m_pWidget = this; 499 param.m_iPart = CFWL_Part::Background; 500 param.m_pPath = &path; 501 pTheme->DrawBackground(¶m); 502 } 503 504 CFX_RenderDevice* pRenderDev = pGraphics->GetRenderDevice(); 505 if (!pRenderDev) 506 return; 507 508 RenderText(pRenderDev, rtClip, mt); 509 510 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_CombText) { 511 pGraphics->RestoreGraphState(); 512 513 CXFA_GEPath path; 514 int32_t iLimit = m_nLimit > 0 ? m_nLimit : 1; 515 float fStep = m_rtEngine.width / iLimit; 516 float fLeft = m_rtEngine.left + 1; 517 for (int32_t i = 1; i < iLimit; i++) { 518 fLeft += fStep; 519 path.AddLine(CFX_PointF(fLeft, m_rtClient.top), 520 CFX_PointF(fLeft, m_rtClient.bottom())); 521 } 522 523 CFWL_ThemeBackground param; 524 param.m_pGraphics = pGraphics; 525 param.m_matrix = *pMatrix; 526 param.m_pWidget = this; 527 param.m_iPart = CFWL_Part::CombTextLine; 528 param.m_pPath = &path; 529 pTheme->DrawBackground(¶m); 530 } 531 pGraphics->RestoreGraphState(); 532 } 533 534 void CFWL_Edit::RenderText(CFX_RenderDevice* pRenderDev, 535 const CFX_RectF& clipRect, 536 const CFX_Matrix& mt) { 537 ASSERT(pRenderDev); 538 539 RetainPtr<CFGAS_GEFont> font = m_EdtEngine.GetFont(); 540 if (!font) 541 return; 542 543 pRenderDev->SetClip_Rect(clipRect); 544 545 CFX_RectF rtDocClip = clipRect; 546 if (rtDocClip.IsEmpty()) { 547 rtDocClip.left = 0; 548 rtDocClip.top = 0; 549 rtDocClip.width = static_cast<float>(pRenderDev->GetWidth()); 550 rtDocClip.height = static_cast<float>(pRenderDev->GetHeight()); 551 } 552 rtDocClip = mt.GetInverse().TransformRect(rtDocClip); 553 554 for (const FDE_TEXTEDITPIECE& info : m_EdtEngine.GetTextPieces()) { 555 // If this character is outside the clip, skip it. 556 if (!rtDocClip.IntersectWith(info.rtPiece)) 557 continue; 558 559 std::vector<FXTEXT_CHARPOS> char_pos = m_EdtEngine.GetDisplayPos(info); 560 if (char_pos.empty()) 561 continue; 562 563 CFDE_TextOut::DrawString(pRenderDev, m_EdtEngine.GetFontColor(), font, 564 char_pos.data(), char_pos.size(), 565 m_EdtEngine.GetFontSize(), &mt); 566 } 567 } 568 569 void CFWL_Edit::UpdateEditEngine() { 570 UpdateEditParams(); 571 UpdateEditLayout(); 572 } 573 574 void CFWL_Edit::UpdateEditParams() { 575 m_EdtEngine.SetAvailableWidth(m_rtEngine.width); 576 m_EdtEngine.SetCombText( 577 !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_CombText)); 578 579 m_EdtEngine.EnableValidation( 580 !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_Validate)); 581 m_EdtEngine.EnablePasswordMode( 582 !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_Password)); 583 584 uint32_t alignment = 0; 585 switch (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_HAlignMask) { 586 case FWL_STYLEEXT_EDT_HNear: { 587 alignment |= CFX_TxtLineAlignment_Left; 588 break; 589 } 590 case FWL_STYLEEXT_EDT_HCenter: { 591 alignment |= CFX_TxtLineAlignment_Center; 592 break; 593 } 594 case FWL_STYLEEXT_EDT_HFar: { 595 alignment |= CFX_TxtLineAlignment_Right; 596 break; 597 } 598 default: 599 break; 600 } 601 switch (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_HAlignModeMask) { 602 case FWL_STYLEEXT_EDT_Justified: { 603 alignment |= CFX_TxtLineAlignment_Justified; 604 break; 605 } 606 default: 607 break; 608 } 609 m_EdtEngine.SetAlignment(alignment); 610 611 bool auto_hscroll = 612 !!(m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_AutoHScroll); 613 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_MultiLine) { 614 m_EdtEngine.EnableMultiLine(true); 615 m_EdtEngine.EnableLineWrap(!auto_hscroll); 616 m_EdtEngine.LimitVerticalScroll( 617 (m_pProperties->m_dwStyles & FWL_WGTSTYLE_VScroll) == 0 && 618 (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_AutoVScroll) == 0); 619 } else { 620 m_EdtEngine.EnableMultiLine(false); 621 m_EdtEngine.EnableLineWrap(false); 622 m_EdtEngine.LimitVerticalScroll(false); 623 } 624 m_EdtEngine.LimitHorizontalScroll(!auto_hscroll); 625 626 IFWL_ThemeProvider* theme = GetAvailableTheme(); 627 CFWL_ThemePart part; 628 part.m_pWidget = this; 629 630 if (!theme) { 631 m_fFontSize = FWLTHEME_CAPACITY_FontSize; 632 return; 633 } 634 m_fFontSize = theme->GetFontSize(&part); 635 636 RetainPtr<CFGAS_GEFont> pFont = theme->GetFont(&part); 637 if (!pFont) 638 return; 639 640 m_EdtEngine.SetFont(pFont); 641 m_EdtEngine.SetFontColor(theme->GetTextColor(&part)); 642 m_EdtEngine.SetFontSize(m_fFontSize); 643 m_EdtEngine.SetLineSpace(theme->GetLineHeight(&part)); 644 m_EdtEngine.SetTabWidth(m_fFontSize); 645 m_EdtEngine.SetVisibleLineCount(m_rtEngine.height / 646 theme->GetLineHeight(&part)); 647 } 648 649 void CFWL_Edit::UpdateEditLayout() { 650 m_EdtEngine.Layout(); 651 } 652 653 bool CFWL_Edit::UpdateOffset() { 654 CFX_RectF rtCaret = m_rtCaret; 655 656 float fOffSetX = m_rtEngine.left - m_fScrollOffsetX; 657 float fOffSetY = m_rtEngine.top - m_fScrollOffsetY + m_fVAlignOffset; 658 rtCaret.Offset(fOffSetX, fOffSetY); 659 660 const CFX_RectF& edit_bounds = m_rtEngine; 661 if (edit_bounds.Contains(rtCaret)) { 662 CFX_RectF contents_bounds = m_EdtEngine.GetContentsBoundingBox(); 663 contents_bounds.Offset(fOffSetX, fOffSetY); 664 if (contents_bounds.right() < edit_bounds.right() && m_fScrollOffsetX > 0) { 665 m_fScrollOffsetX += contents_bounds.right() - edit_bounds.right(); 666 m_fScrollOffsetX = std::max(m_fScrollOffsetX, 0.0f); 667 } 668 if (contents_bounds.bottom() < edit_bounds.bottom() && 669 m_fScrollOffsetY > 0) { 670 m_fScrollOffsetY += contents_bounds.bottom() - edit_bounds.bottom(); 671 m_fScrollOffsetY = std::max(m_fScrollOffsetY, 0.0f); 672 } 673 return false; 674 } 675 676 float offsetX = 0.0; 677 float offsetY = 0.0; 678 if (rtCaret.left < edit_bounds.left) 679 offsetX = rtCaret.left - edit_bounds.left; 680 if (rtCaret.right() > edit_bounds.right()) 681 offsetX = rtCaret.right() - edit_bounds.right(); 682 if (rtCaret.top < edit_bounds.top) 683 offsetY = rtCaret.top - edit_bounds.top; 684 if (rtCaret.bottom() > edit_bounds.bottom()) 685 offsetY = rtCaret.bottom() - edit_bounds.bottom(); 686 687 m_fScrollOffsetX += offsetX; 688 m_fScrollOffsetY += offsetY; 689 if (m_fFontSize > m_rtEngine.height) 690 m_fScrollOffsetY = 0; 691 692 return true; 693 } 694 695 bool CFWL_Edit::UpdateOffset(CFWL_ScrollBar* pScrollBar, float fPosChanged) { 696 if (pScrollBar == m_pHorzScrollBar.get()) 697 m_fScrollOffsetX += fPosChanged; 698 else 699 m_fScrollOffsetY += fPosChanged; 700 return true; 701 } 702 703 void CFWL_Edit::UpdateVAlignment() { 704 float fSpaceAbove = 0.0f; 705 float fSpaceBelow = 0.0f; 706 IFWL_ThemeProvider* theme = GetAvailableTheme(); 707 if (theme) { 708 CFWL_ThemePart part; 709 part.m_pWidget = this; 710 711 CFX_SizeF pSpace = theme->GetSpaceAboveBelow(&part); 712 fSpaceAbove = pSpace.width >= 0.1f ? pSpace.width : 0.0f; 713 fSpaceBelow = pSpace.height >= 0.1f ? pSpace.height : 0.0f; 714 } 715 716 float fOffsetY = 0.0f; 717 CFX_RectF contents_bounds = m_EdtEngine.GetContentsBoundingBox(); 718 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_VCenter) { 719 fOffsetY = (m_rtEngine.height - contents_bounds.height) / 2.0f; 720 if (fOffsetY < (fSpaceAbove + fSpaceBelow) / 2.0f && 721 fSpaceAbove < fSpaceBelow) { 722 return; 723 } 724 fOffsetY += (fSpaceAbove - fSpaceBelow) / 2.0f; 725 } else if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_VFar) { 726 fOffsetY = (m_rtEngine.height - contents_bounds.height); 727 fOffsetY -= fSpaceBelow; 728 } else { 729 fOffsetY += fSpaceAbove; 730 } 731 m_fVAlignOffset = std::max(fOffsetY, 0.0f); 732 } 733 734 void CFWL_Edit::UpdateCaret() { 735 CFX_RectF rtCaret = m_rtCaret; 736 rtCaret.Offset(m_rtEngine.left - m_fScrollOffsetX, 737 m_rtEngine.top - m_fScrollOffsetY + m_fVAlignOffset); 738 739 CFX_RectF rtClient = GetClientRect(); 740 rtCaret.Intersect(rtClient); 741 if (rtCaret.left > rtClient.right()) { 742 float right = rtCaret.right(); 743 rtCaret.left = rtClient.right() - 1; 744 rtCaret.width = right - rtCaret.left; 745 } 746 747 if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused && !rtCaret.IsEmpty()) 748 ShowCaret(&rtCaret); 749 else 750 HideCaret(&rtCaret); 751 } 752 753 CFWL_ScrollBar* CFWL_Edit::UpdateScroll() { 754 bool bShowHorz = 755 m_pHorzScrollBar && 756 ((m_pHorzScrollBar->GetStates() & FWL_WGTSTATE_Invisible) == 0); 757 bool bShowVert = 758 m_pVertScrollBar && 759 ((m_pVertScrollBar->GetStates() & FWL_WGTSTATE_Invisible) == 0); 760 if (!bShowHorz && !bShowVert) 761 return nullptr; 762 763 CFX_RectF contents_bounds = m_EdtEngine.GetContentsBoundingBox(); 764 CFWL_ScrollBar* pRepaint = nullptr; 765 if (bShowHorz) { 766 CFX_RectF rtScroll = m_pHorzScrollBar->GetWidgetRect(); 767 if (rtScroll.width < contents_bounds.width) { 768 m_pHorzScrollBar->LockUpdate(); 769 float fRange = contents_bounds.width - rtScroll.width; 770 m_pHorzScrollBar->SetRange(0.0f, fRange); 771 772 float fPos = pdfium::clamp(m_fScrollOffsetX, 0.0f, fRange); 773 m_pHorzScrollBar->SetPos(fPos); 774 m_pHorzScrollBar->SetTrackPos(fPos); 775 m_pHorzScrollBar->SetPageSize(rtScroll.width); 776 m_pHorzScrollBar->SetStepSize(rtScroll.width / 10); 777 m_pHorzScrollBar->RemoveStates(FWL_WGTSTATE_Disabled); 778 m_pHorzScrollBar->UnlockUpdate(); 779 m_pHorzScrollBar->Update(); 780 pRepaint = m_pHorzScrollBar.get(); 781 } else if ((m_pHorzScrollBar->GetStates() & FWL_WGTSTATE_Disabled) == 0) { 782 m_pHorzScrollBar->LockUpdate(); 783 m_pHorzScrollBar->SetRange(0, -1); 784 m_pHorzScrollBar->SetStates(FWL_WGTSTATE_Disabled); 785 m_pHorzScrollBar->UnlockUpdate(); 786 m_pHorzScrollBar->Update(); 787 pRepaint = m_pHorzScrollBar.get(); 788 } 789 } 790 791 if (bShowVert) { 792 CFX_RectF rtScroll = m_pVertScrollBar->GetWidgetRect(); 793 if (rtScroll.height < contents_bounds.height) { 794 m_pVertScrollBar->LockUpdate(); 795 float fStep = m_EdtEngine.GetLineSpace(); 796 float fRange = 797 std::max(contents_bounds.height - m_rtEngine.height, fStep); 798 799 m_pVertScrollBar->SetRange(0.0f, fRange); 800 float fPos = pdfium::clamp(m_fScrollOffsetY, 0.0f, fRange); 801 m_pVertScrollBar->SetPos(fPos); 802 m_pVertScrollBar->SetTrackPos(fPos); 803 m_pVertScrollBar->SetPageSize(rtScroll.height); 804 m_pVertScrollBar->SetStepSize(fStep); 805 m_pVertScrollBar->RemoveStates(FWL_WGTSTATE_Disabled); 806 m_pVertScrollBar->UnlockUpdate(); 807 m_pVertScrollBar->Update(); 808 pRepaint = m_pVertScrollBar.get(); 809 } else if ((m_pVertScrollBar->GetStates() & FWL_WGTSTATE_Disabled) == 0) { 810 m_pVertScrollBar->LockUpdate(); 811 m_pVertScrollBar->SetRange(0, -1); 812 m_pVertScrollBar->SetStates(FWL_WGTSTATE_Disabled); 813 m_pVertScrollBar->UnlockUpdate(); 814 m_pVertScrollBar->Update(); 815 pRepaint = m_pVertScrollBar.get(); 816 } 817 } 818 return pRepaint; 819 } 820 821 bool CFWL_Edit::IsShowScrollBar(bool bVert) { 822 if (!bVert) 823 return false; 824 bool bShow = 825 (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_ShowScrollbarFocus) 826 ? (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 827 FWL_WGTSTATE_Focused 828 : true; 829 return bShow && (m_pProperties->m_dwStyles & FWL_WGTSTYLE_VScroll) && 830 (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_MultiLine) && 831 IsContentHeightOverflow(); 832 } 833 834 bool CFWL_Edit::IsContentHeightOverflow() { 835 return m_EdtEngine.GetContentsBoundingBox().height > m_rtEngine.height + 1.0f; 836 } 837 838 void CFWL_Edit::Layout() { 839 m_rtClient = GetClientRect(); 840 m_rtEngine = m_rtClient; 841 IFWL_ThemeProvider* theme = GetAvailableTheme(); 842 if (!theme) 843 return; 844 845 float fWidth = theme->GetScrollBarWidth(); 846 CFWL_ThemePart part; 847 if (!m_pOuter) { 848 part.m_pWidget = this; 849 CFX_RectF pUIMargin = theme->GetUIMargin(&part); 850 m_rtEngine.Deflate(pUIMargin.left, pUIMargin.top, pUIMargin.width, 851 pUIMargin.height); 852 } else if (m_pOuter->GetClassID() == FWL_Type::DateTimePicker) { 853 part.m_pWidget = m_pOuter; 854 CFX_RectF pUIMargin = theme->GetUIMargin(&part); 855 m_rtEngine.Deflate(pUIMargin.left, pUIMargin.top, pUIMargin.width, 856 pUIMargin.height); 857 } 858 859 bool bShowVertScrollbar = IsShowScrollBar(true); 860 bool bShowHorzScrollbar = IsShowScrollBar(false); 861 if (bShowVertScrollbar) { 862 InitVerticalScrollBar(); 863 864 CFX_RectF rtVertScr; 865 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_OuterScrollbar) { 866 rtVertScr = CFX_RectF(m_rtClient.right() + kEditMargin, m_rtClient.top, 867 fWidth, m_rtClient.height); 868 } else { 869 rtVertScr = CFX_RectF(m_rtClient.right() - fWidth, m_rtClient.top, fWidth, 870 m_rtClient.height); 871 if (bShowHorzScrollbar) 872 rtVertScr.height -= fWidth; 873 m_rtEngine.width -= fWidth; 874 } 875 876 m_pVertScrollBar->SetWidgetRect(rtVertScr); 877 m_pVertScrollBar->RemoveStates(FWL_WGTSTATE_Invisible); 878 m_pVertScrollBar->Update(); 879 } else if (m_pVertScrollBar) { 880 m_pVertScrollBar->SetStates(FWL_WGTSTATE_Invisible); 881 } 882 883 if (bShowHorzScrollbar) { 884 InitHorizontalScrollBar(); 885 886 CFX_RectF rtHoriScr; 887 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_OuterScrollbar) { 888 rtHoriScr = CFX_RectF(m_rtClient.left, m_rtClient.bottom() + kEditMargin, 889 m_rtClient.width, fWidth); 890 } else { 891 rtHoriScr = CFX_RectF(m_rtClient.left, m_rtClient.bottom() - fWidth, 892 m_rtClient.width, fWidth); 893 if (bShowVertScrollbar) 894 rtHoriScr.width -= fWidth; 895 m_rtEngine.height -= fWidth; 896 } 897 m_pHorzScrollBar->SetWidgetRect(rtHoriScr); 898 m_pHorzScrollBar->RemoveStates(FWL_WGTSTATE_Invisible); 899 m_pHorzScrollBar->Update(); 900 } else if (m_pHorzScrollBar) { 901 m_pHorzScrollBar->SetStates(FWL_WGTSTATE_Invisible); 902 } 903 } 904 905 void CFWL_Edit::LayoutScrollBar() { 906 if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_ShowScrollbarFocus) == 907 0) { 908 return; 909 } 910 911 bool bShowVertScrollbar = IsShowScrollBar(true); 912 bool bShowHorzScrollbar = IsShowScrollBar(false); 913 914 IFWL_ThemeProvider* theme = GetAvailableTheme(); 915 float fWidth = theme ? theme->GetScrollBarWidth() : 0; 916 if (bShowVertScrollbar) { 917 if (!m_pVertScrollBar) { 918 InitVerticalScrollBar(); 919 CFX_RectF rtVertScr; 920 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_OuterScrollbar) { 921 rtVertScr = CFX_RectF(m_rtClient.right() + kEditMargin, m_rtClient.top, 922 fWidth, m_rtClient.height); 923 } else { 924 rtVertScr = CFX_RectF(m_rtClient.right() - fWidth, m_rtClient.top, 925 fWidth, m_rtClient.height); 926 if (bShowHorzScrollbar) 927 rtVertScr.height -= fWidth; 928 } 929 m_pVertScrollBar->SetWidgetRect(rtVertScr); 930 m_pVertScrollBar->Update(); 931 } 932 m_pVertScrollBar->RemoveStates(FWL_WGTSTATE_Invisible); 933 } else if (m_pVertScrollBar) { 934 m_pVertScrollBar->SetStates(FWL_WGTSTATE_Invisible); 935 } 936 937 if (bShowHorzScrollbar) { 938 if (!m_pHorzScrollBar) { 939 InitHorizontalScrollBar(); 940 CFX_RectF rtHoriScr; 941 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_OuterScrollbar) { 942 rtHoriScr = 943 CFX_RectF(m_rtClient.left, m_rtClient.bottom() + kEditMargin, 944 m_rtClient.width, fWidth); 945 } else { 946 rtHoriScr = CFX_RectF(m_rtClient.left, m_rtClient.bottom() - fWidth, 947 m_rtClient.width, fWidth); 948 if (bShowVertScrollbar) 949 rtHoriScr.width -= (fWidth); 950 } 951 m_pHorzScrollBar->SetWidgetRect(rtHoriScr); 952 m_pHorzScrollBar->Update(); 953 } 954 m_pHorzScrollBar->RemoveStates(FWL_WGTSTATE_Invisible); 955 } else if (m_pHorzScrollBar) { 956 m_pHorzScrollBar->SetStates(FWL_WGTSTATE_Invisible); 957 } 958 if (bShowVertScrollbar || bShowHorzScrollbar) 959 UpdateScroll(); 960 } 961 962 CFX_PointF CFWL_Edit::DeviceToEngine(const CFX_PointF& pt) { 963 return pt + CFX_PointF(m_fScrollOffsetX - m_rtEngine.left, 964 m_fScrollOffsetY - m_rtEngine.top - m_fVAlignOffset); 965 } 966 967 void CFWL_Edit::InitVerticalScrollBar() { 968 if (m_pVertScrollBar) 969 return; 970 971 auto prop = pdfium::MakeUnique<CFWL_WidgetProperties>(); 972 prop->m_dwStyleExes = FWL_STYLEEXT_SCB_Vert; 973 prop->m_dwStates = FWL_WGTSTATE_Disabled | FWL_WGTSTATE_Invisible; 974 prop->m_pParent = this; 975 prop->m_pThemeProvider = m_pProperties->m_pThemeProvider; 976 m_pVertScrollBar = pdfium::MakeUnique<CFWL_ScrollBar>(m_pOwnerApp.Get(), 977 std::move(prop), this); 978 } 979 980 void CFWL_Edit::InitHorizontalScrollBar() { 981 if (m_pHorzScrollBar) 982 return; 983 984 auto prop = pdfium::MakeUnique<CFWL_WidgetProperties>(); 985 prop->m_dwStyleExes = FWL_STYLEEXT_SCB_Horz; 986 prop->m_dwStates = FWL_WGTSTATE_Disabled | FWL_WGTSTATE_Invisible; 987 prop->m_pParent = this; 988 prop->m_pThemeProvider = m_pProperties->m_pThemeProvider; 989 m_pHorzScrollBar = pdfium::MakeUnique<CFWL_ScrollBar>(m_pOwnerApp.Get(), 990 std::move(prop), this); 991 } 992 993 void CFWL_Edit::ShowCaret(CFX_RectF* pRect) { 994 if (m_pCaret) { 995 m_pCaret->ShowCaret(); 996 if (!pRect->IsEmpty()) 997 m_pCaret->SetWidgetRect(*pRect); 998 RepaintRect(m_rtEngine); 999 return; 1000 } 1001 1002 CFWL_Widget* pOuter = this; 1003 pRect->Offset(m_pProperties->m_rtWidget.left, m_pProperties->m_rtWidget.top); 1004 while (pOuter->GetOuter()) { 1005 pOuter = pOuter->GetOuter(); 1006 1007 CFX_RectF rtOuter = pOuter->GetWidgetRect(); 1008 pRect->Offset(rtOuter.left, rtOuter.top); 1009 } 1010 1011 CXFA_FFWidget* pXFAWidget = pOuter->GetLayoutItem(); 1012 if (!pXFAWidget) 1013 return; 1014 1015 IXFA_DocEnvironment* pDocEnvironment = 1016 pXFAWidget->GetDoc()->GetDocEnvironment(); 1017 if (!pDocEnvironment) 1018 return; 1019 1020 CFX_RectF rt = pXFAWidget->GetRotateMatrix().TransformRect(*pRect); 1021 pDocEnvironment->DisplayCaret(pXFAWidget, true, &rt); 1022 } 1023 1024 void CFWL_Edit::HideCaret(CFX_RectF* pRect) { 1025 if (m_pCaret) { 1026 m_pCaret->HideCaret(); 1027 RepaintRect(m_rtEngine); 1028 return; 1029 } 1030 1031 CFWL_Widget* pOuter = this; 1032 while (pOuter->GetOuter()) 1033 pOuter = pOuter->GetOuter(); 1034 1035 CXFA_FFWidget* pXFAWidget = pOuter->GetLayoutItem(); 1036 if (!pXFAWidget) 1037 return; 1038 1039 IXFA_DocEnvironment* pDocEnvironment = 1040 pXFAWidget->GetDoc()->GetDocEnvironment(); 1041 if (!pDocEnvironment) 1042 return; 1043 1044 pDocEnvironment->DisplayCaret(pXFAWidget, false, pRect); 1045 } 1046 1047 bool CFWL_Edit::ValidateNumberChar(wchar_t cNum) { 1048 if (!m_bSetRange) 1049 return true; 1050 1051 WideString wsText = m_EdtEngine.GetText(); 1052 if (wsText.IsEmpty()) 1053 return cNum != L'0'; 1054 1055 if (HasSelection()) 1056 return wsText.GetInteger() <= m_iMax; 1057 if (cNum == L'0' && m_CursorPosition == 0) 1058 return false; 1059 1060 int32_t nLen = wsText.GetLength(); 1061 WideString l = wsText.Left(m_CursorPosition); 1062 WideString r = wsText.Right(nLen - m_CursorPosition); 1063 WideString wsNew = l + cNum + r; 1064 return wsNew.GetInteger() <= m_iMax; 1065 } 1066 1067 void CFWL_Edit::InitCaret() { 1068 m_pCaret.reset(); 1069 m_rtCaret = CFX_RectF(); 1070 } 1071 1072 void CFWL_Edit::UpdateCursorRect() { 1073 int32_t bidi_level = 0; 1074 m_rtCaret = CFX_RectF(); 1075 std::tie(bidi_level, m_rtCaret) = 1076 m_EdtEngine.GetCharacterInfo(m_CursorPosition); 1077 // TODO(dsinclair): This should handle bidi level ... 1078 1079 if (m_rtCaret.width == 0 && m_rtCaret.left > 1.0f) 1080 m_rtCaret.left -= 1.0f; 1081 1082 m_rtCaret.width = 1.0f; 1083 } 1084 1085 void CFWL_Edit::SetCursorPosition(size_t position) { 1086 if (m_CursorPosition == position) 1087 return; 1088 1089 m_CursorPosition = position; 1090 UpdateCursorRect(); 1091 OnCaretChanged(); 1092 } 1093 1094 void CFWL_Edit::OnProcessMessage(CFWL_Message* pMessage) { 1095 if (!pMessage) 1096 return; 1097 1098 switch (pMessage->GetType()) { 1099 case CFWL_Message::Type::SetFocus: 1100 OnFocusChanged(pMessage, true); 1101 break; 1102 case CFWL_Message::Type::KillFocus: 1103 OnFocusChanged(pMessage, false); 1104 break; 1105 case CFWL_Message::Type::Mouse: { 1106 CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage); 1107 switch (pMsg->m_dwCmd) { 1108 case FWL_MouseCommand::LeftButtonDown: 1109 OnLButtonDown(pMsg); 1110 break; 1111 case FWL_MouseCommand::LeftButtonUp: 1112 OnLButtonUp(pMsg); 1113 break; 1114 case FWL_MouseCommand::LeftButtonDblClk: 1115 OnButtonDoubleClick(pMsg); 1116 break; 1117 case FWL_MouseCommand::Move: 1118 OnMouseMove(pMsg); 1119 break; 1120 case FWL_MouseCommand::RightButtonDown: 1121 DoRButtonDown(pMsg); 1122 break; 1123 default: 1124 break; 1125 } 1126 break; 1127 } 1128 case CFWL_Message::Type::Key: { 1129 CFWL_MessageKey* pKey = static_cast<CFWL_MessageKey*>(pMessage); 1130 if (pKey->m_dwCmd == FWL_KeyCommand::KeyDown) 1131 OnKeyDown(pKey); 1132 else if (pKey->m_dwCmd == FWL_KeyCommand::Char) 1133 OnChar(pKey); 1134 break; 1135 } 1136 default: 1137 break; 1138 } 1139 CFWL_Widget::OnProcessMessage(pMessage); 1140 } 1141 1142 void CFWL_Edit::OnProcessEvent(CFWL_Event* pEvent) { 1143 if (!pEvent || pEvent->GetType() != CFWL_Event::Type::Scroll) 1144 return; 1145 1146 CFWL_Widget* pSrcTarget = pEvent->m_pSrcTarget; 1147 if ((pSrcTarget == m_pVertScrollBar.get() && m_pVertScrollBar) || 1148 (pSrcTarget == m_pHorzScrollBar.get() && m_pHorzScrollBar)) { 1149 CFWL_EventScroll* pScrollEvent = static_cast<CFWL_EventScroll*>(pEvent); 1150 OnScroll(static_cast<CFWL_ScrollBar*>(pSrcTarget), 1151 pScrollEvent->m_iScrollCode, pScrollEvent->m_fPos); 1152 } 1153 } 1154 1155 void CFWL_Edit::OnDrawWidget(CXFA_Graphics* pGraphics, 1156 const CFX_Matrix& matrix) { 1157 DrawWidget(pGraphics, matrix); 1158 } 1159 1160 void CFWL_Edit::DoRButtonDown(CFWL_MessageMouse* pMsg) { 1161 if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0) 1162 SetFocus(true); 1163 1164 m_CursorPosition = m_EdtEngine.GetIndexForPoint(DeviceToEngine(pMsg->m_pos)); 1165 } 1166 1167 void CFWL_Edit::OnFocusChanged(CFWL_Message* pMsg, bool bSet) { 1168 bool bRepaint = false; 1169 if (bSet) { 1170 m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused; 1171 1172 UpdateVAlignment(); 1173 UpdateOffset(); 1174 UpdateCaret(); 1175 } else if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) { 1176 m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused; 1177 HideCaret(nullptr); 1178 1179 if (HasSelection()) { 1180 ClearSelection(); 1181 bRepaint = true; 1182 } 1183 UpdateOffset(); 1184 } 1185 1186 LayoutScrollBar(); 1187 if (!bRepaint) 1188 return; 1189 1190 CFX_RectF rtInvalidate(0, 0, m_pProperties->m_rtWidget.width, 1191 m_pProperties->m_rtWidget.height); 1192 RepaintRect(rtInvalidate); 1193 } 1194 1195 void CFWL_Edit::OnLButtonDown(CFWL_MessageMouse* pMsg) { 1196 if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled) 1197 return; 1198 1199 m_bLButtonDown = true; 1200 SetGrab(true); 1201 1202 if ((m_pProperties->m_dwStates & FWL_WGTSTATE_Focused) == 0) 1203 SetFocus(true); 1204 1205 bool bRepaint = false; 1206 if (m_EdtEngine.HasSelection()) { 1207 m_EdtEngine.ClearSelection(); 1208 bRepaint = true; 1209 } 1210 1211 size_t index_at_click = 1212 m_EdtEngine.GetIndexForPoint(DeviceToEngine(pMsg->m_pos)); 1213 1214 if (index_at_click != m_CursorPosition && 1215 !!(pMsg->m_dwFlags & FWL_KEYFLAG_Shift)) { 1216 size_t start = std::min(m_CursorPosition, index_at_click); 1217 size_t end = std::max(m_CursorPosition, index_at_click); 1218 1219 m_EdtEngine.SetSelection(start, end - start); 1220 bRepaint = true; 1221 } else { 1222 m_CursorPosition = index_at_click; 1223 } 1224 1225 if (bRepaint) 1226 RepaintRect(m_rtEngine); 1227 } 1228 1229 void CFWL_Edit::OnLButtonUp(CFWL_MessageMouse* pMsg) { 1230 m_bLButtonDown = false; 1231 SetGrab(false); 1232 } 1233 1234 void CFWL_Edit::OnButtonDoubleClick(CFWL_MessageMouse* pMsg) { 1235 size_t click_idx = m_EdtEngine.GetIndexForPoint(DeviceToEngine(pMsg->m_pos)); 1236 size_t start_idx; 1237 size_t count; 1238 std::tie(start_idx, count) = m_EdtEngine.BoundsForWordAt(click_idx); 1239 1240 m_EdtEngine.SetSelection(start_idx, count); 1241 m_CursorPosition = start_idx + count; 1242 RepaintRect(m_rtEngine); 1243 } 1244 1245 void CFWL_Edit::OnMouseMove(CFWL_MessageMouse* pMsg) { 1246 bool shift = !!(pMsg->m_dwFlags & FWL_KEYFLAG_Shift); 1247 if (!m_bLButtonDown || !shift) 1248 return; 1249 1250 size_t old_cursor_pos = m_CursorPosition; 1251 SetCursorPosition(m_EdtEngine.GetIndexForPoint(DeviceToEngine(pMsg->m_pos))); 1252 if (old_cursor_pos == m_CursorPosition) 1253 return; 1254 1255 size_t length = m_EdtEngine.GetLength(); 1256 if (m_CursorPosition > length) 1257 SetCursorPosition(length); 1258 1259 size_t sel_start = 0; 1260 size_t count = 0; 1261 if (m_EdtEngine.HasSelection()) 1262 std::tie(sel_start, count) = m_EdtEngine.GetSelection(); 1263 else 1264 sel_start = old_cursor_pos; 1265 1266 size_t start_pos = std::min(sel_start, m_CursorPosition); 1267 size_t end_pos = std::max(sel_start, m_CursorPosition); 1268 m_EdtEngine.SetSelection(start_pos, end_pos - start_pos); 1269 } 1270 1271 void CFWL_Edit::OnKeyDown(CFWL_MessageKey* pMsg) { 1272 bool bShift = !!(pMsg->m_dwFlags & FWL_KEYFLAG_Shift); 1273 bool bCtrl = !!(pMsg->m_dwFlags & FWL_KEYFLAG_Ctrl); 1274 1275 size_t sel_start = m_CursorPosition; 1276 if (m_EdtEngine.HasSelection()) { 1277 size_t start_idx; 1278 size_t count; 1279 std::tie(start_idx, count) = m_EdtEngine.GetSelection(); 1280 sel_start = start_idx; 1281 } 1282 1283 switch (pMsg->m_dwKeyCode) { 1284 case FWL_VKEY_Left: 1285 SetCursorPosition(m_EdtEngine.GetIndexLeft(m_CursorPosition)); 1286 break; 1287 case FWL_VKEY_Right: 1288 SetCursorPosition(m_EdtEngine.GetIndexRight(m_CursorPosition)); 1289 break; 1290 case FWL_VKEY_Up: 1291 SetCursorPosition(m_EdtEngine.GetIndexUp(m_CursorPosition)); 1292 break; 1293 case FWL_VKEY_Down: 1294 SetCursorPosition(m_EdtEngine.GetIndexDown(m_CursorPosition)); 1295 break; 1296 case FWL_VKEY_Home: 1297 SetCursorPosition( 1298 bCtrl ? 0 : m_EdtEngine.GetIndexAtStartOfLine(m_CursorPosition)); 1299 break; 1300 case FWL_VKEY_End: 1301 SetCursorPosition( 1302 bCtrl ? m_EdtEngine.GetLength() 1303 : m_EdtEngine.GetIndexAtEndOfLine(m_CursorPosition)); 1304 break; 1305 case FWL_VKEY_Delete: { 1306 if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_ReadOnly) || 1307 (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)) { 1308 break; 1309 } 1310 1311 if (m_CursorPosition > 0) { 1312 SetCursorPosition(m_EdtEngine.GetIndexBefore(m_CursorPosition)); 1313 m_EdtEngine.Delete(m_CursorPosition, 1); 1314 } 1315 break; 1316 } 1317 case FWL_VKEY_Insert: 1318 case FWL_VKEY_F2: 1319 case FWL_VKEY_Tab: 1320 default: 1321 break; 1322 } 1323 1324 // Update the selection. 1325 if (bShift && sel_start != m_CursorPosition) { 1326 m_EdtEngine.SetSelection(std::min(sel_start, m_CursorPosition), 1327 std::max(sel_start, m_CursorPosition)); 1328 RepaintRect(m_rtEngine); 1329 } 1330 } 1331 1332 void CFWL_Edit::OnChar(CFWL_MessageKey* pMsg) { 1333 if ((m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_ReadOnly) || 1334 (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)) { 1335 return; 1336 } 1337 1338 wchar_t c = static_cast<wchar_t>(pMsg->m_dwKeyCode); 1339 switch (c) { 1340 case FWL_VKEY_Back: 1341 if (m_CursorPosition > 0) { 1342 SetCursorPosition(m_EdtEngine.GetIndexBefore(m_CursorPosition)); 1343 m_EdtEngine.Delete(m_CursorPosition, 1); 1344 } 1345 break; 1346 case FWL_VKEY_NewLine: 1347 case FWL_VKEY_Escape: 1348 break; 1349 case FWL_VKEY_Tab: 1350 m_EdtEngine.Insert(m_CursorPosition, L"\t"); 1351 SetCursorPosition(m_CursorPosition + 1); 1352 break; 1353 case FWL_VKEY_Return: 1354 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_WantReturn) { 1355 m_EdtEngine.Insert(m_CursorPosition, L"\n"); 1356 SetCursorPosition(m_CursorPosition + 1); 1357 } 1358 break; 1359 default: { 1360 if (!m_pWidgetMgr->IsFormDisabled()) { 1361 if (m_pProperties->m_dwStyleExes & FWL_STYLEEXT_EDT_Number) { 1362 if (((pMsg->m_dwKeyCode < FWL_VKEY_0) && 1363 (pMsg->m_dwKeyCode != 0x2E && pMsg->m_dwKeyCode != 0x2D)) || 1364 pMsg->m_dwKeyCode > FWL_VKEY_9) { 1365 break; 1366 } 1367 if (!ValidateNumberChar(c)) 1368 break; 1369 } 1370 } 1371 if (pMsg->m_dwFlags & kEditingModifier) 1372 break; 1373 1374 m_EdtEngine.Insert(m_CursorPosition, WideString(c)); 1375 SetCursorPosition(m_CursorPosition + 1); 1376 break; 1377 } 1378 } 1379 } 1380 1381 bool CFWL_Edit::OnScroll(CFWL_ScrollBar* pScrollBar, 1382 CFWL_EventScroll::Code dwCode, 1383 float fPos) { 1384 CFX_SizeF fs; 1385 pScrollBar->GetRange(&fs.width, &fs.height); 1386 float iCurPos = pScrollBar->GetPos(); 1387 float fStep = pScrollBar->GetStepSize(); 1388 switch (dwCode) { 1389 case CFWL_EventScroll::Code::Min: { 1390 fPos = fs.width; 1391 break; 1392 } 1393 case CFWL_EventScroll::Code::Max: { 1394 fPos = fs.height; 1395 break; 1396 } 1397 case CFWL_EventScroll::Code::StepBackward: { 1398 fPos -= fStep; 1399 if (fPos < fs.width + fStep / 2) { 1400 fPos = fs.width; 1401 } 1402 break; 1403 } 1404 case CFWL_EventScroll::Code::StepForward: { 1405 fPos += fStep; 1406 if (fPos > fs.height - fStep / 2) { 1407 fPos = fs.height; 1408 } 1409 break; 1410 } 1411 case CFWL_EventScroll::Code::PageBackward: { 1412 fPos -= pScrollBar->GetPageSize(); 1413 if (fPos < fs.width) { 1414 fPos = fs.width; 1415 } 1416 break; 1417 } 1418 case CFWL_EventScroll::Code::PageForward: { 1419 fPos += pScrollBar->GetPageSize(); 1420 if (fPos > fs.height) { 1421 fPos = fs.height; 1422 } 1423 break; 1424 } 1425 case CFWL_EventScroll::Code::Pos: 1426 case CFWL_EventScroll::Code::TrackPos: 1427 case CFWL_EventScroll::Code::None: 1428 break; 1429 case CFWL_EventScroll::Code::EndScroll: 1430 return false; 1431 } 1432 if (iCurPos == fPos) 1433 return true; 1434 1435 pScrollBar->SetPos(fPos); 1436 pScrollBar->SetTrackPos(fPos); 1437 UpdateOffset(pScrollBar, fPos - iCurPos); 1438 UpdateCaret(); 1439 1440 CFX_RectF rect = GetWidgetRect(); 1441 RepaintRect(CFX_RectF(0, 0, rect.width + 2, rect.height + 2)); 1442 return true; 1443 } 1444