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/fxedit/fxet_stub.h"
8#include "../../include/fxedit/fxet_edit.h"
9#include "../../include/fxedit/fxet_list.h"
10
11/* ------------------------------- CFX_ListItem ---------------------------------- */
12
13CFX_ListItem::CFX_ListItem() : m_pEdit(NULL),
14	m_bSelected(FALSE),
15	m_bCaret(FALSE),
16	m_rcListItem(0.0f,0.0f,0.0f,0.0f)
17{
18	m_pEdit = IFX_Edit::NewEdit();
19	ASSERT(m_pEdit != NULL);
20
21	m_pEdit->SetAlignmentV(1);
22	m_pEdit->Initialize();
23}
24
25CFX_ListItem::~CFX_ListItem()
26{
27	IFX_Edit::DelEdit(m_pEdit);
28}
29
30void CFX_ListItem::SetFontMap(IFX_Edit_FontMap * pFontMap)
31{
32	if (m_pEdit)
33		m_pEdit->SetFontMap(pFontMap);
34}
35
36IFX_Edit* CFX_ListItem::GetEdit() const
37{
38	return m_pEdit;
39}
40
41IFX_Edit_Iterator*	CFX_ListItem::GetIterator() const
42{
43	if (m_pEdit)
44		return m_pEdit->GetIterator();
45
46	return NULL;
47}
48
49void CFX_ListItem::SetRect(const CLST_Rect & rect)
50{
51	m_rcListItem = rect;
52}
53
54CLST_Rect CFX_ListItem::GetRect() const
55{
56	return m_rcListItem;
57}
58
59FX_BOOL CFX_ListItem::IsSelected() const
60{
61	return m_bSelected;
62}
63
64void CFX_ListItem::SetSelect(FX_BOOL bSelected)
65{
66	m_bSelected = bSelected;
67}
68
69FX_BOOL CFX_ListItem::IsCaret() const
70{
71	return m_bCaret;
72}
73
74void CFX_ListItem::SetCaret(FX_BOOL bCaret)
75{
76	m_bCaret = bCaret;
77}
78
79void CFX_ListItem::SetText(FX_LPCWSTR text)
80{
81	if (m_pEdit)
82		m_pEdit->SetText(text);
83}
84
85void CFX_ListItem::SetFontSize(FX_FLOAT fFontSize)
86{
87	if (m_pEdit)
88		m_pEdit->SetFontSize(fFontSize);
89}
90
91FX_FLOAT CFX_ListItem::GetItemHeight() const
92{
93	if (m_pEdit)
94		return m_pEdit->GetContentRect().Height();
95
96	return 0.0f;
97}
98
99FX_WORD CFX_ListItem::GetFirstChar() const
100{
101	CPVT_Word word;
102
103	if (IFX_Edit_Iterator*	pIterator = GetIterator())
104	{
105		pIterator->SetAt(1);
106		pIterator->GetWord(word);
107	}
108
109	return word.Word;
110}
111
112CFX_WideString CFX_ListItem::GetText() const
113{
114	if (m_pEdit)
115		return m_pEdit->GetText();
116
117	return L"";
118}
119
120/* ------------------------------------ CFX_List --------------------------------- */
121
122CFX_List::CFX_List() : m_pFontMap(NULL), m_fFontSize(0.0f), m_bMultiple(FALSE)
123{
124}
125
126CFX_List::~CFX_List()
127{
128	Empty();
129}
130
131void CFX_List::Empty()
132{
133	for (FX_INT32 i=0,sz=m_aListItems.GetSize(); i<sz; i++)
134		delete m_aListItems.GetAt(i);
135
136	m_aListItems.RemoveAll();
137}
138
139void CFX_List::SetFontMap(IFX_Edit_FontMap * pFontMap)
140{
141	m_pFontMap = pFontMap;
142}
143
144void CFX_List::SetFontSize(FX_FLOAT fFontSize)
145{
146	m_fFontSize = fFontSize;
147}
148
149void CFX_List::AddItem(FX_LPCWSTR str)
150{
151	if (CFX_ListItem * pListItem = new CFX_ListItem())
152	{
153		pListItem->SetFontMap(m_pFontMap);
154		pListItem->SetFontSize(m_fFontSize);
155		pListItem->SetText(str);
156		m_aListItems.Add(pListItem);
157	}
158}
159
160void CFX_List::ReArrange(FX_INT32 nItemIndex)
161{
162	FX_FLOAT fPosY = 0.0f;
163
164	if (CFX_ListItem * pPrevItem = m_aListItems.GetAt(nItemIndex - 1))
165		fPosY = pPrevItem->GetRect().bottom;
166
167	for (FX_INT32 i=nItemIndex,sz=m_aListItems.GetSize(); i<sz; i++)
168	{
169		if (CFX_ListItem * pListItem = m_aListItems.GetAt(i))
170		{
171			FX_FLOAT fListItemHeight = pListItem->GetItemHeight();
172			pListItem->SetRect(CLST_Rect(0.0f,fPosY,0.0f,fPosY + fListItemHeight));
173			fPosY += fListItemHeight;
174		}
175	}
176
177	SetContentRect(CLST_Rect(0.0f,0.0f,0.0f,fPosY));
178}
179
180IFX_Edit * CFX_List::GetItemEdit(FX_INT32 nIndex) const
181{
182	if (CFX_ListItem * pListItem = m_aListItems.GetAt(nIndex))
183	{
184		return pListItem->GetEdit();
185	}
186
187	return NULL;
188}
189
190FX_INT32 CFX_List::GetCount() const
191{
192	return m_aListItems.GetSize();
193}
194
195CPDF_Rect CFX_List::GetPlateRect() const
196{
197	return CFX_ListContainer::GetPlateRect();
198}
199
200CPDF_Rect CFX_List::GetContentRect() const
201{
202	return InnerToOuter(CFX_ListContainer::GetContentRect());
203}
204
205FX_FLOAT CFX_List::GetFontSize() const
206{
207	return m_fFontSize;
208}
209
210FX_INT32 CFX_List::GetItemIndex(const CPDF_Point & point) const
211{
212	CPDF_Point pt = OuterToInner(point);
213
214	FX_BOOL bFirst = TRUE;
215	FX_BOOL bLast = TRUE;
216
217	for (FX_INT32 i=0,sz=m_aListItems.GetSize(); i<sz; i++)
218	{
219		if (CFX_ListItem * pListItem = m_aListItems.GetAt(i))
220		{
221			CLST_Rect rcListItem = pListItem->GetRect();
222
223			if (FX_EDIT_IsFloatBigger(pt.y, rcListItem.top))
224			{
225				bFirst = FALSE;
226			}
227
228			if (FX_EDIT_IsFloatSmaller(pt.y, rcListItem.bottom))
229			{
230				bLast = FALSE;
231			}
232
233			if (pt.y >= rcListItem.top && pt.y < rcListItem.bottom)
234			{
235				return i;
236			}
237		}
238	}
239
240	if (bFirst) return 0;
241	if (bLast) return m_aListItems.GetSize()-1;
242
243	return -1;
244}
245
246FX_FLOAT CFX_List::GetFirstHeight() const
247{
248	if (CFX_ListItem * pListItem = m_aListItems.GetAt(0))
249	{
250		return pListItem->GetItemHeight();
251	}
252
253	return 1.0f;
254}
255
256FX_INT32 CFX_List::GetFirstSelected() const
257{
258	for (FX_INT32 i=0,sz=m_aListItems.GetSize(); i<sz; i++)
259	{
260		if (CFX_ListItem * pListItem = m_aListItems.GetAt(i))
261		{
262			if (pListItem->IsSelected())
263				return i;
264		}
265	}
266	return -1;
267}
268
269FX_INT32 CFX_List::GetLastSelected() const
270{
271	for (FX_INT32 i=m_aListItems.GetSize()-1; i>=0; i--)
272	{
273		if (CFX_ListItem * pListItem = m_aListItems.GetAt(i))
274		{
275			if (pListItem->IsSelected())
276				return i;
277		}
278	}
279	return -1;
280}
281
282FX_WCHAR CFX_List::Toupper(FX_WCHAR c) const
283{
284	if ( (c >= 'a') && (c <= 'z') )
285		c = c - ('a' - 'A');
286	return c;
287}
288
289FX_INT32 CFX_List::FindNext(FX_INT32 nIndex,FX_WCHAR nChar) const
290{
291	FX_INT32 nCircleIndex = nIndex;
292
293	for (FX_INT32 i=0,sz=m_aListItems.GetSize(); i<sz; i++)
294	{
295		nCircleIndex ++;
296		if (nCircleIndex >= sz) nCircleIndex = 0;
297
298		if (CFX_ListItem * pListItem = m_aListItems.GetAt(nCircleIndex))
299		{
300			if (Toupper(pListItem->GetFirstChar()) == Toupper(nChar))
301				return nCircleIndex;
302		}
303	}
304
305	return nCircleIndex;
306}
307
308CPDF_Rect CFX_List::GetItemRect(FX_INT32 nIndex) const
309{
310	if (CFX_ListItem * pListItem = m_aListItems.GetAt(nIndex))
311	{
312		CPDF_Rect rcItem = pListItem->GetRect();
313		rcItem.left = 0.0f;
314		rcItem.right = GetPlateRect().Width();
315		return InnerToOuter(rcItem);
316	}
317
318	return CPDF_Rect();
319}
320
321FX_BOOL CFX_List::IsItemSelected(FX_INT32 nIndex) const
322{
323	if (CFX_ListItem * pListItem = m_aListItems.GetAt(nIndex))
324	{
325		return pListItem->IsSelected();
326	}
327
328	return FALSE;
329}
330
331void CFX_List::SetItemSelect(FX_INT32 nItemIndex, FX_BOOL bSelected)
332{
333	if (CFX_ListItem * pListItem = m_aListItems.GetAt(nItemIndex))
334	{
335		pListItem->SetSelect(bSelected);
336	}
337}
338
339void CFX_List::SetItemCaret(FX_INT32 nItemIndex, FX_BOOL bCaret)
340{
341	if (CFX_ListItem * pListItem = m_aListItems.GetAt(nItemIndex))
342	{
343		pListItem->SetCaret(bCaret);
344	}
345}
346
347void CFX_List::SetMultipleSel(FX_BOOL bMultiple)
348{
349	m_bMultiple = bMultiple;
350}
351
352FX_BOOL CFX_List::IsMultipleSel() const
353{
354	return m_bMultiple;
355}
356
357FX_BOOL CFX_List::IsValid(FX_INT32 nItemIndex) const
358{
359	return nItemIndex >= 0 && nItemIndex < m_aListItems.GetSize();
360}
361
362CFX_WideString CFX_List::GetItemText(FX_INT32 nIndex) const
363{
364	if (CFX_ListItem * pListItem = m_aListItems.GetAt(nIndex))
365	{
366		return pListItem->GetText();
367	}
368
369	return L"";
370}
371
372/* ------------------------------------ CPLST_Select ---------------------------------- */
373
374CPLST_Select::CPLST_Select()
375{
376}
377
378CPLST_Select::~CPLST_Select()
379{
380	for (FX_INT32 i=0,sz=m_aItems.GetSize(); i<sz; i++)
381		delete m_aItems.GetAt(i);
382
383	m_aItems.RemoveAll();
384}
385
386void CPLST_Select::Add(FX_INT32 nItemIndex)
387{
388	FX_INT32 nIndex = Find(nItemIndex);
389
390	if (nIndex < 0)
391		m_aItems.Add(new CPLST_Select_Item(nItemIndex,1));
392	else
393	{
394		if (CPLST_Select_Item * pItem = m_aItems.GetAt(nIndex))
395		{
396			pItem->nState = 1;
397		}
398	}
399}
400
401void CPLST_Select::Add(FX_INT32 nBeginIndex, FX_INT32 nEndIndex)
402{
403	if (nBeginIndex > nEndIndex)
404	{
405		FX_INT32 nTemp = nEndIndex;
406		nEndIndex = nBeginIndex;
407		nBeginIndex = nTemp;
408	}
409
410	for (FX_INT32 i=nBeginIndex; i<=nEndIndex; i++)	Add(i);
411}
412
413void CPLST_Select::Sub(FX_INT32 nItemIndex)
414{
415	for (FX_INT32 i=m_aItems.GetSize()-1; i>=0; i--)
416	{
417		if (CPLST_Select_Item * pItem = m_aItems.GetAt(i))
418			if (pItem->nItemIndex == nItemIndex)
419				pItem->nState = -1;
420	}
421}
422
423void CPLST_Select::Sub(FX_INT32 nBeginIndex, FX_INT32 nEndIndex)
424{
425	if (nBeginIndex > nEndIndex)
426	{
427		FX_INT32 nTemp = nEndIndex;
428		nEndIndex = nBeginIndex;
429		nBeginIndex = nTemp;
430	}
431
432	for (FX_INT32 i=nBeginIndex; i<=nEndIndex; i++)	Sub(i);
433}
434
435FX_INT32 CPLST_Select::Find(FX_INT32 nItemIndex) const
436{
437	for (FX_INT32 i=0,sz=m_aItems.GetSize(); i<sz; i++)
438	{
439		if (CPLST_Select_Item * pItem = m_aItems.GetAt(i))
440		{
441			if (pItem->nItemIndex == nItemIndex)
442				return i;
443		}
444	}
445
446	return -1;
447}
448
449FX_BOOL CPLST_Select::IsExist(FX_INT32 nItemIndex) const
450{
451	return Find(nItemIndex) >= 0;
452}
453
454FX_INT32 CPLST_Select::GetCount() const
455{
456	return m_aItems.GetSize();
457}
458
459FX_INT32 CPLST_Select::GetItemIndex(FX_INT32 nIndex) const
460{
461	if (nIndex >= 0 && nIndex < m_aItems.GetSize())
462		if (CPLST_Select_Item * pItem = m_aItems.GetAt(nIndex))
463			return pItem->nItemIndex;
464
465	return -1;
466}
467
468FX_INT32 CPLST_Select::GetState(FX_INT32 nIndex) const
469{
470	if (nIndex >= 0 && nIndex < m_aItems.GetSize())
471		if (CPLST_Select_Item * pItem = m_aItems.GetAt(nIndex))
472			return pItem->nState;
473
474	return 0;
475}
476
477void CPLST_Select::DeselectAll()
478{
479	for (FX_INT32 i=0,sz=m_aItems.GetSize(); i<sz; i++)
480	{
481		if (CPLST_Select_Item * pItem = m_aItems.GetAt(i))
482		{
483			pItem->nState = -1;
484		}
485	}
486}
487
488void CPLST_Select::Done()
489{
490	for (FX_INT32 i=m_aItems.GetSize()-1; i>=0; i--)
491	{
492		if (CPLST_Select_Item * pItem = m_aItems.GetAt(i))
493		{
494			if (pItem->nState == -1)
495			{
496				delete pItem;
497				m_aItems.RemoveAt(i);
498			}
499			else
500			{
501				pItem->nState = 0;
502			}
503		}
504	}
505}
506
507/* ------------------------------------ CFX_ListCtrl --------------------------------- */
508
509CFX_ListCtrl::CFX_ListCtrl() : m_pNotify(NULL),
510	m_ptScrollPos(0.0f,0.0f),
511	m_nSelItem(-1),
512	m_nFootIndex(-1),
513	m_bCtrlSel(FALSE),
514	m_nCaretIndex(-1),
515	m_bNotifyFlag(FALSE)
516{
517}
518
519CFX_ListCtrl::~CFX_ListCtrl()
520{
521}
522
523void CFX_ListCtrl::SetNotify(IFX_List_Notify * pNotify)
524{
525	m_pNotify = pNotify;
526}
527
528CPDF_Point CFX_ListCtrl::InToOut(const CPDF_Point & point) const
529{
530	CPDF_Rect rcPlate = GetPlateRect();
531
532	return CPDF_Point(point.x - (m_ptScrollPos.x - rcPlate.left),
533		point.y - (m_ptScrollPos.y - rcPlate.top));
534}
535
536CPDF_Point CFX_ListCtrl::OutToIn(const CPDF_Point & point) const
537{
538	CPDF_Rect rcPlate = GetPlateRect();
539
540	return CPDF_Point(point.x + (m_ptScrollPos.x - rcPlate.left),
541		point.y + (m_ptScrollPos.y - rcPlate.top));
542}
543
544CPDF_Rect CFX_ListCtrl::InToOut(const CPDF_Rect & rect) const
545{
546	CPDF_Point ptLeftBottom = InToOut(CPDF_Point(rect.left,rect.bottom));
547	CPDF_Point ptRightTop = InToOut(CPDF_Point(rect.right,rect.top));
548
549	return CPDF_Rect(ptLeftBottom.x,ptLeftBottom.y,ptRightTop.x,ptRightTop.y);
550}
551
552CPDF_Rect CFX_ListCtrl::OutToIn(const CPDF_Rect & rect) const
553{
554	CPDF_Point ptLeftBottom = OutToIn(CPDF_Point(rect.left,rect.bottom));
555	CPDF_Point ptRightTop = OutToIn(CPDF_Point(rect.right,rect.top));
556
557	return CPDF_Rect(ptLeftBottom.x,ptLeftBottom.y,ptRightTop.x,ptRightTop.y);
558}
559
560void CFX_ListCtrl::OnMouseDown(const CPDF_Point & point,FX_BOOL bShift,FX_BOOL bCtrl)
561{
562	FX_INT32 nHitIndex = this->GetItemIndex(point);
563
564	if (IsMultipleSel())
565	{
566		if (bCtrl)
567		{
568			if (IsItemSelected(nHitIndex))
569			{
570				m_aSelItems.Sub(nHitIndex);
571				SelectItems();
572				m_bCtrlSel = FALSE;
573			}
574			else
575			{
576				m_aSelItems.Add(nHitIndex);
577				SelectItems();
578				m_bCtrlSel = TRUE;
579			}
580
581			m_nFootIndex = nHitIndex;
582		}
583		else if (bShift)
584		{
585			m_aSelItems.DeselectAll();
586			m_aSelItems.Add(m_nFootIndex,nHitIndex);
587			SelectItems();
588		}
589		else
590		{
591			m_aSelItems.DeselectAll();
592			m_aSelItems.Add(nHitIndex);
593			SelectItems();
594
595			m_nFootIndex = nHitIndex;
596		}
597
598		SetCaret(nHitIndex);
599	}
600	else
601	{
602		SetSingleSelect(nHitIndex);
603	}
604
605	if (!this->IsItemVisible(nHitIndex))
606		this->ScrollToListItem(nHitIndex);
607}
608
609void CFX_ListCtrl::OnMouseMove(const CPDF_Point & point,FX_BOOL bShift,FX_BOOL bCtrl)
610{
611	FX_INT32 nHitIndex = this->GetItemIndex(point);
612
613	if (IsMultipleSel())
614	{
615		if (bCtrl)
616		{
617			if (m_bCtrlSel)
618				m_aSelItems.Add(m_nFootIndex,nHitIndex);
619			else
620				m_aSelItems.Sub(m_nFootIndex,nHitIndex);
621
622			SelectItems();
623		}
624		else
625		{
626			m_aSelItems.DeselectAll();
627			m_aSelItems.Add(m_nFootIndex,nHitIndex);
628			SelectItems();
629		}
630
631		SetCaret(nHitIndex);
632	}
633	else
634	{
635		SetSingleSelect(nHitIndex);
636	}
637
638	if (!this->IsItemVisible(nHitIndex))
639		this->ScrollToListItem(nHitIndex);
640}
641
642void CFX_ListCtrl::OnVK(FX_INT32 nItemIndex,FX_BOOL bShift,FX_BOOL bCtrl)
643{
644	if (IsMultipleSel())
645	{
646		if (nItemIndex >= 0 && nItemIndex < GetCount())
647		{
648			if (bCtrl)
649			{
650			}
651			else if (bShift)
652			{
653				m_aSelItems.DeselectAll();
654				m_aSelItems.Add(m_nFootIndex,nItemIndex);
655				SelectItems();
656			}
657			else
658			{
659				m_aSelItems.DeselectAll();
660				m_aSelItems.Add(nItemIndex);
661				SelectItems();
662				m_nFootIndex = nItemIndex;
663			}
664
665			SetCaret(nItemIndex);
666		}
667	}
668	else
669	{
670		SetSingleSelect(nItemIndex);
671	}
672
673	if (!this->IsItemVisible(nItemIndex))
674		this->ScrollToListItem(nItemIndex);
675}
676
677void CFX_ListCtrl::OnVK_UP(FX_BOOL bShift,FX_BOOL bCtrl)
678{
679	OnVK(IsMultipleSel() ? GetCaret()-1 : GetSelect()-1, bShift, bCtrl);
680}
681
682void CFX_ListCtrl::OnVK_DOWN(FX_BOOL bShift,FX_BOOL bCtrl)
683{
684	OnVK(IsMultipleSel() ? GetCaret()+1 : GetSelect()+1, bShift, bCtrl);
685}
686
687void CFX_ListCtrl::OnVK_LEFT(FX_BOOL bShift,FX_BOOL bCtrl)
688{
689	OnVK(0, bShift, bCtrl);
690}
691
692void CFX_ListCtrl::OnVK_RIGHT(FX_BOOL bShift,FX_BOOL bCtrl)
693{
694	OnVK(GetCount()-1, bShift, bCtrl);
695}
696
697void CFX_ListCtrl::OnVK_HOME(FX_BOOL bShift,FX_BOOL bCtrl)
698{
699	OnVK(0, bShift, bCtrl);
700}
701
702void CFX_ListCtrl::OnVK_END(FX_BOOL bShift,FX_BOOL bCtrl)
703{
704	OnVK(GetCount()-1, bShift, bCtrl);
705}
706
707FX_BOOL	CFX_ListCtrl::OnChar(FX_WORD nChar,FX_BOOL bShift,FX_BOOL bCtrl)
708{
709	FX_INT32 nIndex = GetLastSelected();
710	FX_INT32 nFindIndex = FindNext(nIndex,nChar);
711
712	if (nFindIndex != nIndex)
713	{
714		OnVK(nFindIndex, bShift, bCtrl);
715		return TRUE;
716	}
717	return FALSE;
718}
719
720/* -------- inner methods ------- */
721
722void CFX_ListCtrl::SetPlateRect(const CPDF_Rect & rect)
723{
724	CFX_ListContainer::SetPlateRect(rect);
725	m_ptScrollPos.x = rect.left;
726	SetScrollPos(CPDF_Point(rect.left,rect.top));
727	ReArrange(0);
728	InvalidateItem(-1);
729}
730
731CPDF_Rect CFX_ListCtrl::GetItemRect(FX_INT32 nIndex) const
732{
733	return InToOut(CFX_List::GetItemRect(nIndex));
734}
735
736void CFX_ListCtrl::AddString(FX_LPCWSTR string)
737{
738	AddItem(string);
739	ReArrange(GetCount() - 1);
740}
741
742void CFX_ListCtrl::SetMultipleSelect(FX_INT32 nItemIndex, FX_BOOL bSelected)
743{
744	if (!IsValid(nItemIndex)) return;
745
746	if (bSelected != this->IsItemSelected(nItemIndex))
747	{
748		if (bSelected)
749		{
750			SetItemSelect(nItemIndex,TRUE);
751			InvalidateItem(nItemIndex);
752		}
753		else
754		{
755			SetItemSelect(nItemIndex,FALSE);
756			InvalidateItem(nItemIndex);
757		}
758	}
759}
760
761void CFX_ListCtrl::SetSingleSelect(FX_INT32 nItemIndex)
762{
763	if (!IsValid(nItemIndex)) return;
764
765	if (m_nSelItem != nItemIndex)
766	{
767		if (m_nSelItem >= 0)
768		{
769			SetItemSelect(m_nSelItem,FALSE);
770			InvalidateItem(m_nSelItem);
771		}
772
773		SetItemSelect(nItemIndex,TRUE);
774		InvalidateItem(nItemIndex);
775		m_nSelItem = nItemIndex;
776	}
777}
778
779void CFX_ListCtrl::SetCaret(FX_INT32 nItemIndex)
780{
781	if (!IsValid(nItemIndex)) return;
782
783	if (this->IsMultipleSel())
784	{
785		FX_INT32 nOldIndex = m_nCaretIndex;
786
787		if (nOldIndex != nItemIndex)
788		{
789			m_nCaretIndex = nItemIndex;
790
791			SetItemCaret(nOldIndex, FALSE);
792			SetItemCaret(nItemIndex,TRUE);
793
794			InvalidateItem(nOldIndex);
795			InvalidateItem(nItemIndex);
796		}
797	}
798}
799
800void CFX_ListCtrl::InvalidateItem(FX_INT32 nItemIndex)
801{
802	if (m_pNotify)
803	{
804		if (nItemIndex == -1)
805		{
806			if (!m_bNotifyFlag)
807			{
808				m_bNotifyFlag = TRUE;
809				CPDF_Rect rcRefresh = GetPlateRect();
810				m_pNotify->IOnInvalidateRect(&rcRefresh);
811				m_bNotifyFlag = FALSE;
812			}
813		}
814		else
815		{
816			if (!m_bNotifyFlag)
817			{
818				m_bNotifyFlag = TRUE;
819				CPDF_Rect rcRefresh = GetItemRect(nItemIndex);
820				rcRefresh.left -= 1.0f;
821				rcRefresh.right += 1.0f;
822				rcRefresh.bottom -= 1.0f;
823				rcRefresh.top += 1.0f;
824
825				m_pNotify->IOnInvalidateRect(&rcRefresh);
826				m_bNotifyFlag = FALSE;
827			}
828		}
829	}
830}
831
832void CFX_ListCtrl::SelectItems()
833{
834	for (FX_INT32 i=0,sz=m_aSelItems.GetCount(); i<sz; i++)
835	{
836		FX_INT32 nItemIndex = m_aSelItems.GetItemIndex(i);
837		FX_INT32 nState = m_aSelItems.GetState(i);
838
839		switch(nState)
840		{
841		case 1:
842			SetMultipleSelect(nItemIndex, TRUE);
843			break;
844		case -1:
845			SetMultipleSelect(nItemIndex, FALSE);
846			break;
847		}
848	}
849
850	m_aSelItems.Done();
851}
852
853void CFX_ListCtrl::Select(FX_INT32 nItemIndex)
854{
855	if (!IsValid(nItemIndex)) return;
856
857	if (this->IsMultipleSel())
858	{
859		m_aSelItems.Add(nItemIndex);
860		SelectItems();
861	}
862	else
863		SetSingleSelect(nItemIndex);
864}
865
866FX_BOOL	CFX_ListCtrl::IsItemVisible(FX_INT32 nItemIndex) const
867{
868	CPDF_Rect rcPlate = this->GetPlateRect();
869	CPDF_Rect rcItem = this->GetItemRect(nItemIndex);
870
871	return rcItem.bottom >= rcPlate.bottom && rcItem.top <= rcPlate.top;
872}
873
874void CFX_ListCtrl::ScrollToListItem(FX_INT32 nItemIndex)
875{
876	if (!IsValid(nItemIndex)) return;
877
878	CPDF_Rect rcPlate = this->GetPlateRect();
879	CPDF_Rect rcItem = CFX_List::GetItemRect(nItemIndex);
880	CPDF_Rect rcItemCtrl = GetItemRect(nItemIndex);
881
882	if (FX_EDIT_IsFloatSmaller(rcItemCtrl.bottom, rcPlate.bottom))
883	{
884		if (FX_EDIT_IsFloatSmaller(rcItemCtrl.top, rcPlate.top))
885		{
886			SetScrollPosY(rcItem.bottom + rcPlate.Height());
887		}
888	}
889	else if (FX_EDIT_IsFloatBigger(rcItemCtrl.top, rcPlate.top))
890	{
891		if (FX_EDIT_IsFloatBigger(rcItemCtrl.bottom, rcPlate.bottom))
892		{
893			SetScrollPosY(rcItem.top);
894		}
895	}
896}
897
898void CFX_ListCtrl::SetScrollInfo()
899{
900	if (m_pNotify)
901	{
902		CPDF_Rect rcPlate = GetPlateRect();
903		CPDF_Rect rcContent = CFX_List::GetContentRect();
904
905		if (!m_bNotifyFlag)
906		{
907			m_bNotifyFlag = TRUE;
908			m_pNotify->IOnSetScrollInfoY(rcPlate.bottom, rcPlate.top,
909					rcContent.bottom, rcContent.top, GetFirstHeight(), rcPlate.Height());
910			m_bNotifyFlag = FALSE;
911		}
912	}
913}
914
915void CFX_ListCtrl::SetScrollPos(const CPDF_Point & point)
916{
917	SetScrollPosY(point.y);
918}
919
920void CFX_ListCtrl::SetScrollPosY(FX_FLOAT fy)
921{
922	if (!FX_EDIT_IsFloatEqual(m_ptScrollPos.y,fy))
923	{
924		CPDF_Rect rcPlate = this->GetPlateRect();
925		CPDF_Rect rcContent = CFX_List::GetContentRect();
926
927		if (rcPlate.Height() > rcContent.Height())
928		{
929			fy = rcPlate.top;
930		}
931		else
932		{
933			if (FX_EDIT_IsFloatSmaller(fy - rcPlate.Height(), rcContent.bottom))
934			{
935				fy = rcContent.bottom + rcPlate.Height();
936			}
937			else if (FX_EDIT_IsFloatBigger(fy, rcContent.top))
938			{
939				fy = rcContent.top;
940			}
941		}
942
943		m_ptScrollPos.y = fy;
944		InvalidateItem(-1);
945
946		if (m_pNotify)
947		{
948			if (!m_bNotifyFlag)
949			{
950				m_bNotifyFlag = TRUE;
951				m_pNotify->IOnSetScrollPosY(fy);
952				m_bNotifyFlag = FALSE;
953			}
954		}
955	}
956}
957
958CPDF_Rect CFX_ListCtrl::GetContentRect() const
959{
960	return InToOut(CFX_List::GetContentRect());
961}
962
963void CFX_ListCtrl::ReArrange(FX_INT32 nItemIndex)
964{
965	CFX_List::ReArrange(nItemIndex);
966	SetScrollInfo();
967}
968
969void CFX_ListCtrl::SetTopItem(FX_INT32 nIndex)
970{
971	if (IsValid(nIndex))
972	{
973		this->GetPlateRect();
974		CPDF_Rect rcItem = CFX_List::GetItemRect(nIndex);
975		SetScrollPosY(rcItem.top);
976	}
977}
978
979FX_INT32 CFX_ListCtrl::GetTopItem() const
980{
981	FX_INT32 nItemIndex = this->GetItemIndex(this->GetBTPoint());
982
983	if (!IsItemVisible(nItemIndex) && IsItemVisible(nItemIndex + 1))
984			nItemIndex += 1;
985
986	return nItemIndex;
987}
988
989void CFX_ListCtrl::Empty()
990{
991	CFX_List::Empty();
992	InvalidateItem(-1);
993}
994
995void CFX_ListCtrl::Cancel()
996{
997	m_aSelItems.DeselectAll();
998}
999
1000FX_INT32 CFX_ListCtrl::GetItemIndex(const CPDF_Point & point) const
1001{
1002	return CFX_List::GetItemIndex(OutToIn(point));
1003}
1004
1005CFX_WideString CFX_ListCtrl::GetText() const
1006{
1007	if (this->IsMultipleSel())
1008		return this->GetItemText(this->m_nCaretIndex);
1009	else
1010		return this->GetItemText(this->m_nSelItem);
1011}
1012
1013