Русский 中文 Español Deutsch 日本語 Português
Graphics in DoEasy library (Part 89): Programming standard graphical objects. Basic functionality

Graphics in DoEasy library (Part 89): Programming standard graphical objects. Basic functionality

MetaTrader 5Examples | 21 January 2022, 16:07
7 355 0
Artyom Trishkin
Artyom Trishkin

Contents


Concept

Currently, the library is able to track standard graphical objects on the client terminal chart, including their removal and modification of some of their parameters. Sometimes, it is useful to know about newly set, modified or removed custom graphical objects on a chart directly from your program. However, at the moment, we lack the ability to create standard graphical objects from custom programs. Having the functionality for programming graphical objects and tracking changes in their properties, we are able to create composite graphical objects of any complexity, degree of nesting and the number of controlled pivot points. In this article, I will create the basic functionality for programming standard graphical objects. I will refine the functionality in the coming articles while considering the ability to create custom composite graphical objects based on the standard ones.

Besides, I will gradually make so that library objects use dynamic arrays for storing their properties. In fact, I have already started doing that in the previous article. Here I will fix a logical error made in the previous article causing the inability to track property changes of objects having more than two pivot points. Besides, I will fix and improve the class of a multidimensional dynamic array, so that it can be used as a separate unit of the library, and move it to a separate file.


Improving library classes

The descendant classes of an abstract graphical object need some properties supported by the object so that these properties can be considered when searching, sorting and displaying the properties in the terminal journal. In the GStdFiboArcObj.mqh and GStdGannFanObj.mqh files, add the string for supporting the "Level value" real property by the object:

//+------------------------------------------------------------------+
//| Return 'true' if an object supports a passed                     |
//| real property, otherwise return 'false'                          |
//+------------------------------------------------------------------+
bool CGStdFiboArcObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property)
  {
   switch((int)property)
     {
      //--- Supported properties
      case GRAPH_OBJ_PROP_SCALE        :
      case GRAPH_OBJ_PROP_PRICE        :
      case GRAPH_OBJ_PROP_LEVELVALUE   : return true;
      //--- Other properties are not supported
      //--- Default is 'false'
      default: break;
     }
   return false;
  }
//+------------------------------------------------------------------+

In the GStdExpansionObj.mqh, GStdFiboChannelObj.mqh, GStdFiboFanObj.mqh, GStdFiboObj.mqh, GStdFiboTimesObj.mqh and GStdPitchforkObj.mqh files, make the following changes in the same method to support the same property:

//+------------------------------------------------------------------+
//| Return 'true' if an object supports a passed                     |
//| real property, otherwise return 'false'                          |
//+------------------------------------------------------------------+
bool CGStdPitchforkObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property)
  {
   switch((int)property)
     {
      //--- Supported properties
      case GRAPH_OBJ_PROP_PRICE        :
      case GRAPH_OBJ_PROP_LEVELVALUE   : return true;
      //--- Other properties are not supported
      //--- Default is 'false'
      default: break;
     }
   return false;
  }
//+------------------------------------------------------------------+

In the GStdHLineObj.mqh file of the "Horizontal line" object class, remove the "Pivot point time" property from the method returning the flag of the object supporting the integer property since the object uses only the price for construction:

//+------------------------------------------------------------------+
//| Return 'true' if an object supports a passed                     |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+
bool CGStdHLineObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property)
  {
   switch((int)property)
     {
      //--- Supported properties
      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;
      //--- Other properties are not supported
      //--- Default is 'false'
      default: break;
     }
   return false;
  }
//+------------------------------------------------------------------+

The "Vertical line" object in the GStdVLineObj.mqh file uses only time for its construction. Therefore, I will remove everything from the method returning the flag of the object supporting a real property — the object does not support real properties:

//+------------------------------------------------------------------+
//| Return 'true' if an object supports a passed                     |
//| real property, otherwise return 'false'                          |
//+------------------------------------------------------------------+
bool CGStdVLineObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property)
  {
   return false;
  }
//+------------------------------------------------------------------+


In the previous article, I skipped one logical error preventing us from controlling the changes in the properties of object pivot points and levels in cases when two pivot points are used for the object construction or the object features more than two levels. It was mainly about the method adding the specified number of cells to the end of the array. The method received the pointer to an object created outside, while the array received the specified number of these pointers:

//--- Add the specified number of cells with objects to the end of the array
   bool              AddQuantity(const string source,const int total,CObject *object)
                       {
                        //--- Declare the variable for storing the result of adding objects to the list
                        bool res=true;
                        //--- in the list by the number of added objects passed to the method
                        for(int i=0;i<total;i++)
                          {
                           //--- if failed to add the object to the list
                           if(!this.Add(object))
                             {
                              //--- display the appropriate message, add 'false' to the variable value
                              //--- and move on to the loop next iteration
                              CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);

                              res &=false;
                              continue;
                             }
                          }
                        //--- Return the total result of adding the specified number of objects to the list
                        return res;
                       }

However, this is the same object! We simply pass the pointer to an object created outside the method and multiply the pointers to it in a loop. Therefore, changing the properties of the object itself affects all the pointers — they also refer to the same object. Thus, we filled the array with instances of the same object property rather than with different properties. If the object featured several reference points, we multiplied the pivot point by the specified number. Instead of managing the real values of the second, third, fourth and fifth points, we added the property of the second pivot point to the array. This prevented us from getting, tracking and changing the values of the real reference point. Changing the second pivot point led these changes to be copied to the third, fourth and fifth object points.

Let's add one more method for creating a new data object. This method will create a new property object. We will add this new property (rather than the pointer) to the array in the AddQuantity() method, in which we simply added the pointer created from the outside in the specified amount to the array.

Since we have three sets of identical classes in \MQL5\Include\DoEasy\Services\XDimArray.mqh — the classes for creating dynamic multidimensional integer, real and string arrays, let's consider this using the classes for creating an integer multidimensional dynamic array as an example.

In the class of one long array dimension, write the method for creating a new data object:

