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_scroll_bar.h"
      8 
      9 #include <algorithm>
     10 #include <sstream>
     11 #include <vector>
     12 
     13 #include "core/fxge/cfx_pathdata.h"
     14 #include "core/fxge/cfx_renderdevice.h"
     15 #include "fpdfsdk/pwl/cpwl_wnd.h"
     16 
     17 namespace {
     18 
     19 constexpr float kButtonWidth = 9.0f;
     20 constexpr float kPosButtonMinWidth = 2.0f;
     21 constexpr float kScrollBarTriangleHalfLength = 2.0f;
     22 
     23 }  // namespace
     24 
     25 #define PWL_DEFAULT_HEAVYGRAYCOLOR CFX_Color(CFX_Color::kGray, 0.50)
     26 
     27 PWL_FLOATRANGE::PWL_FLOATRANGE() {
     28   Default();
     29 }
     30 
     31 PWL_FLOATRANGE::PWL_FLOATRANGE(float min, float max) {
     32   Set(min, max);
     33 }
     34 
     35 void PWL_FLOATRANGE::Default() {
     36   fMin = 0;
     37   fMax = 0;
     38 }
     39 
     40 void PWL_FLOATRANGE::Set(float min, float max) {
     41   if (min > max) {
     42     fMin = max;
     43     fMax = min;
     44   } else {
     45     fMin = min;
     46     fMax = max;
     47   }
     48 }
     49 
     50 bool PWL_FLOATRANGE::In(float x) const {
     51   return (IsFloatBigger(x, fMin) || IsFloatEqual(x, fMin)) &&
     52          (IsFloatSmaller(x, fMax) || IsFloatEqual(x, fMax));
     53 }
     54 
     55 float PWL_FLOATRANGE::GetWidth() const {
     56   return fMax - fMin;
     57 }
     58 
     59 PWL_SCROLL_PRIVATEDATA::PWL_SCROLL_PRIVATEDATA() {
     60   Default();
     61 }
     62 
     63 void PWL_SCROLL_PRIVATEDATA::Default() {
     64   ScrollRange.Default();
     65   fScrollPos = ScrollRange.fMin;
     66   fClientWidth = 0;
     67   fBigStep = 10;
     68   fSmallStep = 1;
     69 }
     70 
     71 void PWL_SCROLL_PRIVATEDATA::SetScrollRange(float min, float max) {
     72   ScrollRange.Set(min, max);
     73 
     74   if (IsFloatSmaller(fScrollPos, ScrollRange.fMin))
     75     fScrollPos = ScrollRange.fMin;
     76   if (IsFloatBigger(fScrollPos, ScrollRange.fMax))
     77     fScrollPos = ScrollRange.fMax;
     78 }
     79 
     80 void PWL_SCROLL_PRIVATEDATA::SetClientWidth(float width) {
     81   fClientWidth = width;
     82 }
     83 
     84 void PWL_SCROLL_PRIVATEDATA::SetSmallStep(float step) {
     85   fSmallStep = step;
     86 }
     87 
     88 void PWL_SCROLL_PRIVATEDATA::SetBigStep(float step) {
     89   fBigStep = step;
     90 }
     91 
     92 bool PWL_SCROLL_PRIVATEDATA::SetPos(float pos) {
     93   if (ScrollRange.In(pos)) {
     94     fScrollPos = pos;
     95     return true;
     96   }
     97   return false;
     98 }
     99 
    100 void PWL_SCROLL_PRIVATEDATA::AddSmall() {
    101   if (!SetPos(fScrollPos + fSmallStep))
    102     SetPos(ScrollRange.fMax);
    103 }
    104 
    105 void PWL_SCROLL_PRIVATEDATA::SubSmall() {
    106   if (!SetPos(fScrollPos - fSmallStep))
    107     SetPos(ScrollRange.fMin);
    108 }
    109 
    110 void PWL_SCROLL_PRIVATEDATA::AddBig() {
    111   if (!SetPos(fScrollPos + fBigStep))
    112     SetPos(ScrollRange.fMax);
    113 }
    114 
    115 void PWL_SCROLL_PRIVATEDATA::SubBig() {
    116   if (!SetPos(fScrollPos - fBigStep))
    117     SetPos(ScrollRange.fMin);
    118 }
    119 
    120 CPWL_SBButton::CPWL_SBButton(PWL_SCROLLBAR_TYPE eScrollBarType,
    121                              PWL_SBBUTTON_TYPE eButtonType) {
    122   m_eScrollBarType = eScrollBarType;
    123   m_eSBButtonType = eButtonType;
    124 
    125   m_bMouseDown = false;
    126 }
    127 
    128 CPWL_SBButton::~CPWL_SBButton() {}
    129 
    130 ByteString CPWL_SBButton::GetClassName() const {
    131   return "CPWL_SBButton";
    132 }
    133 
    134 void CPWL_SBButton::OnCreate(CreateParams* pParamsToAdjust) {
    135   pParamsToAdjust->eCursorType = FXCT_ARROW;
    136 }
    137 
    138 void CPWL_SBButton::DrawThisAppearance(CFX_RenderDevice* pDevice,
    139                                        const CFX_Matrix& mtUser2Device) {
    140   if (!IsVisible())
    141     return;
    142 
    143   CFX_FloatRect rectWnd = GetWindowRect();
    144   if (rectWnd.IsEmpty())
    145     return;
    146 
    147   CFX_PointF ptCenter = GetCenterPoint();
    148   int32_t nTransparency = GetTransparency();
    149 
    150   if (m_eScrollBarType == SBT_HSCROLL) {
    151     CPWL_Wnd::DrawThisAppearance(pDevice, mtUser2Device);
    152 
    153     CFX_PointF pt1;
    154     CFX_PointF pt2;
    155     CFX_PointF pt3;
    156     static constexpr float kScrollBarTriangleQuarterLength =
    157         kScrollBarTriangleHalfLength * 0.5;
    158     if (m_eSBButtonType == PSBT_MIN) {
    159       pt1 =
    160           CFX_PointF(ptCenter.x - kScrollBarTriangleQuarterLength, ptCenter.y);
    161       pt2 = CFX_PointF(ptCenter.x + kScrollBarTriangleQuarterLength,
    162                        ptCenter.y + kScrollBarTriangleHalfLength);
    163       pt3 = CFX_PointF(ptCenter.x + kScrollBarTriangleQuarterLength,
    164                        ptCenter.y - kScrollBarTriangleHalfLength);
    165     } else if (m_eSBButtonType == PSBT_MAX) {
    166       pt1 =
    167           CFX_PointF(ptCenter.x + kScrollBarTriangleQuarterLength, ptCenter.y);
    168       pt2 = CFX_PointF(ptCenter.x - kScrollBarTriangleQuarterLength,
    169                        ptCenter.y + kScrollBarTriangleHalfLength);
    170       pt3 = CFX_PointF(ptCenter.x - kScrollBarTriangleQuarterLength,
    171                        ptCenter.y - kScrollBarTriangleHalfLength);
    172     }
    173 
    174     if (rectWnd.right - rectWnd.left > kScrollBarTriangleHalfLength * 2 &&
    175         rectWnd.top - rectWnd.bottom > kScrollBarTriangleHalfLength) {
    176       CFX_PathData path;
    177       path.AppendPoint(pt1, FXPT_TYPE::MoveTo, false);
    178       path.AppendPoint(pt2, FXPT_TYPE::LineTo, false);
    179       path.AppendPoint(pt3, FXPT_TYPE::LineTo, false);
    180       path.AppendPoint(pt1, FXPT_TYPE::LineTo, false);
    181 
    182       pDevice->DrawPath(&path, &mtUser2Device, nullptr,
    183                         PWL_DEFAULT_BLACKCOLOR.ToFXColor(nTransparency), 0,
    184                         FXFILL_ALTERNATE);
    185     }
    186     return;
    187   }
    188 
    189   // draw border
    190   pDevice->DrawStrokeRect(&mtUser2Device, rectWnd,
    191                           ArgbEncode(nTransparency, 100, 100, 100), 0.0f);
    192   pDevice->DrawStrokeRect(&mtUser2Device, rectWnd.GetDeflated(0.5f, 0.5f),
    193                           ArgbEncode(nTransparency, 255, 255, 255), 1.0f);
    194 
    195   if (m_eSBButtonType != PSBT_POS) {
    196     // draw background
    197     if (IsEnabled()) {
    198       pDevice->DrawShadow(&mtUser2Device, true, false,
    199                           rectWnd.GetDeflated(1.0f, 1.0f), nTransparency, 80,
    200                           220);
    201     } else {
    202       pDevice->DrawFillRect(&mtUser2Device, rectWnd.GetDeflated(1.0f, 1.0f),
    203                             ArgbEncode(255, 255, 255, 255));
    204     }
    205 
    206     // draw arrow
    207     if (rectWnd.top - rectWnd.bottom > 6.0f) {
    208       float fX = rectWnd.left + 1.5f;
    209       float fY = rectWnd.bottom;
    210       std::vector<CFX_PointF> pts;
    211       if (m_eSBButtonType == PSBT_MIN) {
    212         pts.push_back(CFX_PointF(fX + 2.5f, fY + 4.0f));
    213         pts.push_back(CFX_PointF(fX + 2.5f, fY + 3.0f));
    214         pts.push_back(CFX_PointF(fX + 4.5f, fY + 5.0f));
    215         pts.push_back(CFX_PointF(fX + 6.5f, fY + 3.0f));
    216         pts.push_back(CFX_PointF(fX + 6.5f, fY + 4.0f));
    217         pts.push_back(CFX_PointF(fX + 4.5f, fY + 6.0f));
    218         pts.push_back(CFX_PointF(fX + 2.5f, fY + 4.0f));
    219       } else {
    220         pts.push_back(CFX_PointF(fX + 2.5f, fY + 5.0f));
    221         pts.push_back(CFX_PointF(fX + 2.5f, fY + 6.0f));
    222         pts.push_back(CFX_PointF(fX + 4.5f, fY + 4.0f));
    223         pts.push_back(CFX_PointF(fX + 6.5f, fY + 6.0f));
    224         pts.push_back(CFX_PointF(fX + 6.5f, fY + 5.0f));
    225         pts.push_back(CFX_PointF(fX + 4.5f, fY + 3.0f));
    226         pts.push_back(CFX_PointF(fX + 2.5f, fY + 5.0f));
    227       }
    228       pDevice->DrawFillArea(&mtUser2Device, pts.data(), 7,
    229                             IsEnabled()
    230                                 ? ArgbEncode(nTransparency, 255, 255, 255)
    231                                 : PWL_DEFAULT_HEAVYGRAYCOLOR.ToFXColor(255));
    232     }
    233     return;
    234   }
    235 
    236   if (IsEnabled()) {
    237     // draw shadow effect
    238     CFX_PointF ptTop = CFX_PointF(rectWnd.left, rectWnd.top - 1.0f);
    239     CFX_PointF ptBottom = CFX_PointF(rectWnd.left, rectWnd.bottom + 1.0f);
    240 
    241     ptTop.x += 1.5f;
    242     ptBottom.x += 1.5f;
    243 
    244     const FX_COLORREF refs[] = {ArgbEncode(nTransparency, 210, 210, 210),
    245                                 ArgbEncode(nTransparency, 220, 220, 220),
    246                                 ArgbEncode(nTransparency, 240, 240, 240),
    247                                 ArgbEncode(nTransparency, 240, 240, 240),
    248                                 ArgbEncode(nTransparency, 210, 210, 210),
    249                                 ArgbEncode(nTransparency, 180, 180, 180),
    250                                 ArgbEncode(nTransparency, 150, 150, 150),
    251                                 ArgbEncode(nTransparency, 150, 150, 150),
    252                                 ArgbEncode(nTransparency, 180, 180, 180),
    253                                 ArgbEncode(nTransparency, 210, 210, 210)};
    254     for (FX_COLORREF ref : refs) {
    255       pDevice->DrawStrokeLine(&mtUser2Device, ptTop, ptBottom, ref, 1.0f);
    256 
    257       ptTop.x += 1.0f;
    258       ptBottom.x += 1.0f;
    259     }
    260   } else {
    261     pDevice->DrawFillRect(&mtUser2Device, rectWnd.GetDeflated(0.5f, 0.5f),
    262                           ArgbEncode(255, 255, 255, 255));
    263   }
    264 
    265   // draw friction
    266   if (rectWnd.Height() <= 8.0f)
    267     return;
    268 
    269   FX_COLORREF crStroke = ArgbEncode(nTransparency, 120, 120, 120);
    270   if (!IsEnabled())
    271     crStroke = PWL_DEFAULT_HEAVYGRAYCOLOR.ToFXColor(255);
    272 
    273   float nFrictionWidth = 5.0f;
    274   float nFrictionHeight = 5.5f;
    275 
    276   CFX_PointF ptLeft = CFX_PointF(ptCenter.x - nFrictionWidth / 2.0f,
    277                                  ptCenter.y - nFrictionHeight / 2.0f + 0.5f);
    278   CFX_PointF ptRight = CFX_PointF(ptCenter.x + nFrictionWidth / 2.0f,
    279                                   ptCenter.y - nFrictionHeight / 2.0f + 0.5f);
    280 
    281   for (size_t i = 0; i < 3; ++i) {
    282     pDevice->DrawStrokeLine(&mtUser2Device, ptLeft, ptRight, crStroke, 1.0f);
    283     ptLeft.y += 2.0f;
    284     ptRight.y += 2.0f;
    285   }
    286 }
    287 
    288 bool CPWL_SBButton::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
    289   CPWL_Wnd::OnLButtonDown(point, nFlag);
    290 
    291   if (CPWL_Wnd* pParent = GetParentWindow())
    292     pParent->NotifyLButtonDown(this, point);
    293 
    294   m_bMouseDown = true;
    295   SetCapture();
    296 
    297   return true;
    298 }
    299 
    300 bool CPWL_SBButton::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
    301   CPWL_Wnd::OnLButtonUp(point, nFlag);
    302 
    303   if (CPWL_Wnd* pParent = GetParentWindow())
    304     pParent->NotifyLButtonUp(this, point);
    305 
    306   m_bMouseDown = false;
    307   ReleaseCapture();
    308 
    309   return true;
    310 }
    311 
    312 bool CPWL_SBButton::OnMouseMove(const CFX_PointF& point, uint32_t nFlag) {
    313   CPWL_Wnd::OnMouseMove(point, nFlag);
    314 
    315   if (CPWL_Wnd* pParent = GetParentWindow())
    316     pParent->NotifyMouseMove(this, point);
    317 
    318   return true;
    319 }
    320 
    321 CPWL_ScrollBar::CPWL_ScrollBar(PWL_SCROLLBAR_TYPE sbType)
    322     : m_sbType(sbType),
    323       m_pMinButton(nullptr),
    324       m_pMaxButton(nullptr),
    325       m_pPosButton(nullptr),
    326       m_bMouseDown(false),
    327       m_bMinOrMax(false),
    328       m_bNotifyForever(true) {}
    329 
    330 CPWL_ScrollBar::~CPWL_ScrollBar() {}
    331 
    332 ByteString CPWL_ScrollBar::GetClassName() const {
    333   return "CPWL_ScrollBar";
    334 }
    335 
    336 void CPWL_ScrollBar::OnCreate(CreateParams* pParamsToAdjust) {
    337   pParamsToAdjust->eCursorType = FXCT_ARROW;
    338 }
    339 
    340 void CPWL_ScrollBar::OnDestroy() {
    341   // Until cleanup takes place in the virtual destructor for CPWL_Wnd
    342   // subclasses, implement the virtual OnDestroy method that does the
    343   // cleanup first, then invokes the superclass OnDestroy ... gee,
    344   // like a dtor would.
    345   m_pMinButton.Release();
    346   m_pMaxButton.Release();
    347   m_pPosButton.Release();
    348   CPWL_Wnd::OnDestroy();
    349 }
    350 
    351 bool CPWL_ScrollBar::RePosChildWnd() {
    352   CFX_FloatRect rcClient = GetClientRect();
    353   CFX_FloatRect rcMinButton, rcMaxButton;
    354   float fBWidth = 0;
    355 
    356   switch (m_sbType) {
    357     case SBT_HSCROLL:
    358       if (rcClient.right - rcClient.left >
    359           kButtonWidth * 2 + kPosButtonMinWidth + 2) {
    360         rcMinButton = CFX_FloatRect(rcClient.left, rcClient.bottom,
    361                                     rcClient.left + kButtonWidth, rcClient.top);
    362         rcMaxButton =
    363             CFX_FloatRect(rcClient.right - kButtonWidth, rcClient.bottom,
    364                           rcClient.right, rcClient.top);
    365       } else {
    366         fBWidth = (rcClient.right - rcClient.left - kPosButtonMinWidth - 2) / 2;
    367 
    368         if (fBWidth > 0) {
    369           rcMinButton = CFX_FloatRect(rcClient.left, rcClient.bottom,
    370                                       rcClient.left + fBWidth, rcClient.top);
    371           rcMaxButton = CFX_FloatRect(rcClient.right - fBWidth, rcClient.bottom,
    372                                       rcClient.right, rcClient.top);
    373         } else {
    374           if (!SetVisible(false))
    375             return false;
    376         }
    377       }
    378       break;
    379     case SBT_VSCROLL:
    380       if (IsFloatBigger(rcClient.top - rcClient.bottom,
    381                         kButtonWidth * 2 + kPosButtonMinWidth + 2)) {
    382         rcMinButton = CFX_FloatRect(rcClient.left, rcClient.top - kButtonWidth,
    383                                     rcClient.right, rcClient.top);
    384         rcMaxButton =
    385             CFX_FloatRect(rcClient.left, rcClient.bottom, rcClient.right,
    386                           rcClient.bottom + kButtonWidth);
    387       } else {
    388         fBWidth = (rcClient.top - rcClient.bottom - kPosButtonMinWidth - 2) / 2;
    389 
    390         if (IsFloatBigger(fBWidth, 0)) {
    391           rcMinButton = CFX_FloatRect(rcClient.left, rcClient.top - fBWidth,
    392                                       rcClient.right, rcClient.top);
    393           rcMaxButton =
    394               CFX_FloatRect(rcClient.left, rcClient.bottom, rcClient.right,
    395                             rcClient.bottom + fBWidth);
    396         } else {
    397           if (!SetVisible(false))
    398             return false;
    399         }
    400       }
    401       break;
    402   }
    403 
    404   ObservedPtr thisObserved(this);
    405 
    406   if (m_pMinButton) {
    407     m_pMinButton->Move(rcMinButton, true, false);
    408     if (!thisObserved)
    409       return false;
    410   }
    411 
    412   if (m_pMaxButton) {
    413     m_pMaxButton->Move(rcMaxButton, true, false);
    414     if (!thisObserved)
    415       return false;
    416   }
    417 
    418   if (!MovePosButton(false))
    419     return false;
    420 
    421   return true;
    422 }
    423 
    424 void CPWL_ScrollBar::DrawThisAppearance(CFX_RenderDevice* pDevice,
    425                                         const CFX_Matrix& mtUser2Device) {
    426   CFX_FloatRect rectWnd = GetWindowRect();
    427 
    428   if (IsVisible() && !rectWnd.IsEmpty()) {
    429     pDevice->DrawFillRect(&mtUser2Device, rectWnd, GetBackgroundColor(),
    430                           GetTransparency());
    431 
    432     pDevice->DrawStrokeLine(
    433         &mtUser2Device, CFX_PointF(rectWnd.left + 2.0f, rectWnd.top - 2.0f),
    434         CFX_PointF(rectWnd.left + 2.0f, rectWnd.bottom + 2.0f),
    435         ArgbEncode(GetTransparency(), 100, 100, 100), 1.0f);
    436 
    437     pDevice->DrawStrokeLine(
    438         &mtUser2Device, CFX_PointF(rectWnd.right - 2.0f, rectWnd.top - 2.0f),
    439         CFX_PointF(rectWnd.right - 2.0f, rectWnd.bottom + 2.0f),
    440         ArgbEncode(GetTransparency(), 100, 100, 100), 1.0f);
    441   }
    442 }
    443 
    444 bool CPWL_ScrollBar::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
    445   CPWL_Wnd::OnLButtonDown(point, nFlag);
    446 
    447   if (HasFlag(PWS_AUTOTRANSPARENT)) {
    448     if (GetTransparency() != 255) {
    449       SetTransparency(255);
    450       if (!InvalidateRect(nullptr))
    451         return true;
    452     }
    453   }
    454 
    455   CFX_FloatRect rcMinArea, rcMaxArea;
    456 
    457   if (m_pPosButton && m_pPosButton->IsVisible()) {
    458     CFX_FloatRect rcClient = GetClientRect();
    459     CFX_FloatRect rcPosButton = m_pPosButton->GetWindowRect();
    460 
    461     switch (m_sbType) {
    462       case SBT_HSCROLL:
    463         rcMinArea = CFX_FloatRect(rcClient.left + kButtonWidth, rcClient.bottom,
    464                                   rcPosButton.left, rcClient.top);
    465         rcMaxArea = CFX_FloatRect(rcPosButton.right, rcClient.bottom,
    466                                   rcClient.right - kButtonWidth, rcClient.top);
    467 
    468         break;
    469       case SBT_VSCROLL:
    470         rcMinArea = CFX_FloatRect(rcClient.left, rcPosButton.top,
    471                                   rcClient.right, rcClient.top - kButtonWidth);
    472         rcMaxArea = CFX_FloatRect(rcClient.left, rcClient.bottom + kButtonWidth,
    473                                   rcClient.right, rcPosButton.bottom);
    474         break;
    475     }
    476 
    477     rcMinArea.Normalize();
    478     rcMaxArea.Normalize();
    479 
    480     if (rcMinArea.Contains(point)) {
    481       m_sData.SubBig();
    482       if (!MovePosButton(true))
    483         return true;
    484       NotifyScrollWindow();
    485     }
    486 
    487     if (rcMaxArea.Contains(point)) {
    488       m_sData.AddBig();
    489       if (!MovePosButton(true))
    490         return true;
    491       NotifyScrollWindow();
    492     }
    493   }
    494 
    495   return true;
    496 }
    497 
    498 bool CPWL_ScrollBar::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
    499   CPWL_Wnd::OnLButtonUp(point, nFlag);
    500 
    501   if (HasFlag(PWS_AUTOTRANSPARENT)) {
    502     if (GetTransparency() != PWL_SCROLLBAR_TRANSPARENCY) {
    503       SetTransparency(PWL_SCROLLBAR_TRANSPARENCY);
    504       if (!InvalidateRect(nullptr))
    505         return true;
    506     }
    507   }
    508 
    509   EndTimer();
    510   m_bMouseDown = false;
    511 
    512   return true;
    513 }
    514 
    515 void CPWL_ScrollBar::SetScrollInfo(const PWL_SCROLL_INFO& info) {
    516   if (info == m_OriginInfo)
    517     return;
    518 
    519   m_OriginInfo = info;
    520   float fMax =
    521       std::max(0.0f, info.fContentMax - info.fContentMin - info.fPlateWidth);
    522   SetScrollRange(0, fMax, info.fPlateWidth);
    523   SetScrollStep(info.fBigStep, info.fSmallStep);
    524 }
    525 
    526 void CPWL_ScrollBar::SetScrollPosition(float pos) {
    527   switch (m_sbType) {
    528     case SBT_HSCROLL:
    529       pos = pos - m_OriginInfo.fContentMin;
    530       break;
    531     case SBT_VSCROLL:
    532       pos = m_OriginInfo.fContentMax - pos;
    533       break;
    534   }
    535   SetScrollPos(pos);
    536 }
    537 
    538 void CPWL_ScrollBar::NotifyLButtonDown(CPWL_Wnd* child, const CFX_PointF& pos) {
    539   if (child == m_pMinButton)
    540     OnMinButtonLBDown(pos);
    541   else if (child == m_pMaxButton)
    542     OnMaxButtonLBDown(pos);
    543   else if (child == m_pPosButton)
    544     OnPosButtonLBDown(pos);
    545 }
    546 
    547 void CPWL_ScrollBar::NotifyLButtonUp(CPWL_Wnd* child, const CFX_PointF& pos) {
    548   if (child == m_pMinButton)
    549     OnMinButtonLBUp(pos);
    550   else if (child == m_pMaxButton)
    551     OnMaxButtonLBUp(pos);
    552   else if (child == m_pPosButton)
    553     OnPosButtonLBUp(pos);
    554 }
    555 
    556 void CPWL_ScrollBar::NotifyMouseMove(CPWL_Wnd* child, const CFX_PointF& pos) {
    557   if (child == m_pMinButton)
    558     OnMinButtonMouseMove(pos);
    559   else if (child == m_pMaxButton)
    560     OnMaxButtonMouseMove(pos);
    561   else if (child == m_pPosButton)
    562     OnPosButtonMouseMove(pos);
    563 }
    564 
    565 void CPWL_ScrollBar::CreateButtons(const CreateParams& cp) {
    566   CreateParams scp = cp;
    567   scp.pParentWnd = this;
    568   scp.dwBorderWidth = 2;
    569   scp.nBorderStyle = BorderStyle::BEVELED;
    570 
    571   scp.dwFlags =
    572       PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PWS_BACKGROUND | PWS_NOREFRESHCLIP;
    573 
    574   if (!m_pMinButton) {
    575     m_pMinButton = new CPWL_SBButton(m_sbType, PSBT_MIN);
    576     m_pMinButton->Create(scp);
    577   }
    578 
    579   if (!m_pMaxButton) {
    580     m_pMaxButton = new CPWL_SBButton(m_sbType, PSBT_MAX);
    581     m_pMaxButton->Create(scp);
    582   }
    583 
    584   if (!m_pPosButton) {
    585     m_pPosButton = new CPWL_SBButton(m_sbType, PSBT_POS);
    586 
    587     ObservedPtr thisObserved(this);
    588     if (!m_pPosButton->SetVisible(false) || !thisObserved)
    589       return;
    590     m_pPosButton->Create(scp);
    591   }
    592 }
    593 
    594 float CPWL_ScrollBar::GetScrollBarWidth() const {
    595   if (!IsVisible())
    596     return 0;
    597 
    598   return PWL_SCROLLBAR_WIDTH;
    599 }
    600 
    601 void CPWL_ScrollBar::SetScrollRange(float fMin,
    602                                     float fMax,
    603                                     float fClientWidth) {
    604   if (!m_pPosButton)
    605     return;
    606 
    607   m_sData.SetScrollRange(fMin, fMax);
    608   m_sData.SetClientWidth(fClientWidth);
    609 
    610   ObservedPtr thisObserved(this);
    611 
    612   if (IsFloatSmaller(m_sData.ScrollRange.GetWidth(), 0.0f)) {
    613     m_pPosButton->SetVisible(false);
    614     // Note, |this| may no longer be viable at this point. If more work needs
    615     // to be done, check thisObserved.
    616     return;
    617   }
    618 
    619   if (!m_pPosButton->SetVisible(true) || !thisObserved)
    620     return;
    621 
    622   MovePosButton(true);
    623   // Note, |this| may no longer be viable at this point. If more work needs
    624   // to be done, check the return value of MovePosButton().
    625 }
    626 
    627 void CPWL_ScrollBar::SetScrollPos(float fPos) {
    628   float fOldPos = m_sData.fScrollPos;
    629   m_sData.SetPos(fPos);
    630   if (!IsFloatEqual(m_sData.fScrollPos, fOldPos)) {
    631     MovePosButton(true);
    632     // Note, |this| may no longer be viable at this point. If more work needs
    633     // to be done, check the return value of MovePosButton().
    634   }
    635 }
    636 
    637 void CPWL_ScrollBar::SetScrollStep(float fBigStep, float fSmallStep) {
    638   m_sData.SetBigStep(fBigStep);
    639   m_sData.SetSmallStep(fSmallStep);
    640 }
    641 
    642 bool CPWL_ScrollBar::MovePosButton(bool bRefresh) {
    643   ASSERT(m_pMinButton);
    644   ASSERT(m_pMaxButton);
    645 
    646   if (m_pPosButton->IsVisible()) {
    647     CFX_FloatRect rcClient;
    648     CFX_FloatRect rcPosArea, rcPosButton;
    649 
    650     rcClient = GetClientRect();
    651     rcPosArea = GetScrollArea();
    652 
    653     float fLeft, fRight, fTop, fBottom;
    654 
    655     switch (m_sbType) {
    656       case SBT_HSCROLL:
    657         fLeft = TrueToFace(m_sData.fScrollPos);
    658         fRight = TrueToFace(m_sData.fScrollPos + m_sData.fClientWidth);
    659 
    660         if (fRight - fLeft < kPosButtonMinWidth)
    661           fRight = fLeft + kPosButtonMinWidth;
    662 
    663         if (fRight > rcPosArea.right) {
    664           fRight = rcPosArea.right;
    665           fLeft = fRight - kPosButtonMinWidth;
    666         }
    667 
    668         rcPosButton =
    669             CFX_FloatRect(fLeft, rcPosArea.bottom, fRight, rcPosArea.top);
    670 
    671         break;
    672       case SBT_VSCROLL:
    673         fBottom = TrueToFace(m_sData.fScrollPos + m_sData.fClientWidth);
    674         fTop = TrueToFace(m_sData.fScrollPos);
    675 
    676         if (IsFloatSmaller(fTop - fBottom, kPosButtonMinWidth))
    677           fBottom = fTop - kPosButtonMinWidth;
    678 
    679         if (IsFloatSmaller(fBottom, rcPosArea.bottom)) {
    680           fBottom = rcPosArea.bottom;
    681           fTop = fBottom + kPosButtonMinWidth;
    682         }
    683 
    684         rcPosButton =
    685             CFX_FloatRect(rcPosArea.left, fBottom, rcPosArea.right, fTop);
    686 
    687         break;
    688     }
    689 
    690     ObservedPtr thisObserved(this);
    691 
    692     m_pPosButton->Move(rcPosButton, true, bRefresh);
    693     if (!thisObserved)
    694       return false;
    695   }
    696 
    697   return true;
    698 }
    699 
    700 void CPWL_ScrollBar::OnMinButtonLBDown(const CFX_PointF& point) {
    701   m_sData.SubSmall();
    702   if (!MovePosButton(true))
    703     return;
    704   NotifyScrollWindow();
    705 
    706   m_bMinOrMax = true;
    707 
    708   EndTimer();
    709   BeginTimer(100);
    710 }
    711 
    712 void CPWL_ScrollBar::OnMinButtonLBUp(const CFX_PointF& point) {}
    713 
    714 void CPWL_ScrollBar::OnMinButtonMouseMove(const CFX_PointF& point) {}
    715 
    716 void CPWL_ScrollBar::OnMaxButtonLBDown(const CFX_PointF& point) {
    717   m_sData.AddSmall();
    718   if (!MovePosButton(true))
    719     return;
    720   NotifyScrollWindow();
    721 
    722   m_bMinOrMax = false;
    723 
    724   EndTimer();
    725   BeginTimer(100);
    726 }
    727 
    728 void CPWL_ScrollBar::OnMaxButtonLBUp(const CFX_PointF& point) {}
    729 
    730 void CPWL_ScrollBar::OnMaxButtonMouseMove(const CFX_PointF& point) {}
    731 
    732 void CPWL_ScrollBar::OnPosButtonLBDown(const CFX_PointF& point) {
    733   m_bMouseDown = true;
    734 
    735   if (m_pPosButton) {
    736     CFX_FloatRect rcPosButton = m_pPosButton->GetWindowRect();
    737 
    738     switch (m_sbType) {
    739       case SBT_HSCROLL:
    740         m_nOldPos = point.x;
    741         m_fOldPosButton = rcPosButton.left;
    742         break;
    743       case SBT_VSCROLL:
    744         m_nOldPos = point.y;
    745         m_fOldPosButton = rcPosButton.top;
    746         break;
    747     }
    748   }
    749 }
    750 
    751 void CPWL_ScrollBar::OnPosButtonLBUp(const CFX_PointF& point) {
    752   if (m_bMouseDown) {
    753     if (!m_bNotifyForever)
    754       NotifyScrollWindow();
    755   }
    756   m_bMouseDown = false;
    757 }
    758 
    759 void CPWL_ScrollBar::OnPosButtonMouseMove(const CFX_PointF& point) {
    760   float fOldScrollPos = m_sData.fScrollPos;
    761 
    762   float fNewPos = 0;
    763 
    764   switch (m_sbType) {
    765     case SBT_HSCROLL:
    766       if (fabs(point.x - m_nOldPos) < 1)
    767         return;
    768       fNewPos = FaceToTrue(m_fOldPosButton + point.x - m_nOldPos);
    769       break;
    770     case SBT_VSCROLL:
    771       if (fabs(point.y - m_nOldPos) < 1)
    772         return;
    773       fNewPos = FaceToTrue(m_fOldPosButton + point.y - m_nOldPos);
    774       break;
    775   }
    776 
    777   if (m_bMouseDown) {
    778     switch (m_sbType) {
    779       case SBT_HSCROLL:
    780 
    781         if (IsFloatSmaller(fNewPos, m_sData.ScrollRange.fMin)) {
    782           fNewPos = m_sData.ScrollRange.fMin;
    783         }
    784 
    785         if (IsFloatBigger(fNewPos, m_sData.ScrollRange.fMax)) {
    786           fNewPos = m_sData.ScrollRange.fMax;
    787         }
    788 
    789         m_sData.SetPos(fNewPos);
    790 
    791         break;
    792       case SBT_VSCROLL:
    793 
    794         if (IsFloatSmaller(fNewPos, m_sData.ScrollRange.fMin)) {
    795           fNewPos = m_sData.ScrollRange.fMin;
    796         }
    797 
    798         if (IsFloatBigger(fNewPos, m_sData.ScrollRange.fMax)) {
    799           fNewPos = m_sData.ScrollRange.fMax;
    800         }
    801 
    802         m_sData.SetPos(fNewPos);
    803 
    804         break;
    805     }
    806 
    807     if (!IsFloatEqual(fOldScrollPos, m_sData.fScrollPos)) {
    808       if (!MovePosButton(true))
    809         return;
    810 
    811       if (m_bNotifyForever)
    812         NotifyScrollWindow();
    813     }
    814   }
    815 }
    816 
    817 void CPWL_ScrollBar::NotifyScrollWindow() {
    818   CPWL_Wnd* pParent = GetParentWindow();
    819   if (!pParent || m_sbType != SBT_VSCROLL)
    820     return;
    821 
    822   pParent->ScrollWindowVertically(m_OriginInfo.fContentMax -
    823                                   m_sData.fScrollPos);
    824 }
    825 
    826 CFX_FloatRect CPWL_ScrollBar::GetScrollArea() const {
    827   CFX_FloatRect rcClient = GetClientRect();
    828   CFX_FloatRect rcArea;
    829 
    830   if (!m_pMinButton || !m_pMaxButton)
    831     return rcClient;
    832 
    833   CFX_FloatRect rcMin = m_pMinButton->GetWindowRect();
    834   CFX_FloatRect rcMax = m_pMaxButton->GetWindowRect();
    835 
    836   float fMinWidth = rcMin.right - rcMin.left;
    837   float fMinHeight = rcMin.top - rcMin.bottom;
    838   float fMaxWidth = rcMax.right - rcMax.left;
    839   float fMaxHeight = rcMax.top - rcMax.bottom;
    840 
    841   switch (m_sbType) {
    842     case SBT_HSCROLL:
    843       if (rcClient.right - rcClient.left > fMinWidth + fMaxWidth + 2) {
    844         rcArea = CFX_FloatRect(rcClient.left + fMinWidth + 1, rcClient.bottom,
    845                                rcClient.right - fMaxWidth - 1, rcClient.top);
    846       } else {
    847         rcArea = CFX_FloatRect(rcClient.left + fMinWidth + 1, rcClient.bottom,
    848                                rcClient.left + fMinWidth + 1, rcClient.top);
    849       }
    850       break;
    851     case SBT_VSCROLL:
    852       if (rcClient.top - rcClient.bottom > fMinHeight + fMaxHeight + 2) {
    853         rcArea = CFX_FloatRect(rcClient.left, rcClient.bottom + fMinHeight + 1,
    854                                rcClient.right, rcClient.top - fMaxHeight - 1);
    855       } else {
    856         rcArea =
    857             CFX_FloatRect(rcClient.left, rcClient.bottom + fMinHeight + 1,
    858                           rcClient.right, rcClient.bottom + fMinHeight + 1);
    859       }
    860       break;
    861   }
    862 
    863   rcArea.Normalize();
    864 
    865   return rcArea;
    866 }
    867 
    868 float CPWL_ScrollBar::TrueToFace(float fTrue) {
    869   CFX_FloatRect rcPosArea;
    870   rcPosArea = GetScrollArea();
    871 
    872   float fFactWidth = m_sData.ScrollRange.GetWidth() + m_sData.fClientWidth;
    873   fFactWidth = fFactWidth == 0 ? 1 : fFactWidth;
    874 
    875   float fFace = 0;
    876 
    877   switch (m_sbType) {
    878     case SBT_HSCROLL:
    879       fFace = rcPosArea.left +
    880               fTrue * (rcPosArea.right - rcPosArea.left) / fFactWidth;
    881       break;
    882     case SBT_VSCROLL:
    883       fFace = rcPosArea.top -
    884               fTrue * (rcPosArea.top - rcPosArea.bottom) / fFactWidth;
    885       break;
    886   }
    887 
    888   return fFace;
    889 }
    890 
    891 float CPWL_ScrollBar::FaceToTrue(float fFace) {
    892   CFX_FloatRect rcPosArea;
    893   rcPosArea = GetScrollArea();
    894 
    895   float fFactWidth = m_sData.ScrollRange.GetWidth() + m_sData.fClientWidth;
    896   fFactWidth = fFactWidth == 0 ? 1 : fFactWidth;
    897 
    898   float fTrue = 0;
    899 
    900   switch (m_sbType) {
    901     case SBT_HSCROLL:
    902       fTrue = (fFace - rcPosArea.left) * fFactWidth /
    903               (rcPosArea.right - rcPosArea.left);
    904       break;
    905     case SBT_VSCROLL:
    906       fTrue = (rcPosArea.top - fFace) * fFactWidth /
    907               (rcPosArea.top - rcPosArea.bottom);
    908       break;
    909   }
    910 
    911   return fTrue;
    912 }
    913 
    914 void CPWL_ScrollBar::CreateChildWnd(const CreateParams& cp) {
    915   CreateButtons(cp);
    916 }
    917 
    918 void CPWL_ScrollBar::TimerProc() {
    919   PWL_SCROLL_PRIVATEDATA sTemp = m_sData;
    920   if (m_bMinOrMax)
    921     m_sData.SubSmall();
    922   else
    923     m_sData.AddSmall();
    924 
    925   if (sTemp != m_sData) {
    926     if (!MovePosButton(true))
    927       return;
    928     NotifyScrollWindow();
    929   }
    930 }
    931