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