English Русский 中文 Español Deutsch 日本語
Gráficos na biblioteca DoEasy (Parte 93): preparando a funcionalidade para criar objetos gráficos compostos

Gráficos na biblioteca DoEasy (Parte 93): preparando a funcionalidade para criar objetos gráficos compostos

MetaTrader 5Exemplos | 4 maio 2022, 11:28
393 0
Artyom Trishkin
Artyom Trishkin

Sumário


Ideia

O terminal cliente MetaTrader 5 fornece uma ampla gama de ferramentas de análise gráfica para uso em diversos tipos de construções gráficas. Mas é comum ouvir dizer que falta uma ferramenta ou outra. No terminal, temos 44 ferramentas analíticas. Agora vamos imaginar o que poderíamos fazer se pudéssemos unir estes objetos gráficos em conjuntos afins, criando assim novas ferramentas de análise técnica. Bem, a linguagem da MQL5 nos permite fazer isso!

Nossa biblioteca terá suporte para a criação de objetos gráficos compostos complexos, sendo que tais objetos podem ter qualquer hierarquia de relações. Cada um desses objetos terá um objeto gráfico de base, que terá uma lista de outros objetos gráficos vinculados aos mesmos. O objeto de base terá a funcionalidade para gerenciar propriedades de objetos gráficos subordinados, e estes últimos por sua vez terão um conjunto de coordenadas X e Y do objeto de base, ao qual estarão vinculados. Uma coordenada não pode ser tanto um único valor, mas uma lista de propriedades do objeto de base. Por exemplo, para calcular a coordenada X de um objeto dependente, podemos usar várias coordenadas X, não apenas a coordenada X de algum ponto do objeto de base, mas várias coordenadas, por exemplo, duas coordenadas X, nomeadamente os pontos 0 e 1 da linha de tendência. Dessa forma, a coordenada X do objeto dependente pode ser a média das duas coordenadas X do objeto de base.

Cada objeto dependente na lista do objeto de base também pode ser o objeto de base em relação a outros objetos gráficos de sua lista. Isto permitirá a criação de conjuntos muito diferentes de diversos objetos gráficos compostos.
Não apenas tentaremos criar tais objetos programaticamente, com cada objeto dependente sendo adicionado à lista de objetos de base no código do programa, mas também procurar gerá-los em tempo real - quando arrastamos um objeto gráfico sobre outro, o primeiro será anexado ao segundo com uma seleção visual de pontos de ancoragem, que podem então ser substituídos.

Hoje vamos criar uma funcionalidade para criar objetos gráficos padrão estendidos, gerando por um lado novas propriedades para objetos gráficos padrão, que indicam que o objeto é estendido, e por outro classes para armazenar propriedades associativas das coordenadas do objeto de base para calcular e especificar as coordenadas do objeto dependente.


Modificando as classes da biblioteca

À lista de tipos de elementos gráficos localizada no arquivo \MQL5\Include\DoEasy\Defines.mqh vamos adicionar mais um tipo, nomeadamente o objeto gráfico padrão estendido:

//+------------------------------------------------------------------+
//| The list of graphical element types                              |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_ELEMENT_TYPE
  {
   GRAPH_ELEMENT_TYPE_STANDARD,                       // Standard graphical object
   GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,              // Extended standard graphical object
   GRAPH_ELEMENT_TYPE_ELEMENT,                        // Element
   GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                     // Shadow object
   GRAPH_ELEMENT_TYPE_FORM,                           // Form
   GRAPH_ELEMENT_TYPE_WINDOW,                         // Window
  };
//+------------------------------------------------------------------+

Na lista de propriedades inteiras dos objetos gráficos, adicionaremos uma propriedade, nomeadamente o identificador do objeto gráfico de base e aumentaremos o número de propriedades inteiras de 54 para 55:

//+------------------------------------------------------------------+
//| Integer properties of a standard graphical object                |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_OBJ_PROP_INTEGER
  {
   //--- Additional properties
   GRAPH_OBJ_PROP_ID = 0,                             // Object ID
   GRAPH_OBJ_PROP_BASE_ID,                            // Base object ID
   GRAPH_OBJ_PROP_TYPE,                               // Graphical object type (ENUM_OBJECT)
   GRAPH_OBJ_PROP_ELEMENT_TYPE,                       // Graphical element type (ENUM_GRAPH_ELEMENT_TYPE)
   GRAPH_OBJ_PROP_SPECIES,                            // Graphical object species (ENUM_GRAPH_OBJ_SPECIES)
   GRAPH_OBJ_PROP_BELONG,                             // Graphical object affiliation
   GRAPH_OBJ_PROP_CHART_ID,                           // Chart ID
   GRAPH_OBJ_PROP_WND_NUM,                            // Chart subwindow index
   GRAPH_OBJ_PROP_NUM,                                // Object index in the list
   GRAPH_OBJ_PROP_CHANGE_HISTORY,                     // Flag of storing the change history
   GRAPH_OBJ_PROP_GROUP,                              // Group of objects the graphical object belongs to
   //--- Common properties of all graphical objects
   GRAPH_OBJ_PROP_CREATETIME,                         // Object creation time
   GRAPH_OBJ_PROP_TIMEFRAMES,                         // Object visibility on timeframes
   GRAPH_OBJ_PROP_BACK,                               // Background object
   GRAPH_OBJ_PROP_ZORDER,                             // Priority of a graphical object for receiving the event of clicking on a chart
   GRAPH_OBJ_PROP_HIDDEN,                             // Disable displaying the name of a graphical object in the terminal object list
   GRAPH_OBJ_PROP_SELECTED,                           // Object selection
   GRAPH_OBJ_PROP_SELECTABLE,                         // Object availability
//--- Properties belonging to different graphical objects
   GRAPH_OBJ_PROP_TIME,                               // Time coordinate
   GRAPH_OBJ_PROP_COLOR,                              // Color
   GRAPH_OBJ_PROP_STYLE,                              // Style
   GRAPH_OBJ_PROP_WIDTH,                              // Line width
   GRAPH_OBJ_PROP_FILL,                               // Object color filling
   GRAPH_OBJ_PROP_READONLY,                           // Ability to edit text in the Edit object
   GRAPH_OBJ_PROP_LEVELS,                             // Number of levels
   GRAPH_OBJ_PROP_LEVELCOLOR,                         // Level line color
   GRAPH_OBJ_PROP_LEVELSTYLE,                         // Level line style
   GRAPH_OBJ_PROP_LEVELWIDTH,                         // Level line width
   GRAPH_OBJ_PROP_ALIGN,                              // Horizontal text alignment in the Edit object (OBJ_EDIT)
   GRAPH_OBJ_PROP_FONTSIZE,                           // Font size
   GRAPH_OBJ_PROP_RAY_LEFT,                           // Ray goes to the left
   GRAPH_OBJ_PROP_RAY_RIGHT,                          // Ray goes to the right
   GRAPH_OBJ_PROP_RAY,                                // Vertical line goes through all windows of a chart
   GRAPH_OBJ_PROP_ELLIPSE,                            // Display the full ellipse of the Fibonacci Arc object
   GRAPH_OBJ_PROP_ARROWCODE,                          // Arrow code for the "Arrow" object
   GRAPH_OBJ_PROP_ANCHOR,                             // Position of the binding point of the graphical object
   GRAPH_OBJ_PROP_XDISTANCE,                          // Distance from the base corner along the X axis in pixels
   GRAPH_OBJ_PROP_YDISTANCE,                          // Distance from the base corner along the Y axis in pixels
   GRAPH_OBJ_PROP_DIRECTION,                          // Gann object trend
   GRAPH_OBJ_PROP_DEGREE,                             // Elliott wave marking level
   GRAPH_OBJ_PROP_DRAWLINES,                          // Display lines for Elliott wave marking
   GRAPH_OBJ_PROP_STATE,                              // Button state (pressed/released)
   GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID,                 // Chart object ID (OBJ_CHART).
   GRAPH_OBJ_PROP_CHART_OBJ_PERIOD,                   // Chart object period
   GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE,               // Time scale display flag for the Chart object
   GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE,              // Price scale display flag for the Chart object
   GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE,              // Chart object scale
   GRAPH_OBJ_PROP_XSIZE,                              // Object width along the X axis in pixels.
   GRAPH_OBJ_PROP_YSIZE,                              // Object height along the Y axis in pixels.
   GRAPH_OBJ_PROP_XOFFSET,                            // X coordinate of the upper-left corner of the visibility area.
   GRAPH_OBJ_PROP_YOFFSET,                            // Y coordinate of the upper-left corner of the visibility area.
   GRAPH_OBJ_PROP_BGCOLOR,                            // Background color for OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL
   GRAPH_OBJ_PROP_CORNER,                             // Chart corner for binding a graphical object
   GRAPH_OBJ_PROP_BORDER_TYPE,                        // Border type for "Rectangle border"
   GRAPH_OBJ_PROP_BORDER_COLOR,                       // Border color for OBJ_EDIT and OBJ_BUTTON
  };
#define GRAPH_OBJ_PROP_INTEGER_TOTAL (55)             // Total number of integer properties
#define GRAPH_OBJ_PROP_INTEGER_SKIP  (0)              // Number of integer properties not used in sorting
//+------------------------------------------------------------------+

O identificador do objeto de base será indicado a cada objeto subordinado (a partir de 1). Se o valor desta propriedade for zero é porque que o objeto não está vinculado a nenhum outro e, ademais, não é subordinado.

Na lista de propriedades de string dos objetos gráficos adicionaremos outra propriedade, particularmente o nome do objeto gráfico de base, e aumentaremos o número de propriedades de string de 7 para 8:

//+------------------------------------------------------------------+
//| String properties of a standard graphical object                 |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_OBJ_PROP_STRING
  {
   GRAPH_OBJ_PROP_NAME = (GRAPH_OBJ_PROP_INTEGER_TOTAL+GRAPH_OBJ_PROP_DOUBLE_TOTAL), // Object name
   GRAPH_OBJ_PROP_BASE_NAME,                          // Base object name
   GRAPH_OBJ_PROP_TEXT,                               // Object description (text contained in the object)
   GRAPH_OBJ_PROP_TOOLTIP,                            // Tooltip text
   GRAPH_OBJ_PROP_LEVELTEXT,                          // Level description
   GRAPH_OBJ_PROP_FONT,                               // Font
   GRAPH_OBJ_PROP_BMPFILE,                            // BMP file name for the "Bitmap Level" object
   GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL,                   // Symbol for the Chart object 
  };
#define GRAPH_OBJ_PROP_STRING_TOTAL  (8)              // Total number of string properties
//+------------------------------------------------------------------+

Adicionaremos novas constantes à lista de possíveis critérios de classificação dos objetos gráficos, constantes essas que correspondem às novas propriedades do objeto gráfico adicionado acima:

