ChartBrowser.bundle

Author: Enrico Lambino
Price Data Components
Series array that contains open time of each barSeries array that contains open prices of each barSeries array that contains close prices for each bar
Miscellaneous
It writes information to fileIt reads information from a fileIt writes information to fileIt writes information to fileIt reads information from a fileIt reads information from a fileUses files from the file systemIt writes information to fileIt reads information from a fileIt opens Message Boxes to the user
0 Views
0 Downloads
0 Favorites
ChartBrowser.bundle
// THIS FILE IS GENERATED BY MQL5 PACKER UTILITY
// TO PASS THE CODEBASE VALIDATION PROCESS
// WHICH DOES NOT SUPPORT MORE THAN 32 SOURCE FILES IN ONE PROGRAM

// TO GET ACTUAL SOURCE CODES PLEASE EXTRACT THEM
// FROM THE ATTACHED ARCHIVE ChartBrowser.mq5.zip   

//+------------------------------------------------------------------+
//|                                                 ChartBrowser.mq5 |
//|                                    Copyright (c) 2021, Marketeer |
//|                          https://www.mql5.com/en/users/marketeer |
//|                              https://www.mql5.com/en/code/33770/ |
//|                                                                  |
//|                           https://www.mql5.com/en/articles/7734/ |
//|                           https://www.mql5.com/en/articles/7739/ |
//|                           https://www.mql5.com/ru/articles/7795/ |
//+------------------------------------------------------------------+
#property copyright "Copyright (c) 2021, Marketeer"
#property link "https://www.mql5.com/en/users/marketeer"
#property version "1.0"
#property description "Lists all open charts, indicators, experts, and scripts in sorted order.\n"
#property description "Can be used for fast switching."

const string DIALOG_TITLE = "ChartBrowser";

//#include "ChartBrowser.mqh"
//+------------------------------------------------------------------+
//|                                                 ChartBrowser.mqh |
//|                                    Copyright (c) 2021, Marketeer |
//|                          https://www.mql5.com/en/users/marketeer |
//|                              https://www.mql5.com/en/code/33770/ |
//|                                                                  |
//|                           https://www.mql5.com/en/articles/7734/ |
//|                           https://www.mql5.com/en/articles/7739/ |
//|                           https://www.mql5.com/ru/articles/7795/ |
//+------------------------------------------------------------------+

//#include <ControlsPlus/Dialog.mqh>
//+------------------------------------------------------------------+
//|                                                       Dialog.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "WndContainer.mqh"
//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "Wnd.mqh"
//+------------------------------------------------------------------+
//|                                                          Wnd.mqh |
//|                   Copyright 2009-2018, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include <Controls/Rect.mqh>
//+------------------------------------------------------------------+
//|                                                         Rect.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Structure CPoint                                                 |
//| Usage: point of chart in Cartesian coordinates                   |
//+------------------------------------------------------------------+
struct CPoint
  {
   int               x;                   // horizontal coordinate
   int               y;                   // vertical coordinate
  };
//+------------------------------------------------------------------+
//| Structure CSize                                                  |
//| Usage: size of area of chart in Cartesian coordinates            |
//+------------------------------------------------------------------+
struct CSize
  {
   int               cx;                  // horizontal size
   int               cy;                  // vertical size
  };
//+------------------------------------------------------------------+
//| Structure CRect                                                  |
//| Usage: area of chart in Cartesian coordinates                    |
//+------------------------------------------------------------------+
struct CRect
  {
   int               left;                // left coordinate
   int               top;                 // top coordinate
   int               right;               // right coordinate
   int               bottom;              // bottom coordinate

   //--- methods
   CPoint            LeftTop(void)            const;
   void              LeftTop(const int x,const int y);
   void              LeftTop(const CPoint& point);
   CPoint            RightBottom(void)        const;
   void              RightBottom(const int x,const int y);
   void              RightBottom(const CPoint& point);
   CPoint            CenterPoint(void) const;
   int               Width(void)              const { return(right-left); }
   void              Width(const int w)             { right=left+w;       }
   int               Height(void)             const { return(bottom-top); }
   void              Height(const int h)            { bottom=top+h;       }
   CSize             Size(void)               const;
   void              Size(const int cx,const int cy);
   void              Size(const CSize& size);
   void              SetBound(const int l,const int t,const int r,const int b);
   void              SetBound(const CRect& rect);
   void              SetBound(const CPoint& point,const CSize& size);
   void              SetBound(const CPoint& left_top,const CPoint& right_bottom);
   void              Move(const int x,const int y);
   void              Move(const CPoint& point);
   void              Shift(const int dx,const int dy);
   void              Shift(const CPoint& point);
   void              Shift(const CSize& size);
   bool              Contains(const int x,const int y) const;
   bool              Contains(const CPoint& point) const;
   void              Normalize(void);
  };
//+------------------------------------------------------------------+
//| Get parameters of area                                           |
//+------------------------------------------------------------------+
CPoint CRect::LeftTop(void) const
  {
   CPoint point;
//--- action
   point.x=left;
   point.y=top;
//--- result
   return(point);
  }
//+------------------------------------------------------------------+
//| Set parameters of area                                           |
//+------------------------------------------------------------------+
void CRect::LeftTop(const int x,const int y)
  {
   left=x;
   top =y;
  }
//+------------------------------------------------------------------+
//| Set parameters of area                                           |
//+------------------------------------------------------------------+
void CRect::LeftTop(const CPoint& point)
  {
   left=point.x;
   top =point.y;
  }
//+------------------------------------------------------------------+
//| Get parameters of area                                           |
//+------------------------------------------------------------------+
CPoint CRect::RightBottom(void) const
  {
   CPoint point;
//--- action
   point.x=right;
   point.y=bottom;
//--- result
   return(point);
  }
//+------------------------------------------------------------------+
//| Set parameters of area                                           |
//+------------------------------------------------------------------+
void CRect::RightBottom(const int x,const int y)
  {
   right =x;
   bottom=y;
  }
//+------------------------------------------------------------------+
//| Set parameters of area                                           |
//+------------------------------------------------------------------+
void CRect::RightBottom(const CPoint& point)
  {
   right =point.x;
   bottom=point.y;
  }
//+------------------------------------------------------------------+
//| Get parameters of area                                           |
//+------------------------------------------------------------------+
CPoint CRect::CenterPoint(void) const
  {
   CPoint point;
//--- action
   point.x=left+Width()/2;
   point.y=top+Height()/2;
//--- result
   return(point);
  }
//+------------------------------------------------------------------+
//| Get parameters of area                                           |
//+------------------------------------------------------------------+
CSize CRect::Size(void) const
  {
   CSize size;
//--- action
   size.cx=right-left;
   size.cy=bottom-top;
//--- result
   return(size);
  }
//+------------------------------------------------------------------+
//| Set parameters of area                                           |
//+------------------------------------------------------------------+
void CRect::Size(const int cx,const int cy)
  {
   right =left+cx;
   bottom=top+cy;
  }
//+------------------------------------------------------------------+
//| Set parameters of area                                           |
//+------------------------------------------------------------------+
void CRect::Size(const CSize& size)
  {
   right =left+size.cx;
   bottom=top+size.cy;
  }
//+------------------------------------------------------------------+
//| Set parameters of area                                           |
//+------------------------------------------------------------------+
void CRect::SetBound(const int l,const int t,const int r,const int b)
  {
   left  =l;
   top   =t;
   right =r;
   bottom=b;
  }
//+------------------------------------------------------------------+
//| Set parameters of area                                           |
//+------------------------------------------------------------------+
void CRect::SetBound(const CRect& rect)
  {
   left  =rect.left;
   top   =rect.top;
   right =rect.right;
   bottom=rect.bottom;
  }
//+------------------------------------------------------------------+
//| Set parameters of area                                           |
//+------------------------------------------------------------------+
void CRect::SetBound(const CPoint& point,const CSize& size)
  {
   LeftTop(point);
   Size(size);
  }
//+------------------------------------------------------------------+
//| Set parameters of area                                           |
//+------------------------------------------------------------------+
void CRect::SetBound(const CPoint& left_top,const CPoint& right_bottom)
  {
   LeftTop(left_top);
   RightBottom(right_bottom);
  }
//+------------------------------------------------------------------+
//| Absolute movement of area                                        |
//+------------------------------------------------------------------+
void CRect::Move(const int x,const int y)
  {
   right +=x-left;
   bottom+=y-top;
   left   =x;
   top    =y;
  }
//+------------------------------------------------------------------+
//| Absolute movement of area                                        |
//+------------------------------------------------------------------+
void CRect::Move(const CPoint& point)
  {
   right +=point.x-left;
   bottom+=point.y-top;
   left   =point.x;
   top    =point.y;
  }
//+------------------------------------------------------------------+
//| Relative movement of area                                        |
//+------------------------------------------------------------------+
void CRect::Shift(const int dx,const int dy)
  {
   left  +=dx;
   top   +=dy;
   right +=dx;
   bottom+=dy;
  }
//+------------------------------------------------------------------+
//| Relative movement of area                                        |
//+------------------------------------------------------------------+
void CRect::Shift(const CPoint& point)
  {
   left  +=point.x;
   top   +=point.y;
   right +=point.x;
   bottom+=point.y;
  }
//+------------------------------------------------------------------+
//| Relative movement of area                                        |
//+------------------------------------------------------------------+
void CRect::Shift(const CSize& size)
  {
   left  +=size.cx;
   top   +=size.cy;
   right +=size.cx;
   bottom+=size.cy;
  }
//+------------------------------------------------------------------+
//| Check if a point is within the area                              |
//+------------------------------------------------------------------+
bool CRect::Contains(const int x,const int y) const
  {
//--- check and return the result
   return(x>=left && x<=right && y>=top && y<=bottom);
  }
//+------------------------------------------------------------------+
//| Check if a point is within the area                              |
//+------------------------------------------------------------------+
bool CRect::Contains(const CPoint& point) const
  {
//--- check and return the result
   return(point.x>=left && point.x<=right && point.y>=top && point.y<=bottom);
  }
//+------------------------------------------------------------------+
//| Standardizes the height and width                                |
//+------------------------------------------------------------------+
void CRect::Normalize(void)
  {
   if(left>right)
     {
      int tmp1=left;
      left=right;
      right=tmp1;
     }
   if(top>bottom)
     {
      int tmp2=top;
      top=bottom;
      bottom=tmp2;
     }
  }
//+------------------------------------------------------------------+
//#include "Defines.mqh"
//+------------------------------------------------------------------+
//|                                                      Defines.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+
//--- properties flags
enum ENUM_WND_PROP_FLAGS
  {
   WND_PROP_FLAG_CAN_DBL_CLICK  = 1,                              // can be double clicked by mouse
   WND_PROP_FLAG_CAN_DRAG       = 2,                              // can be dragged by mouse
   WND_PROP_FLAG_CLICKS_BY_PRESS= 4,                              // generates the "click" event series on pressing left mouse button
   WND_PROP_FLAG_CAN_LOCK       = 8,                              // control with fixed state (usually it is a button)
   WND_PROP_FLAG_READ_ONLY      =16                               // read only (usually it is a edit)
  };
//--- state flags
enum ENUM_WND_STATE_FLAGS
  {
   WND_STATE_FLAG_ENABLE        = 1,                              // "object is enabled" flag
   WND_STATE_FLAG_VISIBLE       = 2,                              // "object is visible" flag
   WND_STATE_FLAG_ACTIVE        = 4,                              // "object is active" flag
  };
//--- mouse flags
enum ENUM_MOUSE_FLAGS
  {
   MOUSE_INVALID_FLAGS          =-1,                              // no buttons state
   MOUSE_EMPTY                  = 0,                              // buttons are not pressed
   MOUSE_LEFT                   = 1,                              // left button pressed
   MOUSE_RIGHT                  = 2                               // right button pressed
  };
//--- alignment flags
enum ENUM_WND_ALIGN_FLAGS
  {
   WND_ALIGN_NONE               = 0,                               // no alignment
   WND_ALIGN_LEFT               = 1,                               // align by left border
   WND_ALIGN_TOP                = 2,                               // align by top border
   WND_ALIGN_RIGHT              = 4,                               // align by right border
   WND_ALIGN_BOTTOM             = 8,                               // align by bottom border
   WND_ALIGN_WIDTH              = WND_ALIGN_LEFT|WND_ALIGN_RIGHT,  // justify
   WND_ALIGN_HEIGHT             = WND_ALIGN_TOP|WND_ALIGN_BOTTOM,  // align by top and bottom border
   WND_ALIGN_CLIENT             = WND_ALIGN_WIDTH|WND_ALIGN_HEIGHT // align by all sides
  };
//+------------------------------------------------------------------+
//| Drawing styles and colors                                        |
//+------------------------------------------------------------------+
//--- common
#define CONTROLS_FONT_NAME                  "Trebuchet MS"
#define CONTROLS_FONT_SIZE                  (10)
//--- Text
#define CONTROLS_COLOR_TEXT                 C'0x3B,0x29,0x28'
#define CONTROLS_COLOR_TEXT_SEL             White
#define CONTROLS_COLOR_BG                   White
#define CONTROLS_COLOR_BG_SEL               C'0x33,0x99,0xFF'
//--- Button
#define CONTROLS_BUTTON_COLOR               C'0x3B,0x29,0x28'
#define CONTROLS_BUTTON_COLOR_BG            C'0xDD,0xE2,0xEB'
#define CONTROLS_BUTTON_COLOR_BORDER        C'0xB2,0xC3,0xCF'
//--- Label
#define CONTROLS_LABEL_COLOR                C'0x3B,0x29,0x28'
//--- Edit
#define CONTROLS_EDIT_COLOR                 C'0x3B,0x29,0x28'
#define CONTROLS_EDIT_COLOR_BG              White
#define CONTROLS_EDIT_COLOR_BORDER          C'0xB2,0xC3,0xCF'
//--- Scrolls
#define CONTROLS_SCROLL_COLOR_BG            C'0xEC,0xEC,0xEC'
#define CONTROLS_SCROLL_COLOR_BORDER        C'0xD3,0xD3,0xD3'
//--- Client
#define CONTROLS_CLIENT_COLOR_BG            C'0xDE,0xDE,0xDE'
#define CONTROLS_CLIENT_COLOR_BORDER        C'0x2C,0x2C,0x2C'
//--- ListView
#define CONTROLS_LISTITEM_COLOR_TEXT        C'0x3B,0x29,0x28'
#define CONTROLS_LISTITEM_COLOR_TEXT_SEL    White
#define CONTROLS_LISTITEM_COLOR_BG          White
#define CONTROLS_LISTITEM_COLOR_BG_SEL      C'0x33,0x99,0xFF'
#define CONTROLS_LIST_COLOR_BG              White
#define CONTROLS_LIST_COLOR_BORDER          C'0xB2,0xC3,0xCF'
//--- CheckGroup
#define CONTROLS_CHECKGROUP_COLOR_BG        C'0xF7,0xF7,0xF7'
#define CONTROLS_CHECKGROUP_COLOR_BORDER    C'0xB2,0xC3,0xCF'
//--- RadioGroup
#define CONTROLS_RADIOGROUP_COLOR_BG        C'0xF7,0xF7,0xF7'
#define CONTROLS_RADIOGROUP_COLOR_BORDER    C'0xB2,0xC3,0xCF'
//--- Dialog
#define CONTROLS_DIALOG_COLOR_BORDER_LIGHT  White
#define CONTROLS_DIALOG_COLOR_BORDER_DARK   C'0xB6,0xB6,0xB6'
#define CONTROLS_DIALOG_COLOR_BG            C'0xF0,0xF0,0xF0'
#define CONTROLS_DIALOG_COLOR_CAPTION_TEXT  C'0x28,0x29,0x3B'
#define CONTROLS_DIALOG_COLOR_CLIENT_BG     C'0xF7,0xF7,0xF7'
#define CONTROLS_DIALOG_COLOR_CLIENT_BORDER C'0xC8,0xC8,0xC8'
//+------------------------------------------------------------------+
//| Constants for the controls                                       |
//+------------------------------------------------------------------+
//--- common
#define CONTROLS_INVALID_ID                 (-1)     // invalid ID
#define CONTROLS_INVALID_INDEX              (-1)     // invalid index of array
#define CONTROLS_SELF_MESSAGE               (-1)     // message to oneself
#define CONTROLS_MAXIMUM_ID                 (10000)  // maximum number of IDs in application
#define CONTROLS_BORDER_WIDTH               (1)      // border width
#define CONTROLS_SUBWINDOW_GAP              (3)      // gap between sub-windows along the Y axis
#define CONTROLS_DRAG_SPACING               (50)     // sensitivity threshold for dragging
#define CONTROLS_DBL_CLICK_TIME             (100)    // double click interval
//--- BmpButton
#define CONTROLS_BUTTON_SIZE                (16)     // default size of button (16 x 16)
//--- Scrolls
#define CONTROLS_SCROLL_SIZE                (18)     // default lateral size of scrollbar
#define CONTROLS_SCROLL_THUMB_SIZE          (22)     // default length of scroll box
//--- RadioButton
#define CONTROLS_RADIO_BUTTON_X_OFF         (3)      // X offset of radio button (for RadioButton)
#define CONTROLS_RADIO_BUTTON_Y_OFF         (3)      // Y offset of radio button (for RadioButton)
#define CONTROLS_RADIO_LABEL_X_OFF          (20)     // X offset of label (for RadioButton)
#define CONTROLS_RADIO_LABEL_Y_OFF          (0)      // Y offset of label (for RadioButton)
//--- CheckBox
#define CONTROLS_CHECK_BUTTON_X_OFF         (3)      // X offset of check button (for CheckBox)
#define CONTROLS_CHECK_BUTTON_Y_OFF         (3)      // Y offset of check button (for CheckBox)
#define CONTROLS_CHECK_LABEL_X_OFF          (20)     // X offset of label (for CheckBox)
#define CONTROLS_CHECK_LABEL_Y_OFF          (0)      // Y offset of label (for CheckBox)
//--- Spin
#define CONTROLS_SPIN_BUTTON_X_OFF          (2)      // X offset of button from right (for SpinEdit)
#define CONTROLS_SPIN_MIN_HEIGHT            (18)     // minimal height (for SpinEdit)
#define CONTROLS_SPIN_BUTTON_SIZE           (8)      // default size of button (16 x 8) (for SpinEdit)
//--- Combo
#define CONTROLS_COMBO_BUTTON_X_OFF         (2)      // X offset of button from right (for ComboBox)
#define CONTROLS_COMBO_MIN_HEIGHT           (18)     // minimal height (for ComboBox)
#define CONTROLS_COMBO_ITEM_HEIGHT          (18)     // height of combo box item (for ComboBox)
#define CONTROLS_COMBO_ITEMS_VIEW           (8)      // number of items in combo box (for ComboBox)
//--- ListView
#define CONTROLS_LIST_ITEM_HEIGHT           (18)     // height of list item (for ListView)
//--- Dialog
#define CONTROLS_DIALOG_CAPTION_HEIGHT      (22)     // height of dialog header
#define CONTROLS_DIALOG_BUTTON_OFF          (3)      // offset of dialog buttons
#define CONTROLS_DIALOG_CLIENT_OFF          (2)      // offset of dialog client area
#define CONTROLS_DIALOG_MINIMIZE_LEFT       (10)     // left coordinate of dialog in minimized state
#define CONTROLS_DIALOG_MINIMIZE_TOP        (10)     // top coordinate of dialog in minimized state
#define CONTROLS_DIALOG_MINIMIZE_WIDTH      (100)    // width of dialog in minimized state
#define CONTROLS_DIALOG_MINIMIZE_HEIGHT     (4*CONTROLS_BORDER_WIDTH+CONTROLS_DIALOG_CAPTION_HEIGHT) // height of dialog in minimized state
//+------------------------------------------------------------------+
//| Macro                                                            |
//+------------------------------------------------------------------+
//--- check properties
#define IS_CAN_DBL_CLICK     ((m_prop_flags&WND_PROP_FLAG_CAN_DBL_CLICK)!=0)
#define IS_CAN_DRAG          ((m_prop_flags&WND_PROP_FLAG_CAN_DRAG)!=0)
#define IS_CLICKS_BY_PRESS   ((m_prop_flags&WND_PROP_FLAG_CLICKS_BY_PRESS)!=0)
#define IS_CAN_LOCK          ((m_prop_flags&WND_PROP_FLAG_CAN_LOCK)!=0)
#define IS_READ_ONLY         ((m_prop_flags&WND_PROP_FLAG_READ_ONLY)!=0)
//--- check state
#define IS_ENABLED           ((m_state_flags&WND_STATE_FLAG_ENABLE)!=0)
#define IS_VISIBLE           ((m_state_flags&WND_STATE_FLAG_VISIBLE)!=0)
#define IS_ACTIVE            ((m_state_flags&WND_STATE_FLAG_ACTIVE)!=0)
//+------------------------------------------------------------------+
//| Macro of event handling map                                      |
//+------------------------------------------------------------------+
#define INTERNAL_EVENT                           (-1)
//--- beginning of map
#define EVENT_MAP_BEGIN(class_name)              bool class_name::OnEvent(const int id,const long& lparam,const double& dparam,const string& sparam) {
//--- end of map
#define EVENT_MAP_END(parent_class_name)         return(parent_class_name::OnEvent(id,lparam,dparam,sparam)); }
//--- event handling by numeric ID
#define ON_EVENT(event,control,handler)          if(id==(event+CHARTEVENT_CUSTOM) && lparam==control.Id()) { handler(); return(true); }
//--- event handling by numeric ID by pointer of control
#define ON_EVENT_PTR(event,control,handler)      if(control!=NULL && id==(event+CHARTEVENT_CUSTOM) && lparam==control.Id()) { handler(); return(true); }
//--- event handling without ID analysis
#define ON_NO_ID_EVENT(event,handler)            if(id==(event+CHARTEVENT_CUSTOM)) { return(handler()); }
//--- event handling by row ID
#define ON_NAMED_EVENT(event,control,handler)    if(id==(event+CHARTEVENT_CUSTOM) && sparam==control.Name()) { handler(); return(true); }
//--- handling of indexed event
#define ON_INDEXED_EVENT(event,controls,handler) { int total=ArraySize(controls); for(int i=0;i<total;i++) if(id==(event+CHARTEVENT_CUSTOM) && lparam==controls[i].Id()) return(handler(i)); }
//--- handling of external event
#define ON_EXTERNAL_EVENT(event,handler)         if(id==(event+CHARTEVENT_CUSTOM)) { handler(lparam,dparam,sparam); return(true); }
//+------------------------------------------------------------------+
//| Events                                                           |
//+------------------------------------------------------------------+
#define ON_CLICK                (0)   // clicking on control event
#define ON_DBL_CLICK            (1)   // double clicking on control event
#define ON_SHOW                 (2)   // showing control event
#define ON_HIDE                 (3)   // hiding control event
#define ON_CHANGE               (4)   // changing control event
#define ON_START_EDIT           (5)   // start of editing event
#define ON_END_EDIT             (6)   // end of editing event
#define ON_SCROLL_INC           (7)   // increment of scrollbar event
#define ON_SCROLL_DEC           (8)   // decrement of scrollbar event
#define ON_MOUSE_FOCUS_SET      (9)   // the "mouse cursor entered the control" event
#define ON_MOUSE_FOCUS_KILL     (10)  // the "mouse cursor exited the control" event
#define ON_DRAG_START           (11)  // the "control dragging start" event
#define ON_DRAG_PROCESS         (12)  // the "control is being dragged" event
#define ON_DRAG_END             (13)  // the "control dragging end" event
#define ON_BRING_TO_TOP         (14)  // the "mouse events priority increase" event
#define ON_APP_CLOSE            (100) // "closing the application" event
//+------------------------------------------------------------------+
//#include <Object.mqh>
//+------------------------------------------------------------------+
//|                                                       Object.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "StdLibErr.mqh"
//+------------------------------------------------------------------+
//|                                                    StdLibErr.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#define ERR_USER_INVALID_HANDLE                            1
#define ERR_USER_INVALID_BUFF_NUM                          2
#define ERR_USER_ITEM_NOT_FOUND                            3
#define ERR_USER_ARRAY_IS_EMPTY                            1000
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Class CObject.                                                   |
//| Purpose: Base class for storing elements.                        |
//+------------------------------------------------------------------+
class CObject
  {
private:
   CObject          *m_prev;               // previous item of list
   CObject          *m_next;               // next item of list

public:
                     CObject(void): m_prev(NULL),m_next(NULL)            {                 }
                    ~CObject(void)                                       {                 }
   //--- methods to access protected data
   CObject          *Prev(void)                                    const { return(m_prev); }
   void              Prev(CObject *node)                                 { m_prev=node;    }
   CObject          *Next(void)                                    const { return(m_next); }
   void              Next(CObject *node)                                 { m_next=node;    }
   //--- methods for working with files
   virtual bool      Save(const int file_handle)                         { return(true);   }
   virtual bool      Load(const int file_handle)                         { return(true);   }
   //--- method of identifying the object
   virtual int       Type(void)                                    const { return(0);      }
   //--- method of comparing the objects
   virtual int       Compare(const CObject *node,const int mode=0) const { return(0);      }
  };
//+------------------------------------------------------------------+
class CDragWnd;

#define RTTI _rtti = StringFormat("%s %d", typename(this), &this);

struct CRectCreator: public CRect
{
  CRectCreator(const int x1, const int y1, const int x2, const int y2)
  {
    left = x1;
    top = y1;
    right = x2;
    bottom = y2;
  }
};

//+------------------------------------------------------------------+
//| Class CWnd                                                       |
//| Usage: base class of the control object that creates             |
//|             control panels and indicator panels                  |
//+------------------------------------------------------------------+
class CWnd : public CObject
  {
  public:
   string _rtti;
protected:
   //--- parameters of creation
   long              m_chart_id;            // chart ID
   int               m_subwin;              // chart subwindow
   string            m_name;                // object name
   //--- geometry
   CRect             m_rect;                // chart area
   //--- ID
   long              m_id;                  // object ID
   //--- state flags
   int               m_state_flags;
   //--- properties flags
   int               m_prop_flags;
   //--- alignment
   int               m_align_flags;         // alignment flags
   int               m_align_left;          // fixed offset from left border
   int               m_align_top;           // fixed offset from top border
   int               m_align_right;         // fixed offset from right border
   int               m_align_bottom;        // fixed offset from bottom border
   //--- the last saved state of mouse
   int               m_mouse_x;             // X coordinate
   int               m_mouse_y;             // Y coordinate
   int               m_mouse_flags;         // state of buttons
   uint              m_last_click;          // last click time
   //--- drag object
   CDragWnd         *m_drag_object;         // pointer to the dragged object

public:
                     CWnd(void);
                    ~CWnd(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- release memory
   virtual void      Destroy(const int reason=0);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   virtual bool      OnMouseEvent(const int x,const int y,const int flags);
   //--- naming (read only)
   string            Name(void)                        const { return(m_name);               }
   //--- access the contents of container
   int               ControlsTotal(void)               const { return(0);                    }
   CWnd*             Control(const int ind)            const { return(NULL);                 }
   virtual CWnd*     ControlFind(const long id);
   //--- geometry
   const CRect       Rect(void)                        const { return(m_rect);               }
   int               Left(void)                        const { return(m_rect.left);          }
   virtual void      Left(const int x)                       { m_rect.left=x;                }
   int               Top(void)                         const { return(m_rect.top);           }
   virtual void      Top(const int y)                        { m_rect.top=y;                 }
   int               Right(void)                       const { return(m_rect.right);         }
   virtual void      Right(const int x)                      { m_rect.right=x;               }
   int               Bottom(void)                      const { return(m_rect.bottom);        }
   virtual void      Bottom(const int y)                     { m_rect.bottom=y;              }
   int               Width(void)                       const { return(m_rect.Width());       }
   virtual bool      Width(const int w);
   int               Height(void)                      const { return(m_rect.Height());      }
   virtual bool      Height(const int h);
   CSize             Size(void)                        const { return(m_rect.Size());        }
   virtual bool      Size(const int w,const int h);
   virtual bool      Size(const CSize &size);
   virtual bool      Move(const int x,const int y);
   virtual bool      Move(const CPoint &point);
   virtual bool      Shift(const int dx,const int dy);
   bool              Contains(const int x,const int y) const { return(m_rect.Contains(x,y)); }
   bool              Contains(CWnd *control) const;
   //--- alignment
   void              Alignment(const int flags,const int left,const int top,const int right,const int bottom);
   virtual bool      Align(const CRect &rect);
   
    ENUM_WND_ALIGN_FLAGS Alignment(void) const
    {
      return (ENUM_WND_ALIGN_FLAGS)m_align_flags;
    }
    CRect Margins(void) const
    {
      CRectCreator rect(m_align_left, m_align_top, m_align_right, m_align_bottom);
      return rect;
    }
    void Alignment(const int flags)
    {
      m_align_flags = flags;
    }
    void Margins(const int left, const int top, const int right, const int bottom)
    {
      m_align_left = left;
      m_align_top = top;
      m_align_right = right;
      m_align_bottom = bottom;
    }
    void Margins(const int all)
    {
      m_align_left = all;
      m_align_top = all;
      m_align_right = all;
      m_align_bottom = all;
    }
    
   //--- ID
   virtual long      Id(const long id);
   long              Id(void)                          const { return(m_id);                 }
   //--- state
   bool              IsEnabled(void)                   const { return(IS_ENABLED);           }
   virtual bool      Enable(void);
   virtual bool      Disable(void);
   bool              IsVisible(void)                   const { return(IS_VISIBLE);           }
   virtual bool      Visible(const bool flag);
   virtual bool      Show(void);
   virtual bool      Hide(void);
   bool              IsActive(void)                    const { return(IS_ACTIVE);            }
   virtual bool      Activate(void);
   virtual bool      Deactivate(void);
   //--- state flags
   int               StateFlags(void)                  const { return(m_state_flags);        }
   void              StateFlags(const int flags)             { m_state_flags=flags;          }
   void              StateFlagsSet(const int flags)          { m_state_flags|=flags;         }
   void              StateFlagsReset(const int flags)        { m_state_flags&=~flags;        }
   //--- properties flags
   int               PropFlags(void)                   const { return(m_prop_flags);         }
   void              PropFlags(const int flags)              { m_prop_flags=flags;           }
   void              PropFlagsSet(const int flags)           { m_prop_flags|=flags;          }
   void              PropFlagsReset(const int flags)         { m_prop_flags&=~flags;         }
   //--- for mouse operations
   int               MouseX(void)                      const { return(m_mouse_x);            }
   void              MouseX(const int value)                 { m_mouse_x=value;              }
   int               MouseY(void)                      const { return(m_mouse_y);            }
   void              MouseY(const int value)                 { m_mouse_y=value;              }
   int               MouseFlags(void)                  const { return(m_mouse_flags);        }
   virtual void      MouseFlags(const int value)             { m_mouse_flags=value;          }
   bool              MouseFocusKill(const long id=CONTROLS_INVALID_ID);
   bool              BringToTop(void);

protected:
   //--- internal event handlers
   virtual bool      OnCreate(void)                          { return(true);                 }
   virtual bool      OnDestroy(void)                         { return(true);                 }
   virtual bool      OnMove(void)                            { return(true);                 }
   virtual bool      OnResize(void)                          { return(true);                 }
   virtual bool      OnEnable(void)                          { return(true);                 }
   virtual bool      OnDisable(void)                         { return(true);                 }
   virtual bool      OnShow(void)                            { return(true);                 }
   virtual bool      OnHide(void)                            { return(true);                 }
   virtual bool      OnActivate(void)                        { return(true);                 }
   virtual bool      OnDeactivate(void)                      { return(true);                 }
   virtual bool      OnClick(void);
   virtual bool      OnDblClick(void);
   virtual bool      OnChange(void)                          { return(true);                 }
   //--- mouse event handlers
   virtual bool      OnMouseDown(void);
   virtual bool      OnMouseUp(void);
   //--- handlers of dragging
   virtual bool      OnDragStart(void);
   virtual bool      OnDragProcess(const int x,const int y);
   virtual bool      OnDragEnd(void);
   //--- methods for drag-object
   virtual bool      DragObjectCreate(void)                  { return(false);                }
   virtual bool      DragObjectDestroy(void);
  };
//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
bool CWnd::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   if((id!=CHARTEVENT_MOUSE_MOVE))
      return(false);
   if(!IS_VISIBLE)
      return(false);
   int x=(int)lparam;
   int y=(int)dparam;
   int flags=(int)StringToInteger(sparam);
//---
   if(m_drag_object!=NULL)
      return(m_drag_object.OnMouseEvent(x,y,flags));
//---
   return(OnMouseEvent(x,y,flags));
  }
//+------------------------------------------------------------------+
//| Common handler of mouse events                                   |
//+------------------------------------------------------------------+
bool CWnd::OnMouseEvent(const int x,const int y,const int flags)
  {
  if(!IS_ENABLED || !IS_VISIBLE) return false;
   if(!Contains(x,y))
     {
      //--- if cursor is not inside the element and this element is active - deactivate
      if(IS_ACTIVE)
        {
         //--- reset state and coordinates
         m_mouse_x    =0;
         m_mouse_y    =0;
         m_mouse_flags=MOUSE_INVALID_FLAGS;
         //--- deactivate
         Deactivate();
        }
      return(false);
     }
//--- check the state of the left mouse button
   if((flags&MOUSE_LEFT)!=0)
     {
      //--- left mouse button is pressed
      if(m_mouse_flags==MOUSE_INVALID_FLAGS)
        {
         //--- but not in this control (i.e., cursor entered the element with mouse button pressed)
         //--- activate the control, but there will be no click
         if(!IS_ACTIVE)
           {
            //--- generate event
            EventChartCustom(CONTROLS_SELF_MESSAGE,ON_MOUSE_FOCUS_SET,m_id,0.0,m_name);
            //--- activate
            return(Activate());
           }
         return(true);
        }
      if((m_mouse_flags&MOUSE_LEFT)!=0)
        {
         //--- mouse button has already been pressed
         if(IS_CAN_DRAG)
            return(OnDragProcess(x,y));
         if(IS_CLICKS_BY_PRESS)
           {
            EventChartCustom(CONTROLS_SELF_MESSAGE,ON_CLICK,m_id,0.0,m_name);
            //--- handled
            return(true);
           }
        }
      else
        {
         //--- mouse button has been released (pressing)
         //--- save the state and coordinates
         m_mouse_flags=flags;
         m_mouse_x    =x;
         m_mouse_y    =y;
         //--- call the handler
         return(OnMouseDown());
        }
     }
   else
     {
      //--- left mouse button is released
      if(m_mouse_flags==MOUSE_INVALID_FLAGS)
        {
         //--- cursor entered the control with mouse button released
         //--- activate control and save state to the member
         m_mouse_flags=flags;
         //--- generate event
         EventChartCustom(CONTROLS_SELF_MESSAGE,ON_MOUSE_FOCUS_SET,m_id,0.0,m_name);
         //--- activate
         return(Activate());
        }
      if((m_mouse_flags&MOUSE_LEFT)!=0)
        {
         //--- mouse button has been pressed (clicking)
         //--- save the state and coordinates
         m_mouse_flags=flags;
         m_mouse_x    =x;
         m_mouse_y    =y;
         //--- call the handler
         return(OnMouseUp());
        }
     }
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CWnd::CWnd(void) : m_chart_id(CONTROLS_INVALID_ID),
                   m_subwin(CONTROLS_INVALID_ID),
                   m_name(NULL),
                   m_id(CONTROLS_INVALID_ID),
                   m_state_flags(WND_STATE_FLAG_ENABLE+WND_STATE_FLAG_VISIBLE),
                   m_prop_flags(0),
                   m_align_flags(WND_ALIGN_NONE),
                   m_align_left(0),
                   m_align_top(0),
                   m_align_right(0),
                   m_align_bottom(0),
                   m_mouse_x(0),
                   m_mouse_y(0),
                   m_mouse_flags(MOUSE_INVALID_FLAGS),
                   m_last_click(0),
                   m_drag_object(NULL),
                   _rtti(typename(this))
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CWnd::~CWnd(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CWnd::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- attach to chart
   m_chart_id=chart;
   m_name    =name;
   m_subwin  =subwin;
//--- set coordinates of area
   Left(x1);
   Top(y1);
   Right(x2);
   Bottom(y2);
//--- always successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Destruction of the control                                       |
//+------------------------------------------------------------------+
void CWnd::Destroy(const int reason)
  {
//--- call virtual event handler
   if(OnDestroy())
      m_name="";
  }
//+------------------------------------------------------------------+
//| Find control by specified ID                                     |
//+------------------------------------------------------------------+
CWnd* CWnd::ControlFind(const long id)
  {
   CWnd *result=NULL;
//--- check
   if(id==m_id)
      result=GetPointer(this);
//--- return the result
   return(result);
  }
//+------------------------------------------------------------------+
//| Change width of control                                          |
//+------------------------------------------------------------------+
bool CWnd::Width(const int w)
  {
//--- change width
   m_rect.Width(w);
//--- call virtual event handler
   return(OnResize());
  }
//+------------------------------------------------------------------+
//| Change height of control                                         |
//+------------------------------------------------------------------+
bool CWnd::Height(const int h)
  {
//--- change height
   m_rect.Height(h);
//--- call virtual event handler
   return(OnResize());
  }
//+------------------------------------------------------------------+
//| Resize control                                                   |
//+------------------------------------------------------------------+
bool CWnd::Size(const int w,const int h)
  {
//--- change size
   m_rect.Size(w,h);
//--- call virtual event handler
   return(OnResize());
  }
//+------------------------------------------------------------------+
//| Resize control                                                   |
//+------------------------------------------------------------------+
bool CWnd::Size(const CSize &size)
  {
//--- change size
   m_rect.Size(size);
//--- call virtual event handler
   return(OnResize());
  }
//+------------------------------------------------------------------+
//| Absolute movement of the control 	                              |
//+------------------------------------------------------------------+
bool CWnd::Move(const int x,const int y)
  {
//--- moving
   m_rect.Move(x,y);
//--- call virtual event handler
   return(OnMove());
  }
//+------------------------------------------------------------------+
//| Absolute movement of the control 	                              |
//+------------------------------------------------------------------+
bool CWnd::Move(const CPoint &point)
  {
//--- moving
   m_rect.Move(point);
//--- call virtual event handler
   return(OnMove());
  }
//+------------------------------------------------------------------+
//| Relative movement of the control 	                              |
//+------------------------------------------------------------------+
bool CWnd::Shift(const int dx,const int dy)
  {
//--- moving
   m_rect.Shift(dx,dy);
//--- call virtual event handler
   return(OnMove());
  }
//+------------------------------------------------------------------+
//| Check contains                                                   |
//+------------------------------------------------------------------+
bool CWnd::Contains(CWnd *control) const
  {
//--- check
   if(control==NULL)
      return(false);
//--- result
   return(Contains(control.Left(),control.Top()) && Contains(control.Right(),control.Bottom()));
  }
//+------------------------------------------------------------------+
//| Enables event handling by the control                            |
//+------------------------------------------------------------------+
bool CWnd::Enable(void)
  {
//--- if there are now changes, then succeed
   if(IS_ENABLED)
      return(true);
//--- change flag
   StateFlagsSet(WND_STATE_FLAG_ENABLE);
//--- call virtual event handler
   return(OnEnable());
  }
//+------------------------------------------------------------------+
//| Disables event handling by the control                           |
//+------------------------------------------------------------------+
bool CWnd::Disable(void)
  {
//--- if there are now changes, then succeed
   if(!IS_ENABLED)
      return(true);
//--- change flag
   StateFlagsReset(WND_STATE_FLAG_ENABLE);
//--- call virtual event handler
   return(OnDisable());
  }
//+------------------------------------------------------------------+
//| Set the "object is visible" flag for the control                 |
//+------------------------------------------------------------------+
bool CWnd::Visible(const bool flag)
  {
//--- if there are now changes, then succeed
   if(IS_VISIBLE==flag)
      return(true);
//--- call virtual event handler
   return(flag ? Show() : Hide());
  }
//+------------------------------------------------------------------+
//| Makes the control visible                                        |
//+------------------------------------------------------------------+
bool CWnd::Show(void)
  {
//--- change flag
   StateFlagsSet(WND_STATE_FLAG_VISIBLE);
//--- call virtual event handler
   return(OnShow());
  }
//+------------------------------------------------------------------+
//| Makes the control hidden                                         |
//+------------------------------------------------------------------+
bool CWnd::Hide(void)
  {
//--- change flag
   StateFlagsReset(WND_STATE_FLAG_VISIBLE);
//--- call virtual event handler
   return(OnHide());
  }
//+------------------------------------------------------------------+
//| Makes the control active                                         |
//+------------------------------------------------------------------+
bool CWnd::Activate(void)
  {
//--- if there are now changes, then succeed
   if(IS_ACTIVE)
      return(true);
//--- change flag
   StateFlagsSet(WND_STATE_FLAG_ACTIVE);
//--- call virtual event handler
   return(OnActivate());
  }
//+------------------------------------------------------------------+
//| Makes the control inactive                                       |
//+------------------------------------------------------------------+
bool CWnd::Deactivate(void)
  {
//--- if there are now changes, then succeed
   if(!IS_ACTIVE)
      return(true);
//--- change flag
   StateFlagsReset(WND_STATE_FLAG_ACTIVE);
//--- call virtual event handler
   return(OnDeactivate());
  }
//+------------------------------------------------------------------+
//| Set ID of control                                                |
//+------------------------------------------------------------------+
long CWnd::Id(const long id)
  {
   m_id=id;
//--- always use only one ID
   return(1);
  }
//+------------------------------------------------------------------+
//| Set parameters of alignment                                      |
//+------------------------------------------------------------------+
void CWnd::Alignment(const int flags,const int left,const int top,const int right,const int bottom)
  {
   m_align_flags =flags;
   m_align_left  =left;
   m_align_top   =top;
   m_align_right =right;
   m_align_bottom=bottom;
  }
//+------------------------------------------------------------------+
//| Align element in specified chart area                            |
//+------------------------------------------------------------------+
  bool CWnd::Align(const CRect &rect)
  {
    if(m_align_flags == WND_ALIGN_NONE) return true;

    int new_value = 0;
    int new_width = -1;
    int new_height = -1;
    
    if((m_align_flags & WND_ALIGN_RIGHT) != 0)
    {
      // there is alignment by right border,
      if((m_align_flags & WND_ALIGN_LEFT) != 0)
      {
        // and by left border (change size and move)
        new_width = rect.Width() - m_align_left - m_align_right;
        Move(rect.left + m_align_left, Top());
      }
      else
      {
        // no alignment by left border (move)
        new_value = rect.right - Width() - m_align_right;
        Move(new_value, Top());
      }
    }
    else if((m_align_flags & WND_ALIGN_LEFT) != 0)
    {
      // alignment by left border only
      new_value = rect.left + m_align_left;
      Move(new_value, Top());
    }
     
    if((m_align_flags & WND_ALIGN_BOTTOM) !=0 )
    {
      // there is alignment by bottom border,
      if((m_align_flags & WND_ALIGN_TOP) != 0)
      {
        // and by top border (change size and move)
        new_height = rect.Height() - m_align_top - m_align_bottom;
        Move(Left(), rect.top + m_align_top);
      }
      else
      {
        // no alignment by top border (move)
        new_value = rect.bottom - Height() - m_align_bottom;
        Move(Left(), new_value);
      }
    }
    else
    if((m_align_flags & WND_ALIGN_TOP) != 0)
    {
      // alignment by top border only
      new_value = rect.top + m_align_top;
      Move(Left(), new_value);
    }
    
    if(new_width != -1 || new_height != -1)
    {
      Size(new_width != -1 ? new_width : Width(), new_height != -1 ? new_height : Height());
    }
    
    return true;
  }

//+------------------------------------------------------------------+
//| Remove the mouse focus from control                              |
//+------------------------------------------------------------------+
bool CWnd::MouseFocusKill(const long id)
  {
//--- check
   if(id==m_id)
      return(false);
//--- reset flag
   Deactivate();
//--- clean
   m_mouse_x    =0;
   m_mouse_y    =0;
   m_mouse_flags=MOUSE_INVALID_FLAGS;
//--- call the handler
   return(OnDeactivate());
  }
//+------------------------------------------------------------------+
//| Increases the priority of an element                             |
//+------------------------------------------------------------------+
bool CWnd::BringToTop(void)
  {
//--- generate event
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_BRING_TO_TOP,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "click" event                                     |
//+------------------------------------------------------------------+
bool CWnd::OnClick(void)
  {
//--- send notification
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_CLICK,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "doubl click" event                               |
//+------------------------------------------------------------------+
bool CWnd::OnDblClick(void)
  {
//--- send notification
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_DBL_CLICK,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of click on the left mouse button                        |
//+------------------------------------------------------------------+
bool CWnd::OnMouseDown(void)
  {
   if(IS_CAN_DRAG)
      return(OnDragStart());
   if(IS_CLICKS_BY_PRESS)
      return(OnClick());
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of releasing the left mouse button                       |
//+------------------------------------------------------------------+
bool CWnd::OnMouseUp(void)
  {
   if(IS_CAN_DBL_CLICK)
     {
      uint last_time=GetTickCount();
      if(m_last_click==0 || last_time-m_last_click>CONTROLS_DBL_CLICK_TIME)
        {
         m_last_click=(last_time==0) ? 1 : last_time;
        }
      else
        {
         m_last_click=0;
         return(OnDblClick());
        }
     }
   if(IS_CAN_DRAG)
      return(OnDragEnd());
   if(!IS_CLICKS_BY_PRESS)
      return(OnClick());
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the control dragging start                            |
//+------------------------------------------------------------------+
bool CWnd::OnDragStart(void)
  {
   if(!IS_CAN_DRAG)
      return(true);
//--- disable scrolling of chart with mouse
   ChartSetInteger(m_chart_id,CHART_MOUSE_SCROLL,false);
//--- generate event
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_DRAG_START,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of control dragging process                              |
//+------------------------------------------------------------------+
bool CWnd::OnDragProcess(const int x,const int y)
  {
   Shift(x-m_mouse_x,y-m_mouse_y);
//--- save
   m_mouse_x=x;
   m_mouse_y=y;
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the control dragging end                              |
//+------------------------------------------------------------------+
bool CWnd::OnDragEnd(void)
  {
   if(!IS_CAN_DRAG)
      return(true);
//--- enable scrolling of chart with mouse
   ChartSetInteger(m_chart_id,CHART_MOUSE_SCROLL,true);
//--- generate event
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_DRAG_END,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Destroy the dragged object                                       |
//+------------------------------------------------------------------+
bool CWnd::DragObjectDestroy(void)
  {
   if(m_drag_object!=NULL)
     {
      delete m_drag_object;
      m_drag_object=NULL;
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Class CDragWnd                                                   |
//| Usage: base class for drag                                       |
//+------------------------------------------------------------------+
class CDragWnd : public CWnd
  {
protected:
   int               m_limit_left;          // left constraint
   int               m_limit_top;           // top constraint
   int               m_limit_right;         // right constraint
   int               m_limit_bottom;        // bottom constraint

public:
                     CDragWnd(void);
                    ~CDragWnd(void);
   //--- constraints
   void              Limits(const int l,const int t,const int r,const int b);

protected:
   virtual bool      OnDragProcess(const int x,const int y);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CDragWnd::CDragWnd(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CDragWnd::~CDragWnd(void)
  {
  }
//+------------------------------------------------------------------+
//| Constrain the control dragging                                   |
//+------------------------------------------------------------------+
void CDragWnd::Limits(const int l,const int t,const int r,const int b)
  {
//--- save
   m_limit_left  =l;
   m_limit_top   =t;
   m_limit_right =r;
   m_limit_bottom=b;
  }
//+------------------------------------------------------------------+
//| Handler of control dragging process                              |
//+------------------------------------------------------------------+
bool CDragWnd::OnDragProcess(const int x,const int y)
  {
   int dx=x-m_mouse_x;
   int dy=y-m_mouse_y;
//--- check shift
   if(Right()+dx>m_limit_right)
      dx=m_limit_right-Right();
   if(Left()+dx<m_limit_left)
      dx=m_limit_left-Left();
   if(Bottom()+dy>m_limit_bottom)
      dy=m_limit_bottom-Bottom();
   if(Top()+dy<m_limit_top)
      dy=m_limit_top-Top();
//--- shift
   Shift(dx,dy);
//--- save
   m_mouse_x=x;
   m_mouse_y=y;
//--- generate event
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_DRAG_PROCESS,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//#include <Arrays\ArrayObj.mqh>
//+------------------------------------------------------------------+
//|                                                     ArrayObj.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "Array.mqh"
//+------------------------------------------------------------------+
//|                                                        Array.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include <Object.mqh>
//+------------------------------------------------------------------+
//| Class CArray                                                     |
//| Purpose: Base class of dynamic arrays.                           |
//|          Derives from class CObject.                             |
//+------------------------------------------------------------------+
class CArray : public CObject
  {
protected:
   int               m_step_resize;      // increment size of the array
   int               m_data_total;       // number of elements
   int               m_data_max;         // maximmum size of the array without memory reallocation
   int               m_sort_mode;        // mode of array sorting

public:
                     CArray(void);
                    ~CArray(void);
   //--- methods of access to protected data
   int               Step(void) const { return(m_step_resize); }
   bool              Step(const int step);
   int               Total(void) const { return(m_data_total); }
   int               Available(void) const { return(m_data_max-m_data_total); }
   int               Max(void) const { return(m_data_max); }
   bool              IsSorted(const int mode=0) const { return(m_sort_mode==mode); }
   int               SortMode(void) const { return(m_sort_mode); }
   //--- cleaning method
   void              Clear(void) { m_data_total=0; }
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);
   //--- sorting method
   void              Sort(const int mode=0);

protected:
   virtual void      QuickSort(int beg,int end,const int mode=0) { m_sort_mode=-1; }
   //--- templates for methods of searching for minimum and maximum
   template<typename T>
   int               Minimum(const T &data[],const int start,const int count) const;
   template<typename T>
   int               Maximum(const T &data[],const int start,const int count) const;
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CArray::CArray(void) : m_step_resize(16),
                       m_data_total(0),
                       m_data_max(0),
                       m_sort_mode(-1)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CArray::~CArray(void)
  {
  }
//+------------------------------------------------------------------+
//| Method Set for variable m_step_resize                            |
//+------------------------------------------------------------------+
bool CArray::Step(const int step)
  {
//--- check
   if(step>0)
     {
      m_step_resize=step;
      return(true);
     }
//--- failure
   return(false);
  }
//+------------------------------------------------------------------+
//| Sorting an array in ascending order                              |
//+------------------------------------------------------------------+
void CArray::Sort(const int mode)
  {
//--- check
   if(IsSorted(mode))
      return;
   m_sort_mode=mode;
   if(m_data_total<=1)
      return;
//--- sort
   QuickSort(0,m_data_total-1,mode);
  }
//+------------------------------------------------------------------+
//| Writing header of array to file                                  |
//+------------------------------------------------------------------+
bool CArray::Save(const int file_handle)
  {
//--- check handle
   if(file_handle!=INVALID_HANDLE)
     {
      //--- write start marker - 0xFFFFFFFFFFFFFFFF
      if(FileWriteLong(file_handle,-1)==sizeof(long))
        {
         //--- write array type
         if(FileWriteInteger(file_handle,Type(),INT_VALUE)==INT_VALUE)
            return(true);
        }
     }
//--- failure
   return(false);
  }
//+------------------------------------------------------------------+
//| Reading header of array from file                                |
//+------------------------------------------------------------------+
bool CArray::Load(const int file_handle)
  {
//--- check handle
   if(file_handle!=INVALID_HANDLE)
     {
      //--- read and check start marker - 0xFFFFFFFFFFFFFFFF
      if(FileReadLong(file_handle)==-1)
        {
         //--- read and check array type
         if(FileReadInteger(file_handle,INT_VALUE)==Type())
            return(true);
        }
     }
//--- failure
   return(false);
  }
//+------------------------------------------------------------------+
//| Find minimum of array                                            |
//+------------------------------------------------------------------+
template<typename T>
int CArray::Minimum(const T &data[],const int start,const int count) const
  {
   int real_count;
//--- check for empty array
   if(m_data_total<1)
     {
      SetUserError(ERR_USER_ARRAY_IS_EMPTY);
      return(-1);
     }
   //--- check for start is out of range
   if(start<0 || start>=m_data_total)
     {
      SetUserError(ERR_USER_ITEM_NOT_FOUND);
      return(-1);
     }
//--- compute count of elements
   real_count=(count==WHOLE_ARRAY || start+count>m_data_total) ? m_data_total-start : count;
#ifdef __MQL5__
   return(ArrayMinimum(data,start,real_count));
#else
   return(ArrayMinimum(data,real_count,start));
#endif
  }
//+------------------------------------------------------------------+
//| Find maximum of array                                            |
//+------------------------------------------------------------------+
template<typename T>
int CArray::Maximum(const T &data[],const int start,const int count) const
  {
   int real_count;
//--- check for empty array
   if(m_data_total<1)
     {
      SetUserError(ERR_USER_ARRAY_IS_EMPTY);
      return(-1);
     }
   //--- check for start is out of range
   if(start<0 || start>=m_data_total)
     {
      SetUserError(ERR_USER_ITEM_NOT_FOUND);
      return(-1);
     }
//--- compute count of elements
   real_count=(count==WHOLE_ARRAY || start+count>m_data_total) ? m_data_total-start : count;
#ifdef __MQL5__
   return(ArrayMaximum(data,start,real_count));
#else
   return(ArrayMaximum(data,real_count,start));
#endif
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Class CArrayObj.                                                 |
//| Puprose: Class of dynamic array of pointers to instances         |
//|          of the CObject class and its derivatives.               |
//|          Derives from class CArray.                              |
//+------------------------------------------------------------------+
class CArrayObj : public CArray
  {
protected:
   CObject          *m_data[];           // data array
   bool              m_free_mode;        // flag of necessity of "physical" deletion of object

public:
                     CArrayObj(void);
                    ~CArrayObj(void);
   //--- methods of access to protected data
   bool              FreeMode(void) const { return(m_free_mode); }
   void              FreeMode(const bool mode) { m_free_mode=mode; }
   //--- method of identifying the object
   virtual int       Type(void) const { return(0x7778); }
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);
   //--- method of creating an element of array
   virtual bool      CreateElement(const int index) { return(false); }
   //--- methods of managing dynamic memory
   bool              Reserve(const int size);
   bool              Resize(const int size);
   bool              Shutdown(void);
   //--- methods of filling the array
   bool              Add(CObject *element);
   bool              AddArray(const CArrayObj *src);
   bool              Insert(CObject *element,const int pos);
   bool              InsertArray(const CArrayObj *src,const int pos);
   bool              AssignArray(const CArrayObj *src);
   //--- method of access to thre array
   CObject          *At(const int index) const;
   //--- methods of changing
   bool              Update(const int index,CObject *element);
   bool              Shift(const int index,const int shift);
   //--- methods of deleting
   CObject          *Detach(const int index);
   bool              Delete(const int index);
   bool              DeleteRange(int from,int to);
   void              Clear(void);
   //--- method for comparing arrays
   bool              CompareArray(const CArrayObj *array) const;
   //--- methods for working with the sorted array
   bool              InsertSort(CObject *element);
   int               Search(const CObject *element) const;
   int               SearchGreat(const CObject *element) const;
   int               SearchLess(const CObject *element) const;
   int               SearchGreatOrEqual(const CObject *element) const;
   int               SearchLessOrEqual(const CObject *element) const;
   int               SearchFirst(const CObject *element) const;
   int               SearchLast(const CObject *element) const;

protected:
   void              QuickSort(int beg,int end,const int mode);
   int               QuickSearch(const CObject *element) const;
   int               MemMove(const int dest,const int src,int count);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CArrayObj::CArrayObj(void) : m_free_mode(true)
  {
//--- initialize protected data
   m_data_max=ArraySize(m_data);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CArrayObj::~CArrayObj(void)
  {
   if(m_data_max!=0)
      Shutdown();
  }
//+------------------------------------------------------------------+
//| Moving the memory within a single array                          |
//+------------------------------------------------------------------+
int CArrayObj::MemMove(const int dest,const int src,int count)
  {
   int i;
//--- check parameters
   if(dest<0 || src<0 || count<0)
      return(-1);
//--- check count
   if(src+count>m_data_total)
      count=m_data_total-src;
   if(count<0)
      return(-1);
//--- no need to copy
   if(dest==src || count==0)
      return(dest);
//--- check data total
   if(dest+count>m_data_total)
     {
      if(m_data_max<dest+count)
         return(-1);
      m_data_total=dest+count;
     }
//--- copy
   if(dest<src)
     {
      //--- copy from left to right
      for(i=0;i<count;i++)
        {
         //--- "physical" removal of the object (if necessary and possible)
         if(m_free_mode && CheckPointer(m_data[dest+i])==POINTER_DYNAMIC)
            delete m_data[dest+i];
         //---
         m_data[dest+i]=m_data[src+i];
         m_data[src+i]=NULL;
        }
     }
   else
     {
      //--- copy from right to left
      for(i=count-1;i>=0;i--)
        {
         //--- "physical" removal of the object (if necessary and possible)
         if(m_free_mode && CheckPointer(m_data[dest+i])==POINTER_DYNAMIC)
            delete m_data[dest+i];
         //---
         m_data[dest+i]=m_data[src+i];
         m_data[src+i]=NULL;
        }
     }
//--- successful
   return(dest);
  }
//+------------------------------------------------------------------+
//| Request for more memory in an array. Checks if the requested     |
//| number of free elements already exists; allocates additional     |
//| memory with a given step                                         |
//+------------------------------------------------------------------+
bool CArrayObj::Reserve(const int size)
  {
   int new_size;
//--- check
   if(size<=0)
      return(false);
//--- resize array
   if(Available()<size)
     {
      new_size=m_data_max+m_step_resize*(1+(size-Available())/m_step_resize);
      if(new_size<0)
         //--- overflow occurred when calculating new_size
         return(false);
      if((m_data_max=ArrayResize(m_data,new_size))==-1)
         m_data_max=ArraySize(m_data);
      //--- explicitly zeroize all the loose items in the array
      for(int i=m_data_total;i<m_data_max;i++)
         m_data[i]=NULL;
     }
//--- result
   return(Available()>=size);
  }
//+------------------------------------------------------------------+
//| Resizing (with removal of elements on the right)                 |
//+------------------------------------------------------------------+
bool CArrayObj::Resize(const int size)
  {
   int new_size;
//--- check
   if(size<0)
      return(false);
//--- resize array
   new_size=m_step_resize*(1+size/m_step_resize);
   if(m_data_total>size)
     {
      //--- "physical" removal of the object (if necessary and possible)
      if(m_free_mode)
         for(int i=size;i<m_data_total;i++)
            if(CheckPointer(m_data[i])==POINTER_DYNAMIC)
               delete m_data[i];
      m_data_total=size;
     }
   if(m_data_max!=new_size)
     {
      if((m_data_max=ArrayResize(m_data,new_size))==-1)
        {
         m_data_max=ArraySize(m_data);
         return(false);
        }
     }
//--- result
   return(m_data_max==new_size);
  }
//+------------------------------------------------------------------+
//| Complete cleaning of the array with the release of memory        |
//+------------------------------------------------------------------+
bool CArrayObj::Shutdown(void)
  {
//--- check
   if(m_data_max==0)
      return(true);
//--- clean
   Clear();
   if(ArrayResize(m_data,0)==-1)
      return(false);
   m_data_max=0;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Adding an element to the end of the array                        |
//+------------------------------------------------------------------+
bool CArrayObj::Add(CObject *element)
  {
//--- check
   if(!CheckPointer(element))
      return(false);
//--- check/reserve elements of array
   if(!Reserve(1))
      return(false);
//--- add
   m_data[m_data_total++]=element;
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Adding an element to the end of the array from another array     |
//+------------------------------------------------------------------+
bool CArrayObj::AddArray(const CArrayObj *src)
  {
   int num;
//--- check
   if(!CheckPointer(src))
      return(false);
//--- check/reserve elements of array
   num=src.Total();
   if(!Reserve(num))
      return(false);
//--- add
   for(int i=0;i<num;i++)
      m_data[m_data_total++]=src.m_data[i];
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Inserting an element in the specified position                   |
//+------------------------------------------------------------------+
bool CArrayObj::Insert(CObject *element,const int pos)
  {
//--- check
   if(pos<0 || !CheckPointer(element))
      return(false);
//--- check/reserve elements of array
   if(!Reserve(1))
      return(false);
//--- insert
   m_data_total++;
   if(pos<m_data_total-1)
     {
      if(MemMove(pos+1,pos,m_data_total-pos-1)<0)
         return(false);
      m_data[pos]=element;
     }
   else
      m_data[m_data_total-1]=element;
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Inserting elements in the specified position                     |
//+------------------------------------------------------------------+
bool CArrayObj::InsertArray(const CArrayObj *src,const int pos)
  {
   int num;
//--- check
   if(!CheckPointer(src))
      return(false);
//--- check/reserve elements of array
   num=src.Total();
   if(!Reserve(num)) return(false);
//--- insert
   if(MemMove(num+pos,pos,m_data_total-pos)<0)
      return(false);
   for(int i=0;i<num;i++)
      m_data[i+pos]=src.m_data[i];
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Assignment (copying) of another array                            |
//+------------------------------------------------------------------+
bool CArrayObj::AssignArray(const CArrayObj *src)
  {
   int num;
//--- check
   if(!CheckPointer(src))
      return(false);
//--- check/reserve elements of array
   num=src.m_data_total;
   Clear();
   if(m_data_max<num)
     {
      if(!Reserve(num))
         return(false);
     }
   else
      Resize(num);
//--- copy array
   for(int i=0;i<num;i++)
     {
      m_data[i]=src.m_data[i];
      m_data_total++;
     }
   m_sort_mode=src.SortMode();
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Access to data in the specified position                         |
//+------------------------------------------------------------------+
CObject *CArrayObj::At(const int index) const
  {
//--- check
   if(index<0 || index>=m_data_total)
      return(NULL);
//--- result
   return(m_data[index]);
  }
//+------------------------------------------------------------------+
//| Updating element in the specified position                       |
//+------------------------------------------------------------------+
bool CArrayObj::Update(const int index,CObject *element)
  {
//--- check
   if(index<0 || !CheckPointer(element) || index>=m_data_total)
      return(false);
//--- "physical" removal of the object (if necessary and possible)
   if(m_free_mode && CheckPointer(m_data[index])==POINTER_DYNAMIC)
      delete m_data[index];
//--- update
   m_data[index]=element;
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Moving element from the specified position                       |
//| on the specified shift                                           |
//+------------------------------------------------------------------+
bool CArrayObj::Shift(const int index,const int shift)
  {
   CObject *tmp_node;
//--- check
   if(index<0 || index+shift<0 || index+shift>=m_data_total)
      return(false);
   if(shift==0)
      return(true);
//--- move
   tmp_node=m_data[index];
   m_data[index]=NULL;
   if(shift>0)
     {
      if(MemMove(index,index+1,shift)<0)
         return(false);
     }
   else
     {
      if(MemMove(index+shift+1,index+shift,-shift)<0)
         return(false);
     }
   m_data[index+shift]=tmp_node;
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Deleting element from the specified position                     |
//+------------------------------------------------------------------+
bool CArrayObj::Delete(const int index)
  {
//--- check
   if(index>=m_data_total)
      return(false);
//--- delete
   if(index<m_data_total-1)
     {
      if(index>=0 && MemMove(index,index+1,m_data_total-index-1)<0)
         return(false);
     }
   else
   if(m_free_mode && CheckPointer(m_data[index])==POINTER_DYNAMIC)
      delete m_data[index];
   m_data_total--;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Detach element from the specified position                       |
//+------------------------------------------------------------------+
CObject *CArrayObj::Detach(const int index)
  {
   CObject *result;
//--- check
   if(index>=m_data_total)
      return(NULL);
//--- detach
   result=m_data[index];
//--- reset the array element, so as not remove the method MemMove
   m_data[index]=NULL;
   if(index<m_data_total-1 && MemMove(index,index+1,m_data_total-index-1)<0)
      return(NULL);
   m_data_total--;
//--- successful
   return(result);
  }
//+------------------------------------------------------------------+
//| Deleting range of elements                                       |
//+------------------------------------------------------------------+
bool CArrayObj::DeleteRange(int from,int to)
  {
//--- check
   if(from<0 || to<0)
      return(false);
   if(from>to || from>=m_data_total)
      return(false);
//--- delete
   if(to>=m_data_total-1)
      to=m_data_total-1;
   if(MemMove(from,to+1,m_data_total-to-1)<0)
      return(false);
   for(int i=to-from+1;i>0;i--,m_data_total--)
      if(m_free_mode && CheckPointer(m_data[m_data_total-1])==POINTER_DYNAMIC)
         delete m_data[m_data_total-1];
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Clearing of array without the release of memory                  |
//+------------------------------------------------------------------+
void CArrayObj::Clear(void)
  {
//--- "physical" removal of the object (if necessary and possible)
   if(m_free_mode)
     {
      for(int i=0;i<m_data_total;i++)
        {
         if(CheckPointer(m_data[i])==POINTER_DYNAMIC)
            delete m_data[i];
         m_data[i]=NULL;
        }
     }
   m_data_total=0;
  }
//+------------------------------------------------------------------+
//| Equality comparison of two arrays                                |
//+------------------------------------------------------------------+
bool CArrayObj::CompareArray(const CArrayObj *array) const
  {
//--- check
   if(!CheckPointer(array))
      return(false);
//--- compare
   if(m_data_total!=array.m_data_total)
      return(false);
   for(int i=0;i<m_data_total;i++)
      if(m_data[i].Compare(array.m_data[i],0)!=0)
         return(false);
//--- equal
   return(true);
  }
//+------------------------------------------------------------------+
//| Method QuickSort                                                 |
//+------------------------------------------------------------------+
void CArrayObj::QuickSort(int beg,int end,const int mode)
  {
   int      i,j;
   CObject *p_node;
   CObject *t_node;
//--- sort
   i=beg;
   j=end;
   while(i<end)
     {
      //--- ">>1" is quick division by 2
      p_node=m_data[(beg+end)>>1];
      while(i<j)
        {
         while(m_data[i].Compare(p_node,mode)<0)
           {
            //--- control the output of the array bounds
            if(i==m_data_total-1)
               break;
            i++;
           }
         while(m_data[j].Compare(p_node,mode)>0)
           {
            //--- control the output of the array bounds
            if(j==0)
               break;
            j--;
           }
         if(i<=j)
           {
            t_node=m_data[i];
            m_data[i++]=m_data[j];
            m_data[j]=t_node;
            //--- control the output of the array bounds
            if(j==0)
               break;
            j--;
           }
        }
      if(beg<j)
         QuickSort(beg,j,mode);
      beg=i;
      j=end;
     }
  }
//+------------------------------------------------------------------+
//| Inserting element in a sorted array                              |
//+------------------------------------------------------------------+
bool CArrayObj::InsertSort(CObject *element)
  {
   int pos;
//--- check
   if(!CheckPointer(element) || m_sort_mode==-1)
      return(false);
//--- check/reserve elements of array
   if(!Reserve(1))
      return(false);
//--- if the array is empty, add an element
   if(m_data_total==0)
     {
      m_data[m_data_total++]=element;
      return(true);
     }
//--- find position and insert
   int mode=m_sort_mode;
   pos=QuickSearch(element);
   if(m_data[pos].Compare(element,m_sort_mode)>0)
      Insert(element,pos);
   else
      Insert(element,pos+1);
//--- restore the sorting flag after Insert(...)
   m_sort_mode=mode;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Quick search of position of element in a sorted array            |
//+------------------------------------------------------------------+
int CArrayObj::QuickSearch(const CObject *element) const
  {
   int      i,j,m=-1;
   CObject *t_node;
//--- search
   i=0;
   j=m_data_total-1;
   while(j>=i)
     {
      //--- ">>1" is quick division by 2
      m=(j+i)>>1;
      if(m<0 || m==m_data_total-1)
         break;
      t_node=m_data[m];
      if(t_node.Compare(element,m_sort_mode)==0)
         break;
      if(t_node.Compare(element,m_sort_mode)>0)
         j=m-1;
      else
         i=m+1;
     }
//--- position
   return(m);
  }
//+------------------------------------------------------------------+
//| Search of position of element in a sorted array                  |
//+------------------------------------------------------------------+
int CArrayObj::Search(const CObject *element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !CheckPointer(element) || m_sort_mode==-1)
      return(-1);
//--- search
   pos=QuickSearch(element);
   if(m_data[pos].Compare(element,m_sort_mode)==0)
      return(pos);
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Search position of the first element which is greater than       |
//| specified in a sorted array                                      |
//+------------------------------------------------------------------+
int CArrayObj::SearchGreat(const CObject *element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !CheckPointer(element) || m_sort_mode==-1)
      return(-1);
//--- search
   pos=QuickSearch(element);
   while(m_data[pos].Compare(element,m_sort_mode)<=0)
      if(++pos==m_data_total)
         return(-1);
//--- position
   return(pos);
  }
//+------------------------------------------------------------------+
//| Search position of the first element which is less than          |
//| specified in the sorted array                                    |
//+------------------------------------------------------------------+
int CArrayObj::SearchLess(const CObject *element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !CheckPointer(element) || m_sort_mode==-1)
      return(-1);
//--- search
   pos=QuickSearch(element);
   while(m_data[pos].Compare(element,m_sort_mode)>=0)
      if(pos--==0)
         return(-1);
//--- position
   return(pos);
  }
//+------------------------------------------------------------------+
//| Search position of the first element which is greater than or    |
//| equal to the specified in a sorted array                         |
//+------------------------------------------------------------------+
int CArrayObj::SearchGreatOrEqual(const CObject *element) const
  {
//--- check
   if(m_data_total==0 || !CheckPointer(element) || m_sort_mode==-1)
      return(-1);
//--- search
   for(int pos=QuickSearch(element);pos<m_data_total;pos++)
      if(m_data[pos].Compare(element,m_sort_mode)>=0)
         return(pos);
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Search position of the first element which is less than or equal |
//| to the specified in a sorted array                               |
//+------------------------------------------------------------------+
int CArrayObj::SearchLessOrEqual(const CObject *element) const
  {
//--- check
   if(m_data_total==0 || !CheckPointer(element) || m_sort_mode==-1)
      return(-1);
//--- search
   for(int pos=QuickSearch(element);pos>=0;pos--)
      if(m_data[pos].Compare(element,m_sort_mode)<=0)
         return(pos);
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Find position of first appearance of element in a sorted array   |
//+------------------------------------------------------------------+
int CArrayObj::SearchFirst(const CObject *element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !CheckPointer(element) || m_sort_mode==-1)
      return(-1);
//--- search
   pos=QuickSearch(element);
   if(m_data[pos].Compare(element,m_sort_mode)==0)
     {
      while(m_data[pos].Compare(element,m_sort_mode)==0)
         if(pos--==0)
            break;
      return(pos+1);
     }
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Find position of last appearance of element in a sorted array    |
//+------------------------------------------------------------------+
int CArrayObj::SearchLast(const CObject *element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !CheckPointer(element) || m_sort_mode==-1)
      return(-1);
//--- search
   pos=QuickSearch(element);
   if(m_data[pos].Compare(element,m_sort_mode)==0)
     {
      while(m_data[pos].Compare(element,m_sort_mode)==0)
         if(++pos==m_data_total)
            break;
      return(pos-1);
     }
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Writing array to file                                            |
//+------------------------------------------------------------------+
bool CArrayObj::Save(const int file_handle)
  {
   int i=0;
//--- check
   if(!CArray::Save(file_handle))
      return(false);
//--- write array length
   if(FileWriteInteger(file_handle,m_data_total,INT_VALUE)!=INT_VALUE)
      return(false);
//--- write array
   for(i=0;i<m_data_total;i++)
      if(m_data[i].Save(file_handle)!=true)
         break;
//--- result
   return(i==m_data_total);
  }
//+------------------------------------------------------------------+
//| Reading array from file                                          |
//+------------------------------------------------------------------+
bool CArrayObj::Load(const int file_handle)
  {
   int i=0,num;
//--- check
   if(!CArray::Load(file_handle))
      return(false);
//--- read array length
   num=FileReadInteger(file_handle,INT_VALUE);
//--- read array
   Clear();
   if(num!=0)
     {
      if(!Reserve(num))
         return(false);
      for(i=0;i<num;i++)
        {
         //--- create new element
         if(!CreateElement(i))
            break;
         if(m_data[i].Load(file_handle)!=true)
            break;
         m_data_total++;
        }
     }
   m_sort_mode=-1;
//--- result
   return(m_data_total==num);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Class CWndContainer                                              |
//| Usage: base class of the combined control                        |
//+------------------------------------------------------------------+
class CWndContainer : public CWnd
  {
private:
   CArrayObj         m_controls;            // container of the control

public:
                     CWndContainer(void);
                    ~CWndContainer(void);
   //--- release memory
   virtual void      Destroy(const int reason=0);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   virtual bool      OnMouseEvent(const int x,const int y,const int flags);
   //--- access the contents of container
   int               ControlsTotal(void)               const { return(m_controls.Total()); }
   CWnd*             Control(const int ind)            const { return(dynamic_cast<CWnd *>(m_controls.At(ind))); }
   virtual CWnd*     ControlFind(const long id);
   //--- for mouse cursor focus
   virtual bool      MouseFocusKill(const long id=-1);
   //--- fill
   bool              Add(CWnd *control);
   bool              Add(CWnd &control);
   //--- underflowing
   bool              Delete(CWnd *control);
   bool              Delete(CWnd &control);
   bool              DeleteAll(void);
   //--- geometry
   virtual bool      Move(const int x,const int y);
   virtual bool      Move(const CPoint &point);
   virtual bool      Shift(const int dx,const int dy);
   //--- ID
   virtual long      Id(const long id);
   long              Id(void) const { return(CWnd::Id()); }
   //--- state
   virtual bool      Enable(void);
   virtual bool      Disable(void);
   virtual bool      Show(void);
   virtual bool      Hide(void);
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);

protected:
   //--- internal event handlers
   virtual bool      OnResize(void);
   virtual bool      OnActivate(void);
   virtual bool      OnDeactivate(void);
  };
//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
bool CWndContainer::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- if an object is being dragged, pass control to the special drag object
   if(m_drag_object!=NULL && m_drag_object.OnEvent(id,lparam,dparam,sparam))
      return(true);
//--- loop by elements of group
   int total=m_controls.Total();
   for(int i=total-1;i>=0;i--)
     {
      CWnd *control=Control(i);
      //--- check of pointer
      if(control==NULL)
         continue;
      if(control.OnEvent(id,lparam,dparam,sparam))
         return(true);
     }
//--- not handled
   return(false);
  }
//+------------------------------------------------------------------+
//| Common handler of mouse events                                   |
//+------------------------------------------------------------------+
bool CWndContainer::OnMouseEvent(const int x,const int y,const int flags)
  {
   if(!IS_VISIBLE)
      return(false);
//--- if an object is being dragged, pass control to the special drag object
   if(m_drag_object!=NULL && m_drag_object.OnMouseEvent(x,y,flags))
      return(true);
//--- loop by elements of group
   int total=m_controls.Total();
   for(int i=total-1;i>=0;i--)
     {
      CWnd *control=Control(i);
      //--- check of pointer
      if(control==NULL)
         continue;
      if(control.OnMouseEvent(x,y,flags))
         return(true);
     }
//--- call of the method of the parent class
   return(CWnd::OnMouseEvent(x,y,flags));
  }
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CWndContainer::CWndContainer(void)
  {
    RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CWndContainer::~CWndContainer(void)
  {
  }
//+------------------------------------------------------------------+
//| Delete group of controls                                         |
//+------------------------------------------------------------------+
void CWndContainer::Destroy(const int reason)
  {
//--- loop by elements of group
   int total=m_controls.Total();
   for(int i=0;i<total;i++)
     {
      CWnd *control=Control(0);
      //--- check of pointer
      if(control==NULL)
         continue;
      control.Destroy();
      m_controls.Delete(0);
     }
  }
//+------------------------------------------------------------------+
//| Find control by specified ID                                     |
//+------------------------------------------------------------------+
CWnd* CWndContainer::ControlFind(const long id)
  {
   CWnd *result=CWnd::ControlFind(id);
//---
   if(result!=NULL)
      return(result);
//--- loop by elements of group
   int total=m_controls.Total();
   for(int i=0;i<total;i++)
     {
      CWnd *control=Control(i);
      //--- check of pointer
      if(control==NULL)
         continue;
      result=control.ControlFind(id);
      if(result!=NULL)
         break;
     }
//--- return the result
   return(result);
  }
//+------------------------------------------------------------------+
//| Remove the mouse focus from control                              |
//+------------------------------------------------------------------+
bool CWndContainer::MouseFocusKill(const long id=-1)
  {
   if(!IS_ACTIVE)
      return(false);
   Deactivate();
//--- loop by elements of group
   int total=m_controls.Total();
   for(int i=0;i<total;i++)
     {
      CWnd *control=Control(i);
      //--- check of pointer
      if(control==NULL)
         continue;
      control.MouseFocusKill();
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Add control to the group (by pointer)                            |
//+------------------------------------------------------------------+
bool CWndContainer::Add(CWnd *control)
  {
//--- check of pointer
   if(control==NULL)
      return(false);
//--- correct the coordinates of added control
   control.Shift(Left(),Top());
 //--- "projecting" the group flag "visibility" to the added element
   if(IS_VISIBLE && control.IsVisible())
     {
      //--- element will be "visible" only if the group is "visible" and the element is completely "within" this group
      control.Visible(Contains(control));
     }
   else
      control.Hide();
//--- "projecting" the group flag "enabled" to the added element
   if(!IS_ENABLED)
   //   control.Enable(); - we can't enable controls by their containers - controls can be disabled on purpose
   //else
      control.Disable();
//--- adding
   return(m_controls.Add(control));
  }
//+------------------------------------------------------------------+
//| Add control to the group (by reference)                          |
//+------------------------------------------------------------------+
bool CWndContainer::Add(CWnd &control)
  {
//--- add by pointer
   return(Add((CWnd*)GetPointer(control)));
  }
//+------------------------------------------------------------------+
//| Delete control from the group (by pointer)                       |
//+------------------------------------------------------------------+
bool CWndContainer::Delete(CWnd *control)
{
  if(control == NULL) return(false);

  const int total = m_controls.Total();
  for(int i = total - 1; i >= 0; i--)
  {
    CWnd *pointer = Control(i);
    if(pointer == NULL) continue;

    if(pointer == control)
    {
      CWndContainer *container = dynamic_cast<CWndContainer *>(control);
      if(container != NULL) container.DeleteAll();
      return(m_controls.Delete(i));
    }
  }
  return false;
}

bool CWndContainer::DeleteAll(void)
{
  const int total = m_controls.Total();
  for(int i = total - 1; i >= 0; i--)
  {
    CWnd *pointer = Control(i);
    if(pointer == NULL) continue;
    Delete(pointer);
  }
  return true;
}

//+------------------------------------------------------------------+
//| Delete control from the group (by reference)                     |
//+------------------------------------------------------------------+
bool CWndContainer::Delete(CWnd &control)
  {
//--- delete by pointer
   return(Delete((CWnd*)GetPointer(control)));
  }
//+------------------------------------------------------------------+
//| Absolute movement of the controls group                          |
//+------------------------------------------------------------------+
bool CWndContainer::Move(const int x,const int y)
  {
//--- relative movement
   return(Shift(x-Left(),y-Top()));
  }
//+------------------------------------------------------------------+
//| Absolute movement of the controls group                          |
//+------------------------------------------------------------------+
bool CWndContainer::Move(const CPoint &point)
  {
//--- relative movement
   return(Shift(point.x-Left(),point.y-Top()));
  }
//+------------------------------------------------------------------+
//| Relative movement of the controls group                          |
//+------------------------------------------------------------------+
bool CWndContainer::Shift(const int dx,const int dy)
  {
  if(dx == 0 && dy == 0) return true;
  
//--- call of the method of the parent class
   if(!CWnd::Shift(dx,dy)) return(false);
//--- loop by elements of group
   int total=m_controls.Total();
   for(int i=0;i<total;i++)
     {
      CWnd *control=Control(i);
      //--- check of pointer
      if(control==NULL)
         continue;
      //--- move the group item
      control.Shift(dx,dy);
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Set ID of control                                                |
//+------------------------------------------------------------------+
long CWndContainer::Id(const long id)
  {
//--- reserve ID for container
   long id_used=1;
//--- loop by elements of group
   int total=m_controls.Total();
   for(int i=0;i<total;i++)
     {
      CWnd *control=Control(i);
      //--- check of pointer
      if(control==NULL)
         continue;
      id_used+=control.Id(id+id_used);
     }
   m_id=id;
//--- return number of used IDs
   return(id_used);
  }
//+------------------------------------------------------------------+
//| Enables event handling by the group of controls                  |
//+------------------------------------------------------------------+
bool CWndContainer::Enable(void)
  {
//--- loop by elements of group
   int total=m_controls.Total();
   for(int i=0;i<total;i++)
     {
      CWnd *control=Control(i);
      //--- check of pointer
      if(control==NULL)
         continue;
      control.Enable();
     }
//--- call of the method of the parent class
   return(CWnd::Enable());
  }
//+------------------------------------------------------------------+
//| Disables event handling by the group of controls                 |
//+------------------------------------------------------------------+
bool CWndContainer::Disable(void)
  {
//--- loop by elements of group
   int total=m_controls.Total();
   for(int i=0;i<total;i++)
     {
      CWnd *control=Control(i);
      //--- check of pointer
      if(control==NULL)
         continue;
      control.Disable();
     }
//--- call of the method of the parent class
   return(CWnd::Disable());
  }
//+------------------------------------------------------------------+
//| Makes the group of controls visible                              |
//+------------------------------------------------------------------+
bool CWndContainer::Show(void)
  {
//--- loop by elements of group
   int total=m_controls.Total();
   for(int i=0;i<total;i++)
     {
      CWnd *control=Control(i);
      //--- check of pointer
      if(control==NULL)
         continue;
      control.Show();
     }
//--- call of the method of the parent class
   return(CWnd::Show());
  }
//+------------------------------------------------------------------+
//| Makes the group of controls hidden                               |
//+------------------------------------------------------------------+
bool CWndContainer::Hide(void)
  {
//--- loop by elements of group
   int total=m_controls.Total();
   for(int i=0;i<total;i++)
     {
      CWnd *control=Control(i);
      //--- check of pointer
      if(control==NULL)
         continue;
      control.Hide();
     }
//--- call of the method of the parent class
   return(CWnd::Hide());
  }
//+------------------------------------------------------------------+
//| Handler of resizing                                              |
//+------------------------------------------------------------------+
bool CWndContainer::OnResize()
  {
//--- loop by elements of group
   int total=m_controls.Total();
   for(int i=0;i<total;i++)
     {
      CWnd *control=Control(i);
      //--- check of pointer
      if(control==NULL)
         continue;
      if(!control.Align(Rect()))
         return(false);
     }
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of activating the group of controls                      |
//+------------------------------------------------------------------+
bool CWndContainer::OnActivate(void)
  {
   if(IS_ACTIVE)
      return(false);
   Activate();
//--- loop by elements of group
   int total=m_controls.Total();
   for(int i=0;i<total;i++)
     {
      CWnd *control=Control(i);
      //--- check of pointer
      if(control==NULL)
         continue;
      control.Activate();
     }
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of deactivating the group of controls                    |
//+------------------------------------------------------------------+
bool CWndContainer::OnDeactivate(void)
  {
   if(!IS_ACTIVE)
      return(false);
   Deactivate();
//--- loop by elements of group
   int total=m_controls.Total();
   for(int i=0;i<total;i++)
     {
      CWnd *control=Control(i);
      //--- check of pointer
      if(control==NULL)
         continue;
      control.Deactivate();
     }
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Save                                                             |
//+------------------------------------------------------------------+
bool CWndContainer::Save(const int file_handle)
  {
   bool result=true;
//--- loop by elements of group
   int total=m_controls.Total();
   for(int i=0;i<total;i++)
     {
      CWnd *control=Control(i);
      //--- check of pointer
      if(control==NULL)
         continue;
      result&=control.Save(file_handle);
     }
//--- result
   return(result);
  }
//+------------------------------------------------------------------+
//| Load                                                             |
//+------------------------------------------------------------------+
bool CWndContainer::Load(const int file_handle)
  {
   bool result=true;
//--- loop by elements of group
   int total=m_controls.Total();
   for(int i=0;i<total;i++)
     {
      CWnd *control=Control(i);
      //--- check of pointer
      if(control==NULL)
         continue;
      result&=control.Load(file_handle);
     }
//--- result
   return(result);
  }
//+------------------------------------------------------------------+
//#include "WndClient.mqh"
//+------------------------------------------------------------------+
//|                                                    WndClient.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "WndContainer.mqh"
//#include "Panel.mqh"
//+------------------------------------------------------------------+
//|                                                        Panel.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "WndObj.mqh"
//+------------------------------------------------------------------+
//|                                                       WndObj.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "Wnd.mqh"
//+------------------------------------------------------------------+
//| Class CWndObj                                                    |
//| Usage: base class to work with chart objects                     |
//+------------------------------------------------------------------+
class CWndObj : public CWnd
  {
private:
   //--- flags of object
   bool              m_undeletable;         // "object is not deletable" flag
   bool              m_unchangeable;        // "object is not changeable" flag
   bool              m_unmoveable;          // "object is not movable" flag

protected:
   //--- parameters of the chart object
   string            m_text;                // object text
   color             m_color;               // object color
   color             m_color_background;    // object background color
   color             m_color_border;        // object border color
   string            m_font;                // object font
   int               m_font_size;           // object font size
   long              m_zorder;              // Z order

public:
                     CWndObj(void);
                    ~CWndObj(void);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- set up the object
   string            Text(void) const { return(m_text); }
   bool              Text(const string value);
   color             Color(void) const { return(m_color); }
   bool              Color(const color value);
   color             ColorBackground(void) const { return(m_color_background); }
   bool              ColorBackground(const color value);
   color             ColorBorder(void) const { return(m_color_border); }
   bool              ColorBorder(const color value);
   string            Font(void) const { return(m_font); }
   bool              Font(const string value);
   int               FontSize(void) const { return(m_font_size); }
   bool              FontSize(const int value);
   long              ZOrder(void) const { return(m_zorder); }
   bool              ZOrder(const long value);

protected:
   //--- handlers of object events
   virtual bool      OnObjectCreate(void);
   virtual bool      OnObjectChange(void);
   virtual bool      OnObjectDelete(void);
   virtual bool      OnObjectDrag(void);
   //--- handlers of object settings
   virtual bool      OnSetText(void)                  { return(true); }
   virtual bool      OnSetColor(void)                 { return(true); }
   virtual bool      OnSetColorBackground(void)       { return(true); }
   virtual bool      OnSetColorBorder(void)           { return(true); }
   virtual bool      OnSetFont(void)                  { return(true); }
   virtual bool      OnSetFontSize(void)              { return(true); }
   virtual bool      OnSetZOrder(void)                { return(true); }
   //--- internal event handlers
   virtual bool      OnDestroy(void) { return(ObjectDelete(m_chart_id,m_name)); }
   virtual bool      OnChange(void);
  };
//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
bool CWndObj::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   if(m_name==sparam)
     {
      //--- object name and string parameters are equal
      //--- this means that event should be handled
      switch(id)
        {
         case CHARTEVENT_OBJECT_CREATE: return(OnObjectCreate());
         case CHARTEVENT_OBJECT_CHANGE: return(OnObjectChange());
         case CHARTEVENT_OBJECT_DELETE: return(OnObjectDelete());
         case CHARTEVENT_OBJECT_DRAG  : return(OnObjectDrag());
        }
     }
//--- event was not handled
   return(CWnd::OnEvent(id,lparam,dparam,sparam));
  }
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CWndObj::CWndObj(void) : m_color(clrNONE),
                         m_color_background(clrNONE),
                         m_color_border(clrNONE),
                         m_font(CONTROLS_FONT_NAME),
                         m_font_size(CONTROLS_FONT_SIZE),
                         m_zorder(0),
                         m_undeletable(true),
                         m_unchangeable(true),
                         m_unmoveable(true)
  {
  RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CWndObj::~CWndObj(void)
  {
  }
//+------------------------------------------------------------------+
//| Set the "Text" parameter                                         |
//+------------------------------------------------------------------+
bool CWndObj::Text(const string value)
  {
//--- save new value of parameter
   m_text=value;
//--- call virtual event handler
   return(OnSetText());
  }
//+------------------------------------------------------------------+
//| Set the "Color" parameter                                        |
//+------------------------------------------------------------------+
bool CWndObj::Color(const color value)
  {
//--- save new value of parameter
   m_color=value;
//--- call virtual event handler
   return(OnSetColor());
  }
//+------------------------------------------------------------------+
//| Setting the "Background color" parameter                         |
//+------------------------------------------------------------------+
bool CWndObj::ColorBackground(const color value)
  {
//--- save new value of parameter
   m_color_background=value;
//--- call virtual event handler
   return(OnSetColorBackground());
  }
//+------------------------------------------------------------------+
//| Set the "Border color" parameter                                 |
//+------------------------------------------------------------------+
bool CWndObj::ColorBorder(const color value)
  {
//--- save new value of parameter
   m_color_border=value;
//--- call virtual event handler
   return(OnSetColorBorder());
  }
//+------------------------------------------------------------------+
//| Set the "Font" parameter                                         |
//+------------------------------------------------------------------+
bool CWndObj::Font(const string value)
  {
//--- save new value of parameter
   m_font=value;
//--- call virtual event handler
   return(OnSetFont());
  }
//+------------------------------------------------------------------+
//| Set the "Font size" parameter                                    |
//+------------------------------------------------------------------+
bool CWndObj::FontSize(const int value)
  {
//--- save new value of parameter
   m_font_size=value;
//--- call virtual event handler
   return(OnSetFontSize());
  }
//+------------------------------------------------------------------+
//| Set the "Z order" parameter                                      |
//+------------------------------------------------------------------+
bool CWndObj::ZOrder(const long value)
  {
//--- save new value of parameter
   m_zorder=value;
//--- call virtual event handler
   return(OnSetZOrder());
  }
//+------------------------------------------------------------------+
//| Handler of the "Object creation" event                           |
//+------------------------------------------------------------------+
bool CWndObj::OnObjectCreate(void)
  {
//--- event is handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "Object modification" event                       |
//+------------------------------------------------------------------+
bool CWndObj::OnObjectChange(void)
  {
//--- if object is not changeable
   if(m_unchangeable)
     {
      //--- restore position
      if(!OnMove())
         return(false);
      //--- restore size
      if(!OnResize())
         return(false);
      //--- restore settings
      if(!OnChange())
         return(false);
     }
//--- event is handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "Object deletion" event                           |
//+------------------------------------------------------------------+
bool CWndObj::OnObjectDelete(void)
  {
//--- if object is not deletable
   if(m_undeletable)
     {
      //--- restore the object
      if(!OnCreate())
         return(false);
      //--- restore settings
      if(!OnChange())
         return(false);
      //--- restore visibility
      return(IS_VISIBLE ? OnShow():OnHide());
     }
//--- event is handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "Object dragging" event                           |
//+------------------------------------------------------------------+
bool CWndObj::OnObjectDrag(void)
  {
//--- if object is not movable
   if(m_unmoveable)
     {
      //--- restore position
      return(OnMove());
     }
//--- event is handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Set up the object                                                |
//+------------------------------------------------------------------+
bool CWndObj::OnChange(void)
  {
//--- set up the chart object according to previously set parameters
   if(!OnSetText())
      return(false);
   if(!OnSetFont())
      return(false);
   if(!OnSetFontSize())
      return(false);
   if(!OnSetColor())
      return(false);
   if(!OnSetColorBackground())
      return(false);
   if(!OnSetColorBorder())
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//|                                      ChartObjectsTxtControls.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//| All text objects.                                                |
//+------------------------------------------------------------------+
//#include "ChartObject.mqh"
//+------------------------------------------------------------------+
//|                                                  ChartObject.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include <Object.mqh>
//+------------------------------------------------------------------+
//| Class CChartObject.                                              |
//| Pupose: Base class of chart objects.                             |
//|              Derives from class CObject.                         |
//+------------------------------------------------------------------+
class CChartObject : public CObject
  {
protected:
   long              m_chart_id;           // identifier of chart the object belongs to
   int               m_window;             // number of subwindow (0 - main window)
   string            m_name;               // unique name object name
   int               m_num_points;         // number of anchor points of object

public:
                     CChartObject(void);
                    ~CChartObject(void);
   //--- method of identifying the object
   virtual int       Type(void) const { return(0x8888); }
   //--- methods of access to protected data
   long              ChartId(void) const { return(m_chart_id); }
   int               Window(void) const { return(m_window); }
   string            Name(void) const { return(m_name); }
   bool              Name(const string name);
   int               NumPoints(void) const { return(m_num_points); }
   //--- methods of filling the object
   bool              Attach(long chart_id,const string name,const int window,const int points);
   bool              SetPoint(const int point,const datetime time,const double price) const;
   //--- methods of deleting
   bool              Delete(void);
   void              Detach(void);
   //--- methods of access to properties of the object
   datetime          Time(const int point) const;
   bool              Time(const int point,const datetime time) const;
   double            Price(const int point) const;
   bool              Price(const int point,const double price) const;
   color             Color(void) const;
   bool              Color(const color new_color) const;
   ENUM_LINE_STYLE   Style(void) const;
   bool              Style(const ENUM_LINE_STYLE new_style) const;
   int               Width(void) const;
   bool              Width(const int new_width) const;
   bool              Background(void) const;
   bool              Background(const bool new_back) const;
   bool              Fill(void) const;
   bool              Fill(const bool new_fill) const;
   long              Z_Order(void) const;
   bool              Z_Order(const long value) const;
   bool              Selected(void) const;
   bool              Selected(const bool new_sel) const;
   bool              Selectable(void) const;
   bool              Selectable(const bool new_sel) const;
   string            Description(void) const;
   bool              Description(const string new_text) const;
   string            Tooltip(void) const;
   bool              Tooltip(const string new_text) const;
   int               Timeframes(void) const;
   virtual bool      Timeframes(const int timeframes) const;
   datetime          CreateTime(void) const;
   int               LevelsCount(void) const;
   bool              LevelsCount(const int new_count) const;
   //--- methods to access the properties of levels of objects
   color             LevelColor(const int level) const;
   bool              LevelColor(const int level,const color new_color) const;
   ENUM_LINE_STYLE   LevelStyle(const int level) const;
   bool              LevelStyle(const int level,const ENUM_LINE_STYLE new_style) const;
   int               LevelWidth(const int level) const;
   bool              LevelWidth(const int level,const int new_width) const;
   double            LevelValue(const int level) const;
   bool              LevelValue(const int level,const double new_value) const;
   string            LevelDescription(const int level) const;
   bool              LevelDescription(const int level,const string new_text) const;
   //--- access methods to the API functions of MQL5
   long              GetInteger(const ENUM_OBJECT_PROPERTY_INTEGER prop_id,const int modifier=-1) const;
   bool              GetInteger(const ENUM_OBJECT_PROPERTY_INTEGER prop_id,const int modifier,long &value) const;
   bool              SetInteger(const ENUM_OBJECT_PROPERTY_INTEGER prop_id,const int modifier,const long value) const;
   bool              SetInteger(const ENUM_OBJECT_PROPERTY_INTEGER prop_id,const long value) const;
   double            GetDouble(const ENUM_OBJECT_PROPERTY_DOUBLE prop_id,const int modifier=-1) const;
   bool              GetDouble(const ENUM_OBJECT_PROPERTY_DOUBLE prop_id,const int modifier,double &value) const;
   bool              SetDouble(const ENUM_OBJECT_PROPERTY_DOUBLE prop_id,const int modifier,const double value) const;
   bool              SetDouble(const ENUM_OBJECT_PROPERTY_DOUBLE prop_id,const double value) const;
   string            GetString(const ENUM_OBJECT_PROPERTY_STRING prop_id,const int modifier=-1) const;
   bool              GetString(const ENUM_OBJECT_PROPERTY_STRING prop_id,const int modifier,string &value) const;
   bool              SetString(const ENUM_OBJECT_PROPERTY_STRING prop_id,const int modifier,const string value) const;
   bool              SetString(const ENUM_OBJECT_PROPERTY_STRING prop_id,const string value) const;
   //--- methods of moving
   bool              ShiftObject(const datetime d_time,const double d_price) const;
   bool              ShiftPoint(const int point,const datetime d_time,const double d_price) const;
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CChartObject::CChartObject(void)
  {
//--- initialize protected data
   Detach();
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CChartObject::~CChartObject(void)
  {
   if(m_chart_id!=-1)
      ObjectDelete(m_chart_id,m_name);
  }
//+------------------------------------------------------------------+
//| Changing name of the object                                      |
//+------------------------------------------------------------------+
bool CChartObject::Name(const string name)
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- change
   if(ObjectSetString(m_chart_id,m_name,OBJPROP_NAME,name))
     {
      m_name=name;
      return(true);
     }
//--- failure
   return(false);
  };
//+------------------------------------------------------------------+
//| Attach object                                                    |
//+------------------------------------------------------------------+
bool CChartObject::Attach(long chart_id,const string name,const int window,const int points)
  {
//--- check
   if(ObjectFind(chart_id,name)<0)
      return(false);
//--- attach
   if(chart_id==0)
      chart_id=ChartID();
   m_chart_id  =chart_id;
   m_window    =window;
   m_name      =name;
   m_num_points=points;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Setting new coordinates of anchor point of an object             |
//+------------------------------------------------------------------+
bool CChartObject::SetPoint(const int point,const datetime time,const double price) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
   if(point>=m_num_points)
      return(false);
//--- result
   return(ObjectMove(m_chart_id,m_name,point,time,price));
  }
//+------------------------------------------------------------------+
//| Delete an object                                                 |
//+------------------------------------------------------------------+
bool CChartObject::Delete(void)
  {
//--- checki
   if(m_chart_id==-1)
      return(false);
//--- actions
   bool result=ObjectDelete(m_chart_id,m_name);
   Detach();
//--- result
   return(result);
  }
//+------------------------------------------------------------------+
//| Detach object                                                    |
//+------------------------------------------------------------------+
void CChartObject::Detach(void)
  {
   m_chart_id  =-1;
   m_window    =-1;
   m_name      =NULL;
   m_num_points=0;
  }
//+------------------------------------------------------------------+
//| Get the time coordinate of the specified anchor point of object  |
//+------------------------------------------------------------------+
datetime CChartObject::Time(const int point) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
   if(point>=m_num_points)
      return(0);
//--- result
   return((datetime)ObjectGetInteger(m_chart_id,m_name,OBJPROP_TIME,point));
  }
//+------------------------------------------------------------------+
//| Set the time coordinate of the specified anchor point of object  |
//+------------------------------------------------------------------+
bool CChartObject::Time(const int point,const datetime time) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
   if(point>=m_num_points)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_TIME,point,time));
  }
//+------------------------------------------------------------------+
//| Get the price coordinate of the specified anchor point of object.|
//+------------------------------------------------------------------+
double CChartObject::Price(const int point) const
  {
//--- check
   if(m_chart_id==-1)
      return(EMPTY_VALUE);
   if(point>=m_num_points)
      return(EMPTY_VALUE);
//--- result
   return(ObjectGetDouble(m_chart_id,m_name,OBJPROP_PRICE,point));
  }
//+------------------------------------------------------------------+
//| Set the price coordinate of the specified anchor point of object.|
//+------------------------------------------------------------------+
bool CChartObject::Price(const int point,const double price) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
   if(point>=m_num_points)
      return(false);
//--- result
   return(ObjectSetDouble(m_chart_id,m_name,OBJPROP_PRICE,point,price));
  }
//+------------------------------------------------------------------+
//| Get object color                                                 |
//+------------------------------------------------------------------+
color CChartObject::Color(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
//--- result
   return((color)ObjectGetInteger(m_chart_id,m_name,OBJPROP_COLOR));
  }
//+------------------------------------------------------------------+
//| Set object color                                                 |
//+------------------------------------------------------------------+
bool CChartObject::Color(const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_COLOR,new_color));
  }
//+------------------------------------------------------------------+
//| Get style of line of object                                      |
//+------------------------------------------------------------------+
ENUM_LINE_STYLE CChartObject::Style(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(WRONG_VALUE);
//--- result
   return((ENUM_LINE_STYLE)ObjectGetInteger(m_chart_id,m_name,OBJPROP_STYLE));
  }
//+------------------------------------------------------------------+
//| Set style of line of object                                      |
//+------------------------------------------------------------------+
bool CChartObject::Style(const ENUM_LINE_STYLE new_style) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_STYLE,new_style));
  }
//+------------------------------------------------------------------+
//| Get width of line of object                                      |
//+------------------------------------------------------------------+
int CChartObject::Width(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(-1);
//--- result
   return((int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_WIDTH));
  }
//+------------------------------------------------------------------+
//| Set width of line of object                                      |
//+------------------------------------------------------------------+
bool CChartObject::Width(const int new_width) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_WIDTH,new_width));
  }
//+------------------------------------------------------------------+
//| Get the "Draw object as background" flag                         |
//+------------------------------------------------------------------+
bool CChartObject::Background(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ObjectGetInteger(m_chart_id,m_name,OBJPROP_BACK));
  }
//+------------------------------------------------------------------+
//| Set the "Draw object as background" flag                         |
//+------------------------------------------------------------------+
bool CChartObject::Background(const bool new_back) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_BACK,new_back));
  }
//+------------------------------------------------------------------+
//| Get the "Filling" flag                                           |
//+------------------------------------------------------------------+
bool CChartObject::Fill(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ObjectGetInteger(m_chart_id,m_name,OBJPROP_FILL));
  }
//+------------------------------------------------------------------+
//| Set the "Filling" flag                                           |
//+------------------------------------------------------------------+
bool CChartObject::Fill(const bool new_fill) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_FILL,new_fill));
  }
//+------------------------------------------------------------------+
//| Get the "Z-order" property                                       |
//+------------------------------------------------------------------+
long CChartObject::Z_Order(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return(ObjectGetInteger(m_chart_id,m_name,OBJPROP_ZORDER));
  }
//+------------------------------------------------------------------+
//| Set the "Z-order" property                                       |
//+------------------------------------------------------------------+
bool CChartObject::Z_Order(const long value) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_ZORDER,value));
  }
//+------------------------------------------------------------------+
//| Get the "selected" flag                                          |
//+------------------------------------------------------------------+
bool CChartObject::Selected(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ObjectGetInteger(m_chart_id,m_name,OBJPROP_SELECTED));
  }
//+------------------------------------------------------------------+
//| Set the "selected" flag                                          |
//+------------------------------------------------------------------+
bool CChartObject::Selected(const bool new_sel) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_SELECTED,new_sel));
  }
//+------------------------------------------------------------------+
//| Get the "selectable" flag                                        |
//+------------------------------------------------------------------+
bool CChartObject::Selectable(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ObjectGetInteger(m_chart_id,m_name,OBJPROP_SELECTABLE));
  }
//+------------------------------------------------------------------+
//| Set flag the "selectable" flag                                   |
//+------------------------------------------------------------------+
bool CChartObject::Selectable(const bool new_sel) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_SELECTABLE,new_sel));
  }
//+------------------------------------------------------------------+
//| Get comment of object                                            |
//+------------------------------------------------------------------+
string CChartObject::Description(void) const
  {
//--- check
   if(m_chart_id==-1)
      return("");
//--- result
   return(ObjectGetString(m_chart_id,m_name,OBJPROP_TEXT));
  }
//+------------------------------------------------------------------+
//| Set comment of object                                            |
//+------------------------------------------------------------------+
bool CChartObject::Description(const string new_text) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- tune
   if(new_text=="")
      return(ObjectSetString(m_chart_id,m_name,OBJPROP_TEXT," "));
//--- result
   return(ObjectSetString(m_chart_id,m_name,OBJPROP_TEXT,new_text));
  }
//+------------------------------------------------------------------+
//| Get tooltip of object                                            |
//+------------------------------------------------------------------+
string CChartObject::Tooltip(void) const
  {
//--- check
   if(m_chart_id==-1)
      return("");
//--- result
   return(ObjectGetString(m_chart_id,m_name,OBJPROP_TOOLTIP));
  }
//+------------------------------------------------------------------+
//| Set tooltip of object                                            |
//+------------------------------------------------------------------+
bool CChartObject::Tooltip(const string new_text) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- tune
   if(new_text=="")
      return(ObjectSetString(m_chart_id,m_name,OBJPROP_TOOLTIP," "));
//--- result
   return(ObjectSetString(m_chart_id,m_name,OBJPROP_TOOLTIP,new_text));
  }
//+------------------------------------------------------------------+
//| Get the "Timeframes" (visibility) flag                           |
//+------------------------------------------------------------------+
int CChartObject::Timeframes(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_TIMEFRAMES));
  }
//+------------------------------------------------------------------+
//| Set the "Timeframes" (visibility) flag                           |
//+------------------------------------------------------------------+
bool CChartObject::Timeframes(const int timeframes) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_TIMEFRAMES,timeframes));
  }
//+------------------------------------------------------------------+
//| Get time of object creation                                      |
//+------------------------------------------------------------------+
datetime CChartObject::CreateTime(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((datetime)ObjectGetInteger(m_chart_id,m_name,OBJPROP_CREATETIME));
  }
//+------------------------------------------------------------------+
//| Get number of levels of object                                   |
//+------------------------------------------------------------------+
int CChartObject::LevelsCount(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_LEVELS));
  }
//+------------------------------------------------------------------+
//| Set number of levels of object                                   |
//+------------------------------------------------------------------+
bool CChartObject::LevelsCount(const int new_count) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_LEVELS,new_count));
  }
//+------------------------------------------------------------------+
//| Get color of the specified level of object                       |
//+------------------------------------------------------------------+
color CChartObject::LevelColor(const int level) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
   if(level>=LevelsCount())
      return(CLR_NONE);
//--- result
   return((color)ObjectGetInteger(m_chart_id,m_name,OBJPROP_LEVELCOLOR,level));
  }
//+------------------------------------------------------------------+
//| Set color of the specified level of object                       |
//+------------------------------------------------------------------+
bool CChartObject::LevelColor(const int level,const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
   if(level>=LevelsCount())
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_LEVELCOLOR,level,new_color));
  }
//+------------------------------------------------------------------+
//| Get line style of the specified level of object                  |
//+------------------------------------------------------------------+
ENUM_LINE_STYLE CChartObject::LevelStyle(const int level) const
  {
//--- check
   if(m_chart_id==-1)
      return(WRONG_VALUE);
   if(level>=LevelsCount())
      return(WRONG_VALUE);
//--- result
   return((ENUM_LINE_STYLE)ObjectGetInteger(m_chart_id,m_name,OBJPROP_LEVELSTYLE,level));
  }
//+------------------------------------------------------------------+
//| Set line style of the specified level of object                  |
//+------------------------------------------------------------------+
bool CChartObject::LevelStyle(const int level,const ENUM_LINE_STYLE new_style) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
   if(level>=LevelsCount())
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_LEVELSTYLE,level,new_style));
  }
//+------------------------------------------------------------------+
//| Get line width of the specified level of object                  |
//+------------------------------------------------------------------+
int CChartObject::LevelWidth(const int level) const
  {
//--- check
   if(m_chart_id==-1)
      return(-1);
   if(level>=LevelsCount())
      return(-1);
//--- result
   return((int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_LEVELWIDTH,level));
  }
//+------------------------------------------------------------------+
//| Set line width of the specified level of object                  |
//+------------------------------------------------------------------+
bool CChartObject::LevelWidth(const int level,const int new_width) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
   if(level>=LevelsCount())
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_LEVELWIDTH,level,new_width));
  }
//+------------------------------------------------------------------+
//| Get value of the specified level of object                       |
//+------------------------------------------------------------------+
double CChartObject::LevelValue(const int level) const
  {
//--- check
   if(m_chart_id==-1)
      return(EMPTY_VALUE);
   if(level>=LevelsCount())
      return(EMPTY_VALUE);
//--- result
   return(ObjectGetDouble(m_chart_id,m_name,OBJPROP_LEVELVALUE,level));
  }
//+------------------------------------------------------------------+
//| Set value of the specified level of object                       |
//+------------------------------------------------------------------+
bool CChartObject::LevelValue(const int level,const double new_value) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
   if(level>=LevelsCount())
      return(false);
//--- result
   return(ObjectSetDouble(m_chart_id,m_name,OBJPROP_LEVELVALUE,level,new_value));
  }
//+------------------------------------------------------------------+
//| Get comment of of the specified level of object                  |
//+------------------------------------------------------------------+
string CChartObject::LevelDescription(const int level) const
  {
//--- check
   if(m_chart_id==-1)
      return("");
   if(level>=LevelsCount())
      return("");
//--- result
   return(ObjectGetString(m_chart_id,m_name,OBJPROP_LEVELTEXT,level));
  }
//+------------------------------------------------------------------+
//| Set comment to the specified level of object                     |
//+------------------------------------------------------------------+
bool CChartObject::LevelDescription(const int level,const string new_text) const
  {
//--- checking
   if(m_chart_id==-1)
      return(false);
   if(level>=LevelsCount())
      return(false);
//--- result
   return(ObjectSetString(m_chart_id,m_name,OBJPROP_LEVELTEXT,level,new_text));
  }
//+------------------------------------------------------------------+
//| Access function long ObjectGetInteger(...)                       |
//+------------------------------------------------------------------+
long CChartObject::GetInteger(const ENUM_OBJECT_PROPERTY_INTEGER prop_id,const int modifier) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//---
   if(modifier==-1)
      return(ObjectGetInteger(m_chart_id,m_name,prop_id));
//--- result
   return(ObjectGetInteger(m_chart_id,m_name,prop_id,modifier));
  }
//+------------------------------------------------------------------+
//| Access function bool ObjectGetInteger(...)                       |
//+------------------------------------------------------------------+
bool CChartObject::GetInteger(const ENUM_OBJECT_PROPERTY_INTEGER prop_id,const int modifier,long &value) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectGetInteger(m_chart_id,m_name,prop_id,modifier,value));
  }
//+------------------------------------------------------------------+
//| Access function ObjectSetInteger(.,modifier,.)                   |
//+------------------------------------------------------------------+
bool CChartObject::SetInteger(const ENUM_OBJECT_PROPERTY_INTEGER prop_id,const int modifier,const long value) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,prop_id,modifier,value));
  }
//+------------------------------------------------------------------+
//| Access function ObjectSetInteger(...)                            |
//+------------------------------------------------------------------+
bool CChartObject::SetInteger(const ENUM_OBJECT_PROPERTY_INTEGER prop_id,const long value) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,prop_id,value));
  }
//+------------------------------------------------------------------+
//| Access function double ObjectGetDouble(...)                      |
//+------------------------------------------------------------------+
double CChartObject::GetDouble(const ENUM_OBJECT_PROPERTY_DOUBLE prop_id,const int modifier) const
  {
//--- check
   if(m_chart_id==-1)
      return(EMPTY_VALUE);
//---
   if(modifier==-1)
      return(ObjectGetDouble(m_chart_id,m_name,prop_id));
//--- result
   return(ObjectGetDouble(m_chart_id,m_name,prop_id,modifier));
  }
//+------------------------------------------------------------------+
//| Access function bool ObjectGetDouble(...)                        |
//+------------------------------------------------------------------+
bool CChartObject::GetDouble(const ENUM_OBJECT_PROPERTY_DOUBLE prop_id,const int modifier,double &value) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectGetDouble(m_chart_id,m_name,prop_id,modifier,value));
  }
//+------------------------------------------------------------------+
//| Access function ObjectSetDouble(.,modifier,.)                    |
//+------------------------------------------------------------------+
bool CChartObject::SetDouble(const ENUM_OBJECT_PROPERTY_DOUBLE prop_id,const int modifier,const double value) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetDouble(m_chart_id,m_name,prop_id,modifier,value));
  }
//+------------------------------------------------------------------+
//| Access function ObjectSetDouble(...)                             |
//+------------------------------------------------------------------+
bool CChartObject::SetDouble(const ENUM_OBJECT_PROPERTY_DOUBLE prop_id,const double value) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetDouble(m_chart_id,m_name,prop_id,value));
  }
//+------------------------------------------------------------------+
//| Access function string ObjectGetString (...)                     |
//+------------------------------------------------------------------+
string CChartObject::GetString(const ENUM_OBJECT_PROPERTY_STRING prop_id,const int modifier) const
  {
//--- check
   if(m_chart_id==-1)
      return("");
//---
   if(modifier==-1)
      return(ObjectGetString(m_chart_id,m_name,prop_id));
//--- result
   return(ObjectGetString(m_chart_id,m_name,prop_id,modifier));
  }
//+------------------------------------------------------------------+
//| Access function bool ObjectGetString(...)                        |
//+------------------------------------------------------------------+
bool CChartObject::GetString(const ENUM_OBJECT_PROPERTY_STRING prop_id,const int modifier,string &value) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectGetString(m_chart_id,m_name,prop_id,modifier,value));
  }
//+------------------------------------------------------------------+
//| Access function ObjectSetString(.,modifier,.)                    |
//+------------------------------------------------------------------+
bool CChartObject::SetString(const ENUM_OBJECT_PROPERTY_STRING prop_id,const int modifier,const string value) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetString(m_chart_id,m_name,prop_id,modifier,value));
  }
//+------------------------------------------------------------------+
//| Access function ObjectSetString(...)                             |
//+------------------------------------------------------------------+
bool CChartObject::SetString(const ENUM_OBJECT_PROPERTY_STRING prop_id,const string value) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetString(m_chart_id,m_name,prop_id,value));
  }
//+------------------------------------------------------------------+
//| Relative movement of object                                      |
//+------------------------------------------------------------------+
bool CChartObject::ShiftObject(const datetime d_time,const double d_price) const
  {
   bool result=true;
   int  i;
//--- check
   if(m_chart_id==-1)
      return(false);
//--- move
   for(i=0;i<m_num_points;i++)
      result&=ShiftPoint(i,d_time,d_price);
//--- result
   return(result);
  }
//+------------------------------------------------------------------+
//| Relative movement of the specified achor point of object         |
//+------------------------------------------------------------------+
bool CChartObject::ShiftPoint(const int point,const datetime d_time,const double d_price) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
   if(point>=m_num_points)
      return(false);
//--- move
   datetime time=(datetime)ObjectGetInteger(m_chart_id,m_name,OBJPROP_TIME,point);
   double   price=ObjectGetDouble(m_chart_id,m_name,OBJPROP_PRICE,point);
//--- result
   return(ObjectMove(m_chart_id,m_name,point,time+d_time,price+d_price));
  }
//+------------------------------------------------------------------+
//| Writing object parameters to file                                |
//+------------------------------------------------------------------+
bool CChartObject::Save(const int file_handle)
  {
   int    i,len;
   int    levels;
   string str;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- write start marker - 0xFFFFFFFFFFFFFFFF
   if(FileWriteLong(file_handle,-1)!=sizeof(long))
      return(false);
//--- write object type
   if(FileWriteInteger(file_handle,Type(),INT_VALUE)!=INT_VALUE)
      return(false);
//--- write object name
   str=ObjectGetString(m_chart_id,m_name,OBJPROP_NAME);
   len=StringLen(str);
   if(FileWriteInteger(file_handle,len,INT_VALUE)!=INT_VALUE)
      return(false);
   if(len!=0) if(FileWriteString(file_handle,str,len)!=len)
      return(false);
//--- write object color
   if(FileWriteLong(file_handle,ObjectGetInteger(m_chart_id,m_name,OBJPROP_COLOR))!=sizeof(long))
      return(false);
//--- write object line style
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_STYLE))!=sizeof(int))
      return(false);
//--- write object line width
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_WIDTH))!=sizeof(int))
      return(false);
//--- write the property value "Background"
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_BACK),CHAR_VALUE)!=sizeof(char))
      return(false);
//--- write the property value "Selectable"
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_SELECTABLE),CHAR_VALUE)!=sizeof(char))
      return(false);
//--- write the property value "Timeframes"
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_TIMEFRAMES),INT_VALUE)!=sizeof(int))
      return(false);
//--- write comment
   str=ObjectGetString(m_chart_id,m_name,OBJPROP_TEXT);
   len=StringLen(str);
   if(FileWriteInteger(file_handle,len,INT_VALUE)!=INT_VALUE)
      return(false);
   if(len!=0) if(FileWriteString(file_handle,str,len)!=len)
      return(false);
//--- write number of points
   if(FileWriteInteger(file_handle,m_num_points,INT_VALUE)!=INT_VALUE)
      return(false);
//--- write points
   for(i=0;i<m_num_points;i++)
     {
      if(FileWriteLong(file_handle,ObjectGetInteger(m_chart_id,m_name,OBJPROP_TIME,i))!=sizeof(long))
         return(false);
      if(FileWriteDouble(file_handle,ObjectGetDouble(m_chart_id,m_name,OBJPROP_PRICE,i))!=sizeof(double))
         return(false);
     }
//--- write number of levels
   levels=(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_LEVELS);
   if(FileWriteInteger(file_handle,levels,INT_VALUE)!=INT_VALUE)
      return(false);
//--- write levels
   for(i=0;i<levels;i++)
     {
      //--- level color
      if(FileWriteLong(file_handle,ObjectGetInteger(m_chart_id,m_name,OBJPROP_LEVELCOLOR,i))!=sizeof(long))
         return(false);
      //--- level line style
      if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_LEVELSTYLE,i))!=sizeof(int))
         return(false);
      //--- level line width
      if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_LEVELWIDTH,i))!=sizeof(int))
         return(false);
      //--- level value
      if(FileWriteDouble(file_handle,ObjectGetDouble(m_chart_id,m_name,OBJPROP_LEVELVALUE,i))!=sizeof(double))
         return(false);
      //--- level name
      str=ObjectGetString(m_chart_id,m_name,OBJPROP_LEVELTEXT,i);
      len=StringLen(str);
      if(FileWriteInteger(file_handle,len,INT_VALUE)!=INT_VALUE)
         return(false);
      if(len!=0) if(FileWriteString(file_handle,str,len)!=len)
         return(false);
     }
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Reading object parameters from file                              |
//+------------------------------------------------------------------+
bool CChartObject::Load(const int file_handle)
  {
   int    i,len,num;
   string str;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- read and check start marker - 0xFFFFFFFFFFFFFFFF
   if(FileReadLong(file_handle)!=-1)
      return(false);
//--- read and check object type
   if(FileReadInteger(file_handle,INT_VALUE)!=Type())
      return(false);
//--- read object name
   len=FileReadInteger(file_handle,INT_VALUE);
   str=(len!=0) ? FileReadString(file_handle,len) : "";
   if(!ObjectSetString(m_chart_id,m_name,OBJPROP_NAME,str))
      return(false);
//--- read object color
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_COLOR,FileReadLong(file_handle)))
      return(false);
//--- read object line style
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_STYLE,FileReadInteger(file_handle)))
      return(false);
//--- read object line style
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_WIDTH,FileReadInteger(file_handle)))
      return(false);
//--- read the property value "Background"
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_BACK,FileReadInteger(file_handle,CHAR_VALUE)))
      return(false);
//--- read the property value "Selectable"
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_SELECTABLE,FileReadInteger(file_handle,CHAR_VALUE)))
      return(false);
//--- read the property value "Timeframes"
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_TIMEFRAMES,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- read comment
   len=FileReadInteger(file_handle,INT_VALUE);
   str=(len!=0) ? FileReadString(file_handle,len) : "";
   if(!ObjectSetString(m_chart_id,m_name,OBJPROP_TEXT,str))
      return(false);
//--- read number of point
   num=FileReadInteger(file_handle,INT_VALUE);
//--- read points
   if(num!=0)
     {
      for(i=0;i<num;i++)
        {
         if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_TIME,i,FileReadLong(file_handle)))
            return(false);
         if(!ObjectSetDouble(m_chart_id,m_name,OBJPROP_PRICE,i,FileReadDouble(file_handle)))
            return(false);
        }
     }
//--- read number of levels
   num=FileReadInteger(file_handle,INT_VALUE);
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_LEVELS,0,num))
      return(false);
//--- read levels
   if(num!=0)
     {
      for(i=0;i<num;i++)
        {
         //--- level color
         if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_LEVELCOLOR,i,FileReadLong(file_handle)))
            return(false);
         //--- levelline style
         if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_LEVELSTYLE,i,FileReadInteger(file_handle)))
            return(false);
         //--- level line width
         if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_LEVELWIDTH,i,FileReadInteger(file_handle)))
            return(false);
         //--- level value
         if(!ObjectSetDouble(m_chart_id,m_name,OBJPROP_LEVELVALUE,i,FileReadDouble(file_handle)))
            return(false);
         //--- level name
         len=FileReadInteger(file_handle,INT_VALUE);
         str=(len!=0) ? FileReadString(file_handle,len) : "";
         if(!ObjectSetString(m_chart_id,m_name,OBJPROP_LEVELTEXT,i,str))
            return(false);
        }
     }
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Class CChartObjectText.                                          |
//| Purpose: Class of the "Text" object of chart.                    |
//|          Derives from class CChartObject.                        |
//+------------------------------------------------------------------+
class CChartObjectText : public CChartObject
  {
public:
                     CChartObjectText(void);
                    ~CChartObjectText(void);
   //--- method of creating the object
   bool              Create(long chart_id,const string name,const int window,const datetime time,const double price);
   //--- method of identifying the object
   virtual int       Type(void) const override { return(OBJ_TEXT); }
   //--- methods of access to properties of the object
   double            Angle(void) const;
   bool              Angle(const double angle) const;
   string            Font(void) const;
   bool              Font(const string font) const;
   int               FontSize(void) const;
   bool              FontSize(const int size) const;
   ENUM_ANCHOR_POINT Anchor(void) const;
   bool              Anchor(const ENUM_ANCHOR_POINT anchor) const;
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CChartObjectText::CChartObjectText(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CChartObjectText::~CChartObjectText(void)
  {
  }
//+------------------------------------------------------------------+
//| Create object "Text"                                             |
//+------------------------------------------------------------------+
bool CChartObjectText::Create(long chart_id,const string name,const int window,
                              const datetime time,const double price)
  {
   if(!ObjectCreate(chart_id,name,OBJ_TEXT,window,time,price))
      return(false);
   if(!Attach(chart_id,name,window,1))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Get value of the "Angle" property                                |
//+------------------------------------------------------------------+
double CChartObjectText::Angle(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(EMPTY_VALUE);
//--- result
   return(ObjectGetDouble(m_chart_id,m_name,OBJPROP_ANGLE));
  }
//+------------------------------------------------------------------+
//| Set value of the "Angle" property                                |
//+------------------------------------------------------------------+
bool CChartObjectText::Angle(const double angle) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetDouble(m_chart_id,m_name,OBJPROP_ANGLE,angle));
  }
//+------------------------------------------------------------------+
//| Get font name                                                    |
//+------------------------------------------------------------------+
string CChartObjectText::Font(void) const
  {
//--- check
   if(m_chart_id==-1)
      return("");
//--- result
   return(ObjectGetString(m_chart_id,m_name,OBJPROP_FONT));
  }
//+------------------------------------------------------------------+
//| Set font name                                                    |
//+------------------------------------------------------------------+
bool CChartObjectText::Font(const string font) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetString(m_chart_id,m_name,OBJPROP_FONT,font));
  }
//+------------------------------------------------------------------+
//| Get font size                                                    |
//+------------------------------------------------------------------+
int CChartObjectText::FontSize(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_FONTSIZE));
  }
//+------------------------------------------------------------------+
//| Set font size                                                    |
//+------------------------------------------------------------------+
bool CChartObjectText::FontSize(const int size) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_FONTSIZE,size));
  }
//+------------------------------------------------------------------+
//| Get anchor point                                                 |
//+------------------------------------------------------------------+
ENUM_ANCHOR_POINT CChartObjectText::Anchor(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(WRONG_VALUE);
//--- result
   return((ENUM_ANCHOR_POINT)ObjectGetInteger(m_chart_id,m_name,OBJPROP_ANCHOR));
  }
//+------------------------------------------------------------------+
//| Set anchor point                                                 |
//+------------------------------------------------------------------+
bool CChartObjectText::Anchor(const ENUM_ANCHOR_POINT anchor) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_ANCHOR,anchor));
  }
//+------------------------------------------------------------------+
//| Writing parameters of object to file                             |
//+------------------------------------------------------------------+
bool CChartObjectText::Save(const int file_handle)
  {
   int    len;
   string str;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- write
   if(!CChartObject::Save(file_handle))
      return(false);
//--- write value of the "Angle" property
   if(FileWriteDouble(file_handle,ObjectGetDouble(m_chart_id,m_name,OBJPROP_ANGLE))!=sizeof(double))
      return(false);
//--- write value of the "Font Name" property
   str=ObjectGetString(m_chart_id,m_name,OBJPROP_FONT);
   len=StringLen(str);
   if(FileWriteInteger(file_handle,len,INT_VALUE)!=INT_VALUE)
      return(false);
   if(len!=0 && FileWriteString(file_handle,str,len)!=len)
      return(false);
//--- write value of the "Font Size" property
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_FONTSIZE),INT_VALUE)!=sizeof(int))
      return(false);
//--- write value of the "Anchor Point" property
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_ANCHOR),INT_VALUE)!=sizeof(int))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Reading parameters of object from file                           |
//+------------------------------------------------------------------+
bool CChartObjectText::Load(const int file_handle)
  {
   int    len;
   string str;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- read
   if(!CChartObject::Load(file_handle))
      return(false);
//--- reading value of the "Angle" property
   if(!ObjectSetDouble(m_chart_id,m_name,OBJPROP_ANGLE,0,FileReadDouble(file_handle)))
      return(false);
//--- read value of the "Font Name" property
   len=FileReadInteger(file_handle,INT_VALUE);
   str=(len!=0) ? FileReadString(file_handle,len) : "";
   if(!ObjectSetString(m_chart_id,m_name,OBJPROP_FONT,str))
      return(false);
//--- read value of the "Font Size" property
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_FONTSIZE,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- read value of the "Anchor Point" property
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_ANCHOR,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Class CChartObjectLabel.                                         |
//| Purpose: Class of the "Label" object of chart.                   |
//|          Derives from class CChartObjectText.                    |
//+------------------------------------------------------------------+
class CChartObjectLabel : public CChartObjectText
  {
public:
                     CChartObjectLabel(void);
                    ~CChartObjectLabel(void);
   //--- method of creating the object
   bool              Create(long chart_id,const string name,const int window,const int X,const int Y);
   //--- method of identifying the object
   virtual int       Type(void) const { return(OBJ_LABEL); }
   //--- methods of access to properties of the object
   int               X_Distance(void) const;
   bool              X_Distance(const int X) const;
   int               Y_Distance(void) const;
   bool              Y_Distance(const int Y) const;
   int               X_Size(void) const;
   int               Y_Size(void) const;
   
   ENUM_BASE_CORNER  Corner(void) const;
   bool              Corner(const ENUM_BASE_CORNER corner) const;
   //--- change of time/price coordinates is blocked
   datetime          Time(const int point) const { return(CChartObjectText::Time(point)); }
   bool              Time(const int point,const datetime time) const { return(false); }
   double            Price(const int point) const { return(CChartObjectText::Price(point)); }
   bool              Price(const int point,const double price) const { return(false); }
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CChartObjectLabel::CChartObjectLabel(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CChartObjectLabel::~CChartObjectLabel(void)
  {
  }
//+------------------------------------------------------------------+
//| Create object "Label"                                            |
//+------------------------------------------------------------------+
bool CChartObjectLabel::Create(long chart_id,const string name,const int window,const int X,const int Y)
  {
   if(!ObjectCreate(chart_id,name,OBJ_LABEL,window,0,0.0))
      return(false);
   if(!Attach(chart_id,name,window,1))
      return(false);
   if(!Description(name))
      return(false);
   if(!X_Distance(X) || !Y_Distance(Y))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Get the X-distance                                               |
//+------------------------------------------------------------------+
int CChartObjectLabel::X_Distance(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_XDISTANCE));
  }
//+------------------------------------------------------------------+
//| Set the X-distance                                               |
//+------------------------------------------------------------------+
bool CChartObjectLabel::X_Distance(const int X) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_XDISTANCE,X));
  }
//+------------------------------------------------------------------+
//| Get the Y-distance                                               |
//+------------------------------------------------------------------+
int CChartObjectLabel::Y_Distance(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_YDISTANCE));
  }
//+------------------------------------------------------------------+
//| Set the Y-distance                                               |
//+------------------------------------------------------------------+
bool CChartObjectLabel::Y_Distance(const int Y) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_YDISTANCE,Y));
  }
//+------------------------------------------------------------------+
//| Get the X-size                                                   |
//+------------------------------------------------------------------+
int CChartObjectLabel::X_Size(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_XSIZE));
  }
//+------------------------------------------------------------------+
//| Get the Y-size                                                   |
//+------------------------------------------------------------------+
int CChartObjectLabel::Y_Size(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_YSIZE));
  }
//+------------------------------------------------------------------+
//| Get base corner                                                  |
//+------------------------------------------------------------------+
ENUM_BASE_CORNER CChartObjectLabel::Corner(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(WRONG_VALUE);
//--- result
   return((ENUM_BASE_CORNER)ObjectGetInteger(m_chart_id,m_name,OBJPROP_CORNER));
  }
//+------------------------------------------------------------------+
//| Set base corner                                                  |
//+------------------------------------------------------------------+
bool CChartObjectLabel::Corner(const ENUM_BASE_CORNER corner) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_CORNER,corner));
  }
//+------------------------------------------------------------------+
//| Writing parameters of object to file                             |
//+------------------------------------------------------------------+
bool CChartObjectLabel::Save(const int file_handle)
  {
   string str;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- write
   if(!CChartObjectText::Save(file_handle))
      return(false);
//--- write value of the "X-distance" property
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_XDISTANCE),INT_VALUE)!=sizeof(int))
      return(false);
//--- write value of the "Y-distance" property
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_YDISTANCE),INT_VALUE)!=sizeof(int))
      return(false);
//--- write value of the "Corner" property
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_CORNER),INT_VALUE)!=sizeof(int))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Reading parameters of object from file                           |
//+------------------------------------------------------------------+
bool CChartObjectLabel::Load(const int file_handle)
  {
   string str;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- read
   if(!CChartObjectText::Load(file_handle))
      return(false);
//--- reading value of the "X-distance" property
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_XDISTANCE,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- read value of the "Y-distance" property
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_YDISTANCE,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- read value of the "Corner" property
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_CORNER,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Class CChartObjectEdit.                                          |
//| Purpose: Class of the "Edit" object of chart.                    |
//|          Derives from class CChartObjectLabel.                   |
//+------------------------------------------------------------------+
class CChartObjectEdit : public CChartObjectLabel
  {
public:
                     CChartObjectEdit(void);
                    ~CChartObjectEdit(void);
   //--- method of creating the object
   bool              Create(long chart_id,const string name,const int window,const int X,const int Y,const int sizeX,const int sizeY);
   //--- method of identifying the object
   virtual int       Type(void) const override { return(OBJ_EDIT); }
   //--- methods of access to properties of the object
   bool              X_Size(const int X) const;
   int               X_Size(void) const { return(CChartObjectLabel::X_Size()); }
   bool              Y_Size(const int Y) const;
   int               Y_Size(void) const { return(CChartObjectLabel::Y_Size()); }
   color             BackColor(void) const;
   bool              BackColor(const color new_color) const;
   color             BorderColor(void) const;
   bool              BorderColor(const color new_color) const;
   bool              ReadOnly(void) const;
   bool              ReadOnly(const bool flag) const;
   ENUM_ALIGN_MODE   TextAlign(void) const;
   bool              TextAlign(const ENUM_ALIGN_MODE align) const;
   //--- change of angle is blocked
   bool              Angle(const double angle) const { return(false); }
   double            Angle(void) const { return(CChartObjectLabel::Angle()); }
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CChartObjectEdit::CChartObjectEdit(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CChartObjectEdit::~CChartObjectEdit(void)
  {
  }
//+------------------------------------------------------------------+
//| Create object "Edit"                                             |
//+------------------------------------------------------------------+
bool CChartObjectEdit::Create(long chart_id,const string name,const int window,const int X,const int Y,const int sizeX,const int sizeY)
  {
   if(!ObjectCreate(chart_id,name,(ENUM_OBJECT)Type(),window,0,0,0))
      return(false);
   if(!Attach(chart_id,name,window,1))
      return(false);
   if(!X_Distance(X) || !Y_Distance(Y))
      return(false);
   if(!X_Size(sizeX) || !Y_Size(sizeY))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Set X-size                                                       |
//+------------------------------------------------------------------+
bool CChartObjectEdit::X_Size(const int X) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_XSIZE,X));
  }
//+------------------------------------------------------------------+
//| Set Y-size                                                       |
//+------------------------------------------------------------------+
bool CChartObjectEdit::Y_Size(const int Y) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_YSIZE,Y));
  }
//+------------------------------------------------------------------+
//| Get background color                                             |
//+------------------------------------------------------------------+
color CChartObjectEdit::BackColor(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
//--- result
   return((color)ObjectGetInteger(m_chart_id,m_name,OBJPROP_BGCOLOR));
  }
//+------------------------------------------------------------------+
//| Set background color                                             |
//+------------------------------------------------------------------+
bool CChartObjectEdit::BackColor(const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_BGCOLOR,new_color));
  }
//+------------------------------------------------------------------+
//| Get border color                                                 |
//+------------------------------------------------------------------+
color CChartObjectEdit::BorderColor(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
//--- result
   return((color)ObjectGetInteger(m_chart_id,m_name,OBJPROP_BORDER_COLOR));
  }
//+------------------------------------------------------------------+
//| Set border color                                                 |
//+------------------------------------------------------------------+
bool CChartObjectEdit::BorderColor(const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_BORDER_COLOR,new_color));
  }
//+------------------------------------------------------------------+
//| Get the "Read only" property                                     |
//+------------------------------------------------------------------+
bool CChartObjectEdit::ReadOnly(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ObjectGetInteger(m_chart_id,m_name,OBJPROP_READONLY));
  }
//+------------------------------------------------------------------+
//| Set the "Read only" property                                     |
//+------------------------------------------------------------------+
bool CChartObjectEdit::ReadOnly(const bool flag) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_READONLY,flag));
  }
//+------------------------------------------------------------------+
//| Get the "Align" property                                         |
//+------------------------------------------------------------------+
ENUM_ALIGN_MODE CChartObjectEdit::TextAlign(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((ENUM_ALIGN_MODE)ObjectGetInteger(m_chart_id,m_name,OBJPROP_ALIGN));
  }
//+------------------------------------------------------------------+
//| Set the "Align" property                                         |
//+------------------------------------------------------------------+
bool CChartObjectEdit::TextAlign(const ENUM_ALIGN_MODE align) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_ALIGN,align));
  }
//+------------------------------------------------------------------+
//| Writing parameters of object to file                             |
//+------------------------------------------------------------------+
bool CChartObjectEdit::Save(const int file_handle)
  {
   string str;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- write
   if(!CChartObjectLabel::Save(file_handle))
      return(false);
//--- write value of the "X-size" property
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_XSIZE),INT_VALUE)!=sizeof(int))
      return(false);
//--- write value of the "Y-size" property
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_YSIZE),INT_VALUE)!=sizeof(int))
      return(false);
//--- write background color
   if(FileWriteLong(file_handle,ObjectGetInteger(m_chart_id,m_name,OBJPROP_BGCOLOR))!=sizeof(long))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Reading parameters of object from file                           |
//+------------------------------------------------------------------+
bool CChartObjectEdit::Load(const int file_handle)
  {
   string str;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- read
   if(!CChartObjectLabel::Load(file_handle))
      return(false);
//--- read value of the "X-size" property
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_XSIZE,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- read value of the "Y-size" property
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_YSIZE,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- read background color
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_BGCOLOR,FileReadLong(file_handle)))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Class CChartObjectButton.                                        |
//| Purpose: Class of the "Button" object of chart.                  |
//|          Derives from class CChartObjectEdit.                    |
//+------------------------------------------------------------------+
class CChartObjectButton : public CChartObjectEdit
  {
public:
                     CChartObjectButton(void);
                    ~CChartObjectButton(void);
   //--- method of identifying the object
   virtual int       Type(void) const override { return(OBJ_BUTTON); }
   //--- methods of access to properties of the object
   bool              State(void) const;
   bool              State(const bool state) const;
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CChartObjectButton::CChartObjectButton(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CChartObjectButton::~CChartObjectButton(void)
  {
  }
//+------------------------------------------------------------------+
//| Get state                                                        |
//+------------------------------------------------------------------+
bool CChartObjectButton::State(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ObjectGetInteger(m_chart_id,m_name,OBJPROP_STATE));
  }
//+------------------------------------------------------------------+
//| Set state                                                        |
//+------------------------------------------------------------------+
bool CChartObjectButton::State(const bool state) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_STATE,state));
  }
//+------------------------------------------------------------------+
//| Writing parameters of object to file                             |
//+------------------------------------------------------------------+
bool CChartObjectButton::Save(const int file_handle)
  {
   string str;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- write
   if(!CChartObjectEdit::Save(file_handle))
      return(false);
//--- write state
   if(FileWriteLong(file_handle,ObjectGetInteger(m_chart_id,m_name,OBJPROP_STATE))!=sizeof(long))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Reading parameters of object from file                           |
//+------------------------------------------------------------------+
bool CChartObjectButton::Load(const int file_handle)
  {
   string str;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- read
   if(!CChartObjectEdit::Load(file_handle))
      return(false);
//--- read state
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_STATE,FileReadLong(file_handle)))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Class CChartObjectRectLabel.                                     |
//| Purpose: Class of the "Rectangle Label" object of chart.         |
//|          Derives from class CChartObjectLabel.                   |
//+------------------------------------------------------------------+
class CChartObjectRectLabel : public CChartObjectLabel
  {
public:
                     CChartObjectRectLabel(void);
                    ~CChartObjectRectLabel(void);
   //--- method of creating the object
   bool              Create(long chart_id,const string name,const int window,const int X,const int Y,const int sizeX,const int sizeY);
   //--- method of identifying the object
   virtual int       Type(void) const { return(OBJ_RECTANGLE_LABEL); }
   //--- methods of access to properties of the object
   bool              X_Size(const int X) const;
   int               X_Size(void)const { return(CChartObjectLabel::X_Size()); }
   bool              Y_Size(const int Y) const;
   int               Y_Size(void)const { return(CChartObjectLabel::Y_Size()); }
   color             BackColor(void) const;
   bool              BackColor(const color new_color) const;
   ENUM_BORDER_TYPE  BorderType(void) const;
   bool              BorderType(const ENUM_BORDER_TYPE flag) const;
   //--- change of angle is blocked
   bool              Angle(const double angle) const { return(false); }
   double            Angle(void) const { return(CChartObjectLabel::Angle()); }
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CChartObjectRectLabel::CChartObjectRectLabel(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CChartObjectRectLabel::~CChartObjectRectLabel(void)
  {
  }
//+------------------------------------------------------------------+
//| Create object "Ractangle Label"                                  |
//+------------------------------------------------------------------+
bool CChartObjectRectLabel::Create(long chart_id,const string name,const int window,const int X,const int Y,const int sizeX,const int sizeY)
  {
   if(!ObjectCreate(chart_id,name,(ENUM_OBJECT)Type(),window,0,0,0))
      return(false);
   if(!Attach(chart_id,name,window,1))
      return(false);
   if(!X_Distance(X) || !Y_Distance(Y))
      return(false);
   if(!X_Size(sizeX) || !Y_Size(sizeY))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Set X-size                                                       |
//+------------------------------------------------------------------+
bool CChartObjectRectLabel::X_Size(const int X) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_XSIZE,X));
  }
//+------------------------------------------------------------------+
//| Set Y-size                                                       |
//+------------------------------------------------------------------+
bool CChartObjectRectLabel::Y_Size(const int Y) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_YSIZE,Y));
  }
//+------------------------------------------------------------------+
//| Get background color                                             |
//+------------------------------------------------------------------+
color CChartObjectRectLabel::BackColor(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
//--- result
   return((color)ObjectGetInteger(m_chart_id,m_name,OBJPROP_BGCOLOR));
  }
//+------------------------------------------------------------------+
//| Set background color                                             |
//+------------------------------------------------------------------+
bool CChartObjectRectLabel::BackColor(const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_BGCOLOR,new_color));
  }
//+------------------------------------------------------------------+
//| Get the "Border type" property                                   |
//+------------------------------------------------------------------+
ENUM_BORDER_TYPE CChartObjectRectLabel::BorderType(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((ENUM_BORDER_TYPE)ObjectGetInteger(m_chart_id,m_name,OBJPROP_BORDER_TYPE));
  }
//+------------------------------------------------------------------+
//| Set the "Border type" property                                   |
//+------------------------------------------------------------------+
bool CChartObjectRectLabel::BorderType(const ENUM_BORDER_TYPE type) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_BORDER_TYPE,type));
  }
//+------------------------------------------------------------------+
//| Writing parameters of object to file                             |
//+------------------------------------------------------------------+
bool CChartObjectRectLabel::Save(const int file_handle)
  {
   string str;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- write
   if(!CChartObjectLabel::Save(file_handle))
      return(false);
//--- write value of the "X-size" property
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_XSIZE),INT_VALUE)!=sizeof(int))
      return(false);
//--- write value of the "Y-size" property
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_YSIZE),INT_VALUE)!=sizeof(int))
      return(false);
//--- write background color
   if(FileWriteLong(file_handle,ObjectGetInteger(m_chart_id,m_name,OBJPROP_BGCOLOR))!=sizeof(long))
      return(false);
//--- write value of the "Border type" property
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_BORDER_TYPE),INT_VALUE)!=sizeof(int))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Reading parameters of object from file                           |
//+------------------------------------------------------------------+
bool CChartObjectRectLabel::Load(const int file_handle)
  {
   string str;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- read
   if(!CChartObjectLabel::Load(file_handle))
      return(false);
//--- read value of the "X-size" property
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_XSIZE,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- read value of the "Y-size" property
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_YSIZE,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- read background color
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_BGCOLOR,FileReadLong(file_handle)))
      return(false);
//--- read value of the "Border type" property
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_BORDER_TYPE,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Class CPanel                                                     |
//| Usage: control that is displayed by                              |
//|             the CChartObjectRectLabel object                     |
//+------------------------------------------------------------------+
class CPanel : public CWndObj
  {
private:
   CChartObjectRectLabel m_rectangle;       // chart object
   //--- parameters of the chart object
   ENUM_BORDER_TYPE  m_border;              // border type

public:
                     CPanel(void);
                    ~CPanel(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- parameters of the chart object
   ENUM_BORDER_TYPE  BorderType(void)       const { return(m_border);                                  }
   bool              BorderType(const ENUM_BORDER_TYPE type);

protected:
   //--- handlers of object settings
   virtual bool      OnSetText(void)              { return(m_rectangle.Description(m_text));           }
   virtual bool      OnSetColorBackground(void)   { return(m_rectangle.BackColor(m_color_background)); }
   virtual bool      OnSetColorBorder(void)       { return(m_rectangle.Color(m_color_border));         }
   //--- internal event handlers
   virtual bool      OnCreate(void);
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnMove(void);
   virtual bool      OnResize(void);
   virtual bool      OnChange(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CPanel::CPanel(void) : m_border(BORDER_FLAT)
  {
  RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPanel::~CPanel(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CPanel::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndObj::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create the chart object
   if(!m_rectangle.Create(chart,name,subwin,x1,y1,Width(),Height()))
      return(false);
//--- call the settings handler
   return(OnChange());
  }
//+------------------------------------------------------------------+
//| Set border type                                                  |
//+------------------------------------------------------------------+
bool CPanel::BorderType(const ENUM_BORDER_TYPE type)
  {
//--- save new value of parameter
   m_border=type;
//--- set up the chart object
   return(m_rectangle.BorderType(type));
  }
//+------------------------------------------------------------------+
//| Create object on chart                                           |
//+------------------------------------------------------------------+
bool CPanel::OnCreate(void)
  {
//--- create the chart object by previously set parameters
   return(m_rectangle.Create(m_chart_id,m_name,m_subwin,m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Display object on chart                                          |
//+------------------------------------------------------------------+
bool CPanel::OnShow(void)
  {
   return(m_rectangle.Timeframes(OBJ_ALL_PERIODS));
  }
//+------------------------------------------------------------------+
//| Hide object from chart                                           |
//+------------------------------------------------------------------+
bool CPanel::OnHide(void)
  {
   return(m_rectangle.Timeframes(OBJ_NO_PERIODS));
  }
//+------------------------------------------------------------------+
//| Absolute movement of the chart object                            |
//+------------------------------------------------------------------+
bool CPanel::OnMove(void)
  {
//--- position the chart object
   return(m_rectangle.X_Distance(m_rect.left) && m_rectangle.Y_Distance(m_rect.top));
  }
//+------------------------------------------------------------------+
//| Resize the chart object                                          |
//+------------------------------------------------------------------+
bool CPanel::OnResize(void)
  {
//--- resize the chart object
   return(m_rectangle.X_Size(m_rect.Width()) && m_rectangle.Y_Size(m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Set up the chart object                                          |
//+------------------------------------------------------------------+
bool CPanel::OnChange(void)
  {
//--- set up the chart object
   return(CWndObj::OnChange() && m_rectangle.BorderType(m_border));
  }
//+------------------------------------------------------------------+
//#include "Scrolls.mqh"
//+------------------------------------------------------------------+
//|                                                      Scrolls.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "WndContainer.mqh"
//#include "Panel.mqh"
//#include "BmpButton.mqh"
//+------------------------------------------------------------------+
//|                                                    BmpButton.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "WndObj.mqh"
//#include <ChartObjects\ChartObjectsBmpControls.mqh>
//+------------------------------------------------------------------+
//|                                      ChartObjectsBmpControls.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//| All objects with "bmp" pictures.                                 |
//+------------------------------------------------------------------+
//#include "ChartObject.mqh"
//+------------------------------------------------------------------+
//| Class CChartObjectBitmap.                                        |
//| Purpose: Class of the "Bitmap" object of chart.                  |
//|          Derives from class CChartObject.                        |
//+------------------------------------------------------------------+
class CChartObjectBitmap : public CChartObject
  {
public:
                     CChartObjectBitmap(void);
                    ~CChartObjectBitmap(void);
   //--- methods of access to properties of the object
   string            BmpFile(void) const;
   bool              BmpFile(const string name) const;
   int               X_Offset(void) const;
   bool              X_Offset(const int X) const;
   int               Y_Offset(void) const;
   bool              Y_Offset(const int Y) const;
   //--- method of creating the object
   bool              Create(long chart_id,const string name,const int window,const datetime time,const double price);
   //--- method of identifying the object
   virtual int       Type(void) const { return(OBJ_BITMAP); }
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CChartObjectBitmap::CChartObjectBitmap(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CChartObjectBitmap::~CChartObjectBitmap(void)
  {
  }
//+------------------------------------------------------------------+
//| Create object "Bitmapp"                                          |
//+------------------------------------------------------------------+
bool CChartObjectBitmap::Create(long chart_id,const string name,const int window,const datetime time,const double price)
  {
   if(!ObjectCreate(chart_id,name,OBJ_BITMAP,window,time,price))
      return(false);
   if(!Attach(chart_id,name,window,1))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Get name of bmp-file                                             |
//+------------------------------------------------------------------+
string CChartObjectBitmap::BmpFile(void) const
  {
//--- check
   if(m_chart_id==-1)
      return("");
//--- result
   return(ObjectGetString(m_chart_id,m_name,OBJPROP_BMPFILE));
  }
//+------------------------------------------------------------------+
//| Set name of bmp-file                                             |
//+------------------------------------------------------------------+
bool CChartObjectBitmap::BmpFile(const string name) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetString(m_chart_id,m_name,OBJPROP_BMPFILE,name));
  }
//+------------------------------------------------------------------+
//| Get the XOffset property                                         |
//+------------------------------------------------------------------+
int CChartObjectBitmap::X_Offset(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_XOFFSET));
  }
//+------------------------------------------------------------------+
//| Set the XOffset property                                         |
//+------------------------------------------------------------------+
bool CChartObjectBitmap::X_Offset(const int X) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_XOFFSET,X));
  }
//+------------------------------------------------------------------+
//| Get the YOffset property                                         |
//+------------------------------------------------------------------+
int CChartObjectBitmap::Y_Offset(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_YOFFSET));
  }
//+------------------------------------------------------------------+
//| Set the YOffset property                                         |
//+------------------------------------------------------------------+
bool CChartObjectBitmap::Y_Offset(const int Y) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_YOFFSET,Y));
  }
//+------------------------------------------------------------------+
//| Writing parameters of object to file                             |
//+------------------------------------------------------------------+
bool CChartObjectBitmap::Save(const int file_handle)
  {
   int    len;
   string str;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- write
   if(!CChartObject::Save(file_handle))
      return(false);
//--- write value of the "name of bmp-file" property
   str=ObjectGetString(m_chart_id,m_name,OBJPROP_BMPFILE);
   len=StringLen(str);
   if(FileWriteInteger(file_handle,len,INT_VALUE)!=INT_VALUE)
      return(false);
   if(len!=0 && FileWriteString(file_handle,str,len)!=len)
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Reading parameters of object from file                           |
//+------------------------------------------------------------------+
bool CChartObjectBitmap::Load(const int file_handle)
  {
   int    len;
   string str;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- read
   if(!CChartObject::Load(file_handle))
      return(false);
//--- read value of the "name of bmp-file" property
   len=FileReadInteger(file_handle,INT_VALUE);
   str=(len!=0) ? FileReadString(file_handle,len) : "";
   if(!ObjectSetString(m_chart_id,m_name,OBJPROP_BMPFILE,str))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Class CChartObjectBmpLabel.                                      |
//| Purpose: Class of the "Bitmap label" object of chart.            |
//|          Derives from class CChartObject.                        |
//+------------------------------------------------------------------+
class CChartObjectBmpLabel : public CChartObject
  {
public:
                     CChartObjectBmpLabel(void);
                    ~CChartObjectBmpLabel(void);
   //--- methods of access to properties of the object
   int               X_Distance(void) const;
   bool              X_Distance(const int X) const;
   int               Y_Distance(void) const;
   bool              Y_Distance(const int Y) const;
   int               X_Size(void) const;
   int               Y_Size(void) const;
   ENUM_BASE_CORNER  Corner(void) const;
   bool              Corner(const ENUM_BASE_CORNER corner) const;
   string            BmpFileOn(void) const;
   bool              BmpFileOn(const string name) const;
   string            BmpFileOff(void) const;
   bool              BmpFileOff(const string name) const;
   bool              State(void) const;
   bool              State(const bool state) const;
   int               X_Offset(void) const;
   bool              X_Offset(const int X) const;
   int               Y_Offset(void) const;
   bool              Y_Offset(const int Y) const;
   //--- change of time/price coordinates is blocked
   bool              Time(const datetime time) const { return(false); }
   bool              Price(const double price) const { return(false); }
   //--- method of creating the object
   bool              Create(long chart_id,const string name,const int window,const int X,const int Y);
   //--- method of identifying the object
   virtual int       Type(void) const { return(OBJ_BITMAP_LABEL); }
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CChartObjectBmpLabel::CChartObjectBmpLabel(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CChartObjectBmpLabel::~CChartObjectBmpLabel(void)
  {
  }
//+------------------------------------------------------------------+
//| Create object "Bitmap label"                                     |
//+------------------------------------------------------------------+
bool CChartObjectBmpLabel::Create(long chart_id,const string name,const int window,const int X,const int Y)
  {
   if(!ObjectCreate(chart_id,name,OBJ_BITMAP_LABEL,window,0,0.0))
      return(false);
   if(!Attach(chart_id,name,window,1))
      return(false);
   if(!X_Distance(X) || !Y_Distance(Y))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Get the X-distance property                                      |
//+------------------------------------------------------------------+
int CChartObjectBmpLabel::X_Distance(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_XDISTANCE));
  }
//+------------------------------------------------------------------+
//| Set the X-distance property                                      |
//+------------------------------------------------------------------+
bool CChartObjectBmpLabel::X_Distance(const int X) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_XDISTANCE,X));
  }
//+------------------------------------------------------------------+
//| Get the Y-distance property                                      |
//+------------------------------------------------------------------+
int CChartObjectBmpLabel::Y_Distance(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_YDISTANCE));
  }
//+------------------------------------------------------------------+
//| Set the Y-distance property                                      |
//+------------------------------------------------------------------+
bool CChartObjectBmpLabel::Y_Distance(const int Y) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_YDISTANCE,Y));
  }
//+------------------------------------------------------------------+
//| Get the X-size                                                   |
//+------------------------------------------------------------------+
int CChartObjectBmpLabel::X_Size(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_XSIZE));
  }
//+------------------------------------------------------------------+
//| Get the Y-size                                                   |
//+------------------------------------------------------------------+
int CChartObjectBmpLabel::Y_Size(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_YSIZE));
  }
//+------------------------------------------------------------------+
//| Get the Corner property                                          |
//+------------------------------------------------------------------+
ENUM_BASE_CORNER CChartObjectBmpLabel::Corner(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(WRONG_VALUE);
//--- result
   return((ENUM_BASE_CORNER)ObjectGetInteger(m_chart_id,m_name,OBJPROP_CORNER));
  }
//+------------------------------------------------------------------+
//| Set the Corner property                                          |
//+------------------------------------------------------------------+
bool CChartObjectBmpLabel::Corner(const ENUM_BASE_CORNER corner) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_CORNER,corner));
  }
//+------------------------------------------------------------------+
//| Get filename of the "bmp-ON" property                            |
//+------------------------------------------------------------------+
string CChartObjectBmpLabel::BmpFileOn(void) const
  {
//--- check
   if(m_chart_id==-1)
      return("");
//--- result
   return(ObjectGetString(m_chart_id,m_name,OBJPROP_BMPFILE,0));
  }
//+------------------------------------------------------------------+
//| Set filename for the "bmp-ON" property                           |
//+------------------------------------------------------------------+
bool CChartObjectBmpLabel::BmpFileOn(const string name) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetString(m_chart_id,m_name,OBJPROP_BMPFILE,0,name));
  }
//+------------------------------------------------------------------+
//| Get filename of the "bmp-OFF" property                           |
//+------------------------------------------------------------------+
string CChartObjectBmpLabel::BmpFileOff(void) const
  {
//--- check
   if(m_chart_id==-1)
      return("");
//--- result
   return(ObjectGetString(m_chart_id,m_name,OBJPROP_BMPFILE,1));
  }
//+------------------------------------------------------------------+
//| Set filename for the "bmp-OFF" property                          |
//+------------------------------------------------------------------+
bool CChartObjectBmpLabel::BmpFileOff(const string name) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetString(m_chart_id,m_name,OBJPROP_BMPFILE,1,name));
  }
//+------------------------------------------------------------------+
//| Get the State property                                           |
//+------------------------------------------------------------------+
bool CChartObjectBmpLabel::State(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ObjectGetInteger(m_chart_id,m_name,OBJPROP_STATE));
  }
//+------------------------------------------------------------------+
//| Set the State property                                           |
//+------------------------------------------------------------------+
bool CChartObjectBmpLabel::State(const bool state) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_STATE,state));
  }
//+------------------------------------------------------------------+
//| Get the XOffset property                                         |
//+------------------------------------------------------------------+
int CChartObjectBmpLabel::X_Offset(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_XOFFSET));
  }
//+------------------------------------------------------------------+
//| Set the XOffset property                                         |
//+------------------------------------------------------------------+
bool CChartObjectBmpLabel::X_Offset(const int X) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_XOFFSET,X));
  }
//+------------------------------------------------------------------+
//| Get the YOffset property                                         |
//+------------------------------------------------------------------+
int CChartObjectBmpLabel::Y_Offset(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_YOFFSET));
  }
//+------------------------------------------------------------------+
//| Set the YOffset property                                         |
//+------------------------------------------------------------------+
bool CChartObjectBmpLabel::Y_Offset(const int Y) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ObjectSetInteger(m_chart_id,m_name,OBJPROP_YOFFSET,Y));
  }
//+------------------------------------------------------------------+
//| Writing parameters of object to file                             |
//+------------------------------------------------------------------+
bool CChartObjectBmpLabel::Save(const int file_handle)
  {
   int    len;
   string str;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- write
   if(!CChartObject::Save(file_handle))
      return(false);
//--- write value of the "X-distance" property 
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_XDISTANCE),INT_VALUE)!=sizeof(int))
      return(false);
//--- write value of the "Y-distance" property
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_YDISTANCE),INT_VALUE)!=sizeof(int))
      return(false);
//--- write value of the "Corner" property 
   if(FileWriteInteger(file_handle,(int)ObjectGetInteger(m_chart_id,m_name,OBJPROP_CORNER),INT_VALUE)!=sizeof(int))
      return(false);
//--- write value of the "filename bmp-ON" property 
   str=ObjectGetString(m_chart_id,m_name,OBJPROP_BMPFILE,0);
   len=StringLen(str);
   if(FileWriteInteger(file_handle,len,INT_VALUE)!=INT_VALUE)
      return(false);
   if(len!=0 && FileWriteString(file_handle,str,len)!=len)
      return(false);
//--- write value of the "filename bmp-OFF" property
   str=ObjectGetString(m_chart_id,m_name,OBJPROP_BMPFILE,1);
   len=StringLen(str);
   if(FileWriteInteger(file_handle,len,INT_VALUE)!=INT_VALUE)
      return(false);
   if(len!=0 && FileWriteString(file_handle,str,len)!=len)
      return(false);
//--- write state
   if(FileWriteLong(file_handle,ObjectGetInteger(m_chart_id,m_name,OBJPROP_STATE))!=sizeof(long))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Reading object parameters from file                              |
//+------------------------------------------------------------------+
bool CChartObjectBmpLabel::Load(const int file_handle)
  {
   int    len;
   string str;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- read
   if(!CChartObject::Load(file_handle))
      return(false);
//--- read value of the "X-distance" property
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_XDISTANCE,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- read value of the "Y-distance" property
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_YDISTANCE,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- read value of "Corner" property
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_CORNER,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- read value of the "filename bmp-ON" property
   len=FileReadInteger(file_handle,INT_VALUE);
   str=(len!=0) ? FileReadString(file_handle,len) : "";
   if(!ObjectSetString(m_chart_id,m_name,OBJPROP_BMPFILE,0,str))
      return(false);
//--- read value of the "filename bmp-OFF" property
   len=FileReadInteger(file_handle,INT_VALUE);
   str=(len!=0) ? FileReadString(file_handle,len) : "";
   if(!ObjectSetString(m_chart_id,m_name,OBJPROP_BMPFILE,1,str))
      return(false);
//--- read state
   if(!ObjectSetInteger(m_chart_id,m_name,OBJPROP_STATE,FileReadLong(file_handle)))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Class CBmpButton                                                 |
//| Usage: control that is displayed by                              |
//|             the CChartObjectBmpLabel object                      |
//+------------------------------------------------------------------+
class CBmpButton : public CWndObj
  {
private:
   CChartObjectBmpLabel m_button;           // chart object
   //--- parameters of the chart object
   int               m_border;              // border width
   string            m_bmp_off_name;        // name of BMP file for the "OFF" state (default state)
   string            m_bmp_on_name;         // name of BMP file for the "ON" state
   string            m_bmp_passive_name;
   string            m_bmp_active_name;

public:
                     CBmpButton(void);
                    ~CBmpButton(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- parameters of the chart object
   int               Border(void)          const { return(m_border);                   }
   bool              Border(const int value);
   bool              BmpNames(const string off="",const string on="");
   string            BmpOffName(void)      const { return(m_bmp_off_name);             }
   bool              BmpOffName(const string name);
   string            BmpOnName(void)       const { return(m_bmp_on_name);              }
   bool              BmpOnName(const string name);
   string            BmpPassiveName(void)  const { return(m_bmp_passive_name);         }
   bool              BmpPassiveName(const string name);
   string            BmpActiveName(void)   const { return(m_bmp_active_name);          }
   bool              BmpActiveName(const string name);
   //--- state
   bool              Pressed(void)         const { return(m_button.State());           }
   bool              Pressed(const bool pressed) { return(m_button.State(pressed));    }
   //--- properties
   bool              Locking(void)         const { return(IS_CAN_LOCK);                }
   void              Locking(const bool locking);

protected:
   //--- handlers of object settings
   virtual bool      OnSetZOrder(void)           { return(m_button.Z_Order(m_zorder)); }
   //--- internal event handlers
   virtual bool      OnCreate(void);
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnMove(void);
   virtual bool      OnChange(void);
   //--- ????? ???????????
   virtual bool      OnActivate(void);
   virtual bool      OnDeactivate(void);
   virtual bool      OnMouseDown(void);
   virtual bool      OnMouseUp(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CBmpButton::CBmpButton(void) : m_border(0),
                               m_bmp_off_name(NULL),
                               m_bmp_on_name(NULL),
                               m_bmp_passive_name(NULL),
                               m_bmp_active_name(NULL)
  {
  RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CBmpButton::~CBmpButton(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CBmpButton::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndObj::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create the chart object
   if(!m_button.Create(chart,name,subwin,x1,y1))
      return(false);
//--- call the settings handler
   return(OnChange());
  }
//+------------------------------------------------------------------+
//| Set border width                                                 |
//+------------------------------------------------------------------+
bool CBmpButton::Border(const int value)
  {
//--- save new value of parameter
   m_border=value;
//--- set up the chart object
   return(m_button.Width(value));
  }
//+------------------------------------------------------------------+
//| Set two images at once                                           |
//+------------------------------------------------------------------+
bool CBmpButton::BmpNames(const string off,const string on)
  {
//--- save new values of parameters
   m_bmp_off_name=off;
   m_bmp_on_name =on;
//--- set up the chart object
   if(!m_button.BmpFileOff(off))
      return(false);
   if(!m_button.BmpFileOn(on))
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Set image for the "OFF" state                                    |
//+------------------------------------------------------------------+
bool CBmpButton::BmpOffName(const string name)
  {
//--- save new value of parameter
   m_bmp_off_name=name;
//--- set up the chart object
   if(!m_button.BmpFileOff(name))
      return(false);
//--- set size by image dimensions
   Width(m_button.X_Size());
   Height(m_button.Y_Size());
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Set image for the "ON" state                                     |
//+------------------------------------------------------------------+
bool CBmpButton::BmpOnName(const string name)
  {
//--- save new value of parameter
   m_bmp_on_name=name;
//--- set up the chart object
   if(!m_button.BmpFileOn(name))
      return(false);
//--- set size by image dimensions
   Width(m_button.X_Size());
   Height(m_button.Y_Size());
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Set image for the "OFF" state (passive)                          |
//+------------------------------------------------------------------+
bool CBmpButton::BmpPassiveName(const string name)
  {
//--- save new value of parameter
   m_bmp_passive_name=name;
//--- set up the chart object
   if(!IS_ACTIVE)
      return(BmpOffName(name));
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Set image for the "OFF" state (active)                           |
//+------------------------------------------------------------------+
bool CBmpButton::BmpActiveName(const string name)
  {
//--- save new value of parameter
   m_bmp_active_name=name;
//--- set up the chart object
   if(IS_ACTIVE)
      return(BmpOffName(name));
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Locking flag                                                     |
//+------------------------------------------------------------------+
void CBmpButton::Locking(const bool flag)
  {
   if(flag)
      PropFlagsSet(WND_PROP_FLAG_CAN_LOCK);
   else
      PropFlagsReset(WND_PROP_FLAG_CAN_LOCK);
  }
//+------------------------------------------------------------------+
//| Create object on chart                                           |
//+------------------------------------------------------------------+
bool CBmpButton::OnCreate(void)
  {
//--- create the chart object by previously set parameters
   return(m_button.Create(m_chart_id,m_name,m_subwin,m_rect.left,m_rect.top));
  }
//+------------------------------------------------------------------+
//| Display object on chart                                          |
//+------------------------------------------------------------------+
bool CBmpButton::OnShow(void)
  {
   return(m_button.Timeframes(OBJ_ALL_PERIODS));
  }
//+------------------------------------------------------------------+
//| Hide object from chart                                           |
//+------------------------------------------------------------------+
bool CBmpButton::OnHide(void)
  {
   return(m_button.Timeframes(OBJ_NO_PERIODS));
  }
//+------------------------------------------------------------------+
//| Absolute movement of the chart object                            |
//+------------------------------------------------------------------+
bool CBmpButton::OnMove(void)
  {
//--- position the chart object
   return(m_button.X_Distance(m_rect.left) && m_button.Y_Distance(m_rect.top));
  }
//+------------------------------------------------------------------+
//| Set up the chart object                                          |
//+------------------------------------------------------------------+
bool CBmpButton::OnChange(void)
  {
//--- set up the chart object
   return(m_button.Width(m_border) && m_button.BmpFileOff(m_bmp_off_name) && m_button.BmpFileOn(m_bmp_on_name));
  }
//+------------------------------------------------------------------+
//| Handler of activating the group of controls                      |
//+------------------------------------------------------------------+
bool CBmpButton::OnActivate(void)
  {
   if(m_bmp_active_name!=NULL)
      BmpOffName(m_bmp_active_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of deactivating the group of controls                    |
//+------------------------------------------------------------------+
bool CBmpButton::OnDeactivate(void)
  {
   if(m_bmp_passive_name!=NULL)
      BmpOffName(m_bmp_passive_name);
   if(!IS_CAN_LOCK)
      Pressed(false);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of click on the left mouse button                        |
//+------------------------------------------------------------------+
bool CBmpButton::OnMouseDown(void)
  {
   if(!IS_CAN_LOCK)
      Pressed(!Pressed());
//--- call of the method of the parent class
   return(CWnd::OnMouseDown());
  }
//+------------------------------------------------------------------+
//| Handler of click on the left mouse button                        |
//+------------------------------------------------------------------+
bool CBmpButton::OnMouseUp(void)
  {
//--- depress the button if it is not fixed
   if(m_button.State() && !IS_CAN_LOCK)
      m_button.State(false);
//--- call of the method of the parent class
   return(CWnd::OnMouseUp());
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Resources                                                        |
//+------------------------------------------------------------------+
#resource "\\Include\\Controls\\res\\Up.bmp"
#resource "\\Include\\Controls\\res\\ThumbVert.bmp"
#resource "\\Include\\Controls\\res\\Down.bmp"
#resource "\\Include\\Controls\\res\\Left.bmp"
#resource "\\Include\\Controls\\res\\ThumbHor.bmp"
#resource "\\Include\\Controls\\res\\Right.bmp"
//+------------------------------------------------------------------+
//| Class CScroll                                                    |
//| Usage: base class for scrollbars                                 |
//+------------------------------------------------------------------+
class CScroll : public CWndContainer
  {
protected:
   //--- dependent controls
   CPanel            m_back;                // the "scrollbar background" object
   CBmpButton        m_inc;                 // the "increment button" object ("down" for vertical scrollbar, "right" for horizontal scrollbar)
   CBmpButton        m_dec;                 // the "decrement button" object ("up" for vertical scrollbar, "left" for horizontal scrollbar)
   CBmpButton        m_thumb;               // the "scroll box" object
   //--- set up
   int               m_min_pos;             // minimum value
   int               m_max_pos;             // maximum value
   //--- state
   int               m_curr_pos;            // current value

public:
                     CScroll(void);
                    ~CScroll(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- set up
   int               MinPos(void)           const { return(m_min_pos);  }
   void              MinPos(const int value);
   int               MaxPos(void)           const { return(m_max_pos);  }
   void              MaxPos(const int value);
   //--- state
   int               CurrPos(void)          const { return(m_curr_pos); }
   bool              CurrPos(int value);

protected:
   //--- create dependent controls
   virtual bool      CreateBack(void);
   virtual bool      CreateInc(void)              { return(true);       }
   virtual bool      CreateDec(void)              { return(true);       }
   virtual bool      CreateThumb(void)            { return(true);       }
   //--- handlers of the dependent controls events
   virtual bool      OnClickInc(void);
   virtual bool      OnClickDec(void);
   //--- internal event handlers
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnChangePos(void)             { return(true);       }
   //--- handlers of dragging
   virtual bool      OnThumbDragStart(void)        { return(true);       }
   virtual bool      OnThumbDragProcess(void)      { return(true);       }
   virtual bool      OnThumbDragEnd(void)          { return(true);       }
   //--- calculate position by coordinate
   virtual int       CalcPos(const int coord)      { return(0);          }
  };
//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CScroll)
   ON_EVENT(ON_CLICK,m_inc,OnClickInc)
   ON_EVENT(ON_CLICK,m_dec,OnClickDec)
   ON_EVENT(ON_DRAG_START,m_thumb,OnThumbDragStart)
   ON_EVENT_PTR(ON_DRAG_PROCESS,m_drag_object,OnThumbDragProcess)
   ON_EVENT_PTR(ON_DRAG_END,m_drag_object,OnThumbDragEnd)
EVENT_MAP_END(CWndContainer)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CScroll::CScroll(void) : m_curr_pos(0),
                         m_min_pos(0),
                         m_max_pos(0)
  {
  RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CScroll::~CScroll(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CScroll::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndContainer::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create dependent controls
   if(!CreateBack())
      return(false);
   if(!CreateInc())
      return(false);
   if(!CreateDec())
      return(false);
   if(!CreateThumb())
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create scrollbar background                                      |
//+------------------------------------------------------------------+
bool CScroll::CreateBack(void)
  {
//--- create
   if(!m_back.Create(m_chart_id,m_name+"Back",m_subwin,0,0,Width(),Height()))
      return(false);
   if(!m_back.ColorBackground(CONTROLS_SCROLL_COLOR_BG))
      return(false);
   if(!m_back.ColorBorder(CONTROLS_SCROLL_COLOR_BORDER))
      return(false);
   if(!Add(m_back))
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Set current value                                                |
//+------------------------------------------------------------------+
bool CScroll::CurrPos(int value)
  {
//--- check value
   if(value<m_min_pos)
      value=m_min_pos;
   if(value>m_max_pos)
      value=m_max_pos;
//--- if value was changed
   if(m_curr_pos!=value)
     {
      m_curr_pos=value;
      //--- call virtual handler
      return(OnChangePos());
     }
//--- value has not been changed
   return(false);
  }
//+------------------------------------------------------------------+
//| Set minimum value                                                |
//+------------------------------------------------------------------+
void CScroll::MinPos(const int value)
  {
//--- if value was changed
   if(m_min_pos!=value)
     {
      m_min_pos=value;
      //--- adjust the scroll box position
      CurrPos(m_curr_pos);
     }
  }
//+------------------------------------------------------------------+
//| Set maximum value                                                |
//+------------------------------------------------------------------+
void CScroll::MaxPos(const int value)
  {
//--- if value was changed
   if(m_max_pos!=value)
     {
      m_max_pos=value;
      //--- adjust the scroll box position
      CurrPos(m_curr_pos);
     }
  }
//+------------------------------------------------------------------+
//| Handler of the "Show scrollbar" event                            |
//+------------------------------------------------------------------+
bool CScroll::OnShow(void)
  {
   if(m_id==CONTROLS_INVALID_ID)
      return(true);
//--- send notification
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_SHOW,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "Hide scrollbar" event                            |
//+------------------------------------------------------------------+
bool CScroll::OnHide(void)
  {
   if(m_id==CONTROLS_INVALID_ID)
      return(true);
//--- send notification
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_HIDE,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of click on the "increment" button                       |
//+------------------------------------------------------------------+
bool CScroll::OnClickInc(void)
  {
//--- try to increment current value
   if(!CurrPos(m_curr_pos+1))
      return(true);
//--- if value was changed, send notification
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_SCROLL_INC,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of click on the "decrement" button                       |
//+------------------------------------------------------------------+
bool CScroll::OnClickDec(void)
  {
//--- try to decrement current value
   if(!CurrPos(m_curr_pos-1))
      return(true);
//--- if value was changed, send notification
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_SCROLL_DEC,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Class CScrollV                                                   |
//| Usage: class of vertical scrollbar                               |
//+------------------------------------------------------------------+
class CScrollV : public CScroll
  {
public:
                     CScrollV(void);
                    ~CScrollV(void);

protected:
   //--- create dependent controls
   virtual bool      CreateInc(void);
   virtual bool      CreateDec(void);
   virtual bool      CreateThumb(void);
   //--- internal event handlers
   virtual bool      OnResize(void);
   virtual bool      OnChangePos(void);
   //--- handlers of dragging
   virtual bool      OnThumbDragStart(void);
   virtual bool      OnThumbDragProcess(void);
   virtual bool      OnThumbDragEnd(void);
   //--- calculate position by coordinate
   virtual int       CalcPos(const int coord);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CScrollV::CScrollV(void)
  {
  RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CScrollV::~CScrollV(void)
  {
  }
//+------------------------------------------------------------------+
//| Create the "Increment" button                                    |
//+------------------------------------------------------------------+
bool CScrollV::CreateInc(void)
  {
//--- calculate coordinates
   int x1=CONTROLS_BORDER_WIDTH;
   int y1=Height()-CONTROLS_SCROLL_SIZE+CONTROLS_BORDER_WIDTH;
   int x2=x1+CONTROLS_BUTTON_SIZE;
   int y2=y1+CONTROLS_BUTTON_SIZE;
//--- create
   if(!m_inc.Create(m_chart_id,m_name+"Inc",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_inc.BmpNames("::Include\\Controls\\res\\Down.bmp"))
      return(false);
   if(!Add(m_inc))
      return(false);
//--- property
   m_inc.PropFlags(WND_PROP_FLAG_CLICKS_BY_PRESS);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create the "Decrement" button                                    |
//+------------------------------------------------------------------+
bool CScrollV::CreateDec(void)
  {
//--- calculate coordinates
   int x1=CONTROLS_BORDER_WIDTH;
   int y1=CONTROLS_BORDER_WIDTH;
   int x2=x1+CONTROLS_BUTTON_SIZE;
   int y2=y1+CONTROLS_BUTTON_SIZE;
//--- create
   if(!m_dec.Create(m_chart_id,m_name+"Dec",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_dec.BmpNames("::Include\\Controls\\res\\Up.bmp"))
      return(false);
   if(!Add(m_dec))
      return(false);
//--- property
   m_dec.PropFlags(WND_PROP_FLAG_CLICKS_BY_PRESS);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create scroll box                                                |
//+------------------------------------------------------------------+
bool CScrollV::CreateThumb(void)
  {
//--- calculate coordinates
   int x1=CONTROLS_BORDER_WIDTH;
   int y1=CONTROLS_SCROLL_SIZE-CONTROLS_BORDER_WIDTH;
   int x2=x1+CONTROLS_BUTTON_SIZE;
   int y2=y1+CONTROLS_SCROLL_THUMB_SIZE;
//--- create
   if(!m_thumb.Create(m_chart_id,m_name+"Thumb",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_thumb.BmpNames("::Include\\Controls\\res\\ThumbVert.bmp"))
      return(false);
   if(!Add(m_thumb))
      return(false);
   m_thumb.PropFlags(WND_PROP_FLAG_CAN_DRAG);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of changing current state                                |
//+------------------------------------------------------------------+
bool CScrollV::OnChangePos(void)
  {
//--- check if scrolling is possible
   if(m_max_pos-m_min_pos<=0)
      return(Visible(false));
   else
      if(!Visible(true))
         return(false);
//--- calculate new coordinated of the scrollbar
   int steps    =m_max_pos-m_min_pos;           // number of steps to change position
   int min_coord=m_dec.Bottom();                // minimum possible coordinate (corresponds to the m_min_pos value)
   int max_coord=m_inc.Top()-m_thumb.Height();  // maximum possible coordinate (corresponds to the m_max_pos value)
   int new_coord=min_coord+(max_coord-min_coord)*m_curr_pos/steps;  // new coordinate
//--- adjust the scroll box position
   return(m_thumb.Move(m_thumb.Left(),new_coord));
  }
//+------------------------------------------------------------------+
//| Handler of resizing                                              |
//+------------------------------------------------------------------+
bool CScrollV::OnResize(void)
  {
//--- can not change the lateral size
   if(Width()!=CONTROLS_SCROLL_SIZE)
      m_rect.Width(CONTROLS_SCROLL_SIZE);
//--- resize the scrollbar background
   if(!m_back.Size(Size()))
      return(false);
//--- move the "Increment" button
   if(!m_inc.Move(m_inc.Left(),Bottom()-CONTROLS_SCROLL_SIZE))
      return(false);
//--- adjust the scroll box position
   return(OnChangePos());
  }
//+------------------------------------------------------------------+
//| Start dragging the "slider"                                      |
//+------------------------------------------------------------------+
bool CScrollV::OnThumbDragStart(void)
  {
   if(m_drag_object==NULL)
     {
      m_drag_object=new CDragWnd;
      if(m_drag_object==NULL)
         return(false);
     }
//--- calculate coordinates
   int x1=m_thumb.Left()-CONTROLS_DRAG_SPACING;
   int y1=m_thumb.Top()-CONTROLS_DRAG_SPACING;
   int x2=m_thumb.Right()+CONTROLS_DRAG_SPACING;
   int y2=m_thumb.Bottom()+CONTROLS_DRAG_SPACING;
//--- create
   m_drag_object.Create(m_chart_id,"",m_subwin,x1,y1,x2,y2);
   m_drag_object.PropFlags(WND_PROP_FLAG_CAN_DRAG);
//--- limits
   m_drag_object.Limits(x1,m_dec.Bottom()-CONTROLS_DRAG_SPACING,x2,m_inc.Top()+CONTROLS_DRAG_SPACING);
//--- set mouse params
   m_drag_object.MouseX(m_thumb.MouseX());
   m_drag_object.MouseY(m_thumb.MouseY());
   m_drag_object.MouseFlags(m_thumb.MouseFlags());
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Continue dragging the "slider"                                   |
//+------------------------------------------------------------------+
bool CScrollV::OnThumbDragProcess(void)
  {
//--- checking
   if(m_drag_object==NULL)
      return(false);
//--- calculate coordinates
   int x=m_drag_object.Left()+CONTROLS_DRAG_SPACING;
   int y=m_drag_object.Top()+CONTROLS_DRAG_SPACING;
//--- calculate new position
   int new_pos=CalcPos(y);
   if(new_pos!=m_curr_pos)
     {
      ushort event_id=(m_curr_pos<new_pos) ? ON_SCROLL_INC : ON_SCROLL_DEC;
      m_curr_pos=new_pos;
      EventChartCustom(CONTROLS_SELF_MESSAGE,event_id,m_id,0.0,m_name);
     }
//--- move thumb
   m_thumb.Move(x,y);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| End dragging the "slider"                                        |
//+------------------------------------------------------------------+
bool CScrollV::OnThumbDragEnd(void)
  {
   if(m_drag_object!=NULL)
     {
      m_thumb.MouseFlags(m_drag_object.MouseFlags());
      delete m_drag_object;
      m_drag_object=NULL;
     }
//--- succeed
   return(m_thumb.Pressed(false));
  }
//+------------------------------------------------------------------+
//| Calculate position by coordinate                                 |
//+------------------------------------------------------------------+
int CScrollV::CalcPos(const int coord)
  {
//--- calculate new position of the scrollbar
   int steps    =m_max_pos-m_min_pos;           // number of steps to change position
   int min_coord=m_dec.Bottom();                // minimum possible coordinate (corresponds to the m_min_pos value)
   int max_coord=m_inc.Top()-m_thumb.Height();  // maximum possible coordinate (corresponds to the m_max_pos value)
//--- checkeng
   if(max_coord==min_coord)
      return(0);
   if(coord<min_coord || coord>max_coord)
      return(m_curr_pos);
//---
   int new_pos=(int)MathRound((((double)(coord-min_coord))/(max_coord-min_coord))*steps);  // new position
//---
   return(new_pos);
  }
//+------------------------------------------------------------------+
//| Class CScrollH                                                   |
//| Usage: class of horizontal scrollbar                             |
//+------------------------------------------------------------------+
class CScrollH : public CScroll
  {
public:
                     CScrollH(void);
                    ~CScrollH(void);

protected:
   //--- create dependent controls
   virtual bool      CreateInc(void);
   virtual bool      CreateDec(void);
   virtual bool      CreateThumb(void);
   //--- internal event handlers
   virtual bool      OnResize(void);
   virtual bool      OnChangePos(void);
   //--- handlers of dragging
   virtual bool      OnThumbDragStart(void);
   virtual bool      OnThumbDragProcess(void);
   virtual bool      OnThumbDragEnd(void);
   //--- calculate position by coordinate
   virtual int       CalcPos(const int coord);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CScrollH::CScrollH(void)
  {
  RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CScrollH::~CScrollH(void)
  {
  }
//+------------------------------------------------------------------+
//| Create the "Increment" button                                    |
//+------------------------------------------------------------------+
bool CScrollH::CreateInc(void)
  {
//--- calculate coordinates
   int x1=Width()-CONTROLS_SCROLL_SIZE+CONTROLS_BORDER_WIDTH;
   int y1=CONTROLS_BORDER_WIDTH;
   int x2=x1+CONTROLS_BUTTON_SIZE;
   int y2=y1+CONTROLS_BUTTON_SIZE;
//--- create
   if(!m_inc.Create(m_chart_id,m_name+"Inc",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_inc.BmpNames("::Include\\Controls\\res\\Right.bmp"))
      return(false);
   if(!Add(m_inc))
      return(false);
//--- property
   m_inc.PropFlags(WND_PROP_FLAG_CLICKS_BY_PRESS);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create the "Decrement" button                                    |
//+------------------------------------------------------------------+
bool CScrollH::CreateDec(void)
  {
//--- calculate coordinates
   int x1=CONTROLS_BORDER_WIDTH;
   int y1=CONTROLS_BORDER_WIDTH;
   int x2=x1+CONTROLS_BUTTON_SIZE;
   int y2=y1+CONTROLS_BUTTON_SIZE;
//--- create
   if(!m_dec.Create(m_chart_id,m_name+"Dec",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_dec.BmpNames("::Include\\Controls\\res\\Left.bmp"))
      return(false);
   if(!Add(m_dec))
      return(false);
//--- property
   m_dec.PropFlags(WND_PROP_FLAG_CLICKS_BY_PRESS);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create scroll box                                                |
//+------------------------------------------------------------------+
bool CScrollH::CreateThumb(void)
  {
//--- calculate coordinates
   int x1=CONTROLS_SCROLL_SIZE-CONTROLS_BORDER_WIDTH;
   int y1=CONTROLS_BORDER_WIDTH;
   int x2=x1+CONTROLS_SCROLL_THUMB_SIZE;
   int y2=y1+CONTROLS_BUTTON_SIZE;
//--- create
   if(!m_thumb.Create(m_chart_id,m_name+"Thumb",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_thumb.BmpNames("::Include\\Controls\\res\\ThumbHor.bmp"))
      return(false);
   if(!Add(m_thumb))
      return(false);
   m_thumb.PropFlags(WND_PROP_FLAG_CAN_DRAG);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of changing current state                                |
//+------------------------------------------------------------------+
bool CScrollH::OnChangePos(void)
  {
//--- check if scrolling is possible
   if(m_max_pos-m_min_pos<=0)
      return(Visible(false));
   else
      if(!Visible(true))
         return(false);
//--- calculate new coordinated of the scrollbar
   int steps=m_max_pos-m_min_pos;            // number of steps to change position
   int min_coord=m_dec.Right();                   // minimum possible coordinate (corresponds to the m_min_pos value)
   int max_coord=m_inc.Left()-m_thumb.Width();  // maximum possible coordinate (corresponds to the m_max_pos value)
   int new_coord=min_coord+(max_coord-min_coord)*m_curr_pos/steps;  // new coordinate
//--- adjust the scroll box position
   return(m_thumb.Move(new_coord,m_thumb.Top()));
  }
//+------------------------------------------------------------------+
//| Handler of resizing                                              |
//+------------------------------------------------------------------+
bool CScrollH::OnResize(void)
  {
//--- can not change the lateral size
   if(Height()!=CONTROLS_SCROLL_SIZE)
      m_rect.Height(CONTROLS_SCROLL_SIZE);
//--- resize the scrollbar background
   if(!m_back.Size(Size()))
      return(false);
//--- move the "Increment" button
   if(!m_inc.Move(Right()-CONTROLS_SCROLL_SIZE,m_inc.Top()))
      return(false);
//--- adjust the scroll box position
   return(OnChangePos());
  }
//+------------------------------------------------------------------+
//| Start dragging the "slider"                                      |
//+------------------------------------------------------------------+
bool CScrollH::OnThumbDragStart(void)
  {
   if(m_drag_object==NULL)
     {
      m_drag_object=new CDragWnd;
      if(m_drag_object==NULL)
         return(false);
     }
//--- calculate coordinates
   int x1=m_thumb.Left()-CONTROLS_DRAG_SPACING;
   int y1=m_thumb.Top()-CONTROLS_DRAG_SPACING;
   int x2=m_thumb.Right()+CONTROLS_DRAG_SPACING;
   int y2=m_thumb.Bottom()+CONTROLS_DRAG_SPACING;
//--- create
   m_drag_object.Create(m_chart_id,"",m_subwin,x1,y1,x2,y2);
   m_drag_object.PropFlags(WND_PROP_FLAG_CAN_DRAG);
//--- limits
   m_drag_object.Limits(m_dec.Right()-CONTROLS_DRAG_SPACING,y1,m_inc.Left()+CONTROLS_DRAG_SPACING,y2);
//--- set mouse params
   m_drag_object.MouseX(m_thumb.MouseX());
   m_drag_object.MouseY(m_thumb.MouseY());
   m_drag_object.MouseFlags(m_thumb.MouseFlags());
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Continue dragging the "slider"                                   |
//+------------------------------------------------------------------+
bool CScrollH::OnThumbDragProcess(void)
  {
//--- checking
   if(m_drag_object==NULL)
      return(false);
//--- calculate coordinates
   int x=m_drag_object.Left()+CONTROLS_DRAG_SPACING;
   int y=m_drag_object.Top()+CONTROLS_DRAG_SPACING;
//--- calculate new position
   int new_pos=CalcPos(x);
   if(new_pos!=m_curr_pos)
     {
      ushort event_id=(m_curr_pos<new_pos)?ON_SCROLL_INC:ON_SCROLL_DEC;
      m_curr_pos=new_pos;
      EventChartCustom(CONTROLS_SELF_MESSAGE,event_id,m_id,0.0,m_name);
     }
//--- move thumb
   m_thumb.Move(x,y);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| End dragging the "slider"                                        |
//+------------------------------------------------------------------+
bool CScrollH::OnThumbDragEnd(void)
  {
   if(m_drag_object!=NULL)
     {
      m_thumb.MouseFlags(m_drag_object.MouseFlags());
      delete m_drag_object;
      m_drag_object=NULL;
     }
//--- succeed
   return(m_thumb.Pressed(false));
  }
//+------------------------------------------------------------------+
//| Calculate position by coordinate                                 |
//+------------------------------------------------------------------+
int CScrollH::CalcPos(const int coord)
  {
//--- calculate new position of the scrollbar
   int steps    =m_max_pos-m_min_pos;           // number of steps to change position
   int min_coord=m_dec.Right();                 // minimum possible coordinate (corresponds to the m_min_pos value)
   int max_coord=m_inc.Left()-m_thumb.Width();  // maximum possible coordinate (corresponds to the m_max_pos value)
//--- checkeng
   if(max_coord==min_coord)
      return(0);
   if(coord<min_coord || coord>max_coord)
      return(m_curr_pos);
//---
   int new_pos=(int)MathRound((((double)(coord-min_coord))/(max_coord-min_coord))*steps);  // new position
//---
   return(new_pos);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Class CWndClient                                                 |
//| Usage: base class to create areas with                           |
//|             the scrollbars                                       |
//+------------------------------------------------------------------+
class CWndClient : public CWndContainer
  {
protected:
   //--- flags
   bool              m_v_scrolled;          // "vertical scrolling is possible" flag
   bool              m_h_scrolled;          // "horizontal scrolling is possible" flag
   //--- dependent controls
   CPanel            m_background;          // the "scrollbar background" object
   CScrollV          m_scroll_v;            // the vertical scrollbar object
   CScrollH          m_scroll_h;            // the horizontal scrollbar object

public:
                     CWndClient(void);
                    ~CWndClient(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- parameters
   virtual bool      ColorBackground(const color value)          { return(m_background.ColorBackground(value)); }
   virtual bool      ColorBorder(const color value)              { return(m_background.ColorBorder(value));     }
   virtual color     ColorBackground(void)                       { return(m_background.ColorBackground());      }
   virtual color     ColorBorder(void)                           { return(m_background.ColorBorder());          }
   virtual bool      BorderType(const ENUM_BORDER_TYPE flag)     { return(m_background.BorderType(flag));       }
   //--- settings
   virtual bool      VScrolled(void) { return(m_v_scrolled); }
   virtual bool      VScrolled(const bool flag);
   virtual bool      HScrolled(void) { return(m_h_scrolled); }
   virtual bool      HScrolled(const bool flag);
   //--- ID
   virtual long      Id(const long id);
   virtual long      Id(void) const { return(CWnd::Id()); }
   //--- state
   virtual bool      Show(void);

protected:
   //--- create dependent controls
   virtual bool      CreateBack(void);
   virtual bool      CreateScrollV(void);
   virtual bool      CreateScrollH(void);
   //--- internal event handlers
   virtual bool      OnResize(void);
   //--- handlers of the dependent controls events
   virtual bool      OnVScrollShow(void)                         { return(true); }
   virtual bool      OnVScrollHide(void)                         { return(true); }
   virtual bool      OnHScrollShow(void)                         { return(true); }
   virtual bool      OnHScrollHide(void)                         { return(true); }
   virtual bool      OnScrollLineDown(void)                      { return(true); }
   virtual bool      OnScrollLineUp(void)                        { return(true); }
   virtual bool      OnScrollLineLeft(void)                      { return(true); }
   virtual bool      OnScrollLineRight(void)                     { return(true); }
   virtual bool      OnClickBackground(void)
   {
     EventChartCustom(CONTROLS_SELF_MESSAGE, ON_CLICK, m_id, 0.0, m_name);
     return true;
   }
   //--- resize
   virtual bool      Rebound(const CRect &rect);
  };
//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CWndClient)
   ON_NAMED_EVENT(ON_SHOW,m_scroll_v,OnVScrollShow)
   ON_NAMED_EVENT(ON_HIDE,m_scroll_v,OnVScrollHide)
   ON_EVENT(ON_SCROLL_DEC,m_scroll_v,OnScrollLineUp)
   ON_EVENT(ON_SCROLL_INC,m_scroll_v,OnScrollLineDown)
   ON_NAMED_EVENT(ON_SHOW,m_scroll_h,OnHScrollShow)
   ON_NAMED_EVENT(ON_HIDE,m_scroll_h,OnHScrollHide)
   ON_EVENT(ON_SCROLL_DEC,m_scroll_h,OnScrollLineLeft)
   ON_EVENT(ON_SCROLL_INC,m_scroll_h,OnScrollLineRight)
   ON_EVENT(ON_CLICK, m_background, OnClickBackground)
EVENT_MAP_END(CWndContainer)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CWndClient::CWndClient(void) : m_v_scrolled(false),
                               m_h_scrolled(false)
  {
    RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CWndClient::~CWndClient(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CWndClient::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call of the method of the parent class
   if(!CWndContainer::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create dependent controls
   if(!CreateBack())
      return(false);
   if(m_v_scrolled && !CreateScrollV())
      return(false);
   if(m_h_scrolled && !CreateScrollH())
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create scrollbar background                                      |
//+------------------------------------------------------------------+
bool CWndClient::CreateBack(void)
  {
//--- create
   if(!m_background.Create(m_chart_id,m_name+"Back",m_subwin,0,0,Width(),Height()))
      return(false);
   if(!m_background.ColorBorder(CONTROLS_CLIENT_COLOR_BORDER))
      return(false);
   if(!m_background.ColorBackground(CONTROLS_CLIENT_COLOR_BG))
      return(false);
   if(!Add(m_background))
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create vertical scrollbar                                        |
//+------------------------------------------------------------------+
bool CWndClient::CreateScrollV(void)
  {
//--- calculate coordinates
   int x1=Width()-CONTROLS_SCROLL_SIZE-CONTROLS_BORDER_WIDTH;
   int y1=CONTROLS_BORDER_WIDTH;
   int x2=Width()-CONTROLS_BORDER_WIDTH;
   int y2=Height()-CONTROLS_BORDER_WIDTH;
   if(m_h_scrolled) y2-=CONTROLS_SCROLL_SIZE;
//--- create
   if(!m_scroll_v.Create(m_chart_id,m_name+"VScroll",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!Add(m_scroll_v))
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create horizontal scrollbar                                      |
//+------------------------------------------------------------------+
bool CWndClient::CreateScrollH(void)
  {
//--- calculate coordinates
   int x1=CONTROLS_BORDER_WIDTH;
   int y1=Height()-CONTROLS_SCROLL_SIZE-CONTROLS_BORDER_WIDTH;
   int x2=Width()-CONTROLS_BORDER_WIDTH;
   int y2=Height()-CONTROLS_BORDER_WIDTH;
   if(m_v_scrolled) x2-=CONTROLS_SCROLL_SIZE;
//--- create
   if(!m_scroll_h.Create(m_chart_id,m_name+"HScroll",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!Add(m_scroll_h))
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Set up vertical scrollbar                                        |
//+------------------------------------------------------------------+
bool CWndClient::VScrolled(const bool flag)
  {
   if(m_v_scrolled==flag)
      return(true);
//--- there are changes
   int d_size=0;
   if(flag)
     {
      //--- create vertical scrollbar
      if(!CreateScrollV())
         return(false);
      //--- need to shorten horizontal scrollbar (if there is one)
      d_size=-CONTROLS_SCROLL_SIZE;
     }
   else
     {
      //--- delete vertical scrollbar
      m_scroll_v.Destroy();
      if(!Delete(m_scroll_v))
         return(false);
      //--- need to lengthen horizontal scrollbar (if there is one)
      d_size=CONTROLS_SCROLL_SIZE;
     }
   m_v_scrolled=flag;
//--- change width of horizontal scrollbar (if there is one)
   if(m_h_scrolled)
     {
      if(!m_scroll_h.Width(m_scroll_h.Width()+d_size))
         return(false);
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Set up horizontal scrollbar                                      |
//+------------------------------------------------------------------+
bool CWndClient::HScrolled(const bool flag)
  {
   if(m_h_scrolled==flag)
      return(true);
//--- there are changes
   int d_size=0;
   if(flag)
     {
      //--- create horizontal scrollbar
      if(!CreateScrollH())
         return(false);
      //--- need to shorten vertical scrollbar (if there is one)
      d_size=-CONTROLS_SCROLL_SIZE;
     }
   else
     {
      //--- delete horizontal scrollbar
      m_scroll_h.Destroy();
      if(!Delete(m_scroll_h))
         return(false);
      //--- need to lengthen vertical scrollbar (if there is one)
      d_size=CONTROLS_SCROLL_SIZE;
     }
   m_h_scrolled=flag;
//--- change width of vertical scrollbar (if there is one)
   if(m_v_scrolled)
     {
      if(!m_scroll_v.Height(m_scroll_v.Height()+d_size))
         return(false);
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Set ID of control                                                |
//+------------------------------------------------------------------+
long CWndClient::Id(const long id)
  {
//--- reserve ID for container
   long id_used=CWndContainer::Id(id);
//---
   if(!m_v_scrolled)
      id_used+=m_scroll_v.Id(id+id_used);
   if(!m_h_scrolled)
      id_used+=m_scroll_h.Id(id+id_used);
//--- return number of used IDs
   return(id_used);
  }
//+------------------------------------------------------------------+
//| Makes the control visible                                        |
//+------------------------------------------------------------------+
bool CWndClient::Show(void)
  {
//--- call of the method of the parent class
   CWndContainer::Show();
//---
   if(!m_v_scrolled)
      m_scroll_v.Hide();
   else
      m_scroll_v.Show();
      
   if(!m_h_scrolled)
      m_scroll_h.Hide();
   else
      m_scroll_h.Show();
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of resizing                                              |
//+------------------------------------------------------------------+
bool CWndClient::OnResize(void)
  {
//--- call of the method of the parent class
   if(!CWndContainer::OnResize())
      return(false);
//--- resize background
   int d_size=0;
   m_background.Width(Width());
   m_background.Height(Height());
//---
   if(m_v_scrolled)
     {
      //--- move vertical scrollbar
      m_scroll_v.Move(Right()-CONTROLS_SCROLL_SIZE,Top());
      //--- modify vertical scrollbar
      d_size=(m_h_scrolled) ? CONTROLS_SCROLL_SIZE : 0;
      m_scroll_v.Height(Height()-d_size);
     }
   if(m_h_scrolled)
     {
      //--- move horizontal scrollbar
      m_scroll_h.Move(Left(),Bottom()-CONTROLS_SCROLL_SIZE);
      //--- modify horizontal scrollbar
      d_size=(m_v_scrolled) ? CONTROLS_SCROLL_SIZE : 0;
      m_scroll_h.Width(Width()-d_size);
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Resize                                                           |
//+------------------------------------------------------------------+
bool CWndClient::Rebound(const CRect &rect)
  {
   m_rect.SetBound(rect);
//--- call virtual event handler
   return(OnResize());
  }
//+------------------------------------------------------------------+
//#include "Panel.mqh"
//#include "Edit.mqh"
//+------------------------------------------------------------------+
//|                                                         Edit.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "WndObj.mqh"
//#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//| Class CEdit                                                      |
//| Usage: control that is displayed by                              |
//|             the CChartObjectEdit object                          |
//+------------------------------------------------------------------+
class CEdit : public CWndObj
  {
private:
   CChartObjectEdit  m_edit;                // chart object
   //--- parameters of the chart object
   bool              m_read_only;           // "read-only" mode flag
   ENUM_ALIGN_MODE   m_align_mode;          // align mode

public:
                     CEdit(void);
                    ~CEdit(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- parameters of the chart object
   bool              ReadOnly(void)         const { return(m_read_only);                          }
   bool              ReadOnly(const bool flag);
   ENUM_ALIGN_MODE   TextAlign(void)        const { return(m_align_mode);                         }
   bool              TextAlign(const ENUM_ALIGN_MODE align);
   //--- data access
   string            Text(void)             const { return(m_edit.Description());                 }
   bool              Text(const string value)     { return(CWndObj::Text(value));                 }

protected:
   //--- handlers of object events
   virtual bool      OnObjectEndEdit(void);
   //--- handlers of object settings
   virtual bool      OnSetText(void)              { return(m_edit.Description(m_text));           }
   virtual bool      OnSetColor(void)             { return(m_edit.Color(m_color));                }
   virtual bool      OnSetColorBackground(void)   { return(m_edit.BackColor(m_color_background)); }
   virtual bool      OnSetColorBorder(void)       { return(m_edit.BorderColor(m_color_border));   }
   virtual bool      OnSetFont(void)              { return(m_edit.Font(m_font));                  }
   virtual bool      OnSetFontSize(void)          { return(m_edit.FontSize(m_font_size));         }
   virtual bool      OnSetZOrder(void)            { return(m_edit.Z_Order(m_zorder));             }
   //--- internal event handlers
   virtual bool      OnCreate(void);
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnMove(void);
   virtual bool      OnResize(void);
   virtual bool      OnChange(void);
   virtual bool      OnClick(void);
  };
//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
bool CEdit::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   if(m_name==sparam && id==CHARTEVENT_OBJECT_ENDEDIT)
      return(OnObjectEndEdit());
//--- event was not handled
   return(CWndObj::OnEvent(id,lparam,dparam,sparam));
  }
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CEdit::CEdit(void) : m_read_only(false),
                     m_align_mode(ALIGN_LEFT)
  {
   m_color           =CONTROLS_EDIT_COLOR;
   m_color_background=CONTROLS_EDIT_COLOR_BG;
   m_color_border    =CONTROLS_EDIT_COLOR_BORDER;
  RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CEdit::~CEdit(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CEdit::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndObj::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create the chart object
   if(!m_edit.Create(chart,name,subwin,x1,y1,Width(),Height()))
      return(false);
//--- call the settings handler
   return(OnChange());
  }
//+------------------------------------------------------------------+
//| Set parameter                                                    |
//+------------------------------------------------------------------+
bool CEdit::ReadOnly(const bool flag)
  {
//--- save new value of parameter
   m_read_only=flag;
//--- set up the chart object
   return(m_edit.ReadOnly(flag));
  }
//+------------------------------------------------------------------+
//| Set parameter                                                    |
//+------------------------------------------------------------------+
bool CEdit::TextAlign(const ENUM_ALIGN_MODE align)
  {
//--- save new value of parameter
   m_align_mode=align;
//--- set up the chart object
   return(m_edit.TextAlign(align));
  }
//+------------------------------------------------------------------+
//| Create object on chart                                           |
//+------------------------------------------------------------------+
bool CEdit::OnCreate(void)
  {
//--- create the chart object by previously set parameters
   return(m_edit.Create(m_chart_id,m_name,m_subwin,m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Display object on chart                                          |
//+------------------------------------------------------------------+
bool CEdit::OnShow(void)
  {
   return(m_edit.Timeframes(OBJ_ALL_PERIODS));
  }
//+------------------------------------------------------------------+
//| Hide object from chart                                           |
//+------------------------------------------------------------------+
bool CEdit::OnHide(void)
  {
   return(m_edit.Timeframes(OBJ_NO_PERIODS));
  }
//+------------------------------------------------------------------+
//| Absolute movement of the chart object                            |
//+------------------------------------------------------------------+
bool CEdit::OnMove(void)
  {
//--- position the chart object
   return(m_edit.X_Distance(m_rect.left) && m_edit.Y_Distance(m_rect.top));
  }
//+------------------------------------------------------------------+
//| Resize the chart object                                          |
//+------------------------------------------------------------------+
bool CEdit::OnResize(void)
  {
//--- resize the chart object
   return(m_edit.X_Size(m_rect.Width()) && m_edit.Y_Size(m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Set up the chart object                                          |
//+------------------------------------------------------------------+
bool CEdit::OnChange(void)
  {
//--- set up the chart object
   return(CWndObj::OnChange() && ReadOnly(m_read_only) && TextAlign(m_align_mode));
  }
//+------------------------------------------------------------------+
//| Handler of the "End of editing" event                            |
//+------------------------------------------------------------------+
bool CEdit::OnObjectEndEdit(void)
  {
//--- send the ON_END_EDIT notification
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_END_EDIT,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "click" event                                     |
//+------------------------------------------------------------------+
bool CEdit::OnClick(void)
  {
//--- if editing is enabled, send the ON_START_EDIT notification
   if(!m_read_only)
     {
      EventChartCustom(CONTROLS_SELF_MESSAGE,ON_START_EDIT,m_id,0.0,m_name);
      //--- handled
      //return(true);
     }
//--- else send the ON_CLICK notification
   return(CWnd::OnClick());
  }
//+------------------------------------------------------------------+
//#include "BmpButton.mqh"
//#include <Charts\Chart.mqh>
//+------------------------------------------------------------------+
//|                                                       Chart.mqh  |
//|                   Copyright 2009-2016, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include <Object.mqh>
//+------------------------------------------------------------------+
//| Class CChart.                                                    |
//| Purpose: Class of the "Chart" object.                            |
//|          Derives from class CObject.                             |
//+------------------------------------------------------------------+
class CChart : public CObject
  {
protected:
   long              m_chart_id;           // chart identifier
public:
                     CChart(void);
                    ~CChart(void);
   //--- methods of access to protected data
   long              ChartId(void) const { return(m_chart_id); }
   //--- method of identifying the object
   virtual int       Type(void) const { return(0x1111); }
   //--- methods of access to properties of the chart
   //--- common properties
   ENUM_CHART_MODE   Mode(void) const;
   bool              Mode(const ENUM_CHART_MODE mode) const;
   bool              Foreground(void) const;
   bool              Foreground(const bool foreground) const;
   bool              Shift(void) const;
   bool              Shift(const bool shift) const;
   double            ShiftSize(void) const;
   bool              ShiftSize(double shift) const;
   bool              AutoScroll(void) const;
   bool              AutoScroll(const bool auto_scroll) const;
   int               Scale(void) const;
   bool              Scale(int scale) const;
   bool              ScaleFix(void) const;
   bool              ScaleFix(const bool scale_fix) const;
   bool              ScaleFix_11(void) const;
   bool              ScaleFix_11(const bool scale_fix_11) const;
   double            FixedMax(void) const;
   bool              FixedMax(const double fixed_max) const;
   double            FixedMin(void) const;
   bool              FixedMin(const double fixed_min) const;
   bool              ScalePPB(void) const;
   bool              ScalePPB(const bool scale_ppb) const;
   double            PointsPerBar(void) const;
   bool              PointsPerBar(const double points_per_bar) const;
   //--- show properties
   bool              ShowOHLC(void) const;
   bool              ShowOHLC(const bool show) const;
   bool              ShowLineBid(void) const;
   bool              ShowLineBid(const bool show) const;
   bool              ShowLineAsk(void) const;
   bool              ShowLineAsk(const bool show) const;
   bool              ShowLastLine(void) const;
   bool              ShowLastLine(const bool show) const;
   bool              ShowPeriodSep(void) const;
   bool              ShowPeriodSep(const bool show) const;
   bool              ShowGrid(void) const;
   bool              ShowGrid(const bool show) const;
   ENUM_CHART_VOLUME_MODE ShowVolumes(void) const;
   bool              ShowVolumes(const ENUM_CHART_VOLUME_MODE show) const;
   bool              ShowObjectDescr(void) const;
   bool              ShowObjectDescr(const bool show) const;
   bool              ShowDateScale(const bool show) const;
   bool              ShowPriceScale(const bool show) const;
   //--- color properties
   color             ColorBackground(void) const;
   bool              ColorBackground(const color new_color) const;
   color             ColorForeground(void) const;
   bool              ColorForeground(const color new_color) const;
   color             ColorGrid(void) const;
   bool              ColorGrid(const color new_color) const;
   color             ColorBarUp(void) const;
   bool              ColorBarUp(const color new_color) const;
   color             ColorBarDown(void) const;
   bool              ColorBarDown(const color new_color) const;
   color             ColorCandleBull(void) const;
   bool              ColorCandleBull(const color new_color) const;
   color             ColorCandleBear(void) const;
   bool              ColorCandleBear(const color new_color) const;
   color             ColorChartLine(void) const;
   bool              ColorChartLine(const color new_color) const;
   color             ColorVolumes(void) const;
   bool              ColorVolumes(const color new_color) const;
   color             ColorLineBid(void) const;
   bool              ColorLineBid(const color new_color) const;
   color             ColorLineAsk(void) const;
   bool              ColorLineAsk(const color new_color) const;
   color             ColorLineLast(void) const;
   bool              ColorLineLast(const color new_color) const;
   color             ColorStopLevels(void) const;
   bool              ColorStopLevels(const color new_color) const;
   //--- other properties
   bool              BringToTop(void) const;
   bool              EventObjectCreate(const bool flag=true) const;
   bool              EventObjectDelete(const bool flag=true) const;
   bool              EventMouseMove(const bool flag=true) const;
   bool              MouseScroll(const bool flag=true) const;
   //--- methods of access to READ ONLY properties of the chart
   int               VisibleBars(void) const;
   int               WindowsTotal(void) const;
   bool              WindowIsVisible(const int num) const;
   int               WindowHandle(void) const;
   int               FirstVisibleBar(void) const;
   int               WidthInBars(void) const;
   int               WidthInPixels(void) const;
   int               HeightInPixels(const int num) const;
   int               SubwindowY(const int num) const;
   double            PriceMin(const int num) const;
   double            PriceMax(const int num) const;
   bool              IsObject(void) const;
   //--- methods of binding chart
   void              Attach(void) { m_chart_id=ChartID(); }
   void              Attach(const long chart) { m_chart_id=chart; }
   void              FirstChart(void) { m_chart_id=ChartFirst(); }
   void              NextChart(void) { m_chart_id=ChartNext(m_chart_id); }
   long              Open(const string symbol_name,const ENUM_TIMEFRAMES timeframe);
   void              Detach(void) { m_chart_id=-1;                    }
   void              Close(void);
   //--- navigation method
   bool              Navigate(const ENUM_CHART_POSITION position,const int shift=0) const;
   //--- methods of access to the API functions of MQL5
   string            Symbol(void) const { return(ChartSymbol(m_chart_id)); }
   ENUM_TIMEFRAMES   Period(void) const { return(ChartPeriod(m_chart_id)); }
   void              Redraw(void) const { ChartRedraw(m_chart_id); }
   long              GetInteger(const ENUM_CHART_PROPERTY_INTEGER prop_id,const int sub_window=0) const;
   bool              GetInteger(const ENUM_CHART_PROPERTY_INTEGER prop_id,const int sub_window,long &value) const;
   bool              SetInteger(const ENUM_CHART_PROPERTY_INTEGER prop_id,const long value) const;
   double            GetDouble(const ENUM_CHART_PROPERTY_DOUBLE prop_id,const int sub_window=0) const;
   bool              GetDouble(const ENUM_CHART_PROPERTY_DOUBLE prop_id,const int sub_window,double &value) const;
   bool              SetDouble(const ENUM_CHART_PROPERTY_DOUBLE prop_id,const double value) const;
   string            GetString(const ENUM_CHART_PROPERTY_STRING prop_id) const;
   bool              GetString(const ENUM_CHART_PROPERTY_STRING prop_id,string &value) const;
   bool              SetString(const ENUM_CHART_PROPERTY_STRING prop_id,const string value) const;
   bool              SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES period) const;
   bool              ApplyTemplate(const string filename) const;
   bool              ScreenShot(const string filename,const int width,const int height,
                                const ENUM_ALIGN_MODE align_mode=ALIGN_RIGHT) const;
   int               WindowOnDropped(void) const;
   double            PriceOnDropped(void) const;
   datetime          TimeOnDropped(void) const;
   int               XOnDropped(void) const;
   int               YOnDropped(void) const;
   //--- methods for working with indicators
   bool              IndicatorAdd(const int subwin,const int handle) const;
   bool              IndicatorDelete(const int subwin,const string name) const;
   int               IndicatorsTotal(const int subwin) const;
   string            IndicatorName(const int subwin,const int index) const;
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CChart::CChart(void) : m_chart_id(-1)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CChart::~CChart(void)
  {
   if(m_chart_id!=-1)
      Close();
  }
//+------------------------------------------------------------------+
//| Opening chart                                                    |
//+------------------------------------------------------------------+
long CChart::Open(const string symbol_name,const ENUM_TIMEFRAMES timeframe)
  {
   m_chart_id=ChartOpen(symbol_name,timeframe);
   if(m_chart_id==0)
      m_chart_id=-1;
   return(m_chart_id);
  }
//+------------------------------------------------------------------+
//| Get the type of representation of chart                          |
//+------------------------------------------------------------------+
ENUM_CHART_MODE CChart::Mode(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(WRONG_VALUE);
//--- result
   return((ENUM_CHART_MODE)ChartGetInteger(m_chart_id,CHART_MODE));
  }
//+------------------------------------------------------------------+
//| Set the type of representation chart                             |
//+------------------------------------------------------------------+
bool CChart::Mode(const ENUM_CHART_MODE mode) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_MODE,mode));
  }
//+------------------------------------------------------------------+
//| Get value of the "Foreground" property                           |
//+------------------------------------------------------------------+
bool CChart::Foreground(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ChartGetInteger(m_chart_id,CHART_FOREGROUND));
  }
//+------------------------------------------------------------------+
//| Set value of the "Foreground" property                           |
//+------------------------------------------------------------------+
bool CChart::Foreground(const bool foreground) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_FOREGROUND,foreground));
  }
//+------------------------------------------------------------------+
//| Get value of the "Shift" property                                |
//+------------------------------------------------------------------+
bool CChart::Shift(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ChartGetInteger(m_chart_id,CHART_SHIFT));
  }
//+------------------------------------------------------------------+
//| Set value of the "Shift"property                                 |
//+------------------------------------------------------------------+
bool CChart::Shift(const bool shift) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_SHIFT,shift));
  }
//+------------------------------------------------------------------+
//| Get value of the "ShiftSize" property                            |
//+------------------------------------------------------------------+
double CChart::ShiftSize(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(DBL_MAX);
//--- result
   return(ChartGetDouble(m_chart_id,CHART_SHIFT_SIZE));
  }
//+------------------------------------------------------------------+
//| Set value of the "ShiftSize" property                            |
//+------------------------------------------------------------------+
bool CChart::ShiftSize(double shift) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
   if(shift<10)
      shift=10;
   if(shift>50)
      shift=50;
//--- result
   return(ChartSetDouble(m_chart_id,CHART_SHIFT_SIZE,shift));
  }
//+------------------------------------------------------------------+
//| Get value of the "AutoScroll" property                           |
//+------------------------------------------------------------------+
bool CChart::AutoScroll(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ChartGetInteger(m_chart_id,CHART_AUTOSCROLL));
  }
//+------------------------------------------------------------------+
//| Set value of the "AutoScroll" property                           |
//+------------------------------------------------------------------+
bool CChart::AutoScroll(const bool auto_scroll) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_AUTOSCROLL,auto_scroll));
  }
//+------------------------------------------------------------------+
//| Get value of the "Scale" property                                |
//+------------------------------------------------------------------+
int CChart::Scale(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ChartGetInteger(m_chart_id,CHART_SCALE));
  }
//+------------------------------------------------------------------+
//| Set value of the "Scale" property                                |
//+------------------------------------------------------------------+
bool CChart::Scale(int shift) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
   if(shift<0)
      shift=0;
   if(shift>32)
      shift=32;
//--- result
   return(ChartSetInteger(m_chart_id,CHART_SCALE,shift));
  }
//+------------------------------------------------------------------+
//| Get value of the "ScaleFix" property                             |
//+------------------------------------------------------------------+
bool CChart::ScaleFix(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ChartGetInteger(m_chart_id,CHART_SCALEFIX));
  }
//+------------------------------------------------------------------+
//| Set value of the "ScaleFix" property                             |
//+------------------------------------------------------------------+
bool CChart::ScaleFix(const bool scale_fix) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_SCALEFIX,scale_fix));
  }
//+------------------------------------------------------------------+
//| Get value of the "ScaleFix_11" property                          |
//+------------------------------------------------------------------+
bool CChart::ScaleFix_11(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ChartGetInteger(m_chart_id,CHART_SCALEFIX_11));
  }
//+------------------------------------------------------------------+
//| Set value of the "ScaleFix_11" property                          |
//+------------------------------------------------------------------+
bool CChart::ScaleFix_11(const bool scale_fix_11) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_SCALEFIX_11,scale_fix_11));
  }
//+------------------------------------------------------------------+
//| Get value of the "FixedMax" property                             |
//+------------------------------------------------------------------+
double CChart::FixedMax(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(EMPTY_VALUE);
//--- result
   return(ChartGetDouble(m_chart_id,CHART_FIXED_MAX));
  }
//+------------------------------------------------------------------+
//| Set value of the "FixedMax" property                             |
//+------------------------------------------------------------------+
bool CChart::FixedMax(const double fixed_max) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetDouble(m_chart_id,CHART_FIXED_MAX,fixed_max));
  }
//+------------------------------------------------------------------+
//| Get value of the "FixedMin" property                             |
//+------------------------------------------------------------------+
double CChart::FixedMin(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(EMPTY_VALUE);
//--- result
   return(ChartGetDouble(m_chart_id,CHART_FIXED_MIN));
  }
//+------------------------------------------------------------------+
//| Set value of the "FixedMin" property                             |
//+------------------------------------------------------------------+
bool CChart::FixedMin(const double fixed_min) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetDouble(m_chart_id,CHART_FIXED_MIN,fixed_min));
  }
//+------------------------------------------------------------------+
//| Get value of the "ScalePointsPerBar" property                    |
//+------------------------------------------------------------------+
bool CChart::ScalePPB(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ChartGetInteger(m_chart_id,CHART_SCALE_PT_PER_BAR));
  }
//+------------------------------------------------------------------+
//| Set value of the "ScalePointsPerBar" property                    |
//+------------------------------------------------------------------+
bool CChart::ScalePPB(const bool scale_ppb) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_SCALE_PT_PER_BAR,scale_ppb));
  }
//+------------------------------------------------------------------+
//| Get value of the "PointsPerBar" property                         |
//+------------------------------------------------------------------+
double CChart::PointsPerBar(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(EMPTY_VALUE);
//--- result
   return(ChartGetDouble(m_chart_id,CHART_POINTS_PER_BAR));
  }
//+------------------------------------------------------------------+
//| Set value of the "PointsPerBar" property                         |
//+------------------------------------------------------------------+
bool CChart::PointsPerBar(const double points_per_bar) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetDouble(m_chart_id,CHART_POINTS_PER_BAR,points_per_bar));
  }
//+------------------------------------------------------------------+
//| Get value of the "ShowOHLC" property                             |
//+------------------------------------------------------------------+
bool CChart::ShowOHLC(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ChartGetInteger(m_chart_id,CHART_SHOW_OHLC));
  }
//+------------------------------------------------------------------+
//| Set value of the "ShowOHLC" property                             |
//+------------------------------------------------------------------+
bool CChart::ShowOHLC(const bool show) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_SHOW_OHLC,show));
  }
//+------------------------------------------------------------------+
//| Get value of the "ShowLineBid" property                          |
//+------------------------------------------------------------------+
bool CChart::ShowLineBid(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ChartGetInteger(m_chart_id,CHART_SHOW_BID_LINE));
  }
//+------------------------------------------------------------------+
//| Set value of the "ShowLineBid" property                          |
//+------------------------------------------------------------------+
bool CChart::ShowLineBid(const bool show) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_SHOW_BID_LINE,show));
  }
//+------------------------------------------------------------------+
//| Get value of the "ShowLineAsk" property                          |
//+------------------------------------------------------------------+
bool CChart::ShowLineAsk(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ChartGetInteger(m_chart_id,CHART_SHOW_ASK_LINE));
  }
//+------------------------------------------------------------------+
//| Set value of the "ShowLineAsk" property                          |
//+------------------------------------------------------------------+
bool CChart::ShowLineAsk(const bool show) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_SHOW_ASK_LINE,show));
  }
//+------------------------------------------------------------------+
//| Get value of the "ShowLastLine" property                         |
//+------------------------------------------------------------------+
bool CChart::ShowLastLine(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ChartGetInteger(m_chart_id,CHART_SHOW_LAST_LINE));
  }
//+------------------------------------------------------------------+
//| Set value of the "ShowLastLine" property                         |
//+------------------------------------------------------------------+
bool CChart::ShowLastLine(const bool show) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_SHOW_LAST_LINE,show));
  }
//+------------------------------------------------------------------+
//| Get value of the "ShowPeriodSep" property                        |
//+------------------------------------------------------------------+
bool CChart::ShowPeriodSep(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ChartGetInteger(m_chart_id,CHART_SHOW_PERIOD_SEP));
  }
//+------------------------------------------------------------------+
//| Set value of the "ShowPeriodSep" property                        |
//+------------------------------------------------------------------+
bool CChart::ShowPeriodSep(const bool show) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_SHOW_PERIOD_SEP,show));
  }
//+------------------------------------------------------------------+
//| Get value of the "ShowGrid" property                             |
//+------------------------------------------------------------------+
bool CChart::ShowGrid(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ChartGetInteger(m_chart_id,CHART_SHOW_GRID));
  }
//+------------------------------------------------------------------+
//| Set value of the "ShowGrid" property                             |
//+------------------------------------------------------------------+
bool CChart::ShowGrid(const bool show) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_SHOW_GRID,show));
  }
//+------------------------------------------------------------------+
//| Get value of the "ShowVolumes" property                          |
//+------------------------------------------------------------------+
ENUM_CHART_VOLUME_MODE CChart::ShowVolumes(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(WRONG_VALUE);
//--- result
   return((ENUM_CHART_VOLUME_MODE)ChartGetInteger(m_chart_id,CHART_SHOW_VOLUMES));
  }
//+------------------------------------------------------------------+
//| Set value of the "ShowVolumes" property                          |
//+------------------------------------------------------------------+
bool CChart::ShowVolumes(const ENUM_CHART_VOLUME_MODE show) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_SHOW_VOLUMES,show));
  }
//+------------------------------------------------------------------+
//| Get value of the "ShowObjectDescr" property                      |
//+------------------------------------------------------------------+
bool CChart::ShowObjectDescr(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ChartGetInteger(m_chart_id,CHART_SHOW_OBJECT_DESCR));
  }
//+------------------------------------------------------------------+
//| Set value of the "ShowObjectDescr" property                      |
//+------------------------------------------------------------------+
bool CChart::ShowObjectDescr(const bool show) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_SHOW_OBJECT_DESCR,show));
  }
//+------------------------------------------------------------------+
//| Set value of the "ShowDateScale" property                        |
//+------------------------------------------------------------------+
bool CChart::ShowDateScale(const bool show) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_SHOW_DATE_SCALE,show));
  }
//+------------------------------------------------------------------+
//| Set value of the "ShowPriceScale" property                       |
//+------------------------------------------------------------------+
bool CChart::ShowPriceScale(const bool show) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_SHOW_PRICE_SCALE,show));
  }
//+------------------------------------------------------------------+
//| Get color value of the "Background" property                     |
//+------------------------------------------------------------------+
color CChart::ColorBackground(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
//--- result
   return((color)ChartGetInteger(m_chart_id,CHART_COLOR_BACKGROUND));
  }
//+------------------------------------------------------------------+
//| Set color value of the "Background" property                     |
//+------------------------------------------------------------------+
bool CChart::ColorBackground(const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_COLOR_BACKGROUND,new_color));
  }
//+------------------------------------------------------------------+
//| Get color value of the "Foreground" property                     |
//+------------------------------------------------------------------+
color CChart::ColorForeground(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
//--- result
   return((color)ChartGetInteger(m_chart_id,CHART_COLOR_FOREGROUND));
  }
//+------------------------------------------------------------------+
//| Set color value for the "Foreground" property                    |
//+------------------------------------------------------------------+
bool CChart::ColorForeground(const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_COLOR_FOREGROUND,new_color));
  }
//+------------------------------------------------------------------+
//| Get color value of the "Grid" property                           |
//+------------------------------------------------------------------+
color CChart::ColorGrid(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
//--- result
   return((color)ChartGetInteger(m_chart_id,CHART_COLOR_GRID));
  }
//+------------------------------------------------------------------+
//| Set color value for the "Grid" property                          |
//+------------------------------------------------------------------+
bool CChart::ColorGrid(const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_COLOR_GRID,new_color));
  }
//+------------------------------------------------------------------+
//| Get color value of the "Bar Up" property                         |
//+------------------------------------------------------------------+
color CChart::ColorBarUp(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
//--- result
   return((color)ChartGetInteger(m_chart_id,CHART_COLOR_CHART_UP));
  }
//+------------------------------------------------------------------+
//| Set color value of the "Bar Up" property                         |
//+------------------------------------------------------------------+
bool CChart::ColorBarUp(const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_COLOR_CHART_UP,new_color));
  }
//+------------------------------------------------------------------+
//| Get color value of the "Bar Down" property                       |
//+------------------------------------------------------------------+
color CChart::ColorBarDown(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
//--- result
   return((color)ChartGetInteger(m_chart_id,CHART_COLOR_CHART_DOWN));
  }
//+------------------------------------------------------------------+
//| Set color value of the "Bar Down" property                       |
//+------------------------------------------------------------------+
bool CChart::ColorBarDown(const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_COLOR_CHART_DOWN,new_color));
  }
//+------------------------------------------------------------------+
//| Get color value of the "Candle Bull" property                    |
//+------------------------------------------------------------------+
color CChart::ColorCandleBull(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
//--- result
   return((color)ChartGetInteger(m_chart_id,CHART_COLOR_CANDLE_BULL));
  }
//+------------------------------------------------------------------+
//| Set color value of the "Candle Bull" property                    |
//+------------------------------------------------------------------+
bool CChart::ColorCandleBull(const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_COLOR_CANDLE_BULL,new_color));
  }
//+------------------------------------------------------------------+
//| Get color value of the "Candle Bear" property                    |
//+------------------------------------------------------------------+
color CChart::ColorCandleBear(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
//--- result
   return((color)ChartGetInteger(m_chart_id,CHART_COLOR_CANDLE_BEAR));
  }
//+------------------------------------------------------------------+
//| Set color value of the "Candle Bear" property                    |
//+------------------------------------------------------------------+
bool CChart::ColorCandleBear(const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_COLOR_CANDLE_BEAR,new_color));
  }
//+------------------------------------------------------------------+
//| Get color value of the "Chart Line" property                     |
//+------------------------------------------------------------------+
color CChart::ColorChartLine(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
//--- result
   return((color)ChartGetInteger(m_chart_id,CHART_COLOR_CHART_LINE));
  }
//+------------------------------------------------------------------+
//| Set color value of the "Chart Line" property                     |
//+------------------------------------------------------------------+
bool CChart::ColorChartLine(const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_COLOR_CHART_LINE,new_color));
  }
//+------------------------------------------------------------------+
//| Get color value of the "Volumes" property                        |
//+------------------------------------------------------------------+
color CChart::ColorVolumes(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
//--- result
   return((color)ChartGetInteger(m_chart_id,CHART_COLOR_VOLUME));
  }
//+------------------------------------------------------------------+
//| Set color value of the "Volumes" property                        |
//+------------------------------------------------------------------+
bool CChart::ColorVolumes(const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_COLOR_VOLUME,new_color));
  }
//+------------------------------------------------------------------+
//| Get color value of the "Line Bid" property                       |
//+------------------------------------------------------------------+
color CChart::ColorLineBid(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
//--- result
   return((color)ChartGetInteger(m_chart_id,CHART_COLOR_BID));
  }
//+------------------------------------------------------------------+
//| Set color value of the "Line Bid" property                       |
//+------------------------------------------------------------------+
bool CChart::ColorLineBid(const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_COLOR_BID,new_color));
  }
//+------------------------------------------------------------------+
//| Get color value of the "Line Ask" property                       |
//+------------------------------------------------------------------+
color CChart::ColorLineAsk(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
//--- result
   return((color)ChartGetInteger(m_chart_id,CHART_COLOR_ASK));
  }
//+------------------------------------------------------------------+
//| Set color value of the "Line Ask" property                       |
//+------------------------------------------------------------------+
bool CChart::ColorLineAsk(const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_COLOR_ASK,new_color));
  }
//+------------------------------------------------------------------+
//| Get color value of the "Line Last" property                      |
//+------------------------------------------------------------------+
color CChart::ColorLineLast(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
//--- result
   return((color)ChartGetInteger(m_chart_id,CHART_COLOR_LAST));
  }
//+------------------------------------------------------------------+
//| Set color value of the "Line Last" property                      |
//+------------------------------------------------------------------+
bool CChart::ColorLineLast(const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_COLOR_LAST,new_color));
  }
//+------------------------------------------------------------------+
//| Get color value of the "Stop Levels" property                    |
//+------------------------------------------------------------------+
color CChart::ColorStopLevels(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(CLR_NONE);
//--- result
   return((color)ChartGetInteger(m_chart_id,CHART_COLOR_STOP_LEVEL));
  }
//+------------------------------------------------------------------+
//| Set color value of the "Stop Levels" property                    |
//+------------------------------------------------------------------+
bool CChart::ColorStopLevels(const color new_color) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_COLOR_STOP_LEVEL,new_color));
  }
//+------------------------------------------------------------------+
//| Shows chart always on top                                        |
//+------------------------------------------------------------------+
bool CChart::BringToTop(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_BRING_TO_TOP,true));
  }
//+------------------------------------------------------------------+
//| Sets flag to generate event of creating objects                  |
//+------------------------------------------------------------------+
bool CChart::EventObjectCreate(const bool flag) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_EVENT_OBJECT_CREATE,flag));
  }
//+------------------------------------------------------------------+
//| Sets flag to generate event of deleting objects                  |
//+------------------------------------------------------------------+
bool CChart::EventObjectDelete(const bool flag) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_EVENT_OBJECT_DELETE,flag));
  }
//+------------------------------------------------------------------+
//| Sets flag to generate event of moving mouse cursor               |
//+------------------------------------------------------------------+
bool CChart::EventMouseMove(const bool flag) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_EVENT_MOUSE_MOVE,flag));
  }
//+------------------------------------------------------------------+
//| Sets flag to mouse scrolling                                     |
//+------------------------------------------------------------------+
bool CChart::MouseScroll(const bool flag) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetInteger(m_chart_id,CHART_MOUSE_SCROLL,flag));
  }
//+------------------------------------------------------------------+
//| Get value of the "VisibleBars" property                          |
//+------------------------------------------------------------------+
int CChart::VisibleBars(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ChartGetInteger(m_chart_id,CHART_WIDTH_IN_BARS));
  }
//+------------------------------------------------------------------+
//| Get value of the "WindowsTotal" property                         |
//+------------------------------------------------------------------+
int CChart::WindowsTotal(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ChartGetInteger(m_chart_id,CHART_WINDOWS_TOTAL));
  }
//+------------------------------------------------------------------+
//| Get value of the "WindowIsVisible" property                      |
//+------------------------------------------------------------------+
bool CChart::WindowIsVisible(const int num) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ChartGetInteger(m_chart_id,CHART_WINDOW_IS_VISIBLE,num));
  }
//+------------------------------------------------------------------+
//| Get value of the "WindowHandle" property                         |
//+------------------------------------------------------------------+
int CChart::WindowHandle(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(INVALID_HANDLE);
//--- result
   return((int)ChartGetInteger(m_chart_id,CHART_WINDOW_HANDLE));
  }
//+------------------------------------------------------------------+
//| Get value of the "FirstVisibleBar" property                      |
//+------------------------------------------------------------------+
int CChart::FirstVisibleBar(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(-1);
//--- result
   return((int)ChartGetInteger(m_chart_id,CHART_FIRST_VISIBLE_BAR));
  }
//+------------------------------------------------------------------+
//| Get value of the "WidthInBars" property                          |
//+------------------------------------------------------------------+
int CChart::WidthInBars(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ChartGetInteger(m_chart_id,CHART_WIDTH_IN_BARS));
  }
//+------------------------------------------------------------------+
//| Get value of the "WidthInPixels" property                        |
//+------------------------------------------------------------------+
int CChart::WidthInPixels(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ChartGetInteger(m_chart_id,CHART_WIDTH_IN_PIXELS));
  }
//+------------------------------------------------------------------+
//| Get value of the "HeightInPixels" property                       |
//+------------------------------------------------------------------+
int CChart::HeightInPixels(const int num) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ChartGetInteger(m_chart_id,CHART_HEIGHT_IN_PIXELS,num));
  }
//+------------------------------------------------------------------+
//| Get value of the "WindowYDistance" property                      |
//+------------------------------------------------------------------+
int CChart::SubwindowY(const int num) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return((int)ChartGetInteger(m_chart_id,CHART_WINDOW_YDISTANCE,num));
  }
//+------------------------------------------------------------------+
//| Get value of the "PriceMin" property                             |
//+------------------------------------------------------------------+
double CChart::PriceMin(const int num) const
  {
//--- check
   if(m_chart_id==-1)
      return(EMPTY_VALUE);
//--- result
   return(ChartGetDouble(m_chart_id,CHART_PRICE_MIN,num));
  }
//+------------------------------------------------------------------+
//| Get value of the "PriceMax" property                             |
//+------------------------------------------------------------------+
double CChart::PriceMax(const int num) const
  {
//--- check
   if(m_chart_id==-1)
      return(EMPTY_VALUE);
//--- result
   return(ChartGetDouble(m_chart_id,CHART_PRICE_MAX,num));
  }
//+------------------------------------------------------------------+
//| Get value of the "IsObject" property                             |
//+------------------------------------------------------------------+
bool CChart::IsObject(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return((bool)ChartGetInteger(m_chart_id,CHART_IS_OBJECT));
  }
//+------------------------------------------------------------------+
//| Chart close                                                      |
//+------------------------------------------------------------------+
void CChart::Close(void)
  {
   if(m_chart_id!=-1)
     {
      ChartClose(m_chart_id);
      m_chart_id=-1;
     }
  }
//+------------------------------------------------------------------+
//| Chart navigation                                                 |
//+------------------------------------------------------------------+
bool CChart::Navigate(const ENUM_CHART_POSITION position,const int shift) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartNavigate(m_chart_id,position,shift));
  }
//+------------------------------------------------------------------+
//| Access functions long ChartGetInteger(...)                       |
//+------------------------------------------------------------------+
long CChart::GetInteger(const ENUM_CHART_PROPERTY_INTEGER prop_id,const int subwindow) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return(ChartGetInteger(m_chart_id,prop_id,subwindow));
  }
//+------------------------------------------------------------------+
//| Access function bool ChartGetInteger(...)                        |
//+------------------------------------------------------------------+
bool CChart::GetInteger(const ENUM_CHART_PROPERTY_INTEGER prop_id,const int subwindow,long &value) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartGetInteger(m_chart_id,prop_id,subwindow,value));
  }
//+------------------------------------------------------------------+
//| Access function  ChartSetInteger(...)                            |
//+------------------------------------------------------------------+
bool CChart::SetInteger(const ENUM_CHART_PROPERTY_INTEGER prop_id,const long value) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//---
   return(ChartSetInteger(m_chart_id,prop_id,value));
  }
//+------------------------------------------------------------------+
//| Access function double ChartGetDouble(...)                       |
//+------------------------------------------------------------------+
double CChart::GetDouble(const ENUM_CHART_PROPERTY_DOUBLE prop_id,const int subwindow) const
  {
//--- check
   if(m_chart_id==-1)
      return(EMPTY_VALUE);
//--- result
   return(ChartGetDouble(m_chart_id,prop_id,subwindow));
  }
//+------------------------------------------------------------------+
//| Access function bool ChartGetDouble(...)                         |
//+------------------------------------------------------------------+
bool CChart::GetDouble(const ENUM_CHART_PROPERTY_DOUBLE prop_id,const int subwindow,double &value) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartGetDouble(m_chart_id,prop_id,subwindow,value));
  }
//+------------------------------------------------------------------+
//| Access function ChartSetDouble(...)                              |
//+------------------------------------------------------------------+
bool CChart::SetDouble(const ENUM_CHART_PROPERTY_DOUBLE prop_id,const double value) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetDouble(m_chart_id,prop_id,value));
  }
//+------------------------------------------------------------------+
//| Access function string ChartGetString(...)                       |
//+------------------------------------------------------------------+
string CChart::GetString(const ENUM_CHART_PROPERTY_STRING prop_id) const
  {
//--- check
   if(m_chart_id==-1)
      return("");
//--- result
   return(ChartGetString(m_chart_id,prop_id));
  }
//+------------------------------------------------------------------+
//| Access functions bool ChartGetString(...)                        |
//+------------------------------------------------------------------+
bool CChart::GetString(const ENUM_CHART_PROPERTY_STRING prop_id,string &value) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartGetString(m_chart_id,prop_id,value));
  }
//+------------------------------------------------------------------+
//| Access function ChartSetString(...)                              |
//+------------------------------------------------------------------+
bool CChart::SetString(const ENUM_CHART_PROPERTY_STRING prop_id,const string value) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetString(m_chart_id,prop_id,value));
  }
//+------------------------------------------------------------------+
//| Access function ChartSetSymbolPeriod(...)                        |
//+------------------------------------------------------------------+
bool CChart::SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES period) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartSetSymbolPeriod(m_chart_id,symbol,period));
  }
//+------------------------------------------------------------------+
//| Access function ChartApplyTemplate(...)                          |
//+------------------------------------------------------------------+
bool CChart::ApplyTemplate(const string filename) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartApplyTemplate(m_chart_id,filename));
  }
//+------------------------------------------------------------------+
//| Access function ChartScreenShot(...)                             |
//+------------------------------------------------------------------+
bool CChart::ScreenShot(const string filename,const int width,const int height,const ENUM_ALIGN_MODE align_mode) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartScreenShot(m_chart_id,filename,width,height,align_mode));
  }
//+------------------------------------------------------------------+
//| Access function WindowOnDropped()                                |
//+------------------------------------------------------------------+
int CChart::WindowOnDropped(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return(ChartWindowOnDropped());
  }
//+------------------------------------------------------------------+
//| Access function PriceOnDropped()                                 |
//+------------------------------------------------------------------+
double CChart::PriceOnDropped(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(EMPTY_VALUE);
//--- result
   return(ChartPriceOnDropped());
  }
//+------------------------------------------------------------------+
//| Access function TimeOnDropped()                                  |
//+------------------------------------------------------------------+
datetime CChart::TimeOnDropped(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartTimeOnDropped());
  }
//+------------------------------------------------------------------+
//| Access functions XOnDropped()                                    |
//+------------------------------------------------------------------+
int CChart::XOnDropped(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return(ChartXOnDropped());
  }
//+------------------------------------------------------------------+
//| Access functions YOnDropped()                                    |
//+------------------------------------------------------------------+
int CChart::YOnDropped(void) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return(ChartYOnDropped());
  }
//+------------------------------------------------------------------+
//| Adds indicator to chart                                          |
//+------------------------------------------------------------------+
bool CChart::IndicatorAdd(const int subwin,const int handle) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartIndicatorAdd(m_chart_id,subwin,handle));
  }
//+------------------------------------------------------------------+
//| Deletes indicator from chart                                     |
//+------------------------------------------------------------------+
bool CChart::IndicatorDelete(const int subwin,const string name) const
  {
//--- check
   if(m_chart_id==-1)
      return(false);
//--- result
   return(ChartIndicatorDelete(m_chart_id,subwin,name));
  }
//+------------------------------------------------------------------+
//| Gets number of indicators in chart subwindow                     |
//+------------------------------------------------------------------+
int CChart::IndicatorsTotal(const int subwin) const
  {
//--- check
   if(m_chart_id==-1)
      return(0);
//--- result
   return(ChartIndicatorsTotal(m_chart_id,subwin));
  }
//+------------------------------------------------------------------+
//| Gets short name of indicator                                     |
//+------------------------------------------------------------------+
string CChart::IndicatorName(const int subwin,const int index) const
  {
//--- check
   if(m_chart_id==-1)
      return("");
//--- result
   return(ChartIndicatorName(m_chart_id,subwin,index));
  }
//+------------------------------------------------------------------+
//| Writing parameters of chart to file                              |
//+------------------------------------------------------------------+
bool CChart::Save(const int file_handle)
  {
   string work_str;
   int    work_int;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- write start marker - 0xFFFFFFFFFFFFFFFF
   if(FileWriteLong(file_handle,-1)!=sizeof(long))
      return(false);
//--- write chart type
   if(FileWriteInteger(file_handle,Type(),INT_VALUE)!=INT_VALUE)
      return(false);
//--- write chart symbol
   work_str=Symbol();
   work_int=StringLen(work_str);
   if(FileWriteInteger(file_handle,work_int,INT_VALUE)!=INT_VALUE)
      return(false);
   if(work_int!=0) if(FileWriteString(file_handle,work_str,work_int)!=work_int)
      return(false);
//--- write period of chart
   if(FileWriteInteger(file_handle,Period(),INT_VALUE)!=sizeof(int))
      return(false);
//--- write value of the "Mode" property
   if(FileWriteInteger(file_handle,(int)ChartGetInteger(m_chart_id,CHART_MODE),INT_VALUE)!=sizeof(int))
      return(false);
//--- write value of the "Foreground" property
   if(FileWriteInteger(file_handle,(int)ChartGetInteger(m_chart_id,CHART_FOREGROUND),CHAR_VALUE)!=sizeof(char))
      return(false);
//--- write value of the "Shift" property
   if(FileWriteInteger(file_handle,(int)ChartGetInteger(m_chart_id,CHART_SHIFT),CHAR_VALUE)!=sizeof(char))
      return(false);
//--- write value of the "ShiftSize" property
   if(FileWriteInteger(file_handle,(int)ChartGetInteger(m_chart_id,CHART_SHIFT),INT_VALUE)!=sizeof(int))
      return(false);
//--- write value of the "AutoScroll" property
   if(FileWriteInteger(file_handle,(int)ChartGetInteger(m_chart_id,CHART_AUTOSCROLL),CHAR_VALUE)!=sizeof(char))
      return(false);
//--- write value of the "Scale" property
   if(FileWriteInteger(file_handle,(int)ChartGetInteger(m_chart_id,CHART_SCALE),INT_VALUE)!=sizeof(int))
      return(false);
//--- write value of the "ScaleFix" property
   if(FileWriteInteger(file_handle,(int)ChartGetInteger(m_chart_id,CHART_SCALEFIX),CHAR_VALUE)!=sizeof(char))
      return(false);
//--- write value of the "ScaleFix_11" property
   if(FileWriteInteger(file_handle,(int)ChartGetInteger(m_chart_id,CHART_SCALEFIX_11),CHAR_VALUE)!=sizeof(char))
      return(false);
//--- write value of the "FixedMax" property
   if(FileWriteDouble(file_handle,ChartGetDouble(m_chart_id,CHART_FIXED_MAX))!=sizeof(double))
      return(false);
//--- write value of the "FixedMin" property
   if(FileWriteDouble(file_handle,ChartGetDouble(m_chart_id,CHART_FIXED_MIN))!=sizeof(double))
      return(false);
//--- write the "ScalePPB" property
   if(FileWriteInteger(file_handle,(int)ChartGetInteger(m_chart_id,CHART_SCALE_PT_PER_BAR),CHAR_VALUE)!=sizeof(char))
      return(false);
//--- write value of the "PointsPerBar" property
   if(FileWriteDouble(file_handle,ChartGetDouble(m_chart_id,CHART_POINTS_PER_BAR))!=sizeof(double))
      return(false);
//--- write value of the "ShowOHLC" property
   if(FileWriteInteger(file_handle,(int)ChartGetInteger(m_chart_id,CHART_SHOW_OHLC),CHAR_VALUE)!=sizeof(char))
      return(false);
//--- write value of the "ShowLineBid" property
   if(FileWriteInteger(file_handle,(int)ChartGetInteger(m_chart_id,CHART_SHOW_BID_LINE),CHAR_VALUE)!=sizeof(char))
      return(false);
//--- write value of the "ShowLineAsk" property
   if(FileWriteInteger(file_handle,(int)ChartGetInteger(m_chart_id,CHART_SHOW_ASK_LINE),CHAR_VALUE)!=sizeof(char))
      return(false);
//--- write value of the "ShowLastLine" property
   if(FileWriteInteger(file_handle,(int)ChartGetInteger(m_chart_id,CHART_SHOW_LAST_LINE),CHAR_VALUE)!=sizeof(char))
      return(false);
//--- write value of the "ShowPeriodSep" property
   if(FileWriteInteger(file_handle,(int)ChartGetInteger(m_chart_id,CHART_SHOW_PERIOD_SEP),CHAR_VALUE)!=sizeof(char))
      return(false);
//--- write value of the "ShowGrid" property
   if(FileWriteInteger(file_handle,(int)ChartGetInteger(m_chart_id,CHART_SHOW_GRID),CHAR_VALUE)!=sizeof(char))
      return(false);
//--- write value of the "ShowVolumes" property
   if(FileWriteInteger(file_handle,(int)ChartGetInteger(m_chart_id,CHART_SHOW_VOLUMES),INT_VALUE)!=sizeof(int))
      return(false);
//--- write value of the "ShowObjectDescr" property
   if(FileWriteInteger(file_handle,(int)ChartGetInteger(m_chart_id,CHART_SHOW_OBJECT_DESCR),CHAR_VALUE)!=sizeof(char))
      return(false);
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Reading parameters of chart from file                            |
//+------------------------------------------------------------------+
bool CChart::Load(const int file_handle)
  {
   bool   resutl=true;
   string work_str;
   int    work_int;
//--- check
   if(file_handle==INVALID_HANDLE || m_chart_id==-1)
      return(false);
//--- read and checking start marker - 0xFFFFFFFFFFFFFFFF
   if(FileReadLong(file_handle)!=-1) return(false);
//--- read and checking chart type
   if(FileReadInteger(file_handle,INT_VALUE)!=Type()) return(false);
//--- read chart symbol
   work_int=FileReadInteger(file_handle);
   if(work_int!=0) work_str=FileReadString(file_handle,work_int);
   else            work_str="";
//--- read chart period
   work_int=FileReadInteger(file_handle);
   SetSymbolPeriod(work_str,(ENUM_TIMEFRAMES)work_int);
//--- read value of the "Mode" property
   if(!ChartSetInteger(m_chart_id,CHART_MODE,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- read value of the "Foreground" property
   if(!ChartSetInteger(m_chart_id,CHART_FOREGROUND,FileReadInteger(file_handle,CHAR_VALUE)))
      return(false);
//--- read value of the "Shift" property
   if(!ChartSetInteger(m_chart_id,CHART_SHIFT,FileReadInteger(file_handle,CHAR_VALUE)))
      return(false);
//--- read value of the "ShiftSize" property
   if(!ChartSetInteger(m_chart_id,CHART_SHIFT,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- read value of the "AutoScroll" property
   if(!ChartSetInteger(m_chart_id,CHART_AUTOSCROLL,FileReadInteger(file_handle,CHAR_VALUE)))
      return(false);
//--- read value of the "Scale" property
   if(!ChartSetInteger(m_chart_id,CHART_SCALE,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- read value of the "ScaleFix" property
   if(!ChartSetInteger(m_chart_id,CHART_SCALEFIX,FileReadInteger(file_handle,CHAR_VALUE)))
      return(false);
//--- read value of the "ScaleFix_11" property
   if(!ChartSetInteger(m_chart_id,CHART_SCALEFIX_11,FileReadInteger(file_handle,CHAR_VALUE)))
      return(false);
//--- read value of the "FixedMax" property
   if(!ChartSetDouble(m_chart_id,CHART_FIXED_MAX,FileReadDatetime(file_handle)))
      return(false);
//--- read value of the "FixedMin" property
   if(!ChartSetDouble(m_chart_id,CHART_FIXED_MIN,FileReadDatetime(file_handle)))
      return(false);
//--- read value of the "ScalePPB" property
   if(!ChartSetInteger(m_chart_id,CHART_SCALE_PT_PER_BAR,FileReadInteger(file_handle,CHAR_VALUE)))
      return(false);
//--- read value of the "PointsPerBar" property
   if(!ChartSetDouble(m_chart_id,CHART_POINTS_PER_BAR,FileReadDatetime(file_handle)))
      return(false);
//--- read value of the "ShowOHLC" property
   if(!ChartSetInteger(m_chart_id,CHART_SHOW_OHLC,FileReadInteger(file_handle,CHAR_VALUE)))
      return(false);
//--- read value of the "ShowLineBid" property
   if(!ChartSetInteger(m_chart_id,CHART_SHOW_BID_LINE,FileReadInteger(file_handle,CHAR_VALUE)))
      return(false);
//--- read value of the "ShowLineAsk" property
   if(!ChartSetInteger(m_chart_id,CHART_SHOW_ASK_LINE,FileReadInteger(file_handle,CHAR_VALUE)))
      return(false);
//--- read value of the "ShowLastLine" property
   if(!ChartSetInteger(m_chart_id,CHART_SHOW_LAST_LINE,FileReadInteger(file_handle,CHAR_VALUE)))
      return(false);
//--- read value of the "ShowPeriodSep" property
   if(!ChartSetInteger(m_chart_id,CHART_SHOW_PERIOD_SEP,FileReadInteger(file_handle,CHAR_VALUE)))
      return(false);
//--- read value of the "ShowGrid" property
   if(!ChartSetInteger(m_chart_id,CHART_SHOW_GRID,FileReadInteger(file_handle,CHAR_VALUE)))
      return(false);
//--- read value of the "ShowVolumes" property
   if(!ChartSetInteger(m_chart_id,CHART_SHOW_VOLUMES,FileReadInteger(file_handle,INT_VALUE)))
      return(false);
//--- read value of the "ShowObjectDescr" property
   if(!ChartSetInteger(m_chart_id,CHART_SHOW_OBJECT_DESCR,FileReadInteger(file_handle,CHAR_VALUE)))
      return(false);
//--- successful
   return(resutl);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Resources                                                        |
//+------------------------------------------------------------------+
#resource "\\Include\\Controls\\res\\Close.bmp"
#resource "\\Include\\Controls\\res\\Restore.bmp"
#resource "\\Include\\Controls\\res\\Turn.bmp"
//+------------------------------------------------------------------+
//| Class CDialog                                                    |
//| Usage: base class to create dialog boxes                         |
//|             and indicator panels                                 |
//+------------------------------------------------------------------+
class CDialog : public CWndContainer
  {
protected:
   //--- dependent controls
   CPanel            m_white_border;        // the "white border" object
   CPanel            m_background;          // the background object
   CEdit             m_caption;             // the window title object
   CBmpButton        m_button_close;        // the "Close" button object
   CWndClient        m_client_area;         // the client area object

protected:
   //--- flags
   bool              m_panel_flag;          // the "panel in a separate window" flag
   //--- flags
   bool              m_minimized;           // "create in minimized state" flag
   //--- additional areas
   CRect             m_min_rect;            // minimal area coordinates
   CRect             m_norm_rect;           // normal area coordinates

public:
                     CDialog(void);
                    ~CDialog(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- set up
   string            Caption(void)                   const { return(m_caption.Text());               }
   bool              Caption(const string text)            { return(m_caption.Text(text));           }
   //--- fill
   bool              Add(CWnd *control);
   bool              Add(CWnd &control);
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);

protected:
   //--- create dependent controls
   virtual bool      CreateWhiteBorder(void);
   virtual bool      CreateBackground(void);
   virtual bool      CreateCaption(void);
   virtual bool      CreateButtonClose(void);
   virtual bool      CreateClientArea(void);
   //--- handlers of the dependent controls events
   virtual void      OnClickCaption(void);
   virtual void      OnClickButtonClose(void);
   //--- access properties of caption
   void              CaptionAlignment(const int flags,const int left,const int top,const int right,const int bottom)
                        { m_caption.Alignment(flags,left,top,right,bottom); }
   //--- access properties of client area
   bool              ClientAreaVisible(const bool visible) { return(m_client_area.Visible(visible)); }
   int               ClientAreaLeft(void)            const { return(m_client_area.Left());           }
   int               ClientAreaTop(void)             const { return(m_client_area.Top());            }
   int               ClientAreaRight(void)           const { return(m_client_area.Right());          }
   int               ClientAreaBottom(void)          const { return(m_client_area.Bottom());         }
   int               ClientAreaWidth(void)           const { return(m_client_area.Width());          }
   int               ClientAreaHeight(void)          const { return(m_client_area.Height());         }
   //--- handlers of drag
   virtual bool      OnDialogDragStart(void);
   virtual bool      OnDialogDragProcess(void);
   virtual bool      OnDialogDragEnd(void);
  };
//+------------------------------------------------------------------+
//| Common handler of events                                         |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CDialog)
   ON_EVENT(ON_CLICK,m_button_close,OnClickButtonClose)
   ON_EVENT(ON_CLICK,m_caption,OnClickCaption)
   ON_EVENT(ON_DRAG_START,m_caption,OnDialogDragStart)
   ON_EVENT_PTR(ON_DRAG_PROCESS,m_drag_object,OnDialogDragProcess)
   ON_EVENT_PTR(ON_DRAG_END,m_drag_object,OnDialogDragEnd)
EVENT_MAP_END(CWndContainer)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CDialog::CDialog(void) : m_panel_flag(false),
                         m_minimized(false)
  {
    RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CDialog::~CDialog(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of parent class
   if(!CWndContainer::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create dependent controls
   if(!m_panel_flag && !CreateWhiteBorder())
      return(false);
   if(!CreateBackground())
      return(false);
   if(!CreateCaption())
      return(false);
   if(!CreateButtonClose())
      return(false);
   if(!CreateClientArea())
      return(false);
//--- set up additional areas
   m_norm_rect.SetBound(m_rect);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Add control to the client area (by pointer)                      |
//+------------------------------------------------------------------+
bool CDialog::Add(CWnd *control)
  {
   return(m_client_area.Add(control));
  }
//+------------------------------------------------------------------+
//| Add control to the client area (by reference)                    |
//+------------------------------------------------------------------+
bool CDialog::Add(CWnd &control)
  {
   return(m_client_area.Add(control));
  }
//+------------------------------------------------------------------+
//| Save                                                             |
//+------------------------------------------------------------------+
bool CDialog::Save(const int file_handle)
  {
//--- check
   if(file_handle==INVALID_HANDLE)
      return(false);
//--- save
   FileWriteStruct(file_handle,m_norm_rect);
   FileWriteInteger(file_handle,m_min_rect.left);
   FileWriteInteger(file_handle,m_min_rect.top);
   FileWriteInteger(file_handle,m_minimized);
//--- result
   return(CWndContainer::Save(file_handle));
  }
//+------------------------------------------------------------------+
//| Load                                                             |
//+------------------------------------------------------------------+
bool CDialog::Load(const int file_handle)
  {
   if(file_handle==INVALID_HANDLE)
      return(false);
//--- load
   if(!FileIsEnding(file_handle))
     {
      FileReadStruct(file_handle,m_norm_rect);
      int left=FileReadInteger(file_handle);
      int top=FileReadInteger(file_handle);
      m_min_rect.Move(left,top);
      m_minimized=FileReadInteger(file_handle);
     }
//--- result
   return(CWndContainer::Load(file_handle));
  }
//+------------------------------------------------------------------+
//| Create "white border"                                            |
//+------------------------------------------------------------------+
bool CDialog::CreateWhiteBorder(void)
  {
//--- coordinates
   int x1=0;
   int y1=0;
   int x2=Width();
   int y2=Height();
//--- create
   if(!m_white_border.Create(m_chart_id,m_name+"Border",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_white_border.ColorBackground(CONTROLS_DIALOG_COLOR_BG))
      return(false);
   if(!m_white_border.ColorBorder(CONTROLS_DIALOG_COLOR_BORDER_LIGHT))
      return(false);
   if(!CWndContainer::Add(m_white_border))
      return(false);
   m_white_border.Alignment(WND_ALIGN_CLIENT,0,0,0,0);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create background                                                |
//+------------------------------------------------------------------+
bool CDialog::CreateBackground(void)
  {
   int off=(m_panel_flag) ? 0:CONTROLS_BORDER_WIDTH;
//--- coordinates
   int x1=off;
   int y1=off;
   int x2=Width()-off;
   int y2=Height()-off;
//--- create
   if(!m_background.Create(m_chart_id,m_name+"Back",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_background.ColorBackground(CONTROLS_DIALOG_COLOR_BG))
      return(false);
   color border=(m_panel_flag) ? CONTROLS_DIALOG_COLOR_BG : CONTROLS_DIALOG_COLOR_BORDER_DARK;
   if(!m_background.ColorBorder(border))
      return(false);
   if(!CWndContainer::Add(m_background))
      return(false);
   m_background.Alignment(WND_ALIGN_CLIENT,off,off,off,off);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create window title                                              |
//+------------------------------------------------------------------+
bool CDialog::CreateCaption(void)
  {
   int off=(m_panel_flag) ? 0:2*CONTROLS_BORDER_WIDTH;
//--- coordinates
   int x1=off;
   int y1=off;
   int x2=Width()-off;
   int y2=y1+CONTROLS_DIALOG_CAPTION_HEIGHT;
//--- create
   if(!m_caption.Create(m_chart_id,m_name+"Caption",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_caption.Color(CONTROLS_DIALOG_COLOR_CAPTION_TEXT))
      return(false);
   if(!m_caption.ColorBackground(CONTROLS_DIALOG_COLOR_BG))
      return(false);
   if(!m_caption.ColorBorder(CONTROLS_DIALOG_COLOR_BG))
      return(false);
   if(!m_caption.ReadOnly(true))
      return(false);
   if(!m_caption.Text(m_name))
      return(false);
   if(!CWndContainer::Add(m_caption))
      return(false);
   m_caption.Alignment(WND_ALIGN_WIDTH,off,0,off,0);
   if(!m_panel_flag)
      m_caption.PropFlags(WND_PROP_FLAG_CAN_DRAG);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create the "Close" button                                        |
//+------------------------------------------------------------------+
bool CDialog::CreateButtonClose(void)
  {
   int off=(m_panel_flag) ? 0 : 2*CONTROLS_BORDER_WIDTH;
//--- coordinates
   int x1=Width()-off-(CONTROLS_BUTTON_SIZE+CONTROLS_DIALOG_BUTTON_OFF);
   int y1=off+CONTROLS_DIALOG_BUTTON_OFF;
   int x2=x1+CONTROLS_BUTTON_SIZE;
   int y2=y1+CONTROLS_BUTTON_SIZE;
//--- create
   if(!m_button_close.Create(m_chart_id,m_name+"Close",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_button_close.BmpNames("::Include\\Controls\\res\\Close.bmp"))
      return(false);
   if(!CWndContainer::Add(m_button_close))
      return(false);
   m_button_close.Alignment(WND_ALIGN_RIGHT,0,0,off+CONTROLS_DIALOG_BUTTON_OFF,0);
//--- change caption
   CaptionAlignment(WND_ALIGN_WIDTH,off,0,off+(CONTROLS_BUTTON_SIZE+CONTROLS_DIALOG_BUTTON_OFF),0);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create client area                                               |
//+------------------------------------------------------------------+
bool CDialog::CreateClientArea(void)
  {
   int off=(m_panel_flag) ? 0:2*CONTROLS_BORDER_WIDTH;
//--- coordinates
   int x1=off+CONTROLS_DIALOG_CLIENT_OFF;
   int y1=off+CONTROLS_DIALOG_CAPTION_HEIGHT;
   int x2=Width()-(off+CONTROLS_DIALOG_CLIENT_OFF);
   int y2=Height()-(off+CONTROLS_DIALOG_CLIENT_OFF);
//--- create
   if(!m_client_area.Create(m_chart_id,m_name+"Client",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_client_area.ColorBackground(CONTROLS_DIALOG_COLOR_CLIENT_BG))
      return(false);
   if(!m_client_area.ColorBorder(CONTROLS_DIALOG_COLOR_CLIENT_BORDER))
      return(false);
   CWndContainer::Add(m_client_area);
   m_client_area.Alignment(WND_ALIGN_CLIENT,x1,y1,x1,x1);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of click on the window title                             |
//+------------------------------------------------------------------+
void CDialog::OnClickCaption(void)
  {
  }
//+------------------------------------------------------------------+
//| Handler of click on the "Close" button                           |
//+------------------------------------------------------------------+
void CDialog::OnClickButtonClose(void)
  {
   Visible(false);
  }
//+------------------------------------------------------------------+
//| Start dragging the dialog box                                    |
//+------------------------------------------------------------------+
bool CDialog::OnDialogDragStart(void)
  {
   if(m_drag_object==NULL)
     {
      m_drag_object=new CDragWnd;
      if(m_drag_object==NULL)
         return(false);
     }
//--- calculate coordinates
   int x1=Left()-CONTROLS_DRAG_SPACING;
   int y1=Top()-CONTROLS_DRAG_SPACING;
   int x2=Right()+CONTROLS_DRAG_SPACING;
   int y2=Bottom()+CONTROLS_DRAG_SPACING;
//--- create
   m_drag_object.Create(m_chart_id,"",m_subwin,x1,y1,x2,y2);
   m_drag_object.PropFlags(WND_PROP_FLAG_CAN_DRAG);
//--- constraints
   CChart chart;
   chart.Attach(m_chart_id);
   m_drag_object.Limits(-CONTROLS_DRAG_SPACING,-CONTROLS_DRAG_SPACING,
                        chart.WidthInPixels()+CONTROLS_DRAG_SPACING,
                        chart.HeightInPixels(m_subwin)+CONTROLS_DRAG_SPACING);
   chart.Detach();
//--- set mouse params
   m_drag_object.MouseX(m_caption.MouseX());
   m_drag_object.MouseY(m_caption.MouseY());
   m_drag_object.MouseFlags(m_caption.MouseFlags());
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Continue dragging the dialog box                                 |
//+------------------------------------------------------------------+
bool CDialog::OnDialogDragProcess(void)
  {
//--- checking
   if(m_drag_object==NULL)
      return(false);
//--- calculate coordinates
   int x=m_drag_object.Left()+50;
   int y=m_drag_object.Top()+50;
//--- move dialog
   Move(x,y);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| End dragging the dialog box                                      |
//+------------------------------------------------------------------+
bool CDialog::OnDialogDragEnd(void)
  {
   if(m_drag_object!=NULL)
     {
      m_caption.MouseFlags(m_drag_object.MouseFlags());
      delete m_drag_object;
      m_drag_object=NULL;
     }
//--- set up additional areas
   if(m_minimized)
      m_min_rect.SetBound(m_rect);
   else
      m_norm_rect.SetBound(m_rect);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Class CAppDialog                                                 |
//| Usage: main dialog box of MQL5 application                       |
//+------------------------------------------------------------------+
class CAppDialog : public CDialog
  {
protected:
   //--- dependent controls
   CBmpButton        m_button_minmax;       // the "Minimize/Maximize" button object
   //--- variables
   string            m_program_name;        // name of program
   string            m_instance_id;         // unique string ID
   ENUM_PROGRAM_TYPE m_program_type;        // type of program
   string            m_indicator_name;
   int               m_deinit_reason;
   //--- for mouse
   int               m_subwin_Yoff;         // subwindow Y offset
   CWnd*             m_focused_wnd;         // pointer to object that has mouse focus
   CWnd*             m_top_wnd;             // pointer to object that has priority over mouse events handling

protected:
   CChart            m_chart;               // object to access chart

public:
                     CAppDialog(void);
                    ~CAppDialog(void);
   //--- main application dialog creation and destroy
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   virtual void      Destroy(const int reason=REASON_PROGRAM);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- dialog run
   virtual bool      Run(void);
   //--- chart events processing
   void              ChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- set up
   void              Minimized(const bool flag) { m_minimized=flag; }
   //--- to save/restore state
   void              IniFileSave(void);
   void              IniFileLoad(void);
   virtual string    IniFileName(void) const;
   virtual string    IniFileExt(void) const { return(".dat"); }
   virtual bool      Load(const int file_handle);
   virtual bool      Save(const int file_handle);
   int               GetInstanceId() const { return (int)StringToInteger(m_instance_id); }

private:
   bool              CreateCommon(const long chart,const string name,const int subwin);
   bool              CreateExpert(const int x1,const int y1,const int x2,const int y2);
   bool              CreateIndicator(const int x1,const int y1,const int x2,const int y2);

protected:
   //--- create dependent controls
   virtual bool      CreateButtonMinMax(void);
   //--- handlers of the dependent controls events
   virtual void      OnClickButtonClose(void);
   virtual void      OnClickButtonMinMax(void);
   //--- external event handlers
   virtual void      OnAnotherApplicationClose(const long &lparam,const double &dparam,const string &sparam);
   //--- methods
   virtual bool      Rebound(const CRect &rect);
   virtual void      Minimize(void);
   virtual void      Maximize(void);
   string            CreateInstanceId(void);
   string            ProgramName(void) const { return(m_program_name); }
   void              SubwinOff(void);
   int               GetIntegralHeight(void);
  };
//+------------------------------------------------------------------+
//| Common handler of events                                         |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CAppDialog)
ON_EVENT(ON_CLICK,m_button_minmax,OnClickButtonMinMax)
ON_EXTERNAL_EVENT(ON_APP_CLOSE,OnAnotherApplicationClose)
EVENT_MAP_END(CDialog)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CAppDialog::CAppDialog(void) : m_program_type(WRONG_VALUE),
                               m_deinit_reason(WRONG_VALUE),
                               m_subwin_Yoff(0),
                               m_focused_wnd(NULL),
                               m_top_wnd(NULL)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CAppDialog::~CAppDialog(void)
  {
  }
//+------------------------------------------------------------------+
//| Application dialog initialization function                       |
//+------------------------------------------------------------------+
bool CAppDialog::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
   if(!CreateCommon(chart,name,subwin))
      return(false);
//---
   switch(m_program_type)
     {
      case PROGRAM_EXPERT:
         if(!CreateExpert(x1,y1,x2,y2))
         return(false);
         break;
      case PROGRAM_INDICATOR:
         if(!CreateIndicator(x1,y1,x2,y2))
         return(false);
         break;
      default:
         Print("CAppDialog: invalid program type");
         return(false);
     }
//--- Title of dialog window
   if(!Caption(m_program_name))
      return(false);
//--- create dependent controls
   if(!CreateButtonMinMax())
      return(false);
//--- get subwindow offset
   SubwinOff();
//--- if flag is set, minimize the dialog
   if(m_minimized)
      Minimize();
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialize common area                                           |
//+------------------------------------------------------------------+
bool CAppDialog::CreateCommon(const long chart,const string name,const int subwin)
  {
//--- save parameters
   m_chart_id     =chart;
   m_name         =name;
   m_subwin       =subwin;
   m_program_name =name;
   m_deinit_reason=WRONG_VALUE;
//--- get unique ID
   m_instance_id=CreateInstanceId();
//--- initialize chart object
   m_chart.Attach(chart);
//--- determine type of program
   m_program_type=(ENUM_PROGRAM_TYPE)MQL5InfoInteger(MQL5_PROGRAM_TYPE);
//--- specify object and mouse events
   if(!m_chart.EventObjectCreate() || !m_chart.EventObjectDelete() || !m_chart.EventMouseMove())
     {
      Print("CAppDialog: object events specify error");
      m_chart.Detach();
      return(false);
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialize in Expert Advisor                                     |
//+------------------------------------------------------------------+
bool CAppDialog::CreateExpert(const int x1,const int y1,const int x2,const int y2)
  {
//--- EA works only in main window
   m_subwin=0;
//--- geometry for the minimized state
   m_min_rect.SetBound(CONTROLS_DIALOG_MINIMIZE_LEFT,
                       CONTROLS_DIALOG_MINIMIZE_TOP,
                       CONTROLS_DIALOG_MINIMIZE_LEFT+CONTROLS_DIALOG_MINIMIZE_WIDTH,
                       CONTROLS_DIALOG_MINIMIZE_TOP+CONTROLS_DIALOG_MINIMIZE_HEIGHT);
//--- call method of the parent class
   if(!CDialog::Create(m_chart.ChartId(),m_instance_id,m_subwin,x1,y1,x2,y2))
     {
      Print("CAppDialog: expert dialog create error");
      m_chart.Detach();
      return(false);
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Initialize in Indicator                                          |
//+------------------------------------------------------------------+
bool CAppDialog::CreateIndicator(const int x1,const int y1,const int x2,const int y2)
  {
   int width=m_chart.WidthInPixels();
//--- geometry for the minimized state
   m_min_rect.LeftTop(0,0);
   m_min_rect.Width(width);
   m_min_rect.Height(CONTROLS_DIALOG_MINIMIZE_HEIGHT-2*CONTROLS_BORDER_WIDTH);
//--- determine subwindow
   m_subwin=ChartWindowFind();
   if(m_subwin==-1)
     {
      Print("CAppDialog: find subwindow error");
      m_chart.Detach();
      return(false);
     }
//---
   int total=ChartIndicatorsTotal(m_chart.ChartId(),m_subwin);
   m_indicator_name=ChartIndicatorName(m_chart.ChartId(),m_subwin,total-1);
//--- if subwindow number is 0 (main window), then our program is
//--- not an indicator panel, but is an indicator with built-in settings dialog
//--- dialog of such an indicator should behave as an Expert Advisor dialog
   if(m_subwin==0)
      return(CreateExpert(x1,y1,x2,y2));
//--- if subwindow number is not 0, then our program is an indicator panel
//--- check if subwindow is not occupied by other indicators
   if(total!=1)
     {
      Print("CAppDialog: subwindow busy");
      ChartIndicatorDelete(m_chart.ChartId(),m_subwin,ChartIndicatorName(m_chart.ChartId(),m_subwin,total-1));
      m_chart.Detach();
      return(false);
     }
//--- resize subwindow by dialog height
   if(!IndicatorSetInteger(INDICATOR_HEIGHT,(y2-y1)+1))
     {
      Print("CAppDialog: subwindow resize error");
      ChartIndicatorDelete(m_chart.ChartId(),m_subwin,ChartIndicatorName(m_chart.ChartId(),m_subwin,total-1));
      m_chart.Detach();
      return(false);
     }
//--- indicator short name
   m_indicator_name=m_program_name+IntegerToString(m_subwin);
   if(!IndicatorSetString(INDICATOR_SHORTNAME,m_indicator_name))
     {
      Print("CAppDialog: shortname error");
      ChartIndicatorDelete(m_chart.ChartId(),m_subwin,ChartIndicatorName(m_chart.ChartId(),m_subwin,total-1));
      m_chart.Detach();
      return(false);
     }
//--- set flag 
   m_panel_flag=true;
//--- call method of the parent class
   if(!CDialog::Create(m_chart.ChartId(),m_instance_id,m_subwin,0,0,width,y2-y1))
     {
      Print("CAppDialog: indicator dialog create error");
      ChartIndicatorDelete(m_chart.ChartId(),m_subwin,ChartIndicatorName(m_chart.ChartId(),m_subwin,total-1));
      m_chart.Detach();
      return(false);
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Application dialog deinitialization function                     |
//+------------------------------------------------------------------+
void CAppDialog::Destroy(const int reason)
  {
//--- destroyed already?
   if(m_deinit_reason!=WRONG_VALUE)
      return;
//---
   m_deinit_reason=reason;
   IniFileSave();
//--- detach chart object from chart
   m_chart.Detach();
//--- call parent destroy
   CDialog::Destroy();
//---
   if(reason==REASON_PROGRAM)
     {
      if(m_program_type==PROGRAM_EXPERT)
         ExpertRemove();
      if(m_program_type==PROGRAM_INDICATOR)
         ChartIndicatorDelete(m_chart_id,m_subwin,m_indicator_name);
     }
//--- send message
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_APP_CLOSE,m_subwin,0.0,m_program_name);
  }
//+------------------------------------------------------------------+
//| Calculate subwindow offset                                       |
//+------------------------------------------------------------------+
void CAppDialog::SubwinOff(void)
  {
   m_subwin_Yoff=m_chart.SubwindowY(m_subwin);
  }
//+------------------------------------------------------------------+
//| Create the "Minimize/Maximize" button                            |
//+------------------------------------------------------------------+
bool CAppDialog::CreateButtonMinMax(void)
  {
   int off=(m_panel_flag) ? 0:2*CONTROLS_BORDER_WIDTH;
//--- coordinates
   int x1=Width()-off-2*(CONTROLS_BUTTON_SIZE+CONTROLS_DIALOG_BUTTON_OFF);
   int y1=off+CONTROLS_DIALOG_BUTTON_OFF;
   int x2=x1+CONTROLS_BUTTON_SIZE;
   int y2=y1+CONTROLS_BUTTON_SIZE;
//--- create
   if(!m_button_minmax.Create(m_chart_id,m_name+"MinMax",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_button_minmax.BmpNames("::Include\\Controls\\res\\Turn.bmp","::Include\\Controls\\res\\Restore.bmp"))
      return(false);
   if(!CWndContainer::Add(m_button_minmax))
      return(false);
   m_button_minmax.Locking(true);
   m_button_minmax.Alignment(WND_ALIGN_RIGHT,0,0,off+CONTROLS_BUTTON_SIZE+2*CONTROLS_DIALOG_BUTTON_OFF,0);
//--- change caption
   CaptionAlignment(WND_ALIGN_WIDTH,off,0,off+2*(CONTROLS_BUTTON_SIZE+CONTROLS_DIALOG_BUTTON_OFF),0);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Charts event processing                                          |
//+------------------------------------------------------------------+
void CAppDialog::ChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
   int mouse_x=(int)lparam;
   int mouse_y=(int)dparam-m_subwin_Yoff;
//--- separate mouse events from others
   switch(id)
     {
      case CHARTEVENT_CHART_CHANGE:
         //--- assumed that the CHARTEVENT_CHART_CHANGE event can handle only the application dialog
         break;
      case CHARTEVENT_OBJECT_CLICK:
         //--- we won't handle the CHARTEVENT_OBJECT_CLICK event, as we are working with the CHARTEVENT_MOUSE_MOVE events
         return;
      case CHARTEVENT_CUSTOM+ON_MOUSE_FOCUS_SET:
         //--- the CHARTEVENT_CUSTOM + ON_MOUSE_FOCUS_SET event
         if(CheckPointer(m_focused_wnd)!=POINTER_INVALID)
           {
            //--- if there is an element with focus, try to take its focus away
            if(!m_focused_wnd.MouseFocusKill(lparam))
               return;
           }
         m_focused_wnd=ControlFind(lparam);
         return;
      case CHARTEVENT_CUSTOM+ON_BRING_TO_TOP:
         m_top_wnd=ControlFind(lparam);
         return;
      case CHARTEVENT_MOUSE_MOVE:
         //--- the CHARTEVENT_MOUSE_MOVE event
         if(CheckPointer(m_top_wnd)!=POINTER_INVALID)
           {
            //--- if a priority element already exists, pass control to it
            if(m_top_wnd.OnMouseEvent(mouse_x,mouse_y,(int)StringToInteger(sparam)))
              {
               //--- event handled
               m_chart.Redraw();
               return;
              }
           }
         if(OnMouseEvent(mouse_x,mouse_y,(int)StringToInteger(sparam)))
            m_chart.Redraw();
         return;
      default:
         //--- call event processing and redraw chart if event handled
         if(OnEvent(id,lparam,dparam,sparam))
         m_chart.Redraw();
         return;
     }
//--- if event was not handled, try to handle the CHARTEVENT_CHART_CHANGE event
   if(id==CHARTEVENT_CHART_CHANGE)
     {
      //--- if subwindow number is not 0, and dialog subwindow has changed its number, then restart
      if(m_subwin!=0 && m_subwin!=ChartWindowFind())
        {
         long fiction=1;
         OnAnotherApplicationClose(fiction,dparam,sparam);
        }
      //--- if subwindow height is less that dialog height, minimize application window (always)
      if(MathMax(m_chart.HeightInPixels(m_subwin), GetIntegralHeight())<Height()+CONTROLS_BORDER_WIDTH)
        {
         m_button_minmax.Pressed(true);
         Minimize();
         m_chart.Redraw();
        }
      //--- if chart width is less that dialog width, and subwindow number is not 0, try to modify dialog width
      if(m_chart.WidthInPixels()!=Width() && m_subwin!=0)
        {
         Width(m_chart.WidthInPixels());
         m_chart.Redraw();
        }
      //--- get subwindow offset
      SubwinOff();
      return;
     }
  }

int CAppDialog::GetIntegralHeight()
{
  long id = ChartFirst();
  int height = 0;
  bool maximized = false;
  do
  {
    int h = (int)ChartGetInteger(id, CHART_HEIGHT_IN_PIXELS);
    height = MathMax(h, height);
    maximized |= ChartGetInteger(id, CHART_IS_MAXIMIZED);
    id = ChartNext(id);
  } while (id != -1);
  
  if(maximized) return height;
  
  return 0;
}
//+------------------------------------------------------------------+
//| Run application                                                  |
//+------------------------------------------------------------------+
bool CAppDialog::Run(void)
{
  // redraw chart for dialog invalidate
  m_chart.Redraw();
  // here we begin to assign IDs to controls
  if(Id(StringToInteger(m_instance_id) + m_subwin * CONTROLS_MAXIMUM_ID) > CONTROLS_MAXIMUM_ID)
  {
    Print("CAppDialog: too many objects");
    return(false);
  }
  // succeed
  return(true);
}
//+------------------------------------------------------------------+
//| Stop application                                                 |
//+------------------------------------------------------------------+
void CAppDialog::OnClickButtonClose(void)
  {
//--- destroy application
   Destroy();
  }
//+------------------------------------------------------------------+
//| Handler of click on the "Minimize/Maximize" button               |
//+------------------------------------------------------------------+
void CAppDialog::OnClickButtonMinMax(void)
  {
   if(m_button_minmax.Pressed())
      Minimize();
   else
      Maximize();
//--- get subwindow offset
   SubwinOff();
  }
//+------------------------------------------------------------------+
//| Resize                                                           |
//+------------------------------------------------------------------+
bool CAppDialog::Rebound(const CRect &rect)
  {
   if(!Move(rect.LeftTop()))
      return(false);
   if(!Size(rect.Size()))
      return(false);
//--- resize subwindow
   if(m_program_type==PROGRAM_INDICATOR && !IndicatorSetInteger(INDICATOR_HEIGHT,rect.Height()+1))
     {
      Print("CAppDialog: subwindow resize error");
      return(false);
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Minimize dialog window                                           |
//+------------------------------------------------------------------+
void CAppDialog::Minimize(void)
  {
//--- set flag
   m_minimized=true;
//--- resize
   Rebound(m_min_rect);
//--- hide client area
   ClientAreaVisible(false);
  }
//+------------------------------------------------------------------+
//| Restore dialog window                                            |
//+------------------------------------------------------------------+
void CAppDialog::Maximize(void)
  {
//--- reset flag
   m_minimized=false;
//--- resize
   Rebound(m_norm_rect);
//--- show client area
   ClientAreaVisible(true);
  }
//+------------------------------------------------------------------+
//| Create unique prefix for object names                            |
//+------------------------------------------------------------------+
string CAppDialog::CreateInstanceId(void)
  {
   return(IntegerToString(rand(),5,'0'));
  }
//+------------------------------------------------------------------+
//| Handler of the ON_APP_CLOSE external event                       |
//+------------------------------------------------------------------+
void CAppDialog::OnAnotherApplicationClose(const long &lparam,const double &dparam,const string &sparam)
  {
//--- exit if we are in the main window
   if(m_subwin==0)
      return;
//--- exit if external program was closed in main window
   if(lparam==0)
      return;
//--- get subwindow offset
   SubwinOff();
//--- exit if external program was closed in subwindow with greater number
   if(lparam>=m_subwin)
      return;
//--- after all the checks we must change the subwindow
//--- get the new number of subwindow
   m_subwin=ChartWindowFind();
//--- change short name
   m_indicator_name=m_program_name+IntegerToString(m_subwin);
   IndicatorSetString(INDICATOR_SHORTNAME,m_indicator_name);
//--- change dialog title
   Caption(m_program_name);
//--- reassign IDs
   Run();
  }
//+------------------------------------------------------------------+
//| Save the current state of the program                            |
//+------------------------------------------------------------------+
void CAppDialog::IniFileSave(void)
  {
   string filename=IniFileName()+IniFileExt();
   int handle=FileOpen(filename,FILE_WRITE|FILE_BIN|FILE_ANSI);
//---
   if(handle!=INVALID_HANDLE)
     {
      Save(handle);
      FileClose(handle);
     }
  }
//+------------------------------------------------------------------+
//| Read the previous state of the program                           |
//+------------------------------------------------------------------+
void CAppDialog::IniFileLoad(void)
  {
   string filename=IniFileName()+IniFileExt();
   int handle=FileOpen(filename,FILE_READ|FILE_BIN|FILE_ANSI);
//---
   if(handle!=INVALID_HANDLE)
     {
      Load(handle);
      FileClose(handle);
     }
  }
//+------------------------------------------------------------------+
//| Generate the filename                                            |
//+------------------------------------------------------------------+
string CAppDialog::IniFileName(void) const
  {
   string name;
//---
   name=(m_indicator_name!=NULL) ? m_indicator_name : m_program_name;
//---
   name+="_"+Symbol();
   name+="_Ini";
//---
   return(name);
  }
//+------------------------------------------------------------------+
//| Load data                                                        |
//+------------------------------------------------------------------+
bool CAppDialog::Load(const int file_handle)
  {
   if(CDialog::Load(file_handle))
     {
      if(m_minimized)
        {
         m_button_minmax.Pressed(true);
         Minimize();
        }
      else
        {
         m_button_minmax.Pressed(false);
         Maximize();
        }
      int prev_deinit_reason=FileReadInteger(file_handle);
      if(prev_deinit_reason==REASON_CHARTCLOSE || prev_deinit_reason==REASON_CLOSE)
        {
         //--- if the previous time program ended after closing the chart window,
         //--- delete object left since the last start of the program
         string prev_instance_id=IntegerToString(FileReadInteger(file_handle),5,'0');
         if(prev_instance_id!=m_instance_id)
           {
            long chart_id=m_chart.ChartId();
            int  total=ObjectsTotal(chart_id,m_subwin);
            for(int i=total-1;i>=0;i--)
              {
               string obj_name=ObjectName(chart_id,i,m_subwin);
               if(StringFind(obj_name,prev_instance_id)==0)
                  ObjectDelete(chart_id,obj_name);
              }
           }
        }
      return(true);
     }
//--- failure
   return(false);
  }
//+------------------------------------------------------------------+
//| Save data                                                        |
//+------------------------------------------------------------------+
bool CAppDialog::Save(const int file_handle)
  {
   if(CDialog::Save(file_handle))
     {
      FileWriteInteger(file_handle,m_deinit_reason);
      FileWriteInteger(file_handle,(int)StringToInteger(m_instance_id));
      return(true);
     }
//--- failure
   return(false);
  }
//+------------------------------------------------------------------+
//#include <ControlsPlus/Button.mqh>
//+------------------------------------------------------------------+
//|                                                       Button.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "WndObj.mqh"
//#include <ChartObjects\ChartObjectsTxtControls.mqh>
//+------------------------------------------------------------------+
//| Class CButton                                                    |
//| Usage: control that is displayed by                              |
//|             the CChartObjectButton object                        |
//+------------------------------------------------------------------+
class CButton : public CWndObj
  {
protected:
   CChartObjectButton m_button;             // chart object
   bool m_capture;

public:
                     CButton(void);
                    ~CButton(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- state
   bool              Pressed(void)          const { return(m_button.State());                       }
   bool              Pressed(const bool pressed)  { return(m_button.State(pressed));                }
   //--- properties
   bool              Locking(void)          const { return(IS_CAN_LOCK);                            }
   void              Locking(const bool flag);

protected:
   //--- handlers of object settings
   virtual bool      OnSetText(void)              { return(m_button.Description(m_text));           }
   virtual bool      OnSetColor(void)             { return(m_button.Color(m_color));                }
   virtual bool      OnSetColorBackground(void)   { return(m_button.BackColor(m_color_background)); }
   virtual bool      OnSetColorBorder(void)       { return(m_button.BorderColor(m_color_border));   }
   virtual bool      OnSetFont(void)              { return(m_button.Font(m_font));                  }
   virtual bool      OnSetFontSize(void)          { return(m_button.FontSize(m_font_size));         }
   //--- internal event handlers
   virtual bool      OnCreate(void);
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnMove(void);
   virtual bool      OnResize(void);
   
   virtual bool      OnMouseDown(void);
   virtual bool      OnMouseUp(void);
   virtual bool      OnEnable(void) override;
   virtual bool      OnDisable(void) override;
   virtual bool      OnMouseEvent(const int x, const int y, const int flags) override;
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CButton::CButton(void)
  {
   m_color           =CONTROLS_BUTTON_COLOR;
   m_color_background=CONTROLS_BUTTON_COLOR_BG;
   m_color_border    =CONTROLS_BUTTON_COLOR_BORDER;
  RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CButton::~CButton(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CButton::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndObj::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create the chart object
   if(!m_button.Create(chart,name,subwin,x1,y1,Width(),Height()))
      return(false);
//--- call the settings handler
   return(OnChange());
  }
//+------------------------------------------------------------------+
//| Locking flag                                                     |
//+------------------------------------------------------------------+
void CButton::Locking(const bool flag)
  {
   if(flag)
      PropFlagsSet(WND_PROP_FLAG_CAN_LOCK);
   else
      PropFlagsReset(WND_PROP_FLAG_CAN_LOCK);
  }
//+------------------------------------------------------------------+
//| Create object on chart                                           |
//+------------------------------------------------------------------+
bool CButton::OnCreate(void)
  {
//--- create the chart object by previously set parameters
   return(m_button.Create(m_chart_id,m_name,m_subwin,m_rect.left,m_rect.top,m_rect.Width(),m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Display object on chart                                          |
//+------------------------------------------------------------------+
bool CButton::OnShow(void)
  {
   return(m_button.Timeframes(OBJ_ALL_PERIODS));
  }
//+------------------------------------------------------------------+
//| Hide object from chart                                           |
//+------------------------------------------------------------------+
bool CButton::OnHide(void)
  {
   return(m_button.Timeframes(OBJ_NO_PERIODS));
  }
//+------------------------------------------------------------------+
//| Absolute movement of the chart object                            |
//+------------------------------------------------------------------+
bool CButton::OnMove(void)
  {
//--- position the chart object
   return(m_button.X_Distance(m_rect.left) && m_button.Y_Distance(m_rect.top));
  }
//+------------------------------------------------------------------+
//| Resize the chart object                                          |
//+------------------------------------------------------------------+
bool CButton::OnResize(void)
  {
//--- resize the chart object
   return(m_button.X_Size(m_rect.Width()) && m_button.Y_Size(m_rect.Height()));
  }
//+------------------------------------------------------------------+
//| Handler of click on the left mouse button                        |
//+------------------------------------------------------------------+
bool CButton::OnMouseDown(void)
  {
   if(!IS_CAN_LOCK)
      Pressed(!Pressed());
//--- call of the method of the parent class
   return(CWnd::OnMouseDown());
  }
//+------------------------------------------------------------------+
//| Handler of click on the left mouse button                        |
//+------------------------------------------------------------------+
bool CButton::OnMouseUp(void)
  {
//--- depress the button if it is not fixed
   if(m_button.State() && !IS_CAN_LOCK)
      m_button.State(false);
//--- call of the method of the parent class
   return(CWnd::OnMouseUp());
  }
//+------------------------------------------------------------------+
bool CButton::OnEnable(void)
{
  m_button.Z_Order(0);
  m_color = CONTROLS_BUTTON_COLOR;
  m_button.Color(m_color);
  return(true);
}

bool CButton::OnDisable(void)
{
  m_button.Z_Order(-100);
  m_color = CONTROLS_BUTTON_COLOR_BORDER;
  m_button.Color(m_color);
  return(true);
}

bool CButton::OnMouseEvent(const int x, const int y, const int flags) override
{
  if(Contains(x, y))
  {
    m_capture = (flags & MOUSE_LEFT) != 0;
  }
  else
  {
    if(m_capture && !IS_CAN_LOCK && Pressed())
    {
      Pressed(false);
    }
    m_capture = false;
  }
  return CWndObj::OnMouseEvent(x, y, flags);
}
//#include <ControlsPlus/Edit.mqh>
//#include <ControlsPlus/CheckBox.mqh>
//+------------------------------------------------------------------+
//|                                                     CheckBox.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "WndContainer.mqh"
//#include "BmpButton.mqh"
//#include "Edit.mqh"
//+------------------------------------------------------------------+
//| Resources                                                        |
//+------------------------------------------------------------------+
#resource "\\Include\\Controls\\res\\CheckBoxOn.bmp"
#resource "\\Include\\Controls\\res\\CheckBoxOff.bmp"
//+------------------------------------------------------------------+
//| Class CCheckBox                                                  |
//| Usage: class that implements the "CheckBox" control              |
//+------------------------------------------------------------------+
class CCheckBox : public CWndContainer
  {
private:
   //--- dependent controls
   CBmpButton        m_button;              // button object
   CEdit             m_label;               // label object
   //--- data
   int               m_value;               // value

public:
                     CCheckBox(void);
                    ~CCheckBox(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- settings
   string            Text(void)          const { return(m_label.Text());         }
   bool              Text(const string value)  { return(m_label.Text(value));    }
   color             Color(void)         const { return(m_label.Color());        }
   bool              Color(const color value)  { return(m_label.Color(value));   }
   //--- state
   bool              Checked(void)       const { return(m_button.Pressed());     }
   bool              Checked(const bool flag)  { return(m_button.Pressed(flag)); }
   //--- data
   int               Value(void)         const { return(m_value);                }
   void              Value(const int value)    { m_value=value;                  }
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);

protected:
   //--- create dependent controls
   virtual bool      CreateButton(void);
   virtual bool      CreateLabel(void);
   //--- handlers of the dependent controls events
   virtual bool      OnClickButton(void);
   virtual bool      OnClickLabel(void);

   // we can't do this with new subclass, because CheckGroup instantiates CheckBox'es
   // automatically via ArrayResize of object array (not pointer array),
   // so it's impossible to store a derived class pointer there
   virtual bool      OnResize(void) override;
  };
//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CCheckBox)
   ON_EVENT(ON_CLICK,m_button,OnClickButton)
   ON_EVENT(ON_CLICK,m_label,OnClickLabel)
EVENT_MAP_END(CWndContainer)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CCheckBox::CCheckBox(void) : m_value(0)
{
  RTTI;
}
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CCheckBox::~CCheckBox(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CCheckBox::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndContainer::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create dependent controls
   if(!CreateButton())
      return(false);
   if(!CreateLabel())
      return(false);
//--- succeeded
   return(true);
  }
//+------------------------------------------------------------------+
//| Create button                                                    |
//+------------------------------------------------------------------+
bool CCheckBox::CreateButton(void)
  {
//--- calculate coordinates
   int x1=CONTROLS_CHECK_BUTTON_X_OFF;
   int y1=CONTROLS_CHECK_BUTTON_Y_OFF;
   int x2=x1+CONTROLS_BUTTON_SIZE;
   int y2=y1+CONTROLS_BUTTON_SIZE-CONTROLS_BORDER_WIDTH;
//--- create
   if(!m_button.Create(m_chart_id,m_name+"Button",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_button.BmpNames("::Include\\Controls\\res\\CheckBoxOff.bmp","::Include\\Controls\\res\\CheckBoxOn.bmp"))
      return(false);
   if(!Add(m_button))
      return(false);
   m_button.Locking(true);
//--- succeeded
   return(true);
  }
//+------------------------------------------------------------------+
//| Create label                                                     |
//+------------------------------------------------------------------+
bool CCheckBox::CreateLabel(void)
  {
//--- calculate coordinates
   int x1=CONTROLS_CHECK_LABEL_X_OFF;
   int y1=CONTROLS_CHECK_LABEL_Y_OFF;
   int x2=Width();
   int y2=Height();
//--- create
   if(!m_label.Create(m_chart_id,m_name+"Label",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_label.Text(m_name))
      return(false);
   if(!Add(m_label))
      return(false);
   m_label.ReadOnly(true);
   m_label.ColorBackground(CONTROLS_CHECKGROUP_COLOR_BG);
   m_label.ColorBorder(CONTROLS_CHECKGROUP_COLOR_BG);
//--- succeeded
   return(true);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CCheckBox::Save(const int file_handle)
  {
//--- check
   if(file_handle==INVALID_HANDLE)
      return(false);
//---
   FileWriteInteger(file_handle,Checked());
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CCheckBox::Load(const int file_handle)
  {
//--- check
   if(file_handle==INVALID_HANDLE)
      return(false);
//---
   if(!FileIsEnding(file_handle))
      Checked(FileReadInteger(file_handle));
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of click on button                                       |
//+------------------------------------------------------------------+
bool CCheckBox::OnClickButton(void)
  {
//--- send the "changed state" event
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_CHANGE,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of click on label                                        |
//+------------------------------------------------------------------+
bool CCheckBox::OnClickLabel(void)
  {
//--- change button state
   m_button.Pressed(!m_button.Pressed());
//--- return the result of the button click handler
   return(OnClickButton());
  }
//+------------------------------------------------------------------+
//| Resize the chart object                                          |
//+------------------------------------------------------------------+
bool CCheckBox::OnResize(void)
{
  // resize CEdit control, this will trigger OnResize
  // for the label chart object (button is always the same size)
  m_label.Width(m_rect.Width() - CONTROLS_SCROLL_SIZE - CONTROLS_BORDER_WIDTH);
  return true;
}
//#include <Layouts/LayoutDefines.mqh>
// 'cache' is an instance of layout cache
// 'type' should be descendant of Notifiable

#define ON_EVENT_LAYOUT_INDEX(event, cache, controlIndex, handler)  if(id == (event + CHARTEVENT_CUSTOM) && lparam == cache.get(controlIndex).Id()) { handler(); return(true); }
#define ON_EVENT_LAYOUT_ARRAY(event, cache)  if(id == (event + CHARTEVENT_CUSTOM) && cache.onEvent(event, cache.get(-lparam))) { return true; }
#define ON_EVENT_LAYOUT_CTRL_ANY(event, cache, type, anything)  if(id == (event + CHARTEVENT_CUSTOM)) {type *ptr = dynamic_cast<type *>(cache.get(-lparam)); if(ptr != NULL && ptr.onEvent(event, anything)) { return true; }}
#define ON_EVENT_LAYOUT_CTRL_DLG(event, cache, type)  if(id == (event + CHARTEVENT_CUSTOM)) {type *ptr = dynamic_cast<type *>(cache.get(-lparam)); if(ptr != NULL && ptr.onEvent(event, &this)) { return true; }}
#define ON_EVENT_RAW(event, handler)  if(id == event) { handler(lparam, dparam, sparam); return(true); }

#define ON_LAYOUT_REFRESH 99 // "panic" event id

#define PRT(A) Print(#A, " ", (A))
//#include <Layouts/Box.mqh>
//+------------------------------------------------------------------+
//|                                                          Box.mqh |
//|                                                   Enrico Lambino |
//|                                      www.mql5.com/en/users/iceron|
//+------------------------------------------------------------------+
#property copyright "Enrico Lambino"
#property link "www.mql5.com/en/users/iceron"

//#include <ControlsPlus/WndClient.mqh>

//#define CLASS_LAYOUT 999
#define WND_ALIGN_CONTENT 128

#ifdef LAYOUT_BOX_DEBUG
#define COLOR_BOX_BORDER clrRed
#else
#define COLOR_BOX_BORDER clrNONE
#endif
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
enum LAYOUT_STYLE
{
  LAYOUT_STYLE_VERTICAL,
  LAYOUT_STYLE_HORIZONTAL
};
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
enum VERTICAL_ALIGN
{
  VERTICAL_ALIGN_CENTER,
  VERTICAL_ALIGN_CENTER_NOSIDES,
  VERTICAL_ALIGN_TOP,
  VERTICAL_ALIGN_BOTTOM,
  VERTICAL_ALIGN_STACK
};
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
enum HORIZONTAL_ALIGN
{
  HORIZONTAL_ALIGN_CENTER,
  HORIZONTAL_ALIGN_CENTER_NOSIDES,
  HORIZONTAL_ALIGN_LEFT,
  HORIZONTAL_ALIGN_RIGHT,
  HORIZONTAL_ALIGN_STACK
};
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CBox: public CWndClient
{
 protected:
  LAYOUT_STYLE m_layout_style;
  VERTICAL_ALIGN m_vertical_align;
  HORIZONTAL_ALIGN m_horizontal_align;
  CSize m_min_size;
  int m_controls_total;
  int m_padding_top;
  int m_padding_bottom;
  int m_padding_left;
  int m_padding_right;
  int m_total_x;
  int m_total_y;

 public:
  CBox();
  ~CBox();
  //virtual int Type() const
  //{
  //  return CLASS_LAYOUT;
  //}
  virtual bool Create(const long chart, const string name, const int subwin,
                      const int x1, const int y1, const int x2, const int y2);
  virtual bool Pack();
  virtual void AdjustFlexControls(void);
  void LayoutStyle(LAYOUT_STYLE style)
  {
    m_layout_style = style;
  }
  LAYOUT_STYLE LayoutStyle() const
  {
    return (m_layout_style);
  }
  void HorizontalAlign(const HORIZONTAL_ALIGN align)
  {
    m_horizontal_align = align;
  }
  HORIZONTAL_ALIGN HorizontalAlign() const
  {
    return (m_horizontal_align);
  }
  void VerticalAlign(const VERTICAL_ALIGN align)
  {
    m_vertical_align = align;
  }
  VERTICAL_ALIGN VerticalAlign() const
  {
    return (m_vertical_align);
  }
  void Padding(const int top, const int bottom, const int left, const int right);
  void Padding(const int padding);
  void PaddingTop(const int padding)
  {
    m_padding_top = padding;
  }
  int PaddingTop() const
  {
    return (m_padding_top);
  }
  void PaddingRight(const int padding)
  {
    m_padding_right = padding;
  }
  int PaddingRight() const
  {
    return (m_padding_right);
  }
  void PaddingBottom(const int padding)
  {
    m_padding_bottom = padding;
  }
  int PaddingBottom() const
  {
    return (m_padding_bottom);
  }
  void PaddingLeft(const int padding)
  {
    m_padding_left = padding;
  }
  int PaddingLeft() const
  {
    return (m_padding_left);
  }
  CSize GetMinSize() const
  {
    CSize sz;
    sz.cx = m_min_size.cx + m_padding_left + m_padding_right;
    sz.cy = m_min_size.cy + m_padding_top + m_padding_bottom;
    return sz;
  }
  
 protected:
  virtual void CheckControlSize(CWnd *control);
  virtual void GetTotalControlsSize(void);
  virtual bool GetSpace(int &x_space, int &y_space);
  virtual bool Render(void);
  virtual void Shift(CWnd *control, int &x, int &y, const int x_space, const int y_space);
};
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CBox::CBox():
    m_layout_style(LAYOUT_STYLE_HORIZONTAL),
    m_vertical_align(VERTICAL_ALIGN_CENTER),
    m_horizontal_align(HORIZONTAL_ALIGN_CENTER),
    m_controls_total(0),
    m_padding_top(0),
    m_padding_bottom(0),
    m_padding_left(0),
    m_padding_right(0),
    m_total_x(0),
    m_total_y(0)

{
  m_min_size.cx = 0;
  m_min_size.cy = 0;
  RTTI;
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CBox::~CBox()
{
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CBox::Create(const long chart, const string name, const int subwin,
                  const int x1, const int y1, const int x2, const int y2)
{
  if(!CWndContainer::Create(chart, name, subwin, x1, y1, x2, y2))
    return (false);
  if(!CreateBack())
    return (false);
  if(!ColorBackground(CONTROLS_DIALOG_COLOR_CLIENT_BG))
    return (false);
  if(!ColorBorder(COLOR_BOX_BORDER))
    return (false);
  return (true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CBox::Pack(void)
{
  AdjustFlexControls();
  GetTotalControlsSize();
  
  return Render();
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CBox::CheckControlSize(CWnd *control)
{
  bool adjust = false;
  CSize size = Size();
  CSize control_size = control.Size();
  if(control_size.cx > size.cx - (m_padding_left + m_padding_right))
  {
    control_size.cx = size.cx - (m_padding_left + m_padding_right);
    adjust = true;
  }
  if(control_size.cy > size.cy - (m_padding_top + m_padding_bottom))
  {
    control_size.cy = size.cy - (m_padding_top + m_padding_bottom);
    adjust = true;
  }
  if(adjust)
    control.Size(control_size.cx, control_size.cy);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CBox::GetTotalControlsSize(void)
{
  m_total_x = 0;
  m_total_y = 0;
  m_controls_total = 0;
  m_min_size.cx = 0;
  m_min_size.cy = 0;
  int total = ControlsTotal();

  for(int i = 0; i < total; i++)
  {
    CWnd *control = Control(i);
    if(control == NULL) continue;
    if(control == &m_background) continue;
    CheckControlSize(control);
    CBox *box = dynamic_cast<CBox *>(control);
    //if(control.Type() == CLASS_LAYOUT)
    if(box)
    {
      box.GetTotalControlsSize();
    }
    
    CSize control_size = control.Size();
    
    if(m_min_size.cx < control_size.cx)
      m_min_size.cx = control_size.cx;
    if(m_min_size.cy < control_size.cy)
      m_min_size.cy = control_size.cy;
      
    // there is a potential problem here: nested boxes can be aligned in such way
    // that they share the same width/height without interference
    // for example, in horizontal box one can have 2 subboxes aligned on top
    // and bottom sides, so they both fit the parent width, but the algorithm
    // will calculate common width as a sum of the subboxes
    // we need to check alignment flags
      
    if(m_layout_style == LAYOUT_STYLE_HORIZONTAL) m_total_x += control_size.cx;
    else m_total_x = MathMax(m_min_size.cx, m_total_x);
    if(m_layout_style == LAYOUT_STYLE_VERTICAL) m_total_y += control_size.cy;
    else m_total_y = MathMax(m_min_size.cy, m_total_y);
    m_controls_total++;
  }

  CSize size = Size();
  
  if(m_total_x > size.cx && m_layout_style == LAYOUT_STYLE_HORIZONTAL)
  {
    size.cx = m_total_x;
  }
  if(m_total_y > size.cy && m_layout_style == LAYOUT_STYLE_VERTICAL) // shrink
  {
    size.cy = m_total_y;
  }
  
  Size(size);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CBox::GetSpace(int &x_space, int &y_space)
{
  if(m_controls_total == 0)
    return (true);
  /*
  if(m_controls_total == 1)
  {
    if(m_horizontal_align == HORIZONTAL_ALIGN_CENTER_NOSIDES)
      m_horizontal_align = HORIZONTAL_ALIGN_CENTER;
    if(m_vertical_align == VERTICAL_ALIGN_CENTER_NOSIDES)
      m_vertical_align = VERTICAL_ALIGN_CENTER;
  }
  */
  CSize size = Size();

  int x_space_total = 0;
  int y_space_total = 0;
  if(m_layout_style == LAYOUT_STYLE_HORIZONTAL)
  {
    x_space_total = size.cx - (m_total_x + m_padding_left + m_padding_right);
    y_space_total = size.cy - (m_min_size.cy + m_padding_top + m_padding_bottom);

    if(m_horizontal_align == HORIZONTAL_ALIGN_CENTER_NOSIDES)
      x_space = x_space_total / (MathMax(m_controls_total, 2) - 1);
    else if(m_horizontal_align == HORIZONTAL_ALIGN_CENTER)
      x_space = x_space_total / (m_controls_total + 1);
    else
      x_space = x_space_total / m_controls_total;

    if(m_vertical_align == VERTICAL_ALIGN_CENTER || m_vertical_align == VERTICAL_ALIGN_CENTER_NOSIDES)
      y_space = y_space_total / 2;
    else
      y_space = y_space_total;

    if(m_horizontal_align == HORIZONTAL_ALIGN_STACK)
    {
      x_space = 0;
    }
  }
  else if(m_layout_style == LAYOUT_STYLE_VERTICAL)
  {
    x_space_total = size.cx - (m_min_size.cx + m_padding_left + m_padding_right);
    y_space_total = size.cy - (m_total_y + m_padding_top + m_padding_bottom);

    if(m_horizontal_align == HORIZONTAL_ALIGN_CENTER || m_horizontal_align == HORIZONTAL_ALIGN_CENTER_NOSIDES)
      x_space = x_space_total / 2;
    else
      x_space = x_space_total;

    if(m_vertical_align == VERTICAL_ALIGN_CENTER_NOSIDES)
      y_space = y_space_total / (MathMax(m_controls_total, 2) - 1);
    else if(m_vertical_align == VERTICAL_ALIGN_CENTER)
      y_space = y_space_total / (m_controls_total + 1);
    else
      y_space = y_space_total / m_controls_total;

    if(m_vertical_align == VERTICAL_ALIGN_STACK)
    {
      y_space = 0;
    }
  }
  else
    return (false);

  if(x_space < 0) x_space = 0;
  if(y_space < 0) y_space = 0;

  return (true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CBox::Shift(CWnd *control, int &x, int &y, const int x_space, const int y_space)
{
  if(m_layout_style == LAYOUT_STYLE_HORIZONTAL)
    x += x_space + control.Width();
  else if(m_layout_style == LAYOUT_STYLE_VERTICAL)
    y += y_space + control.Height();
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CBox::Render(void)
{
  int x_space = 0, y_space = 0;
  if(!GetSpace(x_space, y_space))
    return (false);

  int x = Left() + m_padding_left +
          ((m_horizontal_align == HORIZONTAL_ALIGN_LEFT || m_horizontal_align == HORIZONTAL_ALIGN_CENTER_NOSIDES) ? 0 : x_space);
  int y = Top() + m_padding_top +
          ((m_vertical_align == VERTICAL_ALIGN_TOP || m_vertical_align == VERTICAL_ALIGN_CENTER_NOSIDES) ? 0 : y_space);

  for(int j = 0; j < ControlsTotal(); j++)
  {
    CWnd *control = Control(j);
    if(control == NULL)
      continue;
    if(control == GetPointer(m_background))
      continue;
    control.Move(x, y);
    control.Show();
    CBox *container = dynamic_cast<CBox *>(control);
    if(container != NULL)
    {
      container.Pack();
    }
    if(j < ControlsTotal() - 1)
      Shift(GetPointer(control), x, y, x_space, y_space);
  }
  return (true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CBox::Padding(const int top, const int right, const int bottom, const int left)
{
  m_padding_top = top;
  m_padding_right = right;
  m_padding_bottom = bottom;
  m_padding_left = left;
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CBox::Padding(const int padding)
{
  m_padding_top = padding;
  m_padding_right = padding;
  m_padding_bottom = padding;
  m_padding_left = padding;
}

//+------------------------------------------------------------------+
void CBox::AdjustFlexControls(void)
{
  // the following is an example of automatic resize
  // makes sense only, if controls support dynamic resize
  if((m_align_flags & WND_ALIGN_CONTENT) != 0)
  {
    if(m_layout_style == LAYOUT_STYLE_HORIZONTAL)
    {
      // if LAYOUT_STYLE_HORIZONTAL should refine auto-widths of nesting controls
      int resizable = 0;
      int consts = 0;
      int total = 0;
      int margins = 0;
      for(int i = 0; i < ControlsTotal(); i++)
      {
        CWnd *control = Control(i);
        if(control == NULL) continue;
        if(control == &m_background) continue;
        total++;
        
        if((control.Alignment() & (WND_ALIGN_LEFT | WND_ALIGN_RIGHT)) == 0)
        {
          resizable++;
        }
        else
        {
          consts += control.Width();
        }
        CRect r = control.Margins();
        margins += (r.left + r.right);
      }
      
      if(resizable > 0 && total > 0)
      {
        margins /= total;

        int borders = (CONTROLS_BORDER_WIDTH + margins) * 2 * total;
        int w = (this.Width() - consts - borders) / resizable;
        
        for(int i = 0; i < ControlsTotal(); i++)
        {
          CWnd *control = Control(i);
          if(control == NULL) continue;
          if(control == &m_background) continue;

          if((control.Alignment() & (WND_ALIGN_LEFT | WND_ALIGN_RIGHT)) == 0)
          {
            control.Width(w);
          }
        }
      }
    }
    else if(m_layout_style == LAYOUT_STYLE_VERTICAL)
    {
      // if LAYOUT_STYLE_VERTICAL should refine auto-heights of nesting controls
      int resizable = 0;
      int consts = 0;
      int total = 0;
      int margins = 0;
      for(int i = 0; i < ControlsTotal(); i++)
      {
        CWnd *control = Control(i);
        if(control == NULL) continue;
        if(control == &m_background) continue;
        total++;
        
        if((control.Alignment() & (WND_ALIGN_TOP | WND_ALIGN_BOTTOM)) == 0)
        {
          resizable++;
        }
        else
        {
          consts += control.Height();
        }
        CRect r = control.Margins();
        margins += (r.top + r.bottom);
      }

      if(resizable > 0 && total > 0)
      {
        margins /= total;

        int borders = (CONTROLS_BORDER_WIDTH + margins) * 2 * total;
        int h = (this.Height() - consts - borders) / resizable;
        
        for(int i = 0; i < ControlsTotal(); i++)
        {
          CWnd *control = Control(i);
          if(control == NULL) continue;
          if(control == &m_background) continue;

          if((control.Alignment() & (WND_ALIGN_TOP | WND_ALIGN_BOTTOM)) == 0)
          {
            control.Height(h);
          }
        }
      }
    }
  }
}

class CBoxH: public CBox
{
  public:
    CBoxH()
    {
      RTTI;
      m_layout_style = LAYOUT_STYLE_HORIZONTAL;
    }
};

class CBoxV: public CBox
{
  public:
    CBoxV()
    {
      RTTI;
      m_layout_style = LAYOUT_STYLE_VERTICAL;
    }
};
//#include <Layouts/ComboBoxResizable.mqh>
//+------------------------------------------------------------------+
//|                                            ComboBoxResizable.mqh |
//|                                    Copyright (c) 2019, Marketeer |
//|                          https://www.mql5.com/en/users/marketeer |
//+------------------------------------------------------------------+

//#include <ControlsPlus/ComboBox.mqh>
//+------------------------------------------------------------------+
//|                                                     ComboBox.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "WndContainer.mqh"
//#include "Edit.mqh"
//#include "BmpButton.mqh"
//#include "ListView.mqh"
//+------------------------------------------------------------------+
//|                                                     ListView.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "WndClient.mqh"
//#include "Edit.mqh"
//#include <Arrays\ArrayString.mqh>
//+------------------------------------------------------------------+
//|                                                  ArrayString.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "Array.mqh"
//+------------------------------------------------------------------+
//| Class CArrayString.                                              |
//| Purpose: Class of dynamic array of variables of string type.     |
//|          Derives from class CArray.                              |
//+------------------------------------------------------------------+
class CArrayString : public CArray
  {
protected:
   string            m_data[];           // data array

public:
                     CArrayString(void);
                    ~CArrayString(void);
   //--- method of identifying the object
   virtual int       Type(void) const { return(TYPE_STRING); }
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);
   //--- methods of managing dynamic memory
   bool              Reserve(const int size);
   bool              Resize(const int size);
   bool              Shutdown(void);
   //--- methods of filling the array
   bool              Add(const string element);
   bool              AddArray(const string &src[]);
   bool              AddArray(const CArrayString *src);
   bool              Insert(const string element,const int pos);
   bool              InsertArray(const string &src[],const int pos);
   bool              InsertArray(const CArrayString *src,const int pos);
   bool              AssignArray(const string &src[]);
   bool              AssignArray(const CArrayString *src);
   //--- method of access to the array
   string            At(const int index) const;
   string operator[](const int index) const { return(At(index)); }
   //--- methods of changing
   bool              Update(const int index,const string element);
   bool              Shift(const int index,const int shift);
   //--- methods of deleting
   bool              Delete(const int index);
   bool              DeleteRange(int from,int to);
   //--- methods for comparing arrays
   bool              CompareArray(const string &array[]) const;
   bool              CompareArray(const CArrayString *array) const;
   //--- methods for working with the sorted array
   bool              InsertSort(const string element);
   int               Search(const string element) const;
   int               SearchGreat(const string element) const;
   int               SearchLess(const string element) const;
   int               SearchGreatOrEqual(const string element) const;
   int               SearchLessOrEqual(const string element) const;
   int               SearchFirst(const string element) const;
   int               SearchLast(const string element) const;
   int               SearchLinear(const string element) const;

protected:
   virtual void      QuickSort(int beg,int end,const int mode=0);
   int               QuickSearch(const string element) const;
   int               MemMove(const int dest,const int src,int count);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CArrayString::CArrayString(void)
  {
//--- initialize protected data
   m_data_max=ArraySize(m_data);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CArrayString::~CArrayString(void)
  {
   if(m_data_max!=0)
      Shutdown();
  }
//+------------------------------------------------------------------+
//| Moving the memory within a single array                          |
//+------------------------------------------------------------------+
int CArrayString::MemMove(const int dest,const int src,int count)
  {
   int i;
//--- check parameters
   if(dest<0 || src<0 || count<0)
      return(-1);
//--- check count
   if(src+count>m_data_total)
      count=m_data_total-src;
   if(count<0)
      return(-1);
//--- no need to copy
   if(dest==src || count==0)
      return(dest);
//--- check data total
   if(dest+count>m_data_total)
     {
      if(m_data_max<dest+count)
         return(-1);
      m_data_total=dest+count;
     }
//--- copy
   if(dest<src)
     {
      //--- copy from left to right
      for(i=0;i<count;i++)
         m_data[dest+i]=m_data[src+i];
     }
   else
     {
      //--- copy from right to left
      for(i=count-1;i>=0;i--)
         m_data[dest+i]=m_data[src+i];
     }
//--- successful
   return(dest);
  }
//+------------------------------------------------------------------+
//| Request for more memory in an array. Checks if the requested     |
//| number of free elements already exists; allocates additional     |
//| memory with a given step                                         |
//+------------------------------------------------------------------+
bool CArrayString::Reserve(const int size)
  {
   int new_size;
//--- check
   if(size<=0)
      return(false);
//--- resize array
   if(Available()<size)
     {
      new_size=m_data_max+m_step_resize*(1+(size-Available())/m_step_resize);
      if(new_size<0)
         //--- overflow occurred when calculating new_size
         return(false);
      if((m_data_max=ArrayResize(m_data,new_size))==-1)
         m_data_max=ArraySize(m_data);
     }
//--- result
   return(Available()>=size);
  }
//+------------------------------------------------------------------+
//| Resizing (with removal of elements on the right)                 |
//+------------------------------------------------------------------+
bool CArrayString::Resize(const int size)
  {
   int new_size;
//--- check
   if(size<0)
      return(false);
//--- resize array
   new_size=m_step_resize*(1+size/m_step_resize);
   if(m_data_max!=new_size)
     {
      if((m_data_max=ArrayResize(m_data,new_size))==-1)
        {
         m_data_max=ArraySize(m_data);
         return(false);
        }
     }
   if(m_data_total>size)
      m_data_total=size;
//--- result
   return(m_data_max==new_size);
  }
//+------------------------------------------------------------------+
//| Complete cleaning of the array with the release of memory        |
//+------------------------------------------------------------------+
bool CArrayString::Shutdown(void)
  {
//--- check
   if(m_data_max==0)
      return(true);
//--- clean
   if(ArrayResize(m_data,0)==-1)
      return(false);
   m_data_total=0;
   m_data_max=0;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Adding an element to the end of the array                        |
//+------------------------------------------------------------------+
bool CArrayString::Add(const string element)
  {
//--- check/reserve elements of array
   if(!Reserve(1))
      return(false);
//--- add
   m_data[m_data_total++]=element;
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Adding an element to the end of the array from another array     |
//+------------------------------------------------------------------+
bool CArrayString::AddArray(const string &src[])
  {
   int num=ArraySize(src);
//--- check/reserve elements of array
   if(!Reserve(num))
      return(false);
//--- add
   for(int i=0;i<num;i++)
      m_data[m_data_total++]=src[i];
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Adding an element to the end of the array from another array     |
//+------------------------------------------------------------------+
bool CArrayString::AddArray(const CArrayString *src)
  {
   int num;
//--- check
   if(!CheckPointer(src))
      return(false);
//--- check/reserve elements of array
   num=src.Total();
   if(!Reserve(num))
      return(false);
//--- add
   for(int i=0;i<num;i++)
      m_data[m_data_total++]=src.m_data[i];
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Inserting an element in the specified position                   |
//+------------------------------------------------------------------+
bool CArrayString::Insert(const string element,const int pos)
  {
//--- check/reserve elements of array
   if(pos<0 || !Reserve(1))
      return(false);
//--- insert
   m_data_total++;
   if(pos<m_data_total-1)
     {
      if(MemMove(pos+1,pos,m_data_total-pos-1)<0)
         return(false);
      m_data[pos]=element;
     }
   else
      m_data[m_data_total-1]=element;
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Inserting elements in the specified position                     |
//+------------------------------------------------------------------+
bool CArrayString::InsertArray(const string &src[],const int pos)
  {
   int num=ArraySize(src);
//--- check/reserve elements of array
   if(!Reserve(num))
      return(false);
//--- insert
   if(MemMove(num+pos,pos,m_data_total-pos)<0)
      return(false);
   for(int i=0;i<num;i++)
      m_data[i+pos]=src[i];
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Inserting elements in the specified position                     |
//+------------------------------------------------------------------+
bool CArrayString::InsertArray(const CArrayString *src,const int pos)
  {
   int num;
//--- check
   if(!CheckPointer(src))
      return(false);
//--- check/reserve elements of array
   num=src.Total();
   if(!Reserve(num))
      return(false);
//--- insert
   if(MemMove(num+pos,pos,m_data_total-pos)<0)
      return(false);
   for(int i=0;i<num;i++)
      m_data[i+pos]=src.m_data[i];
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Assignment (copying) of another array                            |
//+------------------------------------------------------------------+
bool CArrayString::AssignArray(const string &src[])
  {
   int num=ArraySize(src);
//--- check/reserve elements of array
   Clear();
   if(m_data_max<num)
     {
      if(!Reserve(num))
         return(false);
     }
   else
      Resize(num);
//--- copy array
   for(int i=0;i<num;i++)
     {
      m_data[i]=src[i];
      m_data_total++;
     }
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Assignment (copying) of another array                            |
//+------------------------------------------------------------------+
bool CArrayString::AssignArray(const CArrayString *src)
  {
   int num;
//--- check
   if(!CheckPointer(src))
      return(false);
//--- check/reserve elements of array
   num=src.m_data_total;
   Clear();
   if(m_data_max<num)
     {
      if(!Reserve(num))
         return(false);
     }
   else
      Resize(num);
//--- copy array
   for(int i=0;i<num;i++)
     {
      m_data[i]=src.m_data[i];
      m_data_total++;
     }
   m_sort_mode=src.SortMode();
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Access to data in the specified position                         |
//+------------------------------------------------------------------+
string CArrayString::At(const int index) const
  {
//--- check
   if(index<0 || index>=m_data_total)
      return("");
//--- result
   return(m_data[index]);
  }
//+------------------------------------------------------------------+
//| Updating element in the specified position                       |
//+------------------------------------------------------------------+
bool CArrayString::Update(const int index,const string element)
  {
//--- check
   if(index<0 || index>=m_data_total)
      return(false);
//--- update
   m_data[index]=element;
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Moving element from the specified position                       |
//| on the specified shift                                           |
//+------------------------------------------------------------------+
bool CArrayString::Shift(const int index,const int shift)
  {
   string tmp_string;
//--- check
   if(index<0 || index+shift<0 || index+shift>=m_data_total)
      return(false);
   if(shift==0)
      return(true);
//--- move
   tmp_string=m_data[index];
   if(shift>0)
     {
      if(MemMove(index,index+1,shift)<0)
         return(false);
     }
   else
     {
      if(MemMove(index+shift+1,index+shift,-shift)<0)
         return(false);
     }
   m_data[index+shift]=tmp_string;
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Deleting element from the specified position                     |
//+------------------------------------------------------------------+
bool CArrayString::Delete(const int index)
  {
//--- check
   if(index<0 || index>=m_data_total)
      return(false);
//--- delete
   if(index<m_data_total-1 && MemMove(index,index+1,m_data_total-index-1)<0)
      return(false);
   m_data_total--;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Deleting range of elements                                       |
//+------------------------------------------------------------------+
bool CArrayString::DeleteRange(int from,int to)
  {
//--- check
   if(from<0 || to<0)
      return(false);
   if(from>to || from>=m_data_total)
      return(false);
//--- delete
   if(to>=m_data_total-1)
      to=m_data_total-1;
   if(MemMove(from,to+1,m_data_total-to-1)<0)
      return(false);
   m_data_total-=to-from+1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Equality comparison of two arrays                                |
//+------------------------------------------------------------------+
bool CArrayString::CompareArray(const string &array[]) const
  {
//--- compare
   if(m_data_total!=ArraySize(array))
      return(false);
   for(int i=0;i<m_data_total;i++)
      if(m_data[i]!=array[i])
         return(false);
//--- equal
   return(true);
  }
//+------------------------------------------------------------------+
//| Equality comparison of two arrays                                |
//+------------------------------------------------------------------+
bool CArrayString::CompareArray(const CArrayString *array) const
  {
//--- check
   if(!CheckPointer(array))
      return(false);
//--- compare
   if(m_data_total!=array.m_data_total)
      return(false);
   for(int i=0;i<m_data_total;i++)
      if(m_data[i]!=array.m_data[i])
         return(false);
//--- equal
   return(true);
  }
//+------------------------------------------------------------------+
//| Method QuickSort                                                 |
//+------------------------------------------------------------------+
void CArrayString::QuickSort(int beg,int end,const int mode)
  {
   int    i,j;
   string p_string;
   string t_string;
//--- check
   if(beg<0 || end<0)
      return;
//--- sort
   i=beg;
   j=end;
   while(i<end)
     {
      //--- ">>1" is quick division by 2
      p_string=m_data[(beg+end)>>1];
      while(i<j)
        {
         while(m_data[i]<p_string)
           {
            //--- control the output of the array bounds
            if(i==m_data_total-1)
               break;
            i++;
           }
         while(m_data[j]>p_string)
           {
            //--- control the output of the array bounds
            if(j==0)
               break;
            j--;
           }
         if(i<=j)
           {
            t_string=m_data[i];
            m_data[i++]=m_data[j];
            m_data[j]=t_string;
            //--- control the output of the array bounds
            if(j==0)
               break;
            j--;
           }
        }
      if(beg<j)
         QuickSort(beg,j);
      beg=i;
      j=end;
     }
  }
//+------------------------------------------------------------------+
//| Inserting element in a sorted array                              |
//+------------------------------------------------------------------+
bool CArrayString::InsertSort(const string element)
  {
   int pos;
//--- check
   if(!IsSorted())
      return(false);
//--- check/reserve elements of array
   if(!Reserve(1))
      return(false);
//--- if the array is empty, add an element
   if(m_data_total==0)
     {
      m_data[m_data_total++]=element;
      return(true);
     }
//--- find position and insert
   pos=QuickSearch(element);
   if(m_data[pos]>element)
      Insert(element,pos);
   else
      Insert(element,pos+1);
//--- restore the sorting flag after Insert(...)
   m_sort_mode=0;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Search of position of element in a array                         |
//+------------------------------------------------------------------+
int CArrayString::SearchLinear(const string element) const
  {
//--- check
   if(m_data_total==0)
      return(-1);
//---
   for(int i=0;i<m_data_total;i++)
      if(m_data[i]==element)
         return(i);
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Quick search of position of element in a sorted array            |
//+------------------------------------------------------------------+
int CArrayString::QuickSearch(const string element) const
  {
   int    i,j,m=-1;
   string t_string;
//--- search
   i=0;
   j=m_data_total-1;
   while(j>=i)
     {
      //--- ">>1" is quick division by 2
      m=(j+i)>>1;
      if(m<0 || m>=m_data_total)
         break;
      t_string=m_data[m];
      if(t_string==element)
         break;
      if(t_string>element)
         j=m-1;
      else
         i=m+1;
     }
//--- position
   return(m);
  }
//+------------------------------------------------------------------+
//| Search of position of element in a sorted array                  |
//+------------------------------------------------------------------+
int CArrayString::Search(const string element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   pos=QuickSearch(element);
   if(m_data[pos]==element)
      return(pos);
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Search position of the first element which is greater than       |
//| specified in a sorted array                                      |
//+------------------------------------------------------------------+
int CArrayString::SearchGreat(const string element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   pos=QuickSearch(element);
   while(m_data[pos]<=element)
      if(++pos==m_data_total)
         return(-1);
//--- position
   return(pos);
  }
//+------------------------------------------------------------------+
//| Search position of the first element which is less than          |
//| specified in the sorted array                                    |
//+------------------------------------------------------------------+
int CArrayString::SearchLess(const string element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   pos=QuickSearch(element);
   while(m_data[pos]>=element)
      if(pos--==0)
         return(-1);
//--- position
   return(pos);
  }
//+------------------------------------------------------------------+
//| Search position of the first element which is greater than or    |
//| equal to the specified in a sorted array                         |
//+------------------------------------------------------------------+
int CArrayString::SearchGreatOrEqual(const string element) const
  {
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   for(int pos=QuickSearch(element);pos<m_data_total;pos++)
      if(m_data[pos]>=element)
         return(pos);
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Search position of the first element which is less than or equal |
//| to the specified in a sorted array                               |
//+------------------------------------------------------------------+
int CArrayString::SearchLessOrEqual(const string element) const
  {
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   for(int pos=QuickSearch(element);pos>=0;pos--)
      if(m_data[pos]<=element)
         return(pos);
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Find position of first appearance of element in a sorted array   |
//+------------------------------------------------------------------+
int CArrayString::SearchFirst(const string element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   pos=QuickSearch(element);
   if(m_data[pos]==element)
     {
      while(m_data[pos]==element)
         if(pos--==0)
            break;
      return(pos+1);
     }
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Find position of last appearance of element in a sorted array    |
//+------------------------------------------------------------------+
int CArrayString::SearchLast(const string element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   pos=QuickSearch(element);
   if(m_data[pos]==element)
     {
      while(m_data[pos]==element)
         if(++pos==m_data_total)
            break;
      return(pos-1);
     }
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Writing array to file                                            |
//+------------------------------------------------------------------+
bool CArrayString::Save(const int file_handle)
  {
   int i=0,len;
//--- check
   if(!CArray::Save(file_handle))
      return(false);
//--- write array length
   if(FileWriteInteger(file_handle,m_data_total,INT_VALUE)!=INT_VALUE)
      return(false);
//--- write array
   for(i=0;i<m_data_total;i++)
     {
      len=StringLen(m_data[i]);
      //--- write string length
      if(FileWriteInteger(file_handle,len,INT_VALUE)!=INT_VALUE)
         return(false);
      //--- write string
      if(len!=0) if(FileWriteString(file_handle,m_data[i],len)!=len)
         break;
     }
//--- result
   return(i==m_data_total);
  }
//+------------------------------------------------------------------+
//| Reading array from file                                          |
//+------------------------------------------------------------------+
bool CArrayString::Load(const int file_handle)
  {
   int i=0,num,len;
//--- check
   if(!CArray::Load(file_handle))
      return(false);
//--- read array length
   num=FileReadInteger(file_handle,INT_VALUE);
//--- read array
   Clear();
   if(num!=0)
     {
      if(!Reserve(num))
         return(false);
      for(i=0;i<num;i++)
        {
         //--- read string length
         len=FileReadInteger(file_handle,INT_VALUE);
         //--- read string
         if(len!=0)
            m_data[i]=FileReadString(file_handle,len);
         else
            m_data[i]="";
         m_data_total++;
         if(FileIsEnding(file_handle))
            break;
        }
     }
   m_sort_mode=-1;
//--- result
   return(m_data_total==num);
  }
//+------------------------------------------------------------------+
//#include <Arrays\ArrayLong.mqh>
//+------------------------------------------------------------------+
//|                                                    ArrayLong.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "Array.mqh"
//+------------------------------------------------------------------+
//| Class CArrayLong.                                                |
//| Purpose: Class of dynamic array of variables                     |
//|          of long or ulong type.                                  |
//|          Derives from class CArray.                              |
//+------------------------------------------------------------------+
class CArrayLong : public CArray
  {
protected:
   long              m_data[];           // data array

public:
                     CArrayLong(void);
                    ~CArrayLong(void);
   //--- method of identifying the object
   virtual int       Type(void) const { return(TYPE_LONG); }
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);
   //--- methods of managing dynamic memory
   bool              Reserve(const int size);
   bool              Resize(const int size);
   bool              Shutdown(void);
   //--- methods of filling the array
   bool              Add(const long element);
   bool              AddArray(const long &src[]);
   bool              AddArray(const CArrayLong *src);
   bool              Insert(const long element,const int pos);
   bool              InsertArray(const long &src[],const int pos);
   bool              InsertArray(const CArrayLong *src,const int pos);
   bool              AssignArray(const long &src[]);
   bool              AssignArray(const CArrayLong *src);
   //--- method of access to the array
   long              At(const int index) const;
   long operator[](const int index) const { return(At(index)); }
   //--- methods of searching for minimum and maximum
   int               Minimum(const int start,const int count) const { return(CArray::Minimum(m_data,start,count)); }
   int               Maximum(const int start,const int count) const { return(CArray::Maximum(m_data,start,count)); }
   //--- methods change
   bool              Update(const int index,const long element);
   bool              Shift(const int index,const int shift);
   //--- methods for deleting
   bool              Delete(const int index);
   bool              DeleteRange(int from,int to);
   //--- methods for compare arrays
   bool              CompareArray(const long &array[]) const;
   bool              CompareArray(const CArrayLong *array) const;
   //--- methods for working with the sorted array
   bool              InsertSort(const long element);
   int               Search(const long element) const;
   int               SearchGreat(const long element) const;
   int               SearchLess(const long element) const;
   int               SearchGreatOrEqual(const long element) const;
   int               SearchLessOrEqual(const long element) const;
   int               SearchFirst(const long element) const;
   int               SearchLast(const long element) const;
   int               SearchLinear(const long element) const;

protected:
   virtual void      QuickSort(int beg,int end,const int mode=0);
   int               QuickSearch(const long element) const;
   int               MemMove(const int dest,const int src,int count);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CArrayLong::CArrayLong(void)
  {
//--- initialize protected data
   m_data_max=ArraySize(m_data);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CArrayLong::~CArrayLong(void)
  {
   if(m_data_max!=0)
      Shutdown();
  }
//+------------------------------------------------------------------+
//| Moving the memory within a single array                          |
//+------------------------------------------------------------------+
int CArrayLong::MemMove(const int dest,const int src,int count)
  {
   int i;
//--- check parameters
   if(dest<0 || src<0 || count<0)
      return(-1);
//--- check count
   if(src+count>m_data_total)
      count=m_data_total-src;
   if(count<0)
      return(-1);
//--- no need to copy
   if(dest==src || count==0)
      return(dest);
//--- check data total
   if(dest+count>m_data_total)
     {
      if(m_data_max<dest+count)
         return(-1);
      m_data_total=dest+count;
     }
//--- copy
   if(dest<src)
     {
      //--- copy from left to right
      for(i=0;i<count;i++)
         m_data[dest+i]=m_data[src+i];
     }
   else
     {
      //--- copy from right to left
      for(i=count-1;i>=0;i--)
         m_data[dest+i]=m_data[src+i];
     }
//--- successful
   return(dest);
  }
//+------------------------------------------------------------------+
//| Request for more memory in an array. Checks if the requested     |
//| number of free elements already exists; allocates additional     |
//| memory with a given step                                         |
//+------------------------------------------------------------------+
bool CArrayLong::Reserve(const int size)
  {
   int new_size;
//--- check
   if(size<=0)
      return(false);
//--- resize array
   if(Available()<size)
     {
      new_size=m_data_max+m_step_resize*(1+(size-Available())/m_step_resize);
      if(new_size<0)
         //--- overflow occurred when calculating new_size
         return(false);
      if((m_data_max=ArrayResize(m_data,new_size))==-1)
         m_data_max=ArraySize(m_data);
     }
//--- result
   return(Available()>=size);
  }
//+------------------------------------------------------------------+
//| Resizing (with removal of elements on the right)                 |
//+------------------------------------------------------------------+
bool CArrayLong::Resize(const int size)
  {
   int new_size;
//--- check
   if(size<0)
      return(false);
//--- resize array
   new_size=m_step_resize*(1+size/m_step_resize);
   if(m_data_max!=new_size)
     {
      if((m_data_max=ArrayResize(m_data,new_size))==-1)
        {
         m_data_max=ArraySize(m_data);
         return(false);
        }
     }
   if(m_data_total>size)
      m_data_total=size;
//--- result
   return(m_data_max==new_size);
  }
//+------------------------------------------------------------------+
//| Complete cleaning of the array with the release of memory        |
//+------------------------------------------------------------------+
bool CArrayLong::Shutdown(void)
  {
//--- check
   if(m_data_max==0)
      return(true);
//--- clean
   if(ArrayResize(m_data,0)==-1)
      return(false);
   m_data_total=0;
   m_data_max=0;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Adding an element to the end of the array                        |
//+------------------------------------------------------------------+
bool CArrayLong::Add(const long element)
  {
//--- check/reserve elements of array
   if(!Reserve(1))
      return(false);
//--- add
   m_data[m_data_total++]=element;
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Adding an element to the end of the array from another array     |
//+------------------------------------------------------------------+
bool CArrayLong::AddArray(const long &src[])
  {
   int num=ArraySize(src);
//--- check/reserve elements of array
   if(!Reserve(num))
      return(false);
//--- add
   for(int i=0;i<num;i++)
      m_data[m_data_total++]=src[i];
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Adding an element to the end of the array from another array     |
//+------------------------------------------------------------------+
bool CArrayLong::AddArray(const CArrayLong *src)
  {
   int num;
//--- check
   if(!CheckPointer(src))
      return(false);
//--- check/reserve elements of array
   num=src.Total();
   if(!Reserve(num))
      return(false);
//--- add
   for(int i=0;i<num;i++)
      m_data[m_data_total++]=src.m_data[i];
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Inserting an element in the specified position                   |
//+------------------------------------------------------------------+
bool CArrayLong::Insert(const long element,const int pos)
  {
//--- check/reserve elements of array
   if(pos<0 || !Reserve(1))
      return(false);
//--- insert
   m_data_total++;
   if(pos<m_data_total-1)
     {
      if(MemMove(pos+1,pos,m_data_total-pos-1)<0)
         return(false);
      m_data[pos]=element;
     }
   else
      m_data[m_data_total-1]=element;
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Inserting elements in the specified position                     |
//+------------------------------------------------------------------+
bool CArrayLong::InsertArray(const long &src[],const int pos)
  {
   int num=ArraySize(src);
//--- check/reserve elements of array
   if(!Reserve(num))
      return(false);
//--- insert
   if(MemMove(num+pos,pos,m_data_total-pos)<0)
      return(false);
   for(int i=0;i<num;i++)
      m_data[i+pos]=src[i];
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Inserting elements in the specified position                     |
//+------------------------------------------------------------------+
bool CArrayLong::InsertArray(const CArrayLong *src,const int pos)
  {
   int num;
//--- check
   if(!CheckPointer(src))
      return(false);
//--- check/reserve elements of array
   num=src.Total();
   if(!Reserve(num))
      return(false);
//--- insert
   if(MemMove(num+pos,pos,m_data_total-pos)<0)
      return(false);
   for(int i=0;i<num;i++)
      m_data[i+pos]=src.m_data[i];
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Assignment (copying) of another array                            |
//+------------------------------------------------------------------+
bool CArrayLong::AssignArray(const long &src[])
  {
   int num=ArraySize(src);
//--- check/reserve elements of array
   Clear();
   if(m_data_max<num)
     {
      if(!Reserve(num))
         return(false);
     }
   else
      Resize(num);
//--- copy array
   for(int i=0;i<num;i++)
     {
      m_data[i]=src[i];
      m_data_total++;
     }
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Assignment (copying) of another array                            |
//+------------------------------------------------------------------+
bool CArrayLong::AssignArray(const CArrayLong *src)
  {
   int num;
//--- check
   if(!CheckPointer(src))
      return(false);
//--- check/reserve elements of array
   num=src.m_data_total;
   Clear();
   if(m_data_max<num)
     {
      if(!Reserve(num))
         return(false);
     }
   else
      Resize(num);
//--- copy array
   for(int i=0;i<num;i++)
     {
      m_data[i]=src.m_data[i];
      m_data_total++;
     }
   m_sort_mode=src.SortMode();
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Access to data in the specified position                         |
//+------------------------------------------------------------------+
long CArrayLong::At(const int index) const
  {
//--- check
   if(index<0 || index>=m_data_total)
      return(LONG_MAX);
//--- result
   return(m_data[index]);
  }
//+------------------------------------------------------------------+
//| Updating element in the specified position                       |
//+------------------------------------------------------------------+
bool CArrayLong::Update(const int index,const long element)
  {
//--- check
   if(index<0 || index>=m_data_total)
      return(false);
//--- update
   m_data[index]=element;
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Moving element from the specified position                       |
//| on the specified shift                                           |
//+------------------------------------------------------------------+
bool CArrayLong::Shift(const int index,const int shift)
  {
   long tmp_long;
//--- check
   if(index<0 || index+shift<0 || index+shift>=m_data_total)
      return(false);
   if(shift==0)
      return(true);
//--- move
   tmp_long=m_data[index];
   if(shift>0)
     {
      if(MemMove(index,index+1,shift)<0)
         return(false);
     }
   else
     {
      if(MemMove(index+shift+1,index+shift,-shift)<0)
         return(false);
     }
   m_data[index+shift]=tmp_long;
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Deleting element from the specified position                     |
//+------------------------------------------------------------------+
bool CArrayLong::Delete(const int index)
  {
//--- check
   if(index<0 || index>=m_data_total)
      return(false);
//--- delete
   if(index<m_data_total-1 && MemMove(index,index+1,m_data_total-index-1)<0)
      return(false);
   m_data_total--;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Deleting range of elements                                       |
//+------------------------------------------------------------------+
bool CArrayLong::DeleteRange(int from,int to)
  {
//--- check
   if(from<0 || to<0)
      return(false);
   if(from>to || from>=m_data_total)
      return(false);
//--- delete
   if(to>=m_data_total-1)
      to=m_data_total-1;
   if(MemMove(from,to+1,m_data_total-to-1)<0)
      return(false);
   m_data_total-=to-from+1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Equality comparison of two arrays                                |
//+------------------------------------------------------------------+
bool CArrayLong::CompareArray(const long &array[]) const
  {
//--- compare
   if(m_data_total!=ArraySize(array))
      return(false);
   for(int i=0;i<m_data_total;i++)
      if(m_data[i]!=array[i])
         return(false);
//--- equal
   return(true);
  }
//+------------------------------------------------------------------+
//| Equality comparison of two arrays                                |
//+------------------------------------------------------------------+
bool CArrayLong::CompareArray(const CArrayLong *array) const
  {
//--- check
   if(!CheckPointer(array))
      return(false);
//--- compare
   if(m_data_total!=array.m_data_total)
      return(false);
   for(int i=0;i<m_data_total;i++)
      if(m_data[i]!=array.m_data[i])
         return(false);
//--- equal
   return(true);
  }
//+------------------------------------------------------------------+
//| Method QuickSort                                                 |
//+------------------------------------------------------------------+
void CArrayLong::QuickSort(int beg,int end,const int mode)
  {
   int i,j;
   long p_long,t_long;
//--- check
   if(beg<0 || end<0)
      return;
//--- sort
   i=beg;
   j=end;
   while(i<end)
     {
      //--- ">>1" is quick division by 2
      p_long=m_data[(beg+end)>>1];
      while(i<j)
        {
         while(m_data[i]<p_long)
           {
            //--- control the output of the array bounds
            if(i==m_data_total-1)
               break;
            i++;
           }
         while(m_data[j]>p_long)
           {
            //--- control the output of the array bounds
            if(j==0)
               break;
            j--;
           }
         if(i<=j)
           {
            t_long=m_data[i];
            m_data[i++]=m_data[j];
            m_data[j]=t_long;
            //--- control the output of the array bounds
            if(j==0)
               break;
            j--;
           }
        }
      if(beg<j)
         QuickSort(beg,j);
      beg=i;
      j=end;
     }
  }
//+------------------------------------------------------------------+
//| Inserting element in a sorted array                              |
//+------------------------------------------------------------------+
bool CArrayLong::InsertSort(const long element)
  {
   int pos;
//--- check
   if(!IsSorted())
      return(false);
//--- check/reserve elements of array
   if(!Reserve(1))
      return(false);
//--- if the array is empty, add an element
   if(m_data_total==0)
     {
      m_data[m_data_total++]=element;
      return(true);
     }
//--- search position and insert
   pos=QuickSearch(element);
   if(m_data[pos]>element)
      Insert(element,pos);
   else
      Insert(element,pos+1);
//--- restore the sorting flag after Insert(...)
   m_sort_mode=0;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Search of position of element in a array                         |
//+------------------------------------------------------------------+
int CArrayLong::SearchLinear(const long element) const
  {
//--- check
   if(m_data_total==0)
      return(-1);
//---
   for(int i=0;i<m_data_total;i++)
      if(m_data[i]==element)
         return(i);
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Quick search of position of element in a sorted array            |
//+------------------------------------------------------------------+
int CArrayLong::QuickSearch(const long element) const
  {
   int  i,j,m=-1;
   long t_long;
//--- search
   i=0;
   j=m_data_total-1;
   while(j>=i)
     {
      //--- ">>1" is quick division by 2
      m=(j+i)>>1;
      if(m<0 || m>=m_data_total)
         break;
      t_long=m_data[m];
      if(t_long==element)
         break;
      if(t_long>element)
         j=m-1;
      else
         i=m+1;
     }
//--- position
   return(m);
  }
//+------------------------------------------------------------------+
//| Search of position of element in a sorted array                  |
//+------------------------------------------------------------------+
int CArrayLong::Search(const long element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   pos=QuickSearch(element);
   if(m_data[pos]==element)
      return(pos);
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Search position of the first element which is greater than       |
//| specified in a sorted array                                      |
//+------------------------------------------------------------------+
int CArrayLong::SearchGreat(const long element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   pos=QuickSearch(element);
   while(m_data[pos]<=element)
      if(++pos==m_data_total)
         return(-1);
//--- position
   return(pos);
  }
//+------------------------------------------------------------------+
//| Search position of the first element which is less than          |
//| specified in the sorted array                                    |
//+------------------------------------------------------------------+
int CArrayLong::SearchLess(const long element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   pos=QuickSearch(element);
   while(m_data[pos]>=element)
      if(pos--==0)
         return(-1);
//--- position
   return(pos);
  }
//+------------------------------------------------------------------+
//| Search position of the first element which is greater than or    |
//| equal to the specified in a sorted array                         |
//+------------------------------------------------------------------+
int CArrayLong::SearchGreatOrEqual(const long element) const
  {
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   for(int pos=QuickSearch(element);pos<m_data_total;pos++)
      if(m_data[pos]>=element)
         return(pos);
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Search position of the first element which is less than or equal |
//| to the specified in a sorted array                               |
//+------------------------------------------------------------------+
int CArrayLong::SearchLessOrEqual(const long element) const
  {
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   for(int pos=QuickSearch(element);pos>=0;pos--)
      if(m_data[pos]<=element)
         return(pos);
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Find position of first appearance of element in a sorted array   |
//+------------------------------------------------------------------+
int CArrayLong::SearchFirst(const long element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   pos=QuickSearch(element);
   if(m_data[pos]==element)
     {
      while(m_data[pos]==element)
         if(pos--==0)
            break;
      return(pos+1);
     }
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Find position of last appearance of element in a sorted array    |
//+------------------------------------------------------------------+
int CArrayLong::SearchLast(const long element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   pos=QuickSearch(element);
   if(m_data[pos]==element)
     {
      while(m_data[pos]==element)
         if(++pos==m_data_total)
            break;
      return(pos-1);
     }
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Writing array to file                                            |
//+------------------------------------------------------------------+
bool CArrayLong::Save(const int file_handle)
  {
   int i=0;
//--- check
   if(!CArray::Save(file_handle))
      return(false);
//--- write array length
   if(FileWriteInteger(file_handle,m_data_total,INT_VALUE)!=INT_VALUE)
      return(false);
//--- write array
   for(i=0;i<m_data_total;i++)
      if(FileWriteLong(file_handle,m_data[i])!=sizeof(long))
         break;
//--- result
   return(i==m_data_total);
  }
//+------------------------------------------------------------------+
//| Reading array from file                                          |
//+------------------------------------------------------------------+
bool CArrayLong::Load(const int file_handle)
  {
   int i=0,num;
//--- check
   if(!CArray::Load(file_handle))
      return(false);
//--- read array length
   num=FileReadInteger(file_handle,INT_VALUE);
//--- read array
   Clear();
   if(num!=0)
     {
      if(!Reserve(num))
         return(false);
      for(i=0;i<num;i++)
        {
         m_data[i]=FileReadLong(file_handle);
         m_data_total++;
         if(FileIsEnding(file_handle))
            break;
        }
     }
   m_sort_mode=-1;
//--- result
   return(m_data_total==num);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Class CListView                                                  |
//| Usage: display lists                                             |
//+------------------------------------------------------------------+
class CListView : public CWndClient
  {
protected:
   //--- dependent controls
   CEdit             m_rows[];              // array of the row objects
   //--- set up
   int               m_offset;              // index of first visible row in array of rows
   int               m_total_view;          // number of visible rows
   int               m_item_height;         // height of visible row
   bool              m_height_variable;
   //--- data
   CArrayString      m_strings;             // array of rows
   CArrayLong        m_values;              // array of values
   int               m_current;             // index of current row in array of rows
   bool              m_color_mode;

public:
                     CListView(void);
                    ~CListView(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   virtual void      Destroy(const int reason=0);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- set up
           bool      TotalView(const int value);
   //--- fill
   virtual bool      AddItem(const string item,const long value=-1);
   //--- data
   virtual bool      ItemAdd(const string item,const long value=-1);
   virtual bool      ItemInsert(const int index,const string item,const long value=-1);
   virtual bool      ItemUpdate(const int index,const string item,const long value=-1);
   virtual bool      ItemDelete(const int index);
   virtual bool      ItemsClear(void);
   //--- data
   int               Current(void) { return(m_current);               }
   string            Select(void)  { return(m_strings.At(m_current)); }
   bool              Select(const int index);
   bool              SelectByText(const string text);
   bool              SelectByValue(const long value);
   //--- data (read only)
   long              Value(void) { return(m_values.At(m_current));  }
   int               Count(void) const { return m_strings.Total(); }
   //--- state
   virtual bool      Show(void);
   void              SetColorMode(const bool c){ m_color_mode = c; }

protected:
   //--- create dependent controls
           bool      CreateRow(const int index);
   //--- event handlers
   virtual bool      OnResize(void);
   //--- handlers of the dependent controls events
   virtual bool      OnVScrollShow(void);
   virtual bool      OnVScrollHide(void);
   virtual bool      OnScrollLineDown(void);
   virtual bool      OnScrollLineUp(void);
   virtual bool      OnItemClick(const int index);
   //--- redraw
   virtual bool      Redraw(void);
   virtual bool      RowState(const int index,const bool select);
   bool              CheckView(void);
  };
//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CListView)
   ON_INDEXED_EVENT(ON_CLICK,m_rows,OnItemClick)
EVENT_MAP_END(CWndClient)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CListView::CListView(void) : m_offset(0),
                             m_total_view(0),
                             m_item_height(CONTROLS_LIST_ITEM_HEIGHT),
                             m_current(CONTROLS_INVALID_INDEX),
                             m_height_variable(false),
                             m_color_mode(false)
  {
  RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CListView::~CListView(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CListView::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
   int y=y2;
//--- if the number of visible rows is previously determined, adjust the vertical size
   if(!TotalView((y2-y1)/m_item_height))
      y=m_item_height+y1+2*CONTROLS_BORDER_WIDTH;
//--- check the number of visible rows
   if(m_total_view<1)
      return(false);
//--- call method of the parent class
   if(!CWndClient::Create(chart,name,subwin,x1,y1,x2,y))
      return(false);
//--- set up
   if(!m_background.ColorBackground(CONTROLS_LIST_COLOR_BG))
      return(false);
   if(!m_background.ColorBorder(CONTROLS_LIST_COLOR_BORDER))
      return(false);
//--- create dependent controls
   ArrayResize(m_rows,m_total_view);
   for(int i=0;i<m_total_view;i++)
     {
      if(!CreateRow(i))
         return(false);
      if(m_height_variable && i>0)
         m_rows[i].Hide();
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Delete group of controls                                         |
//+------------------------------------------------------------------+
void CListView::Destroy(const int reason)
  {
//--- call of the method of the parent class
   CWndClient::Destroy(reason);
//--- clear items
   m_strings.Clear();
   m_values.Clear();
//---
   m_offset    =0;
   m_total_view=0;
  }
//+------------------------------------------------------------------+
//| Set parameter                                                    |
//+------------------------------------------------------------------+
bool CListView::TotalView(const int value)
  {
//--- if parameter is not equal to 0, modifications are not possible
   if(m_total_view!=0)
     {
      m_height_variable=true;
      return(false);
     }
//--- save value
   m_total_view=value;
//--- parameter has been changed
   return(true);
  }
//+------------------------------------------------------------------+
//| Makes the control visible                                        |
//+------------------------------------------------------------------+
bool CListView::Show(void)
  {
//--- call of the method of the parent class
   CWndClient::Show();
//--- number of items
   int total=m_strings.Total();
//---
   if(total==0)
      total=1;
//---
   if(m_height_variable && total<m_total_view)
      for(int i=total;i<m_total_view;i++)
         m_rows[i].Hide();
  for(int i = m_total_view; i < ArraySize(m_rows); i++)
    m_rows[i].Hide();
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create "row"                                                     |
//+------------------------------------------------------------------+
bool CListView::CreateRow(const int index)
  {
//--- calculate coordinates
   int x1=CONTROLS_BORDER_WIDTH;
   int y1=CONTROLS_BORDER_WIDTH+m_item_height*index;
   int x2=Width()-2*CONTROLS_BORDER_WIDTH;
   int y2=y1+m_item_height;
//--- create
   if(!m_rows[index].Create(m_chart_id,m_name+"Item"+IntegerToString(index),m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_rows[index].Text(""))
      return(false);
   if(!m_rows[index].ReadOnly(true))
      return(false);
   if(!RowState(index,false))
      return(false);
   if(!Add(m_rows[index]))
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Add item (row)                                                   |
//+------------------------------------------------------------------+
bool CListView::AddItem(const string item,const long value)
  {
//--- method left for compatibility with previous version
   return(ItemAdd(item,value));
  }
//+------------------------------------------------------------------+
//| Add item (row)                                                   |
//+------------------------------------------------------------------+
bool CListView::ItemAdd(const string item,const long value)
  {
//--- add
   if(!m_strings.Add(item))
      return(false);
   if(!m_values.Add((value != -1)?value:m_values.Total()))
      return(false);
//--- number of items
   int total=m_strings.Total();
//--- exit if number of items does not exceed the size of visible area
   if(total<m_total_view+1)
     {
      if(m_height_variable && total!=1)
        {
         Height(total*m_item_height+2*CONTROLS_BORDER_WIDTH);
         if(IS_VISIBLE)
            m_rows[total-1].Show();
        }
      return(Redraw());
     }
//--- if number of items exceeded the size of visible area
   if(total==m_total_view+1)
     {
      //--- enable vertical scrollbar
      if(!VScrolled(true))
         return(false);
      //--- and immediately make it invisible (if needed)
      if(IS_VISIBLE && !OnVScrollShow())
         return(false);
     }
//--- set up the scrollbar
   m_scroll_v.MaxPos(m_strings.Total()-m_total_view);
//--- redraw
   return(Redraw());
  }
//+------------------------------------------------------------------+
//| Insert item (row)                                                |
//+------------------------------------------------------------------+
bool CListView::ItemInsert(const int index,const string item,const long value)
  {
//--- insert
   if(!m_strings.Insert(item,index))
      return(false);
   if(!m_values.Insert(value,index))
      return(false);
//--- number of items
   int total=m_strings.Total();
//--- exit if number of items does not exceed the size of visible area
   if(total<m_total_view+1)
     {
      if(m_height_variable && total!=1)
        {
         Height(total*m_item_height+2*CONTROLS_BORDER_WIDTH);
         if(IS_VISIBLE)
            m_rows[total-1].Show();
        }
      return(Redraw());
     }
//--- if number of items exceeded the size of visible area
   if(total==m_total_view+1)
     {
      //--- enable vertical scrollbar
      if(!VScrolled(true))
         return(false);
      //--- and immediately make it invisible (if needed)
      if(IS_VISIBLE && !OnVScrollShow())
         return(false);
     }
//--- set up the scrollbar
   m_scroll_v.MaxPos(m_strings.Total()-m_total_view);
//--- redraw
   return(Redraw());
  }
//+------------------------------------------------------------------+
//| Update item (row)                                                |
//+------------------------------------------------------------------+
bool CListView::ItemUpdate(const int index,const string item,const long value)
  {
//--- update
   if(!m_strings.Update(index,item))
      return(false);
   if(!m_values.Update(index,value))
      return(false);
//--- redraw
   return(Redraw());
  }
//+------------------------------------------------------------------+
//| Delete item (row)                                                |
//+------------------------------------------------------------------+
bool CListView::ItemDelete(const int index)
  {
//--- delete
   if(!m_strings.Delete(index))
      return(false);
   if(!m_values.Delete(index))
      return(false);
//--- number of items
   int total=m_strings.Total();
//--- exit if number of items does not exceed the size of visible area
   if(total<m_total_view)
     {
      if(m_height_variable && total!=0)
        {
         Height(total*m_item_height+2*CONTROLS_BORDER_WIDTH);
         m_rows[total].Hide();
        }
      return(Redraw());
     }
//--- if number of items exceeded the size of visible area
   if(total==m_total_view)
     {
      //--- disable vertical scrollbar
      if(!VScrolled(false))
         return(false);
      //--- and immediately make it unvisible
      if(!OnVScrollHide())
         return(false);
     }
//--- set up the scrollbar
   m_scroll_v.MaxPos(m_strings.Total()-m_total_view);
//--- redraw
   return(Redraw());
  }
//+------------------------------------------------------------------+
//| Delete all items                                                 |
//+------------------------------------------------------------------+
bool CListView::ItemsClear(void)
  {
   m_offset=0;
   m_current = CONTROLS_INVALID_INDEX;
//--- clear
   if(!m_strings.Shutdown())
      return(false);
   if(!m_values.Shutdown())
      return(false);
//---
   if(m_height_variable)
     {
      Height(m_item_height+2*CONTROLS_BORDER_WIDTH);
      for(int i=1;i<m_total_view;i++)
         m_rows[i].Hide();
     }
//--- disable vertical scrollbar
   m_scroll_v.CurrPos(0);
   if(!VScrolled(false))
      return(false);
//--- and immediately make it unvisible (if needed)
   if(!OnVScrollHide())
      return(false);
//--- redraw
   return(Redraw());
  }
//+------------------------------------------------------------------+
//| Sett current item                                                |
//+------------------------------------------------------------------+
bool CListView::Select(const int index)
  {
//--- check index
   if(index>=m_strings.Total())
      return(false);
   if(index<0 && index!=CONTROLS_INVALID_INDEX)
      return(false);
//--- unselect
   if(m_current!=CONTROLS_INVALID_INDEX)
      RowState(m_current-m_offset,false);
//--- select
   if(index!=CONTROLS_INVALID_INDEX)
      RowState(index-m_offset,true);
//--- save value
   m_current=index;
//--- succeed
   return(CheckView());
  }
//+------------------------------------------------------------------+
//| Set current item (by text)                                       |
//+------------------------------------------------------------------+
bool CListView::SelectByText(const string text)
  {
//--- find text
   int index=m_strings.SearchLinear(text);
//--- if text is not found, exit without changing the selection
   if(index==CONTROLS_INVALID_INDEX)
      return(false);
//--- change selection
   return(Select(index));
  }
//+------------------------------------------------------------------+
//| Set current item (by value)                                      |
//+------------------------------------------------------------------+
bool CListView::SelectByValue(const long value)
  {
//--- find value
   int index=m_values.SearchLinear(value);
//--- if value is not found, exit without changing the selection
   if(index==CONTROLS_INVALID_INDEX)
      return(false);
//--- change selection
   return(Select(index));
  }
//+------------------------------------------------------------------+
//| Redraw                                                           |
//+------------------------------------------------------------------+
bool CListView::Redraw(void)
  {
//--- loop by "rows"
   for(int i=0;i<m_total_view;i++)
     {
      //--- copy text
      if(!m_rows[i].Text(m_strings.At(i+m_offset)))
         return(false);
      //--- select
      if(!RowState(i,(m_current==i+m_offset)))
         return(false);
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Change state                                                     |
//+------------------------------------------------------------------+
bool CListView::RowState(const int index,const bool select)
  {
//--- check index
   if(index<0 || index>=ArraySize(m_rows))
      return(true);
//--- determine colors
   color  text_color=(select) ? CONTROLS_LISTITEM_COLOR_TEXT_SEL : CONTROLS_LISTITEM_COLOR_TEXT;
   color  back_color=(select) ? CONTROLS_LISTITEM_COLOR_BG_SEL : CONTROLS_LISTITEM_COLOR_BG;
//--- get pointer
   CEdit *item=GetPointer(m_rows[index]);
//--- recolor the "row"
   return(item.Color(text_color) && item.ColorBackground(m_color_mode ? (m_offset + index > 0 ? (color)m_values.At(m_offset + index) : back_color) : back_color) && item.ColorBorder(back_color));
  }
//+------------------------------------------------------------------+
//| Check visibility of selected row                                 |
//+------------------------------------------------------------------+
bool CListView::CheckView(void)
  {
//--- check visibility
   if(m_current>=m_offset && m_current<m_offset+m_total_view)
      return(true);
//--- selected row is not visible
   if(m_current != CONTROLS_INVALID_INDEX)
   {
     int total=m_strings.Total();
     m_offset=(total-m_current>m_total_view) ? m_current : total-m_total_view;
//--- adjust the scrollbar
     m_scroll_v.CurrPos(m_offset);
   }
//--- redraw
   return(Redraw());
  }
//+------------------------------------------------------------------+
//| Handler of resizing                                              |
//+------------------------------------------------------------------+
bool CListView::OnResize(void)
  {
//--- call of the method of the parent class
   if(!CWndClient::OnResize())
      return(false);
//--- set up the size of "row"
   if(VScrolled())
      OnVScrollShow();
   else
      OnVScrollHide();
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "Show vertical scrollbar" event                   |
//+------------------------------------------------------------------+
bool CListView::OnVScrollShow(void)
  {
//--- loop by "rows"
   for(int i=0;i<m_total_view;i++)
     {
      //--- resize "rows" according to shown vertical scrollbar
      m_rows[i].Width(Width()-(CONTROLS_SCROLL_SIZE+CONTROLS_BORDER_WIDTH));
     }
//--- check visibility
   if(!IS_VISIBLE)
     {
      m_scroll_v.Visible(false);
      return(true);
     }
//--- event is handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "Hide vertical scrollbar" event                   |
//+------------------------------------------------------------------+
bool CListView::OnVScrollHide(void)
  {
//--- check visibility
   if(!IS_VISIBLE)
      return(true);
//--- loop by "rows"
   for(int i=0;i<m_total_view;i++)
     {
      //--- resize "rows" according to hidden vertical scroll bar
      m_rows[i].Width(Width()-2*CONTROLS_BORDER_WIDTH);
     }
//--- event is handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "Scroll up for one row" event                     |
//+------------------------------------------------------------------+
bool CListView::OnScrollLineUp(void)
  {
//--- get new offset
   m_offset=m_scroll_v.CurrPos();
//--- redraw
   return(Redraw());
  }
//+------------------------------------------------------------------+
//| Handler of the "Scroll down for one row" event                   |
//+------------------------------------------------------------------+
bool CListView::OnScrollLineDown(void)
  {
//--- get new offset
   m_offset=m_scroll_v.CurrPos();
//--- redraw
   return(Redraw());
  }
//+------------------------------------------------------------------+
//| Handler of click on row                                          |
//+------------------------------------------------------------------+
bool CListView::OnItemClick(const int index)
  {
//--- select "row"
   Select(index+m_offset);
//--- send notification
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_CHANGE,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Resources                                                        |
//+------------------------------------------------------------------+
//--- Can not place the same file into resource twice
#resource "\\Include\\Controls\\res\\DropOn.bmp"                 // image file
#resource "\\Include\\Controls\\res\\DropOff.bmp"                // image file
//+------------------------------------------------------------------+
//| Class CComboBox                                                  |
//| Usage: drop-down list                                            |
//+------------------------------------------------------------------+
class CComboBox : public CWndContainer
  {
protected:
   //--- dependent controls
   CEdit             m_edit;                // the entry field object
   CBmpButton        m_drop;                // the button object
   CListView         m_list;                // the drop-down list object
   //--- set up
   int               m_item_height;         // height of visible row
   int               m_view_items;          // number of visible rows in the drop-down list

public:
                     CComboBox(void);
                    ~CComboBox(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- fill
   bool              AddItem(const string item,const long value=-1);
   //--- set up
   void              ListViewItems(const int value) { m_view_items=value; }
   //--- data
   virtual bool      ItemAdd(const string item,const long value=-1)                    { return(m_list.ItemAdd(item,value));          }
   virtual bool      ItemInsert(const int index,const string item,const long value=-1) { return(m_list.ItemInsert(index,item,value)); }
   virtual bool      ItemUpdate(const int index,const string item,const long value=-1) { return(m_list.ItemUpdate(index,item,value)); }
   virtual bool      ItemDelete(const int index)                                      { return(m_list.ItemDelete(index));            }
   virtual bool      ItemsClear(void)                                                 { return(m_list.ItemsClear());                 }
   //--- data
   string            Select(void) { return(m_edit.Text()); }
   bool              Select(const int index);
   bool              SelectByText(const string text);
   bool              SelectByValue(const long value);
   //--- data (read only)
   long              Value(void) { return(m_list.Value()); }
   //--- state
   virtual bool      Show(void);
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);

protected:
   //--- create dependent controls
   virtual bool      CreateEdit(void);
   virtual bool      CreateButton(void);
   virtual bool      CreateList(void);
   //--- handlers of the dependent controls events
   virtual bool      OnClickEdit(void);
   virtual bool      OnClickButton(void);
   virtual bool      OnChangeList(void);

   virtual bool      OnEnable(void) override;
   virtual bool      OnDisable(void) override;
   //--- show drop-down list
   bool              ListShow(void);
   bool              ListHide(void);
   void              CheckListHide(const int id,int x,int y);
  };
//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CComboBox)
   ON_EVENT(ON_CLICK,m_edit,OnClickEdit)
   ON_EVENT(ON_CLICK,m_drop,OnClickButton)
   ON_EVENT(ON_CHANGE,m_list,OnChangeList)
CheckListHide(id,(int)lparam,(int)dparam);
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
EVENT_MAP_END(CWndContainer)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CComboBox::CComboBox(void) : m_item_height(CONTROLS_COMBO_ITEM_HEIGHT),
                             m_view_items(CONTROLS_COMBO_ITEMS_VIEW)

  {
  RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CComboBox::~CComboBox(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CComboBox::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- check height
   if(y2-y1<CONTROLS_COMBO_MIN_HEIGHT)
      return(false);
//--- call method of the parent class
   if(!CWndContainer::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create dependent controls
   if(!CreateEdit())
      return(false);
   if(!CreateButton())
      return(false);
   if(!CreateList())
      return(false);
//--- succeeded
   return(true);
  }
//+------------------------------------------------------------------+
//| Create main entry field                                          |
//+------------------------------------------------------------------+
bool CComboBox::CreateEdit(void)
  {
//--- create
   if(!m_edit.Create(m_chart_id,m_name+"Edit",m_subwin,0,0,Width(),Height()))
      return(false);
   if(!m_edit.Text(""))
      return(false);
   if(!m_edit.ReadOnly(true))
      return(false);
   if(!Add(m_edit))
      return(false);
//--- succeeded
   return(true);
  }
//+------------------------------------------------------------------+
//| Create button                                                    |
//+------------------------------------------------------------------+
bool CComboBox::CreateButton(void)
  {
//--- right align button (try to make equal offsets from top and bottom)
   int x1=Width()-(CONTROLS_BUTTON_SIZE+CONTROLS_COMBO_BUTTON_X_OFF);
   int y1=(Height()-CONTROLS_BUTTON_SIZE)/2;
   int x2=x1+CONTROLS_BUTTON_SIZE;
   int y2=y1+CONTROLS_BUTTON_SIZE;
//--- create
   if(!m_drop.Create(m_chart_id,m_name+"Drop",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_drop.BmpNames("::Include\\Controls\\res\\DropOff.bmp","::Include\\Controls\\res\\DropOn.bmp"))
      return(false);
   if(!Add(m_drop))
      return(false);
   m_drop.Locking(true);
//--- succeeded
   return(true);
  }
//+------------------------------------------------------------------+
//| Create drop-down list                                            |
//+------------------------------------------------------------------+
bool CComboBox::CreateList(void)
  {
//--- create
   if(m_list.TotalView(m_view_items))
     {
      if(!m_list.Create(m_chart_id,m_name+"List",m_subwin,0,Height(),Width(),0))
         return(false);
      if(!Add(m_list))
         return(false);
      m_list.Hide();
     }
//--- succeeded
   return(true);
  }
//+------------------------------------------------------------------+
//| Add item (row)                                                   |
//+------------------------------------------------------------------+
bool CComboBox::AddItem(const string item,const long value)
  {
//--- add item to list
   return(m_list.AddItem(item,value));
  }
//+------------------------------------------------------------------+
//| Select item                                                      |
//+------------------------------------------------------------------+
bool CComboBox::Select(const int index)
  {
   if(!m_list.Select(index))
      return(false);
//--- call the handler
   return(OnChangeList());
  }
//+------------------------------------------------------------------+
//| Select item (by text)                                            |
//+------------------------------------------------------------------+
bool CComboBox::SelectByText(const string text)
  {
   if(!m_list.SelectByText(text))
      return(false);
//--- call the handler
   return(OnChangeList());
  }
//+------------------------------------------------------------------+
//| Select item (by value)                                           |
//+------------------------------------------------------------------+
bool CComboBox::SelectByValue(const long value)
  {
   if(!m_list.SelectByValue(value))
      return(false);
//--- call the handler
   return(OnChangeList());
  }
//+------------------------------------------------------------------+
//| Makes the control visible                                        |
//+------------------------------------------------------------------+
bool CComboBox::Show(void)
  {
   m_edit.Show();
   if(IS_ENABLED)
   {
     m_drop.Show();
   }
   else
   {
     m_drop.Hide();
     m_edit.Color(CONTROLS_EDIT_COLOR_BORDER);
   }
   m_list.Hide();
//--- call of the method of the parent class
   return(CWnd::Show());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CComboBox::Save(const int file_handle)
  {
//--- check
   if(file_handle==INVALID_HANDLE)
      return(false);
//---
   FileWriteLong(file_handle,Value());
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CComboBox::Load(const int file_handle)
  {
//--- check
   if(file_handle==INVALID_HANDLE)
      return(false);
//---
   if(!FileIsEnding(file_handle))
      SelectByValue(FileReadLong(file_handle));
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of click on main entry field                             |
//+------------------------------------------------------------------+
bool CComboBox::OnClickEdit(void)
  {
//--- change button state
   if(!m_drop.Pressed(!m_drop.Pressed()))
      return(false);
//--- call the click on button handler
   return(OnClickButton());
  }
//+------------------------------------------------------------------+
//| Handler of click on button                                       |
//+------------------------------------------------------------------+
bool CComboBox::OnClickButton(void)
  {
//--- show or hide the drop-down list depending on the button state
   return((m_drop.Pressed()) ? ListShow() : ListHide());
  }
//+------------------------------------------------------------------+
//| Handler of click on drop-down list                               |
//+------------------------------------------------------------------+
bool CComboBox::OnChangeList(void)
  {
   string text=m_list.Select();
//--- hide the list, depress the button
   ListHide();
   m_drop.Pressed(false);
//--- set text in the main entry field
   m_edit.Text(text);
//--- send notification
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_CHANGE,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Show the drop-down list                                          |
//+------------------------------------------------------------------+
bool CComboBox::ListShow(void)
  {
   BringToTop();
//--- show the list
   return(m_list.Show());
  }
//+------------------------------------------------------------------+
//| Hide drop-down list                                              |
//+------------------------------------------------------------------+
bool CComboBox::ListHide(void)
  {
//--- hide the list
   return(m_list.Hide());
  }
//+------------------------------------------------------------------+
//| Hide the drop-down element if necessary                          |
//+------------------------------------------------------------------+
void CComboBox::CheckListHide(const int id,int x,int y)
  {
//--- check event ID
   if(id!=CHARTEVENT_CLICK)
      return;
//--- check visibility of the drop-down element
   if(!m_list.IsVisible())
      return;
//--- check mouse cursor's position
   y-=(int)ChartGetInteger(m_chart_id,CHART_WINDOW_YDISTANCE,m_subwin);
   if(!m_edit.Contains(x,y) && !m_list.Contains(x,y))
     {
      m_drop.Pressed(false);
      m_list.Hide();
     }
  }
//+------------------------------------------------------------------+
bool CComboBox::OnEnable(void)
{
  //m_drop.ZOrder(0);
  m_drop.Show();
  m_edit.Color(CONTROLS_EDIT_COLOR);
  return(true);
}

bool CComboBox::OnDisable(void)
{
  //m_drop.ZOrder(-100);
  m_drop.Hide();
  m_edit.Color(CONTROLS_EDIT_COLOR_BORDER);
  return(true);
}

class ComboBoxResizable: public CComboBox
{
  public:
    ComboBoxResizable()
    {
      RTTI;
    }
    
    virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam) override;

    virtual bool OnResize(void) override
    {
      m_edit.Width(Width());
      
      int x1 = Width() - (CONTROLS_BUTTON_SIZE + CONTROLS_COMBO_BUTTON_X_OFF);
      int y1 = (Height() - CONTROLS_BUTTON_SIZE) / 2;
      m_drop.Move(Left() + x1, Top() + y1);
      
      m_list.Width(Width());

      return CWndContainer::OnResize();
    }
    
    virtual bool OnClickButton(void) override
    {
      // this is a hack to trigger resizing of elements in the list
      // we need it because standard ListView is incorrectly coded in such a way
      // that elements are resized only if vscroll is present
      bool vs = m_list.VScrolled();
      if(m_drop.Pressed())
      {
        m_list.VScrolled(true);
      }
      bool b = CComboBox::OnClickButton();
      m_list.VScrolled(vs);
      return b;
    }
    /*
    virtual bool Enable(void) override
    {
      m_edit.Show();
      m_drop.Show();
      return CComboBox::Enable();
    }
    
    virtual bool Disable(void) override
    {
      m_edit.Hide();
      m_drop.Hide();
      return CComboBox::Disable();
    }*/
};

#define EXIT_ON_DISABLED \
      if(!IsEnabled())   \
      {                  \
        return false;    \
      }

EVENT_MAP_BEGIN(ComboBoxResizable)
  EXIT_ON_DISABLED
  ON_EVENT(ON_CLICK, m_drop, OnClickButton)
EVENT_MAP_END(CComboBox)
//#include <Layouts/RadioGroupResizable.mqh>
//#include <ControlsPlus/RadioGroup.mqh>
//+------------------------------------------------------------------+
//|                                                   RadioGroup.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "WndClient.mqh"
//#include "RadioButton.mqh"
//+------------------------------------------------------------------+
//|                                                  RadioButton.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "WndContainer.mqh"
//#include "BmpButton.mqh"
//#include "Edit.mqh"
//+------------------------------------------------------------------+
//| Resources                                                        |
//+------------------------------------------------------------------+
#resource "\\Include\\Controls\\res\\RadioButtonOn.bmp"
#resource "\\Include\\Controls\\res\\RadioButtonOff.bmp"
//+------------------------------------------------------------------+
//| Class CRadioButton                                               |
//| Usage: class that implements the "RadioButton" control           |
//+------------------------------------------------------------------+
class CRadioButton : public CWndContainer
  {
private:
   //--- dependent controls
   CBmpButton        m_button;              // button object
   CEdit             m_label;               // label object

public:
                     CRadioButton(void);
                    ~CRadioButton(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- settings
   string            Text(void)          const { return(m_label.Text());         }
   bool              Text(const string value)  { return(m_label.Text(value));    }
   color             Color(void)         const { return(m_label.Color());        }
   bool              Color(const color value)  { return(m_label.Color(value));   }
   //--- state
   bool              State(void)         const { return(m_button.Pressed());     }
   bool              State(const bool flag)    { return(m_button.Pressed(flag)); }

protected:
   //--- create dependent controls
   virtual bool      CreateButton(void);
   virtual bool      CreateLabel(void);
   //--- handlers of the dependent controls events
   virtual bool      OnClickButton(void);
   virtual bool      OnClickLabel(void);

   // we can't do this with new subclass, because RadioGroup instantiates RadioButton's
   // automatically via ArrayResize of object array (not pointer array),
   // so it's impossible to store a derived class pointer there
   virtual bool      OnResize(void) override;
  };
//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CRadioButton)
   ON_EVENT(ON_CLICK,m_button,OnClickButton)
   ON_EVENT(ON_CLICK,m_label,OnClickLabel)
EVENT_MAP_END(CWndContainer)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CRadioButton::CRadioButton(void)
{
  RTTI;
}
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CRadioButton::~CRadioButton(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CRadioButton::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndContainer::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create dependent controls
   if(!CreateButton())
      return(false);
   if(!CreateLabel())
      return(false);
//--- succeeded
   return(true);
  }
//+------------------------------------------------------------------+
//| Create button                                                    |
//+------------------------------------------------------------------+
bool CRadioButton::CreateButton(void)
  {
//--- calculate coordinates
   int x1=CONTROLS_RADIO_BUTTON_X_OFF;
   int y1=CONTROLS_RADIO_BUTTON_Y_OFF;
   int x2=x1+CONTROLS_BUTTON_SIZE;
   int y2=y1+CONTROLS_BUTTON_SIZE-CONTROLS_BORDER_WIDTH;
//--- create
   if(!m_button.Create(m_chart_id,m_name+"Button",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_button.BmpNames("::Include\\Controls\\res\\RadioButtonOff.bmp","::Include\\Controls\\res\\RadioButtonOn.bmp"))
      return(false);
   if(!Add(m_button))
      return(false);
   m_button.Locking(true);
//--- succeeded
   return(true);
  }
//+------------------------------------------------------------------+
//| Create label                                                     |
//+------------------------------------------------------------------+
bool CRadioButton::CreateLabel(void)
  {
//--- calculate coordinates
   int x1=CONTROLS_RADIO_LABEL_X_OFF;
   int y1=CONTROLS_RADIO_LABEL_Y_OFF;
   int x2=Width();
   int y2=Height();
//--- create
   if(!m_label.Create(m_chart_id,m_name+"Label",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_label.Text(m_name))
      return(false);
   if(!Add(m_label))
      return(false);
   m_label.ReadOnly(true);
   m_label.ColorBackground(CONTROLS_CHECKGROUP_COLOR_BG);
   m_label.ColorBorder(CONTROLS_CHECKGROUP_COLOR_BG);
//--- succeeded
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of click on button                                       |
//+------------------------------------------------------------------+
bool CRadioButton::OnClickButton(void)
  {
//--- if button is in the "turned off" state, turn it on again and complete the handling
//--- this is due to that radio button can not be turned off by clicking on it (it can be only turned on)
   if(!m_button.Pressed())
     {
      //--- turn on the radio button
      if(!m_button.Pressed(true))
         return(false);
     }
//--- send the "changed state" event
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_CHANGE,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of click on label                                        |
//+------------------------------------------------------------------+
bool CRadioButton::OnClickLabel(void)
  {
//--- if button is in the "turned on" state, simply complete the handling
//--- this is due to that radio button can not be turned off by clicking on it (it can be only turned on)
   if(m_button.Pressed())
      return(true);
//--- turn on the radio button
   m_button.Pressed(true);
//--- return the result of the button click handler
   return(OnClickButton());
  }
//+------------------------------------------------------------------+
//| Resize the chart object                                          |
//+------------------------------------------------------------------+
bool CRadioButton::OnResize(void)
{
  // resize CEdit control, this will trigger OnResize
  // for the label chart object (button is always the same size)
  m_label.Width(m_rect.Width() - CONTROLS_SCROLL_SIZE - CONTROLS_BORDER_WIDTH);
  return true;
}
//#include <Arrays\ArrayString.mqh>
//#include <Arrays\ArrayLong.mqh>
//+------------------------------------------------------------------+
//| Class CRadioGroup                                                |
//| Usage: view and edit radio buttons                               |
//+------------------------------------------------------------------+
class CRadioGroup : public CWndClient
  {
protected:
   //--- dependent controls
   CRadioButton      m_rows[];              // array of the row objects
   //--- set up
   int               m_offset;              // index of first visible row in array of rows
   int               m_total_view;          // number of visible rows
   int               m_item_height;         // height of visible row
   //--- data
   CArrayString      m_strings;             // array of rows
   CArrayLong        m_values;              // array of values
   int               m_current;             // index of current row in array of rows

public:
                     CRadioGroup(void);
                    ~CRadioGroup(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   virtual void      Destroy(const int reason=0);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- fill
   virtual bool      AddItem(const string item,const long value=0);
   //--- data
   long              Value(void) const { return(m_values.At(m_current)); }
   bool              Value(const long value);
   bool              ValueCheck(long value) const;
   //--- state
   virtual bool      Show(void);
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);

protected:
   //--- create dependent controls
           bool      CreateButton(const int index);
   //--- handlers of the dependent controls events
   virtual bool      OnVScrollShow(void);
   virtual bool      OnVScrollHide(void);
   virtual bool      OnScrollLineDown(void);
   virtual bool      OnScrollLineUp(void);
   virtual bool      OnChangeItem(const int row_index);
   //--- redraw
   virtual bool      Redraw(void);
   bool              RowState(const int index,const bool select);
   void              Select(const int index);
  };
//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CRadioGroup)
   ON_INDEXED_EVENT(ON_CHANGE,m_rows,OnChangeItem)
EVENT_MAP_END(CWndClient)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CRadioGroup::CRadioGroup(void) : m_offset(0),
                                 m_total_view(0),
                                 m_item_height(CONTROLS_LIST_ITEM_HEIGHT),
                                 m_current(CONTROLS_INVALID_INDEX)
  {
  RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CRadioGroup::~CRadioGroup(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CRadioGroup::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- determine the number of visible rows
   m_total_view=(y2-y1)/m_item_height;
//--- check the number of visible rows
   if(m_total_view<1)
      return(false);
//--- call method of the parent class
   if(!CWndClient::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- set up
   if(!m_background.ColorBackground(CONTROLS_RADIOGROUP_COLOR_BG))
      return(false);
   if(!m_background.ColorBorder(CONTROLS_RADIOGROUP_COLOR_BORDER))
      return(false);
//--- create dependent controls
   ArrayResize(m_rows,m_total_view);
   for(int i=0;i<m_total_view;i++)
      if(!CreateButton(i))
         return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Delete group of controls                                         |
//+------------------------------------------------------------------+
void CRadioGroup::Destroy(const int reason)
  {
//--- call of the method of the parent class
   CWndClient::Destroy(reason);
//--- clear items
   m_strings.Clear();
   m_values.Clear();
  }
//+------------------------------------------------------------------+
//| Create "row"                                                     |
//+------------------------------------------------------------------+
bool CRadioGroup::CreateButton(const int index)
  {
//--- calculate coordinates
   int x1=CONTROLS_BORDER_WIDTH;
   int y1=CONTROLS_BORDER_WIDTH+m_item_height*index;
   int x2=Width()-CONTROLS_BORDER_WIDTH;
   int y2=y1+m_item_height;
//--- create
   if(!m_rows[index].Create(m_chart_id,m_name+"Item"+IntegerToString(index),m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_rows[index].Text(""))
      return(false);
   if(!Add(m_rows[index]))
      return(false);
   m_rows[index].Hide();
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Add item (row)                                                   |
//+------------------------------------------------------------------+
bool CRadioGroup::AddItem(const string item,const long value)
  {
//--- check value
   if(value!=0 && !ValueCheck(value))
      return(false);
//--- add
   if(!m_strings.Add(item))
      return(false);
   if(!m_values.Add(value))
      return(false);
//--- number of items
   int total=m_strings.Total();
//--- exit if number of items does not exceed the size of visible area
   if(total<m_total_view+1)
     {
      if(IS_VISIBLE && total!=0)
         m_rows[total-1].Show();
      return(Redraw());
     }
//--- if number of items exceeded the size of visible area
   if(total==m_total_view+1)
     {
      //--- enable vertical scrollbar
      if(!VScrolled(true))
         return(false);
      //--- and immediately make it invisible (if needed)
      if(!IS_VISIBLE)
         m_scroll_v.Visible(false);

     }
//--- set up the scrollbar
   m_scroll_v.MaxPos(m_strings.Total()-m_total_view);
//--- redraw
   return(Redraw());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CRadioGroup::Value(const long value)
  {
//--- ???????? ?????? ?????????????? ? ??????
   int total=m_values.Total();
//---
   for(int i=0;i<total;i++)
      if(m_values.At(i)==value)
         Select(i+m_offset);
//---
   return(true);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CRadioGroup::ValueCheck(long value) const
  {
//--- ??????????? ???????? ?? ?????? ??????????? ??? ????????????
   int total=m_values.Total();
//---
   for(int i=0;i<total;i++)
      if(m_values.At(i)==value)
         return(false);
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Makes the group visible                                          |
//+------------------------------------------------------------------+
bool CRadioGroup::Show(void)
{
  // call of the method of the parent class
  if(!CWndClient::Show())
    return(false);
  // loop by rows
  int total = m_values.Total();
  for(int i = total; i < m_total_view; i++)
    m_rows[i].Hide();
  for(int i = m_total_view; i < ArraySize(m_rows); i++)
    m_rows[i].Hide();
  // handled
  return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CRadioGroup::Save(const int file_handle)
  {
//--- check
   if(file_handle==INVALID_HANDLE)
      return(false);
//---
   FileWriteLong(file_handle,Value());
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CRadioGroup::Load(const int file_handle)
  {
//--- check
   if(file_handle==INVALID_HANDLE)
      return(false);
//---
   if(!FileIsEnding(file_handle))
      Value(FileReadLong(file_handle));
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Sett current item                                                |
//+------------------------------------------------------------------+
void CRadioGroup::Select(const int index)
  {
//--- disable the "ON" state
   if(m_current!=-1)
      RowState(m_current-m_offset,false);
//--- enable the "ON" state
   if(index!=-1)
      RowState(index-m_offset,true);
//--- save value
   m_current=index;
  }
//+------------------------------------------------------------------+
//| Redraw                                                           |
//+------------------------------------------------------------------+
bool CRadioGroup::Redraw(void)
  {
//--- loop by "rows"
   for(int i=0;i<m_total_view;i++)
     {
      //--- copy text
      if(!m_rows[i].Text(m_strings.At(i+m_offset)))
         return(false);
      //--- select
      if(!RowState(i,(m_current==i+m_offset)))
         return(false);
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Change state                                                     |
//+------------------------------------------------------------------+
bool CRadioGroup::RowState(const int index,const bool select)
  {
//--- check index
   if(index<0 || index>=ArraySize(m_rows))
      return(true);
//--- change state
   return(m_rows[index].State(select));
  }
//+------------------------------------------------------------------+
//| Handler of the "Show vertical scrollbar" event                   |
//+------------------------------------------------------------------+
bool CRadioGroup::OnVScrollShow(void)
  {
//--- loop by "rows"
   for(int i=0;i<m_total_view;i++)
     {
      //--- resize "rows" according to shown vertical scrollbar
      m_rows[i].Width(Width()-(CONTROLS_SCROLL_SIZE+2*CONTROLS_BORDER_WIDTH+1));
     }
//--- check visibility
   if(!IS_VISIBLE)
     {
      m_scroll_v.Visible(false);
      return(true);
     }
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "Hide vertical scrollbar" event                   |
//+------------------------------------------------------------------+
bool CRadioGroup::OnVScrollHide(void)
  {
//--- check visibility
   if(!IS_VISIBLE)
      return(true);
//--- loop by "rows"
   for(int i=0;i<m_total_view;i++)
     {
      //--- resize "rows" according to hidden vertical scroll bar
      m_rows[i].Width(Width()-2*CONTROLS_BORDER_WIDTH-1);
     }
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "Scroll up for one row" event                     |
//+------------------------------------------------------------------+
bool CRadioGroup::OnScrollLineUp(void)
  {
//--- get new offset
   m_offset=m_scroll_v.CurrPos();
//--- redraw
   return(Redraw());
  }
//+------------------------------------------------------------------+
//| Handler of the "Scroll down for one row" event                   |
//+------------------------------------------------------------------+
bool CRadioGroup::OnScrollLineDown(void)
  {
//--- get new offset
   m_offset=m_scroll_v.CurrPos();
//--- redraw
   return(Redraw());
  }
//+------------------------------------------------------------------+
//| Handler of changing a "row" state                                |
//+------------------------------------------------------------------+
bool CRadioGroup::OnChangeItem(const int row_index)
  {
//--- select "row"
   Select(row_index+m_offset);
//--- send notification
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_CHANGE,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//#include "GroupTemplate.mqh"
//+------------------------------------------------------------------+
//|                                                GroupTemplate.mqh |
//|                                    Copyright (c) 2020, Marketeer |
//|                          https://www.mql5.com/en/users/marketeer |
//| Core CheckGroupResizable, RadioGroupResizable, ListViewResizable |
//+------------------------------------------------------------------+

template<typename T>
class GroupTemplate: public T // T = CCheckGroup, CRadioGroup, CListView
{
  protected:
    int WIDTH_ADJUSTMENT;

  public:
    GroupTemplate(): WIDTH_ADJUSTMENT(0)
    {
      RTTI;
    }

  protected:
    virtual bool isSelected(const int index) = 0;
    virtual bool createElement(const int index) = 0;
    
    virtual bool Redraw(void) override
    {
      for(int i = 0; i < ArraySize(m_rows); i++)
      {
        if(i + m_offset < m_strings.Total() && i < m_total_view)
        {
          m_rows[i].Show();
          CRect r;
          r.left = Left() + CONTROLS_BORDER_WIDTH;
          r.top = Top() + CONTROLS_BORDER_WIDTH + m_item_height * i;
          r.right = Right() - 2*CONTROLS_BORDER_WIDTH - 1 /*- WIDTH_ADJUSTMENT*/ - (m_scroll_v.IsVisible() ? CONTROLS_SCROLL_SIZE : 0);
          r.bottom = r.top + m_item_height;
          
          m_rows[i].Move(r.left, r.top);
          m_rows[i].Size(r.right - r.left, r.bottom - r.top);
          
          m_rows[i].Text(m_strings[i + m_offset]);
          RowState(i, isSelected(i + m_offset));
        }
        else
        {
          m_rows[i].Hide();
        }
      }
      return true;
    }
  
    virtual bool OnResize(void) override
    {
      if(!IsVisible()) return true;

      int new_total_view = (Height() - 2 * CONTROLS_BORDER_WIDTH) / m_item_height;

      // if minimized/hidden
      if(new_total_view < 0) return T::OnResize();
      
      const int n = ArraySize(m_rows);
      
      if(new_total_view > n                     // first time expanding
      && n < m_strings.Total())
      {
        const int m = MathMin(m_strings.Total(), new_total_view);

        ArrayResize(m_rows, m);
        for(int i = n; i < m; i++)
        {
          if(!createElement(i)) return(false);
          m_rows[i].Show();
          m_rows[i].Id(rand() | (rand() << 32));
        }
        m_total_view = m;
        m_scroll_v.MaxPos(m_strings.Total() - m_total_view + m_offset);
      }
      else
      if(new_total_view < m_total_view)
      {
        for(int i = new_total_view; i < n; i++)
        {
          m_rows[i].Hide();
        }
        m_total_view = new_total_view;
        m_scroll_v.MaxPos(m_strings.Total() - m_total_view + m_offset);
      }
      else
      if(new_total_view > m_total_view
      && new_total_view <= n)                // second time expanding
      {
        for(int i = m_total_view; i < new_total_view; i++)
        {
          m_rows[i].Show();
        }
        m_total_view = new_total_view;
        m_scroll_v.MaxPos(m_strings.Total() - m_total_view + m_offset);
      }
      Redraw();
      
      return T::OnResize();
    }
};

class RadioGroupResizable: public GroupTemplate<CRadioGroup>
{
  public:
    RadioGroupResizable()
    {
      RTTI;
      WIDTH_ADJUSTMENT = CONTROLS_BUTTON_SIZE;
    }

  protected:
    virtual bool isSelected(const int index) override
    {
      return m_current == index;
    }

    virtual bool createElement(const int index) override
    {
      return CreateButton(index);
    }
};
//#include <Layouts/CheckGroupResizable.mqh>
//#include <ControlsPlus/CheckGroup.mqh>
//+------------------------------------------------------------------+
//|                                                   CheckGroup.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "WndClient.mqh"
//#include "CheckBox.mqh"
//#include <Arrays\ArrayString.mqh>
//#include <Arrays\ArrayLong.mqh>
//#include <Arrays\ArrayInt.mqh>
//+------------------------------------------------------------------+
//|                                                     ArrayInt.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "Array.mqh"
//+------------------------------------------------------------------+
//| Class CArrayInt.                                                 |
//| Puprose: Class of dynamic array of variables                     |
//|          of int or uint type.                                    |
//|          Derives from class CArray.                              |
//+------------------------------------------------------------------+
class CArrayInt : public CArray
  {
protected:
   int               m_data[];           // data array

public:
                     CArrayInt(void);
                    ~CArrayInt(void);
   //--- method of identifying the object
   virtual int       Type(void) const { return(TYPE_INT); }
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);
   //--- methods of managing dynamic memory
   bool              Reserve(const int size);
   bool              Resize(const int size);
   bool              Shutdown(void);
   //--- methods of filling the array
   bool              Add(const int element);
   bool              AddArray(const int &src[]);
   bool              AddArray(const CArrayInt *src);
   bool              Insert(const int element,const int pos);
   bool              InsertArray(const int &src[],const int pos);
   bool              InsertArray(const CArrayInt *src,const int pos);
   bool              AssignArray(const int &src[]);
   bool              AssignArray(const CArrayInt *src);
   //--- method of access to the array
   int               At(const int index) const;
   int operator[](const int index) const { return(At(index)); }
   //--- methods of searching for minimum and maximum
   int               Minimum(const int start,const int count) const { return(CArray::Minimum(m_data,start,count)); }
   int               Maximum(const int start,const int count) const { return(CArray::Maximum(m_data,start,count)); }
   //--- methods of changing
   bool              Update(const int index,const int element);
   bool              Shift(const int index,const int shift);
   //--- methods of deleting
   bool              Delete(const int index);
   bool              DeleteRange(int from,int to);
   //--- methods for comparing arrays
   bool              CompareArray(const int &array[]) const;
   bool              CompareArray(const CArrayInt *array) const;
   //--- methods for working with the sorted array
   bool              InsertSort(const int element);
   int               Search(const int element) const;
   int               SearchGreat(const int element) const;
   int               SearchLess(const int element) const;
   int               SearchGreatOrEqual(const int element) const;
   int               SearchLessOrEqual(const int element) const;
   int               SearchFirst(const int element) const;
   int               SearchLast(const int element) const;
   int               SearchLinear(const int element) const;

protected:
   virtual void      QuickSort(int beg,int end,const int mode=0);
   int               QuickSearch(const int element) const;
   int               MemMove(const int dest,const int src,int count);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CArrayInt::CArrayInt(void)
  {
//--- initialize protected data
   m_data_max=ArraySize(m_data);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CArrayInt::~CArrayInt(void)
  {
   if(m_data_max!=0)
      Shutdown();
  }
//+------------------------------------------------------------------+
//| Moving the memory within a single array                          |
//+------------------------------------------------------------------+
int CArrayInt::MemMove(const int dest,const int src,int count)
  {
   int i;
//--- check parameters
   if(dest<0 || src<0 || count<0)
      return(-1);
//--- check count
   if(src+count>m_data_total)
      count=m_data_total-src;
   if(count<0)
      return(-1);
//--- no need to copy
   if(dest==src || count==0)
      return(dest);
//--- check data total
   if(dest+count>m_data_total)
     {
      if(m_data_max<dest+count)
         return(-1);
      m_data_total=dest+count;
     }
//--- copy
   if(dest<src)
     {
      //--- copy from left to right
      for(i=0;i<count;i++)
         m_data[dest+i]=m_data[src+i];
     }
   else
     {
      //--- copy from right to left
      for(i=count-1;i>=0;i--)
         m_data[dest+i]=m_data[src+i];
     }
//--- successful
   return(dest);
  }
//+------------------------------------------------------------------+
//| Request for more memory in an array. Checks if the requested     |
//| number of free elements already exists; allocates additional     |
//| memory with a given step                                         |
//+------------------------------------------------------------------+
bool CArrayInt::Reserve(const int size)
  {
   int new_size;
//--- check
   if(size<=0)
      return(false);
//--- resize array
   if(Available()<size)
     {
      new_size=m_data_max+m_step_resize*(1+(size-Available())/m_step_resize);
      if(new_size<0)
         //--- overflow occurred when calculating new_size
         return(false);
      if((m_data_max=ArrayResize(m_data,new_size))==-1)
         m_data_max=ArraySize(m_data);
     }
//--- result
   return(Available()>=size);
  }
//+------------------------------------------------------------------+
//| Resizing (with removal of elements on the right)                 |
//+------------------------------------------------------------------+
bool CArrayInt::Resize(const int size)
  {
   int new_size;
//--- check
   if(size<0)
      return(false);
//--- resize array
   new_size=m_step_resize*(1+size/m_step_resize);
   if(m_data_max!=new_size)
     {
      if((m_data_max=ArrayResize(m_data,new_size))==-1)
        {
         m_data_max=ArraySize(m_data);
         return(false);
        }
     }
   if(m_data_total>size)
      m_data_total=size;
//--- result
   return(m_data_max==new_size);
  }
//+------------------------------------------------------------------+
//| Complete cleaning of the array with the release of memory        |
//+------------------------------------------------------------------+
bool CArrayInt::Shutdown(void)
  {
//--- check
   if(m_data_max==0)
      return(true);
//--- clean
   if(ArrayResize(m_data,0)==-1)
      return(false);
   m_data_total=0;
   m_data_max=0;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Adding an element to the end of the array                        |
//+------------------------------------------------------------------+
bool CArrayInt::Add(const int element)
  {
//--- check/reserve elements of array
   if(!Reserve(1))
      return(false);
//--- add
   m_data[m_data_total++]=element;
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Adding an element to the end of the array from another array     |
//+------------------------------------------------------------------+
bool CArrayInt::AddArray(const int &src[])
  {
   int num=ArraySize(src);
//--- check/reserve elements of array
   if(!Reserve(num))
      return(false);
//--- add
   for(int i=0;i<num;i++)
      m_data[m_data_total++]=src[i];
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Adding an element to the end of the array from another array     |
//+------------------------------------------------------------------+
bool CArrayInt::AddArray(const CArrayInt *src)
  {
   int num;
//--- check
   if(!CheckPointer(src))
      return(false);
//--- check/reserve elements of array
   num=src.Total();
   if(!Reserve(num))
      return(false);
//--- add
   for(int i=0;i<num;i++)
      m_data[m_data_total++]=src.m_data[i];
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Inserting an element in the specified position                   |
//+------------------------------------------------------------------+
bool CArrayInt::Insert(const int element,const int pos)
  {
//--- check/reserve elements of array
   if(pos<0 || !Reserve(1))
      return(false);
//--- insert
   m_data_total++;
   if(pos<m_data_total-1)
     {
      if(MemMove(pos+1,pos,m_data_total-pos-1)<0)
         return(false);
      m_data[pos]=element;
     }
   else
      m_data[m_data_total-1]=element;
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Inserting elements in the specified position                     |
//+------------------------------------------------------------------+
bool CArrayInt::InsertArray(const int &src[],const int pos)
  {
   int num=ArraySize(src);
//--- check/reserve elements of array
   if(!Reserve(num))
      return(false);
//--- insert
   if(MemMove(num+pos,pos,m_data_total-pos)<0)
      return(false);
   for(int i=0;i<num;i++)
      m_data[i+pos]=src[i];
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Inserting elements in the specified position                     |
//+------------------------------------------------------------------+
bool CArrayInt::InsertArray(const CArrayInt *src,const int pos)
  {
   int num;
//--- check
   if(!CheckPointer(src))
      return(false);
//--- check/reserve elements of array
   num=src.Total();
   if(!Reserve(num))
      return(false);
//--- insert
   if(MemMove(num+pos,pos,m_data_total-pos)<0)
      return(false);
   for(int i=0;i<num;i++)
      m_data[i+pos]=src.m_data[i];
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Assignment (copying) of another array                            |
//+------------------------------------------------------------------+
bool CArrayInt::AssignArray(const int &src[])
  {
   int num=ArraySize(src);
//--- check/reserve elements of array
   Clear();
   if(m_data_max<num)
     {
      if(!Reserve(num))
         return(false);
     }
   else
      Resize(num);
//--- copy array
   for(int i=0;i<num;i++)
     {
      m_data[i]=src[i];
      m_data_total++;
     }
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Assignment (copying) of another array                            |
//+------------------------------------------------------------------+
bool CArrayInt::AssignArray(const CArrayInt *src)
  {
   int num;
//--- check
   if(!CheckPointer(src))
      return(false);
//--- check/reserve elements of array
   num=src.m_data_total;
   Clear();
   if(m_data_max<num)
     {
      if(!Reserve(num))
         return(false);
     }
   else
      Resize(num);
//--- copy array
   for(int i=0;i<num;i++)
     {
      m_data[i]=src.m_data[i];
      m_data_total++;
     }
   m_sort_mode=src.SortMode();
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Access to data in the specified position                         |
//+------------------------------------------------------------------+
int CArrayInt::At(const int index) const
  {
//--- check
   if(index<0 || index>=m_data_total)
      return(INT_MAX);
//--- result
   return(m_data[index]);
  }
//+------------------------------------------------------------------+
//| Updating element in the specified position                       |
//+------------------------------------------------------------------+
bool CArrayInt::Update(const int index,const int element)
  {
//--- check
   if(index<0 || index>=m_data_total)
      return(false);
//--- update
   m_data[index]=element;
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Moving element from the specified position                       |
//| on the specified shift                                           |
//+------------------------------------------------------------------+
bool CArrayInt::Shift(const int index,const int shift)
  {
   int tmp_int;
//--- check
   if(index<0 || index+shift<0 || index+shift>=m_data_total)
      return(false);
   if(shift==0)
      return(true);
//--- move
   tmp_int=m_data[index];
   if(shift>0)
     {
      if(MemMove(index,index+1,shift)<0)
         return(false);
     }
   else
     {
      if(MemMove(index+shift+1,index+shift,-shift)<0)
         return(false);
     }
   m_data[index+shift]=tmp_int;
   m_sort_mode=-1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Deleting element from the specified position                     |
//+------------------------------------------------------------------+
bool CArrayInt::Delete(const int index)
  {
//--- check
   if(index<0 || index>=m_data_total)
      return(false);
//--- delete
   if(index<m_data_total-1 && MemMove(index,index+1,m_data_total-index-1)<0)
      return(false);
   m_data_total--;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Deleting range of elements                                       |
//+------------------------------------------------------------------+
bool CArrayInt::DeleteRange(int from,int to)
  {
//--- check
   if(from<0 || to<0)
      return(false);
   if(from>to || from>=m_data_total)
      return(false);
//--- delete
   if(to>=m_data_total-1)
      to=m_data_total-1;
   if(MemMove(from,to+1,m_data_total-to-1)<0)
      return(false);
   m_data_total-=to-from+1;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Equality comparison of two arrays                                |
//+------------------------------------------------------------------+
bool CArrayInt::CompareArray(const int &array[]) const
  {
//--- compare
   if(m_data_total!=ArraySize(array))
      return(false);
   for(int i=0;i<m_data_total;i++)
      if(m_data[i]!=array[i])
         return(false);
//--- equal
   return(true);
  }
//+------------------------------------------------------------------+
//| Equality comparison of two arrays                                |
//+------------------------------------------------------------------+
bool CArrayInt::CompareArray(const CArrayInt *array) const
  {
//--- check
   if(!CheckPointer(array))
      return(false);
//--- compare
   if(m_data_total!=array.m_data_total)
      return(false);
   for(int i=0;i<m_data_total;i++)
      if(m_data[i]!=array.m_data[i])
         return(false);
//--- equal
   return(true);
  }
//+------------------------------------------------------------------+
//| Method QuickSort                                                 |
//+------------------------------------------------------------------+
void CArrayInt::QuickSort(int beg,int end,const int mode)
  {
   int  i,j;
   int  p_int,t_int;
//--- check
   if(beg<0 || end<0)
      return;
//--- sort
   i=beg;
   j=end;
   while(i<end)
     {
      //--- ">>1" is quick division by 2
      p_int=m_data[(beg+end)>>1];
      while(i<j)
        {
         while(m_data[i]<p_int)
           {
            //--- control the output of the array bounds
            if(i==m_data_total-1)
               break;
            i++;
           }
         while(m_data[j]>p_int)
           {
            //--- control the output of the array bounds
            if(j==0)
               break;
            j--;
           }
         if(i<=j)
           {
            t_int=m_data[i];
            m_data[i++]=m_data[j];
            m_data[j]=t_int;
            //--- control the output of the array bounds
            if(j==0)
               break;
            j--;
           }
        }
      if(beg<j)
         QuickSort(beg,j);
      beg=i;
      j=end;
     }
  }
//+------------------------------------------------------------------+
//| Inserting element in a sorted array                              |
//+------------------------------------------------------------------+
bool CArrayInt::InsertSort(const int element)
  {
   int pos;
//--- check
   if(!IsSorted())
      return(false);
//--- check/reserve elements of array
   if(!Reserve(1))
      return(false);
//--- if the array is empty, add an element
   if(m_data_total==0)
     {
      m_data[m_data_total++]=element;
      return(true);
     }
//--- find position and insert
   pos=QuickSearch(element);
   if(m_data[pos]>element)
      Insert(element,pos);
   else
      Insert(element,pos+1);
//--- restore the sorting flag after Insert(...)
   m_sort_mode=0;
//--- successful
   return(true);
  }
//+------------------------------------------------------------------+
//| Search of position of element in a array                         |
//+------------------------------------------------------------------+
int CArrayInt::SearchLinear(const int element) const
  {
//--- check
   if(m_data_total==0)
      return(-1);
//---
   for(int i=0;i<m_data_total;i++)
      if(m_data[i]==element)
         return(i);
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Quick search of position of element in a sorted array            |
//+------------------------------------------------------------------+
int CArrayInt::QuickSearch(const int element) const
  {
   int  i,j,m=-1;
   int  t_int;
//--- search
   i=0;
   j=m_data_total-1;
   while(j>=i)
     {
      //--- ">>1" is quick division by 2
      m=(j+i)>>1;
      if(m<0 || m>=m_data_total)
         break;
      t_int=m_data[m];
      if(t_int==element)
         break;
      if(t_int>element)
         j=m-1;
      else
         i=m+1;
     }
//--- position
   return(m);
  }
//+------------------------------------------------------------------+
//| Search of position of element in a sorted array                  |
//+------------------------------------------------------------------+
int CArrayInt::Search(const int element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   pos=QuickSearch(element);
   if(m_data[pos]==element)
      return(pos);
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Search position of the first element which is greater than       |
//| specified in a sorted array                                      |
//+------------------------------------------------------------------+
int CArrayInt::SearchGreat(const int element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   pos=QuickSearch(element);
   while(m_data[pos]<=element)
      if(++pos==m_data_total)
         return(-1);
//--- position
   return(pos);
  }
//+------------------------------------------------------------------+
//| Search position of the first element which is less than          |
//| specified in the sorted array                                    |
//+------------------------------------------------------------------+
int CArrayInt::SearchLess(const int element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   pos=QuickSearch(element);
   while(m_data[pos]>=element)
      if(pos--==0)
         return(-1);
//--- position
   return(pos);
  }
//+------------------------------------------------------------------+
//| Search position of the first element which is greater than or    |
//| equal to the specified in a sorted array                         |
//+------------------------------------------------------------------+
int CArrayInt::SearchGreatOrEqual(const int element) const
  {
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   for(int pos=QuickSearch(element);pos<m_data_total;pos++)
      if(m_data[pos]>=element)
         return(pos);
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Search position of the first element which is less than or equal |
//| to the specified in a sorted array                               |
//+------------------------------------------------------------------+
int CArrayInt::SearchLessOrEqual(const int element) const
  {
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   for(int pos=QuickSearch(element);pos>=0;pos--)
      if(m_data[pos]<=element)
         return(pos);
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Find position of first appearance of element in a sorted array   |
//+------------------------------------------------------------------+
int CArrayInt::SearchFirst(const int element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   pos=QuickSearch(element);
   if(m_data[pos]==element)
     {
      while(m_data[pos]==element)
         if(pos--==0)
            break;
      return(pos+1);
     }
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Find position of last appearance of element in a sorted array    |
//+------------------------------------------------------------------+
int CArrayInt::SearchLast(const int element) const
  {
   int pos;
//--- check
   if(m_data_total==0 || !IsSorted())
      return(-1);
//--- search
   pos=QuickSearch(element);
   if(m_data[pos]==element)
     {
      while(m_data[pos]==element)
         if(++pos==m_data_total)
            break;
      return(pos-1);
     }
//--- not found
   return(-1);
  }
//+------------------------------------------------------------------+
//| Writing array to file                                            |
//+------------------------------------------------------------------+
bool CArrayInt::Save(const int file_handle)
  {
   int i=0;
//--- check
   if(!CArray::Save(file_handle))
      return(false);
//--- write array length
   if(FileWriteInteger(file_handle,m_data_total,INT_VALUE)!=INT_VALUE)
      return(false);
//--- write array
   for(i=0;i<m_data_total;i++)
      if(FileWriteInteger(file_handle,m_data[i],INT_VALUE)!=INT_VALUE)
         break;
//--- result
   return(i==m_data_total);
  }
//+------------------------------------------------------------------+
//| Reading array from file                                          |
//+------------------------------------------------------------------+
bool CArrayInt::Load(const int file_handle)
  {
   int i=0,num;
//--- check
   if(!CArray::Load(file_handle))
      return(false);
//--- read array length
   num=FileReadInteger(file_handle,INT_VALUE);
//--- read array
   Clear();
   if(num!=0)
     {
      if(!Reserve(num))
         return(false);
      for(i=0;i<num;i++)
        {
         m_data[i]=FileReadInteger(file_handle,INT_VALUE);
         m_data_total++;
         if(FileIsEnding(file_handle))
            break;
        }
     }
   m_sort_mode=-1;
//--- result
   return(m_data_total==num);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Class CCheckGroup                                                |
//| Usage: view and edit group of flags                              |
//+------------------------------------------------------------------+
class CCheckGroup : public CWndClient
  {
protected:
   //--- dependent controls
   CCheckBox         m_rows[];              // array of the row objects
   //--- set up
   int               m_offset;              // index of first visible row in array of rows
   int               m_total_view;          // number of visible rows
   int               m_item_height;         // height of visible row
   //--- data
   CArrayString      m_strings;             // array of rows
   CArrayLong        m_values;              // array of values
   CArrayInt         m_states;              // array of states
   long              m_value;               // current value
   int               m_current;             // index of current row in array of rows

public:
                     CCheckGroup(void);
                    ~CCheckGroup(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   virtual void      Destroy(const int reason=0);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- fill
   virtual bool      AddItem(const string item,const long value=0);
   //--- data
   long              Value(void) const;
   bool              Value(const long value);
   int               Check(const int idx) const;
   bool              Check(const int idx,const int value);
   //--- state
   virtual bool      Show(void);
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);

protected:
   //--- create dependent controls
           bool      CreateButton(const int index);
   //--- handlers of the dependent controls events
   virtual bool      OnVScrollShow(void);
   virtual bool      OnVScrollHide(void);
   virtual bool      OnScrollLineDown(void);
   virtual bool      OnScrollLineUp(void);
   virtual bool      OnChangeItem(const int row_index);
   //--- redraw
   virtual bool      Redraw(void);
   bool              RowState(const int index,const bool select);
  };
//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CCheckGroup)
   ON_INDEXED_EVENT(ON_CHANGE,m_rows,OnChangeItem)
EVENT_MAP_END(CWndClient)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CCheckGroup::CCheckGroup(void) : m_offset(0),
                                 m_total_view(0),
                                 m_item_height(CONTROLS_LIST_ITEM_HEIGHT),
                                 m_current(CONTROLS_INVALID_INDEX),
                                 m_value(0)
  {
  RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CCheckGroup::~CCheckGroup(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CCheckGroup::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- determine the number of visible rows
   m_total_view=(y2-y1)/m_item_height;
//--- check the number of visible rows
   if(m_total_view<1)
      return(false);
//--- call method of the parent class
   if(!CWndClient::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- set up
   if(!m_background.ColorBackground(CONTROLS_CHECKGROUP_COLOR_BG))
      return(false);
   if(!m_background.ColorBorder(CONTROLS_CHECKGROUP_COLOR_BORDER))
      return(false);
//--- create dependent controls
   ArrayResize(m_rows,m_total_view);
   for(int i=0;i<m_total_view;i++)
      if(!CreateButton(i))
         return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Delete group of controls                                         |
//+------------------------------------------------------------------+
void CCheckGroup::Destroy(const int reason)
  {
//--- call of the method of the parent class
   CWndClient::Destroy(reason);
//--- clear items
   m_strings.Clear();
   m_values.Clear();
   m_states.Clear();
  }
//+------------------------------------------------------------------+
//| Create "row"                                                     |
//+------------------------------------------------------------------+
bool CCheckGroup::CreateButton(const int index)
  {
//--- calculate coordinates
   int x1=CONTROLS_BORDER_WIDTH;
   int y1=CONTROLS_BORDER_WIDTH+m_item_height*index;
   int x2=Width()-CONTROLS_BORDER_WIDTH;
   int y2=y1+m_item_height;
//--- create
   if(!m_rows[index].Create(m_chart_id,m_name+"Item"+IntegerToString(index),m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_rows[index].Text(""))
      return(false);
   if(!Add(m_rows[index]))
      return(false);
   m_rows[index].Hide();
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Add item (row)                                                   |
//+------------------------------------------------------------------+
bool CCheckGroup::AddItem(const string item,const long value)
  {
//--- add
   if(!m_strings.Add(item))
      return(false);
   if(!m_values.Add(value))
      return(false);
   if(!m_states.Add(0))
      return(false);
//--- number of items
   int total=m_strings.Total();
//--- exit if number of items does not exceed the size of visible area
   if(total<m_total_view+1)
     {
      if(IS_VISIBLE && total!=0)
         m_rows[total-1].Show();
      return(Redraw());
     }
//--- if number of items exceeded the size of visible area
   if(total==m_total_view+1)
     {
      //--- enable vertical scrollbar
      if(!VScrolled(true))
         return(false);
      //--- and immediately make it invisible (if needed)
      if(!IS_VISIBLE)
         m_scroll_v.Visible(false);
     }
//--- set up the scrollbar
   m_scroll_v.MaxPos(m_strings.Total()-m_total_view);
//--- redraw
   return(Redraw());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
long CCheckGroup::Value(void) const
  {
   return(m_value);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CCheckGroup::Value(const long value)
  {
   m_value=value;
//---
   return(Redraw());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int CCheckGroup::Check(const int idx) const
  {
//--- check
   if(idx>=m_values.Total())
      return(0);
//---
   return(m_states[idx]);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CCheckGroup::Check(const int idx,const int value)
  {
//--- check
   if(idx>=m_values.Total())
      return(false);
//---
   bool res=(m_states.Update(idx,value) && Redraw());
//--- change value
   if(res && idx<64)
     {
      if(m_rows[idx].Checked())
         Value(m_value|m_values.At(idx));
      else
         Value(m_value&(~m_values.At(idx)));
     }
//---
   return(res);
  }
//+------------------------------------------------------------------+
//| Makes the group visible                                          |
//+------------------------------------------------------------------+
bool CCheckGroup::Show(void)
{
  // call of the method of the parent class
  if(!CWndClient::Show())
    return(false);
  // loop by rows
  int total = m_values.Total();
  for(int i = total; i < m_total_view; i++)
    m_rows[i].Hide();
  for(int i = m_total_view; i < ArraySize(m_rows); i++)
    m_rows[i].Hide();
  // handled
  return(true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CCheckGroup::Save(const int file_handle)
  {
//--- check
   if(file_handle==INVALID_HANDLE)
      return(false);
//---
   FileWriteLong(file_handle,Value());
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CCheckGroup::Load(const int file_handle)
  {
//--- check
   if(file_handle==INVALID_HANDLE)
      return(false);
//---
   if(!FileIsEnding(file_handle))
      Value(FileReadLong(file_handle));
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Redraw                                                           |
//+------------------------------------------------------------------+
bool CCheckGroup::Redraw(void)
  {
//--- loop by "rows"
   for(int i=0;i<m_total_view;i++)
     {
      //--- copy text
      if(!m_rows[i].Text(m_strings[i+m_offset]))
         return(false);
      //--- select
      if(!RowState(i,m_states[i+m_offset]!=0))
         return(false);
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Change state                                                     |
//+------------------------------------------------------------------+
bool CCheckGroup::RowState(const int index,const bool select)
  {
//--- check index
   if(index<0 || index>=ArraySize(m_rows))
      return(true);
//--- change state
   return(m_rows[index].Checked(select));
  }
//+------------------------------------------------------------------+
//| Handler of the "Show vertical scrollbar" event                   |
//+------------------------------------------------------------------+
bool CCheckGroup::OnVScrollShow(void)
  {
//--- loop by "rows"
   for(int i=0;i<m_total_view;i++)
     {
      //--- resize "rows" according to shown vertical scrollbar
      m_rows[i].Width(Width()-(CONTROLS_SCROLL_SIZE+2*CONTROLS_BORDER_WIDTH+1));
     }
//--- check visibility
   if(!IS_VISIBLE)
     {
      m_scroll_v.Visible(false);
      return(true);
     }
//--- event is handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "Hide vertical scrollbar" event                   |
//+------------------------------------------------------------------+
bool CCheckGroup::OnVScrollHide(void)
  {
//--- check visibility
   if(!IS_VISIBLE)
      return(true);
//--- loop by "rows"
   for(int i=0;i<m_total_view;i++)
     {
      //--- resize "rows" according to hidden vertical scroll bar
      m_rows[i].Width(Width()-2*CONTROLS_BORDER_WIDTH-1);
     }
//--- event is handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "Scroll up for one row" event                     |
//+------------------------------------------------------------------+
bool CCheckGroup::OnScrollLineUp(void)
  {
//--- get new offset
   m_offset=m_scroll_v.CurrPos();
//--- redraw
   return(Redraw());
  }
//+------------------------------------------------------------------+
//| Handler of the "Scroll down for one row" event                   |
//+------------------------------------------------------------------+
bool CCheckGroup::OnScrollLineDown(void)
  {
//--- get new offset
   m_offset=m_scroll_v.CurrPos();
//--- redraw
   return(Redraw());
  }
//+------------------------------------------------------------------+
//| Handler of changing a "row" state                                |
//+------------------------------------------------------------------+
bool CCheckGroup::OnChangeItem(const int row_index)
  {
//--- change value
   m_states.Update(row_index+m_offset,m_rows[row_index].Checked());
   if(row_index+m_offset<64)
     {
      if(m_rows[row_index].Checked())
         Value(m_value|m_values.At(row_index+m_offset));
      else
         Value(m_value&(~m_values.At(row_index+m_offset)));
     }
//--- send notification
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_CHANGE,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//#include "GroupTemplate.mqh"

class CheckGroupResizable: public GroupTemplate<CCheckGroup>
{
  public:
    CheckGroupResizable()
    {
      RTTI;
      WIDTH_ADJUSTMENT = CONTROLS_BUTTON_SIZE;
    }

  protected:
    virtual bool isSelected(const int index) override
    {
      return m_states[index] != 0;
    }

    virtual bool createElement(const int index) override
    {
      return CreateButton(index);
    }
};
//#include <Layouts/ListViewResizable.mqh>
//#include <ControlsPlus/ListView.mqh>
//#include "GroupTemplate.mqh"

class ListViewResizable: public GroupTemplate<CListView>
{
  public:
    ListViewResizable()
    {
      RTTI;
    }
    
    void forceVScroll()
    {
      // make sure the scroll is shown
      // (it may remain hidden in some cases due to a bug in SCL)
      m_scroll_v.Show();
    }

    void adjustVSize()
    {
      OnResize(); // adjust number of rows (objects)
    }
    
  protected:
    virtual bool isSelected(const int index) override
    {
      return m_current == index;
    }

    virtual bool createElement(const int index) override
    {
      return CreateRow(index);
    }
};
//#include <Layouts/AppDialogResizable.mqh>
//+------------------------------------------------------------------+
//|                                           AppDialogResizable.mqh |
//|                               Copyright (c) 2019-2021, Marketeer |
//|                          https://www.mql5.com/en/users/marketeer |
//+------------------------------------------------------------------+

//#include <ControlsPlus/Dialog.mqh>
//#include <ControlsPlus/Button.mqh>

#resource "\\Include\\Layouts\\png\\Expand2.csv"
#resource "\\Include\\Layouts\\png\\size6.csv"
#resource "\\Include\\Layouts\\png\\size10.csv"

class AppDialogResizable: public CAppDialog
{
  protected:
    CBmpButton m_button_truemax;
    CBmpButton m_button_size;
    bool m_maximized;
    CRect m_max_rect;
    CSize m_size_limit;
    bool m_sizing;
    static int s_contained;
    static int s_index;
    int m_contained;
    int m_myid;

    // window maximization
    virtual bool CreateButtonMinMax(void) override;
    virtual void OnClickButtonMinMax(void) override;
    virtual void OnClickButtonTrueMax(void);
    virtual void OnClickButtonSizeFixMe(void);
    virtual void Expand(void);
    virtual void Restore(void);

    virtual void Minimize(void) override;
    virtual bool Save(const int handle) override;
    virtual bool Load(const int handle) override;

    // window resizing
    bool CreateButtonSize(void);
    bool OnDialogSizeStart(void);
    virtual bool OnDialogDragStart(void) override;
    virtual bool OnDialogDragProcess(void) override;
    virtual bool OnDialogDragEnd(void) override;

    virtual void SelfAdjustment(const bool restore = false) {};

  public:
    AppDialogResizable(): m_maximized(false), m_sizing(false), m_contained(0) { RTTI; }
    virtual bool Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2) override;
    virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam) override;

    virtual bool OnChartChange(const long &lparam, const double &dparam, const string &sparam);

    void ChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam);
    
    void SetSizeLimit(const CSize &limit) { m_size_limit = limit; }
    CSize GetSizeLimit() { return m_size_limit; }
};

static int AppDialogResizable::s_contained = 0;
static int AppDialogResizable::s_index = 0;

void AppDialogResizable::ChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
  if(id == CHARTEVENT_CHART_CHANGE)
  {
    if(OnChartChange(lparam, dparam, sparam)) return;
  }
  else if(id == CHARTEVENT_MOUSE_MOVE)
  {
    const bool c = Contains((int)lparam, (int)dparam);
    const bool scroll = !c && (m_drag_object == NULL);
    if(!scroll) s_contained |= m_myid;
    else s_contained &= ~m_myid;

    //if((TerminalInfoInteger(TERMINAL_KEYSTATE_CAPSLOCK) & 1) != 0)
    //{
    //  printf("chscrl %s %s [%x] %x %x", this.Name(), (scroll ? "true" : "false"), m_myid, m_contained, s_contained);
    //}

    if(s_contained != m_contained)
    {
      ChartSetInteger(ChartID(), CHART_MOUSE_SCROLL, !s_contained);
      m_contained = s_contained;
    }
  }
  CAppDialog::ChartEvent(id, lparam, dparam, sparam);
}

EVENT_MAP_BEGIN(AppDialogResizable)
  ON_EVENT(ON_CLICK, m_button_truemax, OnClickButtonTrueMax)
  ON_EVENT(ON_CLICK, m_button_size, OnClickButtonSizeFixMe)
  ON_EVENT(ON_DRAG_START, m_button_size, OnDialogSizeStart)
  ON_EVENT_PTR(ON_DRAG_PROCESS, m_drag_object, OnDialogDragProcess)
  ON_EVENT_PTR(ON_DRAG_END, m_drag_object, OnDialogDragEnd)
EVENT_MAP_END(CAppDialog)

bool AppDialogResizable::Create(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2)
{
  // 1 * CONTROLS_BORDER_WIDTH - stays here, because the standard control library minimizes window
  // when it's height is 1 pixel smaller than the entire chart height
  m_max_rect.SetBound(0,
                      0,
                      (int)ChartGetInteger(ChartID(), CHART_WIDTH_IN_PIXELS) - 0 * CONTROLS_BORDER_WIDTH,
                      (int)ChartGetInteger(ChartID(), CHART_HEIGHT_IN_PIXELS) - 1 * CONTROLS_BORDER_WIDTH);
  if(!CAppDialog::Create(chart, name, subwin, x1, y1, x2, y2)) return false;
  if(!CreateButtonSize()) return false;
  m_size_limit.cx = x2 - x1;
  m_size_limit.cy = y2 - y1;
  if(m_size_limit.cx >= m_max_rect.Width() || m_size_limit.cy >= m_max_rect.Height())
  {
    m_size_limit.cx = m_min_rect.Width() * 3;
    m_size_limit.cy = m_min_rect.Height() * 7;
  }
  m_myid = 1 << s_index;
  s_index++;

  return true;
}

bool AppDialogResizable::CreateButtonMinMax(void) override
{
  if(!CAppDialog::CreateButtonMinMax()) return false;

  // add maximization button
  int off = (m_panel_flag) ? 0 : 2 * CONTROLS_BORDER_WIDTH;

  int x1 = Width() - off - 3 * (CONTROLS_BUTTON_SIZE + CONTROLS_DIALOG_BUTTON_OFF);
  int y1 = off + CONTROLS_DIALOG_BUTTON_OFF;
  int x2 = x1 + CONTROLS_BUTTON_SIZE;
  int y2 = y1 + CONTROLS_BUTTON_SIZE;

  if(!m_button_truemax.Create(m_chart_id, m_name + "TrueMax", m_subwin, x1, y1, x2, y2)) return false;
  if(!m_button_truemax.BmpNames("::Include\\Layouts\\png\\Expand2.csv", "::Include\\Controls\\res\\Restore.bmp")) return false;
  if(!CWndContainer::Add(m_button_truemax)) return false;
  
  m_button_truemax.Locking(true);
  m_button_truemax.Alignment(WND_ALIGN_RIGHT, 0, 0, off + 2 * CONTROLS_BUTTON_SIZE + 2 * CONTROLS_DIALOG_BUTTON_OFF, 0);

  CaptionAlignment(WND_ALIGN_WIDTH, off, 0, off + 3 * (CONTROLS_BUTTON_SIZE + CONTROLS_DIALOG_BUTTON_OFF), 0);

  return true;
}

bool AppDialogResizable::CreateButtonSize(void)
{
  int off = (m_panel_flag) ? 0 : 2 * CONTROLS_BORDER_WIDTH;

  int x1 = Width() - CONTROLS_BUTTON_SIZE + 1;
  int y1 = Height() - CONTROLS_BUTTON_SIZE + 1;
  int x2 = x1 + CONTROLS_BUTTON_SIZE - 1;
  int y2 = y1 + CONTROLS_BUTTON_SIZE - 1;

  if(!m_button_size.Create(m_chart_id, m_name + "Size", m_subwin, x1, y1, x2, y2)) return false;
  if(!m_button_size.BmpNames("::Include\\Layouts\\png\\size6.csv", "::Include\\Layouts\\png\\size10.csv")) return false;
  if(!CWndContainer::Add(m_button_size)) return false;
  m_button_size.Alignment(WND_ALIGN_RIGHT|WND_ALIGN_BOTTOM, 0, 0, 0, 0);
  m_button_size.PropFlagsSet(WND_PROP_FLAG_CAN_DRAG);

  return true;
}

void AppDialogResizable::OnClickButtonTrueMax(void)
{
  if(m_button_truemax.Pressed())
    Expand();
  else
    Restore();

  SubwinOff();
}

// This is a hack. It's required because in minimized state sizing button somehow "overlaps"
// the close button and intercepts clicks on it (which prevents exit from minimized app).
// This happens despite the fact that the sizing button is hidden, disabled and assigned
// with minimal Z-order (checked out, then removed).
// Looks like a bug in the standard control library, specifically:
// In CWnd::OnMouseEvent there must be a line:
//
//   if(!IS_ENABLED || !IS_VISIBLE) return false;
//
// but it's not there, so invisible, disabled and even background objects are processed
// in the same manner as all other objects. Specifically in CWndContainer::OnMouseEvent
// there is a reverse loop through all objects (it does _not_ respect Z-order anyhow).

void AppDialogResizable::OnClickButtonSizeFixMe(void)
{
  if(m_minimized)
  {
    Destroy();
  }
}

void AppDialogResizable::Expand(void)
{
  m_maximized = true;
  m_minimized = false;
  m_button_minmax.Pressed(false);
  Rebound(m_max_rect);
  m_button_size.Hide();
  m_button_size.StateFlagsReset(WND_STATE_FLAG_ENABLE);
  m_button_size.PropFlagsReset(WND_PROP_FLAG_CAN_DRAG);
  if(!m_panel_flag)
  {
    m_caption.PropFlagsReset(WND_PROP_FLAG_CAN_DRAG);
  }
  
  ClientAreaVisible(true);
  SelfAdjustment();
}

//+------------------------------------------------------------------+
//| Restore dialog window                                            |
//+------------------------------------------------------------------+
void AppDialogResizable::Restore(void)
{
  m_maximized = false;
  m_minimized = false;
  m_button_minmax.Pressed(false);
  m_button_size.Show();
  m_button_size.StateFlagsSet(WND_STATE_FLAG_ENABLE);
  m_button_size.PropFlagsSet(WND_PROP_FLAG_CAN_DRAG);
  CAppDialog::Maximize();
  if(!m_panel_flag)
  {
    m_caption.PropFlagsSet(WND_PROP_FLAG_CAN_DRAG);
  }
  SelfAdjustment(true);
}

void AppDialogResizable::Minimize()
{
  CAppDialog::Minimize();
  m_button_size.Hide();
  m_button_size.StateFlagsReset(WND_STATE_FLAG_ENABLE);
  m_button_size.PropFlagsReset(WND_PROP_FLAG_CAN_DRAG);
}

bool AppDialogResizable::OnChartChange(const long &lparam, const double &dparam, const string &sparam)
{
  m_max_rect.SetBound(0, 0,
                      (int)ChartGetInteger(ChartID(), CHART_WIDTH_IN_PIXELS) - 0 * CONTROLS_BORDER_WIDTH,
                      (int)ChartGetInteger(ChartID(), CHART_HEIGHT_IN_PIXELS) - 1 * CONTROLS_BORDER_WIDTH);
  if(m_maximized)
  {
    if(m_rect.Width() != m_max_rect.Width() || m_rect.Height() != m_max_rect.Height())
    {
      Rebound(m_max_rect);
      SelfAdjustment();
      m_chart.Redraw();
    }
    return true;
  }
  return false;
}

void AppDialogResizable::OnClickButtonMinMax(void)
{
  CAppDialog::OnClickButtonMinMax();
  m_button_truemax.Pressed(false);
  m_maximized = false;
  if(m_minimized)
  {
    m_button_size.Hide();
    m_button_size.StateFlagsReset(WND_STATE_FLAG_ENABLE);
    m_button_size.PropFlagsReset(WND_PROP_FLAG_CAN_DRAG);
  }
  else
  {
    m_button_size.Show();
    m_button_size.StateFlagsSet(WND_STATE_FLAG_ENABLE);
    m_button_size.PropFlagsSet(WND_PROP_FLAG_CAN_DRAG);
  }
  if(!m_panel_flag)
  {
    m_caption.PropFlagsSet(WND_PROP_FLAG_CAN_DRAG);
  }
  if(!m_minimized)
  {
    SelfAdjustment();
  }
}

bool AppDialogResizable::OnDialogSizeStart(void)
{
  if(m_drag_object == NULL)
  {
    m_drag_object = new CDragWnd;
    if(m_drag_object == NULL) return false;
  }
  int x1 = m_button_size.Left() - CONTROLS_DRAG_SPACING;
  int y1 = m_button_size.Top() - CONTROLS_DRAG_SPACING;
  int x2 = m_button_size.Right() + CONTROLS_DRAG_SPACING;
  int y2 = m_button_size.Bottom() + CONTROLS_DRAG_SPACING;

  m_drag_object.Create(m_chart_id, "", m_subwin, x1, y1, x2, y2);
  m_drag_object.PropFlagsSet(WND_PROP_FLAG_CAN_DRAG);

  CChart chart;
  chart.Attach(m_chart_id);
  m_drag_object.Limits(-CONTROLS_DRAG_SPACING, -CONTROLS_DRAG_SPACING,
                       chart.WidthInPixels() + CONTROLS_DRAG_SPACING,
                       chart.HeightInPixels(m_subwin) + CONTROLS_DRAG_SPACING);
  chart.Detach();

  m_drag_object.MouseX(m_button_size.MouseX());
  m_drag_object.MouseY(m_button_size.MouseY());
  m_drag_object.MouseFlags(m_button_size.MouseFlags());
  
  m_sizing = true;

  return true;
}

bool AppDialogResizable::OnDialogDragStart(void)
{
  if(m_maximized) return false;
  
  return CAppDialog::OnDialogDragStart();
}
//+------------------------------------------------------------------+
//| Continue dragging the dialog box                                 |
//+------------------------------------------------------------------+
bool AppDialogResizable::OnDialogDragProcess(void)
{
  if(!m_sizing) return CDialog::OnDialogDragProcess();

  if(m_drag_object == NULL) return false;

  int x = m_drag_object.Right() - Right() - CONTROLS_DRAG_SPACING;
  int y = m_drag_object.Bottom() - Bottom() - CONTROLS_DRAG_SPACING;

  // resize dialog
  CRect r = Rect();
  r.right += x;
  r.bottom += y;
  
  if(r.Width() < m_size_limit.cx) r.right = r.left + m_size_limit.cx;
  if(r.Height() < m_size_limit.cy) r.bottom = r.top + m_size_limit.cy;
  
  Rebound(r);
  
  SelfAdjustment();

  return true;
}
//+------------------------------------------------------------------+
//| End dragging the dialog box                                      |
//+------------------------------------------------------------------+
bool AppDialogResizable::OnDialogDragEnd(void)
{
  m_contained = 0;

  if(!m_sizing) return CDialog::OnDialogDragEnd();

  if(m_drag_object != NULL)
  {
    m_button_size.MouseFlags(m_drag_object.MouseFlags());
    delete m_drag_object;
    m_drag_object = NULL;
  }

  m_norm_rect.SetBound(m_rect);
  m_sizing = false;
  
  SelfAdjustment();
  
  return true;
}

bool AppDialogResizable::Save(const int handle) override
{
  FileWriteInteger(handle, m_maximized);
  return CAppDialog::Save(handle);
}

bool AppDialogResizable::Load(const int handle) override
{
  m_maximized = (bool)FileReadInteger(handle);
  return CAppDialog::Load(handle);
}
//#include <Layouts/LayoutMonitors.mqh>
//+------------------------------------------------------------------+
//|                                               LayoutMonitors.mqh |
//|                                    Copyright (c) 2020, Marketeer |
//|                          https://www.mql5.com/en/users/marketeer |
//|                                  GUI Layout declarative language |
//|                         (MQL5 standard library controls support) |
//|                           https://www.mql5.com/ru/articles/7795/ |
//+------------------------------------------------------------------+

class StateMonitor
{
  public:
    virtual void notify(void *sender) = 0;
};

class Publisher
{
  public:
    virtual void subscribe(StateMonitor *ptr) = 0;
    virtual void unsubscribe(StateMonitor *ptr) = 0;
};


template<typename V>
class ValuePublisher: public Publisher
{
  protected:
    string _rtti;
    V value;
    StateMonitor *dependencies[];

  public:
    ValuePublisher()
    {
      RTTI;
    }
    
    V operator~(void) const
    {
      return value;
    }

    void operator=(const V &v)
    {
      value = v;
      for(int i = 0; i < ArraySize(dependencies); i++)
      {
        dependencies[i].notify(&this);
      }
    }
    
    void operator=(V v)
    {
      value = v;
      for(int i = 0; i < ArraySize(dependencies); i++)
      {
        dependencies[i].notify(&this);
      }
    }

    virtual void subscribe(StateMonitor *ptr) override
    {
      const int n = ArraySize(dependencies);
      ArrayResize(dependencies, n + 1);
      dependencies[n] = ptr;
    }

    virtual void unsubscribe(StateMonitor *ptr) override
    {
      const int n = ArraySize(dependencies);
      bool found = false;
      for(int i = 0, j = 0; i < n; i++, j++)
      {
        if(i != j)
        {
          dependencies[j] = dependencies[i];
        }
        else
        if(dependencies[i] == ptr)
        {
          j--;
          found = true;
        }
      }
      if(found)
      {
        ArrayResize(dependencies, n - 1);
      }
    }
};


template<typename V>
class StdValue: public ValuePublisher<V>
{
  protected:
    CWnd *provider;

  public:
    StdValue()
    {
      RTTI;
    }
    
    void bind(CWnd *ptr)
    {
      provider = ptr;
    }
    
    CWnd *backlink() const
    {
      return provider;
    }
};

template<typename C>
class EnableStateMonitorBase: public StateMonitor
{
  protected:
    Publisher *sources[];
    C *control;

  public:
    EnableStateMonitorBase(): control(NULL) {}

    virtual void attach(C *c)
    {
      control = c;
      for(int i = 0; i < ArraySize(sources); i++)
      {
        if(control)
        {
          sources[i].subscribe(&this);
        }
        else
        {
          sources[i].unsubscribe(&this);
        }
      }
    }

    virtual bool isEnabled(void) = 0;
};

class EnableStateMonitor: public EnableStateMonitorBase<CWnd>
{
  public:
    EnableStateMonitor() {}

    void notify(void *sender) override
    {
      if(control)
      {
        if(isEnabled())
        {
          control.Enable();
        }
        else
        {
          control.Disable();
        }
      }
    }
};

template<typename C>
class Notifiable: public C
{
  public:
    virtual bool onEvent(const int event, void *parent) { return false; };
};

template<typename C,typename V>
class PlainTypeNotifiable: public Notifiable<C>
{
  public:
    virtual V value() = 0;
};

template<typename C, typename V>
class NotifiableProperty: public PlainTypeNotifiable<C,V>
{
  protected:
    StdValue<V> *property;

  public:
    NotifiableProperty()
    {
      RTTI;
    }

    void bind(StdValue<V> *prop)
    {
      property = prop;
      property.bind(&this);
      property = value();
    }
    
    virtual bool onEvent(const int event, void *parent) override
    {
      if(event == ON_CHANGE || event == ON_END_EDIT)
      {
        property = value();
        return true;
      }
      return false;
    };
};

//#include <Layouts/LayoutStdLib.mqh>
//+------------------------------------------------------------------+
//|                                                 LayoutStdLib.mqh |
//|                                    Copyright (c) 2020, Marketeer |
//|                          https://www.mql5.com/en/users/marketeer |
//|                                  GUI Layout declarative language |
//|                         (MQL5 standard library controls support) |
//|                           https://www.mql5.com/ru/articles/7734/ |
//|                           https://www.mql5.com/ru/articles/7795/ |
//+------------------------------------------------------------------+

//#include <ControlsPlus/Wnd.mqh>
//#include <ControlsPlus/WndObj.mqh>
//#include <ControlsPlus/WndClient.mqh>
//#include <ControlsPlus/WndContainer.mqh>
//#include <ControlsPlus/Dialog.mqh>
//#include <ControlsPlus/Button.mqh>
//#include <ControlsPlus/Edit.mqh>
//#include <ControlsPlus/SpinEdit.mqh>
//+------------------------------------------------------------------+
//|                                                     SpinEdit.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "WndContainer.mqh"
//#include "Edit.mqh"
//#include "BmpButton.mqh"
//+------------------------------------------------------------------+
//| Resources                                                        |
//+------------------------------------------------------------------+
#resource "\\Include\\Controls\\res\\SpinInc.bmp"
#resource "\\Include\\Controls\\res\\SpinDec.bmp"
//+------------------------------------------------------------------+
//| Class CSpinEdit                                                  |
//| Usage: class that implements the "Up-Down" control               |
//+------------------------------------------------------------------+
class CSpinEdit : public CWndContainer
  {
protected:
   //--- dependent controls
   CEdit             m_edit;                // the entry field object
   CBmpButton        m_inc;                 // the "Increment button" object
   CBmpButton        m_dec;                 // the "Decrement button" object
   //--- adjusted parameters
   int               m_min_value;           // minimum value
   int               m_max_value;           // maximum value
   //--- state
   int               m_value;               // current value

   bool m_can_drag;
   int  m_prev_x;

public:
                     CSpinEdit(void);
                    ~CSpinEdit(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- set up
   int               MinValue(void) const { return(m_min_value); }
   void              MinValue(const int value);
   int               MaxValue(void) const { return(m_max_value); }
   void              MaxValue(const int value);
   //--- state
   int               Value(void) const { return(m_value); }
   bool              Value(int value);
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);

protected:
   //--- create dependent controls
   virtual bool      CreateEdit(void);
   virtual bool      CreateInc(void);
   virtual bool      CreateDec(void);
   //--- handlers of the dependent controls events
   virtual bool      OnClickInc(void);
   virtual bool      OnClickDec(void);
   virtual bool      OnClickEdit(void);
   //--- internal event handlers
   virtual bool      OnChangeValue(void);
   virtual bool      OnMouseEvent(const int x, const int y, const int flags) override;
  };


//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CSpinEdit)
   ON_EVENT(ON_CLICK,m_inc,OnClickInc)
   ON_EVENT(ON_CLICK,m_dec,OnClickDec)
   ON_EVENT(ON_CLICK, m_edit, OnClickEdit)
EVENT_MAP_END(CWndContainer)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CSpinEdit::CSpinEdit(void) : m_min_value(0),
                             m_max_value(0),
                             m_value(0),
                             m_can_drag(false)
  {
  RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CSpinEdit::~CSpinEdit(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CSpinEdit::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- check height
   if(y2-y1<CONTROLS_SPIN_MIN_HEIGHT)
      return(false);
//--- call method of the parent class
   if(!CWndContainer::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create dependent controls
   if(!CreateEdit())
      return(false);
   if(!CreateInc())
      return(false);
   if(!CreateDec())
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Set current value                                                |
//+------------------------------------------------------------------+
bool CSpinEdit::Value(int value)
  {
//--- check value
   if(value<m_min_value)
      value=m_min_value;
   if(value>m_max_value)
      value=m_max_value;
//--- if value was changed
   if(m_value!=value)
     {
      m_value=value;
      //--- call virtual handler
      return(OnChangeValue());
     }
//--- value has not been changed
   return(false);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CSpinEdit::Save(const int file_handle)
  {
//--- check
   if(file_handle==INVALID_HANDLE)
      return(false);
//---
   FileWriteInteger(file_handle,m_value);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CSpinEdit::Load(const int file_handle)
  {
//--- check
   if(file_handle==INVALID_HANDLE)
      return(false);
//---
   if(!FileIsEnding(file_handle))
      Value(FileReadInteger(file_handle));
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Set minimum value                                                |
//+------------------------------------------------------------------+
void CSpinEdit::MinValue(const int value)
  {
//--- if value was changed
   if(m_min_value!=value)
     {
      m_min_value=value;
      //--- adjust the edit value
      Value(m_value);
     }
  }
//+------------------------------------------------------------------+
//| Set maximum value                                                |
//+------------------------------------------------------------------+
void CSpinEdit::MaxValue(const int value)
  {
//--- if value was changed
   if(m_max_value!=value)
     {
      m_max_value=value;
      //--- adjust the edit value
      Value(m_value);
     }
  }
//+------------------------------------------------------------------+
//| Create the edit field                                            |
//+------------------------------------------------------------------+
bool CSpinEdit::CreateEdit(void)
  {
//--- create
   if(!m_edit.Create(m_chart_id,m_name+"Edit",m_subwin,0,0,Width(),Height()))
      return(false);
   if(!m_edit.Text(""))
      return(false);
   if(!m_edit.ReadOnly(true))
      return(false);
   if(!Add(m_edit))
      return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create the "Increment" button                                    |
//+------------------------------------------------------------------+
bool CSpinEdit::CreateInc(void)
  {
//--- right align button (try to make equal offsets from top and bottom)
   int x1=Width()-(CONTROLS_BUTTON_SIZE+CONTROLS_SPIN_BUTTON_X_OFF);
   int y1=(Height()-2*CONTROLS_SPIN_BUTTON_SIZE)/2;
   int x2=x1+CONTROLS_BUTTON_SIZE;
   int y2=y1+CONTROLS_SPIN_BUTTON_SIZE;
//--- create
   if(!m_inc.Create(m_chart_id,m_name+"Inc",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_inc.BmpNames("::Include\\Controls\\res\\SpinInc.bmp"))
      return(false);
   if(!Add(m_inc))
      return(false);
//--- property
   m_inc.PropFlags(WND_PROP_FLAG_CLICKS_BY_PRESS);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Create the "Decrement" button                                    |
//+------------------------------------------------------------------+
bool CSpinEdit::CreateDec(void)
  {
//--- right align button (try to make equal offsets from top and bottom)
   int x1=Width()-(CONTROLS_BUTTON_SIZE+CONTROLS_SPIN_BUTTON_X_OFF);
   int y1=(Height()-2*CONTROLS_SPIN_BUTTON_SIZE)/2+CONTROLS_SPIN_BUTTON_SIZE;
   int x2=x1+CONTROLS_BUTTON_SIZE;
   int y2=y1+CONTROLS_SPIN_BUTTON_SIZE;
//--- create
   if(!m_dec.Create(m_chart_id,m_name+"Dec",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_dec.BmpNames("::Include\\Controls\\res\\SpinDec.bmp"))
      return(false);
   if(!Add(m_dec))
      return(false);
//--- property
   m_dec.PropFlags(WND_PROP_FLAG_CLICKS_BY_PRESS);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of click on the "increment" button                       |
//+------------------------------------------------------------------+
bool CSpinEdit::OnClickInc(void)
  {
//--- try to increment current value
   return(Value(m_value+1));
  }
//+------------------------------------------------------------------+
//| Handler of click on the "decrement" button                       |
//+------------------------------------------------------------------+
bool CSpinEdit::OnClickDec(void)
  {
//--- try to decrement current value
   return(Value(m_value-1));
  }

bool CSpinEdit::OnClickEdit(void)
{
  EventChartCustom(CONTROLS_SELF_MESSAGE, ON_CLICK, m_id, 0.0, m_name);
  return true;
}

//+------------------------------------------------------------------+
//| Handler of changing current state                                |
//+------------------------------------------------------------------+
bool CSpinEdit::OnChangeValue(void)
  {
//--- copy text to the edit field edit
   m_edit.Text(IntegerToString(m_value));
//--- send notification
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_CHANGE,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
bool CSpinEdit::OnMouseEvent(const int x, const int y, const int flags) override
{
  if(Contains(x, y))
  {
   // Comment(x, " ", y, " ", flags, " ", m_mouse_flags, " ", m_can_drag);
  
   if((flags & MOUSE_LEFT) != 0)
   {
     if(m_mouse_flags <= 0)
     {
       m_can_drag = true;
     }
     
     if(m_prev_x != x && m_can_drag)
     {
       if(m_mouse_flags == flags)
       {
         Value(m_value + (x - m_prev_x));
         ChartRedraw();
       }
       m_prev_x = x;
     }
   }
   else
   {
     m_can_drag = false;
   }
  }
  else
  {
    m_can_drag = false;
  }
  m_mouse_flags = flags;
  return CWndContainer::OnMouseEvent(x, y, flags);
}
//#include <ControlsPlus/DatePicker.mqh>
//+------------------------------------------------------------------+
//|                                                   DatePicker.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "WndContainer.mqh"
//#include "Edit.mqh"
//#include "BmpButton.mqh"
//#include "DateDropList.mqh"
//+------------------------------------------------------------------+
//|                                                 DateDropList.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "WndContainer.mqh"
//#include "BmpButton.mqh"
//#include "Picture.mqh"
//+------------------------------------------------------------------+
//|                                                      Picture.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "WndObj.mqh"
//#include <ChartObjects\ChartObjectsBmpControls.mqh>
//+------------------------------------------------------------------+
//| Class CPicture                                                   |
//| Note: image displayed by                                         |
//|             the CChartObjectBmpLabel object                      |
//+------------------------------------------------------------------+
class CPicture : public CWndObj
  {
private:
   CChartObjectBmpLabel m_picture;          // chart object
   //--- parameters of the chart object
   int               m_border;              // border width
   string            m_bmp_name;            // filename

public:
                     CPicture(void);
                    ~CPicture(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- parameters of the chart object
   int               Border(void) const { return(m_border); }
   bool              Border(const int value);
   string            BmpName(void) const { return(m_bmp_name); }
   bool              BmpName(const string name);

protected:
   //--- internal event handlers
   virtual bool      OnCreate(void);
   virtual bool      OnShow(void);
   virtual bool      OnHide(void);
   virtual bool      OnMove(void);
   virtual bool      OnChange(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CPicture::CPicture(void) : m_border(0),
                           m_bmp_name(NULL)
  {
  RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPicture::~CPicture(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CPicture::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndObj::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create the chart object
   if(!m_picture.Create(chart,name,subwin,x1,y1))
      return(false);
//--- call the settings handler
   return(OnChange());
  }
//+------------------------------------------------------------------+
//| Set border width                                                 |
//+------------------------------------------------------------------+
bool CPicture::Border(const int value)
  {
//--- save new value of parameter
   m_border=value;
//--- set up the chart object
   return(m_picture.Width(value));
  }
//+------------------------------------------------------------------+
//| Set image                                                        |
//+------------------------------------------------------------------+
bool CPicture::BmpName(const string name)
  {
//--- save new value of parameter
   m_bmp_name=name;
//--- set up the chart object
   return(m_picture.BmpFileOn(name));
  }
//+------------------------------------------------------------------+
//| Create object on chart                                           |
//+------------------------------------------------------------------+
bool CPicture::OnCreate(void)
  {
//--- create the chart object by previously set parameters
   return(m_picture.Create(m_chart_id,m_name,m_subwin,m_rect.left,m_rect.top));
  }
//+------------------------------------------------------------------+
//| Display object on chart                                          |
//+------------------------------------------------------------------+
bool CPicture::OnShow(void)
  {
   return(m_picture.Timeframes(OBJ_ALL_PERIODS));
  }
//+------------------------------------------------------------------+
//| Hide object from chart                                           |
//+------------------------------------------------------------------+
bool CPicture::OnHide(void)
  {
   return(m_picture.Timeframes(OBJ_NO_PERIODS));
  }
//+------------------------------------------------------------------+
//| Absolute movement of the chart object                            |
//+------------------------------------------------------------------+
bool CPicture::OnMove(void)
  {
//--- position the chart object
   return(m_picture.X_Distance(m_rect.left) && m_picture.Y_Distance(m_rect.top));
  }
//+------------------------------------------------------------------+
//| Set up the chart object                                          |
//+------------------------------------------------------------------+
bool CPicture::OnChange(void)
  {
//--- set up the chart object
   return(m_picture.Width(m_border) && m_picture.BmpFileOn(m_bmp_name));
  }
//+------------------------------------------------------------------+
//#include <Canvas\Canvas.mqh>
//+------------------------------------------------------------------+
//|                                                       Canvas.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include <Files\FileBin.mqh>
//+------------------------------------------------------------------+
//|                                                      FileBin.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include "File.mqh"
//+------------------------------------------------------------------+
//|                                                         File.mqh |
//|                   Copyright 2009-2017, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//#include <Object.mqh>
//+------------------------------------------------------------------+
//| Class CFile.                                                     |
//| Purpose: Base class of file operations.                          |
//|          Derives from class CObject.                             |
//+------------------------------------------------------------------+
class CFile : public CObject
  {
protected:
   int               m_handle;             // handle of file
   string            m_name;               // name of opened file
   int               m_flags;              // flags of opened file

public:
                     CFile(void);
                    ~CFile(void);
   //--- methods of access to protected data
   int               Handle(void)              const { return(m_handle); };
   string            FileName(void)            const { return(m_name);   };
   int               Flags(void)               const { return(m_flags);  };
   void              SetUnicode(const bool unicode);
   void              SetCommon(const bool common);
   //--- general methods for working with files
   int               Open(const string file_name,int open_flags,const short delimiter='\t');
   void              Close(void);
   void              Delete(void);
   ulong             Size(void);
   ulong             Tell(void);
   void              Seek(const long offset,const ENUM_FILE_POSITION origin);
   void              Flush(void);
   bool              IsEnding(void);
   bool              IsLineEnding(void);
   //--- general methods for working with files
   void              Delete(const string file_name,const int common_flag=0);
   bool              IsExist(const string file_name,const int common_flag=0);
   bool              Copy(const string src_name,const int common_flag,const string dst_name,const int mode_flags);
   bool              Move(const string src_name,const int common_flag,const string dst_name,const int mode_flags);
   //--- general methods of working with folders
   bool              FolderCreate(const string folder_name);
   bool              FolderDelete(const string folder_name);
   bool              FolderClean(const string folder_name);
   //--- general methods of finding files
   long              FileFindFirst(const string file_filter,string &returned_filename);
   bool              FileFindNext(const long search_handle,string &returned_filename);
   void              FileFindClose(const long search_handle);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CFile::CFile(void) : m_handle(INVALID_HANDLE),
                     m_name(""),
                     m_flags(FILE_ANSI)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CFile::~CFile(void)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      Close();
  }
//+------------------------------------------------------------------+
//| Set the FILE_UNICODE flag                                        |
//+------------------------------------------------------------------+
void CFile::SetUnicode(const bool unicode)
  {
//--- check handle
   if(m_handle==INVALID_HANDLE)
     {
      if(unicode)
         m_flags|=FILE_UNICODE;
      else
         m_flags&=~FILE_UNICODE;
     }
  }
//+------------------------------------------------------------------+
//| Set the "Common Folder" flag                                     |
//+------------------------------------------------------------------+
void CFile::SetCommon(const bool common)
  {
//--- check handle
   if(m_handle==INVALID_HANDLE)
     {
      if(common)
         m_flags|=FILE_COMMON;
      else
         m_flags&=~FILE_COMMON;
     }
  }
//+------------------------------------------------------------------+
//| Open the file                                                    |
//+------------------------------------------------------------------+
int CFile::Open(const string file_name,int open_flags,const short delimiter)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      Close();
//--- action
   if((open_flags &(FILE_BIN|FILE_CSV))==0)
      open_flags|=FILE_TXT;
//--- open
   m_handle=FileOpen(file_name,open_flags|m_flags,delimiter);
   if(m_handle!=INVALID_HANDLE)
     {
      //--- store options of the opened file
      m_flags|=open_flags;
      m_name=file_name;
     }
//--- result
   return(m_handle);
  }
//+------------------------------------------------------------------+
//| Close the file                                                   |
//+------------------------------------------------------------------+
void CFile::Close(void)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
     {
      //--- closing the file and resetting all the variables to the initial state
      FileClose(m_handle);
      m_handle=INVALID_HANDLE;
      m_name="";
      //--- reset all flags except the text
      m_flags&=FILE_ANSI|FILE_UNICODE;
     }
  }
//+------------------------------------------------------------------+
//| Deleting an open file                                            |
//+------------------------------------------------------------------+
void CFile::Delete(void)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
     {
      string file_name=m_name;
      int    common_flag=m_flags&FILE_COMMON;
      //--- close before deleting
      Close();
      //--- delete
      FileDelete(file_name,common_flag);
     }
  }
//+------------------------------------------------------------------+
//| Get size of opened file                                          |
//+------------------------------------------------------------------+
ulong CFile::Size(void)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileSize(m_handle));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Get current position of pointer in file                          |
//+------------------------------------------------------------------+
ulong CFile::Tell(void)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileTell(m_handle));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Set position of pointer in file                                  |
//+------------------------------------------------------------------+
void CFile::Seek(const long offset,const ENUM_FILE_POSITION origin)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      FileSeek(m_handle,offset,origin);
  }
//+------------------------------------------------------------------+
//| Flush data from the file buffer of input-output to disk          |
//+------------------------------------------------------------------+
void CFile::Flush(void)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      FileFlush(m_handle);
  }
//+------------------------------------------------------------------+
//| Detect the end of file                                           |
//+------------------------------------------------------------------+
bool CFile::IsEnding(void)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileIsEnding(m_handle));
//--- failure
   return(false);
  }
//+------------------------------------------------------------------+
//| Detect the end of string                                         |
//+------------------------------------------------------------------+
bool CFile::IsLineEnding(void)
  {
//--- checking
   if(m_handle!=INVALID_HANDLE)
      if((m_flags&FILE_BIN)==0)
         return(FileIsLineEnding(m_handle));
//--- failure
   return(false);
  }
//+------------------------------------------------------------------+
//| Deleting a file                                                  |
//+------------------------------------------------------------------+
void CFile::Delete(const string file_name,const int common_flag)
  {
//--- checking
   if(file_name==m_name)
     {
      int flag=m_flags&FILE_COMMON;
      if(flag==common_flag)
         Close();
     }
//--- delete
   FileDelete(file_name,common_flag);
  }
//+------------------------------------------------------------------+
//| Check if file exists                                             |
//+------------------------------------------------------------------+
bool CFile::IsExist(const string file_name,const int common_flag)
  {
   return(FileIsExist(file_name,common_flag));
  }
//+------------------------------------------------------------------+
//| Copying file                                                     |
//+------------------------------------------------------------------+
bool CFile::Copy(const string src_name,const int common_flag,const string dst_name,const int mode_flags)
  {
   return(FileCopy(src_name,common_flag,dst_name,mode_flags));
  }
//+------------------------------------------------------------------+
//| Move/rename file                                                 |
//+------------------------------------------------------------------+
bool CFile::Move(const string src_name,const int common_flag,const string dst_name,const int mode_flags)
  {
   return(FileMove(src_name,common_flag,dst_name,mode_flags));
  }
//+------------------------------------------------------------------+
//| Create folder                                                    |
//+------------------------------------------------------------------+
bool CFile::FolderCreate(const string folder_name)
  {
   return(::FolderCreate(folder_name,m_flags));
  }
//+------------------------------------------------------------------+
//| Delete folder                                                    |
//+------------------------------------------------------------------+
bool CFile::FolderDelete(const string folder_name)
  {
   return(::FolderDelete(folder_name,m_flags));
  }
//+------------------------------------------------------------------+
//| Clean folder                                                     |
//+------------------------------------------------------------------+
bool CFile::FolderClean(const string folder_name)
  {
   return(::FolderClean(folder_name,m_flags));
  }
//+------------------------------------------------------------------+
//| Start search of files                                            |
//+------------------------------------------------------------------+
long CFile::FileFindFirst(const string file_filter,string &returned_filename)
  {
   return(::FileFindFirst(file_filter,returned_filename,m_flags));
  }
//+------------------------------------------------------------------+
//| Continue search of files                                         |
//+------------------------------------------------------------------+
bool CFile::FileFindNext(const long search_handle,string &returned_filename)
  {
   return(::FileFindNext(search_handle,returned_filename));
  }
//+------------------------------------------------------------------+
//| End search of files                                              |
//+------------------------------------------------------------------+
void CFile::FileFindClose(const long search_handle)
  {
   ::FileFindClose(search_handle);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Class CFileBin                                                   |
//| Purpose: Class of operations with binary files                   |
//|          Derives from class CFile                                |
//+------------------------------------------------------------------+
class CFileBin : public CFile
  {
public:
                     CFileBin(void);
                    ~CFileBin(void);
   //--- methods for working with files
   int               Open(const string file_name,const int open_flags);
   //--- methods for writing data
   uint              WriteChar(const char value);
   uint              WriteShort(const short value);
   uint              WriteInteger(const int value);
   uint              WriteLong(const long value);
   uint              WriteFloat(const float value);
   uint              WriteDouble(const double value);
   uint              WriteString(const string value);
   uint              WriteString(const string value,const int size);
   uint              WriteCharArray(const char &array[],const int start_item=0,const int items_count=WHOLE_ARRAY);
   uint              WriteShortArray(const short& array[],const int start_item=0,const int items_count=WHOLE_ARRAY);
   uint              WriteIntegerArray(const int& array[],const int start_item=0,const int items_count=WHOLE_ARRAY);
   uint              WriteLongArray(const long &array[],const int start_item=0,const int items_count=WHOLE_ARRAY);
   uint              WriteFloatArray(const float &array[],const int start_item=0,const int items_count=WHOLE_ARRAY);
   uint              WriteDoubleArray(const double &array[],const int start_item=0,const int items_count=WHOLE_ARRAY);
   template<typename T>
   uint              WriteArray(T &array[],const int start_item=0,const int items_count=WHOLE_ARRAY);
   template<typename T>
   uint              WriteStruct(T &data);
   bool              WriteObject(CObject *object);
   template<typename T>
   uint              WriteEnum(const T value) { return(WriteInteger((int)value)); }
   //--- methods for reading data
   bool              ReadChar(char &value);
   bool              ReadShort(short &value);
   bool              ReadInteger(int &value);
   bool              ReadLong(long &value);
   bool              ReadFloat(float &value);
   bool              ReadDouble(double &value);
   bool              ReadString(string &value);
   bool              ReadString(string &value,const int size);
   uint              ReadCharArray(char &array[],const int start_item=0,const int items_count=WHOLE_ARRAY);
   uint              ReadShortArray(short& array[],const int start_item=0,const int items_count=WHOLE_ARRAY);
   uint              ReadIntegerArray(int& array[],const int start_item=0,const int items_count=WHOLE_ARRAY);
   uint              ReadLongArray(long &array[],const int start_item=0,const int items_count=WHOLE_ARRAY);
   uint              ReadFloatArray(float &array[],const int start_item=0,const int items_count=WHOLE_ARRAY);
   uint              ReadDoubleArray(double &array[],const int start_item=0,const int items_count=WHOLE_ARRAY);
   template<typename T>
   uint              ReadArray(T &array[],const int start_item=0,const int items_count=WHOLE_ARRAY);
   template<typename T>
   uint              ReadStruct(T &data);
   bool              ReadObject(CObject *object);
   template<typename T>
   bool              ReadEnum(T &value);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CFileBin::CFileBin(void)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CFileBin::~CFileBin(void)
  {
  }
//+------------------------------------------------------------------+
//| Opening a binary file                                            |
//+------------------------------------------------------------------+
int CFileBin::Open(const string file_name,const int open_flags)
  {
   return(CFile::Open(file_name,open_flags|FILE_BIN));
  }
//+------------------------------------------------------------------+
//| Write a variable of char or uchar type                           |
//+------------------------------------------------------------------+
uint CFileBin::WriteChar(const char value)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileWriteInteger(m_handle,value,sizeof(char)));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Write a variable of short or ushort type                         |
//+------------------------------------------------------------------+
uint CFileBin::WriteShort(const short value)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileWriteInteger(m_handle,value,sizeof(short)));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Write a variable of int or uint type                             |
//+------------------------------------------------------------------+
uint CFileBin::WriteInteger(const int value)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileWriteInteger(m_handle,value,sizeof(int)));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Write a variable of long or ulong type                           |
//+------------------------------------------------------------------+
uint CFileBin::WriteLong(const long value)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileWriteLong(m_handle,value));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Write a variable of float type                                   |
//+------------------------------------------------------------------+
uint CFileBin::WriteFloat(const float value)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileWriteFloat(m_handle,value));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Write a variable of double type                                  |
//+------------------------------------------------------------------+
uint CFileBin::WriteDouble(const double value)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileWriteDouble(m_handle,value));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Write a variable of string type                                  |
//+------------------------------------------------------------------+
uint CFileBin::WriteString(const string value)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
     {
      //--- size of string
      int size=StringLen(value);
      //--- write
      if(FileWriteInteger(m_handle,size)==sizeof(int))
         return(FileWriteString(m_handle,value,size));
     }
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Write a part of string                                           |
//+------------------------------------------------------------------+
uint CFileBin::WriteString(const string value,const int size)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileWriteString(m_handle,value,size));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Write array variables of type char or uchar                      |
//+------------------------------------------------------------------+
uint CFileBin::WriteCharArray(const char &array[],const int start_item,const int items_count)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileWriteArray(m_handle,array,start_item,items_count));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Write an array of variables of short or ushort type              |
//+------------------------------------------------------------------+
uint CFileBin::WriteShortArray(const short &array[],const int start_item,const int items_count)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileWriteArray(m_handle,array,start_item,items_count));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Write an array of variables of int or uint type                  |
//+------------------------------------------------------------------+
uint CFileBin::WriteIntegerArray(const int &array[],const int start_item,const int items_count)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileWriteArray(m_handle,array,start_item,items_count));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Write an array of variables of long or ulong type                |
//+------------------------------------------------------------------+
uint CFileBin::WriteLongArray(const long &array[],const int start_item,const int items_count)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileWriteArray(m_handle,array,start_item,items_count));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Write an array of variables of float type                        |
//+------------------------------------------------------------------+
uint CFileBin::WriteFloatArray(const float &array[],const int start_item,const int items_count)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileWriteArray(m_handle,array,start_item,items_count));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Write an array of variables of double type                       |
//+------------------------------------------------------------------+
uint CFileBin::WriteDoubleArray(const double &array[],const int start_item,const int items_count)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileWriteArray(m_handle,array,start_item,items_count));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Write an array of variables of any type                          |
//+------------------------------------------------------------------+
template<typename T>
uint CFileBin::WriteArray(T &array[],const int start_item=0,const int items_count=WHOLE_ARRAY)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileWriteArray(m_handle,array,start_item,items_count));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Write an structure                                               |
//+------------------------------------------------------------------+
template<typename T>
uint CFileBin::WriteStruct(T &data)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileWriteStruct(m_handle,data));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Write data of an instance of the CObject class                   |
//+------------------------------------------------------------------+
bool CFileBin::WriteObject(CObject *object)
  {
//--- check handle & object
   if(m_handle!=INVALID_HANDLE)
      if(CheckPointer(object))
         return(object.Save(m_handle));
//--- failure
   return(false);
  }
//+------------------------------------------------------------------+
//| Read a variable of char or uchar type                            |
//+------------------------------------------------------------------+
bool CFileBin::ReadChar(char &value)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
     {
      ResetLastError();
      value=(char)FileReadInteger(m_handle,sizeof(char));
      return(GetLastError()==0);
     }
//--- failure
   return(false);
  }
//+------------------------------------------------------------------+
//| Read a variable of short or ushort type                          |
//+------------------------------------------------------------------+
bool CFileBin::ReadShort(short &value)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
     {
      ResetLastError();
      value=(short)FileReadInteger(m_handle,sizeof(short));
      return(GetLastError()==0);
     }
//--- failure
   return(false);
  }
//+------------------------------------------------------------------+
//| Read a variable of int or uint type                              |
//+------------------------------------------------------------------+
bool CFileBin::ReadInteger(int &value)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
     {
      ResetLastError();
      value=FileReadInteger(m_handle,sizeof(int));
      return(GetLastError()==0);
     }
//--- failure
   return(false);
  }
//+------------------------------------------------------------------+
//| Read a variable of long or ulong type                            |
//+------------------------------------------------------------------+
bool CFileBin::ReadLong(long &value)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
     {
      ResetLastError();
      value=FileReadLong(m_handle);
      return(GetLastError()==0);
     }
//--- failure
   return(false);
  }
//+------------------------------------------------------------------+
//| Read a variable of float type                                    |
//+------------------------------------------------------------------+
bool CFileBin::ReadFloat(float &value)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
     {
      ResetLastError();
      value=FileReadFloat(m_handle);
      return(GetLastError()==0);
     }
//--- failure
   return(false);
  }
//+------------------------------------------------------------------+
//| Read a variable of double type                                   |
//+------------------------------------------------------------------+
bool CFileBin::ReadDouble(double &value)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
     {
      ResetLastError();
      value=FileReadDouble(m_handle);
      return(GetLastError()==0);
     }
//--- failure
   return(false);
  }
//+------------------------------------------------------------------+
//| Read a variable of string type                                   |
//+------------------------------------------------------------------+
bool CFileBin::ReadString(string &value)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
     {
      ResetLastError();
      int size=FileReadInteger(m_handle);
      if(GetLastError()==0)
        {
         value=FileReadString(m_handle,size);
         return(size==StringLen(value));
        }
     }
//--- failure
   return(false);
  }
//+------------------------------------------------------------------+
//| Read a part of string                                            |
//+------------------------------------------------------------------+
bool CFileBin::ReadString(string &value,const int size)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
     {
      value=FileReadString(m_handle,size);
      return(size==StringLen(value));
     }
//--- failure
   return(false);
  }
//+------------------------------------------------------------------+
//| Read an array of variables of char or uchar type                 |
//+------------------------------------------------------------------+
uint CFileBin::ReadCharArray(char &array[],const int start_item,const int items_count)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileReadArray(m_handle,array,start_item,items_count));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Read an array of variables of short or ushort type               |
//+------------------------------------------------------------------+
uint CFileBin::ReadShortArray(short &array[],const int start_item,const int items_count)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileReadArray(m_handle,array,start_item,items_count));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Read an array of variables of int or uint type                   |
//+------------------------------------------------------------------+
uint CFileBin::ReadIntegerArray(int &array[],const int start_item,const int items_count)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileReadArray(m_handle,array,start_item,items_count));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Read an array of variables of long or ulong type                 |
//+------------------------------------------------------------------+
uint CFileBin::ReadLongArray(long &array[],const int start_item,const int items_count)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileReadArray(m_handle,array,start_item,items_count));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Read an array of variables of float type                         |
//+------------------------------------------------------------------+
uint CFileBin::ReadFloatArray(float &array[],const int start_item,const int items_count)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileReadArray(m_handle,array,start_item,items_count));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Read an array of variables of double type                        |
//+------------------------------------------------------------------+
uint CFileBin::ReadDoubleArray(double &array[],const int start_item,const int items_count)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileReadArray(m_handle,array,start_item,items_count));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Read an array of variables of any type                           |
//+------------------------------------------------------------------+
template<typename T>
uint CFileBin::ReadArray(T &array[],const int start_item=0,const int items_count=WHOLE_ARRAY)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileReadArray(m_handle,array,start_item,items_count));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Read an structure                                                |
//+------------------------------------------------------------------+
template<typename T>
uint CFileBin::ReadStruct(T &data)
  {
//--- check handle
   if(m_handle!=INVALID_HANDLE)
      return(FileReadStruct(m_handle,data));
//--- failure
   return(0);
  }
//+------------------------------------------------------------------+
//| Read data of an instance of the CObject class                    |
//+------------------------------------------------------------------+
bool CFileBin::ReadObject(CObject *object)
  {
//--- check handle & object
   if(m_handle!=INVALID_HANDLE)
      if(CheckPointer(object))
         return(object.Load(m_handle));
//--- failure
   return(false);
  }
//+------------------------------------------------------------------+
//| Read a variable of an enumeration type                           |
//+------------------------------------------------------------------+
template<typename T>
bool CFileBin::ReadEnum(T &value)
  {
   int val;
   if(!ReadInteger(val))
      return(false);
//---
   value=(T)val;
   return(true);
  }
//+------------------------------------------------------------------+
//#include <Controls\Rect.mqh>

#define SIGN(i) ((i<0) ? -1 : 1)

//+------------------------------------------------------------------+
//| Macro to generate color                                          |
//+------------------------------------------------------------------+
#define XRGB(r,g,b)    (0xFF000000|(uchar(r)<<16)|(uchar(g)<<8)|uchar(b))
#define ARGB(a,r,g,b)  ((uchar(a)<<24)|(uchar(r)<<16)|(uchar(g)<<8)|uchar(b))
#define TRGB(a,rgb)    ((uchar(a)<<24)|(rgb))
#define GETRGB(clr)    ((clr)&0xFFFFFF)
#define GETRGBA(clr)   uchar((clr)>>24)
#define GETRGBR(clr)   uchar((clr)>>16)
#define GETRGBG(clr)   uchar((clr)>>8)
#define GETRGBB(clr)   uchar(clr)
#define COLOR2RGB(clr) (0xFF000000|(uchar(clr)<<16)|(uchar((clr)>>8)<<8)|uchar((clr)>>16))
#define RGB2COLOR(rgb) ((uchar(rgb)<<16)|(uchar((rgb)>>8)<<8)|uchar((rgb)>>16))

//+------------------------------------------------------------------+
//| Line end style (round, butt, square)                             |
//+------------------------------------------------------------------+
enum ENUM_LINE_END
  {
   LINE_END_ROUND,
   LINE_END_BUTT,
   LINE_END_SQUARE,
  };

//+------------------------------------------------------------------+
//| Class CCanvas                                                    |
//| Usage: class for working with a dynamic resource                 |
//+------------------------------------------------------------------+
class CCanvas
  {
private:
   uint              m_style;                  // line style template
   uint              m_style_idx;              // variable - current index of bit in line style template
   static uint       m_default_colors[9];      // default colors

protected:
   long              m_chart_id;               // chart ID
   string            m_objname;                // object name
   ENUM_OBJECT       m_objtype;                // object type
   string            m_rcname;                 // resource name
   int               m_width;                  // canvas width
   int               m_height;                 // canvas height
   ENUM_COLOR_FORMAT m_format;                 // method of color processing
   //--- for text
   string            m_fontname;               // font name
   int               m_fontsize;               // font size
   uint              m_fontflags;              // font flags
   uint              m_fontangle;              // angle of text tilt to the X axis in 0.1 degrees
   //--- data
   uint              m_pixels[];               // array of pixels

public:
                     CCanvas(void);
                    ~CCanvas(void);
   //--- create/attach/destroy
   virtual bool      Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);
   bool              CreateBitmap(const string name,const datetime time,const double price,
                                  const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);
   bool              CreateBitmap(const long chart_id,const int subwin,const string name,
                                  const datetime time,const double price,const int width,const int height,
                                  ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);
   bool              CreateBitmapLabel(const string name,const int x,const int y,
                                       const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);
   bool              CreateBitmapLabel(const long chart_id,const int subwin,const string name,
                                       const int x,const int y,const int width,const int height,
                                       ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);
   virtual bool      Attach(const long chart_id,const string objname,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);
   virtual bool      Attach(const long chart_id,const string objname,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA);
   virtual void      Destroy(void);
   //--- properties
   string            ChartObjectName(void)          const { return(m_objname); }
   string            ResourceName(void)             const { return(m_rcname);  }
   int               Width(void)                    const { return(m_width);   }
   int               Height(void)                   const { return(m_height);  }
   //--- update object on screen
   void              Update(const bool redraw=true);
   bool              Resize(const int width,const int height);
   //--- clear/fill color
   void              Erase(const uint clr=0);
   //--- data access
   uint              PixelGet(const int x,const int y) const;
   void              PixelSet(const int x,const int y,const uint clr);
   //--- draw primitives
   void              LineVertical(int x,int y1,int y2,const uint clr);
   void              LineHorizontal(int x1,int x2,int y,const uint clr);
   void              Line(int x1,int y1,int x2,int y2,const uint clr);
   void              Polyline(int &x[],int &y[],const uint clr);
   void              Polygon(int &x[],int &y[],const uint clr);
   void              Rectangle(int x1,int y1,int x2,int y2,const uint clr);
   void              Triangle(int x1,int y1,int x2,int y2,int x3,int y3,const uint clr);
   void              Circle(int x,int y,int r,const uint clr);
   void              Ellipse(int x1,int y1,int x2,int y2,const uint clr);
   void              Arc(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,const uint clr);
   void              Arc(int x,int y,int rx,int ry,double fi3,double fi4,const uint clr);
   void              Arc(int x,int y,int rx,int ry,double fi3,double fi4,int &x3,int &y3,int &x4,int &y4,const uint clr);
   void              Pie(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,const uint clr,const uint fill_clr);
   void              Pie(int x,int y,int rx,int ry,double fi3,double fi4,const uint clr,const uint fill_clr);
   //--- draw filled primitives
   void              FillRectangle(int x1,int y1,int x2,int y2,const uint clr);
   void              FillTriangle(int x1,int y1,int x2,int y2,int x3,int y3,const uint clr);
   void              FillPolygon(int &x[],int &y[],const uint clr);
   void              FillCircle(int x,int y,int r,const uint clr);
   void              FillEllipse(int x1,int y1,int x2,int y2,const uint clr);
   void              Fill(int x,int y,const uint clr);
   void              Fill(int x,int y,const uint clr,const uint threshould);
   //--- draw primitives with antialiasing
   void              PixelSetAA(const double x,const double y,const uint clr);
   void              LineAA(const int x1,const int y1,const int x2,const int y2,const uint clr,const uint style=UINT_MAX);
   void              PolylineAA(int &x[],int &y[],const uint clr,const uint style=UINT_MAX);
   void              PolygonAA(int &x[],int &y[],const uint clr,const uint style=UINT_MAX);
   void              TriangleAA(const int x1,const int y1,const int x2,const int y2,const int x3,const int y3,
                                const uint clr,const uint style=UINT_MAX);
   void              CircleAA(const int x,const int y,const double r,const uint clr,const uint style=UINT_MAX);
   void              EllipseAA(const double x1,const double y1,const double x2,const double y2,const uint clr,const uint style=UINT_MAX);
   //--- draw primitives with antialiasing by Wu's algorithm
   void              LineWu(int x1,int y1,int x2,int y2,const uint clr,const uint style=UINT_MAX);
   void              PolylineWu(const int &x[],const int &y[],const uint clr,const uint style=UINT_MAX);
   void              PolygonWu(const int &x[],const int &y[],const uint clr,const uint style=UINT_MAX);
   void              TriangleWu(const int x1,const int y1,const int x2,const int y2,const int x3,const int y3,const uint clr,const uint style=UINT_MAX);
   void              CircleWu(const int x,const int y,const double r,const uint clr,const uint style=UINT_MAX);
   void              EllipseWu(const int x1,const int y1,const int x2,const int y2,const uint clr,const uint style=UINT_MAX);
   //--- draw primitives with prefiltered antialiasing
   void              LineThickVertical(const int x,const int y1,const int y2,const uint clr,const int size,const uint style,ENUM_LINE_END end_style);
   void              LineThickHorizontal(const int x1,const int x2,const int y,const uint clr,const int size,const uint style,ENUM_LINE_END end_style);
   void              LineThick(const int x1,const int y1,const int x2,const int y2,const uint clr,const int size,const uint style,ENUM_LINE_END end_style);
   void              PolylineThick(const int &x[],const int &y[],const uint clr,const int size,const uint style,ENUM_LINE_END end_style);
   void              PolygonThick(const int &x[],const int &y[],const uint clr,const int size,const uint style,ENUM_LINE_END end_style);
   //--- draw primitives smoothing polyline and polygon
   void              PolylineSmooth(const int &x[],const int &y[],const uint clr,const int size,
                                    ENUM_LINE_STYLE style=STYLE_SOLID,ENUM_LINE_END end_style=LINE_END_ROUND,
                                    double tension=0.5,double step=10);
   void              PolygonSmooth(int &x[],int &y[],const uint clr,const int size,
                                   ENUM_LINE_STYLE style=STYLE_SOLID,ENUM_LINE_END end_style=LINE_END_ROUND,
                                   double tension=0.5,double step=10);
   //--- for text
   bool              FontSet(const string name,const int size,const uint flags=0,const uint angle=0);
   bool              FontNameSet(string name);
   bool              FontSizeSet(int size);
   bool              FontFlagsSet(uint flags);
   bool              FontAngleSet(uint angle);
   void              FontGet(string &name,int &size,uint &flags,uint &angle);
   string            FontNameGet(void)                 const { return(m_fontname);  }
   int               FontSizeGet(void)                 const { return(m_fontsize);  }
   uint              FontFlagsGet(void)                const { return(m_fontflags); }
   uint              FontAngleGet(void)                const { return(m_fontangle); }
   void              TextOut(int x,int y,string text,const uint clr,uint alignment=0);
   int               TextWidth(const string text);
   int               TextHeight(const string text);
   void              TextSize(const string text,int &width,int &height);
   //--- services
   static uint       GetDefaultColor(const int i);
   void              TransparentLevelSet(const uchar value);
   //--- load bitmap from file
   bool              LoadFromFile(const string filename);
   //--- line style property
   uint              LineStyleGet(void) const;
   void              LineStyleSet(const uint style);
   //--- load bitmap from file to buffer
   static bool       LoadBitmap(const string filename,uint &data[],int &width,int &height);

private:
   bool              FontSet(void);
   void              TextOutFast(int x,int y,string text,const uint clr,uint alignment=0);
   bool              PixelsSimilar(const uint clr0,const uint clr1,const uint threshould);
   //--- for Wu's algorithm
   void              PixelTransform(const int x,const int y,const uint clr,const double alpha);
   //--- for circle and ellipse
   void              PixelTransform4(const int x,const int y,const int dx,const int dy,const uint clr,const double alpha);
   void              PixelSet4AA(const double x,const double y,const double dx,const double dy,const uint clr);
   //--- for thick line
   void              SegmentVertical(const int x,const int y1,const int y2,const int ysign,const double r,const uint clr,ENUM_LINE_END end_style);
   void              SegmentHorizontal(const int x1,const int x2,const int y,const int xsign,const double r,const uint clr,ENUM_LINE_END end_style);
   void              Segment(const int x1,const int y1,const int x2,const int y2,const double kp0,const double kp1,const int xsign,const int ysign,
                             const double rcos_k,const double rsin_k,const double r,const uint clr,ENUM_LINE_END end_style);
   double            DistancePointSegment(const double px,const double py,const double x1,const double y1,const double x2,const double y2);
   //--- for pie
   double            AngleCalc(int x1,int y1,int x2,int y2);
   //--- for polygon
   int               PointClassify(const CPoint &p0,const CPoint &p1,const CPoint &p2);
   int               PolygonClassify(const CPoint &p[]);
   bool              IsPolygonConvex(CPoint &p[]);
   void              PolygonNormalize(CPoint &p[]);
   void              PolygonIntersect(CPoint &p[],CPoint &add[]);
   void              PolygonFill(CPoint &p[],const uint clr);
   //--- for smoothing polyline and polygon
   void              CalcCurveBezierEndp(const double xend,const double yend,const double xadj,const double yadj,const double tension,double &x,double &y);
   void              CalcCurveBezier(const int &x[],const int &y[],const int i,const double tension,double &x1,double &y1,double &x2,double &y2);
   double            CalcBezierX(const double t,const double x0,const double x1,const double x2,const double x3);
   double            CalcBezierY(const double t,const double y0,const double y1,const double y2,const double y3);

protected:
   //--- method for prefiltered antialiasing
   virtual double    FilterFunction(const double x);
  };
//+------------------------------------------------------------------+
//| Initialize static array                                          |
//+------------------------------------------------------------------+
uint CCanvas::m_default_colors[9]=
  {
   XRGB(0,0,255),     // blue
   XRGB(255,0,0),     // red
   XRGB(0,128,0),     // green
   XRGB(255,242,0),   // yellow
   XRGB(255,0,128),   // pink
   XRGB(0,255,0),     // lime
   XRGB(185,0,61),    // crimson
   XRGB(0,183,239),   // sky blue
   XRGB(255,128,0)    // orange
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CCanvas::CCanvas(void) : m_chart_id(0),
   m_objname(NULL),
   m_objtype(WRONG_VALUE),
   m_rcname(NULL),
   m_width(0),
   m_height(0),
   m_format(COLOR_FORMAT_XRGB_NOALPHA),
   m_fontname("arial"),
   m_fontsize(-120),
   m_fontflags(0),
   m_fontangle(0),
   m_style(UINT_MAX),
   m_style_idx(0)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CCanvas::~CCanvas(void)
  {
  }
//+------------------------------------------------------------------+
//| Create dynamic resource                                          |
//+------------------------------------------------------------------+
bool CCanvas::Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt)
  {
   Destroy();
//--- prepare data array
   if(width>0 && height>0 && ArrayResize(m_pixels,width*height)>0)
     {
      //--- generate resource name
      m_rcname="::"+name+(string)ChartID()+(string)(GetTickCount()+MathRand());
      //--- initialize data with zeros
      ArrayInitialize(m_pixels,0);
      //--- create dynamic resource
      if(ResourceCreate(m_rcname,m_pixels,width,height,0,0,0,clrfmt))
        {
         //--- successfully created
         //--- complete initialization
         m_width =width;
         m_height=height;
         m_format=clrfmt;
         //--- succeed
         return(true);
        }
     }
//--- error - destroy object
   Destroy();
   return(false);
  }
//+------------------------------------------------------------------+
//| Create object on chart with attached dynamic resource            |
//+------------------------------------------------------------------+
bool CCanvas::CreateBitmap(const string name,const datetime time,const double price,
                           const int width,const int height,ENUM_COLOR_FORMAT clrfmt)
  {
   return(CreateBitmap(0,0,name,time,price,width,height,clrfmt));
  }
//+------------------------------------------------------------------+
//| Create object on chart with attached dynamic resource            |
//+------------------------------------------------------------------+
bool CCanvas::CreateBitmap(const long chart_id,const int subwin,const string name,
                           const datetime time,const double price,const int width,const int height,
                           ENUM_COLOR_FORMAT clrfmt)
  {
//--- create canvas
   if(Create(name,width,height,clrfmt))
     {
      //--- create attached object
      if(ObjectCreate(chart_id,name,OBJ_BITMAP,subwin,time,price))
        {
         //--- bind object with resource
         if(ObjectSetString(chart_id,name,OBJPROP_BMPFILE,m_rcname))
           {
            //--- successfully created
            //--- complete initialization
            m_chart_id=chart_id;
            m_objname =name;
            m_objtype =OBJ_BITMAP;
            //--- succeed
            return(true);
           }
        }
     }
//--- error
   return(false);
  }
//+------------------------------------------------------------------+
//| Create object on chart with attached dynamic resource            |
//+------------------------------------------------------------------+
bool CCanvas::CreateBitmapLabel(const string name,const int x,const int y,
                                const int width,const int height,ENUM_COLOR_FORMAT clrfmt)
  {
   return(CreateBitmapLabel(0,0,name,x,y,width,height,clrfmt));
  }
//+------------------------------------------------------------------+
//| Create object on chart with attached dynamic resource            |
//+------------------------------------------------------------------+
bool CCanvas::CreateBitmapLabel(const long chart_id,const int subwin,const string name,
                                const int x,const int y,const int width,const int height,
                                ENUM_COLOR_FORMAT clrfmt)
  {
//--- create canvas
   if(Create(name,width,height,clrfmt))
     {
      //--- create attached object
      if(ObjectCreate(chart_id,name,OBJ_BITMAP_LABEL,subwin,0,0))
        {
         //--- set x,y and bind object with resource
         if(ObjectSetInteger(chart_id,name,OBJPROP_XDISTANCE,x) &&
            ObjectSetInteger(chart_id,name,OBJPROP_YDISTANCE,y) &&
            ObjectSetString(chart_id,name,OBJPROP_BMPFILE,m_rcname))
           {
            //--- successfully created
            //--- complete initialization
            m_chart_id=chart_id;
            m_objname =name;
            m_objtype =OBJ_BITMAP_LABEL;
            //--- succeed
            return(true);
           }
        }
     }
//--- error
   return(false);
  }
//+------------------------------------------------------------------+
//| Attach new object with bitmap resource                           |
//+------------------------------------------------------------------+
bool CCanvas::Attach(const long chart_id,const string objname,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA)
  {
   if(OBJ_BITMAP_LABEL==ObjectGetInteger(chart_id,objname,OBJPROP_TYPE))
     {
      string rcname=ObjectGetString(chart_id,objname,OBJPROP_BMPFILE);
      rcname=StringSubstr(rcname,StringFind(rcname,"::"));
      if(ResourceReadImage(rcname,m_pixels,m_width,m_height))
        {
         m_chart_id=chart_id;
         m_objname=objname;
         m_rcname=rcname;
         m_format=clrfmt;
         m_objtype=OBJ_BITMAP_LABEL;
         //--- success
         return(true);
        }
     }
//--- failed
   return(false);
  }
//+------------------------------------------------------------------+
//| Attach new object without bitmap resource                        |
//+------------------------------------------------------------------+
bool CCanvas::Attach(const long chart_id,const string objname,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA)
  {
   if(OBJ_BITMAP_LABEL==ObjectGetInteger(chart_id,objname,OBJPROP_TYPE))
     {
      string rcname=ObjectGetString(chart_id,objname,OBJPROP_BMPFILE);
      if(StringLen(rcname)==0 && width>0 && height>0 && ArrayResize(m_pixels,width*height)>0)
        {
         ZeroMemory(m_pixels);
         if(ResourceCreate("::"+objname,m_pixels,width,height,0,0,0,clrfmt) &&
            ObjectSetString(chart_id,objname,OBJPROP_BMPFILE,"::"+objname))
           {
            m_chart_id=chart_id;
            m_width=width;
            m_height=height;
            m_objname=objname;
            m_rcname="::"+objname;
            m_format=clrfmt;
            m_objtype=OBJ_BITMAP_LABEL;
            //--- success
            return(true);
           }
        }
     }
//--- failed
   return(false);
  }
//+------------------------------------------------------------------+
//| Remove object from chart and deallocate data array               |
//+------------------------------------------------------------------+
void CCanvas::Destroy(void)
  {
//--- delete object
   if(m_objname!=NULL)
     {
      ObjectDelete(m_chart_id,m_objname);
      m_chart_id=0;
      m_objname =NULL;
      m_objtype =WRONG_VALUE;
     }
//--- deallocate array
   ArrayFree(m_pixels);
//--- free resource
   if(m_rcname!=NULL)
     {
      ResourceFree(m_rcname);
      m_rcname=NULL;
     }
//--- zeroize data
   m_width =0;
   m_height=0;
  }
//+------------------------------------------------------------------+
//| Update object on screen (redraw)                                 |
//+------------------------------------------------------------------+
void CCanvas::Update(const bool redraw)
  {
//--- check
   if(m_rcname==NULL)
      return;
//--- update resource and redraw
   if(ResourceCreate(m_rcname,m_pixels,m_width,m_height,0,0,0,m_format) && redraw)
      ChartRedraw(this.m_chart_id);
  }
//+------------------------------------------------------------------+
//| Resize                                                           |
//+------------------------------------------------------------------+
bool CCanvas::Resize(const int width,const int height)
  {
//--- check
   if(m_rcname!=NULL && width>0 && height>0)
      if(ArrayResize(m_pixels,width*height)>0)
        {
         m_width =width;
         m_height=height;
         //--- initialize data with zeros
         ArrayInitialize(m_pixels,0);
         //--- create dynamic resource
         if(ResourceCreate(m_rcname,m_pixels,m_width,m_height,0,0,0,m_format))
           {
            //--- bind object with resource
            if(m_objname!=NULL && ObjectSetString(m_chart_id,m_objname,OBJPROP_BMPFILE,m_rcname))
               return(true);
           }
        }
//--- error
   return(false);
  }
//+------------------------------------------------------------------+
//| Clear/Fill color                                                 |
//+------------------------------------------------------------------+
void CCanvas::Erase(const uint clr)
  {
   ArrayInitialize(m_pixels,clr);
  }
//+------------------------------------------------------------------+
//| Get pixel color                                                  |
//+------------------------------------------------------------------+
uint CCanvas::PixelGet(const int x,const int y) const
  {
//--- check coordinates
   if(x>=0 && x<m_width && y>=0 && y<m_height)
      return(m_pixels[y*m_width+x]);
//--- error
   return(0);
  }
//+------------------------------------------------------------------+
//| Set pixel                                                        |
//+------------------------------------------------------------------+
void CCanvas::PixelSet(const int x,const int y,const uint clr)
  {
//--- check coordinates
   if(x>=0 && x<m_width && y>=0 && y<m_height)
      m_pixels[y*m_width+x]=clr;
  }
//+------------------------------------------------------------------+
//| Fill closed region with color                                    |
//+------------------------------------------------------------------+
void CCanvas::Fill(int x,int y,const uint clr)
  {
//--- check
   if(x<0 || x>=m_width || y<0 || y>=m_height)
      return;
//---
   int  index=y*m_width+x;
   uint old_clr=m_pixels[index];
//--- check if replacement is necessary
   if(old_clr==clr)
      return;
//--- use pseudo stack to emulate deeply-nested recursive calls
   int  stack[];
   uint count=1;
   int  idx;
   int  total=ArraySize(m_pixels);
//--- allocate memory for stack
   if(ArrayResize(stack,total)==-1)
      return;
   stack[0]=index;
   m_pixels[index]=clr;
   for(uint i=0; i<count; i++)
     {
      index=stack[i];
      x=index%m_width;
      //--- left adjacent point
      idx=index-1;
      if(x>0 && m_pixels[idx]==old_clr)
        {
         m_pixels[idx]=clr;
         stack[count++]=idx;
        }
      //--- top adjacent point
      idx=index-m_width;
      if(idx>=0 && m_pixels[idx]==old_clr)
        {
         m_pixels[idx]=clr;
         stack[count++]=idx;
        }
      //--- right adjacent point
      idx=index+1;
      if(x<m_width-1 && m_pixels[idx]==old_clr)
        {
         m_pixels[idx]=clr;
         stack[count++]=idx;
        }
      //--- bottom adjacent point
      idx=index+m_width;
      if(idx<total && m_pixels[idx]==old_clr)
        {
         m_pixels[idx]=clr;
         stack[count++]=idx;
        }
     }
//--- deallocate memory
   ArrayFree(stack);
  }
//+------------------------------------------------------------------+
//| Fill closed region with color                                    |
//+------------------------------------------------------------------+
void CCanvas::Fill(int x,int y,const uint clr,const uint threshould)
  {
//--- check
   if(threshould==0.0)
     {
      Fill(x,y,clr);
      return;
     }
   if(x<0 || x>=m_width || y<0 || y>=m_height || threshould>255)
      return;
//---
   int  index=y*m_width+x;
   uint old_clr=m_pixels[index];
//--- check if replacement is necessary
   if(old_clr==clr)
      return;
//--- use pseudo stack to emulate deeply-nested recursive calls
   int  stack[];
   uint count=1;
   int  idx;
   int  total=ArraySize(m_pixels);
//--- allocate memory for stack
   if(ArrayResize(stack,total)==-1)
      return;
   stack[0]=index;
   m_pixels[index]=clr;
   for(uint i=0; i<count; i++)
     {
      index=stack[i];
      x=index%m_width;
      //--- left adjacent point
      idx=index-1;
      if(x>0 && PixelsSimilar(m_pixels[idx],old_clr,threshould) && m_pixels[idx]!=clr)
        {
         m_pixels[idx]=clr;
         stack[count++]=idx;
        }
      //--- top adjacent point
      idx=index-m_width;
      if(idx>=0 && PixelsSimilar(m_pixels[idx],old_clr,threshould) && m_pixels[idx]!=clr)
        {
         m_pixels[idx]=clr;
         stack[count++]=idx;
        }
      //--- right adjacent point
      idx=index+1;
      if(x<m_width-1 && PixelsSimilar(m_pixels[idx],old_clr,threshould) && m_pixels[idx]!=clr)
        {
         m_pixels[idx]=clr;
         stack[count++]=idx;
        }
      //--- bottom adjacent point
      idx=index+m_width;
      if(idx<total && PixelsSimilar(m_pixels[idx],old_clr,threshould) && m_pixels[idx]!=clr)
        {
         m_pixels[idx]=clr;
         stack[count++]=idx;
        }
     }
//--- deallocate memory
   ArrayFree(stack);
  }
//+------------------------------------------------------------------+
//| Draw vertical line                                               |
//+------------------------------------------------------------------+
void CCanvas::LineVertical(int x,int y1,int y2,const uint clr)
  {
   int tmp;
//--- sort by Y
   if(y1>y2)
     {
      tmp=y1;
      y1 =y2;
      y2 =tmp;
     }
//--- line is out of image boundaries
   if(y2<0 || y1>=m_height || x<0 || x>=m_width)
      return;
//--- stay withing image boundaries
   if(y1<0)
      y1=0;
   if(y2>=m_height)
      y2=m_height-1;
//--- draw line
   int index=y1*m_width+x;
   for(int i=y1; i<=y2; i++,index+=m_width)
      m_pixels[index]=clr;
  }
//+------------------------------------------------------------------+
//| Draw horizontal line                                             |
//+------------------------------------------------------------------+
void CCanvas::LineHorizontal(int x1,int x2,int y,const uint clr)
  {
   int tmp;
//--- sort by X
   if(x1>x2)
     {
      tmp=x1;
      x1 =x2;
      x2 =tmp;
     }
//--- line is out of image boundaries
   if(x2<0 || x1>=m_width || y<0 || y>=m_height)
      return;
//--- stay withing image boundaries
   if(x1<0)
      x1=0;
   if(x2>=m_width)
      x2=m_width-1;
//--- draw line
   ArrayFill(m_pixels,y*m_width+x1,(x2-x1)+1,clr);
  }
//+------------------------------------------------------------------+
//| Draw line according to Bresenham's algorithm                     |
//+------------------------------------------------------------------+
void CCanvas::Line(int x1,int y1,int x2,int y2,const uint clr)
  {
//--- line is out of image boundaries
   if((x1<0 && x2<0) || (y1<0 && y2<0))
      return;
   if(x1>=m_width && x2>=m_width)
      return;
   if(y1>=m_height && y2>=m_height)
      return;
//--- get length by X and Y
   int dx=(x2>x1)? x2-x1 : x1-x2;
   int dy=(y2>y1)? y2-y1 : y1-y2;
   if(dx==0)
     {
      //--- vertical line
      LineVertical(x1,y1,y2,clr);
      return;
     }
   if(dy==0)
     {
      //--- horizontal line
      LineHorizontal(x1,x2,y1,clr);
      return;
     }
//--- get direction by X and Y
   int  sx=(x1<x2)? 1 : -1;
   int  sy=(y1<y2)? 1 : -1;
   int  er=dx-dy;
   bool draw=false;
//--- continue to draw line
   while(x1!=x2 || y1!=y2)
     {
      if(x1<0 || x1>=m_width ||
         y1<0 || y1>=m_height)
        {
         if(draw)
            return;
        }
      else
        {
         //--- draw pixel
         m_pixels[y1*m_width+x1]=clr;
         draw=true;
        }
      //--- get coordinates of next pixel
      int er2=er<<1;
      if(er2>-dy)
        {
         er-=dy;
         x1+=sx;
        }
      if(er2<dx)
        {
         er+=dx;
         y1+=sy;
        }
     }
//--- set pixel at the end
   PixelSet(x2,y2,clr);
  }
//+------------------------------------------------------------------+
//| Draw polyline                                                    |
//+------------------------------------------------------------------+
void CCanvas::Polyline(int &x[],int &y[],const uint clr)
  {
   int total=ArraySize(x);
   if(total>ArraySize(y))
      total=ArraySize(y);
//--- check
   if(total<2)
      return;
   total--;
//--- draw
   for(int i=0; i<total; i++)
      Line(x[i],y[i],x[i+1],y[i+1],clr);
  }
//+------------------------------------------------------------------+
//| Draw polygon                                                     |
//+------------------------------------------------------------------+
void CCanvas::Polygon(int &x[],int &y[],const uint clr)
  {
//--- check arrays
   int total=ArraySize(x);
   if(total>ArraySize(y))
      total=ArraySize(y);
//--- check
   if(total<2)
      return;
   total--;
//--- draw
   for(int i=0; i<total; i++)
      Line(x[i],y[i],x[i+1],y[i+1],clr);
//--- close the outline
   Line(x[total],y[total],x[0],y[0],clr);
  }
//+------------------------------------------------------------------+
//| Draw rectangle                                                   |
//+------------------------------------------------------------------+
void CCanvas::Rectangle(int x1,int y1,int x2,int y2,const uint clr)
  {
   LineHorizontal(x1,x2,y1,clr);
   LineVertical(x2,y1,y2,clr);
   LineHorizontal(x2,x1,y2,clr);
   LineVertical(x1,y2,y1,clr);
  }
//+------------------------------------------------------------------+
//| Draw triangle                                                    |
//+------------------------------------------------------------------+
void CCanvas::Triangle(int x1,int y1,int x2,int y2,int x3,int y3,const uint clr)
  {
   Line(x1,y1,x2,y2,clr);
   Line(x2,y2,x3,y3,clr);
   Line(x3,y3,x1,y1,clr);
  }
//+------------------------------------------------------------------+
//| Draw circle according to Bresenham's algorithm                   |
//+------------------------------------------------------------------+
void CCanvas::Circle(int x,int y,int r,const uint clr)
  {
   int f   =1-r;
   int dd_x=1;
   int dd_y=-2*r;
   int dx  =0;
   int dy  =r;
   int xx,yy;
//--- draw
   while(dy>=dx)
     {
      xx=x+dx;
      if(xx>=0 && xx<m_width)
        {
         yy=y+dy;
         if(yy>=0 && yy<m_height)
            m_pixels[yy*m_width+xx]=clr;
         yy=y-dy;
         if(yy>=0 && yy<m_height)
            m_pixels[yy*m_width+xx]=clr;
        }
      xx=x-dx;
      if(xx>=0 && xx<m_width)
        {
         yy=y+dy;
         if(yy>=0 && yy<m_height)
            m_pixels[yy*m_width+xx]=clr;
         yy=y-dy;
         if(yy>=0 && yy<m_height)
            m_pixels[yy*m_width+xx]=clr;
        }
      xx=x+dy;
      if(xx>=0 && xx<m_width)
        {
         yy=y+dx;
         if(yy>=0 && yy<m_height)
            m_pixels[yy*m_width+xx]=clr;
         yy=y-dx;
         if(yy>=0 && yy<m_height)
            m_pixels[yy*m_width+xx]=clr;
        }
      xx=x-dy;
      if(xx>=0 && xx<m_width)
        {
         yy=y+dx;
         if(yy>=0 && yy<m_height)
            m_pixels[yy*m_width+xx]=clr;
         yy=y-dx;
         if(yy>=0 && yy<m_height)
            m_pixels[yy*m_width+xx]=clr;
        }
      //---
      if(f>=0)
        {
         dy--;
         dd_y+=2;
         f+=dd_y;
        }
      dx++;
      dd_x+=2;
      f+=dd_x;
     }
  }
//+------------------------------------------------------------------+
//| Draw ellipse according to Bresenham's algorithm                  |
//+------------------------------------------------------------------+
void CCanvas::Ellipse(int x1,int y1,int x2,int y2,const uint clr)
  {
   int x,y;
   int rx,ry;
   int dx,dy;
   int xx,yy;
   int rx_sq,ry_sq;
   int f;
   int tmp;
//--- handle extreme conditions
   if(x1==x2)
     {
      if(y1==y2)
         PixelSet(x1,y1,clr);
      else
         LineVertical(x1,y1,y2,clr);
      return;
     }
   if(y1==y2)
     {
      LineHorizontal(x1,x2,y1,clr);
      return;
     }
//--- sort by X
   if(x1>x2)
     {
      tmp=x1;
      x1 =x2;
      x2 =tmp;
     }
//--- sort by Y
   if(y1>y2)
     {
      tmp=y1;
      y1 =y2;
      y2 =tmp;
     }
   x =(x2+x1)>>1;
   y =(y2+y1)>>1;
   rx=(x2-x1)>>1;
   ry=(y2-y1)>>1;
   dx=0;
   dy=ry;
   rx_sq=rx*rx;
   ry_sq=ry*ry;
   f=(rx_sq<<1)*((dy-1)*dy)+rx_sq+(ry_sq<<1)*(1-rx_sq);
   while(rx_sq*dy>ry_sq*dx)
     {
      yy=y+dy;
      if(yy>=0 && yy<m_height)
        {
         yy*=m_width;
         xx=x+dx;
         if(xx>=0 && xx<m_width)
            m_pixels[yy+xx]=clr;
         xx=x-dx;
         if(xx>=0 && xx<m_width)
            m_pixels[yy+xx]=clr;
        }
      yy=y-dy;
      if(yy>=0 && yy<m_height)
        {
         yy*=m_width;
         xx=x+dx;
         if(xx>=0 && xx<m_width)
            m_pixels[yy+xx]=clr;
         xx=x-dx;
         if(xx>=0 && xx<m_width)
            m_pixels[yy+xx]=clr;
        }
      if(f>=0)
        {
         dy--;
         f-=(rx_sq<<2)*dy;
        }
      f+=(ry_sq<<1)*(3+(dx<<1));
      dx++;
     }
   f=(ry_sq<<1)*(dx+1)*dx+(rx_sq<<1)*(dy*(dy-2)+1)+(1-(rx_sq<<1))*ry_sq;
   while(dy>=0)
     {
      yy=y+dy;
      if(yy>=0 && yy<m_height)
        {
         yy*=m_width;
         xx=x+dx;
         if(xx>=0 && xx<m_width)
            m_pixels[yy+xx]=clr;
         xx=x-dx;
         if(xx>=0 && xx<m_width)
            m_pixels[yy+xx]=clr;
        }
      yy=y-dy;
      if(yy>=0 && yy<m_height)
        {
         yy*=m_width;
         xx=x+dx;
         if(xx>=0 && xx<m_width)
            m_pixels[yy+xx]=clr;
         xx=x-dx;
         if(xx>=0 && xx<m_width)
            m_pixels[yy+xx]=clr;
        }
      if(f<=0)
        {
         dx++;
         f+=(ry_sq<<2)*dx;
        }
      dy--;
      f+=(rx_sq<<1)*(3-(dy<<1));
     }
  }
//+------------------------------------------------------------------+
//| Draws ellipse arc                                                |
//+------------------------------------------------------------------+
void CCanvas::Arc(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,const uint clr)
  {
   int    tmp;
   int    x,y;
   double fi3;
   double fi4;
//--- check
   if(x1==x2 || y1==y2)
      return;
//--- sort by X
   if(x1>x2)
     {
      tmp=x1;
      x1 =x2;
      x2 =tmp;
     }
//--- sort by Y
   if(y1>y2)
     {
      tmp=y1;
      y1 =y2;
      y2 =tmp;
     }
   x =(x2+x1)>>1;
   y =(y2+y1)>>1;
//--- check rays
   if(x3==x && y3==y)
      return;
   if(x4==x && y4==y)
      return;
//--- calculate parameters of ray x3,y3
   fi3=AngleCalc(x,y,x3,y3);
//--- calculate parameters of ray x4,y4
   fi4=AngleCalc(x,y,x4,y4);
//--- draw arc
   Arc(x,y,x2-x,y2-y,fi3,fi4,clr);
  }
//+------------------------------------------------------------------+
//| Draws ellipse arc                                                |
//+------------------------------------------------------------------+
void CCanvas::Arc(int x,int y,int rx,int ry,double fi3,double fi4,const uint clr)
  {
   int x3,y3,x4,y4;
//--- check
   if(rx<10 || ry<10)
      return;
   if(rx<0)
      rx=-rx;
   if(ry<0)
      ry=-ry;
//--- check rays
   if(fi3==fi4)
      return;
//--- adjustment for passing through 0
   if(fi4<fi3)
      fi4+=2*M_PI;
//--- draw arc
   Arc(x,y,rx,ry,fi3,fi4,x3,y3,x4,y4,clr);
  }
//+------------------------------------------------------------------+
//| Calculates angle between ray (x1,y1),(x1+1,y1) and               |
//|                          ray (x1,y1),(x2,y2)                     |
//| Note that y coordinates are inversed                             |
//+------------------------------------------------------------------+
double CCanvas::AngleCalc(int x1,int y1,int x2,int y2)
  {
   double fi;
//--- check
   if(x1==x2)
     {
      if(y1==y2)
         return(EMPTY_VALUE);
      fi=(y2<y1) ? M_PI_2 : 3*M_PI_2;
     }
   else
     {
      //--- calc
      fi=atan((double)(y1-y2)/(x2-x1));
      //--- adjust to the 0-2Pi range
      if(x2<x1)
         fi+=M_PI;
      else
        {
         if(fi<0)
            fi+=2*M_PI;
        }
     }
//--- result
   return(fi);
  }
//+------------------------------------------------------------------+
//| Draws ellipse arc and finds points of intersection with rays     |
//+------------------------------------------------------------------+
void CCanvas::Arc(int x,int y,int rx,int ry,double fi3,double fi4,int &x3,int &y3,int &x4,int &y4,const uint clr)
  {
//--- check
   if(rx<10 || ry<10)
      return;
//--- variables
   int dx,dy;
   int xx,yy;
   int rx_sq,ry_sq;
   int f;
   double fi;
//--- to find intersections
   bool   ray3=false;
   bool   ray4=false;
   bool   ckw=false;
   int    xx_c=0,yy_c=0;
   double fi_c=0.0;
   bool   ackw=false;
   int    xx_a=0,yy_a=0;
   double fi_a=0.0;
//---
   rx_sq=rx*rx;
   ry_sq=ry*ry;
//--- cannot avoid check if each intersection point is between rays
//--- this will significantly decrease drawing speed, but there is no other way
//--- split ellipse into four arcs
//---
//--- 1 top
//---
//--- if arc is obviously not within the rays range, don't draw
   fi=MathMod(fi4,2*M_PI);
   if((fi3<M_PI && fi3>0) ||  // ray 3 is in the 1st or 2nd quadrant
      (fi<M_PI && fi>0)   ||  // ray 4 is in the 1st or 2nd quadrant
      (fi4-fi3>=M_PI))        // arc will pass through the top of the ellipse
     {
      dx=0;
      dy=ry;
      f=(rx_sq<<1)*((dy-1)*dy)+rx_sq+(ry_sq<<1)*(1-rx_sq);
      while(rx_sq*dy>=ry_sq*dx)
        {
         yy=y-dy;
         if(dx==0)
           {
            //--- central point
            fi=AngleCalc(0,0,0,-dy);
            if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI))
              {
               PixelSet(x,yy,clr);
               ckw=ackw=true;
              }
            else
               ckw=ackw=false;
            xx_c=x;
            yy_c=yy;
            fi_c=fi;
            xx_a=x;
            yy_a=yy;
            fi_a=fi;
           }
         else
           {
            //--- iterate clockwise
            xx=x+dx;
            fi=AngleCalc(0,0,dx,-dy);
            if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI))
              {
               PixelSet(xx,yy,clr);
               //--- if arc haven't been drawn before and intersection point of ray 4 and arc is not defined
               //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4
               if(!ckw)
                 {
                  ckw=true;
                  if(!ray4)
                    {
                     if(MathAbs(fi_c-MathMod(fi4,2*M_PI))<MathAbs(fi-MathMod(fi4,2*M_PI)))
                        PixelSet(x4=xx_c,y4=yy_c,clr);
                     else
                       {
                        x4=xx;
                        y4=yy;
                       }
                     ray4=true;
                    }
                 }
              }
            else
              {
               //--- if arc has been drawn before and intersection point of ray 3 and arc is not defined
               //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3
               if(ckw && !ray3)
                 {
                  if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3))
                     PixelSet(x3=xx,y3=yy,clr);
                  else
                    {
                     x3=xx_c;
                     y3=yy_c;
                    }
                  ray3=true;
                 }
               ckw=false;
              }
            //--- save parameters of the last iteration
            xx_c=xx;
            yy_c=yy;
            fi_c=fi;
            //--- iterate counterclockwise
            xx=x-dx;
            fi=AngleCalc(0,0,-dx,-dy);
            if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI))
              {
               PixelSet(xx,yy,clr);
               //--- if arc haven't been drawn before and intersection point of ray 3 and arc is not defined
               //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3
               if(!ackw)
                 {
                  ackw=true;
                  if(!ray3)
                    {
                     if(MathAbs(fi_a-fi3)<MathAbs(fi-fi3))
                        PixelSet(x3=xx_a,y3=yy_a,clr);
                     else
                       {
                        x3=xx;
                        y3=yy;
                       }
                     ray3=true;
                    }
                 }
              }
            else
              {
               //--- if arc has been drawn before and intersection point of ray 3 and arc is not defined
               //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4
               if(ackw && !ray4)
                 {
                  if(MathAbs(fi_a-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI)))
                     PixelSet(x4=xx,y4=yy,clr);
                  else
                    {
                     x4=xx_a;
                     y4=yy_a;
                    }
                  ray4=true;
                 }
               ackw=false;
              }
            //--- save parameters of the last iteration
            xx_a=xx;
            yy_a=yy;
            fi_a=fi;
           }
         //--- calculate coordinates of the next point
         if(f>=0)
           {
            dy--;
            f-=(rx_sq<<2)*dy;
           }
         f+=(ry_sq<<1)*(3+(dx<<1));
         dx++;
        }
      //--- if arc has been drawn clockwise "to the end" and ray 3 had not been found
      if(ckw && !ray3)
        {
         fi=AngleCalc(0,0,dx,-dy);
         if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3))
            PixelSet(x3=x+dx,y3=y-dy,clr);
         else
           {
            x3=xx_c;
            y3=yy_c;
           }
        }
      //--- if arc has been drawn counterclockwise "to the end" and ray 4 had not been found
      if(ackw && !ray4)
        {
         fi=AngleCalc(0,0,-dx,-dy);
         if(MathAbs(fi_a-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI)))
            PixelSet(x4=x-dx,y4=y-dy,clr);
         else
           {
            x4=xx_a;
            y4=yy_a;
           }
        }
     }
//--- 2 left
//---
//--- if arc is obviously not within the rays range, don't draw
   fi=MathMod(fi4,2*M_PI);
   if((fi3>M_PI_2 && fi3<3*M_PI_2) ||  // ray 3 is in the 2nd or 3rd quadrant
      (fi>M_PI_2 && fi<3*M_PI_2)   ||  // ray 4 is in the 2nd or 3rd quadrant
      (fi4-fi3>=M_PI))                 // arc will pass through the left part of the ellipse
     {
      dx=rx;
      dy=0;
      f=(ry_sq<<1)*((dx-1)*dx)+ry_sq+(rx_sq<<1)*(1-ry_sq);
      while(ry_sq*dx>=rx_sq*dy)
        {
         xx=x-dx;
         if(dy==0)
           {
            //--- central point
            fi=AngleCalc(0,0,-dx,0);
            if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI))
              {
               PixelSet(xx,y,clr);
               ckw=ackw=true;
              }
            else
               ckw=ackw=false;
            xx_c=xx;
            yy_c=y;
            fi_c=fi;
            xx_a=xx;
            yy_a=y;
            fi_a=fi;
           }
         else
           {
            //--- iterate clockwise
            yy=y-dy;
            fi=AngleCalc(0,0,-dx,-dy);
            if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI))
              {
               PixelSet(xx,yy,clr);
               //--- if arc haven't been drawn before and intersection point of ray 4 and arc is not defined
               //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4
               if(!ckw)
                 {
                  ckw=true;
                  if(!ray4)
                    {
                     if(MathAbs(fi_c-MathMod(fi4,2*M_PI))<MathAbs(fi-MathMod(fi4,2*M_PI)))
                        PixelSet(x4=xx_c,y4=yy_c,clr);
                     else
                       {
                        x4=xx;
                        y4=yy;
                       }
                     ray4=true;
                    }
                 }
              }
            else
              {
               //--- if arc has been drawn before and intersection point of ray 3 and arc is not defined
               //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3
               if(ckw && !ray3)
                 {
                  if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3))
                     PixelSet(x3=xx,y3=yy,clr);
                  else
                    {
                     x3=xx_c;
                     y3=yy_c;
                    }
                  ray3=true;
                 }
               ckw=false;
              }
            //--- save parameters of the last iteration
            xx_c=xx;
            yy_c=yy;
            fi_c=fi;
            //--- iterate counterclockwise
            yy=y+dy;
            fi=AngleCalc(0,0,-dx,dy);
            if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI))
              {
               PixelSet(xx,yy,clr);
               //--- if arc haven't been drawn before and intersection point of ray 3 and arc is not defined
               //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3
               if(!ackw)
                 {
                  ackw=true;
                  if(!ray3)
                    {
                     if(MathAbs(fi_a-fi3)<MathAbs(fi-fi3))
                        PixelSet(x3=xx_a,y3=yy_a,clr);
                     else
                       {
                        x3=xx;
                        y3=yy;
                       }
                     ray3=true;
                    }
                 }
              }
            else
              {
               //--- if arc has been drawn before and intersection point of ray 3 and arc is not defined
               //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4
               if(ackw && !ray4)
                 {
                  if(MathAbs(fi_a-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI)))
                     PixelSet(x4=xx,y4=yy,clr);
                  else
                    {
                     x4=xx_a;
                     y4=yy_a;
                    }
                  ray4=true;
                 }
               ackw=false;
              }
            //--- save parameters of the last iteration
            xx_a=xx;
            yy_a=yy;
            fi_a=fi;
           }
         //--- calculate coordinates of the next point
         if(f>=0)
           {
            dx--;
            f-=(ry_sq<<2)*dx;
           }
         f+=(rx_sq<<1)*(3+(dy<<1));
         dy++;
        }
      //--- if arc has been drawn clockwise "to the end" and ray 3 had not been found
      if(ckw && !ray3)
        {
         fi=AngleCalc(0,0,-dx,-dy);
         if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3))
            PixelSet(x3=x-dx,y3=y-dy,clr);
         else
           {
            x3=xx_c;
            y3=yy_c;
           }
        }
      //--- if arc has been drawn counterclockwise "to the end" and ray 4 had not been found
      if(ackw && !ray4)
        {
         fi=AngleCalc(0,0,-dx,dy);
         if(MathAbs(fi_a-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI)))
            PixelSet(x4=x-dx,y4=y+dy,clr);
         else
           {
            x4=xx_a;
            y4=yy_a;
           }
        }
     }
//--- 3 bottom
//---
//--- if arc is obviously not within the rays range, don't draw
   fi=MathMod(fi4,2*M_PI);
   if((fi3>M_PI && fi3<2*M_PI) ||  // ray 3 is in the 3rd or 4th quadrant
      (fi>M_PI && fi<2*M_PI)   ||  // ray 4 is in the 3rd or 4th quadrant
      (fi4-fi3>=M_PI))             // arc will pass through the bottom of the ellipse
     {
      dx=0;
      dy=ry;
      f=(rx_sq<<1)*((dy-1)*dy)+rx_sq+(ry_sq<<1)*(1-rx_sq);
      while(rx_sq*dy>=ry_sq*dx)
        {
         yy=y+dy;
         if(dx==0)
           {
            //--- central point
            fi=AngleCalc(0,0,0,dy);
            if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI))
              {
               PixelSet(x,yy,clr);
               ckw=ackw=true;
              }
            else
               ckw=ackw=false;
            xx_c=x;
            yy_c=yy;
            fi_c=fi;
            xx_a=x;
            yy_a=yy;
            fi_a=fi;
           }
         else
           {
            //--- iterate clockwise
            xx=x-dx;
            fi=AngleCalc(0,0,-dx,dy);
            if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI))
              {
               PixelSet(xx,yy,clr);
               //--- if arc haven't been drawn before and intersection point of ray 4 and arc is not defined
               //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4
               if(!ckw)
                 {
                  ckw=true;
                  if(!ray4)
                    {
                     if(MathAbs(fi_c-MathMod(fi4,2*M_PI))<MathAbs(fi-MathMod(fi4,2*M_PI)))
                        PixelSet(x4=xx_c,y4=yy_c,clr);
                     else
                       {
                        x4=xx;
                        y4=yy;
                       }
                     ray4=true;
                    }
                 }
              }
            else
              {
               //--- if arc has been drawn before and intersection point of ray 3 and arc is not defined
               //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3
               if(ckw && !ray3)
                 {
                  if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3))
                     PixelSet(x3=xx,y3=yy,clr);
                  else
                    {
                     x3=xx_c;
                     y3=yy_c;
                    }
                  ray3=true;
                 }
               ckw=false;
              }
            //--- save parameters of the last iteration
            xx_c=xx;
            yy_c=yy;
            fi_c=fi;
            //--- iterate counterclockwise
            xx=x+dx;
            fi=AngleCalc(0,0,dx,dy);
            if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI))
              {
               PixelSet(xx,yy,clr);
               //--- if arc haven't been drawn before and intersection point of ray 3 and arc is not defined
               //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3
               if(!ackw)
                 {
                  ackw=true;
                  if(!ray3)
                    {
                     if(MathAbs(fi_a-fi3)<MathAbs(fi-fi3))
                        PixelSet(x3=xx_a,y3=yy_a,clr);
                     else
                       {
                        x3=xx;
                        y3=yy;
                       }
                     ray3=true;
                    }
                 }
              }
            else
              {
               //--- if arc has been drawn before and intersection point of ray 3 and arc is not defined
               //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4
               if(ackw && !ray4)
                 {
                  if(MathAbs(fi_a-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI)))
                     PixelSet(x4=xx,y4=yy,clr);
                  else
                    {
                     x4=xx_a;
                     y4=yy_a;
                    }
                  ray4=true;
                 }
               ackw=false;
              }
            //--- save parameters of the last iteration
            xx_a=xx;
            yy_a=yy;
            fi_a=fi;
           }
         //--- calculate coordinates of the next point
         if(f>=0)
           {
            dy--;
            f-=(rx_sq<<2)*dy;
           }
         f+=(ry_sq<<1)*(3+(dx<<1));
         dx++;
        }
      //--- if arc has been drawn clockwise "to the end" and ray 3 had not been found
      if(ckw && !ray3)
        {
         fi=AngleCalc(0,0,-dx,dy);
         if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3))
            PixelSet(x3=x-dx,y3=y+dy,clr);
         else
           {
            x3=xx_c;
            y3=yy_c;
           }
        }
      //--- if arc has been drawn counterclockwise "to the end" and ray 4 had not been found
      if(ackw && !ray4)
        {
         fi=AngleCalc(0,0,dx,dy);
         if(MathAbs(fi_a-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI)))
            PixelSet(x4=x+dx,y4=y+dy,clr);
         else
           {
            x4=xx_a;
            y4=yy_a;
           }
        }
     }
//--- 4 right
//---
//--- if arc is obviously not within the rays range, don't draw
   fi=MathMod(fi4,2*M_PI);
   if((fi3<M_PI_2 || fi3>3*M_PI_2) ||  // ray 3 is 1 or 4 quadrant
      (fi<M_PI_2 || fi>3*M_PI_2)   ||  // ray 4 is 1 or 4 quadrant
      (fi4-fi3>=M_PI))                 // arc will pass through the right side of the ellipse
     {
      dx=rx;
      dy=0;
      f=(ry_sq<<1)*((dx-1)*dx)+ry_sq+(rx_sq<<1)*(1-ry_sq);
      while(ry_sq*dx>=rx_sq*dy)
        {
         xx=x+dx;
         if(dy==0)
           {
            //--- central point
            fi=AngleCalc(0,0,dx,0);
            if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI))
              {
               PixelSet(xx,y,clr);
               ckw=ackw=true;
              }
            else
               ckw=ackw=false;
            xx_c=xx;
            yy_c=y;
            fi_c=fi;
            xx_a=xx;
            yy_a=y;
            fi_a=fi;
           }
         else
           {
            //--- iterate clockwise
            yy=y+dy;
            fi=AngleCalc(0,0,dx,dy);
            if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI))
              {
               PixelSet(xx,yy,clr);
               //--- if arc haven't been drawn before and intersection point of ray 4 and arc is not defined
               //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4
               if(!ckw)
                 {
                  ckw=true;
                  if(!ray4)
                    {
                     if(MathAbs(fi_c-MathMod(fi4,2*M_PI))<MathAbs(fi-MathMod(fi4,2*M_PI)))
                        PixelSet(x4=xx_c,y4=yy_c,clr);
                     else
                       {
                        x4=xx;
                        y4=yy;
                       }
                     ray4=true;
                    }
                 }
              }
            else
              {
               //--- if arc has been drawn before and intersection point of ray 3 and arc is not defined
               //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3
               if(ckw && !ray3)
                 {
                  if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3))
                     PixelSet(x3=xx,y3=yy,clr);
                  else
                    {
                     x3=xx_c;
                     y3=yy_c;
                    }
                  ray3=true;
                 }
               ckw=false;
              }
            //--- save parameters of the last iteration
            xx_c=xx;
            yy_c=yy;
            fi_c=fi;
            //--- iterate counterclockwise
            yy=y-dy;
            fi=AngleCalc(0,0,dx,-dy);
            if((fi<=fi4 && fi3<=fi) || (fi4>=2*M_PI && fi<=fi4-2*M_PI))
              {
               PixelSet(xx,yy,clr);
               //--- if arc haven't been drawn before and intersection point of ray 3 and arc is not defined
               //--- this means that we (while iterating over points of the ellipse) had just crossed ray 3
               if(!ackw)
                 {
                  ackw=true;
                  if(!ray3)
                    {
                     if(MathAbs(MathMod(fi_a,2*M_PI)-fi3)<MathAbs(fi-fi3))
                        PixelSet(x3=xx_a,y3=yy_a,clr);
                     else
                       {
                        x3=xx;
                        y3=yy;
                       }
                     ray3=true;
                    }
                 }
              }
            else
              {
               //--- if arc has been drawn before and intersection point of ray 3 and arc is not defined
               //--- this means that we (while iterating over points of the ellipse) had just crossed ray 4
               if(ackw && !ray4)
                 {
                  if(MathAbs(MathMod(fi_a,2*M_PI)-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI)))
                     PixelSet(x4=xx,y4=yy,clr);
                  else
                    {
                     x4=xx_a;
                     y4=yy_a;
                    }
                  ray4=true;
                 }
               ackw=false;
              }
            //--- save parameters of the last iteration
            xx_a=xx;
            yy_a=yy;
            fi_a=fi;
           }
         //--- calculate coordinates of the next point
         if(f>=0)
           {
            dx--;
            f-=(ry_sq<<2)*dx;
           }
         f+=(rx_sq<<1)*(3+(dy<<1));
         dy++;
        }
      //--- if arc has been drawn clockwise "to the end" and ray 3 had not been found
      if(ckw && !ray3)
        {
         fi=AngleCalc(0,0,dx,dy);
         if(MathAbs(fi_c-fi3)>MathAbs(fi-fi3))
            PixelSet(x3=x+dx,y3=y+dy,clr);
         else
           {
            x3=xx_c;
            y3=yy_c;
           }
        }
      //--- if arc has been drawn counterclockwise "to the end" and ray 4 had not been found
      if(ackw && !ray4)
        {
         fi=AngleCalc(0,0,dx,-dy);
         if(MathAbs(MathMod(fi_a,2*M_PI)-MathMod(fi4,2*M_PI))>MathAbs(fi-MathMod(fi4,2*M_PI)))
            PixelSet(x4=x+dx,y4=y-dy,clr);
         else
           {
            x4=xx_a;
            y4=yy_a;
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Draws ellipse pie                                                |
//+------------------------------------------------------------------+
void CCanvas::Pie(int x1,int y1,int x2,int y2,int x3,int y3,int x4,int y4,const uint clr,const uint fill_clr)
  {
   int tmp;
   int x,y;
//---
   double fi3;
   double fi4;
//--- check
   if(x1==x2 || y1==y2)
      return;
//--- sort by X
   if(x1>x2)
     {
      tmp=x1;
      x1 =x2;
      x2 =tmp;
     }
//--- sort by Y
   if(y1>y2)
     {
      tmp=y1;
      y1 =y2;
      y2 =tmp;
     }
   x =(x2+x1)>>1;
   y =(y2+y1)>>1;
//--- check rays
   if(x3==x && y3==y)
      return;
   if(x4==x && y4==y)
      return;
//--- calculate parameters of ray x3,y3
   fi3=AngleCalc(x,y,x3,y3);
//--- calculate parameters of ray x4,y4
   fi4=AngleCalc(x,y,x4,y4);
//--- draw pie
   Pie(x,y,x2-x,y2-y,fi3,fi4,clr,fill_clr);
  }
//+------------------------------------------------------------------+
//| Draws ellipse pie                                                |
//+------------------------------------------------------------------+
void CCanvas::Pie(int x,int y,int rx,int ry,double fi3,double fi4,const uint clr,const uint fill_clr)
  {
   int x3=x;
   int y3=y;
   int x4=x;
   int y4=y;
//--- check
   if(rx==0 || ry==0)
      return;
   if(rx<0)
      rx=-rx;
   if(ry<0)
      ry=-ry;
//--- check rays
   if(fi3==fi4)
      return;
//--- adjustment for passing through 0
   if(fi4<fi3)
      fi4+=2*M_PI;
//--- draw arc and radii
   Arc(x,y,rx,ry,fi3,fi4,x3,y3,x4,y4,clr);
   if((x==x3 && y==y3) || (x==x4 && y==y4))
      return;
   Line(x,y,x3,y3,clr);
   Line(x,y,x4,y4,clr);
//--- fill
   if(rx>ry)
      rx=ry;
   double fi=(fi3+fi4)/2;
   int xf=x+(int)(0.9*rx*cos(fi));
   int yf=y-(int)(0.9*rx*sin(fi));
   Fill(xf,yf,fill_clr);
  }
//+------------------------------------------------------------------+
//| Draw filled circle                                               |
//+------------------------------------------------------------------+
void CCanvas::FillCircle(int x,int y,int r,const uint clr)
  {
   int f   =1-r;
   int dd_x=1;
   int dd_y=-2*r;
   int dx  =0;
   int dy  =r;
//--- draw
   while(dy>=dx)
     {
      LineHorizontal(x-dy,x+dy,y-dx,clr);
      LineHorizontal(x-dy,x+dy,y+dx,clr);
      //---
      if(f>=0)
        {
         LineHorizontal(x-dx,x+dx,y-dy,clr);
         LineHorizontal(x-dx,x+dx,y+dy,clr);
         dy--;
         dd_y+=2;
         f+=dd_y;
        }
      dx++;
      dd_x+=2;
      f+=dd_x;
     }
  }
//+------------------------------------------------------------------+
//| Draw filled ellipse                                              |
//+------------------------------------------------------------------+
void CCanvas::FillEllipse(int x1,int y1,int x2,int y2,const uint clr)
  {
   int x,y;
   int rx,ry;
   int dx,dy;
   int rx_sq,ry_sq;
   int f;
   int tmp;
//--- handle extreme conditions
   if(x1==x2)
     {
      if(y1==y2)
         PixelSet(x1,y1,clr);
      else
         LineVertical(x1,y1,y2,clr);
      return;
     }
   if(y1==y2)
     {
      LineHorizontal(x1,x2,y1,clr);
      return;
     }
//--- sort by X
   if(x1>x2)
     {
      tmp=x1;
      x1 =x2;
      x2 =tmp;
     }
//--- sort by Y
   if(y1>y2)
     {
      tmp=y1;
      y1 =y2;
      y2 =tmp;
     }
   x =(x2+x1)>>1;
   y =(y2+y1)>>1;
   rx=(x2-x1)>>1;
   ry=(y2-y1)>>1;
   dx=0;
   dy=ry;
   rx_sq=rx*rx;
   ry_sq=ry*ry;
   f=(rx_sq<<1)*((dy-1)*dy)+rx_sq+(ry_sq<<1)*(1-rx_sq);
   while(rx_sq*dy>ry_sq*(dx))
     {
      LineHorizontal(x-dx,x+dx,y+dy,clr);
      LineHorizontal(x-dx,x+dx,y-dy,clr);
      if(f>=0)
        {
         dy--;
         f-=(rx_sq<<2)*dy;
        }
      f+=(ry_sq<<1)*(3+(dx<<1));
      dx++;
     }
   f=(ry_sq<<1)*(dx+1)*dx+(rx_sq<<1)*(dy*(dy-2)+1)+(1-(rx_sq<<1))*ry_sq;
   while(dy>=0)
     {
      LineHorizontal(x-dx,x+dx,y+dy,clr);
      LineHorizontal(x-dx,x+dx,y-dy,clr);
      if(f<=0)
        {
         dx++;
         f+=(ry_sq<<2)*dx;
        }
      dy--;
      f+=(rx_sq<<1)*(3-(dy<<1));
     }
  }
//+------------------------------------------------------------------+
//| Draw filled rectangle                                            |
//+------------------------------------------------------------------+
void CCanvas::FillRectangle(int x1,int y1,int x2,int y2,const uint clr)
  {
   int tmp;
//--- sort vertexes
   if(x2<x1)
     {
      tmp=x1;
      x1 =x2;
      x2 =tmp;
     }
   if(y2<y1)
     {
      tmp=y1;
      y1 =y2;
      y2 =tmp;
     }
//--- out of screen boundaries
   if(x2<0 || y2<0 || x1>=m_width || y1>=m_height)
      return;
//--- stay withing screen boundaries
   if(x1<0)
      x1=0;
   if(y1<0)
      y1=0;
   if(x2>=m_width)
      x2=m_width -1;
   if(y2>=m_height)
      y2=m_height-1;
   int len=(x2-x1)+1;
//--- set pixels
   for(; y1<=y2; y1++)
      ArrayFill(m_pixels,y1*m_width+x1,len,clr);
  }
//+------------------------------------------------------------------+
//| Draw filled triangle                                             |
//+------------------------------------------------------------------+
void CCanvas::FillTriangle(int x1,int y1,int x2,int y2,int x3,int y3,const uint clr)
  {
   int    xx1,xx2,tmp;
   double k1=0,k2=0,xd1,xd2;
//--- sort vertexes from lesser to greater
   if(y1>y2)
     {
      tmp=y2;
      y2 =y1;
      y1 =tmp;
      tmp=x2;
      x2 =x1;
      x1=tmp;
     }
   if(y1>y3)
     {
      tmp=y1;
      y1 =y3;
      y3 =tmp;
      tmp=x1;
      x1 =x3;
      x3 =tmp;
     }
   if(y2>y3)
     {
      tmp=y2;
      y2 =y3;
      y3 =tmp;
      tmp=x2;
      x2 =x3;
      x3 =tmp;
     }
//--- all vertexes are out of image boundaries
   if(y3<0 || y1>m_height)
      return;
   if(x1<0 && x2<0 && x3<0)
      return;
   if(x1>m_width && x2>m_width && x3>m_width)
      return;
//--- find coefficients of lines
   if((tmp=y1-y2)!=0)
      k1=(x1-x2)/(double)tmp;
   if((tmp=y1-y3)!=0)
      k2=(x1-x3)/(double)tmp;
//---
   xd1=x1;
   xd2=x1;
//---
   for(int i=y1; i<=y3; i++)
     {
      if(i==y2)
        {
         if((tmp=y2-y3)!=0)
            k1=(x2-x3)/(double)tmp;
         xd1=x2;
        }
      //--- calculate new boundaries of triangle line
      xx1 =(int)xd1;
      xd1+=k1;
      xx2 =(int)xd2;
      xd2+=k2;
      //--- triangle line is out of screen boundaries
      if(i<0 || i>=m_height)
         continue;
      //--- sort
      if(xx1>xx2)
        {
         tmp=xx1;
         xx1=xx2;
         xx2=tmp;
        }
      //--- line is out of screen boundaries
      if(xx2<0 || xx1>=m_width)
         continue;
      //--- draw only what is within screen boundaries
      if(xx1<0)
         xx1=0;
      if(xx2>=m_width)
         xx2=m_width-1;
      //--- draw horizontal line of triangle
      ArrayFill(m_pixels,i*m_width+xx1,xx2-xx1,clr);
     }
  }
//+------------------------------------------------------------------+
//| Draw filled poligon                                              |
//+------------------------------------------------------------------+
void CCanvas::FillPolygon(int &x[],int &y[],const uint clr)
  {
   static CPoint p[];
   int total=ArraySize(x);
   if(total>ArraySize(y))
      total=ArraySize(y);
//--- check
   if(total<3)
      return;
//--- resize array of points
   ArrayResize(p,total);
//--- find top-left point
   int imin=0;
   int xmin=x[0];
   int ymin=y[0];
   for(int i=1; i<total; i++)
     {
      if(y[i]>ymin)
         continue;
      if(y[i]==ymin)
        {
         if(x[i]<xmin)
           {
            xmin=x[i];
            imin=i;
           }
         continue;
        }
      xmin=x[i];
      ymin=y[i];
      imin=i;
     }
//--- copy coordinates arrays to array of pixels (starting from top-left)
   for(int i=0; i<total; i++,imin++)
     {
      p[i].x=x[imin%total];
      p[i].y=y[imin%total];
     }
   PolygonFill(p,clr);
   ArrayFree(p);
  }
//+------------------------------------------------------------------+
//| Draw pixel with antialiasing                                     |
//+------------------------------------------------------------------+
void CCanvas::PixelSetAA(const double x,const double y,const uint clr)
  {
   static double rr[4];
   static int    xx[4];
   static int    yy[4];
//--- preliminary calculations
   int    ix=(int)MathRound(x);
   int    iy=(int)MathRound(y);
   double rrr=0;
   double k;
   double dx=x-ix;
   double dy=y-iy;
   uchar  a,r,g,b;
   uint   c;
//--- no need for anti-aliasing
   if(dx==0.0 && dy==0.0)
     {
      PixelSet(ix,iy,clr);
      return;
     }
//--- prepare array of pixels
   xx[0]=xx[2]=ix;
   yy[0]=yy[1]=iy;
   if(dx<0.0)
      xx[1]=xx[3]=ix-1;
   if(dx==0.0)
      xx[1]=xx[3]=ix;
   if(dx>0.0)
      xx[1]=xx[3]=ix+1;
   if(dy<0.0)
      yy[2]=yy[2]=iy-1;
   if(dy==0.0)
      yy[2]=yy[2]=iy;
   if(dy>0.0)
      yy[2]=yy[2]=iy+1;
//--- calculate radii and sum of their squares
   for(int i=0; i<4; i++)
     {
      dx=xx[i]-x;
      dy=yy[i]-y;
      rr[i]=1/(dx*dx+dy*dy);
      rrr+=rr[i];
     }
//--- draw pixels
   for(int i=0; i<4; i++)
     {
      k=rr[i]/rrr;
      c=PixelGet(xx[i],yy[i]);
      a=(uchar)(k*GETRGBA(clr)+(1-k)*GETRGBA(c));
      r=(uchar)(k*GETRGBR(clr)+(1-k)*GETRGBR(c));
      g=(uchar)(k*GETRGBG(clr)+(1-k)*GETRGBG(c));
      b=(uchar)(k*GETRGBB(clr)+(1-k)*GETRGBB(c));
      PixelSet(xx[i],yy[i],ARGB(a,r,g,b));
     }
  }
//+------------------------------------------------------------------+
//| Get line style                                                   |
//+------------------------------------------------------------------+
uint CCanvas::LineStyleGet(void) const
  {
   switch(m_style)
     {
      case 0xFFFFFF:
         return(STYLE_SOLID);
         break;
      case 0x3FFFF:
         return(STYLE_DASH);
         break;
      case 0x1C71C7:
         return(STYLE_DOT);
         break;
      case 0x381FF:
         return(STYLE_DASHDOT);
         break;
      case 0x1C71FF:
         return(STYLE_DASHDOTDOT);
         break;
      default:
         return (m_style);
         break;
     }
  }
//+------------------------------------------------------------------+
//| Set line style                                                   |
//+------------------------------------------------------------------+
void CCanvas::LineStyleSet(const uint style)
  {
   switch(style)
     {
      case STYLE_SOLID:
         m_style=0xFFFFFF;
         break;
      case STYLE_DASH:
         m_style=0x3FFFF;
         break;
      case STYLE_DOT:
         m_style=0x1C71C7;
         break;
      case STYLE_DASHDOT:
         m_style=0x381FF;
         break;
      case STYLE_DASHDOTDOT:
         m_style=0x1C71FF;
         break;
      default:
         //--- high-order bit must be set then custom style
         if((style&0x80000000)!=0)
           {
            m_style=style;
           }
         break;
     }
   m_style_idx=0;
  }
//+------------------------------------------------------------------+
//| Draw line with antialiasing (with style)                         |
//+------------------------------------------------------------------+
void CCanvas::LineAA(const int x1,const int y1,const int x2,const int y2,const uint clr,const uint style)
  {
//--- line is out of image boundaries
   if((x1<0 && x2<0) || (y1<0 && y2<0))
      return;
   if(x1>=m_width && x2>=m_width)
      return;
   if(y1>=m_height && y2>=m_height)
      return;
//--- check
   if(x1==x2 && y1==y2)
     {
      PixelSet(x1,y1,clr);
      return;
     }
//--- set the line style
   uint prev_style=m_style;
   if(style!=UINT_MAX)
      LineStyleSet(style);
//--- preliminary calculations
   double dx=x2-x1;
   double dy=y2-y1;
   double xy=sqrt(dx*dx+dy*dy);
   double xx=x1;
   double yy=y1;
   uint   mask=1<<m_style_idx;
//--- set pixels
   dx/=xy;
   dy/=xy;
   do
     {
      if((m_style&mask)==mask)
        {
         PixelSetAA(xx,yy,clr);
        }
      xx+=dx;
      yy+=dy;
      mask<<=1;
      if(mask==0x1000000)
         mask=1;
     }
   while(fabs(x2-xx)>=fabs(dx) && fabs(y2-yy)>=fabs(dy));
//--- set last pixel
   if((m_style&mask)==mask)
     {
      PixelSetAA(x2,y2,clr);
     }
//--- set the previous line style
   if(style!=UINT_MAX)
      m_style=prev_style;
  }
//+------------------------------------------------------------------+
//| Draw polyline with antialiasing (with style)                     |
//+------------------------------------------------------------------+
void CCanvas::PolylineAA(int &x[],int &y[],const uint clr,const uint style)
  {
//--- check arrays
   int total=ArraySize(x);
   if(total>ArraySize(y))
      total=ArraySize(y);
//--- check
   if(total<2)
      return;
   total--;
//--- set the line style
   uint prev_style=m_style;
   if(style!=UINT_MAX)
      LineStyleSet(style);
   uint mask=1<<m_style_idx;
//--- draw
   for(int i=0; i<total; i++)
     {
      int x1=x[i];
      int y1=y[i];
      int x2=x[i+1];
      int y2=y[i+1];
      //--- line is out of image boundaries
      if((x1<0 && x2<0) || (y1<0 && y2<0))
        {
         //--- set the previous line style
         if(style!=UINT_MAX)
            m_style=prev_style;
         return;
        }
      if(x1>=m_width && x2>=m_width)
        {
         //--- set the previous line style
         if(style!=UINT_MAX)
            m_style=prev_style;
         return;
        }
      if(y1>=m_height && y2>=m_height)
        {
         //--- set the previous line style
         if(style!=UINT_MAX)
            m_style=prev_style;
         return;
        }
      //--- check
      if(x1==x2 && y1==y2)
        {
         PixelSet(x1,y1,clr);
         //--- set the previous line style
         if(style!=UINT_MAX)
            m_style=prev_style;
         return;
        }
      //--- preliminary calculations
      double dx=x2-x1;
      double dy=y2-y1;
      double xy=sqrt(dx*dx+dy*dy);
      double xx=x1;
      double yy=y1;
      //--- set pixels
      dx/=xy;
      dy/=xy;
      do
        {
         if((m_style&mask)==mask)
           {
            PixelSetAA(xx,yy,clr);
           }
         xx+=dx;
         yy+=dy;
         mask<<=1;
         if(mask==0x1000000)
            mask=1;
        }
      while(fabs(x2-xx)>=fabs(dx) && fabs(y2-yy)>=fabs(dy));
      //--- set last pixel
      if((m_style&mask)==mask)
        {
         PixelSetAA(x2,y2,clr);
        }
      mask<<=1;
      if(mask==0x1000000)
         mask=1;
     }
//--- set the previous line style
   if(style!=UINT_MAX)
      m_style=prev_style;
  }
//+------------------------------------------------------------------+
//| Draw polygon with antialiasing (with style)                      |
//+------------------------------------------------------------------+
void CCanvas::PolygonAA(int &x[],int &y[],const uint clr,const uint style)
  {
//--- check arrays
   int total=ArraySize(x);
   if(total>ArraySize(y))
      total=ArraySize(y);
//--- check
   if(total<2)
      return;
//--- set the line style
   uint prev_style=m_style;
   if(style!=UINT_MAX)
      LineStyleSet(style);
   uint mask=1<<m_style_idx;
//--- draw
   for(int i=0; i<total; i++)
     {
      int x1=x[i];
      int y1=y[i];
      int x2=(i+1!=total) ? x[i+1] : x[0];
      int y2=(i+1!=total) ? y[i+1] : y[0];
      //--- line is out of image boundaries
      if((x1<0 && x2<0) || (y1<0 && y2<0))
        {
         //--- set the previous line style
         if(style!=UINT_MAX)
            m_style=prev_style;
         return;
        }
      if(x1>=m_width && x2>=m_width)
        {
         //--- set the previous line style
         if(style!=UINT_MAX)
            m_style=prev_style;
         return;
        }
      if(y1>=m_height && y2>=m_height)
        {
         //--- set the previous line style
         if(style!=UINT_MAX)
            m_style=prev_style;
         return;
        }
      //--- check
      if(x1==x2 && y1==y2)
        {
         PixelSet(x1,y1,clr);
         //--- set the previous line style
         if(style!=UINT_MAX)
            m_style=prev_style;
         return;
        }
      //--- preliminary calculations
      double dx=x2-x1;
      double dy=y2-y1;
      double xy=sqrt(dx*dx+dy*dy);
      double xx=x1;
      double yy=y1;
      //--- set pixels
      dx/=xy;
      dy/=xy;
      do
        {
         if((m_style&mask)==mask)
           {
            PixelSetAA(xx,yy,clr);
           }
         xx+=dx;
         yy+=dy;
         mask<<=1;
         if(mask==0x1000000)
            mask=1;
        }
      while(fabs(x2-xx)>=fabs(dx) && fabs(y2-yy)>=fabs(dy));
      //--- set last pixel
      if((m_style&mask)==mask)
        {
         PixelSetAA(x2,y2,clr);
        }
     }
//--- set the previous line style
   if(style!=UINT_MAX)
      m_style=prev_style;
  }
//+------------------------------------------------------------------+
//| Draw triangle with antialiasing                                  |
//+------------------------------------------------------------------+
void CCanvas::TriangleAA(const int x1,const int y1,const int x2,const int y2,const int x3,const int y3,const uint clr,const uint style)
  {
//--- draw
   int x[3];
   int y[3];
   x[0] = x1;
   x[1] = x2;
   x[2] = x3;
   y[0] = y1;
   y[1] = y2;
   y[2] = y3;
   PolygonAA(x,y,clr,style);
  }
//+------------------------------------------------------------------+
//| Draw circle with antialiasing                                    |
//+------------------------------------------------------------------+
void CCanvas::CircleAA(const int x,const int y,const double r,const uint clr,const uint style=UINT_MAX)
  {
   if(r<=0)
      return;
//--- preliminary calculations
   double xx=x+r;
   double yy=y;
   double fi=0;
   double df=M_PI_2/MathCeil(r);
//--- set the line style
   uint prev_style=m_style;
   if(style!=UINT_MAX)
      LineStyleSet(style);
   uint mask=1<<m_style_idx;
//--- draw
   if(r>M_PI)
      df/=2;
   do
     {
      xx=x+r*cos(fi);
      yy=y-r*sin(fi);
      if((m_style&mask)==mask)
         PixelSetAA(xx,yy,clr);
      mask<<=1;
      if(mask==0x1000000)
         mask=1;
      fi+=df;
     }
   while(fabs(2*M_PI-fi)>=df/2);
//--- set the previous line style
   if(style!=UINT_MAX)
      m_style=prev_style;
  }
//+------------------------------------------------------------------+
//| Draw ellipse with antialiasing                                   |
//+------------------------------------------------------------------+
void CCanvas::EllipseAA(const double x1,const double y1,const double x2,const double y2,const uint clr,const uint style=UINT_MAX)
  {
   double rx = (x2-x1)/2;
   double ry = (y2-y1)/2;
//--- preliminary calculations
   double x=(x2>x1) ? x1+rx : x2+rx;
   double y=(y2>y1) ? y1+ry : y2+ry;
   double rx2=rx*rx;
   double ry2=ry*ry;
//--- set the line style
   uint prev_style=m_style;
   if(style!=UINT_MAX)
      LineStyleSet(style);
   uint mask=1<<m_style_idx;
//--- draw
   double quarter=round(rx2/sqrt(rx2+ry2));
   for(double dx=0; dx<=quarter; dx++)
     {
      double dy=ry*sqrt(1-dx*dx/rx2);
      if((m_style&mask)==mask)
         PixelSet4AA(x,y,dx,dy,clr);
      mask<<=1;
      if(mask==0x1000000)
         mask=1;
     }
   quarter=round(ry2/sqrt(rx2+ry2));
   for(double dy=0; dy<=quarter; dy++)
     {
      double dx=rx*sqrt(1-dy*dy/ry2);
      if((m_style&mask)==mask)
         PixelSet4AA(x,y,dx,dy,clr);
      mask<<=1;
      if(mask==0x1000000)
         mask=1;
     }
//--- set the previous line style
   if(style!=UINT_MAX)
      m_style=prev_style;
  }
//+------------------------------------------------------------------+
//| Gets default color                                               |
//+------------------------------------------------------------------+
static uint CCanvas::GetDefaultColor(const int i)
  {
   if(i<0)
      return(0);
   if(i<ArraySize(m_default_colors))
      return(m_default_colors[i]);
//---
   return(XRGB((i%3)*60+i*50,((i+1)%3)*60+i*60,((i+2)%3)*60+i*50));
  }
//+------------------------------------------------------------------+
//| Set level of transparency                                        |
//+------------------------------------------------------------------+
void CCanvas::TransparentLevelSet(const uchar value)
  {
   int total=ArraySize(m_pixels);
   uint value24=(uint)value<<24;
   for(int i=0; i<total; i++)
      m_pixels[i]=value24|(m_pixels[i]&0xFFFFFF);
  }
//+------------------------------------------------------------------+
//| Set font                                                         |
//+------------------------------------------------------------------+
bool CCanvas::FontSet(const string name,const int size,const uint flags,const uint angle)
  {
   if(!TextSetFont(name,size,flags,angle))
      return(false);
//--- save parameters of generated font
   m_fontname =name;
   m_fontsize =size;
   m_fontflags=flags;
   m_fontangle=angle;
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Set fontname                                                     |
//+------------------------------------------------------------------+
bool CCanvas::FontNameSet(string name)
  {
   if(!TextSetFont(name,m_fontsize,m_fontflags,m_fontangle))
      return(false);
//--- save parameter of generated font
   m_fontname=name;
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Set fontsize                                                     |
//+------------------------------------------------------------------+
bool CCanvas::FontSizeSet(int size)
  {
   if(!TextSetFont(m_fontname,size,m_fontflags,m_fontangle))
      return(false);
//--- save parameter of generated font
   m_fontsize=size;
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Set fontflags                                                    |
//+------------------------------------------------------------------+
bool CCanvas::FontFlagsSet(uint flags)
  {
   if(!TextSetFont(m_fontname,m_fontsize,flags,m_fontangle))
      return(false);
//--- save parameter of generated font
   m_fontflags=flags;
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Set fontangle                                                    |
//+------------------------------------------------------------------+
bool CCanvas::FontAngleSet(uint angle)
  {
   if(!TextSetFont(m_fontname,m_fontsize,m_fontflags,angle))
      return(false);
//--- save parameter of generated font
   m_fontangle=angle;
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Set font                                                         |
//+------------------------------------------------------------------+
bool CCanvas::FontSet(void)
  {
   return(TextSetFont(m_fontname,m_fontsize,m_fontflags,m_fontangle));
  }
//+------------------------------------------------------------------+
//| Get font params                                                  |
//+------------------------------------------------------------------+
void CCanvas::FontGet(string &name,int &size,uint &flags,uint &angle)
  {
   name =m_fontname;
   size =m_fontsize;
   flags=m_fontflags;
   angle=m_fontangle;
  }
//+------------------------------------------------------------------+
//| Out text                                                         |
//+------------------------------------------------------------------+
void CCanvas::TextOut(int x,int y,string text,const uint clr,uint alignment)
  {
   if(FontSet())
      TextOut(text,x,y,alignment,m_pixels,m_width,m_height,clr,m_format);
  }
//+------------------------------------------------------------------+
//| Out text                                                         |
//+------------------------------------------------------------------+
void CCanvas::TextOutFast(int x,int y,string text,const uint clr,uint alignment)
  {
   TextOut(text,x,y,alignment,m_pixels,m_width,m_height,clr,m_format);
  }
//+------------------------------------------------------------------+
//| Text width                                                       |
//+------------------------------------------------------------------+
int CCanvas::TextWidth(const string text)
  {
   if(!FontSet())
      return(0);
//---
   int w,h;
   TextGetSize(text,w,h);
//--- result
   return(w);
  }
//+------------------------------------------------------------------+
//| Text height                                                      |
//+------------------------------------------------------------------+
int CCanvas::TextHeight(const string text)
  {
   if(!FontSet())
      return(0);
//---
   int w,h;
   TextGetSize(text,w,h);
//--- result
   return(h);
  }
//+------------------------------------------------------------------+
//| Text rectangle                                                   |
//+------------------------------------------------------------------+
void CCanvas::TextSize(const string text,int &width,int &height)
  {
   if(FontSet())
      TextGetSize(text,width,height);
  }
//+------------------------------------------------------------------+
//| Load data from file                                              |
//+------------------------------------------------------------------+
bool CCanvas::LoadFromFile(const string filename)
  {
//--- load image
   if(!CCanvas::LoadBitmap(filename,m_pixels,m_width,m_height))
      return(false);
//--- color components are not processed by terminal (they should be correctly specified by user)
   if(m_format==COLOR_FORMAT_ARGB_RAW)
     {
      uchar a,r,g,b;
      int img_size=m_width*m_height;
      //--- convert image to premultiplied ARGB
      for(int i=0; i<img_size; i++)

        {
         switch(a=GETRGBA(m_pixels[i]))
           {
            case 0xFF:
               break;
            case 0x00:
               m_pixels[i]=0;
               break;
            default:
               r=GETRGBR(m_pixels[i])*a/255;
               g=GETRGBG(m_pixels[i])*a/255;
               b=GETRGBB(m_pixels[i])*a/255;
               m_pixels[i]=ARGB(a,r,g,b);
               break;
           }
        }
     }
//--- success
   return(true);
  }
//+------------------------------------------------------------------+
//| Load data from file                                              |
//+------------------------------------------------------------------+
bool CCanvas::LoadBitmap(const string filename,uint &data[],int &width,int &height)
  {
   struct BitmapHeader
     {
      ushort            type;
      uint              size;
      uint              reserv;
      uint              offbits;
      uint              imgSSize;
      uint              imgWidth;
      uint              imgHeight;
      ushort            imgPlanes;
      ushort            imgBitCount;
      uint              imgCompression;
      uint              imgSizeImage;
      uint              imgXPelsPerMeter;
      uint              imgYPelsPerMeter;
      uint              imgClrUsed;
      uint              imgClrImportant;
     };
   BitmapHeader header;
   bool     result=true;
   CFileBin file;
   uint     img_size;
   bool     no_alpha,no_flip=false;
   uchar    r,g,b;
//--- open file
   if(file.Open(filename,FILE_READ)==INVALID_HANDLE)
     {
      Print("File not found");
      return(false);
     }
//--- read header
   if(file.ReadStruct(header)!=sizeof(header))
     {
      Print("Failed to read file header");
      file.Close();
      return(false);
     }
   width =(int)header.imgWidth;
   height=(int)header.imgHeight;
   if(height<0)
     {
      height=-height;
      no_flip=true;
     }
//--- process depending on color depth
   if(header.imgBitCount==32)
     {
      no_alpha=true;
      img_size=file.ReadArray(data);
      //--- flip image
      if(!no_flip)
         for(int i=0; i<height/2; i++)
           {
            uint tmp[];
            ArrayCopy(tmp,data,0,width*i,width);
            ArrayCopy(data,data,width*i,width*(height-i-1),width);
            ArrayCopy(data,tmp,width*(height-i-1),0,width);
           }
      //--- check if at least one pixel has alpha channel
      //--- then leave image as is (consider it as premultiplied ARGB)
      for(uint i=0; i<img_size; i++)
        {
         //--- there is alpha channel
         if(GETRGBA(data[i])!=0)
           {
            no_alpha=false;
            break;
           }
        }
      //--- no alpha channel
      if(no_alpha)
        {
         //--- consider image as nontransparent, add alpha channel as 0xFF
         for(uint i=0; i<img_size; i++)
            data[i]|=0xFF000000;
        }
     }
   else
     {
      //--- 24 bits - change image color depth to 32 bits
      int byte_width;
      //--- allocate memory for pixels
      if(ArrayResize(data,width*height)!=-1)
        {
         //--- the number of bytes that define a line of pixels must be multiple of 4
         byte_width=width*3;             // number of bytes in line of pixels
         byte_width=(byte_width+3)&~3;     // align line to the 4 byte boundary
         uchar tmp[];
         for(int i=0; i<height; i++)
           {
            if(file.ReadArray(tmp,0,byte_width)!=byte_width)
              {
               result=false;
               break;
              }
            for(int j=0,k=0,p=width*(height-i-1); j<width; j++,p++,k+=3)
              {
               r=tmp[k+2];
               g=tmp[k+1];
               b=tmp[k];
               data[p]=XRGB(r,g,b);
              }
           }
        }
      else
         result=false;
     }
//--- succeed
   file.Close();
   return(result);
  }
//+------------------------------------------------------------------+
//| Determines direction of points iteration (<0 - clockwise)        |
//+------------------------------------------------------------------+
int CCanvas::PointClassify(const CPoint &p0,const CPoint &p1,const CPoint &p2)
  {
   return((p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y));
  }
//+------------------------------------------------------------------+
//| Determines direction of polygon iteration                        |
//+------------------------------------------------------------------+
int CCanvas::PolygonClassify(const CPoint &p[])
  {
   int total=ArraySize(p);
   int im= 0;
   int xm=p[0].x;
   int ym=p[0].y;
//--- find the most top-left vertex
   for(int i=1; i<total; i++)
     {
      if(p[i].y>ym)
         continue;
      if((p[i].y==ym) && (p[i].x>xm))
         continue;
      im=i;
      xm=p[i].x;
      ym=p[i].y;
     }
//--- check the orientation of triangle
   return PointClassify(p[(im-1+total)%total],p[im],p[(im+1)%total]);
  }
//+------------------------------------------------------------------+
//| Checks convexity of polygon                                      |
//+------------------------------------------------------------------+
bool CCanvas::IsPolygonConvex(CPoint &p[])
  {
   int total=ArraySize(p);
//--- triangle - always convex
   if(total==3)
      return(true);
   int res=SIGN(PointClassify(p[0],p[1],p[2]));
   for(int i=1; i<total; i++)
     {
      int res1=SIGN(PointClassify(p[i],p[(i+1)%total],p[(i+2)%total]));
      if(res!=res1)
         return(false);
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Normalizes polygon for drawing                                   |
//+------------------------------------------------------------------+
void CCanvas::PolygonNormalize(CPoint &p[])
  {
   int total=ArraySize(p);
//--- find top-left point
   int imin=0;
   int xmin=p[0].x;
   int ymin=p[0].y;
   for(int i=1; i<total; i++)
     {
      if(p[i].y>ymin)
         continue;
      if(p[i].y==ymin)
        {
         if(p[i].x<xmin)
           {
            xmin=p[i].x;
            imin=i;
           }
         continue;
        }
      xmin=p[i].x;
      ymin=p[i].y;
      imin=i;
     }
   if(imin==0)
      return;
   for(int i=0; i<imin; i++)
     {
      CPoint tmp=p[0];
      ArrayCopy(p,p,0,1);
      p[total-1]=tmp;
     }
  }
//+------------------------------------------------------------------+
//| Dissects non-convex polygon into two                             |
//+------------------------------------------------------------------+
void CCanvas::PolygonIntersect(CPoint &p[],CPoint &add[])
  {
   int total=ArraySize(p);
   int res=SIGN(PolygonClassify(p));
//--- scan vertices clockwise and counterclockwise to find a non-convex one
   for(int i=0; i<total; i++)
     {
      int rr=SIGN(PointClassify(p[i],p[(i+1)%total],p[(i+2)%total]));
      int rl=SIGN(PointClassify(p[(total-i-2)%total],p[(total-i-1)%total],p[(total-i)%total]));
      if(rl!=res || rr!=res)
        {
         //--- find non-convex vertex, dissect
         res=(total-i-1)-(i+1);
         if(res<0)
            return;
         if(res<2)
           {
            ArrayResize(add,total-i);
            add[0]=p[0];
            ArrayCopy(add,p,1,i+1,total-i-1);
            ArrayResize(p,i+2);
           }
         else
           {
            ArrayResize(add,res+1);
            ArrayCopy(add,p,0,i+1,res+1);
            ArrayCopy(p,p,i+2,i+res+1,total-res-i-1);
            ArrayResize(p,total-res+1);
           }
         break;
        }
     }
  }
//+------------------------------------------------------------------+
//| Draws filled convex polygon (point 0 is top-left)                |
//+------------------------------------------------------------------+
void CCanvas::PolygonFill(CPoint &p[],const uint clr)
  {
   int    il,ir;
   double xl,xr;
   int    yy;
   double dl=0.0,dr=0.0;
   int    total=ArraySize(p);
//--- check
   if(total<3)
      return;
//--- check polygon for convexity
   while(!IsPolygonConvex(p))
     {
      CPoint add[];
      PolygonIntersect(p,add);
      PolygonNormalize(add);
      PolygonFill(add,clr);
      ArrayFree(add);
     }
   total=ArraySize(p);
//--- initial settings
   il=ir=0;
   xl=xr=p[0].x;
   yy=p[0].y;
//--- in case of top horizontal line
   if(yy==p[1].y)
      xr=p[ir=1].x;
//--- draw the top
   LineHorizontal((int)MathCeil(xl),(int)MathFloor(xr),yy,clr);
//--- loop by Y
   do
     {
      while(yy==p[il].y)
        {
         il=(il-1+total)%total;
         if(yy>p[il].y)
            return;
         if(yy!=p[il].y)
           {
            dl=(p[il].x-xl)/(p[il].y-yy);
            //--- make adjustment for half of left increment dl/2
            LineHorizontal((int)MathCeil(xl+dl/2),(int)MathFloor(xl),yy,clr);
            xl+=dl/2;
           }
         else
            LineHorizontal((int)MathCeil(xl),(int)MathFloor(p[il].x),yy,clr);
        }
      while(yy==p[ir].y)
        {
         ir=(ir+1)%total;
         if(yy>p[ir].y)
            return;
         if(yy!=p[ir].y)
           {
            dr=(p[ir].x-xr)/(p[ir].y-yy);
            //--- make adjustment for half of right increment dr/2
            LineHorizontal((int)MathCeil(xr),(int)MathFloor(xr+dr/2),yy,clr);
            xr+=dr/2;
           }
         else
            LineHorizontal((int)MathCeil(p[ir].x),(int)MathFloor(xr),yy,clr);
        }
      yy++;
      if(yy==p[il].y)
         xl=p[il].x;
      else
         xl+=dl;
      if(yy==p[ir].y)
         xr=p[ir].x;
      else
         xr+=dr;
      LineHorizontal((int)MathCeil(xl),(int)MathFloor(xr),yy,clr);
     }
   while(il>=ir && ir!=0);
  }
//+------------------------------------------------------------------+
//| Draw line according to Wu's algorithm                            |
//+------------------------------------------------------------------+
void CCanvas::LineWu(int x1,int y1,int x2,int y2,const uint clr,const uint style=UINT_MAX)
  {
//--- calculating the variation of the coordinates
   int dx = (x2 > x1) ? (x2 - x1) : (x1 - x2);
   int dy = (y2 > y1) ? (y2 - y1) : (y1 - y2);
//--- set the line style
   uint prev_style=m_style;
   if(style!=UINT_MAX)
      LineStyleSet(style);
   uint mask=1<<m_style_idx;
   int tmp;
//--- check if dx==0 then draw vertical line
   if(dx==0)
     {
      //--- sort by Y
      if(y1>y2)
        {
         tmp=y1;
         y1 =y2;
         y2 =tmp;
        }
      //--- line is out of image boundaries
      if(y2<0 || y1>=m_height || x1<0 || x1>=m_width)
        {
         //--- set the previous line style
         if(style!=UINT_MAX)
            m_style=prev_style;
         return;
        }
      //--- stay withing image boundaries
      if(y1<0)
         y1=0;
      if(y2>=m_height-1)
         y2=m_height-1;
      //--- draw line
      int index=y1*m_width+x1;
      for(int i=y1; i<=y2; i++,index+=m_width)
        {
         if((m_style&mask)==mask)
            m_pixels[index]=clr;

         mask<<=1;
         if(mask==0x1000000)
            mask=1;
        }
      //--- set the previous line style
      if(style!=UINT_MAX)
         m_style=prev_style;
      //--- success
      return;
     }
//--- check if dy==0 then draw horizontal line
   if(dy==0)
     {
      //--- sort by X
      if(x1>x2)
        {
         tmp=x1;
         x1 =x2;
         x2 =tmp;
        }
      //--- line is out of image boundaries
      if(x2<0 || x1>=m_width || y1<0 || y1>=m_height)
        {
         //--- set the previous line style
         if(style!=UINT_MAX)
            m_style=prev_style;
         return;
        }
      //--- stay withing image boundaries
      if(x1<0)
         x1=0;
      if(x2>=m_width)
         x2=m_width-1;
      //--- draw line
      for(int i=0,index=y1*m_width+x1; i<x2-x1; i++,index++)
        {
         if((m_style&mask)==mask)
            m_pixels[index]=clr;

         mask<<=1;
         if(mask==0x1000000)
            mask=1;
        }
      //--- set the previous line style
      if(style!=UINT_MAX)
         m_style=prev_style;
      //--- success
      return;
     }
//--- check if dx==0 and dy==0 then draw point
   if(dx==0 && dy==0)
     {
      PixelSet(x1,y1,clr);
      //--- set the previous line style
      if(style!=UINT_MAX)
         m_style=prev_style;
      //--- success
      return;
     }
//--- for the X-line (slope < 1)
   if(dy<dx)
     {
      //--- first point has to have a smaller X coordinate
      if(x2<x1)
        {
         x2 += x1;
         x1 = x2 - x1;
         x2 -= x1;
         y2 += y1;
         y1 = y2 - y1;
         y2 -= y1;
        }
      if(y2<y1)
        {
         dy*=-1;
        }
      //--- relative change of the Y
      float grad=(float)dy/dx;
      //--- intermediate variable for Y
      float intery=y1+grad;
      //--- first point
      if((m_style&mask)==mask)
         PixelSet(x1,y1,clr);

      mask<<=1;
      if(mask==0x1000000)
         mask=1;
      for(int x=x1+1; x<x2; x++)
        {
         double alpha1=1-(intery-(int)intery);
         double alpha2=(intery-(int)intery);
         if((m_style&mask)==mask)
           {
            //--- high point
            PixelTransform(x,(int)(intery),clr,alpha1);
            //--- low point
            PixelTransform(x,(int)(intery)+1,clr,alpha2);
           }
         //--- change the Y coordinate
         intery+=grad;
         mask<<=1;
         if(mask==0x1000000)
            mask=1;
        }
      //--- last point
      if((m_style&mask)==mask)
         PixelSet(x2,y2,clr);
     }
//--- for the Y-line (slope > 1)
   else
     {
      //--- first point has to have a smaller Y coordinate
      if(y2<y1)
        {
         x2 += x1;
         x1 = x2 - x1;
         x2 -= x1;
         y2 += y1;
         y1 = y2 - y1;
         y2 -= y1;
        }
      if(x2<x1)
        {
         dx*=-1;
        }
      //--- relative change of the X
      float grad=(float)dx/dy;
      //--- intermediate variable for X
      float interx=x1+grad;
      //--- first point
      if((m_style&mask)==mask)
         PixelSet(x1,y1,clr);

      mask<<=1;
      if(mask==0x1000000)
         mask=1;
      for(int y=y1+1; y<y2; y++)
        {
         double alpha1=1-(interx-(int)interx);
         double alpha2=(interx-(int)interx);
         if((m_style&mask)==mask)
           {
            //--- high point
            PixelTransform((int)(interx),y,clr,alpha1);
            //--- low point
            PixelTransform((int)(interx)+1,y,clr,alpha2);
           }
         //--- change the X coordinate
         interx+=grad;
         mask<<=1;
         if(mask==0x1000000)
            mask=1;
        }
      //--- last point
      if((m_style&mask)==mask)
         PixelSet(x2,y2,clr);
     }
//--- set the previous line style
   if(style!=UINT_MAX)
      m_style=prev_style;
  }
//+------------------------------------------------------------------+
//| Draw Wu's polyline                                               |
//+------------------------------------------------------------------+
void CCanvas::PolylineWu(const int &x[],const int &y[],const uint clr,const uint style=UINT_MAX)
  {
//--- check arrays
   int total=ArraySize(x);
   if(total>ArraySize(y))
      total=ArraySize(y);
//--- check
   if(total<2)
      return;
   total--;
//--- set the line style
   uint prev_style=m_style;
   if(style!=UINT_MAX)
      LineStyleSet(style);
   uint mask=1<<m_style_idx;
//--- draw
   for(int i=0; i<total; i++)
     {
      int x1=x[i];
      int x2=x[i+1];
      int y1=y[i];
      int y2=y[i+1];
      //--- calculating the variation of the coordinates
      int dx = (x2 > x1) ? (x2 - x1) : (x1 - x2);
      int dy = (y2 > y1) ? (y2 - y1) : (y1 - y2);
      int tmp;
      //--- check if dx==0 then draw vertical line
      if(dx==0)
        {
         //--- sort by Y
         if(y1>y2)
           {
            tmp=y1;
            y1 =y2;
            y2 =tmp;
           }
         //--- line is out of image boundaries
         if(y2<0 || y1>=m_height || x1<0 || x1>=m_width)
            continue;
         //--- stay withing image boundaries
         if(y1<0)
            y1=0;
         if(y2>=m_height-1)
            y2=m_height-1;
         //--- draw line
         int index=y1*m_width+x1;
         for(int j=y1; j<=y2; j++,index+=m_width)
           {
            if((m_style&mask)==mask)
               m_pixels[index]=clr;

            mask<<=1;
            if(mask==0x1000000)
               mask=1;
           }
         continue;
        }
      //--- check if dy==0 then draw horizontal line
      if(dy==0)
        {
         //--- sort by X
         if(x1>x2)
           {
            tmp=x1;
            x1 =x2;
            x2 =tmp;
           }
         //--- line is out of image boundaries
         if(x2<0 || x1>=m_width || y1<0 || y1>=m_height)
            continue;
         //--- stay withing image boundaries
         if(x1<0)
            x1=0;
         if(x2>=m_width)
            x2=m_width-1;
         //--- draw line
         for(int j=0,index=y1*m_width+x1; j<x2-x1; j++,index++)
           {
            if((m_style&mask)==mask)
               m_pixels[index]=clr;

            mask<<=1;
            if(mask==0x1000000)
               mask=1;
           }
         continue;
        }
      //--- check if dx==0 and dy==0 then draw point
      if(dx==0 && dy==0)
        {
         PixelSet(x1,y1,clr);

         mask<<=1;
         if(mask==0x1000000)
            mask=1;
         continue;
        }
      //--- for the X-line (slope < 1)
      if(dy<dx)
        {
         //--- first point has to have a smaller X coordinate
         if(x2<x1)
           {
            x2 += x1;
            x1 = x2 - x1;
            x2 -= x1;
            y2 += y1;
            y1 = y2 - y1;
            y2 -= y1;
           }
         if(y2<y1)
           {
            dy*=-1;
           }
         //--- relative change of the Y
         float grad=(float)dy/dx;
         //--- intermediate variable for Y
         float intery=y1+grad;
         //--- first point
         if((m_style&mask)==mask)
            PixelSet(x1,y1,clr);

         mask<<=1;
         if(mask==0x1000000)
            mask=1;
         for(int xc=x1+1; xc<x2; xc++)
           {
            double alpha1=1-(intery-(int)intery);
            double alpha2=(intery-(int)intery);
            if((m_style&mask)==mask)
              {
               //--- high point
               PixelTransform(xc,(int)(intery),clr,alpha1);
               //--- low point
               PixelTransform(xc,(int)(intery)+1,clr,alpha2);
              }
            //--- change the Y coordinate
            intery+=grad;
            mask<<=1;
            if(mask==0x1000000)
               mask=1;
           }
         //--- last point
         if((m_style&mask)==mask)
            PixelSet(x2,y2,clr);
        }
      //--- for the Y-line (slope > 1)
      else
        {
         //--- first point has to have a smaller Y coordinate
         if(y2<y1)
           {
            x2 += x1;
            x1 = x2 - x1;
            x2 -= x1;
            y2 += y1;
            y1 = y2 - y1;
            y2 -= y1;
           }
         if(x2<x1)
           {
            dx*=-1;
           }
         //--- relative change of the X
         float grad=(float)dx/dy;
         //--- intermediate variable for X
         float interx=x1+grad;
         //--- first point
         if((m_style&mask)==mask)
            PixelSet(x1,y1,clr);

         mask<<=1;
         if(mask==0x1000000)
            mask=1;
         for(int yc=y1+1; yc<y2; yc++)
           {
            double alpha1=1-(interx-(int)interx);
            double alpha2=(interx-(int)interx);
            if((m_style&mask)==mask)
              {
               //--- high point
               PixelTransform((int)(interx),yc,clr,alpha1);
               //--- low point
               PixelTransform((int)(interx)+1,yc,clr,alpha2);
              }
            //--- change the X coordinate
            interx+=grad;
            mask<<=1;
            if(mask==0x1000000)
               mask=1;
           }
         //--- last point
         if((m_style&mask)==mask)
            PixelSet(x2,y2,clr);
        }
      mask<<=1;
      if(mask==0x1000000)
         mask=1;
     }
//--- set the previous line style
   if(style!=UINT_MAX)
      m_style=prev_style;
  }
//+------------------------------------------------------------------+
//| Draw Wu's polygon                                                |
//+------------------------------------------------------------------+
void CCanvas::PolygonWu(const int &x[],const int &y[],const uint clr,const uint style=UINT_MAX)
  {
//--- check arrays
   int total=ArraySize(x);
   if(total>ArraySize(y))
      total=ArraySize(y);
//--- check
   if(total<2)
      return;
//--- set the line style
   uint prev_style=m_style;
   if(style!=UINT_MAX)
      LineStyleSet(style);
   uint mask=1<<m_style_idx;
//--- draw
   for(int i=0; i<total; i++)
     {
      int x1=x[i];
      int y1=y[i];
      int x2=(i+1!=total) ? x[i+1] : x[0];
      int y2=(i+1!=total) ? y[i+1] : y[0];
      //--- calculating the variation of the coordinates
      int dx = (x2 > x1) ? (x2 - x1) : (x1 - x2);
      int dy = (y2 > y1) ? (y2 - y1) : (y1 - y2);
      int tmp;
      //--- check if dx==0 then draw vertical line
      if(dx==0)
        {
         //--- sort by Y
         if(y1>y2)
           {
            tmp=y1;
            y1 =y2;
            y2 =tmp;
           }
         //--- line is out of image boundaries
         if(y2<0 || y1>=m_height || x1<0 || x1>=m_width)
            continue;
         //--- stay withing image boundaries
         if(y1<0)
            y1=0;
         if(y2>=m_height-1)
            y2=m_height-1;
         //--- draw line
         int index=y1*m_width+x1;
         for(int j=y1; j<=y2; j++,index+=m_width)
           {
            if((m_style&mask)==mask)
               m_pixels[index]=clr;

            mask<<=1;
            if(mask==0x1000000)
               mask=1;
           }
         continue;
        }
      //--- check if dy==0 then draw horizontal line
      if(dy==0)
        {
         //--- sort by X
         if(x1>x2)
           {
            tmp=x1;
            x1 =x2;
            x2 =tmp;
           }
         //--- line is out of image boundaries
         if(x2<0 || x1>=m_width || y1<0 || y1>=m_height)
            continue;
         //--- stay withing image boundaries
         if(x1<0)
            x1=0;
         if(x2>=m_width)
            x2=m_width-1;
         //--- draw line
         for(int j=0,index=y1*m_width+x1; j<x2-x1; j++,index++)
           {
            if((m_style&mask)==mask)
               m_pixels[index]=clr;

            mask<<=1;
            if(mask==0x1000000)
               mask=1;
           }
         continue;
        }
      //--- check if dx==0 and dy==0 then draw point
      if(dx==0 && dy==0)
        {
         PixelSet(x1,y1,clr);

         mask<<=1;
         if(mask==0x1000000)
            mask=1;
         continue;
        }
      //--- for the X-line (slope < 1)
      if(dy<dx)
        {
         //--- first point has to have a smaller X coordinate
         if(x2<x1)
           {
            x2 += x1;
            x1 = x2 - x1;
            x2 -= x1;
            y2 += y1;
            y1 = y2 - y1;
            y2 -= y1;
           }
         if(y2<y1)
           {
            dy*=-1;
           }
         //--- relative change of the Y
         float grad=(float)dy/dx;
         //--- intermediate variable for Y
         float intery=y1+grad;
         //--- first point
         if((m_style&mask)==mask)
            PixelSet(x1,y1,clr);

         mask<<=1;
         if(mask==0x1000000)
            mask=1;
         for(int xc=x1+1; xc<x2; xc++)
           {
            double alpha1=1-(intery-(int)intery);
            double alpha2=(intery-(int)intery);
            if((m_style&mask)==mask)
              {
               //--- high point
               PixelTransform(xc,(int)(intery),clr,alpha1);
               //--- low point
               PixelTransform(xc,(int)(intery)+1,clr,alpha2);
              }
            //--- change the Y coordinate
            intery+=grad;
            mask<<=1;
            if(mask==0x1000000)
               mask=1;
           }
         //--- last point
         if((m_style&mask)==mask)
            PixelSet(x2,y2,clr);
        }
      //--- for the Y-line (slope > 1)
      else
        {
         //--- first point has to have a smaller Y coordinate
         if(y2<y1)
           {
            x2 += x1;
            x1 = x2 - x1;
            x2 -= x1;
            y2 += y1;
            y1 = y2 - y1;
            y2 -= y1;
           }
         if(x2<x1)
           {
            dx*=-1;
           }
         //--- relative change of the X
         float grad=(float)dx/dy;
         //--- intermediate variable for X
         float interx=x1+grad;
         //--- first point
         if((m_style&mask)==mask)
            PixelSet(x1,y1,clr);

         mask<<=1;
         if(mask==0x1000000)
            mask=1;
         for(int yc=y1+1; yc<y2; yc++)
           {
            double alpha1=1-(interx-(int)interx);
            double alpha2=(interx-(int)interx);
            if((m_style&mask)==mask)
              {
               //--- high point
               PixelTransform((int)(interx),yc,clr,alpha1);
               //--- low point
               PixelTransform((int)(interx)+1,yc,clr,alpha2);
              }
            //--- change the X coordinate
            interx+=grad;
            mask<<=1;
            if(mask==0x1000000)
               mask=1;
           }
         //--- last point
         if((m_style&mask)==mask)
            PixelSet(x2,y2,clr);
        }
      mask<<=1;
      if(mask==0x1000000)
         mask=1;
     }
//--- set the previous line style
   if(style!=UINT_MAX)
      m_style=prev_style;
  }
//+------------------------------------------------------------------+
//| Draw triangle with Wu's lines                                    |
//+------------------------------------------------------------------+
void CCanvas::TriangleWu(const int x1,const int y1,const int x2,const int y2,const int x3,const int y3,const uint clr,const uint style=UINT_MAX)
  {
//--- draw
   int x[3];
   int y[3];
   x[0] = x1;
   x[1] = x2;
   x[2] = x3;
   y[0] = y1;
   y[1] = y2;
   y[2] = y3;
   PolygonWu(x,y,clr,style);
  }
//+------------------------------------------------------------------+
//| Draw circle according to Wu's algorithm                          |
//+------------------------------------------------------------------+
void CCanvas::CircleWu(const int x,const int y,const double r,const uint clr,const uint style=UINT_MAX)
  {
   if(r<=0)
      return;
//--- preliminary calculations
   double r2=r*r;
   double quarter=round(r/sqrt(2.0));
//--- set the line style
   uint prev_style=m_style;
   if(style!=UINT_MAX)
      LineStyleSet(style);
   uint mask=1<<m_style_idx;
//--- draw
   for(int d1=0; d1<=quarter; d1++)
     {
      double d2=sqrt(r2-d1*d1);
      double alpha1=d2-floor(d2);
      double alpha2=1-alpha1;
      if((m_style&mask)==mask)
        {
         PixelTransform4(x,y,(int)d2+1,d1,clr,alpha1);
         PixelTransform4(x,y,d1,(int)(d2)+1,clr,alpha1);
        }
      mask<<=1;
      if(mask==0x1000000)
         mask=1;
      if((m_style&mask)==mask)
        {
         PixelTransform4(x,y,d1,(int)(d2),clr,alpha2);
         PixelTransform4(x,y,(int)d2,d1,clr,alpha2);
        }
      mask<<=1;
      if(mask==0x1000000)
         mask=1;
     }
//--- set the previous line style
   if(style!=UINT_MAX)
      m_style=prev_style;
  }
//+------------------------------------------------------------------+
//| Draw ellipse according to Wu's algorithm                         |
//+------------------------------------------------------------------+
void CCanvas::EllipseWu(const int x1,const int y1,const int x2,const int y2,const uint clr,const uint style=UINT_MAX)
  {
   int rx=(int)(x2-x1)/2;
   int ry=(int)(y2-y1)/2;
   int x=(x2>x1) ? x1+rx : x2+rx;
   int y=(y2>y1) ? y1+ry : y2+ry;
   if(rx<=0 || ry<=0)
      return;
//--- set the line style
   uint prev_style=m_style;
   if(style!=UINT_MAX)
      LineStyleSet(style);
   uint mask=1<<m_style_idx;
//--- preliminary calculations
   double rx2=rx*rx;
   double ry2=ry*ry;
   double quarter=round(rx2/sqrt(rx2+ry2));
//--- draw
   for(int dx=0; dx<=quarter; dx++)
     {
      double dy=ry*sqrt(1-dx*dx/rx2);
      double alpha1=dy-floor(dy);
      double alpha2=1-alpha1;
      if((m_style&mask)==mask)
        {
         PixelTransform4(x,y,dx,(int)(dy)+1,clr,alpha1);
         PixelTransform4(x,y,dx,(int)(dy),clr,alpha2);
        }
      mask<<=1;
      if(mask==0x1000000)
         mask=1;
     }
   quarter=round(ry2/sqrt(rx2+ry2));
   for(int dy=0; dy<=quarter; dy++)
     {
      double dx=rx*sqrt(1-dy*dy/ry2);
      double alpha1=dx-floor(dx);
      double alpha2=1-alpha1;
      if((m_style&mask)==mask)
        {
         PixelTransform4(x,y,(int)(dx)+1,dy,clr,alpha1);
         PixelTransform4(x,y,(int)(dx),dy,clr,alpha2);
        }
      mask<<=1;
      if(mask==0x1000000)
         mask=1;
     }
//--- set the previous line style
   if(style!=UINT_MAX)
      m_style=prev_style;
  }
//+------------------------------------------------------------------+
//| Draw vertical thick line with prefiltered antialiasing           |
//+------------------------------------------------------------------+
void CCanvas::LineThickVertical(const int x,const int y1,const int y2,const uint clr,const int size,const uint style,ENUM_LINE_END end_style)
  {
   if(size<=2)
     {
      if(size>0)
         LineWu(x,y1,x,y2,clr,style);
      return;
     }
//--- r be the filter radius (and also the half-width of the wide line)
   double r=(size/2.0);
//--- primary calculate
   int dy=MathAbs(y2-y1);
   int sign=(y1<y2) ? 1 : -1;
//--- set the line style
   uint prev_style=m_style;
   if(style!=UINT_MAX)
      LineStyleSet(style);
   uint   mask=1<<m_style_idx;
//--- draw vertical thick line by segment
   if(style==STYLE_SOLID)
     {
      SegmentVertical(x,y1,y2,sign,r,clr,end_style);
     }
   else
     {
      int ys=0;
      int ye=0;
      int steps=(int)MathRound(dy/r);
      bool segment=false;
      for(int i=0; i<=steps; i++)
        {
         if((m_style&mask)==mask && !segment)
           {
            ys=y1+(sign)*(int)(i*r);
            segment=true;
           }
         else
            if((m_style&mask)!=mask && segment)
              {
               ye=y1+(sign)*(int)(i*r);
               SegmentVertical(x,ys,ye,sign,r,clr,end_style);
               segment=false;
              }
         mask<<=1;
         if(mask==0x1000000)
            mask=1;
        }
      //--- last segment
      if(segment)
         SegmentVertical(x,ys,y2,sign,r,clr,end_style);
     }
//--- set the previous line style
   if(style!=UINT_MAX)
      m_style=prev_style;
  }
//+------------------------------------------------------------------+
//| Draw horizontal thick line with prefiltered antialiasing         |
//+------------------------------------------------------------------+
void CCanvas::LineThickHorizontal(const int x1,const int x2,const int y,const uint clr,const int size,const uint style,ENUM_LINE_END end_style)
  {
   if(size<=2)
     {
      if(size>0)
         LineWu(x1,y,x2,y,clr,style);
      return;
     }
//--- r be the filter radius (and also the half-width of the wide line)
   double r=(size/2.0);
//--- primary calculate
   int dx=MathAbs(x2-x1);
   int sign=(x1<x2) ? 1 : -1;
//--- set the line style
   uint prev_style=m_style;
   if(style!=UINT_MAX)
      LineStyleSet(style);
   uint   mask=1<<m_style_idx;
//--- draw horizontal thick line by segment
   if(style==STYLE_SOLID)
     {
      SegmentHorizontal(x1,x2,y,sign,r,clr,end_style);
     }
   else
     {
      int xs=0;
      int xe=0;
      int steps=(int)MathRound(dx/r);
      bool segment=false;
      for(int i=0; i<=steps; i++)
        {
         if((m_style&mask)==mask && !segment)
           {
            xs=x1+(sign)*(int)(i*r);
            segment=true;
           }
         else
            if((m_style&mask)!=mask && segment)
              {
               xe=x1+(sign)*(int)(i*r);
               SegmentHorizontal(xs,xe,y,sign,r,clr,end_style);
               segment=false;
              }
         mask<<=1;
         if(mask==0x1000000)
            mask=1;
        }
      //--- last segment
      if(segment)
         SegmentHorizontal(xs,x2,y,sign,r,clr,end_style);
     }
//--- set the previous line style
   if(style!=UINT_MAX)
      m_style=prev_style;
  }
//+------------------------------------------------------------------+
//| Draw thick line with prefiltered antialiasing                    |
//+------------------------------------------------------------------+
void CCanvas::LineThick(const int x1,const int y1,const int x2,const int y2,const uint clr,const int size,const uint style,ENUM_LINE_END end_style)
  {
   if(size<=2)
     {
      if(size>0)
         LineWu(x1,y1,x2,y2,clr,style);
      return;
     }
//--- r be the filter radius (and also the half-width of the wide line)
   double r=(size/2.0);
//--- compute x and y deltas
   double dx=MathAbs(x2-x1);
   double dy=MathAbs(y2-y1);
   if(dx==0)
     {
      LineThickVertical(x1,y1,y2,size,clr,style,end_style);
      return;
     }
   if(dy==0)
     {
      LineThickHorizontal(x1,x2,y1,size,clr,style,end_style);
      return;
     }
//--- compute the linear coefficients of the two (scaled) edge functions
   double k=MathArctan(dx/dy);
   double rcos_k=r*cos(k);
   double rsin_k=r*sin(k);
//--- set the line style
   uint prev_style=m_style;
   if(style!=UINT_MAX)
      LineStyleSet(style);
   uint   mask=1<<m_style_idx;
//--- primary calculate
   int xsign = (x1<x2) ? 1 : -1;
   int ysign = (y1<y2) ? 1 : -1;
   double kp0=(-xsign*ysign)*(dx/dy);
   double kp1=-1/kp0;
//--- draw thick line by segment
   if(style==STYLE_SOLID)
     {
      Segment(x1,y1,x2,y2,kp0,kp1,xsign,ysign,rcos_k,rsin_k,r,clr,end_style);
     }
   else
     {
      int xs=0;
      int ys=0;
      int xe=0;
      int ye=0;
      double length=MathSqrt(dx*dx+dy*dy);
      int steps=(int)MathRound(length/r);
      bool segment=false;
      for(int i=0; i<=steps; i++)
        {
         if((m_style&mask)==mask && !segment)
           {
            xs=x1+(xsign)*(int)(i*rsin_k);
            ys=y1+(ysign)*(int)(i*rcos_k);
            segment=true;
           }
         else
            if((m_style&mask)!=mask && segment)
              {
               xe=x1+(xsign)*(int)(i*rsin_k);
               ye=y1+(ysign)*(int)(i*rcos_k);
               Segment(xs,ys,xe,ye,kp0,kp1,xsign,ysign,rcos_k,rsin_k,r,clr,end_style);
               segment=false;
              }
         mask<<=1;
         if(mask==0x1000000)
            mask=1;
        }
      //--- last segment
      if(segment)
         Segment(xs,ys,x2,y2,kp0,kp1,xsign,ysign,rcos_k,rsin_k,r,clr,end_style);
     }
//--- set the previous line style
   if(style!=UINT_MAX)
      m_style=prev_style;
  }
//+------------------------------------------------------------------+
//| Draw thick polyline                                              |
//+------------------------------------------------------------------+
void CCanvas::PolylineThick(const int &x[],const int &y[],const uint clr,const int size,const uint style,ENUM_LINE_END end_style)
  {
   if(size<=2)
     {
      if(size>0)
         PolylineWu(x,y,clr,style);
      return;
     }
//--- check arrays
   int total=ArraySize(x);
   if(total>ArraySize(y))
      total=ArraySize(y);
//--- check
   if(total<2)
      return;
   total--;
//--- r be the filter radius (and also the half-width of the wide line)
   double r=(size/2.0);
//---
   double gap=1.0;
//--- set the line style
   uint prev_style=m_style;
   if(style!=UINT_MAX)
      LineStyleSet(style);
   uint   mask=1<<m_style_idx;
//--- draw
   for(int index=0; index<total; index++)
     {
      int x1=x[index];
      int y1=y[index];
      int x2=x[index+1];
      int y2=y[index+1];
      //--- compute x and y deltas
      double dx=MathAbs(x2-x1);
      double dy=MathAbs(y2-y1);
      if(dx==0)
        {
         int sign=(y1<y2) ? 1 : -1;
         //--- draw vertical thick line by segment
         if(style==STYLE_SOLID)
           {
            SegmentVertical(x1,y1,y2,sign,r,clr,end_style);
           }
         else
           {
            int ys=y1;
            int ye=0;
            double steps=dy/r;
            int    isteps=(int)steps;
            bool segment=false;
            for(int i=0; i<isteps; i++)
              {
               if((m_style&mask)==mask && !segment)
                 {
                  ys=y1+(sign)*(int)(i*r);
                  segment=true;
                 }
               else
                  if((m_style&mask)!=mask && segment)
                    {
                     ye=y1+(sign)*(int)(i*r);
                     SegmentVertical(x1,ys,ye,sign,r,clr,end_style);
                     segment=false;
                    }
               mask<<=1;
               if(mask==0x1000000)
                  mask=1;
              }
            //--- last segment
            if(segment || (steps<1 && (m_style&mask)==mask))
               SegmentVertical(x1,ys,y2,sign,r,clr,end_style);
            gap-=steps-isteps;
            if(gap<0)
              {
               gap++;
               mask<<=1;
               if(mask==0x1000000)
                  mask=1;
              }
           }
         continue;
        }
      if(dy==0)
        {
         int sign=(x1<x2) ? 1 : -1;
         //--- draw horizontal thick line by segment
         if(style==STYLE_SOLID)
           {
            SegmentHorizontal(x1,x2,y1,sign,r,clr,end_style);
           }
         else
           {
            int xs=x1;
            int xe=0;
            double steps=dx/r;
            int    isteps=(int)steps;
            bool segment=false;
            for(int i=0; i<isteps; i++)
              {
               if((m_style&mask)==mask && !segment)
                 {
                  xs=x1+(sign)*(int)(i*r);
                  segment=true;
                 }
               else
                  if((m_style&mask)!=mask && segment)
                    {
                     xe=x1+(sign)*(int)(i*r);
                     SegmentHorizontal(xs,xe,y1,sign,r,clr,end_style);
                     segment=false;
                    }
               mask<<=1;
               if(mask==0x1000000)
                  mask=1;
              }
            //--- last segment
            if(segment || (steps<1 && (m_style&mask)==mask))
               SegmentHorizontal(xs,x2,y1,sign,r,clr,end_style);
            gap-=steps-isteps;
            if(gap<0)
              {
               gap++;
               mask<<=1;
               if(mask==0x1000000)
                  mask=1;
              }
           }
         continue;
        }
      //--- compute the linear coefficients of the two (scaled) edge functions
      double k=MathArctan(dx/dy);
      double rcos_k=r*cos(k);
      double rsin_k=r*sin(k);
      //--- primary calculate
      int xsign=(x1<x2) ? 1 : -1;
      int ysign=(y1<y2) ? 1 : -1;
      double kp0=(-xsign*ysign)*(dx/dy);
      double kp1=-1/kp0;
      //--- draw thick line by segment
      if(style==STYLE_SOLID)
        {
         Segment(x1,y1,x2,y2,kp0,kp1,xsign,ysign,rcos_k,rsin_k,r,clr,end_style);
        }
      else
        {
         int xs=x1;
         int ys=y1;
         int xe=0;
         int ye=0;
         double length=MathSqrt(dx*dx+dy*dy);
         double steps=length/r;
         int    isteps=(int)steps;
         bool segment=false;
         for(int i=0; i<isteps; i++)
           {
            if((m_style&mask)==mask && !segment)
              {
               xs=x1+(xsign)*(int)(i*rsin_k);
               ys=y1+(ysign)*(int)(i*rcos_k);
               segment=true;
              }
            else
               if((m_style&mask)!=mask && segment)
                 {
                  xe=x1+(xsign)*(int)(i*rsin_k);
                  ye=y1+(ysign)*(int)(i*rcos_k);
                  Segment(xs,ys,xe,ye,kp0,kp1,xsign,ysign,rcos_k,rsin_k,r,clr,end_style);
                  segment=false;
                 }
            mask<<=1;
            if(mask==0x1000000)
               mask=1;
           }
         //--- last segment
         if(segment || (steps<1 && (m_style&mask)==mask))
            Segment(xs,ys,x2,y2,kp0,kp1,xsign,ysign,rcos_k,rsin_k,r,clr,end_style);
         gap-=steps-isteps;
         if(gap<0)
           {
            gap++;
            mask<<=1;
            if(mask==0x1000000)
               mask=1;
           }
        }
     }
//--- set the previous line style
   if(style!=UINT_MAX)
      m_style=prev_style;
  }
//+------------------------------------------------------------------+
//| Draw thick polygon                                               |
//+------------------------------------------------------------------+
void CCanvas::PolygonThick(const int &x[],const int &y[],const uint clr,const int size,const uint style,ENUM_LINE_END end_style)
  {
   if(size<=2)
     {
      if(size>0)
         PolylineWu(x,y,clr,style);
      return;
     }
//--- check arrays
   int total=ArraySize(x);
   if(total>ArraySize(y))
      total=ArraySize(y);
//--- check
   if(total<2)
      return;
//--- r be the filter radius (and also the half-width of the wide line)
   double r=(size/2.0);
//---
   double gap=1.0;
//--- set the line style
   uint prev_style=m_style;
   if(style!=UINT_MAX)
      LineStyleSet(style);
   uint   mask=1<<m_style_idx;
//--- draw
   for(int index=0; index<total; index++)
     {
      int x1=x[index];
      int y1=y[index];
      int x2=(index!=total-1) ? x[index+1] : x[0];
      int y2=(index!=total-1) ? y[index+1] : y[0];
      //--- compute x and y deltas
      double dx=MathAbs(x2-x1);
      double dy=MathAbs(y2-y1);
      if(dx==0)
        {
         int sign=(y1<y2) ? 1 : -1;
         //--- draw vertical thick line by segment
         if(style==STYLE_SOLID)
           {
            SegmentVertical(x1,y1,y2,sign,r,clr,end_style);
           }
         else
           {
            int ys=y1;
            int ye=0;
            double steps=dy/r;
            int    isteps=(int)steps;
            bool segment=false;
            for(int i=0; i<isteps; i++)
              {
               if((m_style&mask)==mask && !segment)
                 {
                  ys=y1+(sign)*(int)(i*r);
                  segment=true;
                 }
               else
                  if((m_style&mask)!=mask && segment)
                    {
                     ye=y1+(sign)*(int)(i*r);
                     SegmentVertical(x1,ys,ye,sign,r,clr,end_style);
                     segment=false;
                    }
               mask<<=1;
               if(mask==0x1000000)
                  mask=1;
              }
            //--- last segment
            if(segment || (steps<1 && (m_style&mask)==mask))
               SegmentVertical(x1,ys,y2,sign,r,clr,end_style);
            gap-=steps-isteps;
            if(gap<0)
              {
               gap++;
               mask<<=1;
               if(mask==0x1000000)
                  mask=1;
              }
           }
         continue;
        }
      if(dy==0)
        {
         int sign=(x1<x2) ? 1 : -1;
         //--- draw horizontal thick line by segment
         if(style==STYLE_SOLID)
           {
            SegmentHorizontal(x1,x2,y1,sign,r,clr,end_style);
           }
         else
           {
            int xs=x1;
            int xe=0;
            double steps=dx/r;
            int    isteps=(int)steps;
            bool segment=false;
            for(int i=0; i<isteps; i++)
              {
               if((m_style&mask)==mask && !segment)
                 {
                  xs=x1+(sign)*(int)(i*r);
                  segment=true;
                 }
               else
                  if((m_style&mask)!=mask && segment)
                    {
                     xe=x1+(sign)*(int)(i*r);
                     SegmentHorizontal(xs,xe,y1,sign,r,clr,end_style);
                     segment=false;
                    }
               mask<<=1;
               if(mask==0x1000000)
                  mask=1;
              }
            //--- last segment
            if(segment || (steps<1 && (m_style&mask)==mask))
               SegmentHorizontal(xs,x2,y1,sign,r,clr,end_style);
            gap-=steps-isteps;
            if(gap<0)
              {
               gap++;
               mask<<=1;
               if(mask==0x1000000)
                  mask=1;
              }
           }
         continue;
        }
      //--- compute the linear coefficients of the two (scaled) edge functions
      double k=MathArctan(dx/dy);
      double rcos_k=r*cos(k);
      double rsin_k=r*sin(k);
      //--- primary calculate
      int xsign=(x1<x2) ? 1 : -1;
      int ysign=(y1<y2) ? 1 : -1;
      double kp0=(-xsign*ysign)*(dx/dy);
      double kp1=-1/kp0;
      //--- draw thick line by segment
      if(style==STYLE_SOLID)
        {
         Segment(x1,y1,x2,y2,kp0,kp1,xsign,ysign,rcos_k,rsin_k,r,clr,end_style);
        }
      else
        {
         int xs=x1;
         int ys=y1;
         int xe=0;
         int ye=0;
         double length=MathSqrt(dx*dx+dy*dy);
         double steps=length/r;
         int    isteps=(int)steps;
         bool segment=false;
         for(int i=0; i<isteps; i++)
           {
            if((m_style&mask)==mask && !segment)
              {
               xs=x1+(xsign)*(int)(i*rsin_k);
               ys=y1+(ysign)*(int)(i*rcos_k);
               segment=true;
              }
            else
               if((m_style&mask)!=mask && segment)
                 {
                  xe=x1+(xsign)*(int)(i*rsin_k);
                  ye=y1+(ysign)*(int)(i*rcos_k);
                  Segment(xs,ys,xe,ye,kp0,kp1,xsign,ysign,rcos_k,rsin_k,r,clr,end_style);
                  segment=false;
                 }
            mask<<=1;
            if(mask==0x1000000)
               mask=1;
           }
         //--- last segment
         if(segment || (steps<1 && (m_style&mask)==mask))
            Segment(xs,ys,x2,y2,kp0,kp1,xsign,ysign,rcos_k,rsin_k,r,clr,end_style);
         gap-=steps-isteps;
         if(gap<0)
           {
            gap++;
            mask<<=1;
            if(mask==0x1000000)
               mask=1;
           }
        }
     }
//--- set the previous line style
   if(style!=UINT_MAX)
      m_style=prev_style;
  }
//+------------------------------------------------------------------+
//| Parametric method of color comparison                            |
//+------------------------------------------------------------------+
bool CCanvas::PixelsSimilar(const uint clr0,const uint clr1,const uint threshould)
  {
   uint dr=MathAbs(uint((clr0>>16) &0xff) -
                   uint((clr1>>16) &0xff));
   uint dg=MathAbs(uint((clr0>>8) &0xff) -
                   uint((clr1>>8) &0xff));
   uint db=MathAbs(uint((clr0>>0) &0xff) -
                   uint((clr1>>0) &0xff));
//--- return
   return (dr<=threshould || dg<=threshould || db<=threshould);
  }
//+------------------------------------------------------------------+
//| Calculate and set new color                                      |
//+------------------------------------------------------------------+
void CCanvas::PixelTransform(const int x,const int y,const uint clr,const double alpha)
  {
   int index=y*m_width+x;
//--- check
   if(x<0 || y<0 || x>m_width || y>m_height || index>=ArraySize(m_pixels))
      return;
//--- check alpha
   if(alpha==1)
     {
      m_pixels[index]=clr;
      return;
     }
//--- get pixel color
   uint clr0=m_pixels[index];
//--- transform  of color component for the background
   double r0 = ((clr0>>16) & 0xFF) * (1.0-alpha);
   double g0 = ((clr0>>8) & 0xFF) * (1.0-alpha);
   double b0 = ((clr0>>0) & 0xFF) * (1.0-alpha);
//--- transform  of color component
   double r1 = ((clr>>16) & 0xFF) * (alpha);
   double g1 = ((clr>>8) & 0xFF) * (alpha);
   double b1 = ((clr>>0) & 0xFF) * (alpha);
//--- components of the new color
   int r = (int)(r0+r1);
   int g = (int)(g0+g1);
   int b = (int)(b0+b1);
//--- set new color
   m_pixels[y*m_width+x]=((r<<16)|(g<<8)|(b<<0)|(255<<24));
  }
//+------------------------------------------------------------------+
//| Draw 4 pixel with PixelTransform method                          |
//+------------------------------------------------------------------+
void CCanvas::PixelTransform4(const int x,const int y,const int dx,const int dy,const uint clr,const double alpha)
  {
   PixelTransform(x+dx,y+dy,clr,alpha);
   PixelTransform(x-dx,y+dy,clr,alpha);
   PixelTransform(x+dx,y-dy,clr,alpha);
   PixelTransform(x-dx,y-dy,clr,alpha);
  }
//+------------------------------------------------------------------+
//| Draw 4 pixel with antialiasing                                   |
//+------------------------------------------------------------------+
void CCanvas::PixelSet4AA(const double x,const double y,const double dx,const double dy,const uint clr)
  {
   PixelSetAA(x+dx,y+dy,clr);
   PixelSetAA(x-dx,y+dy,clr);
   PixelSetAA(x+dx,y-dy,clr);
   PixelSetAA(x-dx,y-dy,clr);
  }
//+------------------------------------------------------------------+
//| Draw solid segment for vertical thick line                       |
//+------------------------------------------------------------------+
void CCanvas::SegmentVertical(const int x,const int y1,const int y2,const int ysign,const double r,const uint clr,ENUM_LINE_END end_style)
  {
//--- compute the constol points of the solid segment
   int ye1,ye2;
   int ys1,ys2;
   switch(end_style)
     {
      case LINE_END_ROUND:
        {
         ye1=y1;
         ye2=y2;
         ys1=y1-(int)(ysign*r);
         ys2=y2+(int)(ysign*r);
         break;
        }
      case LINE_END_BUTT:
        {
         ye1=y1;
         ye2=y2;
         ys1=y1;
         ys2=y2;
         break;
        }
      case LINE_END_SQUARE:
        {
         ye1=y1-(int)(ysign*r);
         ye2=y2+(int)(ysign*r);
         ys1=ye1;
         ys2=ye2;
         break;
        }
      default:
         return;
     };
//--- darw solid segment
   for(int i=0; i<=MathAbs(ys2-ys1); i++)
     {
      double yi=ys1+(ysign*i);
      for(int j=0; j<2*r; j++)
        {
         double xi=x-r+j;
         double dist=DistancePointSegment(xi,yi,x,ye1,x,ye2);
         double val=MathAbs(dist/r);
         PixelTransform((int)xi,(int)yi,clr,FilterFunction(val));
        }
     }
  }
//+------------------------------------------------------------------+
//| Draw solid segment for horizontal thick line                     |
//+------------------------------------------------------------------+
void CCanvas::SegmentHorizontal(const int x1,const int x2,const int y,const int xsign,const double r,const uint clr,ENUM_LINE_END end_style)
  {
//--- compute the constol points of the solid segment
   int xe1,xe2;
   int xs1,xs2;
   switch(end_style)
     {
      case LINE_END_ROUND:
        {
         xe1=x1;
         xe2=x2;
         xs1=x1-(int)(xsign*r);
         xs2=x2+(int)(xsign*r);
         break;
        }
      case LINE_END_BUTT:
        {
         xe1=x1;
         xe2=x2;
         xs1=x1;
         xs2=x2;
         break;
        }
      case LINE_END_SQUARE:
        {
         xe1=x1-(int)(xsign*r);
         xe2=x2+(int)(xsign*r);
         xs1=xe1;
         xs2=xe2;
         break;
        }
      default:
         return;
     };
//--- draw solid segment
   for(int i=0; i<=MathAbs(xs2-xs1); i++)
     {
      double xi=xs1+(xsign*i);
      for(int j=0; j<2*r; j++)
        {
         double yi=y-r+j;
         double dist=DistancePointSegment(xi,yi,xe1,y,xe2,y);
         double val=MathAbs(dist/r);
         PixelTransform((int)xi,(int)yi,clr,FilterFunction(val));
        }
     }
  }
//+------------------------------------------------------------------+
//| Draw solid segment for thick line                                |
//+------------------------------------------------------------------+
void CCanvas::Segment(const int x1,const int y1,const int x2,const int y2,const double kp0,const double kp1,const int xsign,const int ysign,
                      const double rcos_k,const double rsin_k,const double r,const uint clr,ENUM_LINE_END end_style)
  {
   if(x1==x2 && y1==y2)
      return;
   if(x1==x2)
     {
      SegmentVertical(x1,y1,y2,ysign,r,clr,end_style);
      return;
     }
   if(y1==y2)
     {
      SegmentHorizontal(x1,x2,y1,xsign,r,clr,end_style);
      return;
     }
//--- compute the constol points of the solid segment
   int xe1,ye1,xe2,ye2;
   int xs1,ys1,xs2,ys2;
   switch(end_style)
     {
      case LINE_END_ROUND:
        {
         xe1=x1;
         ye1=y1;
         xe2=x2;
         ye2=y2;
         xs1=x1-(xsign)*(int)(rsin_k);
         ys1=y1-(ysign)*(int)(rcos_k);
         xs2=x2+(xsign)*(int)(rsin_k);
         ys2=y2+(ysign)*(int)(rcos_k);
         break;
        }
      case LINE_END_BUTT:
        {
         xe1=x1;
         ye1=y1;
         xe2=x2;
         ye2=y2;
         xs1=x1;
         ys1=y1;
         xs2=x2;
         ys2=y2;
         break;
        }
      case LINE_END_SQUARE:
        {
         xe1=x1-(xsign)*(int)(rsin_k);
         ye1=y1-(ysign)*(int)(rcos_k);
         xe2=x2+(xsign)*(int)(rsin_k);
         ye2=y2+(ysign)*(int)(rcos_k);
         xs1=xe1;
         ys1=ye1;
         xs2=xe2;
         ys2=ye2;
         break;
        }
      default:
         return;
     };
//--- compute the four corners of the wide line
   double p0x=xs1+(xsign)*(rcos_k);
   double p0y=ys1-(ysign)*(rsin_k);
   double p1x=xs1-(xsign)*(rcos_k);
   double p1y=ys1+(ysign)*(rsin_k);
   double p2x=xs2+(xsign)*(rcos_k);
   double p2y=ys2-(ysign)*(rsin_k);
   double p3x=xs2-(xsign)*(rcos_k);
   double p3y=ys2+(ysign)*(rsin_k);
//--- draw solid segment
   if(MathAbs(kp0)>=1)
     {
      double xi0,xi1;
      double height=MathAbs(p3y-p0y);
      for(int i=0; i<=height; i++)
        {
         double y=p0y+(ysign*i);
         double xi00 = MathRound(p0x + (y-p0y)/kp0);
         double xi01 = MathRound(p1x + (y-p1y)/kp1);
         double xi02 = MathRound(p2x + (y-p2y)/kp1);
         double xi03 = MathRound(p3x + (y-p3y)/kp0);
         if(xsign==1)
           {
            xi0 = MathMax(xi00,xi01);
            xi1 = MathMin(xi02,xi03);
           }
         else
           {
            xi0 = MathMin(xi00,xi01);
            xi1 = MathMax(xi02,xi03);
           }
         double width=MathAbs(MathRound(xi1-xi0));
         for(int j=0; j<=width; j++)
           {
            double xi=xi0+(xsign*j);
            double dist=DistancePointSegment(xi,y,xe1,ye1,xe2,ye2);
            double val = MathAbs(dist/r);
            PixelTransform((int)xi,(int)y,clr,FilterFunction(val));
           }
        }
     }
   else
     {
      double yi0,yi1;
      double width=MathAbs(p2x-p1x);
      for(int i=0; i<=width; i++)
        {
         double x=p1x+(xsign*i);
         double yi00 = MathRound(p0y + (x-p0x)*kp0);
         double yi01 = MathRound(p1y + (x-p1x)*kp1);
         double yi02 = MathRound(p2y + (x-p2x)*kp1);
         double yi03 = MathRound(p3y + (x-p3x)*kp0);
         if(ysign==1)
           {
            yi0 = MathMax(yi00,yi02);
            yi1 = MathMin(yi01,yi03);
           }
         else
           {
            yi0 = MathMin(yi00,yi02);
            yi1 = MathMax(yi01,yi03);
           }
         double height=MathAbs(yi1-yi0);
         for(int j=0; j<=height; j++)
           {
            double yi=yi0+(ysign*j);
            double dist=DistancePointSegment(x,yi,xe1,ye1,xe2,ye2);
            double val=MathAbs(dist/r);
            PixelTransform((int)x,(int)yi,clr,FilterFunction(val));
           }
        }
     }
  }
//+------------------------------------------------------------------+
//| Filter function for calculating alpha channel                    |
//+------------------------------------------------------------------+
double CCanvas::FilterFunction(const double x)
  {
   if(x<=0.8)
      return(1.0);
   else
      return MathExp(-(x-0.8)*(x-0.8)*50);
  }
//+------------------------------------------------------------------+
//| Calculate distance between point and segment                     |
//+------------------------------------------------------------------+
double CCanvas::DistancePointSegment(const double px,const double py,const double x1,const double y1,const double x2,const double y2)
  {
//--- primary calculate
   double a=(px-x1)*(px-x1)+(py-y1)*(py-y1);
   double b=(px-x2)*(px-x2)+(py-y2)*(py-y2);
   double c=(x2-x1)*(x2-x1)+(y2-y1)*(y2-y1);
//--- check
   if(a>=b+c)
      return (MathSqrt(b));
   if(b>=a+c)
      return (MathSqrt(a));
//--- calculate distance
   a=MathSqrt(a);
   b=MathSqrt(b);
   c=MathSqrt(c);
   double p=(a+b+c)/2;
   double s=MathSqrt((p-a)*(p-b)*(p-c)*p);
//--- check distance
   if(MathIsValidNumber(s))
      return(s*2.0/c);
   else
      return(0);
  }
//+------------------------------------------------------------------+
//| Draw smothing polyline                                           |
//+------------------------------------------------------------------+
void CCanvas::PolylineSmooth(const int &x[],const int &y[],const uint clr,const int size,ENUM_LINE_STYLE style=STYLE_SOLID,
                             ENUM_LINE_END end_style=LINE_END_ROUND,double tension=0.5,double step=10)
  {
//---
   int arr_size= ArraySize(x);
   if(arr_size!=ArraySize(y))
      return;
//---
   double x1,x2,y1,y2;
   tension*=0.3;
//--- coordinates of Bezier curve
   int xc[];
   int yc[];
//--- initialize control points
   double ptX[];
   double ptY[];
   int    size_pt=arr_size*3-2;

   ArrayResize(ptX,size_pt);
   ArrayResize(ptY,size_pt);
//--- calculation of control points
   CalcCurveBezierEndp(x[0],y[0],x[1],y[1],tension,x1,y1);

   ptX[0] = x[0];
   ptY[0] = y[0];
   ptX[1] = x1;
   ptY[1] = y1;

   for(int i=0; i<arr_size-2; i++)
     {
      CalcCurveBezier(x,y,i,tension,x1,y1,x2,y2);
      ptX[3*i+2] = x1;
      ptY[3*i+2] = y1;
      ptX[3*i+3] = x[i+1];
      ptY[3*i+3] = y[i+1];
      ptX[3*i+4] = x2;
      ptY[3*i+4] = y2;
     }
   CalcCurveBezierEndp(x[arr_size-1],y[arr_size-1],x[arr_size-2],y[arr_size-2],tension,x1,y1);

   ptX[size_pt-2] = x1;
   ptY[size_pt-2] = y1;
   ptX[size_pt-1] = x[arr_size-1];
   ptY[size_pt-1] = y[arr_size-1];
//--- calculation of the coordinates of Bezier curves
   int index=0;
   for(int i=0; i<arr_size-1; i++)
     {
      //--- Euclidean distance between two neighboring points
      double distance=MathSqrt((x[i+1]-x[i])*(x[i+1]-x[i])+(y[i+1]-y[i])*(y[i+1]-y[i]));
      int size_i=(step>0.0) ?(int)(distance/step) : 1;
      if(size_i<1)
         size_i=2;
      ArrayResize(xc,ArraySize(xc)+size_i,1024);
      ArrayResize(yc,ArraySize(yc)+size_i,1024);
      for(int t=0; t<size_i; t++,index++)
        {
         xc[index]=(int)MathRound(CalcBezierX((double)t/size_i,ptX[3*i],ptX[3*i+1],ptX[3*i+2],ptX[3*i+3]));
         yc[index]=(int)MathRound(CalcBezierY((double)t/size_i,ptY[3*i],ptY[3*i+1],ptY[3*i+2],ptY[3*i+3]));
        }
     }
   PolylineThick(xc,yc,clr,size,style,LINE_END_ROUND);
  }
//+------------------------------------------------------------------+
//| Draw smothing polygone                                           |
//+------------------------------------------------------------------+
void CCanvas::PolygonSmooth(int &x[],int &y[],const uint clr,const int size,ENUM_LINE_STYLE style=STYLE_SOLID,
                            ENUM_LINE_END end_style=LINE_END_ROUND,double tension=0.5,double step=10)
  {
//---
   int size_arr= ArraySize(x);
   if(size_arr!=ArraySize(y))
      return;
//---
   double x1,x2,y1,y2;
   tension*=0.3;
//--- coordinates of Bezier curve
   int xc[];
   int yc[];
//--- initialize control points
   double ptX[];
   double ptY[];
   int    size_pt=(size_arr+1)*3-3;

   ArrayResize(ptX,size_pt);
   ArrayResize(ptY,size_pt);
//--- calculation of control points
   int xe[];
   int ye[];
   ArrayResize(xe,size_arr+2);
   ArrayResize(ye,size_arr+2);
   xe[0]=x[size_arr-1];
   ye[0]=y[size_arr-1];
   xe[size_arr+1] = x[0];
   ye[size_arr+1] = y[0];
   ArrayCopy(xe,x,1,0,size_arr);
   ArrayCopy(ye,y,1,0,size_arr);
//---
   for(int i=0; i<size_arr; i++)
     {
      CalcCurveBezier(xe,ye,i,tension,x1,y1,x2,y2);
      ptX[3*i+0] = x1;
      ptY[3*i+0] = y1;
      ptX[3*i+1] = xe[i+1];
      ptY[3*i+1] = ye[i+1];
      ptX[3*i+2] = x2;
      ptY[3*i+2] = y2;
     }
//--- Euclidean distance between two neighboring points
   int index=0;
   for(int i=0; i<size_arr-1; i++)
     {
      double distance=MathSqrt((x[i]-x[i+1])*(x[i]-x[i+1])+(y[i]-y[i+1])*(y[i]-y[i+1]));
      int size_i=(step>0.0) ?(int)(distance/step) : 1;
      if(size_i<1)
         size_i=2;
      ArrayResize(xc,ArraySize(xc)+size_i,1024);
      ArrayResize(yc,ArraySize(yc)+size_i,1024);
      for(int t=0; t<size_i; t++,index++)
        {
         xc[index]=(int)MathRound(CalcBezierX((double)t/size_i,ptX[1+i*3],ptX[2+i*3],ptX[3+i*3],ptX[4+i*3]));
         yc[index]=(int)MathRound(CalcBezierY((double)t/size_i,ptY[1+i*3],ptY[2+i*3],ptY[3+i*3],ptY[4+i*3]));
        }
     }
//---
   double distance=MathSqrt((x[size_arr-1]-x[0])*(x[size_arr-1]-x[0])+(y[size_arr-1]-y[0])*(y[size_arr-1]-y[0]));
   int size_i=(step>0.0) ?(int)(distance/step) : 1;
   if(size_i<1)
      size_i=2;
   ArrayResize(xc,ArraySize(xc)+size_i,1024);
   ArrayResize(yc,ArraySize(yc)+size_i,1024);
   for(int t=0; t<size_i; t++,index++)
     {
      xc[index]=(int)MathRound(CalcBezierX((double)t/size_i,ptX[size_pt-2],ptX[size_pt-1],ptX[0],ptX[1]));
      yc[index]=(int)MathRound(CalcBezierY((double)t/size_i,ptY[size_pt-2],ptY[size_pt-1],ptY[0],ptY[1]));
     }
   PolygonThick(xc,yc,clr,size,style,LINE_END_ROUND);
  }
//+------------------------------------------------------------------+
//| Calculates Bezier points from cardinal spline endpoints.         |
//+------------------------------------------------------------------+
void CCanvas::CalcCurveBezierEndp(const double xend,const double yend,const double xadj,const double yadj,const double tension,double &x,double &y)
  {
   x = (tension * (xadj - xend) + xend);
   y = (tension * (yadj - yend) + yend);
  }
//+------------------------------------------------------------------+
//| Calculates Bezier points from cardinal spline points             |
//+------------------------------------------------------------------+
void CCanvas::CalcCurveBezier(const int &x[],const int &y[],const int i,const double tension,double &x1,double &y1,double &x2,double &y2)
  {
   double xdiff,ydiff;
//--- calculate tangent
   xdiff = x[i+2] - x[i];
   ydiff = y[i+2] - y[i];
//--- apply tangent to get control points
   x1 = x[i+1] - tension * xdiff;
   y1 = y[i+1] - tension * ydiff;
   x2 = x[i+1] + tension * xdiff;
   y2 = y[i+1] + tension * ydiff;
//---
  }
//+------------------------------------------------------------------+
//| Calculate x coordinate of Bezier curve                           |
//+------------------------------------------------------------------+
double CCanvas::CalcBezierX(const double t,const double x0,const double x1,const double x2,const double x3)
  {
   return(x0*((1-t)*(1-t)*(1-t))+
          x1*3*t*((1-t)*(1-t))+
          x2*3*(t*t)*(1-t)+
          x3*(t*t*t));
  }
//+------------------------------------------------------------------+
//| Calculate y coordinate of Bezier curve                           |
//+------------------------------------------------------------------+
double CCanvas::CalcBezierY(const double t,const double y0,const double y1,const double y2,const double y3)
  {
   return(y0*((1-t)*(1-t)*(1-t))+
          y1*3*t*((1-t)*(1-t))+
          y2*3*(t*t)*(1-t)+
          y3*(t*t*t));
  }
//+------------------------------------------------------------------+
//#include <Tools\DateTime.mqh>
//+------------------------------------------------------------------+
//|                                                     DateTime.mqh |
//|                   Copyright 2009-2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Structure CDateTime.                                             |
//| Purpose: Working with dates and time.                            |
//|         Extends the MqlDateTime structure.                       |
//+------------------------------------------------------------------+
struct CDateTime : public MqlDateTime
  {
   //--- additional information
   string            MonthName(const int num) const;
   string            ShortMonthName(const int num) const;
   string            DayName(const int num) const;
   string            ShortDayName(const int num) const;
   string            MonthName(void)               const { return(MonthName(mon));            }
   string            ShortMonthName(void)          const { return(ShortMonthName(mon));       }
   string            DayName(void)                 const { return(DayName(day_of_week));      }
   string            ShortDayName(void)            const { return(ShortDayName(day_of_week)); }
   int               DaysInMonth(void) const;
   //--- data access
   datetime          DateTime(void)                      { return(StructToTime(this));        }
   void              DateTime(const datetime value)      { TimeToStruct(value,this);          }
   void              DateTime(const MqlDateTime& value)  { this=value;                        }
   void              Date(const datetime value);
   void              Date(const MqlDateTime &value);
   void              Time(const datetime value);
   void              Time(const MqlDateTime &value);
   //--- settings
   void              Sec(const int value);
   void              Min(const int value);
   void              Hour(const int value);
   void              Day(const int value);
   void              Mon(const int value);
   void              Year(const int value);
   //--- increments
   void              SecDec(int delta=1);
   void              SecInc(int delta=1);
   void              MinDec(int delta=1);
   void              MinInc(int delta=1);
   void              HourDec(int delta=1);
   void              HourInc(int delta=1);
   void              DayDec(int delta=1);
   void              DayInc(int delta=1);
   void              MonDec(int delta=1);
   void              MonInc(int delta=1);
   void              YearDec(int delta=1);
   void              YearInc(int delta=1);
   //--- check
   void              DayCheck(void);
  };
//+------------------------------------------------------------------+
//| Gets month name                                                  |
//+------------------------------------------------------------------+
string CDateTime::MonthName(const int num) const
  {
   switch(num)
     {
      case  1: return("January");
      case  2: return("February");
      case  3: return("March");
      case  4: return("April");
      case  5: return("May");
      case  6: return("June");
      case  7: return("July");
      case  8: return("August");
      case  9: return("September");
      case 10: return("October");
      case 11: return("November");
      case 12: return("December");
     }
//---
   return("Bad month");
  }
//+------------------------------------------------------------------+
//| Gets short name of month                                         |
//+------------------------------------------------------------------+
string CDateTime::ShortMonthName(const int num) const
  {
   switch(num)
     {
      case  1: return("jan");
      case  2: return("feb");
      case  3: return("mar");
      case  4: return("apr");
      case  5: return("may");
      case  6: return("jun");
      case  7: return("jul");
      case  8: return("aug");
      case  9: return("sep");
      case 10: return("oct");
      case 11: return("nov");
      case 12: return("dec");
     }
//---
   return("Bad month");
  }
//+------------------------------------------------------------------+
//| Gets name of week day                                            |
//+------------------------------------------------------------------+
string CDateTime::DayName(const int num) const
  {
   switch(num)
     {
      case 0: return("Sunday");
      case 1: return("Monday");
      case 2: return("Tuesday");
      case 3: return("Wednesday");
      case 4: return("Thursday");
      case 5: return("Friday");
      case 6: return("Saturday");
     }
//---
   return("Bad day of week");
  }
//+------------------------------------------------------------------+
//| Gets short name of week day                                      |
//+------------------------------------------------------------------+
string CDateTime::ShortDayName(const int num) const
  {
   switch(num)
     {
      case 0: return("Su");
      case 1: return("Mo");
      case 2: return("Tu");
      case 3: return("We");
      case 4: return("Th");
      case 5: return("Fr");
      case 6: return("Sa");
     }
//---
   return("Bad day of week");
  }
//+------------------------------------------------------------------+
//| Gets number of days in month                                     |
//+------------------------------------------------------------------+
int CDateTime::DaysInMonth(void) const
  {
   int leap_year;
//---
   switch(mon)
     {
      case  1:
      case  3:
      case  5:
      case  7:
      case  8:
      case 10:
      case 12:
         return(31);
      case  2:
         leap_year=year;
         if(year%100==0)
            leap_year/=100;
         return((leap_year%4==0)? 29 : 28);
      case  4:
      case  6:
      case  9:
      case 11:
         return(30);
     }
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Sets date                                                        |
//+------------------------------------------------------------------+
void CDateTime::Date(const datetime value)
  {
   MqlDateTime dt;
//--- convert to structure
   TimeToStruct(value,dt);
//--- set
   Date(dt);
  }
//+------------------------------------------------------------------+
//| Sets date                                                        |
//+------------------------------------------------------------------+
void CDateTime::Date(const MqlDateTime &value)
  {
   day =value.day;
   mon =value.mon;
   year=value.year;
//--- check if day is correct
   DayCheck();
  }
//+------------------------------------------------------------------+
//| Sets time                                                        |
//+------------------------------------------------------------------+
void CDateTime::Time(const datetime value)
  {
   MqlDateTime dt;
//--- convert to structure
   TimeToStruct(value,dt);
//--- set
   Time(dt);
  }
//+------------------------------------------------------------------+
//| Sets time                                                        |
//+------------------------------------------------------------------+
void CDateTime::Time(const MqlDateTime &value)
  {
   hour=value.hour;
   min =value.min;
   sec =value.sec;
  }
//+------------------------------------------------------------------+
//| Sets seconds                                                     |
//+------------------------------------------------------------------+
void CDateTime::Sec(const int value)
  {
//--- check and set
   if(value>=0 && value<60)
      sec=value;
  }
//+------------------------------------------------------------------+
//| Sets minutes                                                     |
//+------------------------------------------------------------------+
void CDateTime::Min(const int value)
  {
//--- check and set
   if(value>=0 && value<60)
      min=value;
  }
//+------------------------------------------------------------------+
//| Sets hours                                                       |
//+------------------------------------------------------------------+
void CDateTime::Hour(const int value)
  {
//--- check and set
   if(value>=0 && value<24)
      hour=value;
  }
//+------------------------------------------------------------------+
//| Sets day of month                                                |
//+------------------------------------------------------------------+
void CDateTime::Day(const int value)
  {
//--- check and set
   if(value>0 && value<=DaysInMonth())
     {
      day=value;
      //--- check if day is correct
      DayCheck();
     }
  }
//+------------------------------------------------------------------+
//| Sets month                                                       |
//+------------------------------------------------------------------+
void CDateTime::Mon(const int value)
  {
//--- check and set
   if(value>0 && value<=12)
     {
      mon=value;
      //--- check if day is correct
      DayCheck();
     }
  }
//+------------------------------------------------------------------+
//| Sets year                                                        |
//+------------------------------------------------------------------+
void CDateTime::Year(const int value)
  {
//--- check and set
   if(value>=1970)
     {
      year=value;
      //--- check if day is correct
      DayCheck();
     }
  }
//+------------------------------------------------------------------+
//| Subtracts specified number of seconds                            |
//+------------------------------------------------------------------+
void CDateTime::SecDec(int delta)
  {
//--- if increment is 0 - exit
   if(delta==0)
      return;
//--- if increment is negative - inverse the operation
   if(delta<0)
     {
      SecInc(-delta);
      return;
     }
//--- check if subtract from upper number positions
   if(delta>60)
     {
      MinDec(delta/60);
      delta%=60;
     }
   sec-=delta;
   if(sec<0)
     {
      sec+=60;
      MinDec();
     }
  }
//+------------------------------------------------------------------+
//| Adds specified number of seconds                                 |
//+------------------------------------------------------------------+
void CDateTime::SecInc(int delta)
  {
//--- if increment is 0 - exit
   if(delta==0)
      return;
//--- if increment is negative - inverse the operation
   if(delta<0)
     {
      SecDec(-delta);
      return;
     }
//--- check if add to upper number positions
   if(delta>60)
     {
      MinInc(delta/60);
      delta%=60;
     }
   sec+=delta;
   if(sec>=60)
     {
      sec-=60;
      MinInc();
     }
  }
//+------------------------------------------------------------------+
//| Subtracts specified number of minutes                            |
//+------------------------------------------------------------------+
void CDateTime::MinDec(int delta)
  {
//--- if increment is 0 - exit
   if(delta==0)
      return;
//--- if increment is negative - inverse the operation
   if(delta<0)
     {
      MinInc(-delta);
      return;
     }
//--- check if subtract from upper number positions
   if(delta>60)
     {
      HourDec(delta/60);
      delta%=60;
     }
   min-=delta;
   if(min<0)
     {
      min+=60;
      HourDec();
     }
  }
//+------------------------------------------------------------------+
//| Adds specified number of minutes                                 |
//+------------------------------------------------------------------+
void CDateTime::MinInc(int delta)
  {
//--- if increment is 0 - exit
   if(delta==0)
      return;
//--- if increment is negative - inverse the operation
   if(delta<0)
     {
      MinDec(-delta);
      return;
     }
//--- check if add to upper number positions
   if(delta>60)
     {
      HourInc(delta/60);
      delta%=60;
     }
   min+=delta;
   if(min>=60)
     {
      min-=60;
      HourInc();
     }
  }
//+------------------------------------------------------------------+
//| Subtracts specified number of hours                              |
//+------------------------------------------------------------------+
void CDateTime::HourDec(int delta)
  {
//--- if increment is 0 - exit
   if(delta==0)
      return;
//--- if increment is negative - inverse the operation
   if(delta<0)
     {
      HourInc(-delta);
      return;
     }
//--- check if subtract from upper number positions
   if(delta>24)
     {
      DayDec(delta/24);
      delta%=24;
     }
   hour-=delta;
   if(hour<0)
     {
      hour+=24;
      DayDec();
     }
  }
//+------------------------------------------------------------------+
//| Adds specified number of hours                                   |
//+------------------------------------------------------------------+
void CDateTime::HourInc(int delta)
  {
//--- if increment is 0 - exit
   if(delta==0)
      return;
//--- if increment is negative - inverse the operation
   if(delta<0)
     {
      HourDec(-delta);
      return;
     }
//--- check if add to upper number positions
   if(delta>24)
     {
      DayInc(delta/24);
      delta%=24;
     }
   hour+=delta;
   if(hour>=24)
     {
      hour-=24;
      DayInc();
     }
  }
//+------------------------------------------------------------------+
//| Subtracts specified number of days                               |
//+------------------------------------------------------------------+
void CDateTime::DayDec(int delta)
  {
//--- if increment is 0 - exit
   if(delta==0)
      return;
//--- if increment is negative - inverse the operation
   if(delta<0)
     {
      DayInc(-delta);
      return;
     }
//--- uncertain condition, as the number of days in month can differ
   while(day<=delta)
     {
      delta-=day;
      MonDec();
      day=DaysInMonth();
     }
   day-=delta;
//--- check if day is correct
   DayCheck();
  }
//+------------------------------------------------------------------+
//| Adds specified number of days                                    |
//+------------------------------------------------------------------+
void CDateTime::DayInc(int delta)
  {
//--- if increment is 0 - exit
   if(delta==0)
      return;
//--- if increment is negative - inverse the operation
   if(delta<0)
     {
      DayDec(-delta);
      return;
     }
//--- uncertain condition, as the number of days in month can differ
   while(DaysInMonth()-day<delta)
     {
      delta-=DaysInMonth()-day+1;
      MonInc();
      day=1;
     }
   day+=delta;
//--- check if day is correct
   DayCheck();
  }
//+------------------------------------------------------------------+
//| Subtracts specified number of months                             |
//+------------------------------------------------------------------+
void CDateTime::MonDec(int delta)
  {
//--- if increment is 0 - exit
   if(delta==0)
      return;
//--- if increment is negative - inverse the operation
   if(delta<0)
     {
      MonInc(-delta);
      return;
     }
//--- check if subtract from upper number positions
   if(delta>12)
     {
      YearDec(delta/12);
      delta%=12;
     }
   mon-=delta;
   if(mon<=0)
     {
      mon+=12;
      YearDec();
     }
//--- check if day is correct
   DayCheck();
  }
//+------------------------------------------------------------------+
//| Adds specified number of months                                  |
//+------------------------------------------------------------------+
void CDateTime::MonInc(int delta)
  {
//--- if increment is 0 - exit
   if(delta==0)
      return;
//--- if increment is negative - inverse the operation
   if(delta<0)
     {
      MonDec(-delta);
      return;
     }
//--- check if add to upper number positions
   if(delta>12)
     {
      YearInc(delta/12);
      delta%=12;
     }
   mon+=delta;
   if(mon>12)
     {
      mon-=12;
      YearInc();
     }
//--- check if day is correct
   DayCheck();
  }
//+------------------------------------------------------------------+
//| Subtracts specified number of years                              |
//+------------------------------------------------------------------+
void CDateTime::YearDec(int delta)
  {
//--- if increment is 0 - exit
   if(delta!=0)
     {
      year-=delta;
      //--- check if day is correct
      DayCheck();
     }
  }
//+------------------------------------------------------------------+
//| Adds specified number of years                                   |
//+------------------------------------------------------------------+
void CDateTime::YearInc(int delta)
  {
//--- if increment is 0 - exit
   if(delta!=0)
     {
      year+=delta;
      //--- check if day is correct
      DayCheck();
     }
  }
//+------------------------------------------------------------------+
//| Checks if day number is correct                                  |
//+------------------------------------------------------------------+
void CDateTime::DayCheck(void)
  {
   if(day>DaysInMonth())
      day=DaysInMonth();
//--- this is required to get day of week and day of year
   TimeToStruct(StructToTime(this),this);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+
//--- date modes
enum ENUM_DATE_MODES
  {
   DATE_MODE_MON,                           // month mode
   DATE_MODE_YEAR                           // year mode
  };
//+------------------------------------------------------------------+
//| Resources                                                        |
//+------------------------------------------------------------------+
//--- Can not place the same file into resource twice
#resource "\\Include\\Controls\\res\\LeftTransp.bmp"
#resource "\\Include\\Controls\\res\\RightTransp.bmp"
//+------------------------------------------------------------------+
//| Class CDateDropList                                              |
//| Usage: drop-down list                                            |
//+------------------------------------------------------------------+
class CDateDropList : public CWndContainer
  {
private:
   //--- dependent controls
   CBmpButton        m_dec;                 // the button object
   CBmpButton        m_inc;                 // the button object
   CPicture          m_list;                // the drop-down list object
   CCanvas           m_canvas;              // and its canvas
   //--- data
   CDateTime         m_value;               // current value
   //--- variable
   ENUM_DATE_MODES   m_mode;                // operation mode
   CRect             m_click_rect[32];      // array of click sensibility areas on canvas

public:
                     CDateDropList(void);
                    ~CDateDropList(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- data
   datetime          Value(void)                    { return(StructToTime(m_value)); }
   void              Value(datetime value)          { m_value.Date(value);           }
   void              Value(MqlDateTime& value)      { m_value=value;                 }
   //--- state
   virtual bool      Show(void);

protected:
   //--- internal event handlers
   virtual bool      OnClick(void);
   //--- create dependent controls
   virtual bool      CreateButtons(void);
   virtual bool      CreateList(void);
   //--- draw
   void              DrawCanvas(void);
   void              DrawClickRect(const int idx,int x,int y,string text,const uint clr,uint alignment=0);
   //--- handlers of the dependent controls events
   virtual bool      OnClickDec(void);
   virtual bool      OnClickInc(void);
   virtual bool      OnClickList(void);
  };
//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CDateDropList)
   ON_EVENT(ON_CLICK,m_dec,OnClickDec)
   ON_EVENT(ON_CLICK,m_inc,OnClickInc)
   ON_EVENT(ON_CLICK,m_list,OnClickList)
EVENT_MAP_END(CWndContainer)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CDateDropList::CDateDropList(void) : m_mode(DATE_MODE_MON)
  {
   ZeroMemory(m_value);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CDateDropList::~CDateDropList(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CDateDropList::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- need to find dimensions depending on font size
//--- width 7 columns + 2 offsets
   int w=7*(2*CONTROLS_FONT_SIZE)+2*CONTROLS_FONT_SIZE;
//--- header height + 7 rows
   int h=(CONTROLS_BUTTON_SIZE+4*CONTROLS_BORDER_WIDTH)+7*(2*CONTROLS_FONT_SIZE);
//--- call method of the parent class
   if(!CWndContainer::Create(chart,name,subwin,x1,y1,x1+w,y1+h))
      return(false);
//--- create dependent controls
   if(!CreateList())
      return(false);
   if(!CreateButtons())
      return(false);
//--- succeeded
   return(true);
  }
//+------------------------------------------------------------------+
//| Create drop-down list                                            |
//+------------------------------------------------------------------+
bool CDateDropList::CreateList(void)
  {
//--- create object
   if(!m_list.Create(m_chart_id,m_name+"List",m_subwin,0,0,Width(),Height()))
      return(false);
   if(!Add(m_list))
      return(false);
//--- create canvas
   if(!m_canvas.Create(m_name,Width(),Height()))
      return(false);
   m_canvas.FontSet(CONTROLS_FONT_NAME,CONTROLS_FONT_SIZE*(-10));
   m_list.BmpName(m_canvas.ResourceName());
//--- succeeded
   return(true);
  }
//+------------------------------------------------------------------+
//| Create buttons                                                   |
//+------------------------------------------------------------------+
bool CDateDropList::CreateButtons(void)
  {
//--- right align button (try to make equal offsets from top and bottom)
   int x1=2*CONTROLS_BORDER_WIDTH;
   int y1=2*CONTROLS_BORDER_WIDTH;
   int x2=x1+CONTROLS_BUTTON_SIZE;
   int y2=y1+CONTROLS_BUTTON_SIZE;
//--- create "Dec" button
   if(!m_dec.Create(m_chart_id,m_name+"Dec",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_dec.BmpNames("::Include\\Controls\\res\\LeftTransp.bmp"))
      return(false);
   if(!Add(m_dec))
      return(false);
//---
   x2=Width()-2*CONTROLS_BORDER_WIDTH;
   x1=x2-CONTROLS_BUTTON_SIZE;
//--- create "Inc" button
   if(!m_inc.Create(m_chart_id,m_name+"Inc",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_inc.BmpNames("::Include\\Controls\\res\\RightTransp.bmp"))
      return(false);
   if(!Add(m_inc))
      return(false);
//--- succeeded
   return(true);
  }
//+------------------------------------------------------------------+
//| Makes the control visible                                        |
//+------------------------------------------------------------------+
bool CDateDropList::Show(void)
  {
//--- draw canvas
   DrawCanvas();
//--- call method of the parent class
   return(CWndContainer::Show());
  }
//+------------------------------------------------------------------+
//| Handler of click on button                                       |
//+------------------------------------------------------------------+
bool CDateDropList::OnClickDec(void)
  {
   switch(m_mode)
     {
      //--- within the month
      case DATE_MODE_MON:
         m_value.MonDec();
         break;
         //--- within the year
      case DATE_MODE_YEAR:
         m_value.YearDec();
         break;
     }
   DrawCanvas();
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of click on button                                       |
//+------------------------------------------------------------------+
bool CDateDropList::OnClickInc(void)
  {
   switch(m_mode)
     {
      //--- within the month
      case DATE_MODE_MON:
         m_value.MonInc();
         break;
         //--- within the year
      case DATE_MODE_YEAR:
         m_value.YearInc();
         break;
     }
   DrawCanvas();
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of click on picture                                      |
//+------------------------------------------------------------------+
bool CDateDropList::OnClickList(void)
  {
   m_mouse_x=m_list.MouseX();
   m_mouse_y=m_list.MouseY();
//---
   OnClick();
//---
   m_mouse_x=0;
   m_mouse_y=0;
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "click" event                                     |
//+------------------------------------------------------------------+
bool CDateDropList::OnClick(void)
  {
   for(int i=0;i<32;i++)
     {
      if(m_click_rect[i].Contains(m_mouse_x,m_mouse_y))
        {
         if(i==0)
           {
            //--- clicked on the header
            switch(m_mode)
              {
               //--- within the month
               case DATE_MODE_MON:
                  //--- switch to the "within the year" mode
                  m_mode=DATE_MODE_YEAR;
                  DrawCanvas();
                  break;
                  //--- within the year
               case DATE_MODE_YEAR:
                  //--- do nothing for now
                  break;
              }
           }
         else
           {
            //--- selected
            switch(m_mode)
              {
               //--- within the month
               case DATE_MODE_MON:
                  m_value.Day(i);
                  Hide();
                  //--- send notification
                  EventChartCustom(CONTROLS_SELF_MESSAGE,ON_CHANGE,m_id,0.0,m_name);
                  break;
                  //--- within the year
               case DATE_MODE_YEAR:
                  m_value.Mon(i);
                  m_mode=DATE_MODE_MON;
                  DrawCanvas();
                  break;
               default:
                  break;
              }
           }
         break;
        }
     }
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Draw canvas                                                      |
//+------------------------------------------------------------------+
void CDateDropList::DrawCanvas(void)
  {
   int         x,y;
   int         dx,dy;
   string      text;
   uint        text_al=TA_CENTER|TA_VCENTER;
   CDateTime   tmp_date;
   int         rows,cols;
   int         idx;
//--- zero out array of areas
   for(int i=0;i<32;i++)
      ZeroMemory(m_click_rect[i]);
//---
   m_canvas.Erase(COLOR2RGB(CONTROLS_EDIT_COLOR_BG));
   m_canvas.Rectangle(0,0,Width()-1,Height()-1,COLOR2RGB(CONTROLS_EDIT_COLOR_BORDER));
   x=Width()/2;
   y=CONTROLS_BUTTON_SIZE/2+2*CONTROLS_BORDER_WIDTH;
   switch(m_mode)
     {
      //--- within the month
      case DATE_MODE_MON:
         text=m_value.MonthName()+" "+IntegerToString(m_value.year);
         DrawClickRect(0,x,y,text,COLOR2RGB(CONTROLS_EDIT_COLOR),text_al);
         rows=6;
         cols=7;
         x=dx=Width()/(cols+1);
         y+=y;
         dy=(Height()-y-2*CONTROLS_BORDER_WIDTH)/(rows+1);
         y+=dy/2;
         for(int i=0;i<cols;i++,x+=dx)
            m_canvas.TextOut(x,y,m_value.ShortDayName(i),COLOR2RGB(CONTROLS_EDIT_COLOR),text_al);
         //--- backup data
         tmp_date=m_value;
         //--- find the beginning of the first displayed week
         tmp_date.DayDec(tmp_date.day_of_week);
         while(tmp_date.mon==m_value.mon && tmp_date.day!=1)
            tmp_date.DayDec(cols);
         //--- draw
         idx=1;
         y+=dy;
         for(int i=0;i<rows;i++,y+=dy)
           {
            x=dx;
            for(int j=0;j<cols;j++,x+=dx)
              {
               text=IntegerToString(tmp_date.day);
               if(tmp_date.mon==m_value.mon)
                 {
                  if(tmp_date.day==m_value.day)
                     m_canvas.FillRectangle(x-dx/2,y-dy/2,x+dx/2,y+dy/2,COLOR2RGB(CONTROLS_COLOR_BG_SEL));
                  DrawClickRect(idx++,x,y,text,COLOR2RGB(CONTROLS_EDIT_COLOR),text_al);
                 }
               else
                  m_canvas.TextOut(x,y,text,COLOR2RGB(CONTROLS_BUTTON_COLOR_BORDER),text_al);
               tmp_date.DayInc();
              }
           }
         break;
         //--- within the year
      case DATE_MODE_YEAR:
         text=IntegerToString(m_value.year);
         DrawClickRect(0,x,y,text,COLOR2RGB(CONTROLS_EDIT_COLOR),text_al);
         rows=3;
         cols=4;
         x=dx=Width()/(cols+1);
         y+=y;
         dy=(Height()-y)/rows;
         y+=dy/2;
         for(int i=0;i<rows*cols;i++)
           {
            if(i+1==m_value.mon)
               m_canvas.FillRectangle(x-dx/2,y-dy/4,x+dx/2,y+dy/4,COLOR2RGB(CONTROLS_COLOR_BG_SEL));
            DrawClickRect(i+1,x,y,m_value.ShortMonthName(i+1),COLOR2RGB(CONTROLS_EDIT_COLOR),text_al);
            if(i%cols==cols-1)
              {
               x=dx;
               y+=dy;
              }
            else
               x+=dx;
           }
         break;
      default:
         break;
     }
   m_canvas.Update();
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CDateDropList::DrawClickRect(const int idx,int x,int y,string text,const uint clr,uint alignment)
  {
   int text_w,text_h;
//--- display the text
   m_canvas.TextOut(x,y,text,clr,alignment);
//--- determine area occupied by text
   m_canvas.TextSize(text,text_w,text_h);
//--- convert relative coordinated to absolute ones
   x+=Left();
   y+=Top();
//--- check flags of horizontal alignment
   switch(alignment&(TA_LEFT|TA_CENTER|TA_RIGHT))
     {
      case TA_LEFT:
         m_click_rect[idx].left=x;
         break;
      case TA_CENTER:
         m_click_rect[idx].left=x-text_w/2;
         break;
      case TA_RIGHT:
         m_click_rect[idx].left=x-text_w;
         break;
     }
   m_click_rect[idx].Width(text_w);
//--- check flags of vertical alignment
   switch(alignment&(TA_TOP|TA_VCENTER|TA_BOTTOM))
     {
      case TA_TOP:
         m_click_rect[idx].top=y;
         break;
      case TA_VCENTER:
         m_click_rect[idx].top=y-text_h/2;
         break;
      case TA_BOTTOM:
         m_click_rect[idx].top=y-text_h;
         break;
     }
   m_click_rect[idx].Height(text_h);
  }
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Resources                                                        |
//+------------------------------------------------------------------+
//--- Can not place the same file into resource twice
#resource "\\Include\\Controls\\res\\DateDropOn.bmp"                 // image file
#resource "\\Include\\Controls\\res\\DateDropOff.bmp"                // image file
//+------------------------------------------------------------------+
//| Class CDatePicker                                                |
//| Usage: date picker                                               |
//+------------------------------------------------------------------+
class CDatePicker : public CWndContainer
  {
protected:
   //--- dependent controls
   CEdit             m_edit;                // the entry field object
   CBmpButton        m_drop;                // the button object
   CDateDropList     m_list;                // the drop-down list object
   //--- data
   datetime          m_value;               // current value

public:
                     CDatePicker(void);
                    ~CDatePicker(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- data
   datetime          Value(void)              const { return(m_value);                                    }
   void              Value(datetime value)          { m_edit.Text(TimeToString(m_value=value,TIME_DATE)); }
   //--- state
   virtual bool      Show(void);
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);

protected:
   //--- create dependent controls
   virtual bool      CreateEdit(void);
   virtual bool      CreateButton(void);
   virtual bool      CreateList(void);
   //--- handlers of the dependent controls events
   virtual bool      OnClickEdit(void);
   virtual bool      OnClickButton(void);
   virtual bool      OnChangeList(void);
   //--- show drop-down list
   bool              ListShow(void);
   bool              ListHide(void);
   void              CheckListHide(const int id,int x,int y);
   virtual bool      OnResize(void) override;
  };
//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CDatePicker)
   ON_EVENT(ON_CLICK,m_edit,OnClickEdit)
   ON_EVENT(ON_CLICK,m_drop,OnClickButton)
   ON_EVENT(ON_CHANGE,m_list,OnChangeList)
CheckListHide(id,(int)lparam,(int)dparam);
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
EVENT_MAP_END(CWndContainer)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CDatePicker::CDatePicker(void) : m_value(0)
  {
  RTTI;
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CDatePicker::~CDatePicker(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CDatePicker::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- call method of the parent class
   if(!CWndContainer::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- create dependent controls
   if(!CreateEdit())
      return(false);
   if(!CreateButton())
      return(false);
   if(!CreateList())
      return(false);
//--- succeeded
   return(true);
  }
//+------------------------------------------------------------------+
//| Create main entry field                                          |
//+------------------------------------------------------------------+
bool CDatePicker::CreateEdit(void)
  {
//--- create
   if(!m_edit.Create(m_chart_id,m_name+"Edit",m_subwin,0,0,Width(),Height()))
      return(false);
   if(!m_edit.Text(""))
      return(false);
   if(!m_edit.ReadOnly(true))
      return(false);
   if(!Add(m_edit))
      return(false);
//--- succeeded
   return(true);
  }
//+------------------------------------------------------------------+
//| Create button                                                    |
//+------------------------------------------------------------------+
bool CDatePicker::CreateButton(void)
  {
//--- right align button (try to make equal offsets from top and bottom)
   int x1=Width()-(2*CONTROLS_BUTTON_SIZE+CONTROLS_COMBO_BUTTON_X_OFF);
   int y1=(Height()-CONTROLS_BUTTON_SIZE)/2;
   int x2=x1+2*CONTROLS_BUTTON_SIZE;
   int y2=y1+CONTROLS_BUTTON_SIZE;
//--- create
   if(!m_drop.Create(m_chart_id,m_name+"Drop",m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_drop.BmpNames("::Include\\Controls\\res\\DateDropOff.bmp","::Include\\Controls\\res\\DateDropOn.bmp"))
      return(false);
   if(!Add(m_drop))
      return(false);
   m_drop.Locking(true);
//--- succeeded
   return(true);
  }
//+------------------------------------------------------------------+
//| Create drop-down list                                            |
//+------------------------------------------------------------------+
bool CDatePicker::CreateList(void)
  {
//--- create
   if(!m_list.Create(m_chart_id,m_name+"List",m_subwin,0,Height()-1,Width(),0))
      return(false);
   if(!Add(m_list))
      return(false);
   m_list.Hide();
//--- succeeded
   return(true);
  }
//+------------------------------------------------------------------+
//| Makes the control visible                                        |
//+------------------------------------------------------------------+
bool CDatePicker::Show(void)
  {
   m_edit.Show();
   m_drop.Show();
   m_list.Hide();
//--- call method of the parent class
   return(CWnd::Show());
  }
//+------------------------------------------------------------------+
//| save                                                             |
//+------------------------------------------------------------------+
bool CDatePicker::Save(const int file_handle)
  {
//--- check
   if(file_handle==INVALID_HANDLE)
      return(false);
//--- write
   FileWriteLong(file_handle,Value());
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| load                                                             |
//+------------------------------------------------------------------+
bool CDatePicker::Load(const int file_handle)
  {
//--- check
   if(file_handle==INVALID_HANDLE)
      return(false);
//--- load
   if(!FileIsEnding(file_handle))
      Value(FileReadLong(file_handle));
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of click on main entry field                             |
//+------------------------------------------------------------------+
bool CDatePicker::OnClickEdit(void)
  {
//--- change button state
   if(!m_drop.Pressed(!m_drop.Pressed()))
      return(false);
//--- call the click on button handler
   return(OnClickButton());
  }
//+------------------------------------------------------------------+
//| Handler of click on button                                       |
//+------------------------------------------------------------------+
bool CDatePicker::OnClickButton(void)
{
  BringToTop();
  // show or hide the drop-down list depending on the button state
  return((m_drop.Pressed()) ? ListShow():ListHide());
}
//+------------------------------------------------------------------+
//| Handler of change on drop-down list                              |
//+------------------------------------------------------------------+
bool CDatePicker::OnChangeList(void)
  {
   string text=TimeToString(m_value=m_list.Value(),TIME_DATE);
//--- hide the list, depress the button
   ListHide();
   m_drop.Pressed(false);
//--- set text in the main entry field
   m_edit.Text(text);
//--- send notification
   EventChartCustom(CONTROLS_SELF_MESSAGE,ON_CHANGE,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Show the drop-down list                                          |
//+------------------------------------------------------------------+
bool CDatePicker::ListShow(void)
  {
//--- set value
   m_list.Value(m_value);
//--- show the list
   return(m_list.Show());
  }
//+------------------------------------------------------------------+
//| Hide drop-down list                                              |
//+------------------------------------------------------------------+
bool CDatePicker::ListHide(void)
  {
//--- hide the list
   return(m_list.Hide());
  }
//+------------------------------------------------------------------+
//| Hide the drop-down element if necessary                          |
//+------------------------------------------------------------------+
void CDatePicker::CheckListHide(const int id,int x,int y)
  {
//--- check event ID
   if(id!=CHARTEVENT_CLICK)
      return;
//--- check visibility of the drop-down element
   if(!m_list.IsVisible())
      return;
//--- check mouse cursor's position
   y-=(int)ChartGetInteger(m_chart_id,CHART_WINDOW_YDISTANCE,m_subwin);
   if(!m_edit.Contains(x,y) && !m_list.Contains(x,y))
     {
      m_drop.Pressed(false);
      m_list.Hide();
     }
  }
//+------------------------------------------------------------------+
//| Resize internal objects                                          |
//+------------------------------------------------------------------+
bool CDatePicker::OnResize(void) override
{
  m_edit.Width(Width());
  m_edit.Height(Height());
  int x1 = Width() - (2 * CONTROLS_BUTTON_SIZE + CONTROLS_COMBO_BUTTON_X_OFF);
  int y1 = (Height() - CONTROLS_BUTTON_SIZE) / 2;
  m_drop.Move(Left() + x1, Top() + y1);
  m_list.Move(Right() - m_list.Width(), Bottom());
  
  return CWndContainer::OnResize();
}
//#include <ControlsPlus/CheckBox.mqh>
//#include <ControlsPlus/RadioButton.mqh>

//#include "Box.mqh"
//#include "Grid.mqh"
//+------------------------------------------------------------------+
//|                                                         Grid.mqh |
//|                                                   Enrico Lambino |
//|                                      www.mql5.com/en/users/iceron|
//+------------------------------------------------------------------+
#property copyright "Enrico Lambino"
#property link "http://www.mql5.com"
#property strict
//#include "Box.mqh"
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
class CGrid: public CBox
{
 protected:
  int m_cols;
  int m_rows;
  int m_hgap;
  int m_vgap;
  CSize m_cell_size;

 public:
  CGrid();
  CGrid(int rows, int cols, int hgap = 0, int vgap = 0);
  ~CGrid();
  //virtual int Type() const
  //{
  //  return CLASS_LAYOUT;
  //}
  virtual bool Init(int rows, int cols, int hgap = 0, int vgap = 0);
  virtual bool Create(const long chart, const string name, const int subwin,
                      const int x1, const int y1, const int x2, const int y2);
  virtual int Columns()
  {
    return (m_cols);
  }
  virtual void Columns(int cols)
  {
    m_cols = cols;
  }
  virtual int Rows()
  {
    return (m_rows);
  }
  virtual void Rows(int rows)
  {
    m_rows = rows;
  }
  virtual int HGap()
  {
    return (m_hgap);
  }
  virtual void HGap(int gap)
  {
    m_hgap = gap;
  }
  virtual int VGap()
  {
    return (m_vgap);
  }
  virtual void VGap(int gap)
  {
    m_vgap = gap;
  }
  virtual bool Pack();

 protected:
  virtual void CheckControlSize(CWnd *control);
};
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CGrid::CGrid()
{
  RTTI;
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CGrid::CGrid(int rows, int cols, int hgap = 0, int vgap = 0)
{
  Init(rows, cols, hgap, vgap);
  RTTI;
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CGrid::~CGrid()
{
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CGrid::Init(int rows, int cols, int hgap = 0, int vgap = 0)
{
  Columns(cols);
  Rows(rows);
  HGap(hgap);
  VGap(vgap);
  return (true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CGrid::Create(const long chart, const string name, const int subwin,
                   const int x1, const int y1, const int x2, const int y2)
{
  return (CBox::Create(chart, name, subwin, x1, y1, x2, y2));
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CGrid::Pack()
{
  CSize size = Size();
  m_cell_size.cx = (size.cx - ((m_cols + 1) * m_hgap)) / m_cols;
  m_cell_size.cy = (size.cy - ((m_rows + 1) * m_vgap)) / m_rows;
  int x = Left(), y = Top();
  int cnt = 0;
  for(int i = 0; i < ControlsTotal(); i++)
  {
    CWnd *control = Control(i);
    if(control == NULL)
      continue;
    if(control == GetPointer(m_background))
      continue;
    if(cnt == 0 || Right() - (x + m_cell_size.cx) < m_cell_size.cx + m_hgap)
    {
      if(cnt == 0)
        y += m_vgap;
      else
        y += m_vgap + m_cell_size.cy;
      x = Left() + m_hgap;
    }
    else
      x += m_cell_size.cx + m_hgap;
    CheckControlSize(control);
    control.Move(x, y);
    CBox *container = dynamic_cast<CBox *>(control);
    //if(control.Type() == CLASS_LAYOUT)
    if(container)
    {
      container.Pack();
    }
    cnt++;
  }
  return (true);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void CGrid::CheckControlSize(CWnd *control)
{
  control.Size(m_cell_size.cx, m_cell_size.cy);
}
//+------------------------------------------------------------------+

//#include "Layout.mqh"
//+------------------------------------------------------------------+
//|                                                       Layout.mqh |
//|                                    Copyright (c) 2020, Marketeer |
//|                          https://www.mql5.com/en/users/marketeer |
//|                                  GUI Layout declarative language |
//|                           https://www.mql5.com/ru/articles/7734/ |
//|                           https://www.mql5.com/ru/articles/7795/ |
//+------------------------------------------------------------------+

//#include <Marketeer/RubbArray.mqh>
//+------------------------------------------------------------------+
//|                                                    RubbArray.mqh |
//|                                    Copyright (c) 2019, Marketeer |
//|                          https://www.mql5.com/en/users/marketeer |
//|                            https://www.mql5.com/ru/articles/5638 |
//+------------------------------------------------------------------+

template <typename T>
interface Clonable
{
  T clone();
};

template <typename T>
class BaseArray
{
  protected:
    T data[];
    
  public:
    virtual ~BaseArray()
    {
      clear();
    }
    
    virtual void clear()
    {
      ArrayResize(data, 0);
    }
    
    T operator[](int i) const
    {
      return get(i);
    }

    T get(int i) const
    {
      if(i < 0 || i >= ArraySize(data))
      {
        Print("Array size=", ArraySize(data), ", index=", i);
        return NULL;
      }
      return data[i];
    }

    T top() const
    {
      if(ArraySize(data) == 0)
      {
        Print("Array size=0");
        return NULL;
      }
      return data[ArraySize(data) - 1];
    }
    
    T peek() const
    {
      return top();
    }
    
    BaseArray *add(T d)
    {
      int n = ArraySize(data);
      ArrayResize(data, n + 1);
      data[n] = d;
      return &this;
    }

    BaseArray *operator<<(T d)
    {
      return add(d);
    }

    BaseArray *operator<<(const BaseArray<T> *x)
    {
      for(int i = 0; i < x.size(); i++)
      {
        Clonable<T> *clone = dynamic_cast<Clonable<T> *>(x[i]);
        if(clone != NULL)
        {
          add(clone.clone());
        }
        else
        {
          add(x[i]);
        }
      }
      return &this;
    }
    
    BaseArray *push(T d)
    {
      return add(d);
    }

    void operator=(const BaseArray &d)
    {
      int i, n = d.size();
      ArrayResize(data, n);
      for(i = 0; i < n; i++)
      {
        data[i] = d[i];
      }
    }

    T operator>>(int i)
    {
      T d = this[i];
      if(d == NULL) return NULL;
      int n = ArraySize(data) - 1;
      if(i < n)
      {
        ArrayCopy(data, data, i, i + 1);
      }
      ArrayResize(data, n);
      return d;
    }
    
    T pop()
    {
      int _size = ArraySize(data) - 1;
      T d = this[_size];
      ArrayResize(data, _size);
      return d;
    }
    
    int size() const
    {
      return ArraySize(data);
    }
    
    
    string toString() const
    {
      static string formats[4][2] = {{"double", "%f"}, {"long", "%i"}, {"string", "%s"}, {"int", "%i"}};
      string fmt = "%x";
      for(int k = 0; k < ArrayRange(formats, 0); k++)
      {
        if(typename(T) == formats[k][0])
        {
          fmt = formats[k][1];
          break;
        }
      }
      
      int i, n = ArraySize(data);
      string s;
      for(i = 0; i < n; i++)
      {
        s += StringFormat(fmt, data[i]) + ",";
      }
      return (s);
    }
    

};

template <typename T>
class RubbArray: public BaseArray<T>
{
  public:
    RubbArray()
    {
    }

    ~RubbArray()
    {
      clear();
    }
    
    virtual void clear() override
    {
      int i, n = ArraySize(data);
      for(i = 0; i < n; i++)
      {
        if(CheckPointer(data[i]) == POINTER_DYNAMIC) delete data[i];
      }
      ArrayResize(data, 0);
    }
    
    T replace(const int i, T v)
    {
      int n = ArraySize(data);
      if(i < n)
      {
        if(CheckPointer(data[i]) == POINTER_DYNAMIC) delete data[i];
        data[i] = v;
      }
      return v;
    }
    
};

#define List RubbArray
#define Stack RubbArray
//#include "Generators.mqh"
template<typename T>
class Generator
{
  public:
    virtual T operator++() = 0;
};

template<typename T>
class ItemGenerator
{
  public:
    virtual bool addItemTo(T *object) = 0;
};

template<typename T>
class SequenceGenerator: public Generator<T>
{
  protected:
    T current;
    int max;
    int count;

  public:
    SequenceGenerator(const T start = NULL, const int _max = 0): current(start), max(_max), count(0) {}
    virtual void reset(const T start, const int _max = 0)
    {
      current = start;
      count = 0;
      max = _max;
    }

    virtual T operator++() = 0;
};

template<typename T>
class SimpleSequenceGenerator: public SequenceGenerator<T>
{
  public:
    SimpleSequenceGenerator(const T start = NULL, const int _max = 0): SequenceGenerator(start, _max) {}

    virtual T operator++() override
    {
      ulong ul = (ulong)current;
      ul++;
      count++;
      if(count > max) return NULL;
      current = (T)ul;
      return current;
    }
};

union PackedRect
{
  const ulong compound;
  const ushort parts[4];
  PackedRect(const ulong value): compound(value) {}
  PackedRect(const ushort left, const ushort top, const ushort right, const ushort bottom): compound(left | ((ulong)top << 16) | ((ulong)right << 32) | ((ulong)bottom << 48)) {}
  PackedRect(const CRect &r): compound(r.left | ((ulong)r.top << 16) | ((ulong)r.right << 32) | ((ulong)r.bottom << 48)) {}
};

template<typename T>
class ControlProperties
{
  protected:
    T *object;
    string context;

  public:
    ControlProperties(): object(NULL), context(NULL) {}
    ControlProperties(T *ptr): object(ptr), context(NULL) {}
    void assign(T *ptr) { object = ptr; }
    T *get(void) { return object; }
    virtual ControlProperties<T> *operator[](const string property) { context = property; StringToLower(context); return &this; };
    virtual T *operator<=(const bool b) = 0;
    virtual T *operator<=(const ENUM_ALIGN_MODE align) = 0;
    virtual T *operator<=(const color c) = 0;
    virtual T *operator<=(const string s) = 0;
    virtual T *operator<=(const int i) = 0;
    virtual T *operator<=(const long l) = 0;
    virtual T *operator<=(const double d) = 0;
    virtual T *operator<=(const float f) = 0;
    virtual T *operator<=(const datetime d) = 0;
    virtual T *operator<=(const PackedRect &r) = 0;
    // TODO: ushort for margins?
};


class LayoutData
{
  protected:
    static RubbArray<LayoutData *> stack;
    static string rootId;
    int _x1, _y1, _x2, _y2;
    string _id;
  
  public:
    LayoutData()
    {
      _x1 = _y1 = _x2 = _y2 = 0;
      _id = NULL;
    }
};

enum STYLER_PHASE
{
  STYLE_PHASE_BEFORE_INIT,
  STYLE_PHASE_AFTER_INIT
};


template<typename C>
class LayoutStyleable
{
  public:
    virtual void apply(C *control, const STYLER_PHASE phase) {};
};


template<typename C>
class LayoutCache
{
  protected:
    C *cache[];   // autocreated controls and boxes

  public:
    ~LayoutCache()
    {
      for(int i = 0; i < ArraySize(cache); i++)
      {
        if(CheckPointer(cache[i]) == POINTER_DYNAMIC) delete cache[i];
      }
    }

    virtual LayoutStyleable<C> *getStyler() const
    {
      return NULL;
    }

    virtual void save(C *control)
    {
      const int n = ArraySize(cache);
      ArrayResize(cache, n + 1);
      cache[n] = control;
    }

    virtual C *get(const string name) = 0;

    virtual C *get(const long m)
    {
      if(m < 0 || m >= ArraySize(cache)) return NULL;
      return cache[(int)m];
    }

    virtual bool find(C *control, const int excludeLast = 0) const
    {
      for(int i = 0; i < ArraySize(cache) - excludeLast; i++) // excluding just added (latest element)
      {
        if(cache[i] == control)
        {
          return true;
        }
      }
      return false;
    }

    virtual int indexOf(C *control)
    {
      for(int i = 0; i < ArraySize(cache); i++)
      {
        if(cache[i] == control)
        {
          return i;
        }
      }
      return -1;
    }

    virtual C *findParent(C *control) const = 0;

    virtual bool revoke(C *control) = 0;
    
    virtual int cacheSize()
    {
      return ArraySize(cache);
    }
    
    virtual bool onEvent(const int event, C *control)
    {
      return false;
    }
};


template<typename P,typename C>
class LayoutBase: public LayoutData
{
  protected:
    P *container; // not null if container (can be used as flag)
    C *object;
    C *array[];
    int shadow;   // set to 1 during creation (when last cached object not yet registered)
    
    static LayoutCache<C> *cacher;

  public:
    LayoutBase(): container(NULL), object(NULL), shadow(0) {}

    C *get()
    {
      return object;
    }

    C *operator[](const int i = 0) const
    {
      if(object != NULL) return object;
      if(i < 0 || i >= ArraySize(array)) return NULL;
      return array[i];
    }

    int size() const
    {
      return ArraySize(array);
    }

    static void setCache(LayoutCache<C> *c)
    {
      cacher = c;
    }
  
  protected:
    virtual bool setContainer(C *control) = 0;

    virtual string create(C *object, const string id = NULL) = 0;
    virtual void add(C *object) = 0;
    virtual string getRootId(const string id) = 0;

    virtual bool save(C *control)
    {
      if(cacher == NULL)
      {
        Print("Before first implicit layout object created please assign a LayoutCache storage");
        return false;
      }
      cacher.save(control);
      return true;
    }
    
    // nonbound layout, control T is implicitly stored in internal cache
    template<typename T>
    T *init(const string name, const int m = 1, const int x1 = 0, const int y1 = 0, const int x2 = 0, const int y2 = 0)
    {
      if(m > 1)
      {
        ArrayResize(array, m);
        object = NULL;
        container = NULL;
      }
      T *temp = NULL;
      for(int i = 0; i < m; i++)
      {
        temp = new T();
        if(save(temp))
        {
          if(m > 1) array[i] = temp;
          shadow = 1;
          init(temp, name + (m > 1 ? (string)(i + 1) : ""), x1, y1, x2, y2);
          shadow = 0;
        }
        else return NULL;
      }
      return temp;
    }
    
    // bound layout, with explicit control object passed as reference/pointer
    template<typename T>
    void init(T *ref, const string id = NULL, const int x1 = 0, const int y1 = 0, const int x2 = 0, const int y2 = 0)
    {
      if(ArraySize(array) == 0)
      {
        setContainer(ref);
        object = ref;
      }
      _x1 = x1;
      _y1 = y1;
      _x2 = x2;
      _y2 = y2;
      if(stack.size() > 0)
      {
        if(_x1 == 0 && _y1 == 0 && _x2 == 0 && _y2 == 0)
        {
          _x1 = stack.top()._x1;
          _y1 = stack.top()._y1;
          _x2 = stack.top()._x2;
          _y2 = stack.top()._y2;
        }

        _id = rootId + (id == NULL ? typename(T) + StringFormat("%d", ref) : id);
      }
      else
      {
        _id = (id == NULL ? typename(T) + StringFormat("%d", ref) : id);
      }
      
      bool existing = false;
      
      if(cacher != NULL && cacher.find(ref, shadow))
      {
        // this object exists in the cache, no need to create again
        // can be a dynamic modification of the dialog
        existing = true;
      }
      
      // FIXME: this is a hack because rootId behaviour is implementation specific for standard library
      
      // this 'if' is used for the case, when a blank form was created on start
      // (it's not in the cache since it's automatic (it's a rule so far)), and then
      // new elements are added ad-hoc, so the form re-creation should be skipped,
      // but it's needed on the stack
      else if(rootId == _id) // the dialog already created
      {
        existing = true;
      }
      else // normal workflow branch
      {
        _id = create(ref);
      }
      
      if(stack.size() == 0)
      {
        rootId = getRootId(_id);
      }
      if(container)
      {
        stack << &this;
      }

      if(cacher != NULL && !existing)
      {
        LayoutStyleable<C> *styler = cacher.getStyler();
        if(styler != NULL)
        {
          styler.apply(ref, STYLE_PHASE_BEFORE_INIT);
        }
      }
      
      if(ArraySize(array) > 0)
      {
        LayoutBase *up = stack.size() > 0 ? stack.top() : NULL;
        if(up != NULL && up.container != NULL)
        {
          up.add(ref);
        }
      }
    }
    
    // array of explicitly defined controls of type T, bound to the single layout object
    // only simple controls (not containers) are allowed to init via arrays
    template<typename T>
    void init(T &refs[], const string id = NULL, const int x1 = 0, const int y1 = 0, const int x2 = 0, const int y2 = 0)
    {
      object = NULL;
      container = NULL;
      _x1 = x1;
      _y1 = y1;
      _x2 = x2;
      _y2 = y2;
      if(stack.size() > 0)
      {
        if(_x1 == 0 && _y1 == 0 && _x2 == 0 && _y2 == 0)
        {
          _x1 = stack.top()._x1;
          _y1 = stack.top()._y1;
          _x2 = stack.top()._x2;
          _y2 = stack.top()._y2;
        }
        _id = rootId + (id == NULL ? typename(T) + StringFormat("%d", &refs[0]) : id) + "_";
      }
      else
      {
        _id = (id == NULL ? typename(T) + StringFormat("%d", &refs[0]) : id) + "_";
      }
      
      LayoutStyleable<C> *styler = cacher != NULL ? cacher.getStyler() : NULL;
      
      int size = ArraySize(refs);
      ArrayResize(array, size);
      LayoutBase *up = stack.size() > 0 ? stack.top() : NULL;
      for(int i = 0; i < size; i++)
      {
        create(&refs[i], _id + (string)(i + 1));

        if(styler != NULL)
        {
          styler.apply(&refs[i], STYLE_PHASE_BEFORE_INIT);
        }

        if(up != NULL && up.container != NULL)
        {
          up.add(&refs[i]);
        }

        array[i] = &refs[i];
      }
    }
    
    ~LayoutBase()
    {
      if(container)
      {
        stack.pop();
      }
      
      if(object)
      {
        // FIXME: should not call styler for "old" objects
        if(cacher != NULL)
        {
          LayoutStyleable<C> *styler = cacher.getStyler();
          if(styler != NULL)
          {
            styler.apply(object, STYLE_PHASE_AFTER_INIT);
          }
        }

        LayoutBase *up = stack.size() > 0 ? stack.top() : NULL;
        if(up != NULL && up.container != NULL)
        {
          up.add(object);
        }
      }
      
      if(ArraySize(array) > 0 && cacher != NULL)
      {
        LayoutStyleable<C> *styler = cacher.getStyler();
        for(int i = 0; i < ArraySize(array); i++)
        {
          styler.apply(array[i], STYLE_PHASE_AFTER_INIT);
        }
      }
      
      if(stack.size() == 0 && cacher != NULL)
      {
        cacher = NULL;
      }
    }

    template<typename V>
    LayoutBase<P,C> *operator<=(const V value) // template function cannot be virtual
    {
      Print("Please, override " , __FUNCSIG__, " in your concrete Layout class");
      return &this;
    }
    
    virtual LayoutBase<P,C> *operator<=(const PackedRect &r) // optional
    {
      Print("Please, override " , __FUNCSIG__, " in your concrete Layout class");
      return &this;
    }

    virtual LayoutBase<P,C> *operator[](const string prop) // optional
    {
      Print("Please, override " , __FUNCSIG__, " in your concrete Layout class");
      return &this;
    }
};

template<typename P,typename C>
static LayoutCache<C> *LayoutBase::cacher = NULL;


static RubbArray<LayoutData *> LayoutData::stack;
static string LayoutData::rootId;



template<typename T>
class StdControlProperties: public ControlProperties<T>
{
  public:
    StdControlProperties(): ControlProperties() {}
    StdControlProperties(T *ptr): ControlProperties(ptr) {}

    // we need dynamic_cast throughout below, because control classes
    // in the standard library does not provide a set of common virtual methods
    // to assign specific properties for all of them (for example, readonly
    // is available for edit field only)
    virtual T *operator<=(const bool b) override
    {
      if(StringFind(context, "enable") > -1)
      {
        if(b) object.Enable();
        else  object.Disable();
      }
      else
      if(StringFind(context, "visible") > -1)
      {
        object.Visible(b);
      }
      else
      {
        CEdit *edit = dynamic_cast<CEdit *>(object);
        if(edit != NULL) edit.ReadOnly(b);
        
        CButton *button = dynamic_cast<CButton *>(object);
        if(button != NULL) button.Locking(b);
      }
      
      return object;
    }

    virtual T *operator<=(const ENUM_ALIGN_MODE align) override
    {
      CEdit *edit = dynamic_cast<CEdit *>(object);
      if(edit != NULL) edit.TextAlign(align);
      return object;
    }
    
    virtual T *operator<=(const color c) override
    {
      CWndObj *ctrl = dynamic_cast<CWndObj *>(object);
      if(ctrl != NULL)
      {
        if(StringFind(context, "background") > -1)
        {
          ctrl.ColorBackground(c);
        }
        else if(StringFind(context, "border") > -1)
        {
          ctrl.ColorBorder(c);
        }
        else // default
        {
          ctrl.Color(c);
        }
      }
      else
      {
        CWndClient *client = dynamic_cast<CWndClient *>(object);
        if(client != NULL)
        {
          if(StringFind(context, "border") > -1)
          {
            client.ColorBorder(c);
          }
          else
          {
            client.ColorBackground(c);
          }
        }
      }
      return object;
    }

    virtual T *operator<=(const string s) override
    {
      CWndObj *ctrl = dynamic_cast<CWndObj *>(object);
      if(ctrl != NULL)
      {
        if(StringFind(context, "font") > -1)
        {
          ctrl.Font(s);
        }
        else // default
        {
          ctrl.Text(s);
        }
      }
      else
      {
        CCheckBox *check = dynamic_cast<CCheckBox *>(object);
        if(check != NULL) check.Text(s);
        else
        {
          CRadioButton *radio = dynamic_cast<CRadioButton *>(object);
          if(radio != NULL) radio.Text(s);
        }
      }
      return object;
    }

    virtual T *operator<=(const int i) override
    {
      if(StringFind(context, "width") > -1)
      {
        object.Width(i);
      }
      else
      if(StringFind(context, "height") > -1)
      {
        object.Height(i);
      }
      else
      if(StringFind(context, "margin") > -1)
      {
        object.Margins(i, i, i, i);
      }
      else
      if(StringFind(context, "left") > -1)
      {
        CRect r = object.Margins();
        object.Margins(i, r.top, r.right, r.bottom);
      }
      else
      if(StringFind(context, "top") > -1)
      {
        CRect r = object.Margins();
        object.Margins(r.left, i, r.right, r.bottom);
      }
      else
      if(StringFind(context, "right") > -1)
      {
        CRect r = object.Margins();
        object.Margins(r.left, r.top, i, r.bottom);
      }
      else
      if(StringFind(context, "bottom") > -1)
      {
        CRect r = object.Margins();
        object.Margins(r.left, r.top, r.right, i);
      }
      else
      if(StringFind(context, "align") > -1)
      {
        object.Alignment(i);
      }
      else
      if(StringFind(context, "fontsize") > -1)
      {
        CWndObj *ctrl = dynamic_cast<CWndObj *>(object);
        if(ctrl != NULL)
        {
          ctrl.FontSize(i);
        }
      }
      else // default
      {
        CSpinEdit *spin = dynamic_cast<CSpinEdit *>(object);
        if(spin != NULL)
        {
          if(StringFind(context, "min") > -1)
          {
            spin.MinValue(i);
          }
          else
          if(StringFind(context, "max") > -1)
          {
            spin.MaxValue(i);
          }
          else
          {
            spin.Value(i);
          }
        }
        else
        {
          CComboBox *combo = dynamic_cast<CComboBox *>(object);
          if(combo != NULL)
          {
            combo.Select(i);
          }
        }
      }
      return object;
    }
    
    virtual T *operator<=(const datetime d) override
    {
      CDatePicker *date = dynamic_cast<CDatePicker *>(object);
      if(date != NULL)
      {
        date.Value(d);
      }
      return object;
    }

    virtual T *operator<=(const long l) override
    {
      if(StringFind(context, "zorder") > -1)
      {
        CWndObj *ctrl = dynamic_cast<CWndObj *>(object);
        if(ctrl != NULL)
        {
          ctrl.ZOrder(l);
        }
      }
      else
      {
        object.Id(l);
      }
      return object;
    }

    virtual T *operator<=(const float f) override
    {
      const int margin = (int)f;
      
      CGrid *grid = dynamic_cast<CGrid *>(object);
      if(grid != NULL)
      {
        if(StringFind(context, "left") > -1
        || StringFind(context, "right") > -1)
        {
          grid.HGap(margin);
        }
        else
        if(StringFind(context, "top") > -1
        || StringFind(context, "bottom") > -1)
        {
          grid.VGap(margin);
        }
        else
        {
          grid.HGap(margin);
          grid.VGap(margin);
        }
      }
      else
      {
        CBox *box = dynamic_cast<CBox *>(object);
        if(box != NULL)
        {
          if(StringFind(context, "left") > -1)
          {
            box.PaddingLeft(margin);
          }
          else
          if(StringFind(context, "top") > -1)
          {
            box.PaddingTop(margin);
          }
          else
          if(StringFind(context, "right") > -1)
          {
            box.PaddingRight(margin);
          }
          else
          if(StringFind(context, "bottom") > -1)
          {
            box.PaddingBottom(margin);
          }
          else
          {
            // wrap this container's content with padding
            // (don't use it side by side with standard alignment!)
            box.Padding(margin, margin, margin, margin);
          }
        }
      }

      return object;
    }

    virtual T *operator<=(const double d) override
    {
      const int margin = (int)d;

      // align this control inside its container
      object.Margins(margin, margin, margin, margin);

      return object;
    }

    virtual T *operator<=(const PackedRect &r) override
    {
      object.Margins(r.parts[0], r.parts[1], r.parts[2], r.parts[3]);
      return object;
    }
    
    virtual T *operator<=(const ENUM_WND_ALIGN_FLAGS align)
    {
      object.Alignment(align);
      return object;
    }
    
    virtual T *operator<=(const LAYOUT_STYLE style)
    {
      CBox *box = dynamic_cast<CBox *>(object);
      if(box != NULL)
      {
        box.LayoutStyle(style);
      }
      return object;
    }
    
    virtual T *operator<=(const VERTICAL_ALIGN v)
    {
      CBox *box = dynamic_cast<CBox *>(object);
      if(box != NULL)
      {
        box.VerticalAlign(v);
      }
      return object;
    }
    
    virtual T *operator<=(const HORIZONTAL_ALIGN h)
    {
      CBox *box = dynamic_cast<CBox *>(object);
      if(box != NULL)
      {
        box.HorizontalAlign(h);
      }
      return object;
    }
};


class StdLayoutStyleable: public LayoutStyleable<CWnd>
{
  public:
    virtual void apply(CWnd *control, const STYLER_PHASE phase) = 0;
};


// CWnd implementation specific
class StdLayoutCache: public LayoutCache<CWnd>
{
  protected:
  public:
    static bool StringEndsWith(const string text, const string suffix)
    {
      if(StringLen(text) == 0) return StringLen(suffix) == 0;
      const int pos = StringFind(text, suffix);
      return pos == 5 && pos == StringLen(text) - StringLen(suffix); // this relies on 5-digit instance id!
    }

    virtual CWnd *get(const string name) override
    {
      const int n = ArraySize(cache);
      for(int i = 0; i < n; i++)
      {
        if(StringEndsWith(cache[i].Name(), name)) return cache[i];
      }
      return NULL;
    }

    virtual CWnd *get(const long m) override
    {
      if(m < 0)
      {
        for(int i = 0; i < ArraySize(cache); i++)
        {
          if(cache[i].Id() == -m) return cache[i];
          CWndContainer *container = dynamic_cast<CWndContainer *>(cache[i]);
          if(container != NULL)
          {
            for(int j = 0; j < container.ControlsTotal(); j++)
            {
              if(container.Control(j).Id() == -m) return container.Control(j);
            }
          }
        }
        return NULL;
      }
      else if(m >= ArraySize(cache)) return NULL;
      return cache[(int)m];
    }

    virtual CWnd *findParent(CWnd *control) const override
    {
      for(int i = 0; i < ArraySize(cache); i++)
      {
        CWndContainer *container = dynamic_cast<CWndContainer *>(cache[i]);
        if(container != NULL)
        {
          for(int j = 0; j < container.ControlsTotal(); j++)
          {
            if(container.Control(j) == control)
            {
              return container;
            }
          }
        }
      }
      return NULL;
    }
    
    virtual bool revoke(CWnd *control) override
    {
      static CWnd dummy;
      for(int i = 0; i < ArraySize(cache); i++)
      {
        if(cache[i] == control)
        {
          CWndContainer *container = dynamic_cast<CWndContainer *>(control);
          if(container != NULL)
          {
            for(int j = 0; j < container.ControlsTotal(); j++)
            {
              revoke(container.Control(j));
            }
          }
          // do not delete objects here, since they belong to their respective
          // parent controls/windows and are deleted from there
          // if(CheckPointer(control) == POINTER_DYNAMIC) delete control;
          cache[i] = &dummy;
          return true;
        }
      }
      return false;
    }
    
    void print()
    {
      for(int i = 0; i < ArraySize(cache); i++)
      {
        CWnd *wnd = cache[i];
        Print(wnd._rtti, " / ", wnd.Name(), " ", wnd.Id(), " F:", wnd.StateFlags());
      }
    }
};


class StdLayoutBase: public LayoutBase<CWndContainer,CWnd>
{
  public:
    virtual string getRootId(const string id) override
    {
      return StringSubstr(id, 0, 5);
    }

    virtual bool setContainer(CWnd *control) override
    {
      CDialog *dialog = dynamic_cast<CDialog *>(control);
      CBox *box = dynamic_cast<CBox *>(control);
      if(dialog != NULL)
      {
        container = dialog;
      }
      else if(box != NULL)
      {
        container = box;
      }
      return true;
    }

    virtual string create(CWnd *child, const string id = NULL) override
    {
      child.Create(ChartID(), id != NULL ? id : _id, 0, _x1, _y1, _x2, _y2);
      if(cacher != NULL)
      {
        child.Id(cacher.cacheSize() - 1);
      }
      return child.Name();
    }

    virtual void add(CWnd *child) override
    {
      CDialog *dlg = dynamic_cast<CDialog *>(container);
      if(dlg != NULL)
      {
        dlg.Add(child);
      }
      else
      {
        CWndContainer *ptr = dynamic_cast<CWndContainer *>(container);
        if(ptr != NULL)
        {
          ptr.Add(child);
        }
        else
        {
          Print("Can't add ", child.Name(), " to ", container.Name());
        }
      }
    }

    ~StdLayoutBase()
    {
    }
};

template<typename T>
class _layout: public StdLayoutBase
{
  protected:
    StdControlProperties<T> wrapper;

  public:
    _layout(const string id)
    {
      T *ptr = init<T>(id);
      wrapper.assign(ptr);
      wrapper <= id;
    }

    _layout(const string id, const int n)
    {
      T *ptr = init<T>(id, n, 0, 0, 0, 0);
    }

    _layout(const string id, const int dx, const int dy)
    {
      T *ptr = init<T>(id, 1, 0, 0, dx, dy);
      wrapper.assign(ptr);
      wrapper <= id;
    }

    _layout(const string id, const int x1, const int y1, const int x2, const int y2)
    {
      T *ptr = init<T>(id, 1, x1, y1, x2, y2);
      wrapper.assign(ptr);
      wrapper <= id;
    }

    template<typename V>
    _layout(const string id, const int dx, const int dy, const V value)
    {
      T *ptr = init<T>(id, 1, 0, 0, dx, dy);
      wrapper.assign(ptr);
      wrapper <= value;
    }
    
    _layout(T &ref, const string id = NULL)
    {
      init(&ref, id, 0, 0, 0, 0);
      wrapper.assign(&ref);
      if(id != NULL) wrapper <= id;
    }

    _layout(T *ptr, const string id = NULL)
    {
      init(ptr, id, 0, 0, 0, 0);
      wrapper.assign(ptr);
      if(id != NULL) wrapper <= id;
    }

    template<typename V>
    _layout(T &ref, const string id, const int dx, const int dy, const V value)
    {
      init(&ref, id, 0, 0, dx, dy);
      wrapper.assign(&ref);
      wrapper <= value;
    }

    _layout(T &ref, const string id, const int dx, const int dy)
    {
      init(&ref, id, 0, 0, dx, dy);
      wrapper.assign(&ref);
      wrapper <= id;
    }

    _layout(T *ptr, const string id, const int dx, const int dy)
    {
      init(ptr, id, 0, 0, dx, dy);
      wrapper.assign(ptr);
      wrapper <= id;
    }

    _layout(T &ref, const string id, const int x1, const int y1, const int x2, const int y2)
    {
      init(&ref, id, x1, y1, x2, y2);
      wrapper.assign(&ref);
      wrapper <= id;
    }
    
    _layout(T *ptr, const string id, const int x1, const int y1, const int x2, const int y2)
    {
      init(ptr, id, x1, y1, x2, y2);
      wrapper.assign(ptr);
      wrapper <= id;
    }

    _layout(T &refs[], const string id, const int x1, const int y1, const int x2, const int y2)
    {
      init(refs, id, x1, y1, x2, y2);
    }
      
    _layout(T &refs[], const string id = NULL)
    {
      init(refs, id, 0, 0, 0, 0);
    }

    template<typename V>
    _layout<T> *operator<=(const V value) // overrides base class method
    {
      if(object != NULL)
      {
        wrapper <= value;
      }
      else
      {
        for(int i = 0; i < ArraySize(array); i++)
        {
          wrapper.assign(array[i]);
          wrapper <= value;
        }
      }
      return &this;
    }

    virtual _layout<T> *operator[](const string prop) override
    {
      wrapper[prop];
      return &this;
    }

    virtual _layout<T> *operator<=(const PackedRect &r) override
    {
      if(object != NULL)
      {
        wrapper <= r;
      }
      else
      {
        for(int i = 0; i < ArraySize(array); i++)
        {
          wrapper.assign(array[i]);
          wrapper <= r;
        }
      }
      return &this;
    }

    // the following methods are specific to StdLayoutBase

    template<typename V>
    _layout<T> *operator<=(SequenceGenerator<V> &gen)
    {
      if(object == NULL)
      {
        for(int i = 0; i < ArraySize(array); i++)
        {
          wrapper.assign(array[i]);
          wrapper <= ++gen;
        }
      }
      return &this;
    }

    _layout<T> *operator<(ItemGenerator<T> *gen)
    {
      while(gen.addItemTo(object));
      if(CheckPointer(gen) == POINTER_DYNAMIC) delete gen;
      return &this;
    }

    _layout<T> *operator<=(ItemGenerator<T> &gen)
    {
      while(gen.addItemTo(object));
      return &this;
    }

    template<typename V>
    void attach(StdValue<V> *v)
    {
      ((T *)object).bind(v);
    }

    template<typename V>
    void attach(StdValue<V> &a[])
    {
      if(ArraySize(array) == ArraySize(a))
      {
        for(int i = 0; i < ArraySize(a); i++)
        {
          ((T *)array[i]).bind(&a[i]);
        }
      }
      else
      {
        Print("Can't bind arrays in ", typename(this));
      }
    }
};


template<typename T>
class StdItemGenerator: public ItemGenerator<T>
{
  protected:
    long maximum;
    long index;
    string prefix;

  public:
    StdItemGenerator(const long max, const string customText = NULL): index(0), maximum(max), prefix(customText) {}
    
    virtual long index2value()
    {
      return index;
    }

    virtual bool addItemTo(T *object) override
    {
      object.AddItem((prefix != NULL ? prefix : typename(T)) + (string)index, index2value());
      index++;
      return index < maximum;
    }
};

template<typename T>
class StdGroupItemGenerator: public StdItemGenerator<T>
{
  public:
    StdGroupItemGenerator(const long max, const string customText = NULL): StdItemGenerator(max, customText) {}

    virtual long index2value() override
    {
      return 1 << index;
    }
};

template<typename T>
class SymbolsItemGenerator: public ItemGenerator<T>
{
  protected:
    long index;

  public:
    SymbolsItemGenerator(): index(0) {}

    virtual bool addItemTo(T *object) override
    {
      object.AddItem(SymbolName((int)index, true), index);
      index++;
      return index < SymbolsTotal(true);
    }
};

template<typename T,typename V>
class ArrayItemGenerator: public ItemGenerator<T>
{
  protected:
    V data[];
    int index;
    bool bitmask;

  public:
    ArrayItemGenerator(const V &array[], const bool group = false): index(0), bitmask(group)
    {
      ArrayCopy(data, array);
    }

    virtual bool addItemTo(T *object)
    {
      if(index < ArraySize(data))
      {
        object.AddItem((string)data[index], (bitmask ? 1 << index : index));
        index++;
        return true;
      }
      return false;
    }
};
//#include <Layouts/SpinEditResizable.mqh>
//+------------------------------------------------------------------+
//|                                            SpinEditResizable.mqh |
//|                                    Copyright (c) 2019, Marketeer |
//|                          https://www.mql5.com/en/users/marketeer |
//+------------------------------------------------------------------+

//#include <ControlsPlus/SpinEdit.mqh>

class SpinEditResizable: public CSpinEdit
{
  public:
    SpinEditResizable()
    {
      RTTI;
    }
    virtual bool OnResize(void) override
    {
      m_edit.Width(Width());
      m_edit.Height(Height());
      
      int x1 = Width() - (CONTROLS_BUTTON_SIZE + CONTROLS_SPIN_BUTTON_X_OFF);
      int y1 = (Height() - 2 * CONTROLS_SPIN_BUTTON_SIZE) / 2;
      m_inc.Move(Left() + x1, Top() + y1);
      
      x1 = Width() - (CONTROLS_BUTTON_SIZE + CONTROLS_SPIN_BUTTON_X_OFF);
      y1 = (Height() - 2 * CONTROLS_SPIN_BUTTON_SIZE) / 2 + CONTROLS_SPIN_BUTTON_SIZE;
      m_dec.Move(Left() + x1, Top() + y1);

      return CWndContainer::OnResize();
    }
};
//#include <Layouts/Sort.mqh>
class SORT
{
  private:
    template<typename T>
    static void Swap(T &Array[], const int i, const int j)
    {
      const T Temp = Array[i];
  
      Array[i] = Array[j];
      Array[j] = Temp;
    }
  
    template<typename T1, typename T2>
    static int Partition(T1 &Array[], const T2 &Compare, const int Start, const int End)
    {
      int Marker = Start;
  
      for(int i = Start; i <= End; i++)
      {
        if(Compare.Compare(Array[i], Array[End]) <= 0)
        {
          Swap(Array, i, Marker);
  
          Marker++;
        }
      }
  
      return(Marker - 1);
    }
  
    template<typename T1, typename T2>
    static void QuickSort(T1 &Array[], const T2 &Compare, const int Start, const int End)
    {
      if(Start < End)
      {
        const int Pivot = Partition(Array, Compare, Start, End);
  
        QuickSort(Array, Compare, Start, Pivot - 1);
        QuickSort(Array, Compare, Pivot + 1, End);
      }
    }
  
  public:
    /* MQL-like ArraySort:
         void&       array[]                   // array to sort
         void&       Compare                   // comparator
         int         count = WHOLE_ARRAY,      // number of elements
         int         start = 0,                // starting index
    */
    template<typename T1, typename T2>
    static void Sort(T1 &Array[], const T2 &Compare, int Count = WHOLE_ARRAY, const int Start = 0)
    {
      if(Count == WHOLE_ARRAY)
        Count = ArraySize(Array);
  
        QuickSort(Array, Compare, Start, Start + Count - 1);
    }
};

template<typename T>
class COMPARE
{
  protected:
    int Direction;
  
  public:
    COMPARE(const int iMode = +1)
    {
      Direction = iMode;
    }
  
    virtual int Compare(const T &First, const T &Second) const
    {
      return 0;
    }
};

template<typename T>
class DefaultCompare: public COMPARE<T>
{
  public:
    DefaultCompare(const int iMode = +1): COMPARE(iMode) {}
  
    virtual int Compare(const T &First, const T &Second) const override
    {
      return (First > Second) ? Direction : -Direction;
    }
};


#define TAB_CURRENT   -1
#define TAB_CHARTS     0
#define TAB_INDICATORS 1
#define TAB_EXPERTS    2
#define TAB_SCRIPTS    3

const static string tabs[4] = {"Charts", "Indicators", "Experts", "Scripts"};

string period2string(const ENUM_TIMEFRAMES tf)
{
  const static int plen = StringLen("PERIOD_");
  return StringSubstr(EnumToString(tf), plen);
}

template<typename T>
int push(T &results[], const T &text)
{
  const int n = ArraySize(results);
  ArrayResize(results, n + 1);
  results[n] = text;
  return n;
}

struct Pair
{
  public:
    Pair() {}
    Pair(const string S, const long L): s(S), id(L) {}
    string s;
    long id;
};

class ComparePairs : public COMPARE<Pair>
{
  public:
    int Compare(const Pair &First, const Pair &Second) const
    {
      return Second.s < First.s ? +1 : (Second.s == First.s ? 0 : -1);
    }
};

int listCharts(const int type, string &results[], long &ids[])
{
  const string gap = "    ";
  const long me = ChartID();
  long id = ChartFirst();
  int count = 0, used = 0, temp, experts = 0, scripts = 0, indicators = 0, subs = 0;
  
  Pair sorter[];
  
  while(id != -1)
  {
    temp = 0;
    const int win = (int)ChartGetInteger(id, CHART_WINDOWS_TOTAL);

    // props: symbol, period, expert, script, main window indicators, subwindow indicators
    
    string header = StringFormat("%s %s %s %s", ChartSymbol(id), period2string(ChartPeriod(id)), (win > 1 ? (string)(win - 1) : ""), (id == me ? " *" : ""));
    
    string expert = ChartGetString(id, CHART_EXPERT_NAME);
    string script = ChartGetString(id, CHART_SCRIPT_NAME);
    if(expert != NULL || script != NULL)
    {
      if(expert != NULL)
      {
        experts++;
        if(type == 0) header += "\n" + gap + "[E] " + expert;
        else if(type == 2)
        {
          expert += "\n" + gap + header;
          push(sorter, Pair(expert, id));
        }
      }
      if(script != NULL)
      {
        scripts++;
        if(type == 0) header += "\n" + gap + "[S] " + script;
        else if(type == 3)
        {
          script += "\n" + gap + header;
          push(sorter, Pair(script, id));
        }
      }
      temp++;
    }
    
    for(int i = 0; i < win; i++)
    {
      const int n = ChartIndicatorsTotal(id, i);
      for(int k = 0; k < n; k++)
      {
        string ind = StringFormat("%s <%d;%d>", ChartIndicatorName(id, i, k), i, k);
        if(type == 0) header += "\n" + gap + "[I] " + ind;
        else if(type == 1)
        {
          ind += "\n" + gap + header;
          push(sorter, Pair(ind, id));
        }
        indicators++;
        if(i > 0) subs++;
        temp++;
      }
    }
    
    if(type == 0)
    {
      push(sorter, Pair(header, id));
    }
    
    count++;
    if(temp > 0)
    {
      used++;
    }
    id = ChartNext(id);
  }

  ComparePairs cmp;
  SORT::Sort(sorter, cmp);
  const int n = ArraySize(sorter);
  ArrayResize(results, n);
  ArrayResize(ids, n);
  
  for(int i = 0; i < n; i++)
  {
    results[i] = sorter[i].s;
    ids[i] = sorter[i].id;
  }

  //Print("Total charts number: ", count, ", with MQL-programs: ", used);
  //Print("Experts: ", experts, ", Scripts: ", scripts, ", Indicators: ", indicators, " (main: ", indicators - subs, " / sub: ", subs, ")");
  return n;
}

class TabButton: public CButton
{
  public:
    virtual bool OnEnable(void) override
    {
      m_button.Z_Order(0);
      ColorBackground(CONTROLS_BUTTON_COLOR_BG);
      return true;
    }

    virtual bool OnDisable(void) override
    {
      m_button.Z_Order(-100);
      ColorBackground(CONTROLS_LISTITEM_COLOR_BG);
      return true;
    }
};


class ChartBrowserForm;

class DefaultLayoutStyleable: public StdLayoutStyleable
{
  public:
    virtual void apply(CWnd *control, const STYLER_PHASE phase) override
    {
      CButton *button = dynamic_cast<CButton *>(control);
      if(button != NULL)
      {
        button.ColorBorder(CONTROLS_LISTITEM_COLOR_BG);
      }
    }
};

class MyStdLayoutCache: public StdLayoutCache
{
  protected:
    ChartBrowserForm *parent;
    DefaultLayoutStyleable styler;
    TabButton *group[];
    ListViewResizable *list;
    int currentTab;

  public:
    MyStdLayoutCache(ChartBrowserForm *owner): parent(owner), list(NULL), currentTab(-1) {}

    virtual bool onEvent(const int event, CWnd *control) override
    {
      if(control != NULL)
      {
        // debug
        // Print(control._rtti, " / ", control.Name(), " / ", event);
        // CWndContainer *container = dynamic_cast<CWndContainer *>(findParent(control));
        // if(container != NULL)
        // Print(container._rtti, " / ", container.Name());
        TabButton *button = dynamic_cast<TabButton *>(control);
        if(button != NULL)
        {
          const int cmd = adjustGroup(button);
          fillList(cmd);
          return true;
        }
        else
        {
          CButton *go = dynamic_cast<CButton *>(control);
          if(go != NULL)
          {
            const long index = list.Value();
            // Print("Selected:", index);
            if(index == LONG_MAX)
            {
              if(list.Count() > 0)
              {
                MessageBox("Please, select an item in the list");
              }
              else
              {
                MessageBox("Nothing to select here");
              }
            }
            else
            {
              ChartSetInteger(list.Value(), CHART_BRING_TO_TOP, true);
              ChartGetInteger(list.Value(), CHART_WINDOW_HANDLE);
            }
          }
        }
      }
      return false; // not processed here, so give a chance to process in other handlers
    }

    virtual StdLayoutStyleable *getStyler() const override
    {
      return (StdLayoutStyleable *)&styler;
    }
    
    // custom stuff
    
    void registerList(ListViewResizable *ptr)
    {
      list = ptr;
    }
    
    void registerGroupButton(TabButton *ptr)
    {
      const int n = ArraySize(group);
      ArrayResize(group, n + 1);
      group[n] = ptr;
    }
  
  private:  
    int adjustGroup(TabButton *pressed)
    {
      const int n = ArraySize(group);
      int index = -1;
      for(int i = 0; i < n; i++)
      {
        if(group[i] == pressed)
        {
          pressed.Disable();
          index = i;
        }
        else group[i].Enable();
      }
      return index;
    }
    
    int expand(const string &a1[], const long &ids[], string &a2[], long &sdi[])
    {
      string lines[];
      const int n = ArraySize(a1);
      for(int i = 0; i < n; i++)
      {
        const int m = StringSplit(a1[i], '\n', lines);
        long temp[];
        ArrayResize(temp, m);
        ArrayFill(temp, 0, m, ids[i]);
        ArrayCopy(a2, lines, ArraySize(a2), 0, m);
        ArrayCopy(sdi, temp, ArraySize(sdi), 0, m);
      }
      return ArraySize(a2);
    }
  
  public:
    void fillList(const int type = TAB_CURRENT)
    {
      if(type != TAB_CURRENT)
      {
        currentTab = type;
      }
    
      string results[];
      long ids[];
      const int t = listCharts(currentTab, results, ids);
      
      parent.Caption(DIALOG_TITLE + ":" + (string)t + " " + tabs[currentTab]);
      
      string expanded[];
      long charts[];
      const int n = expand(results, ids, expanded, charts);
      list.ItemsClear();
      for(int i = 0; i < n; i++)
      {
        list.ItemAdd(expanded[i], charts[i]);
      }
      list.adjustVSize();
      //list.forceVScroll();
    }
};


//+-----------------------------------------------------------------------+
//| Main dialog window with controls                                      |
//+-----------------------------------------------------------------------+
class ChartBrowserForm: public AppDialogResizable
{
  private:
    MyStdLayoutCache *cache;
    CBox *pMain;
    
  public:
    ChartBrowserForm(void);
    ~ChartBrowserForm(void);

    bool CreateLayout(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2);
    
    // general handler for event map
    virtual bool OnEvent(const int id, const long &lparam, const double &dparam, const string &sparam);

    MyStdLayoutCache *getCache(void) const
    {
      return cache;
    }
    
    virtual bool OnChartChange(const long &lparam, const double &dparam, const string &sparam) override
    {
      const bool result = AppDialogResizable::OnChartChange(lparam, dparam, sparam);
      if(this.m_minimized && ChartGetInteger(0, CHART_IS_MAXIMIZED))
      {
        this.Restore();
        cache.fillList();
      }
      return result;
    }

  protected:
    CBox *GetMainContainer(void);
    virtual void SelfAdjustment(const bool restore = false) override;
    bool OnRefresh();
};


//+------------------------------------------------------------------+
//| Event handling                                                   |
//+------------------------------------------------------------------+

EVENT_MAP_BEGIN(ChartBrowserForm)
  ON_EVENT_LAYOUT_ARRAY(ON_CLICK, cache)
  ON_NO_ID_EVENT(ON_LAYOUT_REFRESH, OnRefresh)
EVENT_MAP_END(AppDialogResizable)

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
ChartBrowserForm::ChartBrowserForm(void)
{
  RTTI;
  pMain = NULL;
  cache = new MyStdLayoutCache(&this);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
ChartBrowserForm::~ChartBrowserForm(void)
{
  delete cache;
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool ChartBrowserForm::CreateLayout(const long chart, const string name, const int subwin, const int x1, const int y1, const int x2, const int y2)
{
  StdLayoutBase::setCache(cache);
  {
    _layout<ChartBrowserForm> dialog(this, name, x1, y1, x2, y2);
    
    // ------------------
    // GUI Layout for MQL app (standard controls library)
    {
      _layout<CBoxV> main("main", ClientAreaWidth(), ClientAreaHeight(), WND_ALIGN_CLIENT);
      main <= PackedRect(0, 0, 0, 0);
      {
        {
          _layout<CBoxH> Controls("Controls", 192, 30, (ENUM_WND_ALIGN_FLAGS)(WND_ALIGN_CONTENT|(ENUM_WND_ALIGN_FLAGS)(WND_ALIGN_WIDTH|WND_ALIGN_TOP)));
          Controls <= PackedRect(5, 0, 5, 0) <= HORIZONTAL_ALIGN_STACK;
          {
            {
              _layout<TabButton> ChartsButton("ChartsButton", 64, 20, (ENUM_WND_ALIGN_FLAGS)WND_ALIGN_BOTTOM);
              ChartsButton <= tabs[TAB_CHARTS] <= PackedRect(0, 0, 0, 0);
              ChartsButton["enable"] <= false;
              cache.registerGroupButton(ChartsButton.get());
            }
            {
              _layout<TabButton> IndicatorsButton("IndicatorsButton", 64, 20, (ENUM_WND_ALIGN_FLAGS)WND_ALIGN_BOTTOM);
              IndicatorsButton <= tabs[TAB_INDICATORS] <= PackedRect(0, 0, 0, 0);
              cache.registerGroupButton(IndicatorsButton.get());
            }
            {
              _layout<TabButton> ExpertsButton("ExpertsButton", 64, 20, (ENUM_WND_ALIGN_FLAGS)WND_ALIGN_BOTTOM);
              ExpertsButton <= tabs[TAB_EXPERTS] <= PackedRect(0, 0, 0, 0);
              cache.registerGroupButton(ExpertsButton.get());
            }
            {
              _layout<TabButton> ScriptsButton("ScriptsButton", 64, 20, (ENUM_WND_ALIGN_FLAGS)WND_ALIGN_BOTTOM);
              ScriptsButton <= tabs[TAB_SCRIPTS] <= PackedRect(0, 0, 0, 0);
              cache.registerGroupButton(ScriptsButton.get());
            }

            {
              _layout<CButton> GoButton("GoButton", 50, 20, (ENUM_WND_ALIGN_FLAGS)WND_ALIGN_RIGHT);
              GoButton <= "Go" <= PackedRect(0, 0, 0, 0) <= clrWhite;
              GoButton["background"] <= clrDodgerBlue;
              GoButton["font"] <= "Arial Black";
            }
          }
          
        }
        {
          _layout<CBoxH> listContainer("listContainer", 192, 304, (ENUM_WND_ALIGN_FLAGS)(WND_ALIGN_CONTENT|WND_ALIGN_CLIENT));
          listContainer <= PackedRect(0, 30, 0, 0);
          {
            {
              _layout<ListViewResizable> list("list", 190, 304, WND_ALIGN_HEIGHT);
              list <= PackedRect(0, 0, 0, 0);
              cache.registerList(list.get());
              cache.fillList(TAB_CHARTS);
            }
          }
        }
      }
    }

    // ----------------------
  }

  SelfAdjustment();
  EventChartCustom(CONTROLS_SELF_MESSAGE, ON_LAYOUT_REFRESH, 0, 0.0, NULL);

  return true;
}

CBox *ChartBrowserForm::GetMainContainer(void)
{
  for(int i = 0; i < ControlsTotal(); i++)
  {
    CWndClient *client = dynamic_cast<CWndClient *>(Control(i));
    if(client != NULL)
    {
      for(int j = 0; j < client.ControlsTotal(); j++)
      {
        CBox *box = dynamic_cast<CBox *>(client.Control(j));
        if(box != NULL)
        {
          return box;
        }
      }
    }
  }
  return NULL;
}

void ChartBrowserForm::SelfAdjustment(const bool restore = false)
{
  if(pMain == NULL)
  {
    pMain = GetMainContainer();
  }
  
  if(pMain)
  {
    pMain.Show();
    pMain.Pack();
    Rebound(Rect());
  }
}

bool ChartBrowserForm::OnRefresh()
{
  SelfAdjustment();
  return true;
}


ChartBrowserForm *form;

int OnInit()
{
    form = new ChartBrowserForm();
    ChartSetInteger(0, CHART_SHOW_ONE_CLICK, false);
    // ChartSetInteger(0, CHART_SHOW_TICKER, false);
    
    if(!form.CreateLayout(0, DIALOG_TITLE, 0, 20, 20, 400, 324))
        return (INIT_FAILED);
    form.IniFileLoad();

    if(!form.Run())
        return (INIT_FAILED);

    return (INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
    form.Destroy(reason);
    delete form;
}

void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    form.ChartEvent(id, lparam, dparam, sparam);
}
//+------------------------------------------------------------------+

Comments