//+------------------------------------------------------------------+
//|                                                   TabControl.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 "Container.mqh"
#include "GroupBox.mqh"
#include "..\Helpers\TabHeader.mqh"
#include "..\Helpers\TabField.mqh"
//+------------------------------------------------------------------+
//| TabHeader object class of WForms TabControl                      |
//+------------------------------------------------------------------+
class CTabControl : public CContainer
  {
private:
   int               m_item_width;                    // Fixed width of tab titles
   int               m_item_height;                   // Fixed height of tab titles
   int               m_header_padding_x;              // Additional header width if DrawMode==Fixed
   int               m_header_padding_y;              // Additional header height if DrawMode==Fixed
   int               m_field_padding_top;             // Padding of top tab fields
   int               m_field_padding_bottom;          // Padding of bottom tab fields
   int               m_field_padding_left;            // Padding of left tab fields
   int               m_field_padding_right;           // Padding of right tab fields
   bool              m_arr_butt_ud_visible_flag;      // Tab header "up-down" control buttons visibility flag 
   bool              m_arr_butt_lr_visible_flag;      // Tab header "left-right" control buttons visibility flag
//--- (1) Hide and (2) display right-left and up-down button controls
   void              ShowArrLeftRightBox(void);
   void              ShowArrUpDownBox(void);
   void              HideArrLeftRightBox(void);
   void              HideArrUpDownBox(void);
//--- Move right-left and up-down button controls to the foreground
   void              BringToTopArrLeftRightBox(void);
   void              BringToTopArrUpDownBox(void);
//--- Create a new graphical object
   virtual CGCnvElement *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                          const int element_num,
                                          const string descript,
                                          const int x,
                                          const int y,
                                          const int w,
                                          const int h,
                                          const color colour,
                                          const uchar opacity,
                                          const bool movable,
                                          const bool activity);

//--- Return the list of (1) headers, (2) tab fields, the pointer to the (3) up-down and (4) left-right button objects
   CArrayObj        *GetListHeaders(void)          { return this.GetListElementsByType(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER);        }
   CArrayObj        *GetListFields(void)           { return this.GetListElementsByType(GRAPH_ELEMENT_TYPE_WF_TAB_FIELD);         }
   CArrowUpDownBox  *GetArrUpDownBox(void)         { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX,0); }
   CArrowLeftRightBox *GetArrLeftRightBox(void)    { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX,0); }
   
//--- Return the pointer to the (1) last and (2) first visible tab header
   CTabHeader       *GetLastHeader(void)           { return this.GetTabHeader(this.TabPages()-1);                                }
   CTabHeader       *GetFirstVisibleHeader(void);
//--- Set the tab as selected
   void              SetSelected(const int index);
//--- Set the tab as released
   void              SetUnselected(const int index);
//--- Set the number of a selected tab
   void              SetSelectedTabPageNum(const int value) { this.SetProperty(CANV_ELEMENT_PROP_TAB_PAGE_NUMBER,value);         }
//--- Arrange the tab headers according to the set modes
   void              ArrangeTabHeaders(void);
//--- Arrange the tab headers at the (1) top, (2) bottom, (3) left and (4) right
   void              ArrangeTabHeadersTop(void);
   void              ArrangeTabHeadersBottom(void);
   void              ArrangeTabHeadersLeft(void);
   void              ArrangeTabHeadersRight(void);
//--- Stretch tab headers by control size
   void              StretchHeaders(void);
//--- Stretch tab headers by (1) control width and height when positioned on the (2) left and (3) right
   void              StretchHeadersByWidth(void);
   void              StretchHeadersByHeightLeft(void);
   void              StretchHeadersByHeightRight(void);
//--- Scroll the header row (1) to the left, (2) to the right, (3) up when headers are on the left, (4) down, (3) up, (4) down
   void              ScrollHeadersRowToLeft(void);
   void              ScrollHeadersRowToRight(void);
//--- Scroll the row of headers when they are located on the left (1) up, (2) down
   void              ScrollHeadersRowLeftToUp(void);
   void              ScrollHeadersRowLeftToDown(void);
//--- Scroll the row of headers when they are located on the right (1) up, (2) down
   void              ScrollHeadersRowRightToUp(void);
   void              ScrollHeadersRowRightToDown(void);
public:
//--- Create the specified number of tabs
   bool              CreateTabPages(const int total,const int selected_page,const int tab_w=0,const int tab_h=0,const string header_text="");
   
//--- Create a new attached element
   bool              CreateNewElement(const int tab_page,
                                      const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                      const int x,
                                      const int y,
                                      const int w,
                                      const int h,
                                      const color colour,
                                      const uchar opacity,
                                      const bool activity,
                                      const bool redraw);
 
//--- Return the number of (1) bound elements and (2) the bound element by the index in the list
   int               TabElementsTotal(const int tab_page);
   CGCnvElement     *GetTabElement(const int tab_page,const int index);
   
//--- Return by type the (1) list, (2) the number of bound controls, (3) the bound control by index in the list in the specified tab
   CArrayObj        *GetListTabElementsByType(const int tab_page,const ENUM_GRAPH_ELEMENT_TYPE type);
   int               TabElementsTotalByType(const int tab_page,const ENUM_GRAPH_ELEMENT_TYPE type);
   CGCnvElement     *GetTabElementByType(const int tab_page,const ENUM_GRAPH_ELEMENT_TYPE type,const int index);
   
//--- Return a pointer to the (1) tab header, (2) field, (3) the number of tabs, visibility of (4) left-right and (5) up-down buttons
   CTabHeader       *GetTabHeader(const int index)       { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,index);    }
   CWinFormBase     *GetTabField(const int index)        { return this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_FIELD,index);     }
   int               TabPages(void)                      { return(this.GetListHeaders()!=NULL ? this.GetListHeaders().Total() : 0); }
   bool              IsVisibleLeftRightBox(void)         { return this.m_arr_butt_lr_visible_flag;                                  }
   bool              IsVisibleUpDownBox(void)            { return this.m_arr_butt_ud_visible_flag;                                  }
//--- Set the visibility of the (1) left-right, (2) up-down buttons
   void              SetVisibleLeftRightBox(const bool flag);
   void              SetVisibleUpDownBox(const bool flag);
//--- Set the size of the (1) left-right, (2) up-down buttons
   void              SetSizeLeftRightBox(const int value);
   void              SetSizeUpDownBox(const int value);
//--- (1) Set and (2) return the location of tabs on the control
   void              SetAlignment(const ENUM_CANV_ELEMENT_ALIGNMENT alignment)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_TAB_ALIGNMENT,alignment);
                        CArrayObj *list=this.GetListHeaders();
                        if(list==NULL)
                           return;
                        for(int i=0;i<list.Total();i++)
                          {
                           CTabHeader *header=list.At(i);
                           if(header==NULL)
                              continue;
                           header.SetAlignment(alignment);
                          }
                       }
   ENUM_CANV_ELEMENT_ALIGNMENT Alignment(void)  const { return (ENUM_CANV_ELEMENT_ALIGNMENT)this.GetProperty(CANV_ELEMENT_PROP_TAB_ALIGNMENT);  }

//--- (1) Set and (2) get the mode of setting the tab size
   void              SetTabSizeMode(const ENUM_CANV_ELEMENT_TAB_SIZE_MODE mode)
                       {
                        this.SetProperty(CANV_ELEMENT_PROP_TAB_SIZE_MODE,mode);
                        CArrayObj *list=this.GetListHeaders();
                        if(list==NULL)
                           return;
                        for(int i=0;i<list.Total();i++)
                          {
                           CTabHeader *header=list.At(i);
                           if(header==NULL)
                              continue;
                           header.SetTabSizeMode(mode);
                          }
                       }
   ENUM_CANV_ELEMENT_TAB_SIZE_MODE TabSizeMode(void)const{ return (ENUM_CANV_ELEMENT_TAB_SIZE_MODE)this.GetProperty(CANV_ELEMENT_PROP_TAB_SIZE_MODE);}
   
//--- Sets all tab headers to the PaddingWidth and PaddingHeight values
   void              SetHeaderPadding(const int w,const int h);
//--- Set all tab fields to Padding values
   void              SetFieldPadding(const int top,const int bottom,const int left,const int right);
//--- Return the PaddingWidth and PaddingHeight values of the tab titles
   int               HeaderPaddingWidth(void)      const { return this.m_header_padding_x;      }
   int               HeaderPaddingHeight(void)     const { return this.m_header_padding_y;      }
//--- Return the Padding values of the tab fields
   int               FieldPaddingTop(void)         const { return this.m_field_padding_top;     }
   int               FieldPaddingBottom(void)      const { return this.m_field_padding_bottom;  }
   int               FieldPaddingLeft(void)        const { return this.m_field_padding_left;    }
   int               FieldPaddingRight(void)       const { return this.m_field_padding_right;   }
   
//--- (1) Set and (2) return the flag allowing multiple rows of tab headers on the control
   void              SetMultiline(const bool flag)       { this.SetProperty(CANV_ELEMENT_PROP_TAB_MULTILINE,flag);         }
   bool              Multiline(void)               const { return (bool)this.GetProperty(CANV_ELEMENT_PROP_TAB_MULTILINE); }
//--- (1) Set and (2) return the fixed width of tab headers
   void              SetItemWidth(const int value)       { this.m_item_width=value;             }
   int               ItemWidth(void)               const { return this.m_item_width;            }
//--- (1) Set and (2) return the fixed height of tab headers
   void              SetItemHeight(const int value)      { this.m_item_height=value;            }
   int               ItemHeight(void)              const { return this.m_item_height;           }
//--- Set a fixed tab size
   void              SetItemSize(const int w,const int h)
                       {
                        if(this.ItemWidth()!=w)
                           this.SetItemWidth(w);
                        if(this.ItemHeight()!=h)
                           this.SetItemHeight(h);
                       }
//--- Set the title text (1) of the specified tab and (2) by index
   void              SetHeaderText(CTabHeader *header,const string text);
   void              SetHeaderText(const int index,const string text);
   
//--- Set the tab specified by index to selected/not selected
   void              Select(const int index,const bool flag);
//--- Returns the (1) index, (2) the pointer to the selected tab
   int               SelectedTabPageNum(void)      const { return (int)this.GetProperty(CANV_ELEMENT_PROP_TAB_PAGE_NUMBER);}
   CWinFormBase     *SelectedTabPage(void)               { return this.GetTabField(this.SelectedTabPageNum());             }
   
//--- Set the object above all
   virtual void      BringToTop(void);
//--- Show the control
   virtual void      Show(void);
//--- Event handler
   virtual void      OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam);

