Графика в библиотеке DoEasy (Часть 89): Программное создание стандартных графических объектов. Базовый функционал

27 ноября 2021, 10:27
Artyom Trishkin
8
1 686

Содержание


Концепция

Наша библиотека теперь умеет отслеживать появление на графике клиентского терминала стандартных графических объектов, их удаление и модификацию некоторых их параметров. Иногда бывает полезно из своих программ знать, что на графике появился какой-либо графический объект, выставленный пользователем, или изменены его параметры, либо он удалён, и обработать такие ситуации. Но для полного "комплекта" нам, конечно же, не хватает возможности создавать стандартные графические объекты из своих программ. Имея в своём арсенале функционал по программному созданию графических объектов и отслеживанию изменений их свойств, перед нами открываются интересные возможности по созданию составных графических объектов любой сложности, степени вложенности и количеству управляемых опорных точек. Поэтому сегодня займёмся созданием базового функционала для создания стандартных графических объектов программно. В последующих статьях доведём такой функционал до логического завершения и рассмотрим возможности по созданию пользовательских составных графических объектов на основе стандартных.

Кроме того, начиная с прошлой статьи, мы постепенно переведём объекты библиотеки на использование динамических массивов для хранения их свойств. Сегодня мы исправим логическую ошибку, допущенную в прошлой статье, при создании такого массива, что привело к невозможности отслеживания изменений свойств объектов, у которых опорных точек больше двух. Исправим и доработаем класс многомерного динамического массива так, чтобы его можно было использовать как отдельную единицу библиотеки, и перенесём его в отдельный файл.


Доработка классов библиотеки

В классах-наследниках абстрактного графического объекта нужно дописать некоторые поддерживаемые объектом свойства — для того, чтобы эти свойства могли учитываться при поиске и сортировке и при выводе свойств в журнал терминала. В файлах GStdFiboArcObj.mqh и GStdGannFanObj.mqh впишем строку для поддержания объектом вещественного свойства "Значение уровня":

//+------------------------------------------------------------------+
//| Возвращает истину, если объект поддерживает переданное           |
//| вещественное свойство, возвращает ложь в противном случае        |
//+------------------------------------------------------------------+
bool CGStdFiboArcObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property)
  {
   switch((int)property)
     {
      //--- Поддерживаемые свойства
      case GRAPH_OBJ_PROP_SCALE        :
      case GRAPH_OBJ_PROP_PRICE        :
      case GRAPH_OBJ_PROP_LEVELVALUE   : return true;
      //--- Остальные свойства не поддерживаются
      //--- По умолчанию false
      default: break;
     }
   return false;
  }
//+------------------------------------------------------------------+

В файлах GStdExpansionObj.mqh, GStdFiboChannelObj.mqh, GStdFiboFanObj.mqh, GStdFiboObj.mqh, GStdFiboTimesObj.mqh и GStdPitchforkObj.mqh в этот же метод внесём такие изменения — для поддержания того же свойства:

//+------------------------------------------------------------------+
//| Возвращает истину, если объект поддерживает переданное           |
//| вещественное свойство, возвращает ложь в противном случае        |
//+------------------------------------------------------------------+
bool CGStdPitchforkObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property)
  {
   switch((int)property)
     {
      //--- Поддерживаемые свойства
      case GRAPH_OBJ_PROP_PRICE        :
      case GRAPH_OBJ_PROP_LEVELVALUE   : return true;
      //--- Остальные свойства не поддерживаются
      //--- По умолчанию false
      default: break;
     }
   return false;
  }
//+------------------------------------------------------------------+

В файле класса объекта "Горизонтальная линия" в файле GStdHLineObj.mqh из метода, возвращающего флаг поддержания объектом целочисленного свойства, удалим свойство "Время опорной точки", так как фактически объект для построения использует только цену:

//+------------------------------------------------------------------+
//| Возвращает истину, если объект поддерживает переданное           |
//| целочисленное свойство, возвращает ложь в противном случае       |
//+------------------------------------------------------------------+
bool CGStdHLineObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property)
  {
   switch((int)property)
     {
      //--- Поддерживаемые свойства
      case GRAPH_OBJ_PROP_ID           :
      case GRAPH_OBJ_PROP_TYPE         :
      case GRAPH_OBJ_PROP_ELEMENT_TYPE : 
      case GRAPH_OBJ_PROP_GROUP        : 
      case GRAPH_OBJ_PROP_BELONG       :
      case GRAPH_OBJ_PROP_CHART_ID     :
      case GRAPH_OBJ_PROP_WND_NUM      :
      case GRAPH_OBJ_PROP_NUM          :
      case GRAPH_OBJ_PROP_CREATETIME   :
      case GRAPH_OBJ_PROP_TIMEFRAMES   :
      case GRAPH_OBJ_PROP_BACK         :
      case GRAPH_OBJ_PROP_ZORDER       :
      case GRAPH_OBJ_PROP_HIDDEN       :
      case GRAPH_OBJ_PROP_SELECTED     :
      case GRAPH_OBJ_PROP_SELECTABLE   :
      case GRAPH_OBJ_PROP_TIME         :
      case GRAPH_OBJ_PROP_COLOR        :
      case GRAPH_OBJ_PROP_STYLE        :
      case GRAPH_OBJ_PROP_WIDTH        : return true;
      //--- Остальные свойства не поддерживаются
      //--- По умолчанию false
      default: break;
     }
   return false;
  }
//+------------------------------------------------------------------+

А объект "Вертикальная линия" в файле GStdVLineObj.mqh для своего построения фактически использует только время, поэтому из метода, возвращающего флаг поддержания объектом вещественного свойства, удалим вообще всё — объект не поддерживает никаких вещественных свойств:

//+------------------------------------------------------------------+
//| Возвращает истину, если объект поддерживает переданное           |
//| вещественное свойство, возвращает ложь в противном случае        |
//+------------------------------------------------------------------+
bool CGStdVLineObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property)
  {
   return false;
  }
//+------------------------------------------------------------------+


В прошлой статье мы оставили неисправленной одну логическую ошибку, которая привела к тому, что мы не могли контролировать изменение свойств опорных точек и уровней у объекта, в котором для его построения используется более двух опорных точек, либо есть более двух уровней. Всё дело в методе, добавляющем указанное количество ячеек в конец массива. В метод передавался указатель на объект, созданный извне, и в массив добавлялось указанное количество этих указателей:

//--- Добавляет указанное количество ячеек с объектами в конец массива
   bool              AddQuantity(const string source,const int total,CObject *object)
                       {
                        //--- Объявляем переменную для хранения результата добавления объектов в список
                        bool res=true;
                        //--- в списке по переданному в метод количеству добавляемых объектов
                        for(int i=0;i<total;i++)
                          {
                           //--- если объект добавить в список не удалось
                           if(!this.Add(object))
                             {
                              //--- выводим об этом сообщение, добавляем к значению переменной false
                              //--- и идём на следующую итерацию цикла
                              CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);

                              res &=false;
                              continue;
                             }
                          }
                        //--- Возвращаем общий результат добавления заданного количества объектов в список
                        return res;
                       }

Но это как раз один и тот же объект! Мы просто передаём указатель на где-то созданный за пределами метода объект, и указатели на него размножаем в цикле. Поэтому изменение свойств самого объекта затрагивает все указатели — они же ссылаются на один и тот же объект. Таким образом, мы заполняли массив не разными свойствами, а копиями одного и того же свойства объекта. Соответственно, если у объекта несколько опорных точек, то вторую опорную точку мы размножали на указанное количество, и вместо того, чтобы контролировать реальные значения второй, третьей, четвёртой, пятой точки, мы вписывали в массив свойство второй опорной точки. И это не давало нам получать, отслеживать и изменять значения реальной опорной точки. Изменение второй опорной точки приводило к копированию этих изменений в третью, четвёртую и пятую точки объекта.

Что сделаем. Добавим ещё один метод для создания нового объекта данных. Этот метод будет создавать новый объект-свойство, и уже это новое свойство (а не указатель, как было ранее) мы будем добавлять к массиву в методе AddQuantity(), в котором мы просто добавляли к массиву созданный извне указатель в указанном количестве.

Так как у нас три набора идентичных классов в файле \MQL5\Include\DoEasy\Services\XDimArray.mqh — классы для создания динамических многомерных целочисленного, вещественного и строкового массивов, то рассмотрим на примере классов для создания целочисленного многомерного динамического массива.

В классе одного измерения long-массива напишем метод для создания нового объекта данных:

//+------------------------------------------------------------------+
//| Класс одного измерения long-массива                              |
//+------------------------------------------------------------------+
class CDimLong : public CArrayObj
  {
private:
//--- Создаёт новый объект данных
   CDataUnitLong    *CreateData(const string source,const long value=0)
                       {
                        //--- Создаём новый объект long-данных
                        CDataUnitLong *data=new CDataUnitLong();
                        //--- Если объект создать не удалось - сообщаем об этом в журнал
                        if(data==NULL)
                           ::Print(source,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_LONG_DATA_OBJ));
                        //--- Иначе - устанавливаем объекту переданное в метод значение
                        else
                           data.Value=value;
                        //--- Возвращаем указатель на объект, либо NULL
                        return data;
                       }
//--- Получает объект long-данных из массива

Здесь всё просто: создаём новый объект long-данных и устанавливаем для него переданное в метод значение. В случае, если объект создать не удалось — выводим об этом сообщение в журнал. Метод возвращает указатель на созданный объект, либо NULL при ошибке.

В метод AddQuantity() внесём изменения:

//--- Добавляет указанное количество ячеек с данными в конец массива
   bool              AddQuantity(const string source,const int total,const long value=0)
                       {
                        //--- Объявляем переменную для хранения результата добавления объектов в список
                        bool res=true;
                        //--- в списке по переданному в метод количеству добавляемых объектов
                        for(int i=0;i<total;i++)
                          {
                           //--- Создаём новый объект long-данных
                           CDataUnitLong *data=this.CreateData(DFUN,value);
                           //--- Если объект создать не удалось - сообщаем об этом и идём на следующую итерацию
                           if(data==NULL)
                             {
                              res &=false;
                              continue;
                             }
                           data.Value=value;
                           //--- если объект добавить в список не удалось
                           if(!this.Add(data))
                             {
                              //--- выводим об этом сообщение, удаляем объект, добавляем к значению переменной false
                              //--- и идём на следующую итерацию цикла
                              CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                              delete data;
                              res &=false;
                              continue;
                             }
                          }
                        //--- Возвращаем общий результат добавления заданного количества объектов в список
                        return res;
                       }

Теперь в метод передаём не указатель на объект, а значение, которое нужно присвоить вновь созданному свойству, и в цикле создаём каждый раз новый объект и добавляем его в список.

В методе Increase(), в котором мы ранее создавали новый объект и передавали указатель на него в метод AddQuantity(), теперь просто вызываем метод AddQuantity(), так как теперь именно внутри метода AddQuantity() создаются в цикле новые объекты и добавляются к массиву, а не указатель на один объект, ранее создаваемый в методе Increase().

Удалим из метода этот блок кода:

//--- Увеличивает количество ячеек с данными на указанную величину, возвращает количество добавленных элементов
   int               Increase(const int total,const long value=0)
                       {
                        //--- Запоминаем текущий размер массива
                        int size_prev=this.Total();
                        //--- Создаём новый объект long-данных
                        CDataUnitLong *data=new CDataUnitLong();
                        //--- Если объект создать не удалось - сообщаем об этом и возвращаем ноль
                        if(data==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_LONG_DATA_OBJ));
                           return 0;
                          }
                        //--- Записываем указанное значение во вновь созданный объект
                        data.Value=value;
                        //--- Добавляем указанное количество экземпляров объекта в список
                        //--- и возвращаем разницу между полученным размером массива и прошлым
                        this.AddQuantity(DFUN,total,data);
                        return this.Total()-size_prev;
                       }

А в вызываемый метод AddQuantity() теперь передаём не указатель, а первоначальное значение для вновь добавляемого объекта данных в массив:

//--- Увеличивает количество ячеек с данными на указанную величину, возвращает количество добавленных элементов
   int               Increase(const int total,const long value=0)
                       {
                        //--- Запоминаем текущий размер массива
                        int size_prev=this.Total();
                        //--- Добавляем указанное количество экземпляров объекта в список
                        //--- и возвращаем разницу между полученным размером массива и прошлым
                        this.AddQuantity(DFUN,total,value);
                        return this.Total()-size_prev;
                       }

Точно такие же изменения были проделаны с остальными классами этого файла и здесь мы их повторять не будем — они идентичны.
Со всеми изменениями можно ознакомиться в прилагаемых к статье файлах.

В файле \MQL5\Include\DoEasy\Data.mqh впишем индексы новых сообщений:

//--- CGraphElementsCollection
   MSG_GRAPH_ELM_COLLECTION_ERR_OBJ_ALREADY_EXISTS,   // Ошибка. Уже существует объект управления чартами с идентификатором чарта 
   MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_CREATE_CTRL_OBJ,// Не удалось создать объект управления чартами с идентификатором чарта 
   MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_GET_CTRL_OBJ,  // Не удалось получить объект управления чартами с идентификатором чарта 
   MSG_GRAPH_ELM_COLLECTION_ERR_GR_OBJ_ALREADY_EXISTS,// Такой графический объект уже существует: 
   