//+------------------------------------------------------------------+
//| Class of a single long array dimension                           |
//+------------------------------------------------------------------+
class CDimLong : public CArrayObj
  {
private:
//--- Create a new data object
   CDataUnitLong    *CreateData(const string source,const long value=0)
                       {
                        //--- Create a new long data object
                        CDataUnitLong *data=new CDataUnitLong();
                        //--- If failed to create an object, inform of that in the journal
                        if(data==NULL)
                           ::Print(source,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_LONG_DATA_OBJ));
                        //--- Otherwise, set the value passed to the method for the object
                        else
                           data.Value=value;
                        //--- Return the pointer to the object or NULL
                        return data;
                       }
//--- Get long data object from the array

Here all is simple: we create a new object of long data and set the value passed to the method for it. If failed to create an object, inform of that in the journal. The method returns the pointer to the created object or NULL in case of an error.

Add the changes to the AddQuantity() method:

//--- Add the specified number of cells with data to the end of the array
   bool              AddQuantity(const string source,const int total,const long value=0)
                       {
                        //--- Declare the variable for storing the result of adding objects to the list
                        bool res=true;
                        //--- in the list by the number of added objects passed to the method
                        for(int i=0;i<total;i++)
                          {
                           //--- Create a new long data object
                           CDataUnitLong *data=this.CreateData(DFUN,value);
                           //--- If failed to create an object, inform of that and move on to the next iteration
                           if(data==NULL)
                             {
                              res &=false;
                              continue;
                             }
                           data.Value=value;
                           //--- if failed to add the object to the list
                           if(!this.Add(data))
                             {
                              //--- display the appropriate message, remove the object and add 'false' to the variable value
                              //--- and move on to the loop next iteration
                              CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                              delete data;
                              res &=false;
                              continue;
                             }
                          }
                        //--- Return the total result of adding the specified number of objects to the list
                        return res;
                       }

Now instead of the pointer to the object, the method receives the value to be assigned to a newly created property. In the loop, create a new object in the loop and add it to the list.

In the Increase() method, in which I have previously created a new object and passed the pointer to it to the AddQuantity() method, simply call the AddQuantity() method since new objects in the loop are now created and added to the array inside the AddQuantity() method, rather than a pointer to a single object previously created in the Increase() method.

Let's remove the following code block from the method:

//--- Increase the number of data cells by the specified value, return the number of added elements
   int               Increase(const int total,const long value=0)
                       {
                        //--- Save the current array size
                        int size_prev=this.Total();
                        //--- Create a new long data object
                        CDataUnitLong *data=new CDataUnitLong();
                        //--- If failed to create an object, inform of that and return zero
                        if(data==NULL)
                          {
                           ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_LONG_DATA_OBJ));
                           return 0;
                          }
                        //--- Set the specified value to a newly created object
                        data.Value=value;
                        //--- Add the specified number of object instances to the list
                        //--- and return the difference between the obtained and previous array size
                        this.AddQuantity(DFUN,total,data);
                        return this.Total()-size_prev;
                       }

The called AddQuantity() method now receives an initial value for a data object newly added to the array rather than a pointer:

//--- Increase the number of data cells by the specified value, return the number of added elements
   int               Increase(const int total,const long value=0)
                       {
                        //--- Save the current array size
                        int size_prev=this.Total();
                        //--- Add the specified number of object instances to the list
                        //--- and return the difference between the obtained and previous array size
                        this.AddQuantity(DFUN,total,value);
                        return this.Total()-size_prev;
                       }

The same changes were done to the remaining classes of the file. I am not going to repeat them here since they are identical.
You can view all the changes in the files attached below.

In \MQL5\Include\DoEasy\Data.mqh, add new message indices:

//--- CGraphElementsCollection
   MSG_GRAPH_ELM_COLLECTION_ERR_OBJ_ALREADY_EXISTS,   // Error. A chart control object already exists with chart id 
   MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_CREATE_CTRL_OBJ,// Failed to create chart control object with chart ID 
   MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_GET_CTRL_OBJ,  // Failed to get chart control object with chart ID 
   MSG_GRAPH_ELM_COLLECTION_ERR_GR_OBJ_ALREADY_EXISTS,// Such graphical object already exists: 
   
//--- GStdGraphObj
   MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ,     // Failed to create the class object for a graphical object 
   MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_STD_GRAPH_OBJ, // Failed to create a graphical object 
   MSG_GRAPH_STD_OBJ_ERR_NOT_FIND_SUBWINDOW,          // Failed to find the chart subwindow

...

   MSG_GRAPH_OBJ_TEXT_BMP_FILE_STATE_ON,              // On state
   MSG_GRAPH_OBJ_TEXT_BMP_FILE_STATE_OFF,             // Off state
   
//--- CDataPropObj
   MSG_DATA_PROP_OBJ_OUT_OF_PROP_RANGE,               // Passed property is out of object property range

//--- CGraphElementsCollection
   MSG_GRAPH_OBJ_FAILED_GET_ADDED_OBJ_LIST,           // Failed to get the list of newly added objects
   MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST,         // Failed to remove a graphical object from the list
   
   MSG_GRAPH_OBJ_CREATE_EVN_CTRL_INDICATOR,           // Indicator for controlling and sending events created
   MSG_GRAPH_OBJ_FAILED_CREATE_EVN_CTRL_INDICATOR,    // Failed to create the indicator for controlling and sending events
   MSG_GRAPH_OBJ_CLOSED_CHARTS,                       // Chart windows closed:
   MSG_GRAPH_OBJ_OBJECTS_ON_CLOSED_CHARTS,            // Objects removed together with charts:
   
  };
//+------------------------------------------------------------------+

and message texts corresponding to newly added indices:

//--- 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: "},
   
  };
//+---------------------------------------------------------------------+


Let's make minor improvements in the class of the base object of the library graphical objects.

The graphical objects feature bool properties returning the flags of some object properties. The abstract graphical object class has the methods returning and setting such flags. The method names indicate that the method sets the flag, for example:
SetFlagDrawLines (Display lines for Elliott wave marking). In the class of the base object of the library graphical objects, a corresponding method is called SetDrawLines(). Thus, if we try to set the flag for an object, we will see two hints for choosing a method. This is confusing. Besides, if we select the method of the base graphical object rather than abstract one, no change set in the object arrays will be made in the properties. Instead, we will simply give a command to change a property in the graphical object itself. The appropriate property in the class object will remain unchanged. This means we need to rename all such methods to avoid mistakes when selecting out of two methods. I believe, later it will be necessary to unify the return types of these methods as well so that the compiler is able to select the necessary method unambiguously.

