//+------------------------------------------------------------------+
//|                                            ScrollBarVertical.mqh |
//|                                  Copyright 2022, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2022, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "ScrollBarThumb.mqh"
#include "ArrowLeftButton.mqh"
#include "ArrowRightButton.mqh"
#include "ScrollBar.mqh"
//+------------------------------------------------------------------+
//| CScrollBarHorisontal object class of WForms controls             |
//+------------------------------------------------------------------+
class CScrollBarHorisontal : public CScrollBar
  {
private:
//--- Create the ArrowButton objects
   virtual void      CreateArrowButtons(const int width,const int height);
//--- Calculate the distance of the capture area (slider)
   int               CalculateThumbAreaDistance(const int thumb_size);
protected:
//--- Protected constructor with object type, chart ID and subwindow
                     CScrollBarHorisontal(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          CGCnvElement *main_obj,CGCnvElement *base_obj,
                                          const long chart_id,
                                          const int subwindow,
                                          const string descript,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h);

//--- 'The cursor is inside the active area, the mouse wheel is being scrolled' event handler
   virtual void      MouseActiveAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam);
public:
//--- Supported object properties (1) integer, (2) real and (3) string ones
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_INTEGER property) { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_DOUBLE property)  { return true; }
   virtual bool      SupportProperty(ENUM_CANV_ELEMENT_PROP_STRING property)  { return true; }
   
//--- Return the (1) left and (2) right arrow button
   CArrowLeftButton *GetArrowButtonLeft(void)   { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT,0);    }
   CArrowRightButton*GetArrowButtonRight(void)  { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,0);   }

//--- Return the size of the slider working area
   int               BarWorkAreaSize(void);
//--- Return the coordinate of the beginning of the slider working area
   int               BarWorkAreaCoord(void);
   
//--- Set the new size
   virtual bool      Resize(const int w,const int h,const bool redraw);
//--- Calculate and set the parameters of the capture area (slider)
   int               SetThumbParams(void);

//--- Constructor
                     CScrollBarHorisontal(CGCnvElement *main_obj,CGCnvElement *base_obj,
                                          const long chart_id,
                                          const int subwindow,
                                          const string descript,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h);
//--- Timer
   virtual void      OnTimer(void);
//--- Event handler
   virtual void      OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam);
  };
//+------------------------------------------------------------------+
//| Protected constructor with an object type,                       |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CScrollBarHorisontal::CScrollBarHorisontal(const ENUM_GRAPH_ELEMENT_TYPE type,
                                           CGCnvElement *main_obj,CGCnvElement *base_obj,
                                           const long chart_id,
                                           const int subwindow,
                                           const string descript,
                                           const int x,
                                           const int y,
                                           const int w,
                                           const int h) : CScrollBar(type,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
//--- Set the specified graphical element type for the object and assign the library object type to the current object
   this.SetTypeElement(type);
   this.CreateThumbArea();
  }
//+------------------------------------------------------------------+
//| Constructor indicating the main and base objects,                |
//| chart ID and subwindow                                           |
//+------------------------------------------------------------------+
CScrollBarHorisontal::CScrollBarHorisontal(CGCnvElement *main_obj,CGCnvElement *base_obj,
                                           const long chart_id,
                                           const int subwindow,
                                           const string descript,
                                           const int x,
                                           const int y,
                                           const int w,
                                           const int h) : CScrollBar(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL,main_obj,base_obj,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_HORISONTAL);
   this.CreateThumbArea();
  }
//+------------------------------------------------------------------+
//| Create the ArrowButton objects                                   |
//+------------------------------------------------------------------+
void CScrollBarHorisontal::CreateArrowButtons(const int width,const int height)
  {
//--- Set the size of the buttons equal to the width of the scrollbar without the size of its frame
   int size=this.Thickness()-this.BorderSizeTop()-this.BorderSizeBottom();
//--- Create the buttons with left and right arrows and the area capture object. The arrow size is set to 2
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT,0,0,size,size,this.BackgroundColor(),255,true,false);
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT,this.Width()-width,0,size,size,this.BackgroundColor(),255,true,false);
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_SCROLL_BAR_THUMB,size,0,DEF_CONTROL_SCROLL_BAR_THUMB_SIZE_MIN,size,CLR_DEF_CONTROL_SCROLL_BAR_THUMB_COLOR,255,true,false);
   this.SetArrowSize(2);