//--- GStdGraphObj
   MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ,     // Не удалось создать объект класса для графического объекта 
   MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_STD_GRAPH_OBJ, // Не удалось создать графический объект 
   MSG_GRAPH_STD_OBJ_ERR_NOT_FIND_SUBWINDOW,          // Не удалось найти подокно графика

...

   MSG_GRAPH_OBJ_TEXT_BMP_FILE_STATE_ON,              // Состояние "On"
   MSG_GRAPH_OBJ_TEXT_BMP_FILE_STATE_OFF,             // Состояние "Off"
   
//--- CDataPropObj
   MSG_DATA_PROP_OBJ_OUT_OF_PROP_RANGE,               // Переданное свойство находится за пределами диапазона свойств объекта

//--- CGraphElementsCollection
   MSG_GRAPH_OBJ_FAILED_GET_ADDED_OBJ_LIST,           // Не удалось получить список вновь добавленных объектов
   MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST,         // Не удалось изъять графический объект из списка
   
   MSG_GRAPH_OBJ_CREATE_EVN_CTRL_INDICATOR,           // Создан индикатор контроля и отправки событий
   MSG_GRAPH_OBJ_FAILED_CREATE_EVN_CTRL_INDICATOR,    // Не удалось создать индикатор контроля и отправки событий
   MSG_GRAPH_OBJ_CLOSED_CHARTS,                       // Закрыто окон графиков:
   MSG_GRAPH_OBJ_OBJECTS_ON_CLOSED_CHARTS,            // С ними удалено объектов:
   
  };
//+------------------------------------------------------------------+

и тексты сообщений, соответствующие вновь добавленным индексам:

//--- CGraphElementsCollection
   {"Ошибка. Уже существует объект управления чартами с идентификатором чарта ","Error. A chart control object already exists with chart id "},
   {"Не удалось создать объект управления чартами с идентификатором чарта ","Failed to create chart control object with chart id "},
   {"Не удалось получить объект управления чартами с идентификатором чарта ","Failed to get chart control object with chart id "},
   {"Такой графический объект уже существует: ","Such a graphic object already exists: "},
   
//--- GStdGraphObj
   {"Не удалось создать объект класса для графического объекта ","Failed to create class object for graphic object"},
   {"Не удалось создать графический объект ","Failed to create graphic object "},
   {"Не удалось найти подокно графика","Could not find chart subwindow"},

...

   {"Состояние \"On\"","State \"On\""},
   {"Состояние \"Off\"","State \"Off\""},
   
//--- CDataPropObj
   {"Переданное свойство находится за пределами диапазона свойств объекта","The passed property is outside the range of the object's properties"},
   
//--- CGraphElementsCollection
   {"Не удалось получить список вновь добавленных объектов","Failed to get the list of newly added objects"},
   {"Не удалось изъять графический объект из списка","Failed to detach graphic object from the list"},
   
   {"Создан индикатор контроля и отправки событий","An indicator for monitoring and sending events has been created"},
   {"Не удалось создать индикатор контроля и отправки событий","Failed to create indicator for monitoring and sending events"},
   {"Закрыто окон графиков: ","Closed chart windows: "},
   {"С ними удалено объектов: ","Objects removed with them: "},
   
  };
//+---------------------------------------------------------------------+


Внесём небольшие исправления в класс базового объекта графических объектов библиотеки.

У графических объектов есть bool-свойства, возвращающие флаги некоторых свойств объекта. В классе абстрактного графического объекта у нас есть методы, возвращающие и устанавливающие такие флаги. И в этих методах, в их названии, есть указание на то, что метод устанавливает флаг, например:
SetFlagDrawLines (Отображение линий для волновой разметки Эллиотта). А вот в классе базового объекта графических объектов библиотеки, соответствующий ему метод уже называется SetDrawLines(). Таким образом, если мы пытаемся установить этот флаг для объекта, то видим две подсказки для выбора метода, что сбивает с толку и, мало того, если будет выбран метод не класса абстрактного графического объекта, а базового, то никакого изменения свойств, записываемого в массивы этого объекта, мы не сделаем — мы просто дадим команду на изменение свойства в самом графическом объекте, но в объекте класса этого изменения соответствующего свойства не произойдёт. Поэтому нам стоит переименовать все такие методы так, чтобы их написание было одинаковым — чтобы не ошибаться при выборе из двух методов (впоследствии, думаю, стоит сделать и возвращаемые типы этих методов одинаковыми — для однозначного выбора нужного метода самим компилятором).

В файле \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh сделаем необходимые исправления:

//--- Устанавливает флаг "Объект на заднем плане"
   bool              SetFlagBack(const bool flag)
                       {
                        ::ResetLastError();
                        if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_BACK,flag))
                          {
                           this.m_back=flag;
                           return true;
                          }
                        else
                           CMessage::ToLog(DFUN,::GetLastError(),true);
                        return false;
                       }
//--- Устанавливает флаг "Выделенность объекта"
   bool              SetFlagSelected(const bool flag)
                       {
                        ::ResetLastError();
                        if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTED,flag))
                          {
                           this.m_selected=flag;
                           return true;
                          }
                        else
                           CMessage::ToLog(DFUN,::GetLastError(),true);
                        return false;
                       }
//--- Устанавливает флаг "Выделенность объекта"
   bool              SetFlagSelectable(const bool flag)
                       {
                        ::ResetLastError();
                        if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTABLE,flag))
                          {
                           this.m_selectable=flag;
                           return true;
                          }
                        else
                           CMessage::ToLog(DFUN,::GetLastError(),true);
                        return false;
                       }
//--- Устанавливает флаг "Запрет на показ имени графического объекта в списке объектов терминала"
   bool              SetFlagHidden(const bool flag)
                       {
                        ::ResetLastError();
                        if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTABLE,flag))
                          {
                           this.m_hidden=flag;
                           return true;
                          }
                        else
                           CMessage::ToLog(DFUN,::GetLastError(),true);
                        return false;
                       }


Давайте перенесём классы для создания многомерного динамического массива в отдельный файл. Немного их доработаем так, чтобы они представляли для нас инструмент для создания объекта-свойств для любого из объектов библиотеки, использующего массивы для хранения своих свойств — целочисленных, вещественных и строковых, уже созданного, и ещё планируемого к созданию.

В папке сервисных классов и функций \MQL5\Include\DoEasy\Services\ создадим новый файл Properties.mqh. В него перенесём все классы для создания двумерного массива свойств объекта и для создания объекта-свойств — прошлых и текущих, написанные нами в прошлой статье прямо в теле класса абстрактного стандартного графического объекта CGStdGraphObj. Вот эти:

