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_ListBox.h"
10#include "../../include/pdfwindow/PWL_Utils.h"
11#include "../../include/pdfwindow/PWL_ScrollBar.h"
12#include "../../include/pdfwindow/PWL_EditCtrl.h"
13#include "../../include/pdfwindow/PWL_Edit.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_List_Notify ----------------------- */
21
22CPWL_List_Notify::CPWL_List_Notify(CPWL_ListBox* pList) : m_pList(pList)
23{
24	ASSERT(m_pList != NULL);
25}
26
27CPWL_List_Notify::~CPWL_List_Notify()
28{
29}
30
31void CPWL_List_Notify::IOnSetScrollInfoY(FX_FLOAT fPlateMin, FX_FLOAT fPlateMax,
32												FX_FLOAT fContentMin, FX_FLOAT fContentMax,
33												FX_FLOAT fSmallStep, FX_FLOAT fBigStep)
34{
35	PWL_SCROLL_INFO Info;
36
37	Info.fPlateWidth = fPlateMax - fPlateMin;
38	Info.fContentMin = fContentMin;
39	Info.fContentMax = fContentMax;
40	Info.fSmallStep = fSmallStep;
41	Info.fBigStep = fBigStep;
42
43	m_pList->OnNotify(m_pList,PNM_SETSCROLLINFO,SBT_VSCROLL,(FX_INTPTR)&Info);
44
45	if (CPWL_ScrollBar * pScroll = m_pList->GetVScrollBar())
46	{
47		if (IsFloatBigger(Info.fPlateWidth,Info.fContentMax-Info.fContentMin)
48			|| IsFloatEqual(Info.fPlateWidth,Info.fContentMax-Info.fContentMin))
49		{
50			if (pScroll->IsVisible())
51			{
52				pScroll->SetVisible(FALSE);
53				m_pList->RePosChildWnd();
54			}
55		}
56		else
57		{
58			if (!pScroll->IsVisible())
59			{
60				pScroll->SetVisible(TRUE);
61				m_pList->RePosChildWnd();
62			}
63		}
64	}
65}
66
67void CPWL_List_Notify::IOnSetScrollPosY(FX_FLOAT fy)
68{
69	m_pList->OnNotify(m_pList,PNM_SETSCROLLPOS,SBT_VSCROLL,(FX_INTPTR)&fy);
70}
71
72void CPWL_List_Notify::IOnInvalidateRect(CPDF_Rect * pRect)
73{
74	m_pList->InvalidateRect(pRect);
75}
76
77/* --------------------------- CPWL_ListBox ---------------------------- */
78
79CPWL_ListBox::CPWL_ListBox() :
80	m_pList(NULL),
81	m_pListNotify(NULL),
82	m_bMouseDown(FALSE),
83	m_bHoverSel(FALSE),
84	m_pFillerNotify(NULL)
85{
86	m_pList = IFX_List::NewList();
87
88	ASSERT(m_pList != NULL);
89}
90
91CPWL_ListBox::~CPWL_ListBox()
92{
93	IFX_List::DelList(m_pList);
94
95	if (m_pListNotify)
96	{
97		delete m_pListNotify;
98		m_pListNotify = NULL;
99	}
100}
101
102CFX_ByteString CPWL_ListBox::GetClassName() const
103{
104	return "CPWL_ListBox";
105}
106
107void CPWL_ListBox::OnCreated()
108{
109	if (m_pList)
110	{
111		if (m_pListNotify) delete m_pListNotify;
112
113		m_pList->SetFontMap(GetFontMap());
114		m_pList->SetNotify(m_pListNotify = new CPWL_List_Notify(this));
115
116		SetHoverSel(HasFlag(PLBS_HOVERSEL));
117		m_pList->SetMultipleSel(HasFlag(PLBS_MULTIPLESEL));
118		m_pList->SetFontSize(this->GetCreationParam().fFontSize);
119
120		m_bHoverSel = HasFlag(PLBS_HOVERSEL);
121	}
122}
123
124void CPWL_ListBox::OnDestroy()
125{
126	if (m_pListNotify)
127	{
128		delete m_pListNotify;
129		m_pListNotify = NULL;
130	}
131}
132
133void CPWL_ListBox::GetThisAppearanceStream(CFX_ByteTextBuf & sAppStream)
134{
135	CPWL_Wnd::GetThisAppearanceStream(sAppStream);
136
137	CFX_ByteTextBuf sListItems;
138
139	if (m_pList)
140	{
141		CPDF_Rect rcPlate = m_pList->GetPlateRect();
142		for (FX_INT32 i=0,sz=m_pList->GetCount(); i<sz; i++)
143		{
144			CPDF_Rect rcItem = m_pList->GetItemRect(i);
145
146			if (rcItem.bottom > rcPlate.top || rcItem.top < rcPlate.bottom) continue;
147
148			CPDF_Point ptOffset(rcItem.left, (rcItem.top + rcItem.bottom) * 0.5f);
149			if (m_pList->IsItemSelected(i))
150			{
151				sListItems << CPWL_Utils::GetRectFillAppStream(rcItem,PWL_DEFAULT_SELBACKCOLOR);
152				CFX_ByteString sItem = CPWL_Utils::GetEditAppStream(m_pList->GetItemEdit(i), ptOffset);
153				if (sItem.GetLength() > 0)
154				{
155					sListItems << "BT\n" << CPWL_Utils::GetColorAppStream(PWL_DEFAULT_SELTEXTCOLOR) << sItem << "ET\n";
156				}
157			}
158			else
159			{
160				CFX_ByteString sItem = CPWL_Utils::GetEditAppStream(m_pList->GetItemEdit(i), ptOffset);
161				if (sItem.GetLength() > 0)
162				{
163					sListItems << "BT\n" << CPWL_Utils::GetColorAppStream(GetTextColor()) << sItem << "ET\n";
164				}
165			}
166		}
167	}
168
169	if (sListItems.GetLength() > 0)
170	{
171		CFX_ByteTextBuf sClip;
172		CPDF_Rect rcClient = this->GetClientRect();
173
174		sClip << "q\n";
175		sClip << rcClient.left << " " << rcClient.bottom << " "
176			<< rcClient.right - rcClient.left << " " <<	rcClient.top - rcClient.bottom << " re W n\n";
177
178		sClip << sListItems << "Q\n";
179
180		sAppStream << "/Tx BMC\n" << sClip << "EMC\n";
181	}
182}
183
184void CPWL_ListBox::DrawThisAppearance(CFX_RenderDevice* pDevice, CPDF_Matrix* pUser2Device)
185{
186	CPWL_Wnd::DrawThisAppearance(pDevice,pUser2Device);
187
188	if (m_pList)
189	{
190		CPDF_Rect rcPlate = m_pList->GetPlateRect();
191		CPDF_Rect rcList = GetListRect();
192		CPDF_Rect rcClient = GetClientRect();
193
194		for (FX_INT32 i=0,sz=m_pList->GetCount(); i<sz; i++)
195		{
196			CPDF_Rect rcItem = m_pList->GetItemRect(i);
197			if (rcItem.bottom > rcPlate.top || rcItem.top < rcPlate.bottom) continue;
198
199			CPDF_Point ptOffset(rcItem.left, (rcItem.top + rcItem.bottom) * 0.5f);
200			if (IFX_Edit* pEdit = m_pList->GetItemEdit(i))
201			{
202				CPDF_Rect rcContent = pEdit->GetContentRect();
203				if (rcContent.Width() > rcClient.Width())
204					rcItem.Intersect(rcList);
205				else
206					rcItem.Intersect(rcClient);
207			}
208
209			if (m_pList->IsItemSelected(i))
210			{
211			//	CPWL_Utils::DrawFillRect(pDevice, pUser2Device, rcItem, ArgbEncode(255,0,51,113));
212				IFX_SystemHandler* pSysHandler = GetSystemHandler();
213				if(pSysHandler && pSysHandler->IsSelectionImplemented())
214				{
215					IFX_Edit::DrawEdit(pDevice, pUser2Device, m_pList->GetItemEdit(i), CPWL_Utils::PWLColorToFXColor(GetTextColor()), CPWL_Utils::PWLColorToFXColor(GetTextStrokeColor()),
216						rcList, ptOffset, NULL,pSysHandler, m_pFormFiller);
217					pSysHandler->OutputSelectedRect(m_pFormFiller, rcItem);
218				}
219				else
220				{
221					CPWL_Utils::DrawFillRect(pDevice, pUser2Device, rcItem, ArgbEncode(255,0,51,113));
222					IFX_Edit::DrawEdit(pDevice, pUser2Device, m_pList->GetItemEdit(i), ArgbEncode(255,255,255,255), 0,
223						rcList, ptOffset, NULL, pSysHandler, m_pFormFiller);
224				}
225			}
226			else
227			{
228				IFX_SystemHandler* pSysHandler = GetSystemHandler();
229				IFX_Edit::DrawEdit(pDevice, pUser2Device, m_pList->GetItemEdit(i),
230						CPWL_Utils::PWLColorToFXColor(GetTextColor()),
231						CPWL_Utils::PWLColorToFXColor(GetTextStrokeColor()),
232						rcList, ptOffset, NULL,pSysHandler, NULL);
233
234			}
235		}
236	}
237}
238
239FX_BOOL CPWL_ListBox::OnKeyDown(FX_WORD nChar, FX_DWORD nFlag)
240{
241	CPWL_Wnd::OnKeyDown(nChar, nFlag);
242
243	if (!m_pList) return FALSE;
244
245	switch (nChar)
246	{
247	default:
248		return FALSE;
249	case FWL_VKEY_Up:
250	case FWL_VKEY_Down:
251	case FWL_VKEY_Home:
252	case FWL_VKEY_Left:
253	case FWL_VKEY_End:
254	case FWL_VKEY_Right:
255		break;
256	}
257
258	switch (nChar)
259	{
260	case FWL_VKEY_Up:
261		m_pList->OnVK_UP(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag));
262		break;
263	case FWL_VKEY_Down:
264		m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag));
265		break;
266	case FWL_VKEY_Home:
267		m_pList->OnVK_HOME(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag));
268		break;
269	case FWL_VKEY_Left:
270		m_pList->OnVK_LEFT(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag));
271		break;
272	case FWL_VKEY_End:
273		m_pList->OnVK_END(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag));
274		break;
275	case FWL_VKEY_Right:
276		m_pList->OnVK_RIGHT(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag));
277		break;
278	case FWL_VKEY_Delete:
279		break;
280	}
281
282	FX_BOOL bExit = FALSE;
283	OnNotifySelChanged(TRUE,bExit,nFlag);
284
285	return TRUE;
286}
287
288FX_BOOL CPWL_ListBox::OnChar(FX_WORD nChar, FX_DWORD nFlag)
289{
290	CPWL_Wnd::OnChar(nChar,nFlag);
291
292	if (!m_pList) return FALSE;
293
294	if (!m_pList->OnChar(nChar,IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag))) return FALSE;
295
296	FX_BOOL bExit = FALSE;
297	OnNotifySelChanged(TRUE,bExit, nFlag);
298
299	return TRUE;
300}
301
302FX_BOOL CPWL_ListBox::OnLButtonDown(const CPDF_Point & point, FX_DWORD nFlag)
303{
304	CPWL_Wnd::OnLButtonDown(point,nFlag);
305
306	if (ClientHitTest(point))
307	{
308		m_bMouseDown = TRUE;
309		SetFocus();
310		SetCapture();
311
312		if (m_pList)
313			m_pList->OnMouseDown(point,IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag));
314	}
315
316	return TRUE;
317}
318
319FX_BOOL CPWL_ListBox::OnLButtonUp(const CPDF_Point & point, FX_DWORD nFlag)
320{
321	CPWL_Wnd::OnLButtonUp(point,nFlag);
322
323	if (m_bMouseDown)
324	{
325		ReleaseCapture();
326		m_bMouseDown = FALSE;
327	}
328
329	FX_BOOL bExit = FALSE;
330	OnNotifySelChanged(FALSE,bExit,nFlag);
331
332	return TRUE;
333}
334
335void CPWL_ListBox::SetHoverSel(FX_BOOL bHoverSel)
336{
337	m_bHoverSel = bHoverSel;
338}
339
340FX_BOOL CPWL_ListBox::OnMouseMove(const CPDF_Point & point, FX_DWORD nFlag)
341{
342	CPWL_Wnd::OnMouseMove(point, nFlag);
343
344	if (m_bHoverSel && !IsCaptureMouse() && ClientHitTest(point))
345	{
346		if (m_pList)
347			m_pList->Select(m_pList->GetItemIndex(point));
348	}
349
350	if (m_bMouseDown)
351	{
352		if (m_pList)
353			m_pList->OnMouseMove(point,IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag));
354	}
355
356	return TRUE;
357}
358
359void CPWL_ListBox::OnNotify(CPWL_Wnd* pWnd, FX_DWORD msg, FX_INTPTR wParam, FX_INTPTR lParam)
360{
361	CPWL_Wnd::OnNotify(pWnd,msg,wParam,lParam);
362
363	FX_FLOAT fPos;
364
365	switch (msg)
366	{
367	case PNM_SETSCROLLINFO:
368		switch (wParam)
369		{
370		case SBT_VSCROLL:
371			if (CPWL_Wnd * pChild = GetVScrollBar())
372			{
373				pChild->OnNotify(pWnd,PNM_SETSCROLLINFO,wParam,lParam);
374			}
375			break;
376		}
377		break;
378	case PNM_SETSCROLLPOS:
379		switch (wParam)
380		{
381		case SBT_VSCROLL:
382			if (CPWL_Wnd * pChild = GetVScrollBar())
383			{
384				pChild->OnNotify(pWnd,PNM_SETSCROLLPOS,wParam,lParam);
385			}
386			break;
387		}
388		break;
389	case PNM_SCROLLWINDOW:
390		fPos = *(FX_FLOAT*)lParam;
391		switch (wParam)
392		{
393		case SBT_VSCROLL:
394			if (m_pList)
395				m_pList->SetScrollPos(CPDF_Point(0,fPos));
396			break;
397		}
398		break;
399	}
400}
401
402void CPWL_ListBox::KillFocus()
403{
404	CPWL_Wnd::KillFocus();
405
406	/*
407	if (this->IsMultipleSel())
408	{
409		for(FX_INT32 i=0;i<this->GetCount();i++)
410		{
411			if (this->IsListItemSelected(i))
412			{
413				if (!IsListItemVisible(i))
414					this->ScrollToListItem(i);
415				break;
416			}
417		}
418	}
419	else
420	{
421		if (!IsListItemVisible(this->GetCurSel()))
422			this->ScrollToListItem(this->GetCurSel());
423	}
424
425	SetListItemCaret(m_nCaretIndex,FALSE);
426	*/
427}
428
429void CPWL_ListBox::RePosChildWnd()
430{
431	CPWL_Wnd::RePosChildWnd();
432
433	if (m_pList)
434		m_pList->SetPlateRect(GetListRect());
435}
436
437void CPWL_ListBox::OnNotifySelChanged(FX_BOOL bKeyDown, FX_BOOL & bExit,  FX_DWORD nFlag)
438{
439	if (m_pFillerNotify)
440	{
441		FX_BOOL bRC = TRUE;
442		CFX_WideString swChange = GetText();
443		CFX_WideString strChangeEx;
444		int nSelStart = 0;
445		int nSelEnd = swChange.GetLength();
446		m_pFillerNotify->OnBeforeKeyStroke(FALSE, GetAttachedData(), 0, swChange, strChangeEx, nSelStart, nSelEnd, bKeyDown, bRC, bExit, nFlag);
447		if (bExit) return;
448
449		m_pFillerNotify->OnAfterKeyStroke(FALSE, GetAttachedData(), bExit,nFlag);
450	}
451}
452
453CPDF_Rect CPWL_ListBox::GetFocusRect() const
454{
455	if (m_pList && m_pList->IsMultipleSel())
456	{
457		CPDF_Rect rcCaret = m_pList->GetItemRect(m_pList->GetCaret());
458		rcCaret.Intersect(GetClientRect());
459		return rcCaret;
460	}
461
462	return CPWL_Wnd::GetFocusRect();
463}
464
465void CPWL_ListBox::AddString(FX_LPCWSTR string)
466{
467	if (m_pList)
468	{
469		m_pList->AddString(string);
470	}
471}
472
473void CPWL_ListBox::SetText(FX_LPCWSTR csText,FX_BOOL bRefresh)
474{
475	//return CPDF_List::SetText(csText,bRefresh);
476}
477
478CFX_WideString CPWL_ListBox::GetText() const
479{
480	if (m_pList)
481		return m_pList->GetText();
482
483	return L"";
484}
485
486void CPWL_ListBox::SetFontSize(FX_FLOAT fFontSize)
487{
488	if (m_pList)
489		m_pList->SetFontSize(fFontSize);
490}
491
492FX_FLOAT CPWL_ListBox::GetFontSize() const
493{
494	if (m_pList)
495		return m_pList->GetFontSize();
496	return 0.0f;
497}
498
499void CPWL_ListBox::Select(FX_INT32 nItemIndex)
500{
501	if (m_pList)
502		m_pList->Select(nItemIndex);
503}
504
505void CPWL_ListBox::SetCaret(FX_INT32 nItemIndex)
506{
507	if (m_pList)
508		m_pList->SetCaret(nItemIndex);
509}
510
511void CPWL_ListBox::SetTopVisibleIndex(FX_INT32 nItemIndex)
512{
513	if (m_pList)
514		m_pList->SetTopItem(nItemIndex);
515}
516
517void CPWL_ListBox::ScrollToListItem(FX_INT32 nItemIndex)
518{
519	if (m_pList)
520		m_pList->ScrollToListItem(nItemIndex);
521}
522
523void CPWL_ListBox::ResetContent()
524{
525	if (m_pList)
526		m_pList->Empty();
527}
528
529void CPWL_ListBox::Reset()
530{
531	if (m_pList)
532		m_pList->Cancel();
533}
534
535FX_BOOL CPWL_ListBox::IsMultipleSel() const
536{
537	if (m_pList)
538		return m_pList->IsMultipleSel();
539
540	return FALSE;
541}
542
543FX_INT32 CPWL_ListBox::GetCaretIndex() const
544{
545	if (m_pList)
546		return m_pList->GetCaret();
547
548	return -1;
549}
550
551FX_INT32 CPWL_ListBox::GetCurSel() const
552{
553	if (m_pList)
554		return m_pList->GetSelect();
555
556	return -1;
557}
558
559FX_BOOL CPWL_ListBox::IsItemSelected(FX_INT32 nItemIndex) const
560{
561	if (m_pList)
562		return m_pList->IsItemSelected(nItemIndex);
563
564	return FALSE;
565}
566
567FX_INT32 CPWL_ListBox::GetTopVisibleIndex() const
568{
569	if (m_pList)
570	{
571		m_pList->ScrollToListItem(m_pList->GetFirstSelected());
572		return m_pList->GetTopItem();
573	}
574
575	return -1;
576}
577
578FX_INT32 CPWL_ListBox::GetCount() const
579{
580	if (m_pList)
581		return m_pList->GetCount();
582
583	return 0;
584}
585
586FX_INT32 CPWL_ListBox::FindNext(FX_INT32 nIndex,FX_WCHAR nChar) const
587{
588	if (m_pList)
589		return m_pList->FindNext(nIndex,nChar);
590
591	return nIndex;
592}
593
594CPDF_Rect CPWL_ListBox::GetContentRect() const
595{
596	if (m_pList)
597		return m_pList->GetContentRect();
598
599	return CPDF_Rect();
600}
601
602FX_FLOAT CPWL_ListBox::GetFirstHeight() const
603{
604	if (m_pList)
605		return m_pList->GetFirstHeight();
606
607	return 0.0f;
608}
609
610CPDF_Rect CPWL_ListBox::GetListRect() const
611{
612	return CPWL_Utils::DeflateRect(GetWindowRect(),(FX_FLOAT)(GetBorderWidth()+GetInnerBorderWidth()));
613}
614
615FX_BOOL	CPWL_ListBox::OnMouseWheel(short zDelta, const CPDF_Point & point, FX_DWORD nFlag)
616{
617	if (!m_pList) return FALSE;
618
619	if (zDelta < 0)
620	{
621		m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag));
622	}
623	else
624	{
625		m_pList->OnVK_UP(IsSHIFTpressed(nFlag),IsCTRLpressed(nFlag));
626	}
627
628	FX_BOOL bExit = FALSE;
629	OnNotifySelChanged(FALSE,bExit, nFlag);
630	return TRUE;
631}
632
633