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