//+------------------------------------------------------------------+
//| Класс абстрактного стандартного графического объекта             |
//+------------------------------------------------------------------+
class CGStdGraphObj : public CGBaseObj
  {
private:
   //--- Класс свойств объекта
   class CDataPropObj
     {
   private:
      CArrayObj         m_list;        // список объектов-свойств
      int               m_total_int;   // Количество целочисленных параметров
      int               m_total_dbl;   // Количество вещественных параметров
      int               m_total_str;   // Количество строковых параметров
      //--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство
      int               IndexProp(ENUM_GRAPH_OBJ_PROP_DOUBLE property)              const { return(int)property-this.m_total_int;                     }
      int               IndexProp(ENUM_GRAPH_OBJ_PROP_STRING property)              const { return(int)property-this.m_total_int-this.m_total_dbl;    }
   public:
      //--- Возвращает указатель на (1) список объектов-свойств, объект (2) целочисленных, (3) вещественных, (4) строковых свойств
      CArrayObj        *GetList(void)                                                     { return &this.m_list;                                      }
      CXDimArrayLong   *Long()                                                      const { return this.m_list.At(0);                                 }
      CXDimArrayDouble *Double()                                                    const { return this.m_list.At(1);                                 }
      CXDimArrayString *String()                                                    const { return this.m_list.At(2);                                 }
      //--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство объекта
      void              Set(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value)    { this.Long().Set(property,index,value);                    }
      void              Set(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value)   { this.Double().Set(this.IndexProp(property),index,value);  }
      void              Set(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value)   { this.String().Set(this.IndexProp(property),index,value);  }
      //--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство объекта
      long              Get(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index)         const { return this.Long().Get(property,index);                   }
      double            Get(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index)          const { return this.Double().Get(this.IndexProp(property),index); }
      string            Get(ENUM_GRAPH_OBJ_PROP_STRING property,int index)          const { return this.String().Get(this.IndexProp(property),index); }
      
      //--- Возвращает размер указанного массива данных первого измерения
      int               Size(const int range) const
                          {
                           if(range<this.m_total_int)
                              return this.Long().Size(range);
                           else if(range<this.m_total_int+this.m_total_dbl)
                              return this.Double().Size(this.IndexProp((ENUM_GRAPH_OBJ_PROP_DOUBLE)range));
                           else if(range<this.m_total_int+this.m_total_dbl+this.m_total_str)
                              return this.String().Size(this.IndexProp((ENUM_GRAPH_OBJ_PROP_STRING)range));
                           return 0;
                          }
      //--- Устанавливает размер массива в указанной размерности
      bool              SetSizeRange(const int range,const int size)
                          {
                           if(range<this.m_total_int)
                              return this.Long().SetSizeRange(range,size);
                           else if(range<this.m_total_int+this.m_total_dbl)
                              return this.Double().SetSizeRange(this.IndexProp((ENUM_GRAPH_OBJ_PROP_DOUBLE)range),size);
                           else if(range<this.m_total_int+this.m_total_dbl+this.m_total_str)
                              return this.String().SetSizeRange(this.IndexProp((ENUM_GRAPH_OBJ_PROP_STRING)range),size);
                           return false;
                          }
      //--- Конструктор
                        CDataPropObj(const int prop_total_integer,const int prop_total_double,const int prop_total_string)
                          {
                           this.m_total_int=prop_total_integer;
                           this.m_total_dbl=prop_total_double;
                           this.m_total_str=prop_total_string;
                           this.m_list.Add(new CXDimArrayLong(this.m_total_int, 1));
                           this.m_list.Add(new CXDimArrayDouble(this.m_total_dbl,1));
                           this.m_list.Add(new CXDimArrayString(this.m_total_str,1));
                          }
      //--- Деструктор
                       ~CDataPropObj()
                          {
                           m_list.Clear();
                           m_list.Shutdown();
                          }
     };
   //--- Класс данных текущих и прошлых свойств
   class CProperty
     {
   public:
      CDataPropObj     *Curr;    // Указатель на объект текущих свойств
      CDataPropObj     *Prev;    // Указатель на объект прошлых свойств
      //--- Устанавливает размера массива (size) в указанном измерении (range)
      bool              SetSizeRange(const int range,const int size)
                          {
                           return(this.Curr.SetSizeRange(range,size) && this.Prev.SetSizeRange(range,size) ? true : false);
                          }
      //--- Возвращает размер указанного массива (1) текущих, (2) прошлых данных первого измерения
      int               CurrSize(const int range)  const { return Curr.Size(range); }
      int               PrevSize(const int range)  const { return Prev.Size(range); }
      //--- Копирование текущих данных в прошлые
      void              CurrentToPrevious(void)
                          {
                           //--- Копируем все целочисленные свойства
                           for(int i=0;i<this.Curr.Long().Total();i++)
                              for(int r=0;r<this.Curr.Long().Size(i);r++)
                                 this.Prev.Long().Set(i,r,this.Curr.Long().Get(i,r));
                           //--- Копируем все вещественные свойства
                           for(int i=0;i<this.Curr.Double().Total();i++)
                              for(int r=0;r<this.Curr.Double().Size(i);r++)
                                 this.Prev.Double().Set(i,r,this.Curr.Double().Get(i,r));
                           //--- Копируем все строковые свойства
                           for(int i=0;i<this.Curr.String().Total();i++)
                              for(int r=0;r<this.Curr.String().Size(i);r++)
                                 this.Prev.String().Set(i,r,this.Curr.String().Get(i,r));
                          }
      //--- Конструктор
                        CProperty(const int prop_int_total,const int prop_double_total,const int prop_string_total)
                          {
                           this.Curr=new CDataPropObj(prop_int_total,prop_double_total,prop_string_total);
                           this.Prev=new CDataPropObj(prop_int_total,prop_double_total,prop_string_total);
                          }
     };

И нам нужно теперь избавиться от упоминания любых перечислений, принадлежащих к определённому объекту класса — чтобы сделать класс универсальным.

По этой причине заменим все перечисления, по которым выбирались методы, возвращающие реальный индекс свойства, на обычные int-переменные, а для расчёта индексов свойств будем передавать в конструкторы классов значение максимального свойства для вещественного и строкового свойств (целочисленное свойство не имеет никакого смещения своей величины и полностью соответствует значению индекса свойства), а далее просто высчитывать реальные значения, исходя из значения свойства и максимальной величины свойств. В общем — программно, как обычно, будет понятнее.

Итак, во вновь созданный файл \MQL5\Include\DoEasy\Services\Properties.mqh пропишем такие классы:

//+------------------------------------------------------------------+
//|                                                   Properties.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "XDimArray.mqh"
//+------------------------------------------------------------------+
//| Класс свойств объекта                                            |
//+------------------------------------------------------------------+
//--- Класс свойств объекта
class CDataPropObj : public CObject
  {
private:
   CArrayObj         m_list;           // список объектов-свойств
   int               m_total_int;      // Количество целочисленных параметров
   int               m_total_dbl;      // Количество вещественных параметров
   int               m_total_str;      // Количество строковых параметров
   int               m_prop_max_dbl;   // Максимально-возможное значение вещественного свойства
   int               m_prop_max_str;   // Максимально-возможное значение строкового свойства
//--- Возвращает индекс массива, по которому фактически расположено int-, double-, или string-свойство
   int               IndexProp(int property) const
                       {
                        //--- Если переданное значение меньше количества целочисленных параметров,
                        //--- то это целочисленное свойство - возвращаем переданное в метод значение
                        if(property<this.m_total_int)
                           return property;
                        //--- Иначе, если переданное значение меньше максимально-возможного значения вещественного свойства,
                        //--- значит это вещественное свойство - возвращаем рассчитанный индекс в массиве вещественных свойств
                        else if(property<this.m_prop_max_dbl)
                           return property-this.m_total_int;
                        //--- Иначе, если переданное значение меньше максимально-возможного значения строкового свойства,
                        //--- значит это строковое свойство - возвращаем рассчитанный индекс в массиве строковых свойств
                        else if(property<this.m_prop_max_str)
                           return property-this.m_total_int-this.m_total_dbl;
                        //--- Иначе, если переданное значение больше максимального диапазона всех значений всех свойств - 
                        //--- сообщаем об этом в журнал и возвращаем INT_MAX, что обязательно приведёт к ошибке доступа
                        //--- к массиву в классах файла XDimArray, которые выдадут в журнал предупреждение об этом
                        CMessage::ToLog(DFUN,MSG_DATA_PROP_OBJ_OUT_OF_PROP_RANGE);
                        return INT_MAX;
                       }
public:
//--- Возвращает указатель на (1) список объектов-свойств, объект (2) целочисленных, (3) вещественных, (4) строковых свойств
   CArrayObj        *GetList(void)                                                     { return &this.m_list;                                      }
   CXDimArrayLong   *Long()                                                      const { return this.m_list.At(0);                                 }
   CXDimArrayDouble *Double()                                                    const { return this.m_list.At(1);                                 }
   CXDimArrayString *String()                                                    const { return this.m_list.At(2);                                 }
   
//--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство в соответствующий объект свойств
   void              SetLong(int property,int index,long value)                        { this.Long().Set(property,index,value);                    }
   void              SetDouble(int property,int index,double value)                    { this.Double().Set(this.IndexProp(property),index,value);  }
   void              SetString(int property,int index,string value)                    { this.String().Set(this.IndexProp(property),index,value);  }
   //--- Возвращает из соответствующего объекта свойств (1) целочисленное, (2) вещественное и (3) строковое свойство
   long              GetLong(int property,int index)                             const { return this.Long().Get(property,index);                   }
   double            GetDouble(int property,int index)                           const { return this.Double().Get(this.IndexProp(property),index); }
   string            GetString(int property,int index)                           const { return this.String().Get(this.IndexProp(property),index); }
   
//--- Возвращает размер указанного массива данных первого измерения
   int               Size(const int range) const
                       {
                        if(range<this.m_total_int)
                           return this.Long().Size(range);
                        else if(range<this.m_prop_max_dbl)
                           return this.Double().Size(this.IndexProp(range));
                        else if(range<this.m_prop_max_str)
                           return this.String().Size(this.IndexProp(range));
                        return 0;
                       }
//--- Устанавливает размер массива в указанной размерности
   bool              SetSizeRange(const int range,const int size)
                       {
                        if(range<this.m_total_int)
                           return this.Long().SetSizeRange(range,size);
                        else if(range<this.m_prop_max_dbl)
                           return this.Double().SetSizeRange(this.IndexProp(range),size);
                        else if(range<this.m_prop_max_str)
                           return this.String().SetSizeRange(this.IndexProp(range),size);
                        return false;
                       }
//--- Конструктор
                     CDataPropObj(const int prop_total_integer,const int prop_total_double,const int prop_total_string)
                       {
                        //--- Устанавливаем в переменные переданные количества целочисленных, вещественных и строковых свойств
                        this.m_total_int=prop_total_integer;
                        this.m_total_dbl=prop_total_double;
                        this.m_total_str=prop_total_string;
                        //--- Рассчитываем и устанавливаем в переменные максимальные значения вещественных и строковых свойств
                        this.m_prop_max_dbl=this.m_total_int+this.m_total_dbl;
                        this.m_prop_max_str=this.m_total_int+this.m_total_dbl+this.m_total_str;
                        //--- Добавляем в список созданные объекты целочисленных, вещественных и строковых свойств
                        this.m_list.Add(new CXDimArrayLong(this.m_total_int, 1));
                        this.m_list.Add(new CXDimArrayDouble(this.m_total_dbl,1));
                        this.m_list.Add(new CXDimArrayString(this.m_total_str,1));
                       }
//--- Деструктор
                    ~CDataPropObj()
                       {
                        m_list.Clear();
                        m_list.Shutdown();
                       }
  };
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Класс данных текущих и прошлых свойств                           |
//+------------------------------------------------------------------+
class CProperties : public CObject
  {
private:
   CArrayObj         m_list;  // Список для хранения указателей на объекты свойств
public:
   CDataPropObj     *Curr;    // Указатель на объект текущих свойств
   CDataPropObj     *Prev;    // Указатель на объект прошлых свойств
//--- Устанавливает размера массива (size) в указанном измерении (range)
   bool              SetSizeRange(const int range,const int size)
                       {
                        return(this.Curr.SetSizeRange(range,size) && this.Prev.SetSizeRange(range,size) ? true : false);
                       }
//--- Возвращает размер указанного массива (1) текущих, (2) прошлых данных первого измерения
   int               CurrSize(const int range)  const { return Curr.Size(range); }
   int               PrevSize(const int range)  const { return Prev.Size(range); }
//--- Копирование текущих данных в прошлые
   void              CurrentToPrevious(void)
                       {
                        //--- Копируем все целочисленные свойства
                        for(int i=0;i<this.Curr.Long().Total();i++)
                           for(int r=0;r<this.Curr.Long().Size(i);r++)
                              this.Prev.Long().Set(i,r,this.Curr.Long().Get(i,r));
                        //--- Копируем все вещественные свойства
                        for(int i=0;i<this.Curr.Double().Total();i++)
                           for(int r=0;r<this.Curr.Double().Size(i);r++)
                              this.Prev.Double().Set(i,r,this.Curr.Double().Get(i,r));
                        //--- Копируем все строковые свойства
                        for(int i=0;i<this.Curr.String().Total();i++)
                           for(int r=0;r<this.Curr.String().Size(i);r++)
                              this.Prev.String().Set(i,r,this.Curr.String().Get(i,r));
                       }
//--- Конструктор
                     CProperties(const int prop_int_total,const int prop_double_total,const int prop_string_total)
                       {
                        //--- Создаём новые объекты текущих и прошлых свойств
                        this.Curr=new CDataPropObj(prop_int_total,prop_double_total,prop_string_total);
                        this.Prev=new CDataPropObj(prop_int_total,prop_double_total,prop_string_total);
                        //--- Добавляем созданные объекты в список
                        this.m_list.Add(this.Curr);
                        this.m_list.Add(this.Prev);
                       }
//--- Деструктор
                    ~CProperties()
                       {
                        this.m_list.Clear();
                        this.m_list.Shutdown();
                       }
  };
//+------------------------------------------------------------------+

Все основные пояснения прописаны в комментариях к коду. Предлагаю просто самостоятельно сравнить эти классы с теми, что были сделаны нами в прошлой статье в файле \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqh.

Теперь доработаем класс абстрактного стандартного графического объекта в файле \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqh.

В первую очередь подключим к нему только что созданный файл классов свойств объекта:

//+------------------------------------------------------------------+
//|                                                 GStdGraphObj.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "..\GBaseObj.mqh"
#include "..\..\..\Services\Properties.mqh"
//+------------------------------------------------------------------+
//| Класс абстрактного стандартного графического объекта             |
//+------------------------------------------------------------------+
class CGStdGraphObj : public CGBaseObj
  {

Соответственно, классы объектов-свойств у нас уже удалены из приватной секции класса, в ней же объявлен указатель на объект свойств, а в каждом из методов Get и Set в публичной секции класса идёт обращение к соответствующему методу объекта свойств:

//+------------------------------------------------------------------+
//| Класс абстрактного стандартного графического объекта             |
//+------------------------------------------------------------------+
class CGStdGraphObj : public CGBaseObj
  {
private:
   CProperties      *Prop;                                              // Указатель на объект свойств
   int               m_pivots;                                          // Количество опорных точек объекта
//--- Считывает и устанавливает (1) время, (2) цену указанной опорной точки объекта
   void              SetTimePivot(const int index);
   void              SetPricePivot(const int index);
//--- Считывает и устанавливает (1) цвет, (2) стиль, (3) толщину, (4) значение, (5) текст указанного уровня объекта
   void              SetLevelColor(const int index);
   void              SetLevelStyle(const int index);
   void              SetLevelWidth(const int index);
   void              SetLevelValue(const int index);
   void              SetLevelText(const int index);
//--- Считывает и устанавливает имя BMP-файла для объекта "Графическая метка". Индекс: 0-состояние ON, 1-состояние OFF
   void              SetBMPFile(const int index);

public:
//--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство объекта
   void              SetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value)     { this.Prop.Curr.SetLong(property,index,value);    }
   void              SetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value)    { this.Prop.Curr.SetDouble(property,index,value);  }
   void              SetProperty(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value)    { this.Prop.Curr.SetString(property,index,value);  }
//--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство объекта
   long              GetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index)          const { return this.Prop.Curr.GetLong(property,index);   }
   double            GetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index)           const { return this.Prop.Curr.GetDouble(property,index); }
   string            GetProperty(ENUM_GRAPH_OBJ_PROP_STRING property,int index)           const { return this.Prop.Curr.GetString(property,index); }

//--- Устанавливает прошлое (1) целочисленное, (2) вещественное и (3) строковое свойство объекта
   void              SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value) { this.Prop.Prev.SetLong(property,index,value);    }
   void              SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value){ this.Prop.Prev.SetDouble(property,index,value);  }
   void              SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value){ this.Prop.Prev.SetString(property,index,value);  }
//--- Возвращает из массива прошлых свойств (1) целочисленное, (2) вещественное и (3) строковое свойство объекта
   long              GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index)      const { return this.Prop.Prev.GetLong(property,index);   }
   double            GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index)       const { return this.Prop.Prev.GetDouble(property,index); }
   string            GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_STRING property,int index)       const { return this.Prop.Prev.GetString(property,index); }
   
//--- Возвращает себя
   CGStdGraphObj    *GetObject(void)                                       { return &this;}

В публичной же секции добавим деструктор класса, в котором будем удалять объект свойств:

//--- Конструктор по умолчанию
                     CGStdGraphObj(){ this.m_type=OBJECT_DE_TYPE_GSTD_OBJ; m_group=WRONG_VALUE; }
//--- Деструктор
                    ~CGStdGraphObj()
                       {
                        if(this.Prop!=NULL)
                           delete this.Prop;
                       }
protected:
//--- Защищённый параметрический конструктор
                     CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type,
                                   const ENUM_GRAPH_OBJ_BELONG belong,
                                   const ENUM_GRAPH_OBJ_GROUP group,
                                   const long chart_id, const int pivots,
                                   const string name);

В секции методов упрощённого доступа и установки свойств графического объекта подправим методы установки свойств-флагов:

//--- Объект на заднем плане
   bool              Back(void)                    const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_BACK,0);                          }
   void              SetFlagBack(const bool flag)
                       {
                        if(CGBaseObj::SetFlagBack(flag))
                           this.SetProperty(GRAPH_OBJ_PROP_BACK,0,flag);
                       }
//--- Приоритет графического объекта на получение события нажатия мышки на графике
   long              Zorder(void)                  const { return this.GetProperty(GRAPH_OBJ_PROP_ZORDER,0);                              }
   void              SetZorder(const long value)
                       {
                        if(CGBaseObj::SetZorder(value))
                           this.SetProperty(GRAPH_OBJ_PROP_ZORDER,0,value);
                       }
//--- Запрет на показ имени графического объекта в списке объектов терминала
   bool              Hidden(void)                  const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN,0);                        }
   void              SetFlagHidden(const bool flag)
                       {
                        if(CGBaseObj::SetFlagHidden(flag))
                           this.SetProperty(GRAPH_OBJ_PROP_HIDDEN,0,flag);
                       }
