Home | History | Annotate | Download | only in basewidget
      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/src/foxitlib.h"
      8 #include "xfa/src/fwl/src/core/include/fwl_targetimp.h"
      9 #include "xfa/src/fwl/src/core/include/fwl_noteimp.h"
     10 #include "xfa/src/fwl/src/core/include/fwl_widgetimp.h"
     11 #include "xfa/src/fwl/src/basewidget/include/fwl_scrollbarimp.h"
     12 #define FWL_SCROLLBAR_Elapse 500
     13 #define FWL_SCROLLBAR_MinThumb 5
     14 
     15 // static
     16 IFWL_ScrollBar* IFWL_ScrollBar::Create(
     17     const CFWL_WidgetImpProperties& properties,
     18     IFWL_Widget* pOuter) {
     19   IFWL_ScrollBar* pScrollBar = new IFWL_ScrollBar;
     20   CFWL_ScrollBarImp* pScrollBarImpl = new CFWL_ScrollBarImp(properties, pOuter);
     21   pScrollBar->SetImpl(pScrollBarImpl);
     22   pScrollBarImpl->SetInterface(pScrollBar);
     23   return pScrollBar;
     24 }
     25 IFWL_ScrollBar::IFWL_ScrollBar() {}
     26 FX_BOOL IFWL_ScrollBar::IsVertical() {
     27   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->IsVertical();
     28 }
     29 FWL_ERR IFWL_ScrollBar::GetRange(FX_FLOAT& fMin, FX_FLOAT& fMax) {
     30   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->GetRange(fMin, fMax);
     31 }
     32 FWL_ERR IFWL_ScrollBar::SetRange(FX_FLOAT fMin, FX_FLOAT fMax) {
     33   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->SetRange(fMin, fMax);
     34 }
     35 FX_FLOAT IFWL_ScrollBar::GetPageSize() {
     36   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->GetPageSize();
     37 }
     38 FWL_ERR IFWL_ScrollBar::SetPageSize(FX_FLOAT fPageSize) {
     39   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->SetPageSize(fPageSize);
     40 }
     41 FX_FLOAT IFWL_ScrollBar::GetStepSize() {
     42   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->GetStepSize();
     43 }
     44 FWL_ERR IFWL_ScrollBar::SetStepSize(FX_FLOAT fStepSize) {
     45   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->SetStepSize(fStepSize);
     46 }
     47 FX_FLOAT IFWL_ScrollBar::GetPos() {
     48   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->GetPos();
     49 }
     50 FWL_ERR IFWL_ScrollBar::SetPos(FX_FLOAT fPos) {
     51   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->SetPos(fPos);
     52 }
     53 FX_FLOAT IFWL_ScrollBar::GetTrackPos() {
     54   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->GetTrackPos();
     55 }
     56 FWL_ERR IFWL_ScrollBar::SetTrackPos(FX_FLOAT fTrackPos) {
     57   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->SetTrackPos(fTrackPos);
     58 }
     59 FX_BOOL IFWL_ScrollBar::DoScroll(FX_DWORD dwCode, FX_FLOAT fPos) {
     60   return static_cast<CFWL_ScrollBarImp*>(GetImpl())->DoScroll(dwCode, fPos);
     61 }
     62 CFWL_ScrollBarImp::CFWL_ScrollBarImp(const CFWL_WidgetImpProperties& properties,
     63                                      IFWL_Widget* pOuter)
     64     : CFWL_WidgetImp(properties, pOuter),
     65       m_hTimer(nullptr),
     66       m_fRangeMin(0),
     67       m_fRangeMax(-1),
     68       m_fPageSize(0),
     69       m_fStepSize(0),
     70       m_fPos(0),
     71       m_fTrackPos(0),
     72       m_iMinButtonState(FWL_PARTSTATE_SCB_Normal),
     73       m_iMaxButtonState(FWL_PARTSTATE_SCB_Normal),
     74       m_iThumbButtonState(FWL_PARTSTATE_SCB_Normal),
     75       m_iMinTrackState(FWL_PARTSTATE_SCB_Normal),
     76       m_iMaxTrackState(FWL_PARTSTATE_SCB_Normal),
     77       m_fLastTrackPos(0),
     78       m_cpTrackPointX(0),
     79       m_cpTrackPointY(0),
     80       m_iMouseWheel(0),
     81       m_bTrackMouseLeave(FALSE),
     82       m_bMouseHover(FALSE),
     83       m_bMouseDown(FALSE),
     84       m_bRepaintThumb(FALSE),
     85       m_fButtonLen(0),
     86       m_bMinSize(FALSE),
     87       m_bCustomLayout(FALSE),
     88       m_fMinThumb(FWL_SCROLLBAR_MinThumb) {
     89   m_rtClient.Reset();
     90   m_rtThumb.Reset();
     91   m_rtMinBtn.Reset();
     92   m_rtMaxBtn.Reset();
     93   m_rtMinTrack.Reset();
     94   m_rtMaxTrack.Reset();
     95 }
     96 CFWL_ScrollBarImp::~CFWL_ScrollBarImp() {}
     97 FWL_ERR CFWL_ScrollBarImp::GetClassName(CFX_WideString& wsClass) const {
     98   wsClass = FWL_CLASS_ScrollBar;
     99   return FWL_ERR_Succeeded;
    100 }
    101 FX_DWORD CFWL_ScrollBarImp::GetClassID() const {
    102   return FWL_CLASSHASH_ScrollBar;
    103 }
    104 FWL_ERR CFWL_ScrollBarImp::Initialize() {
    105   if (CFWL_WidgetImp::Initialize() != FWL_ERR_Succeeded)
    106     return FWL_ERR_Indefinite;
    107   m_pDelegate = new CFWL_ScrollBarImpDelegate(this);
    108   return FWL_ERR_Succeeded;
    109 }
    110 FWL_ERR CFWL_ScrollBarImp::Finalize() {
    111   delete m_pDelegate;
    112   m_pDelegate = nullptr;
    113   return CFWL_WidgetImp::Finalize();
    114 }
    115 FWL_ERR CFWL_ScrollBarImp::GetWidgetRect(CFX_RectF& rect, FX_BOOL bAutoSize) {
    116   if (bAutoSize) {
    117     rect.Set(0, 0, 0, 0);
    118     FX_FLOAT* pfMinWidth = static_cast<FX_FLOAT*>(
    119         GetThemeCapacity(FWL_WGTCAPACITY_ScrollBarWidth));
    120     if (!pfMinWidth)
    121       return FWL_ERR_Indefinite;
    122     if (IsVertical()) {
    123       rect.Set(0, 0, (*pfMinWidth), (*pfMinWidth) * 3);
    124     } else {
    125       rect.Set(0, 0, (*pfMinWidth) * 3, (*pfMinWidth));
    126     }
    127     CFWL_WidgetImp::GetWidgetRect(rect, TRUE);
    128   } else {
    129     rect = m_pProperties->m_rtWidget;
    130   }
    131   return FWL_ERR_Succeeded;
    132 }
    133 FWL_ERR CFWL_ScrollBarImp::Update() {
    134   if (IsLocked()) {
    135     return FWL_ERR_Indefinite;
    136   }
    137   if (!m_pProperties->m_pThemeProvider) {
    138     m_pProperties->m_pThemeProvider = GetAvailableTheme();
    139   }
    140   Layout();
    141   return FWL_ERR_Succeeded;
    142 }
    143 FWL_ERR CFWL_ScrollBarImp::DrawWidget(CFX_Graphics* pGraphics,
    144                                       const CFX_Matrix* pMatrix) {
    145   if (!pGraphics)
    146     return FWL_ERR_Indefinite;
    147   if (!m_pProperties->m_pThemeProvider)
    148     return FWL_ERR_Indefinite;
    149   IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
    150   if (HasBorder()) {
    151     DrawBorder(pGraphics, FWL_PART_SCB_Border, pTheme, pMatrix);
    152   }
    153   if (HasEdge()) {
    154     DrawEdge(pGraphics, FWL_PART_SCB_Edge, pTheme, pMatrix);
    155   }
    156   DrawTrack(pGraphics, pTheme, TRUE, pMatrix);
    157   DrawTrack(pGraphics, pTheme, FALSE, pMatrix);
    158   DrawArrowBtn(pGraphics, pTheme, TRUE, pMatrix);
    159   DrawArrowBtn(pGraphics, pTheme, FALSE, pMatrix);
    160   DrawThumb(pGraphics, pTheme, pMatrix);
    161   return FWL_ERR_Succeeded;
    162 }
    163 inline FX_BOOL CFWL_ScrollBarImp::IsVertical() {
    164   return m_pProperties->m_dwStyleExes & FWL_STYLEEXT_SCB_Vert;
    165 }
    166 FWL_ERR CFWL_ScrollBarImp::GetRange(FX_FLOAT& fMin, FX_FLOAT& fMax) {
    167   fMin = m_fRangeMin;
    168   fMax = m_fRangeMax;
    169   return FWL_ERR_Succeeded;
    170 }
    171 FWL_ERR CFWL_ScrollBarImp::SetRange(FX_FLOAT fMin, FX_FLOAT fMax) {
    172   m_fRangeMin = fMin;
    173   m_fRangeMax = fMax;
    174   return FWL_ERR_Succeeded;
    175 }
    176 FX_FLOAT CFWL_ScrollBarImp::GetPageSize() {
    177   return m_fPageSize;
    178 }
    179 FWL_ERR CFWL_ScrollBarImp::SetPageSize(FX_FLOAT fPageSize) {
    180   m_fPageSize = fPageSize;
    181   return FWL_ERR_Succeeded;
    182 }
    183 FX_FLOAT CFWL_ScrollBarImp::GetStepSize() {
    184   return m_fStepSize;
    185 }
    186 FWL_ERR CFWL_ScrollBarImp::SetStepSize(FX_FLOAT fStepSize) {
    187   m_fStepSize = fStepSize;
    188   return FWL_ERR_Succeeded;
    189 }
    190 FX_FLOAT CFWL_ScrollBarImp::GetPos() {
    191   return m_fPos;
    192 }
    193 FWL_ERR CFWL_ScrollBarImp::SetPos(FX_FLOAT fPos) {
    194   m_fPos = fPos;
    195   return FWL_ERR_Succeeded;
    196 }
    197 FX_FLOAT CFWL_ScrollBarImp::GetTrackPos() {
    198   return m_fTrackPos;
    199 }
    200 FWL_ERR CFWL_ScrollBarImp::SetTrackPos(FX_FLOAT fTrackPos) {
    201   m_fTrackPos = fTrackPos;
    202   CalcThumbButtonRect(m_rtThumb);
    203   CalcMinTrackRect(m_rtMinTrack);
    204   CalcMaxTrackRect(m_rtMaxTrack);
    205   return FWL_ERR_Succeeded;
    206 }
    207 FX_BOOL CFWL_ScrollBarImp::DoScroll(FX_DWORD dwCode, FX_FLOAT fPos) {
    208   switch (dwCode) {
    209     case FWL_SCBCODE_Min:
    210     case FWL_SCBCODE_Max:
    211     case FWL_SCBCODE_PageBackward:
    212     case FWL_SCBCODE_PageForward:
    213     case FWL_SCBCODE_StepBackward:
    214       break;
    215     case FWL_SCBCODE_StepForward:
    216       break;
    217     case FWL_SCBCODE_Pos:
    218     case FWL_SCBCODE_TrackPos:
    219     case FWL_SCBCODE_EndScroll:
    220       break;
    221     default: { return FALSE; }
    222   }
    223   return OnScroll(dwCode, fPos);
    224 }
    225 int32_t CFWL_ScrollBarImp::Run(FWL_HTIMER hTimer) {
    226   if (m_hTimer) {
    227     FWL_StopTimer(m_hTimer);
    228   }
    229   if (!SendEvent()) {
    230     m_hTimer = FWL_StartTimer(this, 0);
    231   }
    232   return 1;
    233 }
    234 FWL_ERR CFWL_ScrollBarImp::SetOuter(IFWL_Widget* pOuter) {
    235   m_pOuter = pOuter;
    236   return FWL_ERR_Succeeded;
    237 }
    238 void CFWL_ScrollBarImp::DrawTrack(CFX_Graphics* pGraphics,
    239                                   IFWL_ThemeProvider* pTheme,
    240                                   FX_BOOL bLower,
    241                                   const CFX_Matrix* pMatrix) {
    242   CFWL_ThemeBackground param;
    243   param.m_pWidget = m_pInterface;
    244   param.m_iPart = bLower ? FWL_PART_SCB_LowerTrack : FWL_PART_SCB_UpperTrack;
    245   param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
    246                          ? FWL_PARTSTATE_SCB_Disabled
    247                          : (bLower ? m_iMinTrackState : m_iMaxTrackState);
    248   param.m_pGraphics = pGraphics;
    249   param.m_matrix.Concat(*pMatrix);
    250   param.m_rtPart = bLower ? m_rtMinTrack : m_rtMaxTrack;
    251   pTheme->DrawBackground(&param);
    252 }
    253 void CFWL_ScrollBarImp::DrawArrowBtn(CFX_Graphics* pGraphics,
    254                                      IFWL_ThemeProvider* pTheme,
    255                                      FX_BOOL bMinBtn,
    256                                      const CFX_Matrix* pMatrix) {
    257   CFWL_ThemeBackground param;
    258   param.m_pWidget = m_pInterface;
    259   param.m_iPart = bMinBtn ? FWL_PART_SCB_ForeArrow : FWL_PART_SCB_BackArrow;
    260   param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
    261                          ? FWL_PARTSTATE_SCB_Disabled
    262                          : (bMinBtn ? m_iMinButtonState : m_iMaxButtonState);
    263   param.m_pGraphics = pGraphics;
    264   param.m_matrix.Concat(*pMatrix);
    265   param.m_rtPart = bMinBtn ? m_rtMinBtn : m_rtMaxBtn;
    266   if (param.m_rtPart.height > 0 && param.m_rtPart.width > 0) {
    267     pTheme->DrawBackground(&param);
    268   }
    269 }
    270 void CFWL_ScrollBarImp::DrawThumb(CFX_Graphics* pGraphics,
    271                                   IFWL_ThemeProvider* pTheme,
    272                                   const CFX_Matrix* pMatrix) {
    273   if (!IsEnabled()) {
    274   }
    275   CFWL_ThemeBackground param;
    276   param.m_pWidget = m_pInterface;
    277   param.m_iPart = FWL_PART_SCB_Thumb;
    278   param.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
    279                          ? FWL_PARTSTATE_SCB_Disabled
    280                          : m_iThumbButtonState;
    281   param.m_pGraphics = pGraphics;
    282   param.m_matrix.Concat(*pMatrix);
    283   param.m_rtPart = m_rtThumb;
    284   pTheme->DrawBackground(&param);
    285 }
    286 void CFWL_ScrollBarImp::Layout() {
    287   IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
    288   CFWL_ThemePart part;
    289   part.m_pWidget = m_pInterface;
    290   m_fMinThumb = *static_cast<FX_FLOAT*>(
    291       pTheme->GetCapacity(&part, FWL_CAPACITY_SCB_Size));
    292   m_bCustomLayout = pTheme->IsCustomizedLayout(m_pInterface);
    293   GetClientRect(m_rtClient);
    294   CalcButtonLen();
    295   CalcMinButtonRect(m_rtMinBtn);
    296   CalcMaxButtonRect(m_rtMaxBtn);
    297   CalcThumbButtonRect(m_rtThumb);
    298   CalcMinTrackRect(m_rtMinTrack);
    299   CalcMaxTrackRect(m_rtMaxTrack);
    300 }
    301 void CFWL_ScrollBarImp::CalcButtonLen() {
    302   m_fButtonLen = IsVertical() ? m_rtClient.width : m_rtClient.height;
    303   FX_FLOAT fLength = IsVertical() ? m_rtClient.height : m_rtClient.width;
    304   if (fLength < m_fButtonLen * 2) {
    305     m_fButtonLen = fLength / 2;
    306     m_bMinSize = TRUE;
    307   } else {
    308     m_bMinSize = FALSE;
    309   }
    310 }
    311 void CFWL_ScrollBarImp::CalcMinButtonRect(CFX_RectF& rect) {
    312   if (m_bCustomLayout) {
    313     IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
    314     CFWL_ThemePart pPart;
    315     pPart.m_rtPart = m_rtMinBtn;
    316     pPart.m_pWidget = m_pInterface;
    317     pPart.m_iPart = FWL_PART_SCB_ForeArrow;
    318     pPart.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
    319                            ? FWL_PARTSTATE_SCB_Disabled
    320                            : m_iMinButtonState;
    321     pTheme->GetPartRect(&pPart, rect);
    322   } else {
    323     rect.left = m_rtClient.left;
    324     rect.top = m_rtClient.top;
    325     rect.width = IsVertical() ? m_rtClient.width : m_fButtonLen;
    326     rect.height = IsVertical() ? m_fButtonLen : m_rtClient.height;
    327   }
    328 }
    329 void CFWL_ScrollBarImp::CalcMaxButtonRect(CFX_RectF& rect) {
    330   if (m_bCustomLayout) {
    331     IFWL_ThemeProvider* pTheme = m_pProperties->m_pThemeProvider;
    332     CFWL_ThemePart pPart;
    333     pPart.m_rtPart = m_rtMaxBtn;
    334     pPart.m_pWidget = m_pInterface;
    335     pPart.m_iPart = FWL_PART_SCB_BackArrow;
    336     pPart.m_dwStates = (m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled)
    337                            ? FWL_PARTSTATE_SCB_Disabled
    338                            : m_iMaxButtonState;
    339     pTheme->GetPartRect(&pPart, rect);
    340   } else {
    341     rect.left =
    342         IsVertical() ? m_rtClient.left : m_rtClient.right() - m_fButtonLen;
    343     rect.top =
    344         IsVertical() ? m_rtClient.bottom() - m_fButtonLen : m_rtClient.top;
    345     rect.width = IsVertical() ? m_rtClient.width : m_fButtonLen;
    346     rect.height = IsVertical() ? m_fButtonLen : m_rtClient.height;
    347   }
    348 }
    349 void CFWL_ScrollBarImp::CalcThumbButtonRect(CFX_RectF& rect) {
    350   if (!IsEnabled()) {
    351     m_rtThumb.Reset();
    352     return;
    353   }
    354   if (m_bMinSize) {
    355     m_rtThumb.Empty();
    356     return;
    357   }
    358   FX_FLOAT fRange = m_fRangeMax - m_fRangeMin;
    359   memset(&rect, 0, sizeof(CFX_Rect));
    360   if (fRange < 0) {
    361     if (IsVertical()) {
    362       rect.Set(m_rtClient.left, m_rtMaxBtn.bottom(), m_rtClient.width, 0);
    363     } else {
    364       rect.Set(m_rtMaxBtn.right(), m_rtClient.top, 0, m_rtClient.height);
    365     }
    366     return;
    367   }
    368   CFX_RectF rtClient = m_rtClient;
    369   FX_FLOAT fLength = IsVertical() ? rtClient.height : rtClient.width;
    370   FX_FLOAT fSize = m_fButtonLen;
    371   if (m_bCustomLayout) {
    372     if (IsVertical()) {
    373       fLength = fLength - m_rtMinBtn.height - m_rtMaxBtn.height;
    374       if (fLength < m_rtMinBtn.height || fLength < m_rtMaxBtn.height) {
    375         fLength = 0.0f;
    376       }
    377     } else {
    378       fLength = fLength - m_rtMinBtn.width - m_rtMaxBtn.width;
    379       if (fLength < m_rtMinBtn.width || fLength < m_rtMaxBtn.width) {
    380         fLength = 0.0f;
    381       }
    382     }
    383   } else {
    384     fLength -= fSize * 2.0f;
    385     if (fLength < fSize) {
    386       fLength = 0.0f;
    387     }
    388   }
    389   FX_FLOAT fThumbSize = fLength * fLength / (fRange + fLength);
    390   if (fThumbSize < m_fMinThumb) {
    391     fThumbSize = m_fMinThumb;
    392   }
    393   FX_FLOAT fDiff = fLength - fThumbSize;
    394   if (fDiff < 0.0f) {
    395     fDiff = 0.0f;
    396   }
    397   FX_FLOAT fTrackPos = m_fTrackPos;
    398   if (fTrackPos > m_fRangeMax) {
    399     fTrackPos = m_fRangeMax;
    400   }
    401   if (fTrackPos < m_fRangeMin) {
    402     fTrackPos = m_fRangeMin;
    403   }
    404   if (!fRange)
    405     return;
    406   if (m_bCustomLayout) {
    407     FX_FLOAT iPos = fDiff * (fTrackPos - m_fRangeMin) / fRange;
    408     rect.left = rtClient.left;
    409     if (!IsVertical()) {
    410       if ((m_rtMinBtn.right() == m_rtMaxBtn.left && m_rtMinBtn.width > 0 &&
    411            m_rtMaxBtn.width > 0) ||
    412           (0 == m_rtMinBtn.width && 0 == m_rtMaxBtn.width)) {
    413         rect.left += iPos;
    414       } else {
    415         rect.left += m_rtMinBtn.right() + iPos;
    416       }
    417     }
    418     rect.top = rtClient.top;
    419     if (IsVertical()) {
    420       if ((m_rtMinBtn.bottom() == m_rtMaxBtn.top && m_rtMinBtn.height > 0 &&
    421            m_rtMaxBtn.height > 0) ||
    422           (0 == m_rtMinBtn.height && 0 == m_rtMaxBtn.height)) {
    423         rect.top += iPos;
    424       } else {
    425         rect.top += m_rtMinBtn.bottom() + iPos;
    426       }
    427     }
    428     rect.width = IsVertical() ? rtClient.width : fThumbSize;
    429     rect.height = IsVertical() ? fThumbSize : rtClient.height;
    430   } else {
    431     FX_FLOAT iPos = fSize + fDiff * (fTrackPos - m_fRangeMin) / fRange;
    432     rect.left = rtClient.left;
    433     if (!IsVertical()) {
    434       rect.left += iPos;
    435     }
    436     rect.top = rtClient.top;
    437     if (IsVertical()) {
    438       rect.top += iPos;
    439     }
    440     rect.width = IsVertical() ? rtClient.width : fThumbSize;
    441     rect.height = IsVertical() ? fThumbSize : rtClient.height;
    442   }
    443 }
    444 void CFWL_ScrollBarImp::CalcMinTrackRect(CFX_RectF& rect) {
    445   if (m_bMinSize) {
    446     rect.Empty();
    447     return;
    448   }
    449   FX_FLOAT fBottom = m_rtThumb.bottom();
    450   FX_FLOAT fRight = m_rtThumb.right();
    451   FX_FLOAT ix = (m_rtThumb.left + fRight) / 2;
    452   FX_FLOAT iy = (m_rtThumb.top + fBottom) / 2;
    453   rect.left = m_rtClient.left;
    454   rect.top = m_rtClient.top;
    455   FX_BOOL bVertical = IsVertical();
    456   rect.width = bVertical ? m_rtClient.width : ix;
    457   rect.height = bVertical ? iy : m_rtClient.height;
    458   if (m_bCustomLayout) {
    459     if (bVertical) {
    460       if (0 == m_rtMinBtn.height && 0 == m_rtMaxBtn.height) {
    461         rect.top = m_rtClient.top;
    462       } else if (m_rtMinBtn.top < m_rtThumb.top) {
    463         rect.top = m_rtMinBtn.bottom();
    464         rect.height -= (m_rtMinBtn.bottom() - m_rtClient.top);
    465       }
    466     } else {
    467       if (0 == m_rtMinBtn.width && 0 == m_rtMaxBtn.width) {
    468         rect.left = m_rtClient.left;
    469       } else if (m_rtMinBtn.left < m_rtThumb.left) {
    470         rect.left = m_rtMinBtn.right();
    471         rect.width -= (m_rtMinBtn.right() - m_rtClient.left);
    472       }
    473     }
    474   }
    475 }
    476 void CFWL_ScrollBarImp::CalcMaxTrackRect(CFX_RectF& rect) {
    477   if (m_bMinSize) {
    478     rect.Empty();
    479     return;
    480   }
    481   FX_FLOAT ix = (m_rtThumb.left + m_rtThumb.right()) / 2;
    482   FX_FLOAT iy = (m_rtThumb.top + m_rtThumb.bottom()) / 2;
    483   FX_BOOL bVertical = IsVertical();
    484   rect.left = bVertical ? m_rtClient.left : ix;
    485   rect.top = bVertical ? iy : m_rtClient.top;
    486   rect.width = bVertical ? m_rtClient.width : m_rtClient.right() - ix;
    487   rect.height = bVertical ? m_rtClient.bottom() - iy : m_rtClient.height;
    488   if (m_bCustomLayout) {
    489     if (bVertical) {
    490       if (m_rtMinBtn.top > m_rtThumb.top && m_rtMinBtn.height > 0 &&
    491           m_rtMaxBtn.height > 0) {
    492         rect.height -= (m_rtClient.bottom() - m_rtMinBtn.top);
    493       } else if (m_rtMinBtn.height > 0 && m_rtMaxBtn.height > 0) {
    494         rect.height -= (m_rtClient.bottom() - m_rtMaxBtn.top);
    495       }
    496     } else {
    497       if (m_rtMinBtn.left > m_rtThumb.left && m_rtMinBtn.width > 0 &&
    498           m_rtMaxBtn.width > 0) {
    499         rect.width -= (m_rtClient.right() - m_rtMinBtn.left);
    500       } else if (m_rtMinBtn.width > 0 && m_rtMaxBtn.width > 0) {
    501         rect.width -= (m_rtClient.right() - m_rtMaxBtn.left);
    502       }
    503     }
    504   }
    505 }
    506 FX_FLOAT CFWL_ScrollBarImp::GetTrackPointPos(FX_FLOAT fx, FX_FLOAT fy) {
    507   FX_FLOAT fDiffX = fx - m_cpTrackPointX;
    508   FX_FLOAT fDiffY = fy - m_cpTrackPointY;
    509   FX_FLOAT fRange = m_fRangeMax - m_fRangeMin;
    510   FX_FLOAT fPos;
    511   if (m_bCustomLayout) {
    512     if (IsVertical()) {
    513       if (0 == m_rtMinBtn.height && 0 == m_rtMaxBtn.height) {
    514         fPos = fRange * fDiffY / (m_rtClient.height - m_rtThumb.height);
    515       } else if (m_rtMinBtn.bottom() == m_rtMaxBtn.top) {
    516         fPos = fRange * fDiffY /
    517                (m_rtMinBtn.top - m_rtClient.top - m_rtThumb.height);
    518       } else {
    519         fPos = fRange * fDiffY /
    520                (m_rtMaxBtn.top - m_rtMinBtn.bottom() - m_rtThumb.height);
    521       }
    522     } else {
    523       if (0 == m_rtMinBtn.width && 0 == m_rtMaxBtn.width) {
    524         fPos = fRange * fDiffX / (m_rtClient.width - m_rtThumb.width);
    525       } else if (m_rtMinBtn.right() == m_rtMaxBtn.left) {
    526         fPos = fRange * fDiffX /
    527                (m_rtMinBtn.left - m_rtClient.left - m_rtThumb.width);
    528       } else {
    529         fPos = fRange * fDiffX /
    530                (m_rtMaxBtn.left - m_rtMinBtn.right() - m_rtThumb.width);
    531       }
    532     }
    533   } else {
    534     if (IsVertical()) {
    535       fPos = fRange * fDiffY /
    536              (m_rtMaxBtn.top - m_rtMinBtn.bottom() - m_rtThumb.height);
    537     } else {
    538       fPos = fRange * fDiffX /
    539              (m_rtMaxBtn.left - m_rtMinBtn.right() - m_rtThumb.width);
    540     }
    541   }
    542   fPos += m_fLastTrackPos;
    543   if (fPos < m_fRangeMin) {
    544     fPos = m_fRangeMin;
    545   }
    546   if (fPos > m_fRangeMax) {
    547     fPos = m_fRangeMax;
    548   }
    549   return fPos;
    550 }
    551 void CFWL_ScrollBarImp::GetTrackRect(CFX_RectF& rect, FX_BOOL bLower) {
    552   FX_BOOL bDisabled = m_pProperties->m_dwStates & FWL_WGTSTATE_Disabled;
    553   if (bDisabled || m_bCustomLayout) {
    554     rect = bLower ? m_rtMinTrack : m_rtMaxTrack;
    555   } else {
    556     FX_FLOAT fW = m_rtThumb.width / 2;
    557     FX_FLOAT fH = m_rtThumb.height / 2;
    558     FX_BOOL bVert = IsVertical();
    559     if (bLower) {
    560       if (bVert) {
    561         FX_FLOAT fMinTrackHeight = m_rtMinTrack.height - fH - m_rtMinBtn.height;
    562         fMinTrackHeight = (fMinTrackHeight >= 0.0f) ? fMinTrackHeight : 0.0f;
    563         rect.Set(m_rtMinTrack.left, m_rtMinTrack.top + m_rtMinBtn.height,
    564                  m_rtMinTrack.width, fMinTrackHeight);
    565       } else {
    566         FX_FLOAT fMinTrackWidth =
    567             m_rtMinTrack.width - fW - m_rtMinBtn.width + 2;
    568         fMinTrackWidth = (fMinTrackWidth >= 0.0f) ? fMinTrackWidth : 0.0f;
    569         rect.Set(m_rtMinTrack.left + m_rtMinBtn.width - 1, m_rtMinTrack.top,
    570                  fMinTrackWidth, m_rtMinTrack.height);
    571       }
    572     } else {
    573       if (bVert) {
    574         FX_FLOAT fMaxTrackHeight = m_rtMaxTrack.height - fH - m_rtMaxBtn.height;
    575         fMaxTrackHeight = (fMaxTrackHeight >= 0.0f) ? fMaxTrackHeight : 0.0f;
    576         rect.Set(m_rtMaxTrack.left, m_rtMaxTrack.top + fH, m_rtMaxTrack.width,
    577                  fMaxTrackHeight);
    578       } else {
    579         FX_FLOAT fMaxTrackWidth =
    580             m_rtMaxTrack.width - fW - m_rtMaxBtn.width + 2;
    581         fMaxTrackWidth = (fMaxTrackWidth >= 0.0f) ? fMaxTrackWidth : 0.0f;
    582         rect.Set(m_rtMaxTrack.left + fW, m_rtMaxTrack.top, fMaxTrackWidth,
    583                  m_rtMaxTrack.height);
    584       }
    585     }
    586   }
    587 }
    588 FX_BOOL CFWL_ScrollBarImp::SendEvent() {
    589   if (m_iMinButtonState == FWL_PARTSTATE_SCB_Pressed) {
    590     DoScroll(FWL_SCBCODE_StepBackward, m_fTrackPos);
    591     return FALSE;
    592   }
    593   if (m_iMaxButtonState == FWL_PARTSTATE_SCB_Pressed) {
    594     DoScroll(FWL_SCBCODE_StepForward, m_fTrackPos);
    595     return FALSE;
    596   }
    597   if (m_iMinTrackState == FWL_PARTSTATE_SCB_Pressed) {
    598     DoScroll(FWL_SCBCODE_PageBackward, m_fTrackPos);
    599     return m_rtThumb.Contains(m_cpTrackPointX, m_cpTrackPointY);
    600   }
    601   if (m_iMaxTrackState == FWL_PARTSTATE_SCB_Pressed) {
    602     DoScroll(FWL_SCBCODE_PageForward, m_fTrackPos);
    603     return m_rtThumb.Contains(m_cpTrackPointX, m_cpTrackPointY);
    604   }
    605   if (m_iMouseWheel) {
    606     FX_WORD dwCode =
    607         m_iMouseWheel < 0 ? FWL_SCBCODE_StepForward : FWL_SCBCODE_StepBackward;
    608     DoScroll(dwCode, m_fTrackPos);
    609   }
    610   return TRUE;
    611 }
    612 FX_BOOL CFWL_ScrollBarImp::OnScroll(FX_DWORD dwCode, FX_FLOAT fPos) {
    613   FX_BOOL bRet = TRUE;
    614   CFWL_EvtScroll ev;
    615   ev.m_iScrollCode = dwCode;
    616   ev.m_pSrcTarget = m_pInterface;
    617   ev.m_fPos = fPos;
    618   ev.m_pRet = &bRet;
    619   DispatchEvent(&ev);
    620   return bRet;
    621 }
    622 CFWL_ScrollBarImpDelegate::CFWL_ScrollBarImpDelegate(CFWL_ScrollBarImp* pOwner)
    623     : m_pOwner(pOwner) {}
    624 int32_t CFWL_ScrollBarImpDelegate::OnProcessMessage(CFWL_Message* pMessage) {
    625   if (!pMessage)
    626     return 0;
    627   int32_t iRet = 1;
    628   FX_DWORD dwMsgCode = pMessage->GetClassID();
    629   if (dwMsgCode == FWL_MSGHASH_Mouse) {
    630     CFWL_MsgMouse* pMsg = static_cast<CFWL_MsgMouse*>(pMessage);
    631     FX_DWORD dwCmd = pMsg->m_dwCmd;
    632     switch (dwCmd) {
    633       case FWL_MSGMOUSECMD_LButtonDown: {
    634         OnLButtonDown(pMsg->m_dwFlags, pMsg->m_fx, pMsg->m_fy);
    635         break;
    636       }
    637       case FWL_MSGMOUSECMD_LButtonUp: {
    638         OnLButtonUp(pMsg->m_dwFlags, pMsg->m_fx, pMsg->m_fy);
    639         break;
    640       }
    641       case FWL_MSGMOUSECMD_MouseMove: {
    642         OnMouseMove(pMsg->m_dwFlags, pMsg->m_fx, pMsg->m_fy);
    643         break;
    644       }
    645       case FWL_MSGMOUSECMD_MouseLeave: {
    646         OnMouseLeave();
    647         break;
    648       }
    649       default: { iRet = 0; }
    650     }
    651   } else if (dwMsgCode == FWL_MSGHASH_MouseWheel) {
    652     CFWL_MsgMouseWheel* pMsg = static_cast<CFWL_MsgMouseWheel*>(pMessage);
    653     OnMouseWheel(pMsg->m_fx, pMsg->m_fy, pMsg->m_dwFlags, pMsg->m_fDeltaX,
    654                  pMsg->m_fDeltaY);
    655   } else {
    656     iRet = 0;
    657   }
    658   return iRet;
    659 }
    660 FWL_ERR CFWL_ScrollBarImpDelegate::OnDrawWidget(CFX_Graphics* pGraphics,
    661                                                 const CFX_Matrix* pMatrix) {
    662   return m_pOwner->DrawWidget(pGraphics, pMatrix);
    663 }
    664 void CFWL_ScrollBarImpDelegate::OnLButtonDown(FX_DWORD dwFlags,
    665                                               FX_FLOAT fx,
    666                                               FX_FLOAT fy) {
    667   if (!m_pOwner->IsEnabled()) {
    668     return;
    669   }
    670   m_pOwner->m_bMouseDown = TRUE;
    671   m_pOwner->SetGrab(TRUE);
    672   m_pOwner->m_cpTrackPointX = fx;
    673   m_pOwner->m_cpTrackPointY = fy;
    674   m_pOwner->m_fLastTrackPos = m_pOwner->m_fTrackPos;
    675   if (m_pOwner->m_rtMinBtn.Contains(fx, fy)) {
    676     DoMouseDown(0, m_pOwner->m_rtMinBtn, m_pOwner->m_iMinButtonState, fx, fy);
    677   } else {
    678     if (m_pOwner->m_rtThumb.Contains(fx, fy)) {
    679       DoMouseDown(1, m_pOwner->m_rtThumb, m_pOwner->m_iThumbButtonState, fx,
    680                   fy);
    681     } else {
    682       if (m_pOwner->m_rtMaxBtn.Contains(fx, fy)) {
    683         DoMouseDown(2, m_pOwner->m_rtMaxBtn, m_pOwner->m_iMaxButtonState, fx,
    684                     fy);
    685       } else {
    686         if (m_pOwner->m_rtMinTrack.Contains(fx, fy)) {
    687           DoMouseDown(3, m_pOwner->m_rtMinTrack, m_pOwner->m_iMinTrackState, fx,
    688                       fy);
    689         } else {
    690           DoMouseDown(4, m_pOwner->m_rtMaxTrack, m_pOwner->m_iMaxTrackState, fx,
    691                       fy);
    692         }
    693       }
    694     }
    695   }
    696   if (!m_pOwner->SendEvent()) {
    697     m_pOwner->m_hTimer = FWL_StartTimer(m_pOwner, FWL_SCROLLBAR_Elapse);
    698   }
    699 }
    700 void CFWL_ScrollBarImpDelegate::OnLButtonUp(FX_DWORD dwFlags,
    701                                             FX_FLOAT fx,
    702                                             FX_FLOAT fy) {
    703   FWL_StopTimer(m_pOwner->m_hTimer);
    704   m_pOwner->m_bMouseDown = FALSE;
    705   DoMouseUp(0, m_pOwner->m_rtMinBtn, m_pOwner->m_iMinButtonState, fx, fy);
    706   DoMouseUp(1, m_pOwner->m_rtThumb, m_pOwner->m_iThumbButtonState, fx, fy);
    707   DoMouseUp(2, m_pOwner->m_rtMaxBtn, m_pOwner->m_iMaxButtonState, fx, fy);
    708   DoMouseUp(3, m_pOwner->m_rtMinTrack, m_pOwner->m_iMinTrackState, fx, fy);
    709   DoMouseUp(4, m_pOwner->m_rtMaxTrack, m_pOwner->m_iMaxTrackState, fx, fy);
    710   m_pOwner->SetGrab(FALSE);
    711 }
    712 void CFWL_ScrollBarImpDelegate::OnMouseMove(FX_DWORD dwFlags,
    713                                             FX_FLOAT fx,
    714                                             FX_FLOAT fy) {
    715   DoMouseMove(0, m_pOwner->m_rtMinBtn, m_pOwner->m_iMinButtonState, fx, fy);
    716   DoMouseMove(1, m_pOwner->m_rtThumb, m_pOwner->m_iThumbButtonState, fx, fy);
    717   DoMouseMove(2, m_pOwner->m_rtMaxBtn, m_pOwner->m_iMaxButtonState, fx, fy);
    718   DoMouseMove(3, m_pOwner->m_rtMinTrack, m_pOwner->m_iMinTrackState, fx, fy);
    719   DoMouseMove(4, m_pOwner->m_rtMaxTrack, m_pOwner->m_iMaxTrackState, fx, fy);
    720 }
    721 void CFWL_ScrollBarImpDelegate::OnMouseLeave() {
    722   DoMouseLeave(0, m_pOwner->m_rtMinBtn, m_pOwner->m_iMinButtonState);
    723   DoMouseLeave(1, m_pOwner->m_rtThumb, m_pOwner->m_iThumbButtonState);
    724   DoMouseLeave(2, m_pOwner->m_rtMaxBtn, m_pOwner->m_iMaxButtonState);
    725   DoMouseLeave(3, m_pOwner->m_rtMinTrack, m_pOwner->m_iMinTrackState);
    726   DoMouseLeave(4, m_pOwner->m_rtMaxTrack, m_pOwner->m_iMaxTrackState);
    727 }
    728 void CFWL_ScrollBarImpDelegate::OnMouseWheel(FX_FLOAT fx,
    729                                              FX_FLOAT fy,
    730                                              FX_DWORD dwFlags,
    731                                              FX_FLOAT fDeltaX,
    732                                              FX_FLOAT fDeltaY) {
    733   m_pOwner->m_iMouseWheel = (int32_t)fDeltaX;
    734   m_pOwner->SendEvent();
    735   m_pOwner->m_iMouseWheel = 0;
    736 }
    737 void CFWL_ScrollBarImpDelegate::DoMouseDown(int32_t iItem,
    738                                             const CFX_RectF& rtItem,
    739                                             int32_t& iState,
    740                                             FX_FLOAT fx,
    741                                             FX_FLOAT fy) {
    742   if (!rtItem.Contains(fx, fy)) {
    743     return;
    744   }
    745   if (iState == FWL_PARTSTATE_SCB_Pressed) {
    746     return;
    747   }
    748   iState = FWL_PARTSTATE_SCB_Pressed;
    749   m_pOwner->Repaint(&rtItem);
    750 }
    751 void CFWL_ScrollBarImpDelegate::DoMouseUp(int32_t iItem,
    752                                           const CFX_RectF& rtItem,
    753                                           int32_t& iState,
    754                                           FX_FLOAT fx,
    755                                           FX_FLOAT fy) {
    756   int32_t iNewState = rtItem.Contains(fx, fy) ? FWL_PARTSTATE_SCB_Hovered
    757                                               : FWL_PARTSTATE_SCB_Normal;
    758   if (iState == iNewState) {
    759     return;
    760   }
    761   iState = iNewState;
    762   m_pOwner->Repaint(&rtItem);
    763   m_pOwner->OnScroll(FWL_SCBCODE_EndScroll, m_pOwner->m_fTrackPos);
    764 }
    765 void CFWL_ScrollBarImpDelegate::DoMouseMove(int32_t iItem,
    766                                             const CFX_RectF& rtItem,
    767                                             int32_t& iState,
    768                                             FX_FLOAT fx,
    769                                             FX_FLOAT fy) {
    770   if (!m_pOwner->m_bMouseDown) {
    771     int32_t iNewState = rtItem.Contains(fx, fy) ? FWL_PARTSTATE_SCB_Hovered
    772                                                 : FWL_PARTSTATE_SCB_Normal;
    773     if (iState == iNewState) {
    774       return;
    775     }
    776     iState = iNewState;
    777     m_pOwner->Repaint(&rtItem);
    778   } else if ((2 == iItem) &&
    779              (m_pOwner->m_iThumbButtonState == FWL_PARTSTATE_SCB_Pressed)) {
    780     FX_FLOAT fPos = m_pOwner->GetTrackPointPos(fx, fy);
    781     m_pOwner->m_fTrackPos = fPos;
    782     m_pOwner->OnScroll(FWL_SCBCODE_TrackPos, fPos);
    783   }
    784 }
    785 void CFWL_ScrollBarImpDelegate::DoMouseLeave(int32_t iItem,
    786                                              const CFX_RectF& rtItem,
    787                                              int32_t& iState) {
    788   if (iState == FWL_PARTSTATE_SCB_Normal) {
    789     return;
    790   }
    791   iState = FWL_PARTSTATE_SCB_Normal;
    792   m_pOwner->Repaint(&rtItem);
    793 }
    794 void CFWL_ScrollBarImpDelegate::DoMouseHover(int32_t iItem,
    795                                              const CFX_RectF& rtItem,
    796                                              int32_t& iState) {
    797   if (iState == FWL_PARTSTATE_SCB_Hovered) {
    798     return;
    799   }
    800   iState = FWL_PARTSTATE_SCB_Hovered;
    801   m_pOwner->Repaint(&rtItem);
    802 }
    803