| // Copyright 2014 The PDFium Authors | |
| // 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_ffcheckbutton.h" | |
| #include "core/fxcrt/check.h" | |
| #include "v8/include/cppgc/visitor.h" | |
| #include "xfa/fwl/cfwl_checkbox.h" | |
| #include "xfa/fwl/cfwl_messagemouse.h" | |
| #include "xfa/fwl/cfwl_notedriver.h" | |
| #include "xfa/fwl/cfwl_widgetmgr.h" | |
| #include "xfa/fxfa/cxfa_ffapp.h" | |
| #include "xfa/fxfa/cxfa_ffdoc.h" | |
| #include "xfa/fxfa/cxfa_ffdocview.h" | |
| #include "xfa/fxfa/cxfa_ffexclgroup.h" | |
| #include "xfa/fxfa/cxfa_fffield.h" | |
| #include "xfa/fxfa/cxfa_ffpageview.h" | |
| #include "xfa/fxfa/cxfa_ffwidget.h" | |
| #include "xfa/fxfa/parser/cxfa_border.h" | |
| #include "xfa/fxfa/parser/cxfa_caption.h" | |
| #include "xfa/fxfa/parser/cxfa_checkbutton.h" | |
| #include "xfa/fxfa/parser/cxfa_para.h" | |
| CXFA_FFCheckButton::CXFA_FFCheckButton(CXFA_Node* pNode, | |
| CXFA_CheckButton* button) | |
| : CXFA_FFField(pNode), button_(button) {} | |
| CXFA_FFCheckButton::~CXFA_FFCheckButton() = default; | |
| void CXFA_FFCheckButton::Trace(cppgc::Visitor* visitor) const { | |
| CXFA_FFField::Trace(visitor); | |
| visitor->Trace(old_delegate_); | |
| visitor->Trace(button_); | |
| } | |
| bool CXFA_FFCheckButton::LoadWidget() { | |
| DCHECK(!IsLoaded()); | |
| CFWL_CheckBox* pCheckBox = cppgc::MakeGarbageCollected<CFWL_CheckBox>( | |
| GetFWLApp()->GetHeap()->GetAllocationHandle(), GetFWLApp()); | |
| SetNormalWidget(pCheckBox); | |
| pCheckBox->SetAdapterIface(this); | |
| CFWL_NoteDriver* pNoteDriver = pCheckBox->GetFWLApp()->GetNoteDriver(); | |
| pNoteDriver->RegisterEventTarget(pCheckBox, pCheckBox); | |
| old_delegate_ = pCheckBox->GetDelegate(); | |
| pCheckBox->SetDelegate(this); | |
| if (node_->IsRadioButton()) { | |
| pCheckBox->ModifyStyleExts(FWL_STYLEEXT_CKB_RadioButton, 0xFFFFFFFF); | |
| } | |
| { | |
| CFWL_Widget::ScopedUpdateLock update_lock(pCheckBox); | |
| UpdateWidgetProperty(); | |
| SetFWLCheckState(node_->GetCheckState()); | |
| } | |
| return CXFA_FFField::LoadWidget(); | |
| } | |
| void CXFA_FFCheckButton::UpdateWidgetProperty() { | |
| auto* pCheckBox = static_cast<CFWL_CheckBox*>(GetNormalWidget()); | |
| if (!pCheckBox) { | |
| return; | |
| } | |
| pCheckBox->SetBoxSize(node_->GetCheckButtonSize()); | |
| uint32_t dwStyleEx = FWL_STYLEEXT_CKB_SignShapeCross; | |
| switch (button_->GetMark()) { | |
| case XFA_AttributeValue::Check: | |
| dwStyleEx = FWL_STYLEEXT_CKB_SignShapeCheck; | |
| break; | |
| case XFA_AttributeValue::Circle: | |
| dwStyleEx = FWL_STYLEEXT_CKB_SignShapeCircle; | |
| break; | |
| case XFA_AttributeValue::Cross: | |
| break; | |
| case XFA_AttributeValue::Diamond: | |
| dwStyleEx = FWL_STYLEEXT_CKB_SignShapeDiamond; | |
| break; | |
| case XFA_AttributeValue::Square: | |
| dwStyleEx = FWL_STYLEEXT_CKB_SignShapeSquare; | |
| break; | |
| case XFA_AttributeValue::Star: | |
| dwStyleEx = FWL_STYLEEXT_CKB_SignShapeStar; | |
| break; | |
| default: { | |
| if (button_->IsRound()) { | |
| dwStyleEx = FWL_STYLEEXT_CKB_SignShapeCircle; | |
| } | |
| } break; | |
| } | |
| if (button_->IsAllowNeutral()) { | |
| dwStyleEx |= FWL_STYLEEXT_CKB_3State; | |
| } | |
| pCheckBox->ModifyStyleExts( | |
| dwStyleEx, FWL_STYLEEXT_CKB_SignShapeMask | FWL_STYLEEXT_CKB_3State); | |
| } | |
| void CXFA_FFCheckButton::PerformLayout() { | |
| CXFA_FFWidget::PerformLayout(); | |
| float fCheckSize = node_->GetCheckButtonSize(); | |
| CXFA_Margin* margin = node_->GetMarginIfExists(); | |
| CFX_RectF rtWidget = GetRectWithoutRotate(); | |
| XFA_RectWithoutMargin(&rtWidget, margin); | |
| XFA_AttributeValue iCapPlacement = XFA_AttributeValue::Unknown; | |
| float fCapReserve = 0; | |
| CXFA_Caption* caption = node_->GetCaptionIfExists(); | |
| if (caption && caption->IsVisible()) { | |
| caption_rect_ = rtWidget; | |
| iCapPlacement = caption->GetPlacementType(); | |
| fCapReserve = caption->GetReserve(); | |
| if (fCapReserve <= 0) { | |
| if (iCapPlacement == XFA_AttributeValue::Top || | |
| iCapPlacement == XFA_AttributeValue::Bottom) { | |
| fCapReserve = rtWidget.height - fCheckSize; | |
| } else { | |
| fCapReserve = rtWidget.width - fCheckSize; | |
| } | |
| } | |
| } | |
| XFA_AttributeValue iHorzAlign = XFA_AttributeValue::Left; | |
| XFA_AttributeValue iVertAlign = XFA_AttributeValue::Top; | |
| CXFA_Para* para = node_->GetParaIfExists(); | |
| if (para) { | |
| iHorzAlign = para->GetHorizontalAlign(); | |
| iVertAlign = para->GetVerticalAlign(); | |
| } | |
| uirect_ = rtWidget; | |
| CXFA_Margin* captionMargin = caption ? caption->GetMarginIfExists() : nullptr; | |
| switch (iCapPlacement) { | |
| case XFA_AttributeValue::Left: { | |
| caption_rect_.width = fCapReserve; | |
| CapLeftRightPlacement(captionMargin); | |
| uirect_.width -= fCapReserve; | |
| uirect_.left += fCapReserve; | |
| break; | |
| } | |
| case XFA_AttributeValue::Top: { | |
| caption_rect_.height = fCapReserve; | |
| XFA_RectWithoutMargin(&caption_rect_, captionMargin); | |
| uirect_.height -= fCapReserve; | |
| uirect_.top += fCapReserve; | |
| break; | |
| } | |
| case XFA_AttributeValue::Right: { | |
| caption_rect_.left = caption_rect_.right() - fCapReserve; | |
| caption_rect_.width = fCapReserve; | |
| CapLeftRightPlacement(captionMargin); | |
| uirect_.width -= fCapReserve; | |
| break; | |
| } | |
| case XFA_AttributeValue::Bottom: { | |
| caption_rect_.top = caption_rect_.bottom() - fCapReserve; | |
| caption_rect_.height = fCapReserve; | |
| XFA_RectWithoutMargin(&caption_rect_, captionMargin); | |
| uirect_.height -= fCapReserve; | |
| break; | |
| } | |
| case XFA_AttributeValue::Inline: | |
| break; | |
| default: | |
| iHorzAlign = XFA_AttributeValue::Right; | |
| break; | |
| } | |
| if (iHorzAlign == XFA_AttributeValue::Center) { | |
| uirect_.left += (uirect_.width - fCheckSize) / 2; | |
| } else if (iHorzAlign == XFA_AttributeValue::Right) { | |
| uirect_.left = uirect_.right() - fCheckSize; | |
| } | |
| if (iVertAlign == XFA_AttributeValue::Middle) { | |
| uirect_.top += (uirect_.height - fCheckSize) / 2; | |
| } else if (iVertAlign == XFA_AttributeValue::Bottom) { | |
| uirect_.top = uirect_.bottom() - fCheckSize; | |
| } | |
| uirect_.width = fCheckSize; | |
| uirect_.height = fCheckSize; | |
| AddUIMargin(iCapPlacement); | |
| check_box_rect_ = uirect_; | |
| CXFA_Border* borderUI = node_->GetUIBorder(); | |
| if (borderUI) { | |
| CXFA_Margin* borderMargin = borderUI->GetMarginIfExists(); | |
| XFA_RectWithoutMargin(&uirect_, borderMargin); | |
| } | |
| uirect_.Normalize(); | |
| LayoutCaption(); | |
| SetFWLRect(); | |
| if (GetNormalWidget()) { | |
| GetNormalWidget()->Update(); | |
| } | |
| } | |
| void CXFA_FFCheckButton::CapLeftRightPlacement( | |
| const CXFA_Margin* captionMargin) { | |
| XFA_RectWithoutMargin(&caption_rect_, captionMargin); | |
| if (caption_rect_.height < 0) { | |
| caption_rect_.top += caption_rect_.height; | |
| } | |
| if (caption_rect_.width < 0) { | |
| caption_rect_.left += caption_rect_.width; | |
| caption_rect_.width = -caption_rect_.width; | |
| } | |
| } | |
| void CXFA_FFCheckButton::AddUIMargin(XFA_AttributeValue iCapPlacement) { | |
| CFX_RectF rtUIMargin = node_->GetUIMargin(); | |
| uirect_.top -= rtUIMargin.top / 2 - rtUIMargin.height / 2; | |
| float fLeftAddRight = rtUIMargin.left + rtUIMargin.width; | |
| float fTopAddBottom = rtUIMargin.top + rtUIMargin.height; | |
| if (uirect_.width < fLeftAddRight) { | |
| if (iCapPlacement == XFA_AttributeValue::Right || | |
| iCapPlacement == XFA_AttributeValue::Left) { | |
| uirect_.left -= fLeftAddRight - uirect_.width; | |
| } else { | |
| uirect_.left -= 2 * (fLeftAddRight - uirect_.width); | |
| } | |
| uirect_.width += 2 * (fLeftAddRight - uirect_.width); | |
| } | |
| if (uirect_.height < fTopAddBottom) { | |
| if (iCapPlacement == XFA_AttributeValue::Right) { | |
| uirect_.left -= fTopAddBottom - uirect_.height; | |
| } | |
| uirect_.top -= fTopAddBottom - uirect_.height; | |
| uirect_.height += 2 * (fTopAddBottom - uirect_.height); | |
| } | |
| } | |
| void CXFA_FFCheckButton::RenderWidget(CFGAS_GEGraphics* pGS, | |
| const CFX_Matrix& matrix, | |
| HighlightOption highlight) { | |
| if (!HasVisibleStatus()) { | |
| return; | |
| } | |
| CFX_Matrix mtRotate = GetRotateMatrix(); | |
| mtRotate.Concat(matrix); | |
| CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight); | |
| DrawBorderWithFlag(pGS, node_->GetUIBorder(), uirect_, mtRotate, | |
| button_->IsRound()); | |
| RenderCaption(pGS, mtRotate); | |
| DrawHighlight(pGS, mtRotate, highlight, | |
| button_->IsRound() ? kRoundShape : kSquareShape); | |
| CFX_Matrix mt(1, 0, 0, 1, check_box_rect_.left, check_box_rect_.top); | |
| mt.Concat(mtRotate); | |
| GetApp()->GetFWLWidgetMgr()->OnDrawWidget(GetNormalWidget(), pGS, mt); | |
| } | |
| bool CXFA_FFCheckButton::OnLButtonUp(Mask<XFA_FWL_KeyFlag> dwFlags, | |
| const CFX_PointF& point) { | |
| if (!GetNormalWidget() || !IsButtonDown()) { | |
| return false; | |
| } | |
| SetButtonDown(false); | |
| CFWL_MessageMouse msg(GetNormalWidget(), | |
| CFWL_MessageMouse::MouseCommand::kLeftButtonUp, dwFlags, | |
| FWLToClient(point)); | |
| SendMessageToFWLWidget(&msg); | |
| return true; | |
| } | |
| XFA_CheckState CXFA_FFCheckButton::FWLState2XFAState() { | |
| Mask<pdfium::WidgetState> state = GetNormalWidget()->GetStates(); | |
| if (state & pdfium::WidgetState::kCheckboxChecked) { | |
| return XFA_CheckState::kOn; | |
| } | |
| if (state & pdfium::WidgetState::kCheckboxNeutral) { | |
| return XFA_CheckState::kNeutral; | |
| } | |
| return XFA_CheckState::kOff; | |
| } | |
| bool CXFA_FFCheckButton::CommitData() { | |
| node_->SetCheckState(FWLState2XFAState()); | |
| return true; | |
| } | |
| bool CXFA_FFCheckButton::IsDataChanged() { | |
| XFA_CheckState eCheckState = FWLState2XFAState(); | |
| return node_->GetCheckState() != eCheckState; | |
| } | |
| void CXFA_FFCheckButton::SetFWLCheckState(XFA_CheckState eCheckState) { | |
| if (eCheckState == XFA_CheckState::kNeutral) { | |
| GetNormalWidget()->SetStates(pdfium::WidgetState::kCheckboxNeutral); | |
| } else if (eCheckState == XFA_CheckState::kOn) { | |
| GetNormalWidget()->SetStates(pdfium::WidgetState::kCheckboxChecked); | |
| } else { | |
| GetNormalWidget()->ClearStates(pdfium::WidgetState::kCheckboxChecked); | |
| } | |
| } | |
| void CXFA_FFCheckButton::UpdateFWLData() { | |
| if (!GetNormalWidget()) { | |
| return; | |
| } | |
| SetFWLCheckState(node_->GetCheckState()); | |
| GetNormalWidget()->Update(); | |
| } | |
| void CXFA_FFCheckButton::OnProcessMessage(CFWL_Message* pMessage) { | |
| old_delegate_->OnProcessMessage(pMessage); | |
| } | |
| void CXFA_FFCheckButton::OnProcessEvent(CFWL_Event* pEvent) { | |
| CXFA_FFField::OnProcessEvent(pEvent); | |
| switch (pEvent->GetType()) { | |
| case CFWL_Event::Type::CheckStateChanged: { | |
| CXFA_EventParam eParam(XFA_EVENT_Change); | |
| eParam.prev_text_ = node_->GetValue(XFA_ValuePicture::kRaw); | |
| CXFA_Node* exclNode = node_->GetExclGroupIfExists(); | |
| if (ProcessCommittedData()) { | |
| if (exclNode) { | |
| doc_view_->AddValidateNode(exclNode); | |
| doc_view_->AddCalculateNode(exclNode); | |
| exclNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change, | |
| &eParam); | |
| } | |
| node_->ProcessEvent(GetDocView(), XFA_AttributeValue::Change, &eParam); | |
| } else { | |
| SetFWLCheckState(node_->GetCheckState()); | |
| } | |
| if (exclNode) { | |
| exclNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Click, | |
| &eParam); | |
| } | |
| node_->ProcessEvent(GetDocView(), XFA_AttributeValue::Click, &eParam); | |
| break; | |
| } | |
| default: | |
| break; | |
| } | |
| old_delegate_->OnProcessEvent(pEvent); | |
| } | |
| void CXFA_FFCheckButton::OnDrawWidget(CFGAS_GEGraphics* pGraphics, | |
| const CFX_Matrix& matrix) { | |
| old_delegate_->OnDrawWidget(pGraphics, matrix); | |
| } | |
| FormFieldType CXFA_FFCheckButton::GetFormFieldType() { | |
| return FormFieldType::kXFA_CheckBox; | |
| } |