//+------------------------------------------------------------------+
//| Possible sorting criteria of graphical objects                   |
//+------------------------------------------------------------------+
#define FIRST_GRAPH_OBJ_DBL_PROP  (GRAPH_OBJ_PROP_INTEGER_TOTAL-GRAPH_OBJ_PROP_INTEGER_SKIP)
#define FIRST_GRAPH_OBJ_STR_PROP  (GRAPH_OBJ_PROP_INTEGER_TOTAL-GRAPH_OBJ_PROP_INTEGER_SKIP+GRAPH_OBJ_PROP_DOUBLE_TOTAL-GRAPH_OBJ_PROP_DOUBLE_SKIP)
enum ENUM_SORT_GRAPH_OBJ_MODE
  {
//--- Sort by integer properties
   SORT_BY_GRAPH_OBJ_ID = 0,                             // Sort by object ID
   SORT_BY_GRAPH_OBJ_BASE_ID,                            // Sort by object ID
   SORT_BY_GRAPH_OBJ_TYPE,                               // Sort by object type
   SORT_BY_GRAPH_OBJ_ELEMENT_TYPE,                       // Sort by graphical element type
   SORT_BY_GRAPH_OBJ_SPECIES,                            // Sort by a graphical object species
   SORT_BY_GRAPH_OBJ_BELONG,                             //  Sort by a graphical element affiliation
   SORT_BY_GRAPH_OBJ_CHART_ID,                           // Sort by chart ID
   SORT_BY_GRAPH_OBJ_WND_NUM,                            // Sort by chart subwindow index
   SORT_BY_GRAPH_OBJ_NUM,                                // Sort by object index in the list
   SORT_BY_GRAPH_OBJ_CHANGE_HISTORY,                     // Sort by the flag of storing the change history
   SORT_BY_GRAPH_OBJ_GROUP,                              // Sort by the group of objects the graphical object belongs to
   SORT_BY_GRAPH_OBJ_CREATETIME,                         // Sort by object creation time
   SORT_BY_GRAPH_OBJ_TIMEFRAMES,                         // Sort by object visibility on timeframes
   SORT_BY_GRAPH_OBJ_BACK,                               // Sort by the "Background object" property
   SORT_BY_GRAPH_OBJ_ZORDER,                             // Sort by the priority of a graphical object for receiving the event of clicking on a chart
   SORT_BY_GRAPH_OBJ_HIDDEN,                             // Sort by a disabling display of the name of a graphical object in the terminal object list
   SORT_BY_GRAPH_OBJ_SELECTED,                           // Sort by the "Object selection" property
   SORT_BY_GRAPH_OBJ_SELECTABLE,                         // Sort by the "Object availability" property
   SORT_BY_GRAPH_OBJ_TIME,                               // Sort by time coordinate
   SORT_BY_GRAPH_OBJ_COLOR,                              // Sort by color
   SORT_BY_GRAPH_OBJ_STYLE,                              // Sort by style
   SORT_BY_GRAPH_OBJ_WIDTH,                              // Sort by line width
   SORT_BY_GRAPH_OBJ_FILL,                               // Sort by the "Object color filling" property
   SORT_BY_GRAPH_OBJ_READONLY,                           // Sort by the ability to edit text in the Edit object
   SORT_BY_GRAPH_OBJ_LEVELS,                             // Sort by number of levels
   SORT_BY_GRAPH_OBJ_LEVELCOLOR,                         // Sort by line level color
   SORT_BY_GRAPH_OBJ_LEVELSTYLE,                         // Sort by line level style
   SORT_BY_GRAPH_OBJ_LEVELWIDTH,                         // Sort by line level width
   SORT_BY_GRAPH_OBJ_ALIGN,                              // Sort by the "Horizontal text alignment in the Entry field" property
   SORT_BY_GRAPH_OBJ_FONTSIZE,                           // Sort by font size
   SORT_BY_GRAPH_OBJ_RAY_LEFT,                           // Sort by "Ray goes to the left" property
   SORT_BY_GRAPH_OBJ_RAY_RIGHT,                          // Sort by "Ray goes to the right" property
   SORT_BY_GRAPH_OBJ_RAY,                                // Sort by the "Vertical line goes through all windows of a chart" property
   SORT_BY_GRAPH_OBJ_ELLIPSE,                            // Sort by the "Display the full ellipse of the Fibonacci Arc object" property
   SORT_BY_GRAPH_OBJ_ARROWCODE,                          // Sort by an arrow code for the Arrow object
   SORT_BY_GRAPH_OBJ_ANCHOR,                             // Sort by the position of a binding point of a graphical object
   SORT_BY_GRAPH_OBJ_XDISTANCE,                          // Sort by a distance from the base corner along the X axis in pixels
   SORT_BY_GRAPH_OBJ_YDISTANCE,                          // Sort by a distance from the base corner along the Y axis in pixels
   SORT_BY_GRAPH_OBJ_DIRECTION,                          // Sort by the "Gann object trend" property
   SORT_BY_GRAPH_OBJ_DEGREE,                             // Sort by the "Elliott wave marking level" property
   SORT_BY_GRAPH_OBJ_DRAWLINES,                          // Sort by the "Display lines for Elliott wave marking" property
   SORT_BY_GRAPH_OBJ_STATE,                              // Sort by button state (pressed/released)
   SORT_BY_GRAPH_OBJ_OBJ_CHART_ID,                       // Sort by Chart object ID.
   SORT_BY_GRAPH_OBJ_CHART_OBJ_PERIOD,                   // Sort by Chart object period
   SORT_BY_GRAPH_OBJ_CHART_OBJ_DATE_SCALE,               // Sort by time scale display flag for the Chart object
   SORT_BY_GRAPH_OBJ_CHART_OBJ_PRICE_SCALE,              // Sort by price scale display flag for the Chart object
   SORT_BY_GRAPH_OBJ_CHART_OBJ_CHART_SCALE,              // Sort by Chart object scale
   SORT_BY_GRAPH_OBJ_XSIZE,                              // Sort by Object width along the X axis in pixels
   SORT_BY_GRAPH_OBJ_YSIZE,                              // Sort by object height along the Y axis in pixels
   SORT_BY_GRAPH_OBJ_XOFFSET,                            // Sort by X coordinate of the upper-left corner of the visibility area
   SORT_BY_GRAPH_OBJ_YOFFSET,                            // Sort by Y coordinate of the upper-left corner of the visibility area
   SORT_BY_GRAPH_OBJ_BGCOLOR,                            // Sort by background color for OBJ_EDIT, OBJ_BUTTON and OBJ_RECTANGLE_LABEL
   SORT_BY_GRAPH_OBJ_CORNER,                             // Sort by chart corner for binding a graphical object
   SORT_BY_GRAPH_OBJ_BORDER_TYPE,                        // Sort by border type for the "Rectangle border" object
   SORT_BY_GRAPH_OBJ_BORDER_COLOR,                       // Sort by frame color for the OBJ_EDIT and OBJ_BUTTON objects
//--- Sort by real properties
   SORT_BY_GRAPH_OBJ_PRICE = FIRST_GRAPH_OBJ_DBL_PROP,   // Sort by price coordinate
   SORT_BY_GRAPH_OBJ_LEVELVALUE,                         // Sort by level value
   SORT_BY_GRAPH_OBJ_SCALE,                              // Sort by scale (property of Gann objects and Fibonacci Arcs objects)
   SORT_BY_GRAPH_OBJ_ANGLE,                              // Sort by angle
   SORT_BY_GRAPH_OBJ_DEVIATION,                          // Sort by a deviation of the standard deviation channel
//--- Sort by string properties
   SORT_BY_GRAPH_OBJ_NAME = FIRST_GRAPH_OBJ_STR_PROP,    // Sort by object name
   SORT_BY_GRAPH_OBJ_BASE_NAME,                          // Sort by base object name
   SORT_BY_GRAPH_OBJ_TEXT,                               // Sort by object description
   SORT_BY_GRAPH_OBJ_TOOLTIP,                            // Sort by tooltip text
   SORT_BY_GRAPH_OBJ_LEVELTEXT,                          // Sort by level description
   SORT_BY_GRAPH_OBJ_FONT,                               // Sort by font
   SORT_BY_GRAPH_OBJ_BMPFILE,                            // Sort by BMP file name for the "Bitmap Level" object
   SORT_BY_GRAPH_OBJ_CHART_OBJ_SYMBOL,                   // Sort by Chart object period symbol
  };
//+------------------------------------------------------------------+

Agora poderemos selecionar os objetos de acordo com suas respectivas propriedades, ordená-los e criar listas de objetos que tenham as mesmas propriedades.


No arquivo \MQL5\Include\DoEasy\Data.mqh inserimos os índices das novas mensagens da biblioteca:

   MSG_LIB_SYS_REQUEST_OUTSIDE_LONG_ARRAY,            // Request outside long array
   MSG_LIB_SYS_REQUEST_OUTSIDE_DOUBLE_ARRAY,          // Request outside double array
   MSG_LIB_SYS_REQUEST_OUTSIDE_STRING_ARRAY,          // Request outside string array
   MSG_LIB_SYS_REQUEST_OUTSIDE_ARRAY,                 // Request outside the array

...

   MSG_GRAPH_ELEMENT_TYPE_STANDARD,                   // Standard graphical object
   MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,          // Extended standard graphical object
   MSG_GRAPH_ELEMENT_TYPE_ELEMENT,                    // Element
   MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ,                 // Shadow object
   MSG_GRAPH_ELEMENT_TYPE_FORM,                       // Form
   MSG_GRAPH_ELEMENT_TYPE_WINDOW,                     // Window

...

   MSG_GRAPH_OBJ_PROP_ID,                             // Object ID
   MSG_GRAPH_OBJ_PROP_BASE_ID,                        // Base object ID
   MSG_GRAPH_OBJ_PROP_TYPE,                           // Graphical object type (ENUM_OBJECT)
   MSG_GRAPH_OBJ_PROP_ELEMENT_TYPE,                   // Graphical element type (ENUM_GRAPH_ELEMENT_TYPE)

...

   MSG_GRAPH_OBJ_PROP_NAME,                           // Object name
   MSG_GRAPH_OBJ_PROP_BASE_NAME,                      // Base object name
   MSG_GRAPH_OBJ_PROP_TEXT,                           // Object description (text contained in the object)
   MSG_GRAPH_OBJ_PROP_TOOLTIP,                        // Tooltip text
   MSG_GRAPH_OBJ_PROP_LEVELTEXT,                      // Level description
   MSG_GRAPH_OBJ_PROP_FONT,                           // Font
   MSG_GRAPH_OBJ_PROP_BMPFILE,                        // BMP file name for the "Bitmap Level" object
   MSG_GRAPH_OBJ_PROP_SYMBOL,                         // Chart object symbol

...

   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CREATE,                // New graphical object created
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CHANGE,                // Changed the graphical object property
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_RENAME,                // Graphical object renamed
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DELETE,                // Graphical object removed
   MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DEL_CHART,             // Graphical object removed together with the chart
   
//--- CGStdGraphObj (Extended)
   MSG_GRAPH_OBJ_FAILED_CREATE_NEW_EXT_OBJ,           //  Failed to create the class object of an extended graphical object
   MSG_GRAPH_OBJ_FAILED_CREATE_NEW_BASE_EXT_OBJ,      // Failed to create the base object for an extended graphical object
   MSG_GRAPH_OBJ_FAILED_ADD_EXT_OBJ_TO_LIST,          // Failed to add the extended standard graphical object to the list
   MSG_GRAPH_OBJ_FAILED_ADD_BASE_EXT_OBJ_TO_LIST,     // Failed to add the base object to the list
   MSG_GRAPH_OBJ_FAILED_CREATE_NEW_DEP_EXT_OBJ,       // Failed to create the subordinate object of an extended graphical object
   MSG_GRAPH_OBJ_FAILED_ADD_DEP_EXT_OBJ_TO_LIST,      // Failed to add the subordinate object to the list
   MSG_GRAPH_OBJ_FAILED_GET_EXT_OBJ_FROM_LIST,        // Failed to receive the extended graphical object from the list
   MSG_GRAPH_OBJ_PROP_NUM_EXT_BASE_OBJ,               // Base object of the extended graphical object
   MSG_GRAPH_OBJ_NOT_EXT_OBJ,                         // The object is not an extended standard graphical object
   
//--- CLinkedPivotPoint
   MSG_GRAPH_OBJ_EXT_NOT_ANY_PIVOTS_X,                // Not a single pivot point is set for the object along the X axis
   MSG_GRAPH_OBJ_EXT_NOT_ANY_PIVOTS_Y,                // Not a single pivot point is set for the object along the Y axis
   MSG_GRAPH_OBJ_EXT_NOT_ATACHED_TO_BASE,             // The object is not attached to the basic graphical object
   MSG_GRAPH_OBJ_EXT_FAILED_CREATE_PP_DATA_OBJ,       // Failed to create a data object for the X and Y pivot points
  };
//+------------------------------------------------------------------+

e as mensagens que correspondem aos índices recém-adicionados:

   {"Запрос за пределами long-массива","Data requested outside the long-array"},
   {"Запрос за пределами double-массива","Data requested outside the double-array"},
   {"Запрос за пределами string-массива","Data requested outside the string-array"},
   {"Запрос за пределами массива","Data requested outside the array"},

...

   {"Стандартный графический объект","Standard graphic object"},
   {"Расширенный стандартный графический объект","Extended standard graphic object"},
   {"Элемент","Element"},
   {"Объект тени","Shadow object"},
   {"Форма","Form"},
   {"Окно","Window"},

...

   {"Идентификатор объекта","Object ID"},
   {"Идентификатор базового объекта","Base object ID"},
   {"Тип объекта","Object type"},
   {"Тип графического элемента","Graphic element type"},

...

   {"Имя","Name"},
   {"Имя базового объекта","Base object name"},
   {"Описание","Description"},
   {"Текст всплывающей подсказки","The text of a tooltip"},
   {"Описание уровня","Level description"},
   {"Шрифт","Font"},
   {"Имя BMP-файла","BMP-file name"},
   {"Символ графика","Chart Symbol"},

...

   {"Создан новый графический объект","New graphic object created"},
   {"Изменено свойство графического объекта","Changed graphic object property"},
   {"Графический объект переименован","Graphic object renamed"},
   {"Удалён графический объект","Graphic object deleted"},
   {"Графический объект удалён вместе с графиком","The graphic object has been removed along with the chart"},
   
//--- CGStdGraphObj (Extended)
   {"Не удалось создать объект класса расширенного графического объекта","Failed to create the class object of extended standart graphics object"},
   {"Не удалось создать базовый объект расширенного графического объекта","Failed to create the base object of a extended graphics object"},
   {"Не удалось добавить расширенный стандартный графический объект в список","Failed to add extended standard graphic object to the list"},
   {"Не удалось добавить базовый объект в список","Failed to add base object to list"},
   {"Не удалось создать подчинённый объект расширенного графического объекта","Failed to create the dependent object of a extended graphics object"},
   {"Не удалось добавить подчинённый объект в список","Failed to add dependent object to list"},
   {"Не удалось получить расширенный графический объект из списка","Failed to get extended graphic object from list"},
   {"Базовый объект расширенного графического объекта","The base object of the extended graphical object"},
   {"Объект не является расширенным стандартным графическим объектом","The object is not an extended standard graphical object"},
   
//--- CLinkedPivotPoint
   {"Для объекта не установлено ни одной опорной точки по оси X","The object does not have any pivot points set along the x-axis"},
   {"Для объекта не установлено ни одной опорной точки по оси Y","The object does not have any pivot points set along the y-axis"},
   {"Объект не привязан к базовому графическому объекту","The object is not attached to the base graphical object"},
   {"Не удалось создать объект данных опорной точки X и Y.","Failed to create X and Y reference point data object"},
   
  };
//+---------------------------------------------------------------------+


