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