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