Os objetos gráficos padrão estendidos serão feitos com base em objetos padrão. Para fazer de um objeto um objeto estendido, basta especificar sua propriedade de objeto gráfico estendido, porque, a partir desse ajuste, ele usará a funcionalidade criada para manusear objetos gráficos padrão estendidos, isto é, para adicionar objetos subordinados a sua lista e controlá-los.

Já criamos classes de todos os objetos gráficos padrão que são descendentes da classe do objeto gráfico padrão abstrato, portanto, ao criar um objeto estendido só precisaremos indicar o sinalizador de que não se trata de um objeto gráfico comum, mas de um objeto estendido.

Naturalmente, como objeto é criado programaticamente, a propriedade que assinala qual a vinculação do objeto deve ser definida como "objeto pertence ao programa" (por padrão é definida a propriedade de objeto criado manualmente). Do mesmo modo, será necessário especificar o tipo objeto gráfico não apenas padrão, mas sim padrão estendido. Faremos isso tudo em função do valor do sinalizador passado ao construtor do objeto gráfico, quando criado.

Além destas modificações, precisaremos adicionar as novas propriedades do objeto gráfico - que criamos acima - aos métodos que devolvem os sinalizadores que indicam que o objeto suporta suas propriedades.
Como todas essas mudanças são as mesmas para todas as classes de objetos gráficos padrão localizados na pasta
\MQL5\Include\DoEasy\Objects\Graph\Standard\, usaremos como exemplo o arquivo GStdArrowBuyObj.mqh para analisar as alterações introduzidas.

Passaremos o sinalizador de objeto gráfico padrão estendido ao construtor de classes e, dependendo do valor deste sinalizador, especificaremos se é um objeto gráfico padrão, ou um objeto padrão estendido, e se o objeto é criado manualmente, ou de forma programática:

public:
   //--- Constructor
                     CGStdArrowBuyObj(const long chart_id,const string name,const bool extended) : 
                        CGStdGraphObj(OBJECT_DE_TYPE_GSTD_ARROW_BUY,(!extended ? GRAPH_ELEMENT_TYPE_STANDARD : GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED),(!extended ? GRAPH_OBJ_BELONG_NO_PROGRAM : GRAPH_OBJ_BELONG_PROGRAM),GRAPH_OBJ_SPECIES_ARROWS,chart_id,1,name)
                          {
                           //--- Specify the object property
                           CGStdGraphObj::SetProperty(GRAPH_OBJ_PROP_ANCHOR,0,ANCHOR_TOP);
                          }

Aos métodos que retornam os sinalizadores que indicam se o objeto suporta propriedades inteiras e de string adicionamos as novas propriedade escritas acima:

//+------------------------------------------------------------------+
//| Return 'true' if an object supports a passed                     |
//| integer property, otherwise return 'false'                       |
//+------------------------------------------------------------------+
bool CGStdArrowBuyObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property)
  {
   switch((int)property)
     {
      //--- Supported properties
      case GRAPH_OBJ_PROP_ID           :
      case GRAPH_OBJ_PROP_BASE_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_CHANGE_HISTORY:
      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        :
      case GRAPH_OBJ_PROP_ANCHOR       : return true;
      //--- Other properties are not supported
      //--- Default is 'false'
      default: break;
     }
   return false;
  }
//+------------------------------------------------------------------+
//| Return 'true' if an object supports a passed                     |
//| real property, otherwise return 'false'                          |
//+------------------------------------------------------------------+
bool CGStdArrowBuyObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property)
  {
   switch((int)property)
     {
      //--- Supported properties
      case GRAPH_OBJ_PROP_PRICE        : return true;
      //--- Other properties are not supported
      //--- Default is 'false'
      default: break;
     }
   return false;
  }
//+------------------------------------------------------------------+
//| Return 'true' if an object supports a passed                     |
//| string property, otherwise return 'false'                        |
//+------------------------------------------------------------------+
bool CGStdArrowBuyObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_STRING property)
  {
   switch((int)property)
     {
      //--- Supported properties
      case GRAPH_OBJ_PROP_NAME            :
      case GRAPH_OBJ_PROP_BASE_NAME       :
      case GRAPH_OBJ_PROP_TEXT            :
      case GRAPH_OBJ_PROP_TOOLTIP         :  return true;
      //--- Other properties are not supported
      //--- Default is 'false'
      default: break;
     }
   return false;
  }
//+------------------------------------------------------------------+

Estas mudanças precisam ser feitas em todos os arquivos da pasta \MQL5\Include\DoEasy\Objects\Graph\Standard\, o que, entretanto, já está feito, e todas as modificações podem ser vistas nos arquivos anexos.

Após estas modificações, as novas propriedades serão processadas por todos os objetos das classes dos objetos gráficos padrão e, desse modo, poderão ser manuseadas por nós plenamente, pois especificamos sinalizadores que assinalam o suporte a essas novas propriedades.

No arquivo da classe do objeto gráfico de base \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh, vamos adicionar a verificação de tipo "objeto gráfico padrão estendido" ao método que retorna a descrição do tipo de elemento gráfico, para que a descrição de tal objeto gráfico possa ser corretamente gerada:

//+------------------------------------------------------------------+
//| Return the description of the graphical element type             |
//+------------------------------------------------------------------+
string CGBaseObj::TypeElementDescription(void)
  {
   return
     (
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_STANDARD           ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD)           :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED  ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)  :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_ELEMENT            ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT)            :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_SHADOW_OBJ         ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ)         :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_FORM               ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM)               :
      this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WINDOW             ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW)             :
      "Unknown"
     );
  }
//+------------------------------------------------------------------+


Classes para especificar as coordenadas de um objeto dependente

Qualquer objeto dependente na lista do objeto gráfico de base tem coordenadas próprias sobre as quais é construído. Além disso, o objeto gráfico de base precisa saber como controlar as coordenadas do objeto dependente. Para que o objeto de base saiba a qual de suas coordenadas o objeto gráfico dependente está vinculado, este último deve ter algumas propriedades que o objeto de base possa ler e tem que enviar um comando para alterar as coordenadas do objeto dependente quando suas próprias alterações forem feitas.

Para estabelecer tal relação entre as propriedades do objeto de base e o objeto dependente, vamos criar uma classe que chamaremos de classe do objeto de pontos de ancoragem vinculados.

Teremos três classes:

  1. Uma classe de dados de ponto de ancoragem que armazenará aquelas propriedades pelas quais o objeto dependente é vinculado ao mesmo eixo de coordenadas do objeto de base;
  2. Uma classe de dados de dois pontos de ancoragem que armazenará dois objetos da primeira classe. Sendo que um armazenará os dados das propriedade pelas quais o objeto dependente é ancorado na coordenada X do objeto de base, já o segundo armazenará os dados pelos quais o objeto é ancorado na coordenada Y;
  3. Uma classe de pontos de ancoragem vinculados. Esta classe conterá uma lista de objetos do segundo tipo, sendo que o objeto da classe de dados de dois pontos de ancoragem será responsável por cada ponto de ancoragem. Cada objeto armazenará as propriedades do objeto de base utilizado para construir um ponto de ancoragem do objeto dependente, isto é, terá suas coordenadas X e Y que, por sua vez, podem ser calculadas a partir de várias coordenadas do objeto de base. A lista dessas coordenadas é armazenada em um objeto do primeiro tipo para cada eixo.

O primeiro tipo de objeto será criado com base em uma matriz bidimensional regular.
A segunda dimensão da matriz terá valor 2, a célula zero conterá a propriedade em si, como "Tempo", ou "Preço", e já a primeira célula conterá o modificador de propriedade. Por exemplo, uma linha de tendência tem dois pontos de ancoragem, e o ponto escolhido para indicar a coordenada será escrito na célula 1 da segunda dimensão da matriz. Para o ponto esquerdo da linha de tendência este valor será 0, já para o ponto direito, 1.
Na maioria dos casos, a primeira dimensão da matriz terá uma dimensão de 1, o que assinala que apenas uma propriedade do objeto de base ao qual o dependente está vinculado. Mas se precisarmos ancorar um objeto nas coordenadas calculadas, por exemplo, entre dois pontos da linha de tendência, precisaremos aumentar a dimensão da matriz para duas, sendo que a primeira especificará as propriedades do primeiro ponto de ancoragem do objeto de base e a segunda, as propriedades da segunda. Neste caso, o objeto dependente será vinculado ao objeto base por alguma fórmula (a média padrão), que é definida pelo próprio usuário da biblioteca no código. Mas não faremos esta funcionalidade hoje. Hoje estamos preparando tudo o que precisamos para implementar o que temos em mente.

Vamos escrever estas classes diretamente no arquivo de objeto gráfico padrão abstrato \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqh, pois elas serão declaradas neste local e usadas se o objeto que está sendo criado for o objeto gráfico padrão estendido.

Vamos inserir a classe de dados do ponto de ancoragem para o objeto dependente logo no início da listagem do arquivo:

//+------------------------------------------------------------------+
//|                                                 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"
//+------------------------------------------------------------------+
//| Class of the dependent object pivot point data                   |
//+------------------------------------------------------------------+
class CPivotPointData
  {
private:
   bool              m_axis_x;
   int               m_property_x[][2];
public:
//--- (1) Set and (2) return the flag indicating that the pivot point belongs to the X coordinate
   void              SetAxisX(const bool axis_x)         { this.m_axis_x=axis_x; }
   bool              IsAxisX(void)                 const { return this.m_axis_x; }
//--- Return the number of base object pivot points for calculating the coordinate of the dependent one
   int               GetBasePivotsNum(void)  const { return ::ArrayRange(this.m_property_x,0);  }
//--- Add the new pivot point of the base object for calculating the coordinate of a dependent one
   bool              AddNewBasePivotPoint(const string source,const int pivot_prop,const int pivot_num)
                       {
                        //--- Get the array size 
                        int pivot_index=this.GetBasePivotsNum();
                        //--- if failed to increase the array size, inform of that and return 'false'
                        if(::ArrayResize(this.m_property_x,pivot_index+1)!=pivot_index+1)
                          {
                           CMessage::ToLog(source,MSG_LIB_SYS_FAILED_ARRAY_RESIZE);
                           return false;
                          }
                        //--- Return the result of changing the values of a newly added new array dimension
                        return this.ChangeBasePivotPoint(source,pivot_index,pivot_prop,pivot_num);
                       }
//--- Change the specified pivot point of the base object for calculating the coordinate of a dependent one
   bool              ChangeBasePivotPoint(const string source,const int pivot_index,const int pivot_prop,const int pivot_num)
                       {
                        //--- Get the array size. If it is zero, inform of that and return 'false'
                        int n=this.GetBasePivotsNum();
                        if(n==0)
                          {
                           CMessage::ToLog(source,(this.IsAxisX() ? MSG_GRAPH_OBJ_EXT_NOT_ANY_PIVOTS_X : MSG_GRAPH_OBJ_EXT_NOT_ANY_PIVOTS_Y));
                           return false;
                          }
                        //--- If the specified index goes beyond the array range, inform of that and return 'false'
                        if(pivot_index<0 || pivot_index>n-1)
                          {
                           CMessage::ToLog(source,MSG_LIB_SYS_REQUEST_OUTSIDE_ARRAY);
                           return false;
                          }
                        //--- Set the values, passed to the method, in the specified array cells by index
                        this.m_property_x[pivot_index][0]=pivot_prop;
                        this.m_property_x[pivot_index][1]=pivot_num;
                        return true;
                       }

//--- Constructor/destructor
                     CPivotPointData(void){;}
                    ~CPivotPointData(void){;}
  };
//+------------------------------------------------------------------+

A classe contém uma matriz bidimensional e dois métodos para redimensioná-la (por meio da adição de um novo ponto de ancoragem) e definir valores da primeira dimensão especificada da matriz. A classe também tem métodos para definir quais dados de coordenadas são escritos na matriz - X ou Y. Quando mensagens são produzidas a partir da classe, este sinalizador é verificado e a mensagem de erro correspondente é impressa.

Depois da listagem desta classe, vamos escrever uma classe de dados de pontos de ancoragem X e Y do objeto composto:

