Graphics in DoEasy library (Part 93): Preparing functionality for creating composite graphical objects
Contents
- Concept
- Improving library classes
- Classes for specifying dependent object coordinates
- Test
- What's next?
Concept
MetaTrader 5 client terminal provides a broad set of graphical analytical tools to be used in various graphical constructions on charts. But still I often hear that some users lack certain tools. The terminal features 44 analytical tools. Imagine what we would do if we were able to combine these graphical objects into bundled sets, thus creating new technical analysis tools. MQL5 language allows us to do that!
The library will support creating composite graphical objects allowing those objects have any hierarchy of connections. Each such object will have a basic graphical object featuring the list of other graphical objects attached to it. The base object will have the functionality for managing the properties of subordinate graphical objects, while subordinate objects in turn will have a set of X and Y base object coordinates they will be attached to. The entire list of base object properties can be used as a coordinate. For example, in order to calculate the X coordinate of a dependent object, we can use several X coordinates (for instance, two coordinates — point 0 and 1 of the trend line) rather than an X coordinate of a base object point. In this case, the average value of two X coordinates of the base object can be used as an X coordinate of a dependent object.
Each dependent object in the base object list can also be a base object for other graphical objects located in its list. This allows creating various sets from different composite graphical objects.
Moreover, I will try to programmatically implement not only creation of such objects, in which each dependent object is added to the list of the basic one in the program code, but also their creation in real time. When dragging one graphical object to another, it will attach to it with visual selection of anchor points, which can be redefined later.
In the current article, I will create the functionality for creating extended standard graphical objects — the new properties of standard graphical objects, indicating that the object is extended, and the classes for storing related properties of the base object coordinates for calculating and specifying the coordinates of the dependent one.
Improving library classes
In the enumeration of graphical element types in \MQL5\Include\DoEasy\Defines.mqh, add yet another type — extended standard graphical object:
//+------------------------------------------------------------------+ //| 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 }; //+------------------------------------------------------------------+
In the enumeration of graphical object integer properties, add the property (the base graphical object ID) and increase the number of integer properties from 54 to 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 //+------------------------------------------------------------------+
The base object ID is set in each dependent object (beginning from 1). The zero value of the property indicates that the object is not attached to any other objects and is not a dependent one.
In the enumeration of string properties of graphical objects, add the property — the name of the base graphical object and increase the number of string properties from 7 to 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 //+------------------------------------------------------------------+
Add the new constants (corresponding to the new graphical object properties added above) to the enumeration of possible graphical object sorting criteria:
//+------------------------------------------------------------------+ //| 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 }; //+------------------------------------------------------------------+
Now we are able to select objects by their corresponding properties, sort by them and create object lists with similar properties.
In \MQL5\Include\DoEasy\Data.mqh, add the library new message indices:
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 }; //+------------------------------------------------------------------+
and text messages corresponding to the newly added indices:
{"Запрос за пределами 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"}, }; //+---------------------------------------------------------------------+
Extended standard graphical objects are based on standard ones. To make the object extended, set the extended graphical object property to it. It will use the functionality created for handling extended standard graphical objects by adding subordinate objects to its list and managing them.
We already have the classes of all standard graphical objects that are descendants of the abstract standard graphical object class. All we have to do here is specify a flag indicating an extended graphical object is being created rather than a regular one.
Such an object is created programmatically, therefore the object affiliation property should be set as "the object belongs to the program" (the property of a manually created object is set by default). The standard extended graphical object type is set the same way. All this is done according to the flag value passed to the graphical object constructor during its creation.
In addition to these improvements, we need to set the new graphical object properties to the methods returning the flags indicating the support for the object properties.
Since all these changes are of the same type for all classes of standard graphical objects located in
\MQL5\Include\DoEasy\Objects\Graph\Standard\, I will consider the implemented changes using the GStdArrowBuyObj.mqh as an example.
The class constructor is to receive the flag of an extended standard graphical object. Depending on the flag value, we should specify whether this is a standard or standard extended graphical object, as well as whether the object is created manually or programmatically:
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); }
In the methods returning the flags indicating the support for integer and string properties by the object, add the new properties implemented above:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Such changes should be implemented in all files located in \MQL5\Include\DoEasy\Objects\Graph\Standard\. This has already been done. You can see all the improvements in the files attached below.
After implementing the improvements, the new properties are handled by all objects of standard graphical object classes, so that we are able to work with these properties with maximum efficiency since each such object now features the flags indicating the support for these new properties.
In \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh base graphical object class file, namely in the method returning the description of the graphical element type, add verification of the "extended standard graphical object" graphical element type so that we are able to display the graphical object description correctly:
//+------------------------------------------------------------------+ //| 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 for specifying dependent object coordinates
Any dependent object in the basic graphical object list has its own coordinates used to construct it. At the same time, the base graphical object should know how to manage dependent object coordinates. To let the base object know the coordinates the dependent graphical object is bound to, the latter should feature certain properties the base object is able to calculate to send the command for changing the dependent object coordinates when its own coordinates are changed.
In order to establish such a connection between the base and dependent object properties, create the class and call it the class of the connected pivot points object.
We will have three classes:
- The pivot point data class which is to store the properties used to let the dependent object be attached to a coordinate axis of the base object;
- The class of two pivot points data which is to store two first class objects. The first class is to store the data of the properties used to let the dependent object be attached to the base one by the X coordinate, while the second one stores the data allowing the object to be attached by the Y coordinate;
- The class of connected pivot points. The class is to store the list of second type objects. One object of the class of two anchor points data is responsible per each of its pivot points. In other words, each object will store the properties of the base object used to construct a single anchor point of a dependent object — its X and Y coordinates, which, in turn, can be calculated using multiple base object coordinates whose relevant lists are stored in the first type object per each coordinate axis.
The first type object will be created based on a usual two-dimensional array.
The array second dimension has the dimensionality of 2, the zero cell features the property itself, for example Time or Price, while the first one contains the property modifier. Suppose that the trend line features two anchor points. In this case, the point used to set the coordinate will be set to the cell 1 of the array second dimension. The value is 0 for the left point of the trend line and 1 for the right one.
In most cases, the array in its first dimension will have the size of 1, which means only one base object property, whose coordinates are used to attach the dependent object, is specified. But if we need to attach the object to calculated coordinates, for example, between two trend line points, we need to increase the array dimensionality up to two. The first one sets the properties of the first anchor point of the base object, while the second one contains the properties of the second one. In this case, the dependent object is attached to the base one according to a certain equation set by a library user in the code. This functionality will be implemented later. In the current article, I will make all the necessary preparations.
Let's implement the classes directly in the \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqh abstract standard graphical object. This is the file where the classes are declared in case the created object is an extended standard graphical object.
At the very start of the file listing, add the class of the dependent object pivot point data:
//+------------------------------------------------------------------+ //| 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){;} }; //+------------------------------------------------------------------+
The class contains a two-dimensional array and two methods for changing its size (adding a new pivot point) and setting the values to the specified first array dimension. The class also has the methods to indicate which coordinate data is set in the array — X or Y. When displaying messages from the class, the flag is checked and the appropriate error message is displayed.
The class listing is followed by the class of data on X and Y pivot points of a composite object:
//+------------------------------------------------------------------+ //| 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){;} }; //+------------------------------------------------------------------+
The class contains two objects considered above — for X and Y coordinates, as well as the methods for working with these objects that, in fact, simply return the results of handling the first class methods. The X or Y axis flags are set to each of the objects in the constructor.
The class also has the methods for simultaneous setting of the property values for X and Y coordinates — the methods of the first two classes are called immediately and the cumulative result of calling them is returned. If at least one of the methods returns false, the result is false.
This class is followed by the third one — the class of connected data on composite object pivot points:
//+------------------------------------------------------------------+ //| 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){;} }; //+------------------------------------------------------------------+
The class allows creating the objects of classes of data on composite object X and Y pivot points and returns the pointers to the class objects to be handled. One such object fully describes a single anchor point of a base object using X and Y coordinates, as well as allows adding new anchor points and change the existing ones. Besides, the object contains a base object ID, which allows us to define what object the graphical object is attached to.
In the private section of the abstract standard graphical object class, declare the list of dependent objects (attached to it) and the object of the class of connected pivot points considered above:
//+------------------------------------------------------------------+ //| 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:
In the public section of the class, add the methods for handling the list of bound graphical objects and the object of connected pivot points:
//--- 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:
All methods return the result of calling the same-name methods of the previously considered classes and provide access to working with these objects. Some methods are commented in the code, so all should be clear. If you have any questions, feel free to ask them in the comments.
The protected parametric constructor now receives the type of a created graphical element:
//--- 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:
When creating an object, we are now able to specify that we create an extended standard graphical object by passing GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED from the ENUM_GRAPH_ELEMENT_TYPE enumeration as an element type. This is done in the constructors of its descendant classes.
In the block of methods for a simplified access and setting graphical object properties, add the methods for handling the base object ID and the methods for handling the base object name:
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)
The methods allow setting the base object ID and name to the object properties and return the data.
Next, insert declaring the method returning the object index description to the list of the bound graphical objects:
//--- 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
In the protected parametric constructor, add the variable with the graphical element type and writing the value, passed in it, in the base graphical object, as well as set the default values for new graphical object properties created above:
//+------------------------------------------------------------------+ //| 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(); } //+-------------------------------------------------------------------+
By default, the base object ID is zero (absent), while the base object name is equal to the name of the current graphical object, i.e. the object points to itself meaning there is no connection to the base object.
In the method returning the object integer property description, add displaying the base object ID description. In the block returning the object index description in the list of bound objects, add calling the method to do that (to be added a bit later):
//+------------------------------------------------------------------+ //| 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) ) : "" ); } //+------------------------------------------------------------------+
In the method returning the description of an object string property, add the code block for returning the description of the base object name:
//+------------------------------------------------------------------+ //| 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) ) : "" ); } //+------------------------------------------------------------------+
The method returning the description of the graphical object index in the list of objects of a composite graphical object:
//+------------------------------------------------------------------+ //| 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(); } //+------------------------------------------------------------------+
For an extended standard graphical object, check its index. If it is equal to zero, display the text "Base object of the extended graphical object". In all other cases, the index is displayed as a string written in the object properties.
The method returning the name of a subordinate graphical object by index:
//+------------------------------------------------------------------+ //| 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() : ""); } //+------------------------------------------------------------------+
Get the pointer to the connected graphical object in the list by the specified index and return its name. If failed to get the object, return an empty string.
The method adding the subordinate standard graphical object to the list:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
All non-extended graphical objects are unable to work with subordinate objects. Therefore, we need to check all the methods receiving the pointer to a subordinate object in need of changes. If an object is not extended, the appropriate message is displayed and the method returns false. Similarly, we should perform the check of whether an object is subordinate (bound to the base one) in the current object's methods of handling subordinate object parameters. If the object is not bound, it is not subordinate. The appropriate message is displayed and false is returned.
This is done because any graphical object can be either base or subordinate or base and subordinate simultaneously. Therefore, it features the methods for handling its own subordinate objects (base objects mode) and for changing its properties from the outside (subordinate object mode).
Let's improve the collection class of graphical elements in \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh.
In the public section of the class for managing chart objects, add the flag indicating the necessity to create an extended graphical object to the method of creating a new object of the standard graphical object class:
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
In the method of the class checking the chart objects, set false when creating a new object:
//+------------------------------------------------------------------+ //| 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; } //+------------------------------------------------------------------+
Since the method tracks only the manual creation of graphical objects on the chart, the object of the graphical object class should not be extended. Therefore, pass the false flag to the method for creating an ordinary standard graphical object.
In the method creating a new standard graphical object, add the flag of the extended graphical object to the inputs. The following flag is passed to all strings of creating a new object of the standard graphical object class:
//+------------------------------------------------------------------+ //| 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; } } //+------------------------------------------------------------------+
Now we are able to specify the type of a constructed object (regular or extended) immediately when creating the object of the standard graphical object class. We are able to transform a regular graphical object into an extended one and vice versa at any moment.
In the private section of the collection class of graphical elements, remove the method creating a new standard graphical object and returning the name of a created object from the class listing. The method is relocated to the service function file so that we are always able to create any graphical object on a chart regardless of whether it is based on the collection class of graphical objects or on other classes or the program itself:
//+------------------------------------------------------------------+ //| 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:
In the public section of the class, set the two methods returning the list of standard and extended graphical objects:
//--- 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
The methods simply return the pointer to the list created when sorting the list of all graphical objects by the graphical element type property.
The private method creating a new graphical object and returning the pointer to the chart management object, now calls the method of creating a graphical object not from the class context but from DELib.mqh (simply remove "this." from the method call string):
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; }
Add similar changes in all public methods for creating graphical objects — the extended object flag in the method inputs and passing the flag to the method of creating a new object of the standard graphical object class:
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
Here we can see an example of two methods with similar changes. The remaining methods are modified the same way. You can find them in the files attached below.
Move the listing of implementing the method creating a new standard graphical object from the class to the very end of the \MQL5\Include\DoEasy\Services\DELib.mqh library service functions file:
//+------------------------------------------------------------------+ //| 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; } } //+------------------------------------------------------------------+
Now let's improve the CEngine library main class in \MQL5\Include\DoEasy\Engine.mqh.
The method returning the program name is renamed from pretty featureless Name() into more accurate 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
Since the object names differ depending on how the graphical object has been created (manually or programmatically), let's make some changes in the method returning the object class of the standard graphical object by chart name and ID to be able to search and select objects by a name from the program:
//--- 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; }
If the object was created manually, its name is not changed by the library. If we create a graphical object programmatically, its creation methods add the prefix with the program name to the object name. For example, if we create the object named "object_name" from a custom program and try to find it afterwards, we will not be able to find it since the name now has the prefix with the program name, so that the object name looks as "program_name_object_name". Considering this, we first perform the search for a name passed to the method. Next, if the object with such a name is not found, we try to find the object with the program name prefix added to its name.
Also, add the method returning the class of the object of the extended standard graphical object by chart name and ID:
//--- 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); }
Here we first receive the list of all extended graphical objects. Next, check the presence of a string with the program name in the name passed to the method. If the string is not found, add the program name to the object name. If the object name already has the program name, the name passed to the method is used 'as is'. Next, from the previously obtained list of extended graphical objects, receive the object with the necessary name and return the only object from the list if it has been found, or NULL if the search has failed.
The extended object flag is now passed to all the methods for creating graphical objects. The flag is also passed to the appropriate called methods of the collection class of graphical elements:
//--- 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); }
These improvements are identical to the four ones described above for all graphical object creation methods. Therefore, there is no need to consider other methods. You can find them in the files attached below.
This concludes the library improvement. Almost all is ready for creating extended standard graphical objects.
Test
To perform the test, I will use the EA from the previous article and save it in \MQL5\Experts\TestDoEasy\Part93\ as TestDoEasyPart93.mq5.
I will create the "Trend line" standard graphical object and place the "Left price label" and "Right price label" along its edges (in two of its pivot points) — to the left in point 0 and to the right in point 1. A trend line is created by clicking on a chart while holding Ctrl. The left anchor point of the line is located when the click on the chart has been made, while the right one is located on the Open price of the first chart bar.
Display the full description of the trend line properties and the values set in price label objects (namely, in their objects of connected pivot point classes) in the journal. There is no point in testing the rest since we also need to make some improvements in the object properties and functionality for a full-fledged creation of extended standard graphical objects. This will be done in the coming articles.
Add the following code to the block for handling the clicks on a chart of the OnChartEvent() EA handler:
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)); } }
The entire logic here is commented in the listing. So, all should be clear.
Compile the EA, launch it on the chart and click somewhere away from the right edge of the chart.
The result is a trend line accompanied by price label objects along its edges:
The journal displays the properties of a trend line and the number of trend line pivot points for calculating X and Y coordinates of both price labels. Since a single pivot point per each label has been specified for them, this is displayed in the journal.
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
Currently, we simply construct separate graphical objects and set the properties indicating that the objects belong to the same extended standard graphical object. If we move a trend line, the price labels do not follow it. However, this was not my current objective anyway. My aim was to prepare the necessary functionality for creating full-fledged extended graphical objects.
What's next?
In the next article, I will continue my work on extended standard graphical objects and start "animating" them.
*Previous articles within the series:
Graphics in DoEasy library (Part 89): Programming standard graphical objects. Basic functionality
Graphics in DoEasy library (Part 90): Standard graphical object events. Basic functionality
Graphics in DoEasy library (Part 91): Standard graphical object events. Object name change history
Graphics in DoEasy library (Part 92): Standard graphical object memory class. Object property change history
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/10331
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use