Let's introduce the necessary fixes in \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh:

//--- Set the "Background object" flag
   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;
                       }
//--- Set the "Object selection" flag
   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;
                       }
//--- Set the "Object selection" flag
   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;
                       }
//--- Set the "Disable displaying the name of a graphical object in the terminal object list" flag
   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;
                       }


Let's move the classes for creating a multidimensional dynamic array to a separate file. I will improve them so that they turn into a tool for creating property objects for any existing or planned object of the library applying arrays for storing its properties (integer, real and string ones).

In the \MQL5\Include\DoEasy\Services\ folder of service classes and functions, create a new file Properties.mqh. It is to get all classes for creating a two-dimensional array of object properties and a properties object (both previous and current ones) set in the previous article directly in the class body of the CGStdGraphObj abstract standard graphical object:

//+------------------------------------------------------------------+
//| The class of the abstract standard graphical object              |
//+------------------------------------------------------------------+
class CGStdGraphObj : public CGBaseObj
  {
private:
   //--- Object property class
   class CDataPropObj
     {
   private:
      CArrayObj         m_list;        // list of property objects
      int               m_total_int;   // Number of integer parameters
      int               m_total_dbl;   // Number of real parameters
      int               m_total_str;   // Number of string parameters
      //--- Return the index of the array the (1) double and (2) string properties are actually located at
      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:
      //--- Return the pointer to (1) the list of property objects, as well as to the object of (2) integer, (3) real and (4) string properties
      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);                                 }
      //--- Set object's (1) integer, (2) real and (3) string properties
      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);  }
      //--- Return object’s (1) integer, (2) real and (3) string property from the properties array
      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); }
      
      //--- Return the size of the specified first dimension data array
      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;
                          }
      //--- Set the array size in the specified dimensionality
      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;
                          }
      //--- Constructor
                        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));
                          }
      //--- Destructor
                       ~CDataPropObj()
                          {
                           m_list.Clear();
                           m_list.Shutdown();
                          }
     };
   //--- Data class of the current and previous properties
   class CProperty
     {
   public:
      CDataPropObj     *Curr;    // Pointer to the current properties object
      CDataPropObj     *Prev;    // Pointer to the previous properties object
      //--- Set the array size ('size') in the specified dimension ('range')
      bool              SetSizeRange(const int range,const int size)
                          {
                           return(this.Curr.SetSizeRange(range,size) && this.Prev.SetSizeRange(range,size) ? true : false);
                          }
      //--- Return the size of the specified array of the (1) current and (2) previous first dimension data
      int               CurrSize(const int range)  const { return Curr.Size(range); }
      int               PrevSize(const int range)  const { return Prev.Size(range); }
      //--- Copy the current data to the previous one
      void              CurrentToPrevious(void)
                          {
                           //--- Copy all integer properties
                           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));
                           //--- Copy all real properties
                           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));
                           //--- Copy all string properties
                           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));
                          }
      //--- Constructor
                        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);
                          }
     };

We need to get rid of references to any enumerations belonging to a particular class object to make the class universal.

To achieve this, replace all the enumerations used to select the methods returning a real property index with ordinary int variables. To calculate the property indices, pass the maximum property for real and string properties to the class constructors (an integer property does not shift its value and fully corresponds to the property index). Next, simply calculate the real values based on the property value and the maximum value of properties. As usual, the idea looks more clear when displayed as a code.

Add the following classes in the newly added file \MQL5\Include\DoEasy\Services\Properties.mqh:

//+------------------------------------------------------------------+
//|                                                   Properties.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "XDimArray.mqh"
//+------------------------------------------------------------------+
//| Object property class                                            |
//+------------------------------------------------------------------+
//--- Object property class
class CDataPropObj : public CObject
  {
private:
   CArrayObj         m_list;           // list of property objects
   int               m_total_int;      // Number of integer parameters
   int               m_total_dbl;      // Number of real parameters
   int               m_total_str;      // Number of string parameters
   int               m_prop_max_dbl;   // Maximum possible real property value
   int               m_prop_max_str;   // Maximum possible string property value
//--- Return the index of the array the int, double or string property is actually located at
   int               IndexProp(int property) const
                       {
                        //--- If the passed value is less than the number of integer parameters,
                        //--- this is an integer property. Return the value passed to the method
                        if(property<this.m_total_int)
                           return property;
                        //--- Otherwise if the passed value is less than the maximum possible real property value,
                        //--- then this is a real property - return the calculated index in the array of real properties
                        else if(property<this.m_prop_max_dbl)
                           return property-this.m_total_int;
                        //--- Otherwise if the passed value is less than the maximum possible string property value,
                        //--- then this is a string property - return the calculated index in the array of string properties
                        else if(property<this.m_prop_max_str)
                           return property-this.m_total_int-this.m_total_dbl;
                        //--- Otherwise, if the passed value exceeds the maximum range of all values of all properties, 
                        //--- inform of this in the journal and return INT_MAX causing the error
                        //--- accessing the array in XDimArray file classes which send the appropriate warning to the journal
                        CMessage::ToLog(DFUN,MSG_DATA_PROP_OBJ_OUT_OF_PROP_RANGE);
                        return INT_MAX;
                       }
public:
//--- Return the pointer to (1) the list of property objects, as well as to the object of (2) integer, (3) real and (4) string properties
   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);                                 }
   
//--- Set (1) integer, (2) real and (3) string properties in the appropriate property object
   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);  }
   //--- Return (1) integer, (2) real and (3) string property from the appropriate object
   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); }
   
//--- Return the size of the specified first dimension data array
   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;
                       }
//--- Set the array size in the specified dimensionality
   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;
                       }
//--- Constructor
                     CDataPropObj(const int prop_total_integer,const int prop_total_double,const int prop_total_string)
                       {
                        //--- Set the passed amounts of integer, real and string properties in the variables
                        this.m_total_int=prop_total_integer;
                        this.m_total_dbl=prop_total_double;
                        this.m_total_str=prop_total_string;
                        //--- Calculate and set the maximum values of real and string properties to the variables
                        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;
                        //--- Add newly created objects of integer, real and string properties to the list
                        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));
                       }
