//+------------------------------------------------------------------+ //| StandardChart.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "..\Element.mqh" #include "Pointer.mqh" //+------------------------------------------------------------------+ //| Класс для создания стандартного графика | //+------------------------------------------------------------------+ class CStandardChart : public CElement { private: //--- Объекты для создания элемента CSubChart m_sub_chart[]; CPointer m_x_scroll; //--- Свойства графиков: long m_sub_chart_id[]; string m_sub_chart_symbol[]; ENUM_TIMEFRAMES m_sub_chart_tf[]; //--- Режим горизонтальной прокрутки bool m_x_scroll_mode; //--- Переменные связанные с горизонтальной прокруткой графика int m_prev_x; int m_new_x_point; int m_prev_new_x_point; //--- Режим изменения высоты подокна bool m_drag_border_window_mode; //--- public: CStandardChart(void); ~CStandardChart(void); //--- Методы для создания стандартного графика bool CreateStandardChart(const int x_gap,const int y_gap); //--- private: bool CreateSubCharts(void); bool CreateXScrollPointer(void); //--- public: //--- (1) Возвращает указатель на указатель курсора мыши, (2) возвращает размер массива графиков CPointer *GetMousePointer(void) { return(::GetPointer(m_x_scroll)); } int SubChartsTotal(void) const { return(::ArraySize(m_sub_chart)); } //--- Возвращает указатель на объект-график по указанному индексу CSubChart *GetSubChartPointer(const uint index); //--- Режим горизонтальной прокрутки void XScrollMode(const bool mode) { m_x_scroll_mode=mode; } //--- Добавляет график с указанными свойствами до создания void AddSubChart(const string symbol,const ENUM_TIMEFRAMES tf); //--- Переход к указанной дате void SubChartNavigate(const datetime date); //--- Сброс графиков void ResetCharts(void); //--- public: //--- Обработчик события графика virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- Перемещение элемента virtual void Moving(const bool only_visible=true); //--- Управление virtual void Show(void); virtual void Hide(void); virtual void Delete(void); //--- (1) Установка, (2) сброс приоритетов на нажатие левой кнопки мыши virtual void SetZorders(void); virtual void ResetZorders(void); //--- private: //--- Обработка нажатия на графике bool OnClickSubChart(const string clicked_object); //--- Проверка символа bool CheckSymbol(const string symbol); //--- Горизонтальная прокрутка void HorizontalScroll(void); //--- Обнуление переменных горизонтальной прокрутки void ZeroHorizontalScrollVariables(void); //--- Проверка режима изменения размеров подокна графика bool CheckDragBorderWindowMode(void); //--- Изменить ширину по правому краю окна virtual void ChangeWidthByRightWindowSide(void); //--- Изменить высоту по нижнему краю окна virtual void ChangeHeightByBottomWindowSide(void); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CStandardChart::CStandardChart(void) : m_prev_x(0), m_new_x_point(0), m_prev_new_x_point(0), m_x_scroll_mode(false), m_drag_border_window_mode(false) { //--- Сохраним имя класса элемента в базовом классе CElementBase::ClassName(CLASS_NAME); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CStandardChart::~CStandardChart(void) { } //+------------------------------------------------------------------+ //| Обработка событий | //+------------------------------------------------------------------+ void CStandardChart::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Обработка события перемещения курсора if(id==CHARTEVENT_MOUSE_MOVE) { //--- Выйти, если в режиме изменения размера подокна if(CheckDragBorderWindowMode()) return; //--- Если есть фокус, проверим горизонтальную прокрутку графика if(CElementBase::MouseFocus()) HorizontalScroll(); //--- Если фокуса нет и левая кнопка мыши отжата else if(!m_mouse.LeftButtonState()) { if(!m_x_scroll.IsVisible()) return; //--- m_prev_x=0; //--- Скрыть указатель горизонтальной прокрутки m_x_scroll.Hide(); ::ChartRedraw(); //--- Отправим сообщение на определение доступных элементов ::EventChartCustom(m_chart_id,ON_SET_AVAILABLE,CElementBase::Id(),1,""); //--- Отправим сообщение об изменении в графическом интерфейсе ::EventChartCustom(m_chart_id,ON_CHANGE_GUI,CElementBase::Id(),0,""); } //--- return; } //--- Обработка события нажатия левой кнопки мыши на объекте if(id==CHARTEVENT_OBJECT_CLICK) { if(OnClickSubChart(sparam)) return; } } //+------------------------------------------------------------------+ //| Создаёт элемент "Стандартный график" | //+------------------------------------------------------------------+ bool CStandardChart::CreateStandardChart(const int x_gap,const int y_gap) { //--- Выйти, если нет указателя на главный элемент if(!CElement::CheckMainPointer()) return(false); //--- Инициализация свойств m_x =CElement::CalculateX(x_gap); m_y =CElement::CalculateY(y_gap); m_x_size =(m_x_size<1 || m_auto_xresize_mode)? m_main.X2()-m_x-m_auto_xresize_right_offset : m_x_size; m_y_size =(m_y_size<1 || m_auto_yresize_mode)? m_main.Y2()-m_y-m_auto_yresize_bottom_offset : m_y_size; //--- Отступы от крайней точки CElementBase::XGap(x_gap); CElementBase::YGap(y_gap); //--- Приоритет, как у главного элемента, так как элемент не имеет своей области для нажатия CElement::Z_Order(m_main.Z_Order()); //--- Создание графика if(!CreateSubCharts()) return(false); if(!CreateXScrollPointer()) return(false); //--- return(true); } //+------------------------------------------------------------------+ //| Создаёт графики | //+------------------------------------------------------------------+ bool CStandardChart::CreateSubCharts(void) { //--- Получим количество графиков int sub_charts_total=SubChartsTotal(); //--- Если нет ни одного графика в группе, сообщить об этом if(sub_charts_total<1) { ::Print(__FUNCTION__," > This method must be called when there is at least one graph in the group! " "Use the method CStandardChart::AddSubChart()"); return(false); } //--- Рассчитаем координаты и размер int x=m_x; int x_size=(sub_charts_total>1)? m_x_size/sub_charts_total : m_x_size; //--- Создадим указанное количество графиков for(int i=0; i0)? x+x_size-1 : x; //--- Корректировка ширины последнего графика if(i+1>=sub_charts_total) x_size=m_x_size-(x_size*(sub_charts_total-1)-(sub_charts_total-1)); //--- Установим кнопку if(!m_sub_chart[i].Create(m_chart_id,name,m_subwin,x,m_y,x_size,m_y_size)) return(false); //--- Скрыть m_sub_chart[i].Timeframes(OBJ_NO_PERIODS); //--- Получим и сохраним идентификатор созданного графика m_sub_chart_id[i]=m_sub_chart[i].GetInteger(OBJPROP_CHART_ID); //--- Установим свойства m_sub_chart[i].Symbol(m_sub_chart_symbol[i]); m_sub_chart[i].Period(m_sub_chart_tf[i]); m_sub_chart[i].Z_Order(m_zorder+1); m_sub_chart[i].Tooltip("\n"); //--- Режим фиксированного масштаба //::ChartSetInteger(m_sub_chart_id[i],CHART_SCALEFIX,true); //--- Максимум и минимум //::ChartSetDouble(m_sub_chart_id[i],CHART_FIXED_MAX,2.0); //::ChartSetDouble(m_sub_chart_id[i],CHART_FIXED_MIN,1.0); //--- Сохраним размеры m_sub_chart[i].XSize(x_size); m_sub_chart[i].YSize(m_y_size); //--- Отступы от крайней точки m_sub_chart[i].XGap(CElement::CalculateXGap(x)); m_sub_chart[i].YGap(CElement::CalculateYGap(m_y)); } //--- return(true); } //+------------------------------------------------------------------+ //| Создаёт указатель курсора горизонтальной прокрутки | //+------------------------------------------------------------------+ bool CStandardChart::CreateXScrollPointer(void) { //--- Выйти, если горизонтальная прокрутка не нужна if(!m_x_scroll_mode) return(true); //--- Установка свойств m_x_scroll.XGap(0); m_x_scroll.YGap(-20); m_x_scroll.Id(CElementBase::Id()); m_x_scroll.Type(MP_X_SCROLL); //--- Создание элемента if(!m_x_scroll.CreatePointer(m_chart_id,m_subwin)) return(false); //--- return(true); } //+------------------------------------------------------------------+ //| Возвращает указатель на график по указанному индексу | //+------------------------------------------------------------------+ CSubChart *CStandardChart::GetSubChartPointer(const uint index) { uint array_size=::ArraySize(m_sub_chart); //--- Если нет ни одного графика, сообщить об этом if(array_size<1) { ::Print(__FUNCTION__," > This method must be called when there is at least one graph in the group!"); return(NULL); } //--- Корректировка в случае выхода из диапазона uint i=(index>=array_size)? array_size-1 : index; //--- Вернуть указатель return(::GetPointer(m_sub_chart[i])); } //+------------------------------------------------------------------+ //| Добавляет график | //+------------------------------------------------------------------+ void CStandardChart::AddSubChart(const string symbol,const ENUM_TIMEFRAMES tf) { //--- Проверим, есть ли такой символ на сервере if(!CheckSymbol(symbol)) { ::Print(__FUNCTION__," > The symbol "+symbol+" is not on the server!"); return; } //--- int reserve=10; //--- Увеличим размер массивов на один элемент int array_size=::ArraySize(m_sub_chart); int new_size=array_size+1; ::ArrayResize(m_sub_chart,new_size,reserve); ::ArrayResize(m_sub_chart_id,new_size,reserve); ::ArrayResize(m_sub_chart_symbol,new_size,reserve); ::ArrayResize(m_sub_chart_tf,new_size,reserve); //--- Сохраним значения переданных параметров m_sub_chart_symbol[array_size] =symbol; m_sub_chart_tf[array_size] =tf; } //+------------------------------------------------------------------+ //| Переход к указанной дате | //+------------------------------------------------------------------+ void CStandardChart::SubChartNavigate(const datetime date) { //--- (1) Текущая дата на графике и (2) только что выбранная в календаре datetime current_date =::StringToTime(::TimeToString(::TimeCurrent(),TIME_DATE)); datetime selected_date =date; //--- Отключим автопрокрутку и сдвиг от правого края ::ChartSetInteger(m_chart_id,CHART_AUTOSCROLL,false); ::ChartSetInteger(m_chart_id,CHART_SHIFT,false); //--- Если выбранная в календаре дата больше, чем текущая if(selected_date>=current_date) { //--- Перейти к текущей дате на всех графиках ::ChartNavigate(m_chart_id,CHART_END); ResetCharts(); return; } //--- Получим количество баров от указанной даты int bars_total =::Bars(::Symbol(),::Period(),selected_date,current_date); int visible_bars =(int)::ChartGetInteger(m_chart_id,CHART_VISIBLE_BARS); long seconds_today =::TimeCurrent()-current_date; int bars_today =int(seconds_today/::PeriodSeconds())+2; //--- Установим отступ от правого края всех графиков m_prev_new_x_point=m_new_x_point=-((bars_total-visible_bars)+bars_today); ::ChartNavigate(m_chart_id,CHART_END,m_new_x_point); //--- int sub_charts_total=SubChartsTotal(); for(int i=0; i0) return; //--- Запомнить текущее положение m_prev_new_x_point=m_new_x_point; //--- Применить ко всем графикам int symbols_total=SubChartsTotal(); //--- Отключим автопрокрутку и сдвиг от правого края for(int i=0; i error: ",::GetLastError()); //--- Обновить график ::ChartRedraw(); } //+------------------------------------------------------------------+ //| Обнуление переменных горизонтальной прокрутки | //+------------------------------------------------------------------+ void CStandardChart::ZeroHorizontalScrollVariables(void) { m_prev_x =0; m_new_x_point =0; m_prev_new_x_point =0; } //+------------------------------------------------------------------+ //| Проверка режима изменения размеров подокна графика | //+------------------------------------------------------------------+ bool CStandardChart::CheckDragBorderWindowMode(void) { //--- Получим высоту главного графика int chart_y_size=(int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS); //--- Если левая кнопка мыши нажата if(m_mouse.LeftButtonState()) { //--- Если режим отключен if(!m_drag_border_window_mode) { //--- Запомним состояние, если курсор мыши в зоне захвата границы для изменения высоты подокна if((m_mouse.SubWindowNumber()==m_subwin && m_mouse.Y()<2) || (m_mouse.SubWindowNumber()==m_subwin && m_mouse.Y()==chart_y_size+1) || (m_mouse.SubWindowNumber()==m_subwin-1 && m_mouse.Y()>=chart_y_size-2)) { m_drag_border_window_mode=true; return(false); } } } //--- Сбросим состояние отключенного режима else m_drag_border_window_mode=false; //--- Вернём результат включенного режима if(m_drag_border_window_mode) return(true); //--- return(false); } //+------------------------------------------------------------------+ //| Изменить ширину по правому краю формы | //+------------------------------------------------------------------+ void CStandardChart::ChangeWidthByRightWindowSide(void) { //--- Координаты int x=0; //--- Размеры int x_size=0; //--- Рассчитать новый размер x_size=m_main.X2()-m_sub_chart[0].X()-m_auto_xresize_right_offset; //--- Не изменять размер, если меньше установленного ограничения if(x_size<80) return; //--- Установить новый общий размер CElementBase::XSize(x_size); //--- Получим количество графиков в группе int sub_charts_total=SubChartsTotal(); //--- Расчёт координат и размера x=m_x; x_size=(sub_charts_total>1)? x_size/sub_charts_total : x_size; //--- Если более одного графика if(sub_charts_total>1) { for(int i=0; i0)? x+x_size-1 : x; //--- Корректировка ширины последнего графика if(i+1>=sub_charts_total) x_size=m_x_size-(x_size*(sub_charts_total-1)-(sub_charts_total-1)); //--- m_sub_chart[i].X(x); m_sub_chart[i].X_Distance(x); //--- m_sub_chart[i].XSize(x_size); m_sub_chart[i].X_Size(x_size); //--- Отступы от крайней точки m_sub_chart[i].XGap(CElement::CalculateXGap(x)); } } else { //--- Установить новый размер CElementBase::XSize(x_size); m_sub_chart[0].XSize(x_size); m_sub_chart[0].X_Size(x_size); } //--- Обновить положение объектов Moving(); } //+------------------------------------------------------------------+ //| Изменить высоту по нижнему краю окна | //+------------------------------------------------------------------+ void CStandardChart::ChangeHeightByBottomWindowSide(void) { //--- Координаты int y=0; //--- Размеры int y_size=0; //--- Рассчитать новый размер y_size=m_main.Y2()-m_y-m_auto_yresize_bottom_offset; //--- Не изменять размер, если меньше установленного ограничения if(y_size<50) return; //--- Получим количество графиков в группе int sub_charts_total=SubChartsTotal(); //--- Если более одного графика if(sub_charts_total>1) { //--- Установить новый размер CElementBase::YSize(y_size); for(int i=0; i