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
FxEditIsLatinWord(wchar_t c)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
AddSquigglyPath(CXFA_GEPath * pPathData,float fStartX,float fEndX,float fY,float fStep)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
CFWL_Edit(const CFWL_App * app,std::unique_ptr<CFWL_WidgetProperties> properties,CFWL_Widget * pOuter)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
~CFWL_Edit()86 CFWL_Edit::~CFWL_Edit() {
87 if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused)
88 HideCaret(nullptr);
89 }
90
GetClassID() const91 FWL_Type CFWL_Edit::GetClassID() const {
92 return FWL_Type::Edit;
93 }
94
GetWidgetRect()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
GetAutosizedWidgetRect()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
SetStates(uint32_t dwStates)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
Update()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
HitTest(const CFX_PointF & point)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
AddSpellCheckObj(CXFA_GEPath & PathData,int32_t nStart,int32_t nCount,float fOffSetX,float fOffSetY)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
DrawSpellCheck(CXFA_Graphics * pGraphics,const CFX_Matrix * pMatrix)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
DrawWidget(CXFA_Graphics * pGraphics,const CFX_Matrix & matrix)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
SetThemeProvider(IFWL_ThemeProvider * pThemeProvider)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
SetText(const WideString & wsText)273 void CFWL_Edit::SetText(const WideString& wsText) {
274 m_EdtEngine.Clear();
275 m_EdtEngine.Insert(0, wsText);
276 }
277
GetTextLength() const278 int32_t CFWL_Edit::GetTextLength() const {
279 return m_EdtEngine.GetLength();
280 }
281
GetText() const282 WideString CFWL_Edit::GetText() const {
283 return m_EdtEngine.GetText();
284 }
285
ClearText()286 void CFWL_Edit::ClearText() {
287 m_EdtEngine.Clear();
288 }
289
SelectAll()290 void CFWL_Edit::SelectAll() {
291 m_EdtEngine.SelectAll();
292 }
293
HasSelection() const294 bool CFWL_Edit::HasSelection() const {
295 return m_EdtEngine.HasSelection();
296 }
297
GetSelection() const298 std::pair<size_t, size_t> CFWL_Edit::GetSelection() const {
299 return m_EdtEngine.GetSelection();
300 }
301
ClearSelection()302 void CFWL_Edit::ClearSelection() {
303 return m_EdtEngine.ClearSelection();
304 }
305
GetLimit() const306 int32_t CFWL_Edit::GetLimit() const {
307 return m_nLimit;
308 }
309
SetLimit(int32_t nLimit)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
SetAliasChar(wchar_t wAlias)321 void CFWL_Edit::SetAliasChar(wchar_t wAlias) {
322 m_EdtEngine.SetAliasChar(wAlias);
323 }
324
Copy()325 Optional<WideString> CFWL_Edit::Copy() {
326 if (!m_EdtEngine.HasSelection())
327 return {};
328
329 return {m_EdtEngine.GetSelectedText()};
330 }
331
Cut()332 Optional<WideString> CFWL_Edit::Cut() {
333 if (!m_EdtEngine.HasSelection())
334 return {};
335
336 return {m_EdtEngine.DeleteSelectedText()};
337 }
338
Paste(const WideString & wsPaste)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
Undo()348 bool CFWL_Edit::Undo() {
349 return CanUndo() ? m_EdtEngine.Undo() : false;
350 }
351
Redo()352 bool CFWL_Edit::Redo() {
353 return CanRedo() ? m_EdtEngine.Redo() : false;
354 }
355
CanUndo()356 bool CFWL_Edit::CanUndo() {
357 return m_EdtEngine.CanUndo();
358 }
359
CanRedo()360 bool CFWL_Edit::CanRedo() {
361 return m_EdtEngine.CanRedo();
362 }
363
SetOuter(CFWL_Widget * pOuter)364 void CFWL_Edit::SetOuter(CFWL_Widget* pOuter) {
365 m_pOuter = pOuter;
366 }
367
NotifyTextFull()368 void CFWL_Edit::NotifyTextFull() {
369 CFWL_Event evt(CFWL_Event::Type::TextFull, this);
370 DispatchEvent(&evt);
371 }
372
OnCaretChanged()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
OnTextChanged(const WideString & prevText)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
OnSelChanged()409 void CFWL_Edit::OnSelChanged() {
410 RepaintRect(GetClientRect());
411 }
412
OnValidate(const WideString & wsText)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
SetScrollOffset(float fScrollOffset)425 void CFWL_Edit::SetScrollOffset(float fScrollOffset) {
426 m_fScrollOffsetY = fScrollOffset;
427 }
428
DrawTextBk(CXFA_Graphics * pGraphics,IFWL_ThemeProvider * pTheme,const CFX_Matrix * pMatrix)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
DrawContent(CXFA_Graphics * pGraphics,IFWL_ThemeProvider * pTheme,const CFX_Matrix * pMatrix)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
RenderText(CFX_RenderDevice * pRenderDev,const CFX_RectF & clipRect,const CFX_Matrix & mt)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
UpdateEditEngine()569 void CFWL_Edit::UpdateEditEngine() {
570 UpdateEditParams();
571 UpdateEditLayout();
572 }
573
UpdateEditParams()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
UpdateEditLayout()649 void CFWL_Edit::UpdateEditLayout() {
650 m_EdtEngine.Layout();
651 }
652
UpdateOffset()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
UpdateOffset(CFWL_ScrollBar * pScrollBar,float fPosChanged)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
UpdateVAlignment()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
UpdateCaret()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
UpdateScroll()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
IsShowScrollBar(bool bVert)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
IsContentHeightOverflow()834 bool CFWL_Edit::IsContentHeightOverflow() {
835 return m_EdtEngine.GetContentsBoundingBox().height > m_rtEngine.height + 1.0f;
836 }
837
Layout()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
LayoutScrollBar()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
DeviceToEngine(const CFX_PointF & pt)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
InitVerticalScrollBar()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
InitHorizontalScrollBar()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
ShowCaret(CFX_RectF * pRect)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
HideCaret(CFX_RectF * pRect)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
ValidateNumberChar(wchar_t cNum)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
InitCaret()1067 void CFWL_Edit::InitCaret() {
1068 m_pCaret.reset();
1069 m_rtCaret = CFX_RectF();
1070 }
1071
UpdateCursorRect()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
SetCursorPosition(size_t position)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
OnProcessMessage(CFWL_Message * pMessage)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
OnProcessEvent(CFWL_Event * pEvent)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
OnDrawWidget(CXFA_Graphics * pGraphics,const CFX_Matrix & matrix)1155 void CFWL_Edit::OnDrawWidget(CXFA_Graphics* pGraphics,
1156 const CFX_Matrix& matrix) {
1157 DrawWidget(pGraphics, matrix);
1158 }
1159
DoRButtonDown(CFWL_MessageMouse * pMsg)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
OnFocusChanged(CFWL_Message * pMsg,bool bSet)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
OnLButtonDown(CFWL_MessageMouse * pMsg)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
OnLButtonUp(CFWL_MessageMouse * pMsg)1229 void CFWL_Edit::OnLButtonUp(CFWL_MessageMouse* pMsg) {
1230 m_bLButtonDown = false;
1231 SetGrab(false);
1232 }
1233
OnButtonDoubleClick(CFWL_MessageMouse * pMsg)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
OnMouseMove(CFWL_MessageMouse * pMsg)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
OnKeyDown(CFWL_MessageKey * pMsg)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
OnChar(CFWL_MessageKey * pMsg)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
OnScroll(CFWL_ScrollBar * pScrollBar,CFWL_EventScroll::Code dwCode,float fPos)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