1 // Copyright 2014 The PDFium Authors
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_impl.h"
15 #include "fpdfsdk/pwl/cpwl_scroll_bar.h"
16 #include "fpdfsdk/pwl/ipwl_fillernotify.h"
17 #include "public/fpdf_fwlevent.h"
18 #include "third_party/base/numerics/safe_conversions.h"
19
CPWL_ListBox(const CreateParams & cp,std::unique_ptr<IPWL_FillerNotify::PerWindowData> pAttachedData)20 CPWL_ListBox::CPWL_ListBox(
21 const CreateParams& cp,
22 std::unique_ptr<IPWL_FillerNotify::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_FillerNotify* pSysHandler = GetFillerNotify();
66 if (m_pListCtrl->IsItemSelected(i)) {
67 if (pSysHandler->IsSelectionImplemented()) {
68 m_pListCtrl->GetItemEdit(i)->DrawEdit(
69 pDevice, mtUser2Device, GetTextColor().ToFXColor(255), rcList,
70 ptOffset, nullptr, pSysHandler, GetAttachedData());
71 pSysHandler->OutputSelectedRect(GetAttachedData(), rcItem);
72 } else {
73 pDevice->DrawFillRect(&mtUser2Device, rcItem,
74 ArgbEncode(255, 0, 51, 113));
75 m_pListCtrl->GetItemEdit(i)->DrawEdit(
76 pDevice, mtUser2Device, ArgbEncode(255, 255, 255, 255), rcList,
77 ptOffset, nullptr, pSysHandler, GetAttachedData());
78 }
79 } else {
80 m_pListCtrl->GetItemEdit(i)->DrawEdit(
81 pDevice, mtUser2Device, GetTextColor().ToFXColor(255), rcList,
82 ptOffset, nullptr, pSysHandler, nullptr);
83 }
84 }
85 }
86
OnKeyDown(FWL_VKEYCODE nKeyCode,Mask<FWL_EVENTFLAG> nFlag)87 bool CPWL_ListBox::OnKeyDown(FWL_VKEYCODE nKeyCode, Mask<FWL_EVENTFLAG> nFlag) {
88 CPWL_Wnd::OnKeyDown(nKeyCode, nFlag);
89
90 switch (nKeyCode) {
91 default:
92 return false;
93 case FWL_VKEY_Up:
94 case FWL_VKEY_Down:
95 case FWL_VKEY_Home:
96 case FWL_VKEY_Left:
97 case FWL_VKEY_End:
98 case FWL_VKEY_Right:
99 break;
100 }
101
102 switch (nKeyCode) {
103 case FWL_VKEY_Up:
104 m_pListCtrl->OnVK_UP(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
105 break;
106 case FWL_VKEY_Down:
107 m_pListCtrl->OnVK_DOWN(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
108 break;
109 case FWL_VKEY_Home:
110 m_pListCtrl->OnVK_HOME(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
111 break;
112 case FWL_VKEY_Left:
113 m_pListCtrl->OnVK_LEFT(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
114 break;
115 case FWL_VKEY_End:
116 m_pListCtrl->OnVK_END(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
117 break;
118 case FWL_VKEY_Right:
119 m_pListCtrl->OnVK_RIGHT(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
120 break;
121 default:
122 break;
123 }
124 OnNotifySelectionChanged(true, nFlag);
125 return true;
126 }
127
OnChar(uint16_t nChar,Mask<FWL_EVENTFLAG> nFlag)128 bool CPWL_ListBox::OnChar(uint16_t nChar, Mask<FWL_EVENTFLAG> nFlag) {
129 CPWL_Wnd::OnChar(nChar, nFlag);
130
131 if (!m_pListCtrl->OnChar(nChar, IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag)))
132 return false;
133
134 OnNotifySelectionChanged(true, nFlag);
135 return true;
136 }
137
OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,const CFX_PointF & point)138 bool CPWL_ListBox::OnLButtonDown(Mask<FWL_EVENTFLAG> nFlag,
139 const CFX_PointF& point) {
140 CPWL_Wnd::OnLButtonDown(nFlag, point);
141
142 if (ClientHitTest(point)) {
143 m_bMouseDown = true;
144 SetFocus();
145 SetCapture();
146
147 m_pListCtrl->OnMouseDown(point, IsSHIFTKeyDown(nFlag),
148 IsCTRLKeyDown(nFlag));
149 }
150
151 return true;
152 }
153
OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag,const CFX_PointF & point)154 bool CPWL_ListBox::OnLButtonUp(Mask<FWL_EVENTFLAG> nFlag,
155 const CFX_PointF& point) {
156 CPWL_Wnd::OnLButtonUp(nFlag, point);
157
158 if (m_bMouseDown) {
159 ReleaseCapture();
160 m_bMouseDown = false;
161 }
162 OnNotifySelectionChanged(false, nFlag);
163 return true;
164 }
165
SetHoverSel(bool bHoverSel)166 void CPWL_ListBox::SetHoverSel(bool bHoverSel) {
167 m_bHoverSel = bHoverSel;
168 }
169
OnMouseMove(Mask<FWL_EVENTFLAG> nFlag,const CFX_PointF & point)170 bool CPWL_ListBox::OnMouseMove(Mask<FWL_EVENTFLAG> nFlag,
171 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, IsSHIFTKeyDown(nFlag),
178 IsCTRLKeyDown(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,Mask<FWL_EVENTFLAG> nFlag)205 bool CPWL_ListBox::OnNotifySelectionChanged(bool bKeyDown,
206 Mask<FWL_EVENTFLAG> nFlag) {
207 ObservedPtr<CPWL_Wnd> thisObserved(this);
208
209 WideString swChange = GetText();
210 WideString strChangeEx;
211 int nSelStart = 0;
212 int nSelEnd = pdfium::base::checked_cast<int>(swChange.GetLength());
213 bool bRC;
214 bool bExit;
215 std::tie(bRC, bExit) = GetFillerNotify()->OnBeforeKeyStroke(
216 GetAttachedData(), swChange, strChangeEx, nSelStart, nSelEnd, bKeyDown,
217 nFlag);
218
219 if (!thisObserved)
220 return false;
221
222 return bExit;
223 }
224
GetFocusRect() const225 CFX_FloatRect CPWL_ListBox::GetFocusRect() const {
226 if (m_pListCtrl->IsMultipleSel()) {
227 CFX_FloatRect rcCaret = m_pListCtrl->GetItemRect(m_pListCtrl->GetCaret());
228 rcCaret.Intersect(GetClientRect());
229 return rcCaret;
230 }
231
232 return CPWL_Wnd::GetFocusRect();
233 }
234
AddString(const WideString & str)235 void CPWL_ListBox::AddString(const WideString& str) {
236 m_pListCtrl->AddString(str);
237 }
238
GetText()239 WideString CPWL_ListBox::GetText() {
240 return m_pListCtrl->GetText();
241 }
242
SetFontSize(float fFontSize)243 void CPWL_ListBox::SetFontSize(float fFontSize) {
244 m_pListCtrl->SetFontSize(fFontSize);
245 }
246
GetFontSize() const247 float CPWL_ListBox::GetFontSize() const {
248 return m_pListCtrl->GetFontSize();
249 }
250
OnSetScrollInfoY(float fPlateMin,float fPlateMax,float fContentMin,float fContentMax,float fSmallStep,float fBigStep)251 void CPWL_ListBox::OnSetScrollInfoY(float fPlateMin,
252 float fPlateMax,
253 float fContentMin,
254 float fContentMax,
255 float fSmallStep,
256 float fBigStep) {
257 PWL_SCROLL_INFO Info;
258 Info.fPlateWidth = fPlateMax - fPlateMin;
259 Info.fContentMin = fContentMin;
260 Info.fContentMax = fContentMax;
261 Info.fSmallStep = fSmallStep;
262 Info.fBigStep = fBigStep;
263 SetScrollInfo(Info);
264
265 CPWL_ScrollBar* pScroll = GetVScrollBar();
266 if (!pScroll)
267 return;
268
269 if (FXSYS_IsFloatBigger(Info.fPlateWidth,
270 Info.fContentMax - Info.fContentMin) ||
271 FXSYS_IsFloatEqual(Info.fPlateWidth,
272 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
IsMultipleSel() const313 bool CPWL_ListBox::IsMultipleSel() const {
314 return m_pListCtrl->IsMultipleSel();
315 }
316
GetCaretIndex() const317 int32_t CPWL_ListBox::GetCaretIndex() const {
318 return m_pListCtrl->GetCaret();
319 }
320
GetCurSel() const321 int32_t CPWL_ListBox::GetCurSel() const {
322 return m_pListCtrl->GetSelect();
323 }
324
IsItemSelected(int32_t nItemIndex) const325 bool CPWL_ListBox::IsItemSelected(int32_t nItemIndex) const {
326 return m_pListCtrl->IsItemSelected(nItemIndex);
327 }
328
GetTopVisibleIndex() const329 int32_t CPWL_ListBox::GetTopVisibleIndex() const {
330 m_pListCtrl->ScrollToListItem(m_pListCtrl->GetFirstSelected());
331 return m_pListCtrl->GetTopItem();
332 }
333
GetCount() const334 int32_t CPWL_ListBox::GetCount() const {
335 return m_pListCtrl->GetCount();
336 }
337
GetContentRect() const338 CFX_FloatRect CPWL_ListBox::GetContentRect() const {
339 return m_pListCtrl->GetContentRect();
340 }
341
GetFirstHeight() const342 float CPWL_ListBox::GetFirstHeight() const {
343 return m_pListCtrl->GetFirstHeight();
344 }
345
GetListRect() const346 CFX_FloatRect CPWL_ListBox::GetListRect() const {
347 float width = static_cast<float>(GetBorderWidth() + GetInnerBorderWidth());
348 return GetWindowRect().GetDeflated(width, width);
349 }
350
OnMouseWheel(Mask<FWL_EVENTFLAG> nFlag,const CFX_PointF & point,const CFX_Vector & delta)351 bool CPWL_ListBox::OnMouseWheel(Mask<FWL_EVENTFLAG> nFlag,
352 const CFX_PointF& point,
353 const CFX_Vector& delta) {
354 if (delta.y < 0)
355 m_pListCtrl->OnVK_DOWN(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
356 else
357 m_pListCtrl->OnVK_UP(IsSHIFTKeyDown(nFlag), IsCTRLKeyDown(nFlag));
358
359 OnNotifySelectionChanged(false, nFlag);
360 return true;
361 }
362