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