431 lines
14 KiB
MQL5
431 lines
14 KiB
MQL5
|
//+------------------------------------------------------------------+
|
||
|
//| Comment.mqh |
|
||
|
//| avoitenko |
|
||
|
//| https://login.mql5.com/en/users/avoitenko |
|
||
|
//+------------------------------------------------------------------+
|
||
|
#property copyright "avoitenko"
|
||
|
#property link "https://login.mql5.com/en/users/avoitenko"
|
||
|
#property version "1.00"
|
||
|
#property strict
|
||
|
|
||
|
#include <Canvas/Canvas.mqh>
|
||
|
#include <Arrays/List.mqh>
|
||
|
//---
|
||
|
#define EVENT_NO_EVENTS 0
|
||
|
#define EVENT_MOVE 1
|
||
|
#define EVENT_CHANGE 2
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| TComment |
|
||
|
//+------------------------------------------------------------------+
|
||
|
class TComment : public CObject
|
||
|
{
|
||
|
public:
|
||
|
string text;
|
||
|
color colour;
|
||
|
};
|
||
|
//+------------------------------------------------------------------+
|
||
|
//| CComment |
|
||
|
//+------------------------------------------------------------------+
|
||
|
class CComment
|
||
|
{
|
||
|
private:
|
||
|
|
||
|
bool m_is_collapse;
|
||
|
|
||
|
CPoint m_temp;
|
||
|
CCanvas m_comment;
|
||
|
CPoint m_pos;
|
||
|
CList m_list;
|
||
|
CSize m_size;
|
||
|
//---
|
||
|
string m_name;
|
||
|
string m_font_name;
|
||
|
int m_font_size;
|
||
|
bool m_font_bold;
|
||
|
double m_font_interval;
|
||
|
color m_border_color;
|
||
|
color m_back_color;
|
||
|
uchar m_back_alpha;
|
||
|
bool m_graph_mode;
|
||
|
bool m_auto_colors;
|
||
|
color m_auto_back_color;
|
||
|
color m_auto_text_color;
|
||
|
color m_auto_border_color;
|
||
|
color m_chart_back_color;
|
||
|
//+------------------------------------------------------------------+
|
||
|
color Color2Gray(const color value)
|
||
|
{
|
||
|
int gray=(int)round(0.3*GETRGBR(value)+0.59*GETRGBG(value)+0.11*GETRGBB(value));
|
||
|
if(gray>255)
|
||
|
gray=255;
|
||
|
return((color)ARGB(0,gray,gray,gray));
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|
||
|
uchar GrayChannel(const color value)
|
||
|
{
|
||
|
int gray=(int)round(0.3*GETRGBR(value)+0.59*GETRGBG(value)+0.11*GETRGBB(value));
|
||
|
if(gray>255)
|
||
|
gray=255;
|
||
|
return((uchar)gray);
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|
||
|
color Bright(const color value,const int percent)
|
||
|
{
|
||
|
int r,g,b;
|
||
|
//---
|
||
|
r=GETRGBR(value);
|
||
|
g=GETRGBG(value);
|
||
|
b=GETRGBB(value);
|
||
|
//---
|
||
|
if(percent>=0)
|
||
|
{
|
||
|
r+=(255-r)*percent/100;
|
||
|
if(r>255)
|
||
|
r=255;
|
||
|
//---
|
||
|
g+=(255-g)*percent/100;
|
||
|
if(g>255)
|
||
|
g=255;
|
||
|
//---
|
||
|
b+=(255-b)*percent/100;
|
||
|
if(b>255)
|
||
|
b=255;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
r+=r*percent/100;
|
||
|
if(r<0)
|
||
|
r=0;
|
||
|
//---
|
||
|
g+=g*percent/100;
|
||
|
if(g<0)
|
||
|
g=0;
|
||
|
//---
|
||
|
b+=b*percent/100;
|
||
|
if(b<0)
|
||
|
b=0;
|
||
|
}
|
||
|
//---
|
||
|
return(ARGB(0,r,g,b));
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|
||
|
void CalcColors()
|
||
|
{
|
||
|
m_auto_back_color=(color)ChartGetInteger(0,CHART_COLOR_BACKGROUND);
|
||
|
color m_back_gray=Color2Gray(m_auto_back_color);
|
||
|
uchar channel=GrayChannel(m_back_gray);
|
||
|
//---
|
||
|
if(channel>120)
|
||
|
{
|
||
|
if(m_back_color==clrNONE)
|
||
|
m_auto_border_color=clrNONE;
|
||
|
else
|
||
|
m_auto_border_color=Bright(m_back_gray,-30);
|
||
|
//---
|
||
|
m_auto_text_color=Bright(m_back_gray,-80);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if(m_back_color==clrNONE)
|
||
|
m_auto_border_color=clrNONE;
|
||
|
else
|
||
|
m_auto_border_color=Bright(m_back_gray,30);
|
||
|
//---
|
||
|
m_auto_text_color=Bright(m_back_gray,80);
|
||
|
}
|
||
|
}
|
||
|
public:
|
||
|
//+------------------------------------------------------------------+
|
||
|
void CComment(void)
|
||
|
{
|
||
|
m_name=NULL;
|
||
|
m_font_name="Lucida Console";
|
||
|
m_font_size=14;
|
||
|
m_font_bold=false;
|
||
|
m_font_interval=1.7;
|
||
|
m_border_color=clrNONE;
|
||
|
m_back_color=clrNONE;
|
||
|
m_back_alpha=10;
|
||
|
m_graph_mode=true;
|
||
|
m_auto_colors=false;
|
||
|
m_chart_back_color=(color)ChartGetInteger(0,CHART_COLOR_BACKGROUND);
|
||
|
m_auto_back_color=clrBlack;
|
||
|
m_auto_border_color=clrNONE;
|
||
|
//---
|
||
|
ChartSetInteger(0,CHART_EVENT_MOUSE_MOVE,true);
|
||
|
};
|
||
|
//+------------------------------------------------------------------+
|
||
|
void Create(const string name,const uint x,const uint y)
|
||
|
{
|
||
|
m_name=name;
|
||
|
m_pos.x=(int)x;
|
||
|
m_pos.y=(int)y;
|
||
|
};
|
||
|
//+------------------------------------------------------------------+
|
||
|
void Move(const uint x,const uint y)
|
||
|
{
|
||
|
m_pos.x=(int)x;
|
||
|
m_pos.y=(int)y;
|
||
|
};
|
||
|
//+------------------------------------------------------------------+
|
||
|
void SetAutoColors(const bool value)
|
||
|
{
|
||
|
m_auto_colors=value;
|
||
|
if(value)
|
||
|
CalcColors();
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|
||
|
void SetGraphMode(const bool value)
|
||
|
{
|
||
|
m_graph_mode=value;
|
||
|
};
|
||
|
//+------------------------------------------------------------------+
|
||
|
void SetText(const int row,const string text,const color colour)
|
||
|
{
|
||
|
if(row<0)
|
||
|
return;
|
||
|
//---
|
||
|
int total=m_list.Total();
|
||
|
//---
|
||
|
if(row<total)
|
||
|
{
|
||
|
TComment *item=m_list.GetNodeAtIndex(row);
|
||
|
item.text=text;
|
||
|
item.colour=colour;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//--- create new one string
|
||
|
for(int i=total; i<=row; i++)
|
||
|
{
|
||
|
m_list.Add(new TComment);
|
||
|
TComment *item=m_list.GetLastNode();
|
||
|
if(row==i)
|
||
|
{
|
||
|
item.text=text;
|
||
|
item.colour=colour;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
item.text="";
|
||
|
item.colour=clrNONE;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|
||
|
void SetFont(const string font_name,const int font_size,const bool bold,const double font_interval)
|
||
|
{
|
||
|
m_font_name=font_name;
|
||
|
m_font_size=font_size;
|
||
|
m_font_bold=bold;
|
||
|
m_font_interval=font_interval;
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|
||
|
void SetColor(const color border,const color back,const uchar alpha)
|
||
|
{
|
||
|
m_border_color=border;
|
||
|
m_back_color=back;
|
||
|
m_back_alpha=alpha;
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|
||
|
void Destroy()
|
||
|
{
|
||
|
if(!m_graph_mode)
|
||
|
Comment("");
|
||
|
m_comment.Destroy();
|
||
|
m_name=NULL;
|
||
|
};
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
CList* GetList()
|
||
|
{
|
||
|
return &m_list;
|
||
|
}
|
||
|
|
||
|
//+------------------------------------------------------------------+
|
||
|
void Clear()
|
||
|
{
|
||
|
m_list.Clear();
|
||
|
};
|
||
|
//+------------------------------------------------------------------+
|
||
|
int OnChartEvent(const int id,const long lparam,const double dparam,const string sparam)
|
||
|
{
|
||
|
//--- mouse position
|
||
|
CPoint p;
|
||
|
p.x = (int)lparam;
|
||
|
p.y = (int)dparam;
|
||
|
//---
|
||
|
if(id==CHARTEVENT_MOUSE_MOVE)
|
||
|
{
|
||
|
//--- panel size
|
||
|
CSize psize;
|
||
|
psize.cx=(int)ObjectGetInteger(0,m_name,OBJPROP_XSIZE);
|
||
|
psize.cy=(int)ObjectGetInteger(0,m_name,OBJPROP_YSIZE);
|
||
|
//--- panel position
|
||
|
CPoint pan;
|
||
|
pan.x=(int)ObjectGetInteger(0,m_name,OBJPROP_XDISTANCE);
|
||
|
pan.y=(int)ObjectGetInteger(0,m_name,OBJPROP_YDISTANCE);
|
||
|
//--- chart size
|
||
|
CSize screen;
|
||
|
screen.cx=(int)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS);
|
||
|
screen.cy=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS);
|
||
|
//---
|
||
|
if(sparam=="1")
|
||
|
{
|
||
|
//---
|
||
|
if(m_temp.x==-1 &&
|
||
|
p.x>=pan.x && p.x<pan.x+psize.cx &&
|
||
|
p.y>=pan.y && p.y<pan.y+psize.cy)
|
||
|
{
|
||
|
m_temp.x=p.x-pan.x;
|
||
|
}
|
||
|
//---
|
||
|
if(m_temp.y==-1 &&
|
||
|
p.x>=pan.x && p.x<pan.x+psize.cx &&
|
||
|
p.y>=pan.y && p.y<pan.y+psize.cy)
|
||
|
{
|
||
|
m_temp.y=p.y-pan.y;
|
||
|
}
|
||
|
//---
|
||
|
if(m_temp.x>=0 && m_temp.y>=0)
|
||
|
{
|
||
|
int new_x=p.x-m_temp.x;
|
||
|
if(new_x>screen.cx-psize.cx)
|
||
|
new_x=screen.cx-psize.cx;
|
||
|
if(new_x<0)
|
||
|
new_x=0;
|
||
|
//---
|
||
|
int new_y=p.y-m_temp.y;
|
||
|
if(new_y>screen.cy-psize.cy)
|
||
|
new_y=screen.cy-psize.cy;
|
||
|
if(new_y<0)
|
||
|
new_y=0;
|
||
|
//---
|
||
|
ObjectSetInteger(0,m_name,OBJPROP_XDISTANCE,new_x);
|
||
|
ObjectSetInteger(0,m_name,OBJPROP_YDISTANCE,new_y);
|
||
|
ChartSetInteger(0,CHART_MOUSE_SCROLL,false);
|
||
|
#ifdef __MQL5__
|
||
|
ChartRedraw();
|
||
|
#endif
|
||
|
return(EVENT_MOVE);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_temp.x=-1;
|
||
|
m_temp.y=-1;
|
||
|
ChartSetInteger(0,CHART_MOUSE_SCROLL,true);
|
||
|
}
|
||
|
}
|
||
|
//---
|
||
|
if(m_auto_colors && id==CHARTEVENT_CHART_CHANGE)
|
||
|
{
|
||
|
//--- changing background color event
|
||
|
if(m_chart_back_color!=(color)ChartGetInteger(0,CHART_COLOR_BACKGROUND))
|
||
|
{
|
||
|
CalcColors();
|
||
|
m_chart_back_color=(color)ChartGetInteger(0,CHART_COLOR_BACKGROUND);
|
||
|
return(EVENT_CHANGE);
|
||
|
}
|
||
|
}
|
||
|
//---
|
||
|
if(id==CHARTEVENT_CLICK)
|
||
|
{
|
||
|
|
||
|
//beginning of: detection of a doubleclick
|
||
|
static ulong ClickTimeMemory ; //static is crucial to remember ClickTimeMemory's content next time OnChartEvent() is called
|
||
|
|
||
|
ulong ClickTime = GetTickCount() ; //GetTickCount() uses milliseconds - it's not necessary to use GetMicrosecondCount()
|
||
|
|
||
|
if(ClickTime > ClickTimeMemory && ClickTime < ClickTimeMemory + 300) //the second click should appear within 300 milliseconds after the first click. That's 0.3 seconds - that's about the typical delay of the second click relative to the first click when making a doubleclick
|
||
|
{
|
||
|
//--- panel size
|
||
|
CSize psize;
|
||
|
psize.cx=(int)ObjectGetInteger(0,m_name,OBJPROP_XSIZE);
|
||
|
psize.cy=(int)ObjectGetInteger(0,m_name,OBJPROP_YSIZE);
|
||
|
|
||
|
//--- panel position
|
||
|
CPoint pan;
|
||
|
pan.x=(int)ObjectGetInteger(0,m_name,OBJPROP_XDISTANCE);
|
||
|
pan.y=(int)ObjectGetInteger(0,m_name,OBJPROP_YDISTANCE);
|
||
|
|
||
|
if(p.x>=pan.x && p.x<pan.x+psize.cx &&
|
||
|
p.y>=pan.y && p.y<pan.y+psize.cy)
|
||
|
{
|
||
|
m_is_collapse=!m_is_collapse;
|
||
|
Show();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ClickTimeMemory = ClickTime ;
|
||
|
}
|
||
|
//---
|
||
|
return(EVENT_NO_EVENTS);
|
||
|
}
|
||
|
//+------------------------------------------------------------------+
|
||
|
void Show()
|
||
|
{
|
||
|
int rows=m_is_collapse? 1 : m_list.Total();
|
||
|
//--- text mode
|
||
|
if(!m_graph_mode)
|
||
|
{
|
||
|
string text;
|
||
|
for(int i=0; i<rows; i++)
|
||
|
{
|
||
|
TComment *item=m_list.GetNodeAtIndex(i);
|
||
|
text+="\n"+item.text;
|
||
|
}
|
||
|
Comment(text);
|
||
|
return;
|
||
|
}
|
||
|
//---
|
||
|
m_comment.FontSet(m_font_name,m_font_size,m_font_bold?FW_BOLD:0);
|
||
|
int text_height=m_comment.TextHeight(" ");
|
||
|
int max_height=(int)((rows)*round(text_height*m_font_interval)+text_height-m_font_interval);
|
||
|
//--- calc max width
|
||
|
int max_width=0;
|
||
|
for(int i=0; i<rows; i++)
|
||
|
{
|
||
|
TComment *item=m_list.GetNodeAtIndex(i);
|
||
|
int width=m_comment.TextWidth(item.text);
|
||
|
if(width>max_width)
|
||
|
max_width=width;
|
||
|
}
|
||
|
max_width+=text_height;
|
||
|
//--- create panel
|
||
|
if(ObjectFind(0,m_name)==-1)
|
||
|
{
|
||
|
//m_comment.CreateBitmapLabel(0,0,m_name,m_pos.x,m_pos.y,max_width,max_height,COLOR_FORMAT_ARGB_NORMALIZE);
|
||
|
m_comment.CreateBitmapLabel(0,0,m_name,m_pos.x,m_pos.y,max_width,max_height,COLOR_FORMAT_ARGB_RAW);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//--- resize panel
|
||
|
if(m_comment.Height()!=max_height ||
|
||
|
m_comment.Width()!=max_width)
|
||
|
{
|
||
|
if(!m_comment.Resize(max_width,max_height))
|
||
|
{
|
||
|
ObjectDelete(0,m_name);
|
||
|
ChartRedraw();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
//---
|
||
|
m_comment.Erase(ColorToARGB(m_auto_colors?m_auto_back_color:m_back_color,m_back_alpha));
|
||
|
m_comment.Rectangle(0,0,max_width-1,max_height-1,ColorToARGB(m_auto_colors?m_auto_border_color:m_border_color));
|
||
|
//---
|
||
|
int h=(int)round(text_height/2);
|
||
|
for(int i=0; i<rows; i++)
|
||
|
{
|
||
|
TComment *item=m_list.GetNodeAtIndex(i);
|
||
|
m_comment.TextOut((int)round(text_height/2),h,item.text,ColorToARGB(m_auto_colors?m_auto_text_color:item.colour));
|
||
|
h+=(int)round(text_height*m_font_interval);
|
||
|
}
|
||
|
//---
|
||
|
m_comment.Update();
|
||
|
}
|
||
|
};
|
||
|
//+------------------------------------------------------------------+
|