//--- Get the pointer to the left arrow button and set the colors of its various states for it
   CArrowLeftButton *bl=this.GetArrowButtonLeft();
   if(bl!=NULL)
     {
      bl.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_COLOR,true);
      bl.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_DOWN);
      bl.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_OVER);
      bl.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_COLOR,true);
      bl.SetForeColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_DOWN);
      bl.SetForeColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_OVER);
     }
//--- Get the pointer to the right arrow button and set the colors of its various states for it
   CArrowRightButton *br=this.GetArrowButtonRight();
   if(br!=NULL)
     {
      br.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_COLOR,true);
      br.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_DOWN);
      br.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_MOUSE_OVER);
      br.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_COLOR,true);
      br.SetForeColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_DOWN);
      br.SetForeColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_BUTT_FORE_MOUSE_OVER);
     }
//--- Get the pointer to the capture area object and set the colors of its various states for it
   CScrollBarThumb *th=this.GetThumb();
   if(th!=NULL)
     {
      th.SetBackgroundColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_COLOR,true);
      th.SetBorderColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_BORDER_COLOR,true);
      th.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_MOUSE_DOWN);
      th.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_MOUSE_OVER);
      th.SetForeColor(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_COLOR,true);
      th.SetForeColorMouseDown(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_MOUSE_DOWN);
      th.SetForeColorMouseOver(CLR_DEF_CONTROL_SCROLL_BAR_THUMB_FORE_MOUSE_OVER);
     }
  }
//+------------------------------------------------------------------+
//| Set the new size                                                 |
//+------------------------------------------------------------------+
bool CScrollBarHorisontal::Resize(const int w,const int h,const bool redraw)
  {
//--- If failed to change the object size, return 'false'
   if(!CWinFormBase::Resize(w,h,redraw))
      return false;
//--- Get the button object with the right arrow
   CArrowRightButton *br=this.GetArrowButtonRight();
//--- If the button is not received, return 'false'
   if(br==NULL)
      return false;
//--- Move the button to the right edge of the scrollbar
   if(br.Move(this.RightEdge()-this.BorderSizeRight()-br.Width(),br.CoordY()))
     {
      //--- Set new relative coordinates for the button
      br.SetCoordXRelative(br.CoordX()-this.CoordX());
      br.SetCoordYRelative(br.CoordY()-this.CoordY());
     }
//--- Set the slider parameters
   this.SetThumbParams();
//--- Successful
   return true;
  }
//+------------------------------------------------------------------+
//| Calculate and set the parameters of the capture area (slider)    |
//+------------------------------------------------------------------+
int CScrollBarHorisontal::SetThumbParams(void)
  {
//--- Get the base object
   CWinFormBase *base=this.GetBase();
   if(base==NULL)
      return 0;
//--- Get the capture area object (slider)
   CScrollBarThumb *thumb=this.GetThumb();
   if(thumb==NULL)
      return 0;
//--- Get the width size of the visible part inside the container
   int base_w=base.WidthWorkspace();
//--- Calculate the total width of all attached objects
   int objs_w=base_w+base.OversizeLeft()+base.OversizeRight();
//--- Calculate the relative size of the visible part window
   double px=(double)base_w/double(objs_w!=0 ? objs_w : 1);
//--- Calculate and adjust the size of the slider relative to the width of its workspace (not less than the minimum size)
   int thumb_size=(int)::floor(this.BarWorkAreaSize()*px);
   if(thumb_size<DEF_CONTROL_SCROLL_BAR_THUMB_SIZE_MIN)
      thumb_size=DEF_CONTROL_SCROLL_BAR_THUMB_SIZE_MIN;
   if(thumb_size>this.BarWorkAreaSize())
      thumb_size=this.BarWorkAreaSize();
//--- Calculate the coordinate of the slider and change its size to match the previously calculated one
   int thumb_x=this.CalculateThumbAreaDistance(thumb_size);
   if(!thumb.Resize(thumb_size,thumb.Height(),true))
      return 0;
//--- Shift the slider by the calculated X coordinate
   if(thumb.Move(this.BarWorkAreaCoord()+thumb_x,thumb.CoordY()))
     {
      thumb.SetCoordXRelative(thumb.CoordX()-this.CoordX());
      thumb.SetCoordYRelative(thumb.CoordY()-this.CoordY());
     }
//--- Return the calculated slider size
   return thumb_size;
  }
