Home | History | Annotate | Download | only in pwl
      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 "fpdfsdk/pwl/cpwl_combo_box.h"
      8 
      9 #include <algorithm>
     10 #include <sstream>
     11 
     12 #include "core/fxge/cfx_pathdata.h"
     13 #include "core/fxge/cfx_renderdevice.h"
     14 #include "fpdfsdk/pwl/cpwl_edit.h"
     15 #include "fpdfsdk/pwl/cpwl_edit_ctrl.h"
     16 #include "fpdfsdk/pwl/cpwl_list_box.h"
     17 #include "fpdfsdk/pwl/cpwl_list_impl.h"
     18 #include "fpdfsdk/pwl/cpwl_wnd.h"
     19 #include "public/fpdf_fwlevent.h"
     20 
     21 namespace {
     22 
     23 constexpr float kComboBoxDefaultFontSize = 12.0f;
     24 constexpr float kComboBoxTriangleHalfLength = 3.0f;
     25 constexpr int kDefaultButtonWidth = 13;
     26 
     27 }  // namespace
     28 
     29 bool CPWL_CBListBox::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
     30   CPWL_Wnd::OnLButtonUp(point, nFlag);
     31 
     32   if (!m_bMouseDown)
     33     return true;
     34 
     35   ReleaseCapture();
     36   m_bMouseDown = false;
     37 
     38   if (!ClientHitTest(point))
     39     return true;
     40   if (CPWL_Wnd* pParent = GetParentWindow())
     41     pParent->NotifyLButtonUp(this, point);
     42 
     43   return !OnNotifySelectionChanged(false, nFlag);
     44 }
     45 
     46 bool CPWL_CBListBox::IsMovementKey(uint16_t nChar) const {
     47   switch (nChar) {
     48     case FWL_VKEY_Up:
     49     case FWL_VKEY_Down:
     50     case FWL_VKEY_Home:
     51     case FWL_VKEY_Left:
     52     case FWL_VKEY_End:
     53     case FWL_VKEY_Right:
     54       return true;
     55     default:
     56       return false;
     57   }
     58 }
     59 
     60 bool CPWL_CBListBox::OnMovementKeyDown(uint16_t nChar, uint32_t nFlag) {
     61   ASSERT(IsMovementKey(nChar));
     62 
     63   switch (nChar) {
     64     case FWL_VKEY_Up:
     65       m_pList->OnVK_UP(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
     66       break;
     67     case FWL_VKEY_Down:
     68       m_pList->OnVK_DOWN(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
     69       break;
     70     case FWL_VKEY_Home:
     71       m_pList->OnVK_HOME(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
     72       break;
     73     case FWL_VKEY_Left:
     74       m_pList->OnVK_LEFT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
     75       break;
     76     case FWL_VKEY_End:
     77       m_pList->OnVK_END(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
     78       break;
     79     case FWL_VKEY_Right:
     80       m_pList->OnVK_RIGHT(IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
     81       break;
     82   }
     83   return OnNotifySelectionChanged(true, nFlag);
     84 }
     85 
     86 bool CPWL_CBListBox::IsChar(uint16_t nChar, uint32_t nFlag) const {
     87   return m_pList->OnChar(nChar, IsSHIFTpressed(nFlag), IsCTRLpressed(nFlag));
     88 }
     89 
     90 bool CPWL_CBListBox::OnCharNotify(uint16_t nChar, uint32_t nFlag) {
     91   if (CPWL_ComboBox* pComboBox = (CPWL_ComboBox*)GetParentWindow())
     92     pComboBox->SetSelectText();
     93 
     94   return OnNotifySelectionChanged(true, nFlag);
     95 }
     96 
     97 void CPWL_CBButton::DrawThisAppearance(CFX_RenderDevice* pDevice,
     98                                        const CFX_Matrix& mtUser2Device) {
     99   CPWL_Wnd::DrawThisAppearance(pDevice, mtUser2Device);
    100 
    101   CFX_FloatRect rectWnd = CPWL_Wnd::GetWindowRect();
    102 
    103   if (!IsVisible() || rectWnd.IsEmpty())
    104     return;
    105 
    106   CFX_PointF ptCenter = GetCenterPoint();
    107 
    108   static constexpr float kComboBoxTriangleQuarterLength =
    109       kComboBoxTriangleHalfLength * 0.5;
    110   CFX_PointF pt1(ptCenter.x - kComboBoxTriangleHalfLength,
    111                  ptCenter.y + kComboBoxTriangleQuarterLength);
    112   CFX_PointF pt2(ptCenter.x + kComboBoxTriangleHalfLength,
    113                  ptCenter.y + kComboBoxTriangleQuarterLength);
    114   CFX_PointF pt3(ptCenter.x, ptCenter.y - kComboBoxTriangleQuarterLength);
    115 
    116   if (IsFloatBigger(rectWnd.right - rectWnd.left,
    117                     kComboBoxTriangleHalfLength * 2) &&
    118       IsFloatBigger(rectWnd.top - rectWnd.bottom,
    119                     kComboBoxTriangleHalfLength)) {
    120     CFX_PathData path;
    121     path.AppendPoint(pt1, FXPT_TYPE::MoveTo, false);
    122     path.AppendPoint(pt2, FXPT_TYPE::LineTo, false);
    123     path.AppendPoint(pt3, FXPT_TYPE::LineTo, false);
    124     path.AppendPoint(pt1, FXPT_TYPE::LineTo, false);
    125 
    126     pDevice->DrawPath(&path, &mtUser2Device, nullptr,
    127                       PWL_DEFAULT_BLACKCOLOR.ToFXColor(GetTransparency()), 0,
    128                       FXFILL_ALTERNATE);
    129   }
    130 }
    131 
    132 bool CPWL_CBButton::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
    133   CPWL_Wnd::OnLButtonDown(point, nFlag);
    134 
    135   SetCapture();
    136 
    137   if (CPWL_Wnd* pParent = GetParentWindow())
    138     pParent->NotifyLButtonDown(this, point);
    139 
    140   return true;
    141 }
    142 
    143 bool CPWL_CBButton::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
    144   CPWL_Wnd::OnLButtonUp(point, nFlag);
    145 
    146   ReleaseCapture();
    147 
    148   return true;
    149 }
    150 
    151 CPWL_ComboBox::CPWL_ComboBox() {}
    152 
    153 CPWL_ComboBox::~CPWL_ComboBox() {}
    154 
    155 ByteString CPWL_ComboBox::GetClassName() const {
    156   return "CPWL_ComboBox";
    157 }
    158 
    159 void CPWL_ComboBox::OnCreate(CreateParams* pParamsToAdjust) {
    160   pParamsToAdjust->dwFlags &= ~PWS_HSCROLL;
    161   pParamsToAdjust->dwFlags &= ~PWS_VSCROLL;
    162 }
    163 
    164 void CPWL_ComboBox::OnDestroy() {
    165   // Until cleanup takes place in the virtual destructor for CPWL_Wnd
    166   // subclasses, implement the virtual OnDestroy method that does the
    167   // cleanup first, then invokes the superclass OnDestroy ... gee,
    168   // like a dtor would.
    169   m_pList.Release();
    170   m_pButton.Release();
    171   m_pEdit.Release();
    172   CPWL_Wnd::OnDestroy();
    173 }
    174 
    175 void CPWL_ComboBox::SetFocus() {
    176   if (m_pEdit)
    177     m_pEdit->SetFocus();
    178 }
    179 
    180 void CPWL_ComboBox::KillFocus() {
    181   if (!SetPopup(false))
    182     return;
    183 
    184   CPWL_Wnd::KillFocus();
    185 }
    186 
    187 WideString CPWL_ComboBox::GetSelectedText() {
    188   if (m_pEdit)
    189     return m_pEdit->GetSelectedText();
    190 
    191   return WideString();
    192 }
    193 
    194 void CPWL_ComboBox::ReplaceSelection(const WideString& text) {
    195   if (m_pEdit)
    196     m_pEdit->ReplaceSelection(text);
    197 }
    198 
    199 WideString CPWL_ComboBox::GetText() const {
    200   if (m_pEdit) {
    201     return m_pEdit->GetText();
    202   }
    203   return WideString();
    204 }
    205 
    206 void CPWL_ComboBox::SetText(const WideString& text) {
    207   if (m_pEdit)
    208     m_pEdit->SetText(text);
    209 }
    210 
    211 void CPWL_ComboBox::AddString(const WideString& str) {
    212   if (m_pList)
    213     m_pList->AddString(str);
    214 }
    215 
    216 int32_t CPWL_ComboBox::GetSelect() const {
    217   return m_nSelectItem;
    218 }
    219 
    220 void CPWL_ComboBox::SetSelect(int32_t nItemIndex) {
    221   if (m_pList)
    222     m_pList->Select(nItemIndex);
    223 
    224   m_pEdit->SetText(m_pList->GetText());
    225   m_nSelectItem = nItemIndex;
    226 }
    227 
    228 void CPWL_ComboBox::SetEditSelection(int32_t nStartChar, int32_t nEndChar) {
    229   if (m_pEdit)
    230     m_pEdit->SetSelection(nStartChar, nEndChar);
    231 }
    232 
    233 void CPWL_ComboBox::GetEditSelection(int32_t& nStartChar,
    234                                      int32_t& nEndChar) const {
    235   nStartChar = -1;
    236   nEndChar = -1;
    237 
    238   if (m_pEdit)
    239     m_pEdit->GetSelection(nStartChar, nEndChar);
    240 }
    241 
    242 void CPWL_ComboBox::ClearSelection() {
    243   if (m_pEdit)
    244     m_pEdit->ClearSelection();
    245 }
    246 
    247 void CPWL_ComboBox::CreateChildWnd(const CreateParams& cp) {
    248   CreateEdit(cp);
    249   CreateButton(cp);
    250   CreateListBox(cp);
    251 }
    252 
    253 void CPWL_ComboBox::CreateEdit(const CreateParams& cp) {
    254   if (m_pEdit)
    255     return;
    256 
    257   m_pEdit = new CPWL_Edit();
    258   m_pEdit->AttachFFLData(m_pFormFiller.Get());
    259 
    260   CreateParams ecp = cp;
    261   ecp.pParentWnd = this;
    262   ecp.dwFlags = PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PES_CENTER |
    263                 PES_AUTOSCROLL | PES_UNDO;
    264 
    265   if (HasFlag(PWS_AUTOFONTSIZE))
    266     ecp.dwFlags |= PWS_AUTOFONTSIZE;
    267 
    268   if (!HasFlag(PCBS_ALLOWCUSTOMTEXT))
    269     ecp.dwFlags |= PWS_READONLY;
    270 
    271   ecp.rcRectWnd = CFX_FloatRect();
    272   ecp.dwBorderWidth = 0;
    273   ecp.nBorderStyle = BorderStyle::SOLID;
    274   m_pEdit->Create(ecp);
    275 }
    276 
    277 void CPWL_ComboBox::CreateButton(const CreateParams& cp) {
    278   if (m_pButton)
    279     return;
    280 
    281   m_pButton = new CPWL_CBButton;
    282 
    283   CreateParams bcp = cp;
    284   bcp.pParentWnd = this;
    285   bcp.dwFlags = PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PWS_BACKGROUND;
    286   bcp.sBackgroundColor = CFX_Color(CFX_Color::kRGB, 220.0f / 255.0f,
    287                                    220.0f / 255.0f, 220.0f / 255.0f);
    288   bcp.sBorderColor = PWL_DEFAULT_BLACKCOLOR;
    289   bcp.dwBorderWidth = 2;
    290   bcp.nBorderStyle = BorderStyle::BEVELED;
    291   bcp.eCursorType = FXCT_ARROW;
    292   m_pButton->Create(bcp);
    293 }
    294 
    295 void CPWL_ComboBox::CreateListBox(const CreateParams& cp) {
    296   if (m_pList)
    297     return;
    298 
    299   m_pList = new CPWL_CBListBox();
    300   m_pList->AttachFFLData(m_pFormFiller.Get());
    301 
    302   CreateParams lcp = cp;
    303   lcp.pParentWnd = this;
    304   lcp.dwFlags =
    305       PWS_CHILD | PWS_BORDER | PWS_BACKGROUND | PLBS_HOVERSEL | PWS_VSCROLL;
    306   lcp.nBorderStyle = BorderStyle::SOLID;
    307   lcp.dwBorderWidth = 1;
    308   lcp.eCursorType = FXCT_ARROW;
    309   lcp.rcRectWnd = CFX_FloatRect();
    310 
    311   lcp.fFontSize =
    312       (cp.dwFlags & PWS_AUTOFONTSIZE) ? kComboBoxDefaultFontSize : cp.fFontSize;
    313 
    314   if (cp.sBorderColor.nColorType == CFX_Color::kTransparent)
    315     lcp.sBorderColor = PWL_DEFAULT_BLACKCOLOR;
    316 
    317   if (cp.sBackgroundColor.nColorType == CFX_Color::kTransparent)
    318     lcp.sBackgroundColor = PWL_DEFAULT_WHITECOLOR;
    319 
    320   m_pList->Create(lcp);
    321 }
    322 
    323 bool CPWL_ComboBox::RePosChildWnd() {
    324   ObservedPtr thisObserved(this);
    325 
    326   const CFX_FloatRect rcClient = GetClientRect();
    327   if (m_bPopup) {
    328     const float fOldWindowHeight = m_rcOldWindow.Height();
    329     const float fOldClientHeight = fOldWindowHeight - GetBorderWidth() * 2;
    330 
    331     CFX_FloatRect rcList = CPWL_Wnd::GetWindowRect();
    332     CFX_FloatRect rcButton = rcClient;
    333     rcButton.left =
    334         std::max(rcButton.right - kDefaultButtonWidth, rcClient.left);
    335     CFX_FloatRect rcEdit = rcClient;
    336     rcEdit.right = std::max(rcButton.left - 1.0f, rcEdit.left);
    337     if (m_bBottom) {
    338       rcButton.bottom = rcButton.top - fOldClientHeight;
    339       rcEdit.bottom = rcEdit.top - fOldClientHeight;
    340       rcList.top -= fOldWindowHeight;
    341     } else {
    342       rcButton.top = rcButton.bottom + fOldClientHeight;
    343       rcEdit.top = rcEdit.bottom + fOldClientHeight;
    344       rcList.bottom += fOldWindowHeight;
    345     }
    346 
    347     if (m_pButton) {
    348       m_pButton->Move(rcButton, true, false);
    349       if (!thisObserved)
    350         return false;
    351     }
    352 
    353     if (m_pEdit) {
    354       m_pEdit->Move(rcEdit, true, false);
    355       if (!thisObserved)
    356         return false;
    357     }
    358 
    359     if (m_pList) {
    360       if (!m_pList->SetVisible(true) || !thisObserved)
    361         return false;
    362 
    363       if (!m_pList->Move(rcList, true, false) || !thisObserved)
    364         return false;
    365 
    366       m_pList->ScrollToListItem(m_nSelectItem);
    367       if (!thisObserved)
    368         return false;
    369     }
    370     return true;
    371   }
    372 
    373   CFX_FloatRect rcButton = rcClient;
    374   rcButton.left = std::max(rcButton.right - kDefaultButtonWidth, rcClient.left);
    375 
    376   if (m_pButton) {
    377     m_pButton->Move(rcButton, true, false);
    378     if (!thisObserved)
    379       return false;
    380   }
    381 
    382   CFX_FloatRect rcEdit = rcClient;
    383   rcEdit.right = std::max(rcButton.left - 1.0f, rcEdit.left);
    384 
    385   if (m_pEdit) {
    386     m_pEdit->Move(rcEdit, true, false);
    387     if (!thisObserved)
    388       return false;
    389   }
    390 
    391   if (m_pList) {
    392     m_pList->SetVisible(false);
    393     if (!thisObserved)
    394       return false;
    395   }
    396 
    397   return true;
    398 }
    399 
    400 void CPWL_ComboBox::SelectAll() {
    401   if (m_pEdit && HasFlag(PCBS_ALLOWCUSTOMTEXT))
    402     m_pEdit->SelectAll();
    403 }
    404 
    405 CFX_FloatRect CPWL_ComboBox::GetFocusRect() const {
    406   return CFX_FloatRect();
    407 }
    408 
    409 bool CPWL_ComboBox::SetPopup(bool bPopup) {
    410   if (!m_pList)
    411     return true;
    412   if (bPopup == m_bPopup)
    413     return true;
    414   float fListHeight = m_pList->GetContentRect().Height();
    415   if (!IsFloatBigger(fListHeight, 0.0f))
    416     return true;
    417 
    418   if (!bPopup) {
    419     m_bPopup = bPopup;
    420     return Move(m_rcOldWindow, true, true);
    421   }
    422 
    423   if (!m_pFillerNotify)
    424     return true;
    425 
    426   ObservedPtr thisObserved(this);
    427 
    428 #ifdef PDF_ENABLE_XFA
    429   if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), 0))
    430     return !!thisObserved;
    431   if (!thisObserved)
    432     return false;
    433 #endif  // PDF_ENABLE_XFA
    434 
    435   float fBorderWidth = m_pList->GetBorderWidth() * 2;
    436   float fPopupMin = 0.0f;
    437   if (m_pList->GetCount() > 3)
    438     fPopupMin = m_pList->GetFirstHeight() * 3 + fBorderWidth;
    439   float fPopupMax = fListHeight + fBorderWidth;
    440 
    441   bool bBottom;
    442   float fPopupRet;
    443   m_pFillerNotify->QueryWherePopup(GetAttachedData(), fPopupMin, fPopupMax,
    444                                    &bBottom, &fPopupRet);
    445   if (!IsFloatBigger(fPopupRet, 0.0f))
    446     return true;
    447 
    448   m_rcOldWindow = CPWL_Wnd::GetWindowRect();
    449   m_bPopup = bPopup;
    450   m_bBottom = bBottom;
    451 
    452   CFX_FloatRect rcWindow = m_rcOldWindow;
    453   if (bBottom)
    454     rcWindow.bottom -= fPopupRet;
    455   else
    456     rcWindow.top += fPopupRet;
    457 
    458   if (!Move(rcWindow, true, true))
    459     return false;
    460 
    461 #ifdef PDF_ENABLE_XFA
    462   m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), 0);
    463   if (!thisObserved)
    464     return false;
    465 #endif  // PDF_ENABLE_XFA
    466 
    467   return !!thisObserved;
    468 }
    469 
    470 bool CPWL_ComboBox::OnKeyDown(uint16_t nChar, uint32_t nFlag) {
    471   if (!m_pList)
    472     return false;
    473   if (!m_pEdit)
    474     return false;
    475 
    476   m_nSelectItem = -1;
    477 
    478   switch (nChar) {
    479     case FWL_VKEY_Up:
    480       if (m_pList->GetCurSel() > 0) {
    481 #ifdef PDF_ENABLE_XFA
    482         if (m_pFillerNotify) {
    483           if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), nFlag))
    484             return false;
    485           if (m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), nFlag))
    486             return false;
    487         }
    488 #endif  // PDF_ENABLE_XFA
    489         if (m_pList->IsMovementKey(nChar)) {
    490           if (m_pList->OnMovementKeyDown(nChar, nFlag))
    491             return false;
    492           SetSelectText();
    493         }
    494       }
    495       return true;
    496     case FWL_VKEY_Down:
    497       if (m_pList->GetCurSel() < m_pList->GetCount() - 1) {
    498 #ifdef PDF_ENABLE_XFA
    499         if (m_pFillerNotify) {
    500           if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), nFlag))
    501             return false;
    502           if (m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), nFlag))
    503             return false;
    504         }
    505 #endif  // PDF_ENABLE_XFA
    506         if (m_pList->IsMovementKey(nChar)) {
    507           if (m_pList->OnMovementKeyDown(nChar, nFlag))
    508             return false;
    509           SetSelectText();
    510         }
    511       }
    512       return true;
    513   }
    514 
    515   if (HasFlag(PCBS_ALLOWCUSTOMTEXT))
    516     return m_pEdit->OnKeyDown(nChar, nFlag);
    517 
    518   return false;
    519 }
    520 
    521 bool CPWL_ComboBox::OnChar(uint16_t nChar, uint32_t nFlag) {
    522   if (!m_pList)
    523     return false;
    524 
    525   if (!m_pEdit)
    526     return false;
    527 
    528   m_nSelectItem = -1;
    529   if (HasFlag(PCBS_ALLOWCUSTOMTEXT))
    530     return m_pEdit->OnChar(nChar, nFlag);
    531 
    532 #ifdef PDF_ENABLE_XFA
    533   if (m_pFillerNotify) {
    534     if (m_pFillerNotify->OnPopupPreOpen(GetAttachedData(), nFlag))
    535       return false;
    536     if (m_pFillerNotify->OnPopupPostOpen(GetAttachedData(), nFlag))
    537       return false;
    538   }
    539 #endif  // PDF_ENABLE_XFA
    540   if (!m_pList->IsChar(nChar, nFlag))
    541     return false;
    542   return m_pList->OnCharNotify(nChar, nFlag);
    543 }
    544 
    545 void CPWL_ComboBox::NotifyLButtonDown(CPWL_Wnd* child, const CFX_PointF& pos) {
    546   if (child == m_pButton) {
    547     SetPopup(!m_bPopup);
    548     // Note, |this| may no longer be viable at this point. If more work needs to
    549     // be done, check the return value of SetPopup().
    550   }
    551 }
    552 
    553 void CPWL_ComboBox::NotifyLButtonUp(CPWL_Wnd* child, const CFX_PointF& pos) {
    554   if (!m_pEdit || !m_pList || child != m_pList)
    555     return;
    556 
    557   SetSelectText();
    558   SelectAll();
    559   m_pEdit->SetFocus();
    560   SetPopup(false);
    561   // Note, |this| may no longer be viable at this point. If more work needs to
    562   // be done, check the return value of SetPopup().
    563 }
    564 
    565 bool CPWL_ComboBox::IsPopup() const {
    566   return m_bPopup;
    567 }
    568 
    569 void CPWL_ComboBox::SetSelectText() {
    570   m_pEdit->SelectAll();
    571   m_pEdit->ReplaceSel(m_pList->GetText());
    572   m_pEdit->SelectAll();
    573   m_nSelectItem = m_pList->GetCurSel();
    574 }
    575 
    576 void CPWL_ComboBox::SetFillerNotify(IPWL_Filler_Notify* pNotify) {
    577   m_pFillerNotify = pNotify;
    578 
    579   if (m_pEdit)
    580     m_pEdit->SetFillerNotify(pNotify);
    581 
    582   if (m_pList)
    583     m_pList->SetFillerNotify(pNotify);
    584 }
    585