blob: 65984e512a39e8b8ddbb4618bb91dab0a22c5214 [file] [log] [blame] [edit]
// 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() {
uint32_t dwState = GetNormalWidget()->GetStates();
if (dwState & FWL_STATE_CKB_Checked) {
return XFA_CheckState::kOn;
}
if (dwState & FWL_STATE_CKB_Neutral) {
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(FWL_STATE_CKB_Neutral);
} else if (eCheckState == XFA_CheckState::kOn) {
GetNormalWidget()->SetStates(FWL_STATE_CKB_Checked);
} else {
GetNormalWidget()->RemoveStates(FWL_STATE_CKB_Checked);
}
}
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;
}