//--- Destructor
                    ~CDataPropObj()
                       {
                        m_list.Clear();
                        m_list.Shutdown();
                       }
  };
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Data class of the current and previous properties                |
//+------------------------------------------------------------------+
class CProperties : public CObject
  {
private:
   CArrayObj         m_list;  // List for storing the pointers to property objects
public:
   CDataPropObj     *Curr;    // Pointer to the current properties object
   CDataPropObj     *Prev;    // Pointer to the previous properties object
//--- Set the array size ('size') in the specified dimension ('range')
   bool              SetSizeRange(const int range,const int size)
                       {
                        return(this.Curr.SetSizeRange(range,size) && this.Prev.SetSizeRange(range,size) ? true : false);
                       }
//--- Return the size of the specified array of the (1) current and (2) previous first dimension data
   int               CurrSize(const int range)  const { return Curr.Size(range); }
   int               PrevSize(const int range)  const { return Prev.Size(range); }
//--- Copy the current data to the previous one
   void              CurrentToPrevious(void)
                       {
                        //--- Copy all integer properties
                        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));
                        //--- Copy all real properties
                        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));
                        //--- Copy all string properties
                        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));
                       }
//--- Constructor
                     CProperties(const int prop_int_total,const int prop_double_total,const int prop_string_total)
                       {
                        //--- Create new objects of the current and previous properties
                        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);
                        //--- Add newly created objects to the list
                        this.m_list.Add(this.Curr);
                        this.m_list.Add(this.Prev);
                       }
//--- Destructor
                    ~CProperties()
                       {
                        this.m_list.Clear();
                        this.m_list.Shutdown();
                       }
  };
//+------------------------------------------------------------------+

All the main explanations are set in the code comments. Compare these classes with the ones I made in the previous article in \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqh.

Now let's improve the abstract standard graphical object class in \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqh.

First, include the newly created file of object property classes:

//+------------------------------------------------------------------+
//|                                                 GStdGraphObj.mqh |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                             https://mql5.com/en/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://mql5.com/en/users/artmedia70"
#property version   "1.00"
#property strict    // Necessary for mql4
//+------------------------------------------------------------------+
//| Include files                                                    |
//+------------------------------------------------------------------+
#include "..\GBaseObj.mqh"
#include "..\..\..\Services\Properties.mqh"
//+------------------------------------------------------------------+
//| The class of the abstract standard graphical object              |
//+------------------------------------------------------------------+
class CGStdGraphObj : public CGBaseObj
  {

The classes of properties objects are already removed from the private section of the class. The pointer to the properties object is declared in it as well. The appropriate method of the properties object is accessed in each of the Get and Set methods in the public section of the class:

//+------------------------------------------------------------------+
//| The class of the abstract standard graphical object              |
//+------------------------------------------------------------------+
class CGStdGraphObj : public CGBaseObj
  {
private:
   CProperties      *Prop;                                              // Pointer to the properties object
   int               m_pivots;                                          // Number of object reference points
//--- Read and set (1) the time and (2) the price of the specified object pivot point
   void              SetTimePivot(const int index);
   void              SetPricePivot(const int index);
//--- Read and set (1) color, (2) style, (3) width, (4) value, (5) text of the specified object level
   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);
//--- Read and set the BMP file name for the "Bitmap Level" object. Index: 0 - ON, 1 - OFF
   void              SetBMPFile(const int index);

public:
//--- Set object's (1) integer, (2) real and (3) string properties
   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);  }
//--- Return object’s (1) integer, (2) real and (3) string property from the properties array
   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); }

//--- Set object's previous (1) integer, (2) real and (3) string properties
   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);  }
//--- Return object’s (1) integer, (2) real and (3) string property from the previous properties array
   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); }
   
//--- Return itself
   CGStdGraphObj    *GetObject(void)                                       { return &this;}

In the public section, add the destructor of the class the properties object is to be removed in:

//--- Default constructor
                     CGStdGraphObj(){ this.m_type=OBJECT_DE_TYPE_GSTD_OBJ; m_group=WRONG_VALUE; }
//--- Destructor
                    ~CGStdGraphObj()
                       {
                        if(this.Prop!=NULL)
                           delete this.Prop;
                       }
protected:
//--- Protected parametric constructor
                     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);

In the section of the methods of simplified access and setting graphical object properties, improve the methods of setting flag properties:

//--- Background object
   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);
                       }
//--- Priority of a graphical object for receiving the event of clicking on a chart
   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);
                       }
//--- Disable displaying the name of a graphical object in the terminal object list
   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);
                       }
//--- Object selection
   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);
                       }
//--- Object availability
   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);
                       }
//--- Time coordinate

Move the method copying the current properties to the previous ones from the private section to the public one:

//--- Return the description of the object visibility on timeframes
   string            VisibleOnTimeframeDescription(void);

//--- Re-write all graphical object properties
   void              PropertiesRefresh(void);
//--- Check object property changes
   void              PropertiesCheckChanged(void);
//--- Copy the current data to the previous one
   void              PropertiesCopyToPrevData(void);
   
private:
//--- Get and save (1) integer, (2) real and (3) string properties
   void              GetAndSaveINT(void);
   void              GetAndSaveDBL(void);
   void              GetAndSaveSTR(void);

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

In the protected parametric constructor, create a new object of the graphical object properties:

//+------------------------------------------------------------------+
//| Protected parametric constructor                                 |
//+------------------------------------------------------------------+
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)
  {
   //--- Create the property object with the default values
   this.Prop=new CProperties(GRAPH_OBJ_PROP_INTEGER_TOTAL,GRAPH_OBJ_PROP_DOUBLE_TOTAL,GRAPH_OBJ_PROP_STRING_TOTAL);
   
//--- Set the number of pivot points and object levels

Pass the number of integer, real and string graphical object properties to the class constructor.

In the method returning integer properties from a graphical and saving them in the class object properties, check whether the number of object levels has changed. If yes, then change the size of arrays of all properties storing the level values.
Otherwise, when setting the level properties, we get the array out of range error:

   //--- Properties belonging to different graphical objects
   this.SetProperty(GRAPH_OBJ_PROP_FILL,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_FILL));              // Fill an object with color
   this.SetProperty(GRAPH_OBJ_PROP_READONLY,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_READONLY));      // Ability to edit text in the Edit object
   this.SetProperty(GRAPH_OBJ_PROP_LEVELS,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_LEVELS));          // Number of levels
   
   if(this.GetProperty(GRAPH_OBJ_PROP_LEVELS,0)!=this.GetPropertyPrev(GRAPH_OBJ_PROP_LEVELS,0))                      // Check if the number of levels has changed
     {
      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++)                                                                                  // Level data
     {
      this.SetLevelColor(i);
      this.SetLevelStyle(i);
      this.SetLevelWidth(i);
     }
   this.SetProperty(GRAPH_OBJ_PROP_ALIGN,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ALIGN));            // Horizontal text alignment in the Edit object (OBJ_EDIT)

