// 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/cxfa_ffwidgethandler.h"
#include <vector>
#include "fxjs/xfa/cjx_object.h"
#include "xfa/fxfa/cxfa_ffdoc.h"
#include "xfa/fxfa/cxfa_ffdocview.h"
#include "xfa/fxfa/cxfa_fffield.h"
#include "xfa/fxfa/cxfa_ffwidget.h"
#include "xfa/fxfa/cxfa_fwladapterwidgetmgr.h"
#include "xfa/fxfa/parser/cxfa_calculate.h"
#include "xfa/fxfa/parser/cxfa_checkbutton.h"
#include "xfa/fxfa/parser/cxfa_layoutprocessor.h"
#include "xfa/fxfa/parser/cxfa_measurement.h"
#include "xfa/fxfa/parser/cxfa_node.h"
#include "xfa/fxfa/parser/cxfa_ui.h"
#include "xfa/fxfa/parser/cxfa_validate.h"
CXFA_FFWidgetHandler::CXFA_FFWidgetHandler(CXFA_FFDocView* pDocView)
: m_pDocView(pDocView) {}
CXFA_FFWidgetHandler::~CXFA_FFWidgetHandler() {}
bool CXFA_FFWidgetHandler::OnMouseEnter(CXFA_FFWidget* hWidget) {
m_pDocView->LockUpdate();
bool bRet = hWidget->OnMouseEnter();
m_pDocView->UnlockUpdate();
m_pDocView->UpdateDocView();
return bRet;
}
bool CXFA_FFWidgetHandler::OnMouseExit(CXFA_FFWidget* hWidget) {
m_pDocView->LockUpdate();
bool bRet = hWidget->OnMouseExit();
m_pDocView->UnlockUpdate();
m_pDocView->UpdateDocView();
return bRet;
}
bool CXFA_FFWidgetHandler::OnLButtonDown(CXFA_FFWidget* hWidget,
uint32_t dwFlags,
const CFX_PointF& point) {
m_pDocView->LockUpdate();
bool bRet = hWidget->OnLButtonDown(dwFlags, hWidget->Rotate2Normal(point));
if (bRet && m_pDocView->SetFocus(hWidget)) {
m_pDocView->GetDoc()->GetDocEnvironment()->SetFocusWidget(
m_pDocView->GetDoc(), hWidget);
}
m_pDocView->UnlockUpdate();
m_pDocView->UpdateDocView();
return bRet;
}
bool CXFA_FFWidgetHandler::OnLButtonUp(CXFA_FFWidget* hWidget,
uint32_t dwFlags,
const CFX_PointF& point) {
m_pDocView->LockUpdate();
m_pDocView->m_bLayoutEvent = true;
bool bRet = hWidget->OnLButtonUp(dwFlags, hWidget->Rotate2Normal(point));
m_pDocView->UnlockUpdate();
m_pDocView->UpdateDocView();
return bRet;
}
bool CXFA_FFWidgetHandler::OnLButtonDblClk(CXFA_FFWidget* hWidget,
uint32_t dwFlags,
const CFX_PointF& point) {
bool bRet = hWidget->OnLButtonDblClk(dwFlags, hWidget->Rotate2Normal(point));
m_pDocView->RunInvalidate();
return bRet;
}
bool CXFA_FFWidgetHandler::OnMouseMove(CXFA_FFWidget* hWidget,
uint32_t dwFlags,
const CFX_PointF& point) {
bool bRet = hWidget->OnMouseMove(dwFlags, hWidget->Rotate2Normal(point));
m_pDocView->RunInvalidate();
return bRet;
}
bool CXFA_FFWidgetHandler::OnMouseWheel(CXFA_FFWidget* hWidget,
uint32_t dwFlags,
int16_t zDelta,
const CFX_PointF& point) {
bool bRet =
hWidget->OnMouseWheel(dwFlags, zDelta, hWidget->Rotate2Normal(point));
m_pDocView->RunInvalidate();
return bRet;
}
bool CXFA_FFWidgetHandler::OnRButtonDown(CXFA_FFWidget* hWidget,
uint32_t dwFlags,
const CFX_PointF& point) {
bool bRet = hWidget->OnRButtonDown(dwFlags, hWidget->Rotate2Normal(point));
if (bRet && m_pDocView->SetFocus(hWidget)) {
m_pDocView->GetDoc()->GetDocEnvironment()->SetFocusWidget(
m_pDocView->GetDoc(), hWidget);
}
m_pDocView->RunInvalidate();
return bRet;
}
bool CXFA_FFWidgetHandler::OnRButtonUp(CXFA_FFWidget* hWidget,
uint32_t dwFlags,
const CFX_PointF& point) {
bool bRet = hWidget->OnRButtonUp(dwFlags, hWidget->Rotate2Normal(point));
m_pDocView->RunInvalidate();
return bRet;
}
bool CXFA_FFWidgetHandler::OnRButtonDblClk(CXFA_FFWidget* hWidget,
uint32_t dwFlags,
const CFX_PointF& point) {
bool bRet = hWidget->OnRButtonDblClk(dwFlags, hWidget->Rotate2Normal(point));
m_pDocView->RunInvalidate();
return bRet;
}
bool CXFA_FFWidgetHandler::OnKeyDown(CXFA_FFWidget* hWidget,
uint32_t dwKeyCode,
uint32_t dwFlags) {
bool bRet = hWidget->OnKeyDown(dwKeyCode, dwFlags);
m_pDocView->RunInvalidate();
m_pDocView->UpdateDocView();
return bRet;
}
bool CXFA_FFWidgetHandler::OnKeyUp(CXFA_FFWidget* hWidget,
uint32_t dwKeyCode,
uint32_t dwFlags) {
bool bRet = hWidget->OnKeyUp(dwKeyCode, dwFlags);
m_pDocView->RunInvalidate();
return bRet;
}
bool CXFA_FFWidgetHandler::OnChar(CXFA_FFWidget* hWidget,
uint32_t dwChar,
uint32_t dwFlags) {
bool bRet = hWidget->OnChar(dwChar, dwFlags);
m_pDocView->RunInvalidate();
return bRet;
}
WideString CXFA_FFWidgetHandler::GetSelectedText(CXFA_FFWidget* widget) {
if (!widget->CanCopy())
return WideString();
return widget->Copy().value_or(WideString());
}
void CXFA_FFWidgetHandler::PasteText(CXFA_FFWidget* widget,
const WideString& text) {
if (!widget->CanPaste())
return;
widget->Paste(text);
}
FWL_WidgetHit CXFA_FFWidgetHandler::OnHitTest(CXFA_FFWidget* hWidget,
const CFX_PointF& point) {
if (!(hWidget->GetStatus() & XFA_WidgetStatus_Visible))
return FWL_WidgetHit::Unknown;
return hWidget->OnHitTest(hWidget->Rotate2Normal(point));
}
bool CXFA_FFWidgetHandler::OnSetCursor(CXFA_FFWidget* hWidget,
const CFX_PointF& point) {
return hWidget->OnSetCursor(hWidget->Rotate2Normal(point));
}
void CXFA_FFWidgetHandler::RenderWidget(CXFA_FFWidget* hWidget,
CXFA_Graphics* pGS,
const CFX_Matrix& matrix,
bool bHighlight) {
hWidget->RenderWidget(pGS, matrix,
bHighlight ? XFA_WidgetStatus_Highlight : 0);
}
bool CXFA_FFWidgetHandler::HasEvent(CXFA_WidgetAcc* pWidgetAcc,
XFA_EVENTTYPE eEventType) {
if (eEventType == XFA_EVENT_Unknown)
return false;
if (!pWidgetAcc)
return false;
CXFA_Node* node = pWidgetAcc->GetNode();
if (!node || node->GetElementType() == XFA_Element::Draw)
return false;
switch (eEventType) {
case XFA_EVENT_Calculate: {
CXFA_Calculate* calc = node->GetCalculateIfExists();
return calc && calc->GetScriptIfExists();
}
case XFA_EVENT_Validate: {
CXFA_Validate* validate = node->GetValidateIfExists();
return validate && validate->GetScriptIfExists();
}
default:
break;
}
return !pWidgetAcc->GetEventByActivity(gs_EventActivity[eEventType], false)
.empty();
}
int32_t CXFA_FFWidgetHandler::ProcessEvent(CXFA_WidgetAcc* pWidgetAcc,
CXFA_EventParam* pParam) {
if (!pParam || pParam->m_eType == XFA_EVENT_Unknown)
return XFA_EVENTERROR_NotExist;
if (!pWidgetAcc)
return XFA_EVENTERROR_NotExist;
CXFA_Node* node = pWidgetAcc->GetNode();
if (!node || node->GetElementType() == XFA_Element::Draw)
return XFA_EVENTERROR_NotExist;
switch (pParam->m_eType) {
case XFA_EVENT_Calculate:
return node->ProcessCalculate(m_pDocView);
case XFA_EVENT_Validate:
if (m_pDocView->GetDoc()->GetDocEnvironment()->IsValidationsEnabled(
m_pDocView->GetDoc())) {
return node->ProcessValidate(m_pDocView, 0);
}
return XFA_EVENTERROR_Disabled;
case XFA_EVENT_InitCalculate: {
CXFA_Calculate* calc = node->GetCalculateIfExists();
if (!calc)
return XFA_EVENTERROR_NotExist;
if (node->IsUserInteractive())
return XFA_EVENTERROR_Disabled;
return node->ExecuteScript(m_pDocView, calc->GetScriptIfExists(), pParam);
}
default:
break;
}
int32_t iRet =
node->ProcessEvent(m_pDocView, gs_EventActivity[pParam->m_eType], pParam);
return iRet;
}
CXFA_FFWidget* CXFA_FFWidgetHandler::CreateWidget(CXFA_FFWidget* hParent,
XFA_WIDGETTYPE eType,
CXFA_FFWidget* hBefore) {
CXFA_Node* pParentFormItem = hParent ? hParent->GetNode() : nullptr;
CXFA_Node* pBeforeFormItem = hBefore ? hBefore->GetNode() : nullptr;
CXFA_Node* pNewFormItem =
CreateWidgetFormItem(eType, pParentFormItem, pBeforeFormItem);
if (!pNewFormItem)
return nullptr;
CXFA_Node* templateNode = pNewFormItem->GetTemplateNodeIfExists();
if (!templateNode)
return nullptr;
templateNode->SetFlag(XFA_NodeFlag_Initialized, true);
pNewFormItem->SetFlag(XFA_NodeFlag_Initialized, true);
m_pDocView->RunLayout();
CXFA_LayoutItem* pLayout =
m_pDocView->GetXFALayout()->GetLayoutItem(pNewFormItem);
return static_cast<CXFA_FFWidget*>(pLayout);
}
CXFA_Node* CXFA_FFWidgetHandler::CreateWidgetFormItem(
XFA_WIDGETTYPE eType,
CXFA_Node* pParent,
CXFA_Node* pBefore) const {
switch (eType) {
case XFA_WIDGETTYPE_Barcode:
return nullptr;
case XFA_WIDGETTYPE_PushButton:
return CreatePushButton(pParent, pBefore);
case XFA_WIDGETTYPE_CheckButton:
return CreateCheckButton(pParent, pBefore);
case XFA_WIDGETTYPE_ExcludeGroup:
return CreateExclGroup(pParent, pBefore);
case XFA_WIDGETTYPE_RadioButton:
return CreateRadioButton(pParent, pBefore);
case XFA_WIDGETTYPE_Arc:
return CreateArc(pParent, pBefore);
case XFA_WIDGETTYPE_Rectangle:
return CreateRectangle(pParent, pBefore);
case XFA_WIDGETTYPE_Image:
return CreateImage(pParent, pBefore);
case XFA_WIDGETTYPE_Line:
return CreateLine(pParent, pBefore);
case XFA_WIDGETTYPE_Text:
return CreateText(pParent, pBefore);
case XFA_WIDGETTYPE_DatetimeEdit:
return CreateDatetimeEdit(pParent, pBefore);
case XFA_WIDGETTYPE_DecimalField:
return CreateDecimalField(pParent, pBefore);
case XFA_WIDGETTYPE_NumericField:
return CreateNumericField(pParent, pBefore);
case XFA_WIDGETTYPE_Signature:
return CreateSignature(pParent, pBefore);
case XFA_WIDGETTYPE_TextEdit:
return CreateTextEdit(pParent, pBefore);
case XFA_WIDGETTYPE_DropdownList:
return CreateDropdownList(pParent, pBefore);
case XFA_WIDGETTYPE_ListBox:
return CreateListBox(pParent, pBefore);
case XFA_WIDGETTYPE_ImageField:
return CreateImageField(pParent, pBefore);
case XFA_WIDGETTYPE_PasswordEdit:
return CreatePasswordEdit(pParent, pBefore);
case XFA_WIDGETTYPE_Subform:
return CreateSubform(pParent, pBefore);
default:
return nullptr;
}
}
CXFA_Node* CXFA_FFWidgetHandler::CreatePushButton(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
CXFA_Node* pField = CreateField(XFA_Element::Button, pParent, pBefore);
CXFA_Node* pCaption = CreateCopyNode(XFA_Element::Caption, pField);
CXFA_Node* pValue = CreateCopyNode(XFA_Element::Value, pCaption);
CXFA_Node* pText = CreateCopyNode(XFA_Element::Text, pValue);
pText->JSObject()->SetContent(L"Button", L"Button", false, false, true);
CXFA_Node* pPara = CreateCopyNode(XFA_Element::Para, pCaption);
pPara->JSObject()->SetEnum(XFA_Attribute::VAlign, XFA_AttributeEnum::Middle,
false);
pPara->JSObject()->SetEnum(XFA_Attribute::HAlign, XFA_AttributeEnum::Center,
false);
CreateFontNode(pCaption);
CXFA_Node* pBorder = CreateCopyNode(XFA_Element::Border, pField);
pBorder->JSObject()->SetEnum(XFA_Attribute::Hand, XFA_AttributeEnum::Right,
false);
CXFA_Node* pEdge = CreateCopyNode(XFA_Element::Edge, pBorder);
pEdge->JSObject()->SetEnum(XFA_Attribute::Stroke, XFA_AttributeEnum::Raised,
false);
CXFA_Node* pFill = CreateCopyNode(XFA_Element::Fill, pBorder);
CXFA_Node* pColor = CreateCopyNode(XFA_Element::Color, pFill);
pColor->JSObject()->SetCData(XFA_Attribute::Value, L"212, 208, 200", false,
false);
CXFA_Node* pBind = CreateCopyNode(XFA_Element::Bind, pField);
pBind->JSObject()->SetEnum(XFA_Attribute::Match, XFA_AttributeEnum::None,
false);
return pField;
}
CXFA_Node* CXFA_FFWidgetHandler::CreateCheckButton(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
return CreateField(XFA_Element::CheckButton, pParent, pBefore);
}
CXFA_Node* CXFA_FFWidgetHandler::CreateExclGroup(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
return CreateFormItem(XFA_Element::ExclGroup, pParent, pBefore);
}
CXFA_Node* CXFA_FFWidgetHandler::CreateRadioButton(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
CXFA_Node* pField = CreateField(XFA_Element::CheckButton, pParent, pBefore);
CXFA_Ui* pUi = pField->GetFirstChildByClass<CXFA_Ui>(XFA_Element::Ui);
CXFA_CheckButton* pWidget =
pUi->GetFirstChildByClass<CXFA_CheckButton>(XFA_Element::CheckButton);
pWidget->JSObject()->SetEnum(XFA_Attribute::Shape, XFA_AttributeEnum::Round,
false);
return pField;
}
CXFA_Node* CXFA_FFWidgetHandler::CreateDatetimeEdit(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
CXFA_Node* pField = CreateField(XFA_Element::DateTimeEdit, pParent, pBefore);
CreateValueNode(XFA_Element::Date, pField);
return pField;
}
CXFA_Node* CXFA_FFWidgetHandler::CreateDecimalField(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
CXFA_Node* pField = CreateNumericField(pParent, pBefore);
CreateValueNode(XFA_Element::Decimal, pField);
return pField;
}
CXFA_Node* CXFA_FFWidgetHandler::CreateNumericField(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
return CreateField(XFA_Element::NumericEdit, pParent, pBefore);
}
CXFA_Node* CXFA_FFWidgetHandler::CreateSignature(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
return CreateField(XFA_Element::Signature, pParent, pBefore);
}
CXFA_Node* CXFA_FFWidgetHandler::CreateTextEdit(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
return CreateField(XFA_Element::TextEdit, pParent, pBefore);
}
CXFA_Node* CXFA_FFWidgetHandler::CreateDropdownList(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
return CreateField(XFA_Element::ChoiceList, pParent, pBefore);
}
CXFA_Node* CXFA_FFWidgetHandler::CreateListBox(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
CXFA_Node* pField = CreateDropdownList(pParent, pBefore);
CXFA_Node* pUi = pField->GetFirstChild();
CXFA_Node* pListBox = pUi->GetFirstChild();
pListBox->JSObject()->SetEnum(XFA_Attribute::Open, XFA_AttributeEnum::Always,
false);
pListBox->JSObject()->SetEnum(XFA_Attribute::CommitOn,
XFA_AttributeEnum::Exit, false);
return pField;
}
CXFA_Node* CXFA_FFWidgetHandler::CreateImageField(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
return CreateField(XFA_Element::ImageEdit, pParent, pBefore);
}
CXFA_Node* CXFA_FFWidgetHandler::CreatePasswordEdit(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
CXFA_Node* pField = CreateField(XFA_Element::PasswordEdit, pParent, pBefore);
CXFA_Node* pBind = CreateCopyNode(XFA_Element::Bind, pField);
pBind->JSObject()->SetEnum(XFA_Attribute::Match, XFA_AttributeEnum::None,
false);
return pField;
}
CXFA_Node* CXFA_FFWidgetHandler::CreateField(XFA_Element eElement,
CXFA_Node* pParent,
CXFA_Node* pBefore) const {
CXFA_Node* pField = CreateFormItem(XFA_Element::Field, pParent, pBefore);
CreateCopyNode(eElement, CreateCopyNode(XFA_Element::Ui, pField));
CreateFontNode(pField);
return pField;
}
CXFA_Node* CXFA_FFWidgetHandler::CreateArc(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
return CreateDraw(XFA_Element::Arc, pParent, pBefore);
}
CXFA_Node* CXFA_FFWidgetHandler::CreateRectangle(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
return CreateDraw(XFA_Element::Rectangle, pParent, pBefore);
}
CXFA_Node* CXFA_FFWidgetHandler::CreateImage(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
CXFA_Node* pField = CreateDraw(XFA_Element::Image, pParent, pBefore);
CreateCopyNode(XFA_Element::ImageEdit,
CreateCopyNode(XFA_Element::Ui, pField));
return pField;
}
CXFA_Node* CXFA_FFWidgetHandler::CreateLine(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
return CreateDraw(XFA_Element::Line, pParent, pBefore);
}
CXFA_Node* CXFA_FFWidgetHandler::CreateText(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
CXFA_Node* pField = CreateDraw(XFA_Element::Text, pParent, pBefore);
CreateCopyNode(XFA_Element::TextEdit,
CreateCopyNode(XFA_Element::Ui, pField));
CreateFontNode(pField);
return pField;
}
CXFA_Node* CXFA_FFWidgetHandler::CreateDraw(XFA_Element eElement,
CXFA_Node* pParent,
CXFA_Node* pBefore) const {
CXFA_Node* pDraw = CreateFormItem(XFA_Element::Draw, pParent, pBefore);
CreateValueNode(eElement, pDraw);
return pDraw;
}
CXFA_Node* CXFA_FFWidgetHandler::CreateSubform(CXFA_Node* pParent,
CXFA_Node* pBefore) const {
return CreateFormItem(XFA_Element::Subform, pParent, pBefore);
}
CXFA_Node* CXFA_FFWidgetHandler::CreateFormItem(XFA_Element eElement,
CXFA_Node* pParent,
CXFA_Node* pBefore) const {
if (!pParent)
return nullptr;
CXFA_Node* pTemplateParent = pParent->GetTemplateNodeIfExists();
if (!pTemplateParent)
return nullptr;
CXFA_Node* pNewFormItem = pTemplateParent->CloneTemplateToForm(false);
if (pParent)
pParent->InsertChild(pNewFormItem, pBefore);
return pNewFormItem;
}
CXFA_Node* CXFA_FFWidgetHandler::CreateCopyNode(XFA_Element eElement,
CXFA_Node* pParent,
CXFA_Node* pBefore) const {
if (!pParent)
return nullptr;
CXFA_Node* pTemplateParent = pParent->GetTemplateNodeIfExists();
CXFA_Node* pNewNode =
CreateTemplateNode(eElement, pTemplateParent,
pBefore ? pBefore->GetTemplateNodeIfExists() : nullptr)
->Clone(false);
if (pParent)
pParent->InsertChild(pNewNode, pBefore);
return pNewNode;
}
CXFA_Node* CXFA_FFWidgetHandler::CreateTemplateNode(XFA_Element eElement,
CXFA_Node* pParent,
CXFA_Node* pBefore) const {
CXFA_Document* pXFADoc = GetXFADoc();
CXFA_Node* pNewTemplateNode =
pXFADoc->CreateNode(XFA_PacketType::Template, eElement);
if (pParent)
pParent->InsertChild(pNewTemplateNode, pBefore);
return pNewTemplateNode;
}
CXFA_Node* CXFA_FFWidgetHandler::CreateFontNode(CXFA_Node* pParent) const {
CXFA_Node* pFont = CreateCopyNode(XFA_Element::Font, pParent);
pFont->JSObject()->SetCData(XFA_Attribute::Typeface, L"Myriad Pro", false,
false);
return pFont;
}
CXFA_Node* CXFA_FFWidgetHandler::CreateMarginNode(CXFA_Node* pParent,
uint32_t dwFlags,
float fInsets[4]) const {
CXFA_Node* pMargin = CreateCopyNode(XFA_Element::Margin, pParent);
if (dwFlags & 0x01)
pMargin->JSObject()->SetMeasure(XFA_Attribute::LeftInset,
CXFA_Measurement(fInsets[0], XFA_Unit::Pt),
false);
if (dwFlags & 0x02)
pMargin->JSObject()->SetMeasure(XFA_Attribute::TopInset,
CXFA_Measurement(fInsets[1], XFA_Unit::Pt),
false);
if (dwFlags & 0x04)
pMargin->JSObject()->SetMeasure(XFA_Attribute::RightInset,
CXFA_Measurement(fInsets[2], XFA_Unit::Pt),
false);
if (dwFlags & 0x08)
pMargin->JSObject()->SetMeasure(XFA_Attribute::BottomInset,
CXFA_Measurement(fInsets[3], XFA_Unit::Pt),
false);
return pMargin;
}
CXFA_Node* CXFA_FFWidgetHandler::CreateValueNode(XFA_Element eValue,
CXFA_Node* pParent) const {
CXFA_Node* pValue = CreateCopyNode(XFA_Element::Value, pParent);
CreateCopyNode(eValue, pValue);
return pValue;
}
CXFA_Document* CXFA_FFWidgetHandler::GetObjFactory() const {
return GetXFADoc();
}
CXFA_Document* CXFA_FFWidgetHandler::GetXFADoc() const {
return m_pDocView->GetDoc()->GetXFADoc();
}