// Copyright 2017 PDFium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
#include "xfa/fxfa/cxfa_fflistbox.h"
#include <algorithm>
#include <utility>
#include <vector>
#include "xfa/fwl/cfwl_listbox.h"
#include "xfa/fwl/cfwl_notedriver.h"
#include "xfa/fwl/cfwl_widget.h"
#include "xfa/fxfa/cxfa_eventparam.h"
#include "xfa/fxfa/parser/cxfa_para.h"
namespace {
CFWL_ListBox* ToListBox(CFWL_Widget* widget) {
return static_cast<CFWL_ListBox*>(widget);
}
} // namespace
CXFA_FFListBox::CXFA_FFListBox(CXFA_Node* pNode)
: CXFA_FFField(pNode), m_pOldDelegate(nullptr) {}
CXFA_FFListBox::~CXFA_FFListBox() {
if (!m_pNormalWidget)
return;
CFWL_NoteDriver* pNoteDriver =
m_pNormalWidget->GetOwnerApp()->GetNoteDriver();
pNoteDriver->UnregisterEventTarget(m_pNormalWidget.get());
}
bool CXFA_FFListBox::LoadWidget() {
auto pNew = pdfium::MakeUnique<CFWL_ListBox>(
GetFWLApp(), pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr);
CFWL_ListBox* pListBox = pNew.get();
pListBox->ModifyStyles(FWL_WGTSTYLE_VScroll | FWL_WGTSTYLE_NoBackground,
0xFFFFFFFF);
m_pNormalWidget = std::move(pNew);
m_pNormalWidget->SetLayoutItem(this);
CFWL_NoteDriver* pNoteDriver =
m_pNormalWidget->GetOwnerApp()->GetNoteDriver();
pNoteDriver->RegisterEventTarget(m_pNormalWidget.get(),
m_pNormalWidget.get());
m_pOldDelegate = m_pNormalWidget->GetDelegate();
m_pNormalWidget->SetDelegate(this);
m_pNormalWidget->LockUpdate();
for (const auto& label : m_pNode->GetWidgetAcc()->GetChoiceListItems(false))
pListBox->AddString(label.AsStringView());
uint32_t dwExtendedStyle = FWL_STYLEEXT_LTB_ShowScrollBarFocus;
if (m_pNode->GetWidgetAcc()->IsChoiceListMultiSelect())
dwExtendedStyle |= FWL_STYLEEXT_LTB_MultiSelection;
dwExtendedStyle |= GetAlignment();
m_pNormalWidget->ModifyStylesEx(dwExtendedStyle, 0xFFFFFFFF);
for (int32_t selected : m_pNode->GetWidgetAcc()->GetSelectedItems())
pListBox->SetSelItem(pListBox->GetItem(nullptr, selected), true);
m_pNormalWidget->UnlockUpdate();
return CXFA_FFField::LoadWidget();
}
bool CXFA_FFListBox::OnKillFocus(CXFA_FFWidget* pNewFocus) {
if (!ProcessCommittedData())
UpdateFWLData();
CXFA_FFField::OnKillFocus(pNewFocus);
return true;
}
bool CXFA_FFListBox::CommitData() {
auto* pListBox = ToListBox(m_pNormalWidget.get());
std::vector<int32_t> iSelArray;
int32_t iSels = pListBox->CountSelItems();
for (int32_t i = 0; i < iSels; ++i)
iSelArray.push_back(pListBox->GetSelIndex(i));
m_pNode->GetWidgetAcc()->SetSelectedItems(iSelArray, true, false, true);
return true;
}
bool CXFA_FFListBox::IsDataChanged() {
std::vector<int32_t> iSelArray = m_pNode->GetWidgetAcc()->GetSelectedItems();
int32_t iOldSels = pdfium::CollectionSize<int32_t>(iSelArray);
auto* pListBox = ToListBox(m_pNormalWidget.get());
int32_t iSels = pListBox->CountSelItems();
if (iOldSels != iSels)
return true;
for (int32_t i = 0; i < iSels; ++i) {
CFWL_ListItem* hlistItem = pListBox->GetItem(nullptr, iSelArray[i]);
if (!(hlistItem->GetStates() & FWL_ITEMSTATE_LTB_Selected))
return true;
}
return false;
}
uint32_t CXFA_FFListBox::GetAlignment() {
CXFA_Para* para = m_pNode->GetParaIfExists();
if (!para)
return 0;
uint32_t dwExtendedStyle = 0;
switch (para->GetHorizontalAlign()) {
case XFA_AttributeEnum::Center:
dwExtendedStyle |= FWL_STYLEEXT_LTB_CenterAlign;
break;
case XFA_AttributeEnum::Justify:
break;
case XFA_AttributeEnum::JustifyAll:
break;
case XFA_AttributeEnum::Radix:
break;
case XFA_AttributeEnum::Right:
dwExtendedStyle |= FWL_STYLEEXT_LTB_RightAlign;
break;
default:
dwExtendedStyle |= FWL_STYLEEXT_LTB_LeftAlign;
break;
}
return dwExtendedStyle;
}
bool CXFA_FFListBox::UpdateFWLData() {
if (!m_pNormalWidget)
return false;
auto* pListBox = ToListBox(m_pNormalWidget.get());
std::vector<int32_t> iSelArray = m_pNode->GetWidgetAcc()->GetSelectedItems();
std::vector<CFWL_ListItem*> selItemArray(iSelArray.size());
std::transform(iSelArray.begin(), iSelArray.end(), selItemArray.begin(),
[pListBox](int32_t val) { return pListBox->GetSelItem(val); });
pListBox->SetSelItem(pListBox->GetSelItem(-1), false);
for (CFWL_ListItem* pItem : selItemArray)
pListBox->SetSelItem(pItem, true);
m_pNormalWidget->Update();
return true;
}
void CXFA_FFListBox::OnSelectChanged(CFWL_Widget* pWidget) {
CXFA_EventParam eParam;
eParam.m_eType = XFA_EVENT_Change;
eParam.m_pTarget = m_pNode->GetWidgetAcc();
eParam.m_wsPrevText = m_pNode->GetWidgetAcc()->GetValue(XFA_VALUEPICTURE_Raw);
auto* pListBox = ToListBox(m_pNormalWidget.get());
int32_t iSels = pListBox->CountSelItems();
if (iSels > 0) {
CFWL_ListItem* item = pListBox->GetSelItem(0);
eParam.m_wsNewText = item ? item->GetText() : L"";
}
m_pNode->ProcessEvent(GetDocView(), XFA_AttributeEnum::Change, &eParam);
}
void CXFA_FFListBox::SetItemState(int32_t nIndex, bool bSelected) {
auto* pListBox = ToListBox(m_pNormalWidget.get());
pListBox->SetSelItem(pListBox->GetSelItem(nIndex), bSelected);
m_pNormalWidget->Update();
AddInvalidateRect();
}
void CXFA_FFListBox::InsertItem(const WideStringView& wsLabel, int32_t nIndex) {
WideString wsTemp(wsLabel);
ToListBox(m_pNormalWidget.get())->AddString(wsTemp.AsStringView());
m_pNormalWidget->Update();
AddInvalidateRect();
}
void CXFA_FFListBox::DeleteItem(int32_t nIndex) {
auto* pListBox = ToListBox(m_pNormalWidget.get());
if (nIndex < 0)
pListBox->DeleteAll();
else
pListBox->DeleteString(pListBox->GetItem(nullptr, nIndex));
pListBox->Update();
AddInvalidateRect();
}
void CXFA_FFListBox::OnProcessMessage(CFWL_Message* pMessage) {
m_pOldDelegate->OnProcessMessage(pMessage);
}
void CXFA_FFListBox::OnProcessEvent(CFWL_Event* pEvent) {
CXFA_FFField::OnProcessEvent(pEvent);
switch (pEvent->GetType()) {
case CFWL_Event::Type::SelectChanged:
OnSelectChanged(m_pNormalWidget.get());
break;
default:
break;
}
m_pOldDelegate->OnProcessEvent(pEvent);
}
void CXFA_FFListBox::OnDrawWidget(CXFA_Graphics* pGraphics,
const CFX_Matrix& matrix) {
m_pOldDelegate->OnDrawWidget(pGraphics, matrix);
}
FormFieldType CXFA_FFListBox::GetFormFieldType() {
return FormFieldType::kXFA_ListBox;
}