//--- Constructor
                     CTabControl(const long chart_id,
                                 const int subwindow,
                                 const string descript,
                                 const int x,
                                 const int y,
                                 const int w,
                                 const int h);
  };
//+------------------------------------------------------------------+
//| Constructor indicating the chart and subwindow ID                |
//+------------------------------------------------------------------+
CTabControl::CTabControl(const long chart_id,
                         const int subwindow,
                         const string descript,
                         const int x,
                         const int y,
                         const int w,
                         const int h) : CContainer(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL,chart_id,subwindow,descript,x,y,w,h)
  {
   this.SetTypeElement(GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL);
   this.m_type=OBJECT_DE_TYPE_GWF_CONTAINER;
   this.SetBorderSizeAll(0);
   this.SetBorderStyle(FRAME_STYLE_NONE);
   this.SetOpacity(0,true);
   this.SetBackgroundColor(CLR_CANV_NULL,true);
   this.SetBackgroundColorMouseDown(CLR_CANV_NULL);
   this.SetBackgroundColorMouseOver(CLR_CANV_NULL);
   this.SetBorderColor(CLR_CANV_NULL,true);
   this.SetBorderColorMouseDown(CLR_CANV_NULL);
   this.SetBorderColorMouseOver(CLR_CANV_NULL);
   this.SetForeColor(CLR_DEF_FORE_COLOR,true);
   this.SetAlignment(CANV_ELEMENT_ALIGNMENT_TOP);
   this.SetItemSize(58,18);
   this.SetTabSizeMode(CANV_ELEMENT_TAB_SIZE_MODE_NORMAL);
   this.SetPaddingAll(0);
   this.SetHeaderPadding(6,3);
   this.SetFieldPadding(3,3,3,3);
   this.m_arr_butt_ud_visible_flag=false;
   this.m_arr_butt_lr_visible_flag=false;
  }
//+------------------------------------------------------------------+
//| Create the specified number of tabs                              |
//+------------------------------------------------------------------+
bool CTabControl::CreateTabPages(const int total,const int selected_page,const int tab_w=0,const int tab_h=0,const string header_text="")
  {
//--- Calculate the size and initial coordinates of the tab title
   int w=(tab_w==0 ? this.ItemWidth()  : tab_w);
   int h=(tab_h==0 ? this.ItemHeight() : tab_h);

//--- In the loop by the number of tabs
   CTabHeader *header=NULL;
   CTabField  *field=NULL;
   for(int i=0;i<total;i++)
     {
      //--- Depending on the location of tab titles, set their initial coordinates
      int header_x=2;
      int header_y=2;
      int header_w=w;
      int header_h=h;
      
      //--- Set the current X and Y coordinate depending on the location of the tab headers
      switch(this.Alignment())
        {
         case CANV_ELEMENT_ALIGNMENT_TOP     :
           header_w=w;
           header_h=h;
           header_x=(header==NULL ? 2 : header.RightEdgeRelative());
           header_y=2;
           break;
         case CANV_ELEMENT_ALIGNMENT_BOTTOM  :
           header_w=w;
           header_h=h;
           header_x=(header==NULL ? 2 : header.RightEdgeRelative());
           header_y=this.Height()-header_h-2;
           break;
         case CANV_ELEMENT_ALIGNMENT_LEFT    :
           header_w=h;
           header_h=w;
           header_x=2;
           header_y=(header==NULL ? this.Height()-header_h-2 : header.CoordYRelative()-header_h);
           break;
         case CANV_ELEMENT_ALIGNMENT_RIGHT   :
           header_w=h;
           header_h=w;
           header_x=this.Width()-header_w-2;
           header_y=(header==NULL ? 2 : header.BottomEdgeRelative());
           break;
         default:
           break;
        }
      //--- Create the TabHeader object
      if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,header_x,header_y,header_w,header_h,clrNONE,255,this.Active(),false))
        {
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER),string(i+1));
         return false;
        }
      header=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,i);
      if(header==NULL)
        {
         ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ),this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER),string(i+1));
         return false;
        }
      header.SetBase(this.GetObject());
      header.SetPageNumber(i);
      header.SetGroup(this.Group()+1);
      header.SetBackgroundColor(CLR_DEF_CONTROL_TAB_HEAD_BACK_COLOR,true);
      header.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_TAB_HEAD_MOUSE_DOWN);
      header.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_TAB_HEAD_MOUSE_OVER);
      header.SetBackgroundStateOnColor(CLR_DEF_CONTROL_TAB_HEAD_BACK_COLOR_ON,true);
      header.SetBackgroundStateOnColorMouseDown(CLR_DEF_CONTROL_TAB_HEAD_BACK_DOWN_ON);
      header.SetBackgroundStateOnColorMouseOver(CLR_DEF_CONTROL_TAB_HEAD_BACK_OVER_ON);
      header.SetBorderStyle(FRAME_STYLE_SIMPLE);
      header.SetBorderColor(CLR_DEF_CONTROL_TAB_HEAD_BORDER_COLOR,true);
      header.SetBorderColorMouseDown(CLR_DEF_CONTROL_TAB_HEAD_BORDER_MOUSE_DOWN);
      header.SetBorderColorMouseOver(CLR_DEF_CONTROL_TAB_HEAD_BORDER_MOUSE_OVER);
      header.SetAlignment(this.Alignment());
      header.SetPadding(this.HeaderPaddingWidth(),this.HeaderPaddingHeight(),this.HeaderPaddingWidth(),this.HeaderPaddingHeight());
      if(header_text!="" && header_text!=NULL)
         this.SetHeaderText(header,header_text+string(i+1));
      else
         this.SetHeaderText(header,"TabPage"+string(i+1));
      if(this.Alignment()==CANV_ELEMENT_ALIGNMENT_LEFT)
         header.SetFontAngle(90);
      if(this.Alignment()==CANV_ELEMENT_ALIGNMENT_RIGHT)
         header.SetFontAngle(270);
      header.SetTabSizeMode(this.TabSizeMode());
      
      //--- Save the initial height of the header and set its size in accordance with the header size setting mode
      int h_prev=header_h;
      header.SetSizes(header_w,header_h);
      //--- Get the Y offset of the header position after changing its height and
      //--- shift it by the calculated value only for headers on the left
      int y_shift=header.Height()-h_prev;
      if(header.Move(header.CoordX(),header.CoordY()-(this.Alignment()==CANV_ELEMENT_ALIGNMENT_LEFT ? y_shift : 0)))
        {
         header.SetCoordXRelative(header.CoordX()-this.CoordX());
         header.SetCoordYRelative(header.CoordY()-this.CoordY());
        }
      header.SetVisibleFlag(this.IsVisible(),false);

      //--- Depending on the location of the tab headers, set the initial coordinates of the tab fields
      int field_x=0;
      int field_y=0;
      int field_w=this.Width();
      int field_h=this.Height()-header.Height()-2;
      int header_shift=0;
      
      switch(this.Alignment())
        {
         case CANV_ELEMENT_ALIGNMENT_TOP     :
           field_x=0;
           field_y=header.BottomEdgeRelative();
           field_w=this.Width();
           field_h=this.Height()-header.Height()-2;
           break;
         case CANV_ELEMENT_ALIGNMENT_BOTTOM  :
           field_x=0;
           field_y=0;
           field_w=this.Width();
           field_h=this.Height()-header.Height()-2;
           break;
         case CANV_ELEMENT_ALIGNMENT_LEFT    :
           field_x=header.RightEdgeRelative();
           field_y=0;
           field_h=this.Height();
           field_w=this.Width()-header.Width()-2;
           break;
         case CANV_ELEMENT_ALIGNMENT_RIGHT   :
           field_x=0;
           field_y=0;
           field_h=this.Height();
           field_w=this.Width()-header.Width()-2;
           break;
         default:
           break;
        }
      
      //--- Create the TabField object (tab field)
      if(!this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_TAB_FIELD,field_x,field_y,field_w,field_h,clrNONE,255,true,false))
        {
         ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_TAB_FIELD),string(i+1));
         return false;
        }
      field=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_FIELD,i);
      if(field==NULL)
        {
         ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ),this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_TAB_FIELD),string(i+1));
         return false;
        }
      field.SetBase(this.GetObject());
      field.SetPageNumber(i);
      field.SetGroup(this.Group()+1);
      field.SetBorderSizeAll(1);
      field.SetBorderStyle(FRAME_STYLE_SIMPLE);
      field.SetOpacity(CLR_DEF_CONTROL_TAB_PAGE_OPACITY,true);
      field.SetBackgroundColor(CLR_DEF_CONTROL_TAB_PAGE_BACK_COLOR,true);
      field.SetBackgroundColorMouseDown(CLR_DEF_CONTROL_TAB_PAGE_MOUSE_DOWN);
      field.SetBackgroundColorMouseOver(CLR_DEF_CONTROL_TAB_PAGE_MOUSE_OVER);
      field.SetBorderColor(CLR_DEF_CONTROL_TAB_PAGE_BORDER_COLOR,true);
      field.SetBorderColorMouseDown(CLR_DEF_CONTROL_TAB_PAGE_BORDER_MOUSE_DOWN);
      field.SetBorderColorMouseOver(CLR_DEF_CONTROL_TAB_PAGE_BORDER_MOUSE_OVER);
      field.SetForeColor(CLR_DEF_FORE_COLOR,true);
      field.SetPadding(this.FieldPaddingLeft(),this.FieldPaddingTop(),this.FieldPaddingRight(),this.FieldPaddingBottom());
      field.Hide();
     }
//--- Create left-right and up-down button objects
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX,this.Width()-32,0,15,15,clrNONE,255,this.Active(),false);
   this.CreateNewElement(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX,0,this.Height()-32,15,15,clrNONE,255,this.Active(),false);
//---
   CArrowLeftRightBox *box_lr=this.GetArrLeftRightBox();
   if(box_lr!=NULL)
     {
      this.SetVisibleLeftRightBox(false);
      this.SetSizeLeftRightBox(box_lr.Width());
      box_lr.SetMain(this.GetMain());
      box_lr.SetBase(this.GetObject());
      box_lr.SetBorderStyle(FRAME_STYLE_NONE);
      box_lr.SetBackgroundColor(CLR_CANV_NULL,true);
      box_lr.SetOpacity(0);
      box_lr.Hide();
      CArrowLeftButton *lb=box_lr.GetArrowLeftButton();
      if(lb!=NULL)
        {
         lb.SetMain(this.GetMain());
         lb.SetBase(box_lr);
        }
      CArrowRightButton *rb=box_lr.GetArrowRightButton();
      if(rb!=NULL)
        {
         rb.SetMain(this.GetMain());
         rb.SetBase(box_lr);
        }
     }
