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_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
234void 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
525bool 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
537bool 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
549bool 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
559CPWL_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
568CPWL_ScrollBar::~CPWL_ScrollBar() {}
569
570CFX_ByteString CPWL_ScrollBar::GetClassName() const {
571  return "CPWL_ScrollBar";
572}
573
574void CPWL_ScrollBar::OnCreate(PWL_CREATEPARAM& cp) {
575  cp.eCursorType = FXCT_ARROW;
576}
577
578void 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
644void 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
662void 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
684bool 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
739bool 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
755void 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
827void 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
853FX_FLOAT CPWL_ScrollBar::GetScrollBarWidth() const {
854  if (!IsVisible())
855    return 0;
856
857  return PWL_SCROLLBAR_WIDTH;
858}
859
860void 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
876void 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
885void CPWL_ScrollBar::SetScrollStep(FX_FLOAT fBigStep, FX_FLOAT fSmallStep) {
886  m_sData.SetBigStep(fBigStep);
887  m_sData.SetSmallStep(fSmallStep);
888}
889
890void 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
942void 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
953void CPWL_ScrollBar::OnMinButtonLBUp(const CFX_PointF& point) {}
954
955void CPWL_ScrollBar::OnMinButtonMouseMove(const CFX_PointF& point) {}
956
957void 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
968void CPWL_ScrollBar::OnMaxButtonLBUp(const CFX_PointF& point) {}
969
970void CPWL_ScrollBar::OnMaxButtonMouseMove(const CFX_PointF& point) {}
971
972void 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
991void CPWL_ScrollBar::OnPosButtonLBUp(const CFX_PointF& point) {
992  if (m_bMouseDown) {
993    if (!m_bNotifyForever)
994      NotifyScrollWindow();
995  }
996  m_bMouseDown = false;
997}
998
999void 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
1056void 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
1072CFX_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
1114FX_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
1137FX_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
1160void CPWL_ScrollBar::CreateChildWnd(const PWL_CREATEPARAM& cp) {
1161  CreateButtons(cp);
1162}
1163
1164void 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