//--- Выделенность объекта
   bool              Selected(void)                const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTED,0);                      }
   void              SetFlagSelected(const bool flag)
                       {
                        if(CGBaseObj::SetFlagSelected(flag))
                           this.SetProperty(GRAPH_OBJ_PROP_SELECTED,0,flag);
                       }
//--- Доступность объекта
   bool              Selectable(void)              const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTABLE,0);                    }
   void              SetFlagSelectable(const bool flag)
                       {
                        if(CGBaseObj::SetFlagSelectable(flag))
                           this.SetProperty(GRAPH_OBJ_PROP_SELECTABLE,0,flag);
                       }
//--- Координата времени

Метод, копирующий текущие свойства в прошлые, перенесём из приватной секции в публичную:

//--- Возвращает описание видимости объекта на таймфреймах
   string            VisibleOnTimeframeDescription(void);

//--- Перезаписывает все свойства графического объекта
   void              PropertiesRefresh(void);
//--- Проверяет изменения свойств объекта
   void              PropertiesCheckChanged(void);
//--- Копирует текущие данные в прошлые
   void              PropertiesCopyToPrevData(void);
   
private:
//--- Получает и сохраняет (1) целочисленные, (2) вещественные и (3) строковые свойства
   void              GetAndSaveINT(void);
   void              GetAndSaveDBL(void);
   void              GetAndSaveSTR(void);

  };
//+------------------------------------------------------------------+

В защищённом параметрическом конструкторе создадим новый объект свойств графического объекта:

//+------------------------------------------------------------------+
//| Защищённый параметрический конструктор                           |
//+------------------------------------------------------------------+
CGStdGraphObj::CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type,
                             const ENUM_GRAPH_OBJ_BELONG belong,
                             const ENUM_GRAPH_OBJ_GROUP group,
                             const long chart_id,const int pivots,
                             const string name)
  {
   //--- Создаём объект свойств со значениями по умолчанию
   this.Prop=new CProperties(GRAPH_OBJ_PROP_INTEGER_TOTAL,GRAPH_OBJ_PROP_DOUBLE_TOTAL,GRAPH_OBJ_PROP_STRING_TOTAL);
   
//--- Устанавливаем количество опорных точек и уровней объекта

В конструктор класса объекта свойств передаём количество целочисленных, вещественных и строковых свойств графического объекта.

В методе, получающем целочисленные свойства из графического объекта и сохраняющем их в свойства объекта класса, нам нужно проверить изменилось ли количество уровней у объекта и, если изменилось, то изменить размеры массивов всех свойств, в которых хранятся значения уровней.
В противном случае, при установке свойств уровней мы получим ошибку обращения за пределы массива:

   //--- Свойства, принадлежащие разным графическим объектам
   this.SetProperty(GRAPH_OBJ_PROP_FILL,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_FILL));              // Заливка объекта цветом
   this.SetProperty(GRAPH_OBJ_PROP_READONLY,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_READONLY));      // Возможность редактирования текста в объекте Edit
   this.SetProperty(GRAPH_OBJ_PROP_LEVELS,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_LEVELS));          // Количество уровней
   
   if(this.GetProperty(GRAPH_OBJ_PROP_LEVELS,0)!=this.GetPropertyPrev(GRAPH_OBJ_PROP_LEVELS,0))                      // Проверка изменения количества уровней
     {
      this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELCOLOR,this.Levels());
      this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELSTYLE,this.Levels());
      this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELWIDTH,this.Levels());
      this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELVALUE,this.Levels());
      this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELTEXT,this.Levels()); 
     }
   for(int i=0;i<this.Levels();i++)                                                                                  // Данные уровней
     {
      this.SetLevelColor(i);
      this.SetLevelStyle(i);
      this.SetLevelWidth(i);
     }
   this.SetProperty(GRAPH_OBJ_PROP_ALIGN,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ALIGN));            // Горизонтальное выравнивание текста в объекте "Поле ввода" (OBJ_EDIT)

Упростим метод, проверяющий изменения свойств объекта:

//+------------------------------------------------------------------+
//| Проверяет изменения свойств объекта                              |
//+------------------------------------------------------------------+
void CGStdGraphObj::PropertiesCheckChanged(void)
  {
   bool changed=false;
   int begin=0, end=GRAPH_OBJ_PROP_INTEGER_TOTAL;
   for(int i=begin; i<end; i++)
     {
      ENUM_GRAPH_OBJ_PROP_INTEGER prop=(ENUM_GRAPH_OBJ_PROP_INTEGER)i;
      if(!this.SupportProperty(prop)) continue;
      for(int j=0;j<Prop.CurrSize(prop);j++)
        {
         if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j))
           {
            changed=true;
            ::Print(DFUN,this.Name(),": ",TextByLanguage(" Изменённое свойство: "," Modified property: "),this.GetPropertyDescription(prop));
           }
        }
     }

   begin=end; end+=GRAPH_OBJ_PROP_DOUBLE_TOTAL;
   for(int i=begin; i<end; i++)
     {
      ENUM_GRAPH_OBJ_PROP_DOUBLE prop=(ENUM_GRAPH_OBJ_PROP_DOUBLE)i;
      if(!this.SupportProperty(prop)) continue;
      for(int j=0;j<Prop.CurrSize(prop);j++)
        {
         if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j))
           {
            changed=true;
            ::Print(DFUN,this.Name(),": ",TextByLanguage(" Изменённое свойство: "," Modified property: "),this.GetPropertyDescription(prop));
           }
        }
     }

   begin=end; end+=GRAPH_OBJ_PROP_STRING_TOTAL;
   for(int i=begin; i<end; i++)
     {
      ENUM_GRAPH_OBJ_PROP_STRING prop=(ENUM_GRAPH_OBJ_PROP_STRING)i;
      if(!this.SupportProperty(prop)) continue;
      for(int j=0;j<Prop.CurrSize(prop);j++)
        {
         if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j))

           {
            changed=true;
            ::Print(DFUN,this.Name(),": ",TextByLanguage(" Изменённое свойство: "," Modified property: "),this.GetPropertyDescription(prop));
           }
        }
     }
   if(changed)
      PropertiesCopyToPrevData();
  }
//+------------------------------------------------------------------+

В прошлой реализации метода мы использовали конструкцию if-else для проверки того, что свойство имеет множественные значения (как, например, время точки привязки), и каждое такое свойство обрабатывали отдельным блоком кода. Но, так как для каждого из свойств мы можем знать размер массива свойства, то достаточно в любом случае просто пройтись по количеству значений одного свойства в цикле по размеру второго измерения массива. Для одиночного свойства размер второго измерения равен 1, для множественного — количеству значений мультисвойств этого свойства. Таким образом можно всё делать в одном цикле для каждого из свойств объекта, что мы выше и сделали.

В этом же файле были сделаны незначительные доработки, такие как изменение наименований методов. Например LevelColorsDescription() был переименован в LevelsColorDescription(), что более соответствует назначению метода. Подобные переименования мы здесь рассматривать не будем — их можно посмотреть в прилагаемых к статье файлах.

Каждый из объектов библиотеки у нас имеет собственный идентификатор объекта. По этому идентификатору, в дополнение к другим свойствам объекта, можно его идентифицировать. В классе-коллекции графических элементов у нас располагаются две коллекции — коллекция графических элементов, разработку которой мы пока приостановили до завершения разработки коллекции графических объектов, которой мы на данный момент занимаемся, и в которой в свою очередь есть стандартные графические объекты, создаваемые вручную, и стандартные графические объекты, создаваемые программно. Сегодня мы как раз начнём разработку функционала для программного создания стандартных графических объектов.
Так вот, возвращаясь к идентификаторам объектов: идентификаторы графических объектов, созданных программно, будут находиться в пределах от 1 до 10000 включительно. А значения идентификаторов графических объектов, созданных вручную, будут начинаться от 10001.
Пропишем это пороговое значение в файле \MQL5\Include\DoEasy\Defines.mqh:

//--- Идентификаторы типов отложенных запросов
#define PENDING_REQUEST_ID_TYPE_ERR    (1)                        // Тип отложенного запроса, созданного по коду возврата сервера
#define PENDING_REQUEST_ID_TYPE_REQ    (2)                        // Тип отложенного запроса, созданного по запросу
//--- Параметры таймсерий
#define SERIES_DEFAULT_BARS_COUNT      (1000)                     // Требуемое количество данных таймсерий по умолчанию
#define PAUSE_FOR_SYNC_ATTEMPTS        (16)                       // Количество миллисекунд паузы между попытками синхронизации
#define ATTEMPTS_FOR_SYNC              (5)                        // Количество попыток получения факта синхронизации с сервером
//--- Параметры тиковых серий
#define TICKSERIES_DEFAULT_DAYS_COUNT  (1)                        // Требуемое количество дней для тиковых данных в сериях по умолчанию
#define TICKSERIES_MAX_DATA_TOTAL      (200000)                   // Максимальное количество хранимых тиковых данных одного символа
//--- Параметры серий снимков стакана цен
#define MBOOKSERIES_DEFAULT_DAYS_COUNT (1)                        // Требуемое количество дней для снимков стакана цен в сериях по умолчанию
#define MBOOKSERIES_MAX_DATA_TOTAL     (200000)                   // Максимальное количество хранимых снимков стакана цен одного символа
//--- Параметры канваса
#define PAUSE_FOR_CANV_UPDATE          (16)                       // Частота обновления канваса
#define NULL_COLOR                     (0x00FFFFFF)               // Ноль для канваса с альфа-каналом
#define OUTER_AREA_SIZE                (16)                       // Размер одной стороны внешней области вокруг рабочего пространства
//--- Параметры графических объектов
#define PROGRAM_OBJ_MAX_ID             (10000)                    // Максимальное значение идентификатора графического объекта, принадлежащего программе
//+------------------------------------------------------------------+
//| Перечисления                                                     |
//+------------------------------------------------------------------+


Методы для программного создания стандартных графических объектов

У нас уже есть методы, отслеживающие создание графических объектов на графике вручную, и создающие соответствующие им объекты классов и добавляющие их в список-коллекцию. Конечно хотелось бы их и использовать для текущих задач, но есть некоторые моменты, заставившие отказаться от использования уже частично готовых методов. Просто потому, что мы отслеживаем появление графических объектов в таймере, а при программном создании объекта нам бы не хотелось дожидаться очередного тика таймера и определять, что за объект создан и каким образом — программно или вручную.

Мы поступим иначе — создадим методы в классе-коллекции графических элементов для создания стандартных графических объектов. Сразу же после создания объекта создадим соответствующий ему объект класса и поместим его в коллекцию. А вот поиск изменений в свойствах этого объекта мы возложим на уже созданный функционал. Таким образом, мы получим возможность быстро создавать объекты и добавлять их в коллекцию, но в то же время одинаково использовать поиск их изменения без разделения на то, как был создан этот объект. Это упростит в дальнейшем создание составных графических объектов и управление их свойствами.

В создаваемом программно графическом объекте, в его имени, будет присутствовать наименование программы, из которой был создан объект. Это позволит отличать "свои" графические объекты от созданных вручную.
Для этого в файле класса-коллекции графических элементов  \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh в классе управления объектами чарта в приватной секции добавим новую переменную для хранения имени программы:

