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