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