//+------------------------------------------------------------------+
//| Класс управления объектами чарта                                 |
//+------------------------------------------------------------------+
class CChartObjectsControl : public CObject
  {
private:
   CArrayObj         m_list_new_graph_obj;      // Список добавленных графических объектов
   ENUM_TIMEFRAMES   m_chart_timeframe;         // Период графика
   long              m_chart_id;                // Идентификатор графика
   long              m_chart_id_main;           // Идентификатор графика управляющей программы
   string            m_chart_symbol;            // Символ графика
   bool              m_is_graph_obj_event;      // Флаг события в списке графических объектов
   int               m_total_objects;           // Количество графических объектов
   int               m_last_objects;            // Количество графических объектов на прошлой проверке
   int               m_delta_graph_obj;         // Разница в количестве графических объектов по сравнению с прошлой проверкой
   int               m_handle_ind;              // Хендл индикатора-контроллера событий
   string            m_name_ind;                // Короткое имя индикатора-контроллера событий
   string            m_name_program;            // Название программы
   
//--- Возвращает имя последнего добавленного на график графического объекта
   string            LastAddedGraphObjName(void);
//--- Устанавливает для графика разрешение отслеживания событий мышки и графических объектов
   void              SetMouseEvent(void);

public:

В публичной секции класса удалим из метода CreateNewGraphObj() указание идентификатора чарта, так как этот идентификатор является одним из первостепенных свойств объекта управления объектами чарта, и его можно получить прямо из объекта, а не передавать в метод.
В конструкторах класса установим значение имени программы в соответствующую переменную:

public:
//--- Возврат значений переменных
   ENUM_TIMEFRAMES   Timeframe(void)                           const { return this.m_chart_timeframe;    }
   long              ChartID(void)                             const { return this.m_chart_id;           }
   string            Symbol(void)                              const { return this.m_chart_symbol;       }
   bool              IsEvent(void)                             const { return this.m_is_graph_obj_event; }
   int               TotalObjects(void)                        const { return this.m_total_objects;      }
   int               Delta(void)                               const { return this.m_delta_graph_obj;    }
//--- Создаёт новый объект стандартного графического объекта
   CGStdGraphObj    *CreateNewGraphObj(const ENUM_OBJECT obj_type,const string name);
//--- Возвращает список вновь добавленных объектов
   CArrayObj        *GetListNewAddedObj(void)                        { return &this.m_list_new_graph_obj;}
//--- Создаёт индикатор контроля событий
   bool              CreateEventControlInd(const long chart_id_main);
//--- Добавляет на чарт индикатор контроля событий
   bool              AddEventControlInd(void);
//--- Проверяет объекты на чарте
   void              Refresh(void);
//--- Конструкторы
                     CChartObjectsControl(void)
                       { 
                        this.m_name_program=::MQLInfoString(MQL_PROGRAM_NAME);
                        this.m_chart_id=::ChartID();
                        this.m_chart_timeframe=(ENUM_TIMEFRAMES)::ChartPeriod(this.m_chart_id);
                        this.m_chart_symbol=::ChartSymbol(this.m_chart_id);
                        this.m_chart_id_main=::ChartID();
                        this.m_list_new_graph_obj.Clear();
                        this.m_list_new_graph_obj.Sort();
                        this.m_is_graph_obj_event=false;
                        this.m_total_objects=0;
                        this.m_last_objects=0;
                        this.m_delta_graph_obj=0;
                        this.m_name_ind="";
                        this.m_handle_ind=INVALID_HANDLE;
                        this.SetMouseEvent();
                       }
                     CChartObjectsControl(const long chart_id)
                       { 
                        this.m_name_program=::MQLInfoString(MQL_PROGRAM_NAME);
                        this.m_chart_timeframe=(ENUM_TIMEFRAMES)::ChartPeriod(this.m_chart_id);
                        this.m_chart_symbol=::ChartSymbol(this.m_chart_id);
                        this.m_chart_id_main=::ChartID();
                        this.m_list_new_graph_obj.Clear();
                        this.m_list_new_graph_obj.Sort();
                        this.m_chart_id=chart_id;
                        this.m_is_graph_obj_event=false;
                        this.m_total_objects=0;
                        this.m_last_objects=0;
                        this.m_delta_graph_obj=0;
                        this.m_name_ind="";
                        this.m_handle_ind=INVALID_HANDLE;
                        this.SetMouseEvent();
                       }

В методе, проверяющем объекты на чарте, наряду с проверкой на пустое имя, будем проверять и на то, что объект не создан программно:

//+------------------------------------------------------------------+
//| CChartObjectsControl: Проверяет объекты на чарте                 |
//+------------------------------------------------------------------+
void CChartObjectsControl::Refresh(void)
  {
//--- Графические объекты на чарте
   this.m_total_objects=::ObjectsTotal(this.ChartID());
   this.m_delta_graph_obj=this.m_total_objects-this.m_last_objects;
   
//--- Если количество объектов изменилось
   if(this.m_delta_graph_obj!=0)
     {
      //--- Создадим строку и выведем её в журнал с указанием идентификатора графика, его символа и периода
      string txt=", "+(m_delta_graph_obj>0 ? "Added: " : "Deleted: ")+(string)fabs(m_delta_graph_obj)+" obj";
      Print(DFUN,"ChartID=",this.ChartID(),", ",this.Symbol(),", ",TimeframeDescription(this.Timeframe()),txt);
     }
   //--- Если добавлен объект на график
   if(this.m_delta_graph_obj>0)
     {
      //--- находим последний добавленный графический объект, выбираем его и записываем его имя
      string name=this.LastAddedGraphObjName();
      if(name!="" && ::StringFind(name,m_name_program)==WRONG_VALUE)
        {
         //--- Создаём объект класса графического объекта, соответствующий типу добавленного графического объекта
         ENUM_OBJECT type=(ENUM_OBJECT)::ObjectGetInteger(this.ChartID(),name,OBJPROP_TYPE);
         ENUM_OBJECT_DE_TYPE obj_type=ENUM_OBJECT_DE_TYPE(type+OBJECT_DE_TYPE_GSTD_OBJ+1);
         CGStdGraphObj *obj=this.CreateNewGraphObj(type,name);
         if(obj==NULL)
            return;
         //--- Устанавливаем объекту его принадлежность и добавляем созданный объект в список новых объектов
         obj.SetBelong(GRAPH_OBJ_BELONG_NO_PROGRAM); 
         if(this.m_list_new_graph_obj.Search(obj)==WRONG_VALUE)
           {
            this.m_list_new_graph_obj.Add(obj);
           }
        }
     }
     
//--- сохранение индекса последнего добавленного графического объекта и разницы по сравнению с прошлой проверкой
   this.m_last_objects=this.m_total_objects;
   this.m_is_graph_obj_event=(bool)this.m_delta_graph_obj;
  }
//+------------------------------------------------------------------+

Т.е. если в имени объекта нет вхождения подстроки с именем программы, то такой объект должен обрабатываться этим методом, иначе — это программно созданный графический объект, и его добавлением в список-коллекцию занимается другой метод.

В методе создания нового объекта стандартного графического объекта заменим все chart_id, которые ранее передавались в метод, на значение идентификатора графика, установленного для данного объекта, и которым он и управляет:

//+------------------------------------------------------------------+
//| CChartObjectsControl:                                            |
//| Создаёт новый объект стандартного графического объекта           |
//+------------------------------------------------------------------+
CGStdGraphObj *CChartObjectsControl::CreateNewGraphObj(const ENUM_OBJECT obj_type,const string name)
  {
   CGStdGraphObj *obj=NULL;
   switch((int)obj_type)
     {
      //--- Линии
      case OBJ_VLINE             : return new CGStdVLineObj(this.ChartID(),name);
      case OBJ_HLINE             : return new CGStdHLineObj(this.ChartID(),name);
      case OBJ_TREND             : return new CGStdTrendObj(this.ChartID(),name);
      case OBJ_TRENDBYANGLE      : return new CGStdTrendByAngleObj(this.ChartID(),name);
      case OBJ_CYCLES            : return new CGStdCyclesObj(this.ChartID(),name);
      case OBJ_ARROWED_LINE      : return new CGStdArrowedLineObj(this.ChartID(),name);
      //--- Каналы
      case OBJ_CHANNEL           : return new CGStdChannelObj(this.ChartID(),name);
      case OBJ_STDDEVCHANNEL     : return new CGStdStdDevChannelObj(this.ChartID(),name);
      case OBJ_REGRESSION        : return new CGStdRegressionObj(this.ChartID(),name);
      case OBJ_PITCHFORK         : return new CGStdPitchforkObj(this.ChartID(),name);
      //--- Ганн
      case OBJ_GANNLINE          : return new CGStdGannLineObj(this.ChartID(),name);
      case OBJ_GANNFAN           : return new CGStdGannFanObj(this.ChartID(),name);
      case OBJ_GANNGRID          : return new CGStdGannGridObj(this.ChartID(),name);
      //--- Фибоначчи
      case OBJ_FIBO              : return new CGStdFiboObj(this.ChartID(),name);
      case OBJ_FIBOTIMES         : return new CGStdFiboTimesObj(this.ChartID(),name);
      case OBJ_FIBOFAN           : return new CGStdFiboFanObj(this.ChartID(),name);
      case OBJ_FIBOARC           : return new CGStdFiboArcObj(this.ChartID(),name);
      case OBJ_FIBOCHANNEL       : return new CGStdFiboChannelObj(this.ChartID(),name);
      case OBJ_EXPANSION         : return new CGStdExpansionObj(this.ChartID(),name);
      //--- Эллиотт
      case OBJ_ELLIOTWAVE5       : return new CGStdElliotWave5Obj(this.ChartID(),name);
      case OBJ_ELLIOTWAVE3       : return new CGStdElliotWave3Obj(this.ChartID(),name);
      //--- Фигуры
      case OBJ_RECTANGLE         : return new CGStdRectangleObj(this.ChartID(),name);
      case OBJ_TRIANGLE          : return new CGStdTriangleObj(this.ChartID(),name);
      case OBJ_ELLIPSE           : return new CGStdEllipseObj(this.ChartID(),name);
      //--- Стрелки
      case OBJ_ARROW_THUMB_UP    : return new CGStdArrowThumbUpObj(this.ChartID(),name);
      case OBJ_ARROW_THUMB_DOWN  : return new CGStdArrowThumbDownObj(this.ChartID(),name);
      case OBJ_ARROW_UP          : return new CGStdArrowUpObj(this.ChartID(),name);
      case OBJ_ARROW_DOWN        : return new CGStdArrowDownObj(this.ChartID(),name);
      case OBJ_ARROW_STOP        : return new CGStdArrowStopObj(this.ChartID(),name);
      case OBJ_ARROW_CHECK       : return new CGStdArrowCheckObj(this.ChartID(),name);
      case OBJ_ARROW_LEFT_PRICE  : return new CGStdArrowLeftPriceObj(this.ChartID(),name);
      case OBJ_ARROW_RIGHT_PRICE : return new CGStdArrowRightPriceObj(this.ChartID(),name);
      case OBJ_ARROW_BUY         : return new CGStdArrowBuyObj(this.ChartID(),name);
      case OBJ_ARROW_SELL        : return new CGStdArrowSellObj(this.ChartID(),name);
      case OBJ_ARROW             : return new CGStdArrowObj(this.ChartID(),name);
      //--- Графические объекты
      case OBJ_TEXT              : return new CGStdTextObj(this.ChartID(),name);
      case OBJ_LABEL             : return new CGStdLabelObj(this.ChartID(),name);
      case OBJ_BUTTON            : return new CGStdButtonObj(this.ChartID(),name);
      case OBJ_CHART             : return new CGStdChartObj(this.ChartID(),name);
      case OBJ_BITMAP            : return new CGStdBitmapObj(this.ChartID(),name);
      case OBJ_BITMAP_LABEL      : return new CGStdBitmapLabelObj(this.ChartID(),name);
      case OBJ_EDIT              : return new CGStdEditObj(this.ChartID(),name);
      case OBJ_EVENT             : return new CGStdEventObj(this.ChartID(),name);
      case OBJ_RECTANGLE_LABEL   : return new CGStdRectangleLabelObj(this.ChartID(),name);
      default                    : return NULL;
     }
  }
//+------------------------------------------------------------------+


В классе коллекции графических объектов CGraphElementsCollection в метод, возвращающий первый свободный идентификатор графического объекта, добавим флаг, указывающий идентификатор какого именно объекта нам нужен: false — для созданного вручную, true — для созданного програмно:

//--- Возвращает первый свободный идентификатор графического (1) объекта, (2) элемента на канвасе
   long              GetFreeGraphObjID(bool program_object);
   long              GetFreeCanvElmID(void);
//--- Добавляет графический объект в коллекцию

И объявим приватный метод, создающий новый стандартный графический объект:

//--- Удаляет объект управления графиками из списка
   bool              DeleteGraphObjCtrlObjFromList(CChartObjectsControl *obj);
//--- Создаёт новый стандартный графический объект, возвращает имя объекта
   bool              CreateNewStdGraphObject(const long chart_id,
                                             const string name,
                                             const ENUM_OBJECT type,
                                             const int subwindow,
                                             const datetime time1,
                                             const double price1,
                                             const datetime time2=0,
                                             const double price2=0,
                                             const datetime time3=0,
                                             const double price3=0,
                                             const datetime time4=0,
                                             const double price4=0,
                                             const datetime time5=0,
                                             const double price5=0);
public:

В приватной секции класса напишем метод, создающий новый графический объект и возвращающий указатель на объект управления чартами:

//--- Обработчик событий
   void              OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam);

private:
//--- Создаёт новый графический объект, возвращает указатель на объект управления чартами
   CChartObjectsControl *CreateNewStdGraphObjectAndGetCtrlObj(const long chart_id,
                                                              const string name,
                                                              int subwindow,
                                                              const ENUM_OBJECT type_object,
                                                              const datetime time1,
                                                              const double price1,
                                                              const datetime time2=0,
                                                              const double price2=0,
                                                              const datetime time3=0,
                                                              const double price3=0,
                                                              const datetime time4=0,
                                                              const double price4=0,
                                                              const datetime time5=0,
                                                              const double price5=0)
                       {
                        //--- Если объект с идентификатором графика и именем уже есть в коллекции - сообщаем об этом и возвращаем NULL
                        if(this.IsPresentGraphObjInList(chart_id,name))
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_ELM_COLLECTION_ERR_GR_OBJ_ALREADY_EXISTS)," ChartID ",(string)chart_id,", ",name);
                           return NULL;
                          }
                        //--- Если не удалось создать новый стандартный графический объект - сообщаем об этом и возвращаем NULL
                        if(!this.CreateNewStdGraphObject(chart_id,name,type_object,subwindow,time1,0))
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_STD_GRAPH_OBJ),StdGraphObjectTypeDescription(type_object));
                           CMessage::ToLog(::GetLastError(),true);
                           return NULL;
                          }
                        //--- Если не удалось получить объект управления графиком - сообщаем об этом
                        CChartObjectsControl *ctrl=this.GetChartObjectCtrlObj(chart_id);
                        if(ctrl==NULL)
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_GET_CTRL_OBJ),(string)chart_id);
                        //--- Возвращаем указатель на объект управления графиком, либо NULL при ошибке его получения
                        return ctrl;
                       }