//+------------------------------------------------------------------+
//| Class of data on X and Y pivot points of a composite object      |
//+------------------------------------------------------------------+
class CPivotPointXY : public CObject
  {
private:
   CPivotPointData   m_pivot_point_x;            // X coordinate pivot point
   CPivotPointData   m_pivot_point_y;            // Y coordinate pivot point
public:
//--- Return the pointer to the (1) X and (2) Y coordinate pivot point data object
   CPivotPointData  *GetPivotPointDataX(void)      { return &this.m_pivot_point_x;                    }
   CPivotPointData  *GetPivotPointDataY(void)      { return &this.m_pivot_point_y;                    }
//--- Return the number of base object pivot points for calculating the (1) X and (2) Y coordinate
   int               GetBasePivotsNumX(void) const { return this.m_pivot_point_x.GetBasePivotsNum();  }
   int               GetBasePivotsNumY(void) const { return this.m_pivot_point_y.GetBasePivotsNum();  }
//--- Add the new pivot point of the base object for calculating the X coordinate of a dependent one
   bool              AddNewBasePivotPointX(const int pivot_prop,const int pivot_num)
                       {
                        return this.m_pivot_point_x.AddNewBasePivotPoint(DFUN,pivot_prop,pivot_num);
                       }
//--- Add the new pivot point of the base object for calculating the Y coordinate of a dependent one
   bool              AddNewBasePivotPointY(const int pivot_prop,const int pivot_num)
                       {
                        return this.m_pivot_point_y.AddNewBasePivotPoint(DFUN,pivot_prop,pivot_num);
                       }
//--- Add new pivot points of the base object for calculating the X and Y coordinates of a dependent one
   bool              AddNewBasePivotPointXY(const int pivot_prop_x,const int pivot_num_x,
                                            const int pivot_prop_y,const int pivot_num_y)
                       {
                        bool res=true;
                        res &=this.m_pivot_point_x.AddNewBasePivotPoint(DFUN,pivot_prop_x,pivot_num_x);
                        res &=this.m_pivot_point_y.AddNewBasePivotPoint(DFUN,pivot_prop_y,pivot_num_y);
                        return res;
                       }
//--- Change the specified pivot point of the base object for calculating the X coordinate of a dependent one
   bool              ChangeBasePivotPointX(const int pivot_index,const int pivot_prop,const int pivot_num)
                       {
                        return this.m_pivot_point_x.ChangeBasePivotPoint(DFUN,pivot_index,pivot_prop,pivot_num);
                       }
//--- Change the specified pivot point of the base object for calculating the Y coordinate of a dependent one
   bool              ChangeBasePivotPointY(const int pivot_index,const int pivot_prop,const int pivot_num)
                       {
                        return this.m_pivot_point_y.ChangeBasePivotPoint(DFUN,pivot_index,pivot_prop,pivot_num);
                       }
//--- Change specified pivot points of the base object for calculating the X and Y coordinates
   bool              ChangeBasePivotPointXY(const int pivot_index,
                                            const int pivot_prop_x,const int pivot_num_x,
                                            const int pivot_prop_y,const int pivot_num_y)
                       {
                        bool res=true;
                        res &=this.m_pivot_point_x.ChangeBasePivotPoint(DFUN,pivot_index,pivot_prop_x,pivot_num_x);
                        res &=this.m_pivot_point_y.ChangeBasePivotPoint(DFUN,pivot_index,pivot_prop_y,pivot_num_y);
                        return res;
                       }
                       
//--- Constructor/destructor
                     CPivotPointXY(void){ this.m_pivot_point_x.SetAxisX(true); this.m_pivot_point_y.SetAxisX(false); }
                    ~CPivotPointXY(void){;}
  };  
//+------------------------------------------------------------------+

Esta classe contém os dois objetos discutidos acima, um para a coordenada X e outro para a coordenada Y, e métodos para trabalhar com estes objetos, que essencialmente apenas retornam os resultados do trabalho feito sobre os métodos da primeira classe. Os sinalizadores dos eixos X ou Y de cada um dos objetos são especificados no construtor.
A classe também tem métodos para especificar os valores de propriedades para coordenadas X e Y simultaneamente - eles chamam imediatamente os métodos das duas primeiras classes e retornam o resultado combinado de sua chamada - se pelo menos um dos métodos retornados for false, o resultado será false.

Esta classe é seguida por uma terceira classe, a classe dos dados vinculados dos pontos de ancoragem do objeto composto:

//+------------------------------------------------------------------+
//| Class of connected data on composite object pivot points         |
//+------------------------------------------------------------------+
class CLinkedPivotPoint
  {
private:
   CArrayObj         m_list;                       // List of pivot points of the bound object X and Y coordinates
   int               m_base_obj_index;             // Base object index
public:
//--- (1) Set and (2) return the base object index
   void              SetBaseObjIndex(const int index)       { this.m_base_obj_index=index;                  }
   int               GetBaseObjIndex(void)            const { return this.m_base_obj_index;                 }
//--- Create a new object of the class of data on X and Y pivot points of a composite object and add it to the object list
   bool              CreateNewLinkedPivotPoint(const int pivot_prop_x,const int pivot_num_x,const int pivot_prop_y,const int pivot_num_y)
                       {
                        //--- Create an object of data on X and Y pivot points
                        CPivotPointXY *obj=new CPivotPointXY();
                        if(obj==NULL)
                           return false;
                        //--- Add a single dimension with data on pivot points of the base object by X and Y to each object
                        if(!obj.AddNewBasePivotPointXY(pivot_prop_x,pivot_num_x,pivot_prop_y,pivot_num_y))
                           return false;
                        //--- If failed to add the newly created object to the list, inform of that, remove the object and return 'false'
                        if(!this.m_list.Add(obj))
                          {
                           CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
                           delete obj;
                           return false;
                          }
                        //--- If all is successful, return 'true'
                        return true;
                       }
   
//--- Return the amount of data on pivot points of X and Y coordinates
   int               GetNumLinkedPivotPoints(void)    const {  return this.m_list.Total();                  }
//--- Return the pointer to the (1) first and (2) the last object of data on X and Y pivot points (3) by index
   CPivotPointXY    *GetLinkedPivotPointXYFirst(void)       const { return this.m_list.At(0);                     }
   CPivotPointXY    *GetLinkedPivotPointXYLast(void)        const { return this.m_list.At(this.m_list.Total()-1); }
   CPivotPointXY    *GetLinkedPivotPointXY(const int index) const { return this.m_list.At(index);                 }
//--- Return the pointer to the X coordinate pivot point data object by index
   CPivotPointData  *GetBasePivotPointDataX(const int index) const
                       {
                        CPivotPointXY *obj=this.GetLinkedPivotPointXY(index);
                        if(obj==NULL)
                           return NULL;
                        return obj.GetPivotPointDataX();
                       }
//--- Return the pointer to the Y coordinate pivot point data object by index
   CPivotPointData  *GetBasePivotPointDataY(const int index) const
                       {
                        CPivotPointXY *obj=this.GetLinkedPivotPointXY(index);
                        if(obj==NULL)
                           return NULL;
                        return obj.GetPivotPointDataY();
                       }
//--- Return the number of base object pivot points for calculating the X coordinate by index
   int               GetBasePivotsNumX(const int index) const
                       {
                        CPivotPointData *obj=this.GetBasePivotPointDataX(index);
                        if(obj==NULL)
                           return NULL;
                        return obj.GetBasePivotsNum();
                       }
//--- Return the number of base object pivot points for calculating the Y coordinate by index
   int               GetBasePivotsNumY(const int index) const
                       {
                        CPivotPointData *obj=this.GetBasePivotPointDataY(index);
                        if(obj==NULL)
                           return NULL;
                        return obj.GetBasePivotsNum();
                       }

//--- Constructor/destructor
                     CLinkedPivotPoint(void){;}
                    ~CLinkedPivotPoint(void){;}
  };  
//+------------------------------------------------------------------+

A classe permite criar objetos das classes de dados de pontos de ancoragem X e Y do objeto composto, que discutimos acima, e retorna apontadores para objetos desta classe. Um desses objetos descreve completamente um único ponto de ancoragem do objeto base ao longo das coordenadas X e Y, e permite adicionar novos dados e modificar os existentes. O objeto também contém um identificador de objeto de base que pode ser usado para determinar a que objeto este objeto gráfico está vinculado.

Na seção privada da classe do objeto gráfico padrão abstrato declaramos uma lista de objetos dependentes (vinculados a ele) e o objeto da classe de pontos de ancoragem vinculados que vimos acima:

//+------------------------------------------------------------------+
//| The class of the abstract standard graphical object              |
//+------------------------------------------------------------------+
class CGStdGraphObj : public CGBaseObj
  {
private:
   CArrayObj         m_list;                                            // List of dependent graphical objects
   CProperties      *Prop;                                              // Pointer to the property object
   CLinkedPivotPoint m_linked_pivots;                                   // Connected pivot points
   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:

Na seção pública da classe, vamos adicionar métodos para operar com a lista de objetos gráficos vinculados e com o objeto da classe de pontos de ancoragem vinculados mencionado acima:

//--- Set the object previous name
   bool              SetNamePrev(const string name)
                       {
                        if(!this.Prop.SetSizeRange(GRAPH_OBJ_PROP_NAME,this.Prop.CurrSize(GRAPH_OBJ_PROP_NAME)+1))
                           return false;
                        this.SetProperty(GRAPH_OBJ_PROP_NAME,this.Prop.CurrSize(GRAPH_OBJ_PROP_NAME)-1,name);
                        return true;
                       }
                       
//--- Return (1) the list of dependent objects and (2) dependent graphical object by index
   CArrayObj        *GetListDependentObj(void)        { return &this.m_list;           }
   CGStdGraphObj    *GetDependentObj(const int index) { return this.m_list.At(index);  }
//--- Return the name of the dependent object by index
   string            NameDependent(const int index);
//--- Add the dependent graphical object to the list
   bool              AddDependentObj(CGStdGraphObj *obj);

//--- Return the object of data on pivot points
   CLinkedPivotPoint*GetLinkedPivotPoint(void)        { return &this.m_linked_pivots;  }
   
//--- Add a new pivot point for calculating X and Y coordinates to the current object
   bool              AddNewLinkedPivotPointXY(const int pivot_prop_x,const int pivot_num_x,const int pivot_prop_y,const int pivot_num_y)
                       {
                        //--- If the current object is not bound to the base one, display the appropriate message and return 'false'
                        if(this.BaseObjectID()==0)
                          {
                           CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_EXT_NOT_ATACHED_TO_BASE);
                           return false;
                          }
                        //--- Return the result of adding a new connected pivot point from the CLinkedPivotPoint class to the current object
                        return this.m_linked_pivots.CreateNewLinkedPivotPoint(pivot_prop_x,pivot_num_x,pivot_prop_y,pivot_num_y);
                       }
//--- Add a new pivot point for calculating X and Y coordinates to the specified object
   bool              AddNewLinkedPivotPointXY(CGStdGraphObj *obj,const int pivot_prop_x,const int pivot_num_x,const int pivot_prop_y,const int pivot_num_y)
                       {
                        //--- If the current object is not an extended one, display the appropriate message and return 'false'
                        if(this.TypeGraphElement()!=GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)
                          {
                           CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_NOT_EXT_OBJ);
                           return false;
                          }
                        //--- If a zero pointer to the object is passed, return 'false'
                        if(obj==NULL)
                           return false;
                        //--- Return the result of adding a new connected pivot point from the CLinkedPivotPoint class to the specified object
                        return obj.AddNewLinkedPivotPointXY(pivot_prop_x,pivot_num_x,pivot_prop_y,pivot_num_y);
                       }
                       
//--- Add the new base object anchor point for calculating the X coordinate
   bool              AddNewBaseLinkedPivotX(const int index,const int pivot_prop,const int pivot_num)
                       {
                        //--- If the current object is not bound to the base one, display the appropriate message and return 'false'
                        if(this.BaseObjectID()==0)
                          {
                           CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_EXT_NOT_ATACHED_TO_BASE);
                           return false;
                          }
                        //--- Get the pointer to the necessary pivot point data
                        CPivotPointData *data=m_linked_pivots.GetBasePivotPointDataX(index); 
                        if(data==NULL)
                           return false;
                        //--- Return the result of adding a new anchor point to the current object
                        return data.AddNewBasePivotPoint(DFUN,pivot_prop,pivot_num);
                       }
//--- Add the new base object anchor point for calculating the Y coordinate
   bool              AddNewBaseLinkedPivotY(const int index,const int pivot_prop,const int pivot_num)
                       {
                        //--- If the current object is not bound to the base one, display the appropriate message and return 'false'
                        if(this.BaseObjectID()==0)
                          {
                           CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_EXT_NOT_ATACHED_TO_BASE);
                           return false;
                          }
                        //--- Get the pointer to the necessary pivot point data
                        CPivotPointData *data=m_linked_pivots.GetBasePivotPointDataY(index); 
                        if(data==NULL)
                           return false;
                        //--- Return the result of adding a new anchor point to the current object
                        return data.AddNewBasePivotPoint(DFUN,pivot_prop,pivot_num);
                       }

//--- Add the new base object anchor point for calculating the X coordinate to the specified subordinate graphical object
   bool              AddNewBaseLinkedPivotX(CGStdGraphObj *obj,const int index,const int pivot_prop,const int pivot_num)
                       {
                        //--- If the current object is not an extended one, display the appropriate message and return 'false'
                        if(this.TypeGraphElement()!=GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)
                          {
                           CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_NOT_EXT_OBJ);
                           return false;
                          }
                        //--- If a zero pointer to the object is passed, return 'false'
                        if(obj==NULL)
                           return false;
                        //--- Return the result of adding a new anchor point to the specified object
                        return obj.AddNewBaseLinkedPivotX(index,pivot_prop,pivot_num);
                       }
//--- Add the new base object anchor point for calculating the Y coordinate to the specified subordinate graphical object
   bool              AddNewBaseLinkedPivotY(CGStdGraphObj *obj,const int index,const int pivot_prop,const int pivot_num)
                       {
                        //--- If the current object is not an extended one, display the appropriate message and return 'false'
                        if(this.TypeGraphElement()!=GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)
                          {
                           CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_NOT_EXT_OBJ);
                           return false;
                          }
                        //--- If a zero pointer to the object is passed, return 'false'
                        if(obj==NULL)
                           return false;
                        //--- Return the result of adding a new anchor point to the specified object
                        return obj.AddNewBaseLinkedPivotY(index,pivot_prop,pivot_num);
                       }

