• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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