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