1826 Zeilen
183 KiB
MQL5
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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//+------------------------------------------------------------------+
|