public:

Логика метода расписана в комментариях к коду. Данный метод будет использоваться при создании конкретно указанных типов стандартных графических объектов, которые расположим далее в публичной секции класса.

Метод, создающий графический объект "Вертикальная линия":

public:
//--- Создаёт графический объект "Вертикальная линия"
   bool              CreateLineVertical(const long chart_id,const string name,const int subwindow,const datetime time)
                       {
                        //--- Задаём имя и тип создаваемого объекта
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_VLINE;
                        //--- Создаём новый графический объект и получаем указатель на объект управления графиком
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,0);
                        if(ctrl==NULL)
                           return false;
                        //--- Создаём новый объект класса, соответствующий созданному графическому объекту
                        CGStdVLineObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        //--- Если объект не удалось добавить в список-коллекцию,
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           //--- сообщаем об этом, удаляем графический объект и объект класса и возвращаем false
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        //--- Перерисовываем график и (временно для теста) выводим все свойства объекта в журнал
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

Метод достаточно подробно расписан в комментариях к коду. В каждом таком методе будут устанавливаться свои свойства, присущие каждому конкретному объекту, передаваемые в параметрах. Сначала создаётся физический графический объект на указанном графике, получается указатель на объект управления этого графика и при помощи его метода CreateNewGraphObj() создаётся объект класса, соответствующий типу созданного объекта. Если этот объект не удалось добавить в список, то сам физический графический объект и объект класса удаляются с сообщением об ошибке в журнал. При успешном создании график обновляется и метод возвращает true.

Остальные методы для создания графических объектов идентичны вышерассмотренному, и отличаются лишь своим набором устанавливаемых параметров для каждого конкретного графического объекта.
Просто рассмотрим листинг всех остальных добавленных методов:

//--- Создаёт графический объект "Горизонтальная линия"
   bool              CreateLineHorizontal(const long chart_id,const string name,const int subwindow,const double price)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_HLINE;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,0,price);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdHLineObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }
 
//--- Создаёт графический объект "Трендовая линия"
   bool              CreateLineTrend(const long chart_id,const string name,const int subwindow,
                                     const datetime time1,const double price1,const datetime time2,const double price2)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_TREND;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdTrendObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }
//--- Создаёт графический объект "Трендовая линия по углу"
   bool              CreateLineTrendByAngle(const long chart_id,const string name,const int subwindow,
                                            const datetime time1,const double price1,const datetime time2,const double price2,const double angle)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_TRENDBYANGLE;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdTrendByAngleObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.SetAngle(angle);
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Циклические линии"
   bool              CreateLineCycle(const long chart_id,const string name,const int subwindow,
                                     const datetime time1,double price1,const datetime time2,double price2)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_CYCLES;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdCyclesObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Линия со стрелкой"
   bool              CreateLineArrowed(const long chart_id,const string name,const int subwindow,
                                       const datetime time1,double price1,const datetime time2,double price2)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_ARROWED_LINE;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdArrowedLineObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Равноудаленный канал"
   bool              CreateChannel(const long chart_id,const string name,const int subwindow,
                                   const datetime time1,double price1,const datetime time2,
                                   double price2,const datetime time3,double price3)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_CHANNEL;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2,time3,price3);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdChannelObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Канал стандартного отклонения"
   bool              CreateChannelStdDeviation(const long chart_id,const string name,const int subwindow,
                                               const datetime time1,double price1,const datetime time2,double price2,const double deviation=1.5)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_STDDEVCHANNEL;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdStdDevChannelObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.SetDeviation(deviation);
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Канал на линейной регрессии"
   bool              CreateChannelRegression(const long chart_id,const string name,const int subwindow,
                                             const datetime time1,double price1,const datetime time2,double price2)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_REGRESSION;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdRegressionObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Вилы Эндрюса"
   bool              CreatePitchforkAndrews(const long chart_id,const string name,const int subwindow,
                                            const datetime time1,double price1,const datetime time2,double price2,const datetime time3,double price3)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_PITCHFORK;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2,time3,price3);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdPitchforkObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Линия Ганна"
   bool              CreateGannLine(const long chart_id,const string name,const int subwindow,
                                    const datetime time1,double price1,const datetime time2,double price2,double angle)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_GANNLINE;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdGannLineObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.SetAngle(angle);
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Веер Ганна"
   bool              CreateGannFan(const long chart_id,const string name,const int subwindow,
                                   const datetime time1,double price1,const datetime time2,double price2,
                                   const ENUM_GANN_DIRECTION direction,const double scale)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_GANNFAN;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdGannFanObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.SetDirection(direction);
                        obj.SetScale(scale);
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Сетка Ганна"
   bool              CreateGannGrid(const long chart_id,const string name,const int subwindow,
                                    const datetime time1,double price1,const datetime time2,
                                    const ENUM_GANN_DIRECTION direction,const double scale)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_GANNGRID;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdGannGridObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.SetDirection(direction);
                        obj.SetScale(scale);
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Уровни Фибоначчи"
   bool              CreateFiboLevels(const long chart_id,const string name,const int subwindow,
                                      const datetime time1,double price1,const datetime time2,double price2)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_FIBO;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdFiboObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Временные зоны Фибоначчи"
   bool              CreateFiboTimeZones(const long chart_id,const string name,const int subwindow,
                                         const datetime time1,double price1,const datetime time2,double price2)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_FIBOTIMES;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdFiboTimesObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Веер Фибоначчи"
   bool              CreateFiboFan(const long chart_id,const string name,const int subwindow,
                                   const datetime time1,double price1,const datetime time2,double price2)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_FIBOFAN;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdFiboFanObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Дуги Фибоначчи"
   bool              CreateFiboArc(const long chart_id,const string name,const int subwindow,
                                   const datetime time1,double price1,const datetime time2,double price2,
                                   const double scale,const bool ellipse)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_FIBOARC;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdFiboArcObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.SetScale(scale);
                        obj.SetFlagEllipse(ellipse);
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Канал Фибоначчи"
   bool              CreateFiboChannel(const long chart_id,const string name,const int subwindow,
                                       const datetime time1,double price1,const datetime time2,double price2,const datetime time3,double price3)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_FIBOCHANNEL;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2,time3,price3);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdFiboChannelObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }
//--- Создаёт графический объект "Расширение Фибоначчи"
   bool              CreateFiboExpansion(const long chart_id,const string name,const int subwindow,
                                         const datetime time1,double price1,const datetime time2,double price2,const datetime time3,double price3)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_EXPANSION;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2,time3,price3);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdExpansionObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "5-волновка Эллиота"
   bool              CreateElliothWave5(const long chart_id,const string name,const int subwindow,
                                        const datetime time1,double price1,const datetime time2,double price2,
                                        const datetime time3,double price3,const datetime time4,double price4,
                                        const datetime time5,double price5,const ENUM_ELLIOT_WAVE_DEGREE degree,
                                        const bool draw_lines)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_ELLIOTWAVE5;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2,time3,price3,time4,price4,time5,price5);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdElliotWave5Obj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.SetDegree(degree);
                        obj.SetFlagDrawLines(draw_lines);
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "3-волновка Эллиота"
   bool              CreateElliothWave3(const long chart_id,const string name,const int subwindow,
                                        const datetime time1,double price1,const datetime time2,
                                        double price2,const datetime time3,double price3,
                                        const ENUM_ELLIOT_WAVE_DEGREE degree,const bool draw_lines)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_ELLIOTWAVE3;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2,time3,price3);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdElliotWave3Obj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.SetDegree(degree);
                        obj.SetFlagDrawLines(draw_lines);
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Прямоугольник"
   bool              CreateRectangle(const long chart_id,const string name,const int subwindow,
                                     const datetime time1,double price1,const datetime time2,double price2)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_RECTANGLE;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdRectangleObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Треугольник"
   bool              CreateTriangle(const long chart_id,const string name,const int subwindow,
                                    const datetime time1,double price1,const datetime time2,double price2,const datetime time3,double price3)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_TRIANGLE;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2,time3,price3);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdTriangleObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Эллипс"
   bool              CreateEllipse(const long chart_id,const string name,const int subwindow,
                                   const datetime time1,double price1,const datetime time2,double price2,const datetime time3,double price3)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_ELLIPSE;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2,time3,price3);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdEllipseObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Знак "Хорошо"
   bool              CreateThumbUp(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_ARROW_THUMB_UP;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdArrowThumbUpObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Знак "Плохо"
   bool              CreateThumbDown(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_ARROW_THUMB_DOWN;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdArrowThumbDownObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Знак "Стрелка вверх"
   bool              CreateArrowUp(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_ARROW_UP;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdArrowUpObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Знак "Стрелка вниз"
   bool              CreateArrowDown(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_ARROW_DOWN;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdArrowDownObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Знак "Стоп"
   bool              CreateSignalStop(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_ARROW_STOP;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdArrowStopObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Знак "Птичка"
   bool              CreateSignalCheck(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_ARROW_CHECK;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdArrowCheckObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Левая ценовая метка"
   bool              CreatePriceLabelLeft(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_ARROW_LEFT_PRICE;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdArrowLeftPriceObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Правая ценовая метка"
   bool              CreatePriceLabelRight(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_ARROW_RIGHT_PRICE;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdArrowRightPriceObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Знак "Buy"
   bool              CreateSignalBuy(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_ARROW_BUY;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdArrowBuyObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Знак "Sell"
   bool              CreateSignalSell(const long chart_id,const string name,const int subwindow,const datetime time,const double price)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_ARROW_SELL;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdArrowSellObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Стрелка"
   bool              CreateArrow(const long chart_id,const string name,const int subwindow,const datetime time,const double price,
                                 const uchar arrow_code,const ENUM_ARROW_ANCHOR anchor)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_ARROW;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdArrowObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.SetArrowCode(arrow_code);
                        obj.SetAnchor(anchor);
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }
//--- Создаёт графический объект "Текст"
   bool              CreateText(const long chart_id,const string name,const int subwindow,const datetime time,const double price,
                                const string text,const int size,const ENUM_ANCHOR_POINT anchor_point,const double angle)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_TEXT;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdTextObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.SetText(text);
                        obj.SetFontSize(size);
                        obj.SetAnchor(anchor_point);
                        obj.SetAngle(angle);
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Текстовая метка"
   bool              CreateTextLabel(const long chart_id,const string name,const int subwindow,const int x,const int y,
                                     const string text,const int size,const ENUM_BASE_CORNER corner,
                                     const ENUM_ANCHOR_POINT anchor_point,const double angle)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_LABEL;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,0,0);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdLabelObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.SetXDistance(x);
                        obj.SetYDistance(y);
                        obj.SetText(text);
                        obj.SetFontSize(size);
                        obj.SetCorner(corner);
                        obj.SetAnchor(anchor_point);
                        obj.SetAngle(angle);
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Кнопка"
   bool              CreateButton(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h,
                                  const ENUM_BASE_CORNER corner,const int font_size,const bool button_state)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_BUTTON;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,0,0);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdButtonObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.SetXDistance(x);
                        obj.SetYDistance(y);
                        obj.SetXSize(w);
                        obj.SetYSize(h);
                        obj.SetCorner(corner);
                        obj.SetFontSize(font_size);
                        obj.SetFlagState(button_state);
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "График"
   bool              CreateChart(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h,
                                 const ENUM_BASE_CORNER corner,const int scale,const string symbol,const ENUM_TIMEFRAMES timeframe)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_CHART;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,0,0);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdChartObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        obj.SetXDistance(x);
                        obj.SetYDistance(y);
                        obj.SetXSize(w);
                        obj.SetYSize(h);
                        obj.SetCorner(corner);
                        obj.SetChartObjChartScale(scale);
                        obj.SetChartObjSymbol(symbol);
                        obj.SetChartObjPeriod(timeframe);
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Рисунок"
   bool              CreateBitmap(const long chart_id,const string name,const int subwindow,const datetime time,const double price,
                                  const string image1,const string image2,const ENUM_ANCHOR_POINT anchor)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_BITMAP;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdBitmapObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.SetAnchor(anchor);
                        obj.SetBMPFile(image1,0);
                        obj.SetBMPFile(image2,1);
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Графическая метка"
   bool              CreateBitmapLabel(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h,
                                       const string image1,const string image2,const ENUM_BASE_CORNER corner,const ENUM_ANCHOR_POINT anchor,
                                       const bool state)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_BITMAP_LABEL;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,0,0);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdBitmapLabelObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.SetXDistance(x);
                        obj.SetYDistance(y);
                        obj.SetXSize(w);
                        obj.SetYSize(h);
                        obj.SetCorner(corner);
                        obj.SetAnchor(anchor);
                        obj.SetBMPFile(image1,0);
                        obj.SetBMPFile(image2,1);
                        obj.SetFlagState(state);
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Поле ввода"
   bool              CreateEditField(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h,
                                     const int font_size,const ENUM_BASE_CORNER corner,const ENUM_ALIGN_MODE align,const bool readonly)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_EDIT;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,0,0);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdEditObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.SetXDistance(x);
                        obj.SetYDistance(y);
                        obj.SetXSize(w);
                        obj.SetYSize(h);
                        obj.SetFontSize(font_size);
                        obj.SetCorner(corner);
                        obj.SetAlign(align);
                        obj.SetFlagReadOnly(readonly);
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Событие экономического календаря"
   bool              CreateCalendarEvent(const long chart_id,const string name,const int subwindow,const datetime time)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_EVENT;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,0);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdEventObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

//--- Создаёт графический объект "Прямоугольная метка"
   bool              CreateRectangleLabel(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h,
                                          const ENUM_BASE_CORNER corner,const ENUM_BORDER_TYPE border)
                       {
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_RECTANGLE_LABEL;
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,0,0);
                        if(ctrl==NULL)
                           return false;
                        
                        CGStdRectangleLabelObj *obj=ctrl.CreateNewGraphObj(type_object,nm);
                        if(obj==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object));
                           return false;
                          }
                        //--- Устанавливаем объекту необходимые минимальные параметры
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.SetXDistance(x);
                        obj.SetYDistance(y);
                        obj.SetXSize(w);
                        obj.SetYSize(h);
                        obj.SetCorner(corner);
                        obj.SetBorderType(border);
                        obj.PropertiesCopyToPrevData();
                        
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }
 
  };