Simplify the method checking the changes in object properties:

//+------------------------------------------------------------------+
//| Check object property changes                                    |
//+------------------------------------------------------------------+
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();
  }
//+------------------------------------------------------------------+

In the past method implementation, I used the if-else construction to make sure the property has multiple values (such as the anchor point time) and handled each such property in a separate code block. Since we are able to know the property array size for each of the properties, it is sufficient to simply go through the values of a single property in the loop by the size of the array second dimension. For a single property, the size of the second dimension is 1, while for a multiple one, it is equal to the number of values of the property multi-properties. Thus, all can be done in a single loop for each of the object properties. This is what I have done above.

I have also implemented some minor improvements (for example changing method names) in the same file. For instance, LevelColorsDescription() has been renamed to LevelsColorDescription(), which is more in line with the method purpose. I will not consider such renaming here. You can find them in the attached files.

Each of the library objects has a custom object ID in addition to other properties. The collection class of graphical elements features two collections — collection of graphical elements, the development of which I have suspended until the completion of the graphical object collection (featuring manually created standard graphical objects) I am working on right now and standard graphical objects that are created programmatically. Here I will start developing the functionality for programming standard graphical objects.
IDs of graphical objects created programmatically will be located in the range from 1 to 10000 inclusive. IDs of manually created graphical objects start from 10001.
Let's set this threshold value in \MQL5\Include\DoEasy\Defines.mqh:

//--- Pending request type IDs
#define PENDING_REQUEST_ID_TYPE_ERR    (1)                        // Type of a pending request created based on the server return code
#define PENDING_REQUEST_ID_TYPE_REQ    (2)                        // Type of a pending request created by request
//--- Timeseries parameters
#define SERIES_DEFAULT_BARS_COUNT      (1000)                     // Required default amount of timeseries data
#define PAUSE_FOR_SYNC_ATTEMPTS        (16)                       // Amount of pause milliseconds between synchronization attempts
#define ATTEMPTS_FOR_SYNC              (5)                        // Number of attempts to receive synchronization with the server
//--- Tick series parameters
#define TICKSERIES_DEFAULT_DAYS_COUNT  (1)                        // Required number of days for tick data in default series
#define TICKSERIES_MAX_DATA_TOTAL      (200000)                   // Maximum number of stored tick data of a single symbol
//--- Parameters of the DOM snapshot series
#define MBOOKSERIES_DEFAULT_DAYS_COUNT (1)                        // The default required number of days for DOM snapshots in the series
#define MBOOKSERIES_MAX_DATA_TOTAL     (200000)                   // Maximum number of stored DOM snapshots of a single symbol
//--- Canvas parameters
#define PAUSE_FOR_CANV_UPDATE          (16)                       // Canvas update frequency
#define NULL_COLOR                     (0x00FFFFFF)               // Zero for the canvas with the alpha channel
#define OUTER_AREA_SIZE                (16)                       // Size of one side of the outer area around the workspace
//--- Graphical object parameters
#define PROGRAM_OBJ_MAX_ID             (10000)                    // Maximum value of an ID of a graphical object belonging to a program
//+------------------------------------------------------------------+
//| Enumerations                                                     |
//+------------------------------------------------------------------+


Methods for programming standard graphical objects

We already have the methods tracking manual creation of graphical objects on a chart, creating the appropriate class objects and adding them to the collection list. Of course, it would be good to use them for the current tasks but there are some reasons that forced me to abandon the use of partially ready-made methods. We simply track the appearance of graphical objects in the timer. When creating an object programmatically, I would not want to wait for the next timer tick and define the newly created object, as well as its creation method (programmatic and manual).

Instead, I will create the method in the collection class of graphical elements for constructing standard graphical objects. Immediately after constructing the object, I will create the corresponding class object and place it in the collection. The search for changes in the object properties is performed by the already created functionality. Thus, we will be able to create objects and add them to the collection, while retaining the ability to search for changes with no consideration to the object creation method. In the future, this will simplify the creation of composite graphical objects and managing their properties.

In a programmatically created graphical object, its name will contain the name of the program the object was created from. This will allow us to distinguish our "own" graphical objects from manually created ones.
To achieve this, add the new variable for storing the program name in the private section of the chart object management class in the file of the graphical element collection class \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh:

//+------------------------------------------------------------------+
//| Chart object management class                                    |
//+------------------------------------------------------------------+
class CChartObjectsControl : public CObject
  {
private:
   CArrayObj         m_list_new_graph_obj;      // List of added graphical objects
   ENUM_TIMEFRAMES   m_chart_timeframe;         // Chart timeframe
   long              m_chart_id;                // Chart ID
   long              m_chart_id_main;           // Control program chart ID
   string            m_chart_symbol;            // Chart symbol
   bool              m_is_graph_obj_event;      // Event flag in the list of graphical objects
   int               m_total_objects;           // Number of graphical objects
   int               m_last_objects;            // Number of graphical objects during the previous check
   int               m_delta_graph_obj;         // Difference in the number of graphical objects compared to the previous check
   int               m_handle_ind;              // Event controller indicator handle
   string            m_name_ind;                // Short name of the event controller indicator
   string            m_name_program;            // Program name
   
//--- Return the name of the last graphical object added to the chart
   string            LastAddedGraphObjName(void);
//--- Set the permission to track mouse events and graphical objects
   void              SetMouseEvent(void);

public:

In the public section of the class, remove specifying a chart ID from the CreateNewGraphObj() method since the ID is one of the primary properties of the chart objects management object and it can be obtained directly from the object rather than by passing to the method.
Set the program name value to the appropriate variable in the class constructors:

public:
//--- Return the variable values
   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;    }
