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