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_pushbutton.h"
8 
9 #include <memory>
10 #include <utility>
11 
12 #include "third_party/base/ptr_util.h"
13 #include "xfa/fde/cfde_textout.h"
14 #include "xfa/fwl/cfwl_event.h"
15 #include "xfa/fwl/cfwl_eventmouse.h"
16 #include "xfa/fwl/cfwl_messagekey.h"
17 #include "xfa/fwl/cfwl_messagemouse.h"
18 #include "xfa/fwl/cfwl_notedriver.h"
19 #include "xfa/fwl/cfwl_themebackground.h"
20 #include "xfa/fwl/cfwl_themetext.h"
21 #include "xfa/fwl/fwl_widgetdef.h"
22 #include "xfa/fwl/ifwl_themeprovider.h"
23 
CFWL_PushButton(const CFWL_App * app)24 CFWL_PushButton::CFWL_PushButton(const CFWL_App* app)
25     : CFWL_Widget(app, pdfium::MakeUnique<CFWL_WidgetProperties>(), nullptr) {}
26 
~CFWL_PushButton()27 CFWL_PushButton::~CFWL_PushButton() {}
28 
GetClassID() const29 FWL_Type CFWL_PushButton::GetClassID() const {
30   return FWL_Type::PushButton;
31 }
32 
SetStates(uint32_t dwStates)33 void CFWL_PushButton::SetStates(uint32_t dwStates) {
34   if (dwStates & FWL_WGTSTATE_Disabled) {
35     m_pProperties->m_dwStates = FWL_WGTSTATE_Disabled;
36     return;
37   }
38   CFWL_Widget::SetStates(dwStates);
39 }
40 
Update()41 void CFWL_PushButton::Update() {
42   if (IsLocked())
43     return;
44   if (!m_pProperties->m_pThemeProvider)
45     m_pProperties->m_pThemeProvider = GetAvailableTheme();
46 
47   m_rtClient = GetClientRect();
48   m_rtCaption = m_rtClient;
49 }
50 
DrawWidget(CXFA_Graphics * pGraphics,const CFX_Matrix & matrix)51 void CFWL_PushButton::DrawWidget(CXFA_Graphics* pGraphics,
52                                  const CFX_Matrix& matrix) {
53   if (!pGraphics)
54     return;
55 
56   IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider.Get();
57   if (!pTheme)
58     return;
59 
60   if (HasBorder())
61     DrawBorder(pGraphics, CFWL_Part::Border, pTheme, matrix);
62   DrawBkground(pGraphics, pTheme, &matrix);
63 }
64 
DrawBkground(CXFA_Graphics * pGraphics,IFWL_ThemeProvider * pTheme,const CFX_Matrix * pMatrix)65 void CFWL_PushButton::DrawBkground(CXFA_Graphics* pGraphics,
66                                    IFWL_ThemeProvider* pTheme,
67                                    const CFX_Matrix* pMatrix) {
68   CFWL_ThemeBackground param;
69   param.m_pWidget = this;
70   param.m_iPart = CFWL_Part::Background;
71   param.m_dwStates = GetPartStates();
72   param.m_pGraphics = pGraphics;
73   if (pMatrix)
74     param.m_matrix.Concat(*pMatrix);
75   param.m_rtPart = m_rtClient;
76   if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused)
77     param.m_pRtData = &m_rtCaption;
78   pTheme->DrawBackground(param);
79 }
80 
GetPartStates()81 uint32_t CFWL_PushButton::GetPartStates() {
82   uint32_t dwStates = CFWL_PartState_Normal;
83   if (m_pProperties->m_dwStates & FWL_WGTSTATE_Focused)
84     dwStates |= CFWL_PartState_Focused;
85   if (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
86     dwStates = CFWL_PartState_Disabled;
87   else if (m_pProperties->m_dwStates & FWL_STATE_PSB_Pressed)
88     dwStates |= CFWL_PartState_Pressed;
89   else if (m_pProperties->m_dwStates & FWL_STATE_PSB_Hovered)
90     dwStates |= CFWL_PartState_Hovered;
91   return dwStates;
92 }
93 
OnProcessMessage(CFWL_Message * pMessage)94 void CFWL_PushButton::OnProcessMessage(CFWL_Message* pMessage) {
95   if (!pMessage)
96     return;
97   if (!IsEnabled())
98     return;
99 
100   switch (pMessage->GetType()) {
101     case CFWL_Message::Type::SetFocus:
102       OnFocusChanged(pMessage, true);
103       break;
104     case CFWL_Message::Type::KillFocus:
105       OnFocusChanged(pMessage, false);
106       break;
107     case CFWL_Message::Type::Mouse: {
108       CFWL_MessageMouse* pMsg = static_cast<CFWL_MessageMouse*>(pMessage);
109       switch (pMsg->m_dwCmd) {
110         case FWL_MouseCommand::LeftButtonDown:
111           OnLButtonDown(pMsg);
112           break;
113         case FWL_MouseCommand::LeftButtonUp:
114           OnLButtonUp(pMsg);
115           break;
116         case FWL_MouseCommand::Move:
117           OnMouseMove(pMsg);
118           break;
119         case FWL_MouseCommand::Leave:
120           OnMouseLeave(pMsg);
121           break;
122         default:
123           break;
124       }
125       break;
126     }
127     case CFWL_Message::Type::Key: {
128       CFWL_MessageKey* pKey = static_cast<CFWL_MessageKey*>(pMessage);
129       if (pKey->m_dwCmd == FWL_KeyCommand::KeyDown)
130         OnKeyDown(pKey);
131       break;
132     }
133     default:
134       break;
135   }
136   // Dst target could be |this|, continue only if not destroyed by above.
137   if (pMessage->GetDstTarget())
138     CFWL_Widget::OnProcessMessage(pMessage);
139 }
140 
OnDrawWidget(CXFA_Graphics * pGraphics,const CFX_Matrix & matrix)141 void CFWL_PushButton::OnDrawWidget(CXFA_Graphics* pGraphics,
142                                    const CFX_Matrix& matrix) {
143   DrawWidget(pGraphics, matrix);
144 }
145 
OnFocusChanged(CFWL_Message * pMsg,bool bSet)146 void CFWL_PushButton::OnFocusChanged(CFWL_Message* pMsg, bool bSet) {
147   if (bSet)
148     m_pProperties->m_dwStates |= FWL_WGTSTATE_Focused;
149   else
150     m_pProperties->m_dwStates &= ~FWL_WGTSTATE_Focused;
151 
152   RepaintRect(m_rtClient);
153 }
154 
OnLButtonDown(CFWL_MessageMouse * pMsg)155 void CFWL_PushButton::OnLButtonDown(CFWL_MessageMouse* pMsg) {
156   m_bBtnDown = true;
157   m_pProperties->m_dwStates |= FWL_STATE_PSB_Hovered;
158   m_pProperties->m_dwStates |= FWL_STATE_PSB_Pressed;
159   RepaintRect(m_rtClient);
160 }
161 
OnLButtonUp(CFWL_MessageMouse * pMsg)162 void CFWL_PushButton::OnLButtonUp(CFWL_MessageMouse* pMsg) {
163   m_bBtnDown = false;
164   if (m_rtClient.Contains(pMsg->m_pos)) {
165     m_pProperties->m_dwStates &= ~FWL_STATE_PSB_Pressed;
166     m_pProperties->m_dwStates |= FWL_STATE_PSB_Hovered;
167   } else {
168     m_pProperties->m_dwStates &= ~FWL_STATE_PSB_Hovered;
169     m_pProperties->m_dwStates &= ~FWL_STATE_PSB_Pressed;
170   }
171   if (m_rtClient.Contains(pMsg->m_pos)) {
172     CFWL_Event wmClick(CFWL_Event::Type::Click, this);
173     DispatchEvent(&wmClick);
174   }
175   RepaintRect(m_rtClient);
176 }
177 
OnMouseMove(CFWL_MessageMouse * pMsg)178 void CFWL_PushButton::OnMouseMove(CFWL_MessageMouse* pMsg) {
179   bool bRepaint = false;
180   if (m_bBtnDown) {
181     if (m_rtClient.Contains(pMsg->m_pos)) {
182       if ((m_pProperties->m_dwStates & FWL_STATE_PSB_Pressed) == 0) {
183         m_pProperties->m_dwStates |= FWL_STATE_PSB_Pressed;
184         bRepaint = true;
185       }
186       if (m_pProperties->m_dwStates & FWL_STATE_PSB_Hovered) {
187         m_pProperties->m_dwStates &= ~FWL_STATE_PSB_Hovered;
188         bRepaint = true;
189       }
190     } else {
191       if (m_pProperties->m_dwStates & FWL_STATE_PSB_Pressed) {
192         m_pProperties->m_dwStates &= ~FWL_STATE_PSB_Pressed;
193         bRepaint = true;
194       }
195       if ((m_pProperties->m_dwStates & FWL_STATE_PSB_Hovered) == 0) {
196         m_pProperties->m_dwStates |= FWL_STATE_PSB_Hovered;
197         bRepaint = true;
198       }
199     }
200   } else {
201     if (!m_rtClient.Contains(pMsg->m_pos))
202       return;
203     if ((m_pProperties->m_dwStates & FWL_STATE_PSB_Hovered) == 0) {
204       m_pProperties->m_dwStates |= FWL_STATE_PSB_Hovered;
205       bRepaint = true;
206     }
207   }
208   if (bRepaint)
209     RepaintRect(m_rtClient);
210 }
211 
OnMouseLeave(CFWL_MessageMouse * pMsg)212 void CFWL_PushButton::OnMouseLeave(CFWL_MessageMouse* pMsg) {
213   m_bBtnDown = false;
214   m_pProperties->m_dwStates &= ~FWL_STATE_PSB_Hovered;
215   m_pProperties->m_dwStates &= ~FWL_STATE_PSB_Pressed;
216   RepaintRect(m_rtClient);
217 }
218 
OnKeyDown(CFWL_MessageKey * pMsg)219 void CFWL_PushButton::OnKeyDown(CFWL_MessageKey* pMsg) {
220   if (pMsg->m_dwKeyCode != XFA_FWL_VKEY_Return)
221     return;
222 
223   CFWL_EventMouse wmMouse(this);
224   wmMouse.m_dwCmd = FWL_MouseCommand::LeftButtonUp;
225   DispatchEvent(&wmMouse);
226   if (!wmMouse.GetSrcTarget())
227     return;
228 
229   CFWL_Event wmClick(CFWL_Event::Type::Click, this);
230   DispatchEvent(&wmClick);
231 }
232