//+------------------------------------------------------------------+
//| Calculate the distance of the capture area (slider)              |
//+------------------------------------------------------------------+
int CScrollBarHorisontal::CalculateThumbAreaDistance(const int thumb_size)
  {
   CWinFormBase *base=this.GetBase();
   if(base==NULL)
      return 0;
   double x=(double)thumb_size/(double)base.WidthWorkspace();
   return (int)::ceil((double)base.OversizeLeft()*x);
  }
//+------------------------------------------------------------------+
//| Return the size of the slider working area                       |
//+------------------------------------------------------------------+
int CScrollBarHorisontal::BarWorkAreaSize(void)
  {
   CArrowLeftButton  *bl=this.GetArrowButtonLeft();
   CArrowRightButton *br=this.GetArrowButtonRight();
   int x1=(bl!=NULL ? bl.RightEdge() : this.CoordX()+this.BorderSizeLeft());
   int x2=(br!=NULL ? br.CoordX() : this.RightEdge()-this.BorderSizeRight());
   return(x2-x1);
  }
//+------------------------------------------------------------------+
//| Return the coordinate of the beginning of the slider working area|
//+------------------------------------------------------------------+
int CScrollBarHorisontal::BarWorkAreaCoord(void)
  {
   CArrowLeftButton  *bl=this.GetArrowButtonLeft();
   return(bl!=NULL ? bl.RightEdge() : this.CoordX()+this.BorderSizeLeft());
  }
//+------------------------------------------------------------------+
//| Timer                                                            |
//+------------------------------------------------------------------+
void CScrollBarHorisontal::OnTimer(void)
  {

  }
//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CScrollBarHorisontal::OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Adjust subwindow Y shift
   CGCnvElement::OnChartEvent(id,lparam,dparam,sparam);
//--- Get the pointers to control objects of the scrollbar
   CArrowLeftButton  *buttl=this.GetArrowButtonLeft();
   CArrowRightButton *buttr=this.GetArrowButtonRight();
   CScrollBarThumb   *thumb=this.GetThumb();
   if(buttl==NULL || buttr==NULL || thumb==NULL)
      return;
