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