//--- Create a new standard graphical object
   CGStdGraphObj    *CreateNewGraphObj(const ENUM_OBJECT obj_type,const string name);
//--- Return the list of newly added objects
   CArrayObj        *GetListNewAddedObj(void)                        { return &this.m_list_new_graph_obj;}
//--- Create the event control indicator
   bool              CreateEventControlInd(const long chart_id_main);
//--- Add the event control indicator to the chart
   bool              AddEventControlInd(void);
//--- Check the chart objects
   void              Refresh(void);
//--- Constructors
                     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();
                       }

In addition for the empty name check, make sure the object is not created programmatically in the method checking chart objects:

//+------------------------------------------------------------------+
//| CChartObjectsControl: Check objects on a chart                   |
//+------------------------------------------------------------------+
void CChartObjectsControl::Refresh(void)
  {
//--- Graphical objects on the chart
   this.m_total_objects=::ObjectsTotal(this.ChartID());
   this.m_delta_graph_obj=this.m_total_objects-this.m_last_objects;
   
//--- If the number of objects has changed
   if(this.m_delta_graph_obj!=0)
     {
      //--- Create the string and display it in the journal with the chart ID, its symbol and timeframe
      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 an object is added to the chart
   if(this.m_delta_graph_obj>0)
     {
      //--- find the last added graphical object, select it and write its name
      string name=this.LastAddedGraphObjName();
      if(name!="" && ::StringFind(name,m_name_program)==WRONG_VALUE)
        {
         //--- Create the object of the graphical object class corresponding to the added graphical object type
         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;
         //--- Set the object affiliation and add the created object to the list of new objects
         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);
           }
        }
     }
     
//--- save the index of the last added graphical object and the difference with the last check
   this.m_last_objects=this.m_total_objects;
   this.m_is_graph_obj_event=(bool)this.m_delta_graph_obj;
  }
//+------------------------------------------------------------------+

In other words, if an object name has no a substring with a program name, such an object should be handled by the method, otherwise this is a programmatically created graphical object which is added to the collection list by another method.

In the method for creating a new standard graphical object, replace all chart_id, previously passed to the method, with the chart ID set for the object it controls:

//+------------------------------------------------------------------+
//| CChartObjectsControl:                                            |
//| Create a new standard graphical object                           |
//+------------------------------------------------------------------+
CGStdGraphObj *CChartObjectsControl::CreateNewGraphObj(const ENUM_OBJECT obj_type,const string name)
  {
   CGStdGraphObj *obj=NULL;
   switch((int)obj_type)
     {
      //--- Lines
      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);
      //--- Channels
      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);
      //--- Gann
      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);
      //--- Fibo
      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);
      //--- Elliott
      case OBJ_ELLIOTWAVE5       : return new CGStdElliotWave5Obj(this.ChartID(),name);
      case OBJ_ELLIOTWAVE3       : return new CGStdElliotWave3Obj(this.ChartID(),name);
      //--- Shapes
      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);
      //--- Arrows
      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);
      //--- Graphical objects
      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;
     }
  }
//+------------------------------------------------------------------+


In the CGraphElementsCollection collection class of graphical objects, namely, in the method returning the first free graphical object ID, add the flag specifying the ID of the necessary object: false — for a manually created one, true — for a programmatically created one:

//--- Return the first free ID of the graphical (1) object and (2) element on canvas
   long              GetFreeGraphObjID(bool program_object);
   long              GetFreeCanvElmID(void);
//--- Add a graphical object to the collection

Declare the private method creating a new standard graphical object:

//--- Remove the object of managing charts from the list
   bool              DeleteGraphObjCtrlObjFromList(CChartObjectsControl *obj);
//--- Create a new standard graphical object, return an object name
   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:

In the private section of the class, write the method creating a new graphical object and returning the pointer to the chart management object:

//--- Event handler
   void              OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam);

private:
//--- Create a new graphical object, return the pointer to the chart management object
   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)
                       {
                        //--- If an object with a chart ID and name is already present in the collection, inform of that and return 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;
                          }
                        //--- If failed to create a new standard graphical object, inform of that and return 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;
                          }
                        //--- If failed to get a chart management object, inform of that
                        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);
                        //--- Return the pointer to a chart management object or NULL in case of a failed attempt to get it
                        return ctrl;
                       }

public:

The method logic is described in the code comments. The method will be used when creating specified standard graphical object types located further in the public section of the class.

The method creating the "Vertical line" graphical object:

public:
//--- Create the "Vertical line" graphical object
   bool              CreateLineVertical(const long chart_id,const string name,const int subwindow,const datetime time)
                       {
                        //--- Set the name and type of a created object
                        string nm=this.m_name_program+"_"+name;
                        ENUM_OBJECT type_object=OBJ_VLINE;
                        //--- Create a new graphical object and get the pointer to the chart management object
                        CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,0);
                        if(ctrl==NULL)
                           return false;
                        //--- Create a new class object corresponding to the newly created graphical object
                        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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM);
                        obj.SetFlagSelectable(true);
                        obj.SetFlagSelected(true);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        //--- If failed to add an object to the collection list,
                        if(!this.m_list_all_graph_obj.Add(obj))
                          {
                           //--- inform of that, remove the graphical and class object, and return 'false'
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           ::ObjectDelete(chart_id,nm);
                           delete obj;
                           return false;
                          }
                        //--- Redraw the chart and display all object properties in the journal (temporarily, for test purposes only)
                        ::ChartRedraw(chart_id);
                        obj.Print();
                        return true;
                       }

The method is described in sufficient detail in the code comments. The properties inherent in each specific object and passed in the parameters will be set in each such object. A physical graphical object is created on the specified chart first forming a pointer to the chart management object. Its CreateNewGraphObj() method is used to create the class object corresponding to the created object type. If failed to add the object to the list, the physical graphical object itself and the class object are removed with an error message sent to the journal. If created successfully, the chart is updated and the method returns true.

The remaining methods for creating graphical objects are identical to the one considered above and differ only in their set of parameters defined for each specific graphical object.
Let's have a look at the listing of all other added methods:

//--- Create the "Horizontal line" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }
 
