Article-17457-MQL5-Optimiza.../Controls.mqh
2026-03-24 15:23:43 +07:00

1826 Zeilen
183 KiB
MQL5

//+------------------------------------------------------------------+
//| Controls.mqh |
//| Copyright 2024, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link "https://www.mql5.com"
//+------------------------------------------------------------------+
//| defines |
//+------------------------------------------------------------------+
#define DEF_BUTTON_WIDTH 60 // ширина кнопки по умолчанию
//+------------------------------------------------------------------+
//| includes |
//+------------------------------------------------------------------+
#include <Canvas\Canvas.mqh>
#include <Arrays\ArrayObj.mqh>
enum ENUM_OBJECT_TYPE // Перечисление типов объектов
{
OBJECT_TYPE_BASE=1, // Базовый объект рисования на канвасе
OBJECT_TYPE_PANEL, // Простая панель
OBJECT_TYPE_LABEL, // Текстовая метка
OBJECT_TYPE_BUTTON, // Простая кнопка
OBJECT_TYPE_TBUTTON, // Двухпозиционная кнопка (вкл/выкл)
OBJECT_TYPE_SBUTTON, // Кнопка-переключатель
OBJECT_TYPE_TAB_BUTTON, // Кнопка вкладки
OBJECT_TYPE_TAB, // Вкладка
OBJECT_TYPE_TAB_CONTROL, // Набор вкладок
};
//+------------------------------------------------------------------+
//| Базовый класс рисования |
//+------------------------------------------------------------------+
class CBaseCanvas : public CObject
{
private:
bool m_base_obj; // Признак базового объекта в иерархии связанных объектов
bool m_blocked; // Признак заблокированного объекта
int m_id; // Идентификатор объекта
protected:
CCanvas m_canvas; // Канвас
CRect m_bound; // Границы объекта, края и координаты
long m_chart_id; // Идентификатор графика
uchar m_alpha; // Значение прозрачности холста
ushort m_text_obj[]; // Массив символов текста объекта
public:
CCanvas *GetCanvas(void) { return &this.m_canvas; }
virtual int Type(void) const { return(OBJECT_TYPE_BASE); }
void SetBaseObjFlag(const bool flag) { this.m_base_obj=flag; }
bool IsBaseObject(void) const { return this.m_base_obj; }
bool IsBlocked(void) const { return this.m_blocked; }
void SetChartID(const long chart_id) { this.m_chart_id=chart_id; }
long ChartID(void) const { return this.m_chart_id; }
void SetID(const int id) { this.m_id=id; }
int ID(void) const { return this.m_id; }
int X(void) const { return this.m_bound.left; }
int Y(void) const { return this.m_bound.top; }
int Width(void) const { return this.m_canvas.Width(); }
int Height(void) const { return this.m_canvas.Height(); }
int Right(void) const { return this.m_bound.right; }
int Bottom(void) const { return this.m_bound.bottom; }
string Text(void) const { return ::ShortArrayToString(this.m_text_obj); }
string Name(void) const { return this.m_canvas.ChartObjectName(); }
//--- Все виртуальные методы могут переопределяться в наследуемых классах
//--- Метод рисования внешнего вида объекта
virtual void Draw(const bool chart_redraw) { return; }
//--- Очистка всего объекта - заливка полностью прозрачным цветом
virtual void Clear(void) { this.m_canvas.Erase(0x00FFFFFF); }
//--- Обновление канваса для отображения проведённых на холсте изменений
virtual void UpdateView(const bool chart_redraw) { this.m_canvas.Update(chart_redraw); }
//--- Создание графического объекта и присоединение к нему ресурса холста
virtual bool Create(const string name, const string object_text, const int x, const int y, const int w, const int h);
//--- Установка текста, содержащегося в объекте
virtual bool SetTextObject(const string text) { return(::StringToShortArray(text, this.m_text_obj)==text.Length()); }
//--- Скрытие и отображение объекта на графике
virtual bool Hide(void) const { return ::ObjectSetInteger(this.m_chart_id, this.Name(), OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS); }
virtual bool Show(void) const { return ::ObjectSetInteger(this.m_chart_id, this.Name(), OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS); }
//--- Перенос объекта на передний план
virtual bool BringToTop(void) const { bool res=this.Hide(); res &=this.Show(); return res;}
//--- Возврат флага скрытого объекта
bool IsHidden(void) const { return(::ObjectGetInteger(this.m_chart_id, this.Name(), OBJPROP_TIMEFRAMES)==OBJ_NO_PERIODS); }
//--- Установка блокированного/разблокированного состояний объекта
virtual void SetBlocked(void) { this.m_blocked=true; }
virtual void SetUnblocked(void) { this.m_blocked=false; }
//--- Изменение размеров
virtual bool Resize(const int w, const int h);
//--- Перемещение
virtual bool MoveX(const int x);
virtual bool MoveY(const int y);
virtual bool Move(const int x, const int y);
//--- Конструкторы/деструктор
CBaseCanvas(void) : m_chart_id(::ChartID()), m_id(0), m_base_obj(false), m_alpha(255) {}
CBaseCanvas(const long chart_id) : m_chart_id(chart_id), m_id(0), m_base_obj(false), m_alpha(255) {}
~CBaseCanvas(void){ this.m_canvas.Destroy(); }
};
//+------------------------------------------------------------------+
//| Создаёт базовый объект канвас |
//+------------------------------------------------------------------+
bool CBaseCanvas::Create(const string name, const string object_text, const int x, const int y, const int w, const int h)
{
//--- создаём объект OBJ_BITMAP_LABEL и его графический ресурс (ширина и высота не менее 1 пикселя)
if(!this.m_canvas.CreateBitmapLabel(this.m_chart_id, 0, name, x, y, (w<1 ? 1 : w), (h<1 ? 1 : h), COLOR_FORMAT_ARGB_NORMALIZE))
{
::PrintFormat("%s: CreateBitmapLabel() failed. x=%d, y=%d, w=%d, h=%d",__FUNCTION__, x, y, w, h);
return false;
}
//--- устанавливаем координаты и размеры ограничивающего прямоугольника и записываем текст, содержащийся в объекте
this.m_bound.LeftTop(x, y);
this.m_bound.Width(w);
this.m_bound.Height(h);
this.SetTextObject(object_text);
return true;
}
//+------------------------------------------------------------------+
//| Изменяет размеры холста |
//+------------------------------------------------------------------+
bool CBaseCanvas::Resize(const int w,const int h)
{
//--- если переданы уже установленные значения размеров - возвращаем true
if(w==this.Width() && h==this.Height())
return true;
//--- изменяем размеры графического объекта
if(!this.m_canvas.Resize(w, h))
return false;
//--- устанавливаем новые размеры очерчивающему прямоугольнику и возвращаем true
this.m_bound.Width(w);
this.m_bound.Height(h);
return true;
}
//+------------------------------------------------------------------+
//| Перемещает объект на новую координату x |
//+------------------------------------------------------------------+
bool CBaseCanvas::MoveX(const int x)
{
//--- если передано уже установленное значение координаты - возвращаем true
if(x==this.X())
return true;
//--- изменяем координату графического объекта
if(!::ObjectSetInteger(this.m_chart_id, this.Name(), OBJPROP_XDISTANCE, x))
return false;
//--- устанавливаем новое значение очерчивающему прямоугольнику и возвращаем true
this.m_bound.Move(x, this.Y());
return true;
}
//+------------------------------------------------------------------+
//| Перемещает объект на новую координату y |
//+------------------------------------------------------------------+
bool CBaseCanvas::MoveY(const int y)
{
//--- если передано уже установленное значение координаты - возвращаем true
if(y==this.Y())
return true;
//--- изменяем координату графического объекта
if(!::ObjectSetInteger(this.m_chart_id, this.Name(), OBJPROP_YDISTANCE, y))
return false;
//--- устанавливаем новое значение очерчивающему прямоугольнику и возвращаем true
this.m_bound.Move(this.X(), y);
return true;
}
//+------------------------------------------------------------------+
//| Перемещает объект на новые координаты x, y |
//+------------------------------------------------------------------+
bool CBaseCanvas::Move(const int x,const int y)
{
//--- возвращаем результат изменения обеих координат объекта
if(!this.MoveX(x))
return false;
return this.MoveY(y);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| enums |
//+------------------------------------------------------------------+
enum ENUM_COLOR // Список элементов объекта с изменяемым цветом
{
COLOR_BACKGROUND, // Фон
COLOR_FOREGROUND, // Текст
COLOR_BORDER, // Рамка
};
#define NUM_COLOR_ELEMENTS 3 // Количество элементов объекта с изменяемым цветом
enum ENUM_REASON_COLOR // Список причин изменения цвета
{
REASON_COLOR_STANDARD, // Объект в обычном состоянии
REASON_COLOR_FOCUSED, // При наведении курсора
REASON_COLOR_PRESSED, // При нажатии
REASON_COLOR_BLOCKED, // Заблокирован
};
#define NUM_REASON_COLORS 4 // Количество цветов различных состояний
enum ENUM_STATE // Состояния кнопки
{
STATE_OFF, // Отжата
STATE_ON, // Нажата
};
#define NUM_STATES 2 // Количество состояний кнопок
enum ENUM_REFERENCE_OBJ // Эталонный объект для контроля размеров
{
REFERENCE_OBJ_NONE, // Нет
REFERENCE_OBJ_CHART, // График
REFERENCE_OBJ_OBJ, // Иной объект
};
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс панели |
//+------------------------------------------------------------------+
class CPanel : public CBaseCanvas
{
private:
bool m_chart_mouse_wheel; // Флаг отправки сообщений о прокрутке колёсика мышки
bool m_chart_mouse_move; // Флаг отправки сообщений о перемещениях курсора мышки
bool m_chart_context_menu; // Флаг доступа к контекстному меню по нажатию правой клавиши мышки
bool m_chart_crosshair_tool; // Флаг доступа к инструменту "Перекрестие" по нажатию средней клавиши мышки
ENUM_STATE m_state; // Состояние элемента (нажат, отжат и т.д.)
//--- Установка разрешений для событий мышки на графике
void MouseEventSet(void);
protected:
CArrayObj m_list_obj; // Список присоединённых дочерних объектов
bool m_resize; // Флаг возможности менять размеры
ENUM_REFERENCE_OBJ m_reference_obj; // Объект для контроля изменения размеров
color m_array_default_color[NUM_COLOR_ELEMENTS][NUM_REASON_COLORS][NUM_STATES]; // Массив предустановленных цветов различных состояний объекта
color m_array_used_color[NUM_COLOR_ELEMENTS][NUM_REASON_COLORS][NUM_STATES]; // Массив цветов различных состояний объекта
//--- Установка запретов для графика
void ChartToolsSet(const bool flag);
//--- Инициализация объекта класса
void Init(void);
//--- Изменяет цвета фона, текста и рамки в зависимости от условия
void ColorChange(const ENUM_REASON_COLOR reason);
//--- Проверяет установленный цвет на равенство указанному
bool CheckColor(const ENUM_REASON_COLOR reason) const;
//--- Обработчики событий перемещения курсора и нажатий клавиш мышки. Должны определяться в наследниках
virtual void MouseMoveHandler(const int id, const long lparam, const double dparam, const string sparam) { return; } // обработчик здесь отключен
virtual void MousePressHandler(const int id, const long lparam, const double dparam, const string sparam) { return; } // обработчик здесь отключен
virtual void CustomMouseMoveHandler(const int id, const long lparam, const double dparam, const string sparam) { return; } // обработчик здесь отключен
virtual void CustomMousePressHandler(const int id, const long lparam, const double dparam, const string sparam) { return; } // обработчик здесь отключен
public:
//--- Установка и возврат состояния элемента
virtual void SetState(const ENUM_STATE state) { this.m_state=state; }
ENUM_STATE State(void) const { return this.m_state; }
//--- Устанавливает флаг возможности изменения размеров
void SetResizeFlag(const bool flag) { this.m_resize=flag; }
//--- Тип объекта
virtual int Type(void) const { return(OBJECT_TYPE_PANEL); }
//--- Возврат списка присоединённых объектов
virtual CArrayObj*GetList(void) { return &this.m_list_obj; }
//--- Добавление нового объекта в список
virtual bool AttachObject(CBaseCanvas *element)
{
if(element==NULL)
return false;
element.SetBaseObjFlag(false);
return(this.m_list_obj.Add(element));
}
//--- Возвращает (1) указатель на присоединённый объект по индексу, (2) количество объектов в списке
CObject *GetAttachedObj(const int index) { return this.m_list_obj.At(index); }
int AttachedObjTotal(void) const { return this.m_list_obj.Total(); }
private:
//--- (1) Установка, (2) возврат предустановленного цвета указанного состояния для указанного элемента объекта
void SetColorDefault(const ENUM_COLOR color_element, const ENUM_REASON_COLOR reason, const ENUM_STATE state, const color clr)
{ this.m_array_default_color[color_element][reason][state]=clr; }
color GetColorDefault(const ENUM_COLOR color_element, const ENUM_REASON_COLOR reason, const ENUM_STATE state) const
{ return this.m_array_default_color[color_element][reason][state]; }
//--- (1) Установка, (2) возврат цвета указанного состояния для указанного элемента объекта
void SetColor(const ENUM_COLOR color_element, const ENUM_REASON_COLOR reason, const ENUM_STATE state, const color clr)
{ this.m_array_used_color[color_element][reason][state]=clr; }
color GetColor(const ENUM_COLOR color_element, const ENUM_REASON_COLOR reason, const ENUM_STATE state) const
{ return this.m_array_used_color[color_element][reason][state]; }
public:
//--- Установка предустановленных цветов всех состояний для указанного элемента объекта
void SetDefaultColors(const ENUM_COLOR color_element, const ENUM_STATE state, const color standard, const color focused, const color pressed, const color blocked)
{
this.SetColorDefault(color_element, REASON_COLOR_STANDARD,state, standard);
this.SetColorDefault(color_element, REASON_COLOR_FOCUSED, state, focused);
this.SetColorDefault(color_element, REASON_COLOR_PRESSED, state, pressed);
this.SetColorDefault(color_element, REASON_COLOR_BLOCKED, state, blocked);
}
//--- Возврат предустановленных цветов фона в различных состояниях
color DefaultBackColor(void) const { return this.GetColorDefault(COLOR_BACKGROUND, REASON_COLOR_STANDARD,this.State()); }
color DefaultBackColorFocused(void) const { return this.GetColorDefault(COLOR_BACKGROUND, REASON_COLOR_FOCUSED, this.State()); }
color DefaultBackColorPressed(void) const { return this.GetColorDefault(COLOR_BACKGROUND, REASON_COLOR_PRESSED, this.State()); }
color DefaultBackColorBlocked(void) const { return this.GetColorDefault(COLOR_BACKGROUND, REASON_COLOR_BLOCKED, this.State()); }
//--- Возврат предустановленных цветов текста в различных состояниях
color DefaultForeColor(void) const { return this.GetColorDefault(COLOR_FOREGROUND, REASON_COLOR_STANDARD,this.State()); }
color DefaultForeColorFocused(void) const { return this.GetColorDefault(COLOR_FOREGROUND, REASON_COLOR_FOCUSED, this.State()); }
color DefaultForeColorPressed(void) const { return this.GetColorDefault(COLOR_FOREGROUND, REASON_COLOR_PRESSED, this.State()); }
color DefaultForeColorBlocked(void) const { return this.GetColorDefault(COLOR_FOREGROUND, REASON_COLOR_BLOCKED, this.State()); }
//--- Возврат предустановленных цветов рамки в различных состояниях
color DefaultBorderColor(void) const { return this.GetColorDefault(COLOR_BORDER, REASON_COLOR_STANDARD,this.State()); }
color DefaultBorderColorFocused(void) const { return this.GetColorDefault(COLOR_BORDER, REASON_COLOR_FOCUSED, this.State()); }
color DefaultBorderColorPressed(void) const { return this.GetColorDefault(COLOR_BORDER, REASON_COLOR_PRESSED, this.State()); }
color DefaultBorderColorBlocked(void) const { return this.GetColorDefault(COLOR_BORDER, REASON_COLOR_BLOCKED, this.State()); }
//--- Установка цветов всех состояний для указанного элемента объекта
void SetColors(const ENUM_COLOR color_element, const ENUM_STATE state, const color standard, const color focused, const color pressed, const color blocked)
{
this.SetColor(color_element, REASON_COLOR_STANDARD,state, standard);
this.SetColor(color_element, REASON_COLOR_FOCUSED, state, focused);
this.SetColor(color_element, REASON_COLOR_PRESSED, state, pressed);
this.SetColor(color_element, REASON_COLOR_BLOCKED, state, blocked);
}
//--- Установка цветов фона для различных состояний
void SetBackColor(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_BACKGROUND, REASON_COLOR_STANDARD,state, clr); }
void SetBackColorFocused(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_BACKGROUND, REASON_COLOR_FOCUSED, state, clr); }
void SetBackColorPressed(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_BACKGROUND, REASON_COLOR_PRESSED, state, clr); }
void SetBackColorBlocked(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_BACKGROUND, REASON_COLOR_BLOCKED, state, clr); }
//--- Установка цветов текста для различных состояний
void SetForeColor(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_FOREGROUND, REASON_COLOR_STANDARD,state, clr); }
void SetForeColorFocused(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_FOREGROUND, REASON_COLOR_FOCUSED, state, clr); }
void SetForeColorPressed(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_FOREGROUND, REASON_COLOR_PRESSED, state, clr); }
void SetForeColorBlocked(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_FOREGROUND, REASON_COLOR_BLOCKED, state, clr); }
//--- Установка цветов рамки для различных состояний
void SetBorderColor(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_BORDER, REASON_COLOR_STANDARD,state, clr); }
void SetBorderColorFocused(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_BORDER, REASON_COLOR_FOCUSED, state, clr); }
void SetBorderColorPressed(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_BORDER, REASON_COLOR_PRESSED, state, clr); }
void SetBorderColorBlocked(const ENUM_STATE state, const color clr) { this.SetColor(COLOR_BORDER, REASON_COLOR_BLOCKED, state, clr); }
//--- Возврат цветов фона в различных состояниях
color BackColor(void) const { return this.GetColor(COLOR_BACKGROUND, REASON_COLOR_STANDARD,this.State()); }
color BackColorFocused(void) const { return this.GetColor(COLOR_BACKGROUND, REASON_COLOR_FOCUSED, this.State()); }
color BackColorPressed(void) const { return this.GetColor(COLOR_BACKGROUND, REASON_COLOR_PRESSED, this.State()); }
color BackColorBlocked(void) const { return this.GetColor(COLOR_BACKGROUND, REASON_COLOR_BLOCKED, this.State()); }
//--- Возврат цветов текста в различных состояниях
color ForeColor(void) const { return this.GetColor(COLOR_FOREGROUND, REASON_COLOR_STANDARD,this.State()); }
color ForeColorFocused(void) const { return this.GetColor(COLOR_FOREGROUND, REASON_COLOR_FOCUSED, this.State()); }
color ForeColorPressed(void) const { return this.GetColor(COLOR_FOREGROUND, REASON_COLOR_PRESSED, this.State()); }
color ForeColorBlocked(void) const { return this.GetColor(COLOR_FOREGROUND, REASON_COLOR_BLOCKED, this.State()); }
//--- Возврат цветов рамки в различных состояниях
color BorderColor(void) const { return this.GetColor(COLOR_BORDER, REASON_COLOR_STANDARD,this.State()); }
color BorderColorFocused(void) const { return this.GetColor(COLOR_BORDER, REASON_COLOR_FOCUSED, this.State()); }
color BorderColorPressed(void) const { return this.GetColor(COLOR_BORDER, REASON_COLOR_PRESSED, this.State()); }
color BorderColorBlocked(void) const { return this.GetColor(COLOR_BORDER, REASON_COLOR_BLOCKED, this.State()); }
//--- Восстановление всех цветов в DEFAULT значения
void ResetUsedColors(const ENUM_STATE state)
{
this.SetBackColor(state, this.DefaultBackColor());
this.SetBackColorFocused(state, this.DefaultBackColorFocused());
this.SetBackColorPressed(state, this.DefaultBackColorPressed());
this.SetBackColorBlocked(state, this.DefaultBackColorBlocked());
this.SetForeColor(state, this.DefaultForeColor());
this.SetForeColorFocused(state, this.DefaultForeColorFocused());
this.SetForeColorPressed(state, this.DefaultForeColorPressed());
this.SetForeColorBlocked(state, this.DefaultForeColorBlocked());
this.SetBorderColor(state, this.DefaultBorderColor());
this.SetBorderColorFocused(state, this.DefaultBorderColorFocused());
this.SetBorderColorPressed(state, this.DefaultBorderColorPressed());
this.SetBorderColorBlocked(state, this.DefaultBorderColorBlocked());
}
//--- Переопределяемые виртуальные методы родительского класса
virtual void InitDefaultColors(void);
virtual void Draw(const bool chart_redraw) override;
virtual void UpdateView(const bool chart_redraw) override;
virtual bool Create(const string name, const string object_text, const int x, const int y, const int w, const int h) override;
virtual bool Resize(const int w, const int h) override;
virtual bool Hide(void) override const;
virtual bool Show(void) override const;
virtual bool BringToTop(void);
virtual void SetBlocked(void);
virtual void SetUnblocked(void);
//--- Обработчик событий
void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Конструкторы/деструктор
CPanel(void) : CBaseCanvas(::ChartID()), m_state(STATE_OFF), m_resize(true), m_reference_obj(REFERENCE_OBJ_NONE) { this.Init(); }
CPanel(const long chart_id) : CBaseCanvas(chart_id), m_state(STATE_OFF), m_resize(true), m_reference_obj(REFERENCE_OBJ_NONE) { this.Init(); }
~CPanel(void);
};
//+------------------------------------------------------------------+
//| Деструктор |
//+------------------------------------------------------------------+
CPanel::~CPanel(void)
{
//--- Возвращаем разрешения для мышки и инструментов графика
::ChartSetInteger(this.m_chart_id, CHART_EVENT_MOUSE_WHEEL, this.m_chart_mouse_wheel);
::ChartSetInteger(this.m_chart_id, CHART_EVENT_MOUSE_MOVE, this.m_chart_mouse_move);
::ChartSetInteger(this.m_chart_id, CHART_CONTEXT_MENU, this.m_chart_context_menu);
::ChartSetInteger(this.m_chart_id, CHART_CROSSHAIR_TOOL, this.m_chart_crosshair_tool);
}
//+------------------------------------------------------------------+
//| Инициализация класса |
//+------------------------------------------------------------------+
void CPanel::Init(void)
{
//--- Запоминаем разрешения для мышки и инструментов графика
this.m_chart_mouse_wheel=::ChartGetInteger(this.m_chart_id, CHART_EVENT_MOUSE_WHEEL);
this.m_chart_mouse_move=::ChartGetInteger(this.m_chart_id, CHART_EVENT_MOUSE_MOVE);
this.m_chart_context_menu=::ChartGetInteger(this.m_chart_id, CHART_CONTEXT_MENU);
this.m_chart_crosshair_tool=::ChartGetInteger(this.m_chart_id, CHART_CROSSHAIR_TOOL);
//--- Устанавливаем разрешения для мышки, цвета по умолчанию и параметры шрифта
this.MouseEventSet();
this.InitDefaultColors();
this.ResetUsedColors(STATE_OFF);
this.ResetUsedColors(STATE_ON);
this.m_canvas.FontSet("Calibri",-100);
}
//+------------------------------------------------------------------+
//| Установка разрешений на отправку сообщений о событиях мышки |
//+------------------------------------------------------------------+
void CPanel::MouseEventSet(void)
{
if(!this.m_chart_mouse_wheel)
::ChartSetInteger(this.m_chart_id, CHART_EVENT_MOUSE_WHEEL, true);
if(!this.m_chart_mouse_move)
::ChartSetInteger(this.m_chart_id, CHART_EVENT_MOUSE_MOVE, true);
}
//+------------------------------------------------------------------+
//| Установка запретов для графика (контекстное меню и перекрестие) |
//+------------------------------------------------------------------+
void CPanel::ChartToolsSet(const bool flag)
{
//--- Флаг разрешения изменяем только в случае,
//--- если у объекта он имеет противоположное значение
if(flag)
{
if(!this.m_chart_context_menu)
::ChartSetInteger(this.m_chart_id, CHART_CONTEXT_MENU, true);
if(!this.m_chart_crosshair_tool)
::ChartSetInteger(this.m_chart_id, CHART_CROSSHAIR_TOOL, true);
return;
}
if(this.m_chart_context_menu)
::ChartSetInteger(this.m_chart_id, CHART_CONTEXT_MENU, false);
if(this.m_chart_crosshair_tool)
::ChartSetInteger(this.m_chart_id, CHART_CROSSHAIR_TOOL, false);
}
//+------------------------------------------------------------------+
//| Устанавливает цвета по умолчанию |
//+------------------------------------------------------------------+
void CPanel::InitDefaultColors(void)
{ // standard focused pressed blocked
this.SetDefaultColors(COLOR_BACKGROUND, STATE_OFF, clrWhite, clrWhite, clrWhite, clrWhite);
this.SetDefaultColors(COLOR_FOREGROUND, STATE_OFF, clrDimGray, clrDimGray, clrDimGray, clrDimGray);
this.SetDefaultColors(COLOR_BORDER, STATE_OFF, clrDimGray, clrDimGray, clrDimGray, clrDimGray);
}
//+------------------------------------------------------------------+
//| Смена цвета элементов объекта по событию |
//+------------------------------------------------------------------+
void CPanel::ColorChange(const ENUM_REASON_COLOR reason)
{
//--- В зависимости от события устанавливаем цвета события как основные
switch(reason)
{
case REASON_COLOR_STANDARD :
this.ResetUsedColors(this.State());
break;
case REASON_COLOR_FOCUSED :
this.SetBackColor(this.State(), this.BackColorFocused());
this.SetForeColor(this.State(), this.ForeColorFocused());
this.SetBorderColor(this.State(), this.BorderColorFocused());
break;
case REASON_COLOR_PRESSED :
this.SetBackColor(this.State(), this.BackColorPressed());
this.SetForeColor(this.State(), this.ForeColorPressed());
this.SetBorderColor(this.State(), this.BorderColorPressed());
break;
case REASON_COLOR_BLOCKED :
this.SetBackColor(this.State(), this.BackColorBlocked());
this.SetForeColor(this.State(), this.ForeColorBlocked());
this.SetBorderColor(this.State(), this.BorderColorBlocked());
break;
default:
break;
}
}
//+------------------------------------------------------------------+
//| Проверяет установленный цвет на равенство указанному |
//+------------------------------------------------------------------+
bool CPanel::CheckColor(const ENUM_REASON_COLOR reason) const
{
bool res=true;
//--- В зависимости от проверяемого события
switch(reason)
{
//--- проверяем равенство всех STANDARD цветов фона, текста и рамки предустановленным значениям
case REASON_COLOR_STANDARD :
res &=this.BackColor()==this.DefaultBackColor();
res &=this.ForeColor()==this.DefaultForeColor();
res &=this.BorderColor()==this.DefaultBorderColor();
break;
//--- проверяем равенство всех FOCUSED цветов фона, текста и рамки предустановленным значениям
case REASON_COLOR_FOCUSED :
res &=this.BackColor()==this.BackColorFocused();
res &=this.ForeColor()==this.ForeColorFocused();
res &=this.BorderColor()==this.BorderColorFocused();
break;
//--- проверяем равенство всех PRESSED цветов фона, текста и рамки предустановленным значениям
case REASON_COLOR_PRESSED :
res &=this.BackColor()==this.BackColorPressed();
res &=this.ForeColor()==this.ForeColorPressed();
res &=this.BorderColor()==this.BorderColorPressed();
break;
//--- проверяем равенство всех BLOCKED цветов фона, текста и рамки предустановленным значениям
case REASON_COLOR_BLOCKED :
res &=this.BackColor()==this.BackColorBlocked();
res &=this.ForeColor()==this.ForeColorBlocked();
res &=this.BorderColor()==this.BorderColorBlocked();
break;
default: res=false;
break;
}
return res;
}
//+------------------------------------------------------------------+
//| Рисует внешний вид панели |
//+------------------------------------------------------------------+
void CPanel::Draw(const bool chart_redraw) override
{
//--- Заливаем канвас цветом фона с установленной прозрачностью, рисуем рамку и обновляем канвас
this.m_canvas.Erase(::ColorToARGB(this.BackColor(), this.m_alpha));
this.m_canvas.Rectangle(0, 0, this.m_bound.Width()-1, this.m_bound.Height()-1, ::ColorToARGB(this.BorderColor()));
this.UpdateView(chart_redraw);
}
//+------------------------------------------------------------------+
//| Создаёт панель |
//+------------------------------------------------------------------+
bool CPanel::Create(const string name, const string object_text,const int x,const int y,const int w,const int h) override
{
//--- Создаём графический объект и рисуем его внешний вид с перерисовкой графика
if(!CBaseCanvas::Create(name, object_text, x, y, w, h))
return false;
this.Draw(true);
return true;
}
//+------------------------------------------------------------------+
//| Изменяет размеры холста |
//+------------------------------------------------------------------+
bool CPanel::Resize(const int w,const int h)
{
//--- Если переданы уже установленные размеры, или запрет на изменение - возвращаем true
if(!this.m_resize || (w==this.Width() && h==this.Height()))
return true;
//--- При ошибке изменении размеров возвращаем false
if(!this.m_canvas.Resize(w, h))
return false;
//--- записываем новые размеры в ограничивающий прямоугольник
this.m_bound.Width(w);
this.m_bound.Height(h);
//--- всё успешно
return true;
}
//+------------------------------------------------------------------+
//| Скрывает объект панели на графике |
//+------------------------------------------------------------------+
bool CPanel::Hide(void) override const
{
//--- Скрываем родительский объект
bool res=CBaseCanvas::Hide();
//--- В цикле по всем привязанным объектам скрываем каждый
int total=this.m_list_obj.Total();
for(int i=0; i<total; i++)
{
CBaseCanvas *obj=this.m_list_obj.At(i);
if(obj!=NULL)
res &=obj.Hide();
}
//--- Возвращаем результат скрытия всех объектов
return res;
}
//+------------------------------------------------------------------+
//| Показывает объект панели на графике |
//+------------------------------------------------------------------+
bool CPanel::Show(void) override const
{
//--- Показываем родительский объект
bool res=CBaseCanvas::Show();
//--- В цикле по всем привязанным объектам показываем каждый
int total=this.m_list_obj.Total();
for(int i=0; i<total; i++)
{
CBaseCanvas *obj=this.m_list_obj.At(i);
if(obj!=NULL)
res &=obj.Show();
}
//--- Возвращаем результат отображения всех объектов
return res;
}
//+------------------------------------------------------------------+
//| Перемещает объект на передний план |
//+------------------------------------------------------------------+
bool CPanel::BringToTop(void)
{
//--- Переносим на передний план родительский объект
bool res=CBaseCanvas::BringToTop();
//--- В цикле по всем привязанным объектам переносим на передний план каждый
int total=this.m_list_obj.Total();
for(int i=0; i<total; i++)
{
CBaseCanvas *obj=this.m_list_obj.At(i);
if(obj!=NULL)
res &=obj.BringToTop();
}
//--- Возвращаем результат перемещения на передний план всех объектов
return res;
}
//+------------------------------------------------------------------+
//| Обновляет объект панели для отображения изменений |
//+------------------------------------------------------------------+
void CPanel::UpdateView(const bool chart_redraw) override
{
//--- Обновляем канвас родительского объекта
CBaseCanvas::UpdateView(false);
int total=this.m_list_obj.Total();
//--- В цикле по всем привязанным объектам обновляем канвас каждого
for(int i=0; i<total; i++)
{
CBaseCanvas *obj=this.m_list_obj.At(i);
if(obj!=NULL)
obj.UpdateView(false);
}
//--- Обновляем график при установленном флаге
if(chart_redraw)
::ChartRedraw(this.m_chart_id);
}
//+------------------------------------------------------------------+
//| Установка блокированного состояния объекта |
//+------------------------------------------------------------------+
void CPanel::SetBlocked(void)
{
//--- Устанавливаем флаг блокировки родительскому объекту и перерисовываем его
CBaseCanvas::SetBlocked();
this.Draw(false);
//--- В цикле по всем привязанным объектам устанавливаем флаг блокировки и перерисовываем каждый
int total=this.m_list_obj.Total();
for(int i=0; i<total; i++)
{
CBaseCanvas *obj=this.m_list_obj.At(i);
if(obj==NULL)
continue;
obj.SetBlocked();
obj.Draw(false);
}
//--- Обновляем график
::ChartRedraw(this.m_chart_id);
}
//+------------------------------------------------------------------+
//| Установка разблокированного состояния объекта |
//+------------------------------------------------------------------+
void CPanel::SetUnblocked(void)
{
//--- Снимаем флаг блокировки родительскому объекту и перерисовываем его
CBaseCanvas::SetUnblocked();
this.Draw(false);
//--- В цикле по всем привязанным объектам снимаем флаг блокировки и перерисовываем каждый
int total=this.m_list_obj.Total();
for(int i=0; i<total; i++)
{
CBaseCanvas *obj=this.m_list_obj.At(i);
if(obj==NULL)
continue;
obj.SetUnblocked();
obj.Draw(false);
}
//--- Обновляем график
::ChartRedraw(this.m_chart_id);
}
//+------------------------------------------------------------------+
//| Обработчик событий панели |
//+------------------------------------------------------------------+
void CPanel::OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam)
{
//--- Заблокирован - уходим
if(this.IsBlocked())
return;
//--- Координаты курсора мышки
int x=(int)lparam;
int y=(int)dparam;
//--- Событие перемещения курсора, либо щелчка кнопкой мышки
if(id==CHARTEVENT_MOUSE_MOVE || id==CHARTEVENT_CLICK)
{
//--- Если курсор в пределах объекта
if(this.m_bound.Contains(x, y))
{
//--- Получаем состояние кнопок мышки, если нажаты - вызываем обработчик нажатий
if(sparam=="1" || sparam=="2" || sparam=="16")
this.MousePressHandler(id, lparam, dparam, sparam);
//--- кнопки не нажаты - обрабатываем перемещение курсора
else
this.MouseMoveHandler(id, lparam, dparam, sparam);
}
//--- Курсор за пределами объекта
else
{
//--- восстанавливаем исходные цвета и перерисовываем объект
if(!this.CheckColor(REASON_COLOR_STANDARD))
{
this.ColorChange(REASON_COLOR_STANDARD);
this.Draw(true);
}
}
}
//--- Если пришло пользовательское событие графика
if(id>CHARTEVENT_CUSTOM)
{
//--- собственные события не обрабатываем
if(sparam==this.m_canvas.ChartObjectName())
return;
//--- приводим пользовательское событие в соответствие со стандартными
ENUM_CHART_EVENT chart_event=ENUM_CHART_EVENT(id-CHARTEVENT_CUSTOM);
//--- Если щелчок мышки по объекту
if(chart_event==CHARTEVENT_CLICK)
{
this.CustomMousePressHandler(chart_event, lparam, dparam, sparam);
}
//--- Если перемещение курсора мышки
if(chart_event==CHARTEVENT_MOUSE_MOVE)
{
this.CustomMouseMoveHandler(chart_event, lparam, dparam, sparam);
}
}
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс текстовой метки |
//+------------------------------------------------------------------+
class CLabel : public CPanel
{
public:
//--- Переопределяемые виртуальные методы родительского класса
virtual int Type(void) const { return(OBJECT_TYPE_LABEL); }
virtual CArrayObj*GetList(void) { return NULL; }
virtual bool AttachObject(CBaseCanvas *element) { return false; }
virtual void InitDefaultColors(void);
virtual void Draw(const bool chart_redraw) override;
//--- Конструкторы/деструктор
CLabel (void) : CPanel(::ChartID()) { this.Init(); this.SetTextObject(""); }
CLabel (const long chart_id, const string text) : CPanel(chart_id) { this.Init(); this.SetTextObject(text); }
~CLabel (void){}
};
//+------------------------------------------------------------------+
//| Рисует внешний вид текстовой метки |
//+------------------------------------------------------------------+
void CLabel::Draw(const bool chart_redraw) override
{
//--- Заливаем фон прозрачным цветом, выводим текст и обновляем канвас
this.Clear();
this.m_canvas.TextOut(0, 0, this.Text(),::ColorToARGB(this.ForeColor(), this.m_alpha));
this.UpdateView(true);
}
//+------------------------------------------------------------------+
//| Устанавливает цвета по умолчанию |
//+------------------------------------------------------------------+
void CLabel::InitDefaultColors(void)
{ // standard focused pressed blocked
this.SetDefaultColors(COLOR_BACKGROUND, STATE_OFF, clrNONE, clrNONE, clrNONE, clrNONE);
this.SetDefaultColors(COLOR_FOREGROUND, STATE_OFF, clrBlack, clrBlue, clrDarkBlue, clrSilver);
this.SetDefaultColors(COLOR_BORDER, STATE_OFF, clrNONE, clrNONE, clrNONE, clrNONE);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс простой кнопки |
//+------------------------------------------------------------------+
class CButton : public CLabel
{
private:
//--- Виртуальные обработчики событий
virtual void MouseMoveHandler(const int id, const long lparam, const double dparam, const string sparam);
virtual void MousePressHandler(const int id, const long lparam, const double dparam, const string sparam);
public:
//--- Переопределяемые виртуальные методы родительского класса
virtual int Type(void) const { return(OBJECT_TYPE_BUTTON); }
virtual CArrayObj*GetList(void) { return NULL; }
virtual bool AttachObject(CBaseCanvas *element) { return false; }
virtual void InitDefaultColors(void);
virtual void Draw(const bool chart_redraw) override;
//--- Конструкторы/деструктор
CButton (void) : CLabel(::ChartID(), "") { this.Init(); }
CButton (const long chart_id, const string text) : CLabel(chart_id, text) { this.Init(); }
~CButton (void){}
};
//+------------------------------------------------------------------+
//| Рисует внешний вид кнопки |
//+------------------------------------------------------------------+
void CButton::Draw(const bool chart_redraw) override
{
//--- Заливаем фон цветом фона с установленной прозрачностью и рисуем рамку
this.m_canvas.Erase(::ColorToARGB(this.BackColor(), this.m_alpha));
this.m_canvas.Rectangle(0, 0, this.m_bound.Width()-1, this.m_bound.Height()-1, ::ColorToARGB(this.BorderColor()));
//--- Получаем центральную точку и рассчитываем координаты текста
CPoint pt=this.m_bound.CenterPoint();
int x=pt.x-this.m_bound.left;
int y=pt.y-this.m_bound.top;
//--- Выводим текст с выравниванием по центру и обновляем канвас
this.m_canvas.TextOut(x, y, this.Text(), ::ColorToARGB(this.ForeColor(), this.m_alpha), TA_CENTER|TA_VCENTER);
CBaseCanvas::UpdateView(chart_redraw);
}
//+------------------------------------------------------------------+
//| Устанавливает цвета по умолчанию |
//+------------------------------------------------------------------+
void CButton::InitDefaultColors(void)
{ // standard focused pressed blocked
this.SetDefaultColors(COLOR_BACKGROUND, STATE_OFF, clrWhite, C'216,230,242', C'192,220,243', clrLightGray);
this.SetDefaultColors(COLOR_FOREGROUND, STATE_OFF, clrDimGray, C'0,127,192', C'47,125,180', clrSilver);
this.SetDefaultColors(COLOR_BORDER, STATE_OFF, clrSilver, C'192,220,243', C'144,200,246', clrSilver);
}
//+------------------------------------------------------------------+
//| Обработчик перемещения курсора мышки на объекте |
//+------------------------------------------------------------------+
void CButton::MouseMoveHandler(const int id,const long lparam,const double dparam,const string sparam)
{
//--- Если цвета объекта не соответствуют цветам при наведении курсора
if(!this.CheckColor(REASON_COLOR_FOCUSED))
{
//--- устанавливаем нужный цвет и перерисовываем внешний вид
this.ColorChange(REASON_COLOR_FOCUSED);
this.Draw(true);
}
}
//+------------------------------------------------------------------+
//| Обработчик нажатия кнопки мышки на объекте |
//+------------------------------------------------------------------+
void CButton::MousePressHandler(const int id,const long lparam,const double dparam,const string sparam)
{
//--- Если цвета объекта не соответствуют цветам при щелчке на объекте
if(!this.CheckColor(REASON_COLOR_PRESSED))
{
//--- устанавливаем нужный цвет и перерисовываем внешний вид
this.ColorChange(REASON_COLOR_PRESSED);
this.Draw(true);
}
//--- на щелчок любой кнопкой мышки (sparam = 1: ЛКМ, sparam = 2: ПКМ, sparam = 16: СКМ)
//--- отправляем пользовательское событие на график с именем объекта, по которому был щелчок в sparam
::EventChartCustom(this.m_chart_id, (ushort)CHARTEVENT_CLICK, lparam, dparam, this.Name());
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс двухпозиционной кнопки |
//+------------------------------------------------------------------+
class CButtonTriggered : public CButton
{
private:
//--- Виртуальные обработчики событий
virtual void MousePressHandler(const int id, const long lparam, const double dparam, const string sparam);
public:
//--- Переопределяемые виртуальные методы родительского класса
virtual int Type(void) const { return(OBJECT_TYPE_TBUTTON); }
virtual void InitDefaultColors(void);
//--- Конструкторы/деструктор
CButtonTriggered (void) : CButton(::ChartID(), "") { this.Init(); }
CButtonTriggered (const long chart_id, const string text) : CButton(chart_id, text) { this.Init(); }
~CButtonTriggered (void) {}
};
//+------------------------------------------------------------------+
//| Устанавливает цвета по умолчанию |
//+------------------------------------------------------------------+
void CButtonTriggered::InitDefaultColors(void)
{ // standard focused pressed blocked
this.SetDefaultColors(COLOR_BACKGROUND, STATE_OFF, C'240,240,240', C'235,235,235', C'234,234,234', clrLightGray);
this.SetDefaultColors(COLOR_FOREGROUND, STATE_OFF, C'153,105,129', C'154,106,106', C'152,104,104', clrSilver);
this.SetDefaultColors(COLOR_BORDER, STATE_OFF, C'239,239,239', C'234,234,234', C'233,233,233', clrSilver);
this.SetDefaultColors(COLOR_BACKGROUND, STATE_ON, clrSnow, C'245,250,250', clrSnow, clrSnow);
this.SetDefaultColors(COLOR_FOREGROUND, STATE_ON, C'102,0,58', C'33,0,200', C'102,0,58', clrSilver);
this.SetDefaultColors(COLOR_BORDER, STATE_ON, clrBlack, clrBlack, clrBlack, clrSilver);
}
//+------------------------------------------------------------------+
//| Обработчик нажатия кнопки мышки на объекте |
//+------------------------------------------------------------------+
void CButtonTriggered::MousePressHandler(const int id,const long lparam,const double dparam,const string sparam)
{
//--- Устанавливаем состояние кнопки, обратное уже установленному
ENUM_STATE state=(this.State()==STATE_OFF ? STATE_ON : STATE_OFF);
this.SetState(state);
this.ResetUsedColors(state);
//--- Если цвета объекта не соответствуют цветам при щелчке на объекте
if(!this.CheckColor(REASON_COLOR_PRESSED))
{
//--- устанавливаем нужный цвет и перерисовываем внешний вид
this.ColorChange(REASON_COLOR_PRESSED);
this.Draw(true);
}
//--- на щелчок любой кнопкой мышки (sparam = 1: ЛКМ, sparam = 2: ПКМ, sparam = 16: СКМ)
//--- отправляем пользовательское событие на график с именем объекта, по которому был щелчок в sparam
::EventChartCustom(this.m_chart_id, (ushort)CHARTEVENT_CLICK, lparam, dparam, this.Name());
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс кнопки для вкладки |
//+------------------------------------------------------------------+
class CTabButton : public CButtonTriggered
{
private:
//--- Виртуальные обработчики событий
virtual void MousePressHandler(const int id, const long lparam, const double dparam, const string sparam);
protected:
ENUM_ANCHOR_POINT m_placing; // Расположение кнопки относительно поля вкладки
public:
//--- Переопределяемые виртуальные методы родительского класса
virtual int Type(void) const { return(OBJECT_TYPE_TAB_BUTTON); }
virtual void Draw(const bool chart_redraw) override;
//--- Конструкторы/деструктор
CTabButton (void) : CButtonTriggered(::ChartID(), ""), m_placing(ANCHOR_UPPER) { this.Init(); }
CTabButton (const long chart_id, const string text) : CButtonTriggered(chart_id, text), m_placing(ANCHOR_UPPER) { this.Init(); }
~CTabButton (void) {}
};
//+------------------------------------------------------------------+
//| Рисует внешний вид кнопки |
//+------------------------------------------------------------------+
void CTabButton::Draw(const bool chart_redraw) override
{
int x=0, y=0, w=0, h=0;
if(this.m_placing==ANCHOR_UPPER) // в данной версии расположение кнопки только над полем вкладки (ANCHOR_UPPER)
{
//--- высота рамки на 1 пиксель больше размера кнопки
h=this.m_bound.Height();
w=this.m_bound.Width()-1;
}
//--- Заливаем фон цветом фона и рисуем рамку (нижняя граница рамки на пиксель ниже нижней границы объекта)
this.m_canvas.Erase(::ColorToARGB(this.BackColor(), this.m_alpha));
this.m_canvas.Rectangle(x, y, w, h, ::ColorToARGB(this.BorderColor()));
//--- Получаем центральную точку и рассчитываем координаты текста
CPoint pt=this.m_bound.CenterPoint();
x=pt.x-this.m_bound.left;
y=pt.y-this.m_bound.top;
//--- Выводим текст с выравниванием по центру и обновляем канвас
this.m_canvas.TextOut(x, y, this.Text(), ::ColorToARGB(this.ForeColor(), this.m_alpha), TA_CENTER|TA_VCENTER);
CBaseCanvas::UpdateView(chart_redraw);
}
//+------------------------------------------------------------------+
//| Обработчик нажатия кнопки мышки на объекте |
//+------------------------------------------------------------------+
void CTabButton::MousePressHandler(const int id,const long lparam,const double dparam,const string sparam)
{
//--- Если кнопка в состоянии STATE_ON - уходим
if(this.State())
return;
//--- Устанавливаем состояние кнопки, обратное уже установленному
ENUM_STATE state=(this.State()==STATE_OFF ? STATE_ON : STATE_OFF);
this.SetState(state);
this.ResetUsedColors(state);
//--- Если цвета объекта не соответствуют цветам при щелчке на объекте
if(!this.CheckColor(REASON_COLOR_PRESSED))
{
//--- устанавливаем нужный цвет и перерисовываем внешний вид
this.ColorChange(REASON_COLOR_PRESSED);
this.Draw(true);
}
//--- на щелчок любой кнопкой мышки (sparam = 1: ЛКМ, sparam = 2: ПКМ, sparam = 16: СКМ)
//--- отправляем пользовательское событие на график с именем объекта, по которому был щелчок в sparam
::EventChartCustom(this.m_chart_id, (ushort)CHARTEVENT_CLICK, lparam, dparam, this.Name());
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс кнопки-переключателя |
//+------------------------------------------------------------------+
class CButtonSwitch : public CPanel
{
private:
uchar m_buttons_total; // Количество кнопок в переключателе
uint m_spacing; // Дистанция между кнопками
uint m_selected_button; // Индекс выбранной (нажатой) кнопки
//--- Распределение всех кнопок в ряд по горизонтали
int ArrangeButtons(const uint spacing);
public:
//--- Переопределяемые виртуальные методы родительского класса
virtual int Type(void) const { return(OBJECT_TYPE_SBUTTON); }
virtual void Draw(const bool chart_redraw) override;
virtual bool Create(const string name, const string object_text, const int x, const int y, const int w, const int h) override;
virtual bool MoveX(const int x) override;
virtual bool MoveY(const int y) override;
//--- Программное включение кнопки по индексу
void Select(const uint index);
//--- Установка дистанции между кнопками
void SetSpacing(const uint spacing) { this.m_spacing=spacing; }
//--- Возвращает дистанцию между кнопками
uint Spacing(void) const { return this.m_spacing; }
//--- Добавление новой кнопки в объект
bool AddNewButton(const string &butt_texts[], const int width, const uint spacing=0);
//--- Возвращает указатель на кнопку по индексу
CButtonTriggered *GetButton(const uint index) { return this.GetAttachedObj(index);}
//--- Возвращает индекс выбранной (нажатой) кнопки
uint SelectedButton(void) const { return this.m_selected_button; }
void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Конструкторы/деструктор
CButtonSwitch (void) : m_buttons_total(2), m_spacing(0), m_selected_button(0) { this.Init(); }
CButtonSwitch (const long chart_id, const uchar buttons, const uchar spacing) :
CPanel(chart_id), m_buttons_total(buttons<2 ? 2 : buttons), m_spacing(spacing), m_selected_button(0) { this.Init(); }
~CButtonSwitch (void){}
};
//+------------------------------------------------------------------+
//| Создаёт объект кнопку в указанных координатах |
//+------------------------------------------------------------------+
bool CButtonSwitch::Create(const string name, const string object_text,const int x,const int y,const int w,const int h) override
{
//--- Размер создаваемого объекта не может быть меньше 6 пикселей в ширину (2 кнопки по 3 пикселя), и 3 пикселей в высоту
return(CPanel::Create(name, object_text, x, y, (w<6 ? 6 : w), (h<3 ? 3 : h)));
}
//+------------------------------------------------------------------+
//| Добавляет новые кнопки |
//+------------------------------------------------------------------+
bool CButtonSwitch::AddNewButton(const string &butt_texts[],const int width,const uint spacing=0)
{
//--- Если передан пустой массив текстов кнопок - сообщаем об этом и возвращаем false
if(butt_texts.Size()==0)
{
::PrintFormat("%s: Error. Empty array passed",__FUNCTION__);
return false;
}
//--- Координата X кнопки и количество добавляемых кнопок
int x=0, total=(int)butt_texts.Size();
//--- В цикле по количеству добавляемых кнопок
for(int i=0; i<total; i++)
{
//--- создаём новую двухпозиционную кнопку
CButtonTriggered *obj=new CButtonTriggered(this.m_chart_id, butt_texts[i]);
if(obj==NULL)
{
::PrintFormat("%s: An error occurred while creating a new [%d]\"%s\" button object",__FUNCTION__, i, butt_texts[i]);
return false;
}
//--- Добавляем созданную кнопку в список прикреплённых объектов
if(!this.AttachObject(obj))
{
::PrintFormat("%s: An error occurred while adding a new [%d]\"%s\" button object to the object list",__FUNCTION__, i, butt_texts[i]);
delete obj;
return false;
}
//--- Рассчитываем координату X добавленной кнопки и создаём графический объект кнопки
x=this.X()+width*i+(int)spacing*i;
if(!obj.Create(this.Name()+"_"+(string)i, butt_texts[i], x, this.Y(), width, this.Height()))
{
::PrintFormat("%s: An error occurred while creating the [%d]\"%s\" button",__FUNCTION__, i, butt_texts[i]);
return false;
}
}
//--- Все кнопки успешно добавлены - записываем значение дистанции между кнопками в объект и выбираем кнопку по индексу в m_selected_button
this.SetSpacing(spacing);
this.Select(this.m_selected_button);
//--- В объект Button Switch устанавливаем флаг базового объекта
this.SetBaseObjFlag(true);
//--- Рассчитываем новую ширину объекта и возвращаем результат изменения ширины на новую
int w=total*width+total*(int)this.m_spacing-1;
return(w!=this.Width() ? this.Resize(w, this.Height()) : true);
}
//+------------------------------------------------------------------+
//| Располагает кнопки по горизонтали на дистанции spacing, |
//| возвращает получившуюся ширину объекта |
//+------------------------------------------------------------------+
int CButtonSwitch::ArrangeButtons(const uint spacing)
{
//--- если передано существующее значение дистанции - возвращаем текущую ширину объекта
if(spacing==this.m_spacing)
return this.Width();
//--- Записываем в объект новую дистанцию
this.m_spacing=spacing;
//--- В цикле по количеству кнопок в объекте
CButtonTriggered *obj=NULL;
int total=this.m_list_obj.Total();
for(int i=0; i<total; i++)
{
//--- рассчитываем координату X очередной кнгопки и сдвигаем её на рассчитанную координату
int x=(obj!=NULL ? obj.Right()+(int)this.m_spacing : this.X());
obj=this.m_list_obj.At(i);
if(obj!=NULL)
obj.MoveX(x);
}
//--- Рассчитываем расстояние между правым краем последней кнопки
//--- и координатой X объекта и возвращаем это значение
obj=this.m_list_obj.At(this.m_list_obj.Total()-1);
return(obj!=NULL ? obj.Right()-this.X() : this.Width());
}
//+------------------------------------------------------------------+
//| Рисует кнопки |
//+------------------------------------------------------------------+
void CButtonSwitch::Draw(const bool chart_redraw) override
{
//--- В цикле по всем кнопкам объекта
int total=this.m_list_obj.Total();
for(int i=0;i<total;i++)
{
//--- рисуем каждую без перерисовки графика
CButtonTriggered *obj=this.m_list_obj.At(i);
if(obj!=NULL)
obj.Draw(false);
}
//--- если установлен флаг - перерисовываем график
if(chart_redraw)
::ChartRedraw(this.m_chart_id);
}
//+------------------------------------------------------------------+
//| Выбирает кнопку |
//+------------------------------------------------------------------+
void CButtonSwitch::Select(const uint index)
{
//--- Получаем количество кнопок и проверяем индекс
int total=this.m_list_obj.Total();
if((int)index>total-1)
return;
//--- В цикле по всем кнопкам объекта
for(int i=0;i<total;i++)
{
//--- получаем очередную
//--- если индекс цикла равен индексу выбираемой кнопки - пропускаем её,
CButtonTriggered *obj=this.m_list_obj.At(i);
if(obj==NULL || i==index)
continue;
//--- ставим кнопку в положение OFF и перерисовываем её
obj.SetState(STATE_OFF);
obj.ResetUsedColors(STATE_OFF);
obj.Draw(false);
}
//--- Теперь получаем кнопку, которую нужно выбрать,
CButtonTriggered *obj=this.GetButton(index);
if(obj==NULL)
return;
//--- ставим кнопку в положение ON и перерисовываем её
obj.SetState(STATE_ON);
obj.ResetUsedColors(STATE_ON);
obj.Draw(true);
//--- Записываем в переменную индекс выбранной кнопки
this.m_selected_button=index;
}
//+------------------------------------------------------------------+
//| Обработчик событий |
//+------------------------------------------------------------------+
void CButtonSwitch::OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam)
{
//--- Вызываем обработчики событий каждой прикреплённой кнопки
int total=this.m_list_obj.Total();
for(int i=0;i<total;i++)
{
CButtonTriggered *obj=this.m_list_obj.At(i);
if(obj!=NULL)
obj.OnChartEvent(id, lparam, dparam, sparam);
}
//--- Если пришло пользовательское событие
if(id>CHARTEVENT_CUSTOM)
{
//--- Если кнопка не принадлежит этому объекту - уходим
if(::StringFind(sparam, this.Name())==WRONG_VALUE)
return;
//--- Позиция номера кнопки в строке имени нажатой кнопки в sparam
int pos_butt=::StringLen(this.Name())+1;
if(pos_butt<0)
return;
//--- Получаем из строки номер кнопки и преобразуем в число
string butt_num_str=::StringSubstr(sparam, pos_butt);
if(butt_num_str=="")
return;
long butt_num=::StringToInteger(butt_num_str);
//--- Выбираем кнопку по полученному номеру
this.Select((int)butt_num);
//--- отправляем пользовательское событие на график программы
//--- lparam = номер кнопки
//--- dparam - 0
//--- sparam - имя объекта, по которому был щелчок
::EventChartCustom(this.m_chart_id, (ushort)CHARTEVENT_CLICK, butt_num, 0, this.Name());
}
}
//+------------------------------------------------------------------+
//| Перемещает объект на новую координату x |
//+------------------------------------------------------------------+
bool CButtonSwitch::MoveX(const int x)
{
//--- Смещаем родительский объект на указанную координату
if(!CBaseCanvas::MoveX(x))
return false;
//--- В цикле по всем привязанным кнопкам смещаем каждую, записывая результат в переменную res
bool res=true;
int total=this.m_list_obj.Total();
for(int i=0; i<total; i++)
{
CButtonTriggered *obj=this.m_list_obj.At(i);
if(obj!=NULL)
res &=obj.MoveX(x);
}
//--- Возвращаем результат смещения всех привязанных кнопок
return res;
}
//+------------------------------------------------------------------+
//| Перемещает объект на новую координату y |
//+------------------------------------------------------------------+
bool CButtonSwitch::MoveY(const int y)
{
//--- Смещаем родительский объект на указанную координату
if(!CBaseCanvas::MoveY(y))
return false;
//--- В цикле по всем привязанным кнопкам смещаем каждую, записывая результат в переменную res
bool res=true;
int total=this.m_list_obj.Total();
for(int i=0; i<total; i++)
{
CButtonTriggered *obj=this.m_list_obj.At(i);
if(obj!=NULL)
res &=obj.MoveY(y);
}
//--- Возвращаем результат смещения всех привязанных кнопок
return res;
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс рабочей области вкладки |
//+------------------------------------------------------------------+
class CTabWorkArea : public CObject
{
protected:
CBaseCanvas m_background; // Здесь рисуем всё, что на фоне
CBaseCanvas m_foreground; // Здесь рисуем всё, что на переднем плане
public:
//--- Возврат указателей на фон и передний план
CBaseCanvas *GetBackground(void) { return(&this.m_background); }
CBaseCanvas *GetForeground(void) { return(&this.m_foreground); }
//--- (1) скрытие, (2) отображение, (3) перенос на передний план, (4) изменение размеров объекта
bool Hide(void) const { return(this.m_background.Hide() && this.m_foreground.Hide()); }
bool Show(void) const { return(this.m_background.Show() && this.m_foreground.Show()); }
bool BringToTop(void) { return(this.m_background.BringToTop() && this.m_foreground.BringToTop());}
bool Resize(const int w, const int h) { return(this.m_background.Resize(w, h) && this.m_foreground.Resize(w, h));}
//--- Конструктор/деструктор
CTabWorkArea (void) {}
~CTabWorkArea (void) {}
};
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс объекта вкладки |
//+------------------------------------------------------------------+
class CTab : public CPanel
{
protected:
CTabButton m_button; // Кнопка вкладки
CPanel m_field; // Поле вкладки
CTabWorkArea m_work_area; // Рабочая область вкладки
int m_button_x; // Координата X кнопки
int m_button_y; // Координата Y кнопки
public:
//--- Переопределяемые виртуальные методы родительского класса
virtual int Type(void) const { return(OBJECT_TYPE_TAB); }
virtual bool Create(const string name, const string object_text, const int x, const int y, const int w, const int h) override;
virtual bool Resize(const int w, const int h) override;
//--- Возврат указателей на (1) кнопку, (2) поле, (3) рабочую область, (4) фон, (5) передний план рабочей области
CTabButton *GetButton(void) { return(&this.m_button); }
CPanel *GetField(void) { return(&this.m_field); }
CTabWorkArea *GetWorkArea(void) { return(&this.m_work_area); }
CBaseCanvas *GetWorkAreaBG(void) { return(this.m_work_area.GetBackground()); }
CBaseCanvas *GetWorkAreaFG(void) { return(this.m_work_area.GetForeground()); }
//--- Делает вкладку (1) выбранной, (2) не выбранной
void Select(void);
void Unselect(void);
//--- Координаты (1) X, (2) Y, (3) правого, (4) нижнего края кнопки
int TabButtonX(void) { return this.m_button.X(); }
int TabButtonY(void) { return this.m_button.Y(); }
int TabButtonRight(void) const { return this.m_button.Right(); }
int TabButtonBottom(void) const { return this.m_button.Bottom(); }
//--- Установка координаты (1) X, (2) Y кнопки
void SetButtonX(const int x) { this.m_button_x=x; }
void SetButtonY(const int y) { this.m_button_y=y; }
//--- Скрывает вкладку
virtual bool Hide(void) override const
{
return(this.Hide() && this.m_button.Hide() && this.m_field.Hide() && this.m_work_area.Hide());
}
//--- Показывает вкладку
virtual bool Show(void) override const
{
return(this.Show() && this.m_button.Show() && this.m_field.Show() && this.m_work_area.Show());
}
//--- Обработчик событий
void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam);
//--- Конструкторы/деструктор
CTab(void) : CPanel(::ChartID()), m_button_x(0), m_button_y(0) { this.Init(); }
CTab(const long chart_id, const int id) : CPanel(chart_id), m_button_x(0), m_button_y(0) { this.Init(); this.SetID(id); }
~CTab(void) {}
};
//+------------------------------------------------------------------+
//| Создаёт вкладку |
//+------------------------------------------------------------------+
bool CTab::Create(const string name,const string object_text,const int x,const int y,const int w,const int h)
{
//--- Создаём панель и заливаем прозрачным цветом
if(!CPanel::Create(name, "", x, y, w, h))
return false;
this.Clear();
this.UpdateView(false);
//--- Корректируем значения координат кнопки
if(this.m_button_x==0)
this.m_button_x=3;
if(this.m_button_y==0)
this.m_button_y=2;
//--- Создаём кнопку вкладки
if(!this.m_button.Create(name+"_Button", object_text, this.m_button_x, this.m_button_y, this.m_canvas.TextWidth(object_text)+18, 19))
return false;
//--- Создаём поле вкладки
if(!this.m_field.Create(name+"_Field", "", x, this.m_button.Bottom()-1, this.Width(), this.Height()-this.m_button.Height()-1))
return false;
//--- Создаём фон рабочей области вкладки
if(!this.GetWorkAreaBG().Create(name+"_WA_BG", "", this.m_field.X(), this.m_field.Y(), this.m_field.Width(), this.m_field.Height()))
return false;
//--- Создаём передний план рабочей области вкладки
if(!this.GetWorkAreaFG().Create(name+"_WA_FG", "", this.m_field.X(), this.m_field.Y(), this.m_field.Width(), this.m_field.Height()))
return false;
//--- Заливаем прпозрачным цветом фон и передний план рабочей области
this.GetWorkAreaBG().Clear();
this.GetWorkAreaFG().Clear();
//--- Всё успешно
return true;
}
//+------------------------------------------------------------------+
//| Устанавливает вкладку выбранной |
//+------------------------------------------------------------------+
void CTab::Select(void)
{
//--- Устанавливаем в объект флаг выбранной вкладки
this.SetState(STATE_ON);
//--- Устанавливаем выбранной кнопку,
this.m_button.SetState(STATE_ON);
this.m_button.ResetUsedColors(STATE_ON);
//--- перерисовываем кнопку и переносим на передний план
this.m_button.Draw(false);
this.m_button.BringToTop();
//--- Поле вкладки переносим на передний план и перерисовываем
this.m_field.BringToTop();
this.m_field.Draw(false);
//--- Получаем канвас поля вкладки и
CCanvas canvas=this.m_field.GetCanvas();
int x1=this.m_button.X()-this.m_field.X();
int x2=x1+this.m_button.Width()-1;
//--- закрашиваем цветом фона место соприкосновения кнопки с полем
canvas.Line(x1, 0, x2, 0, this.m_field.BackColor());
canvas.Update(true);
//--- Рабочую область вкладки переносим на передний план
this.m_work_area.BringToTop();
}
//+------------------------------------------------------------------+
//| Устанавливает вкладку невыбранной |
//+------------------------------------------------------------------+
void CTab::Unselect(void)
{
//--- Сбрасываем в объекте флаг выбранной вкладки
this.SetState(STATE_OFF);
//--- Делаем кнопку не выбранной
this.m_button.SetState(STATE_OFF);
this.m_button.ResetUsedColors(STATE_OFF);
this.m_button.Draw(true);
//--- Скрываем поле и рабочую область вкладки
this.m_field.Hide();
this.m_work_area.Hide();
}
//+------------------------------------------------------------------+
//| Изменяет размеры холста |
//+------------------------------------------------------------------+
bool CTab::Resize(const int w, const int h)
{
//--- Размер по высоте должен быть меньше на высоту кнопки (кнопка не меняет размеры)
int xh=h-this.m_button.Height()-1;
if(xh<1)
return false;
//--- Если размер поля вкладки изменён - возвращаем результат изменения размера рабочей области
if(this.m_field.Resize(w, xh))
return this.m_work_area.Resize(w, xh);
//--- Ошибка изменения размера поля вкладки
return false;
}
//+------------------------------------------------------------------+
//| Обработчик событий |
//+------------------------------------------------------------------+
void CTab::OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam)
{
//--- Заблокирован - уходим
if(this.IsBlocked())
return;
//--- Вызываем обработчики событий кнопки и поля вкладки
this.m_button.OnChartEvent(id, lparam, dparam, sparam);
this.m_field.OnChartEvent(id, lparam, dparam, sparam);
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс объекта управления вкладками Tab Control |
//+------------------------------------------------------------------+
class CTabControl : public CPanel
{
public:
//--- Переопределяемые виртуальные методы родительского класса
virtual int Type(void) const { return(OBJECT_TYPE_TAB_CONTROL); }
virtual bool Create(const string name, const string object_text, const int x, const int y, const int w, const int h) override;
virtual bool Resize(const int w, const int h) override;
//--- Возвращает указатель на вкладку по (1) идентификатору, (2) имени, (3) последнюю добавленную, (4) выбранную вкладку
CTab *GetTab(const int id);
CTab *GetTabByName(const string name);
CTab *GetTabLast(void);
CTab *GetTabSelected(void);
//--- Возвращает указатель на (1) кнопку вкладки по идентификатору, (2) фон, (3) передний план рабочей области вкладки
CTabButton *GetTabButton(const int tab_id);
CCanvas *GetTabBackground(const int tab_id);
CCanvas *GetTabForeground(const int tab_id);
//--- Добавляет новую вкладку к объекту
bool AddTab(const int id, const string text);
//--- Возвращает (1) идентификатор выбранной вкладки, (2) количество вкладок в объекте
int GetSelectedTabID(void);
int TabsTotal(void);
//--- Обработчик событий
void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam);
CTabControl(void) : CPanel(::ChartID()) { this.Init(); this.ChartToolsSet(false); this.m_reference_obj=REFERENCE_OBJ_CHART; }
CTabControl(const long chart_id) : CPanel(chart_id) { this.Init(); this.ChartToolsSet(false); this.m_reference_obj=REFERENCE_OBJ_CHART; }
~CTabControl(void) {}
};
//+------------------------------------------------------------------+
//| Создаёт объект Tab Control в указанных координатах |
//+------------------------------------------------------------------+
bool CTabControl::Create(const string name, const string object_text,const int x,const int y,const int w,const int h) override
{
//--- Создаём панель-основание
if(!CPanel::Create(name, object_text, x, y, w, h))
return false;
//--- Устанавливаем цвета по умолчанию
this.SetDefaultColors(COLOR_BACKGROUND, STATE_OFF, C'240,240,240', this.BackColorFocused(), this.BackColorPressed(), this.BackColorBlocked());
this.ResetUsedColors(STATE_OFF);
this.SetDefaultColors(COLOR_BACKGROUND, STATE_ON, C'240,240,240', this.BackColorFocused(), this.BackColorPressed(), this.BackColorBlocked());
this.ResetUsedColors(STATE_ON);
//--- Рисуем объект
this.Draw(true);
return true;
}
//+------------------------------------------------------------------+
//| Изменяет размер вкладок |
//+------------------------------------------------------------------+
bool CTabControl::Resize(const int w,const int h) override
{
//--- Если размеры не изменяемые - возвращаем true
if(!this.m_resize)
return true;
//--- Изменяем размер панели-основания
if(!CPanel::Resize(w, h))
return false;
//--- Рисуем основание
this.Draw(false);
//--- В цикле по количеству вкладок
bool res=true;
CTab *tab=NULL;
int total=this.m_list_obj.Total();
for(int i=0;i<total;i++)
{
//--- получаем очередной объект
tab=this.m_list_obj.At(i);
if(tab==NULL)
continue;
//--- Если это не вкладка, или это выбранная вкладка - пропускаем
if(tab.Type()!=OBJECT_TYPE_TAB || tab.State()==STATE_ON)
continue;
//--- Изменяем размер вкладки
res &=tab.Resize(w, h);
}
//--- Теперь получаем указатель на выбранную вкладку,
tab=this.GetTabSelected();
if(tab==NULL)
return false;
//--- изменяем размер вкладки и ещё раз выбираем её для перерисовки
res &=tab.Resize(w, h);
tab.Select();
return res;
}
//+------------------------------------------------------------------+
//| Возвращает указатель на вкладку по идентификатору |
//+------------------------------------------------------------------+
CTab *CTabControl::GetTab(const int id)
{
//--- В цикле по привязанным объектам
int total=this.m_list_obj.Total();
for(int i=0;i<total;i++)
{
//--- получаем очередной объект и, если это вкладка,
//--- и её идентификатор равен искомому - возвращаем указатель на вкладку
CTab *obj=this.m_list_obj.At(i);
if(obj!=NULL && obj.Type()==OBJECT_TYPE_TAB && obj.ID()==id)
return obj;
}
//--- Ничего не нашли
return NULL;
}
//+------------------------------------------------------------------+
//| Возвращает последнюю добавленную в список вкладку |
//+------------------------------------------------------------------+
CTab *CTabControl::GetTabLast(void)
{
//--- В цикле по всем привязанным объектам от начала до конца
CTab *tab=NULL;
int total=this.m_list_obj.Total();
for(int i=0;i<total;i++)
{
//--- получаем каждый объект и, если это вкладка - запоминаем указатель на него
CTab *obj=this.m_list_obj.At(i);
if(obj!=NULL && obj.Type()==OBJECT_TYPE_TAB)
tab=obj;
}
//--- по завершении цикла возвращаем указатель на последний объект-вкладку
return tab;
}
//+------------------------------------------------------------------+
//| Возвращает указатель на вкладку по имени объекта |
//+------------------------------------------------------------------+
CTab *CTabControl::GetTabByName(const string name)
{
//--- В цикле по количеству привязанных объектов
int total=this.m_list_obj.Total();
for(int i=0;i<total;i++)
{
//--- получаем очередной объект и, если это вкладка
//--- и имя объекта совпадает с искомым - возвращаем указатель на найденный объект
CTab *obj=this.m_list_obj.At(i);
if(obj!=NULL && obj.Type()==OBJECT_TYPE_TAB && obj.Name()==name)
return obj;
}
//--- Ничего не нашли
return NULL;
}
//+------------------------------------------------------------------+
//| Возвращает текущую выбранную вкладку |
//+------------------------------------------------------------------+
CTab *CTabControl::GetTabSelected(void)
{
//--- В цикле по количеству привязанных объектов
int total=this.m_list_obj.Total();
for(int i=0;i<total;i++)
{
//--- получаем очередной объект и, если это вкладка
//--- и объект в состоянии STATE_ON - возвращаем указатель на найденный объект
CTab *obj=this.m_list_obj.At(i);
if(obj!=NULL && obj.Type()==OBJECT_TYPE_TAB && obj.State()==STATE_ON)
return obj;
}
//--- Ничего не нашли
return NULL;
}
//+------------------------------------------------------------------+
//| Возвращает идентификатор выбранной вкладки |
//+------------------------------------------------------------------+
int CTabControl::GetSelectedTabID(void)
{
//--- В цикле по количеству привязанных объектов
int total=this.m_list_obj.Total();
for(int i=0;i<total;i++)
{
//--- получаем очередной объект и, если это вкладка
//--- и объект в состоянии STATE_ON - возвращаем идентификатор найденного объекта
CTab *obj=this.m_list_obj.At(i);
if(obj!=NULL && obj.Type()==OBJECT_TYPE_TAB && obj.State()==STATE_ON)
return obj.ID();
}
return WRONG_VALUE;
}
//+------------------------------------------------------------------+
//| Возвращает указатель на кнопку указанной вкладки |
//+------------------------------------------------------------------+
CTabButton *CTabControl::GetTabButton(const int tab_id)
{
//--- Получаем указатель на вкладку и возвращаем указатель на кнопку этой вкладки
CTab *tab=this.GetTab(tab_id);
return(tab!=NULL ? tab.GetButton() : NULL);
}
//+------------------------------------------------------------------+
//| Возвращает фон рабочей области указанной вкладки |
//+------------------------------------------------------------------+
CCanvas *CTabControl::GetTabBackground(const int tab_id)
{
//--- Получаем указатель на вкладку по идентификатору
CTab *tab=this.GetTab(tab_id);
if(tab==NULL)
return NULL;
//--- Получаем указатель на базовый объект рисования фона рабочей области
//--- и возвращаем объект канвас из полученного объекта
CBaseCanvas *obj=tab.GetWorkAreaBG();
return(obj!=NULL ? obj.GetCanvas() : NULL);
}
//+------------------------------------------------------------------+
//| Возвращает передний план рабочей области указанной вкладки |
//+------------------------------------------------------------------+
CCanvas *CTabControl::GetTabForeground(const int tab_id)
{
//--- Получаем указатель на вкладку по идентификатору
CTab *tab=this.GetTab(tab_id);
if(tab==NULL)
return NULL;
//--- Получаем указатель на базовый объект рисования переднего плана рабочей области
//--- и возвращаем объект канвас из полученного объекта
CBaseCanvas *obj=tab.GetWorkAreaFG();
return(obj!=NULL ? obj.GetCanvas() : NULL);
}
//+------------------------------------------------------------------+
//| Возвращает количество вкладок |
//+------------------------------------------------------------------+
int CTabControl::TabsTotal(void)
{
//--- В цикле по количеству привязанных объектов
int res=0, total=this.m_list_obj.Total();
for(int i=0;i<total;i++)
{
//--- получаем очередной объект и, если это вкладка - увеличиваем счётчик
CBaseCanvas *obj=this.m_list_obj.At(i);
if(obj!=NULL && obj.Type()==OBJECT_TYPE_TAB)
res++;
}
//--- Возвращаем посчитанное количество вкладок
return res;
}
//+------------------------------------------------------------------+
//| Добавляет новую вкладку |
//+------------------------------------------------------------------+
bool CTabControl::AddTab(const int id, const string text)
{
//--- Получаем указатель на вкладку по идентификатору
CTab *obj=this.GetTab(id);
//--- Если вкладка с таким идентификатором уже есть
if(obj!=NULL)
{
//--- сообщаем об этом в журнале
string obj_text=(obj.Text()!="" ? ::StringFormat("(\"%s\")",obj.Text()) : "");
::PrintFormat("%s: Tab with ID %d already exists %s",__FUNCTION__, id, obj_text);
//--- Если у вкладки сброшен флаг состояния (STATE_OFF)
if(!obj.State())
{
//--- делаем вкладку не выбранной и переносим на передний план
obj.Unselect();
obj.GetButton().BringToTop();
}
//--- Получаем указатель на вкладку с состоянием STATE_ON и выбираем её
obj=this.GetTabSelected();
if(obj!=NULL)
obj.Select();
//--- Возвращаем true
return true;
}
//--- Создаём идентификатор новой вкладки. Если передано отрицательное число,
//--- то идентификатор будет равен значению (количество существующих вкладок)+1
int tab_id=(id<0 ? this.TabsTotal()+1 : id);
//--- Создаём новый объект вкладки
obj=new CTab(this.m_chart_id, tab_id);
if(obj==NULL)
return false;
//--- Получаем указатель на последнюю добавленную вкладку в списке
CTab *prev=this.GetTabLast();
//--- Для кнопки новой вкладки устанавливаем координату X в зависимости от наличия вкладок в списке.
//--- Если это самая первая вкладка, то координатой будет 0, иначе - правая граница кнопки предыдущей вкладки+1
obj.SetButtonX(prev==NULL ? 0 : prev.TabButtonRight()+1);
//--- Создаём имя новой вкладки и графический объект вкладки
string name="Tab"+(string)tab_id;
if(!obj.Create(name, text, this.X(), this.Y(), this.Width(), this.Height()))
{
delete obj;
return false;
}
//--- Добавляем новый объект в список прикреплённых объектов
if(!this.AttachObject(obj))
{
delete obj;
return false;
}
//--- Если это самая первая вкладка - делаем её выбранной
if(this.TabsTotal()==1)
obj.Select();
//--- иначе -
else
{
//--- снимаем выбор с добавленной вкладки
obj.Unselect();
//--- находим вкладку с состоянием STATE_ON и выбираем её
obj=this.GetTabSelected();
if(obj!=NULL)
obj.Select();
}
//--- В объект Tab Control устанавливаем флаг базового объекта
this.SetBaseObjFlag(true);
//--- Всё успешно
return true;
}
//+------------------------------------------------------------------+
//| Обработчик событий |
//+------------------------------------------------------------------+
void CTabControl::OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam)
{
//--- Заблокирован - уходим
if(this.IsBlocked())
return;
//--- В цикле по всем прикреплённым объектам вызываем их обработчики событий
int total=this.m_list_obj.Total();
for(int i=0;i<total;i++)
{
CTab *obj=this.m_list_obj.At(i);
if(obj!=NULL)
obj.OnChartEvent(id, lparam, dparam, sparam);
}
//--- Если график изменён
if(id==CHARTEVENT_CHART_CHANGE)
{
//--- Если изменение размеров запрещено - уходим
if(!this.m_resize)
return;
//--- Получаем ширину и высоту графика (остальные типы эталонных объектов здесь пока не используются)
int w=0;
int h=0;
if(this.m_reference_obj==REFERENCE_OBJ_CHART)
{
w=(int)::ChartGetInteger(this.m_chart_id, CHART_WIDTH_IN_PIXELS);
h=(int)::ChartGetInteger(this.m_chart_id, CHART_HEIGHT_IN_PIXELS);
}
if(w==0 || h==0)
return;
//--- Если ширина или высота объекта не равны эталонным - изменяем их
if(w!=this.Width() || h!=this.Height())
this.Resize(w, h);
}
//--- Если пришло пользовательское событие
if(id>CHARTEVENT_CUSTOM)
{
//--- Если имя объекта в sparam соответствует вкладке (начинается с "Tab")
if(::StringFind(sparam, "Tab")==0)
{
//--- расщепляем строку по разделителю "_"
string array[]={};
if(::StringSplit(sparam, ::StringGetCharacter("_", 0), array)==2)
{
//--- Здесь имя вкладки
string tab_name=array[0];
//--- Из имени извлекаем номер вкладки с позиции 3
//string num=::StringSubstr(tab_name, 3);
//--- Если событие пришло от щелчка по кнопке вкладки (после разделителя в строке запись "Button")
if(array[1]=="Button")
{
//--- Получаем указатель на вкладку по её имени
CTab *tab=this.GetTabByName(tab_name);
//--- Если указатель получен
if(tab!=NULL)
{
//--- выбираем вкладку
tab.Select();
//--- В цикле по всем привязанным объектам
int total=this.m_list_obj.Total();
for(int i=0;i<total;i++)
{
//--- получаем очередной объект и, если это не вкладка,
//--- или это вкладка, по которой был щелчок - пропускаем его
CPanel *obj=this.m_list_obj.At(i);
if(obj==NULL || obj.Type()!=OBJECT_TYPE_TAB || obj.Name()==tab_name)
continue;
//--- Это объект вкладки. Щелчок был не по ней - делаем её невыбранной
tab=obj;
tab.Unselect();
}
}
}
}
}
}
}
//+------------------------------------------------------------------+