// 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/basewidget/fwl_spinbuttonimp.h" #include "xfa/fwl/basewidget/ifwl_spinbutton.h" #include "xfa/fwl/core/cfwl_message.h" #include "xfa/fwl/core/cfwl_themebackground.h" #include "xfa/fwl/core/cfwl_widgetimpproperties.h" #include "xfa/fwl/core/fwl_noteimp.h" #include "xfa/fwl/core/fwl_targetimp.h" #include "xfa/fwl/core/fwl_widgetimp.h" #include "xfa/fwl/core/ifwl_themeprovider.h" #include "xfa/fwl/core/ifwl_themeprovider.h" #include "xfa/fwl/core/ifwl_timer.h" #define FWL_SPN_MinWidth 18 #define FWL_SPN_MinHeight 32 #define FWL_SPIN_Elapse 200 // static IFWL_SpinButton* IFWL_SpinButton::Create( const CFWL_WidgetImpProperties& properties, IFWL_Widget* pOuter) { IFWL_SpinButton* pSpinButton = new IFWL_SpinButton; CFWL_SpinButtonImp* pSpinButtonImpl = new CFWL_SpinButtonImp(properties, nullptr); pSpinButton->SetImpl(pSpinButtonImpl); pSpinButtonImpl->SetInterface(pSpinButton); return pSpinButton; } IFWL_SpinButton::IFWL_SpinButton() {} FWL_ERR IFWL_SpinButton::EnableButton(FX_BOOL bEnable, FX_BOOL bUp) { return static_cast<CFWL_SpinButtonImp*>(GetImpl()) ->EnableButton(bEnable, bUp); } FX_BOOL IFWL_SpinButton::IsButtonEnable(FX_BOOL bUp) { return static_cast<CFWL_SpinButtonImp*>(GetImpl())->IsButtonEnable(bUp); } CFWL_SpinButtonImp::CFWL_SpinButtonImp( const CFWL_WidgetImpProperties& properties, IFWL_Widget* pOuter) : CFWL_WidgetImp(properties, pOuter), m_dwUpState(FWL_PARTSTATE_SPB_Normal), m_dwDnState(FWL_PARTSTATE_SPB_Normal), m_iButtonIndex(0), m_bLButtonDwn(FALSE), m_hTimer(NULL) { m_rtClient.Reset(); m_rtUpButton.Reset(); m_rtDnButton.Reset(); m_pProperties->m_dwStyleExes |= FWL_STYLEEXE_SPB_Vert; } CFWL_SpinButtonImp::~CFWL_SpinButtonImp() {} FWL_ERR CFWL_SpinButtonImp::GetClassName(CFX_WideString& wsClass) const { wsClass = FWL_CLASS_SpinButton; return FWL_ERR_Succeeded; } uint32_t CFWL_SpinButtonImp::GetClassID() const { return FWL_CLASSHASH_SpinButton; } FWL_ERR CFWL_SpinButtonImp::Initialize() { if (CFWL_WidgetImp::Initialize() != FWL_ERR_Succeeded) return FWL_ERR_Indefinite; m_pDelegate = new CFWL_SpinButtonImpDelegate(this); return FWL_ERR_Succeeded; } FWL_ERR CFWL_SpinButtonImp::Finalize() { delete m_pDelegate; m_pDelegate = nullptr; return CFWL_WidgetImp::Finalize(); } FWL_ERR CFWL_SpinButtonImp::GetWidgetRect(CFX_RectF& rect, FX_BOOL bAutoSize) { if (bAutoSize) { rect.Set(0, 0, FWL_SPN_MinWidth, FWL_SPN_MinHeight); CFWL_WidgetImp::GetWidgetRect(rect, TRUE); } else { rect = m_pProperties->m_rtWidget; } return FWL_ERR_Succeeded; } FWL_ERR CFWL_SpinButtonImp::Update() { if (IsLocked()) { return FWL_ERR_Indefinite; } GetClientRect(m_rtClient); if (m_pProperties->m_dwStyleExes & FWL_STYLEEXE_SPB_Vert) { m_rtUpButton.Set(m_rtClient.top, m_rtClient.left, m_rtClient.width, m_rtClient.height / 2); m_rtDnButton.Set(m_rtClient.left, m_rtClient.top + m_rtClient.height / 2, m_rtClient.width, m_rtClient.height / 2); } else { m_rtUpButton.Set(m_rtClient.left, m_rtClient.top, m_rtClient.width / 2, m_rtClient.height); m_rtDnButton.Set(m_rtClient.left + m_rtClient.width / 2, m_rtClient.top, m_rtClient.width / 2, m_rtClient.height); } return FWL_ERR_Succeeded; } uint32_t CFWL_SpinButtonImp::HitTest(FX_FLOAT fx, FX_FLOAT fy) { if (m_rtClient.Contains(fx, fy)) { return FWL_WGTHITTEST_Client; } if (HasBorder() && (m_rtClient.Contains(fx, fy))) { return FWL_WGTHITTEST_Border; } if (HasEdge()) { CFX_RectF rtEdge; GetEdgeRect(rtEdge); if (rtEdge.Contains(fx, fy)) { return FWL_PART_SPB_Edge; } } if (m_rtUpButton.Contains(fx, fy)) { return FWL_WGTHITTEST_SPB_UpButton; } if (m_rtDnButton.Contains(fx, fy)) { return FWL_WGTHITTEST_SPB_DownButton; } return FWL_WGTHITTEST_Unknown; } FWL_ERR CFWL_SpinButtonImp::DrawWidget(CFX_Graphics* pGraphics, const CFX_Matrix* pMatrix) { if (!pGraphics) return FWL_ERR_Indefinite; CFX_RectF rtClip(m_rtClient); if (pMatrix != NULL) { pMatrix->TransformRect(rtClip); } IFWL_ThemeProvider* pTheme = GetAvailableTheme(); if (HasBorder()) { DrawBorder(pGraphics, FWL_PART_SPB_Border, pTheme, pMatrix); } if (HasEdge()) { DrawEdge(pGraphics, FWL_PART_SPB_Edge, pTheme, pMatrix); } DrawUpButton(pGraphics, pTheme, pMatrix); DrawDownButton(pGraphics, pTheme, pMatrix); return FWL_ERR_Succeeded; } int32_t CFWL_SpinButtonImp::Run(FWL_HTIMER hTimer) { if (m_hTimer) { CFWL_EvtSpbClick wmPosChanged; wmPosChanged.m_pSrcTarget = m_pInterface; wmPosChanged.m_bUp = m_iButtonIndex == 0; DispatchEvent(&wmPosChanged); } return 1; } FWL_ERR CFWL_SpinButtonImp::EnableButton(FX_BOOL bEnable, FX_BOOL bUp) { if (bUp) { if (bEnable) { m_dwUpState = FWL_PARTSTATE_SPB_Normal; } else { m_dwUpState = FWL_PARTSTATE_SPB_Disabled; } } else { if (bEnable) { m_dwDnState = FWL_PARTSTATE_SPB_Normal; } else { m_dwDnState = FWL_PARTSTATE_SPB_Disabled; } } return FWL_ERR_Succeeded; } FX_BOOL CFWL_SpinButtonImp::IsButtonEnable(FX_BOOL bUp) { if (bUp) { return (m_dwUpState != FWL_PARTSTATE_SPB_Disabled); } return (m_dwDnState != FWL_PARTSTATE_SPB_Disabled); } void CFWL_SpinButtonImp::DrawUpButton(CFX_Graphics* pGraphics, IFWL_ThemeProvider* pTheme, const CFX_Matrix* pMatrix) { CFWL_ThemeBackground params; params.m_pWidget = m_pInterface; params.m_iPart = FWL_PART_SPB_UpButton; params.m_pGraphics = pGraphics; params.m_dwStates = m_dwUpState + 1; if (pMatrix) { params.m_matrix.Concat(*pMatrix); } params.m_rtPart = m_rtUpButton; pTheme->DrawBackground(¶ms); } void CFWL_SpinButtonImp::DrawDownButton(CFX_Graphics* pGraphics, IFWL_ThemeProvider* pTheme, const CFX_Matrix* pMatrix) { CFWL_ThemeBackground params; params.m_pWidget = m_pInterface; params.m_iPart = FWL_PART_SPB_DownButton; params.m_pGraphics = pGraphics; params.m_dwStates = m_dwDnState + 1; if (pMatrix) { params.m_matrix.Concat(*pMatrix); } params.m_rtPart = m_rtDnButton; pTheme->DrawBackground(¶ms); } CFWL_SpinButtonImpDelegate::CFWL_SpinButtonImpDelegate( CFWL_SpinButtonImp* pOwner) : m_pOwner(pOwner) {} int32_t CFWL_SpinButtonImpDelegate::OnProcessMessage(CFWL_Message* pMessage) { if (!pMessage) return 0; int32_t iRet = 1; uint32_t dwMsgCode = pMessage->GetClassID(); switch (dwMsgCode) { case FWL_MSGHASH_SetFocus: case FWL_MSGHASH_KillFocus: { OnFocusChanged(pMessage, dwMsgCode == FWL_MSGHASH_SetFocus); break; } case FWL_MSGHASH_Mouse: { CFWL_MsgMouse* pMsg = static_cast<CFWL_MsgMouse*>(pMessage); uint32_t dwCmd = pMsg->m_dwCmd; switch (dwCmd) { case FWL_MSGMOUSECMD_LButtonDown: { OnLButtonDown(pMsg); break; } case FWL_MSGMOUSECMD_LButtonUp: { OnLButtonUp(pMsg); break; } case FWL_MSGMOUSECMD_MouseMove: { OnMouseMove(pMsg); break; } case FWL_MSGMOUSECMD_MouseLeave: { OnMouseLeave(pMsg); break; } default: {} } break; } case FWL_MSGHASH_Key: { CFWL_MsgKey* pKey = static_cast<CFWL_MsgKey*>(pMessage); if (pKey->m_dwCmd == FWL_MSGKEYCMD_KeyDown) { OnKeyDown(pKey); } break; } default: { iRet = 0; break; } } CFWL_WidgetImpDelegate::OnProcessMessage(pMessage); return iRet; } FWL_ERR CFWL_SpinButtonImpDelegate::OnProcessEvent(CFWL_Event* pEvent) { return FWL_ERR_Succeeded; } FWL_ERR CFWL_SpinButtonImpDelegate::OnDrawWidget(CFX_Graphics* pGraphics, const CFX_Matrix* pMatrix) { return m_pOwner->DrawWidget(pGraphics, pMatrix); } void CFWL_SpinButtonImpDelegate::OnFocusChanged(CFWL_Message* pMsg, FX_BOOL bSet) { if (bSet) { m_pOwner->m_pProperties->m_dwStates |= (FWL_WGTSTATE_Focused); } else { m_pOwner->m_pProperties->m_dwStates &= ~(FWL_WGTSTATE_Focused); } m_pOwner->Repaint(&m_pOwner->m_rtClient); } void CFWL_SpinButtonImpDelegate::OnLButtonDown(CFWL_MsgMouse* pMsg) { m_pOwner->m_bLButtonDwn = TRUE; m_pOwner->SetGrab(TRUE); m_pOwner->SetFocus(TRUE); if (!m_pOwner->m_pProperties->m_pDataProvider) return; FX_BOOL bUpPress = (m_pOwner->m_rtUpButton.Contains(pMsg->m_fx, pMsg->m_fy) && m_pOwner->IsButtonEnable(TRUE)); FX_BOOL bDnPress = (m_pOwner->m_rtDnButton.Contains(pMsg->m_fx, pMsg->m_fy) && m_pOwner->IsButtonEnable(FALSE)); if (!bUpPress && !bDnPress) { return; } if (bUpPress) { m_pOwner->m_iButtonIndex = 0; m_pOwner->m_dwUpState = FWL_PARTSTATE_SPB_Pressed; } if (bDnPress) { m_pOwner->m_iButtonIndex = 1; m_pOwner->m_dwDnState = FWL_PARTSTATE_SPB_Pressed; } CFWL_EvtSpbClick wmPosChanged; wmPosChanged.m_pSrcTarget = m_pOwner->m_pInterface; wmPosChanged.m_bUp = bUpPress; m_pOwner->DispatchEvent(&wmPosChanged); m_pOwner->Repaint(bUpPress ? &m_pOwner->m_rtUpButton : &m_pOwner->m_rtDnButton); m_pOwner->m_hTimer = FWL_StartTimer(m_pOwner, FWL_SPIN_Elapse); } void CFWL_SpinButtonImpDelegate::OnLButtonUp(CFWL_MsgMouse* pMsg) { if (m_pOwner->m_pProperties->m_dwStates & FWL_PARTSTATE_SPB_Disabled) { return; } m_pOwner->m_bLButtonDwn = FALSE; m_pOwner->SetGrab(FALSE); m_pOwner->SetFocus(FALSE); if (m_pOwner->m_hTimer) { FWL_StopTimer(m_pOwner->m_hTimer); m_pOwner->m_hTimer = NULL; } FX_BOOL bRepaint = FALSE; CFX_RectF rtInvalidate; if (m_pOwner->m_dwUpState == FWL_PARTSTATE_SPB_Pressed && m_pOwner->IsButtonEnable(TRUE)) { m_pOwner->m_dwUpState = FWL_PARTSTATE_SPB_Normal; bRepaint = TRUE; rtInvalidate = m_pOwner->m_rtUpButton; } else if (m_pOwner->m_dwDnState == FWL_PARTSTATE_SPB_Pressed && m_pOwner->IsButtonEnable(FALSE)) { m_pOwner->m_dwDnState = FWL_PARTSTATE_SPB_Normal; bRepaint = TRUE; rtInvalidate = m_pOwner->m_rtDnButton; } if (bRepaint) { m_pOwner->Repaint(&rtInvalidate); } } void CFWL_SpinButtonImpDelegate::OnMouseMove(CFWL_MsgMouse* pMsg) { if (!m_pOwner->m_pProperties->m_pDataProvider) return; if (m_pOwner->m_bLButtonDwn) { return; } FX_BOOL bRepaint = FALSE; CFX_RectF rtInvlidate; rtInvlidate.Reset(); if (m_pOwner->m_rtUpButton.Contains(pMsg->m_fx, pMsg->m_fy)) { if (m_pOwner->IsButtonEnable(TRUE)) { if (m_pOwner->m_dwUpState == FWL_PARTSTATE_SPB_Hovered) { m_pOwner->m_dwUpState = FWL_PARTSTATE_SPB_Hovered; bRepaint = TRUE; rtInvlidate = m_pOwner->m_rtUpButton; } if (m_pOwner->m_dwDnState != FWL_PARTSTATE_SPB_Normal && m_pOwner->IsButtonEnable(FALSE)) { m_pOwner->m_dwDnState = FWL_PARTSTATE_SPB_Normal; if (bRepaint) { rtInvlidate.Union(m_pOwner->m_rtDnButton); } else { rtInvlidate = m_pOwner->m_rtDnButton; } bRepaint = TRUE; } } if (!m_pOwner->IsButtonEnable(FALSE)) { m_pOwner->EnableButton(FALSE, FALSE); } } else if (m_pOwner->m_rtDnButton.Contains(pMsg->m_fx, pMsg->m_fy)) { if (m_pOwner->IsButtonEnable(FALSE)) { if (m_pOwner->m_dwDnState != FWL_PARTSTATE_SPB_Hovered) { m_pOwner->m_dwDnState = FWL_PARTSTATE_SPB_Hovered; bRepaint = TRUE; rtInvlidate = m_pOwner->m_rtDnButton; } if (m_pOwner->m_dwUpState != FWL_PARTSTATE_SPB_Normal && m_pOwner->IsButtonEnable(TRUE)) { m_pOwner->m_dwUpState = FWL_PARTSTATE_SPB_Normal; if (bRepaint) { rtInvlidate.Union(m_pOwner->m_rtUpButton); } else { rtInvlidate = m_pOwner->m_rtUpButton; } bRepaint = TRUE; } } } else if (m_pOwner->m_dwUpState != FWL_PARTSTATE_SPB_Normal || m_pOwner->m_dwDnState != FWL_PARTSTATE_SPB_Normal) { if (m_pOwner->m_dwUpState != FWL_PARTSTATE_SPB_Normal) { m_pOwner->m_dwUpState = FWL_PARTSTATE_SPB_Normal; bRepaint = TRUE; rtInvlidate = m_pOwner->m_rtUpButton; } if (m_pOwner->m_dwDnState != FWL_PARTSTATE_SPB_Normal) { m_pOwner->m_dwDnState = FWL_PARTSTATE_SPB_Normal; if (bRepaint) { rtInvlidate.Union(m_pOwner->m_rtDnButton); } else { rtInvlidate = m_pOwner->m_rtDnButton; } bRepaint = TRUE; } } if (bRepaint) { m_pOwner->Repaint(&rtInvlidate); } } void CFWL_SpinButtonImpDelegate::OnMouseLeave(CFWL_MsgMouse* pMsg) { if (!pMsg) return; if (m_pOwner->m_dwUpState != FWL_PARTSTATE_SPB_Normal && m_pOwner->IsButtonEnable(TRUE)) { m_pOwner->m_dwUpState = FWL_PARTSTATE_SPB_Normal; } if (m_pOwner->m_dwDnState != FWL_PARTSTATE_SPB_Normal && m_pOwner->IsButtonEnable(FALSE)) { m_pOwner->m_dwDnState = FWL_PARTSTATE_SPB_Normal; } m_pOwner->Repaint(&m_pOwner->m_rtClient); } void CFWL_SpinButtonImpDelegate::OnKeyDown(CFWL_MsgKey* pMsg) { if (!m_pOwner->m_pProperties->m_pDataProvider) return; FX_BOOL bUp = pMsg->m_dwKeyCode == FWL_VKEY_Up || pMsg->m_dwKeyCode == FWL_VKEY_Left; FX_BOOL bDown = pMsg->m_dwKeyCode == FWL_VKEY_Down || pMsg->m_dwKeyCode == FWL_VKEY_Right; if (!bUp && !bDown) { return; } FX_BOOL bUpEnable = m_pOwner->IsButtonEnable(TRUE); FX_BOOL bDownEnable = m_pOwner->IsButtonEnable(FALSE); if (!bUpEnable && !bDownEnable) { return; } CFWL_EvtSpbClick wmPosChanged; wmPosChanged.m_pSrcTarget = m_pOwner->m_pInterface; wmPosChanged.m_bUp = bUpEnable; m_pOwner->DispatchEvent(&wmPosChanged); m_pOwner->Repaint(bUpEnable ? &m_pOwner->m_rtUpButton : &m_pOwner->m_rtDnButton); }