//--- Return the number of base object pivot points for calculating the (1) X and (2) Y coordinate in the current object
   int               GetBasePivotsNumX(const int index)           { return this.m_linked_pivots.GetBasePivotsNumX(index);  }
   int               GetBasePivotsNumY(const int index)           { return this.m_linked_pivots.GetBasePivotsNumY(index);  }
//--- Return the number of base object pivot points for calculating the (1) X and (2) Y coordinate in the specified object
   int               GetBasePivotsNumX(CGStdGraphObj *obj,const int index) const { return(obj!=NULL ? obj.GetBasePivotsNumX(index): 0); }
   int               GetBasePivotsNumY(CGStdGraphObj *obj,const int index) const { return(obj!=NULL ? obj.GetBasePivotsNumY(index): 0); }
//--- Return the number of base object pivot points for calculating the coordinates in the (1) current (2) object
   int               GetLinkedPivotsNum(void)               const { return this.m_linked_pivots.GetNumLinkedPivotPoints(); }
   int               GetLinkedPivotsNum(CGStdGraphObj *obj) const { return(obj!=NULL ? obj.GetLinkedPivotsNum() : 0);      }
   
//--- Default constructor
                     CGStdGraphObj(){ this.m_type=OBJECT_DE_TYPE_GSTD_OBJ; this.m_species=WRONG_VALUE; }
//--- Destructor
                    ~CGStdGraphObj()
                       {
                        if(this.Prop!=NULL)
                           delete this.Prop;
                       }
protected:

Todos os métodos retornam o resultado da chamada dos métodos homônimos das classes discutidas acima e só permitem manusear esses objetos. Alguns métodos permanecem comentados no código e não precisam ser explicados. Em qualquer caso, você sempre pode colocar perguntas na discussão do artigo.

Agora passaremos o tipo de elemento gráfico criado ao construtor paramétrico protegido:

//--- Default constructor
                     CGStdGraphObj(){ this.m_type=OBJECT_DE_TYPE_GSTD_OBJ; this.m_species=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_ELEMENT_TYPE elm_type,
                                   const ENUM_GRAPH_OBJ_BELONG belong,
                                   const ENUM_GRAPH_OBJ_SPECIES species,
                                   const long chart_id, const int pivots,
                                   const string name);
                     
public:

Ao criar um objeto, podemos agora especificar que um objeto gráfico padrão estendido está sendo criado passando o valor GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED da enumeração ENUM_GRAPH_ELEMENT_TYPE como o tipo de elemento, o que é feito nos construtores de suas classes herdeiras.

No bloco de métodos para acesso simplificado e definição de propriedades de objeto gráfico, vamos escrever métodos para trabalhar com o identificador do objeto de base e métodos para trabalhar com o nome do objeto de base:

public:
//+--------------------------------------------------------------------+ 
//|Methods of simplified access and setting graphical object properties|
//+--------------------------------------------------------------------+
//--- Object index in the list
   int               Number(void)                  const { return (int)this.GetProperty(GRAPH_OBJ_PROP_NUM,0);                            }
   void              SetNumber(const int number)         { this.SetProperty(GRAPH_OBJ_PROP_NUM,0,number);                                 }
//--- Flag of storing the change history
   bool              AllowChangeHistory(void)      const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_CHANGE_HISTORY,0);                }
   void              SetAllowChangeMemory(const bool flag){ this.SetProperty(GRAPH_OBJ_PROP_CHANGE_HISTORY,0,flag);                       }
//--- Object ID
   long              ObjectID(void)                const { return this.GetProperty(GRAPH_OBJ_PROP_ID,0);                                  }
   void              SetObjectID(const long obj_id)
                       {
                        CGBaseObj::SetObjectID(obj_id);
                        this.SetProperty(GRAPH_OBJ_PROP_ID,0,obj_id);
                        this.SetPropertyPrev(GRAPH_OBJ_PROP_ID,0,obj_id);
                       }
//--- Base object ID
   long              BaseObjectID(void)            const { return this.GetProperty(GRAPH_OBJ_PROP_BASE_ID,0);                                  }
   void              SetBaseObjectID(const long obj_id)
                       {
                        this.SetProperty(GRAPH_OBJ_PROP_BASE_ID,0,obj_id);
                        this.SetPropertyPrev(GRAPH_OBJ_PROP_BASE_ID,0,obj_id);
                       }
//--- Graphical object type
   ENUM_OBJECT       GraphObjectType(void)         const { return (ENUM_OBJECT)this.GetProperty(GRAPH_OBJ_PROP_TYPE,0);                   }
   void              SetGraphObjectType(const ENUM_OBJECT obj_type)
                       {
                        CGBaseObj::SetTypeGraphObject(obj_type);
                        this.SetProperty(GRAPH_OBJ_PROP_TYPE,0,obj_type);
                       }

...

//--- Object name
   string            Name(void)                    const { return this.GetProperty(GRAPH_OBJ_PROP_NAME,0);                                }
   bool              SetName(const string name)
                       {
                        if(CGBaseObj::Name()==name)
                           return true;
                        if(CGBaseObj::Name()=="")
                          {
                           CGBaseObj::SetName(name);
                           this.SetProperty(GRAPH_OBJ_PROP_NAME,0,name);
                           return true;
                          }
                        else
                          {
                           if(!::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_NAME,name))
                              return false;
                           CGBaseObj::SetName(name);
                           this.SetProperty(GRAPH_OBJ_PROP_NAME,0,name);
                           return true;
                          }
                       }
//--- Base object name
   string            BaseName(void)                const { return this.GetProperty(GRAPH_OBJ_PROP_BASE_NAME,0);                           }
   bool              SetBaseName(const string name)
                       {
                        this.SetProperty(GRAPH_OBJ_PROP_BASE_NAME,0,name);
                        return true;
                       }
//--- Object description (text contained in the object)

Os métodos permitem definir o identificador e o nome do objeto de base nas propriedades do objeto, bem como retornar estes dados.

Em seguida, vamos inserir uma declaração do método que retorna a descrição do número do objeto na lista de objetos gráficos vinculados:

//--- Return the flags indicating object visibility on timeframes
   bool              IsVisibleOnTimeframeM1(void)  const { return IsVisibleOnTimeframe(PERIOD_M1, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM2(void)  const { return IsVisibleOnTimeframe(PERIOD_M2, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM3(void)  const { return IsVisibleOnTimeframe(PERIOD_M3, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM4(void)  const { return IsVisibleOnTimeframe(PERIOD_M4, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM5(void)  const { return IsVisibleOnTimeframe(PERIOD_M5, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM6(void)  const { return IsVisibleOnTimeframe(PERIOD_M6, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM10(void) const { return IsVisibleOnTimeframe(PERIOD_M10,(int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM12(void) const { return IsVisibleOnTimeframe(PERIOD_M12,(int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM15(void) const { return IsVisibleOnTimeframe(PERIOD_M15,(int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM20(void) const { return IsVisibleOnTimeframe(PERIOD_M20,(int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeM30(void) const { return IsVisibleOnTimeframe(PERIOD_M30,(int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeH1(void)  const { return IsVisibleOnTimeframe(PERIOD_H1, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeH2(void)  const { return IsVisibleOnTimeframe(PERIOD_H2, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeH3(void)  const { return IsVisibleOnTimeframe(PERIOD_H3, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeH4(void)  const { return IsVisibleOnTimeframe(PERIOD_H4, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeH6(void)  const { return IsVisibleOnTimeframe(PERIOD_H6, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeH8(void)  const { return IsVisibleOnTimeframe(PERIOD_H8, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeH12(void) const { return IsVisibleOnTimeframe(PERIOD_H12,(int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeD1(void)  const { return IsVisibleOnTimeframe(PERIOD_D1, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeW1(void)  const { return IsVisibleOnTimeframe(PERIOD_W1, (int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }
   bool              IsVisibleOnTimeframeMN1(void) const { return IsVisibleOnTimeframe(PERIOD_MN1,(int)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,0)); }

//--- Return the description of the (1) object visibility on timeframes and (2) the index in the composite graphical object list
   string            VisibleOnTimeframeDescription(void);
   string            NumberDescription(void);

//--- Re-write all graphical object properties


No construtor paramétrico protegido vamos escrever a variável com o tipo de elemento gráfico e registrar o valor passado a ele no objeto gráfico de base, bem como definir os valores padrão das novas propriedades do objeto gráfico criado acima:

//+------------------------------------------------------------------+
//| Protected parametric constructor                                 |
//+------------------------------------------------------------------+
CGStdGraphObj::CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type,
                             const ENUM_GRAPH_ELEMENT_TYPE elm_type,
                             const ENUM_GRAPH_OBJ_BELONG belong,
                             const ENUM_GRAPH_OBJ_SPECIES species,
                             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
   this.m_pivots=pivots;
   int levels=(int)::ObjectGetInteger(chart_id,name,OBJPROP_LEVELS);

//--- Set the property array dimensionalities according to the number of pivot points and levels
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_TIME,this.m_pivots);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_PRICE,this.m_pivots);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELCOLOR,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELSTYLE,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELWIDTH,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELVALUE,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELTEXT,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_BMPFILE,2);
   
//--- Set the object (1) type, type of graphical (2) object, (3) element, (4) subwindow affiliation and (5) index, as well as (6) chart symbol Digits
   this.m_type=obj_type;
   this.SetName(name);
   CGBaseObj::SetChartID(chart_id);
   CGBaseObj::SetTypeGraphObject(CGBaseObj::GraphObjectType(obj_type));
   CGBaseObj::SetTypeElement(elm_type);
   CGBaseObj::SetBelong(belong);
   CGBaseObj::SetSpecies(species);
   CGBaseObj::SetSubwindow(chart_id,name);
   CGBaseObj::SetDigits((int)::SymbolInfoInteger(::ChartSymbol(chart_id),SYMBOL_DIGITS));
   
//--- Save the integer properties inherent in all graphical objects but not present in the current one
   this.SetProperty(GRAPH_OBJ_PROP_CHART_ID,0,CGBaseObj::ChartID());                // Chart ID
   this.SetProperty(GRAPH_OBJ_PROP_WND_NUM,0,CGBaseObj::SubWindow());               // Chart subwindow index
   this.SetProperty(GRAPH_OBJ_PROP_TYPE,0,CGBaseObj::TypeGraphObject());            // Graphical object type (ENUM_OBJECT)
   this.SetProperty(GRAPH_OBJ_PROP_ELEMENT_TYPE,0,CGBaseObj::TypeGraphElement());   // Graphical element type (ENUM_GRAPH_ELEMENT_TYPE)
   this.SetProperty(GRAPH_OBJ_PROP_BELONG,0,CGBaseObj::Belong());                   // Graphical object affiliation
   this.SetProperty(GRAPH_OBJ_PROP_SPECIES,0,CGBaseObj::Species());                 // Graphical object species
   this.SetProperty(GRAPH_OBJ_PROP_GROUP,0,0);                                      // Graphical object group
   this.SetProperty(GRAPH_OBJ_PROP_ID,0,0);                                         // Object ID
   this.SetProperty(GRAPH_OBJ_PROP_BASE_ID,0,0);                                    // Base object ID
   this.SetProperty(GRAPH_OBJ_PROP_NUM,0,0);                                        // Object index in the list
   this.SetProperty(GRAPH_OBJ_PROP_CHANGE_HISTORY,0,false);                         // Flag of storing the change history
   this.SetProperty(GRAPH_OBJ_PROP_BASE_NAME,0,this.Name());                        // Base object name
   
//--- Save the properties inherent in all graphical objects and present in a graphical object
   this.PropertiesRefresh();
   
//--- Save basic properties in the parent object
   this.m_create_time=(datetime)this.GetProperty(GRAPH_OBJ_PROP_CREATETIME,0);
   this.m_back=(bool)this.GetProperty(GRAPH_OBJ_PROP_BACK,0);
   this.m_selected=(bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTED,0);
   this.m_selectable=(bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTABLE,0);
   this.m_hidden=(bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN,0);

//--- Save the current properties to the previous ones
   this.PropertiesCopyToPrevData();
  }
//+-------------------------------------------------------------------+

Por padrão, o identificador do objeto base é zero, o que significa que ele não existe, e o nome do objeto base é igual ao nome do objeto gráfico atual, ou seja, o objeto aponta para si mesmo, o que também significa que ele não está vinculado ao objeto de base.

No método que retorna a descrição da propriedade do objeto inteiro escrevemos a exibição da descrição do identificador do objeto de base, e no bloco que retorna a descrição do número do objeto na lista de objetos vinculados escrevemos a chamada do método que irá se encarregar disso (escreveremos mais adiante):

//+------------------------------------------------------------------+
//| Return description of object's integer property                  |
//+------------------------------------------------------------------+
string CGStdGraphObj::GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_INTEGER property)
  {
   return
     (
      property==GRAPH_OBJ_PROP_ID         ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ID)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_BASE_ID    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BASE_ID)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_TYPE       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_TYPE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.TypeDescription()
         )  :
      property==GRAPH_OBJ_PROP_ELEMENT_TYPE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ELEMENT_TYPE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+CGBaseObj::TypeElementDescription()
         )  :
      property==GRAPH_OBJ_PROP_SPECIES    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+CGBaseObj::SpeciesDescription()
         )  :
      property==GRAPH_OBJ_PROP_GROUP    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_GROUP)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(CGBaseObj::Group()>0 ? (string)this.GetProperty(property,0) : CMessage::Text(MSG_LIB_PROP_EMPTY))
         )  :
      property==GRAPH_OBJ_PROP_BELONG     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BELONG)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+CGBaseObj::BelongDescription()
         )  :
      property==GRAPH_OBJ_PROP_CHART_ID   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_ID)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_WND_NUM    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_WND_NUM)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_CHANGE_HISTORY ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHANGE_MEMORY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_CREATETIME   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CREATETIME)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::TimeToString(this.GetProperty(property,0),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==GRAPH_OBJ_PROP_TIMEFRAMES ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_TIMEFRAMES)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.VisibleOnTimeframeDescription()
         )  :
      property==GRAPH_OBJ_PROP_BACK       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BACK)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.IsBack() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_ZORDER     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ZORDER)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_HIDDEN     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_HIDDEN)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.IsHidden() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_SELECTED   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SELECTED)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.IsSelected() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_SELECTABLE ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SELECTABLE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.IsSelectable() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_NUM        ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_NUM)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.NumberDescription()
         )  :
      property==GRAPH_OBJ_PROP_TIME       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_TIME)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+"\n"+this.TimesDescription()
         )  :
      property==GRAPH_OBJ_PROP_COLOR      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_COLOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property,0),true)
         )  :
      property==GRAPH_OBJ_PROP_STYLE      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_STYLE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+LineStyleDescription((ENUM_LINE_STYLE)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_WIDTH     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_WIDTH)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_FILL       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_FILL)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_READONLY   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_READONLY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_LEVELS     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELS)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_LEVELCOLOR ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELCOLOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ":\n"+this.LevelsColorDescription()
         )  :
      property==GRAPH_OBJ_PROP_LEVELSTYLE ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELSTYLE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ":\n"+this.LevelsStyleDescription()
         )  :
      property==GRAPH_OBJ_PROP_LEVELWIDTH ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELWIDTH)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ":\n"+this.LevelsWidthDescription()
         )  :
      property==GRAPH_OBJ_PROP_ALIGN      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ALIGN)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+AlignModeDescription((ENUM_ALIGN_MODE)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_FONTSIZE   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_FONTSIZE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_RAY_LEFT   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_RAY_LEFT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_RAY_RIGHT  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_RAY_RIGHT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_RAY        ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_RAY)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_ELLIPSE    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ELLIPSE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_ARROWCODE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ARROWCODE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_ANCHOR     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_ANCHOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.AnchorDescription()
         )  :
      property==GRAPH_OBJ_PROP_XDISTANCE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_XDISTANCE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_YDISTANCE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_YDISTANCE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_DIRECTION  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_DIRECTION)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+GannDirectDescription((ENUM_GANN_DIRECTION)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_DEGREE     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_DEGREE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+ElliotWaveDegreeDescription((ENUM_ELLIOT_WAVE_DEGREE)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_DRAWLINES  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_DRAWLINES)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_STATE      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_STATE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_BUTTON_STATE_PRESSED) : CMessage::Text(MSG_LIB_TEXT_BUTTON_STATE_DEPRESSED))
         )  :
      property==GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID  ?  CMessage::Text(MSG_CHART_OBJ_ID)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_CHART_OBJ_PERIOD ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_OBJ_PERIOD)+
         (!this.SupportProperty(property)       ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+TimeframeDescription((ENUM_TIMEFRAMES)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE)+
         (!this.SupportProperty(property)             ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE)+
         (!this.SupportProperty(property)             ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
         )  :
      property==GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE)+
         (!this.SupportProperty(property)             ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_XSIZE      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_XSIZE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_YSIZE      ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_YSIZE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_XOFFSET    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_XOFFSET)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_YOFFSET    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_YOFFSET)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property,0)
         )  :
      property==GRAPH_OBJ_PROP_BGCOLOR    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BGCOLOR)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property,0),true)
         )  :
      property==GRAPH_OBJ_PROP_CORNER     ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_CORNER)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+BaseCornerDescription((ENUM_BASE_CORNER)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_BORDER_TYPE   ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BORDER_TYPE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+BorderTypeDescription((ENUM_BORDER_TYPE)this.GetProperty(property,0))
         )  :
      property==GRAPH_OBJ_PROP_BORDER_COLOR  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BORDER_COLOR)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::ColorToString((color)this.GetProperty(property,0),true)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+