//--- If the event ID is an object movement
   if(id==WF_CONTROL_EVENT_MOVING)
     {
      //--- Move the scrollbar to the foreground
      this.BringToTop();
      //--- Declare the variables for the coordinates of the capture area
      int x=(int)lparam;
      int y=(int)dparam;
      //--- Set the Y coordinate equal to the Y coordinate of the control element
      y=this.CoordY()+this.BorderSizeTop();
      //--- Adjust the X coordinate so that the capture area does not go beyond the control, taking into account the arrow buttons
      if(x<buttl.RightEdge())
        x=buttl.RightEdge();
      if(x>buttr.CoordX()-thumb.Width())
        x=buttr.CoordX()-thumb.Width();
      //--- If the capture area object is shifted by the calculated coordinates
      if(thumb.Move(x,y,true))
        {
         //--- set the object relative coordinates
         thumb.SetCoordXRelative(thumb.CoordX()-this.CoordX());
         thumb.SetCoordYRelative(thumb.CoordY()-this.CoordY());
        }
      //--- Get the pointer to the base object
      CWinFormBase *base=this.GetBase();
      if(base!=NULL)
        {
         //--- Check if the content goes beyond the container
         base.CheckForOversize();

         //--- Calculate the distance the slider is from the left border of the scrollbar (from the right side of the left arrow button)
         int distance=thumb.CoordX()-buttl.RightEdge();
         
         //--- Declare a variable that stores the distance value before the slider shift
         static int distance_last=distance;
         //--- Declare a variable that stores the value in screen pixels the slider was shifted by
         int shift_value=0;
         
         //--- If the values of the past and current distances are not equal (the slider is shifted),
         if(distance!=distance_last)
           {
            //--- calculate the value the slider is shifted by
            shift_value=distance_last-distance;
            //--- and enter the new distance into the value of the previous distance for the next calculation
            distance_last=distance;
           }
         
         //--- Get the largest and smallest coordinates of the right and left sides of the base object content
         int cntt_r=(int)base.GetMaxLongPropFromDependent(CANV_ELEMENT_PROP_RIGHT);
         int cntt_l=(int)base.GetMinLongPropFromDependent(CANV_ELEMENT_PROP_COORD_X);

         //--- Get the coordinate offset of the left side of the base object content
         //--- relative to the initial coordinate of the base object working area
         int extl=base.CoordXWorkspace()-cntt_l;
         
         //--- Calculate the relative value of the desired coordinate,
         //--- where the contents of the base object, shifted by the slider, should be located
         double x=(double)this.WidthWorkspace()*(double)distance/double(thumb.Width()!=0 ? thumb.Width() : DBL_MIN);
         
         //--- Calculate the required shift value of the base object content along the above calculated coordinate
         int shift_need=extl-(int)::round(x);
         
         //--- If the slider is shifted to the left (positive shift value)
         if(shift_value>0)
           {
            if(cntt_l+shift_need<=base.CoordXWorkspace())
               base.ShiftDependentObj(shift_need,0);
           }
         //--- If the slider is shifted to the right (negative shift value)
         if(shift_value<0)
           {
            if(cntt_r-shift_need>=base.RightEdgeWorkspace())
               base.ShiftDependentObj(shift_need,0);
           }
         ::ChartRedraw(this.ChartID());
        }
     }
//--- If any scroll button is clicked
   if(id==WF_CONTROL_EVENT_CLICK_SCROLL_LEFT || id==WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT)
     {
      //--- Move the scrollbar to the foreground
      this.BringToTop();
      //--- Get the base object
      CWinFormBase *base=this.GetBase();
      if(base==NULL)
         return;
      //--- Calculate how much each side of the content of the base object goes beyond its borders
      base.CheckForOversize();
      //--- Get the largest and smallest coordinates of the right and left sides of the base object content
      int cntt_r=(int)base.GetMaxLongPropFromDependent(CANV_ELEMENT_PROP_RIGHT);
      int cntt_l=(int)base.GetMinLongPropFromDependent(CANV_ELEMENT_PROP_COORD_X);
      //--- Set the number of pixels, by which the content of the base object should be shifted
      int shift=(sparam!="" ? DEF_CONTROL_SCROLL_BAR_SCROLL_STEP_CLICK : DEF_CONTROL_SCROLL_BAR_SCROLL_STEP_WHELL);
      //--- If the left button is clicked
      if(id==WF_CONTROL_EVENT_CLICK_SCROLL_LEFT)
        {
         if(cntt_l+shift<=base.CoordXWorkspace())
            base.ShiftDependentObj(shift,0);
        }
      //--- If the right button is clicked
      if(id==WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT)
        {
         if(cntt_r-shift>=base.RightEdgeWorkspace())
            base.ShiftDependentObj(-shift,0);
        }
      //--- Calculate the width and coordinates of the slider
      this.SetThumbParams();
     }
  }
//+------------------------------------------------------------------+
//| 'The cursor is inside the active area,                           |
//| the mouse wheel is being scrolled                                |
//+------------------------------------------------------------------+
void CScrollBarHorisontal::MouseActiveAreaWhellHandler(const int id,const long& lparam,const double& dparam,const string& sparam)
  {
   ENUM_WF_CONTROL_EVENT evn=(dparam>0 ? WF_CONTROL_EVENT_CLICK_SCROLL_LEFT : dparam<0 ? WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT : WF_CONTROL_EVENT_NO_EVENT);
   this.OnChartEvent(evn,lparam,dparam,sparam);
   ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+