//+------------------------------------------------------------------+

Во всех методах есть свой набор входных параметров, и эти параметры устанавливаются как минимальные свойства объекта, достаточные для его создания. Все остальные свойства можно поменять после создания графического объекта.

Метод, возвращающий первый свободный идентификатор графического объекта:

//+------------------------------------------------------------------+
//| Возвращает первый свободный идентификатор графического объекта   |
//+------------------------------------------------------------------+
long CGraphElementsCollection::GetFreeGraphObjID(bool program_object)
  {
   CArrayObj *list=NULL;
   int index=WRONG_VALUE;
   if(program_object)
      list=CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),GRAPH_OBJ_PROP_ID,0,PROGRAM_OBJ_MAX_ID,EQUAL_OR_LESS);
   else
      list=CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),GRAPH_OBJ_PROP_ID,0,PROGRAM_OBJ_MAX_ID,MORE);
   index=CSelect::FindGraphicStdObjectMax(list,GRAPH_OBJ_PROP_ID,0);
   CGStdGraphObj *obj=list.At(index);
   int first_id=(program_object ? 1 : PROGRAM_OBJ_MAX_ID+1);
   return(obj!=NULL ? obj.ObjectID()+1 : first_id);
  }
//+------------------------------------------------------------------+

Теперь метод учитывает какой именно идентификатор нам нужно получить.
Если для графического объекта, созданного программно, то получаем список всех объектов, у которых идентификатор меньше, либо равен значению константы PROGRAM_OBJ_MAX_ID (10000).
Если же для графического объекта, созданного вручную, то получаем список всех объектов, у которых идентификатор больше значения PROGRAM_OBJ_MAX_ID.
Далее из полученного списка получаем индекс объекта с максимальным значением идентификатора, а по индексу — объект из списка.
Затем рассчитываем значение самого первого идентификатора (для программного объекта — 1, для созданного вручную — 10000+1).
Если объект с максимальным значением идентификатора получен, то возвращаем значение его идентификатора+1, иначе — объекта нет в списке, и возвращаем рассчитанное значение первого идентификатора (1 или 10001)

В методе, добавляющем графический объект в коллекцию, для поиска идентификатора объекта узнаем какой это объект — созданный программно, или вручную (по имени объекта), и это значение передадим в метод GetFreeGraphObjID():

//+------------------------------------------------------------------+
//| Добавляет графический объект в коллекцию                         |
//+------------------------------------------------------------------+
bool CGraphElementsCollection::AddGraphObjToCollection(const string source,CChartObjectsControl *obj_control)
  {
   //--- Из класса управления графическими объектами получаем список последних добавленных графических объектов
   CArrayObj *list=obj_control.GetListNewAddedObj();
   //--- Если список получить не удалось - сообщаем об этом и возвращаем false
   if(list==NULL)
     {
      CMessage::ToLog(DFUN_ERR_LINE,MSG_GRAPH_OBJ_FAILED_GET_ADDED_OBJ_LIST);
      return false;
     }
   //--- Если список пустой - возвращаем false
   if(list.Total()==0)
      return false;
   //--- Объявляем переменную для хранения результата
   bool res=true;
   //--- В цикле по списку вновь добавленных стандартных графических объектов
   for(int i=0;i<list.Total();i++)
     {
      //--- извлекаем из списка очередной объект и
      CGStdGraphObj *obj=list.Detach(i);
      //--- если объект извлечь не удалось - сообщаем об этом, добавляем в результирующую переменную false и идём к следующему
      if(obj==NULL)
        {
         CMessage::ToLog(source,MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST);
         res &=false;
         continue;
        }
      //--- если объект не удалось добавить в список-коллекцию - сообщаем об этом,
      //--- удаляем объект, добавляем в результирующую переменную false и идём к следующему
      if(!this.m_list_all_graph_obj.Add(obj))
        {
         CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
         delete obj;
         res &=false;
         continue;
        }
      //--- Успешно извлекли объект из списка вновь добавленных графических объектов и добавили его в коллекцию -
      //--- находим очередной свободный идентификатор объекта, записываем его в свойство и выводим краткое описание объекта в журнал
      else
        {
         bool program_object=(::StringFind(obj.Name(),this.m_name_program)==0);
         obj.SetObjectID(this.GetFreeGraphObjID(program_object));
         obj.Print();
        }
     }
   //--- Возвращаем результат добавления объекта в коллекцию
   return res;
  }
//+------------------------------------------------------------------+

Упростим логику обработчика событий, избавившись от конструкции if-else, и добавим отслеживание щелчков по объекту для определения в дальнейшем факта выделения объекта мышью (на данный момент это пока не реализовано):

//+------------------------------------------------------------------+
//| Обработчик событий                                               |
//+------------------------------------------------------------------+
void CGraphElementsCollection::OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
  {
   CGStdGraphObj *obj=NULL;
   ushort idx=ushort(id-CHARTEVENT_CUSTOM);
   if(id==CHARTEVENT_OBJECT_CHANGE  || id==CHARTEVENT_OBJECT_DRAG    || 
      idx==CHARTEVENT_OBJECT_CHANGE || idx==CHARTEVENT_OBJECT_DRAG   ||
      id==CHARTEVENT_OBJECT_CLICK   || idx==CHARTEVENT_OBJECT_CLICK)
     {
      //--- Получим идентификатор графика. Если lparam равен нулю -
      //--- значит событие с текущего графика,
      //--- иначе - пользовательское событие от индикатора
      long chart_id=(lparam==0 ? ::ChartID() : lparam);
      //--- Получим объект из списка-коллекции по его имени, записанном в sparam,
      //--- свойства которого изменены, или который был перемещён
      obj=this.GetStdGraphObject(sparam,chart_id);
      
      //--- Если объект не удалось получить по имени - он отсутствует в списке,
      //--- а значит его имя было изменено
      if(obj==NULL)
        {
         //--- Найдём в списке объект, которого нет на графике
         obj=this.FindMissingObj(chart_id);
         //--- Если и тут не удалось найти объект - уходим
         if(obj==NULL)
            return;
         //--- Получим имя переименованного  графического объекта на графике, которого нет в списке-коллекции
         string name_new=this.FindExtraObj(chart_id);
         //--- Установим новое имя объекту в списке-коллекции, которому не соответствует ни один графический объект на графике
         obj.SetName(name_new);
        }
      //--- Обновим свойства полученного объекта
      //--- и проверим их изменение
      obj.PropertiesRefresh();
      obj.PropertiesCheckChanged();
     }
  }
//+------------------------------------------------------------------+

Метод, создающий новый стандартный графический объект:

//+------------------------------------------------------------------+
//| Создаёт новый стандартный графический объект                     |
//+------------------------------------------------------------------+
bool CGraphElementsCollection::CreateNewStdGraphObject(const long chart_id,
                                                       const string name,
                                                       const ENUM_OBJECT type,
                                                       const int subwindow,
                                                       const datetime time1,
                                                       const double price1,
                                                       const datetime time2=0,
                                                       const double price2=0,
                                                       const datetime time3=0,
                                                       const double price3=0,
                                                       const datetime time4=0,
                                                       const double price4=0,
                                                       const datetime time5=0,
                                                       const double price5=0)
  {
   ::ResetLastError();
   switch(type)
     {
      //--- Линии
      case OBJ_VLINE             : return ::ObjectCreate(chart_id,name,OBJ_VLINE,subwindow,time1,0);
      case OBJ_HLINE             : return ::ObjectCreate(chart_id,name,OBJ_HLINE,subwindow,0,price1);
      case OBJ_TREND             : return ::ObjectCreate(chart_id,name,OBJ_TREND,subwindow,time1,price1,time2,price2);
      case OBJ_TRENDBYANGLE      : return ::ObjectCreate(chart_id,name,OBJ_TRENDBYANGLE,subwindow,time1,price1,time2,price2);
      case OBJ_CYCLES            : return ::ObjectCreate(chart_id,name,OBJ_CYCLES,subwindow,time1,price1,time2,price2);
      case OBJ_ARROWED_LINE      : return ::ObjectCreate(chart_id,name,OBJ_ARROWED_LINE,subwindow,time1,price1,time2,price2);
      //--- Каналы
      case OBJ_CHANNEL           : return ::ObjectCreate(chart_id,name,OBJ_CHANNEL,subwindow,time1,price1,time2,price2,time3,price3);
      case OBJ_STDDEVCHANNEL     : return ::ObjectCreate(chart_id,name,OBJ_STDDEVCHANNEL,subwindow,time1,price1,time2,price2);
      case OBJ_REGRESSION        : return ::ObjectCreate(chart_id,name,OBJ_REGRESSION,subwindow,time1,price1,time2,price2);
      case OBJ_PITCHFORK         : return ::ObjectCreate(chart_id,name,OBJ_PITCHFORK,subwindow,time1,price1,time2,price2,time3,price3);
      //--- Ганн
      case OBJ_GANNLINE          : return ::ObjectCreate(chart_id,name,OBJ_GANNLINE,subwindow,time1,price1,time2,price2);
      case OBJ_GANNFAN           : return ::ObjectCreate(chart_id,name,OBJ_GANNFAN,subwindow,time1,price1,time2,price2);
      case OBJ_GANNGRID          : return ::ObjectCreate(chart_id,name,OBJ_GANNGRID,subwindow,time1,price1,time2,price2);
      //--- Фибоначчи
      case OBJ_FIBO              : return ::ObjectCreate(chart_id,name,OBJ_FIBO,subwindow,time1,price1,time2,price2);
      case OBJ_FIBOTIMES         : return ::ObjectCreate(chart_id,name,OBJ_FIBOTIMES,subwindow,time1,price1,time2,price2);
      case OBJ_FIBOFAN           : return ::ObjectCreate(chart_id,name,OBJ_FIBOFAN,subwindow,time1,price1,time2,price2);
      case OBJ_FIBOARC           : return ::ObjectCreate(chart_id,name,OBJ_FIBOARC,subwindow,time1,price1,time2,price2);
      case OBJ_FIBOCHANNEL       : return ::ObjectCreate(chart_id,name,OBJ_FIBOCHANNEL,subwindow,time1,price1,time2,price2,time3,price3);
      case OBJ_EXPANSION         : return ::ObjectCreate(chart_id,name,OBJ_EXPANSION,subwindow,time1,price1,time2,price2,time3,price3);
      //--- Эллиотт
      case OBJ_ELLIOTWAVE5       : return ::ObjectCreate(chart_id,name,OBJ_ELLIOTWAVE5,subwindow,time1,price1,time2,price2,time3,price3,time4,price4,time5,price5);
      case OBJ_ELLIOTWAVE3       : return ::ObjectCreate(chart_id,name,OBJ_ELLIOTWAVE3,subwindow,time1,price1,time2,price2,time3,price3);
      //--- Фигуры
      case OBJ_RECTANGLE         : return ::ObjectCreate(chart_id,name,OBJ_RECTANGLE,subwindow,time1,price1,time2,price2);
      case OBJ_TRIANGLE          : return ::ObjectCreate(chart_id,name,OBJ_TRIANGLE,subwindow,time1,price1,time2,price2,time3,price3);
      case OBJ_ELLIPSE           : return ::ObjectCreate(chart_id,name,OBJ_ELLIPSE,subwindow,time1,price1,time2,price2,time3,price3);
      //--- Стрелки
      case OBJ_ARROW_THUMB_UP    : return ::ObjectCreate(chart_id,name,OBJ_ARROW_THUMB_UP,subwindow,time1,price1);
      case OBJ_ARROW_THUMB_DOWN  : return ::ObjectCreate(chart_id,name,OBJ_ARROW_THUMB_DOWN,subwindow,time1,price1);
      case OBJ_ARROW_UP          : return ::ObjectCreate(chart_id,name,OBJ_ARROW_UP,subwindow,time1,price1);
      case OBJ_ARROW_DOWN        : return ::ObjectCreate(chart_id,name,OBJ_ARROW_DOWN,subwindow,time1,price1);
      case OBJ_ARROW_STOP        : return ::ObjectCreate(chart_id,name,OBJ_ARROW_STOP,subwindow,time1,price1);
      case OBJ_ARROW_CHECK       : return ::ObjectCreate(chart_id,name,OBJ_ARROW_CHECK,subwindow,time1,price1);
      case OBJ_ARROW_LEFT_PRICE  : return ::ObjectCreate(chart_id,name,OBJ_ARROW_LEFT_PRICE,subwindow,time1,price1);
      case OBJ_ARROW_RIGHT_PRICE : return ::ObjectCreate(chart_id,name,OBJ_ARROW_RIGHT_PRICE,subwindow,time1,price1);
      case OBJ_ARROW_BUY         : return ::ObjectCreate(chart_id,name,OBJ_ARROW_BUY,subwindow,time1,price1);
      case OBJ_ARROW_SELL        : return ::ObjectCreate(chart_id,name,OBJ_ARROW_SELL,subwindow,time1,price1);
      case OBJ_ARROW             : return ::ObjectCreate(chart_id,name,OBJ_ARROW,subwindow,time1,price1);
      //--- Графические объекты
      case OBJ_TEXT              : return ::ObjectCreate(chart_id,name,OBJ_TEXT,subwindow,time1,price1);
      case OBJ_LABEL             : return ::ObjectCreate(chart_id,name,OBJ_LABEL,subwindow,0,0);
      case OBJ_BUTTON            : return ::ObjectCreate(chart_id,name,OBJ_BUTTON,subwindow,0,0);
      case OBJ_CHART             : return ::ObjectCreate(chart_id,name,OBJ_CHART,subwindow,0,0);
      case OBJ_BITMAP            : return ::ObjectCreate(chart_id,name,OBJ_BITMAP,subwindow,time1,price1);
      case OBJ_BITMAP_LABEL      : return ::ObjectCreate(chart_id,name,OBJ_BITMAP_LABEL,subwindow,0,0);
      case OBJ_EDIT              : return ::ObjectCreate(chart_id,name,OBJ_EDIT,subwindow,0,0);
      case OBJ_EVENT             : return ::ObjectCreate(chart_id,name,OBJ_EVENT,subwindow,time1,0);
      case OBJ_RECTANGLE_LABEL   : return ::ObjectCreate(chart_id,name,OBJ_RECTANGLE_LABEL,subwindow,0,0);
      //---
      default: return false;
     }
  }