No método que retorna a descrição da propriedade do objeto de string, vamos escrever um bloco de código para retornar a descrição do nome do objeto de base:

//+------------------------------------------------------------------+
//| Return description of object's string property                   |
//+------------------------------------------------------------------+
string CGStdGraphObj::GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_STRING property)
  {
   return
     (
      property==GRAPH_OBJ_PROP_NAME       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_NAME)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+"\""+this.GetProperty(property,0)+"\""
         )  :
      property==GRAPH_OBJ_PROP_BASE_NAME  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BASE_NAME)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+"\""+this.GetProperty(property,0)+"\""
         )  :
      property==GRAPH_OBJ_PROP_TEXT       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_TEXT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0)=="" ? CMessage::Text(MSG_LIB_PROP_EMPTY) : "\""+this.GetProperty(property,0)+"\"")
         )  :
      property==GRAPH_OBJ_PROP_TOOLTIP    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_TOOLTIP)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(this.GetProperty(property,0)=="" ? CMessage::Text(MSG_LIB_PROP_AUTO) : 
                this.GetProperty(property,0)=="\n" ? CMessage::Text(MSG_LIB_PROP_EMPTY) :
                "\""+this.GetProperty(property,0)+"\"")
         )  :
      property==GRAPH_OBJ_PROP_LEVELTEXT  ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELTEXT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ":\n"+this.LevelsTextDescription()
         )  :
      property==GRAPH_OBJ_PROP_FONT       ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_FONT)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+"\""+this.GetProperty(property,0)+"\""
         )  :
      property==GRAPH_OBJ_PROP_BMPFILE    ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_BMPFILE)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ":\n"+this.BMPFilesDescription()
         )  :
      property==GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL ?  CMessage::Text(MSG_GRAPH_OBJ_PROP_SYMBOL)+
         (!this.SupportProperty(property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.GetProperty(property,0)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+


Método que retorna a descrição do número do objeto gráfico na lista de objetos do objeto gráfico composto:

//+------------------------------------------------------------------+
//| Return the description of the graphical object index             |
//| in the list of objects of a composite graphical object           |
//+------------------------------------------------------------------+
string CGStdGraphObj::NumberDescription(void)
  {
   if(CGBaseObj::TypeGraphElement()==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)
     {
      if(this.Number()==0)
         return CMessage::Text(MSG_GRAPH_OBJ_PROP_NUM_EXT_BASE_OBJ);
     }
   return (string)this.Number();
  }
//+------------------------------------------------------------------+

Quanto ao objeto gráfico padrão estendido, verificamos seu número, e se for igual a zero é porque será impresso o texto "Objeto básico do objeto gráfico estendido". Em todos os outros casos, o número é exibido como uma string escrita nas propriedades do objeto.


Método que retorna o nome do objeto gráfico subordinado por índice:

//+------------------------------------------------------------------+
//| Return the name of a subordinate graphical object by index       |
//+------------------------------------------------------------------+
string CGStdGraphObj::NameDependent(const int index)
  {
   CGStdGraphObj *obj=this.GetDependentObj(index);
   return(obj!=NULL ? obj.Name() : "");
  }
//+------------------------------------------------------------------+

Recupera um ponteiro para o objeto gráfico vinculado na lista pelo índice especificado e retornamos seu nome. Se o objeto não pôde ser obtido, devolvemos uma string vazia.

Método que acrescenta o objeto gráfico padrão subordinado à lista:

//+------------------------------------------------------------------+
//| Add a subordinate standard graphical object to the list          |
//+------------------------------------------------------------------+
bool CGStdGraphObj::AddDependentObj(CGStdGraphObj *obj)
  {
   //--- If the current object is not an extended one, inform of that and return 'false'
   if(this.TypeGraphElement()!=GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)
     {
      CMessage::ToLog(MSG_GRAPH_OBJ_NOT_EXT_OBJ);
      return false;
     }
   //--- If failed to add the pointer to the passed object into the list, inform of that and return 'false'
   if(!this.m_list.Add(obj))
     {
      CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_ADD_DEP_EXT_OBJ_TO_LIST);
      return false;
     }
   //--- Object added to the list - set its number in the list,
   //--- name and ID of the current object as the base one
   obj.SetNumber(this.m_list.Total()-1);
   obj.SetBaseName(this.Name());
   obj.SetBaseObjectID(this.ObjectID());
   return true;
  }
//+------------------------------------------------------------------+

Em geral, qualquer objeto gráfico que não seja estendido não pode trabalhar com subordinados, por isso devemos verificar todos esses métodos que recebem um ponteiro para um objeto subordinado que é suposto ser subordinado e modificado, já se o objeto não for estendido, será exibida uma mensagem e o método retornará false. E da mesma forma para o objeto atual, nos métodos para manusear parâmetros do objeto subordinado é verificado se o objeto é subordinado (se está vinculado ao objeto de base), e se não está assim, então o objeto não é subordinado, e a mesma mensagem será mostrada e retornado false.

A razão para isso é que qualquer objeto gráfico pode ser tanto de base quanto subordinado, mesmo em simultâneo. Por esta razão, ele tem métodos para manusear objetos subordinados próprios, objetos de base, e mudar suas propriedades externamente, como objeto subordinado.


Modificamos a classe-coleção de elementos gráficos no arquivo \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh.

Na seção pública da classe de gerenciamento de objetos gráficos, adicionamos um sinalizador de criação de objeto gráfico estendido ao método de criação do novo objeto da classe de objetos gráficos padrão:

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 (or extended) graphical object
   CGStdGraphObj    *CreateNewGraphObj(const ENUM_OBJECT obj_type,const string name,const bool extended);
//--- Return the list of newly added objects

No mesmo método de classe que verifica objetos no gráfico, inserimos o valor false ao criar o novo objeto:

//+------------------------------------------------------------------+
//| CChartObjectsControl: Check objects on a chart                   |
//+------------------------------------------------------------------+
void CChartObjectsControl::Refresh(void)
  {
//--- Clear the list of newly added objects
   this.m_list_new_graph_obj.Clear();
//--- Calculate the number of new 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 an object is added to the chart

   if(this.m_delta_graph_obj>0)
     {
      //--- Create the list of added graphical objects
      for(int i=0;i<this.m_delta_graph_obj;i++)
        {
         //--- Get the name of the last added object (if a single new object is added),
         //--- or a name from the terminal object list by index (if several objects have been added)
         string name=(this.m_delta_graph_obj==1 ? this.LastAddedGraphObjName() : ::ObjectName(this.m_chart_id,i));
         //--- Handle only non-programmatically created objects
         if(name==NULL || ::StringFind(name,this.m_name_program)>WRONG_VALUE)
            continue;
         //--- 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,false);
         //--- If failed to create an object, inform of that and move on to the new iteration
         if(obj==NULL)
           {
            CMessage::ToLog(DFUN,MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ);
            continue;
           }
         //--- Set the object affiliation and add the created object to the list of new objects
         obj.SetBelong(GRAPH_OBJ_BELONG_NO_PROGRAM); 
         //--- If failed to add the object to the list, inform of that, remove the object and move on to the next iteration
         if(!this.m_list_new_graph_obj.Add(obj))
           {
            CMessage::ToLog(DFUN_ERR_LINE,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST);
            delete obj;
            continue;
           }
        }
      //--- Send events to the control program chart from the created list
      for(int i=0;i<this.m_list_new_graph_obj.Total();i++)
        {
         CGStdGraphObj *obj=this.m_list_new_graph_obj.At(i);
         if(obj==NULL)
            continue;
         //--- Send an event to the control program chart
         ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_CREATE,this.ChartID(),obj.TimeCreate(),obj.Name());
        }
     }
//--- 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;
  }
//+------------------------------------------------------------------+

Como este método rastreia a criação de objetos gráficos no gráfico manualmente, o objeto da classe do objeto gráfico deve ser exclusivamente não estendido. Por isso, passamos o sinalizador criado false ao método, assim criamos um objeto gráfico padrão normal.

Vamos adicionar o sinalizador de objeto gráfico estendido aos parâmetros de entrada do método, que cria um novo objeto do objeto gráfico padrão, e passamos este sinalizador a todas as strings de criação de novo objeto da classe de objeto gráfico padrão:

