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/pdfwindow/PWL_ListBox.h"
8
9 #include "fpdfsdk/include/pdfwindow/PWL_Edit.h"
10 #include "fpdfsdk/include/pdfwindow/PWL_EditCtrl.h"
11 #include "fpdfsdk/include/pdfwindow/PWL_ScrollBar.h"
12 #include "fpdfsdk/include/pdfwindow/PWL_Utils.h"
13 #include "fpdfsdk/include/pdfwindow/PWL_Wnd.h"
14 #include "public/fpdf_fwlevent.h"
15
16 #define IsFloatZero(f) ((f) < 0.0001 && (f) > -0.0001)
17 #define IsFloatBigger(fa, fb) ((fa) > (fb) && !IsFloatZero((fa) - (fb)))
18 #define IsFloatSmaller(fa, fb) ((fa) < (fb) && !IsFloatZero((fa) - (fb)))
19 #define IsFloatEqual(fa, fb) IsFloatZero((fa) - (fb))
20
CPWL_List_Notify(CPWL_ListBox * pList)21 CPWL_List_Notify::CPWL_List_Notify(CPWL_ListBox* pList) : m_pList(pList) {
22 ASSERT(m_pList);
23 }
24
~CPWL_List_Notify()25 CPWL_List_Notify::~CPWL_List_Notify() {}
26
IOnSetScrollInfoY(FX_FLOAT fPlateMin,FX_FLOAT fPlateMax,FX_FLOAT fContentMin,FX_FLOAT fContentMax,FX_FLOAT fSmallStep,FX_FLOAT fBigStep)27 void CPWL_List_Notify::IOnSetScrollInfoY(FX_FLOAT fPlateMin,
28 FX_FLOAT fPlateMax,
29 FX_FLOAT fContentMin,
30 FX_FLOAT fContentMax,
31 FX_FLOAT fSmallStep,
32 FX_FLOAT fBigStep) {
33 PWL_SCROLL_INFO Info;
34
35 Info.fPlateWidth = fPlateMax - fPlateMin;
36 Info.fContentMin = fContentMin;
37 Info.fContentMax = fContentMax;
38 Info.fSmallStep = fSmallStep;
39 Info.fBigStep = fBigStep;
40
41 m_pList->OnNotify(m_pList, PNM_SETSCROLLINFO, SBT_VSCROLL, (intptr_t)&Info);
42
43 if (CPWL_ScrollBar* pScroll = m_pList->GetVScrollBar()) {
44 if (IsFloatBigger(Info.fPlateWidth, Info.fContentMax - Info.fContentMin) ||
45 IsFloatEqual(Info.fPlateWidth, Info.fContentMax - Info.fContentMin)) {
46 if (pScroll->IsVisible()) {
47 pScroll->SetVisible(FALSE);
48 m_pList->RePosChildWnd();
49 }
50 } else {
51 if (!pScroll->IsVisible()) {
52 pScroll->SetVisible(TRUE);
53 m_pList->RePosChildWnd();
54 }
55 }
56 }
57 }
58
IOnSetScrollPosY(FX_FLOAT fy)59 void CPWL_List_Notify::IOnSetScrollPosY(FX_FLOAT fy) {
60 m_pList->OnNotify(m_pList, PNM_SETSCROLLPOS, SBT_VSCROLL, (intptr_t)&fy);
61 }
62
IOnInvalidateRect(CPDF_Rect * pRect)63 void CPWL_List_Notify::IOnInvalidateRect(CPDF_Rect* pRect) {
64 m_pList->InvalidateRect(pRect);
65 }
66
CPWL_ListBox()67 CPWL_ListBox::CPWL_ListBox()
68 : m_pList(NULL),
69 m_pListNotify(NULL),
70 m_bMouseDown(FALSE),
71 m_bHoverSel(FALSE),
72 m_pFillerNotify(NULL) {
73 m_pList = IFX_List::NewList();
74 }
75
~CPWL_ListBox()76 CPWL_ListBox::~CPWL_ListBox() {
77 IFX_List::DelList(m_pList);
78 delete m_pListNotify;
79 m_pListNotify = NULL;
80 }
81
GetClassName() const82 CFX_ByteString CPWL_ListBox::GetClassName() const {
83 return "CPWL_ListBox";
84 }
85
OnCreated()86 void CPWL_ListBox::OnCreated() {
87 if (m_pList) {
88 delete m_pListNotify;
89
90 m_pList->SetFontMap(GetFontMap());
91 m_pList->SetNotify(m_pListNotify = new CPWL_List_Notify(this));
92
93 SetHoverSel(HasFlag(PLBS_HOVERSEL));
94 m_pList->SetMultipleSel(HasFlag(PLBS_MULTIPLESEL));
95 m_pList->SetFontSize(GetCreationParam().fFontSize);
96
97 m_bHoverSel = HasFlag(PLBS_HOVERSEL);
98 }
99 }
100
OnDestroy()101 void CPWL_ListBox::OnDestroy() {
102 delete m_pListNotify;
103 m_pListNotify = NULL;
104 }
105
GetThisAppearanceStream(CFX_ByteTextBuf & sAppStream)106 void CPWL_ListBox::GetThisAppearanceStream(CFX_ByteTextBuf& sAppStream) {
107 CPWL_Wnd::GetThisAppearanceStream(sAppStream);
108
109 CFX_ByteTextBuf sListItems;
110
111 if (m_pList) {
112 CPDF_Rect rcPlate = m_pList->GetPlateRect();
113 for (int32_t i = 0, sz = m_pList->GetCount(); i < sz; i++) {
114 CPDF_Rect rcItem = m_pList->GetItemRect(i);
115
116 if (rcItem.bottom > rcPlate.top || rcItem.top < rcPlate.bottom)
117 continue;
118
119 CPDF_Point ptOffset(rcItem.left, (rcItem.top + rcItem.bottom) * 0.5f);
120 if (m_pList->IsItemSelected(i)) {
121 sListItems << CPWL_Utils::GetRectFillAppStream(
122 rcItem, PWL_DEFAULT_SELBACKCOLOR);
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(PWL_DEFAULT_SELTEXTCOLOR)
128 << sItem << "ET\n";
129 }
130 } else {
131 CFX_ByteString sItem =
132 CPWL_Utils::GetEditAppStream(m_pList->GetItemEdit(i), ptOffset);
133 if (sItem.GetLength() > 0) {
134 sListItems << "BT\n" << CPWL_Utils::GetColorAppStream(GetTextColor())
135 << sItem << "ET\n";
136 }
137 }
138 }
139 }
140
141 if (sListItems.GetLength() > 0) {
142 CFX_ByteTextBuf sClip;
143 CPDF_Rect rcClient = GetClientRect();
144
145 sClip << "q\n";
146 sClip << rcClient.left << " " << rcClient.bottom << " "
147 << rcClient.right - rcClient.left << " "
148 << rcClient.top - rcClient.bottom << " re W n\n";
149
150 sClip << sListItems << "Q\n";
151
152 sAppStream << "/Tx BMC\n" << sClip << "EMC\n";
153 }
154 }
155
DrawThisAppearance(CFX_RenderDevice * pDevice,CFX_Matrix * pUser2Device)156 void CPWL_ListBox::DrawThisAppearance(CFX_RenderDevice* pDevice,
157 CFX_Matrix* pUser2Device) {
158 CPWL_Wnd::DrawThisAppearance(pDevice, pUser2Device);
159
160 if (m_pList) {
161 CPDF_Rect rcPlate = m_pList->GetPlateRect();
162 CPDF_Rect rcList = GetListRect();
163 CPDF_Rect rcClient = GetClientRect();
164
165 for (int32_t i = 0, sz = m_pList->GetCount(); i < sz; i++) {
166 CPDF_Rect rcItem = m_pList->GetItemRect(i);
167 if (rcItem.bottom > rcPlate.top || rcItem.top < rcPlate.bottom)
168 continue;
169
170 CPDF_Point ptOffset(rcItem.left, (rcItem.top + rcItem.bottom) * 0.5f);
171 if (IFX_Edit* pEdit = m_pList->GetItemEdit(i)) {
172 CPDF_Rect rcContent = pEdit->GetContentRect();
173 if (rcContent.Width() > rcClient.Width())
174 rcItem.Intersect(rcList);
175 else
176 rcItem.Intersect(rcClient);
177 }
178
179 if (m_pList->IsItemSelected(i)) {
180 IFX_SystemHandler* pSysHandler = GetSystemHandler();
181 if (pSysHandler && pSysHandler->IsSelectionImplemented()) {
182 IFX_Edit::DrawEdit(
183 pDevice, pUser2Device, m_pList->GetItemEdit(i),
184 CPWL_Utils::PWLColorToFXColor(GetTextColor()),
185 CPWL_Utils::PWLColorToFXColor(GetTextStrokeColor()), rcList,
186 ptOffset, NULL, pSysHandler, m_pFormFiller);
187 pSysHandler->OutputSelectedRect(m_pFormFiller, rcItem);
188 } else {
189 CPWL_Utils::DrawFillRect(pDevice, pUser2Device, rcItem,
190 ArgbEncode(255, 0, 51, 113));
191 IFX_Edit::DrawEdit(pDevice, pUser2Device, m_pList->GetItemEdit(i),
192 ArgbEncode(255, 255, 255, 255), 0, rcList,
193 ptOffset, NULL, pSysHandler, m_pFormFiller);
194 }
195 } else {
196 IFX_SystemHandler* pSysHandler = GetSystemHandler();
197 IFX_Edit::DrawEdit(pDevice, pUser2Device, m_pList->GetItemEdit(i),
198 CPWL_Utils::PWLColorToFXColor(GetTextColor()),
199 CPWL_Utils::PWLColorToFXColor(GetTextStrokeColor()),
200 rcList, ptOffset, NULL, pSysHandler, NULL);
201 }
202 }
203 }
204 }
205
OnKeyDown(FX_WORD nChar,FX_DWORD nFlag)206 FX_BOOL CPWL_ListBox::OnKeyDown(FX_WORD nChar, FX_DWORD nFlag) {
207 CPWL_Wnd::OnKeyDown(nChar, nFlag);
208
209 if (!m_pList)
210 return FALSE;
211
212 switch (nChar) {
213 default:
214 return FALSE;
215 case FWL_VKEY_Up:
216 case FWL_VKEY_Down:
217 case FWL_VKEY_Home:
218 case FWL_VKEY_Left:
219 case FWL_VKEY_End:
220 case FWL_VKEY_Right:
221 break;
222 }
223
224 switch (nChar) {
225 case FWL_VKEY_Up:
226 m_pList->OnVK_UP(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
227 break;
228 case FWL_VKEY_Down:
229 m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
230 break;
231 case FWL_VKEY_Home:
232 m_pList->OnVK_HOME(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
233 break;
234 case FWL_VKEY_Left:
235 m_pList->OnVK_LEFT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
236 break;
237 case FWL_VKEY_End:
238 m_pList->OnVK_END(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
239 break;
240 case FWL_VKEY_Right:
241 m_pList->OnVK_RIGHT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
242 break;
243 case FWL_VKEY_Delete:
244 break;
245 }
246
247 FX_BOOL bExit = FALSE;
248 OnNotifySelChanged(TRUE, bExit, nFlag);
249
250 return TRUE;
251 }
252
OnChar(FX_WORD nChar,FX_DWORD nFlag)253 FX_BOOL CPWL_ListBox::OnChar(FX_WORD nChar, FX_DWORD nFlag) {
254 CPWL_Wnd::OnChar(nChar, nFlag);
255
256 if (!m_pList)
257 return FALSE;
258
259 if (!m_pList->OnChar(nChar, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag)))
260 return FALSE;
261
262 FX_BOOL bExit = FALSE;
263 OnNotifySelChanged(TRUE, bExit, nFlag);
264
265 return TRUE;
266 }
267
OnLButtonDown(const CPDF_Point & point,FX_DWORD nFlag)268 FX_BOOL CPWL_ListBox::OnLButtonDown(const CPDF_Point& point, FX_DWORD nFlag) {
269 CPWL_Wnd::OnLButtonDown(point, nFlag);
270
271 if (ClientHitTest(point)) {
272 m_bMouseDown = TRUE;
273 SetFocus();
274 SetCapture();
275
276 if (m_pList)
277 m_pList->OnMouseDown(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
278 }
279
280 return TRUE;
281 }
282
OnLButtonUp(const CPDF_Point & point,FX_DWORD nFlag)283 FX_BOOL CPWL_ListBox::OnLButtonUp(const CPDF_Point& point, FX_DWORD nFlag) {
284 CPWL_Wnd::OnLButtonUp(point, nFlag);
285
286 if (m_bMouseDown) {
287 ReleaseCapture();
288 m_bMouseDown = FALSE;
289 }
290
291 FX_BOOL bExit = FALSE;
292 OnNotifySelChanged(FALSE, bExit, nFlag);
293
294 return TRUE;
295 }
296
SetHoverSel(FX_BOOL bHoverSel)297 void CPWL_ListBox::SetHoverSel(FX_BOOL bHoverSel) {
298 m_bHoverSel = bHoverSel;
299 }
300
OnMouseMove(const CPDF_Point & point,FX_DWORD nFlag)301 FX_BOOL CPWL_ListBox::OnMouseMove(const CPDF_Point& point, FX_DWORD nFlag) {
302 CPWL_Wnd::OnMouseMove(point, nFlag);
303
304 if (m_bHoverSel && !IsCaptureMouse() && ClientHitTest(point)) {
305 if (m_pList)
306 m_pList->Select(m_pList->GetItemIndex(point));
307 }
308
309 if (m_bMouseDown) {
310 if (m_pList)
311 m_pList->OnMouseMove(point, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
312 }
313
314 return TRUE;
315 }
316
OnNotify(CPWL_Wnd * pWnd,FX_DWORD msg,intptr_t wParam,intptr_t lParam)317 void CPWL_ListBox::OnNotify(CPWL_Wnd* pWnd,
318 FX_DWORD msg,
319 intptr_t wParam,
320 intptr_t lParam) {
321 CPWL_Wnd::OnNotify(pWnd, msg, wParam, lParam);
322
323 FX_FLOAT fPos;
324
325 switch (msg) {
326 case PNM_SETSCROLLINFO:
327 switch (wParam) {
328 case SBT_VSCROLL:
329 if (CPWL_Wnd* pChild = GetVScrollBar()) {
330 pChild->OnNotify(pWnd, PNM_SETSCROLLINFO, wParam, lParam);
331 }
332 break;
333 }
334 break;
335 case PNM_SETSCROLLPOS:
336 switch (wParam) {
337 case SBT_VSCROLL:
338 if (CPWL_Wnd* pChild = GetVScrollBar()) {
339 pChild->OnNotify(pWnd, PNM_SETSCROLLPOS, wParam, lParam);
340 }
341 break;
342 }
343 break;
344 case PNM_SCROLLWINDOW:
345 fPos = *(FX_FLOAT*)lParam;
346 switch (wParam) {
347 case SBT_VSCROLL:
348 if (m_pList)
349 m_pList->SetScrollPos(CPDF_Point(0, fPos));
350 break;
351 }
352 break;
353 }
354 }
355
KillFocus()356 void CPWL_ListBox::KillFocus() {
357 CPWL_Wnd::KillFocus();
358 }
359
RePosChildWnd()360 void CPWL_ListBox::RePosChildWnd() {
361 CPWL_Wnd::RePosChildWnd();
362
363 if (m_pList)
364 m_pList->SetPlateRect(GetListRect());
365 }
366
OnNotifySelChanged(FX_BOOL bKeyDown,FX_BOOL & bExit,FX_DWORD nFlag)367 void CPWL_ListBox::OnNotifySelChanged(FX_BOOL bKeyDown,
368 FX_BOOL& bExit,
369 FX_DWORD nFlag) {
370 if (!m_pFillerNotify)
371 return;
372
373 FX_BOOL bRC = TRUE;
374 CFX_WideString swChange = GetText();
375 CFX_WideString strChangeEx;
376 int nSelStart = 0;
377 int nSelEnd = swChange.GetLength();
378 m_pFillerNotify->OnBeforeKeyStroke(GetAttachedData(), swChange, strChangeEx,
379 nSelStart, nSelEnd, bKeyDown, bRC, bExit,
380 nFlag);
381 }
382
GetFocusRect() const383 CPDF_Rect CPWL_ListBox::GetFocusRect() const {
384 if (m_pList && m_pList->IsMultipleSel()) {
385 CPDF_Rect rcCaret = m_pList->GetItemRect(m_pList->GetCaret());
386 rcCaret.Intersect(GetClientRect());
387 return rcCaret;
388 }
389
390 return CPWL_Wnd::GetFocusRect();
391 }
392
AddString(const FX_WCHAR * string)393 void CPWL_ListBox::AddString(const FX_WCHAR* string) {
394 if (m_pList) {
395 m_pList->AddString(string);
396 }
397 }
398
GetText() const399 CFX_WideString CPWL_ListBox::GetText() const {
400 if (m_pList)
401 return m_pList->GetText();
402
403 return L"";
404 }
405
SetFontSize(FX_FLOAT fFontSize)406 void CPWL_ListBox::SetFontSize(FX_FLOAT fFontSize) {
407 if (m_pList)
408 m_pList->SetFontSize(fFontSize);
409 }
410
GetFontSize() const411 FX_FLOAT CPWL_ListBox::GetFontSize() const {
412 if (m_pList)
413 return m_pList->GetFontSize();
414 return 0.0f;
415 }
416
Select(int32_t nItemIndex)417 void CPWL_ListBox::Select(int32_t nItemIndex) {
418 if (m_pList)
419 m_pList->Select(nItemIndex);
420 }
421
SetCaret(int32_t nItemIndex)422 void CPWL_ListBox::SetCaret(int32_t nItemIndex) {
423 if (m_pList)
424 m_pList->SetCaret(nItemIndex);
425 }
426
SetTopVisibleIndex(int32_t nItemIndex)427 void CPWL_ListBox::SetTopVisibleIndex(int32_t nItemIndex) {
428 if (m_pList)
429 m_pList->SetTopItem(nItemIndex);
430 }
431
ScrollToListItem(int32_t nItemIndex)432 void CPWL_ListBox::ScrollToListItem(int32_t nItemIndex) {
433 if (m_pList)
434 m_pList->ScrollToListItem(nItemIndex);
435 }
436
ResetContent()437 void CPWL_ListBox::ResetContent() {
438 if (m_pList)
439 m_pList->Empty();
440 }
441
Reset()442 void CPWL_ListBox::Reset() {
443 if (m_pList)
444 m_pList->Cancel();
445 }
446
IsMultipleSel() const447 FX_BOOL CPWL_ListBox::IsMultipleSel() const {
448 if (m_pList)
449 return m_pList->IsMultipleSel();
450
451 return FALSE;
452 }
453
GetCaretIndex() const454 int32_t CPWL_ListBox::GetCaretIndex() const {
455 if (m_pList)
456 return m_pList->GetCaret();
457
458 return -1;
459 }
460
GetCurSel() const461 int32_t CPWL_ListBox::GetCurSel() const {
462 if (m_pList)
463 return m_pList->GetSelect();
464
465 return -1;
466 }
467
IsItemSelected(int32_t nItemIndex) const468 FX_BOOL CPWL_ListBox::IsItemSelected(int32_t nItemIndex) const {
469 if (m_pList)
470 return m_pList->IsItemSelected(nItemIndex);
471
472 return FALSE;
473 }
474
GetTopVisibleIndex() const475 int32_t CPWL_ListBox::GetTopVisibleIndex() const {
476 if (m_pList) {
477 m_pList->ScrollToListItem(m_pList->GetFirstSelected());
478 return m_pList->GetTopItem();
479 }
480
481 return -1;
482 }
483
GetCount() const484 int32_t CPWL_ListBox::GetCount() const {
485 if (m_pList)
486 return m_pList->GetCount();
487
488 return 0;
489 }
490
FindNext(int32_t nIndex,FX_WCHAR nChar) const491 int32_t CPWL_ListBox::FindNext(int32_t nIndex, FX_WCHAR nChar) const {
492 if (m_pList)
493 return m_pList->FindNext(nIndex, nChar);
494
495 return nIndex;
496 }
497
GetContentRect() const498 CPDF_Rect CPWL_ListBox::GetContentRect() const {
499 if (m_pList)
500 return m_pList->GetContentRect();
501
502 return CPDF_Rect();
503 }
504
GetFirstHeight() const505 FX_FLOAT CPWL_ListBox::GetFirstHeight() const {
506 if (m_pList)
507 return m_pList->GetFirstHeight();
508
509 return 0.0f;
510 }
511
GetListRect() const512 CPDF_Rect CPWL_ListBox::GetListRect() const {
513 return CPWL_Utils::DeflateRect(
514 GetWindowRect(), (FX_FLOAT)(GetBorderWidth() + GetInnerBorderWidth()));
515 }
516
OnMouseWheel(short zDelta,const CPDF_Point & point,FX_DWORD nFlag)517 FX_BOOL CPWL_ListBox::OnMouseWheel(short zDelta,
518 const CPDF_Point& point,
519 FX_DWORD nFlag) {
520 if (!m_pList)
521 return FALSE;
522
523 if (zDelta < 0) {
524 m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
525 } else {
526 m_pList->OnVK_UP(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
527 }
528
529 FX_BOOL bExit = FALSE;
530 OnNotifySelChanged(FALSE, bExit, nFlag);
531 return TRUE;
532 }
533