Home | History | Annotate | Download | only in pdfwindow
      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/pdfwindow/PWL_ScrollBar.h"
      8 
      9 #include "core/fxge/cfx_pathdata.h"
     10 #include "core/fxge/cfx_renderdevice.h"
     11 #include "fpdfsdk/pdfwindow/PWL_Utils.h"
     12 #include "fpdfsdk/pdfwindow/PWL_Wnd.h"
     13 
     14 PWL_FLOATRANGE::PWL_FLOATRANGE() {
     15   Default();
     16 }
     17 
     18 PWL_FLOATRANGE::PWL_FLOATRANGE(FX_FLOAT min, FX_FLOAT max) {
     19   Set(min, max);
     20 }
     21 
     22 void PWL_FLOATRANGE::Default() {
     23   fMin = 0;
     24   fMax = 0;
     25 }
     26 
     27 void PWL_FLOATRANGE::Set(FX_FLOAT min, FX_FLOAT max) {
     28   if (min > max) {
     29     fMin = max;
     30     fMax = min;
     31   } else {
     32     fMin = min;
     33     fMax = max;
     34   }
     35 }
     36 
     37 bool PWL_FLOATRANGE::In(FX_FLOAT x) const {
     38   return (IsFloatBigger(x, fMin) || IsFloatEqual(x, fMin)) &&
     39          (IsFloatSmaller(x, fMax) || IsFloatEqual(x, fMax));
     40 }
     41 
     42 FX_FLOAT PWL_FLOATRANGE::GetWidth() const {
     43   return fMax - fMin;
     44 }
     45 
     46 PWL_SCROLL_PRIVATEDATA::PWL_SCROLL_PRIVATEDATA() {
     47   Default();
     48 }
     49 
     50 void PWL_SCROLL_PRIVATEDATA::Default() {
     51   ScrollRange.Default();
     52   fScrollPos = ScrollRange.fMin;
     53   fClientWidth = 0;
     54   fBigStep = 10;
     55   fSmallStep = 1;
     56 }
     57 
     58 void PWL_SCROLL_PRIVATEDATA::SetScrollRange(FX_FLOAT min, FX_FLOAT max) {
     59   ScrollRange.Set(min, max);
     60 
     61   if (IsFloatSmaller(fScrollPos, ScrollRange.fMin))
     62     fScrollPos = ScrollRange.fMin;
     63   if (IsFloatBigger(fScrollPos, ScrollRange.fMax))
     64     fScrollPos = ScrollRange.fMax;
     65 }
     66 
     67 void PWL_SCROLL_PRIVATEDATA::SetClientWidth(FX_FLOAT width) {
     68   fClientWidth = width;
     69 }
     70 
     71 void PWL_SCROLL_PRIVATEDATA::SetSmallStep(FX_FLOAT step) {
     72   fSmallStep = step;
     73 }
     74 
     75 void PWL_SCROLL_PRIVATEDATA::SetBigStep(FX_FLOAT step) {
     76   fBigStep = step;
     77 }
     78 
     79 bool PWL_SCROLL_PRIVATEDATA::SetPos(FX_FLOAT pos) {
     80   if (ScrollRange.In(pos)) {
     81     fScrollPos = pos;
     82     return true;
     83   }
     84   return false;
     85 }
     86 
     87 void PWL_SCROLL_PRIVATEDATA::AddSmall() {
     88   if (!SetPos(fScrollPos + fSmallStep))
     89     SetPos(ScrollRange.fMax);
     90 }
     91 
     92 void PWL_SCROLL_PRIVATEDATA::SubSmall() {
     93   if (!SetPos(fScrollPos - fSmallStep))
     94     SetPos(ScrollRange.fMin);
     95 }
     96 
     97 void PWL_SCROLL_PRIVATEDATA::AddBig() {
     98   if (!SetPos(fScrollPos + fBigStep))
     99     SetPos(ScrollRange.fMax);
    100 }
    101 
    102 void PWL_SCROLL_PRIVATEDATA::SubBig() {
    103   if (!SetPos(fScrollPos - fBigStep))
    104     SetPos(ScrollRange.fMin);
    105 }
    106 
    107 CPWL_SBButton::CPWL_SBButton(PWL_SCROLLBAR_TYPE eScrollBarType,
    108                              PWL_SBBUTTON_TYPE eButtonType) {
    109   m_eScrollBarType = eScrollBarType;
    110   m_eSBButtonType = eButtonType;
    111 
    112   m_bMouseDown = false;
    113 }
    114 
    115 CPWL_SBButton::~CPWL_SBButton() {}
    116 
    117 CFX_ByteString CPWL_SBButton::GetClassName() const {
    118   return "CPWL_SBButton";
    119 }
    120 
    121 void CPWL_SBButton::OnCreate(PWL_CREATEPARAM& cp) {
    122   cp.eCursorType = FXCT_ARROW;
    123 }
    124 
    125 void CPWL_SBButton::GetThisAppearanceStream(CFX_ByteTextBuf& sAppStream) {
    126   CPWL_Wnd::GetThisAppearanceStream(sAppStream);
    127 
    128   if (!IsVisible())
    129     return;
    130 
    131   CFX_ByteTextBuf sButton;
    132 
    133   CFX_FloatRect rectWnd = GetWindowRect();
    134 
    135   if (rectWnd.IsEmpty())
    136     return;
    137 
    138   sAppStream << "q\n";
    139 
    140   CFX_PointF ptCenter = GetCenterPoint();
    141 
    142   switch (m_eScrollBarType) {
    143     case SBT_HSCROLL:
    144       switch (m_eSBButtonType) {
    145         case PSBT_MIN: {
    146           CFX_PointF pt1(ptCenter.x - PWL_TRIANGLE_HALFLEN * 0.5f, ptCenter.y);
    147           CFX_PointF pt2(ptCenter.x + PWL_TRIANGLE_HALFLEN * 0.5f,
    148                          ptCenter.y + PWL_TRIANGLE_HALFLEN);
    149           CFX_PointF pt3(ptCenter.x + PWL_TRIANGLE_HALFLEN * 0.5f,
    150                          ptCenter.y - PWL_TRIANGLE_HALFLEN);
    151 
    152           if (rectWnd.right - rectWnd.left > PWL_TRIANGLE_HALFLEN * 2 &&
    153               rectWnd.top - rectWnd.bottom > PWL_TRIANGLE_HALFLEN) {
    154             sButton << "0 g\n";
    155             sButton << pt1.x << " " << pt1.y << " m\n";
    156             sButton << pt2.x << " " << pt2.y << " l\n";
    157             sButton << pt3.x << " " << pt3.y << " l\n";
    158             sButton << pt1.x << " " << pt1.y << " l f\n";
    159 
    160             sAppStream << sButton;
    161           }
    162         } break;
    163         case PSBT_MAX: {
    164           CFX_PointF pt1(ptCenter.x + PWL_TRIANGLE_HALFLEN * 0.5f, ptCenter.y);
    165           CFX_PointF pt2(ptCenter.x - PWL_TRIANGLE_HALFLEN * 0.5f,
    166                          ptCenter.y + PWL_TRIANGLE_HALFLEN);
    167           CFX_PointF pt3(ptCenter.x - PWL_TRIANGLE_HALFLEN * 0.5f,
    168                          ptCenter.y - PWL_TRIANGLE_HALFLEN);
    169 
    170           if (rectWnd.right - rectWnd.left > PWL_TRIANGLE_HALFLEN * 2 &&
    171               rectWnd.top - rectWnd.bottom > PWL_TRIANGLE_HALFLEN) {
    172             sButton << "0 g\n";
    173             sButton << pt1.x << " " << pt1.y << " m\n";
    174             sButton << pt2.x << " " << pt2.y << " l\n";
    175             sButton << pt3.x << " " << pt3.y << " l\n";
    176             sButton << pt1.x << " " << pt1.y << " l f\n";
    177 
    178             sAppStream << sButton;
    179           }
    180         } break;
    181         default:
    182           break;
    183       }
    184       break;
    185     case SBT_VSCROLL:
    186       switch (m_eSBButtonType) {
    187         case PSBT_MIN: {
    188           CFX_PointF pt1(ptCenter.x - PWL_TRIANGLE_HALFLEN,
    189                          ptCenter.y - PWL_TRIANGLE_HALFLEN * 0.5f);
    190           CFX_PointF pt2(ptCenter.x + PWL_TRIANGLE_HALFLEN,
    191                          ptCenter.y - PWL_TRIANGLE_HALFLEN * 0.5f);
    192           CFX_PointF pt3(ptCenter.x, ptCenter.y + PWL_TRIANGLE_HALFLEN * 0.5f);
    193 
    194           if (rectWnd.right - rectWnd.left > PWL_TRIANGLE_HALFLEN * 2 &&
    195               rectWnd.top - rectWnd.bottom > PWL_TRIANGLE_HALFLEN) {
    196             sButton << "0 g\n";
    197             sButton << pt1.x << " " << pt1.y << " m\n";
    198             sButton << pt2.x << " " << pt2.y << " l\n";
    199             sButton << pt3.x << " " << pt3.y << " l\n";
    200             sButton << pt1.x << " " << pt1.y << " l f\n";
    201 
    202             sAppStream << sButton;
    203           }
    204         } break;
    205         case PSBT_MAX: {
    206           CFX_PointF pt1(ptCenter.x - PWL_TRIANGLE_HALFLEN,
    207                          ptCenter.y + PWL_TRIANGLE_HALFLEN * 0.5f);
    208           CFX_PointF pt2(ptCenter.x + PWL_TRIANGLE_HALFLEN,
    209                          ptCenter.y + PWL_TRIANGLE_HALFLEN * 0.5f);
    210           CFX_PointF pt3(ptCenter.x, ptCenter.y - PWL_TRIANGLE_HALFLEN * 0.5f);
    211 
    212           if (rectWnd.right - rectWnd.left > PWL_TRIANGLE_HALFLEN * 2 &&
    213               rectWnd.top - rectWnd.bottom > PWL_TRIANGLE_HALFLEN) {
    214             sButton << "0 g\n";
    215             sButton << pt1.x << " " << pt1.y << " m\n";
    216             sButton << pt2.x << " " << pt2.y << " l\n";
    217             sButton << pt3.x << " " << pt3.y << " l\n";
    218             sButton << pt1.x << " " << pt1.y << " l f\n";
    219 
    220             sAppStream << sButton;
    221           }
    222         } break;
    223         default:
    224           break;
    225       }
    226       break;
    227     default:
    228       break;
    229   }
    230 
    231   sAppStream << "Q\n";
    232 }
    233 
    234 void CPWL_SBButton::DrawThisAppearance(CFX_RenderDevice* pDevice,
    235                                        CFX_Matrix* pUser2Device) {
    236   if (!IsVisible())
    237     return;
    238 
    239   CFX_FloatRect rectWnd = GetWindowRect();
    240   if (rectWnd.IsEmpty())
    241     return;
    242 
    243   CFX_PointF ptCenter = GetCenterPoint();
    244   int32_t nTransparency = GetTransparency();
    245 
    246   switch (m_eScrollBarType) {
    247     case SBT_HSCROLL:
    248       CPWL_Wnd::DrawThisAppearance(pDevice, pUser2Device);
    249       switch (m_eSBButtonType) {
    250         case PSBT_MIN: {
    251           CFX_PointF pt1(ptCenter.x - PWL_TRIANGLE_HALFLEN * 0.5f, ptCenter.y);
    252           CFX_PointF pt2(ptCenter.x + PWL_TRIANGLE_HALFLEN * 0.5f,
    253                          ptCenter.y + PWL_TRIANGLE_HALFLEN);
    254           CFX_PointF pt3(ptCenter.x + PWL_TRIANGLE_HALFLEN * 0.5f,
    255                          ptCenter.y - PWL_TRIANGLE_HALFLEN);
    256 
    257           if (rectWnd.right - rectWnd.left > PWL_TRIANGLE_HALFLEN * 2 &&
    258               rectWnd.top - rectWnd.bottom > PWL_TRIANGLE_HALFLEN) {
    259             CFX_PathData path;
    260             path.AppendPoint(pt1, FXPT_TYPE::MoveTo, false);
    261             path.AppendPoint(pt2, FXPT_TYPE::LineTo, false);
    262             path.AppendPoint(pt3, FXPT_TYPE::LineTo, false);
    263             path.AppendPoint(pt1, FXPT_TYPE::LineTo, false);
    264 
    265             pDevice->DrawPath(&path, pUser2Device, nullptr,
    266                               PWL_DEFAULT_BLACKCOLOR.ToFXColor(nTransparency),
    267                               0, FXFILL_ALTERNATE);
    268           }
    269         } break;
    270         case PSBT_MAX: {
    271           CFX_PointF pt1(ptCenter.x + PWL_TRIANGLE_HALFLEN * 0.5f, ptCenter.y);
    272           CFX_PointF pt2(ptCenter.x - PWL_TRIANGLE_HALFLEN * 0.5f,
    273                          ptCenter.y + PWL_TRIANGLE_HALFLEN);
    274           CFX_PointF pt3(ptCenter.x - PWL_TRIANGLE_HALFLEN * 0.5f,
    275                          ptCenter.y - PWL_TRIANGLE_HALFLEN);
    276 
    277           if (rectWnd.right - rectWnd.left > PWL_TRIANGLE_HALFLEN * 2 &&
    278               rectWnd.top - rectWnd.bottom > PWL_TRIANGLE_HALFLEN) {
    279             CFX_PathData path;
    280             path.AppendPoint(pt1, FXPT_TYPE::MoveTo, false);
    281             path.AppendPoint(pt2, FXPT_TYPE::LineTo, false);
    282             path.AppendPoint(pt3, FXPT_TYPE::LineTo, false);
    283             path.AppendPoint(pt1, FXPT_TYPE::LineTo, false);
    284 
    285             pDevice->DrawPath(&path, pUser2Device, nullptr,
    286                               PWL_DEFAULT_BLACKCOLOR.ToFXColor(nTransparency),
    287                               0, FXFILL_ALTERNATE);
    288           }
    289         } break;
    290         default:
    291           break;
    292       }
    293       break;
    294     case SBT_VSCROLL:
    295       switch (m_eSBButtonType) {
    296         case PSBT_MIN: {
    297           // draw border
    298           CFX_FloatRect rcDraw = rectWnd;
    299           CPWL_Utils::DrawStrokeRect(pDevice, pUser2Device, rcDraw,
    300                                      ArgbEncode(nTransparency, 100, 100, 100),
    301                                      0.0f);
    302 
    303           // draw inner border
    304           rcDraw = CPWL_Utils::DeflateRect(rectWnd, 0.5f);
    305           CPWL_Utils::DrawStrokeRect(pDevice, pUser2Device, rcDraw,
    306                                      ArgbEncode(nTransparency, 255, 255, 255),
    307                                      1.0f);
    308 
    309           // draw background
    310 
    311           rcDraw = CPWL_Utils::DeflateRect(rectWnd, 1.0f);
    312 
    313           if (IsEnabled())
    314             CPWL_Utils::DrawShadow(pDevice, pUser2Device, true, false, rcDraw,
    315                                    nTransparency, 80, 220);
    316           else
    317             CPWL_Utils::DrawFillRect(pDevice, pUser2Device, rcDraw,
    318                                      ArgbEncode(255, 255, 255, 255));
    319 
    320           // draw arrow
    321 
    322           if (rectWnd.top - rectWnd.bottom > 6.0f) {
    323             FX_FLOAT fX = rectWnd.left + 1.5f;
    324             FX_FLOAT fY = rectWnd.bottom;
    325             CFX_PointF pts[7] = {CFX_PointF(fX + 2.5f, fY + 4.0f),
    326                                  CFX_PointF(fX + 2.5f, fY + 3.0f),
    327                                  CFX_PointF(fX + 4.5f, fY + 5.0f),
    328                                  CFX_PointF(fX + 6.5f, fY + 3.0f),
    329                                  CFX_PointF(fX + 6.5f, fY + 4.0f),
    330                                  CFX_PointF(fX + 4.5f, fY + 6.0f),
    331                                  CFX_PointF(fX + 2.5f, fY + 4.0f)};
    332 
    333             if (IsEnabled())
    334               CPWL_Utils::DrawFillArea(
    335                   pDevice, pUser2Device, pts, 7,
    336                   ArgbEncode(nTransparency, 255, 255, 255));
    337             else
    338               CPWL_Utils::DrawFillArea(
    339                   pDevice, pUser2Device, pts, 7,
    340                   PWL_DEFAULT_HEAVYGRAYCOLOR.ToFXColor(255));
    341           }
    342         } break;
    343         case PSBT_MAX: {
    344           // draw border
    345           CFX_FloatRect rcDraw = rectWnd;
    346           CPWL_Utils::DrawStrokeRect(pDevice, pUser2Device, rcDraw,
    347                                      ArgbEncode(nTransparency, 100, 100, 100),
    348                                      0.0f);
    349 
    350           // draw inner border
    351           rcDraw = CPWL_Utils::DeflateRect(rectWnd, 0.5f);
    352           CPWL_Utils::DrawStrokeRect(pDevice, pUser2Device, rcDraw,
    353                                      ArgbEncode(nTransparency, 255, 255, 255),
    354                                      1.0f);
    355 
    356           // draw background
    357           rcDraw = CPWL_Utils::DeflateRect(rectWnd, 1.0f);
    358           if (IsEnabled())
    359             CPWL_Utils::DrawShadow(pDevice, pUser2Device, true, false, rcDraw,
    360                                    nTransparency, 80, 220);
    361           else
    362             CPWL_Utils::DrawFillRect(pDevice, pUser2Device, rcDraw,
    363                                      ArgbEncode(255, 255, 255, 255));
    364 
    365           // draw arrow
    366 
    367           if (rectWnd.top - rectWnd.bottom > 6.0f) {
    368             FX_FLOAT fX = rectWnd.left + 1.5f;
    369             FX_FLOAT fY = rectWnd.bottom;
    370 
    371             CFX_PointF pts[7] = {CFX_PointF(fX + 2.5f, fY + 5.0f),
    372                                  CFX_PointF(fX + 2.5f, fY + 6.0f),
    373                                  CFX_PointF(fX + 4.5f, fY + 4.0f),
    374                                  CFX_PointF(fX + 6.5f, fY + 6.0f),
    375                                  CFX_PointF(fX + 6.5f, fY + 5.0f),
    376                                  CFX_PointF(fX + 4.5f, fY + 3.0f),
    377                                  CFX_PointF(fX + 2.5f, fY + 5.0f)};
    378 
    379             if (IsEnabled())
    380               CPWL_Utils::DrawFillArea(
    381                   pDevice, pUser2Device, pts, 7,
    382                   ArgbEncode(nTransparency, 255, 255, 255));
    383             else
    384               CPWL_Utils::DrawFillArea(
    385                   pDevice, pUser2Device, pts, 7,
    386                   PWL_DEFAULT_HEAVYGRAYCOLOR.ToFXColor(255));
    387           }
    388         } break;
    389         case PSBT_POS: {
    390           // draw border
    391           CFX_FloatRect rcDraw = rectWnd;
    392           CPWL_Utils::DrawStrokeRect(pDevice, pUser2Device, rcDraw,
    393                                      ArgbEncode(nTransparency, 100, 100, 100),
    394                                      0.0f);
    395 
    396           // draw inner border
    397           rcDraw = CPWL_Utils::DeflateRect(rectWnd, 0.5f);
    398           CPWL_Utils::DrawStrokeRect(pDevice, pUser2Device, rcDraw,
    399                                      ArgbEncode(nTransparency, 255, 255, 255),
    400                                      1.0f);
    401 
    402           if (IsEnabled()) {
    403             // draw shadow effect
    404 
    405             CFX_PointF ptTop = CFX_PointF(rectWnd.left, rectWnd.top - 1.0f);
    406             CFX_PointF ptBottom =
    407                 CFX_PointF(rectWnd.left, rectWnd.bottom + 1.0f);
    408 
    409             ptTop.x += 1.5f;
    410             ptBottom.x += 1.5f;
    411 
    412             CPWL_Utils::DrawStrokeLine(pDevice, pUser2Device, ptTop, ptBottom,
    413                                        ArgbEncode(nTransparency, 210, 210, 210),
    414                                        1.0f);
    415 
    416             ptTop.x += 1.0f;
    417             ptBottom.x += 1.0f;
    418 
    419             CPWL_Utils::DrawStrokeLine(pDevice, pUser2Device, ptTop, ptBottom,
    420                                        ArgbEncode(nTransparency, 220, 220, 220),
    421                                        1.0f);
    422 
    423             ptTop.x += 1.0f;
    424             ptBottom.x += 1.0f;
    425 
    426             CPWL_Utils::DrawStrokeLine(pDevice, pUser2Device, ptTop, ptBottom,
    427                                        ArgbEncode(nTransparency, 240, 240, 240),
    428                                        1.0f);
    429 
    430             ptTop.x += 1.0f;
    431             ptBottom.x += 1.0f;
    432 
    433             CPWL_Utils::DrawStrokeLine(pDevice, pUser2Device, ptTop, ptBottom,
    434                                        ArgbEncode(nTransparency, 240, 240, 240),
    435                                        1.0f);
    436 
    437             ptTop.x += 1.0f;
    438             ptBottom.x += 1.0f;
    439 
    440             CPWL_Utils::DrawStrokeLine(pDevice, pUser2Device, ptTop, ptBottom,
    441                                        ArgbEncode(nTransparency, 210, 210, 210),
    442                                        1.0f);
    443 
    444             ptTop.x += 1.0f;
    445             ptBottom.x += 1.0f;
    446 
    447             CPWL_Utils::DrawStrokeLine(pDevice, pUser2Device, ptTop, ptBottom,
    448                                        ArgbEncode(nTransparency, 180, 180, 180),
    449                                        1.0f);
    450 
    451             ptTop.x += 1.0f;
    452             ptBottom.x += 1.0f;
    453 
    454             CPWL_Utils::DrawStrokeLine(pDevice, pUser2Device, ptTop, ptBottom,
    455                                        ArgbEncode(nTransparency, 150, 150, 150),
    456                                        1.0f);
    457 
    458             ptTop.x += 1.0f;
    459             ptBottom.x += 1.0f;
    460 
    461             CPWL_Utils::DrawStrokeLine(pDevice, pUser2Device, ptTop, ptBottom,
    462                                        ArgbEncode(nTransparency, 150, 150, 150),
    463                                        1.0f);
    464 
    465             ptTop.x += 1.0f;
    466             ptBottom.x += 1.0f;
    467 
    468             CPWL_Utils::DrawStrokeLine(pDevice, pUser2Device, ptTop, ptBottom,
    469                                        ArgbEncode(nTransparency, 180, 180, 180),
    470                                        1.0f);
    471 
    472             ptTop.x += 1.0f;
    473             ptBottom.x += 1.0f;
    474 
    475             CPWL_Utils::DrawStrokeLine(pDevice, pUser2Device, ptTop, ptBottom,
    476                                        ArgbEncode(nTransparency, 210, 210, 210),
    477                                        1.0f);
    478           } else {
    479             CPWL_Utils::DrawFillRect(pDevice, pUser2Device, rcDraw,
    480                                      ArgbEncode(255, 255, 255, 255));
    481           }
    482 
    483           // draw friction
    484 
    485           if (rectWnd.Height() > 8.0f) {
    486             FX_COLORREF crStroke = ArgbEncode(nTransparency, 120, 120, 120);
    487             if (!IsEnabled())
    488               crStroke = PWL_DEFAULT_HEAVYGRAYCOLOR.ToFXColor(255);
    489 
    490             FX_FLOAT nFrictionWidth = 5.0f;
    491             FX_FLOAT nFrictionHeight = 5.5f;
    492 
    493             CFX_PointF ptLeft =
    494                 CFX_PointF(ptCenter.x - nFrictionWidth / 2.0f,
    495                            ptCenter.y - nFrictionHeight / 2.0f + 0.5f);
    496             CFX_PointF ptRight =
    497                 CFX_PointF(ptCenter.x + nFrictionWidth / 2.0f,
    498                            ptCenter.y - nFrictionHeight / 2.0f + 0.5f);
    499 
    500             CPWL_Utils::DrawStrokeLine(pDevice, pUser2Device, ptLeft, ptRight,
    501                                        crStroke, 1.0f);
    502 
    503             ptLeft.y += 2.0f;
    504             ptRight.y += 2.0f;
    505 
    506             CPWL_Utils::DrawStrokeLine(pDevice, pUser2Device, ptLeft, ptRight,
    507                                        crStroke, 1.0f);
    508 
    509             ptLeft.y += 2.0f;
    510             ptRight.y += 2.0f;
    511 
    512             CPWL_Utils::DrawStrokeLine(pDevice, pUser2Device, ptLeft, ptRight,
    513                                        crStroke, 1.0f);
    514           }
    515         } break;
    516         default:
    517           break;
    518       }
    519       break;
    520     default:
    521       break;
    522   }
    523 }
    524 
    525 bool CPWL_SBButton::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
    526   CPWL_Wnd::OnLButtonDown(point, nFlag);
    527 
    528   if (CPWL_Wnd* pParent = GetParentWindow())
    529     pParent->OnNotify(this, PNM_LBUTTONDOWN, 0, (intptr_t)&point);
    530 
    531   m_bMouseDown = true;
    532   SetCapture();
    533 
    534   return true;
    535 }
    536 
    537 bool CPWL_SBButton::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
    538   CPWL_Wnd::OnLButtonUp(point, nFlag);
    539 
    540   if (CPWL_Wnd* pParent = GetParentWindow())
    541     pParent->OnNotify(this, PNM_LBUTTONUP, 0, (intptr_t)&point);
    542 
    543   m_bMouseDown = false;
    544   ReleaseCapture();
    545 
    546   return true;
    547 }
    548 
    549 bool CPWL_SBButton::OnMouseMove(const CFX_PointF& point, uint32_t nFlag) {
    550   CPWL_Wnd::OnMouseMove(point, nFlag);
    551 
    552   if (CPWL_Wnd* pParent = GetParentWindow()) {
    553     pParent->OnNotify(this, PNM_MOUSEMOVE, 0, (intptr_t)&point);
    554   }
    555 
    556   return true;
    557 }
    558 
    559 CPWL_ScrollBar::CPWL_ScrollBar(PWL_SCROLLBAR_TYPE sbType)
    560     : m_sbType(sbType),
    561       m_pMinButton(nullptr),
    562       m_pMaxButton(nullptr),
    563       m_pPosButton(nullptr),
    564       m_bMouseDown(false),
    565       m_bMinOrMax(false),
    566       m_bNotifyForever(true) {}
    567 
    568 CPWL_ScrollBar::~CPWL_ScrollBar() {}
    569 
    570 CFX_ByteString CPWL_ScrollBar::GetClassName() const {
    571   return "CPWL_ScrollBar";
    572 }
    573 
    574 void CPWL_ScrollBar::OnCreate(PWL_CREATEPARAM& cp) {
    575   cp.eCursorType = FXCT_ARROW;
    576 }
    577 
    578 void CPWL_ScrollBar::RePosChildWnd() {
    579   CFX_FloatRect rcClient = GetClientRect();
    580   CFX_FloatRect rcMinButton, rcMaxButton;
    581   FX_FLOAT fBWidth = 0;
    582 
    583   switch (m_sbType) {
    584     case SBT_HSCROLL:
    585       if (rcClient.right - rcClient.left >
    586           PWL_SCROLLBAR_BUTTON_WIDTH * 2 + PWL_SCROLLBAR_POSBUTTON_MINWIDTH +
    587               2) {
    588         rcMinButton = CFX_FloatRect(rcClient.left, rcClient.bottom,
    589                                     rcClient.left + PWL_SCROLLBAR_BUTTON_WIDTH,
    590                                     rcClient.top);
    591         rcMaxButton =
    592             CFX_FloatRect(rcClient.right - PWL_SCROLLBAR_BUTTON_WIDTH,
    593                           rcClient.bottom, rcClient.right, rcClient.top);
    594       } else {
    595         fBWidth = (rcClient.right - rcClient.left -
    596                    PWL_SCROLLBAR_POSBUTTON_MINWIDTH - 2) /
    597                   2;
    598 
    599         if (fBWidth > 0) {
    600           rcMinButton = CFX_FloatRect(rcClient.left, rcClient.bottom,
    601                                       rcClient.left + fBWidth, rcClient.top);
    602           rcMaxButton = CFX_FloatRect(rcClient.right - fBWidth, rcClient.bottom,
    603                                       rcClient.right, rcClient.top);
    604         } else {
    605           SetVisible(false);
    606         }
    607       }
    608       break;
    609     case SBT_VSCROLL:
    610       if (IsFloatBigger(rcClient.top - rcClient.bottom,
    611                         PWL_SCROLLBAR_BUTTON_WIDTH * 2 +
    612                             PWL_SCROLLBAR_POSBUTTON_MINWIDTH + 2)) {
    613         rcMinButton = CFX_FloatRect(rcClient.left,
    614                                     rcClient.top - PWL_SCROLLBAR_BUTTON_WIDTH,
    615                                     rcClient.right, rcClient.top);
    616         rcMaxButton =
    617             CFX_FloatRect(rcClient.left, rcClient.bottom, rcClient.right,
    618                           rcClient.bottom + PWL_SCROLLBAR_BUTTON_WIDTH);
    619       } else {
    620         fBWidth = (rcClient.top - rcClient.bottom -
    621                    PWL_SCROLLBAR_POSBUTTON_MINWIDTH - 2) /
    622                   2;
    623 
    624         if (IsFloatBigger(fBWidth, 0)) {
    625           rcMinButton = CFX_FloatRect(rcClient.left, rcClient.top - fBWidth,
    626                                       rcClient.right, rcClient.top);
    627           rcMaxButton =
    628               CFX_FloatRect(rcClient.left, rcClient.bottom, rcClient.right,
    629                             rcClient.bottom + fBWidth);
    630         } else {
    631           SetVisible(false);
    632         }
    633       }
    634       break;
    635   }
    636 
    637   if (m_pMinButton)
    638     m_pMinButton->Move(rcMinButton, true, false);
    639   if (m_pMaxButton)
    640     m_pMaxButton->Move(rcMaxButton, true, false);
    641   MovePosButton(false);
    642 }
    643 
    644 void CPWL_ScrollBar::GetThisAppearanceStream(CFX_ByteTextBuf& sAppStream) {
    645   CFX_FloatRect rectWnd = GetWindowRect();
    646 
    647   if (IsVisible() && !rectWnd.IsEmpty()) {
    648     CFX_ByteTextBuf sButton;
    649 
    650     sButton << "q\n";
    651     sButton << "0 w\n"
    652             << CPWL_Utils::GetColorAppStream(GetBackgroundColor(), true)
    653                    .AsStringC();
    654     sButton << rectWnd.left << " " << rectWnd.bottom << " "
    655             << rectWnd.right - rectWnd.left << " "
    656             << rectWnd.top - rectWnd.bottom << " re b Q\n";
    657 
    658     sAppStream << sButton;
    659   }
    660 }
    661 
    662 void CPWL_ScrollBar::DrawThisAppearance(CFX_RenderDevice* pDevice,
    663                                         CFX_Matrix* pUser2Device) {
    664   CFX_FloatRect rectWnd = GetWindowRect();
    665 
    666   if (IsVisible() && !rectWnd.IsEmpty()) {
    667     CPWL_Utils::DrawFillRect(pDevice, pUser2Device, rectWnd,
    668                              GetBackgroundColor(), GetTransparency());
    669 
    670     CPWL_Utils::DrawStrokeLine(
    671         pDevice, pUser2Device,
    672         CFX_PointF(rectWnd.left + 2.0f, rectWnd.top - 2.0f),
    673         CFX_PointF(rectWnd.left + 2.0f, rectWnd.bottom + 2.0f),
    674         ArgbEncode(GetTransparency(), 100, 100, 100), 1.0f);
    675 
    676     CPWL_Utils::DrawStrokeLine(
    677         pDevice, pUser2Device,
    678         CFX_PointF(rectWnd.right - 2.0f, rectWnd.top - 2.0f),
    679         CFX_PointF(rectWnd.right - 2.0f, rectWnd.bottom + 2.0f),
    680         ArgbEncode(GetTransparency(), 100, 100, 100), 1.0f);
    681   }
    682 }
    683 
    684 bool CPWL_ScrollBar::OnLButtonDown(const CFX_PointF& point, uint32_t nFlag) {
    685   CPWL_Wnd::OnLButtonDown(point, nFlag);
    686 
    687   if (HasFlag(PWS_AUTOTRANSPARENT)) {
    688     if (GetTransparency() != 255) {
    689       SetTransparency(255);
    690       InvalidateRect();
    691     }
    692   }
    693 
    694   CFX_FloatRect rcMinArea, rcMaxArea;
    695 
    696   if (m_pPosButton && m_pPosButton->IsVisible()) {
    697     CFX_FloatRect rcClient = GetClientRect();
    698     CFX_FloatRect rcPosButton = m_pPosButton->GetWindowRect();
    699 
    700     switch (m_sbType) {
    701       case SBT_HSCROLL:
    702         rcMinArea =
    703             CFX_FloatRect(rcClient.left + PWL_SCROLLBAR_BUTTON_WIDTH,
    704                           rcClient.bottom, rcPosButton.left, rcClient.top);
    705         rcMaxArea = CFX_FloatRect(rcPosButton.right, rcClient.bottom,
    706                                   rcClient.right - PWL_SCROLLBAR_BUTTON_WIDTH,
    707                                   rcClient.top);
    708 
    709         break;
    710       case SBT_VSCROLL:
    711         rcMinArea =
    712             CFX_FloatRect(rcClient.left, rcPosButton.top, rcClient.right,
    713                           rcClient.top - PWL_SCROLLBAR_BUTTON_WIDTH);
    714         rcMaxArea = CFX_FloatRect(rcClient.left,
    715                                   rcClient.bottom + PWL_SCROLLBAR_BUTTON_WIDTH,
    716                                   rcClient.right, rcPosButton.bottom);
    717         break;
    718     }
    719 
    720     rcMinArea.Normalize();
    721     rcMaxArea.Normalize();
    722 
    723     if (rcMinArea.Contains(point)) {
    724       m_sData.SubBig();
    725       MovePosButton(true);
    726       NotifyScrollWindow();
    727     }
    728 
    729     if (rcMaxArea.Contains(point)) {
    730       m_sData.AddBig();
    731       MovePosButton(true);
    732       NotifyScrollWindow();
    733     }
    734   }
    735 
    736   return true;
    737 }
    738 
    739 bool CPWL_ScrollBar::OnLButtonUp(const CFX_PointF& point, uint32_t nFlag) {
    740   CPWL_Wnd::OnLButtonUp(point, nFlag);
    741 
    742   if (HasFlag(PWS_AUTOTRANSPARENT)) {
    743     if (GetTransparency() != PWL_SCROLLBAR_TRANSPARENCY) {
    744       SetTransparency(PWL_SCROLLBAR_TRANSPARENCY);
    745       InvalidateRect();
    746     }
    747   }
    748 
    749   EndTimer();
    750   m_bMouseDown = false;
    751 
    752   return true;
    753 }
    754 
    755 void CPWL_ScrollBar::OnNotify(CPWL_Wnd* pWnd,
    756                               uint32_t msg,
    757                               intptr_t wParam,
    758                               intptr_t lParam) {
    759   CPWL_Wnd::OnNotify(pWnd, msg, wParam, lParam);
    760 
    761   switch (msg) {
    762     case PNM_LBUTTONDOWN:
    763       if (pWnd == m_pMinButton) {
    764         OnMinButtonLBDown(*(CFX_PointF*)lParam);
    765       }
    766 
    767       if (pWnd == m_pMaxButton) {
    768         OnMaxButtonLBDown(*(CFX_PointF*)lParam);
    769       }
    770 
    771       if (pWnd == m_pPosButton) {
    772         OnPosButtonLBDown(*(CFX_PointF*)lParam);
    773       }
    774       break;
    775     case PNM_LBUTTONUP:
    776       if (pWnd == m_pMinButton) {
    777         OnMinButtonLBUp(*(CFX_PointF*)lParam);
    778       }
    779 
    780       if (pWnd == m_pMaxButton) {
    781         OnMaxButtonLBUp(*(CFX_PointF*)lParam);
    782       }
    783 
    784       if (pWnd == m_pPosButton) {
    785         OnPosButtonLBUp(*(CFX_PointF*)lParam);
    786       }
    787       break;
    788     case PNM_MOUSEMOVE:
    789       if (pWnd == m_pMinButton) {
    790         OnMinButtonMouseMove(*(CFX_PointF*)lParam);
    791       }
    792 
    793       if (pWnd == m_pMaxButton) {
    794         OnMaxButtonMouseMove(*(CFX_PointF*)lParam);
    795       }
    796 
    797       if (pWnd == m_pPosButton) {
    798         OnPosButtonMouseMove(*(CFX_PointF*)lParam);
    799       }
    800       break;
    801     case PNM_SETSCROLLINFO: {
    802       PWL_SCROLL_INFO* pInfo = reinterpret_cast<PWL_SCROLL_INFO*>(lParam);
    803       if (pInfo && *pInfo != m_OriginInfo) {
    804         m_OriginInfo = *pInfo;
    805         FX_FLOAT fMax =
    806             pInfo->fContentMax - pInfo->fContentMin - pInfo->fPlateWidth;
    807         fMax = fMax > 0.0f ? fMax : 0.0f;
    808         SetScrollRange(0, fMax, pInfo->fPlateWidth);
    809         SetScrollStep(pInfo->fBigStep, pInfo->fSmallStep);
    810       }
    811     } break;
    812     case PNM_SETSCROLLPOS: {
    813       FX_FLOAT fPos = *(FX_FLOAT*)lParam;
    814       switch (m_sbType) {
    815         case SBT_HSCROLL:
    816           fPos = fPos - m_OriginInfo.fContentMin;
    817           break;
    818         case SBT_VSCROLL:
    819           fPos = m_OriginInfo.fContentMax - fPos;
    820           break;
    821       }
    822       SetScrollPos(fPos);
    823     } break;
    824   }
    825 }
    826 
    827 void CPWL_ScrollBar::CreateButtons(const PWL_CREATEPARAM& cp) {
    828   PWL_CREATEPARAM scp = cp;
    829   scp.pParentWnd = this;
    830   scp.dwBorderWidth = 2;
    831   scp.nBorderStyle = BorderStyle::BEVELED;
    832 
    833   scp.dwFlags =
    834       PWS_VISIBLE | PWS_CHILD | PWS_BORDER | PWS_BACKGROUND | PWS_NOREFRESHCLIP;
    835 
    836   if (!m_pMinButton) {
    837     m_pMinButton = new CPWL_SBButton(m_sbType, PSBT_MIN);
    838     m_pMinButton->Create(scp);
    839   }
    840 
    841   if (!m_pMaxButton) {
    842     m_pMaxButton = new CPWL_SBButton(m_sbType, PSBT_MAX);
    843     m_pMaxButton->Create(scp);
    844   }
    845 
    846   if (!m_pPosButton) {
    847     m_pPosButton = new CPWL_SBButton(m_sbType, PSBT_POS);
    848     m_pPosButton->SetVisible(false);
    849     m_pPosButton->Create(scp);
    850   }
    851 }
    852 
    853 FX_FLOAT CPWL_ScrollBar::GetScrollBarWidth() const {
    854   if (!IsVisible())
    855     return 0;
    856 
    857   return PWL_SCROLLBAR_WIDTH;
    858 }
    859 
    860 void CPWL_ScrollBar::SetScrollRange(FX_FLOAT fMin,
    861                                     FX_FLOAT fMax,
    862                                     FX_FLOAT fClientWidth) {
    863   if (m_pPosButton) {
    864     m_sData.SetScrollRange(fMin, fMax);
    865     m_sData.SetClientWidth(fClientWidth);
    866 
    867     if (IsFloatSmaller(m_sData.ScrollRange.GetWidth(), 0.0f)) {
    868       m_pPosButton->SetVisible(false);
    869     } else {
    870       m_pPosButton->SetVisible(true);
    871       MovePosButton(true);
    872     }
    873   }
    874 }
    875 
    876 void CPWL_ScrollBar::SetScrollPos(FX_FLOAT fPos) {
    877   FX_FLOAT fOldPos = m_sData.fScrollPos;
    878 
    879   m_sData.SetPos(fPos);
    880 
    881   if (!IsFloatEqual(m_sData.fScrollPos, fOldPos))
    882     MovePosButton(true);
    883 }
    884 
    885 void CPWL_ScrollBar::SetScrollStep(FX_FLOAT fBigStep, FX_FLOAT fSmallStep) {
    886   m_sData.SetBigStep(fBigStep);
    887   m_sData.SetSmallStep(fSmallStep);
    888 }
    889 
    890 void CPWL_ScrollBar::MovePosButton(bool bRefresh) {
    891   ASSERT(m_pMinButton);
    892   ASSERT(m_pMaxButton);
    893 
    894   if (m_pPosButton->IsVisible()) {
    895     CFX_FloatRect rcClient;
    896     CFX_FloatRect rcPosArea, rcPosButton;
    897 
    898     rcClient = GetClientRect();
    899     rcPosArea = GetScrollArea();
    900 
    901     FX_FLOAT fLeft, fRight, fTop, fBottom;
    902 
    903     switch (m_sbType) {
    904       case SBT_HSCROLL:
    905         fLeft = TrueToFace(m_sData.fScrollPos);
    906         fRight = TrueToFace(m_sData.fScrollPos + m_sData.fClientWidth);
    907 
    908         if (fRight - fLeft < PWL_SCROLLBAR_POSBUTTON_MINWIDTH)
    909           fRight = fLeft + PWL_SCROLLBAR_POSBUTTON_MINWIDTH;
    910 
    911         if (fRight > rcPosArea.right) {
    912           fRight = rcPosArea.right;
    913           fLeft = fRight - PWL_SCROLLBAR_POSBUTTON_MINWIDTH;
    914         }
    915 
    916         rcPosButton =
    917             CFX_FloatRect(fLeft, rcPosArea.bottom, fRight, rcPosArea.top);
    918 
    919         break;
    920       case SBT_VSCROLL:
    921         fBottom = TrueToFace(m_sData.fScrollPos + m_sData.fClientWidth);
    922         fTop = TrueToFace(m_sData.fScrollPos);
    923 
    924         if (IsFloatSmaller(fTop - fBottom, PWL_SCROLLBAR_POSBUTTON_MINWIDTH))
    925           fBottom = fTop - PWL_SCROLLBAR_POSBUTTON_MINWIDTH;
    926 
    927         if (IsFloatSmaller(fBottom, rcPosArea.bottom)) {
    928           fBottom = rcPosArea.bottom;
    929           fTop = fBottom + PWL_SCROLLBAR_POSBUTTON_MINWIDTH;
    930         }
    931 
    932         rcPosButton =
    933             CFX_FloatRect(rcPosArea.left, fBottom, rcPosArea.right, fTop);
    934 
    935         break;
    936     }
    937 
    938     m_pPosButton->Move(rcPosButton, true, bRefresh);
    939   }
    940 }
    941 
    942 void CPWL_ScrollBar::OnMinButtonLBDown(const CFX_PointF& point) {
    943   m_sData.SubSmall();
    944   MovePosButton(true);
    945   NotifyScrollWindow();
    946 
    947   m_bMinOrMax = true;
    948 
    949   EndTimer();
    950   BeginTimer(100);
    951 }
    952 
    953 void CPWL_ScrollBar::OnMinButtonLBUp(const CFX_PointF& point) {}
    954 
    955 void CPWL_ScrollBar::OnMinButtonMouseMove(const CFX_PointF& point) {}
    956 
    957 void CPWL_ScrollBar::OnMaxButtonLBDown(const CFX_PointF& point) {
    958   m_sData.AddSmall();
    959   MovePosButton(true);
    960   NotifyScrollWindow();
    961 
    962   m_bMinOrMax = false;
    963 
    964   EndTimer();
    965   BeginTimer(100);
    966 }
    967 
    968 void CPWL_ScrollBar::OnMaxButtonLBUp(const CFX_PointF& point) {}
    969 
    970 void CPWL_ScrollBar::OnMaxButtonMouseMove(const CFX_PointF& point) {}
    971 
    972 void CPWL_ScrollBar::OnPosButtonLBDown(const CFX_PointF& point) {
    973   m_bMouseDown = true;
    974 
    975   if (m_pPosButton) {
    976     CFX_FloatRect rcPosButton = m_pPosButton->GetWindowRect();
    977 
    978     switch (m_sbType) {
    979       case SBT_HSCROLL:
    980         m_nOldPos = point.x;
    981         m_fOldPosButton = rcPosButton.left;
    982         break;
    983       case SBT_VSCROLL:
    984         m_nOldPos = point.y;
    985         m_fOldPosButton = rcPosButton.top;
    986         break;
    987     }
    988   }
    989 }
    990 
    991 void CPWL_ScrollBar::OnPosButtonLBUp(const CFX_PointF& point) {
    992   if (m_bMouseDown) {
    993     if (!m_bNotifyForever)
    994       NotifyScrollWindow();
    995   }
    996   m_bMouseDown = false;
    997 }
    998 
    999 void CPWL_ScrollBar::OnPosButtonMouseMove(const CFX_PointF& point) {
   1000   FX_FLOAT fOldScrollPos = m_sData.fScrollPos;
   1001 
   1002   FX_FLOAT fNewPos = 0;
   1003 
   1004   switch (m_sbType) {
   1005     case SBT_HSCROLL:
   1006       if (FXSYS_fabs(point.x - m_nOldPos) < 1)
   1007         return;
   1008       fNewPos = FaceToTrue(m_fOldPosButton + point.x - m_nOldPos);
   1009       break;
   1010     case SBT_VSCROLL:
   1011       if (FXSYS_fabs(point.y - m_nOldPos) < 1)
   1012         return;
   1013       fNewPos = FaceToTrue(m_fOldPosButton + point.y - m_nOldPos);
   1014       break;
   1015   }
   1016 
   1017   if (m_bMouseDown) {
   1018     switch (m_sbType) {
   1019       case SBT_HSCROLL:
   1020 
   1021         if (IsFloatSmaller(fNewPos, m_sData.ScrollRange.fMin)) {
   1022           fNewPos = m_sData.ScrollRange.fMin;
   1023         }
   1024 
   1025         if (IsFloatBigger(fNewPos, m_sData.ScrollRange.fMax)) {
   1026           fNewPos = m_sData.ScrollRange.fMax;
   1027         }
   1028 
   1029         m_sData.SetPos(fNewPos);
   1030 
   1031         break;
   1032       case SBT_VSCROLL:
   1033 
   1034         if (IsFloatSmaller(fNewPos, m_sData.ScrollRange.fMin)) {
   1035           fNewPos = m_sData.ScrollRange.fMin;
   1036         }
   1037 
   1038         if (IsFloatBigger(fNewPos, m_sData.ScrollRange.fMax)) {
   1039           fNewPos = m_sData.ScrollRange.fMax;
   1040         }
   1041 
   1042         m_sData.SetPos(fNewPos);
   1043 
   1044         break;
   1045     }
   1046 
   1047     if (!IsFloatEqual(fOldScrollPos, m_sData.fScrollPos)) {
   1048       MovePosButton(true);
   1049 
   1050       if (m_bNotifyForever)
   1051         NotifyScrollWindow();
   1052     }
   1053   }
   1054 }
   1055 
   1056 void CPWL_ScrollBar::NotifyScrollWindow() {
   1057   if (CPWL_Wnd* pParent = GetParentWindow()) {
   1058     FX_FLOAT fPos;
   1059     switch (m_sbType) {
   1060       case SBT_HSCROLL:
   1061         fPos = m_OriginInfo.fContentMin + m_sData.fScrollPos;
   1062         break;
   1063       case SBT_VSCROLL:
   1064         fPos = m_OriginInfo.fContentMax - m_sData.fScrollPos;
   1065         break;
   1066     }
   1067     pParent->OnNotify(this, PNM_SCROLLWINDOW, (intptr_t)m_sbType,
   1068                       (intptr_t)&fPos);
   1069   }
   1070 }
   1071 
   1072 CFX_FloatRect CPWL_ScrollBar::GetScrollArea() const {
   1073   CFX_FloatRect rcClient = GetClientRect();
   1074   CFX_FloatRect rcArea;
   1075 
   1076   if (!m_pMinButton || !m_pMaxButton)
   1077     return rcClient;
   1078 
   1079   CFX_FloatRect rcMin = m_pMinButton->GetWindowRect();
   1080   CFX_FloatRect rcMax = m_pMaxButton->GetWindowRect();
   1081 
   1082   FX_FLOAT fMinWidth = rcMin.right - rcMin.left;
   1083   FX_FLOAT fMinHeight = rcMin.top - rcMin.bottom;
   1084   FX_FLOAT fMaxWidth = rcMax.right - rcMax.left;
   1085   FX_FLOAT fMaxHeight = rcMax.top - rcMax.bottom;
   1086 
   1087   switch (m_sbType) {
   1088     case SBT_HSCROLL:
   1089       if (rcClient.right - rcClient.left > fMinWidth + fMaxWidth + 2) {
   1090         rcArea = CFX_FloatRect(rcClient.left + fMinWidth + 1, rcClient.bottom,
   1091                                rcClient.right - fMaxWidth - 1, rcClient.top);
   1092       } else {
   1093         rcArea = CFX_FloatRect(rcClient.left + fMinWidth + 1, rcClient.bottom,
   1094                                rcClient.left + fMinWidth + 1, rcClient.top);
   1095       }
   1096       break;
   1097     case SBT_VSCROLL:
   1098       if (rcClient.top - rcClient.bottom > fMinHeight + fMaxHeight + 2) {
   1099         rcArea = CFX_FloatRect(rcClient.left, rcClient.bottom + fMinHeight + 1,
   1100                                rcClient.right, rcClient.top - fMaxHeight - 1);
   1101       } else {
   1102         rcArea =
   1103             CFX_FloatRect(rcClient.left, rcClient.bottom + fMinHeight + 1,
   1104                           rcClient.right, rcClient.bottom + fMinHeight + 1);
   1105       }
   1106       break;
   1107   }
   1108 
   1109   rcArea.Normalize();
   1110 
   1111   return rcArea;
   1112 }
   1113 
   1114 FX_FLOAT CPWL_ScrollBar::TrueToFace(FX_FLOAT fTrue) {
   1115   CFX_FloatRect rcPosArea;
   1116   rcPosArea = GetScrollArea();
   1117 
   1118   FX_FLOAT fFactWidth = m_sData.ScrollRange.GetWidth() + m_sData.fClientWidth;
   1119   fFactWidth = fFactWidth == 0 ? 1 : fFactWidth;
   1120 
   1121   FX_FLOAT fFace = 0;
   1122 
   1123   switch (m_sbType) {
   1124     case SBT_HSCROLL:
   1125       fFace = rcPosArea.left +
   1126               fTrue * (rcPosArea.right - rcPosArea.left) / fFactWidth;
   1127       break;
   1128     case SBT_VSCROLL:
   1129       fFace = rcPosArea.top -
   1130               fTrue * (rcPosArea.top - rcPosArea.bottom) / fFactWidth;
   1131       break;
   1132   }
   1133 
   1134   return fFace;
   1135 }
   1136 
   1137 FX_FLOAT CPWL_ScrollBar::FaceToTrue(FX_FLOAT fFace) {
   1138   CFX_FloatRect rcPosArea;
   1139   rcPosArea = GetScrollArea();
   1140 
   1141   FX_FLOAT fFactWidth = m_sData.ScrollRange.GetWidth() + m_sData.fClientWidth;
   1142   fFactWidth = fFactWidth == 0 ? 1 : fFactWidth;
   1143 
   1144   FX_FLOAT fTrue = 0;
   1145 
   1146   switch (m_sbType) {
   1147     case SBT_HSCROLL:
   1148       fTrue = (fFace - rcPosArea.left) * fFactWidth /
   1149               (rcPosArea.right - rcPosArea.left);
   1150       break;
   1151     case SBT_VSCROLL:
   1152       fTrue = (rcPosArea.top - fFace) * fFactWidth /
   1153               (rcPosArea.top - rcPosArea.bottom);
   1154       break;
   1155   }
   1156 
   1157   return fTrue;
   1158 }
   1159 
   1160 void CPWL_ScrollBar::CreateChildWnd(const PWL_CREATEPARAM& cp) {
   1161   CreateButtons(cp);
   1162 }
   1163 
   1164 void CPWL_ScrollBar::TimerProc() {
   1165   PWL_SCROLL_PRIVATEDATA sTemp = m_sData;
   1166   if (m_bMinOrMax)
   1167     m_sData.SubSmall();
   1168   else
   1169     m_sData.AddSmall();
   1170 
   1171   if (sTemp != m_sData) {
   1172     MovePosButton(true);
   1173     NotifyScrollWindow();
   1174   }
   1175 }
   1176