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
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}
729