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