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/pwl/cpwl_list_box.h"
8
9#include <sstream>
10
11#include "core/fxge/cfx_renderdevice.h"
12#include "fpdfsdk/pwl/cpwl_edit.h"
13#include "fpdfsdk/pwl/cpwl_edit_ctrl.h"
14#include "fpdfsdk/pwl/cpwl_edit_impl.h"
15#include "fpdfsdk/pwl/cpwl_list_impl.h"
16#include "fpdfsdk/pwl/cpwl_scroll_bar.h"
17#include "fpdfsdk/pwl/cpwl_wnd.h"
18#include "public/fpdf_fwlevent.h"
19#include "third_party/base/ptr_util.h"
20
21CPWL_List_Notify::CPWL_List_Notify(CPWL_ListBox* pList) : m_pList(pList) {
22  ASSERT(m_pList);
23}
24
25CPWL_List_Notify::~CPWL_List_Notify() {}
26
27void CPWL_List_Notify::IOnSetScrollInfoY(float fPlateMin,
28                                         float fPlateMax,
29                                         float fContentMin,
30                                         float fContentMax,
31                                         float fSmallStep,
32                                         float fBigStep) {
33  PWL_SCROLL_INFO Info;
34  Info.fPlateWidth = fPlateMax - fPlateMin;
35  Info.fContentMin = fContentMin;
36  Info.fContentMax = fContentMax;
37  Info.fSmallStep = fSmallStep;
38  Info.fBigStep = fBigStep;
39  m_pList->SetScrollInfo(Info);
40
41  CPWL_ScrollBar* pScroll = m_pList->GetVScrollBar();
42  if (!pScroll)
43    return;
44
45  if (IsFloatBigger(Info.fPlateWidth, Info.fContentMax - Info.fContentMin) ||
46      IsFloatEqual(Info.fPlateWidth, Info.fContentMax - Info.fContentMin)) {
47    if (pScroll->IsVisible()) {
48      pScroll->SetVisible(false);
49      m_pList->RePosChildWnd();
50    }
51  } else {
52    if (!pScroll->IsVisible()) {
53      pScroll->SetVisible(true);
54      m_pList->RePosChildWnd();
55    }
56  }
57}
58
59void CPWL_List_Notify::IOnSetScrollPosY(float fy) {
60  m_pList->SetScrollPosition(fy);
61}
62
63void CPWL_List_Notify::IOnInvalidateRect(CFX_FloatRect* pRect) {
64  m_pList->InvalidateRect(pRect);
65}
66
67CPWL_ListBox::CPWL_ListBox()
68    : m_pList(new CPWL_ListCtrl),
69      m_bMouseDown(false),
70      m_bHoverSel(false),
71      m_pFillerNotify(nullptr) {}
72
73CPWL_ListBox::~CPWL_ListBox() {}
74
75ByteString CPWL_ListBox::GetClassName() const {
76  return "CPWL_ListBox";
77}
78
79void CPWL_ListBox::OnCreated() {
80  m_pList->SetFontMap(GetFontMap());
81  m_pListNotify = pdfium::MakeUnique<CPWL_List_Notify>(this);
82  m_pList->SetNotify(m_pListNotify.get());
83
84  SetHoverSel(HasFlag(PLBS_HOVERSEL));
85  m_pList->SetMultipleSel(HasFlag(PLBS_MULTIPLESEL));
86  m_pList->SetFontSize(GetCreationParams().fFontSize);
87
88  m_bHoverSel = HasFlag(PLBS_HOVERSEL);
89}
90
91void CPWL_ListBox::OnDestroy() {
92  // Make sure the notifier is removed from the list as we are about to
93  // destroy the notifier and don't want to leave a dangling pointer.
94  m_pList->SetNotify(nullptr);
95  m_pListNotify.reset();
96}
97
98void CPWL_ListBox::DrawThisAppearance(CFX_RenderDevice* pDevice,
99                                      const CFX_Matrix& mtUser2Device) {
100  CPWL_Wnd::DrawThisAppearance(pDevice, mtUser2Device);
101
102  CFX_FloatRect rcPlate = m_pList->GetPlateRect();
103  CFX_FloatRect rcList = GetListRect();
104  CFX_FloatRect rcClient = GetClientRect();
105
106  for (int32_t i = 0, sz = m_pList->GetCount(); i < sz; i++) {
107    CFX_FloatRect rcItem = m_pList->GetItemRect(i);
108    if (rcItem.bottom > rcPlate.top || rcItem.top < rcPlate.bottom)
109      continue;
110
111    CFX_PointF ptOffset(rcItem.left, (rcItem.top + rcItem.bottom) * 0.5f);
112    if (CPWL_EditImpl* pEdit = m_pList->GetItemEdit(i)) {
113      CFX_FloatRect rcContent = pEdit->GetContentRect();
114      if (rcContent.Width() > rcClient.Width())
115        rcItem.Intersect(rcList);
116      else
117        rcItem.Intersect(rcClient);
118    }
119
120    if (m_pList->IsItemSelected(i)) {
121      CFX_SystemHandler* pSysHandler = GetSystemHandler();
122      if (pSysHandler && pSysHandler->IsSelectionImplemented()) {
123        CPWL_EditImpl::DrawEdit(pDevice, mtUser2Device, m_pList->GetItemEdit(i),
124                                GetTextColor().ToFXColor(255), rcList, ptOffset,
125                                nullptr, pSysHandler, m_pFormFiller.Get());
126        pSysHandler->OutputSelectedRect(m_pFormFiller.Get(), rcItem);
127      } else {
128        pDevice->DrawFillRect(&mtUser2Device, rcItem,
129                              ArgbEncode(255, 0, 51, 113));
130        CPWL_EditImpl::DrawEdit(pDevice, mtUser2Device, m_pList->GetItemEdit(i),
131                                ArgbEncode(255, 255, 255, 255), rcList,
132                                ptOffset, nullptr, pSysHandler,
133                                m_pFormFiller.Get());
134      }
135    } else {
136      CFX_SystemHandler* pSysHandler = GetSystemHandler();
137      CPWL_EditImpl::DrawEdit(pDevice, mtUser2Device, m_pList->GetItemEdit(i),
138                              GetTextColor().ToFXColor(255), rcList, ptOffset,
139                              nullptr, pSysHandler, nullptr);
140    }
141  }
142}
143
144bool CPWL_ListBox::OnKeyDown(uint16_t nChar, uint32_t nFlag) {
145  CPWL_Wnd::OnKeyDown(nChar, nFlag);
146
147  switch (nChar) {
148    default:
149      return false;
150    case FWL_VKEY_Up:
151    case FWL_VKEY_Down:
152    case FWL_VKEY_Home:
153    case FWL_VKEY_Left:
154    case FWL_VKEY_End:
155    case FWL_VKEY_Right:
156      break;
157  }
158
159  switch (nChar) {
160    case FWL_VKEY_Up:
161      m_pList->OnVK_UP(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
162      break;
163    case FWL_VKEY_Down:
164      m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
165      break;
166    case FWL_VKEY_Home:
167      m_pList->OnVK_HOME(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
168      break;
169    case FWL_VKEY_Left:
170      m_pList->OnVK_LEFT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
171      break;
172    case FWL_VKEY_End:
173      m_pList->OnVK_END(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
174      break;
175    case FWL_VKEY_Right:
176      m_pList->OnVK_RIGHT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
177      break;
178    case FWL_VKEY_Delete:
179      break;
180  }
181  OnNotifySelectionChanged(true, nFlag);
182  return true;
183}
184
185bool CPWL_ListBox::OnChar(uint16_t nChar, uint32_t nFlag) {
186  CPWL_Wnd::OnChar(nChar, nFlag);
187
188  if (!m_pList->OnChar(nChar, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)))
189    return false;
190
191  OnNotifySelectionChanged(true, nFlag);
192  return true;
193}
194
195bool CPWL_ListBox::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
196  CPWL_Wnd::OnLButtonDown(point, nFlag);
197
198  if (ClientHitTest(point)) {
199    m_bMouseDown = true;
200    SetFocus();
201    SetCapture();
202
203    m_pList->OnMouseDown(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
204  }
205
206  return true;
207}
208
209bool CPWL_ListBox::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
210  CPWL_Wnd::OnLButtonUp(point, nFlag);
211
212  if (m_bMouseDown) {
213    ReleaseCapture();
214    m_bMouseDown = false;
215  }
216  OnNotifySelectionChanged(false, nFlag);
217  return true;
218}
219
220void CPWL_ListBox::SetHoverSel(bool bHoverSel) {
221  m_bHoverSel = bHoverSel;
222}
223
224bool CPWL_ListBox::OnMouseMove(const CFX_PointF& point, uint32_t nFlag) {
225  CPWL_Wnd::OnMouseMove(point, nFlag);
226
227  if (m_bHoverSel && !IsCaptureMouse() && ClientHitTest(point))
228    m_pList->Select(m_pList->GetItemIndex(point));
229  if (m_bMouseDown)
230    m_pList->OnMouseMove(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
231
232  return true;
233}
234
235void CPWL_ListBox::SetScrollInfo(const PWL_SCROLL_INFO& info) {
236  if (CPWL_Wnd* pChild = GetVScrollBar())
237    pChild->SetScrollInfo(info);
238}
239
240void CPWL_ListBox::SetScrollPosition(float pos) {
241  if (CPWL_Wnd* pChild = GetVScrollBar())
242    pChild->SetScrollPosition(pos);
243}
244
245void CPWL_ListBox::ScrollWindowVertically(float pos) {
246  m_pList->SetScrollPos(CFX_PointF(0, pos));
247}
248
249void CPWL_ListBox::KillFocus() {
250  CPWL_Wnd::KillFocus();
251}
252
253bool CPWL_ListBox::RePosChildWnd() {
254  if (!CPWL_Wnd::RePosChildWnd())
255    return false;
256
257  m_pList->SetPlateRect(GetListRect());
258  return true;
259}
260
261bool CPWL_ListBox::OnNotifySelectionChanged(bool bKeyDown, uint32_t nFlag) {
262  if (!m_pFillerNotify)
263    return false;
264
265  CPWL_Wnd::ObservedPtr thisObserved(this);
266
267  WideString swChange = GetText();
268  WideString strChangeEx;
269  int nSelStart = 0;
270  int nSelEnd = swChange.GetLength();
271  bool bRC;
272  bool bExit;
273  std::tie(bRC, bExit) = m_pFillerNotify->OnBeforeKeyStroke(
274      GetAttachedData(), swChange, strChangeEx, nSelStart, nSelEnd, bKeyDown,
275      nFlag);
276
277  if (!thisObserved)
278    return false;
279
280  return bExit;
281}
282
283CFX_FloatRect CPWL_ListBox::GetFocusRect() const {
284  if (m_pList->IsMultipleSel()) {
285    CFX_FloatRect rcCaret = m_pList->GetItemRect(m_pList->GetCaret());
286    rcCaret.Intersect(GetClientRect());
287    return rcCaret;
288  }
289
290  return CPWL_Wnd::GetFocusRect();
291}
292
293void CPWL_ListBox::AddString(const WideString& str) {
294  m_pList->AddString(str);
295}
296
297WideString CPWL_ListBox::GetText() const {
298  return m_pList->GetText();
299}
300
301void CPWL_ListBox::SetFontSize(float fFontSize) {
302  m_pList->SetFontSize(fFontSize);
303}
304
305float CPWL_ListBox::GetFontSize() const {
306  return m_pList->GetFontSize();
307}
308
309void CPWL_ListBox::Select(int32_t nItemIndex) {
310  m_pList->Select(nItemIndex);
311}
312
313void CPWL_ListBox::SetCaret(int32_t nItemIndex) {
314  m_pList->SetCaret(nItemIndex);
315}
316
317void CPWL_ListBox::SetTopVisibleIndex(int32_t nItemIndex) {
318  m_pList->SetTopItem(nItemIndex);
319}
320
321void CPWL_ListBox::ScrollToListItem(int32_t nItemIndex) {
322  m_pList->ScrollToListItem(nItemIndex);
323}
324
325void CPWL_ListBox::ResetContent() {
326  m_pList->Empty();
327}
328
329void CPWL_ListBox::Reset() {
330  m_pList->Cancel();
331}
332
333bool CPWL_ListBox::IsMultipleSel() const {
334  return m_pList->IsMultipleSel();
335}
336
337int32_t CPWL_ListBox::GetCaretIndex() const {
338  return m_pList->GetCaret();
339}
340
341int32_t CPWL_ListBox::GetCurSel() const {
342  return m_pList->GetSelect();
343}
344
345bool CPWL_ListBox::IsItemSelected(int32_t nItemIndex) const {
346  return m_pList->IsItemSelected(nItemIndex);
347}
348
349int32_t CPWL_ListBox::GetTopVisibleIndex() const {
350  m_pList->ScrollToListItem(m_pList->GetFirstSelected());
351  return m_pList->GetTopItem();
352}
353
354int32_t CPWL_ListBox::GetCount() const {
355  return m_pList->GetCount();
356}
357
358int32_t CPWL_ListBox::FindNext(int32_t nIndex, wchar_t nChar) const {
359  return m_pList->FindNext(nIndex, nChar);
360}
361
362CFX_FloatRect CPWL_ListBox::GetContentRect() const {
363  return m_pList->GetContentRect();
364}
365
366float CPWL_ListBox::GetFirstHeight() const {
367  return m_pList->GetFirstHeight();
368}
369
370CFX_FloatRect CPWL_ListBox::GetListRect() const {
371  float width = static_cast<float>(GetBorderWidth() + GetInnerBorderWidth());
372  return GetWindowRect().GetDeflated(width, width);
373}
374
375bool CPWL_ListBox::OnMouseWheel(short zDelta,
376                                const CFX_PointF& point,
377                                uint32_t nFlag) {
378  if (zDelta < 0)
379    m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
380  else
381    m_pList->OnVK_UP(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
382
383  OnNotifySelectionChanged(false, nFlag);
384  return true;
385}
386