//---
   CArrowUpDownBox *box_ud=this.GetArrUpDownBox();
   if(box_ud!=NULL)
     {
      this.SetVisibleUpDownBox(false);
      this.SetSizeUpDownBox(box_ud.Height());
      box_ud.SetMain(this.GetMain());
      box_ud.SetBase(this.GetObject());
      box_ud.SetBorderStyle(FRAME_STYLE_NONE);
      box_ud.SetBackgroundColor(CLR_CANV_NULL,true);
      box_ud.SetOpacity(0);
      box_ud.Hide();
      CArrowDownButton *db=box_ud.GetArrowDownButton();
      if(db!=NULL)
        {
         db.SetMain(this.GetMain());
         db.SetBase(box_ud);
        }
      CArrowUpButton *ub=box_ud.GetArrowUpButton();
      if(ub!=NULL)
        {
         ub.SetMain(this.GetMain());
         ub.SetBase(box_ud);
        }
     }
//--- Arrange all titles in accordance with the specified display modes and select the specified tab
   this.ArrangeTabHeaders();
   this.Select(selected_page,true);
   return true;
  }
//+------------------------------------------------------------------+
//| Create a new attached element                                    |
//+------------------------------------------------------------------+
bool CTabControl::CreateNewElement(const int tab_page,
                                   const ENUM_GRAPH_ELEMENT_TYPE element_type,
                                   const int x,
                                   const int y,
                                   const int w,
                                   const int h,
                                   const color colour,
                                   const uchar opacity,
                                   const bool activity,
                                   const bool redraw)
  {
   CTabField *field=this.GetTabField(tab_page);
   if(field==NULL)
     {
      CMessage::ToLog(DFUN,MSG_ELM_LIST_ERR_FAILED_GET_TAB_OBJ);
      ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_TAB_OBJ)," (Tab",(string)tab_page,")");
      return false;
     }
   return field.CreateNewElement(element_type,x,y,w,h,colour,opacity,activity,redraw);
  }
//+------------------------------------------------------------------+
//| Arrange the tab headers                                          |
//| according to the specified modes                                 |
//+------------------------------------------------------------------+
void CTabControl::ArrangeTabHeaders(void)
  {
   switch(this.Alignment())
     {
      case CANV_ELEMENT_ALIGNMENT_TOP     :  this.ArrangeTabHeadersTop();     break;
      case CANV_ELEMENT_ALIGNMENT_BOTTOM  :  this.ArrangeTabHeadersBottom();  break;
      case CANV_ELEMENT_ALIGNMENT_LEFT    :  this.ArrangeTabHeadersLeft();    break;
      case CANV_ELEMENT_ALIGNMENT_RIGHT   :  this.ArrangeTabHeadersRight();   break;
      default: break;
     }
  }
