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