1 // Copyright 2017 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 "xfa/fxfa/cxfa_fflistbox.h"
8 
9 #include <algorithm>
10 #include <utility>
11 #include <vector>
12 
13 #include "third_party/base/ptr_util.h"
14 #include "third_party/base/stl_util.h"
15 #include "xfa/fwl/cfwl_listbox.h"
16 #include "xfa/fwl/cfwl_notedriver.h"
17 #include "xfa/fwl/cfwl_widget.h"
18 #include "xfa/fxfa/cxfa_eventparam.h"
19 #include "xfa/fxfa/parser/cxfa_para.h"
20 
21 namespace {
22 
ToListBox(CFWL_Widget * widget)23 CFWL_ListBox* ToListBox(CFWL_Widget* widget) {
24   return static_cast<CFWL_ListBox*>(widget);
25 }
26 
27 }  // namespace
28 
CXFA_FFListBox(CXFA_Node * pNode)29 CXFA_FFListBox::CXFA_FFListBox(CXFA_Node* pNode) : CXFA_FFDropDown(pNode) {}
30 
~CXFA_FFListBox()31 CXFA_FFListBox::~CXFA_FFListBox() {
32   if (!GetNormalWidget())
33     return;
34 
35   CFWL_NoteDriver* pNoteDriver =
36       GetNormalWidget()->GetOwnerApp()->GetNoteDriver();
37   pNoteDriver->UnregisterEventTarget(GetNormalWidget());
38 }
39 
LoadWidget()40 bool CXFA_FFListBox::LoadWidget() {
41   ASSERT(!IsLoaded());
42   auto pNew = pdfium::MakeUnique<CFWL_ListBox>(
43       GetFWLApp(), pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr);
44   CFWL_ListBox* pListBox = pNew.get();
45   pListBox->ModifyStyles(FWL_WGTSTYLE_VScroll | FWL_WGTSTYLE_NoBackground,
46                          0xFFFFFFFF);
47   SetNormalWidget(std::move(pNew));
48   pListBox->SetAdapterIface(this);
49 
50   CFWL_NoteDriver* pNoteDriver = pListBox->GetOwnerApp()->GetNoteDriver();
51   pNoteDriver->RegisterEventTarget(pListBox, pListBox);
52   m_pOldDelegate = pListBox->GetDelegate();
53   pListBox->SetDelegate(this);
54 
55   {
56     CFWL_Widget::ScopedUpdateLock update_lock(pListBox);
57     for (const auto& label : m_pNode->GetChoiceListItems(false))
58       pListBox->AddString(label);
59 
60     uint32_t dwExtendedStyle = FWL_STYLEEXT_LTB_ShowScrollBarFocus;
61     if (m_pNode->IsChoiceListMultiSelect())
62       dwExtendedStyle |= FWL_STYLEEXT_LTB_MultiSelection;
63 
64     dwExtendedStyle |= GetAlignment();
65     pListBox->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
66     for (int32_t selected : m_pNode->GetSelectedItems())
67       pListBox->SetSelItem(pListBox->GetItem(nullptr, selected), true);
68   }
69 
70   return CXFA_FFField::LoadWidget();
71 }
72 
OnKillFocus(CXFA_FFWidget * pNewFocus)73 bool CXFA_FFListBox::OnKillFocus(CXFA_FFWidget* pNewFocus) {
74   ObservedPtr<CXFA_FFListBox> pWatched(this);
75   ObservedPtr<CXFA_FFWidget> pNewWatched(pNewFocus);
76   if (!ProcessCommittedData())
77     UpdateFWLData();
78 
79   return pWatched && pNewWatched &&
80          CXFA_FFField::OnKillFocus(pNewWatched.Get());
81 }
82 
CommitData()83 bool CXFA_FFListBox::CommitData() {
84   auto* pListBox = ToListBox(GetNormalWidget());
85   std::vector<int32_t> iSelArray;
86   int32_t iSels = pListBox->CountSelItems();
87   for (int32_t i = 0; i < iSels; ++i)
88     iSelArray.push_back(pListBox->GetSelIndex(i));
89 
90   m_pNode->SetSelectedItems(iSelArray, true, false, true);
91   return true;
92 }
93 
IsDataChanged()94 bool CXFA_FFListBox::IsDataChanged() {
95   std::vector<int32_t> iSelArray = m_pNode->GetSelectedItems();
96   int32_t iOldSels = pdfium::CollectionSize<int32_t>(iSelArray);
97   auto* pListBox = ToListBox(GetNormalWidget());
98   int32_t iSels = pListBox->CountSelItems();
99   if (iOldSels != iSels)
100     return true;
101 
102   for (int32_t i = 0; i < iSels; ++i) {
103     CFWL_ListItem* hlistItem = pListBox->GetItem(nullptr, iSelArray[i]);
104     if (!(hlistItem->GetStates() & FWL_ITEMSTATE_LTB_Selected))
105       return true;
106   }
107   return false;
108 }
109 
GetAlignment()110 uint32_t CXFA_FFListBox::GetAlignment() {
111   CXFA_Para* para = m_pNode->GetParaIfExists();
112   if (!para)
113     return 0;
114 
115   uint32_t dwExtendedStyle = 0;
116   switch (para->GetHorizontalAlign()) {
117     case XFA_AttributeValue::Center:
118       dwExtendedStyle |= FWL_STYLEEXT_LTB_CenterAlign;
119       break;
120     case XFA_AttributeValue::Justify:
121       break;
122     case XFA_AttributeValue::JustifyAll:
123       break;
124     case XFA_AttributeValue::Radix:
125       break;
126     case XFA_AttributeValue::Right:
127       dwExtendedStyle |= FWL_STYLEEXT_LTB_RightAlign;
128       break;
129     default:
130       dwExtendedStyle |= FWL_STYLEEXT_LTB_LeftAlign;
131       break;
132   }
133   return dwExtendedStyle;
134 }
135 
UpdateFWLData()136 bool CXFA_FFListBox::UpdateFWLData() {
137   if (!GetNormalWidget())
138     return false;
139 
140   auto* pListBox = ToListBox(GetNormalWidget());
141   std::vector<int32_t> iSelArray = m_pNode->GetSelectedItems();
142   std::vector<CFWL_ListItem*> selItemArray(iSelArray.size());
143   std::transform(iSelArray.begin(), iSelArray.end(), selItemArray.begin(),
144                  [pListBox](int32_t val) { return pListBox->GetSelItem(val); });
145 
146   pListBox->SetSelItem(pListBox->GetSelItem(-1), false);
147   for (CFWL_ListItem* pItem : selItemArray)
148     pListBox->SetSelItem(pItem, true);
149 
150   GetNormalWidget()->Update();
151   return true;
152 }
153 
OnSelectChanged(CFWL_Widget * pWidget)154 void CXFA_FFListBox::OnSelectChanged(CFWL_Widget* pWidget) {
155   CXFA_EventParam eParam;
156   eParam.m_eType = XFA_EVENT_Change;
157   eParam.m_pTarget = m_pNode.Get();
158   eParam.m_wsPrevText = m_pNode->GetValue(XFA_VALUEPICTURE_Raw);
159   m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change, &eParam);
160 }
161 
SetItemState(int32_t nIndex,bool bSelected)162 void CXFA_FFListBox::SetItemState(int32_t nIndex, bool bSelected) {
163   auto* pListBox = ToListBox(GetNormalWidget());
164   pListBox->SetSelItem(pListBox->GetSelItem(nIndex), bSelected);
165   GetNormalWidget()->Update();
166   InvalidateRect();
167 }
168 
InsertItem(const WideString & wsLabel,int32_t nIndex)169 void CXFA_FFListBox::InsertItem(const WideString& wsLabel, int32_t nIndex) {
170   ToListBox(GetNormalWidget())->AddString(wsLabel);
171   GetNormalWidget()->Update();
172   InvalidateRect();
173 }
174 
DeleteItem(int32_t nIndex)175 void CXFA_FFListBox::DeleteItem(int32_t nIndex) {
176   auto* pListBox = ToListBox(GetNormalWidget());
177   if (nIndex < 0)
178     pListBox->DeleteAll();
179   else
180     pListBox->DeleteString(pListBox->GetItem(nullptr, nIndex));
181 
182   pListBox->Update();
183   InvalidateRect();
184 }
185 
OnProcessMessage(CFWL_Message * pMessage)186 void CXFA_FFListBox::OnProcessMessage(CFWL_Message* pMessage) {
187   m_pOldDelegate->OnProcessMessage(pMessage);
188 }
189 
OnProcessEvent(CFWL_Event * pEvent)190 void CXFA_FFListBox::OnProcessEvent(CFWL_Event* pEvent) {
191   CXFA_FFField::OnProcessEvent(pEvent);
192   switch (pEvent->GetType()) {
193     case CFWL_Event::Type::SelectChanged:
194       OnSelectChanged(GetNormalWidget());
195       break;
196     default:
197       break;
198   }
199   m_pOldDelegate->OnProcessEvent(pEvent);
200 }
201 
OnDrawWidget(CXFA_Graphics * pGraphics,const CFX_Matrix & matrix)202 void CXFA_FFListBox::OnDrawWidget(CXFA_Graphics* pGraphics,
203                                   const CFX_Matrix& matrix) {
204   m_pOldDelegate->OnDrawWidget(pGraphics, matrix);
205 }
206 
GetFormFieldType()207 FormFieldType CXFA_FFListBox::GetFormFieldType() {
208   return FormFieldType::kXFA_ListBox;
209 }
210