//--- Create the "Trend line" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }
//--- Create the "Trend line by angle" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Cyclic lines" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Arrowed line" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Equidistant channel" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Standard deviation channel" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Linear regression channel" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Andrews' Pitchfork" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Gann line" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Gann fan" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Gann grid" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Fibo levels" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Fibo Time Zones" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Fibo fan" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Fibo arc" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Fibo channel" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }
//--- Create the "Fibo extension" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Elliott 5 waves" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Elliott 3 waves" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the Rectangle graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the Triangle graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the Ellipse graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Thumb up" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Thumb down" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Arrow up" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Arrow down" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the Stop graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Check mark" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Left price label" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Right price label" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the Buy graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the Sell graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the Arrow graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }
//--- Create the Text graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Text label" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the Button graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the Chart graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the Bitmap graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Bitmap label" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Input field" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Economic calendar event" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }

//--- Create the "Rectangular label" graphical object
   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;
                          }
                        //--- Set the necessary minimal parameters for an object
                        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;
                       }
 
  };
//+------------------------------------------------------------------+

All methods feature their own set of inputs. These parameters are set as minimal object properties sufficient for its creation. All remaining properties can be changed after creating a graphical object.

The method returning the first free graphical object ID:

//+------------------------------------------------------------------+
//| Return the first free graphical object ID                        |
//+------------------------------------------------------------------+
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);
  }
//+------------------------------------------------------------------+

Now the method considers a necessary ID to be received.
If it is intended for a programmatically created graphical object, get the list of all objects having IDs less or equal to the PROGRAM_OBJ_MAX_ID (10000) constant value.
If it is intended for a manually created graphical object, get the list of all objects having IDs exceeding PROGRAM_OBJ_MAX_ID.
Next, get the index of an object with the maximum ID from the obtained list, as well as a listed object by its index.
Next, calculate the value of the very first ID (for a programmed object — 1, for a manually created object — 10000+1).
If the object with the maximum ID has been received, get the value of its ID+1. Otherwise, the object is not in the list and the calculated value of the first ID (1 or 10001) is returned.

In the method adding the graphical object to the collection, find out whether an object is programmatically or manually created (by object name) to search for an ID and pass the value to the GetFreeGraphObjID() method:

//+------------------------------------------------------------------+
//| Add a graphical object to the collection                         |
//+------------------------------------------------------------------+
bool CGraphElementsCollection::AddGraphObjToCollection(const string source,CChartObjectsControl *obj_control)
  {
   //--- Get the list of the last added graphical objects from the class for managing graphical objects
   CArrayObj *list=obj_control.GetListNewAddedObj();
   //--- If failed to obtain the list, inform of that and return 'false'
   if(list==NULL)
     {
      CMessage::ToLog(DFUN_ERR_LINE,MSG_GRAPH_OBJ_FAILED_GET_ADDED_OBJ_LIST);
      return false;
     }
   //--- If the list is empty, return 'false'
   if(list.Total()==0)
      return false;
   //--- Declare the variable for storing the result
   bool res=true;
   //--- In the loop by the list of newly added standard graphical objects,
   for(int i=0;i<list.Total();i++)
     {
      //--- retrieve the next object from the list and
      CGStdGraphObj *obj=list.Detach(i);
      //--- if failed to retrieve the object, inform of that, add 'false' to the resulting variable and move on to the next one
      if(obj==NULL)
        {
         CMessage::ToLog(source,MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST);
         res &=false;
         continue;
        }
      //--- if failed to add the object to the collection list, inform of that,
      //--- remove the object, add 'false' to the resulting variable and move on to the next one
      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;
        }
      //--- The object has been successfully retrieved from the list of newly added graphical objects and introduced into the collection -
      //--- find the next free object ID, write it to the property and display the short object description in the journal
      else
        {
         bool program_object=(::StringFind(obj.Name(),this.m_name_program)==0);
         obj.SetObjectID(this.GetFreeGraphObjID(program_object));
         obj.Print();
        }
     }
   //--- Return the result of adding the object to the collection
   return res;
  }
//+------------------------------------------------------------------+

Let's simplify the event handler logic by getting rid of if-else and add object click tracking to determine the object selection with a mouse (not implemented yet):

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
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)
     {
      //--- Get the chart ID. If lparam is zero,
      //--- the event is from the current chart,
      //--- otherwise, this is a custom event from an indicator
      long chart_id=(lparam==0 ? ::ChartID() : lparam);
      //--- Get the object, whose properties were changed or which was relocated,
      //--- from the collection list by its name set in sparam
      obj=this.GetStdGraphObject(sparam,chart_id);
      
      //--- If failed to get the object by its name, it is not on the list,
      //--- which means its name has been changed
      if(obj==NULL)
        {
         //--- Let's search the list for the object that is not on the chart
         obj=this.FindMissingObj(chart_id);
         //--- If failed to find the object here as well, exit
         if(obj==NULL)
            return;
         //--- Get the name of the renamed graphical object on the chart, which is not in the collection list
         string name_new=this.FindExtraObj(chart_id);
         //--- Set a new name for the collection list object, which does not correspond to any graphical object on the chart
         obj.SetName(name_new);
        }
      //--- Update the properties of the obtained object
      //--- and check their change
      obj.PropertiesRefresh();
      obj.PropertiesCheckChanged();
     }
  }
//+------------------------------------------------------------------+

The method creating a new standard graphical object:

//+------------------------------------------------------------------+
//| Create a new standard graphical object                           |
//+------------------------------------------------------------------+
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)
     {
      //--- Lines
      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);
      //--- Channels
      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);
      //--- Gann
      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);
      //--- Fibo
      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);
      //--- Elliott
      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);
      //--- Shapes
      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);
      //--- Arrows
      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);
      //--- Graphical objects
      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;
     }
  }
//+------------------------------------------------------------------+

The method receives the ID of the chart to feature an object, its name, type, chart subwindow and five pivot point coordinates. The first coordinate (time and price) is mandatory, while the rest have predefined default values allowing users to construct any standard graphical object. In the method, reset the last error code and return the result of the ObjectCreate() function execution depending on an object type. In case of an object creation error, the CreateNewStdGraphObjectAndGetCtrlObj() method considered above and calling the method sends the error message with the error code and its description to the journal.

Now all is ready to test the improvements implemented to classes and program standard graphical objects.

Test