//+------------------------------------------------------------------+
//| Arrange tab headers on top                                       |
//+------------------------------------------------------------------+
void CTabControl::ArrangeTabHeadersTop(void)
  {
//--- Get the list of tab headers
   CArrayObj *list=this.GetListHeaders();
   if(list==NULL)
      return;
//--- Declare the variables
   int col=0;                                // Column
   int row=0;                                // Row
   int x1_base=2;                            // Initial X coordinate
   int x2_base=this.Width()-2;               // Final X coordinate
   int x_shift=0;                            // Shift the tab set for calculating their exit beyond the container
   int n=0;                                  // The variable for calculating the column index relative to the loop index
//--- In a loop by the list of headers,
   for(int i=0;i<list.Total();i++)
     {
      //--- get the next tab header object
      CTabHeader *header=list.At(i);
      if(header==NULL)
         continue;
      //--- If the flag for positioning headers in several rows is set
      if(this.Multiline())
        {
         //--- Calculate the value of the right edge of the header, taking into account that
         //--- the origin always comes from the left edge of TabControl + 2 pixels
         int x2=header.RightEdgeRelative()-x_shift;
         //--- If the calculated value does not go beyond the right edge of the TabControl minus 2 pixels, 
         //--- set the column number equal to the loop index minus the value in the n variable
         if(x2<x2_base)
            col=i-n;
         //--- If the calculated value goes beyond the right edge of the TabControl minus 2 pixels,
         else
           {
            //--- Increase the row index, calculate the new shift (so that the next object is compared with the TabControl left edge + 2 pixels),
            //--- set the loop index for the n variable, while the column index is set to zero, this is the start of the new row
            row++;
            x_shift=header.CoordXRelative()-2;
            n=i;
            col=0;
           }
         //--- Assign the row and column indices to the tab header and shift it to the calculated coordinates
         header.SetTabLocation(row,col);
         if(header.Move(header.CoordX()-x_shift,header.CoordY()-header.Row()*header.Height()))
           {
            header.SetCoordXRelative(header.CoordX()-this.CoordX());
            header.SetCoordYRelative(header.CoordY()-this.CoordY());
           }
        }
      //--- If only one row of headers is allowed
      else
        {
         header.SetRow(0);
         header.SetColumn(i);
        }
     }

//--- The location of all tab titles is set. Now place them all together with the fields
//--- according to the header row and column indices.

//--- Get the last title in the list
   CTabHeader *last=this.GetTabHeader(list.Total()-1);
//--- If the object is received
   if(last!=NULL)
     {
      //--- If the mode of stretching headers to the width of the container is set, call the stretching method
      if(this.TabSizeMode()==CANV_ELEMENT_TAB_SIZE_MODE_FILL)
         this.StretchHeaders();
      //--- If this is not the first row (with index 0)
      if(last.Row()>0)
        {
         //--- Calculate the offset of the tab field Y coordinate
         int y_shift=last.Row()*last.Height();
         //--- In a loop by the list of headers,
         for(int i=0;i<list.Total();i++)
           {
            //--- get the next object
            CTabHeader *header=list.At(i);
            if(header==NULL)
               continue;
            //--- get the tab field corresponding to the received header
            CTabField  *field=header.GetFieldObj();
            if(field==NULL)
               continue;
            //--- shift the tab header by the calculated row coordinates
            if(header.Move(header.CoordX(),header.CoordY()+y_shift))
              {
               header.SetCoordXRelative(header.CoordX()-this.CoordX());
               header.SetCoordYRelative(header.CoordY()-this.CoordY());
              }
            //--- shift the tab field by the calculated shift
            if(field.Move(field.CoordX(),field.CoordY()+y_shift))
              {
               field.SetCoordXRelative(field.CoordX()-this.CoordX());
               field.SetCoordYRelative(field.CoordY()-this.CoordY());
               //--- change the size of the shifted field by the value of its shift
               field.Resize(field.Width(),field.Height()-y_shift,false);
              }
           }
        }
      //--- If this is the first and only string
      else if(!this.Multiline())
        {
         //--- If the right edge of the header goes beyond the right edge of the container area,
         if(last.RightEdge()>this.RightEdgeWorkspace())
           {
            //--- get the button object with left-right arrows
            CArrowLeftRightBox *arr_box=this.GetArrLeftRightBox();
            if(arr_box!=NULL)
              {
               //--- Calculate object location coordinates
               int x=this.RightEdgeWorkspace()-arr_box.Width()+1;
               int y=last.BottomEdge()-arr_box.Height();
               //--- If the object is shifted by the specified coordinates,
               if(arr_box.Move(x,y))
                 {
                  //--- set its relative coordinates
                  arr_box.SetCoordXRelative(arr_box.CoordX()-this.CoordX());
                  arr_box.SetCoordYRelative(arr_box.CoordY()-this.CoordY());
                  //--- set the visibility flag for the object
                  this.SetVisibleLeftRightBox(true);
                  //--- If the control is visible, display the buttons and bring them to the foreground
                  if(this.IsVisible())
                    {
                     arr_box.Show();
                     arr_box.BringToTop();
                    }
                 }
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Arrange tab headers at the bottom                                |
//+------------------------------------------------------------------+
void CTabControl::ArrangeTabHeadersBottom(void)
  {
//--- Get the list of tab headers
   CArrayObj *list=this.GetListHeaders();
   if(list==NULL)
      return;
//--- Declare the variables
   int col=0;                                // Column
   int row=0;                                // Row
   int x1_base=2;                            // Initial X coordinate
   int x2_base=this.Width()-2;               // Final X coordinate
   int x_shift=0;                            // Shift the tab set for calculating their exit beyond the container
   int n=0;                                  // The variable for calculating the column index relative to the loop index
//--- In a loop by the list of headers,
   for(int i=0;i<list.Total();i++)
     {
      //--- get the next tab header object
      CTabHeader *header=list.At(i);
      if(header==NULL)
         continue;
      //--- If the flag for positioning headers in several rows is set
      if(this.Multiline())
        {
         //--- Calculate the value of the right edge of the header, taking into account that
         //--- the origin always comes from the left edge of TabControl + 2 pixels
         int x2=header.RightEdgeRelative()-x_shift;
         //--- If the calculated value does not go beyond the right edge of the TabControl minus 2 pixels, 
         //--- set the column number equal to the loop index minus the value in the n variable
         if(x2<x2_base)
            col=i-n;
         //--- If the calculated value goes beyond the right edge of the TabControl minus 2 pixels,
         else
           {
            //--- Increase the row index, calculate the new shift (so that the next object is compared with the TabControl left edge + 2 pixels),
            //--- set the loop index for the n variable, while the column index is set to zero, this is the start of the new row
            row++;
            x_shift=header.CoordXRelative()-2;
            n=i;
            col=0;
           }
         //--- Assign the row and column indices to the tab header and shift it to the calculated coordinates
         header.SetTabLocation(row,col);
         if(header.Move(header.CoordX()-x_shift,header.CoordY()+header.Row()*header.Height()))
           {
            header.SetCoordXRelative(header.CoordX()-this.CoordX());
            header.SetCoordYRelative(header.CoordY()-this.CoordY());
           }
        }
      //--- If only one row of headers is allowed
      else
        {
         header.SetRow(0);
         header.SetColumn(i);
        }
     }

//--- The location of all tab titles is set. Now place them all together with the fields
//--- according to the header row and column indices.

//--- Get the last title in the list
   CTabHeader *last=this.GetTabHeader(list.Total()-1);
//--- If the object is received
   if(last!=NULL)
     {
      //--- If the mode of stretching headers to the width of the container is set, call the stretching method
      if(this.TabSizeMode()==CANV_ELEMENT_TAB_SIZE_MODE_FILL)
         this.StretchHeaders();
      //--- If this is not the first row (with index 0)
      if(last.Row()>0)
        {
         //--- Calculate the offset of the tab field Y coordinate
         int y_shift=last.Row()*last.Height();
         //--- In a loop by the list of headers,
         for(int i=0;i<list.Total();i++)
           {
            //--- get the next object
            CTabHeader *header=list.At(i);
            if(header==NULL)
               continue;
            //--- get the tab field corresponding to the received header
            CTabField  *field=header.GetFieldObj();
            if(field==NULL)
               continue;
            //--- shift the tab header by the calculated row coordinates
            if(header.Move(header.CoordX(),header.CoordY()-y_shift))
              {
               header.SetCoordXRelative(header.CoordX()-this.CoordX());
               header.SetCoordYRelative(header.CoordY()-this.CoordY());
              }
            //--- shift the tab field by the calculated shift
            if(field.Move(field.CoordX(),field.CoordY()))
              {
               field.SetCoordXRelative(field.CoordX()-this.CoordX());
               field.SetCoordYRelative(field.CoordY()-this.CoordY());
               //--- change the size of the shifted field by the value of its shift
               field.Resize(field.Width(),field.Height()-y_shift,false);
              }
           }
        }
      //--- If this is the first and only string
      else if(!this.Multiline())
        {
         if(last.RightEdge()>this.RightEdgeWorkspace())
           {
            CArrowLeftRightBox *arr_box=this.GetArrLeftRightBox();
            if(arr_box!=NULL)
              {
               int x=this.RightEdgeWorkspace()-arr_box.Width()+1;
               int y=last.CoordY();
               if(arr_box.Move(x,y))
                 {
                  arr_box.SetCoordXRelative(arr_box.CoordX()-this.CoordX());
                  arr_box.SetCoordYRelative(arr_box.CoordY()-this.CoordY());
                  this.SetVisibleLeftRightBox(true);
                  if(this.IsVisible())
                    {
                     arr_box.Show();
                     arr_box.BringToTop();
                    }
                 }
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Arrange tab headers on the left                                  |
//+------------------------------------------------------------------+
void CTabControl::ArrangeTabHeadersLeft(void)
  {
//--- Get the list of tab headers
   CArrayObj *list=this.GetListHeaders();
   if(list==NULL)
      return;
//--- Declare the variables
   int col=0;                                // Column
   int row=0;                                // Row
   int y1_base=this.Height()-2;              // Initial Y coordinate
   int y2_base=2;                            // Final Y coordinate
   int y_shift=0;                            // Shift the tab set for calculating their exit beyond the container
   int n=0;                                  // The variable for calculating the column index relative to the loop index
//--- In a loop by the list of headers,
   for(int i=0;i<list.Total();i++)
     {
      //--- get the next tab header object
      CTabHeader *header=list.At(i);
      if(header==NULL)
         continue;
      //--- If the flag for positioning headers in several rows is set
      if(this.Multiline())
        {
         //--- Calculate the value of the upper edge of the header, taking into account that
         //--- the origin always comes from the bottom edge of TabControl minus 2 pixels
         int y2=header.CoordYRelative()+y_shift;
         //--- If the calculated value does not go beyond the upper edge of the TabControl minus 2 pixels, 
         //--- set the column number equal to the loop index minus the value in the n variable
         if(y2>=y2_base)
            col=i-n;
         //--- If the calculated value goes beyond the upper edge of the TabControl minus 2 pixels,
         else
           {
            //--- Increase the row index, calculate the new shift (so that the next object is compared with the TabControl left edge + 2 pixels),
            //--- set the loop index for the n variable, while the column index is set to zero, this is the start of the new row
            row++;
            y_shift=this.BottomEdge()-header.BottomEdge()-2;
            n=i;
            col=0;
           }
         //--- Assign the row and column indices to the tab header and shift it to the calculated coordinates
         header.SetTabLocation(row,col);
         if(header.Move(header.CoordX()-header.Row()*header.Width(),header.CoordY()+y_shift))
           {
            header.SetCoordXRelative(header.CoordX()-this.CoordX());
            header.SetCoordYRelative(header.CoordY()-this.CoordY());
           }
        }
      //--- If only one row of headers is allowed
      else
        {
         header.SetRow(0);
         header.SetColumn(i);
        }
     }

//--- The location of all tab titles is set. Now place them all together with the fields
//--- according to the header row and column indices.

//--- Get the last title in the list
   CTabHeader *last=this.GetTabHeader(list.Total()-1);
//--- If the object is received
   if(last!=NULL)
     {
      //--- If the mode of stretching headers to the width of the container is set, call the stretching method
      if(this.TabSizeMode()==CANV_ELEMENT_TAB_SIZE_MODE_FILL)
         this.StretchHeaders();
      //--- If this is not the first row (with index 0)
      if(last.Row()>0)
        {
         //--- Calculate the offset of the tab field X coordinate
         int x_shift=last.Row()*last.Width();
         //--- In a loop by the list of headers,
         for(int i=0;i<list.Total();i++)
           {
            //--- get the next object
            CTabHeader *header=list.At(i);
            if(header==NULL)
               continue;
            //--- get the tab field corresponding to the received header
            CTabField  *field=header.GetFieldObj();
            if(field==NULL)
               continue;
            //--- shift the tab header by the calculated row coordinates
            if(header.Move(header.CoordX()+x_shift,header.CoordY()))
              {
               header.SetCoordXRelative(header.CoordX()-this.CoordX());
               header.SetCoordYRelative(header.CoordY()-this.CoordY());
              }
            //--- shift the tab field by the calculated shift
            if(field.Move(field.CoordX()+x_shift,field.CoordY()))
              {
               field.SetCoordXRelative(field.CoordX()-this.CoordX());
               field.SetCoordYRelative(field.CoordY()-this.CoordY());
               //--- change the size of the shifted field by the value of its shift
               field.Resize(field.Width()-x_shift,field.Height(),false);
              }
           }
        }
      //--- If this is the first and only string
      else if(!this.Multiline())
        {
         if(last.CoordY()<this.CoordY())
           {
            CArrowUpDownBox *arr_box=this.GetArrUpDownBox();
            if(arr_box!=NULL)
              {
               int x=last.RightEdge()-arr_box.Width();
               int y=this.CoordY()-1;
               if(arr_box.Move(x,y))
                 {
                  arr_box.SetCoordXRelative(arr_box.CoordX()-this.CoordX());
                  arr_box.SetCoordYRelative(arr_box.CoordY()-this.CoordY());
                  this.SetVisibleUpDownBox(true);
                  if(this.IsVisible())
                    {
                     arr_box.Show();
                     arr_box.BringToTop();
                    }
                 }
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Arrange tab headers to the right                                 |
//+------------------------------------------------------------------+
void CTabControl::ArrangeTabHeadersRight(void)
  {
//--- Get the list of tab headers
   CArrayObj *list=this.GetListHeaders();
   if(list==NULL)
      return;
//--- Declare the variables
   int col=0;                                // Column
   int row=0;                                // Row
   int y1_base=2;                            // Initial Y coordinate
   int y2_base=this.Height()-2;              // Final Y coordinate
   int y_shift=0;                            // Shift the tab set for calculating their exit beyond the container
   int n=0;                                  // The variable for calculating the column index relative to the loop index
//--- In a loop by the list of headers,
   for(int i=0;i<list.Total();i++)
     {
      //--- get the next tab header object
      CTabHeader *header=list.At(i);
      if(header==NULL)
         continue;
      //--- If the flag for positioning headers in several rows is set
      if(this.Multiline())
        {
         //--- Calculate the value of the bottom edge of the header, taking into account that
         //--- the origin always comes from the upper edge of TabControl + 2 pixels
         int y2=header.BottomEdgeRelative()-y_shift;
         //--- If the calculated value does not go beyond the bottom edge of the TabControl minus 2 pixels, 
         //--- set the column number equal to the loop index minus the value in the n variable
         if(y2<y2_base)
            col=i-n;
         //--- If the calculated value goes beyond the bottom edge of the TabControl minus 2 pixels,
         else
           {
            //--- Increase the row index, calculate the new shift (so that the next object is compared with the TabControl bottom edge + 2 pixels),
            //--- set the loop index for the n variable, while the column index is set to zero, this is the start of the new row
            row++;
            y_shift=header.CoordYRelative()-2;
            n=i;
            col=0;
           }
         //--- Assign the row and column indices to the tab header and shift it to the calculated coordinates
         header.SetTabLocation(row,col);
         if(header.Move(header.CoordX()+header.Row()*header.Width(),header.CoordY()-y_shift))
           {
            header.SetCoordXRelative(header.CoordX()-this.CoordX());
            header.SetCoordYRelative(header.CoordY()-this.CoordY());
           }
        }
      //--- If only one row of headers is allowed
      else
        {
         header.SetRow(0);
         header.SetColumn(i);
        }
     }

//--- The location of all tab titles is set. Now place them all together with the fields
//--- according to the header row and column indices.

//--- Get the last title in the list
   CTabHeader *last=this.GetTabHeader(list.Total()-1);
//--- If the object is received
   if(last!=NULL)
     {
      //--- If the mode of stretching headers to the width of the container is set, call the stretching method
      if(this.TabSizeMode()==CANV_ELEMENT_TAB_SIZE_MODE_FILL)
         this.StretchHeaders();
      //--- If this is not the first row (with index 0)
      if(last.Row()>0)
        {
         //--- Calculate the offset of the tab field X coordinate
         int x_shift=last.Row()*last.Width();
         //--- In a loop by the list of headers,
         for(int i=0;i<list.Total();i++)
           {
            //--- get the next object
            CTabHeader *header=list.At(i);
            if(header==NULL)
               continue;
            //--- get the tab field corresponding to the received header
            CTabField  *field=header.GetFieldObj();
            if(field==NULL)
               continue;
            //--- shift the tab header by the calculated row coordinates
            if(header.Move(header.CoordX()-x_shift,header.CoordY()))
              {
               header.SetCoordXRelative(header.CoordX()-this.CoordX());
               header.SetCoordYRelative(header.CoordY()-this.CoordY());
               //--- change the tab field size to the X offset value
               field.Resize(field.Width()-x_shift,field.Height(),false);
              }
           }
        }
      //--- If this is the first and only string
      else if(!this.Multiline())
        {
         if(last.BottomEdge()>this.BottomEdge())
           {
            CArrowUpDownBox *arr_box=this.GetArrUpDownBox();
            if(arr_box!=NULL)
              {
               int x=last.CoordX();
               int y=this.BottomEdgeWorkspace()-arr_box.Height()+1;
               if(arr_box.Move(x,y))
                 {
                  arr_box.SetCoordXRelative(arr_box.CoordX()-this.CoordX());
                  arr_box.SetCoordYRelative(arr_box.CoordY()-this.CoordY());
                  this.SetVisibleUpDownBox(true);
                  if(this.IsVisible())
                    {
                     arr_box.Show();
                     arr_box.BringToTop();
                    }
                 }
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Stretch tab headers by control size                              |
//+------------------------------------------------------------------+
void CTabControl::StretchHeaders(void)
  {
//--- Leave if the headers are in one row
   if(!this.Multiline())
      return;
//--- Depending on the location of headers
   switch(this.Alignment())
     {
      case CANV_ELEMENT_ALIGNMENT_TOP     :
      case CANV_ELEMENT_ALIGNMENT_BOTTOM  :
        this.StretchHeadersByWidth();
        break;
      case CANV_ELEMENT_ALIGNMENT_LEFT    :
        this.StretchHeadersByHeightLeft();
        break;
      case CANV_ELEMENT_ALIGNMENT_RIGHT   :
        this.StretchHeadersByHeightRight();
        break;
      default:
        break;
     }
  }
//+------------------------------------------------------------------+
//| Stretch tab headers by control width                             |
//+------------------------------------------------------------------+
void CTabControl::StretchHeadersByWidth(void)
  {
//--- Get the list of tab headers
   CArrayObj *list=this.GetListHeaders();
   if(list==NULL)
      return;
//--- Get the last title in the list
   CTabHeader *last=this.GetTabHeader(list.Total()-1);
   if(last==NULL)
      return;
//--- In the loop by the number of header rows
   for(int i=0;i<last.Row()+1;i++)
     {
      //--- Get the list with the row index equal to the loop index
      CArrayObj *list_row=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_TAB_PAGE_ROW,i,EQUAL);
      if(list_row==NULL)
         continue;
      //--- Get the width of the container, as well as the number of headers in a row, and calculate the width of each header
      int base_size=this.Width()-4;
      int num=list_row.Total();
      int w=(int)round((double)base_size/double(num>0 ? num : 1));
      //--- In the loop by row headers
      for(int j=0;j<list_row.Total();j++)
        {
         //--- Get the current and previous headers from the list by loop index
         CTabHeader *header=list_row.At(j);
         CTabHeader *prev=list_row.At(j-1);
         if(header==NULL)
            continue;
         //--- If the header size is changed
         if(header.Resize(w,header.Height(),false))
           {
            //--- Set new sizes for the header for pressed/unpressed states
            header.SetWidthOn(w+4);
            header.SetWidthOff(w);
            //--- If this is the first header in the row (there is no previous header in the list),
            //--- then it is not necessary to shift it - move on to the next iteration
            if(prev==NULL)
               continue;
            //--- Shift the header to the coordinate of the right edge of the previous header
            if(header.Move(prev.RightEdge(),header.CoordY()))
              {
               header.SetCoordXRelative(header.CoordX()-this.CoordX());
               header.SetCoordYRelative(header.CoordY()-this.CoordY());
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Stretch tab headers by control height                            |
//| when placed on the left                                          |
//+------------------------------------------------------------------+
void CTabControl::StretchHeadersByHeightLeft(void)
  {
//--- Get the list of tab headers
   CArrayObj *list=this.GetListHeaders();
   if(list==NULL)
      return;
//--- Get the last title in the list
   CTabHeader *last=this.GetTabHeader(list.Total()-1);
   if(last==NULL)
      return;
//--- In the loop by the number of header rows
   for(int i=0;i<last.Row()+1;i++)
     {
      //--- Get the list with the row index equal to the loop index
      CArrayObj *list_row=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_TAB_PAGE_ROW,i,EQUAL);
      if(list_row==NULL)
         continue;
      //--- Get the height of the container, as well as the number of headers in a row, and calculate the height of each header
      int base_size=this.Height()-4;
      int num=list_row.Total();
      int h=base_size/(num>0 ? num : 1);
      //--- In the loop by row headers
      for(int j=0;j<list_row.Total();j++)
        {
         //--- Get the current and previous headers from the list by loop index
         CTabHeader *header=list_row.At(j);
         CTabHeader *prev=list_row.At(j-1);
         if(header==NULL)
            continue;
         //--- Save the initial header height
         int h_prev=header.Height();
         //--- If the header size is changed
         if(header.Resize(header.Width(),h,false))
           {
            //--- Set new sizes for the header for pressed/unpressed states
            header.SetHeightOn(h+4);
            header.SetHeightOff(h);
            //--- If this is the first header in the row (there is no previous header in the list)
            if(prev==NULL)
              {
               //--- Calculate the Y offset
               int y_shift=header.Height()-h_prev;
               //--- Shift the header by its calculated offset and move on to the next one
               if(header.Move(header.CoordX(),header.CoordY()-y_shift))
                 {
                  header.SetCoordXRelative(header.CoordX()-this.CoordX());
                  header.SetCoordYRelative(header.CoordY()-this.CoordY());
                 }
               continue;
              }
            //--- Move the header by the coordinate of the top edge of the previous header minus the height of the current one and its calculated offset
            if(header.Move(header.CoordX(),prev.CoordY()-header.Height()))
              {
               header.SetCoordXRelative(header.CoordX()-this.CoordX());
               header.SetCoordYRelative(header.CoordY()-this.CoordY());
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Stretch tab headers by control height                            |
//| when placed on the right                                         |
//+------------------------------------------------------------------+
void CTabControl::StretchHeadersByHeightRight(void)
  {
//--- Get the list of tab headers
   CArrayObj *list=this.GetListHeaders();
   if(list==NULL)
      return;
//--- Get the last title in the list
   CTabHeader *last=this.GetTabHeader(list.Total()-1);
   if(last==NULL)
      return;
//--- In the loop by the number of header rows
   for(int i=0;i<last.Row()+1;i++)
     {
      //--- Get the list with the row index equal to the loop index
      CArrayObj *list_row=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_TAB_PAGE_ROW,i,EQUAL);
      if(list_row==NULL)
         continue;
      //--- Get the height of the container, as well as the number of headers in a row, and calculate the height of each header
      int base_size=this.Height()-4;
      int num=list_row.Total();
      int h=base_size/(num>0 ? num : 1);
      //--- In the loop by row headers
      for(int j=0;j<list_row.Total();j++)
        {
         //--- Get the current and previous headers from the list by loop index
         CTabHeader *header=list_row.At(j);
         CTabHeader *prev=list_row.At(j-1);
         if(header==NULL)
            continue;
         //--- If the header size is changed
         if(header.Resize(header.Width(),h,false))
           {
            //--- Set new sizes for the header for pressed/unpressed states
            header.SetHeightOn(h+4);
            header.SetHeightOff(h);
            //--- If this is the first header in the row (there is no previous header in the list),
            //--- then it is not necessary to shift it - move on to the next iteration
            if(prev==NULL)
               continue;
            //--- Shift the header to the coordinate of the bottom edge of the previous header
            if(header.Move(header.CoordX(),prev.BottomEdge()))
              {
               header.SetCoordXRelative(header.CoordX()-this.CoordX());
               header.SetCoordYRelative(header.CoordY()-this.CoordY());
              }
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Set all tab titles to Padding values                             |
//+------------------------------------------------------------------+
void CTabControl::SetHeaderPadding(const int w,const int h)
  {
   this.m_header_padding_x=w;
   this.m_header_padding_y=h;
   CArrayObj *list=this.GetListHeaders();
   if(list==NULL)
      return;
   for(int i=0;i<list.Total();i++)
     {
      CTabHeader *header=list.At(i);
      if(header==NULL)
         continue;
      header.SetPadding(this.m_header_padding_x,this.m_header_padding_y,this.m_header_padding_x,this.m_header_padding_y);
     }
  }
//+------------------------------------------------------------------+
//| Set all tab fields to Padding                                    |
//+------------------------------------------------------------------+
void CTabControl::SetFieldPadding(const int top,const int bottom,const int left,const int right)
  {
   this.m_field_padding_top=top;
   this.m_field_padding_bottom=bottom;
   this.m_field_padding_left=left;
   this.m_field_padding_right=right;
   CArrayObj *list=this.GetListFields();
   if(list==NULL)
      return;
   for(int i=0;i<list.Total();i++)
     {
      CTabField *field=list.At(i);
      if(field==NULL)
         continue;
      field.SetPadding(left,top,right,bottom);
     }
  }
//+------------------------------------------------------------------+
//| Set the tab as selected                                          |
//+------------------------------------------------------------------+
void CTabControl::SetSelected(const int index)
  {
//--- Get the header by index and
   CTabHeader *header=this.GetTabHeader(index);
   if(header==NULL)
      return;
//--- set it to the "selected" state
   if(!header.State())
     {
      CArrayObj *list=this.GetListHeaders();
      if(list==NULL)
         return;
      for(int i=0;i<list.Total();i++)
        {
         if(i==index)
            continue;
         this.SetUnselected(i);
        }
      header.SetState(true);
     }
//--- save the index of the selected tab
   this.SetSelectedTabPageNum(index);
  }
//+------------------------------------------------------------------+
//| Select the tab as released                                       |
//+------------------------------------------------------------------+
void CTabControl::SetUnselected(const int index)
  {
//--- Get the header by index and
   CTabHeader *header=this.GetTabHeader(index);
   if(header==NULL)
      return;
//--- set it to the "released" state
   if(header.State())
      header.SetState(false);
  }
//+------------------------------------------------------------------+
//| Set the tab as selected/released                                 |
//+------------------------------------------------------------------+
void CTabControl::Select(const int index,const bool flag)
  {
   if(flag)
      this.SetSelected(index);
   else
      this.SetUnselected(index);
  }
//+------------------------------------------------------------------+
//| Set the title text of the specified tab                          |
//+------------------------------------------------------------------+
void CTabControl::SetHeaderText(CTabHeader *header,const string text)
  {
   if(header==NULL)
      return;
   header.SetText(text);
   header.Crop();
  }
//+------------------------------------------------------------------+
//| Set the tab title text by index                                  |
//+------------------------------------------------------------------+
void CTabControl::SetHeaderText(const int index,const string text)
  {
   CTabHeader *header=this.GetElementByType(GRAPH_ELEMENT_TYPE_WF_TAB_HEADER,index);
   this.SetHeaderText(header,text);
  }
//+------------------------------------------------------------------+
//| Get the number of bound elements in the specified tab            |
//+------------------------------------------------------------------+
int CTabControl::TabElementsTotal(const int tab_page)
  {
   CTabField *field=this.GetTabField(tab_page);
   return(field!=NULL ? field.ElementsTotal() : 0);
  }
//+------------------------------------------------------------------+
//| Return the bound element by index in the list                    |
//| in the specified tab                                             |
//+------------------------------------------------------------------+
CGCnvElement *CTabControl::GetTabElement(const int tab_page,const int index)
  {
   CTabField *field=this.GetTabField(tab_page);
   return(field!=NULL ? field.GetElement(index) : NULL);
  }
//+------------------------------------------------------------------+
//| Return the list of bound controls by type                        |
//| in the specified tab                                             |
//+------------------------------------------------------------------+
CArrayObj *CTabControl::GetListTabElementsByType(const int tab_page,const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   CTabField *field=this.GetTabField(tab_page);
   return(field!=NULL ? field.GetListElementsByType(type) : NULL);
  }
//+------------------------------------------------------------------+
//| Get the list of bound elements by type                           |
//| in the specified tab                                             |
//+------------------------------------------------------------------+
int CTabControl::TabElementsTotalByType(const int tab_page,const ENUM_GRAPH_ELEMENT_TYPE type)
  {
   CTabField *field=this.GetTabField(tab_page);
   return(field!=NULL ? field.ElementsTotalByType(type) : 0);
  }
//+------------------------------------------------------------------+
//| Get (by type) the bound element by index in the list             |
//| in the specified tab                                             |
//+------------------------------------------------------------------+
CGCnvElement *CTabControl::GetTabElementByType(const int tab_page,const ENUM_GRAPH_ELEMENT_TYPE type,const int index)
  {
   CTabField *field=this.GetTabField(tab_page);
   return(field!=NULL ? field.GetElementByType(type,index) : NULL);
  }
//+------------------------------------------------------------------+
//| Create a new graphical object                                    |
//+------------------------------------------------------------------+
CGCnvElement *CTabControl::CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                         const int obj_num,
                                         const string descript,
                                         const int x,
                                         const int y,
                                         const int w,
                                         const int h,
                                         const color colour,
                                         const uchar opacity,
                                         const bool movable,
                                         const bool activity)
  {
   CGCnvElement *element=NULL;
   switch(type)
     {
      case GRAPH_ELEMENT_TYPE_ELEMENT                 : element=new CGCnvElement(type,this.ID(),obj_num,this.ChartID(),this.SubWindow(),descript,x,y,w,h,colour,opacity,movable,activity); break;
      case GRAPH_ELEMENT_TYPE_FORM                    : element=new CForm(this.ChartID(),this.SubWindow(),descript,x,y,w,h);              break;
      case GRAPH_ELEMENT_TYPE_WF_CONTAINER            : element=new CContainer(this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_GROUPBOX             : element=new CGroupBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_PANEL                : element=new CPanel(this.ChartID(),this.SubWindow(),descript,x,y,w,h);             break;
      case GRAPH_ELEMENT_TYPE_WF_LABEL                : element=new CLabel(this.ChartID(),this.SubWindow(),descript,x,y,w,h);             break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKBOX             : element=new CCheckBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_RADIOBUTTON          : element=new CRadioButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON               : element=new CButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);            break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX             : element=new CListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);           break;
      case GRAPH_ELEMENT_TYPE_WF_LIST_BOX_ITEM        : element=new CListBoxItem(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_CHECKED_LIST_BOX     : element=new CCheckedListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_BUTTON_LIST_BOX      : element=new CButtonListBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_HEADER           : element=new CTabHeader(this.ChartID(),this.SubWindow(),descript,x,y,w,h);         break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_FIELD            : element=new CTabField(this.ChartID(),this.SubWindow(),descript,x,y,w,h);          break;
      case GRAPH_ELEMENT_TYPE_WF_TAB_CONTROL          : element=new CTabControl(this.ChartID(),this.SubWindow(),descript,x,y,w,h);        break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON         : element=new CArrowButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);       break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_UP      : element=new CArrowUpButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);     break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_DOWN    : element=new CArrowDownButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);   break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_LEFT    : element=new CArrowLeftButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);   break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTON_RIGHT   : element=new CArrowRightButton(this.ChartID(),this.SubWindow(),descript,x,y,w,h);  break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX : element=new CArrowUpDownBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h);    break;
      case GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX : element=new CArrowLeftRightBox(this.ChartID(),this.SubWindow(),descript,x,y,w,h); break;
      default  : break;
     }
   if(element==NULL)
      ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.TypeElementDescription(type));
   return element;
  }
//+------------------------------------------------------------------+
//| Show the control                                                 |
//+------------------------------------------------------------------+
void CTabControl::Show(void)
  {
//--- If the element should not be displayed (hidden inside another control), leave
   if(!this.Displayed())
      return;
//--- Get the list of all tab headers
   CArrayObj *list=this.GetListHeaders();
   if(list==NULL)
      return;
//--- If the object has a shadow, display it
   if(this.m_shadow_obj!=NULL)
      this.m_shadow_obj.Show();
//--- Display the container
   CGCnvElement::Show();
//--- Move all elements of the object to the foreground
   this.BringToTop();
  }
//+------------------------------------------------------------------+
//| Set the object above all the rest                                |
//+------------------------------------------------------------------+
void CTabControl::BringToTop(void)
  {
//--- Move all elements of the object to the foreground
   CGCnvElement::Show();
//--- Get the index of the selected tab
   int selected=this.SelectedTabPageNum();
//--- Declare the pointers to tab header objects and tab fields
   CTabHeader *header=NULL;
   CTabField  *field=NULL;
//--- Get the list of all tab headers
   CArrayObj *list=this.GetListHeaders();
   if(list==NULL)
      return;
//--- In a loop by the list of tab headers,
   for(int i=0;i<list.Total();i++)
     {
      //--- get the next header, and if failed to get the object,
      //--- or this is the header of the selected tab, skip it
      header=list.At(i);
      if(header==NULL || header.PageNumber()==selected)
         continue;
      //--- bring the header to the foreground
      header.BringToTop();
      //--- get the tab field corresponding to the current header
      field=header.GetFieldObj();
      if(field==NULL)
         continue;
      //--- Hide the tab field
      field.Hide();
     }
//--- Get the pointer to the title of the selected tab
   header=this.GetTabHeader(selected);
   if(header!=NULL)
     {
      //--- bring the header to the front
      header.BringToTop();
      //--- get the tab field corresponding to the selected tab header
      field=header.GetFieldObj();
      //--- Display the tab field on the foreground
      if(field!=NULL)
         field.BringToTop();
     }
//--- If the object is visible and the "up-down" and "left-right" buttons should be visible, move them to the foreground
   if(this.IsVisible())
     {
      if(this.m_arr_butt_ud_visible_flag)
         this.BringToTopArrUpDownBox();
      if(this.m_arr_butt_lr_visible_flag)
         this.BringToTopArrLeftRightBox();
     }
  }
//+------------------------------------------------------------------+
//| Display Up-down button controls                                  |
//+------------------------------------------------------------------+
void CTabControl::ShowArrUpDownBox(void)
  {
   CArrowUpDownBox *box=this.GetArrUpDownBox();
   if(box==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ)," ",this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX));
      return;
     }
   box.Show();
  }
//+------------------------------------------------------------------+
//| Display the Right-left button controls                           |
//+------------------------------------------------------------------+
void CTabControl::ShowArrLeftRightBox(void)
  {
   CArrowLeftRightBox *box=this.GetArrLeftRightBox();
   if(box==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ)," ",this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX));
      return;
     }
   box.Show();
  }
//+------------------------------------------------------------------+
//| Hide the Up-down button controls                                 |
//+------------------------------------------------------------------+
void CTabControl::HideArrUpDownBox(void)
  {
   CArrowUpDownBox *box=this.GetArrUpDownBox();
   if(box==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ)," ",this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX));
      return;
     }
   box.Hide();
  }
//+------------------------------------------------------------------+
//| Hide the Right-left button controls                              |
//+------------------------------------------------------------------+
void CTabControl::HideArrLeftRightBox(void)
  {
   CArrowLeftRightBox *box=this.GetArrLeftRightBox();
   if(box==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ)," ",this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX));
      return;
     }
   box.Hide();
  }
//+------------------------------------------------------------------+
//| Move Up-down button controls to the foreground                   |
//+------------------------------------------------------------------+
void CTabControl::BringToTopArrUpDownBox(void)
  {
   CArrowUpDownBox *box=this.GetArrUpDownBox();
   if(box==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ)," ",this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_UD_BOX));
      return;
     }
   box.BringToTop();
  }
//+------------------------------------------------------------------+
//|Move right-left button controls to the foreground                 |
//+------------------------------------------------------------------+
void CTabControl::BringToTopArrLeftRightBox(void)
  {
   CArrowLeftRightBox *box=this.GetArrLeftRightBox();
   if(box==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_ELM_LIST_ERR_FAILED_GET_GRAPH_ELEMENT_OBJ)," ",this.TypeElementDescription(GRAPH_ELEMENT_TYPE_WF_ARROW_BUTTONS_LR_BOX));
      return;
     }
   box.BringToTop();
  }
//+------------------------------------------------------------------+
//| Set the visibility of the left-right buttons                     |
//+------------------------------------------------------------------+
void CTabControl::SetVisibleLeftRightBox(const bool flag)
  {
   this.m_arr_butt_lr_visible_flag=flag;
   CArrayObj *list=this.GetListHeaders();
   if(list==NULL)
      return;
   for(int i=0;i<list.Total();i++)
     {
      CTabHeader *obj=list.At(i);
      if(obj==NULL)
         continue;
      obj.SetVisibleLeftRightBox(flag);
     }
  }
//+------------------------------------------------------------------+
//| Set the visibility of up-down buttons                            |
//+------------------------------------------------------------------+
void CTabControl::SetVisibleUpDownBox(const bool flag)
  {
   this.m_arr_butt_ud_visible_flag=flag;
   CArrayObj *list=this.GetListHeaders();
   if(list==NULL)
      return;
   for(int i=0;i<list.Total();i++)
     {
      CTabHeader *obj=list.At(i);
      if(obj==NULL)
         continue;
      obj.SetVisibleUpDownBox(flag);
     }
  }
//+------------------------------------------------------------------+
//| Set the size of left-right buttons                               |
//+------------------------------------------------------------------+
void CTabControl::SetSizeLeftRightBox(const int value)
  {
   CArrowLeftRightBox *butt=this.GetArrLeftRightBox();
   if(butt!=NULL)
      butt.Resize(value,butt.Height(),false);
   CArrayObj *list=this.GetListHeaders();
   if(list==NULL)
      return;
   for(int i=0;i<list.Total();i++)
     {
      CTabHeader *obj=list.At(i);
      if(obj==NULL)
         continue;
      obj.SetSizeLeftRightBox(value);
     }
  }
//+------------------------------------------------------------------+
//| Set the up-down button size                                      |
//+------------------------------------------------------------------+
void CTabControl::SetSizeUpDownBox(const int value)
  {
   CArrowUpDownBox *butt=this.GetArrUpDownBox();
   if(butt!=NULL)
      butt.Resize(butt.Width(),value,false);
   CArrayObj *list=this.GetListHeaders();
   if(list==NULL)
      return;
   for(int i=0;i<list.Total();i++)
     {
      CTabHeader *obj=list.At(i);
      if(obj==NULL)
         continue;
      obj.SetSizeUpDownBox(value);
     }
  }
//+------------------------------------------------------------------+
//| Return the pointer to the first visible header                   |
//+------------------------------------------------------------------+
CTabHeader *CTabControl::GetFirstVisibleHeader(void)
  {
   for(int i=0;i<this.TabPages();i++)
     {
      CTabHeader *obj=this.GetTabHeader(i);
      if(obj==NULL)
         continue;
      switch(this.Alignment())
        {
         case CANV_ELEMENT_ALIGNMENT_TOP     :
         case CANV_ELEMENT_ALIGNMENT_BOTTOM  :
           if(obj.CoordX()==this.CoordXWorkspace()+(obj.State() ? 0 : 2))
              return obj;
           break;
         case CANV_ELEMENT_ALIGNMENT_LEFT  :
           if(obj.BottomEdge()==this.BottomEdgeWorkspace()+(obj.State() ? 0 : -2))
              return obj;
           break;
         case CANV_ELEMENT_ALIGNMENT_RIGHT  :
           if(obj.CoordY()==this.CoordYWorkspace()+(obj.State() ? 0 : 2))
              return obj;
           break;
         default:
           break;
        }
     }
   return NULL;
  }
//+------------------------------------------------------------------+
//| Scroll the header bar to the left                                |
//+------------------------------------------------------------------+
void CTabControl::ScrollHeadersRowToLeft(void)
  {
//--- If there are multiline headers, leave
   if(this.Multiline())
      return;
//--- Declare the variables and get the index of the selected tab
   int shift=0;
   int correct_size=0;
   int selected=this.SelectedTabPageNum();
//--- Get the first visible header
   CTabHeader *first=this.GetFirstVisibleHeader();
   if(first==NULL)
      return;
//--- If the first visible header is selected, set the size adjustment value
   if(first.PageNumber()==selected)
      correct_size=4;
//--- Get the pointer to the very last header
   CTabHeader *last=this.GetLastHeader();
   if(last==NULL)
      return;
//--- If the last heading is fully visible, leave since the shift of all headers to the left is completed
   if(last.RightEdge()<=this.RightEdgeWorkspace())
      return;
//--- Get the shift size
   shift=first.Width()-correct_size;
//--- In the loop by all headers
   for(int i=0;i<this.TabPages();i++)
     {
      //--- get the next header
      CTabHeader *header=this.GetTabHeader(i);
      if(header==NULL)
         continue;
      //--- and, if the header is successfully shifted to the left by 'shift' value,
      if(header.Move(header.CoordX()-shift,header.CoordY()))
        {
         //--- save its new relative coordinates
         header.SetCoordXRelative(header.CoordX()-this.CoordX());
         header.SetCoordYRelative(header.CoordY()-this.CoordY());
         //--- If the title has gone beyond the left edge,
         int x=(i==selected ? 0 : 2);
         if(header.CoordX()-x<this.CoordXWorkspace())
           {
            //--- crop and hide it
            header.Crop();
            header.Hide();
            //--- Get the selected header
            CTabHeader *header_selected=this.GetTabHeader(selected);
            if(header_selected==NULL)
               continue;
            //--- Get the tab field corresponding to the selected header
            CTabField *field_selected=header_selected.GetFieldObj();
            if(field_selected==NULL)
               continue;
            //--- Draw the field frame
            field_selected.DrawFrame();
            field_selected.Update();
           }
         //--- If the header fits the visible area of the control,
         else
           {
            //--- display and redraw it
            header.Show();
            header.Redraw(false);
            //--- Get the tab field corresponding to the header
            CTabField *field=header.GetFieldObj();
            if(field==NULL)
               continue;
            //--- If this is a selected header,
            if(i==selected)
              {
               //--- Draw the field frame
               field.DrawFrame();
               field.Update();
              }
           }
        }
     }
//--- Get the selected header
   CTabHeader *obj=this.GetTabHeader(selected);
//--- If the header is placed in the visible part of the control, bring it to the foreground
   if(obj!=NULL && obj.CoordX()>=this.CoordXWorkspace() && obj.RightEdge()<=this.RightEdgeWorkspace())
      obj.BringToTop();
//--- Redraw the chart to display changes immediately
   ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+
//| Scroll the header bar to the right                               |
//+------------------------------------------------------------------+
void CTabControl::ScrollHeadersRowToRight(void)
  {
//--- If there are multiline headers, leave
   if(this.Multiline())
      return;
//--- Declare the variables and get the index of the selected tab
   int shift=0;
   int correct_size=0;
   int selected=this.SelectedTabPageNum();
//--- Get the first visible header
   CTabHeader *first=this.GetFirstVisibleHeader();
   if(first==NULL)
      return;
//--- Get the header located before the first visible one
   CTabHeader *prev=this.GetTabHeader(first.PageNumber()-1);
//--- If there is no such header, leave since the shift of all headers to the right is completed
   if(prev==NULL)
      return;
//--- If the header is selected, specify the size adjustment value
   if(prev.PageNumber()==selected)
      correct_size=4;
//--- Get the shift size
   shift=prev.Width()-correct_size;
//--- In the loop by all headers
   for(int i=0;i<this.TabPages();i++)
     {
      //--- get the next header
      CTabHeader *header=this.GetTabHeader(i);
      if(header==NULL)
         continue;
      //--- and, if the header is successfully shifted to the right by 'shift' value,
      if(header.Move(header.CoordX()+shift,header.CoordY()))
        {
         //--- save its new relative coordinates
         header.SetCoordXRelative(header.CoordX()-this.CoordX());
         header.SetCoordYRelative(header.CoordY()-this.CoordY());
         //--- If the title goes beyond the left edge,
         int x=(i==selected ? 0 : 2);
         if(header.CoordX()-x<this.CoordXWorkspace())
           {
            //--- crop and hide it
            header.Crop();
            header.Hide();
           }
         //--- If the header fits the visible area of the control,
         else
           {
            //--- display and redraw it
            header.Show();
            header.Redraw(false);
            //--- Get the tab field corresponding to the header
            CTabField *field=header.GetFieldObj();
            if(field==NULL)
               continue;
            //--- If this is a selected header,
            if(i==selected)
              {
               //--- Draw the field frame
               field.DrawFrame();
               field.Update();
              }
           }
        }
     }
//--- Get the selected header
   CTabHeader *obj=this.GetTabHeader(selected);
//--- If the header is placed in the visible part of the control, bring it to the foreground
   if(obj!=NULL && obj.CoordX()>=this.CoordXWorkspace() && obj.RightEdge()<=this.RightEdgeWorkspace())
      obj.BringToTop();
//--- Redraw the chart to display changes immediately
   ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+
//| Scroll the header row up when the headers are on the left        |
//+------------------------------------------------------------------+
void CTabControl::ScrollHeadersRowLeftToUp(void)
  {
//--- If there are multiline headers, leave
   if(this.Multiline())
      return;
//--- Declare the variables and get the index of the selected tab
   int shift=0;
   int correct_size=0;
   int selected=this.SelectedTabPageNum();
//--- Get the first visible header
   CTabHeader *first=this.GetFirstVisibleHeader();
   if(first==NULL)
      return;
//--- Get the header located before the first visible one
   CTabHeader *prev=this.GetTabHeader(first.PageNumber()-1);
//--- If there is no such header, leave since the shift of all headers upwards is completed
   if(prev==NULL)
      return;
//--- If the header is selected, specify the size adjustment value
   if(prev.PageNumber()==selected)
      correct_size=4;
//--- Get the shift size
   shift=prev.Height()-correct_size;
//--- In the loop by all headers
   for(int i=0;i<this.TabPages();i++)
     {
      //--- get the next header
      CTabHeader *header=this.GetTabHeader(i);
      if(header==NULL)
         continue;
      //--- and, if the header is successfully shifted upwards by 'shift' value,
      if(header.Move(header.CoordX(),header.CoordY()-shift))
        {
         //--- save its new relative coordinates
         header.SetCoordXRelative(header.CoordX()-this.CoordX());
         header.SetCoordYRelative(header.CoordY()-this.CoordY());
         //--- If the header goes beyond the lower edge,
         int x=(i==selected ? 0 : 2);
         if(header.BottomEdge()+x>this.BottomEdgeWorkspace())
           {
            //--- crop and hide it
            header.Crop();
            header.Hide();
           }
         //--- If the header fits the visible area of the control,
         else
           {
            //--- display and redraw it
            header.Show();
            header.Redraw(false);
            //--- Get the tab field corresponding to the header
            CTabField *field=header.GetFieldObj();
            if(field==NULL)
               continue;
            //--- If this is a selected header,
            if(i==selected)
              {
               //--- Draw the field frame
               field.DrawFrame();
               field.Update();
              }
           }
        }
     }
//--- Get the selected header
   CTabHeader *obj=this.GetTabHeader(selected);
//--- If the header is placed in the visible part of the control, bring it to the foreground
   if(obj!=NULL && obj.CoordY()>=this.CoordYWorkspace() && obj.BottomEdge()<=this.BottomEdgeWorkspace())
      obj.BringToTop();
//--- Redraw the chart to display changes immediately
   ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+
//| Scroll the header row down when the headers are on the left      |
//+------------------------------------------------------------------+
void CTabControl::ScrollHeadersRowLeftToDown(void)
  {
//--- If there are multiline headers, leave
   if(this.Multiline())
      return;
//--- Declare the variables and get the index of the selected tab
   int shift=0;
   int correct_size=0;
   int selected=this.SelectedTabPageNum();
//--- Get the first visible header
   CTabHeader *first=this.GetFirstVisibleHeader();
   if(first==NULL)
      return;
//--- If the first visible header is selected, set the size adjustment value
   if(first.PageNumber()==selected)
      correct_size=4;
//--- Get the pointer to the very last header
   CTabHeader *last=this.GetLastHeader();
   if(last==NULL)
      return;
//--- If the last heading is fully visible, leave since the shift of all headers downwards is completed
   if(last.CoordY()>=this.CoordYWorkspace())
      return;
//--- Get the shift size
   shift=first.Height()-correct_size;
//--- In the loop by all headers
   for(int i=0;i<this.TabPages();i++)
     {
      //--- get the next header
      CTabHeader *header=this.GetTabHeader(i);
      if(header==NULL)
         continue;
      //--- and, if the header is successfully shifted downwards by 'shift' value,
      if(header.Move(header.CoordX(),header.CoordY()+shift))
        {
         //--- save its new relative coordinates
         header.SetCoordXRelative(header.CoordX()-this.CoordX());
         header.SetCoordYRelative(header.CoordY()-this.CoordY());
         //--- If the header has gone beyond the lower edge,
         int x=(i==selected ? 0 : 2);
         if(header.BottomEdge()-x>this.BottomEdgeWorkspace())
           {
            //--- crop and hide it
            header.Crop();
            header.Hide();
            //--- Get the selected header
            CTabHeader *header_selected=this.GetTabHeader(selected);
            if(header_selected==NULL)
               continue;
            //--- Get the tab field corresponding to the selected header
            CTabField *field_selected=header_selected.GetFieldObj();
            if(field_selected==NULL)
               continue;
            //--- Draw the field frame
            field_selected.DrawFrame();
            field_selected.Update();
           }
         //--- If the header fits the visible area of the control,
         else
           {
            //--- display and redraw it
            header.Show();
            header.Redraw(false);
            //--- Get the tab field corresponding to the header
            CTabField *field=header.GetFieldObj();
            if(field==NULL)
               continue;
            //--- If this is a selected header,
            if(i==selected)
              {
               //--- Draw the field frame
               field.DrawFrame();
               field.Update();
              }
           }
        }
     }
//--- Get the selected header
   CTabHeader *obj=this.GetTabHeader(selected);
//--- If the header is placed in the visible part of the control, bring it to the foreground
   if(obj!=NULL && obj.CoordY()>=this.CoordYWorkspace() && obj.BottomEdge()<=this.BottomEdgeWorkspace())
      obj.BringToTop();
//--- Redraw the chart to display changes immediately
   ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+
//| Scroll the header row up when the headers are on the right       |
//+------------------------------------------------------------------+
void CTabControl::ScrollHeadersRowRightToUp(void)
  {
//--- If there are multiline headers, leave
   if(this.Multiline())
      return;
//--- Declare the variables and get the index of the selected tab
   int shift=0;
   int correct_size=0;
   int selected=this.SelectedTabPageNum();
//--- Get the first visible header
   CTabHeader *first=this.GetFirstVisibleHeader();
   if(first==NULL)
      return;
//--- If the first visible header is selected, set the size adjustment value
   if(first.PageNumber()==selected)
      correct_size=4;
//--- Get the pointer to the very last header
   CTabHeader *last=this.GetLastHeader();
   if(last==NULL)
      return;
//--- If the last heading is fully visible, leave since the shift of all headers upwards is completed
   if(last.BottomEdge()<=this.BottomEdgeWorkspace())
      return;
//--- Get the shift size
   shift=first.Height()-correct_size;
//--- In the loop by all headers
   for(int i=0;i<this.TabPages();i++)
     {
      //--- get the next header
      CTabHeader *header=this.GetTabHeader(i);
      if(header==NULL)
         continue;
      //--- and, if the header is successfully shifted upwards by 'shift' value,
      if(header.Move(header.CoordX(),header.CoordY()-shift))
        {
         //--- save its new relative coordinates
         header.SetCoordXRelative(header.CoordX()-this.CoordX());
         header.SetCoordYRelative(header.CoordY()-this.CoordY());
         //--- If the header has gone beyond the upper edge,
         int x=(i==selected ? 0 : 2);
         if(header.CoordY()-x<this.CoordYWorkspace())
           {
            //--- crop and hide it
            header.Crop();
            header.Hide();
            //--- Get the selected header
            CTabHeader *header_selected=this.GetTabHeader(selected);
            if(header_selected==NULL)
               continue;
            //--- Get the tab field corresponding to the selected header
            CTabField *field_selected=header_selected.GetFieldObj();
            if(field_selected==NULL)
               continue;
            //--- Draw the field frame
            field_selected.DrawFrame();
            field_selected.Update();
           }
         //--- If the header fits the visible area of the control,
         else
           {
            //--- display and redraw it
            header.Show();
            header.Redraw(false);
            //--- Get the tab field corresponding to the header
            CTabField *field=header.GetFieldObj();
            if(field==NULL)
               continue;
            //--- If this is a selected header,
            if(i==selected)
              {
               //--- Draw the field frame
               field.DrawFrame();
               field.Update();
              }
           }
        }
     }
//--- Get the selected header
   CTabHeader *obj=this.GetTabHeader(selected);
//--- If the header is placed in the visible part of the control, bring it to the foreground
   if(obj!=NULL && obj.CoordY()>=this.CoordYWorkspace() && obj.BottomEdge()<=this.BottomEdgeWorkspace())
      obj.BringToTop();
//--- Redraw the chart to display changes immediately
   ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+
//| Scroll the header row down when the headers are on the right     |
//+------------------------------------------------------------------+
void CTabControl::ScrollHeadersRowRightToDown(void)
  {
//--- If there are multiline headers, leave
   if(this.Multiline())
      return;
//--- Declare the variables and get the index of the selected tab
   int shift=0;
   int correct_size=0;
   int selected=this.SelectedTabPageNum();
//--- Get the first visible header
   CTabHeader *first=this.GetFirstVisibleHeader();
   if(first==NULL)
      return;
//--- Get the header located before the first visible one
   CTabHeader *prev=this.GetTabHeader(first.PageNumber()-1);
//--- If there is no such header, leave since the shift of all headers downwards is completed
   if(prev==NULL)
      return;
//--- If the header is selected, specify the size adjustment value
   if(prev.PageNumber()==selected)
      correct_size=4;
//--- Get the shift size
   shift=prev.Height()-correct_size;
//--- In the loop by all headers
   for(int i=0;i<this.TabPages();i++)
     {
      //--- get the next header
      CTabHeader *header=this.GetTabHeader(i);
      if(header==NULL)
         continue;
      //--- and, if the header is successfully shifted downwards by 'shift' value,
      if(header.Move(header.CoordX(),header.CoordY()+shift))
        {
         //--- save its new relative coordinates
         header.SetCoordXRelative(header.CoordX()-this.CoordX());
         header.SetCoordYRelative(header.CoordY()-this.CoordY());
         //--- If the title goes beyond the upper edge
         int x=(i==selected ? 0 : 2);
         if(header.CoordY()-x<this.CoordYWorkspace())
           {
            //--- crop and hide it
            header.Crop();
            header.Hide();
           }
         //--- If the header fits the visible area of the control,
         else
           {
            //--- display and redraw it
            header.Show();
            header.Redraw(false);
            //--- Get the tab field corresponding to the header
            CTabField *field=header.GetFieldObj();
            if(field==NULL)
               continue;
            //--- If this is a selected header,
            if(i==selected)
              {
               //--- Draw the field frame
               field.DrawFrame();
               field.Update();
              }
           }
        }
     }
//--- Get the selected header
   CTabHeader *obj=this.GetTabHeader(selected);
//--- If the header is placed in the visible part of the control, bring it to the foreground
   if(obj!=NULL && obj.CoordY()>=this.CoordYWorkspace() && obj.BottomEdge()<=this.BottomEdgeWorkspace())
      obj.BringToTop();
//--- Redraw the chart to display changes immediately
   ::ChartRedraw(this.ChartID());
  }
//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CTabControl::OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Adjust subwindow Y shift
   CGCnvElement::OnChartEvent(id,lparam,dparam,sparam);

//--- If the tab is selected
   if(id==WF_CONTROL_EVENT_TAB_SELECT)
     {
      //--- Get the header of the selected tab
      CTabHeader *header=this.GetTabHeader(this.SelectedTabPageNum());
      if(header==NULL)
         return;
      //--- Depending on the location of the header row
      switch(this.Alignment())
        {
         //--- Headers at the top/bottom
         case CANV_ELEMENT_ALIGNMENT_TOP     :
         case CANV_ELEMENT_ALIGNMENT_BOTTOM  :
            //--- If the header is cropped, shift the header row to the left
            if(header.RightEdge()>this.RightEdgeWorkspace())
               this.ScrollHeadersRowToLeft();
            break;
         //--- Headers on the left
         case CANV_ELEMENT_ALIGNMENT_LEFT    :
            //--- If the header is cropped, shift the header row downwards
            if(header.CoordY()<this.CoordYWorkspace())
               this.ScrollHeadersRowLeftToDown();
            break;
         //--- Headers on the right
         case CANV_ELEMENT_ALIGNMENT_RIGHT   :
            //--- If the header is cropped, shift the header row upwards
            Print(DFUN,"header.BottomEdge=",header.BottomEdge(),", this.BottomEdgeWorkspace=",this.BottomEdgeWorkspace());
            if(header.BottomEdge()>this.BottomEdgeWorkspace())
               this.ScrollHeadersRowRightToUp();
            break;
         default:
           break;
        }
      
     }

//--- When clicking on any header row scroll button
   if(id>=WF_CONTROL_EVENT_CLICK_SCROLL_LEFT && id<=WF_CONTROL_EVENT_CLICK_SCROLL_DOWN)
     {
      //--- Get the header of the last tab
      CTabHeader *header=this.GetTabHeader(this.GetListHeaders().Total()-1);
      if(header==NULL)
         return;
      int hidden=0;
      
      //--- When clicking on the left arrow header row scroll button
      if(id==WF_CONTROL_EVENT_CLICK_SCROLL_LEFT)
         this.ScrollHeadersRowToRight();
      
      //--- When clicking on the right arrow header row scroll button
      if(id==WF_CONTROL_EVENT_CLICK_SCROLL_RIGHT)
         this.ScrollHeadersRowToLeft();
      
      //--- When clicking on the down arrow header row scroll button
      if(id==WF_CONTROL_EVENT_CLICK_SCROLL_DOWN)
        {
         //--- Depending on the location of the header row
         switch(this.Alignment())
           {
            //--- scroll the headings upwards using the appropriate method
            case CANV_ELEMENT_ALIGNMENT_LEFT    :  this.ScrollHeadersRowLeftToUp();    break;
            case CANV_ELEMENT_ALIGNMENT_RIGHT   :  this.ScrollHeadersRowRightToUp();   break;
            default: break;
           }
        }
      
      //--- When clicking on the up arrow header row scroll button
      if(id==WF_CONTROL_EVENT_CLICK_SCROLL_UP)
        {
         //--- Depending on the location of the header row
         switch(this.Alignment())
           {
            //--- scroll the headings downwards using the appropriate method
            case CANV_ELEMENT_ALIGNMENT_LEFT    :  this.ScrollHeadersRowLeftToDown();  break;
            case CANV_ELEMENT_ALIGNMENT_RIGHT   :  this.ScrollHeadersRowRightToDown(); break;
            default: break;
           }
        }
     }
  }
//+------------------------------------------------------------------+
