Re-land else-after-returns
[pdfium.git] / fpdfsdk / src / pdfwindow / PWL_EditCtrl.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 "../../include/pdfwindow/PDFWindow.h"
8 #include "../../include/pdfwindow/PWL_Wnd.h"
9 #include "../../include/pdfwindow/PWL_EditCtrl.h"
10 #include "../../include/pdfwindow/PWL_ScrollBar.h"
11 #include "../../include/pdfwindow/PWL_Utils.h"
12 #include "../../include/pdfwindow/PWL_Caret.h"
13 #include "../../include/pdfwindow/PWL_FontMap.h"
14
15 #define IsFloatZero(f)                      ((f) < 0.0001 && (f) > -0.0001)
16 #define IsFloatBigger(fa,fb)                ((fa) > (fb) && !IsFloatZero((fa) - (fb)))
17 #define IsFloatSmaller(fa,fb)               ((fa) < (fb) && !IsFloatZero((fa) - (fb)))
18 #define IsFloatEqual(fa,fb)                 IsFloatZero((fa)-(fb))
19
20 /* ---------------------------- CPWL_EditCtrl ------------------------------ */
21
22 CPWL_EditCtrl::CPWL_EditCtrl() :
23     m_pEdit(NULL),
24     m_pEditCaret(NULL),
25     m_bMouseDown(FALSE),
26     m_pEditNotify(NULL),
27     m_nCharSet(DEFAULT_CHARSET),
28     m_nCodePage(0)
29 {
30     m_pEdit = IFX_Edit::NewEdit();
31     ASSERT(m_pEdit != NULL);
32 }
33
34 CPWL_EditCtrl::~CPWL_EditCtrl()
35 {
36     IFX_Edit::DelEdit(m_pEdit);
37 }
38
39 void CPWL_EditCtrl::OnCreate(PWL_CREATEPARAM& cp)
40 {
41     cp.eCursorType = FXCT_VBEAM;
42 }
43
44 void CPWL_EditCtrl::OnCreated()
45 {
46     SetFontSize(GetCreationParam().fFontSize);
47
48     m_pEdit->SetFontMap(GetFontMap());
49     m_pEdit->SetNotify(this);
50     m_pEdit->Initialize();
51 }
52
53 FX_BOOL CPWL_EditCtrl::IsWndHorV()
54 {
55     CPDF_Matrix mt = GetWindowMatrix();
56     CPDF_Point point1(0,1);
57     CPDF_Point point2(1,1);
58
59     mt.Transform(point1.x, point1.y);
60     mt.Transform(point2.x, point2.y);
61
62     return point2.y == point1.y;
63 }
64
65 void CPWL_EditCtrl::SetCursor()
66 {
67     if (IsValid())
68     {
69         if (IFX_SystemHandler* pSH = GetSystemHandler())
70         {
71             if (IsWndHorV())
72                 pSH->SetCursor(FXCT_VBEAM);
73             else
74                 pSH->SetCursor(FXCT_HBEAM);
75         }
76     }
77 }
78
79 void CPWL_EditCtrl::RePosChildWnd()
80 {
81     m_pEdit->SetPlateRect(GetClientRect());
82 }
83
84 void CPWL_EditCtrl::OnNotify(CPWL_Wnd* pWnd, FX_DWORD msg, intptr_t wParam, intptr_t lParam)
85 {
86     CPWL_Wnd::OnNotify(pWnd,msg,wParam,lParam);
87
88     switch (msg)
89     {
90         case PNM_SETSCROLLINFO:
91             switch (wParam)
92             {
93                 case SBT_VSCROLL:
94                     if (CPWL_Wnd * pChild = GetVScrollBar())
95                     {
96                         pChild->OnNotify(pWnd,PNM_SETSCROLLINFO,wParam,lParam);
97                     }
98                     break;
99             }
100             break;
101         case PNM_SETSCROLLPOS:
102             switch (wParam)
103             {
104                 case SBT_VSCROLL:
105                     if (CPWL_Wnd * pChild = GetVScrollBar())
106                     {
107                         pChild->OnNotify(pWnd,PNM_SETSCROLLPOS,wParam,lParam);
108                     }
109                     break;
110             }
111             break;
112         case PNM_SCROLLWINDOW:
113             {
114                 FX_FLOAT fPos = *(FX_FLOAT*)lParam;
115                 switch (wParam)
116                 {
117                     case SBT_VSCROLL:
118                         m_pEdit->SetScrollPos(CPDF_Point(m_pEdit->GetScrollPos().x,fPos));
119                         break;
120                 }
121             }
122             break;
123         case PNM_SETCARETINFO:
124             {
125                 if (PWL_CARET_INFO * pCaretInfo = (PWL_CARET_INFO *)wParam)
126                 {
127                     SetCaret(pCaretInfo->bVisible,
128                         pCaretInfo->ptHead,
129                         pCaretInfo->ptFoot);
130                 }
131             }
132             break;
133     }
134 }
135
136 void CPWL_EditCtrl::CreateChildWnd(const PWL_CREATEPARAM & cp)
137 {
138     if (!IsReadOnly())
139         CreateEditCaret(cp);
140 }
141
142 void CPWL_EditCtrl::CreateEditCaret(const PWL_CREATEPARAM & cp)
143 {
144     if (!m_pEditCaret)
145     {
146         m_pEditCaret = new CPWL_Caret;
147         m_pEditCaret->SetInvalidRect(GetClientRect());
148
149         PWL_CREATEPARAM ecp = cp;
150         ecp.pParentWnd = this;
151         ecp.dwFlags = PWS_CHILD | PWS_NOREFRESHCLIP;
152         ecp.dwBorderWidth = 0;
153         ecp.nBorderStyle = PBS_SOLID;
154         ecp.rcRectWnd = CPDF_Rect(0,0,0,0);
155
156         m_pEditCaret->Create(ecp);
157     }
158 }
159
160 void CPWL_EditCtrl::SetFontSize(FX_FLOAT fFontSize)
161 {
162     m_pEdit->SetFontSize(fFontSize);
163 }
164
165 FX_FLOAT CPWL_EditCtrl::GetFontSize() const
166 {
167     return m_pEdit->GetFontSize();
168 }
169
170 FX_BOOL CPWL_EditCtrl::OnKeyDown(FX_WORD nChar, FX_DWORD nFlag)
171 {
172     if (m_bMouseDown) return TRUE;
173
174     FX_BOOL bRet = CPWL_Wnd::OnKeyDown(nChar,nFlag);
175
176     //FILTER
177     switch (nChar)
178     {
179     default:
180         return FALSE;
181     case FWL_VKEY_Delete:
182     case FWL_VKEY_Up:
183     case FWL_VKEY_Down:
184     case FWL_VKEY_Left:
185     case FWL_VKEY_Right:
186     case FWL_VKEY_Home:
187     case FWL_VKEY_End:
188     case FWL_VKEY_Insert:
189     case 'C':
190     case 'V':
191     case 'X':
192     case 'A':
193     case 'Z':
194     case 'c':
195     case 'v':
196     case 'x':
197     case 'a':
198     case 'z':
199         break;
200     }
201
202     if (nChar == FWL_VKEY_Delete)
203     {
204         if (m_pEdit->IsSelected())
205             nChar = FWL_VKEY_Unknown;
206     }
207
208     switch (nChar)
209     {
210         case FWL_VKEY_Delete:
211             Delete();
212             return TRUE;
213         case FWL_VKEY_Insert:
214             if (IsSHIFTpressed(nFlag))
215                 PasteText();
216             return TRUE;
217         case FWL_VKEY_Up:
218             m_pEdit->OnVK_UP(IsSHIFTpressed(nFlag),FALSE);
219             return TRUE;
220         case FWL_VKEY_Down:
221             m_pEdit->OnVK_DOWN(IsSHIFTpressed(nFlag),FALSE);
222             return TRUE;
223         case FWL_VKEY_Left:
224             m_pEdit->OnVK_LEFT(IsSHIFTpressed(nFlag),FALSE);
225             return TRUE;
226         case FWL_VKEY_Right:
227             m_pEdit->OnVK_RIGHT(IsSHIFTpressed(nFlag),FALSE);
228             return TRUE;
229         case FWL_VKEY_Home:
230             m_pEdit->OnVK_HOME(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag));
231             return TRUE;
232         case FWL_VKEY_End:
233             m_pEdit->OnVK_END(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag));
234             return TRUE;
235         case FWL_VKEY_Unknown:
236             if (!IsSHIFTpressed(nFlag))
237                 Clear();
238             else
239                 CutText();
240             return TRUE;
241         default:
242             break;
243     }
244
245     return bRet;
246 }
247
248 FX_BOOL CPWL_EditCtrl::OnChar(FX_WORD nChar, FX_DWORD nFlag)
249 {
250     if (m_bMouseDown) return TRUE;
251
252     CPWL_Wnd::OnChar(nChar,nFlag);
253
254     //FILTER
255     switch (nChar)
256     {
257         case 0x0A:
258         case 0x1B:
259             return FALSE;
260         default:
261             break;
262     }
263
264     FX_BOOL bCtrl = IsCTRLpressed(nFlag);
265     FX_BOOL bAlt = IsALTpressed(nFlag);
266     FX_BOOL bShift = IsSHIFTpressed(nFlag);
267
268     FX_WORD word = nChar;
269
270     if (bCtrl && !bAlt)
271     {
272         switch (nChar)
273         {
274             case 'C' - 'A' + 1:
275                 CopyText();
276                 return TRUE;
277             case 'V' - 'A' + 1:
278                 PasteText();
279                 return TRUE;
280             case 'X' - 'A' + 1:
281                 CutText();
282                 return TRUE;
283             case 'A' - 'A' + 1:
284                 SelectAll();
285                 return TRUE;
286             case 'Z' - 'A' + 1:
287                 if (bShift)
288                     Redo();
289                 else
290                     Undo();
291                 return TRUE;
292             default:
293                 if (nChar < 32)
294                     return FALSE;
295         }
296     }
297
298     if (IsReadOnly()) return TRUE;
299
300     if (m_pEdit->IsSelected() && word ==  FWL_VKEY_Back)
301         word = FWL_VKEY_Unknown;
302
303     Clear();
304
305     switch (word)
306     {
307     case FWL_VKEY_Back:
308         Backspace();
309         break;
310     case FWL_VKEY_Return:
311         InsertReturn();
312         break;
313     case FWL_VKEY_Unknown:
314         break;
315     default:
316         if (IsINSERTpressed(nFlag))
317             Delete();
318         InsertWord(word, GetCharSet());
319         break;
320     }
321
322     return TRUE;
323 }
324
325 FX_BOOL CPWL_EditCtrl::OnLButtonDown(const CPDF_Point & point, FX_DWORD nFlag)
326 {
327     CPWL_Wnd::OnLButtonDown(point,nFlag);
328
329     if (ClientHitTest(point))
330     {
331         if (m_bMouseDown)
332             InvalidateRect();
333
334         m_bMouseDown = TRUE;
335         SetCapture();
336
337         m_pEdit->OnMouseDown(point,IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag));
338     }
339
340     return TRUE;
341 }
342
343 FX_BOOL CPWL_EditCtrl::OnLButtonUp(const CPDF_Point & point, FX_DWORD nFlag)
344 {
345     CPWL_Wnd::OnLButtonUp(point,nFlag);
346
347     if (m_bMouseDown)
348     {
349         //can receive keybord message
350         if (ClientHitTest(point) && !IsFocused())
351             SetFocus();
352
353         ReleaseCapture();
354         m_bMouseDown = FALSE;
355     }
356
357     return TRUE;
358 }
359
360 FX_BOOL CPWL_EditCtrl::OnMouseMove(const CPDF_Point & point, FX_DWORD nFlag)
361 {
362     CPWL_Wnd::OnMouseMove(point,nFlag);
363
364     if (m_bMouseDown)
365         m_pEdit->OnMouseMove(point,FALSE,FALSE);
366
367     return TRUE;
368 }
369
370 CPDF_Rect CPWL_EditCtrl::GetContentRect() const
371 {
372     return m_pEdit->GetContentRect();
373 }
374
375 void CPWL_EditCtrl::SetEditCaret(FX_BOOL bVisible)
376 {
377     CPDF_Point ptHead(0,0),ptFoot(0,0);
378
379     if (bVisible)
380     {
381         GetCaretInfo(ptHead,ptFoot);
382     }
383
384     CPVT_WordPlace wpTemp = m_pEdit->GetCaretWordPlace();
385     IOnSetCaret(bVisible,ptHead,ptFoot,wpTemp);
386 }
387
388 void CPWL_EditCtrl::GetCaretInfo(CPDF_Point & ptHead, CPDF_Point & ptFoot) const
389 {
390     if (IFX_Edit_Iterator * pIterator = m_pEdit->GetIterator())
391     {
392         pIterator->SetAt(m_pEdit->GetCaret());
393         CPVT_Word word;
394         CPVT_Line line;
395         if (pIterator->GetWord(word))
396         {
397             ptHead.x = word.ptWord.x + word.fWidth;
398             ptHead.y = word.ptWord.y + word.fAscent;
399             ptFoot.x = word.ptWord.x + word.fWidth;
400             ptFoot.y = word.ptWord.y + word.fDescent;
401         }
402         else if (pIterator->GetLine(line))
403         {
404             ptHead.x = line.ptLine.x;
405             ptHead.y = line.ptLine.y + line.fLineAscent;
406             ptFoot.x = line.ptLine.x;
407             ptFoot.y = line.ptLine.y + line.fLineDescent;
408         }
409     }
410 }
411
412 void CPWL_EditCtrl::GetCaretPos(int32_t& x, int32_t& y) const
413 {
414     CPDF_Point ptHead(0,0), ptFoot(0,0);
415
416     GetCaretInfo(ptHead,ptFoot);
417
418     PWLtoWnd(ptHead, x, y);
419 }
420
421 void CPWL_EditCtrl::SetCaret(FX_BOOL bVisible, const CPDF_Point & ptHead, const CPDF_Point & ptFoot)
422 {
423     if (m_pEditCaret)
424     {
425         if (!IsFocused() || m_pEdit->IsSelected())
426             bVisible = FALSE;
427
428         m_pEditCaret->SetCaret(bVisible, ptHead, ptFoot);
429     }
430 }
431
432 FX_BOOL CPWL_EditCtrl::IsModified() const
433 {
434     return m_pEdit->IsModified();
435 }
436
437 CFX_WideString CPWL_EditCtrl::GetText() const
438 {
439     return m_pEdit->GetText();
440 }
441
442 void CPWL_EditCtrl::SetSel(int32_t nStartChar,int32_t nEndChar)
443 {
444     m_pEdit->SetSel(nStartChar, nEndChar);
445 }
446
447 void CPWL_EditCtrl::GetSel(int32_t & nStartChar, int32_t & nEndChar ) const
448 {
449     m_pEdit->GetSel(nStartChar, nEndChar);
450 }
451
452 void CPWL_EditCtrl::Clear()
453 {
454     if (!IsReadOnly())
455         m_pEdit->Clear();
456 }
457
458 void CPWL_EditCtrl::SelectAll()
459 {
460     m_pEdit->SelectAll();
461 }
462
463 void CPWL_EditCtrl::Paint()
464 {
465     if (m_pEdit)
466         m_pEdit->Paint();
467 }
468
469 void CPWL_EditCtrl::EnableRefresh(FX_BOOL bRefresh)
470 {
471     if (m_pEdit)
472         m_pEdit->EnableRefresh(bRefresh);
473 }
474
475 int32_t CPWL_EditCtrl::GetCaret() const
476 {
477     if (m_pEdit)
478         return m_pEdit->GetCaret();
479
480     return -1;
481 }
482
483 void CPWL_EditCtrl::SetCaret(int32_t nPos)
484 {
485     if (m_pEdit)
486         m_pEdit->SetCaret(nPos);
487 }
488
489 int32_t CPWL_EditCtrl::GetTotalWords() const
490 {
491     if (m_pEdit)
492         return m_pEdit->GetTotalWords();
493
494     return 0;
495 }
496
497 void CPWL_EditCtrl::SetScrollPos(const CPDF_Point& point)
498 {
499     if (m_pEdit)
500         m_pEdit->SetScrollPos(point);
501 }
502
503 CPDF_Point CPWL_EditCtrl::GetScrollPos() const
504 {
505     if (m_pEdit)
506         return m_pEdit->GetScrollPos();
507
508     return CPDF_Point(0.0f, 0.0f);
509 }
510
511 CPDF_Font * CPWL_EditCtrl::GetCaretFont() const
512 {
513     int32_t nFontIndex = 0;
514
515     if (IFX_Edit_Iterator * pIterator = m_pEdit->GetIterator())
516     {
517         pIterator->SetAt(m_pEdit->GetCaret());
518         CPVT_Word word;
519         CPVT_Section section;
520         if (pIterator->GetWord(word))
521         {
522             nFontIndex = word.nFontIndex;
523         }
524         else if (HasFlag(PES_RICH))
525         {
526             if (pIterator->GetSection(section))
527             {
528                 nFontIndex = section.WordProps.nFontIndex;
529             }
530         }
531     }
532
533     if (IFX_Edit_FontMap* pFontMap = GetFontMap())
534         return pFontMap->GetPDFFont(nFontIndex);
535
536     return NULL;
537 }
538
539 FX_FLOAT CPWL_EditCtrl::GetCaretFontSize() const
540 {
541     FX_FLOAT fFontSize = GetFontSize();
542
543     if (IFX_Edit_Iterator * pIterator = m_pEdit->GetIterator())
544     {
545         pIterator->SetAt(m_pEdit->GetCaret());
546         CPVT_Word word;
547         CPVT_Section section;
548         if (pIterator->GetWord(word))
549         {
550             fFontSize = word.fFontSize;
551         }
552         else if (HasFlag(PES_RICH))
553         {
554             if (pIterator->GetSection(section))
555             {
556                 fFontSize = section.WordProps.fFontSize;
557             }
558         }
559     }
560
561     return fFontSize;
562 }
563
564 void CPWL_EditCtrl::SetText(const FX_WCHAR* csText)
565 {
566     m_pEdit->SetText(csText);
567 }
568
569 void CPWL_EditCtrl::CopyText()
570 {
571 }
572
573 void CPWL_EditCtrl::PasteText()
574 {
575 }
576
577 void CPWL_EditCtrl::CutText()
578 {
579 }
580
581 void CPWL_EditCtrl::ShowVScrollBar(FX_BOOL bShow)
582 {
583 }
584
585 void CPWL_EditCtrl::InsertText(const FX_WCHAR* csText)
586 {
587     if (!IsReadOnly())
588         m_pEdit->InsertText(csText);
589 }
590
591 void CPWL_EditCtrl::InsertWord(FX_WORD word, int32_t nCharset)
592 {
593     if (!IsReadOnly())
594         m_pEdit->InsertWord(word, nCharset);
595 }
596
597 void CPWL_EditCtrl::InsertReturn()
598 {
599     if (!IsReadOnly())
600         m_pEdit->InsertReturn();
601 }
602
603 void CPWL_EditCtrl::Delete()
604 {
605     if (!IsReadOnly())
606         m_pEdit->Delete();
607 }
608
609 void CPWL_EditCtrl::Backspace()
610 {
611     if (!IsReadOnly())
612         m_pEdit->Backspace();
613 }
614
615 FX_BOOL CPWL_EditCtrl::CanUndo() const
616 {
617     return !IsReadOnly() && m_pEdit->CanUndo();
618 }
619
620 FX_BOOL CPWL_EditCtrl::CanRedo() const
621 {
622     return !IsReadOnly() && m_pEdit->CanRedo();
623 }
624
625 void CPWL_EditCtrl::Redo()
626 {
627     if (CanRedo())
628         m_pEdit->Redo();
629 }
630
631 void CPWL_EditCtrl::Undo()
632 {
633     if (CanUndo())
634         m_pEdit->Undo();
635 }
636
637 void CPWL_EditCtrl::IOnSetScrollInfoY(FX_FLOAT fPlateMin, FX_FLOAT fPlateMax,
638                                                 FX_FLOAT fContentMin, FX_FLOAT fContentMax,
639                                                 FX_FLOAT fSmallStep, FX_FLOAT fBigStep)
640 {
641     PWL_SCROLL_INFO Info;
642
643     Info.fPlateWidth = fPlateMax - fPlateMin;
644     Info.fContentMin = fContentMin;
645     Info.fContentMax = fContentMax;
646     Info.fSmallStep = fSmallStep;
647     Info.fBigStep = fBigStep;
648
649     OnNotify(this,PNM_SETSCROLLINFO,SBT_VSCROLL,(intptr_t)&Info);
650
651     if (IsFloatBigger(Info.fPlateWidth,Info.fContentMax-Info.fContentMin)
652         || IsFloatEqual(Info.fPlateWidth,Info.fContentMax-Info.fContentMin))
653     {
654         ShowVScrollBar(FALSE);
655     }
656     else
657     {
658         ShowVScrollBar(TRUE);
659     }
660 }
661
662 void CPWL_EditCtrl::IOnSetScrollPosY(FX_FLOAT fy)
663 {
664     OnNotify(this, PNM_SETSCROLLPOS,SBT_VSCROLL, (intptr_t)&fy);
665 }
666
667 void CPWL_EditCtrl::IOnSetCaret(FX_BOOL bVisible, const CPDF_Point & ptHead, const CPDF_Point & ptFoot, const CPVT_WordPlace& place)
668 {
669     PWL_CARET_INFO cInfo;
670     cInfo.bVisible = bVisible;
671     cInfo.ptHead = ptHead;
672     cInfo.ptFoot = ptFoot;
673
674     OnNotify(this, PNM_SETCARETINFO, (intptr_t)&cInfo, (intptr_t)NULL);
675 }
676
677 void CPWL_EditCtrl::IOnCaretChange(const CPVT_SecProps & secProps, const CPVT_WordProps & wordProps)
678 {
679 }
680
681 void CPWL_EditCtrl::IOnContentChange(const CPDF_Rect& rcContent)
682 {
683     if (IsValid())
684     {
685         if (m_pEditNotify)
686         {
687             m_pEditNotify->OnContentChange(rcContent);
688         }
689     }
690 }
691
692 void CPWL_EditCtrl::IOnInvalidateRect(CPDF_Rect * pRect)
693 {
694     InvalidateRect(pRect);
695 }
696
697 int32_t CPWL_EditCtrl::GetCharSet() const
698 {
699     return m_nCharSet < 0 ? DEFAULT_CHARSET : m_nCharSet;
700 }
701
702 void CPWL_EditCtrl::GetTextRange(const CPDF_Rect& rect, int32_t & nStartChar, int32_t & nEndChar) const
703 {
704     nStartChar = m_pEdit->WordPlaceToWordIndex(m_pEdit->SearchWordPlace(CPDF_Point(rect.left, rect.top)));
705     nEndChar = m_pEdit->WordPlaceToWordIndex(m_pEdit->SearchWordPlace(CPDF_Point(rect.right, rect.bottom)));
706 }
707
708 CFX_WideString CPWL_EditCtrl::GetText(int32_t & nStartChar, int32_t & nEndChar) const
709 {
710     CPVT_WordPlace wpStart = m_pEdit->WordIndexToWordPlace(nStartChar);
711     CPVT_WordPlace wpEnd = m_pEdit->WordIndexToWordPlace(nEndChar);
712     return m_pEdit->GetRangeText(CPVT_WordRange(wpStart, wpEnd));
713 }
714
715 void    CPWL_EditCtrl::SetReadyToInput()
716 {
717     if (m_bMouseDown)
718     {
719         ReleaseCapture();
720         m_bMouseDown = FALSE;
721     }
722 }