| // Copyright 2014 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/app/xfa_ffnotify.h" |
| |
| #include "fxjs/cfxjse_value.h" |
| #include "xfa/fxfa/app/cxfa_textlayout.h" |
| #include "xfa/fxfa/app/xfa_ffbarcode.h" |
| #include "xfa/fxfa/app/xfa_ffcheckbutton.h" |
| #include "xfa/fxfa/app/xfa_ffchoicelist.h" |
| #include "xfa/fxfa/app/xfa_ffdraw.h" |
| #include "xfa/fxfa/app/xfa_ffexclgroup.h" |
| #include "xfa/fxfa/app/xfa_fffield.h" |
| #include "xfa/fxfa/app/xfa_ffimage.h" |
| #include "xfa/fxfa/app/xfa_ffimageedit.h" |
| #include "xfa/fxfa/app/xfa_ffpath.h" |
| #include "xfa/fxfa/app/xfa_ffpushbutton.h" |
| #include "xfa/fxfa/app/xfa_ffsignature.h" |
| #include "xfa/fxfa/app/xfa_ffsubform.h" |
| #include "xfa/fxfa/app/xfa_fftext.h" |
| #include "xfa/fxfa/app/xfa_fftextedit.h" |
| #include "xfa/fxfa/app/xfa_ffwidgetacc.h" |
| #include "xfa/fxfa/app/xfa_fwladapter.h" |
| #include "xfa/fxfa/xfa_ffapp.h" |
| #include "xfa/fxfa/xfa_ffdoc.h" |
| #include "xfa/fxfa/xfa_ffdocview.h" |
| #include "xfa/fxfa/xfa_ffpageview.h" |
| #include "xfa/fxfa/xfa_ffwidget.h" |
| #include "xfa/fxfa/xfa_ffwidgethandler.h" |
| |
| static void XFA_FFDeleteWidgetAcc(void* pData) { |
| delete static_cast<CXFA_WidgetAcc*>(pData); |
| } |
| |
| static XFA_MAPDATABLOCKCALLBACKINFO gs_XFADeleteWidgetAcc = { |
| XFA_FFDeleteWidgetAcc, nullptr}; |
| |
| CXFA_FFNotify::CXFA_FFNotify(CXFA_FFDoc* pDoc) : m_pDoc(pDoc) {} |
| CXFA_FFNotify::~CXFA_FFNotify() {} |
| |
| void CXFA_FFNotify::OnPageEvent(CXFA_ContainerLayoutItem* pSender, |
| uint32_t dwEvent) { |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(pSender->GetLayout()); |
| if (pDocView) |
| pDocView->OnPageEvent(pSender, dwEvent); |
| } |
| |
| void CXFA_FFNotify::OnWidgetListItemAdded(CXFA_WidgetData* pSender, |
| const FX_WCHAR* pLabel, |
| const FX_WCHAR* pValue, |
| int32_t iIndex) { |
| CXFA_WidgetAcc* pWidgetAcc = static_cast<CXFA_WidgetAcc*>(pSender); |
| if (pWidgetAcc->GetUIType() != XFA_Element::ChoiceList) |
| return; |
| |
| CXFA_FFWidget* pWidget = nullptr; |
| while ((pWidget = pWidgetAcc->GetNextWidget(pWidget)) != nullptr) { |
| if (pWidget->IsLoaded()) { |
| if (pWidgetAcc->IsListBox()) { |
| static_cast<CXFA_FFListBox*>(pWidget)->InsertItem(pLabel, iIndex); |
| } else { |
| static_cast<CXFA_FFComboBox*>(pWidget)->InsertItem(pLabel, iIndex); |
| } |
| } |
| } |
| } |
| |
| void CXFA_FFNotify::OnWidgetListItemRemoved(CXFA_WidgetData* pSender, |
| int32_t iIndex) { |
| CXFA_WidgetAcc* pWidgetAcc = static_cast<CXFA_WidgetAcc*>(pSender); |
| if (pWidgetAcc->GetUIType() != XFA_Element::ChoiceList) |
| return; |
| |
| CXFA_FFWidget* pWidget = nullptr; |
| while ((pWidget = pWidgetAcc->GetNextWidget(pWidget)) != nullptr) { |
| if (pWidget->IsLoaded()) { |
| if (pWidgetAcc->IsListBox()) { |
| static_cast<CXFA_FFListBox*>(pWidget)->DeleteItem(iIndex); |
| } else { |
| static_cast<CXFA_FFComboBox*>(pWidget)->DeleteItem(iIndex); |
| } |
| } |
| } |
| } |
| |
| CXFA_LayoutItem* CXFA_FFNotify::OnCreateLayoutItem(CXFA_Node* pNode) { |
| CXFA_LayoutProcessor* pLayout = m_pDoc->GetXFADoc()->GetDocLayout(); |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(pLayout); |
| XFA_Element eType = pNode->GetElementType(); |
| if (eType == XFA_Element::PageArea) |
| return new CXFA_FFPageView(pDocView, pNode); |
| if (eType == XFA_Element::ContentArea) |
| return new CXFA_ContainerLayoutItem(pNode); |
| |
| CXFA_WidgetAcc* pAcc = static_cast<CXFA_WidgetAcc*>(pNode->GetWidgetData()); |
| if (!pAcc) |
| return new CXFA_ContentLayoutItem(pNode); |
| |
| CXFA_FFWidget* pWidget; |
| switch (pAcc->GetUIType()) { |
| case XFA_Element::Barcode: |
| pWidget = new CXFA_FFBarcode(nullptr, pAcc); |
| break; |
| case XFA_Element::Button: |
| pWidget = new CXFA_FFPushButton(nullptr, pAcc); |
| break; |
| case XFA_Element::CheckButton: |
| pWidget = new CXFA_FFCheckButton(nullptr, pAcc); |
| break; |
| case XFA_Element::ChoiceList: { |
| if (pAcc->IsListBox()) { |
| pWidget = new CXFA_FFListBox(nullptr, pAcc); |
| } else { |
| pWidget = new CXFA_FFComboBox(nullptr, pAcc); |
| } |
| } break; |
| case XFA_Element::DateTimeEdit: |
| pWidget = new CXFA_FFDateTimeEdit(nullptr, pAcc); |
| break; |
| case XFA_Element::ImageEdit: |
| pWidget = new CXFA_FFImageEdit(nullptr, pAcc); |
| break; |
| case XFA_Element::NumericEdit: |
| pWidget = new CXFA_FFNumericEdit(nullptr, pAcc); |
| break; |
| case XFA_Element::PasswordEdit: |
| pWidget = new CXFA_FFPasswordEdit(nullptr, pAcc); |
| break; |
| case XFA_Element::Signature: |
| pWidget = new CXFA_FFSignature(nullptr, pAcc); |
| break; |
| case XFA_Element::TextEdit: |
| pWidget = new CXFA_FFTextEdit(nullptr, pAcc); |
| break; |
| case XFA_Element::Arc: |
| pWidget = new CXFA_FFArc(nullptr, pAcc); |
| break; |
| case XFA_Element::Line: |
| pWidget = new CXFA_FFLine(nullptr, pAcc); |
| break; |
| case XFA_Element::Rectangle: |
| pWidget = new CXFA_FFRectangle(nullptr, pAcc); |
| break; |
| case XFA_Element::Text: |
| pWidget = new CXFA_FFText(nullptr, pAcc); |
| break; |
| case XFA_Element::Image: |
| pWidget = new CXFA_FFImage(nullptr, pAcc); |
| break; |
| case XFA_Element::Draw: |
| pWidget = new CXFA_FFDraw(nullptr, pAcc); |
| break; |
| case XFA_Element::Subform: |
| pWidget = new CXFA_FFSubForm(nullptr, pAcc); |
| break; |
| case XFA_Element::ExclGroup: |
| pWidget = new CXFA_FFExclGroup(nullptr, pAcc); |
| break; |
| case XFA_Element::DefaultUi: |
| default: |
| pWidget = nullptr; |
| break; |
| } |
| |
| if (pWidget) |
| pWidget->SetDocView(pDocView); |
| return pWidget; |
| } |
| |
| void CXFA_FFNotify::StartFieldDrawLayout(CXFA_Node* pItem, |
| FX_FLOAT& fCalcWidth, |
| FX_FLOAT& fCalcHeight) { |
| CXFA_WidgetAcc* pAcc = static_cast<CXFA_WidgetAcc*>(pItem->GetWidgetData()); |
| if (!pAcc) |
| return; |
| |
| pAcc->StartWidgetLayout(fCalcWidth, fCalcHeight); |
| } |
| |
| bool CXFA_FFNotify::FindSplitPos(CXFA_Node* pItem, |
| int32_t iBlockIndex, |
| FX_FLOAT& fCalcHeightPos) { |
| CXFA_WidgetAcc* pAcc = static_cast<CXFA_WidgetAcc*>(pItem->GetWidgetData()); |
| return pAcc && pAcc->FindSplitPos(iBlockIndex, fCalcHeightPos); |
| } |
| |
| bool CXFA_FFNotify::RunScript(CXFA_Node* pScript, CXFA_Node* pFormItem) { |
| bool bRet = false; |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(); |
| if (!pDocView) { |
| return bRet; |
| } |
| CXFA_WidgetAcc* pWidgetAcc = |
| static_cast<CXFA_WidgetAcc*>(pFormItem->GetWidgetData()); |
| if (!pWidgetAcc) { |
| return bRet; |
| } |
| CXFA_EventParam EventParam; |
| EventParam.m_eType = XFA_EVENT_Unknown; |
| CFXJSE_Value* pRetValue = nullptr; |
| int32_t iRet = |
| pWidgetAcc->ExecuteScript(CXFA_Script(pScript), &EventParam, &pRetValue); |
| if (iRet == XFA_EVENTERROR_Success && pRetValue) { |
| bRet = pRetValue->ToBoolean(); |
| delete pRetValue; |
| } |
| return bRet; |
| } |
| int32_t CXFA_FFNotify::ExecEventByDeepFirst(CXFA_Node* pFormNode, |
| XFA_EVENTTYPE eEventType, |
| bool bIsFormReady, |
| bool bRecursive, |
| CXFA_WidgetAcc* pExclude) { |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(); |
| if (!pDocView) { |
| return XFA_EVENTERROR_NotExist; |
| } |
| return pDocView->ExecEventActivityByDeepFirst( |
| pFormNode, eEventType, bIsFormReady, bRecursive, |
| pExclude ? pExclude->GetNode() : nullptr); |
| } |
| void CXFA_FFNotify::AddCalcValidate(CXFA_Node* pNode) { |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(); |
| if (!pDocView) { |
| return; |
| } |
| CXFA_WidgetAcc* pWidgetAcc = |
| static_cast<CXFA_WidgetAcc*>(pNode->GetWidgetData()); |
| if (!pWidgetAcc) { |
| return; |
| } |
| pDocView->AddCalculateWidgetAcc(pWidgetAcc); |
| pDocView->AddValidateWidget(pWidgetAcc); |
| } |
| CXFA_FFDoc* CXFA_FFNotify::GetHDOC() { |
| return m_pDoc; |
| } |
| IXFA_DocEnvironment* CXFA_FFNotify::GetDocEnvironment() const { |
| return m_pDoc->GetDocEnvironment(); |
| } |
| IXFA_AppProvider* CXFA_FFNotify::GetAppProvider() { |
| return m_pDoc->GetApp()->GetAppProvider(); |
| } |
| CXFA_FFWidgetHandler* CXFA_FFNotify::GetWidgetHandler() { |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(); |
| return pDocView ? pDocView->GetWidgetHandler() : nullptr; |
| } |
| CXFA_FFWidget* CXFA_FFNotify::GetHWidget(CXFA_LayoutItem* pLayoutItem) { |
| return XFA_GetWidgetFromLayoutItem(pLayoutItem); |
| } |
| void CXFA_FFNotify::OpenDropDownList(CXFA_FFWidget* hWidget) { |
| if (hWidget->GetDataAcc()->GetUIType() != XFA_Element::ChoiceList) { |
| return; |
| } |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(); |
| pDocView->LockUpdate(); |
| static_cast<CXFA_FFComboBox*>(hWidget)->OpenDropDownList(); |
| pDocView->UnlockUpdate(); |
| pDocView->UpdateDocView(); |
| } |
| CFX_WideString CXFA_FFNotify::GetCurrentDateTime() { |
| CFX_Unitime dataTime; |
| dataTime.Now(); |
| CFX_WideString wsDateTime; |
| wsDateTime.Format(L"%d%02d%02dT%02d%02d%02d", dataTime.GetYear(), |
| dataTime.GetMonth(), dataTime.GetDay(), dataTime.GetHour(), |
| dataTime.GetMinute(), dataTime.GetSecond()); |
| return wsDateTime; |
| } |
| void CXFA_FFNotify::ResetData(CXFA_WidgetData* pWidgetData) { |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(); |
| if (!pDocView) { |
| return; |
| } |
| pDocView->ResetWidgetData(static_cast<CXFA_WidgetAcc*>(pWidgetData)); |
| } |
| int32_t CXFA_FFNotify::GetLayoutStatus() { |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(); |
| return pDocView ? pDocView->GetLayoutStatus() : 0; |
| } |
| void CXFA_FFNotify::RunNodeInitialize(CXFA_Node* pNode) { |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(); |
| if (!pDocView) { |
| return; |
| } |
| pDocView->AddNewFormNode(pNode); |
| } |
| void CXFA_FFNotify::RunSubformIndexChange(CXFA_Node* pSubformNode) { |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(); |
| if (!pDocView) { |
| return; |
| } |
| pDocView->AddIndexChangedSubform(pSubformNode); |
| } |
| CXFA_Node* CXFA_FFNotify::GetFocusWidgetNode() { |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(); |
| if (!pDocView) { |
| return nullptr; |
| } |
| CXFA_WidgetAcc* pAcc = pDocView->GetFocusWidgetAcc(); |
| return pAcc ? pAcc->GetNode() : nullptr; |
| } |
| void CXFA_FFNotify::SetFocusWidgetNode(CXFA_Node* pNode) { |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(); |
| if (!pDocView) { |
| return; |
| } |
| CXFA_WidgetAcc* pAcc = |
| pNode ? static_cast<CXFA_WidgetAcc*>(pNode->GetWidgetData()) : nullptr; |
| pDocView->SetFocusWidgetAcc(pAcc); |
| } |
| |
| void CXFA_FFNotify::OnNodeReady(CXFA_Node* pNode) { |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(); |
| if (!pDocView) |
| return; |
| |
| XFA_Element eType = pNode->GetElementType(); |
| if (XFA_IsCreateWidget(eType)) { |
| CXFA_WidgetAcc* pAcc = new CXFA_WidgetAcc(pDocView, pNode); |
| pNode->SetObject(XFA_ATTRIBUTE_WidgetData, pAcc, &gs_XFADeleteWidgetAcc); |
| return; |
| } |
| switch (eType) { |
| case XFA_Element::BindItems: |
| pDocView->m_BindItems.push_back(pNode); |
| break; |
| case XFA_Element::Validate: { |
| pNode->SetFlag(XFA_NodeFlag_NeedsInitApp, false); |
| } break; |
| default: |
| break; |
| } |
| } |
| |
| void CXFA_FFNotify::OnValueChanging(CXFA_Node* pSender, XFA_ATTRIBUTE eAttr) { |
| if (eAttr != XFA_ATTRIBUTE_Presence) |
| return; |
| |
| if (pSender->GetPacketID() & XFA_XDPPACKET_Datasets) |
| return; |
| |
| if (!pSender->IsFormContainer()) |
| return; |
| |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(); |
| if (!pDocView) |
| return; |
| |
| if (pDocView->GetLayoutStatus() < XFA_DOCVIEW_LAYOUTSTATUS_End) |
| return; |
| |
| CXFA_WidgetAcc* pWidgetAcc = |
| static_cast<CXFA_WidgetAcc*>(pSender->GetWidgetData()); |
| if (!pWidgetAcc) |
| return; |
| |
| CXFA_FFWidget* pWidget = nullptr; |
| while ((pWidget = pWidgetAcc->GetNextWidget(pWidget)) != nullptr) { |
| if (pWidget->IsLoaded()) |
| pWidget->AddInvalidateRect(); |
| } |
| } |
| |
| void CXFA_FFNotify::OnValueChanged(CXFA_Node* pSender, |
| XFA_ATTRIBUTE eAttr, |
| CXFA_Node* pParentNode, |
| CXFA_Node* pWidgetNode) { |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(); |
| if (!pDocView) |
| return; |
| |
| if (!(pSender->GetPacketID() & XFA_XDPPACKET_Form)) { |
| if (eAttr == XFA_ATTRIBUTE_Value) |
| pDocView->AddCalculateNodeNotify(pSender); |
| return; |
| } |
| |
| XFA_Element eType = pParentNode->GetElementType(); |
| bool bIsContainerNode = pParentNode->IsContainerNode(); |
| CXFA_WidgetAcc* pWidgetAcc = |
| static_cast<CXFA_WidgetAcc*>(pWidgetNode->GetWidgetData()); |
| if (!pWidgetAcc) |
| return; |
| |
| bool bUpdateProperty = false; |
| pDocView->SetChangeMark(); |
| switch (eType) { |
| case XFA_Element::Caption: { |
| CXFA_TextLayout* pCapOut = pWidgetAcc->GetCaptionTextLayout(); |
| if (!pCapOut) |
| return; |
| |
| pCapOut->Unload(); |
| } break; |
| case XFA_Element::Ui: |
| case XFA_Element::Para: |
| bUpdateProperty = true; |
| break; |
| default: |
| break; |
| } |
| if (bIsContainerNode && eAttr == XFA_ATTRIBUTE_Access) |
| bUpdateProperty = true; |
| |
| if (eAttr == XFA_ATTRIBUTE_Value) { |
| pDocView->AddCalculateNodeNotify(pSender); |
| if (eType == XFA_Element::Value || bIsContainerNode) { |
| if (bIsContainerNode) { |
| pWidgetAcc->UpdateUIDisplay(); |
| pDocView->AddCalculateWidgetAcc(pWidgetAcc); |
| pDocView->AddValidateWidget(pWidgetAcc); |
| } else if (pWidgetNode->GetNodeItem(XFA_NODEITEM_Parent) |
| ->GetElementType() == XFA_Element::ExclGroup) { |
| pWidgetAcc->UpdateUIDisplay(); |
| } |
| return; |
| } |
| } |
| CXFA_FFWidget* pWidget = nullptr; |
| while ((pWidget = pWidgetAcc->GetNextWidget(pWidget)) != nullptr) { |
| if (!pWidget->IsLoaded()) |
| continue; |
| |
| if (bUpdateProperty) |
| pWidget->UpdateWidgetProperty(); |
| pWidget->PerformLayout(); |
| pWidget->AddInvalidateRect(); |
| } |
| } |
| |
| void CXFA_FFNotify::OnChildAdded(CXFA_Node* pSender) { |
| if (!pSender->IsFormContainer()) { |
| return; |
| } |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(); |
| if (!pDocView) { |
| return; |
| } |
| bool bLayoutReady = |
| !(pDocView->m_bInLayoutStatus) && |
| (pDocView->GetLayoutStatus() == XFA_DOCVIEW_LAYOUTSTATUS_End); |
| if (bLayoutReady) |
| m_pDoc->GetDocEnvironment()->SetChangeMark(m_pDoc); |
| } |
| |
| void CXFA_FFNotify::OnChildRemoved() { |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(); |
| if (!pDocView) |
| return; |
| |
| bool bLayoutReady = |
| !(pDocView->m_bInLayoutStatus) && |
| (pDocView->GetLayoutStatus() == XFA_DOCVIEW_LAYOUTSTATUS_End); |
| if (bLayoutReady) |
| m_pDoc->GetDocEnvironment()->SetChangeMark(m_pDoc); |
| } |
| |
| void CXFA_FFNotify::OnLayoutItemAdded(CXFA_LayoutProcessor* pLayout, |
| CXFA_LayoutItem* pSender, |
| int32_t iPageIdx, |
| uint32_t dwStatus) { |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(pLayout); |
| if (!pDocView) |
| return; |
| |
| CXFA_FFWidget* pWidget = XFA_GetWidgetFromLayoutItem(pSender); |
| if (!pWidget) |
| return; |
| |
| CXFA_FFPageView* pNewPageView = pDocView->GetPageView(iPageIdx); |
| uint32_t dwFilter = XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable | |
| XFA_WidgetStatus_Printable; |
| pWidget->ModifyStatus(dwStatus, dwFilter); |
| CXFA_FFPageView* pPrePageView = pWidget->GetPageView(); |
| if (pPrePageView != pNewPageView || |
| (dwStatus & (XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable)) == |
| (XFA_WidgetStatus_Visible | XFA_WidgetStatus_Viewable)) { |
| pWidget->SetPageView(pNewPageView); |
| m_pDoc->GetDocEnvironment()->WidgetPostAdd(pWidget, pWidget->GetDataAcc()); |
| } |
| if (pDocView->GetLayoutStatus() != XFA_DOCVIEW_LAYOUTSTATUS_End || |
| !(dwStatus & XFA_WidgetStatus_Visible)) { |
| return; |
| } |
| if (pWidget->IsLoaded()) { |
| CFX_RectF rtOld; |
| pWidget->GetWidgetRect(rtOld); |
| if (rtOld != pWidget->ReCacheWidgetRect()) |
| pWidget->PerformLayout(); |
| } else { |
| pWidget->LoadWidget(); |
| } |
| pWidget->AddInvalidateRect(nullptr); |
| } |
| |
| void CXFA_FFNotify::OnLayoutItemRemoving(CXFA_LayoutProcessor* pLayout, |
| CXFA_LayoutItem* pSender) { |
| CXFA_FFDocView* pDocView = m_pDoc->GetDocView(pLayout); |
| if (!pDocView) |
| return; |
| |
| CXFA_FFWidget* pWidget = XFA_GetWidgetFromLayoutItem(pSender); |
| if (!pWidget) |
| return; |
| |
| pDocView->DeleteLayoutItem(pWidget); |
| m_pDoc->GetDocEnvironment()->WidgetPreRemove(pWidget, pWidget->GetDataAcc()); |
| pWidget->AddInvalidateRect(nullptr); |
| } |