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