//+------------------------------------------------------------------+

В метод передаются идентификатор графика, на котором нужно создать объект, имя и тип создаваемого объекта, подокно графика, в котором строится объект, и пять координат опорных точек объекта. Первая координата (время и цена) обязательны к заполнению, а остальные имеют предустановленные значения по умолчанию, что позволит построить любой стандартный графический объект. В методе сбрасываем код последней ошибки и, в зависимости от типа объекта, возвращаем результат выполнения функции ObjectCreate(). В методе CreateNewStdGraphObjectAndGetCtrlObj(), рассмотренном нами выше и вызывающем этот метод, при ошибке создания объекта выводится в журнал сообщение с кодом ошибки и его расшифровкой.

У нас всё готово для тестирования исправлений, внесённых в классы и программного создания стандартных графических объектов.

Тестирование

Что и как будем тестировать. Мы не будем создавать все графические объекты, а ограничимся лишь одним — вертикальной линией. Она будет создаваться при щелчке левой кнопки мышки по графику с зажатой клавишей Ctrl. Мы проверим создание объекта, обработку ошибки при попытке повторного создания объекта с таким же именем, обработку изменения его координаты времени, а также отслеживание изменения координат опорных точек объектов, имеющих более двух опорных точек.

Для тестирования возьмём советник из прошлой статьи
и сохраним его в новой папке \MQL5\Experts\TestDoEasy\Part89\ под новым именем TestDoEasyPart89.mq5.

В советнике в обработчике OnChartEvent() отключим блок кода для создания объектов-форм при зажатой клавише Ctrl и добавим блок кода для создания вертикальной линии с указанным именем при щелчке по графику при зажатой клавише Ctrl в координате щелчка мышью:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- Если работа в тестере - выход
   if(MQLInfoInteger(MQL_TESTER))
      return;
//--- Если перемещаем мышь
   /*
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      CForm *form=NULL;
      datetime time=0;
      double price=0;
      int wnd=0;
      
      //--- Если клавиша Ctrl не нажата
      if(!IsCtrlKeyPressed())
        {
         //--- очищаем список созданных объектов-форм и разрешаем прокручивать график мышкой и показывать контекстное меню
         list_forms.Clear();
         ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,true);
         ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,true);
         return;
        }
      
      //--- Если координаты  X и Y графика успешно преобразованы в значения время и цена
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- получим индекс бара, над которым находится курсор
         int index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         if(index==WRONG_VALUE)
            return;
         
         //--- Получим объект-бар по индексу
         CBar *bar=engine.SeriesGetBar(Symbol(),Period(),index);
         if(bar==NULL)
            return;
         
         //--- Преобразуем координаты графика из представления время/цена объекта-бара в координаты по оси X и Y
         int x=(int)lparam,y=(int)dparam;
         if(!ChartTimePriceToXY(ChartID(),0,bar.Time(),(bar.Open()+bar.Close())/2.0,x,y))
            return;
         
         //--- Запретим перемещать график мышкой и показывать контекстное меню
         ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,false);
         ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,false);
         
         //--- Создадим имя объекта-формы и скроем все объекты кроме одного с таким именем
         string name="FormBar_"+(string)index;
         HideFormAllExceptOne(name);
         
         //--- Если ещё нет объекта-формы с таким именем
         if(!IsPresentForm(name))
           {
            //--- создадим новый объект-форму
            form=bar.CreateForm(index,name,x,y,114,16);   
            if(form==NULL)
               return;
            
            //--- Установим форме флаги активности и неперемещаемости
            form.SetActive(true);
            form.SetMovable(false);
            //--- Установим непрозрачность 200
            form.SetOpacity(200);
            //--- Цвет фона формы зададим как первый цвет из массива цветов
            form.SetColorBackground(array_clr[0]);
            //--- Цвет очерчивающей рамки формы
            form.SetColorFrame(C'47,70,59');
            //--- Установим флаг рисования тени
            form.SetShadow(true);
            //--- Рассчитаем цвет тени как цвет фона графика, преобразованный в монохромный
            color clrS=form.ChangeColorSaturation(form.ColorBackground(),-100);
            //--- Если в настройках задано использовать цвет фона графика, то затемним монохромный цвет на 20 единиц
            //--- Иначе - будем использовать для рисования тени заданный в настройках цвет
            color clr=(InpUseColorBG ? form.ChangeColorLightness(clrS,-20) : InpColorForm3);
            //--- Нарисуем тень формы со смещением от формы вправо-вниз на три пикселя по всем осям
            //--- Непрозрачность тени при этом установим равной 200, а радиус размытия равный 4
            form.DrawShadow(2,2,clr,200,3);
            //--- Зальём фон формы вертикальным градиентом
            form.Erase(array_clr,form.Opacity());
            //--- Нарисуем очерчивающий прямоугольник по краям формы
            form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,form.ColorFrame(),form.Opacity());
            //--- Если объект-форму не удалось добавить в список - удаляем форму и уходим из обработчика
            if(!list_forms.Add(form))
              {
               delete form;
               return;
              }
            //--- Зафиксируем внешний вид формы
            form.Done();
           }
         //--- Если объект-форма существует
         if(form!=NULL)
           {
            //--- нарисуем на нём текст с описанием типа бара, соответствующего положению курсора мышки и покажем форму
            form.TextOnBG(0,bar.BodyTypeDescription(),form.Width()/2,form.Height()/2-1,FRAME_ANCHOR_CENTER,C'7,28,21');
            form.Show();
           }
         //--- Перерисуем чарт
         ChartRedraw();
        }
     }
   */
   if(id==CHARTEVENT_CLICK)
     {
      if(!IsCtrlKeyPressed())
         return;
      datetime time=0;
      double price=0;
      int sw=0;
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,sw,time,price))
         engine.GetGraphicObjCollection().CreateLineVertical(ChartID(),"LineVertical",0,time);
     }
   engine.GetGraphicObjCollection().OnChartEvent(id,lparam,dparam,sparam);
   
  }
//+------------------------------------------------------------------+

Скомпилируем советник и запустим его на графике.

Сначала создадим вертикальную линию щелчком на графике при зажатой клавише Ctrl, поглядим с каким идентификатором создана линия и как изменяются свойства объекта при перемещении линии по графику. Попробуем повторно создать такую же линию — получим сообщение об ошибке в журнале.
Далее создадим равноудалённый канал, посмотрим значение его идентификатора, а затем — как отслеживаются изменения свойств трёх его опорных точек:



Что дальше

В следующей статье продолжим работу над функционалом программного создания графических объектов.

Ниже прикреплены все файлы текущей версии библиотеки и файл тестового советника для MQL5. Их можно скачать и протестировать всё самостоятельно. Следует отметить, что для работы библиотеки с графическими объектами на графиках, не принадлежащих программе, необходим файл индикатора для библиотеки из статьи 87.

При возникновении вопросов, замечаний и пожеланий вы можете озвучить их в комментариях к статье.

К содержанию

*Статьи этой серии:


Прикрепленные файлы |
MQL5.zip (4231.09 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (8)
Konstantin Nikitin
andrik377 #:

1. Я тут начинающий. Что это  ::Print ? Где об этом почитать?

2. "logging" это журналирование, т.е. генерация инфы о событиях и их направление куда-либо (в журнал эксперта, файл ...). Необходимая вещь для отдадки сложного софта. Какие то зачатки этого я видел в текстах базовых объектов.

Документация -> Print

Artyom Trishkin
andrik377 #:

1. Я тут начинающий. Что это  ::Print ? Где об этом почитать?

2. "logging" это журналирование, т.е. генерация инфы о событиях и их направление куда-либо (в журнал эксперта, файл ...). Необходимая вещь для отдадки сложного софта. Какие то зачатки этого я видел в текстах базовых объектов.

Для этого есть класс CMessage.

:: перед названием функции - это операция разрешения контекста.

andrik377
Глобальный метод ::Print пока отсутствует?
Artyom Trishkin
andrik377 #:
Глобальный метод ::Print пока отсутствует?
Зачем он нужен?
Есть же функция. Нужно - используйте.
andrik377
Все таки, как ее переопределить, т.е. сделать так , чтоб ::Print вызывал не Print, а переопределение?
Пожалуйста, пример.
Универсальная регрессионная модель для прогнозирования рыночной цены (Часть 2): Функции природных, техногенных и общественных переходных процессов Универсальная регрессионная модель для прогнозирования рыночной цены (Часть 2): Функции природных, техногенных и общественных переходных процессов
Настоящая статья является логическим продолжением предыдущей и написана для освещения выявленных фактов подтверждения ее выводов в течении последующих десяти лет после ее выхода, по части выявленных трех функций динамических переходных процессов, описывающих закономерности изменения рыночной цены.
Комбинаторика и теория вероятностей для трейдинга (Часть V): Анализ кривых Комбинаторика и теория вероятностей для трейдинга (Часть V): Анализ кривых
В данной статье я решил провести исследование на тему сведения множественных состояний к двойным. Основная цель — это анализ и полезные выводы, которые могут помочь в дальнейшей разработке масштабируемых торговых алгоритмов на базе теории вероятностей. Конечно, не обошлось и без математики, но учитывая опыт предыдущих статей, вижу, что более общая информация гораздо полезнее деталей.
Графика в библиотеке DoEasy (Часть 90): События стандартных графических объектов. Базовый функционал Графика в библиотеке DoEasy (Часть 90): События стандартных графических объектов. Базовый функционал
В статье создадим базовый функционал для отслеживания событий стандартных графических объектов. Начнем с события двойного щелчка мыши на графическом объекте.
Пользуйтесь каналами и групповыми чатами MQL5.community Пользуйтесь каналами и групповыми чатами MQL5.community
На сайте MQL5.com встречаются трейдеры со всего мира — публикуют статьи, бесплатные коды и продукты в Маркете, выполняют работы на фриланс бирже и копируют торговые сигналы. Вы можете общаться с ними на форуме, в трейдерские чатах и каналах MetaTrader.