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