//+------------------------------------------------------------------+
//| CChartObjectsControl:                                            |
//| Create a new standard graphical object                           |
//+------------------------------------------------------------------+
CGStdGraphObj *CChartObjectsControl::CreateNewGraphObj(const ENUM_OBJECT obj_type,const string name,const bool extended)
  {

   switch((int)obj_type)
     {
      //--- Lines
      case OBJ_VLINE             : return new CGStdVLineObj(this.ChartID(),name,extended);
      case OBJ_HLINE             : return new CGStdHLineObj(this.ChartID(),name,extended);
      case OBJ_TREND             : return new CGStdTrendObj(this.ChartID(),name,extended);
      case OBJ_TRENDBYANGLE      : return new CGStdTrendByAngleObj(this.ChartID(),name,extended);
      case OBJ_CYCLES            : return new CGStdCyclesObj(this.ChartID(),name,extended);
      case OBJ_ARROWED_LINE      : return new CGStdArrowedLineObj(this.ChartID(),name,extended);
      //--- Channels
      case OBJ_CHANNEL           : return new CGStdChannelObj(this.ChartID(),name,extended);
      case OBJ_STDDEVCHANNEL     : return new CGStdStdDevChannelObj(this.ChartID(),name,extended);
      case OBJ_REGRESSION        : return new CGStdRegressionObj(this.ChartID(),name,extended);
      case OBJ_PITCHFORK         : return new CGStdPitchforkObj(this.ChartID(),name,extended);
      //--- Gann
      case OBJ_GANNLINE          : return new CGStdGannLineObj(this.ChartID(),name,extended);
      case OBJ_GANNFAN           : return new CGStdGannFanObj(this.ChartID(),name,extended);
      case OBJ_GANNGRID          : return new CGStdGannGridObj(this.ChartID(),name,extended);
      //--- Fibo
      case OBJ_FIBO              : return new CGStdFiboObj(this.ChartID(),name,extended);
      case OBJ_FIBOTIMES         : return new CGStdFiboTimesObj(this.ChartID(),name,extended);
      case OBJ_FIBOFAN           : return new CGStdFiboFanObj(this.ChartID(),name,extended);
      case OBJ_FIBOARC           : return new CGStdFiboArcObj(this.ChartID(),name,extended);
      case OBJ_FIBOCHANNEL       : return new CGStdFiboChannelObj(this.ChartID(),name,extended);
      case OBJ_EXPANSION         : return new CGStdExpansionObj(this.ChartID(),name,extended);
      //--- Elliott
      case OBJ_ELLIOTWAVE5       : return new CGStdElliotWave5Obj(this.ChartID(),name,extended);
      case OBJ_ELLIOTWAVE3       : return new CGStdElliotWave3Obj(this.ChartID(),name,extended);
      //--- Shapes
      case OBJ_RECTANGLE         : return new CGStdRectangleObj(this.ChartID(),name,extended);
      case OBJ_TRIANGLE          : return new CGStdTriangleObj(this.ChartID(),name,extended);
      case OBJ_ELLIPSE           : return new CGStdEllipseObj(this.ChartID(),name,extended);
      //--- Arrows
      case OBJ_ARROW_THUMB_UP    : return new CGStdArrowThumbUpObj(this.ChartID(),name,extended);
      case OBJ_ARROW_THUMB_DOWN  : return new CGStdArrowThumbDownObj(this.ChartID(),name,extended);
      case OBJ_ARROW_UP          : return new CGStdArrowUpObj(this.ChartID(),name,extended);
      case OBJ_ARROW_DOWN        : return new CGStdArrowDownObj(this.ChartID(),name,extended);
      case OBJ_ARROW_STOP        : return new CGStdArrowStopObj(this.ChartID(),name,extended);
      case OBJ_ARROW_CHECK       : return new CGStdArrowCheckObj(this.ChartID(),name,extended);
      case OBJ_ARROW_LEFT_PRICE  : return new CGStdArrowLeftPriceObj(this.ChartID(),name,extended);
      case OBJ_ARROW_RIGHT_PRICE : return new CGStdArrowRightPriceObj(this.ChartID(),name,extended);
      case OBJ_ARROW_BUY         : return new CGStdArrowBuyObj(this.ChartID(),name,extended);
      case OBJ_ARROW_SELL        : return new CGStdArrowSellObj(this.ChartID(),name,extended);
      case OBJ_ARROW             : return new CGStdArrowObj(this.ChartID(),name,extended);
      //--- Graphical objects
      case OBJ_TEXT              : return new CGStdTextObj(this.ChartID(),name,extended);
      case OBJ_LABEL             : return new CGStdLabelObj(this.ChartID(),name,extended);
      case OBJ_BUTTON            : return new CGStdButtonObj(this.ChartID(),name,extended);
      case OBJ_CHART             : return new CGStdChartObj(this.ChartID(),name,extended);
      case OBJ_BITMAP            : return new CGStdBitmapObj(this.ChartID(),name,extended);
      case OBJ_BITMAP_LABEL      : return new CGStdBitmapLabelObj(this.ChartID(),name,extended);
      case OBJ_EDIT              : return new CGStdEditObj(this.ChartID(),name,extended);
      case OBJ_EVENT             : return new CGStdEventObj(this.ChartID(),name,extended);
      case OBJ_RECTANGLE_LABEL   : return new CGStdRectangleLabelObj(this.ChartID(),name,extended);
      default                    : return NULL;
     }
  }
//+------------------------------------------------------------------+

Agora quando criamos um objeto da classe de objeto gráfico padrão, podemos especificar se o objeto deve ser construído como normal ou estendido. Entretanto, podemos converter um objeto gráfico comum em um objeto ampliado a qualquer momento e vice-versa.

O método da seção privada da classe-coleção de elementos gráficos que cria um novo objeto gráfico padrão e retorna o nome do objeto criado deve ser removido da listagem da classe (vamos movê-lo para o arquivo de funções de serviço para que possamos sempre criar qualquer objeto gráfico, independentemente de ser construído a partir da classe-coleção ou de outras classes ou do próprio programa):

//+------------------------------------------------------------------+
//| Collection of graphical objects                                  |
//+------------------------------------------------------------------+
#resource "\\"+PATH_TO_EVENT_CTRL_IND;          // Indicator for controlling graphical object events packed into the program resources
class CGraphElementsCollection : public CBaseObj
  {
private:
   CArrayObj         m_list_charts_control;     // List of chart management objects
   CListObj          m_list_all_canv_elm_obj;   // List of all graphical elements on canvas
   CListObj          m_list_all_graph_obj;      // List of all graphical objects
   CArrayObj         m_list_deleted_obj;        // List of removed graphical objects
   bool              m_is_graph_obj_event;      // Event flag in the list of graphical objects
   int               m_total_objects;           // Number of graphical objects
   int               m_delta_graph_obj;         // Difference in the number of graphical objects compared to the previous check
   
//--- Return the flag indicating the graphical element class object presence in the collection list of graphical elements
   bool              IsPresentGraphElmInList(const int id,const ENUM_GRAPH_ELEMENT_TYPE type_obj);
//--- Return the flag indicating the presence of the graphical object class in the graphical object collection list
   bool              IsPresentGraphObjInList(const long chart_id,const string name);
//--- Return the flag indicating the presence of a graphical object on a chart by name
   bool              IsPresentGraphObjOnChart(const long chart_id,const string name);
//--- Return the pointer to the object of managing objects of the specified chart
   CChartObjectsControl *GetChartObjectCtrlObj(const long chart_id);
//--- Create a new object of managing graphical objects of a specified chart and add it to the list
   CChartObjectsControl *CreateChartObjectCtrlObj(const long chart_id);
//--- Update the list of graphical objects by chart ID
   CChartObjectsControl *RefreshByChartID(const long chart_id);
//--- Check if the chart window is present
   bool              IsPresentChartWindow(const long chart_id);
//--- Handle removing the chart window
   void              RefreshForExtraObjects(void);
//--- 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
   bool              AddGraphObjToCollection(const string source,CChartObjectsControl *obj_control);
//--- Find an object present in the collection but not on a chart
   CGStdGraphObj    *FindMissingObj(const long chart_id);
   CGStdGraphObj    *FindMissingObj(const long chart_id,int &index);
//--- Find the graphical object present on a chart but not in the collection
   string            FindExtraObj(const long chart_id);
//--- Remove the graphical object class object from the graphical object collection list: (1) specified object, (2) by chart ID
   bool              DeleteGraphObjFromList(CGStdGraphObj *obj);
   void              DeleteGraphObjectsFromList(const long chart_id);
//--- Move the graphical object class object to the list of removed graphical objects: (1) specified object, (2) by index
   bool              MoveGraphObjToDeletedObjList(CGStdGraphObj *obj);
   bool              MoveGraphObjToDeletedObjList(const int index);
//--- Move all objects by chart ID to the list of removed graphical objects
   void              MoveGraphObjectsToDeletedObjList(const long chart_id);
//--- 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:


Na seção pública da classe escrevemos dois métodos que retornam uma lista de objetos gráficos padrão e estendidos:

//--- Return an (1) existing and (2) removed graphical object by chart name and ID
   CGStdGraphObj    *GetStdGraphObject(const string name,const long chart_id);
   CGStdGraphObj    *GetStdDelGraphObject(const string name,const long chart_id);
//--- Return the list of (1) chart management objects and (2) removed graphical objects
   CArrayObj        *GetListChartsControl(void)                                                          { return &this.m_list_charts_control;  }
   CArrayObj        *GetListDeletedObj(void)                                                             { return &this.m_list_deleted_obj;     }
//--- Return the list of (1) standard and (2) extended graphical objects
   CArrayObj        *GetListStdGraphObject(void)                              { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),GRAPH_OBJ_PROP_ELEMENT_TYPE,0,GRAPH_ELEMENT_TYPE_STANDARD,EQUAL);           }
   CArrayObj        *GetListStdGraphObjectExt(void)                           { return CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),GRAPH_OBJ_PROP_ELEMENT_TYPE,0,GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED,EQUAL);  }
//--- Return (1) the last removed graphical object and (2) the array size of graphical object properties

Os métodos simplesmente retornam um ponteiro para a lista criada ao filtrar a lista de objetos gráficos pelo tipo de propriedade do elemento gráfico.

No método privado que cria um novo objeto gráfico e retorna um ponteiro para objeto de controle de gráficos, agora vamos chamar o método para criar um objeto gráfico a partir do arquivo DELib.mqh (simplesmente excluímos "this." da string de chamada do método):

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(!CreateNewStdGraphObject(chart_id,name,type_object,subwindow,time1,price1,time2,price2,time3,price3,time4,price4,time5,price5))
                          {
                           ::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;
                       }


Em todos os métodos públicos para criação de objetos gráficos, fazemos o mesmo tipo de mudanças - o sinalizador do objeto estendido nos parâmetros de entrada do método e a transferência deste sinalizador para o método de criação de um novo objeto da classe do objeto gráfico padrão:

public:
//--- Create the "Vertical line" graphical object
   bool              CreateLineVertical(const long chart_id,const string name,const int subwindow,const bool extended,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,extended);
                        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,false);
                        obj.SetFlagSelected(true,false);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        //--- Return the result of adding the object to the list
                        return this.AddCreatedObjToList(DFUN,chart_id,nm,obj);
                       }

//--- Create the "Horizontal line" graphical object
   bool              CreateLineHorizontal(const long chart_id,const string name,const int subwindow,const bool extended,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,extended);

                        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,false);
                        obj.SetFlagSelected(true,false);
                        obj.SetObjectID(this.GetFreeGraphObjID(true));
                        obj.PropertiesCopyToPrevData();
                        //--- Return the result of adding the object to the list
                        return this.AddCreatedObjToList(DFUN,chart_id,nm,obj);
                       }
 
//--- Create the "Trend line" graphical object

Aqui vemos um exemplo de dois métodos em que foram feitas as mesmas mudanças. Os outros métodos são modificados de forma idêntica e podem ser vistos nos arquivos anexados ao artigo.

A listagem da implementação do método que cria um novo objeto gráfico padrão desta classe deve ser transferida desta classe para o final do arquivo de funções de serviço da biblioteca \MQL5\Include\DoEasy\Services\DELib.mqh:

//+------------------------------------------------------------------+
//| Create a new standard graphical object                           |
//+------------------------------------------------------------------+
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)
  {
   ::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;
     }
  }
//+------------------------------------------------------------------+


Agora editamos a classe principal da biblioteca CEngine no arquivo \MQL5\Include\DoEasy\Engine.mqh.

Quanto ao método que retorna o nome do programa, vamos renomeá-lo de Name() para ProgramName():

//--- Return event (1) milliseconds, (2) reason and (3) source from its 'long' value
   ushort               EventMSC(const long lparam)               const { return this.LongToUshortFromByte(lparam,0);         }
   ushort               EventReason(const long lparam)            const { return this.LongToUshortFromByte(lparam,1);         }
   ushort               EventSource(const long lparam)            const { return this.LongToUshortFromByte(lparam,2);         }

//--- Return the program name
   string               ProgramName(void)                         const { return this.m_name;                                 }

//--- Set the new (1) pause countdown start time and (2) pause in milliseconds

Como os nomes dos nossos objetos diferem dependendo de como o objeto gráfico foi criado (manual ou programaticamente), para poder pesquisar e selecionar objetos por nome do programa, vamos fazer algumas mudanças no método que retorna a classe do objeto de objeto gráfico padrão por nome e identificador de objeto gráfico:

//--- Return the class of the object of the standard graphical object by chart name and ID
   CGStdGraphObj       *GraphGetStdGraphObject(const string name,const long chart_id)
                          {
                           CGStdGraphObj *obj=this.m_graph_objects.GetStdGraphObject(name,chart_id);
                           if(obj==NULL)
                              obj=this.m_graph_objects.GetStdGraphObject(this.ProgramName()+"_"+name,chart_id);
                           return obj;
                          }

Se o objeto foi criado manualmente, a biblioteca não altera seu nome de forma alguma. Mas se criarmos um objeto gráfico de forma programática, os métodos de criação acrescentam o prefixo do nome do programa a seu nome. Por isso, se tivermos criado um objeto com um nome como "object_name" e tentar encontrá-lo, não teremos resultado, porque um prefixo do nome do programa foi adicionado ao nome, e agora o nome do objeto fica como "program_name_object_name". Com base nisso, o método primeiro procura pelo nome exato passado ao método, e depois, se nenhum objeto com esse nome for encontrado, tentamos encontrar um objeto com um prefixo contendo o nome do programa.

