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