//+------------------------------------------------------------------+ //| ChartCanvas.mqh | //| Copyright 2000-2025, MetaQuotes Ltd. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #include "..\Canvas.mqh" #include #include #include #include //--- enumerations enum ENUM_SHOW_FLAGS { FLAG_SHOW_NONE =0, FLAG_SHOW_LEGEND =1, FLAG_SHOW_SCALE_LEFT =2, FLAG_SHOW_SCALE_RIGHT =4, FLAG_SHOW_SCALE_TOP =8, FLAG_SHOW_SCALE_BOTTOM=16, FLAG_SHOW_GRID =32, FLAG_SHOW_DESCRIPTORS =64, FLAG_SHOW_VALUE =128, FLAG_SHOW_PERCENT =256, FLAGS_SHOW_SCALES =(FLAG_SHOW_SCALE_LEFT+FLAG_SHOW_SCALE_RIGHT+ FLAG_SHOW_SCALE_TOP+FLAG_SHOW_SCALE_BOTTOM), FLAGS_SHOW_ALL =(FLAG_SHOW_LEGEND+FLAGS_SHOW_SCALES+FLAG_SHOW_GRID+ FLAG_SHOW_DESCRIPTORS+FLAG_SHOW_VALUE+FLAG_SHOW_PERCENT) }; enum ENUM_ALIGNMENT { ALIGNMENT_LEFT = 1, // align by left border ALIGNMENT_TOP = 2, // align by top border ALIGNMENT_RIGHT = 4, // align by right border ALIGNMENT_BOTTOM = 8 // align by bottom border }; //--- macro #define IS_SHOW_LEGEND ((m_show_flags&FLAG_SHOW_LEGEND) !=0) #define IS_SHOW_SCALES ((m_show_flags&FLAGS_SHOW_SCALES) !=0) #define IS_SHOW_SCALE_LEFT ((m_show_flags&FLAG_SHOW_SCALE_LEFT) !=0) #define IS_SHOW_SCALE_RIGHT ((m_show_flags&FLAG_SHOW_SCALE_RIGHT) !=0) #define IS_SHOW_SCALE_TOP ((m_show_flags&FLAG_SHOW_SCALE_TOP) !=0) #define IS_SHOW_SCALE_BOTTOM ((m_show_flags&FLAG_SHOW_SCALE_BOTTOM)!=0) #define IS_SHOW_GRID ((m_show_flags&FLAG_SHOW_GRID) !=0) #define IS_SHOW_DESCRIPTORS ((m_show_flags&FLAG_SHOW_DESCRIPTORS) !=0) #define IS_SHOW_VALUE ((m_show_flags&FLAG_SHOW_VALUE) !=0) #define IS_SHOW_PERCENT ((m_show_flags&FLAG_SHOW_PERCENT) !=0) //+------------------------------------------------------------------+ //| Class CChartCanvas | //| Usage: base class for graphical charts | //+------------------------------------------------------------------+ class CChartCanvas : public CCanvas { protected: //--- colors uint m_color_background; uint m_color_border; uint m_color_text; uint m_color_grid; //--- adjusted parameters uint m_max_data; uint m_max_descr_len; uint m_allowed_show_flags; uint m_show_flags; ENUM_ALIGNMENT m_legend_alignment; uint m_threshold_drawing; bool m_accumulative; //--- parameters for scales and grid double m_v_scale_min; double m_v_scale_max; uint m_num_grid; int m_scale_digits; //--- data int m_data_offset; uint m_data_total; CArray *m_data; CArrayUInt m_colors; CArrayString m_descriptors; //--- CArrayInt m_index; uint m_index_size; double m_sum; double m_others; uint m_max_descr_width; uint m_max_value_width; //--- variables CRect m_data_area; //--- variables for scaling and scales double m_scale_x; int m_x_min; int m_x_0; int m_x_max; int m_dx_grid; double m_scale_y; int m_y_min; int m_y_0; int m_y_max; int m_dy_grid; string m_scale_text[]; public: CChartCanvas(void); ~CChartCanvas(void); //--- create virtual bool Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt=COLOR_FORMAT_XRGB_NOALPHA); //--- colors uint ColorBackground(void) const { return(m_color_background); } void ColorBackground(const uint value); uint ColorBorder(void) const { return(m_color_border); } void ColorBorder(const uint value); uint ColorText(void) const { return(m_color_text); } void ColorText(const uint value); uint ColorGrid(void) const { return(m_color_grid); } void ColorGrid(const uint value) { m_color_grid=value; } //--- adjusted parameters uint MaxData(void) const { return(m_max_data); } void MaxData(const uint value); uint MaxDescrLen(void) const { return(m_max_descr_len); } void MaxDescrLen(const uint value); //--- show flags void AllowedShowFlags(const uint flags); uint ShowFlags(void) const { return(m_show_flags); } void ShowFlags(const uint flags); bool IsShowLegend(void) const { return(IS_SHOW_LEGEND); } bool IsShowScaleLeft(void) const { return(IS_SHOW_SCALE_LEFT); } bool IsShowScaleRight(void) const { return(IS_SHOW_SCALE_RIGHT); } bool IsShowScaleTop(void) const { return(IS_SHOW_SCALE_TOP); } bool IsShowScaleBottom(void) const { return(IS_SHOW_SCALE_BOTTOM); } bool IsShowGrid(void) const { return(IS_SHOW_GRID); } bool IsShowDescriptors(void) const { return(IS_SHOW_DESCRIPTORS); } bool IsShowPercent(void) const { return(IS_SHOW_PERCENT); } void ShowLegend(const bool flag=true); void ShowScaleLeft(const bool flag=true); void ShowScaleRight(const bool flag=true); void ShowScaleTop(const bool flag=true); void ShowScaleBottom(const bool flag=true); void ShowGrid(const bool flag=true); void ShowDescriptors(const bool flag=true); void ShowValue(const bool flag=true); void ShowPercent(const bool flag=true); void LegendAlignment(const ENUM_ALIGNMENT value); void Accumulative(const bool flag=true); //--- for scales and grid double VScaleMin(void) const { return(m_v_scale_min); } void VScaleMin(const double value); double VScaleMax(void) const { return(m_v_scale_max); } void VScaleMax(const double value); uint NumGrid(void) const { return(m_num_grid); } void NumGrid(const uint value); void VScaleParams(const double max,const double min,const uint grid); //--- state int DataOffset(void) const { return(m_data_offset); } void DataOffset(const int value); //--- data uint DataTotal(void) const { return(m_data_total); } bool DescriptorUpdate(const uint pos,const string descr); bool ColorUpdate(const uint pos,const uint clr); protected: virtual void ValuesCheck(void); virtual void Redraw(void); virtual void DrawBackground(void); virtual void DrawLegend(void); int DrawLegendVertical(const int w,const int h); int DrawLegendHorizontal(const int w,const int h); virtual void CalcScales(void); virtual void DrawScales(void); virtual int DrawScaleLeft(const bool draw=true); virtual int DrawScaleRight(const bool draw=true); virtual int DrawScaleTop(const bool draw=true); virtual int DrawScaleBottom(const bool draw=true); virtual void DrawGrid(void); virtual void DrawDescriptors(void) {} virtual void DrawChart(void); virtual void DrawData(const uint idx=0) {} }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CChartCanvas::CChartCanvas(void) : m_color_background(XRGB(0xFF,0xFF,0xFF)), m_color_border(XRGB(0x9F,0x9F,0x9F)), m_color_text(XRGB(0x3F,0x3F,0x3F)), m_color_grid(XRGB(0xCF,0xCF,0xCF)), m_max_data(10), m_max_descr_len(10), m_allowed_show_flags(FLAGS_SHOW_ALL), m_show_flags(FLAG_SHOW_NONE), m_legend_alignment(ALIGNMENT_BOTTOM), m_threshold_drawing(2), m_accumulative(false), m_data_offset(0), m_data_total(0), m_data(NULL), m_v_scale_min(0.0), m_v_scale_max(10.0), m_num_grid(5), m_scale_digits(0) { } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CChartCanvas::~CChartCanvas(void) { if(m_data!=NULL) delete m_data; } //+------------------------------------------------------------------+ //| Create dynamic resource | //+------------------------------------------------------------------+ bool CChartCanvas::Create(const string name,const int width,const int height,ENUM_COLOR_FORMAT clrfmt) { //--- call method of parent class if(!CCanvas::Create(name,width,height,clrfmt)) return(false); //--- set font FontSet("Tahoma",-100); //--- succeed return(true); } //+------------------------------------------------------------------+ //| Sets background color | //+------------------------------------------------------------------+ void CChartCanvas::ColorBackground(const uint value) { m_color_background=value; //--- redraw if(m_data_total>0) Redraw(); } //+------------------------------------------------------------------+ //| Sets border color | //+------------------------------------------------------------------+ void CChartCanvas::ColorBorder(const uint value) { m_color_border=value; //--- redraw if(m_data_total>0) Redraw(); } //+------------------------------------------------------------------+ //| Sets text color | //+------------------------------------------------------------------+ void CChartCanvas::ColorText(const uint value) { m_color_text=value; //--- redraw if(m_data_total>0) Redraw(); } //+------------------------------------------------------------------+ //| Sets maximum amount of data | //+------------------------------------------------------------------+ void CChartCanvas::MaxData(const uint value) { //--- check if((value==0) || (m_data_total==value)) return; //--- save m_max_data=value; if(m_data_total>m_max_data) { m_data_total=value; m_colors.Resize(value); m_descriptors.Resize(value); } } //+------------------------------------------------------------------+ //| Sets maximum length of descriptor | //+------------------------------------------------------------------+ void CChartCanvas::MaxDescrLen(const uint value) { m_max_descr_len=value; //--- redraw if(m_data_total>0) Redraw(); } //+------------------------------------------------------------------+ //| Sets allowed visibility flags | //+------------------------------------------------------------------+ void CChartCanvas::AllowedShowFlags(const uint flags) { m_allowed_show_flags=flags; m_show_flags&=m_allowed_show_flags; } //+------------------------------------------------------------------+ //| Sets visibility flags | //+------------------------------------------------------------------+ void CChartCanvas::ShowFlags(const uint flags) { m_show_flags=flags&m_allowed_show_flags; //--- redraw if(m_data_total>0) Redraw(); } //+------------------------------------------------------------------+ //| Sets visibility flag for "legend" | //+------------------------------------------------------------------+ void CChartCanvas::ShowLegend(const bool flag) { if((m_allowed_show_flags&FLAG_SHOW_LEGEND)!=0) { if(flag) m_show_flags|=FLAG_SHOW_LEGEND; else m_show_flags&=~FLAG_SHOW_LEGEND; //--- redraw if(m_data_total>0) Redraw(); } } //+------------------------------------------------------------------+ //| Sets visibility flag for left scale | //+------------------------------------------------------------------+ void CChartCanvas::ShowScaleLeft(const bool flag) { if((m_allowed_show_flags&FLAG_SHOW_SCALE_LEFT)!=0) { if(flag) m_show_flags|=FLAG_SHOW_SCALE_LEFT; else m_show_flags&=~FLAG_SHOW_SCALE_LEFT; //--- redraw if(m_data_total>0) Redraw(); } } //+------------------------------------------------------------------+ //| Sets visibility flag for right scale | //+------------------------------------------------------------------+ void CChartCanvas::ShowScaleRight(const bool flag) { if((m_allowed_show_flags&FLAG_SHOW_SCALE_RIGHT)!=0) { if(flag) m_show_flags|=FLAG_SHOW_SCALE_RIGHT; else m_show_flags&=~FLAG_SHOW_SCALE_RIGHT; //--- redraw if(m_data_total>0) Redraw(); } } //+------------------------------------------------------------------+ //| Sets visibility flag for top scale | //+------------------------------------------------------------------+ void CChartCanvas::ShowScaleTop(const bool flag) { if((m_allowed_show_flags&FLAG_SHOW_SCALE_TOP)!=0) { if(flag) m_show_flags|=FLAG_SHOW_SCALE_TOP; else m_show_flags&=~FLAG_SHOW_SCALE_TOP; //--- redraw if(m_data_total>0) Redraw(); } } //+------------------------------------------------------------------+ //| Sets visibility flag for bottom scale | //+------------------------------------------------------------------+ void CChartCanvas::ShowScaleBottom(const bool flag) { if((m_allowed_show_flags&FLAG_SHOW_SCALE_BOTTOM)!=0) { if(flag) m_show_flags|=FLAG_SHOW_SCALE_BOTTOM; else m_show_flags&=~FLAG_SHOW_SCALE_BOTTOM; //--- redraw if(m_data_total>0) Redraw(); } } //+------------------------------------------------------------------+ //| Sets visibility flag for grid | //+------------------------------------------------------------------+ void CChartCanvas::ShowGrid(const bool flag) { if((m_allowed_show_flags&FLAG_SHOW_GRID)!=0) { if(flag) m_show_flags|=FLAG_SHOW_GRID; else m_show_flags&=~FLAG_SHOW_GRID; //--- redraw if(m_data_total>0) Redraw(); } } //+------------------------------------------------------------------+ //| Sets visibility flag for descriptors | //+------------------------------------------------------------------+ void CChartCanvas::ShowDescriptors(const bool flag) { if((m_allowed_show_flags&FLAG_SHOW_DESCRIPTORS)!=0) { if(flag) m_show_flags|=FLAG_SHOW_DESCRIPTORS; else m_show_flags&=~FLAG_SHOW_DESCRIPTORS; //--- redraw if(m_data_total>0) Redraw(); } } //+------------------------------------------------------------------+ //| Sets visibility flag for value | //+------------------------------------------------------------------+ void CChartCanvas::ShowValue(const bool flag) { if((m_allowed_show_flags&FLAG_SHOW_VALUE)!=0) { if(flag) { m_show_flags|=FLAG_SHOW_VALUE; m_show_flags&=~FLAG_SHOW_PERCENT; } else m_show_flags&=~FLAG_SHOW_VALUE; //--- redraw if(m_data_total>0) Redraw(); } } //+------------------------------------------------------------------+ //| Sets visibility flag for percentage | //+------------------------------------------------------------------+ void CChartCanvas::ShowPercent(const bool flag) { if((m_allowed_show_flags&FLAG_SHOW_PERCENT)!=0) { if(flag) { m_show_flags|=FLAG_SHOW_PERCENT; m_show_flags&=~FLAG_SHOW_VALUE; } else m_show_flags&=~FLAG_SHOW_PERCENT; //--- redraw if(m_data_total>0) Redraw(); } } //+------------------------------------------------------------------+ //| Sets legend alignment | //+------------------------------------------------------------------+ void CChartCanvas::LegendAlignment(const ENUM_ALIGNMENT value) { m_legend_alignment=value; //--- redraw if(m_data_total>0) Redraw(); } //+------------------------------------------------------------------+ //| Sets accumulative flag | //+------------------------------------------------------------------+ void CChartCanvas::Accumulative(const bool flag=true) { m_accumulative=flag; //--- redraw if(m_data_total>0) Redraw(); } //+------------------------------------------------------------------+ //| Sets lower limit for vertical scale | //+------------------------------------------------------------------+ void CChartCanvas::VScaleMin(const double value) { //--- check if(value==m_v_scale_max) return; //--- save m_v_scale_min=value; //--- redraw if(m_data_total>0) Redraw(); } //+------------------------------------------------------------------+ //| Sets upper limit for vertical scale | //+------------------------------------------------------------------+ void CChartCanvas::VScaleMax(const double value) { if(value==m_v_scale_min) return; //--- save m_v_scale_max=value; //--- redraw if(m_data_total>0) Redraw(); } //+------------------------------------------------------------------+ //| Sets number of vertical scale divisions | //+------------------------------------------------------------------+ void CChartCanvas::NumGrid(const uint value) { //--- check if(value==0) return; //--- save m_num_grid=value; //--- redraw if(m_data_total>0) Redraw(); } //+------------------------------------------------------------------+ //| Sets parameters for vertical scale | //+------------------------------------------------------------------+ void CChartCanvas::VScaleParams(const double max,const double min,const uint grid) { //--- check if(grid==0) return; if(max<=min) return; //--- save m_v_scale_max=max; m_v_scale_min=min; m_num_grid =grid; //--- redraw if(m_data_total>0) Redraw(); } //+------------------------------------------------------------------+ //| Sets data offset | //+------------------------------------------------------------------+ void CChartCanvas::DataOffset(const int value) { m_data_offset=value; //--- redraw Redraw(); } //+------------------------------------------------------------------+ //| Updates parameter descriptor only (in specified position) | //+------------------------------------------------------------------+ bool CChartCanvas::DescriptorUpdate(const uint pos,const string descr) { //--- update if(descr!=NULL && !m_descriptors.Update(pos,descr)) return(false); //--- redraw Redraw(); //--- succeed return(true); } //+------------------------------------------------------------------+ //| Updates parameter color only (in specified position) | //+------------------------------------------------------------------+ bool CChartCanvas::ColorUpdate(const uint pos,const uint clr) { //--- update if(clr!=0 && !m_colors.Update(pos,clr)) return(false); //--- redraw Redraw(); //--- succeed return(true); } //+------------------------------------------------------------------+ //| Checks values for insignificance | //+------------------------------------------------------------------+ void CChartCanvas::ValuesCheck(void) { string text; int w,h; //--- clear m_max_value_width=0; m_sum =0; m_others =0; m_index_size =0; m_index.Clear(); //--- check if(m_data==NULL) return; if(m_data.Type()==TYPE_DOUBLE) { //--- single-series chart //--- calculate sum of all values for(uint i=0;im_height) { cols++; rows=(int)m_index_size/cols; if((int)m_index_size%cols!=0) rows++; } //--- draw int x0=(m_legend_alignment==ALIGNMENT_RIGHT) ? width-w*cols+h : h; int x=0; int y =-h/2; int i; if(m_data_total==m_index_size) { for(i=0;i<(int)m_data_total;i++,x+=w) { if(i%cols==0) { x=x0; y+=dy; } FillRectangle(x,y,x+h,y+h,(uint)m_colors[i]); TextOut(x+h,y," - "+m_descriptors[i],m_color_text); } } else { for(i=0;i<(int)m_index_size;i++,x+=w) { int index=m_index[i]; if(i%cols==0) { x=x0; y+=dy; } FillRectangle(x,y,x+h,y+h,(uint)m_colors[index]); TextOut(x+h,y," - "+m_descriptors[index],m_color_text); } if(i%cols==0) { x=x0; y+=dy; } FillRectangle(x,y,x+h,y+h,COLOR2RGB(clrBlack)); TextOut(x+h,y," - Others",m_color_text); } //--- width return(w*cols); } //+------------------------------------------------------------------+ //| Draw horizontal "legend" | //+------------------------------------------------------------------+ int CChartCanvas::DrawLegendHorizontal(const int w,const int h) { int width =m_data_area.Width(); int height=m_data_area.Height(); int rows =1; int cols =(int)m_index_size; //--- calculate while(w*cols>m_width) { rows++; cols=(int)m_index_size/rows; if((int)m_index_size%rows!=0) cols++; } //--- draw int dx=width/(cols+1); int x =dx-w/2+h; int dy=(int)(1.5*h); int y =(m_legend_alignment==ALIGNMENT_BOTTOM) ? height-dy*(rows+1) : -h/2; int i; if(m_data_total==m_index_size) { for(i=0;i<(int)m_data_total;i++,x+=dx) { if(i%cols==0) { x=dx-w/2+h; y+=dy; } FillRectangle(x,y,x+h,y+h,(uint)m_colors[i]); TextOut(x+h,y," - "+m_descriptors[i],m_color_text); } } else { for(i=0;i<(int)m_index_size;i++,x+=dx) { int index=m_index[i]; if(i%cols==0) { x=dx-w/2+h; y+=dy; } FillRectangle(x,y,x+h,y+h,(uint)m_colors[index]); TextOut(x+h,y," - "+m_descriptors[index],m_color_text); } if(i%cols==0) { x=dx-w/2+h; y+=dy; } FillRectangle(x,y,x+h,y+h,COLOR2RGB(clrBlack)); TextOut(x+h,y," - Others",m_color_text); } //--- height return(dy*(rows+1)); } //+------------------------------------------------------------------+ //| Calculates coordinates of scales | //+------------------------------------------------------------------+ void CChartCanvas::CalcScales(void) { int width =m_data_area.Width(); int height=m_data_area.Height(); //--- limits m_y_max=m_data_area.top+DrawScaleTop(false); m_y_min=m_data_area.bottom-DrawScaleBottom(false); //--- additional m_dy_grid=(int)((m_y_min-m_y_max)/m_num_grid); m_y_max+=(int)(((m_y_min-m_y_max)-m_dy_grid*m_num_grid)/2); m_y_min=(int)(m_y_max+m_dy_grid*m_num_grid); //--- normalize if(m_v_scale_min>=0.0) m_y_0=m_y_min; else { if(m_v_scale_max<=0.0) m_y_0=m_y_max; else m_y_0=(int)(m_y_max+(m_y_min-m_y_max)*m_v_scale_max/(m_v_scale_max-m_v_scale_min)); } //--- scale m_scale_y=(m_v_scale_max!=m_v_scale_min) ? (m_y_min-m_y_max)/(m_v_scale_max-m_v_scale_min) : 1; //--- labels on scale if(ArraySize(m_scale_text)!=m_num_grid+1 && ArrayResize(m_scale_text,m_num_grid+1)==-1) return; double val=m_v_scale_min; double dval=(m_v_scale_max-m_v_scale_min)/m_num_grid; for(uint i=0;i<=m_num_grid;i++,val+=dval) m_scale_text[i]=DoubleToString(val,m_scale_digits); } //+------------------------------------------------------------------+ //| Redraws scales | //+------------------------------------------------------------------+ void CChartCanvas::DrawScales(void) { //--- recalculate CalcScales(); //--- redraw scales if(IS_SHOW_SCALE_LEFT) DrawScaleLeft(); if(IS_SHOW_SCALE_RIGHT) DrawScaleRight(); if(IS_SHOW_SCALE_TOP) DrawScaleTop(); if(IS_SHOW_SCALE_BOTTOM) DrawScaleBottom(); } //+------------------------------------------------------------------+ //| Redraws left scale | //+------------------------------------------------------------------+ int CChartCanvas::DrawScaleLeft(const bool draw) { //--- check flag if(!IS_SHOW_SCALE_LEFT) return(0); //--- variables int x1=m_data_area.left; int x2; int y=m_y_min; //--- calculate scale width int size=0; for(uint i=0;i<=m_num_grid;i++) { if(size