| // Copyright 2014 PDFium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com | 
 |  | 
 | #include "xfa/fwl/cfwl_checkbox.h" | 
 |  | 
 | #include <algorithm> | 
 | #include <utility> | 
 |  | 
 | #include "xfa/fde/cfde_textout.h" | 
 | #include "xfa/fwl/cfwl_app.h" | 
 | #include "xfa/fwl/cfwl_event.h" | 
 | #include "xfa/fwl/cfwl_messagekey.h" | 
 | #include "xfa/fwl/cfwl_messagemouse.h" | 
 | #include "xfa/fwl/cfwl_notedriver.h" | 
 | #include "xfa/fwl/cfwl_themebackground.h" | 
 | #include "xfa/fwl/cfwl_themetext.h" | 
 | #include "xfa/fwl/cfwl_widgetmgr.h" | 
 | #include "xfa/fwl/fwl_widgetdef.h" | 
 | #include "xfa/fwl/ifwl_themeprovider.h" | 
 |  | 
 | namespace { | 
 |  | 
 | const int kCaptionMargin = 5; | 
 |  | 
 | }  // namespace | 
 |  | 
 | CFWL_CheckBox::CFWL_CheckBox(CFWL_App* app) | 
 |     : CFWL_Widget(app, Properties(), nullptr) { | 
 |   m_TTOStyles.single_line_ = true; | 
 | } | 
 |  | 
 | CFWL_CheckBox::~CFWL_CheckBox() = default; | 
 |  | 
 | FWL_Type CFWL_CheckBox::GetClassID() const { | 
 |   return FWL_Type::CheckBox; | 
 | } | 
 |  | 
 | void CFWL_CheckBox::SetBoxSize(float fHeight) { | 
 |   m_fBoxHeight = fHeight; | 
 | } | 
 |  | 
 | void CFWL_CheckBox::Update() { | 
 |   if (IsLocked()) | 
 |     return; | 
 |  | 
 |   UpdateTextOutStyles(); | 
 |   Layout(); | 
 | } | 
 |  | 
 | void CFWL_CheckBox::DrawWidget(CFGAS_GEGraphics* pGraphics, | 
 |                                const CFX_Matrix& matrix) { | 
 |   if (!pGraphics) | 
 |     return; | 
 |  | 
 |   if (HasBorder()) | 
 |     DrawBorder(pGraphics, CFWL_Part::Border, matrix); | 
 |  | 
 |   int32_t dwStates = GetPartStates(); | 
 |   CFWL_ThemeBackground param; | 
 |   param.m_pWidget = this; | 
 |   param.m_iPart = CFWL_Part::Background; | 
 |   param.m_dwStates = dwStates; | 
 |   param.m_pGraphics = pGraphics; | 
 |   param.m_matrix.Concat(matrix); | 
 |   param.m_PartRect = m_ClientRect; | 
 |   if (m_Properties.m_dwStates & FWL_WGTSTATE_Focused) | 
 |     param.m_pRtData = &m_FocusRect; | 
 |  | 
 |   IFWL_ThemeProvider* pTheme = GetThemeProvider(); | 
 |   pTheme->DrawBackground(param); | 
 |  | 
 |   param.m_iPart = CFWL_Part::CheckBox; | 
 |   param.m_PartRect = m_BoxRect; | 
 |   pTheme->DrawBackground(param); | 
 |  | 
 |   CFWL_ThemeText textParam; | 
 |   textParam.m_pWidget = this; | 
 |   textParam.m_iPart = CFWL_Part::Caption; | 
 |   textParam.m_dwStates = dwStates; | 
 |   textParam.m_pGraphics = pGraphics; | 
 |   textParam.m_matrix.Concat(matrix); | 
 |   textParam.m_PartRect = m_CaptionRect; | 
 |   textParam.m_wsText = L"Check box"; | 
 |   textParam.m_dwTTOStyles = m_TTOStyles; | 
 |   textParam.m_iTTOAlign = m_iTTOAlign; | 
 |   pTheme->DrawText(textParam); | 
 | } | 
 |  | 
 | void CFWL_CheckBox::SetCheckState(int32_t iCheck) { | 
 |   m_Properties.m_dwStates &= ~FWL_STATE_CKB_CheckMask; | 
 |   switch (iCheck) { | 
 |     case 1: | 
 |       m_Properties.m_dwStates |= FWL_STATE_CKB_Checked; | 
 |       break; | 
 |     case 2: | 
 |       if (m_Properties.m_dwStyleExes & FWL_STYLEEXT_CKB_3State) | 
 |         m_Properties.m_dwStates |= FWL_STATE_CKB_Neutral; | 
 |       break; | 
 |     default: | 
 |       break; | 
 |   } | 
 |   RepaintRect(m_ClientRect); | 
 | } | 
 |  | 
 | void CFWL_CheckBox::Layout() { | 
 |   m_WidgetRect.width = FXSYS_roundf(m_WidgetRect.width); | 
 |   m_WidgetRect.height = FXSYS_roundf(m_WidgetRect.height); | 
 |   m_ClientRect = GetClientRect(); | 
 |  | 
 |   float fTextLeft = m_ClientRect.left + m_fBoxHeight; | 
 |   m_BoxRect = CFX_RectF(m_ClientRect.TopLeft(), m_fBoxHeight, m_fBoxHeight); | 
 |   m_CaptionRect = | 
 |       CFX_RectF(fTextLeft, m_ClientRect.top, m_ClientRect.right() - fTextLeft, | 
 |                 m_ClientRect.height); | 
 |   m_CaptionRect.Inflate(-kCaptionMargin, -kCaptionMargin); | 
 |  | 
 |   CFX_RectF rtFocus = m_CaptionRect; | 
 |   CalcTextRect(L"Check box", m_TTOStyles, m_iTTOAlign, &rtFocus); | 
 |   m_FocusRect = CFX_RectF(m_CaptionRect.TopLeft(), | 
 |                           std::max(m_CaptionRect.width, rtFocus.width), | 
 |                           std::min(m_CaptionRect.height, rtFocus.height)); | 
 |   m_FocusRect.Inflate(1, 1); | 
 | } | 
 |  | 
 | uint32_t CFWL_CheckBox::GetPartStates() const { | 
 |   int32_t dwStates = CFWL_PartState_Normal; | 
 |   if ((m_Properties.m_dwStates & FWL_STATE_CKB_CheckMask) == | 
 |       FWL_STATE_CKB_Neutral) { | 
 |     dwStates = CFWL_PartState_Neutral; | 
 |   } else if ((m_Properties.m_dwStates & FWL_STATE_CKB_CheckMask) == | 
 |              FWL_STATE_CKB_Checked) { | 
 |     dwStates = CFWL_PartState_Checked; | 
 |   } | 
 |   if (m_Properties.m_dwStates & FWL_WGTSTATE_Disabled) | 
 |     dwStates |= CFWL_PartState_Disabled; | 
 |   else if (m_Properties.m_dwStates & FWL_STATE_CKB_Hovered) | 
 |     dwStates |= CFWL_PartState_Hovered; | 
 |   else if (m_Properties.m_dwStates & FWL_STATE_CKB_Pressed) | 
 |     dwStates |= CFWL_PartState_Pressed; | 
 |   else | 
 |     dwStates |= CFWL_PartState_Normal; | 
 |   if (m_Properties.m_dwStates & FWL_WGTSTATE_Focused) | 
 |     dwStates |= CFWL_PartState_Focused; | 
 |   return dwStates; | 
 | } | 
 |  | 
 | void CFWL_CheckBox::UpdateTextOutStyles() { | 
 |   m_iTTOAlign = FDE_TextAlignment::kTopLeft; | 
 |   m_TTOStyles.Reset(); | 
 |   m_TTOStyles.single_line_ = true; | 
 | } | 
 |  | 
 | void CFWL_CheckBox::NextStates() { | 
 |   uint32_t dwFirststate = m_Properties.m_dwStates; | 
 |   if (m_Properties.m_dwStyleExes & FWL_STYLEEXT_CKB_RadioButton) { | 
 |     if ((m_Properties.m_dwStates & FWL_STATE_CKB_CheckMask) == | 
 |         FWL_STATE_CKB_Unchecked) { | 
 |       m_Properties.m_dwStates |= FWL_STATE_CKB_Checked; | 
 |     } | 
 |   } else { | 
 |     if ((m_Properties.m_dwStates & FWL_STATE_CKB_CheckMask) == | 
 |         FWL_STATE_CKB_Neutral) { | 
 |       m_Properties.m_dwStates &= ~FWL_STATE_CKB_CheckMask; | 
 |       if (m_Properties.m_dwStyleExes & FWL_STYLEEXT_CKB_3State) | 
 |         m_Properties.m_dwStates |= FWL_STATE_CKB_Checked; | 
 |     } else if ((m_Properties.m_dwStates & FWL_STATE_CKB_CheckMask) == | 
 |                FWL_STATE_CKB_Checked) { | 
 |       m_Properties.m_dwStates &= ~FWL_STATE_CKB_CheckMask; | 
 |     } else { | 
 |       if (m_Properties.m_dwStyleExes & FWL_STYLEEXT_CKB_3State) | 
 |         m_Properties.m_dwStates |= FWL_STATE_CKB_Neutral; | 
 |       else | 
 |         m_Properties.m_dwStates |= FWL_STATE_CKB_Checked; | 
 |     } | 
 |   } | 
 |  | 
 |   RepaintRect(m_ClientRect); | 
 |   if (dwFirststate == m_Properties.m_dwStates) | 
 |     return; | 
 |  | 
 |   CFWL_Event wmCheckBoxState(CFWL_Event::Type::CheckStateChanged, this); | 
 |   DispatchEvent(&wmCheckBoxState); | 
 | } | 
 |  | 
 | void CFWL_CheckBox::OnProcessMessage(CFWL_Message* pMessage) { | 
 |   switch (pMessage->GetType()) { | 
 |     case CFWL_Message::Type::kSetFocus: | 
 |       OnFocusChanged(true); | 
 |       break; | 
 |     case CFWL_Message::Type::kKillFocus: | 
 |       OnFocusChanged(false); | 
 |       break; | 
 |     case CFWL_Message::Type::kMouse: { | 
 |       CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage); | 
 |       switch (pMsg->m_dwCmd) { | 
 |         case FWL_MouseCommand::LeftButtonDown: | 
 |           OnLButtonDown(); | 
 |           break; | 
 |         case FWL_MouseCommand::LeftButtonUp: | 
 |           OnLButtonUp(pMsg); | 
 |           break; | 
 |         case FWL_MouseCommand::Move: | 
 |           OnMouseMove(pMsg); | 
 |           break; | 
 |         case FWL_MouseCommand::Leave: | 
 |           OnMouseLeave(); | 
 |           break; | 
 |         default: | 
 |           break; | 
 |       } | 
 |       break; | 
 |     } | 
 |     case CFWL_Message::Type::kKey: { | 
 |       CFWL_MessageKey* pKey = static_cast<CFWL_MessageKey*>(pMessage); | 
 |       if (pKey->m_dwCmd == CFWL_MessageKey::Type::kKeyDown) | 
 |         OnKeyDown(pKey); | 
 |       break; | 
 |     } | 
 |     default: | 
 |       break; | 
 |   } | 
 |   // Dst target could be |this|, continue only if not destroyed by above. | 
 |   if (pMessage->GetDstTarget()) | 
 |     CFWL_Widget::OnProcessMessage(pMessage); | 
 | } | 
 |  | 
 | void CFWL_CheckBox::OnDrawWidget(CFGAS_GEGraphics* pGraphics, | 
 |                                  const CFX_Matrix& matrix) { | 
 |   DrawWidget(pGraphics, matrix); | 
 | } | 
 |  | 
 | void CFWL_CheckBox::OnFocusChanged(bool bSet) { | 
 |   if (bSet) | 
 |     m_Properties.m_dwStates |= FWL_WGTSTATE_Focused; | 
 |   else | 
 |     m_Properties.m_dwStates &= ~FWL_WGTSTATE_Focused; | 
 |  | 
 |   RepaintRect(m_ClientRect); | 
 | } | 
 |  | 
 | void CFWL_CheckBox::OnLButtonDown() { | 
 |   if (m_Properties.m_dwStates & FWL_WGTSTATE_Disabled) | 
 |     return; | 
 |  | 
 |   m_bBtnDown = true; | 
 |   m_Properties.m_dwStates &= ~FWL_STATE_CKB_Hovered; | 
 |   m_Properties.m_dwStates |= FWL_STATE_CKB_Pressed; | 
 |   RepaintRect(m_ClientRect); | 
 | } | 
 |  | 
 | void CFWL_CheckBox::OnLButtonUp(CFWL_MessageMouse* pMsg) { | 
 |   if (!m_bBtnDown) | 
 |     return; | 
 |  | 
 |   m_bBtnDown = false; | 
 |   if (!m_ClientRect.Contains(pMsg->m_pos)) | 
 |     return; | 
 |  | 
 |   m_Properties.m_dwStates |= FWL_STATE_CKB_Hovered; | 
 |   m_Properties.m_dwStates &= ~FWL_STATE_CKB_Pressed; | 
 |   NextStates(); | 
 | } | 
 |  | 
 | void CFWL_CheckBox::OnMouseMove(CFWL_MessageMouse* pMsg) { | 
 |   if (m_Properties.m_dwStates & FWL_WGTSTATE_Disabled) | 
 |     return; | 
 |  | 
 |   bool bRepaint = false; | 
 |   if (m_bBtnDown) { | 
 |     if (m_ClientRect.Contains(pMsg->m_pos)) { | 
 |       if ((m_Properties.m_dwStates & FWL_STATE_CKB_Pressed) == 0) { | 
 |         bRepaint = true; | 
 |         m_Properties.m_dwStates |= FWL_STATE_CKB_Pressed; | 
 |       } | 
 |       if ((m_Properties.m_dwStates & FWL_STATE_CKB_Hovered)) { | 
 |         bRepaint = true; | 
 |         m_Properties.m_dwStates &= ~FWL_STATE_CKB_Hovered; | 
 |       } | 
 |     } else { | 
 |       if (m_Properties.m_dwStates & FWL_STATE_CKB_Pressed) { | 
 |         bRepaint = true; | 
 |         m_Properties.m_dwStates &= ~FWL_STATE_CKB_Pressed; | 
 |       } | 
 |       if ((m_Properties.m_dwStates & FWL_STATE_CKB_Hovered) == 0) { | 
 |         bRepaint = true; | 
 |         m_Properties.m_dwStates |= FWL_STATE_CKB_Hovered; | 
 |       } | 
 |     } | 
 |   } else { | 
 |     if (m_ClientRect.Contains(pMsg->m_pos)) { | 
 |       if ((m_Properties.m_dwStates & FWL_STATE_CKB_Hovered) == 0) { | 
 |         bRepaint = true; | 
 |         m_Properties.m_dwStates |= FWL_STATE_CKB_Hovered; | 
 |       } | 
 |     } | 
 |   } | 
 |   if (bRepaint) | 
 |     RepaintRect(m_BoxRect); | 
 | } | 
 |  | 
 | void CFWL_CheckBox::OnMouseLeave() { | 
 |   if (m_bBtnDown) | 
 |     m_Properties.m_dwStates |= FWL_STATE_CKB_Hovered; | 
 |   else | 
 |     m_Properties.m_dwStates &= ~FWL_STATE_CKB_Hovered; | 
 |  | 
 |   RepaintRect(m_BoxRect); | 
 | } | 
 |  | 
 | void CFWL_CheckBox::OnKeyDown(CFWL_MessageKey* pMsg) { | 
 |   if (pMsg->m_dwKeyCode == XFA_FWL_VKEY_Tab) | 
 |     return; | 
 |   if (pMsg->m_dwKeyCode == XFA_FWL_VKEY_Return || | 
 |       pMsg->m_dwKeyCode == XFA_FWL_VKEY_Space) { | 
 |     NextStates(); | 
 |   } | 
 | } |