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