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