E vamos adicionar um método que retorna a classe do objeto gráfico padrão estendido por nome e ID do gráfico:

//--- Return the class of the object of the extended standard graphical object by chart name and ID
   CGStdGraphObj       *GraphGetStdGraphObjectExt(const string name,const long chart_id)
                          {
                           CArrayObj *list=this.m_graph_objects.GetListStdGraphObjectExt();
                           string nm=(::StringFind(name,this.ProgramName()+"_")==WRONG_VALUE ? this.ProgramName()+"_"+name : name);
                           list=CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_NAME,0,nm,EQUAL);
                           return(list!=NULL ? list.At(0) : NULL);
                          }

Aqui primeiro obtemos toda a lista dos objetos gráficos padrão estendidos, depois verificamos se o nome passado ao método contém uma string com o nome do programa e, se a string não for encontrada, adicionamos o nome do programa ao nome. Caso contrário, se o nome do programa já existe no nome procurado, usamos o nome passado para o método como está. Em seguida, obtemos o objeto com o nome que estamos procurando a partir da lista de objetos gráficos estendida, e retornamos o único objeto da lista se ele foi encontrado, ou NULL em caso de falha.

Em todos os métodos para criação de objetos gráficos, agora passamos o sinalizador do objeto estendido e passamos este sinalizador aos devidos métodos chamados da classe-coleção de elementos gráficos:

//--- Create the "Vertical line" graphical object
   bool              CreateLineVertical(const long chart_id,const string name,const int subwindow,const bool extended,const datetime time)
                       { return this.m_graph_objects.CreateLineVertical(chart_id,name,subwindow,extended,time); }
   bool              CreateLineVertical(const string name,const int subwindow,const bool extended,const datetime time)
                       { return this.m_graph_objects.CreateLineVertical(::ChartID(),name,subwindow,extended,time); }

//--- Create the "Horizontal line" graphical object
   bool              CreateLineHorizontal(const long chart_id,const string name,const int subwindow,const bool extended,const double price)
                       { return this.m_graph_objects.CreateLineHorizontal(chart_id,name,subwindow,extended,price); }
   bool              CreateLineHorizontal(const string name,const int subwindow,const bool extended,const double price)
                       { return this.m_graph_objects.CreateLineHorizontal(::ChartID(),name,subwindow,extended,price); }

Para todos os métodos de criação de objetos gráficos, estas modificações são absolutamente idênticas às quatro apresentadas acima. Portanto, não consideraremos os demais métodos, porque podem ser encontrados nos arquivos anexos ao artigo.

Isto completa a modificação da biblioteca e estamos quase prontos para criar objetos gráficos padrão estendidos.


Teste

Para realizar o teste, vamos pegar o Expert Advisor do artigo anterior e
vamos salvá-lo na nova pasta \MQL5\Experts\TestDoEasy\Part93\ com o novo nome TestDoEasyPart93.mq5.

O que vamos fazer? Vamos criar um objeto gráfico padrão "Linha de tendência" e colocar em suas bordas (em seus dois pontos de ancoragem) um objeto "Etiqueta de preço à esquerda" (à esquerda na posição 0) e um "Etiqueta de à direita" (à direita na posição 1). Vamos criar a linha de tendência clicando no gráfico com a tecla Ctrl pressionada, ao passo que o ponto esquerdo da linha será posicionado no local do clique no gráfico, já o direito, no preço de abertura da primeira barra do gráfico.
Imprimimos no log a descrição completa das propriedades da linha de tendência e os valores escritos nos rótulos de objetos/preços, em seus objetos das classes vinculadas aos pontos de ancoragem. Não testaremos todo o resto, pois ainda precisamos fazer alguns ajustes nas propriedades e funcionalidade do objeto para desenvolver completamente os objetos gráficos estendidos padrão.

Colocamos o seguinte código dentro do bloco gráfico de processamento de cliques no manipulador do Expert Advisor OnChartEvent():

   if(id==CHARTEVENT_CLICK)
     {
      if(!IsCtrlKeyPressed())
         return;
      //--- Get the chart click coordinates
      datetime time=0;
      double price=0;
      int sw=0;
      if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,sw,time,price))
        {
         //--- Get the right point coordinates for a trend line
         datetime time2=iTime(Symbol(),PERIOD_CURRENT,1);
         double price2=iOpen(Symbol(),PERIOD_CURRENT,1);
         
         //--- Create the "Trend line" object
         string name_base="TrendLineExt";
         engine.CreateLineTrend(name_base,0,true,time,price,time2,price2);
         //--- Get the object from the list of graphical objects by chart name and ID and pass its properties to the journal
         CGStdGraphObj *obj=engine.GraphGetStdGraphObjectExt(name_base,ChartID());
         obj.Print();
         
         //--- Create the "Left price label" object
         string name_dep="PriceLeft";
         engine.CreatePriceLabelLeft(name_dep,0,false,time,price);
         //--- Get the object from the list of graphical objects by chart name and ID and
         CGStdGraphObj *dep=engine.GraphGetStdGraphObject(name_dep,ChartID());
         //--- add it to the list of graphical objects bound to the "Trend line" object
         obj.AddDependentObj(dep);
         //--- Set its pivot point by X and Y axis to the trend line left point
         obj.AddNewLinkedPivotPointXY(dep,GRAPH_OBJ_PROP_TIME,0,GRAPH_OBJ_PROP_PRICE,0);
         //--- Send the number of pivot anchor points by both axes (one point per axis) to the journal
         Print(DFUN,"PriceLeft: Num linked coord X: ",dep.GetBasePivotsNumX(0),", Num linked coord Y: ",dep.GetBasePivotsNumY(0));
         
         //--- Create the "Right price label" object
         name_dep="PriceRight";
         engine.CreatePriceLabelRight(name_dep,0,false,time2,price2);
         //--- Get the object from the list of graphical objects by chart name and ID and
         dep=engine.GraphGetStdGraphObject(name_dep,ChartID());
         //--- add it to the list of graphical objects bound to the "Trend line" object
         obj.AddDependentObj(dep);
         //--- Set its pivot point by X and Y axis to the trend line right point
         obj.AddNewLinkedPivotPointXY(dep,GRAPH_OBJ_PROP_TIME,1,GRAPH_OBJ_PROP_PRICE,1);
         //--- Send the number of pivot anchor points by both axes (one point per axis) to the journal
         Print(DFUN,"PriceRight: Num linked coord X: ",dep.GetBasePivotsNumX(0),", Num linked coord Y: ",dep.GetBasePivotsNumY(0));
        }
     }

Aqui toda a lógica aparece comentada na listagem, por isso espero que não haja perguntas.

Compilamos o EA, o executamos no gráfico e clicamos em algum lugar longe da borda direita do gráfico.

Será traçada uma linha de tendência, com marcadores de preço ao longo de suas bordas:


As propriedades da linha de tendência e o número de pontos de ancoragem da mesma para o cálculo das coordenadas X e Y dois marcadores de preço serão impressas no log. Por ter especificado apenas um ponto de ancoragem para cada uma dos marcadores, isto será refletido no log.

2022.01.20 16:37:29.340 ============= The beginning of the parameter list (Trend Line) =============
2022.01.20 16:37:29.340 Object ID: 1
2022.01.20 16:37:29.340 Base object ID: 0
2022.01.20 16:37:29.340 Object type: Trend Line
2022.01.20 16:37:29.340 Graphic element type: Extended standard graphic object
2022.01.20 16:37:29.340 Object belongs to: The graphic object belongs to the program
2022.01.20 16:37:29.340 Object chart ID: 131733844391938630
2022.01.20 16:37:29.340 Chart subwindow number: 0
2022.01.20 16:37:29.340 Object number in the list: The base object of the extended graphical object
2022.01.20 16:37:29.340 Change history: No
2022.01.20 16:37:29.340 Object group: Not set
2022.01.20 16:37:29.340 Time of creation: 2022.01.20 16:37:29
2022.01.20 16:37:29.340 Visibility of an object at timeframes: Drawn on all timeframes
2022.01.20 16:37:29.340 Object in the background: No
2022.01.20 16:37:29.340 Priority of a graphical object for receiving events of clicking on a chart: 0
2022.01.20 16:37:29.340 Prohibit showing of the name of a graphical object in the terminal objects list: Yes
2022.01.20 16:37:29.340 Object is selected: Yes
2022.01.20 16:37:29.340 Object availability: Yes
2022.01.20 16:37:29.340 Time coordinate: 
2022.01.20 16:37:29.340  - Pivot point 0: 2022.01.13 09:00
2022.01.20 16:37:29.340  - Pivot point 1: 2022.01.20 10:00
2022.01.20 16:37:29.340 Color: clrRed
2022.01.20 16:37:29.340 Style: Solid line
2022.01.20 16:37:29.340 Line thickness: 1
2022.01.20 16:37:29.340 Ray goes to the left: No
2022.01.20 16:37:29.340 Ray goes to the right: No
2022.01.20 16:37:29.340 ------
2022.01.20 16:37:29.340 Price coordinate: 
2022.01.20 16:37:29.340  - Pivot point 0: 1.14728
2022.01.20 16:37:29.340  - Pivot point 1: 1.13598
2022.01.20 16:37:29.340 ------
2022.01.20 16:37:29.340 Name: "TestDoEasyPart93_TrendLineExt"
2022.01.20 16:37:29.340 Base object name: "TestDoEasyPart93_TrendLineExt"
2022.01.20 16:37:29.340 Description: Not set
2022.01.20 16:37:29.340 The text of a tooltip: Formed by the terminal
2022.01.20 16:37:29.340 ============= End of the parameter list (Trend Line) =============
2022.01.20 16:37:29.340 
2022.01.20 16:37:29.352 OnChartEvent: PriceLeft: Num linked coord X: 1, Num linked coord Y: 1
2022.01.20 16:37:29.364 OnChartEvent: PriceRight: Num linked coord X: 1, Num linked coord Y: 1

Neste ponto, nós simplesmente construímos objetos gráficos em separado e assinalamos em suas propriedades que eles pertencem ao mesmo objeto gráfico padrão estendido. Caso tentarmos mover a linha de tendência, os marcadores de preço não a seguirão. Nosso objetivo hoje não era tanto esse, mas preparamos a funcionalidade necessária para criar objetos gráficos estendidos e completos.

O que vem agora?

No próximo artigo, continuaremos a trabalhar sobre os objetos gráficos padrão estendidos e começaremos a "dar-lhes vida".

Todos os arquivos da versão atual da biblioteca e o arquivo do EA de teste, bem como o indicador de controle de eventos de gráficos para MQL5 estão anexados abaixo. Você pode baixá-los e testar tudo por conta própria. Se você tiver dúvidas, comentários e sugestões, pode expressá-los nos comentários ao artigo.

Complementos

*Artigos desta série:

Gráficos na biblioteca DoEasy (Parte 89): programando objetos gráficos padrão. Funcionalidade básica
Gráficos na biblioteca DoEasy (Parte 90): eventos de objetos gráficos padrão. Funcionalidade básica
Gráficos na biblioteca DoEasy (Parte 91): eventos de objetos gráficos padrão no programa. Histórico de alterações de nome do objeto
Gráficos na biblioteca do DoEasy (Parte 92): classe de memória de objetos gráficos padrão. Histórico de mudanças de propriedades do objeto

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/10331

Arquivos anexados |
MQL5.zip (4196.8 KB)
Como e por que desenvolver seu próprio sistema de negociação algorítmica Como e por que desenvolver seu próprio sistema de negociação algorítmica
Neste artigo, abordaremos os conceitos básicos da linguagem de programação MQL. O objetivo do artigo é ajudar os programadores iniciantes a desenvolver seu próprio sistema de negociação algorítmico (Expert Advisor).
Gráficos na biblioteca DoEasy (Parte 94): objetos gráficos compostos, movimentação e eliminação Gráficos na biblioteca DoEasy (Parte 94): objetos gráficos compostos, movimentação e eliminação
Neste artigo vamos começar a desenvolver os diversos eventos de um objeto gráfico composto. Analisaremos em parte a movimentação e a eliminação de um objeto gráfico composto. Hoje, estaremos, sobretudo, refinando o que foi criado no último artigo.
Desenvolvendo um EA de negociação do zero (Parte 13): Times And Trade (II) Desenvolvendo um EA de negociação do zero (Parte 13): Times And Trade (II)
Construa um sistema de Times & Trade para analisar o mercado. Esta é a segunda parte. No artigo anterior Times & Trade ( I ) apresentei um sistema alternativo para montar um gráfico de forma a você ter um indicador que lhe permitisse interpretar os negócios que foram executados no mercado de forma o mais rápido possível.
Desenvolvendo um EA de negociação do zero (Parte 12): Times And Trade (I) Desenvolvendo um EA de negociação do zero (Parte 12): Times And Trade (I)
Crie um times and trade de rápida intepretação para leitura de fluxo de ordens. Esta é a primeira parte da construção deste sistema. No próximo artigo iremos completar o sistema com as informações que estão faltando, já que para fazer isto precisaremos acrescentar várias coisas novas ao nosso código do EA.