1 // Copyright 2014 The PDFium Authors
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/fwl/cfwl_checkbox.h"
8
9 #include <algorithm>
10 #include <utility>
11
12 #include "xfa/fde/cfde_textout.h"
13 #include "xfa/fwl/cfwl_app.h"
14 #include "xfa/fwl/cfwl_event.h"
15 #include "xfa/fwl/cfwl_messagekey.h"
16 #include "xfa/fwl/cfwl_messagemouse.h"
17 #include "xfa/fwl/cfwl_notedriver.h"
18 #include "xfa/fwl/cfwl_themebackground.h"
19 #include "xfa/fwl/cfwl_themetext.h"
20 #include "xfa/fwl/cfwl_widgetmgr.h"
21 #include "xfa/fwl/fwl_widgetdef.h"
22 #include "xfa/fwl/ifwl_themeprovider.h"
23
24 namespace {
25
26 const int kCaptionMargin = 5;
27
28 } // namespace
29
CFWL_CheckBox(CFWL_App * app)30 CFWL_CheckBox::CFWL_CheckBox(CFWL_App* app)
31 : CFWL_Widget(app, Properties(), nullptr) {
32 m_TTOStyles.single_line_ = true;
33 }
34
35 CFWL_CheckBox::~CFWL_CheckBox() = default;
36
GetClassID() const37 FWL_Type CFWL_CheckBox::GetClassID() const {
38 return FWL_Type::CheckBox;
39 }
40
SetBoxSize(float fHeight)41 void CFWL_CheckBox::SetBoxSize(float fHeight) {
42 m_fBoxHeight = fHeight;
43 }
44
Update()45 void CFWL_CheckBox::Update() {
46 if (IsLocked())
47 return;
48
49 UpdateTextOutStyles();
50 Layout();
51 }
52
DrawWidget(CFGAS_GEGraphics * pGraphics,const CFX_Matrix & matrix)53 void CFWL_CheckBox::DrawWidget(CFGAS_GEGraphics* pGraphics,
54 const CFX_Matrix& matrix) {
55 if (!pGraphics)
56 return;
57
58 if (HasBorder())
59 DrawBorder(pGraphics, CFWL_ThemePart::Part::kBorder, matrix);
60
61 Mask<CFWL_PartState> dwStates = GetPartStates();
62 IFWL_ThemeProvider* pTheme = GetThemeProvider();
63 CFWL_ThemeBackground param(CFWL_ThemePart::Part::kBackground, this,
64 pGraphics);
65 param.m_dwStates = dwStates;
66 param.m_matrix = matrix;
67 param.m_PartRect = m_ClientRect;
68 if (m_Properties.m_dwStates & FWL_STATE_WGT_Focused)
69 param.m_pRtData = &m_FocusRect;
70 pTheme->DrawBackground(param);
71
72 CFWL_ThemeBackground checkParam(CFWL_ThemePart::Part::kCheckBox, this,
73 pGraphics);
74 checkParam.m_dwStates = dwStates;
75 checkParam.m_matrix = matrix;
76 checkParam.m_PartRect = m_BoxRect;
77 if (m_Properties.m_dwStates & FWL_STATE_WGT_Focused)
78 checkParam.m_pRtData = &m_FocusRect;
79 pTheme->DrawBackground(checkParam);
80
81 CFWL_ThemeText textParam(CFWL_ThemePart::Part::kCaption, this, pGraphics);
82 textParam.m_dwStates = dwStates;
83 textParam.m_matrix = matrix;
84 textParam.m_PartRect = m_CaptionRect;
85 textParam.m_wsText = L"Check box";
86 textParam.m_dwTTOStyles = m_TTOStyles;
87 textParam.m_iTTOAlign = m_iTTOAlign;
88 pTheme->DrawText(textParam);
89 }
90
SetCheckState(int32_t iCheck)91 void CFWL_CheckBox::SetCheckState(int32_t iCheck) {
92 m_Properties.m_dwStates &= ~FWL_STATE_CKB_CheckMask;
93 switch (iCheck) {
94 case 1:
95 m_Properties.m_dwStates |= FWL_STATE_CKB_Checked;
96 break;
97 case 2:
98 if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_CKB_3State)
99 m_Properties.m_dwStates |= FWL_STATE_CKB_Neutral;
100 break;
101 default:
102 break;
103 }
104 RepaintRect(m_ClientRect);
105 }
106
Layout()107 void CFWL_CheckBox::Layout() {
108 m_WidgetRect.width = FXSYS_roundf(m_WidgetRect.width);
109 m_WidgetRect.height = FXSYS_roundf(m_WidgetRect.height);
110 m_ClientRect = GetClientRect();
111
112 float fTextLeft = m_ClientRect.left + m_fBoxHeight;
113 m_BoxRect = CFX_RectF(m_ClientRect.TopLeft(), m_fBoxHeight, m_fBoxHeight);
114 m_CaptionRect =
115 CFX_RectF(fTextLeft, m_ClientRect.top, m_ClientRect.right() - fTextLeft,
116 m_ClientRect.height);
117 m_CaptionRect.Inflate(-kCaptionMargin, -kCaptionMargin);
118
119 CFX_RectF rtFocus = m_CaptionRect;
120 CalcTextRect(L"Check box", m_TTOStyles, m_iTTOAlign, &rtFocus);
121 m_FocusRect = CFX_RectF(m_CaptionRect.TopLeft(),
122 std::max(m_CaptionRect.width, rtFocus.width),
123 std::min(m_CaptionRect.height, rtFocus.height));
124 m_FocusRect.Inflate(1, 1);
125 }
126
GetPartStates() const127 Mask<CFWL_PartState> CFWL_CheckBox::GetPartStates() const {
128 Mask<CFWL_PartState> dwStates = CFWL_PartState::kNormal;
129 if ((m_Properties.m_dwStates & FWL_STATE_CKB_CheckMask) ==
130 FWL_STATE_CKB_Neutral) {
131 dwStates = CFWL_PartState::kNeutral;
132 } else if ((m_Properties.m_dwStates & FWL_STATE_CKB_CheckMask) ==
133 FWL_STATE_CKB_Checked) {
134 dwStates = CFWL_PartState::kChecked;
135 }
136 if (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
137 dwStates |= CFWL_PartState::kDisabled;
138 else if (m_Properties.m_dwStates & FWL_STATE_CKB_Hovered)
139 dwStates |= CFWL_PartState::kHovered;
140 else if (m_Properties.m_dwStates & FWL_STATE_CKB_Pressed)
141 dwStates |= CFWL_PartState::kPressed;
142 else
143 dwStates |= CFWL_PartState::kNormal;
144 if (m_Properties.m_dwStates & FWL_STATE_WGT_Focused)
145 dwStates |= CFWL_PartState::kFocused;
146 return dwStates;
147 }
148
UpdateTextOutStyles()149 void CFWL_CheckBox::UpdateTextOutStyles() {
150 m_iTTOAlign = FDE_TextAlignment::kTopLeft;
151 m_TTOStyles.Reset();
152 m_TTOStyles.single_line_ = true;
153 }
154
NextStates()155 void CFWL_CheckBox::NextStates() {
156 uint32_t dwFirststate = m_Properties.m_dwStates;
157 if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_CKB_RadioButton) {
158 if ((m_Properties.m_dwStates & FWL_STATE_CKB_CheckMask) ==
159 FWL_STATE_CKB_Unchecked) {
160 m_Properties.m_dwStates |= FWL_STATE_CKB_Checked;
161 }
162 } else {
163 if ((m_Properties.m_dwStates & FWL_STATE_CKB_CheckMask) ==
164 FWL_STATE_CKB_Neutral) {
165 m_Properties.m_dwStates &= ~FWL_STATE_CKB_CheckMask;
166 if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_CKB_3State)
167 m_Properties.m_dwStates |= FWL_STATE_CKB_Checked;
168 } else if ((m_Properties.m_dwStates & FWL_STATE_CKB_CheckMask) ==
169 FWL_STATE_CKB_Checked) {
170 m_Properties.m_dwStates &= ~FWL_STATE_CKB_CheckMask;
171 } else {
172 if (m_Properties.m_dwStyleExts & FWL_STYLEEXT_CKB_3State)
173 m_Properties.m_dwStates |= FWL_STATE_CKB_Neutral;
174 else
175 m_Properties.m_dwStates |= FWL_STATE_CKB_Checked;
176 }
177 }
178
179 RepaintRect(m_ClientRect);
180 if (dwFirststate == m_Properties.m_dwStates)
181 return;
182
183 CFWL_Event wmCheckBoxState(CFWL_Event::Type::CheckStateChanged, this);
184 DispatchEvent(&wmCheckBoxState);
185 }
186
OnProcessMessage(CFWL_Message * pMessage)187 void CFWL_CheckBox::OnProcessMessage(CFWL_Message* pMessage) {
188 switch (pMessage->GetType()) {
189 case CFWL_Message::Type::kSetFocus:
190 OnFocusGained();
191 break;
192 case CFWL_Message::Type::kKillFocus:
193 OnFocusLost();
194 break;
195 case CFWL_Message::Type::kMouse: {
196 CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
197 switch (pMsg->m_dwCmd) {
198 case CFWL_MessageMouse::MouseCommand::kLeftButtonDown:
199 OnLButtonDown();
200 break;
201 case CFWL_MessageMouse::MouseCommand::kLeftButtonUp:
202 OnLButtonUp(pMsg);
203 break;
204 case CFWL_MessageMouse::MouseCommand::kMove:
205 OnMouseMove(pMsg);
206 break;
207 case CFWL_MessageMouse::MouseCommand::kLeave:
208 OnMouseLeave();
209 break;
210 default:
211 break;
212 }
213 break;
214 }
215 case CFWL_Message::Type::kKey: {
216 CFWL_MessageKey* pKey = static_cast<CFWL_MessageKey*>(pMessage);
217 if (pKey->m_dwCmd == CFWL_MessageKey::KeyCommand::kKeyDown)
218 OnKeyDown(pKey);
219 break;
220 }
221 default:
222 break;
223 }
224 // Dst target could be |this|, continue only if not destroyed by above.
225 if (pMessage->GetDstTarget())
226 CFWL_Widget::OnProcessMessage(pMessage);
227 }
228
OnDrawWidget(CFGAS_GEGraphics * pGraphics,const CFX_Matrix & matrix)229 void CFWL_CheckBox::OnDrawWidget(CFGAS_GEGraphics* pGraphics,
230 const CFX_Matrix& matrix) {
231 DrawWidget(pGraphics, matrix);
232 }
233
OnFocusGained()234 void CFWL_CheckBox::OnFocusGained() {
235 m_Properties.m_dwStates |= FWL_STATE_WGT_Focused;
236 RepaintRect(m_ClientRect);
237 }
238
OnFocusLost()239 void CFWL_CheckBox::OnFocusLost() {
240 m_Properties.m_dwStates &= ~FWL_STATE_WGT_Focused;
241 RepaintRect(m_ClientRect);
242 }
243
OnLButtonDown()244 void CFWL_CheckBox::OnLButtonDown() {
245 if (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
246 return;
247
248 m_bBtnDown = true;
249 m_Properties.m_dwStates &= ~FWL_STATE_CKB_Hovered;
250 m_Properties.m_dwStates |= FWL_STATE_CKB_Pressed;
251 RepaintRect(m_ClientRect);
252 }
253
OnLButtonUp(CFWL_MessageMouse * pMsg)254 void CFWL_CheckBox::OnLButtonUp(CFWL_MessageMouse* pMsg) {
255 if (!m_bBtnDown)
256 return;
257
258 m_bBtnDown = false;
259 if (!m_ClientRect.Contains(pMsg->m_pos))
260 return;
261
262 m_Properties.m_dwStates |= FWL_STATE_CKB_Hovered;
263 m_Properties.m_dwStates &= ~FWL_STATE_CKB_Pressed;
264 NextStates();
265 }
266
OnMouseMove(CFWL_MessageMouse * pMsg)267 void CFWL_CheckBox::OnMouseMove(CFWL_MessageMouse* pMsg) {
268 if (m_Properties.m_dwStates & FWL_STATE_WGT_Disabled)
269 return;
270
271 bool bRepaint = false;
272 if (m_bBtnDown) {
273 if (m_ClientRect.Contains(pMsg->m_pos)) {
274 if ((m_Properties.m_dwStates & FWL_STATE_CKB_Pressed) == 0) {
275 bRepaint = true;
276 m_Properties.m_dwStates |= FWL_STATE_CKB_Pressed;
277 }
278 if ((m_Properties.m_dwStates & FWL_STATE_CKB_Hovered)) {
279 bRepaint = true;
280 m_Properties.m_dwStates &= ~FWL_STATE_CKB_Hovered;
281 }
282 } else {
283 if (m_Properties.m_dwStates & FWL_STATE_CKB_Pressed) {
284 bRepaint = true;
285 m_Properties.m_dwStates &= ~FWL_STATE_CKB_Pressed;
286 }
287 if ((m_Properties.m_dwStates & FWL_STATE_CKB_Hovered) == 0) {
288 bRepaint = true;
289 m_Properties.m_dwStates |= FWL_STATE_CKB_Hovered;
290 }
291 }
292 } else {
293 if (m_ClientRect.Contains(pMsg->m_pos)) {
294 if ((m_Properties.m_dwStates & FWL_STATE_CKB_Hovered) == 0) {
295 bRepaint = true;
296 m_Properties.m_dwStates |= FWL_STATE_CKB_Hovered;
297 }
298 }
299 }
300 if (bRepaint)
301 RepaintRect(m_BoxRect);
302 }
303
OnMouseLeave()304 void CFWL_CheckBox::OnMouseLeave() {
305 if (m_bBtnDown)
306 m_Properties.m_dwStates |= FWL_STATE_CKB_Hovered;
307 else
308 m_Properties.m_dwStates &= ~FWL_STATE_CKB_Hovered;
309
310 RepaintRect(m_BoxRect);
311 }
312
OnKeyDown(CFWL_MessageKey * pMsg)313 void CFWL_CheckBox::OnKeyDown(CFWL_MessageKey* pMsg) {
314 if (pMsg->m_dwKeyCodeOrChar == XFA_FWL_VKEY_Tab)
315 return;
316 if (pMsg->m_dwKeyCodeOrChar == XFA_FWL_VKEY_Return ||
317 pMsg->m_dwKeyCodeOrChar == XFA_FWL_VKEY_Space) {
318 NextStates();
319 }
320 }
321