1 // Copyright 2014 PDFium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6 
7 #include "xfa/fxfa/cxfa_ffcheckbutton.h"
8 
9 #include "third_party/base/check.h"
10 #include "v8/include/cppgc/visitor.h"
11 #include "xfa/fwl/cfwl_checkbox.h"
12 #include "xfa/fwl/cfwl_messagemouse.h"
13 #include "xfa/fwl/cfwl_notedriver.h"
14 #include "xfa/fwl/cfwl_widgetmgr.h"
15 #include "xfa/fxfa/cxfa_ffapp.h"
16 #include "xfa/fxfa/cxfa_ffdoc.h"
17 #include "xfa/fxfa/cxfa_ffdocview.h"
18 #include "xfa/fxfa/cxfa_ffexclgroup.h"
19 #include "xfa/fxfa/cxfa_fffield.h"
20 #include "xfa/fxfa/cxfa_ffpageview.h"
21 #include "xfa/fxfa/cxfa_ffwidget.h"
22 #include "xfa/fxfa/parser/cxfa_border.h"
23 #include "xfa/fxfa/parser/cxfa_caption.h"
24 #include "xfa/fxfa/parser/cxfa_checkbutton.h"
25 #include "xfa/fxfa/parser/cxfa_para.h"
26 
CXFA_FFCheckButton(CXFA_Node * pNode,CXFA_CheckButton * button)27 CXFA_FFCheckButton::CXFA_FFCheckButton(CXFA_Node* pNode,
28                                        CXFA_CheckButton* button)
29     : CXFA_FFField(pNode), button_(button) {}
30 
31 CXFA_FFCheckButton::~CXFA_FFCheckButton() = default;
32 
Trace(cppgc::Visitor * visitor) const33 void CXFA_FFCheckButton::Trace(cppgc::Visitor* visitor) const {
34   CXFA_FFField::Trace(visitor);
35   visitor->Trace(m_pOldDelegate);
36   visitor->Trace(button_);
37 }
38 
LoadWidget()39 bool CXFA_FFCheckButton::LoadWidget() {
40   DCHECK(!IsLoaded());
41 
42   CFWL_CheckBox* pCheckBox = cppgc::MakeGarbageCollected<CFWL_CheckBox>(
43       GetFWLApp()->GetHeap()->GetAllocationHandle(), GetFWLApp());
44   SetNormalWidget(pCheckBox);
45   pCheckBox->SetAdapterIface(this);
46 
47   CFWL_NoteDriver* pNoteDriver = pCheckBox->GetFWLApp()->GetNoteDriver();
48   pNoteDriver->RegisterEventTarget(pCheckBox, pCheckBox);
49   m_pOldDelegate = pCheckBox->GetDelegate();
50   pCheckBox->SetDelegate(this);
51   if (m_pNode->IsRadioButton())
52     pCheckBox->ModifyStylesEx(FWL_STYLEEXT_CKB_RadioButton, 0xFFFFFFFF);
53 
54   {
55     CFWL_Widget::ScopedUpdateLock update_lock(pCheckBox);
56     UpdateWidgetProperty();
57     SetFWLCheckState(m_pNode->GetCheckState());
58   }
59 
60   return CXFA_FFField::LoadWidget();
61 }
62 
UpdateWidgetProperty()63 void CXFA_FFCheckButton::UpdateWidgetProperty() {
64   auto* pCheckBox = static_cast<CFWL_CheckBox*>(GetNormalWidget());
65   if (!pCheckBox)
66     return;
67 
68   pCheckBox->SetBoxSize(m_pNode->GetCheckButtonSize());
69   uint32_t dwStyleEx = FWL_STYLEEXT_CKB_SignShapeCross;
70   switch (button_->GetMark()) {
71     case XFA_AttributeValue::Check:
72       dwStyleEx = FWL_STYLEEXT_CKB_SignShapeCheck;
73       break;
74     case XFA_AttributeValue::Circle:
75       dwStyleEx = FWL_STYLEEXT_CKB_SignShapeCircle;
76       break;
77     case XFA_AttributeValue::Cross:
78       break;
79     case XFA_AttributeValue::Diamond:
80       dwStyleEx = FWL_STYLEEXT_CKB_SignShapeDiamond;
81       break;
82     case XFA_AttributeValue::Square:
83       dwStyleEx = FWL_STYLEEXT_CKB_SignShapeSquare;
84       break;
85     case XFA_AttributeValue::Star:
86       dwStyleEx = FWL_STYLEEXT_CKB_SignShapeStar;
87       break;
88     default: {
89       if (button_->IsRound())
90         dwStyleEx = FWL_STYLEEXT_CKB_SignShapeCircle;
91     } break;
92   }
93   if (button_->IsAllowNeutral())
94     dwStyleEx |= FWL_STYLEEXT_CKB_3State;
95 
96   pCheckBox->ModifyStylesEx(
97       dwStyleEx, FWL_STYLEEXT_CKB_SignShapeMask | FWL_STYLEEXT_CKB_3State);
98 }
99 
PerformLayout()100 bool CXFA_FFCheckButton::PerformLayout() {
101   CXFA_FFWidget::PerformLayout();
102 
103   float fCheckSize = m_pNode->GetCheckButtonSize();
104   CXFA_Margin* margin = m_pNode->GetMarginIfExists();
105   CFX_RectF rtWidget = GetRectWithoutRotate();
106   XFA_RectWithoutMargin(&rtWidget, margin);
107 
108   XFA_AttributeValue iCapPlacement = XFA_AttributeValue::Unknown;
109   float fCapReserve = 0;
110   CXFA_Caption* caption = m_pNode->GetCaptionIfExists();
111   if (caption && caption->IsVisible()) {
112     m_CaptionRect = rtWidget;
113     iCapPlacement = caption->GetPlacementType();
114     fCapReserve = caption->GetReserve();
115     if (fCapReserve <= 0) {
116       if (iCapPlacement == XFA_AttributeValue::Top ||
117           iCapPlacement == XFA_AttributeValue::Bottom) {
118         fCapReserve = rtWidget.height - fCheckSize;
119       } else {
120         fCapReserve = rtWidget.width - fCheckSize;
121       }
122     }
123   }
124 
125   XFA_AttributeValue iHorzAlign = XFA_AttributeValue::Left;
126   XFA_AttributeValue iVertAlign = XFA_AttributeValue::Top;
127   CXFA_Para* para = m_pNode->GetParaIfExists();
128   if (para) {
129     iHorzAlign = para->GetHorizontalAlign();
130     iVertAlign = para->GetVerticalAlign();
131   }
132 
133   m_UIRect = rtWidget;
134   CXFA_Margin* captionMargin = caption ? caption->GetMarginIfExists() : nullptr;
135   switch (iCapPlacement) {
136     case XFA_AttributeValue::Left: {
137       m_CaptionRect.width = fCapReserve;
138       CapLeftRightPlacement(captionMargin);
139       m_UIRect.width -= fCapReserve;
140       m_UIRect.left += fCapReserve;
141       break;
142     }
143     case XFA_AttributeValue::Top: {
144       m_CaptionRect.height = fCapReserve;
145       XFA_RectWithoutMargin(&m_CaptionRect, captionMargin);
146       m_UIRect.height -= fCapReserve;
147       m_UIRect.top += fCapReserve;
148       break;
149     }
150     case XFA_AttributeValue::Right: {
151       m_CaptionRect.left = m_CaptionRect.right() - fCapReserve;
152       m_CaptionRect.width = fCapReserve;
153       CapLeftRightPlacement(captionMargin);
154       m_UIRect.width -= fCapReserve;
155       break;
156     }
157     case XFA_AttributeValue::Bottom: {
158       m_CaptionRect.top = m_CaptionRect.bottom() - fCapReserve;
159       m_CaptionRect.height = fCapReserve;
160       XFA_RectWithoutMargin(&m_CaptionRect, captionMargin);
161       m_UIRect.height -= fCapReserve;
162       break;
163     }
164     case XFA_AttributeValue::Inline:
165       break;
166     default:
167       iHorzAlign = XFA_AttributeValue::Right;
168       break;
169   }
170 
171   if (iHorzAlign == XFA_AttributeValue::Center)
172     m_UIRect.left += (m_UIRect.width - fCheckSize) / 2;
173   else if (iHorzAlign == XFA_AttributeValue::Right)
174     m_UIRect.left = m_UIRect.right() - fCheckSize;
175 
176   if (iVertAlign == XFA_AttributeValue::Middle)
177     m_UIRect.top += (m_UIRect.height - fCheckSize) / 2;
178   else if (iVertAlign == XFA_AttributeValue::Bottom)
179     m_UIRect.top = m_UIRect.bottom() - fCheckSize;
180 
181   m_UIRect.width = fCheckSize;
182   m_UIRect.height = fCheckSize;
183   AddUIMargin(iCapPlacement);
184   m_CheckBoxRect = m_UIRect;
185   CXFA_Border* borderUI = m_pNode->GetUIBorder();
186   if (borderUI) {
187     CXFA_Margin* borderMargin = borderUI->GetMarginIfExists();
188     XFA_RectWithoutMargin(&m_UIRect, borderMargin);
189   }
190 
191   m_UIRect.Normalize();
192   LayoutCaption();
193   SetFWLRect();
194   if (GetNormalWidget())
195     GetNormalWidget()->Update();
196 
197   return true;
198 }
199 
CapLeftRightPlacement(const CXFA_Margin * captionMargin)200 void CXFA_FFCheckButton::CapLeftRightPlacement(
201     const CXFA_Margin* captionMargin) {
202   XFA_RectWithoutMargin(&m_CaptionRect, captionMargin);
203   if (m_CaptionRect.height < 0)
204     m_CaptionRect.top += m_CaptionRect.height;
205   if (m_CaptionRect.width < 0) {
206     m_CaptionRect.left += m_CaptionRect.width;
207     m_CaptionRect.width = -m_CaptionRect.width;
208   }
209 }
210 
AddUIMargin(XFA_AttributeValue iCapPlacement)211 void CXFA_FFCheckButton::AddUIMargin(XFA_AttributeValue iCapPlacement) {
212   CFX_RectF rtUIMargin = m_pNode->GetUIMargin();
213   m_UIRect.top -= rtUIMargin.top / 2 - rtUIMargin.height / 2;
214 
215   float fLeftAddRight = rtUIMargin.left + rtUIMargin.width;
216   float fTopAddBottom = rtUIMargin.top + rtUIMargin.height;
217   if (m_UIRect.width < fLeftAddRight) {
218     if (iCapPlacement == XFA_AttributeValue::Right ||
219         iCapPlacement == XFA_AttributeValue::Left) {
220       m_UIRect.left -= fLeftAddRight - m_UIRect.width;
221     } else {
222       m_UIRect.left -= 2 * (fLeftAddRight - m_UIRect.width);
223     }
224     m_UIRect.width += 2 * (fLeftAddRight - m_UIRect.width);
225   }
226   if (m_UIRect.height < fTopAddBottom) {
227     if (iCapPlacement == XFA_AttributeValue::Right)
228       m_UIRect.left -= fTopAddBottom - m_UIRect.height;
229 
230     m_UIRect.top -= fTopAddBottom - m_UIRect.height;
231     m_UIRect.height += 2 * (fTopAddBottom - m_UIRect.height);
232   }
233 }
234 
RenderWidget(CFGAS_GEGraphics * pGS,const CFX_Matrix & matrix,HighlightOption highlight)235 void CXFA_FFCheckButton::RenderWidget(CFGAS_GEGraphics* pGS,
236                                       const CFX_Matrix& matrix,
237                                       HighlightOption highlight) {
238   if (!HasVisibleStatus())
239     return;
240 
241   CFX_Matrix mtRotate = GetRotateMatrix();
242   mtRotate.Concat(matrix);
243 
244   CXFA_FFWidget::RenderWidget(pGS, mtRotate, highlight);
245   DrawBorderWithFlag(pGS, m_pNode->GetUIBorder(), m_UIRect, mtRotate,
246                      button_->IsRound());
247   RenderCaption(pGS, &mtRotate);
248   DrawHighlight(pGS, &mtRotate, highlight,
249                 button_->IsRound() ? kRoundShape : kSquareShape);
250   CFX_Matrix mt(1, 0, 0, 1, m_CheckBoxRect.left, m_CheckBoxRect.top);
251   mt.Concat(mtRotate);
252   GetApp()->GetFWLWidgetMgr()->OnDrawWidget(GetNormalWidget(), pGS, mt);
253 }
254 
OnLButtonUp(uint32_t dwFlags,const CFX_PointF & point)255 bool CXFA_FFCheckButton::OnLButtonUp(uint32_t dwFlags,
256                                      const CFX_PointF& point) {
257   if (!GetNormalWidget() || !IsButtonDown())
258     return false;
259 
260   SetButtonDown(false);
261   CFWL_MessageMouse msg(GetNormalWidget(), FWL_MouseCommand::LeftButtonUp,
262                         dwFlags, FWLToClient(point));
263   SendMessageToFWLWidget(&msg);
264   return true;
265 }
266 
FWLState2XFAState()267 XFA_CHECKSTATE CXFA_FFCheckButton::FWLState2XFAState() {
268   uint32_t dwState = GetNormalWidget()->GetStates();
269   if (dwState & FWL_STATE_CKB_Checked)
270     return XFA_CHECKSTATE_On;
271   if (dwState & FWL_STATE_CKB_Neutral)
272     return XFA_CHECKSTATE_Neutral;
273   return XFA_CHECKSTATE_Off;
274 }
275 
CommitData()276 bool CXFA_FFCheckButton::CommitData() {
277   XFA_CHECKSTATE eCheckState = FWLState2XFAState();
278   m_pNode->SetCheckState(eCheckState, true);
279   return true;
280 }
281 
IsDataChanged()282 bool CXFA_FFCheckButton::IsDataChanged() {
283   XFA_CHECKSTATE eCheckState = FWLState2XFAState();
284   return m_pNode->GetCheckState() != eCheckState;
285 }
286 
SetFWLCheckState(XFA_CHECKSTATE eCheckState)287 void CXFA_FFCheckButton::SetFWLCheckState(XFA_CHECKSTATE eCheckState) {
288   if (eCheckState == XFA_CHECKSTATE_Neutral)
289     GetNormalWidget()->SetStates(FWL_STATE_CKB_Neutral);
290   else if (eCheckState == XFA_CHECKSTATE_On)
291     GetNormalWidget()->SetStates(FWL_STATE_CKB_Checked);
292   else
293     GetNormalWidget()->RemoveStates(FWL_STATE_CKB_Checked);
294 }
295 
UpdateFWLData()296 bool CXFA_FFCheckButton::UpdateFWLData() {
297   if (!GetNormalWidget())
298     return false;
299 
300   SetFWLCheckState(m_pNode->GetCheckState());
301   GetNormalWidget()->Update();
302   return true;
303 }
304 
OnProcessMessage(CFWL_Message * pMessage)305 void CXFA_FFCheckButton::OnProcessMessage(CFWL_Message* pMessage) {
306   m_pOldDelegate->OnProcessMessage(pMessage);
307 }
308 
OnProcessEvent(CFWL_Event * pEvent)309 void CXFA_FFCheckButton::OnProcessEvent(CFWL_Event* pEvent) {
310   CXFA_FFField::OnProcessEvent(pEvent);
311   switch (pEvent->GetType()) {
312     case CFWL_Event::Type::CheckStateChanged: {
313       CXFA_EventParam eParam;
314       eParam.m_eType = XFA_EVENT_Change;
315       eParam.m_wsPrevText = m_pNode->GetValue(XFA_VALUEPICTURE_Raw);
316 
317       CXFA_Node* exclNode = m_pNode->GetExclGroupIfExists();
318       if (ProcessCommittedData()) {
319         eParam.m_pTarget = exclNode;
320         if (exclNode) {
321           m_pDocView->AddValidateNode(exclNode);
322           m_pDocView->AddCalculateNode(exclNode);
323           exclNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change,
324                                  &eParam);
325         }
326         eParam.m_pTarget = m_pNode.Get();
327         m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Change,
328                               &eParam);
329       } else {
330         SetFWLCheckState(m_pNode->GetCheckState());
331       }
332       if (exclNode) {
333         eParam.m_pTarget = exclNode;
334         exclNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Click,
335                                &eParam);
336       }
337       eParam.m_pTarget = m_pNode.Get();
338       m_pNode->ProcessEvent(GetDocView(), XFA_AttributeValue::Click, &eParam);
339       break;
340     }
341     default:
342       break;
343   }
344   m_pOldDelegate->OnProcessEvent(pEvent);
345 }
346 
OnDrawWidget(CFGAS_GEGraphics * pGraphics,const CFX_Matrix & matrix)347 void CXFA_FFCheckButton::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
348                                       const CFX_Matrix& matrix) {
349   m_pOldDelegate->OnDrawWidget(pGraphics, matrix);
350 }
351 
GetFormFieldType()352 FormFieldType CXFA_FFCheckButton::GetFormFieldType() {
353   return FormFieldType::kXFA_CheckBox;
354 }
355