blob: 90a198a77119ee1983bff108acd081e29fc0ec78 [file] [log] [blame]
John Abd-El-Malek3f3b45c2014-05-23 17:28:10 -07001// 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
22CPWL_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
34CPWL_EditCtrl::~CPWL_EditCtrl()
35{
36 IFX_Edit::DelEdit(m_pEdit);
37}
38
39void CPWL_EditCtrl::OnCreate(PWL_CREATEPARAM & cp)
40{
41 cp.eCursorType = FXCT_VBEAM;
42}
43
44void CPWL_EditCtrl::OnCreated()
45{
46 SetFontSize(this->GetCreationParam().fFontSize);
47
48 m_pEdit->SetFontMap(this->GetFontMap());
49 m_pEdit->SetNotify(this);
50 m_pEdit->Initialize();
51}
52
53FX_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
65void 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
79void CPWL_EditCtrl::RePosChildWnd()
80{
81 m_pEdit->SetPlateRect(GetClientRect());
82}
83
84void CPWL_EditCtrl::OnNotify(CPWL_Wnd* pWnd, FX_DWORD msg, FX_INTPTR wParam, FX_INTPTR 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 this->SetCaret(pCaretInfo->bVisible,
128 pCaretInfo->ptHead,
129 pCaretInfo->ptFoot);
130 }
131 }
132 break;
133 }
134}
135
136void CPWL_EditCtrl::CreateChildWnd(const PWL_CREATEPARAM & cp)
137{
138 if (!IsReadOnly())
139 CreateEditCaret(cp);
140}
141
142void 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
160void CPWL_EditCtrl::SetFontSize(FX_FLOAT fFontSize)
161{
162 m_pEdit->SetFontSize(fFontSize);
163}
164
165FX_FLOAT CPWL_EditCtrl::GetFontSize() const
166{
167 return m_pEdit->GetFontSize();
168}
169
170FX_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
248FX_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 this->CopyText();
276 return TRUE;
277 case 'V' - 'A' + 1:
278 this->PasteText();
279 return TRUE;
280 case 'X' - 'A' + 1:
281 this->CutText();
282 return TRUE;
283 case 'A' - 'A' + 1:
284 this->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, this->GetCharSet());
319 break;
320 }
321
322 return TRUE;
323}
324
325FX_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 this->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
343FX_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) && !this->IsFocused())
351 SetFocus();
352
353 ReleaseCapture();
354 m_bMouseDown = FALSE;
355 }
356
357 return TRUE;
358}
359
360FX_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
370CPDF_Rect CPWL_EditCtrl::GetContentRect() const
371{
372 return m_pEdit->GetContentRect();
373}
374
375void 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 this->IOnSetCaret(bVisible,ptHead,ptFoot,wpTemp);
386}
387
388void 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
412void CPWL_EditCtrl::GetCaretPos(FX_INT32& x, FX_INT32& 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
421void 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
432FX_BOOL CPWL_EditCtrl::IsModified() const
433{
434 return m_pEdit->IsModified();
435}
436
437CFX_WideString CPWL_EditCtrl::GetText() const
438{
439 return m_pEdit->GetText();
440}
441
442void CPWL_EditCtrl::SetSel(FX_INT32 nStartChar,FX_INT32 nEndChar)
443{
444 m_pEdit->SetSel(nStartChar, nEndChar);
445}
446
447void CPWL_EditCtrl::GetSel(FX_INT32 & nStartChar, FX_INT32 & nEndChar ) const
448{
449 m_pEdit->GetSel(nStartChar, nEndChar);
450}
451
452void CPWL_EditCtrl::Clear()
453{
454 if (!IsReadOnly())
455 m_pEdit->Clear();
456}
457
458void CPWL_EditCtrl::SelectAll()
459{
460 m_pEdit->SelectAll();
461}
462
463void CPWL_EditCtrl::Paint()
464{
465 if (m_pEdit)
466 m_pEdit->Paint();
467}
468
469void CPWL_EditCtrl::EnableRefresh(FX_BOOL bRefresh)
470{
471 if (m_pEdit)
472 m_pEdit->EnableRefresh(bRefresh);
473}
474
475FX_INT32 CPWL_EditCtrl::GetCaret() const
476{
477 if (m_pEdit)
478 return m_pEdit->GetCaret();
479
480 return -1;
481}
482
483void CPWL_EditCtrl::SetCaret(FX_INT32 nPos)
484{
485 if (m_pEdit)
486 m_pEdit->SetCaret(nPos);
487}
488
489FX_INT32 CPWL_EditCtrl::GetTotalWords() const
490{
491 if (m_pEdit)
492 return m_pEdit->GetTotalWords();
493
494 return 0;
495}
496
497void CPWL_EditCtrl::SetScrollPos(const CPDF_Point& point)
498{
499 if (m_pEdit)
500 m_pEdit->SetScrollPos(point);
501}
502
503CPDF_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
511CPDF_Font * CPWL_EditCtrl::GetCaretFont() const
512{
513 FX_INT32 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 else
536 return NULL;
537}
538
539FX_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
564void CPWL_EditCtrl::SetText(FX_LPCWSTR csText)
565{
566 m_pEdit->SetText(csText);
567}
568
569void CPWL_EditCtrl::CopyText()
570{
571}
572
573void CPWL_EditCtrl::PasteText()
574{
575}
576
577void CPWL_EditCtrl::CutText()
578{
579}
580
581void CPWL_EditCtrl::ShowVScrollBar(FX_BOOL bShow)
582{
583}
584
585void CPWL_EditCtrl::InsertText(FX_LPCWSTR csText)
586{
587 if (!IsReadOnly())
588 m_pEdit->InsertText(csText);
589}
590
591void CPWL_EditCtrl::InsertWord(FX_WORD word, FX_INT32 nCharset)
592{
593 if (!IsReadOnly())
594 m_pEdit->InsertWord(word, nCharset);
595}
596
597void CPWL_EditCtrl::InsertReturn()
598{
599 if (!IsReadOnly())
600 m_pEdit->InsertReturn();
601}
602
603void CPWL_EditCtrl::Delete()
604{
605 if (!IsReadOnly())
606 m_pEdit->Delete();
607}
608
609void CPWL_EditCtrl::Backspace()
610{
611 if (!IsReadOnly())
612 m_pEdit->Backspace();
613}
614
615FX_BOOL CPWL_EditCtrl::CanUndo() const
616{
617 return !IsReadOnly() && m_pEdit->CanUndo();
618}
619
620FX_BOOL CPWL_EditCtrl::CanRedo() const
621{
622 return !IsReadOnly() && m_pEdit->CanRedo();
623}
624
625void CPWL_EditCtrl::Redo()
626{
627 if (CanRedo())
628 m_pEdit->Redo();
629}
630
631void CPWL_EditCtrl::Undo()
632{
633 if (CanUndo())
634 m_pEdit->Undo();
635}
636
637void 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 this->OnNotify(this,PNM_SETSCROLLINFO,SBT_VSCROLL,(FX_INTPTR)&Info);
650
651// PWL_TRACE("set scroll info:%f\n",fContentMax - fContentMin);
652
653 if (IsFloatBigger(Info.fPlateWidth,Info.fContentMax-Info.fContentMin)
654 || IsFloatEqual(Info.fPlateWidth,Info.fContentMax-Info.fContentMin))
655 {
656 this->ShowVScrollBar(FALSE);
657 }
658 else
659 {
660 this->ShowVScrollBar(TRUE);
661 }
662}
663
664void CPWL_EditCtrl::IOnSetScrollPosY(FX_FLOAT fy)
665{
666// PWL_TRACE("set scroll position:%f\n",fy);
667 this->OnNotify(this,PNM_SETSCROLLPOS,SBT_VSCROLL,(FX_INTPTR)&fy);
668}
669
670void CPWL_EditCtrl::IOnSetCaret(FX_BOOL bVisible, const CPDF_Point & ptHead, const CPDF_Point & ptFoot, const CPVT_WordPlace& place)
671{
672 PWL_CARET_INFO cInfo;
673 cInfo.bVisible = bVisible;
674 cInfo.ptHead = ptHead;
675 cInfo.ptFoot = ptFoot;
676
677 this->OnNotify(this,PNM_SETCARETINFO,(FX_INTPTR)&cInfo,(FX_INTPTR)NULL);
678}
679
680void CPWL_EditCtrl::IOnCaretChange(const CPVT_SecProps & secProps, const CPVT_WordProps & wordProps)
681{
682}
683
684void CPWL_EditCtrl::IOnContentChange(const CPDF_Rect& rcContent)
685{
686 if (this->IsValid())
687 {
688 if (m_pEditNotify)
689 {
690 m_pEditNotify->OnContentChange(rcContent);
691 }
692 }
693}
694
695void CPWL_EditCtrl::IOnInvalidateRect(CPDF_Rect * pRect)
696{
697 this->InvalidateRect(pRect);
698}
699
700FX_INT32 CPWL_EditCtrl::GetCharSet() const
701{
702 if (m_nCharSet < 0)
703 return DEFAULT_CHARSET;
704 else
705 return m_nCharSet;
706}
707
708void CPWL_EditCtrl::GetTextRange(const CPDF_Rect& rect, FX_INT32 & nStartChar, FX_INT32 & nEndChar) const
709{
710 nStartChar = m_pEdit->WordPlaceToWordIndex(m_pEdit->SearchWordPlace(CPDF_Point(rect.left, rect.top)));
711 nEndChar = m_pEdit->WordPlaceToWordIndex(m_pEdit->SearchWordPlace(CPDF_Point(rect.right, rect.bottom)));
712}
713
714CFX_WideString CPWL_EditCtrl::GetText(FX_INT32 & nStartChar, FX_INT32 & nEndChar) const
715{
716 CPVT_WordPlace wpStart = m_pEdit->WordIndexToWordPlace(nStartChar);
717 CPVT_WordPlace wpEnd = m_pEdit->WordIndexToWordPlace(nEndChar);
718 return m_pEdit->GetRangeText(CPVT_WordRange(wpStart, wpEnd));
719}
720
721void CPWL_EditCtrl::SetReadyToInput()
722{
723 if (m_bMouseDown)
724 {
725 ReleaseCapture();
726 m_bMouseDown = FALSE;
727 }
728}