Rather than creating all graphical objects, I will limit myself to a vertical line only. It will be created when left-clicking on a chart while holding down Ctrl. Let's check creation of an object, handling an error when trying to create an object with the same name, handling changes in the time coordinate, as well as tracking changes in the pivot point coordinates of objects having more than two pivot points.

To perform the test, I will use the EA from the previous article and save it in \MQL5\Experts\TestDoEasy\Part89\ as TestDoEasyPart89.mq5.

In the EA's OnChartEvent() handler, disable the code block for creating form objects while holding down Ctrl and add the code block for creating a vertical line with a specified name when clicking on a chart while holding down Ctrl in the mouse click coordinate:

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
//--- If working in the tester, exit
   if(MQLInfoInteger(MQL_TESTER))
      return;
//--- If the mouse is moved
   /*
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      CForm *form=NULL;
      datetime time=0;
      double price=0;
      int wnd=0;
      
      //--- If Ctrl is not pressed,
      if(!IsCtrlKeyPressed())
        {
         //--- clear the list of created form objects, allow scrolling a chart with the mouse and show the context menu
         list_forms.Clear();
         ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,true);
         ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,true);
         return;
        }
      
      //--- If X and Y chart coordinates are successfully converted into time and price,
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price))
        {
         //--- get the bar index the cursor is hovered over
         int index=iBarShift(Symbol(),PERIOD_CURRENT,time);
         if(index==WRONG_VALUE)
            return;
         
         //--- Get the bar index by index
         CBar *bar=engine.SeriesGetBar(Symbol(),Period(),index);
         if(bar==NULL)
            return;
         
         //--- Convert the coordinates of a chart from the time/price representation of the bar object to the X and Y coordinates
         int x=(int)lparam,y=(int)dparam;
         if(!ChartTimePriceToXY(ChartID(),0,bar.Time(),(bar.Open()+bar.Close())/2.0,x,y))
            return;
         
         //--- Disable moving a chart with the mouse and showing the context menu
         ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,false);
         ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,false);
         
         //--- Create the form object name and hide all objects except one having such a name
         string name="FormBar_"+(string)index;
         HideFormAllExceptOne(name);
         
         //--- If the form object with such a name does not exist yet,
         if(!IsPresentForm(name))
           {
            //--- create a new form object
            form=bar.CreateForm(index,name,x,y,114,16);   
            if(form==NULL)
               return;
            
            //--- Set activity and unmoveability flags for the form
            form.SetActive(true);
            form.SetMovable(false);
            //--- Set the opacity of 200
            form.SetOpacity(200);
            //--- The form background color is set as the first color from the color array
            form.SetColorBackground(array_clr[0]);
            //--- Form outlining frame color
            form.SetColorFrame(C'47,70,59');
            //--- Draw the shadow drawing flag
            form.SetShadow(true);
            //--- Calculate the shadow color as the chart background color converted to the monochrome one
            color clrS=form.ChangeColorSaturation(form.ColorBackground(),-100);
            //--- If the settings specify the usage of the chart background color, replace the monochrome color with 20 units
            //--- Otherwise, use the color specified in the settings for drawing the shadow
            color clr=(InpUseColorBG ? form.ChangeColorLightness(clrS,-20) : InpColorForm3);
            //--- Draw the form shadow with the right-downwards offset from the form by three pixels along all axes
            //--- Set the shadow opacity to 200, while the blur radius is equal to 4
            form.DrawShadow(2,2,clr,200,3);
            //--- Fill the form background with a vertical gradient
            form.Erase(array_clr,form.Opacity());
            //--- Draw an outlining rectangle at the edges of the form
            form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,form.ColorFrame(),form.Opacity());
            //--- If failed to add the form object to the list, remove the form and exit the handler
            if(!list_forms.Add(form))
              {
               delete form;
               return;
              }
            //--- Capture the form appearance
            form.Done();
           }
         //--- If the form object exists,
         if(form!=NULL)
           {
            //--- draw a text with the bar type description on it and show the form. The description corresponds to the mouse cursor position
            form.TextOnBG(0,bar.BodyTypeDescription(),form.Width()/2,form.Height()/2-1,FRAME_ANCHOR_CENTER,C'7,28,21');
            form.Show();
           }
         //--- Re-draw the chart
         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);
   
  }
//+------------------------------------------------------------------+

Compile the EA and launch it on the chart.

First, create a vertical line by clicking on a chart while holding down Ctrl, have a look at the line ID and at how object properties change when moving the line along the chart. If we re-create the same line, we get the error message in the journal.
Next, create an equidistant channel, see its ID value and check how changes in the properties of its three pivot points are tracked:



What's next?

In the next article, I will continue working on the functionality for programming graphical objects.

All files of the current version of the library are attached below together with the test EA file for MQL5 for you to test and download. Keep in mind that you need the indicator file from the article 87 to let the library work with chart graphical objects that do not belong to the program.

Leave your questions, comments and suggestions in the comments.

Back to contents

*Previous articles within the series:


Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/10119

Attached files |
MQL5.zip (4174.43 KB)
Universal regression model for market price prediction (Part 2): Natural, technological and social transient functions Universal regression model for market price prediction (Part 2): Natural, technological and social transient functions
This article is a logical continuation of the previous one. It highlights the facts that confirm the conclusions made in the first article. These facts were revealed within ten years after its publication. They are centered around three detected dynamic transient functions describing the patterns in market price changes.
Learn Why and How to Design Your Algorithmic Trading System Learn Why and How to Design Your Algorithmic Trading System
This article shows the basics of MQL for beginners to design their Algorithmic trading system (Expert Advisor) through designing a simple algorithmic trading system after mentioning some basics of MQL5
Advanced EA constructor for MetaTrader - botbrains.app Advanced EA constructor for MetaTrader - botbrains.app
In this article, we demonstrate features of botbrains.app - a no-code platform for trading robots development. To create a trading robot you don't need to write any code - just drag and drop the necessary blocks onto the scheme, set their parameters, and establish connections between them.
WebSockets for MetaTrader 5 — Using the Windows API WebSockets for MetaTrader 5 — Using the Windows API
In this article, we will use the WinHttp.dll to create a WebSocket client for MetaTrader 5 programs. The client will ultimately be implemented as a class and also tested against the Deriv.com WebSocket API.