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