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/pdfwindow/PWL_ListBox.h"
8
9 #include "fpdfsdk/fxedit/fxet_edit.h"
10 #include "fpdfsdk/fxedit/fxet_list.h"
11 #include "fpdfsdk/pdfwindow/PWL_Edit.h"
12 #include "fpdfsdk/pdfwindow/PWL_EditCtrl.h"
13 #include "fpdfsdk/pdfwindow/PWL_ScrollBar.h"
14 #include "fpdfsdk/pdfwindow/PWL_Utils.h"
15 #include "fpdfsdk/pdfwindow/PWL_Wnd.h"
16 #include "public/fpdf_fwlevent.h"
17 #include "third_party/base/ptr_util.h"
18
CPWL_List_Notify(CPWL_ListBox * pList)19 CPWL_List_Notify::CPWL_List_Notify(CPWL_ListBox* pList) : m_pList(pList) {
20 ASSERT(m_pList);
21 }
22
~CPWL_List_Notify()23 CPWL_List_Notify::~CPWL_List_Notify() {}
24
IOnSetScrollInfoY(FX_FLOAT fPlateMin,FX_FLOAT fPlateMax,FX_FLOAT fContentMin,FX_FLOAT fContentMax,FX_FLOAT fSmallStep,FX_FLOAT fBigStep)25 void CPWL_List_Notify::IOnSetScrollInfoY(FX_FLOAT fPlateMin,
26 FX_FLOAT fPlateMax,
27 FX_FLOAT fContentMin,
28 FX_FLOAT fContentMax,
29 FX_FLOAT fSmallStep,
30 FX_FLOAT fBigStep) {
31 PWL_SCROLL_INFO Info;
32
33 Info.fPlateWidth = fPlateMax - fPlateMin;
34 Info.fContentMin = fContentMin;
35 Info.fContentMax = fContentMax;
36 Info.fSmallStep = fSmallStep;
37 Info.fBigStep = fBigStep;
38
39 m_pList->OnNotify(m_pList, PNM_SETSCROLLINFO, SBT_VSCROLL, (intptr_t)&Info);
40
41 if (CPWL_ScrollBar* pScroll = m_pList->GetVScrollBar()) {
42 if (IsFloatBigger(Info.fPlateWidth, Info.fContentMax - Info.fContentMin) ||
43 IsFloatEqual(Info.fPlateWidth, Info.fContentMax - Info.fContentMin)) {
44 if (pScroll->IsVisible()) {
45 pScroll->SetVisible(false);
46 m_pList->RePosChildWnd();
47 }
48 } else {
49 if (!pScroll->IsVisible()) {
50 pScroll->SetVisible(true);
51 m_pList->RePosChildWnd();
52 }
53 }
54 }
55 }
56
IOnSetScrollPosY(FX_FLOAT fy)57 void CPWL_List_Notify::IOnSetScrollPosY(FX_FLOAT fy) {
58 m_pList->OnNotify(m_pList, PNM_SETSCROLLPOS, SBT_VSCROLL, (intptr_t)&fy);
59 }
60
IOnInvalidateRect(CFX_FloatRect * pRect)61 void CPWL_List_Notify::IOnInvalidateRect(CFX_FloatRect* pRect) {
62 m_pList->InvalidateRect(pRect);
63 }
64
CPWL_ListBox()65 CPWL_ListBox::CPWL_ListBox()
66 : m_pList(new CFX_ListCtrl),
67 m_bMouseDown(false),
68 m_bHoverSel(false),
69 m_pFillerNotify(nullptr) {}
70
~CPWL_ListBox()71 CPWL_ListBox::~CPWL_ListBox() {
72 }
73
GetClassName() const74 CFX_ByteString CPWL_ListBox::GetClassName() const {
75 return "CPWL_ListBox";
76 }
77
OnCreated()78 void CPWL_ListBox::OnCreated() {
79 m_pList->SetFontMap(GetFontMap());
80 m_pListNotify = pdfium::MakeUnique<CPWL_List_Notify>(this);
81 m_pList->SetNotify(m_pListNotify.get());
82
83 SetHoverSel(HasFlag(PLBS_HOVERSEL));
84 m_pList->SetMultipleSel(HasFlag(PLBS_MULTIPLESEL));
85 m_pList->SetFontSize(GetCreationParam().fFontSize);
86
87 m_bHoverSel = HasFlag(PLBS_HOVERSEL);
88 }
89
OnDestroy()90 void CPWL_ListBox::OnDestroy() {
91 // Make sure the notifier is removed from the list as we are about to
92 // destroy the notifier and don't want to leave a dangling pointer.
93 m_pList->SetNotify(nullptr);
94 m_pListNotify.reset();
95 }
96
GetThisAppearanceStream(CFX_ByteTextBuf & sAppStream)97 void CPWL_ListBox::GetThisAppearanceStream(CFX_ByteTextBuf& sAppStream) {
98 CPWL_Wnd::GetThisAppearanceStream(sAppStream);
99
100 CFX_ByteTextBuf sListItems;
101
102 CFX_FloatRect rcPlate = m_pList->GetPlateRect();
103 for (int32_t i = 0, sz = m_pList->GetCount(); i < sz; i++) {
104 CFX_FloatRect rcItem = m_pList->GetItemRect(i);
105
106 if (rcItem.bottom > rcPlate.top || rcItem.top < rcPlate.bottom)
107 continue;
108
109 CFX_PointF ptOffset(rcItem.left, (rcItem.top + rcItem.bottom) * 0.5f);
110 if (m_pList->IsItemSelected(i)) {
111 sListItems << CPWL_Utils::GetRectFillAppStream(rcItem,
112 PWL_DEFAULT_SELBACKCOLOR)
113 .AsStringC();
114 CFX_ByteString sItem =
115 CPWL_Utils::GetEditAppStream(m_pList->GetItemEdit(i), ptOffset);
116 if (sItem.GetLength() > 0) {
117 sListItems << "BT\n"
118 << CPWL_Utils::GetColorAppStream(PWL_DEFAULT_SELTEXTCOLOR)
119 .AsStringC()
120 << sItem.AsStringC() << "ET\n";
121 }
122 } else {
123 CFX_ByteString sItem =
124 CPWL_Utils::GetEditAppStream(m_pList->GetItemEdit(i), ptOffset);
125 if (sItem.GetLength() > 0) {
126 sListItems << "BT\n"
127 << CPWL_Utils::GetColorAppStream(GetTextColor()).AsStringC()
128 << sItem.AsStringC() << "ET\n";
129 }
130 }
131 }
132
133 if (sListItems.GetLength() > 0) {
134 CFX_ByteTextBuf sClip;
135 CFX_FloatRect rcClient = GetClientRect();
136
137 sClip << "q\n";
138 sClip << rcClient.left << " " << rcClient.bottom << " "
139 << rcClient.right - rcClient.left << " "
140 << rcClient.top - rcClient.bottom << " re W n\n";
141
142 sClip << sListItems << "Q\n";
143
144 sAppStream << "/Tx BMC\n" << sClip << "EMC\n";
145 }
146 }
147
DrawThisAppearance(CFX_RenderDevice * pDevice,CFX_Matrix * pUser2Device)148 void CPWL_ListBox::DrawThisAppearance(CFX_RenderDevice* pDevice,
149 CFX_Matrix* pUser2Device) {
150 CPWL_Wnd::DrawThisAppearance(pDevice, pUser2Device);
151
152 CFX_FloatRect rcPlate = m_pList->GetPlateRect();
153 CFX_FloatRect rcList = GetListRect();
154 CFX_FloatRect rcClient = GetClientRect();
155
156 for (int32_t i = 0, sz = m_pList->GetCount(); i < sz; i++) {
157 CFX_FloatRect rcItem = m_pList->GetItemRect(i);
158 if (rcItem.bottom > rcPlate.top || rcItem.top < rcPlate.bottom)
159 continue;
160
161 CFX_PointF ptOffset(rcItem.left, (rcItem.top + rcItem.bottom) * 0.5f);
162 if (CFX_Edit* pEdit = m_pList->GetItemEdit(i)) {
163 CFX_FloatRect rcContent = pEdit->GetContentRect();
164 if (rcContent.Width() > rcClient.Width())
165 rcItem.Intersect(rcList);
166 else
167 rcItem.Intersect(rcClient);
168 }
169
170 if (m_pList->IsItemSelected(i)) {
171 CFX_SystemHandler* pSysHandler = GetSystemHandler();
172 if (pSysHandler && pSysHandler->IsSelectionImplemented()) {
173 CFX_Edit::DrawEdit(pDevice, pUser2Device, m_pList->GetItemEdit(i),
174 GetTextColor().ToFXColor(255), rcList, ptOffset,
175 nullptr, pSysHandler, m_pFormFiller);
176 pSysHandler->OutputSelectedRect(m_pFormFiller, rcItem);
177 } else {
178 CPWL_Utils::DrawFillRect(pDevice, pUser2Device, rcItem,
179 ArgbEncode(255, 0, 51, 113));
180 CFX_Edit::DrawEdit(pDevice, pUser2Device, m_pList->GetItemEdit(i),
181 ArgbEncode(255, 255, 255, 255), rcList, ptOffset,
182 nullptr, pSysHandler, m_pFormFiller);
183 }
184 } else {
185 CFX_SystemHandler* pSysHandler = GetSystemHandler();
186 CFX_Edit::DrawEdit(pDevice, pUser2Device, m_pList->GetItemEdit(i),
187 GetTextColor().ToFXColor(255), rcList, ptOffset,
188 nullptr, pSysHandler, nullptr);
189 }
190 }
191 }
192
OnKeyDown(uint16_t nChar,uint32_t nFlag)193 bool CPWL_ListBox::OnKeyDown(uint16_t nChar, uint32_t nFlag) {
194 CPWL_Wnd::OnKeyDown(nChar, nFlag);
195
196 switch (nChar) {
197 default:
198 return false;
199 case FWL_VKEY_Up:
200 case FWL_VKEY_Down:
201 case FWL_VKEY_Home:
202 case FWL_VKEY_Left:
203 case FWL_VKEY_End:
204 case FWL_VKEY_Right:
205 break;
206 }
207
208 switch (nChar) {
209 case FWL_VKEY_Up:
210 m_pList->OnVK_UP(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
211 break;
212 case FWL_VKEY_Down:
213 m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
214 break;
215 case FWL_VKEY_Home:
216 m_pList->OnVK_HOME(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
217 break;
218 case FWL_VKEY_Left:
219 m_pList->OnVK_LEFT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
220 break;
221 case FWL_VKEY_End:
222 m_pList->OnVK_END(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
223 break;
224 case FWL_VKEY_Right:
225 m_pList->OnVK_RIGHT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
226 break;
227 case FWL_VKEY_Delete:
228 break;
229 }
230
231 bool bExit = false;
232 OnNotifySelChanged(true, bExit, nFlag);
233
234 return true;
235 }
236
OnChar(uint16_t nChar,uint32_t nFlag)237 bool CPWL_ListBox::OnChar(uint16_t nChar, uint32_t nFlag) {
238 CPWL_Wnd::OnChar(nChar, nFlag);
239
240 if (!m_pList->OnChar(nChar, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)))
241 return false;
242
243 bool bExit = false;
244 OnNotifySelChanged(true, bExit, nFlag);
245
246 return true;
247 }
248
OnLButtonDown(const CFX_PointF & point,uint32_t nFlag)249 bool CPWL_ListBox::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
250 CPWL_Wnd::OnLButtonDown(point, nFlag);
251
252 if (ClientHitTest(point)) {
253 m_bMouseDown = true;
254 SetFocus();
255 SetCapture();
256
257 m_pList->OnMouseDown(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
258 }
259
260 return true;
261 }
262
OnLButtonUp(const CFX_PointF & point,uint32_t nFlag)263 bool CPWL_ListBox::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
264 CPWL_Wnd::OnLButtonUp(point, nFlag);
265
266 if (m_bMouseDown) {
267 ReleaseCapture();
268 m_bMouseDown = false;
269 }
270
271 bool bExit = false;
272 OnNotifySelChanged(false, bExit, nFlag);
273
274 return true;
275 }
276
SetHoverSel(bool bHoverSel)277 void CPWL_ListBox::SetHoverSel(bool bHoverSel) {
278 m_bHoverSel = bHoverSel;
279 }
280
OnMouseMove(const CFX_PointF & point,uint32_t nFlag)281 bool CPWL_ListBox::OnMouseMove(const CFX_PointF& point, uint32_t nFlag) {
282 CPWL_Wnd::OnMouseMove(point, nFlag);
283
284 if (m_bHoverSel && !IsCaptureMouse() && ClientHitTest(point))
285 m_pList->Select(m_pList->GetItemIndex(point));
286 if (m_bMouseDown)
287 m_pList->OnMouseMove(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
288
289 return true;
290 }
291
OnNotify(CPWL_Wnd * pWnd,uint32_t msg,intptr_t wParam,intptr_t lParam)292 void CPWL_ListBox::OnNotify(CPWL_Wnd* pWnd,
293 uint32_t msg,
294 intptr_t wParam,
295 intptr_t lParam) {
296 CPWL_Wnd::OnNotify(pWnd, msg, wParam, lParam);
297
298 FX_FLOAT fPos;
299
300 switch (msg) {
301 case PNM_SETSCROLLINFO:
302 switch (wParam) {
303 case SBT_VSCROLL:
304 if (CPWL_Wnd* pChild = GetVScrollBar()) {
305 pChild->OnNotify(pWnd, PNM_SETSCROLLINFO, wParam, lParam);
306 }
307 break;
308 }
309 break;
310 case PNM_SETSCROLLPOS:
311 switch (wParam) {
312 case SBT_VSCROLL:
313 if (CPWL_Wnd* pChild = GetVScrollBar()) {
314 pChild->OnNotify(pWnd, PNM_SETSCROLLPOS, wParam, lParam);
315 }
316 break;
317 }
318 break;
319 case PNM_SCROLLWINDOW:
320 fPos = *(FX_FLOAT*)lParam;
321 switch (wParam) {
322 case SBT_VSCROLL:
323 m_pList->SetScrollPos(CFX_PointF(0, fPos));
324 break;
325 }
326 break;
327 }
328 }
329
KillFocus()330 void CPWL_ListBox::KillFocus() {
331 CPWL_Wnd::KillFocus();
332 }
333
RePosChildWnd()334 void CPWL_ListBox::RePosChildWnd() {
335 CPWL_Wnd::RePosChildWnd();
336
337 m_pList->SetPlateRect(GetListRect());
338 }
339
OnNotifySelChanged(bool bKeyDown,bool & bExit,uint32_t nFlag)340 void CPWL_ListBox::OnNotifySelChanged(bool bKeyDown,
341 bool& bExit,
342 uint32_t nFlag) {
343 if (!m_pFillerNotify)
344 return;
345
346 bool bRC = true;
347 CFX_WideString swChange = GetText();
348 CFX_WideString strChangeEx;
349 int nSelStart = 0;
350 int nSelEnd = swChange.GetLength();
351 m_pFillerNotify->OnBeforeKeyStroke(GetAttachedData(), swChange, strChangeEx,
352 nSelStart, nSelEnd, bKeyDown, bRC, bExit,
353 nFlag);
354 }
355
GetFocusRect() const356 CFX_FloatRect CPWL_ListBox::GetFocusRect() const {
357 if (m_pList->IsMultipleSel()) {
358 CFX_FloatRect rcCaret = m_pList->GetItemRect(m_pList->GetCaret());
359 rcCaret.Intersect(GetClientRect());
360 return rcCaret;
361 }
362
363 return CPWL_Wnd::GetFocusRect();
364 }
365
AddString(const CFX_WideString & str)366 void CPWL_ListBox::AddString(const CFX_WideString& str) {
367 m_pList->AddString(str);
368 }
369
GetText() const370 CFX_WideString CPWL_ListBox::GetText() const {
371 return m_pList->GetText();
372 }
373
SetFontSize(FX_FLOAT fFontSize)374 void CPWL_ListBox::SetFontSize(FX_FLOAT fFontSize) {
375 m_pList->SetFontSize(fFontSize);
376 }
377
GetFontSize() const378 FX_FLOAT CPWL_ListBox::GetFontSize() const {
379 return m_pList->GetFontSize();
380 }
381
Select(int32_t nItemIndex)382 void CPWL_ListBox::Select(int32_t nItemIndex) {
383 m_pList->Select(nItemIndex);
384 }
385
SetCaret(int32_t nItemIndex)386 void CPWL_ListBox::SetCaret(int32_t nItemIndex) {
387 m_pList->SetCaret(nItemIndex);
388 }
389
SetTopVisibleIndex(int32_t nItemIndex)390 void CPWL_ListBox::SetTopVisibleIndex(int32_t nItemIndex) {
391 m_pList->SetTopItem(nItemIndex);
392 }
393
ScrollToListItem(int32_t nItemIndex)394 void CPWL_ListBox::ScrollToListItem(int32_t nItemIndex) {
395 m_pList->ScrollToListItem(nItemIndex);
396 }
397
ResetContent()398 void CPWL_ListBox::ResetContent() {
399 m_pList->Empty();
400 }
401
Reset()402 void CPWL_ListBox::Reset() {
403 m_pList->Cancel();
404 }
405
IsMultipleSel() const406 bool CPWL_ListBox::IsMultipleSel() const {
407 return m_pList->IsMultipleSel();
408 }
409
GetCaretIndex() const410 int32_t CPWL_ListBox::GetCaretIndex() const {
411 return m_pList->GetCaret();
412 }
413
GetCurSel() const414 int32_t CPWL_ListBox::GetCurSel() const {
415 return m_pList->GetSelect();
416 }
417
IsItemSelected(int32_t nItemIndex) const418 bool CPWL_ListBox::IsItemSelected(int32_t nItemIndex) const {
419 return m_pList->IsItemSelected(nItemIndex);
420 }
421
GetTopVisibleIndex() const422 int32_t CPWL_ListBox::GetTopVisibleIndex() const {
423 m_pList->ScrollToListItem(m_pList->GetFirstSelected());
424 return m_pList->GetTopItem();
425 }
426
GetCount() const427 int32_t CPWL_ListBox::GetCount() const {
428 return m_pList->GetCount();
429 }
430
FindNext(int32_t nIndex,FX_WCHAR nChar) const431 int32_t CPWL_ListBox::FindNext(int32_t nIndex, FX_WCHAR nChar) const {
432 return m_pList->FindNext(nIndex, nChar);
433 }
434
GetContentRect() const435 CFX_FloatRect CPWL_ListBox::GetContentRect() const {
436 return m_pList->GetContentRect();
437 }
438
GetFirstHeight() const439 FX_FLOAT CPWL_ListBox::GetFirstHeight() const {
440 return m_pList->GetFirstHeight();
441 }
442
GetListRect() const443 CFX_FloatRect CPWL_ListBox::GetListRect() const {
444 return CPWL_Utils::DeflateRect(
445 GetWindowRect(), (FX_FLOAT)(GetBorderWidth() + GetInnerBorderWidth()));
446 }
447
OnMouseWheel(short zDelta,const CFX_PointF & point,uint32_t nFlag)448 bool CPWL_ListBox::OnMouseWheel(short zDelta,
449 const CFX_PointF& point,
450 uint32_t nFlag) {
451 if (zDelta < 0)
452 m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
453 else
454 m_pList->OnVK_UP(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
455
456 bool bExit = false;
457 OnNotifySelChanged(false, bExit, nFlag);
458 return true;
459 }
460