Add type cast definitions for CPDF_Boolean.
[pdfium.git] / fpdfsdk / src / pdfwindow / PWL_Edit.cpp
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 "../../../core/include/fxcrt/fx_safe_types.h"
8 #include "../../../core/include/fxcrt/fx_xml.h"
9 #include "../../include/pdfwindow/PDFWindow.h"
10 #include "../../include/pdfwindow/PWL_Caret.h"
11 #include "../../include/pdfwindow/PWL_Edit.h"
12 #include "../../include/pdfwindow/PWL_EditCtrl.h"
13 #include "../../include/pdfwindow/PWL_FontMap.h"
14 #include "../../include/pdfwindow/PWL_ScrollBar.h"
15 #include "../../include/pdfwindow/PWL_Utils.h"
16 #include "../../include/pdfwindow/PWL_Wnd.h"
17
18 /* ---------------------------- CPWL_Edit ------------------------------ */
19
20 CPWL_Edit::CPWL_Edit()
21     : m_pFillerNotify(NULL), m_pSpellCheck(NULL), m_bFocus(FALSE) {
22   m_pFormFiller = NULL;
23 }
24
25 CPWL_Edit::~CPWL_Edit() {
26   ASSERT(m_bFocus == FALSE);
27 }
28
29 CFX_ByteString CPWL_Edit::GetClassName() const {
30   return PWL_CLASSNAME_EDIT;
31 }
32
33 void CPWL_Edit::OnDestroy() {}
34
35 void CPWL_Edit::SetText(const FX_WCHAR* csText) {
36   CFX_WideString swText = csText;
37
38   if (HasFlag(PES_RICH)) {
39     CFX_ByteString sValue = CFX_ByteString::FromUnicode(swText);
40
41     if (CXML_Element* pXML =
42             CXML_Element::Parse(sValue.c_str(), sValue.GetLength())) {
43       int32_t nCount = pXML->CountChildren();
44       FX_BOOL bFirst = TRUE;
45
46       swText.Empty();
47
48       for (int32_t i = 0; i < nCount; i++) {
49         if (CXML_Element* pSubElement = pXML->GetElement(i)) {
50           CFX_ByteString tag = pSubElement->GetTagName();
51           if (tag.EqualNoCase("p")) {
52             int nChild = pSubElement->CountChildren();
53             CFX_WideString swSection;
54             for (int32_t j = 0; j < nChild; j++) {
55               swSection += pSubElement->GetContent(j);
56             }
57
58             if (bFirst)
59               bFirst = FALSE;
60             else
61               swText += FWL_VKEY_Return;
62             swText += swSection;
63           }
64         }
65       }
66
67       delete pXML;
68     }
69   }
70
71   m_pEdit->SetText(swText.c_str());
72 }
73
74 void CPWL_Edit::RePosChildWnd() {
75   if (CPWL_ScrollBar* pVSB = GetVScrollBar()) {
76     CPDF_Rect rcWindow = m_rcOldWindow;
77     CPDF_Rect rcVScroll =
78         CPDF_Rect(rcWindow.right, rcWindow.bottom,
79                   rcWindow.right + PWL_SCROLLBAR_WIDTH, rcWindow.top);
80     pVSB->Move(rcVScroll, TRUE, FALSE);
81   }
82
83   if (m_pEditCaret && !HasFlag(PES_TEXTOVERFLOW))
84     m_pEditCaret->SetClipRect(CPWL_Utils::InflateRect(
85         GetClientRect(), 1.0f));  //+1 for caret beside border
86
87   CPWL_EditCtrl::RePosChildWnd();
88 }
89
90 CPDF_Rect CPWL_Edit::GetClientRect() const {
91   CPDF_Rect rcClient = CPWL_Utils::DeflateRect(
92       GetWindowRect(), (FX_FLOAT)(GetBorderWidth() + GetInnerBorderWidth()));
93
94   if (CPWL_ScrollBar* pVSB = GetVScrollBar()) {
95     if (pVSB->IsVisible()) {
96       rcClient.right -= PWL_SCROLLBAR_WIDTH;
97     }
98   }
99
100   return rcClient;
101 }
102
103 void CPWL_Edit::SetAlignFormatH(PWL_EDIT_ALIGNFORMAT_H nFormat,
104                                 FX_BOOL bPaint /* = TRUE*/) {
105   m_pEdit->SetAlignmentH((int32_t)nFormat, bPaint);
106 }
107
108 void CPWL_Edit::SetAlignFormatV(PWL_EDIT_ALIGNFORMAT_V nFormat,
109                                 FX_BOOL bPaint /* = TRUE*/) {
110   m_pEdit->SetAlignmentV((int32_t)nFormat, bPaint);
111 }
112
113 FX_BOOL CPWL_Edit::CanSelectAll() const {
114   return GetSelectWordRange() != m_pEdit->GetWholeWordRange();
115 }
116
117 FX_BOOL CPWL_Edit::CanClear() const {
118   return !IsReadOnly() && m_pEdit->IsSelected();
119 }
120
121 FX_BOOL CPWL_Edit::CanCopy() const {
122   return !HasFlag(PES_PASSWORD) && !HasFlag(PES_NOREAD) &&
123          m_pEdit->IsSelected();
124 }
125
126 FX_BOOL CPWL_Edit::CanCut() const {
127   return CanCopy() && !IsReadOnly();
128 }
129
130 FX_BOOL CPWL_Edit::CanPaste() const {
131   if (IsReadOnly())
132     return FALSE;
133
134   CFX_WideString swClipboard;
135   if (IFX_SystemHandler* pSH = GetSystemHandler())
136     swClipboard = pSH->GetClipboardText(GetAttachedHWnd());
137
138   return !swClipboard.IsEmpty();
139 }
140
141 void CPWL_Edit::CopyText() {
142   if (!CanCopy())
143     return;
144
145   CFX_WideString str = m_pEdit->GetSelText();
146
147   if (IFX_SystemHandler* pSH = GetSystemHandler())
148     pSH->SetClipboardText(GetAttachedHWnd(), str);
149 }
150
151 void CPWL_Edit::PasteText() {
152   if (!CanPaste())
153     return;
154
155   CFX_WideString swClipboard;
156   if (IFX_SystemHandler* pSH = GetSystemHandler())
157     swClipboard = pSH->GetClipboardText(GetAttachedHWnd());
158
159   if (m_pFillerNotify) {
160     FX_BOOL bRC = TRUE;
161     FX_BOOL bExit = FALSE;
162     CFX_WideString strChangeEx;
163     int nSelStart = 0;
164     int nSelEnd = 0;
165     GetSel(nSelStart, nSelEnd);
166     m_pFillerNotify->OnBeforeKeyStroke(GetAttachedData(), swClipboard,
167                                        strChangeEx, nSelStart, nSelEnd, TRUE,
168                                        bRC, bExit, 0);
169     if (!bRC)
170       return;
171     if (bExit)
172       return;
173   }
174
175   if (swClipboard.GetLength() > 0) {
176     Clear();
177     InsertText(swClipboard.c_str());
178   }
179 }
180
181 void CPWL_Edit::CutText() {
182   if (!CanCut())
183     return;
184
185   CFX_WideString str = m_pEdit->GetSelText();
186
187   if (IFX_SystemHandler* pSH = GetSystemHandler())
188     pSH->SetClipboardText(GetAttachedHWnd(), str);
189
190   m_pEdit->Clear();
191 }
192
193 void CPWL_Edit::OnCreated() {
194   CPWL_EditCtrl::OnCreated();
195
196   if (CPWL_ScrollBar* pScroll = GetVScrollBar()) {
197     pScroll->RemoveFlag(PWS_AUTOTRANSPARENT);
198     pScroll->SetTransparency(255);
199   }
200
201   SetParamByFlag();
202
203   m_rcOldWindow = GetWindowRect();
204
205   m_pEdit->SetOprNotify(this);
206   m_pEdit->EnableOprNotify(TRUE);
207 }
208
209 void CPWL_Edit::SetParamByFlag() {
210   if (HasFlag(PES_RIGHT)) {
211     m_pEdit->SetAlignmentH(2, FALSE);
212   } else if (HasFlag(PES_MIDDLE)) {
213     m_pEdit->SetAlignmentH(1, FALSE);
214   } else {
215     m_pEdit->SetAlignmentH(0, FALSE);
216   }
217
218   if (HasFlag(PES_BOTTOM)) {
219     m_pEdit->SetAlignmentV(2, FALSE);
220   } else if (HasFlag(PES_CENTER)) {
221     m_pEdit->SetAlignmentV(1, FALSE);
222   } else {
223     m_pEdit->SetAlignmentV(0, FALSE);
224   }
225
226   if (HasFlag(PES_PASSWORD)) {
227     m_pEdit->SetPasswordChar('*', FALSE);
228   }
229
230   m_pEdit->SetMultiLine(HasFlag(PES_MULTILINE), FALSE);
231   m_pEdit->SetAutoReturn(HasFlag(PES_AUTORETURN), FALSE);
232   m_pEdit->SetAutoFontSize(HasFlag(PWS_AUTOFONTSIZE), FALSE);
233   m_pEdit->SetAutoScroll(HasFlag(PES_AUTOSCROLL), FALSE);
234   m_pEdit->EnableUndo(HasFlag(PES_UNDO));
235
236   if (HasFlag(PES_TEXTOVERFLOW)) {
237     SetClipRect(CPDF_Rect(0.0f, 0.0f, 0.0f, 0.0f));
238     m_pEdit->SetTextOverflow(TRUE, FALSE);
239   } else {
240     if (m_pEditCaret) {
241       m_pEditCaret->SetClipRect(CPWL_Utils::InflateRect(
242           GetClientRect(), 1.0f));  //+1 for caret beside border
243     }
244   }
245
246   if (HasFlag(PES_SPELLCHECK)) {
247     m_pSpellCheck = GetCreationParam().pSpellCheck;
248   }
249 }
250
251 void CPWL_Edit::GetThisAppearanceStream(CFX_ByteTextBuf& sAppStream) {
252   CPWL_Wnd::GetThisAppearanceStream(sAppStream);
253
254   CPDF_Rect rcClient = GetClientRect();
255   CFX_ByteTextBuf sLine;
256
257   int32_t nCharArray = m_pEdit->GetCharArray();
258
259   if (nCharArray > 0) {
260     switch (GetBorderStyle()) {
261       case PBS_SOLID: {
262         sLine << "q\n" << GetBorderWidth() << " w\n"
263               << CPWL_Utils::GetColorAppStream(GetBorderColor(), FALSE)
264               << " 2 J 0 j\n";
265
266         for (int32_t i = 1; i < nCharArray; i++) {
267           sLine << rcClient.left +
268                        ((rcClient.right - rcClient.left) / nCharArray) * i
269                 << " " << rcClient.bottom << " m\n"
270                 << rcClient.left +
271                        ((rcClient.right - rcClient.left) / nCharArray) * i
272                 << " " << rcClient.top << " l S\n";
273         }
274
275         sLine << "Q\n";
276       } break;
277       case PBS_DASH: {
278         sLine << "q\n" << GetBorderWidth() << " w\n"
279               << CPWL_Utils::GetColorAppStream(GetBorderColor(), FALSE)
280               << " 2 J 0 j\n"
281               << "[" << GetBorderDash().nDash << " " << GetBorderDash().nGap
282               << "] " << GetBorderDash().nPhase << " d\n";
283
284         for (int32_t i = 1; i < nCharArray; i++) {
285           sLine << rcClient.left +
286                        ((rcClient.right - rcClient.left) / nCharArray) * i
287                 << " " << rcClient.bottom << " m\n"
288                 << rcClient.left +
289                        ((rcClient.right - rcClient.left) / nCharArray) * i
290                 << " " << rcClient.top << " l S\n";
291         }
292
293         sLine << "Q\n";
294       } break;
295     }
296   }
297
298   sAppStream << sLine;
299
300   CFX_ByteTextBuf sText;
301
302   CPDF_Point ptOffset = CPDF_Point(0.0f, 0.0f);
303
304   CPVT_WordRange wrWhole = m_pEdit->GetWholeWordRange();
305   CPVT_WordRange wrSelect = GetSelectWordRange();
306   CPVT_WordRange wrVisible =
307       (HasFlag(PES_TEXTOVERFLOW) ? wrWhole : m_pEdit->GetVisibleWordRange());
308   CPVT_WordRange wrSelBefore(wrWhole.BeginPos, wrSelect.BeginPos);
309   CPVT_WordRange wrSelAfter(wrSelect.EndPos, wrWhole.EndPos);
310
311   CPVT_WordRange wrTemp =
312       CPWL_Utils::OverlapWordRange(GetSelectWordRange(), wrVisible);
313   CFX_ByteString sEditSel =
314       CPWL_Utils::GetEditSelAppStream(m_pEdit, ptOffset, &wrTemp);
315
316   if (sEditSel.GetLength() > 0)
317     sText << CPWL_Utils::GetColorAppStream(PWL_DEFAULT_SELBACKCOLOR)
318           << sEditSel;
319
320   wrTemp = CPWL_Utils::OverlapWordRange(wrVisible, wrSelBefore);
321   CFX_ByteString sEditBefore = CPWL_Utils::GetEditAppStream(
322       m_pEdit, ptOffset, &wrTemp, !HasFlag(PES_CHARARRAY),
323       m_pEdit->GetPasswordChar());
324
325   if (sEditBefore.GetLength() > 0)
326     sText << "BT\n" << CPWL_Utils::GetColorAppStream(GetTextColor())
327           << sEditBefore << "ET\n";
328
329   wrTemp = CPWL_Utils::OverlapWordRange(wrVisible, wrSelect);
330   CFX_ByteString sEditMid = CPWL_Utils::GetEditAppStream(
331       m_pEdit, ptOffset, &wrTemp, !HasFlag(PES_CHARARRAY),
332       m_pEdit->GetPasswordChar());
333
334   if (sEditMid.GetLength() > 0)
335     sText << "BT\n"
336           << CPWL_Utils::GetColorAppStream(CPWL_Color(COLORTYPE_GRAY, 1))
337           << sEditMid << "ET\n";
338
339   wrTemp = CPWL_Utils::OverlapWordRange(wrVisible, wrSelAfter);
340   CFX_ByteString sEditAfter = CPWL_Utils::GetEditAppStream(
341       m_pEdit, ptOffset, &wrTemp, !HasFlag(PES_CHARARRAY),
342       m_pEdit->GetPasswordChar());
343
344   if (sEditAfter.GetLength() > 0)
345     sText << "BT\n" << CPWL_Utils::GetColorAppStream(GetTextColor())
346           << sEditAfter << "ET\n";
347
348   if (HasFlag(PES_SPELLCHECK)) {
349     CFX_ByteString sSpellCheck = CPWL_Utils::GetSpellCheckAppStream(
350         m_pEdit, m_pSpellCheck, ptOffset, &wrVisible);
351     if (sSpellCheck.GetLength() > 0)
352       sText << CPWL_Utils::GetColorAppStream(CPWL_Color(COLORTYPE_RGB, 1, 0, 0),
353                                              FALSE)
354             << sSpellCheck;
355   }
356
357   if (sText.GetLength() > 0) {
358     CPDF_Rect rcClient = GetClientRect();
359     sAppStream << "q\n/Tx BMC\n";
360
361     if (!HasFlag(PES_TEXTOVERFLOW))
362       sAppStream << rcClient.left << " " << rcClient.bottom << " "
363                  << rcClient.right - rcClient.left << " "
364                  << rcClient.top - rcClient.bottom << " re W n\n";
365
366     sAppStream << sText;
367
368     sAppStream << "EMC\nQ\n";
369   }
370 }
371
372 void CPWL_Edit::DrawThisAppearance(CFX_RenderDevice* pDevice,
373                                    CPDF_Matrix* pUser2Device) {
374   CPWL_Wnd::DrawThisAppearance(pDevice, pUser2Device);
375
376   CPDF_Rect rcClient = GetClientRect();
377   CFX_ByteTextBuf sLine;
378
379   int32_t nCharArray = m_pEdit->GetCharArray();
380   FX_SAFE_INT32 nCharArraySafe = nCharArray;
381   nCharArraySafe -= 1;
382   nCharArraySafe *= 2;
383
384   if (nCharArray > 0 && nCharArraySafe.IsValid()) {
385     switch (GetBorderStyle()) {
386       case PBS_SOLID: {
387         CFX_GraphStateData gsd;
388         gsd.m_LineWidth = (FX_FLOAT)GetBorderWidth();
389
390         CFX_PathData path;
391         path.SetPointCount(nCharArraySafe.ValueOrDie());
392
393         for (int32_t i = 0; i < nCharArray - 1; i++) {
394           path.SetPoint(
395               i * 2,
396               rcClient.left +
397                   ((rcClient.right - rcClient.left) / nCharArray) * (i + 1),
398               rcClient.bottom, FXPT_MOVETO);
399           path.SetPoint(
400               i * 2 + 1,
401               rcClient.left +
402                   ((rcClient.right - rcClient.left) / nCharArray) * (i + 1),
403               rcClient.top, FXPT_LINETO);
404         }
405         if (path.GetPointCount() > 0)
406           pDevice->DrawPath(
407               &path, pUser2Device, &gsd, 0,
408               CPWL_Utils::PWLColorToFXColor(GetBorderColor(), 255),
409               FXFILL_ALTERNATE);
410       } break;
411       case PBS_DASH: {
412         CFX_GraphStateData gsd;
413         gsd.m_LineWidth = (FX_FLOAT)GetBorderWidth();
414
415         gsd.SetDashCount(2);
416         gsd.m_DashArray[0] = (FX_FLOAT)GetBorderDash().nDash;
417         gsd.m_DashArray[1] = (FX_FLOAT)GetBorderDash().nGap;
418         gsd.m_DashPhase = (FX_FLOAT)GetBorderDash().nPhase;
419
420         CFX_PathData path;
421         path.SetPointCount(nCharArraySafe.ValueOrDie());
422
423         for (int32_t i = 0; i < nCharArray - 1; i++) {
424           path.SetPoint(
425               i * 2,
426               rcClient.left +
427                   ((rcClient.right - rcClient.left) / nCharArray) * (i + 1),
428               rcClient.bottom, FXPT_MOVETO);
429           path.SetPoint(
430               i * 2 + 1,
431               rcClient.left +
432                   ((rcClient.right - rcClient.left) / nCharArray) * (i + 1),
433               rcClient.top, FXPT_LINETO);
434         }
435         if (path.GetPointCount() > 0)
436           pDevice->DrawPath(
437               &path, pUser2Device, &gsd, 0,
438               CPWL_Utils::PWLColorToFXColor(GetBorderColor(), 255),
439               FXFILL_ALTERNATE);
440       } break;
441     }
442   }
443
444   CPDF_Rect rcClip;
445   CPVT_WordRange wrRange = m_pEdit->GetVisibleWordRange();
446   CPVT_WordRange* pRange = NULL;
447
448   if (!HasFlag(PES_TEXTOVERFLOW)) {
449     rcClip = GetClientRect();
450     pRange = &wrRange;
451   }
452   IFX_SystemHandler* pSysHandler = GetSystemHandler();
453   IFX_Edit::DrawEdit(
454       pDevice, pUser2Device, m_pEdit,
455       CPWL_Utils::PWLColorToFXColor(GetTextColor(), GetTransparency()),
456       CPWL_Utils::PWLColorToFXColor(GetTextStrokeColor(), GetTransparency()),
457       rcClip, CPDF_Point(0.0f, 0.0f), pRange, pSysHandler, m_pFormFiller);
458
459   if (HasFlag(PES_SPELLCHECK)) {
460     CPWL_Utils::DrawEditSpellCheck(pDevice, pUser2Device, m_pEdit, rcClip,
461                                    CPDF_Point(0.0f, 0.0f), pRange,
462                                    GetCreationParam().pSpellCheck);
463   }
464 }
465
466 FX_BOOL CPWL_Edit::OnLButtonDown(const CPDF_Point& point, FX_DWORD nFlag) {
467   CPWL_Wnd::OnLButtonDown(point, nFlag);
468
469   if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) {
470     if (m_bMouseDown)
471       InvalidateRect();
472
473     m_bMouseDown = TRUE;
474     SetCapture();
475
476     m_pEdit->OnMouseDown(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
477   }
478
479   return TRUE;
480 }
481
482 FX_BOOL CPWL_Edit::OnLButtonDblClk(const CPDF_Point& point, FX_DWORD nFlag) {
483   CPWL_Wnd::OnLButtonDblClk(point, nFlag);
484
485   if (HasFlag(PES_TEXTOVERFLOW) || ClientHitTest(point)) {
486     m_pEdit->SelectAll();
487   }
488
489   return TRUE;
490 }
491
492 #define WM_PWLEDIT_UNDO 0x01
493 #define WM_PWLEDIT_REDO 0x02
494 #define WM_PWLEDIT_CUT 0x03
495 #define WM_PWLEDIT_COPY 0x04
496 #define WM_PWLEDIT_PASTE 0x05
497 #define WM_PWLEDIT_DELETE 0x06
498 #define WM_PWLEDIT_SELECTALL 0x07
499 #define WM_PWLEDIT_SUGGEST 0x08
500
501 FX_BOOL CPWL_Edit::OnRButtonUp(const CPDF_Point& point, FX_DWORD nFlag) {
502   if (m_bMouseDown)
503     return FALSE;
504
505   CPWL_Wnd::OnRButtonUp(point, nFlag);
506
507   if (!HasFlag(PES_TEXTOVERFLOW) && !ClientHitTest(point))
508     return TRUE;
509
510   IFX_SystemHandler* pSH = GetSystemHandler();
511   if (!pSH)
512     return FALSE;
513
514   SetFocus();
515
516   CPVT_WordRange wrLatin = GetLatinWordsRange(point);
517   CFX_WideString swLatin = m_pEdit->GetRangeText(wrLatin);
518
519   FX_HMENU hPopup = pSH->CreatePopupMenu();
520   if (!hPopup)
521     return FALSE;
522
523   CFX_ByteStringArray sSuggestWords;
524   CPDF_Point ptPopup = point;
525
526   if (!IsReadOnly()) {
527     if (HasFlag(PES_SPELLCHECK) && !swLatin.IsEmpty()) {
528       if (m_pSpellCheck) {
529         CFX_ByteString sLatin = CFX_ByteString::FromUnicode(swLatin);
530
531         if (!m_pSpellCheck->CheckWord(sLatin)) {
532           m_pSpellCheck->SuggestWords(sLatin, sSuggestWords);
533
534           int32_t nSuggest = sSuggestWords.GetSize();
535
536           for (int32_t nWord = 0; nWord < nSuggest; nWord++) {
537             pSH->AppendMenuItem(hPopup, WM_PWLEDIT_SUGGEST + nWord,
538                                 sSuggestWords[nWord].UTF8Decode());
539           }
540
541           if (nSuggest > 0)
542             pSH->AppendMenuItem(hPopup, 0, L"");
543
544           ptPopup = GetWordRightBottomPoint(wrLatin.EndPos);
545         }
546       }
547     }
548   }
549
550   IPWL_Provider* pProvider = GetProvider();
551
552   if (HasFlag(PES_UNDO)) {
553     pSH->AppendMenuItem(
554         hPopup, WM_PWLEDIT_UNDO,
555         pProvider ? pProvider->LoadPopupMenuString(0) : L"&Undo");
556     pSH->AppendMenuItem(
557         hPopup, WM_PWLEDIT_REDO,
558         pProvider ? pProvider->LoadPopupMenuString(1) : L"&Redo");
559     pSH->AppendMenuItem(hPopup, 0, L"");
560
561     if (!m_pEdit->CanUndo())
562       pSH->EnableMenuItem(hPopup, WM_PWLEDIT_UNDO, FALSE);
563     if (!m_pEdit->CanRedo())
564       pSH->EnableMenuItem(hPopup, WM_PWLEDIT_REDO, FALSE);
565   }
566
567   pSH->AppendMenuItem(hPopup, WM_PWLEDIT_CUT,
568                       pProvider ? pProvider->LoadPopupMenuString(2) : L"Cu&t");
569   pSH->AppendMenuItem(hPopup, WM_PWLEDIT_COPY,
570                       pProvider ? pProvider->LoadPopupMenuString(3) : L"&Copy");
571   pSH->AppendMenuItem(
572       hPopup, WM_PWLEDIT_PASTE,
573       pProvider ? pProvider->LoadPopupMenuString(4) : L"&Paste");
574   pSH->AppendMenuItem(
575       hPopup, WM_PWLEDIT_DELETE,
576       pProvider ? pProvider->LoadPopupMenuString(5) : L"&Delete");
577
578   CFX_WideString swText = pSH->GetClipboardText(GetAttachedHWnd());
579   if (swText.IsEmpty())
580     pSH->EnableMenuItem(hPopup, WM_PWLEDIT_PASTE, FALSE);
581
582   if (!m_pEdit->IsSelected()) {
583     pSH->EnableMenuItem(hPopup, WM_PWLEDIT_CUT, FALSE);
584     pSH->EnableMenuItem(hPopup, WM_PWLEDIT_COPY, FALSE);
585     pSH->EnableMenuItem(hPopup, WM_PWLEDIT_DELETE, FALSE);
586   }
587
588   if (IsReadOnly()) {
589     pSH->EnableMenuItem(hPopup, WM_PWLEDIT_CUT, FALSE);
590     pSH->EnableMenuItem(hPopup, WM_PWLEDIT_DELETE, FALSE);
591     pSH->EnableMenuItem(hPopup, WM_PWLEDIT_PASTE, FALSE);
592   }
593
594   if (HasFlag(PES_PASSWORD)) {
595     pSH->EnableMenuItem(hPopup, WM_PWLEDIT_CUT, FALSE);
596     pSH->EnableMenuItem(hPopup, WM_PWLEDIT_COPY, FALSE);
597   }
598
599   if (HasFlag(PES_NOREAD)) {
600     pSH->EnableMenuItem(hPopup, WM_PWLEDIT_CUT, FALSE);
601     pSH->EnableMenuItem(hPopup, WM_PWLEDIT_COPY, FALSE);
602   }
603
604   pSH->AppendMenuItem(hPopup, 0, L"");
605   pSH->AppendMenuItem(
606       hPopup, WM_PWLEDIT_SELECTALL,
607       pProvider ? pProvider->LoadPopupMenuString(6) : L"&Select All");
608
609   if (m_pEdit->GetTotalWords() == 0) {
610     pSH->EnableMenuItem(hPopup, WM_PWLEDIT_SELECTALL, FALSE);
611   }
612
613   int32_t x, y;
614   PWLtoWnd(ptPopup, x, y);
615   pSH->ClientToScreen(GetAttachedHWnd(), x, y);
616   pSH->SetCursor(FXCT_ARROW);
617   int32_t nCmd = pSH->TrackPopupMenu(hPopup, x, y, GetAttachedHWnd());
618
619   switch (nCmd) {
620     case WM_PWLEDIT_UNDO:
621       Undo();
622       break;
623     case WM_PWLEDIT_REDO:
624       Redo();
625       break;
626     case WM_PWLEDIT_CUT:
627       CutText();
628       break;
629     case WM_PWLEDIT_COPY:
630       CopyText();
631       break;
632     case WM_PWLEDIT_PASTE:
633       PasteText();
634       break;
635     case WM_PWLEDIT_DELETE:
636       Clear();
637       break;
638     case WM_PWLEDIT_SELECTALL:
639       SelectAll();
640       break;
641     case WM_PWLEDIT_SUGGEST + 0:
642       SetSel(m_pEdit->WordPlaceToWordIndex(wrLatin.BeginPos),
643              m_pEdit->WordPlaceToWordIndex(wrLatin.EndPos));
644       ReplaceSel(sSuggestWords[0].UTF8Decode().c_str());
645       break;
646     case WM_PWLEDIT_SUGGEST + 1:
647       SetSel(m_pEdit->WordPlaceToWordIndex(wrLatin.BeginPos),
648              m_pEdit->WordPlaceToWordIndex(wrLatin.EndPos));
649       ReplaceSel(sSuggestWords[1].UTF8Decode().c_str());
650       break;
651     case WM_PWLEDIT_SUGGEST + 2:
652       SetSel(m_pEdit->WordPlaceToWordIndex(wrLatin.BeginPos),
653              m_pEdit->WordPlaceToWordIndex(wrLatin.EndPos));
654       ReplaceSel(sSuggestWords[2].UTF8Decode().c_str());
655       break;
656     case WM_PWLEDIT_SUGGEST + 3:
657       SetSel(m_pEdit->WordPlaceToWordIndex(wrLatin.BeginPos),
658              m_pEdit->WordPlaceToWordIndex(wrLatin.EndPos));
659       ReplaceSel(sSuggestWords[3].UTF8Decode().c_str());
660       break;
661     case WM_PWLEDIT_SUGGEST + 4:
662       SetSel(m_pEdit->WordPlaceToWordIndex(wrLatin.BeginPos),
663              m_pEdit->WordPlaceToWordIndex(wrLatin.EndPos));
664       ReplaceSel(sSuggestWords[4].UTF8Decode().c_str());
665       break;
666     default:
667       break;
668   }
669
670   pSH->DestroyMenu(hPopup);
671
672   return TRUE;
673 }
674
675 void CPWL_Edit::OnSetFocus() {
676   SetEditCaret(TRUE);
677
678   if (!IsReadOnly()) {
679     if (IPWL_FocusHandler* pFocusHandler = GetFocusHandler())
680       pFocusHandler->OnSetFocus(this);
681   }
682
683   m_bFocus = TRUE;
684 }
685
686 void CPWL_Edit::OnKillFocus() {
687   ShowVScrollBar(FALSE);
688
689   m_pEdit->SelectNone();
690   SetCaret(FALSE, CPDF_Point(0.0f, 0.0f), CPDF_Point(0.0f, 0.0f));
691
692   SetCharSet(0);
693
694   if (!IsReadOnly()) {
695     if (IPWL_FocusHandler* pFocusHandler = GetFocusHandler())
696       pFocusHandler->OnKillFocus(this);
697   }
698
699   m_bFocus = FALSE;
700 }
701
702 void CPWL_Edit::SetHorzScale(int32_t nHorzScale, FX_BOOL bPaint /* = TRUE*/) {
703   m_pEdit->SetHorzScale(nHorzScale, bPaint);
704 }
705
706 void CPWL_Edit::SetCharSpace(FX_FLOAT fCharSpace, FX_BOOL bPaint /* = TRUE*/) {
707   m_pEdit->SetCharSpace(fCharSpace, bPaint);
708 }
709
710 void CPWL_Edit::SetLineLeading(FX_FLOAT fLineLeading,
711                                FX_BOOL bPaint /* = TRUE*/) {
712   m_pEdit->SetLineLeading(fLineLeading, bPaint);
713 }
714
715 CFX_ByteString CPWL_Edit::GetSelectAppearanceStream(
716     const CPDF_Point& ptOffset) const {
717   CPVT_WordRange wr = GetSelectWordRange();
718   return CPWL_Utils::GetEditSelAppStream(m_pEdit, ptOffset, &wr);
719 }
720
721 CPVT_WordRange CPWL_Edit::GetSelectWordRange() const {
722   if (m_pEdit->IsSelected()) {
723     int32_t nStart = -1;
724     int32_t nEnd = -1;
725
726     m_pEdit->GetSel(nStart, nEnd);
727
728     CPVT_WordPlace wpStart = m_pEdit->WordIndexToWordPlace(nStart);
729     CPVT_WordPlace wpEnd = m_pEdit->WordIndexToWordPlace(nEnd);
730
731     return CPVT_WordRange(wpStart, wpEnd);
732   }
733
734   return CPVT_WordRange();
735 }
736
737 CFX_ByteString CPWL_Edit::GetTextAppearanceStream(
738     const CPDF_Point& ptOffset) const {
739   CFX_ByteTextBuf sRet;
740   CFX_ByteString sEdit = CPWL_Utils::GetEditAppStream(m_pEdit, ptOffset);
741
742   if (sEdit.GetLength() > 0) {
743     sRet << "BT\n" << CPWL_Utils::GetColorAppStream(GetTextColor()) << sEdit
744          << "ET\n";
745   }
746
747   return sRet.GetByteString();
748 }
749
750 CFX_ByteString CPWL_Edit::GetCaretAppearanceStream(
751     const CPDF_Point& ptOffset) const {
752   if (m_pEditCaret)
753     return m_pEditCaret->GetCaretAppearanceStream(ptOffset);
754
755   return CFX_ByteString();
756 }
757
758 CPDF_Point CPWL_Edit::GetWordRightBottomPoint(const CPVT_WordPlace& wpWord) {
759   CPDF_Point pt(0.0f, 0.0f);
760
761   if (IFX_Edit_Iterator* pIterator = m_pEdit->GetIterator()) {
762     CPVT_WordPlace wpOld = pIterator->GetAt();
763     pIterator->SetAt(wpWord);
764     CPVT_Word word;
765     if (pIterator->GetWord(word)) {
766       pt = CPDF_Point(word.ptWord.x + word.fWidth,
767                       word.ptWord.y + word.fDescent);
768     }
769
770     pIterator->SetAt(wpOld);
771   }
772
773   return pt;
774 }
775
776 FX_BOOL CPWL_Edit::IsTextFull() const {
777   return m_pEdit->IsTextFull();
778 }
779
780 FX_FLOAT CPWL_Edit::GetCharArrayAutoFontSize(CPDF_Font* pFont,
781                                              const CPDF_Rect& rcPlate,
782                                              int32_t nCharArray) {
783   if (pFont && !pFont->IsStandardFont()) {
784     FX_RECT rcBBox;
785     pFont->GetFontBBox(rcBBox);
786
787     CPDF_Rect rcCell = rcPlate;
788     FX_FLOAT xdiv = rcCell.Width() / nCharArray * 1000.0f / rcBBox.Width();
789     FX_FLOAT ydiv = -rcCell.Height() * 1000.0f / rcBBox.Height();
790
791     return xdiv < ydiv ? xdiv : ydiv;
792   }
793
794   return 0.0f;
795 }
796
797 void CPWL_Edit::SetCharArray(int32_t nCharArray) {
798   if (HasFlag(PES_CHARARRAY) && nCharArray > 0) {
799     m_pEdit->SetCharArray(nCharArray);
800     m_pEdit->SetTextOverflow(TRUE);
801
802     if (HasFlag(PWS_AUTOFONTSIZE)) {
803       if (IFX_Edit_FontMap* pFontMap = GetFontMap()) {
804         FX_FLOAT fFontSize = GetCharArrayAutoFontSize(
805             pFontMap->GetPDFFont(0), GetClientRect(), nCharArray);
806         if (fFontSize > 0.0f) {
807           m_pEdit->SetAutoFontSize(FALSE);
808           m_pEdit->SetFontSize(fFontSize);
809         }
810       }
811     }
812   }
813 }
814
815 void CPWL_Edit::SetLimitChar(int32_t nLimitChar) {
816   m_pEdit->SetLimitChar(nLimitChar);
817 }
818
819 void CPWL_Edit::ReplaceSel(const FX_WCHAR* csText) {
820   m_pEdit->Clear();
821   m_pEdit->InsertText(csText);
822 }
823
824 CPDF_Rect CPWL_Edit::GetFocusRect() const {
825   return CPDF_Rect();
826 }
827
828 void CPWL_Edit::ShowVScrollBar(FX_BOOL bShow) {
829   if (CPWL_ScrollBar* pScroll = GetVScrollBar()) {
830     if (bShow) {
831       if (!pScroll->IsVisible()) {
832         pScroll->SetVisible(TRUE);
833         CPDF_Rect rcWindow = GetWindowRect();
834         m_rcOldWindow = rcWindow;
835         rcWindow.right += PWL_SCROLLBAR_WIDTH;
836         Move(rcWindow, TRUE, TRUE);
837       }
838     } else {
839       if (pScroll->IsVisible()) {
840         pScroll->SetVisible(FALSE);
841         Move(m_rcOldWindow, TRUE, TRUE);
842       }
843     }
844   }
845 }
846
847 FX_BOOL CPWL_Edit::IsVScrollBarVisible() const {
848   if (CPWL_ScrollBar* pScroll = GetVScrollBar()) {
849     return pScroll->IsVisible();
850   }
851
852   return FALSE;
853 }
854
855 void CPWL_Edit::EnableSpellCheck(FX_BOOL bEnabled) {
856   if (bEnabled)
857     AddFlag(PES_SPELLCHECK);
858   else
859     RemoveFlag(PES_SPELLCHECK);
860 }
861
862 FX_BOOL CPWL_Edit::OnKeyDown(FX_WORD nChar, FX_DWORD nFlag) {
863   if (m_bMouseDown)
864     return TRUE;
865
866   if (nChar == FWL_VKEY_Delete) {
867     if (m_pFillerNotify) {
868       FX_BOOL bRC = TRUE;
869       FX_BOOL bExit = FALSE;
870       CFX_WideString strChange;
871       CFX_WideString strChangeEx;
872
873       int nSelStart = 0;
874       int nSelEnd = 0;
875       GetSel(nSelStart, nSelEnd);
876
877       if (nSelStart == nSelEnd)
878         nSelEnd = nSelStart + 1;
879       m_pFillerNotify->OnBeforeKeyStroke(GetAttachedData(), strChange,
880                                          strChangeEx, nSelStart, nSelEnd, TRUE,
881                                          bRC, bExit, nFlag);
882       if (!bRC)
883         return FALSE;
884       if (bExit)
885         return FALSE;
886     }
887   }
888
889   FX_BOOL bRet = CPWL_EditCtrl::OnKeyDown(nChar, nFlag);
890
891   // In case of implementation swallow the OnKeyDown event.
892   if (IsProceedtoOnChar(nChar, nFlag))
893     return TRUE;
894
895   return bRet;
896 }
897
898 /**
899 *In case of implementation swallow the OnKeyDown event.
900 *If the event is swallowed, implementation may do other unexpected things, which
901 *is not the control means to do.
902 */
903 FX_BOOL CPWL_Edit::IsProceedtoOnChar(FX_WORD nKeyCode, FX_DWORD nFlag) {
904   FX_BOOL bCtrl = IsCTRLpressed(nFlag);
905   FX_BOOL bAlt = IsALTpressed(nFlag);
906   if (bCtrl && !bAlt) {
907     // hot keys for edit control.
908     switch (nKeyCode) {
909       case 'C':
910       case 'V':
911       case 'X':
912       case 'A':
913       case 'Z':
914         return TRUE;
915       default:
916         break;
917     }
918   }
919   // control characters.
920   switch (nKeyCode) {
921     case FWL_VKEY_Escape:
922     case FWL_VKEY_Back:
923     case FWL_VKEY_Return:
924     case FWL_VKEY_Space:
925       return TRUE;
926     default:
927       return FALSE;
928   }
929 }
930
931 FX_BOOL CPWL_Edit::OnChar(FX_WORD nChar, FX_DWORD nFlag) {
932   if (m_bMouseDown)
933     return TRUE;
934
935   FX_BOOL bRC = TRUE;
936   FX_BOOL bExit = FALSE;
937
938   if (!IsCTRLpressed(nFlag)) {
939     if (m_pFillerNotify) {
940       CFX_WideString swChange;
941
942       int nSelStart = 0;
943       int nSelEnd = 0;
944       GetSel(nSelStart, nSelEnd);
945
946       switch (nChar) {
947         case FWL_VKEY_Back:
948           if (nSelStart == nSelEnd)
949             nSelStart = nSelEnd - 1;
950           break;
951         case FWL_VKEY_Return:
952           break;
953         default:
954           swChange += nChar;
955           break;
956       }
957
958       CFX_WideString strChangeEx;
959       m_pFillerNotify->OnBeforeKeyStroke(GetAttachedData(), swChange,
960                                          strChangeEx, nSelStart, nSelEnd, TRUE,
961                                          bRC, bExit, nFlag);
962     }
963   }
964
965   if (!bRC)
966     return TRUE;
967   if (bExit)
968     return FALSE;
969
970   if (IFX_Edit_FontMap* pFontMap = GetFontMap()) {
971     int32_t nOldCharSet = GetCharSet();
972     int32_t nNewCharSet = pFontMap->CharSetFromUnicode(nChar, DEFAULT_CHARSET);
973     if (nOldCharSet != nNewCharSet) {
974       SetCharSet(nNewCharSet);
975     }
976   }
977
978   return CPWL_EditCtrl::OnChar(nChar, nFlag);
979 }
980
981 FX_BOOL CPWL_Edit::OnMouseWheel(short zDelta,
982                                 const CPDF_Point& point,
983                                 FX_DWORD nFlag) {
984   if (HasFlag(PES_MULTILINE)) {
985     CPDF_Point ptScroll = GetScrollPos();
986
987     if (zDelta > 0) {
988       ptScroll.y += GetFontSize();
989     } else {
990       ptScroll.y -= GetFontSize();
991     }
992     SetScrollPos(ptScroll);
993
994     return TRUE;
995   }
996
997   return FALSE;
998 }
999
1000 void CPWL_Edit::OnInsertReturn(const CPVT_WordPlace& place,
1001                                const CPVT_WordPlace& oldplace) {
1002   if (HasFlag(PES_SPELLCHECK)) {
1003     m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
1004                                                GetLatinWordsRange(place)));
1005   }
1006
1007   if (m_pEditNotify) {
1008     m_pEditNotify->OnInsertReturn(place, oldplace);
1009   }
1010 }
1011
1012 void CPWL_Edit::OnBackSpace(const CPVT_WordPlace& place,
1013                             const CPVT_WordPlace& oldplace) {
1014   if (HasFlag(PES_SPELLCHECK)) {
1015     m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
1016                                                GetLatinWordsRange(place)));
1017   }
1018
1019   if (m_pEditNotify) {
1020     m_pEditNotify->OnBackSpace(place, oldplace);
1021   }
1022 }
1023
1024 void CPWL_Edit::OnDelete(const CPVT_WordPlace& place,
1025                          const CPVT_WordPlace& oldplace) {
1026   if (HasFlag(PES_SPELLCHECK)) {
1027     m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
1028                                                GetLatinWordsRange(place)));
1029   }
1030
1031   if (m_pEditNotify) {
1032     m_pEditNotify->OnDelete(place, oldplace);
1033   }
1034 }
1035
1036 void CPWL_Edit::OnClear(const CPVT_WordPlace& place,
1037                         const CPVT_WordPlace& oldplace) {
1038   if (HasFlag(PES_SPELLCHECK)) {
1039     m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
1040                                                GetLatinWordsRange(place)));
1041   }
1042
1043   if (m_pEditNotify) {
1044     m_pEditNotify->OnClear(place, oldplace);
1045   }
1046 }
1047
1048 void CPWL_Edit::OnInsertWord(const CPVT_WordPlace& place,
1049                              const CPVT_WordPlace& oldplace) {
1050   if (HasFlag(PES_SPELLCHECK)) {
1051     m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
1052                                                GetLatinWordsRange(place)));
1053   }
1054
1055   if (m_pEditNotify) {
1056     m_pEditNotify->OnInsertWord(place, oldplace);
1057   }
1058 }
1059
1060 void CPWL_Edit::OnSetText(const CPVT_WordPlace& place,
1061                           const CPVT_WordPlace& oldplace) {}
1062
1063 void CPWL_Edit::OnInsertText(const CPVT_WordPlace& place,
1064                              const CPVT_WordPlace& oldplace) {
1065   if (HasFlag(PES_SPELLCHECK)) {
1066     m_pEdit->RefreshWordRange(CombineWordRange(GetLatinWordsRange(oldplace),
1067                                                GetLatinWordsRange(place)));
1068   }
1069
1070   if (m_pEditNotify) {
1071     m_pEditNotify->OnInsertText(place, oldplace);
1072   }
1073 }
1074
1075 void CPWL_Edit::OnAddUndo(IFX_Edit_UndoItem* pUndoItem) {
1076   if (m_pEditNotify) {
1077     m_pEditNotify->OnAddUndo(this);
1078   }
1079 }
1080
1081 CPVT_WordRange CPWL_Edit::CombineWordRange(const CPVT_WordRange& wr1,
1082                                            const CPVT_WordRange& wr2) {
1083   CPVT_WordRange wrRet;
1084
1085   if (wr1.BeginPos.WordCmp(wr2.BeginPos) < 0) {
1086     wrRet.BeginPos = wr1.BeginPos;
1087   } else {
1088     wrRet.BeginPos = wr2.BeginPos;
1089   }
1090
1091   if (wr1.EndPos.WordCmp(wr2.EndPos) < 0) {
1092     wrRet.EndPos = wr2.EndPos;
1093   } else {
1094     wrRet.EndPos = wr1.EndPos;
1095   }
1096
1097   return wrRet;
1098 }
1099
1100 CPVT_WordRange CPWL_Edit::GetLatinWordsRange(const CPDF_Point& point) const {
1101   return GetSameWordsRange(m_pEdit->SearchWordPlace(point), TRUE, FALSE);
1102 }
1103
1104 CPVT_WordRange CPWL_Edit::GetLatinWordsRange(
1105     const CPVT_WordPlace& place) const {
1106   return GetSameWordsRange(place, TRUE, FALSE);
1107 }
1108
1109 CPVT_WordRange CPWL_Edit::GetArabicWordsRange(
1110     const CPVT_WordPlace& place) const {
1111   return GetSameWordsRange(place, FALSE, TRUE);
1112 }
1113
1114 #define PWL_ISARABICWORD(word) \
1115   ((word >= 0x0600 && word <= 0x06FF) || (word >= 0xFB50 && word <= 0xFEFC))
1116
1117 CPVT_WordRange CPWL_Edit::GetSameWordsRange(const CPVT_WordPlace& place,
1118                                             FX_BOOL bLatin,
1119                                             FX_BOOL bArabic) const {
1120   CPVT_WordRange range;
1121
1122   if (IFX_Edit_Iterator* pIterator = m_pEdit->GetIterator()) {
1123     CPVT_Word wordinfo;
1124     CPVT_WordPlace wpStart(place), wpEnd(place);
1125     pIterator->SetAt(place);
1126
1127     if (bLatin) {
1128       while (pIterator->NextWord()) {
1129         if (pIterator->GetWord(wordinfo) &&
1130             FX_EDIT_ISLATINWORD(wordinfo.Word)) {
1131           wpEnd = pIterator->GetAt();
1132           continue;
1133         } else
1134           break;
1135       };
1136     } else if (bArabic) {
1137       while (pIterator->NextWord()) {
1138         if (pIterator->GetWord(wordinfo) && PWL_ISARABICWORD(wordinfo.Word)) {
1139           wpEnd = pIterator->GetAt();
1140           continue;
1141         } else
1142           break;
1143       };
1144     }
1145
1146     pIterator->SetAt(place);
1147
1148     if (bLatin) {
1149       do {
1150         if (pIterator->GetWord(wordinfo) &&
1151             FX_EDIT_ISLATINWORD(wordinfo.Word)) {
1152           continue;
1153         } else {
1154           wpStart = pIterator->GetAt();
1155           break;
1156         }
1157       } while (pIterator->PrevWord());
1158     } else if (bArabic) {
1159       do {
1160         if (pIterator->GetWord(wordinfo) && PWL_ISARABICWORD(wordinfo.Word)) {
1161           continue;
1162         } else {
1163           wpStart = pIterator->GetAt();
1164           break;
1165         }
1166       } while (pIterator->PrevWord());
1167     }
1168
1169     range.Set(wpStart, wpEnd);
1170   }
1171
1172   return range;
1173 }
1174
1175 void CPWL_Edit::GeneratePageObjects(
1176     CPDF_PageObjects* pPageObjects,
1177     const CPDF_Point& ptOffset,
1178     CFX_ArrayTemplate<CPDF_TextObject*>& ObjArray) {
1179   IFX_Edit::GeneratePageObjects(
1180       pPageObjects, m_pEdit, ptOffset, NULL,
1181       CPWL_Utils::PWLColorToFXColor(GetTextColor(), GetTransparency()),
1182       ObjArray);
1183 }
1184
1185 void CPWL_Edit::GeneratePageObjects(CPDF_PageObjects* pPageObjects,
1186                                     const CPDF_Point& ptOffset) {
1187   CFX_ArrayTemplate<CPDF_TextObject*> ObjArray;
1188   IFX_Edit::GeneratePageObjects(
1189       pPageObjects, m_pEdit, ptOffset, NULL,
1190       CPWL_Utils::PWLColorToFXColor(GetTextColor(), GetTransparency()),
1191       ObjArray);
1192 }