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