Article-13179-MQL5-InfoPane.../Dashboard.mqh

2025 lines
118 KiB
MQL5
Raw Permalink Normal View History

2026-03-19 00:56:24 +07:00
//+------------------------------------------------------------------+
2026-03-19 00:53:45 +07:00
//| Dashboard.mqh |
//| Copyright 2026, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, MetaQuotes Ltd."
#property link "https://www.mql5.com"
2026-03-19 00:56:24 +07:00
//--- includes
#include <Canvas\Canvas.mqh>
#include <Arrays\ArrayObj.mqh>
//+------------------------------------------------------------------+
//| Класс ячейки таблицы |
//+------------------------------------------------------------------+
class CTableCell : public CObject
{
private:
int m_row; // Строка
int m_col; // Столбец
int m_x; // Координата X
int m_y; // Координата Y
public:
//--- Методы установки значений
void SetRow(const uint row) { this.m_row=(int)row; }
void SetColumn(const uint col) { this.m_col=(int)col; }
void SetX(const uint x) { this.m_x=(int)x; }
void SetY(const uint y) { this.m_y=(int)y; }
void SetXY(const uint x,const uint y)
{
this.m_x=(int)x;
this.m_y=(int)y;
}
//--- Методы получения значений
int Row(void) const { return this.m_row; }
int Column(void) const { return this.m_col; }
int X(void) const { return this.m_x; }
int Y(void) const { return this.m_y; }
//--- Виртуальный метод сравнения двух объектов
virtual int Compare(const CObject *node,const int mode=0) const
{
const CTableCell *compared=node;
return(this.Column()>compared.Column() ? 1 : this.Column()<compared.Column() ? -1 : 0);
}
//--- Конструктор/деструктор
CTableCell(const int row,const int column) : m_row(row),m_col(column){}
~CTableCell(void){}
};
//+------------------------------------------------------------------+
//| Класс строк таблиц |
//+------------------------------------------------------------------+
class CTableRow : public CObject
{
private:
CArrayObj m_list_cell; // Список ячеек
int m_row; // Номер строки
int m_y; // Координата Y
public:
//--- Возвращает список ячеек таблицы в строке
CArrayObj *GetListCell(void) { return &this.m_list_cell; }
//--- Возвращает (1) количество ячеек таблицы в строке (2) индекс строки в таблице
int CellsTotal(void) const { return this.m_list_cell.Total(); }
int Row(void) const { return this.m_row; }
//--- (1) Устанавливает, (2) возвращает координату Y строки
void SetY(const int y) { this.m_y=y; }
int Y(void) const { return this.m_y; }
//--- Добавляет новую ячейку таблицы в строку
bool AddCell(CTableCell *cell)
{
this.m_list_cell.Sort();
if(this.m_list_cell.Search(cell)!=WRONG_VALUE)
{
::PrintFormat("%s: Table cell with index %lu is already in the list",__FUNCTION__,cell.Column());
return false;
}
if(!this.m_list_cell.InsertSort(cell))
{
::PrintFormat("%s: Failed to add table cell with index %lu to list",__FUNCTION__,cell.Column());
return false;
}
return true;
}
//--- Возвращает указатель на указанную ячейку в строке
CTableCell *GetCell(const int column)
{
const CTableCell *obj=new CTableCell(this.m_row,column);
int index=this.m_list_cell.Search(obj);
delete obj;
return this.m_list_cell.At(index);
}
//--- Виртуальный метод сравнения двух объектов
virtual int Compare(const CObject *node,const int mode=0) const
{
const CTableRow *compared=node;
return(this.Row()>compared.Row() ? 1 : this.Row()<compared.Row() ? -1 : 0);
}
//--- Конструктор/деструктор
CTableRow(const int row) : m_row(row) { this.m_list_cell.Clear(); }
~CTableRow(void) { this.m_list_cell.Clear(); }
};
//+------------------------------------------------------------------+
//| Класс данных таблиц |
//+------------------------------------------------------------------+
class CTableData : public CObject
{
private:
CArrayObj m_list_rows; // Список строк
public:
//--- Возвращает список строк таблицы
CArrayObj *GetListRows(void) { return &this.m_list_rows; }
//--- Добавляет новую строку в таблицу
bool AddRow(CTableRow *row)
{
//--- Устанавливаем флаг сортированного списка
this.m_list_rows.Sort();
//--- Если такой объект уже есть в списке (поиск вернул индекс объекта, а не -1),
//--- сообщаем об этом в журнал и возвращаем false
if(this.m_list_rows.Search(row)!=WRONG_VALUE)
{
::PrintFormat("%s: Table row with index %lu is already in the list",__FUNCTION__,row.Row());
return false;
}
//--- Если не удалось добавить указатель в сортированный список - сообщаем об этом в журнал и возвращаем false
if(!this.m_list_rows.InsertSort(row))
{
::PrintFormat("%s: Failed to add table cell with index %lu to list",__FUNCTION__,row.Row());
return false;
}
//--- Успешно - возвращаем true
return true;
}
//--- Возвращает указатель на (1) указанную строку, (2) указанную ячейку в указанной строке таблицы
CTableRow *GetRow(const int index) { return this.m_list_rows.At(index); }
CTableCell *GetCell(const int row,const int column)
{
//--- Получаем указатель на объект-строку в списке строк
CTableRow *row_obj=this.GetRow(row);
//--- Если объект получить не удалось - возвращаем NULL
if(row_obj==NULL)
return NULL;
//--- Получаем указатель на объект-ячейку в строке по номеру столбца и
CTableCell *cell=row_obj.GetCell(column);
//--- возвращаем результат (указатель на объект, либо NULL)
return cell;
}
//--- Записывает в переданные в метод переменные координаты X и Y указанной ячейки таблицы
void CellXY(const uint row,const uint column, int &x, int &y)
{
x=WRONG_VALUE;
y=WRONG_VALUE;
CTableCell *cell=this.GetCell(row,column);
if(cell==NULL)
return;
x=cell.X();
y=cell.Y();
}
//--- Возвращает координату X указанной ячейки таблицы
int CellX(const uint row,const uint column)
{
CTableCell *cell=this.GetCell(row,column);
return(cell!=NULL ? cell.X() : WRONG_VALUE);
}
//--- Возвращает координату Y указанной ячейки таблицы
int CellY(const uint row,const uint column)
{
CTableCell *cell=this.GetCell(row,column);
return(cell!=NULL ? cell.Y() : WRONG_VALUE);
}
//--- Возвращает количество (1) строк, (2) столбцов в таблице
int RowsTotal(void) { return this.m_list_rows.Total(); }
int ColumnsTotal(void)
{
//--- Если в списке нет ни одной строки - возвращаем 0
if(this.RowsTotal()==0)
return 0;
//--- Получаем указатель на первую строку и возвращаем количество ячеек в ней
CTableRow *row=this.GetRow(0);
return(row!=NULL ? row.CellsTotal() : 0);
}
//--- Возвращает общее количество ячеек таблицы
int CellsTotal(void){ return this.RowsTotal()*this.ColumnsTotal(); }
//--- Очищает списки строк и ячеек таблицы
void Clear(void)
{
//--- В цикле по количеству строк в списке строк таблицы
for(int i=0;i<this.m_list_rows.Total();i++)
{
//--- получаем указатель на очередную строку
CTableRow *row=this.m_list_rows.At(i);
if(row==NULL)
continue;
//--- из полученного объекта-строки получаем список ячеек,
CArrayObj *list_cell=row.GetListCell();
//--- очищаем список ячеек
if(list_cell!=NULL)
list_cell.Clear();
}
//--- Очищаем список строк
this.m_list_rows.Clear();
}
//--- Распечатывает в журнал данные ячеек таблицы
void Print(const uint indent=0)
{
//--- Печатаем в журнал заголовок
::PrintFormat("Table: Rows: %lu, Columns: %lu",this.RowsTotal(),this.ColumnsTotal());
//--- В цикле по строкам таблицы
for(int r=0;r<this.RowsTotal();r++)
//--- в цикле по ячейкам очередной строки
for(int c=0;c<this.ColumnsTotal();c++)
{
//--- получаем указатель на очередную ячейку и выводим в журнал её данные
CTableCell *cell=this.GetCell(r,c);
if(cell!=NULL)
::PrintFormat("%*s%-5s %-4lu %-8s %-6lu %-8s %-6lu %-8s %-4lu",indent,"","Row",r,"Column",c,"Cell X:",cell.X(),"Cell Y:",cell.Y());
}
}
//--- Конструктор/деструктор
CTableData(void) { this.m_list_rows.Clear(); }
~CTableData(void) { this.m_list_rows.Clear(); }
};
//+------------------------------------------------------------------+
//--- enums
enum ENUM_MOUSE_STATE
{
MOUSE_STATE_NOT_PRESSED,
MOUSE_STATE_PRESSED_OUTSIDE_WINDOW,
MOUSE_STATE_PRESSED_INSIDE_WINDOW,
MOUSE_STATE_PRESSED_INSIDE_HEADER,
MOUSE_STATE_PRESSED_INSIDE_CLOSE,
MOUSE_STATE_PRESSED_INSIDE_MINIMIZE,
MOUSE_STATE_PRESSED_INSIDE_PIN,
MOUSE_STATE_OUTSIDE_WINDOW,
MOUSE_STATE_INSIDE_WINDOW,
MOUSE_STATE_INSIDE_HEADER,
MOUSE_STATE_INSIDE_CLOSE,
MOUSE_STATE_INSIDE_MINIMIZE,
MOUSE_STATE_INSIDE_PIN
};
//+------------------------------------------------------------------+
//| Класс Dashboard |
//+------------------------------------------------------------------+
class CDashboard : public CObject
{
private:
CCanvas m_canvas; // Канвас
CCanvas m_workspace; // Рабочая область
CTableData m_table_data; // Массив ячеек таблиц
ENUM_PROGRAM_TYPE m_program_type; // Тип программы
ENUM_MOUSE_STATE m_mouse_state; // Состояние кнопок мышки
uint m_id; // Идентификатор объекта
long m_chart_id; // ChartID
int m_chart_w; // Ширина графика
int m_chart_h; // Высота графика
int m_x; // Координата X
int m_y; // Координата Y
int m_w; // Ширина
int m_h; // Высота
int m_x_dock; // Координата X закреплённой свёрнутой панели
int m_y_dock; // Координата Y закреплённой свёрнутой панели
bool m_header; // Флаг наличия заголовка
bool m_butt_close; // Флаг наличия кнопки закрытия
bool m_butt_minimize; // Флаг наличия кнопки сворачивания/разворачивания
bool m_butt_pin; // Флаг наличия кнопки закрепления
bool m_wider_wnd; // Флаг превышения горизонтального размера панели ширины окна
bool m_higher_wnd; // Флаг превышения вертикольного размера панели высоты окна
bool m_movable; // Флаг перемещаемости панели
int m_header_h; // Высота заголовка
int m_wnd; // Номер подокна графика
uchar m_header_alpha; // Прозрачность заголовка
uchar m_header_alpha_c; // Текущая прозрачность заголовка
color m_header_back_color; // Цвет фона заголовка
color m_header_back_color_c; // Текущий цвет фона заголовка
color m_header_fore_color; // Цвет текста заголовка
color m_header_fore_color_c; // Текущий цвет текста заголовка
color m_header_border_color; // Цвет рамки заголовка
color m_header_border_color_c; // Текущий цвет рамки заголовка
color m_butt_close_back_color; // Цвет фона кнопки закрытия
color m_butt_close_back_color_c; // Текущий цвет фона кнопки закрытия
color m_butt_close_fore_color; // Цвет значка кнопки закрытия
color m_butt_close_fore_color_c; // Текущий цвет значка кнопки закрытия
color m_butt_min_back_color; // Цвет фона кнопки сворачивания/разворачивания
color m_butt_min_back_color_c; // Текущий цвет фона кнопки сворачивания/разворачивания
color m_butt_min_fore_color; // Цвет значка кнопки сворачивания/разворачивания
color m_butt_min_fore_color_c; // Текущий цвет значка кнопки сворачивания/разворачивания
color m_butt_pin_back_color; // Цвет фона кнопки закрепления
color m_butt_pin_back_color_c; // Текущий цвет фона кнопки закрепления
color m_butt_pin_fore_color; // Цвет значка кнопки закрепления
color m_butt_pin_fore_color_c; // Текущий цвет значка кнопки закрепления
uchar m_alpha; // Прозрачность панели
uchar m_alpha_c; // Текущая прозрачность панели
uchar m_fore_alpha; // Прозрачность текста
uchar m_fore_alpha_c; // Текущая прозрачность текста
color m_back_color; // Цвет фона
color m_back_color_c; // Текущий цвет фона
color m_fore_color; // Цвет текста
color m_fore_color_c; // Текущий цвет текста
color m_border_color; // Цвет рамки
color m_border_color_c; // Текущий цвет рамки
string m_title; // Текст заголовка
string m_title_font; // Фонт заголовка
int m_title_font_size; // Размер шрифта заголовка
string m_font; // Фонт
int m_font_size; // Размер шрифта
bool m_minimized; // Флаг свёрнутого окна панели
string m_program_name; // Имя программы
string m_name_gv_x; // Наименование глобальной переменной терминала, хранящей координату X
string m_name_gv_y; // Наименование глобальной переменной терминала, хранящей координату Y
string m_name_gv_m; // Наименование глобальной переменной терминала, хранящей флаг свёрнутости панели
string m_name_gv_u; // Наименование глобальной переменной терминала, хранящей флаг закреплённой панели
uint m_array_wpx[]; // Массив пикселей для сохранения/восстановления рабочей области
uint m_array_ppx[]; // Массив пикселей для сохранения/восстановления фона панели
//--- Возвращает флаг превышения (1) высотой, (2) шириной панели соответствующих размеров графика
bool HigherWnd(void) const { return(this.m_h+2>this.m_chart_h); }
bool WiderWnd(void) const { return(this.m_w+2>this.m_chart_w); }
//--- Включает/выключает режимы работы с графиком
void SetChartsTool(const bool flag);
//--- Сохраняет (1) рабочую область, (2) фон панели в массив пикселей
void SaveWorkspace(void);
void SaveBackground(void);
//--- Восстанавливает (1) рабочую область, (2) фон панели из массива пикселей
void RestoreWorkspace(void);
void RestoreBackground(void);
//--- Сохраняет массив пикселей (1) рабочей области, (2) фона панели в файл
bool FileSaveWorkspace(void);
bool FileSaveBackground(void);
//--- Загружает массив пикселей (1) рабочей области, (2) фона панели из файла
bool FileLoadWorkspace(void);
bool FileLoadBackground(void);
//--- Возвращает номер подокна
int GetSubWindow(void) const
{
return(this.m_program_type==PROGRAM_EXPERT || this.m_program_type==PROGRAM_SCRIPT ? 0 : ::ChartWindowFind());
}
protected:
//--- (1) Скрывает, (2) показывает, (3) переносит на передний план панель
void Hide(const bool redraw=false);
void Show(const bool redraw=false);
void BringToTop(void);
//--- Возвращает идентификатор графика
long ChartID(void) const { return this.m_chart_id; }
//--- Рисует область заголовка
void DrawHeaderArea(const string title);
//--- Перерисовывает область заголовка с новыми значениями цвета и текста
void RedrawHeaderArea(const color new_color=clrNONE,const string title="",const color title_new_color=clrNONE,const ushort new_alpha=USHORT_MAX);
//--- Рисует рамку панели
void DrawFrame(void);
//--- (1) Рисует, (2) перерисовывает кнопку закрытия панели
void DrawButtonClose(void);
void RedrawButtonClose(const color new_back_color=clrNONE,const color new_fore_color=clrNONE,const ushort new_alpha=USHORT_MAX);
//--- (1) Рисует, (2) перерисовывает кнопку сворачивания/разворачивания панели
void DrawButtonMinimize(void);
void RedrawButtonMinimize(const color new_back_color=clrNONE,const color new_fore_color=clrNONE,const ushort new_alpha=USHORT_MAX);
//--- (1) Рисует, (2) перерисовывает кнопку закрепления панели
void DrawButtonPin(void);
void RedrawButtonPin(const color new_back_color=clrNONE,const color new_fore_color=clrNONE,const ushort new_alpha=USHORT_MAX);
//--- Возвращает флаг работы в визуальном тестере
bool IsVisualMode(void) const
{ return (bool)::MQLInfoInteger(MQL_VISUAL_MODE); }
//--- Возвращает описание таймфрейма
string TimeframeDescription(const ENUM_TIMEFRAMES timeframe) const
{ return ::StringSubstr(EnumToString(timeframe),7); }
//--- Возвращает состояние кнопок мышки
ENUM_MOUSE_STATE MouseButtonState(const int x,const int y,bool pressed);
//--- Смещает панель на новые координаты
void Move(int x,int y);
//--- Преобразует RGB в color
color RGBToColor(const double r,const double g,const double b) const;
//--- Записывает в переменные значения компонентов RGB
void ColorToRGB(const color clr,double &r,double &g,double &b);
//--- Возвращает составляющую цвета (1) Red, (2) Green, (3) Blue
double GetR(const color clr) { return clr&0xff ; }
double GetG(const color clr) { return(clr>>8)&0xff; }
double GetB(const color clr) { return(clr>>16)&0xff; }
//--- Возвращает новый цвет
color NewColor(color base_color, int shift_red, int shift_green, int shift_blue);
//--- Рисует панель
void Draw(const string title);
//--- (1) Сворачивает, (2) разворачивает панель
void Collapse(void);
void Expand(void);
//--- Устанавливает координату (1) X, (2) Y панели
bool SetCoordX(const int coord_x);
bool SetCoordY(const int coord_y);
//--- Устанавливает (1) ширину, (2) высоту панели
bool SetWidth(const int width,const bool redraw=false);
bool SetHeight(const int height,const bool redraw=false);
public:
//--- Отображает панель
void View(const string title) { this.Draw(title); }
//--- Возвращает объект (1) CCanvas, (2) рабочую область, (3) идентификатор объекта
CCanvas *Canvas(void) { return &this.m_canvas; }
CCanvas *Workspace(void) { return &this.m_workspace; }
uint ID(void) { return this.m_id; }
//--- Возвращает координату (1) X, (2) Y панели
int CoordX(void) const { return this.m_x; }
int CoordY(void) const { return this.m_y; }
//--- Возвращает (1) ширину, (2) высоту панели
int Width(void) const { return this.m_w; }
int Height(void) const { return this.m_h; }
//--- Возвращает (1) ширину, (2) высоту, (3) размеры указанного текста
int TextWidth(const string text)
{ return this.m_workspace.TextWidth(text); }
int TextHeight(const string text)
{ return this.m_workspace.TextHeight(text); }
void TextSize(const string text,int &width,int &height)
{ this.m_workspace.TextSize(text,width,height); }
//--- Устанавливает флаг (1) наличия, (2) отсутствия кнопки закрытия
void SetButtonCloseOn(void);
void SetButtonCloseOff(void);
//--- Устанавливает флаг (1) наличия, (2) отсутствия кнопки сворачивания/разворачивания
void SetButtonMinimizeOn(void);
void SetButtonMinimizeOff(void);
//--- Устанавливает координаты панели
bool SetCoords(const int x,const int y);
//--- Устанавливает размеры панели
bool SetSizes(const int w,const int h,const bool update=false);
//--- Устанавливает координаты и размеры панели
bool SetParams(const int x,const int y,const int w,const int h,const bool update=false);
//--- Устанавливает прозрачность (1) заголовка, (2) рабочей области панели
void SetHeaderTransparency(const uchar value);
void SetTransparency(const uchar value);
//--- Устанавливает параметры шрифта панели по умолчанию
void SetFontParams(const string name,const int size,const uint flags=0,const uint angle=0);
//--- Выводит текстовое сообщение в указанные координаты
void DrawText(const string text,const int x,const int y,const int width=WRONG_VALUE,const int height=WRONG_VALUE);
//--- Рисует (1) фоновую сетку, (2) с автоматическим размером ячеек
void DrawGrid(const uint x,const uint y,const uint rows,const uint columns,const uint row_size,const uint col_size,const color line_color=clrNONE,bool alternating_color=true);
void DrawGridAutoFill(const uint border,const uint rows,const uint columns,const color line_color=clrNONE,bool alternating_color=true);
//--- Распечатывает данные сетки (координаты пересечения линий)
void GridPrint(const uint indent=0) { this.m_table_data.Print(indent); }
//--- Записывает в переменные значения координат X и Y указанной ячейки таблицы
void CellXY(const uint row,const uint column, int &x, int &y) { this.m_table_data.CellXY(row,column,x,y); }
//--- Возвращает координату (1) X, (2) Y указанной ячейки таблицы
int CellX(const uint row,const uint column) { return this.m_table_data.CellX(row,column); }
int CellY(const uint row,const uint column) { return this.m_table_data.CellY(row,column); }
//--- Обработчик событий
void OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
//--- Конструктор/Деструктор
CDashboard(const uint id,const int x,const int y, const int w,const int h,const int wnd=-1);
~CDashboard();
};
//+------------------------------------------------------------------+
//| Конструктор |
//+------------------------------------------------------------------+
CDashboard::CDashboard(const uint id,const int x,const int y, const int w,const int h,const int wnd=-1) :
m_id(id),
m_chart_id(::ChartID()),
m_program_type((ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE)),
m_program_name(::MQLInfoString(MQL_PROGRAM_NAME)),
m_wnd(wnd==-1 ? GetSubWindow() : wnd),
m_chart_w((int)::ChartGetInteger(m_chart_id,CHART_WIDTH_IN_PIXELS,m_wnd)),
m_chart_h((int)::ChartGetInteger(m_chart_id,CHART_HEIGHT_IN_PIXELS,m_wnd)),
m_mouse_state(MOUSE_STATE_NOT_PRESSED),
m_x(x),
m_y(::ChartGetInteger(m_chart_id,CHART_SHOW_ONE_CLICK) ? (y<79 ? 79 : y) : y),
m_w(w),
m_h(h),
m_x_dock(m_x),
m_y_dock(m_y),
m_header(true),
m_butt_close(true),
m_butt_minimize(true),
m_butt_pin(true),
m_header_h(18),
//--- Оформление заголовка панели
m_header_alpha(128),
m_header_alpha_c(m_header_alpha),
m_header_back_color(C'0,153,188'),
m_header_back_color_c(m_header_back_color),
m_header_fore_color(C'182,255,244'),
m_header_fore_color_c(m_header_fore_color),
m_header_border_color(C'167,167,168'),
m_header_border_color_c(m_header_border_color),
m_title("Dashboard"),
m_title_font("Calibri"),
m_title_font_size(-100),
//--- кнопка закрытия
m_butt_close_back_color(C'0,153,188'),
m_butt_close_back_color_c(m_butt_close_back_color),
m_butt_close_fore_color(clrWhite),
m_butt_close_fore_color_c(m_butt_close_fore_color),
//--- кнопка сворачивания/разворачивания
m_butt_min_back_color(C'0,153,188'),
m_butt_min_back_color_c(m_butt_min_back_color),
m_butt_min_fore_color(clrWhite),
m_butt_min_fore_color_c(m_butt_min_fore_color),
//--- кнопка закрепления
m_butt_pin_back_color(C'0,153,188'),
m_butt_pin_back_color_c(m_butt_min_back_color),
m_butt_pin_fore_color(clrWhite),
m_butt_pin_fore_color_c(m_butt_min_fore_color),
//--- Оформление панели
m_alpha(240),
m_alpha_c(m_alpha),
m_fore_alpha(255),
m_fore_alpha_c(m_fore_alpha),
m_back_color(C'240,240,240'),
m_back_color_c(m_back_color),
m_fore_color(C'53,0,0'),
m_fore_color_c(m_fore_color),
m_border_color(C'167,167,168'),
m_border_color_c(m_border_color),
m_font("Calibri"),
m_font_size(-100),
m_minimized(false),
m_movable(true)
{
//--- Устанавливаем для графика разрешения на отправку сообщений о событиях перемещения и нажатия кнопок мышки,
//--- о событиях колёсика мышки и событиях создания и удаления графического объекта
::ChartSetInteger(this.m_chart_id,CHART_EVENT_MOUSE_MOVE,true);
::ChartSetInteger(this.m_chart_id,CHART_EVENT_MOUSE_WHEEL,true);
::ChartSetInteger(this.m_chart_id,CHART_EVENT_OBJECT_CREATE,true);
::ChartSetInteger(this.m_chart_id,CHART_EVENT_OBJECT_DELETE,true);
//--- Задаём имена глобальным переменным терминала для хранения координат панели, состояния свёрнуто/развернуто и закрепления
this.m_name_gv_x=this.m_program_name+"_id_"+(string)this.m_id+"_"+(string)this.m_chart_id+"_X";
this.m_name_gv_y=this.m_program_name+"_id_"+(string)this.m_id+"_"+(string)this.m_chart_id+"_Y";
this.m_name_gv_m=this.m_program_name+"_id_"+(string)this.m_id+"_"+(string)this.m_chart_id+"_Minimize";
this.m_name_gv_u=this.m_program_name+"_id_"+(string)this.m_id+"_"+(string)this.m_chart_id+"_Unpin";
//--- Если глобальной переменной не существует - создаём её и записываем текущее значение,
//--- иначе - считываем в неё значение из глобальной переменной терминала
//--- Координата X
if(!::GlobalVariableCheck(this.m_name_gv_x))
::GlobalVariableSet(this.m_name_gv_x,this.m_x);
else
this.m_x=(int)::GlobalVariableGet(this.m_name_gv_x);
//--- Координата Y
if(!::GlobalVariableCheck(this.m_name_gv_y))
::GlobalVariableSet(this.m_name_gv_y,this.m_y);
else
this.m_y=(int)::GlobalVariableGet(this.m_name_gv_y);
//--- Свёрнуто/развёрнуто
if(!::GlobalVariableCheck(this.m_name_gv_m))
::GlobalVariableSet(this.m_name_gv_m,this.m_minimized);
else
this.m_minimized=(int)::GlobalVariableGet(this.m_name_gv_m);
//--- Закреплено/не закреплено
if(!::GlobalVariableCheck(this.m_name_gv_u))
::GlobalVariableSet(this.m_name_gv_u,this.m_movable);
else
this.m_movable=(int)::GlobalVariableGet(this.m_name_gv_u);
//--- Устанавливаем флаги превышения размерами панели размеров окна графика
this.m_higher_wnd=this.HigherWnd();
this.m_wider_wnd=this.WiderWnd();
//--- Если графический ресурс панели создан,
if(this.m_canvas.CreateBitmapLabel(this.m_chart_id,this.m_wnd,"P"+(string)this.m_id,this.m_x,this.m_y,this.m_w,this.m_h,COLOR_FORMAT_ARGB_NORMALIZE))
{
//--- устанавливаем для канваса шрифт и заполняем канвас прозрачным цветом
this.m_canvas.FontSet(this.m_title_font,this.m_title_font_size,FW_BOLD);
this.m_canvas.Erase(0x00FFFFFF);
}
//--- иначе - сообщаем о неудачном создании объекта в журнал
else
::PrintFormat("%s: Error. CreateBitmapLabel for canvas failed",(string)__FUNCTION__);
//--- Если графический ресурс рабочей области создан,
if(this.m_workspace.CreateBitmapLabel(this.m_chart_id,this.m_wnd,"W"+(string)this.m_id,this.m_x+1,this.m_y+this.m_header_h,this.m_w-2,this.m_h-this.m_header_h-1,COLOR_FORMAT_ARGB_NORMALIZE))
{
//--- устанавливаем для рабочей области шрифт и заполняем рабочую область прозрачным цветом
this.m_workspace.FontSet(this.m_font,this.m_font_size);
this.m_workspace.Erase(0x00FFFFFF);
}
//--- иначе - сообщаем о неудачном создании объекта в журнал
else
::PrintFormat("%s: Error. CreateBitmapLabel for workspace failed",(string)__FUNCTION__);
}
//+------------------------------------------------------------------+
//| Деструктор |
//+------------------------------------------------------------------+
CDashboard::~CDashboard()
{
//--- Записываем текущие значения в глобальные переменные терминала
::GlobalVariableSet(this.m_name_gv_x,this.m_x);
::GlobalVariableSet(this.m_name_gv_y,this.m_y);
::GlobalVariableSet(this.m_name_gv_m,this.m_minimized);
::GlobalVariableSet(this.m_name_gv_u,this.m_movable);
//--- Удаляем объекты панели
this.m_canvas.Destroy();
this.m_workspace.Destroy();
}
//+------------------------------------------------------------------+
//| Возвращает состояние курсора и кнопки мыши |
//+------------------------------------------------------------------+
ENUM_MOUSE_STATE CDashboard::MouseButtonState(const int x,const int y,bool pressed)
{
//--- Если кнопка нажата
if(pressed)
{
//--- Если уже зафиксировано состояние - уходим
if(this.m_mouse_state!=MOUSE_STATE_NOT_PRESSED)
return this.m_mouse_state;
//--- Если нажата кнопка внутри окна
if(x>this.m_x && x<this.m_x+this.m_w && y>this.m_y && y<this.m_y+this.m_h)
{
//--- Если нажата кнопка внутри заголовка
if(y>this.m_y && y<=this.m_y+this.m_header_h)
{
//--- Выводим панель на передний план
this.BringToTop();
//--- Координаты кнопок закрытия, сворачивания/разворачивания и закрепления
int wc=(this.m_butt_close ? this.m_header_h : 0);
int wm=(this.m_butt_minimize ? this.m_header_h : 0);
int wp=(this.m_butt_pin ? this.m_header_h : 0);
//--- Если нажата кнопка закрытия - возвращаем это состояние
if(x>this.m_x+this.m_w-wc)
return MOUSE_STATE_PRESSED_INSIDE_CLOSE;
//--- Если нажата кнопка сворачивания/разворачивания - возвращаем это состояние
if(x>this.m_x+this.m_w-wc-wm)
return MOUSE_STATE_PRESSED_INSIDE_MINIMIZE;
//--- Если нажата кнопка закрепления - возвращаем это состояние
if(x>this.m_x+this.m_w-wc-wm-wp)
return MOUSE_STATE_PRESSED_INSIDE_PIN;
//--- Если кнопка нажата не на управляющих кнопках панели - записываем и возвращаем состояние нажатия кнопки внутри заголовка
this.m_mouse_state=MOUSE_STATE_PRESSED_INSIDE_HEADER;
return this.m_mouse_state;
}
//--- Если нажата кнопка внутри окна - записываем состояние в переменную и возвращаем это состояние
else if(y>this.m_y+this.m_header_h && y<this.m_y+this.m_h)
{
this.m_mouse_state=MOUSE_STATE_PRESSED_INSIDE_WINDOW;
return this.m_mouse_state;
}
}
//--- Кнопка нажата вне пределов окна - записываем состояние в переменную и возвращаем это состояние
else
{
this.m_mouse_state=MOUSE_STATE_PRESSED_OUTSIDE_WINDOW;
return this.m_mouse_state;
}
}
//--- Если кнопка не нажата
else
{
//--- Записываем в переменную состояние не нажатой кнопки
this.m_mouse_state=MOUSE_STATE_NOT_PRESSED;
//--- Если курсор внутри панели
if(x>this.m_x && x<this.m_x+this.m_w && y>this.m_y && y<this.m_y+this.m_h)
{
//--- Если курсор внутри заголовка
if(y>this.m_y && y<=this.m_y+this.m_header_h)
{
//--- Указываем ширину кнопок закрытия, сворачивания/разворачивания и закрепления
int wc=(this.m_butt_close ? this.m_header_h : 0);
int wm=(this.m_butt_minimize ? this.m_header_h : 0);
int wp=(this.m_butt_pin ? this.m_header_h : 0);
//--- Если курсор внутри кнопки закрытия - возвращаем это состояние
if(x>this.m_x+this.m_w-wc)
return MOUSE_STATE_INSIDE_CLOSE;
//--- Если курсор внутри кнопки сворачивания/разворачивания - возвращаем это состояние
if(x>this.m_x+this.m_w-wc-wm)
return MOUSE_STATE_INSIDE_MINIMIZE;
//--- Если курсор внутри кнопки закрепления - возвращаем это состояние
if(x>this.m_x+this.m_w-wc-wm-wp)
return MOUSE_STATE_INSIDE_PIN;
//--- Если курсор за пределами кнопок внутри области заголовка - возвращаем это состояние
return MOUSE_STATE_INSIDE_HEADER;
}
//--- Иначе - Курсор внутри рабочей области - возвращаем это состояние
else
return MOUSE_STATE_INSIDE_WINDOW;
}
}
//--- В любом ином случае возвращаем состояние не нажатой кнопки мышки
return MOUSE_STATE_NOT_PRESSED;
}
//+------------------------------------------------------------------+
//| Обработчик событий |
//+------------------------------------------------------------------+
void CDashboard::OnChartEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
{
//--- Если создан графический объект
if(id==CHARTEVENT_OBJECT_CREATE)
{
this.BringToTop();
::ObjectSetInteger(this.m_chart_id,sparam,OBJPROP_SELECTED,true);
}
//--- Если график изменён
if(id==CHARTEVENT_CHART_CHANGE)
{
//--- Получаем номер подокна графика (он может измениться при удалении окна какого-либо индикатора)
this.m_wnd=this.GetSubWindow();
long value;
//--- сбросим значение ошибки
::ResetLastError();
//--- получим значение свойства
if(!::ChartGetInteger(m_chart_id, CHART_BRING_TO_TOP, m_wnd, value))
{
//--- выведем сообщение об ошибке в журнал "Эксперты"
Print(__FUNCTION__ + ", Error Code = ", ::GetLastError());
return;
}
// если это не показ графика поверх всех других - выходим
if(value == 0)
return;
2026-03-19 00:56:24 +07:00
//--- Получаем новые размеры графика
int w=(int)::ChartGetInteger(this.m_chart_id,CHART_WIDTH_IN_PIXELS,this.m_wnd);
int h=(int)::ChartGetInteger(this.m_chart_id,CHART_HEIGHT_IN_PIXELS,this.m_wnd);
//--- Определяем выход размеров панели за пределы окна графика
//--- Если высота графика изменилась - корректируем расположение панели по вертикали
if(this.m_chart_h!=h)
{
this.m_chart_h=h;
int y=this.m_y;
if(this.m_y+this.m_h>h-1)
y=h-this.m_h-1;
if(y<1)
y=1;
this.m_higher_wnd=this.HigherWnd();
2026-03-19 00:56:24 +07:00
this.Move(this.m_x,y);
}
//--- Если ширина графика изменилась - корректируем расположение панели по горизонтали
if(this.m_chart_w!=w)
{
this.m_chart_w=w;
int x=this.m_x;
if(this.m_x+this.m_w>w-1)
x=w-this.m_w-1;
if(x<1)
x=1;
this.m_wider_wnd=this.WiderWnd();
2026-03-19 00:56:24 +07:00
this.Move(x,this.m_y);
}
}
//--- Объявляем переменные для хранения текущего смещения курсора относительно начальных координат панели
static int diff_x=0;
static int diff_y=0;
//--- Получаем флаг удерживаемой кнопки мышки. Для визуального тестера правую кнопку тоже учитываем (sparam=="2")
bool pressed=(!this.IsVisualMode() ? (sparam=="1" || sparam=="" ? true : false) : sparam=="1" || sparam=="2" ? true : false);
//--- Получаем координаты X и Y курсора. Для координаты Y учитываем смещение при работе в подокне графика
int mouse_x=(int)lparam;
int mouse_y=(int)dparam-(int)::ChartGetInteger(this.m_chart_id,CHART_WINDOW_YDISTANCE,this.m_wnd);
//--- Получаем состояние курсора и кнопок мышки относительно панели
ENUM_MOUSE_STATE state=this.MouseButtonState(mouse_x,mouse_y,pressed);
//--- Если курсор перемещается
if(id==CHARTEVENT_MOUSE_MOVE)
{
//--- Если нажата кнопка внутри рабочей области панели
if(state==MOUSE_STATE_PRESSED_INSIDE_WINDOW)
{
//--- Отключаем прокрутку графика, меню правой кнопки мышки и перекрестие
this.SetChartsTool(false);
//--- Перерисовываем область заголовка с цветом фона по умолчанию
if(this.m_header_back_color_c!=this.m_header_back_color)
{
this.RedrawHeaderArea(this.m_header_back_color);
this.m_canvas.Update();
}
return;
}
//--- Если нажата кнопка внутри области заголовка панели
else if(state==MOUSE_STATE_PRESSED_INSIDE_HEADER)
{
//--- Отключаем прокрутку графика, меню правой кнопки мышки и перекрестие
this.SetChartsTool(false);
//--- Перерисовываем область заголовка с новым цветом фона
color new_color=this.NewColor(this.m_header_back_color,-10,-10,-10);
if(this.m_header_back_color_c!=new_color)
{
this.RedrawHeaderArea(new_color);
this.m_canvas.Update();
}
//--- Смещаем панель вслед за курсором с учётом величины смещения курсора относительно начальных координат панели
if(this.m_movable)
this.Move(mouse_x-diff_x,mouse_y-diff_y);
return;
}
//--- Если нажата кнопка закрытия
else if(state==MOUSE_STATE_PRESSED_INSIDE_CLOSE)
{
//--- Отключаем прокрутку графика, меню правой кнопки и перекрестие
this.SetChartsTool(false);
//--- Перерисовываем кнопку закрытия с новым цветом фона
color new_color=this.NewColor(clrRed,0,40,40);
if(this.m_butt_close_back_color_c!=new_color)
{
this.RedrawButtonClose(new_color);
this.m_canvas.Update();
}
//--- Обработка нажатия кнопки закрытия должна определяться в программе.
//--- Отправим в её обработчик OnChartEvent событие нажатия этой кнопки.
//--- Идентификатор события 1001,
//--- lparam=идентификатор панели (m_id),
//--- dparam=0
//--- sparam="Close button pressed"
ushort event=CHARTEVENT_CUSTOM+1;
::EventChartCustom(this.m_chart_id,ushort(event-CHARTEVENT_CUSTOM),this.m_id,0,"Close button pressed");
}
//--- Если нажата кнопка сворачивания/разворачивания панели
else if(state==MOUSE_STATE_PRESSED_INSIDE_MINIMIZE)
{
//--- Отключаем прокрутку графика, меню правой кнопки и перекрестие
this.SetChartsTool(false);
//--- "переворачиваем" флаг свёрнутости панели,
this.m_minimized=!this.m_minimized;
//--- перерисовываем панель с учётом нового состояния флага,
this.Draw(this.m_title);
//--- перерисовываем область заголовка панели
this.RedrawHeaderArea();
//--- Если панель закреплена и развёрнута - переместим её в запомненные координаты расположения
if(this.m_minimized && !this.m_movable)
this.Move(this.m_x_dock,this.m_y_dock);
//--- Обновляем канвас с перерисовкой графика и
this.m_canvas.Update();
//--- записываем в глобальную переменную терминала состояние флага свёрнутости панели
::GlobalVariableSet(this.m_name_gv_m,this.m_minimized);
}
//--- Если нажата кнопка закрепления панели
else if(state==MOUSE_STATE_PRESSED_INSIDE_PIN)
{
//--- Отключаем прокрутку графика, меню правой кнопки и перекрестие
this.SetChartsTool(false);
//--- "переворачиваем" флаг свёрнутости панели,
this.m_movable=!this.m_movable;
//--- Перерисовываем кнопку закрепления с новым цветом фона
color new_color=this.NewColor(this.m_butt_pin_back_color,30,30,30);
if(this.m_butt_pin_back_color_c!=new_color)
this.RedrawButtonPin(new_color);
//--- Если панель свёрнута и закреплена - запомним её координаты
//--- При разворачивании и повторном сворачивании панель вернётся на эти координаты
//--- Актуально для закрепления свёрнутой панели внизу экрана
if(this.m_minimized && !this.m_movable)
{
this.m_x_dock=this.m_x;
this.m_y_dock=this.m_y;
}
//--- Обновляем канвас с перерисовкой графика и
this.m_canvas.Update();
//--- записываем в глобальную переменную терминала состояние флага перемещаемости панели
::GlobalVariableSet(this.m_name_gv_u,this.m_movable);
}
//--- Если курсор находится внутри области заголовка панели
else if(state==MOUSE_STATE_INSIDE_HEADER)
{
//--- Отключаем прокрутку графика, меню правой кнопки и перекрестие
this.SetChartsTool(false);
//--- Перерисовываем область заголовка с новым цветом фона
color new_color=this.NewColor(this.m_header_back_color,20,20,20);
if(this.m_header_back_color_c!=new_color)
{
this.RedrawHeaderArea(new_color);
this.m_canvas.Update();
}
}
//--- Если курсор находится внутри кнопки закрытия
else if(state==MOUSE_STATE_INSIDE_CLOSE)
{
//--- Отключаем прокрутку графика, меню правой кнопки и перекрестие
this.SetChartsTool(false);
//--- Перерисовываем область заголовка с минимальным изменением цвета фона
color new_color=this.NewColor(this.m_header_back_color,0,0,1);
if(this.m_header_back_color_c!=new_color)
this.RedrawHeaderArea(new_color);
//--- Перерисовываем кнопку сворачивания/разворачивания с цветом фона по умолчанию
if(this.m_butt_min_back_color_c!=this.m_butt_min_back_color)
this.RedrawButtonMinimize(this.m_butt_min_back_color);
//--- Перерисовываем кнопку закрепления с цветом фона по умолчанию
if(this.m_butt_pin_back_color_c!=this.m_butt_pin_back_color)
this.RedrawButtonPin(this.m_butt_pin_back_color);
//--- Перерисовываем кнопку закрытия с красным цветом фона
if(this.m_butt_close_back_color_c!=clrRed)
{
this.RedrawButtonClose(clrRed);
this.m_canvas.Update();
}
}
//--- Если курсор находится внутри кнопки сворачивания/разворачивания
else if(state==MOUSE_STATE_INSIDE_MINIMIZE)
{
//--- Отключаем прокрутку графика, меню правой кнопки и перекрестие
this.SetChartsTool(false);
//--- Перерисовываем область заголовка с минимальным изменением цвета фона
color new_color=this.NewColor(this.m_header_back_color,0,0,1);
if(this.m_header_back_color_c!=new_color)
this.RedrawHeaderArea(new_color);
//--- Перерисовываем кнопку закрытия с цветом фона по умолчанию
if(this.m_butt_close_back_color_c!=this.m_butt_close_back_color)
this.RedrawButtonClose(this.m_butt_close_back_color);
//--- Перерисовываем кнопку закрепления с цветом фона по умолчанию
if(this.m_butt_pin_back_color_c!=this.m_butt_pin_back_color)
this.RedrawButtonPin(this.m_butt_pin_back_color);
//--- Перерисовываем кнопку сворачивания/разворачивания с новым цветом фона
new_color=this.NewColor(this.m_butt_min_back_color,20,20,20);
if(this.m_butt_min_back_color_c!=new_color)
{
this.RedrawButtonMinimize(new_color);
this.m_canvas.Update();
}
}
//--- Если курсор находится внутри кнопки закрепления
else if(state==MOUSE_STATE_INSIDE_PIN)
{
//--- Отключаем прокрутку графика, меню правой кнопки и перекрестие
this.SetChartsTool(false);
//--- Перерисовываем область заголовка с минимальным изменением цвета фона
color new_color=this.NewColor(this.m_header_back_color,0,0,1);
if(this.m_header_back_color_c!=new_color)
this.RedrawHeaderArea(new_color);
//--- Перерисовываем кнопку закрытия с цветом фона по умолчанию
if(this.m_butt_close_back_color_c!=this.m_butt_close_back_color)
this.RedrawButtonClose(this.m_butt_close_back_color);
//--- Перерисовываем кнопку сворачивания/разворачивания с цветом фона по умолчанию
if(this.m_butt_min_back_color_c!=this.m_butt_min_back_color)
this.RedrawButtonMinimize(this.m_butt_min_back_color);
//--- Перерисовываем кнопку закрепления с новым цветом фона
new_color=this.NewColor(this.m_butt_pin_back_color,20,20,20);
if(this.m_butt_pin_back_color_c!=new_color)
{
this.RedrawButtonPin(new_color);
this.m_canvas.Update();
}
}
//--- Если курсор находится внутри рабочей области
else if(state==MOUSE_STATE_INSIDE_WINDOW)
{
//--- Отключаем прокрутку графика, меню правой кнопки и перекрестие
this.SetChartsTool(false);
//--- Перерисовываем область заголовка с цветом фона по умолчанию
if(this.m_header_back_color_c!=this.m_header_back_color)
{
this.RedrawHeaderArea(this.m_header_back_color);
this.m_canvas.Update();
}
}
//--- Иначе (курсор за пределами панели, и нужно восстановить параметры графика)
else
{
//--- Включаем прокрутку графика, меню правой кнопки и перекрестие
this.SetChartsTool(true);
//--- Перерисовываем область заголовка с цветом фона по умолчанию
if(this.m_header_back_color_c!=this.m_header_back_color)
{
this.RedrawHeaderArea(this.m_header_back_color);
this.m_canvas.Update();
}
}
//--- Записываем смещение курсора по X и Y относительно начальных координат панели
diff_x=mouse_x-this.m_x;
diff_y=mouse_y-this.m_y;
}
}
//+------------------------------------------------------------------+
//| Перемещает панель |
//+------------------------------------------------------------------+
void CDashboard::Move(int x,int y)
{
int h=this.m_canvas.Height();
int w=this.m_canvas.Width();
if(!this.m_wider_wnd)
{
if(x+w>this.m_chart_w-1)
x=this.m_chart_w-w-1;
if(x<1)
x=1;
}
else
{
if(x>1)
x=1;
if(x<this.m_chart_w-w-1)
x=this.m_chart_w-w-1;
}
if(!this.m_higher_wnd)
{
if(y+h>this.m_chart_h-2)
y=this.m_chart_h-h-2;
if(y<1)
y=1;
}
else
{
if(y>1)
y=1;
if(y<this.m_chart_h-h-2)
y=this.m_chart_h-h-2;
}
if(this.SetCoords(x,y))
this.m_canvas.Update();
}
//+------------------------------------------------------------------+
//| Устанавливает координату X панели |
//+------------------------------------------------------------------+
bool CDashboard::SetCoordX(const int coord_x)
{
int x=(int)::ObjectGetInteger(this.m_chart_id,this.m_canvas.ChartObjectName(),OBJPROP_XDISTANCE);
if(x==coord_x)
return true;
if(!::ObjectSetInteger(this.m_chart_id,this.m_canvas.ChartObjectName(),OBJPROP_XDISTANCE,coord_x))
return false;
if(!::ObjectSetInteger(this.m_chart_id,this.m_workspace.ChartObjectName(),OBJPROP_XDISTANCE,coord_x+1))
return false;
this.m_x=coord_x;
return true;
}
//+------------------------------------------------------------------+
//| Устанавливает координату Y панели |
//+------------------------------------------------------------------+
bool CDashboard::SetCoordY(const int coord_y)
{
int y=(int)::ObjectGetInteger(this.m_chart_id,this.m_canvas.ChartObjectName(),OBJPROP_YDISTANCE);
if(y==coord_y)
return true;
if(!::ObjectSetInteger(this.m_chart_id,this.m_canvas.ChartObjectName(),OBJPROP_YDISTANCE,coord_y))
return false;
if(!::ObjectSetInteger(this.m_chart_id,this.m_workspace.ChartObjectName(),OBJPROP_YDISTANCE,coord_y+this.m_header_h))
return false;
this.m_y=coord_y;
return true;
}
//+------------------------------------------------------------------+
//| Устанавливает ширину панели |
//+------------------------------------------------------------------+
bool CDashboard::SetWidth(const int width,const bool redraw=false)
{
if(width<4)
{
::PrintFormat("%s: Error. Width cannot be less than 4px",(string)__FUNCTION__);
return false;
}
if(width==this.m_canvas.Width())
return true;
if(!this.m_canvas.Resize(width,this.m_canvas.Height()))
return false;
if(width-2<1)
::ObjectSetInteger(this.m_chart_id,this.m_workspace.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS);
else
{
::ObjectSetInteger(this.m_chart_id,this.m_workspace.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);
if(!this.m_workspace.Resize(width-2,this.m_workspace.Height()))
return false;
}
this.m_w=width;
return true;
}
//+------------------------------------------------------------------+
//| Устанавливает высоту панели |
//+------------------------------------------------------------------+
bool CDashboard::SetHeight(const int height,const bool redraw=false)
{
if(height<::fmax(this.m_header_h,1))
{
::PrintFormat("%s: Error. Width cannot be less than %lupx",(string)__FUNCTION__,::fmax(this.m_header_h,1));
return false;
}
if(height==this.m_canvas.Height())
return true;
if(!this.m_canvas.Resize(this.m_canvas.Width(),height))
return false;
if(height-this.m_header_h-2<1)
::ObjectSetInteger(this.m_chart_id,this.m_workspace.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS);
else
{
::ObjectSetInteger(this.m_chart_id,this.m_workspace.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);
if(!this.m_workspace.Resize(this.m_workspace.Width(),height-this.m_header_h-2))
return false;
}
this.m_h=height;
return true;
}
//+------------------------------------------------------------------+
//| Устанавливает координаты панели |
//+------------------------------------------------------------------+
bool CDashboard::SetCoords(const int x,const int y)
{
bool res=true;
res &=this.SetCoordX(x);
res &=this.SetCoordY(y);
return res;
}
//+------------------------------------------------------------------+
//| Устанавливает размеры панели |
//+------------------------------------------------------------------+
bool CDashboard::SetSizes(const int w,const int h,const bool update=false)
{
bool res=true;
res &=this.SetWidth(w);
res &=this.SetHeight(h);
if(res && update)
this.Expand();
return res;
}
//+------------------------------------------------------------------+
//| Устанавливает координаты и размеры панели |
//+------------------------------------------------------------------+
bool CDashboard::SetParams(const int x,const int y,const int w,const int h,const bool update=false)
{
bool res=true;
res &=this.SetCoords(x,y);
res &=this.SetSizes(w,h);
if(res && update)
this.Expand();
return res;
}
//+------------------------------------------------------------------+
//| Рисует область заголовка |
//+------------------------------------------------------------------+
void CDashboard::DrawHeaderArea(const string title)
{
//--- Если заголовок не используется - уходим
if(!this.m_header)
return;
//--- Устанавливаем текст заголовка
this.m_title=title;
//--- Координата Y текста расположена по вертикали по центру области заголовка
int y=this.m_header_h/2;
//--- Заполняем область цветом
this.m_canvas.FillRectangle(0,0,this.m_w-1,this.m_header_h-1,::ColorToARGB(this.m_header_back_color,this.m_header_alpha));
//--- Выводим текст заголовка
this.m_canvas.TextOut(2,y,this.m_title,::ColorToARGB(this.m_header_fore_color,this.m_header_alpha),TA_LEFT|TA_VCENTER);
//--- Запоминаем текущий цвет фона заголовка
this.m_header_back_color_c=this.m_header_back_color;
//--- Рисуем управляющие элементы (кнопки закрытия, сворачивания/разворачивания и закрепления) и
this.DrawButtonClose();
this.DrawButtonMinimize();
this.DrawButtonPin();
//--- обновляем канвас без перерисовки экрана
this.m_canvas.Update(false);
}
//+------------------------------------------------------------------+
//| Перерисовывает область заголовка |
//+------------------------------------------------------------------+
void CDashboard::RedrawHeaderArea(const color new_color=clrNONE,const string title="",const color title_new_color=clrNONE,const ushort new_alpha=USHORT_MAX)
{
//--- Если заголовок не используется или все переданные параметры имеют значения по умолчанию - уходим
if(!this.m_header || (new_color==clrNONE && title=="" && title_new_color==clrNONE && new_alpha==USHORT_MAX))
return;
//--- Если все переданные параметры равны уже установленным - уходим
if(new_color==this.m_header_back_color && title==this.m_title && title_new_color==this.m_header_fore_color && new_alpha==this.m_header_alpha)
return;
//--- Если заголовок не равен значению по умолчанию - устанавливаем новый заголовок
if(title!="")
this.m_title=title;
//--- Определяем новые цвета фона и текста и прозрачность
color back_clr=(new_color!=clrNONE ? new_color : this.m_header_back_color);
color fore_clr=(title_new_color!=clrNONE ? title_new_color : this.m_header_fore_color);
uchar alpha=uchar(new_alpha==USHORT_MAX ? this.m_header_alpha : new_alpha>255 ? 255 : new_alpha);
//--- Координата Y текста расположена по вертикали по центру области заголовка
int y=this.m_header_h/2;
//--- Заполняем область цветом
this.m_canvas.FillRectangle(0,0,this.m_w-1,this.m_header_h-1,::ColorToARGB(back_clr,alpha));
//--- Выводим текст заголовка
this.m_canvas.TextOut(2,y,this.m_title,::ColorToARGB(fore_clr,alpha),TA_LEFT|TA_VCENTER);
//--- Запоминаем текущий цвет фона заголовка, текста и прозрачность
this.m_header_back_color_c=back_clr;
this.m_header_fore_color_c=fore_clr;
this.m_header_alpha_c=alpha;
//--- Рисуем управляющие элементы (кнопки закрытия, сворачивания/разворачивания и закрепления) и
this.RedrawButtonClose(back_clr,clrNONE,alpha);
this.RedrawButtonMinimize(back_clr,clrNONE,alpha);
this.RedrawButtonPin(back_clr,clrNONE,alpha);
//--- обновляем канвас без перерисовки экрана
this.m_canvas.Update(true);
}
//+------------------------------------------------------------------+
//| Рисует рамку панели |
//+------------------------------------------------------------------+
void CDashboard::DrawFrame(void)
{
this.m_canvas.Rectangle(0,0,this.m_w-1,this.m_h-1,::ColorToARGB(this.m_border_color,this.m_alpha));
this.m_border_color_c=this.m_border_color;
this.m_canvas.Update(false);
}
//+------------------------------------------------------------------+
//| Рисует кнопку закрытия панели |
//+------------------------------------------------------------------+
void CDashboard::DrawButtonClose(void)
{
//--- Если кнопка не используется - уходим
if(!this.m_butt_close)
return;
//--- Ширина кнопки равна высоте области заголовка
int w=this.m_header_h;
//--- Координаты и размеры кнопки
int x1=this.m_w-w;
int x2=this.m_w-1;
int y1=0;
int y2=w-1;
//--- Смещение левого верхнего угла прямоугольной области рисунка от левого верхнего угла кнопки
int shift=4;
//--- Рисуем фон кнопки
this.m_canvas.FillRectangle(x1,y1,x2,y2,::ColorToARGB(this.m_butt_close_back_color,this.m_header_alpha));
//--- Рисуем "Крестик" закрытия
this.m_canvas.LineThick(x1+shift+1,y1+shift+1,x2-shift,y2-shift,::ColorToARGB(this.m_butt_close_fore_color,255),3,STYLE_SOLID,LINE_END_ROUND);
this.m_canvas.LineThick(x1+shift+1,y2-shift-1,x2-shift,y1+shift,::ColorToARGB(this.m_butt_close_fore_color,255),3,STYLE_SOLID,LINE_END_ROUND);
//--- Запоминаем текущий цвет фона и рисунка кнопки
this.m_butt_close_back_color_c=this.m_butt_close_back_color;
this.m_butt_close_fore_color_c=this.m_butt_close_fore_color;
//--- обновляем канвас без перерисовки экрана
this.m_canvas.Update(false);
}
//+------------------------------------------------------------------+
//| Перерисовывает кнопку закрытия панели |
//+------------------------------------------------------------------+
void CDashboard::RedrawButtonClose(const color new_back_color=clrNONE,const color new_fore_color=clrNONE,const ushort new_alpha=USHORT_MAX)
{
//--- Если кнопка не используется или все переданные параметры имеют значения по умолчанию - уходим
if(!this.m_butt_close || (new_back_color==clrNONE && new_fore_color==clrNONE && new_alpha==USHORT_MAX))
return;
//--- Ширина кнопки равна высоте области заголовка
int w=this.m_header_h;
//--- Координаты и размеры кнопки
int x1=this.m_w-w;
int x2=this.m_w-1;
int y1=0;
int y2=w-1;
//--- Смещение левого верхнего угла прямоугольной области рисунка от левого верхнего угла кнопки
int shift=4;
//--- Определяем новые цвета фона и текста и прозрачность
color back_color=(new_back_color!=clrNONE ? new_back_color : this.m_butt_close_back_color);
color fore_color=(new_fore_color!=clrNONE ? new_fore_color : this.m_butt_close_fore_color);
uchar alpha=uchar(new_alpha==USHORT_MAX ? this.m_header_alpha : new_alpha>255 ? 255 : new_alpha);
//--- Рисуем фон кнопки
this.m_canvas.FillRectangle(x1,y1,x2,y2,::ColorToARGB(back_color,alpha));
//--- Рисуем "Крестик" закрытия
this.m_canvas.LineThick(x1+shift+1,y1+shift+1,x2-shift,y2-shift,::ColorToARGB(fore_color,255),3,STYLE_SOLID,LINE_END_ROUND);
this.m_canvas.LineThick(x1+shift+1,y2-shift-1,x2-shift,y1+shift,::ColorToARGB(fore_color,255),3,STYLE_SOLID,LINE_END_ROUND);
//--- Запоминаем текущий цвет фона и рисунка кнопки
this.m_butt_close_back_color_c=back_color;
this.m_butt_close_fore_color_c=fore_color;
//--- обновляем канвас без перерисовки экрана
this.m_canvas.Update(false);
}
//+------------------------------------------------------------------+
//| Рисует кнопку сворачивания/разворачивания панели |
//+------------------------------------------------------------------+
void CDashboard::DrawButtonMinimize(void)
{
//--- Если кнопка не используется - уходим
if(!this.m_butt_minimize)
return;
//--- Ширина кнопки равна высоте области заголовка
int w=this.m_header_h;
//--- Ширина кнопки закрытия равна нулю, если кнопка не используется
int wc=(this.m_butt_close ? w : 0);
//--- Координаты и размеры кнопки
int x1=this.m_w-wc-w;
int x2=this.m_w-wc-1;
int y1=0;
int y2=w-1;
//--- Смещение левого верхнего угла прямоугольной области рисунка от левого верхнего угла кнопки
int shift=4;
//--- Рисуем фон кнопки
this.m_canvas.FillRectangle(x1,y1,x2,y2,::ColorToARGB(this.m_butt_min_back_color,this.m_header_alpha));
//--- Если панель свёрнута - рисуем прямоугольник
if(this.m_minimized)
this.m_canvas.Rectangle(x1+shift,y1+shift,x2-shift,y2-shift,::ColorToARGB(this.m_butt_min_fore_color,255));
//--- Иначе - панель развёрнута - рисуем отрезок линии
else
this.m_canvas.LineThick(x1+shift,y2-shift,x2-shift,y2-shift,::ColorToARGB(this.m_butt_min_fore_color,255),3,STYLE_SOLID,LINE_END_ROUND);
//--- Запоминаем текущий цвет фона и рисунка кнопки
this.m_butt_min_back_color_c=this.m_butt_min_back_color;
this.m_butt_min_fore_color_c=this.m_butt_min_fore_color;
//--- обновляем канвас без перерисовки экрана
this.m_canvas.Update(false);
}
//+------------------------------------------------------------------+
//| Перерисовывает кнопку сворачивания/разворачивания панели |
//+------------------------------------------------------------------+
void CDashboard::RedrawButtonMinimize(const color new_back_color=clrNONE,const color new_fore_color=clrNONE,const ushort new_alpha=USHORT_MAX)
{
//--- Если кнопка не используется или все переданные параметры имеют значения по умолчанию - уходим
if(!this.m_butt_minimize || (new_back_color==clrNONE && new_fore_color==clrNONE && new_alpha==USHORT_MAX))
return;
//--- Ширина кнопки равна высоте области заголовка
int w=this.m_header_h;
//--- Ширина кнопки закрытия равна нулю, если кнопка не используется
int wc=(this.m_butt_close ? w : 0);
//--- Координаты и размеры кнопки
int x1=this.m_w-wc-w;
int x2=this.m_w-wc-1;
int y1=0;
int y2=w-1;
//--- Смещение левого верхнего угла прямоугольной области рисунка от левого верхнего угла кнопки
int shift=4;
//--- Определяем новые цвета фона и текста и прозрачность
color back_color=(new_back_color!=clrNONE ? new_back_color : this.m_butt_min_back_color);
color fore_color=(new_fore_color!=clrNONE ? new_fore_color : this.m_butt_min_fore_color);
uchar alpha=uchar(new_alpha==USHORT_MAX ? this.m_header_alpha : new_alpha>255 ? 255 : new_alpha);
//--- Рисуем фон кнопки
this.m_canvas.FillRectangle(x1,y1,x2,y2,::ColorToARGB(back_color,alpha));
//--- Если панель свёрнута - рисуем прямоугольник
if(this.m_minimized)
this.m_canvas.Rectangle(x1+shift,y1+shift,x2-shift,y2-shift,::ColorToARGB(fore_color,255));
//--- Иначе - панель развёрнута - рисуем отрезок линии
else
this.m_canvas.LineThick(x1+shift,y2-shift,x2-shift,y2-shift,::ColorToARGB(fore_color,255),3,STYLE_SOLID,LINE_END_ROUND);
//--- Запоминаем текущий цвет фона и рисунка кнопки
this.m_butt_min_back_color_c=back_color;
this.m_butt_min_fore_color_c=fore_color;
//--- обновляем канвас без перерисовки экрана
this.m_canvas.Update(false);
}
//+------------------------------------------------------------------+
//| Рисует кнопку закрепления панели |
//+------------------------------------------------------------------+
void CDashboard::DrawButtonPin(void)
{
//--- Если кнопка не используется - уходим
if(!this.m_butt_pin)
return;
//--- Ширина кнопки равна высоте области заголовка
int w=this.m_header_h;
//--- Ширина кнопки закрытия и кнопки сворачивания равна нулю, если кнопка не используется
int wc=(this.m_butt_close ? w : 0);
int wm=(this.m_butt_minimize ? w : 0);
//--- Координаты и размеры кнопки
int x1=this.m_w-wc-wm-w;
int x2=this.m_w-wc-wm-1;
int y1=0;
int y2=w-1;
//--- Рисуем фон кнопки
this.m_canvas.FillRectangle(x1,y1,x2,y2,::ColorToARGB(this.m_butt_pin_back_color,this.m_header_alpha));
//--- Координаты точек ломаной линии
int x[]={x1+3, x1+6, x1+3,x1+4,x1+6,x1+9,x1+9,x1+10,x1+15,x1+14,x1+13,x1+10,x1+10,x1+9,x1+6};
int y[]={y1+14,y1+11,y1+8,y1+7,y1+7,y1+4,y1+3,y1+2, y1+7, y1+8, y1+8, y1+11,y1+13,y1+14,y1+11};
//--- Рисуем фигурку "кнопки"
this.m_canvas.Polygon(x,y,::ColorToARGB(this.m_butt_pin_fore_color,255));
//--- Если флаг перемещаемости сброшен (закреплено) - перечёркиваем нарисованную кнопку
if(!this.m_movable)
this.m_canvas.Line(x1+3,y1+2,x1+15,y1+14,::ColorToARGB(this.m_butt_pin_fore_color,255));
//--- Запоминаем текущий цвет фона и рисунка кнопки
this.m_butt_pin_back_color_c=this.m_butt_pin_back_color;
this.m_butt_pin_fore_color_c=this.m_butt_pin_fore_color;
//--- обновляем канвас без перерисовки экрана
this.m_canvas.Update(false);
}
//+------------------------------------------------------------------+
//| Перерисовывает кнопку закрепления панели |
//+------------------------------------------------------------------+
void CDashboard::RedrawButtonPin(const color new_back_color=clrNONE,const color new_fore_color=clrNONE,const ushort new_alpha=USHORT_MAX)
{
//--- Если кнопка не используется или все переданные параметры имеют значения по умолчанию - уходим
if(!this.m_butt_pin || (new_back_color==clrNONE && new_fore_color==clrNONE && new_alpha==USHORT_MAX))
return;
//--- Ширина кнопки равна высоте области заголовка
int w=this.m_header_h;
//--- Ширина кнопки закрытия и кнопки сворачивания равна нулю, если кнопка не используется
int wc=(this.m_butt_close ? w : 0);
int wm=(this.m_butt_minimize ? w : 0);
//--- Координаты и размеры кнопки
int x1=this.m_w-wc-wm-w;
int x2=this.m_w-wc-wm-1;
int y1=0;
int y2=w-1;
//--- Определяем новые цвета фона и текста и прозрачность
color back_color=(new_back_color!=clrNONE ? new_back_color : this.m_butt_pin_back_color);
color fore_color=(new_fore_color!=clrNONE ? new_fore_color : this.m_butt_pin_fore_color);
uchar alpha=uchar(new_alpha==USHORT_MAX ? this.m_header_alpha : new_alpha>255 ? 255 : new_alpha);
//--- Рисуем фон кнопки
this.m_canvas.FillRectangle(x1,y1,x2,y2,::ColorToARGB(back_color,alpha));
//--- Координаты точек ломаной линии
int x[]={x1+3, x1+6, x1+3,x1+4,x1+6,x1+9,x1+9,x1+10,x1+15,x1+14,x1+13,x1+10,x1+10,x1+9,x1+6};
int y[]={y1+14,y1+11,y1+8,y1+7,y1+7,y1+4,y1+3,y1+2, y1+7, y1+8, y1+8, y1+11,y1+13,y1+14,y1+11};
//--- Рисуем фигурку "кнопки"
this.m_canvas.Polygon(x,y,::ColorToARGB(this.m_butt_pin_fore_color,255));
//--- Если флаг перемещаемости сброшен (закреплено) - перечёркиваем нарисованную кнопку
if(!this.m_movable)
this.m_canvas.Line(x1+3,y1+2,x1+15,y1+14,::ColorToARGB(this.m_butt_pin_fore_color,255));
//--- Запоминаем текущий цвет фона и рисунка кнопки
this.m_butt_pin_back_color_c=back_color;
this.m_butt_pin_fore_color_c=fore_color;
//--- обновляем канвас без перерисовки экрана
this.m_canvas.Update(false);
}
//+------------------------------------------------------------------+
//| Рисует панель |
//+------------------------------------------------------------------+
void CDashboard::Draw(const string title)
{
//--- Устанавливаем текст заголовка
this.m_title=title;
//--- Если флаг свёрнутости не установлен - разворачиваем панель
if(!this.m_minimized)
this.Expand();
//--- Иначе - сворачиваем панель
else
this.Collapse();
//--- Обновляем канвас без перерисовки чарта
this.m_canvas.Update(false);
//--- Обновляем рабочую область с перерисовкой графика
this.m_workspace.Update();
}
//+------------------------------------------------------------------+
//| Сворачивает панель |
//+------------------------------------------------------------------+
void CDashboard::Collapse(void)
{
//--- Сохраняем в массивы пиксели рабочей области и фона панели
this.SaveWorkspace();
this.SaveBackground();
//--- Запоминаем текущую высоту панели
int h=this.m_h;
//--- Изменяем размеры (высоту) канваса и рабочей области
if(!this.SetSizes(this.m_canvas.Width(),this.m_header_h))
return;
//--- Рисуем область заголовка
this.DrawHeaderArea(this.m_title);
//--- Возвращаем в переменную запомненную высоту панели
this.m_h=h;
}
//+------------------------------------------------------------------+
//| Разворачивает панель |
//+------------------------------------------------------------------+
void CDashboard::Expand(void)
{
//--- Изменяем размеры панели
if(!this.SetSizes(this.m_canvas.Width(),this.m_h))
return;
//--- Если ещё ни разу пиксели фона панели не сохранялись в массив
if(this.m_array_ppx.Size()==0)
{
//--- Рисуем панель и
this.m_canvas.Erase(::ColorToARGB(this.m_back_color,this.m_alpha));
this.DrawFrame();
this.DrawHeaderArea(this.m_title);
//--- сохраняем пиксели фона панели и рабочей области в массивы
this.SaveWorkspace();
this.SaveBackground();
}
//--- Если пиксели фона панели и рабочей области сохранялись ранее,
else
{
//--- восстанавливаем пиксели фона панели и рабочей области из массивов
this.RestoreBackground();
if(this.m_array_wpx.Size()>0)
this.RestoreWorkspace();
}
//--- Если после разворачивания панель выходит за пределы окна графика - корректируем расположение панели
if(this.m_y+this.m_canvas.Height()>this.m_chart_h-1)
this.Move(this.m_x,this.m_chart_h-1-this.m_canvas.Height());
}
//+------------------------------------------------------------------+
//| Возвращает цвет с новой цветовой составляющей |
//+------------------------------------------------------------------+
color CDashboard::NewColor(color base_color, int shift_red, int shift_green, int shift_blue)
{
double clR=0, clG=0, clB=0;
this.ColorToRGB(base_color,clR,clG,clB);
double clRn=(clR+shift_red < 0 ? 0 : clR+shift_red > 255 ? 255 : clR+shift_red);
double clGn=(clG+shift_green< 0 ? 0 : clG+shift_green> 255 ? 255 : clG+shift_green);
double clBn=(clB+shift_blue < 0 ? 0 : clB+shift_blue > 255 ? 255 : clB+shift_blue);
return this.RGBToColor(clRn,clGn,clBn);
}
//+------------------------------------------------------------------+
//| Преобразует RGB в color |
//+------------------------------------------------------------------+
color CDashboard::RGBToColor(const double r,const double g,const double b) const
{
int int_r=(int)::round(r);
int int_g=(int)::round(g);
int int_b=(int)::round(b);
int clr=0;
clr=int_b;
clr<<=8;
clr|=int_g;
clr<<=8;
clr|=int_r;
//---
return (color)clr;
}
//+------------------------------------------------------------------+
//| Получение значений компонентов RGB |
//+------------------------------------------------------------------+
void CDashboard::ColorToRGB(const color clr,double &r,double &g,double &b)
{
r=GetR(clr);
g=GetG(clr);
b=GetB(clr);
}
//+------------------------------------------------------------------+
//| Устанавливает прозрачность заголовка |
//+------------------------------------------------------------------+
void CDashboard::SetHeaderTransparency(const uchar value)
{
this.m_header_alpha=value;
if(this.m_header_alpha_c!=this.m_header_alpha)
this.RedrawHeaderArea(clrNONE,NULL,clrNONE,value);
this.m_header_alpha_c=value;
}
//+------------------------------------------------------------------+
//| Устанавливает прозрачность панели |
//+------------------------------------------------------------------+
void CDashboard::SetTransparency(const uchar value)
{
this.m_alpha=value;
if(this.m_alpha_c!=this.m_alpha)
{
this.m_canvas.Erase(::ColorToARGB(this.m_back_color,value));
this.DrawFrame();
this.RedrawHeaderArea(clrNONE,NULL,clrNONE,value);
this.m_canvas.Update(false);
}
this.m_alpha_c=value;
}
//+------------------------------------------------------------------+
//| Устанавливает параметры шрифта рабочей области по умолчанию |
//+------------------------------------------------------------------+
void CDashboard::SetFontParams(const string name,const int size,const uint flags=0,const uint angle=0)
{
if(!this.m_workspace.FontSet(name,size*-10,flags,angle))
{
::PrintFormat("%s: Failed to set font options. Error %lu",(string)__FUNCTION__,::GetLastError());
return;
}
this.m_font=name;
this.m_font_size=size*-10;
}
//+------------------------------------------------------------------+
//| Включает/выключает режимы работы с графиком |
//+------------------------------------------------------------------+
void CDashboard::SetChartsTool(const bool flag)
{
//--- Если передан флаг true и если прокрутка графика отключена
if(flag && !::ChartGetInteger(this.m_chart_id,CHART_MOUSE_SCROLL))
{
//--- включаем прокрутку графика, меню правой кнопки мышки и перекрестие
::ChartSetInteger(0,CHART_MOUSE_SCROLL,true);
::ChartSetInteger(0,CHART_CONTEXT_MENU,true);
::ChartSetInteger(0,CHART_CROSSHAIR_TOOL,true);
}
//--- иначе, если передан флаг false и если прокрутка графика включена
else if(!flag && ::ChartGetInteger(this.m_chart_id,CHART_MOUSE_SCROLL))
{
//--- отключаем прокрутку графика, меню правой кнопки мышки и перекрестие
::ChartSetInteger(0,CHART_MOUSE_SCROLL,false);
::ChartSetInteger(0,CHART_CONTEXT_MENU,false);
::ChartSetInteger(0,CHART_CROSSHAIR_TOOL,false);
}
}
//+------------------------------------------------------------------+
//| Выводит текстовое сообщение в указанные координаты |
//+------------------------------------------------------------------+
void CDashboard::DrawText(const string text,const int x,const int y,const int width=WRONG_VALUE,const int height=WRONG_VALUE)
{
//--- Объявим переменные для записи в них ширины и высоты текста
int w=width;
int h=height;
//--- Если ширина и высота текста, переданные в метод, имеют нулевые значения,
//--- то полностью очищается всё паространство рабочей области прозрачным цветом
if(width==0 && height==0)
this.m_workspace.Erase(0x00FFFFFF);
//--- Иначе
else
{
//--- Если переданные ширина и высота имеют значения по умолчанию (-1) - получаем из текста его ширину и высоту
if(width==WRONG_VALUE && height==WRONG_VALUE)
this.m_workspace.TextSize(text,w,h);
//--- иначе,
else
{
//--- если ширина, переданная в метод, имеет значение по умолчанию (-1) - получаем ширину из текста, либо
//--- если ширина, переданная в метод, имеет значение больше нуля - используем переданную в метод ширину, либо
//--- если ширина, переданная в метод, имеет нулевое значение, используем значение 1 для ширины
w=(width ==WRONG_VALUE ? this.m_workspace.TextWidth(text) : width>0 ? width : 1);
//--- если высота, переданная в метод, имеет значение по умолчанию (-1) - получаем высоту из текста, либо
//--- если высота, переданная в метод, имеет значение больше нуля - используем переданную в метод высоту, либо
//--- если высота, переданная в метод, имеет нулевое значение, используем значение 1 для высоты
h=(height==WRONG_VALUE ? this.m_workspace.TextHeight(text) : height>0 ? height : 1);
}
//--- Заполняем пространство по указанным координатам и полученной шириной и высотой прозрачным цветом (стираем прошлую запись)
this.m_workspace.FillRectangle(x,y,x+w,y+h,0x00FFFFFF);
}
//--- Выводим текст на очищенное от прошлого текста места и обновляем рабочую область без перерисовки экрана
this.m_workspace.TextOut(x,y,text,::ColorToARGB(this.m_fore_color));
this.m_workspace.Update(false);
}
//+------------------------------------------------------------------+
//| Рисует фоновую сетку |
//+------------------------------------------------------------------+
void CDashboard::DrawGrid(const uint x,const uint y,const uint rows,const uint columns,const uint row_size,const uint col_size,
const color line_color=clrNONE,bool alternating_color=true)
{
//--- Если панель свёрнута - уходим
if(this.m_minimized)
return;
//--- Очищаем все списки объекта табличных данных (удаляем ячейки из строк и все строки)
this.m_table_data.Clear();
//--- Высота строки не может быть меньше 2
int row_h=int(row_size<2 ? 2 : row_size);
//--- Ширина столбца не может быть меньше 2
int col_w=int(col_size<2 ? 2 : col_size);
//--- Координата X1 (слева) таблицы не может быть меньше 1 (чтобы оставить один пиксель по периметру панели для рамки)
int x1=int(x<1 ? 1 : x);
//--- Рассчитываем координату X2 (справа) в зависимости от количества столбцов и их ширины
int x2=x1+col_w*int(columns>0 ? columns : 1);
//--- Координата Y1 находится под областью заголовка панели
int y1=this.m_header_h+(int)y;
//--- Рассчитываем координату Y2 (снизу) в зависимости от количества строк и их высоты
int y2=y1+row_h*int(rows>0 ? rows : 1);
//--- Получаем цвет линий сетки таблицы, либо по умолчанию, либо переданный в метод
color clr=(line_color==clrNONE ? C'200,200,200' : line_color);
//--- Если начальная координата X больше 1 - рисуем рамку таблицы
//--- (при координате 1 рамкой таблицы выступает рамка панели)
if(x1>1)
this.m_canvas.Rectangle(x1,y1,x2,y2,::ColorToARGB(clr,this.m_alpha));
//--- В цикле во строкам таблицы
for(int i=0;i<(int)rows;i++)
{
//--- рассчитываем координату Y очередной горизонтальной линии сетки (координата Y очередной строки таблицы)
int row_y=y1+row_h*i;
//--- если передан флаг "чередующихся" цветов строк и строка чётная
if(alternating_color && i%2==0)
{
//--- осветляем цвет фона таблицы и рисуем фоновый прямоугольник
color new_color=this.NewColor(clr,45,45,45);
this.m_canvas.FillRectangle(x1+1,row_y+1,x2-1,row_y+row_h-1,::ColorToARGB(new_color,this.m_alpha));
}
//--- Рисуем горизонтальную линию сетки таблицы
this.m_canvas.Line(x1,row_y,x2,row_y,::ColorToARGB(clr,this.m_alpha));
//--- Создаём новый объект строки таблицы
CTableRow *row_obj=new CTableRow(i);
if(row_obj==NULL)
{
::PrintFormat("%s: Failed to create table row object at index %lu",(string)__FUNCTION__,i);
continue;
}
//--- Добавляем его в список строк объекта табличных данных
//--- (если добавить объект не удалось - удаляем созданный объект)
if(!this.m_table_data.AddRow(row_obj))
delete row_obj;
//--- Устанавливаем в созданном объекте-строке его координату Y с учётом смещения от заголовка панели
row_obj.SetY(row_y-this.m_header_h);
}
//--- В цикле по столбцам таблицы
for(int i=0;i<(int)columns;i++)
{
//--- рассчитываем координату X очередной вертикальной линии сетки (координата X очередного столбца таблицы)
int col_x=x1+col_w*i;
//--- Если линия сетки вышла за пределы панели - прерываем цикл
if(x1==1 && col_x>=x1+m_canvas.Width()-2)
break;
//--- Рисуем вертикальную линию сетки таблицы
this.m_canvas.Line(col_x,y1,col_x,y2,::ColorToARGB(clr,this.m_alpha));
//--- Получаем из объекта табличных данных количество созданных строк
int total=this.m_table_data.RowsTotal();
//--- В цикле по строкам таблицы
for(int j=0;j<total;j++)
{
//--- получаем очередную строку
CTableRow *row=m_table_data.GetRow(j);
if(row==NULL)
continue;
//--- Создаём новую ячейку таблицы
CTableCell *cell=new CTableCell(row.Row(),i);
if(cell==NULL)
{
::PrintFormat("%s: Failed to create table cell object at index %lu",(string)__FUNCTION__,i);
continue;
}
//--- Добавляем созданную ячейку в строку
//--- (если добавить объект не удалось - удаляем созданный объект)
if(!row.AddCell(cell))
{
delete cell;
continue;
}
//--- Устанавливаем в созданном объекте-ячейке его координату X и координату Y из объекта-строки
cell.SetXY(col_x,row.Y());
}
}
//--- Обновляем канвас без перерисовки графика
this.m_canvas.Update(false);
}
//+------------------------------------------------------------------+
//| Рисует фоновую сетку с автоматическим размером ячеек |
//+------------------------------------------------------------------+
void CDashboard::DrawGridAutoFill(const uint border,const uint rows,const uint columns,const color line_color=clrNONE,bool alternating_color=true)
{
//--- Если панель свёрнута - уходим
if(this.m_minimized)
return;
//--- Координата X1 (левая) таблицы
int x1=(int)border;
//--- Координата X2 (правая) таблицы
int x2=this.m_canvas.Width()-(int)border-1;
//--- Координата Y1 (верхняя) таблицы
int y1=this.m_header_h+(int)border;
//--- Координата Y2 (нижняя) таблицы
int y2=this.m_canvas.Height()-(int)border-1;
//--- Получаем цвет линий сетки таблицы, либо по умолчанию, либо переданный в метод
color clr=(line_color==clrNONE ? C'200,200,200' : line_color);
//--- Если отступ от края панели больше нуля - рисуем рамку таблицы
//--- иначе - рамкой таблицы выступает рамка панели
if(border>0)
this.m_canvas.Rectangle(x1,y1,x2,y2,::ColorToARGB(clr,this.m_alpha));
//--- Высота всей сетки таблицы
int greed_h=y2-y1;
//--- Рассчитываем высоту строки в зависимости от высоты таблицы и количества строк
int row_h=(int)::round((double)greed_h/(double)rows);
//--- В цикле по количеству строк
for(int i=0;i<(int)rows;i++)
{
//--- рассчитываем координату Y очередной горизонтальной линии сетки (координата Y очередной строки таблицы)
int row_y=y1+row_h*i;
//--- если передан флаг "чередующихся" цветов строк и строка чётная
if(alternating_color && i%2==0)
{
//--- осветляем цвет фона таблицы и рисуем фоновый прямоугольник
color new_color=this.NewColor(clr,45,45,45);
this.m_canvas.FillRectangle(x1+1,row_y+1,x2-1,row_y+row_h-1,::ColorToARGB(new_color,this.m_alpha));
}
//--- Рисуем горизонтальную линию сетки таблицы
this.m_canvas.Line(x1,row_y,x2,row_y,::ColorToARGB(clr,this.m_alpha));
//--- Создаём новый объект строки таблицы
CTableRow *row_obj=new CTableRow(i);
if(row_obj==NULL)
{
::PrintFormat("%s: Failed to create table row object at index %lu",(string)__FUNCTION__,i);
continue;
}
//--- Добавляем его в список строк объекта табличных данных
//--- (если добавить объект не удалось - удаляем созданный объект)
if(!this.m_table_data.AddRow(row_obj))
delete row_obj;
//--- Устанавливаем в созданном объекте-строке его координату Y с учётом смещения от заголовка панели
row_obj.SetY(row_y-this.m_header_h);
}
//--- Ширина сетки таблицы
int greed_w=x2-x1;
//--- Рассчитываем ширину столбца в зависимости от ширины таблицы и количества столбцов
int col_w=(int)::round((double)greed_w/(double)columns);
//--- В цикле по столбцам таблицы
for(int i=0;i<(int)columns;i++)
{
//--- рассчитываем координату X очередной вертикальной линии сетки (координата X очередного столбца таблицы)
int col_x=x1+col_w*i;
//--- Если это не самая первая вертикальная линия - рисуем её
//--- (первой вертикальной линией выступает либо рамка таблицы, либо рамка панели)
if(i>0)
this.m_canvas.Line(col_x,y1,col_x,y2,::ColorToARGB(clr,this.m_alpha));
//--- Получаем из объекта табличных данных количество созданных строк
int total=this.m_table_data.RowsTotal();
//--- В цикле по строкам таблицы
for(int j=0;j<total;j++)
{
//--- получаем очередную строку
CTableRow *row=this.m_table_data.GetRow(j);
if(row==NULL)
continue;
//--- Создаём новую ячейку таблицы
CTableCell *cell=new CTableCell(row.Row(),i);
if(cell==NULL)
{
::PrintFormat("%s: Failed to create table cell object at index %lu",(string)__FUNCTION__,i);
continue;
}
//--- Добавляем созданную ячейку в строку
//--- (если добавить объект не удалось - удаляем созданный объект)
if(!row.AddCell(cell))
{
delete cell;
continue;
}
//--- Устанавливаем в созданном объекте-ячейке его координату X и координату Y из объекта-строки
cell.SetXY(col_x,row.Y());
}
}
//--- Обновляем канвас без перерисовки графика
this.m_canvas.Update(false);
}
//+------------------------------------------------------------------+
//| Сохраняет рабочую область в массив пикселей |
//+------------------------------------------------------------------+
void CDashboard::SaveWorkspace(void)
{
//--- Рассчитываем необходимый размер массива (ширина * высота рабочей области)
uint size=this.m_workspace.Width()*this.m_workspace.Height();
//--- Если размер массива не равен рассчитанному - изменяем его
if(this.m_array_wpx.Size()!=size)
{
::ResetLastError();
if(::ArrayResize(this.m_array_wpx,size)!=size)
{
::PrintFormat("%s: ArrayResize failed. Error %lu",(string)__FUNCTION__,::GetLastError());
return;
}
}
uint n=0;
//--- В цикле по высоте рабочей области (координата Y пикселя)
for(int y=0;y<this.m_workspace.Height();y++)
//--- в цикле по ширине рабочей области (координата X пикселя)
for(int x=0;x<this.m_workspace.Width();x++)
{
//--- рассчитываем индекс пикселя в приёмном массиве
n=this.m_workspace.Width()*y+x;
if(n>this.m_array_wpx.Size()-1)
break;
//--- копируем пиксель в приёмный массив из X и Y рабочей области
this.m_array_wpx[n]=this.m_workspace.PixelGet(x,y);
}
}
//+------------------------------------------------------------------+
//| Восстанавливает рабочую область из массива пикселей |
//+------------------------------------------------------------------+
void CDashboard::RestoreWorkspace(void)
{
//--- Если массив пустой - уходим
if(this.m_array_wpx.Size()==0)
return;
uint n=0;
//--- В цикле по высоте рабочей области (координата Y пикселя)
for(int y=0;y<this.m_workspace.Height();y++)
//--- в цикле по ширине рабочей области (координата X пикселя)
for(int x=0;x<this.m_workspace.Width();x++)
{
//--- рассчитываем индекс пикселя в массиве
n=this.m_workspace.Width()*y+x;
if(n>this.m_array_wpx.Size()-1)
break;
//--- копируем пиксель из массива в координаты X и Y рабочей области
this.m_workspace.PixelSet(x,y,this.m_array_wpx[n]);
}
}
//+------------------------------------------------------------------+
//| Сохраняет фон панели в массив пикселей |
//+------------------------------------------------------------------+
void CDashboard::SaveBackground(void)
{
//--- Рассчитываем необходимый размер массива (ширина * высота панели)
uint size=this.m_canvas.Width()*this.m_canvas.Height();
//--- Если размер массива не равен рассчитанному - изменяем его
if(this.m_array_ppx.Size()!=size)
{
::ResetLastError();
if(::ArrayResize(this.m_array_ppx,size)!=size)
{
::PrintFormat("%s: ArrayResize failed. Error %lu",(string)__FUNCTION__,::GetLastError());
return;
}
}
uint n=0;
//--- В цикле по высоте панели (координата Y пикселя)
for(int y=0;y<this.m_canvas.Height();y++)
//--- в цикле по ширине панели (координата X пикселя)
for(int x=0;x<this.m_canvas.Width();x++)
{
//--- рассчитываем индекс пикселя в приёмном массиве
n=this.m_canvas.Width()*y+x;
if(n>this.m_array_ppx.Size()-1)
break;
//--- копируем пиксель в приёмный массив из X и Y панели
this.m_array_ppx[n]=this.m_canvas.PixelGet(x,y);
}
}
//+------------------------------------------------------------------+
//| Восстанавливает фон панели из массива пикселей |
//+------------------------------------------------------------------+
void CDashboard::RestoreBackground(void)
{
//--- Если массив пустой - уходим
if(this.m_array_ppx.Size()==0)
return;
uint n=0;
//--- В цикле по высоте панели (координата Y пикселя)
for(int y=0;y<this.m_canvas.Height();y++)
//--- в цикле по ширине панели (координата X пикселя)
for(int x=0;x<this.m_canvas.Width();x++)
{
//--- рассчитываем индекс пикселя в массиве
n=this.m_canvas.Width()*y+x;
if(n>this.m_array_ppx.Size()-1)
break;
//--- копируем пиксель из массива в координаты X и Y панели
this.m_canvas.PixelSet(x,y,this.m_array_ppx[n]);
}
}
//+------------------------------------------------------------------+
//| Скрывает панель |
//+------------------------------------------------------------------+
void CDashboard::Hide(const bool redraw=false)
{
::ObjectSetInteger(this.m_chart_id,this.m_workspace.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS);
::ObjectSetInteger(this.m_chart_id,this.m_canvas.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS);
if(redraw)
::ChartRedraw(this.m_chart_id);
}
//+------------------------------------------------------------------+
//| Показывает панель |
//+------------------------------------------------------------------+
void CDashboard::Show(const bool redraw=false)
{
::ObjectSetInteger(this.m_chart_id,this.m_canvas.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);
if(!this.m_minimized)
::ObjectSetInteger(this.m_chart_id,this.m_workspace.ChartObjectName(),OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);
if(redraw)
::ChartRedraw(this.m_chart_id);
}
//+------------------------------------------------------------------+
//| Переносит панель на передний план |
//+------------------------------------------------------------------+
void CDashboard::BringToTop(void)
{
this.Hide(false);
this.Show(true);
}
//+------------------------------------------------------------------+
//| Сохраняет массив пикселей рабочей области в файл |
//+------------------------------------------------------------------+
bool CDashboard::FileSaveWorkspace(void)
{
//--- Определяем папку и имя файла
string filename=this.m_program_name+"\\Dashboard"+(string)this.m_id+"\\workspace.bin";
//--- Если сохраняемый массив пустой - сообщаем об этом и возвращаем false
if(this.m_array_wpx.Size()==0)
{
::PrintFormat("%s: Error. The workspace pixel array is empty.",__FUNCTION__);
return false;
}
//--- Если массив не удалось сохранить в файл - сообщаем об этом и возвращаем false
if(!::FileSave(filename,this.m_array_wpx))
{
::PrintFormat("%s: FileSave '%s' failed. Error %lu",__FUNCTION__,filename,::GetLastError());
return false;
}
//--- Успешно, возвращаем true
return true;
}
2026-03-19 00:53:45 +07:00
//+------------------------------------------------------------------+
2026-03-19 00:56:24 +07:00
//| Сохраняет массив пикселей фона панели в файл |
2026-03-19 00:53:45 +07:00
//+------------------------------------------------------------------+
2026-03-19 00:56:24 +07:00
bool CDashboard::FileSaveBackground(void)
{
//--- Определяем папку и имя файла
string filename=this.m_program_name+"\\Dashboard"+(string)this.m_id+"\\background.bin";
//--- Если сохраняемый массив пустой - сообщаем об этом и возвращаем false
if(this.m_array_ppx.Size()==0)
{
::PrintFormat("%s: Error. The background pixel array is empty.",__FUNCTION__);
return false;
}
//--- Если массив не удалось сохранить в файл - сообщаем об этом и возвращаем false
if(!::FileSave(filename,this.m_array_ppx))
{
::PrintFormat("%s: FileSave '%s' failed. Error %lu",__FUNCTION__,filename,::GetLastError());
return false;
}
//--- Успешно, возвращаем true
return true;
}
2026-03-19 00:53:45 +07:00
//+------------------------------------------------------------------+
2026-03-19 00:56:24 +07:00
//| Загружает массив пикселей рабочей области из файла |
2026-03-19 00:53:45 +07:00
//+------------------------------------------------------------------+
2026-03-19 00:56:24 +07:00
bool CDashboard::FileLoadWorkspace(void)
{
//--- Определяем папку и имя файла
string filename=this.m_program_name+"\\Dashboard"+(string)this.m_id+"\\workspace.bin";
//--- Если не удалось загрузить данные из файла в массив, сообщаем об этом и возвращаем false
if(::FileLoad(filename,this.m_array_wpx)==WRONG_VALUE)
{
::PrintFormat("%s: FileLoad '%s' failed. Error %lu",__FUNCTION__,filename,::GetLastError());
return false;
}
//--- Успешно, возвращаем true
return true;
}
2026-03-19 00:53:45 +07:00
//+------------------------------------------------------------------+
2026-03-19 00:56:24 +07:00
//| Загружает массив пикселей фона панели из файла |
2026-03-19 00:53:45 +07:00
//+------------------------------------------------------------------+
2026-03-19 00:56:24 +07:00
bool CDashboard::FileLoadBackground(void)
{
//--- Определяем папку и имя файла
string filename=this.m_program_name+"\\Dashboard"+(string)this.m_id+"\\background.bin";
//--- Если не удалось загрузить данные из файла в массив, сообщаем об этом и возвращаем false
if(::FileLoad(filename,this.m_array_ppx)==WRONG_VALUE)
{
::PrintFormat("%s: FileLoad '%s' failed. Error %lu",__FUNCTION__,filename,::GetLastError());
return false;
}
//--- Успешно, возвращаем true
return true;
}
2026-03-19 00:53:45 +07:00
//+------------------------------------------------------------------+
2026-03-19 00:56:24 +07:00