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