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