Graphics in DoEasy library (Part 92): Standard graphical object memory class. Object property change history
Contents
Concept
We have already tested the implementation of the graphical object renaming history to its properties. In fact, the functionality created in the previous article allows us to define the entire sequence of graphical object renaming. It is hard to forecast how useful it will turn out to be. However, it may be useful to know which objects were present on the chart in the past considering that during a renaming a previous object is removed and a new one with another name is created. If we take into account the possibility of storing the entire history of changes in the object, then we will have all its states including previous names in any graphical object. Thus, the object allows us to know any of its previous names. By matching the required name and its properties stored in memory, we can easily restore the object on the chart.
I am not going to discuss where and how it may come in handy, but it is certainly an additional tool for conducting technical analysis. For example, an object name may be used to mark a day, week, month or any other period of time the graphical object is built on. When a new time interval arrives, the old object is renamed so that its name matches the new time interval, and the object is rebuilt on the chart. Accordingly, all its properties will be stored in its memory (I am going to implement such functionality in the current article), including the history of its renaming. If we mark something on the chart using a graphical object, as well as rebuild that object on a daily basis, it can be renamed to match the new date. When scrolling the chart manually, we can find out the time of a visible bar and "retrieve" the graphical object status from its memory. The status should correspond to the time of bars on a scrolled chart and apply its properties to the current object. Thus, we will be able to create a smart object that will change its status on its own depending on the time of visible bars on the chart.
Thus, the above example will allow us to adjust the graphical object properties to the current market state every day within a trading week. At the end of the week, we can simply scroll the chart back and the graphical object will display each past trading day in the visible part of the chart. Since the object has the memory of its own, it saves all the changes each time the object properties are altered. If the chart features a library-controlled EA stating that a certain graphical object should take a "snapshot" of its status corresponding to a day visible on the chart and apply these parameters to itself, scrolling the chart is used as a condition of changing the graphical object properties and it will display all modifications made over the course of the trading week.
Why do we need this? I believe, this feature is convenient for analyzing a trading week. Besides, this is just one example that immediately came to my mind and seemed useful.
Improving library classes
The enumeration of standard graphical object properties has the Group property. It is currently used to specify the group a graphical object belongs to:
However, if we keep in mind that other library objects have their groups as well and they are used to sort objects according to certain properties, it would be reasonable to establish the same order for graphical objects as well. The current Group property will be renamed to Species, while the new Group property will be used to sort objects by some of their properties. In order to test the functionality I am creating here, I will assign the group #1 to graphical objects created on the chart. All objects of the group will save their status when they are modified.
In \MQL5\Include\DoEasy\Defines.mqh, replace the "Graphical object group" enumeration:
//+------------------------------------------------------------------+ //| The list of graphical element types | //+------------------------------------------------------------------+ enum ENUM_GRAPH_ELEMENT_TYPE { GRAPH_ELEMENT_TYPE_STANDARD, // 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 }; //+------------------------------------------------------------------+ //| Graphical object group | //+------------------------------------------------------------------+ enum ENUM_GRAPH_OBJ_GROUP { GRAPH_OBJ_GROUP_LINES, // Lines GRAPH_OBJ_GROUP_CHANNELS, // Channels GRAPH_OBJ_GROUP_GANN, // Gann GRAPH_OBJ_GROUP_FIBO, // Fibo GRAPH_OBJ_GROUP_ELLIOTT, // Elliott GRAPH_OBJ_GROUP_SHAPES, // Shapes GRAPH_OBJ_GROUP_ARROWS, // Arrows GRAPH_OBJ_GROUP_GRAPHICAL, // Graphical objects }; //+------------------------------------------------------------------+ //| Integer properties of a standard graphical object | //+------------------------------------------------------------------+
with the "Graphical object species" enumeration:
//+------------------------------------------------------------------+
//| Graphical object species |
//+------------------------------------------------------------------+
enum ENUM_GRAPH_OBJ_SPECIES
{
GRAPH_OBJ_SPECIES_LINES, // Lines
GRAPH_OBJ_SPECIES_CHANNELS, // Channels
GRAPH_OBJ_SPECIES_GANN, // Gann
GRAPH_OBJ_SPECIES_FIBO, // Fibo
GRAPH_OBJ_SPECIES_ELLIOTT, // Elliott
GRAPH_OBJ_SPECIES_SHAPES, // Shapes
GRAPH_OBJ_SPECIES_ARROWS, // Arrows
GRAPH_OBJ_SPECIES_GRAPHICAL, // Graphical objects
};
//+------------------------------------------------------------------+
In the enumeration of integer properties of the standard graphical object, replace the Group property
//--- Additional properties GRAPH_OBJ_PROP_ID = 0, // 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_GROUP, // Graphical object group (ENUM_GRAPH_OBJ_GROUP) 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
with the Species property, as well as add two new parameters — flag of storing the change history and object group:
//+------------------------------------------------------------------+ //| 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_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
By default, none of the graphical objects store the property change history. Therefore, I have implemented the property storing the flag that indicates whether a graphical object records its change history or not. As mentioned above, I have renamed the Group property to Species and created the new Group property for storing the index of the group of objects sorted by a certain property.
Since we added two new properties to the enumeration of integer graphical object properties, specify their new number (54 instead of 52):
#define GRAPH_OBJ_PROP_INTEGER_TOTAL (54) // Total number of integer properties #define GRAPH_OBJ_PROP_INTEGER_SKIP (0) // Number of integer properties not used in sorting //+------------------------------------------------------------------+ //| Real properties of a standard graphical object | //+------------------------------------------------------------------+
Add the new properties 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_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_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 }; //+------------------------------------------------------------------+
In \MQL5\Include\DoEasy\Data.mqh, add the new message indices and fix the names of enumeration constants:
//--- GStdGraphObj MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ, // Failed to create the class object for a graphical object MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_STD_GRAPH_OBJ, // Failed to create a graphical object MSG_GRAPH_STD_OBJ_ERR_NOT_FIND_SUBWINDOW, // Failed to find the chart subwindow MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_SNAPSHOT, // Failed to create a snapshot of the graphical object change history MSG_GRAPH_STD_OBJ_SUCCESS_CREATE_SNAPSHOT, // Created a snapshot of the graphical object change history
...
MSG_GRAPH_OBJ_PROP_ID, // 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_BELONG, // Graphical object affiliation MSG_GRAPH_OBJ_PROP_CHART_ID, // Chart ID MSG_GRAPH_OBJ_PROP_WND_NUM, // Chart subwindow index MSG_GRAPH_OBJ_PROP_CHANGE_MEMORY, // Change history MSG_GRAPH_OBJ_PROP_CREATETIME, // Creation time MSG_GRAPH_OBJ_PROP_TIMEFRAMES, // Object visibility on timeframes
...
MSG_GRAPH_OBJ_PROP_SPECIES, // Graphical object species MSG_GRAPH_OBJ_PROP_SPECIES_LINES, // Lines MSG_GRAPH_OBJ_PROP_SPECIES_CHANNELS, // Channels MSG_GRAPH_OBJ_PROP_SPECIES_GANN, // Gann MSG_GRAPH_OBJ_PROP_SPECIES_FIBO, // Fibo MSG_GRAPH_OBJ_PROP_SPECIES_ELLIOTT, // Elliott MSG_GRAPH_OBJ_PROP_SPECIES_SHAPES, // Shapes MSG_GRAPH_OBJ_PROP_SPECIES_ARROWS, // Arrows MSG_GRAPH_OBJ_PROP_SPECIES_GRAPHICAL, // Graphical objects MSG_GRAPH_OBJ_PROP_GROUP, // Group of objects MSG_GRAPH_OBJ_TEXT_CLICK_COORD, // (Chart click coordinate) MSG_GRAPH_OBJ_TEXT_ANCHOR_TOP, // Arrow anchor point is located at the top MSG_GRAPH_OBJ_TEXT_ANCHOR_BOTTOM, // Arrow anchor point is located at the bottom
...
//--- CDataPropObj MSG_DATA_PROP_OBJ_OUT_OF_PROP_RANGE, // Passed property is out of object property range MSG_GRAPH_OBJ_FAILED_CREATE_NEW_HIST_OBJ, // Failed to create an object of the graphical object change history MSG_GRAPH_OBJ_FAILED_ADD_OBJ_TO_HIST_LIST, // Failed to add the change history object to the list MSG_GRAPH_OBJ_FAILED_GET_HIST_OBJ, // Failed to receive the change history object MSG_GRAPH_OBJ_FAILED_INC_ARRAY_SIZE, // Failed to increase the array size
and the text messages corresponding to newly added indices:
//--- GStdGraphObj {"Не удалось создать объект класса для графического объекта ","Failed to create class object for graphic object"}, {"Не удалось создать графический объект ","Failed to create graphic object "}, {"Не удалось найти подокно графика","Could not find chart subwindow"}, {"Не удалось создать снимок истории изменений графического объекта","Failed to create a snapshot of the change history of a graphic object"}, {"Создан снимок истории изменений графического объекта","A snapshot of the history of changes to a graphical object has been created"},
...
{"Идентификатор объекта","Object ID"}, {"Тип объекта","Object type"}, {"Тип графического элемента","Graphic element type"}, {"Принадлежность объекта","Object belongs to"}, {"Идентификатор графика объекта","Object chart ID"}, {"Номер подокна графика","Chart subwindow number"}, {"История изменений","Change history"}, {"Время создания","Time of creation"}, {"Видимость объекта на таймфреймах","Visibility of an object at timeframes"},
...
{"Вид графического объекта","Graphic object species"}, {"Линии","Lines"}, {"Каналы","Channels"}, {"Ганн","Gann"}, {"Фибоначчи","Fibonacci"}, {"Эллиотт","Elliott"}, {"Фигуры","Shapes"}, {"Стрелки","Arrows"}, {"Графические объекты","Graphical"}, {"Группа объектов","Object group"},
...
//--- CDataPropObj {"Переданное свойство находится за пределами диапазона свойств объекта","The passed property is outside the range of the object's properties"}, {"Не удалось создать объект истории изменений графического объекта","Failed to create a graphical object change history object"}, {"Не удалось добавить объект истории изменений в список","Failed to add change history object to the list"}, {"Не удалось получить объект истории изменений","Failed to get change history object"}, {"Не удалось увеличить размер массива","Failed to increase array size"},
We need to implement changes to all objects derived from the abstract standard graphical object in \MQL5\Include\DoEasy\Objects\Graph\Standard\ (GStdArrowBuyObj.mqh is used as an example).
In the initialization list of the class constructor, fix the name of the enumeration constant indicating the graphical object species:
//--- Constructor CGStdArrowBuyObj(const long chart_id,const string name) : CGStdGraphObj(OBJECT_DE_TYPE_GSTD_ARROW_BUY,GRAPH_OBJ_BELONG_NO_PROGRAM,GRAPH_OBJ_SPECIES_ARROWS,chart_id,1,name) { //--- Specify the object property CGStdGraphObj::SetProperty(GRAPH_OBJ_PROP_ANCHOR,0,ANCHOR_TOP); }
In other files, there will be other types of graphical objects. But _GROUP_ should be replaced with _SPECIES_ everywhere.
In the methods returning the flag of supporting integer properties by the object, add the "Flag of saving object change history" property:
//+------------------------------------------------------------------+ //| 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_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; } //+------------------------------------------------------------------+
All the changes have already been made in the specified folder. They are identical to the considered ones so I am not going to describe them. You can find them in the attached files below.
In the base graphical object file of all library graphical objects \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh, add the variables for storing the species and groups of graphical objects:
//+------------------------------------------------------------------+ //| Class of the base object of the library graphical objects | //+------------------------------------------------------------------+ class CGBaseObj : public CObject { protected: CArrayObj m_list_events; // Object event list ENUM_OBJECT m_type_graph_obj; // Graphical object type ENUM_GRAPH_ELEMENT_TYPE m_type_element; // Graphical element type ENUM_GRAPH_OBJ_BELONG m_belong; // Program affiliation ENUM_GRAPH_OBJ_SPECIES m_species; // Graphical object species string m_name_prefix; // Object name prefix string m_name; // Object name long m_chart_id; // Object chart ID long m_object_id; // Object ID long m_zorder; // Priority of a graphical object for receiving the mouse click event int m_subwindow; // Subwindow index int m_shift_y; // Subwindow Y coordinate shift int m_type; // Object type int m_timeframes_visible; // Visibility of an object on timeframes (a set of flags) int m_digits; // Number of decimal places in a quote int m_group; // Graphical object group bool m_visible; // Object visibility bool m_back; // "Background object" flag bool m_selected; // "Object selection" flag bool m_selectable; // "Object availability" flag bool m_hidden; // "Disable displaying the name of a graphical object in the terminal object list" flag datetime m_create_time; // Object creation time
and the methods for setting and returning the values of these variables:
public: //--- Set the values of the class variables void SetObjectID(const long value) { this.m_object_id=value; } void SetBelong(const ENUM_GRAPH_OBJ_BELONG belong){ this.m_belong=belong; } void SetTypeGraphObject(const ENUM_OBJECT obj) { this.m_type_graph_obj=obj; } void SetTypeElement(const ENUM_GRAPH_ELEMENT_TYPE type) { this.m_type_element=type; } void SetSpecies(const ENUM_GRAPH_OBJ_SPECIES species){ this.m_species=species; } void SetGroup(const int group) { this.m_group=group; } void SetName(const string name) { this.m_name=name; } void SetChartID(const long chart_id) { this.m_chart_id=chart_id; } void SetDigits(const int value) { this.m_digits=value; }
...
//--- Return the values of class variables ENUM_GRAPH_ELEMENT_TYPE TypeGraphElement(void) const { return this.m_type_element; } ENUM_GRAPH_OBJ_BELONG Belong(void) const { return this.m_belong; } ENUM_GRAPH_OBJ_SPECIES Species(void) const { return this.m_species; } ENUM_OBJECT TypeGraphObject(void) const { return this.m_type_graph_obj; } datetime TimeCreate(void) const { return this.m_create_time; } string Name(void) const { return this.m_name; } long ChartID(void) const { return this.m_chart_id; } long ObjectID(void) const { return this.m_object_id; } long Zorder(void) const { return this.m_zorder; } int SubWindow(void) const { return this.m_subwindow; } int ShiftY(void) const { return this.m_shift_y; } int VisibleOnTimeframes(void) const { return this.m_timeframes_visible; } int Digits(void) const { return this.m_digits; } int Group(void) const { return this.m_group; } bool IsBack(void) const { return this.m_back; } bool IsSelected(void) const { return this.m_selected; } bool IsSelectable(void) const { return this.m_selectable; } bool IsHidden(void) const { return this.m_hidden; } bool IsVisible(void) const { return this.m_visible; }
Rename the method that previously returned the description of the graphical object into the method returning the graphical object species description:
//--- Return the description of the type of the graphical object (1) type, (2) element, (3) affiliation and (4) species string TypeGraphObjectDescription(void); string TypeElementDescription(void); string BelongDescription(void); string SpeciesDescription(void);
and fix its implementation according to the new names of the object species enumeration constants:
//+------------------------------------------------------------------+ //| Return the description of the graphical object group | //+------------------------------------------------------------------+ string CGBaseObj::SpeciesDescription(void) { return ( this.Species()==GRAPH_OBJ_SPECIES_LINES ? CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES_LINES) : this.Species()==GRAPH_OBJ_SPECIES_CHANNELS ? CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES_CHANNELS) : this.Species()==GRAPH_OBJ_SPECIES_GANN ? CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES_GANN) : this.Species()==GRAPH_OBJ_SPECIES_FIBO ? CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES_FIBO) : this.Species()==GRAPH_OBJ_SPECIES_ELLIOTT ? CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES_ELLIOTT) : this.Species()==GRAPH_OBJ_SPECIES_SHAPES ? CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES_SHAPES) : this.Species()==GRAPH_OBJ_SPECIES_ARROWS ? CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES_ARROWS) : this.Species()==GRAPH_OBJ_SPECIES_GRAPHICAL ? CMessage::Text(MSG_GRAPH_OBJ_PROP_SPECIES_GRAPHICAL) : "Unknown" ); }
In the class constructor, set the default object group to 0, which corresponds to its absence:
//+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CGBaseObj::CGBaseObj() : m_shift_y(0),m_visible(false), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_"),m_belong(GRAPH_OBJ_BELONG_PROGRAM) { this.m_list_events.Clear(); // Clear the event list this.m_list_events.Sort(); // Sorted list flag this.m_type=OBJECT_DE_TYPE_GBASE; // Object type this.m_type_graph_obj=WRONG_VALUE; // Graphical object type this.m_type_element=WRONG_VALUE; // Graphical object type this.m_belong=WRONG_VALUE; // Program/terminal affiliation this.m_group=0; // Graphical object group this.m_name_prefix=""; // Object name prefix this.m_name=""; // Object name this.m_chart_id=0; // Object chart ID this.m_object_id=0; // Object ID this.m_zorder=0; // Priority of a graphical object for receiving the mouse click event this.m_subwindow=0; // Subwindow index this.m_shift_y=0; // Subwindow Y coordinate shift this.m_timeframes_visible=OBJ_ALL_PERIODS; // Visibility of an object on timeframes (a set of flags) this.m_visible=true; // Object visibility this.m_back=false; // "Background object" flag this.m_selected=false; // "Object selection" flag this.m_selectable=false; // "Object availability" flag this.m_hidden=true; // "Disable displaying the name of a graphical object in the terminal object list" flag this.m_create_time=0; // Object creation time } //+------------------------------------------------------------------+
Standard graphical object memory class
The graphical object memory class is a list of objects featuring all graphical object properties (integer, real and strings ones) at the moment any of them is changed. Why not just the changed property? Knowing which property has been changed is insufficient. We should also be able to set all properties to the object, thus getting its status. Therefore, we will store a complete snapshot of its properties. In that case, we will be able to copy all these properties from the object memory to its real properties without resorting to calculating which property to take from memory and which from its current status.
In order to implement the changed object property snapshot class, I will use the CDataPropObj object property class. But since we need to know and consider some additional parameters (change time, symbol and its Digits), the changed object property snapshot class is to be inherited from the graphical object property object class.
The graphical object memory class is to contain the list of changed property snapshot objects, as well as provide access to handling these lists and objects contained in it.
Both of these classes are to be located in \MQL5\Include\DoEasy\Services\Properties.mqh object property class file.
Include the file of the library service functions to it:
//+------------------------------------------------------------------+ //| Properties.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "DELib.mqh" #include "XDimArray.mqh" //+------------------------------------------------------------------+
In the public section of the object property class, add the methods returning the number of integer, real and string object properties:
//--- Return the number of (1) integer, (2) real and (3) string parameters int TotalLong(void) const { return this.m_total_int; } int TotalDouble(void) const { return this.m_total_dbl; } int TotalString(void) const { return this.m_total_str; } //--- Constructor
These methods will be useful for setting the number of properties for the objects of the change history class.
The object property class is followed by the object changed property snapshot class:
//+------------------------------------------------------------------+ //| Object changed property snapshot class | //+------------------------------------------------------------------+ class CChangedProps : public CDataPropObj { private: long m_time_change; // Property modification time string m_symbol; // Chart window symbol int m_digits; // Symbol's Digits public: //--- Set the (1) change time value, (2) symbol and (3) symbol's Digits void SetTimeChanged(const long time) { this.m_time_change=time; } void SetSymbol(const string symbol) { this.m_symbol=symbol; } void SetDigits(const int digits) { this.m_digits=digits; } //--- Return the (1) change time value, (2) change time, (3) symbol and (4) symbol's Digits long TimeChanged(void) const { return this.m_time_change; } string TimeChangedToString(void) const { return TimeMSCtoString(this.m_time_change);} string Symbol(void) const { return this.m_symbol; } int Digits(void) const { return this.m_digits; } //--- Constructor/destructor CChangedProps (const int prop_total_integer,const int prop_total_double,const int prop_total_string,const long time_changed) : CDataPropObj(prop_total_integer,prop_total_double,prop_total_string) { this.m_time_change=time_changed;} ~CChangedProps (void){;} }; //+------------------------------------------------------------------+
As we can see, the class is derived from the graphical object property object class. Thus, it has all the properties of its parent, as well as additional properties set in the class. Here we only have the object modification time in milliseconds, symbol of the chart the graphical object was changed on and symbol's Digits for the correct display of the number of decimal places in the object price property.
In the class constructor, pass the number of integer, real and string properties, as well as their change time, in milliseconds.
Thus, we are able to create a copy of graphical object parameters and place it to the list of the parameter change history class object. The class is written in the same file — right below the class of object changed property snapshot:
//+------------------------------------------------------------------+ //| Class of the history of graphical object property changes | //+------------------------------------------------------------------+ class CChangeHistory { private: CArrayObj m_list_changes; // List of the property change history public: //--- Return (1) the pointer to the property change history object and (2) the number of changes CChangedProps *GetChangedPropsObj(const string source,const int index) { CChangedProps *props=this.m_list_changes.At(index<0 ? 0 : index); if(props==NULL) CMessage::ToLog(source,MSG_GRAPH_OBJ_FAILED_GET_HIST_OBJ); return props; } int TotalChanges(void) { return this.m_list_changes.Total(); } //--- Create a new object of the graphical object property change history bool CreateNewElement(CDataPropObj *element,const long time_change) { //--- Create a new object of the graphical object property snapshot CChangedProps *obj=new CChangedProps(element.TotalLong(),element.TotalDouble(),element.TotalString(),time_change); //--- If failed to create an object, inform of that and return 'false' if(obj==NULL) { CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_CREATE_NEW_HIST_OBJ); return false; } //--- If failed to add the object to the list, inform of that, remove the object and return 'false' if(!this.m_list_changes.Add(obj)) { CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_ADD_OBJ_TO_HIST_LIST); delete obj; return false; } //--- Get the ID of the chart the graphical object is located on long chart_id=element.GetLong(GRAPH_OBJ_PROP_CHART_ID,0); //--- Set a chart symbol and symbol's Digits for the graphical object property snapshot object obj.SetSymbol(::ChartSymbol(chart_id)); obj.SetDigits((int)::SymbolInfoInteger(obj.Symbol(),SYMBOL_DIGITS)); //--- Copy all integer properties for(int i=0;i<element.TotalLong();i++) { int total=element.Long().Size(i); if(obj.SetSizeRange(i,total)) { for(int r=0;r<total;r++) obj.Long().Set(i,r,element.Long().Get(i,r)); } else CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_INC_ARRAY_SIZE); } //--- Copy all real properties for(int i=0;i<element.TotalDouble();i++) { int total=element.Double().Size(i); if(obj.Double().SetSizeRange(i,total)) { for(int r=0;r<total;r++) obj.Double().Set(i,r,element.Double().Get(i,r)); } else CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_INC_ARRAY_SIZE); } //--- Copy all string properties for(int i=0;i<element.TotalString();i++) { int total=element.String().Size(i); if(obj.String().SetSizeRange(i,total)) { for(int r=0;r<total;r++) obj.String().Set(i,r,element.String().Get(i,r)); } else CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_FAILED_INC_ARRAY_SIZE); } return true; } //--- Return by index in the list of the graphical object change history object //--- the value from the specified index of the (1) long, (2) double and (3) string array long GetLong(const int time_index,const ENUM_GRAPH_OBJ_PROP_INTEGER prop,const int index) { CChangedProps *properties=this.GetChangedPropsObj(DFUN,time_index); if(properties==NULL) return 0; return properties.GetLong(prop,index); } double GetDouble(const int time_index,const ENUM_GRAPH_OBJ_PROP_DOUBLE prop,const int index) { CChangedProps *properties=this.GetChangedPropsObj(DFUN,time_index); if(properties==NULL) return 0; return properties.GetDouble(prop,index); } string GetString(const int time_index,const ENUM_GRAPH_OBJ_PROP_STRING prop,const int index) { CChangedProps *properties=this.GetChangedPropsObj(DFUN,time_index); if(properties==NULL) return ""; return properties.GetString(prop,index); } //--- Constructor/destructor CChangeHistory(void){;} ~CChangeHistory(void){;} }; //+------------------------------------------------------------------+
The class is also simple. It features the list that will contain all the graphical object changes represented by the objects of the graphical object changed property snapshot class.
The method of creating a new snapshot of changed properties receives the current (already changed) graphical object properties and the change time in milliseconds. A new property snapshot object is created and added to the list. Further on, additional parameters are set for the object. All properties of the changed graphical object property object passed to the method are then copied to the created object in three loops.
Thus, when changing each graphical object, the copy of its properties is created and added to the list. Thus, we are able to get the pointer to any saved property object and use it for the program.
All class methods are identical to each other. Their logic is quite transparent: get the necessary property object from the list and return the requested property from it.
You can ask all questions about the methods in the comments below. I believe, there is no point in describing them here. The logic of such methods was repeatedly considered in previous articles.
The class of the current and previous property data receives the pointer to the change history object and the method returning the number of graphical object changes. In the class constructor, create a new change history and delete it in the destructor:
//+------------------------------------------------------------------+ //| Data class of the current and previous properties | //+------------------------------------------------------------------+ class CProperties : public CObject { private: CArrayObj m_list; // List for storing the pointers to property objects public: CDataPropObj *Curr; // Pointer to the current properties object CDataPropObj *Prev; // Pointer to the previous properties object CChangeHistory *History; // Pointer to the change history object //--- Set the array size ('size') in the specified dimension ('range') bool SetSizeRange(const int range,const int size) { return(this.Curr.SetSizeRange(range,size) && this.Prev.SetSizeRange(range,size) ? true : false); } //--- Return the size of the specified array of the (1) current and (2) previous first dimension data int CurrSize(const int range) const { return Curr.Size(range); } int PrevSize(const int range) const { return Prev.Size(range); } //--- Copy the current data to the previous one void CurrentToPrevious(void) { //--- Copy all integer properties for(int i=0;i<this.Curr.Long().Total();i++) for(int r=0;r<this.Curr.Long().Size(i);r++) this.Prev.Long().Set(i,r,this.Curr.Long().Get(i,r)); //--- Copy all real properties for(int i=0;i<this.Curr.Double().Total();i++) for(int r=0;r<this.Curr.Double().Size(i);r++) this.Prev.Double().Set(i,r,this.Curr.Double().Get(i,r)); //--- Copy all string properties for(int i=0;i<this.Curr.String().Total();i++) for(int r=0;r<this.Curr.String().Size(i);r++) this.Prev.String().Set(i,r,this.Curr.String().Get(i,r)); } //--- Return the amount of graphical object changes since the start of recording them int TotalChanges(void) { return this.History.TotalChanges(); } //--- Constructor CProperties(const int prop_int_total,const int prop_double_total,const int prop_string_total) { //--- Create new objects of the current and previous properties this.Curr=new CDataPropObj(prop_int_total,prop_double_total,prop_string_total); this.Prev=new CDataPropObj(prop_int_total,prop_double_total,prop_string_total); //--- Add newly created objects to the list this.m_list.Add(this.Curr); this.m_list.Add(this.Prev); //--- Create the change history object this.History=new CChangeHistory(); } //--- Destructor ~CProperties() { this.m_list.Clear(); this.m_list.Shutdown(); if(this.History!=NULL) delete this.History; } }; //+------------------------------------------------------------------+
Now each graphical object will store the list of property changes in its properties.
In order to interact with the graphical object change history, in the \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqh file (namely, in the abstract standard graphical object class), add the method returning the pointer to the list of change history in the object properties:
public: //--- Set object's (1) integer, (2) real and (3) string properties void SetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value) { this.Prop.Curr.SetLong(property,index,value); } void SetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value) { this.Prop.Curr.SetDouble(property,index,value); } void SetProperty(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value) { this.Prop.Curr.SetString(property,index,value); } //--- Return object’s (1) integer, (2) real and (3) string property from the properties array long GetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index) const { return this.Prop.Curr.GetLong(property,index); } double GetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index) const { return this.Prop.Curr.GetDouble(property,index); } string GetProperty(ENUM_GRAPH_OBJ_PROP_STRING property,int index) const { return this.Prop.Curr.GetString(property,index); } //--- Set object's previous (1) integer, (2) real and (3) string properties void SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value) { this.Prop.Prev.SetLong(property,index,value); } void SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value){ this.Prop.Prev.SetDouble(property,index,value); } void SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value){ this.Prop.Prev.SetString(property,index,value); } //--- Return object’s (1) integer, (2) real and (3) string property from the previous properties array long GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index) const { return this.Prop.Prev.GetLong(property,index); } double GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index) const { return this.Prop.Prev.GetDouble(property,index); } string GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_STRING property,int index) const { return this.Prop.Prev.GetString(property,index); } //--- Return (1) itself, (2) properties and (3) the change history CGStdGraphObj *GetObject(void) { return &this; } CProperties *Properties(void) { return this.Prop; } CChangeHistory *History(void) { return this.Prop.History;} //--- Return the flag of the object supporting this property
In the class default constructor, replace the object group indication with the species indication. In the closed parametric constructor, pass the object species rather than the group:
//--- 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_OBJ_BELONG belong, const ENUM_GRAPH_OBJ_SPECIES species, const long chart_id, const int pivots, const string name);
In the block of methods for a simplified access and setting graphical object properties, add the methods for setting and returning the flag of storing the change history and the group of graphical objects:
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); } //--- 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); } //--- Graphical element type ENUM_GRAPH_ELEMENT_TYPE GraphElementType(void) const { return (ENUM_GRAPH_ELEMENT_TYPE)this.GetProperty(GRAPH_OBJ_PROP_ELEMENT_TYPE,0);} void SetGraphElementType(const ENUM_GRAPH_ELEMENT_TYPE elm_type) { CGBaseObj::SetTypeElement(elm_type); this.SetProperty(GRAPH_OBJ_PROP_ELEMENT_TYPE,0,elm_type); } //--- Graphical object affiliation ENUM_GRAPH_OBJ_BELONG Belong(void) const { return (ENUM_GRAPH_OBJ_BELONG)this.GetProperty(GRAPH_OBJ_PROP_BELONG,0); } void SetBelong(const ENUM_GRAPH_OBJ_BELONG belong) { CGBaseObj::SetBelong(belong); this.SetProperty(GRAPH_OBJ_PROP_BELONG,0,belong); } //--- Group of graphical objects int Group(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_GROUP,0); } void SetGroup(const int group) { CGBaseObj::SetGroup(group); this.SetProperty(GRAPH_OBJ_PROP_GROUP,0,group); } //--- Chart ID
By default, each graphical object on the chart does not record its change history. To start doing this, use the method for setting the flag allowing recording the change history. The group of graphical objects includes various graphical objects on the chart combined into a group used to select them and perform the necessary actions.
Add the methods for working with the object change history:
//--- Re-write all graphical object properties void PropertiesRefresh(void); //--- Check object property changes void PropertiesCheckChanged(void); //--- Copy the current data to the previous one void PropertiesCopyToPrevData(void); //--- Return (1) the number of property changes in history specified (2) by the property index, (3) the last and (4) the first changed object int HistoryChangesTotal(void) { return this.History().TotalChanges(); } CChangedProps *GetHistoryChangedProps(const string source,const int index) { return this.History().GetChangedPropsObj(source,index); } CChangedProps *GetHistoryChangedPropsLast(const string source) { return this.History().GetChangedPropsObj(source,this.HistoryChangesTotal()-1); } CChangedProps *GetHistoryChangedPropsFirst(const string source) { return this.History().GetChangedPropsObj(source,0); } //--- Using the specified index in the list of change history objects, return //--- the specified value of (1) integer, (2) real and (3) string property long HistoryChangedObjGetLong(const int time_index,const ENUM_GRAPH_OBJ_PROP_INTEGER prop,const int prop_index) { CChangedProps *obj=this.GetHistoryChangedProps(DFUN,time_index); return(obj!=NULL ? obj.GetLong(prop,prop_index) : 0); } double HistoryChangedObjGetDouble(const int time_index,const ENUM_GRAPH_OBJ_PROP_DOUBLE prop,const int prop_index) { CChangedProps *obj=this.GetHistoryChangedProps(DFUN,time_index); return(obj!=NULL ? obj.GetDouble(prop,prop_index) : 0); } string HistoryChangedObjGetString(const int time_index,const ENUM_GRAPH_OBJ_PROP_STRING prop,const int prop_index) { CChangedProps *obj=this.GetHistoryChangedProps(DFUN,time_index); return(obj!=NULL ? obj.GetString(prop,prop_index) : "ERROR"); } //--- Return (1) a symbol, (2) symbol's Digits and (3) the time of changing the change history object string HistoryChangedObjSymbol(const int time_index) { CChangedProps *obj=this.GetHistoryChangedProps(DFUN,time_index); return(obj!=NULL ? obj.Symbol() : "ERROR"); } int HistoryChangedObjDigits(const int time_index) { CChangedProps *obj=this.GetHistoryChangedProps(DFUN,time_index); return(obj!=NULL ? obj.Digits() : 0); } long HistoryChangedObjTimeChanged(const int time_index) { CChangedProps *obj=this.GetHistoryChangedProps(DFUN,time_index); return(obj!=NULL ? obj.TimeChanged() : 0); } string HistoryChangedObjTimeChangedToString(const int time_index) { CChangedProps *obj=this.GetHistoryChangedProps(DFUN,time_index); return(obj!=NULL ? obj.TimeChangedToString() : "ERROR"); } //--- Set object parameters from the specified history snapshot bool SetPropertiesFromHistory(const int time_index) { CChangedProps *obj=this.GetHistoryChangedProps(DFUN,time_index); if(obj==NULL) return false; int begin=0, end=GRAPH_OBJ_PROP_INTEGER_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_INTEGER prop=(ENUM_GRAPH_OBJ_PROP_INTEGER)i; for(int j=0;j<this.Prop.CurrSize(prop);j++) if(this.GetProperty(prop,j)!=obj.GetLong(prop,j)) this.SetHistoryINT(prop,obj.GetLong(prop,j),j); } begin=end; end+=GRAPH_OBJ_PROP_DOUBLE_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_DOUBLE prop=(ENUM_GRAPH_OBJ_PROP_DOUBLE)i; for(int j=0;j<this.Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=obj.GetDouble(prop,j)) this.SetHistoryDBL(prop,obj.GetDouble(prop,j),j); } } begin=end; end+=GRAPH_OBJ_PROP_STRING_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_STRING prop=(ENUM_GRAPH_OBJ_PROP_STRING)i; for(int j=0;j<this.Prop.CurrSize(prop);j++) if(this.GetProperty(prop,j)!=obj.GetString(prop,j)) this.SetHistorySTR(prop,obj.GetString(prop,j),j); } return true; }
Almost all methods return the result of calling the same-name methods of the graphical object change history class.
The method setting the graphical object properties from the specified history snapshot receives the object by index and sets all the properties from the history snapshot object to the graphical object using the SetHistoryINT(), SetHistoryDBL() and SetHistorySTR() methods (considered below) in three loops.
Add the methods to the private section of the class:
private: //--- Get and save (1) integer, (2) real and (3) string properties void GetAndSaveINT(void); void GetAndSaveDBL(void); void GetAndSaveSTR(void); //--- Create a new object of the graphical object change history bool CreateNewChangeHistoryObj(const bool first) { bool res=true; if(first) res &=this.History().CreateNewElement(this.Prop.Prev,this.GetMarketWatchTime()); res &=this.History().CreateNewElement(this.Prop.Curr,this.GetMarketWatchTime()); if(!res) CMessage::ToLog(DFUN,MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_SNAPSHOT); return res; } //--- Set (1) integer, (2) real and (3) string property values from the change history void SetHistoryINT(const ENUM_GRAPH_OBJ_PROP_INTEGER prop,const long value,const int modifier); void SetHistoryDBL(const ENUM_GRAPH_OBJ_PROP_DOUBLE prop,const double value,const int modifier); void SetHistorySTR(const ENUM_GRAPH_OBJ_PROP_STRING prop,const string value,const int modifier); //--- Return the time of the last symbol tick long GetSymbolTime(const string symbol) { MqlTick tick; return(::SymbolInfoTick(symbol,tick) ? tick.time_msc : 0); } //--- Return the time of the last Market Watch tick long GetMarketWatchTime(void) { long res=0; for(int i=::SymbolsTotal(true)-1;i>WRONG_VALUE;i--) { const long time=this.GetSymbolTime(::SymbolName(i,true)); if(time>res) res=time; } return res; } }; //+------------------------------------------------------------------+
The method creating a new object of the graphical object change history passes the flag of the very first graphical object change.
If the flag is set, this is the first change. First, we need to save the previous graphical object status in history (before making changes to the properties). Next, write the current object status to history. If no flag is set, save the current object status in the change history:
//--- Create a new object of the graphical object change history bool CreateNewChangeHistoryObj(const bool first) { bool res=true; if(first) res &=this.History().CreateNewElement(this.Prop.Prev,this.GetMarketWatchTime()); res &=this.History().CreateNewElement(this.Prop.Curr,this.GetMarketWatchTime()); if(!res) CMessage::ToLog(DFUN,MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_SNAPSHOT); return res; }
The result of the called methods is added to the resulting res variable and is equal to false only if any of the called methods returns false. As a result, return the variable value.
The method returning the time of the last Market Watch tick iterates over all symbols in the Market Watch window, reads the current time in milliseconds and compares the time of each symbol to return the most resent time:
//--- Return the time of the last Market Watch tick long GetMarketWatchTime(void) { long res=0; for(int i=::SymbolsTotal(true)-1;i>WRONG_VALUE;i--) { const long time=this.GetSymbolTime(::SymbolName(i,true)); if(time>res) res=time; } return res; }
In the protected parametric constructor, pass the graphical object species (instead of a group) and set all new properties to the object:
//+------------------------------------------------------------------+ //| Protected parametric constructor | //+------------------------------------------------------------------+ CGStdGraphObj::CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_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(GRAPH_ELEMENT_TYPE_STANDARD); 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_NUM,0,0); // Object index in the list this.SetProperty(GRAPH_OBJ_PROP_CHANGE_HISTORY,0,false); // Flag of storing the change history< //--- 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(); } //+-------------------------------------------------------------------+
The method returning the object integer property description receives the description of new object properties:
//+------------------------------------------------------------------+ //| 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_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) : ": "+(string)this.GetProperty(property,0) ) : 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 checking the changes of object properties, add the code block creating a new object of the graphical object change history provided that the flag allowing recording the change history is set:
//+------------------------------------------------------------------+ //| Check object property changes | //+------------------------------------------------------------------+ void CGStdGraphObj::PropertiesCheckChanged(void) { CGBaseObj::ClearEventsList(); bool changed=false; int begin=0, end=GRAPH_OBJ_PROP_INTEGER_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_INTEGER prop=(ENUM_GRAPH_OBJ_PROP_INTEGER)i; if(!this.SupportProperty(prop)) continue; for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j)) { changed=true; this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name()); } } } begin=end; end+=GRAPH_OBJ_PROP_DOUBLE_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_DOUBLE prop=(ENUM_GRAPH_OBJ_PROP_DOUBLE)i; if(!this.SupportProperty(prop)) continue; for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j)) { changed=true; this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name()); } } } begin=end; end+=GRAPH_OBJ_PROP_STRING_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_STRING prop=(ENUM_GRAPH_OBJ_PROP_STRING)i; if(!this.SupportProperty(prop)) continue; for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j) && prop!=GRAPH_OBJ_PROP_NAME) { changed=true; this.CreateAndAddNewEvent(GRAPH_OBJ_EVENT_CHANGE,this.ChartID(),prop,this.Name()); } } } if(changed) { for(int i=0;i<this.m_list_events.Total();i++) { CGBaseEvent *event=this.m_list_events.At(i); if(event==NULL) continue; ::EventChartCustom(::ChartID(),event.ID(),event.Lparam(),event.Dparam(),event.Sparam()); } if(this.AllowChangeHistory()) { int total=HistoryChangesTotal(); if(this.CreateNewChangeHistoryObj(total<1)) ::Print ( DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_SUCCESS_CREATE_SNAPSHOT)," #",(total==0 ? "0-1" : (string)total), ": ",this.HistoryChangedObjTimeChangedToString(total-1) ); } this.PropertiesCopyToPrevData(); } } //+------------------------------------------------------------------+
Here we get the amount of graphical object changes and pass them to the method of creating a new snapshot object of the object properties in the form of a bool flag (if 'total' is less than 1, the passed value is true, which means this is the first change of the graphical object). If the object is created and added to the change list, the appropriate message is displayed in the journal with the change index. If this is the first change, the message contains "0-1", which means two objects have been created at once (0 — graphical object status before changing its properties, 1 — object current status).
The methods setting integer, real and string property values from the change history for the graphical object:
//+------------------------------------------------------------------+ //| Set integer property values from the change history | //| for the graphical object | //+------------------------------------------------------------------+ void CGStdGraphObj::SetHistoryINT(const ENUM_GRAPH_OBJ_PROP_INTEGER prop,const long value,const int modifier) { switch(prop) { case GRAPH_OBJ_PROP_TIMEFRAMES : this.SetVisibleOnTimeframes((int)value,false); break; // Object visibility on timeframes case GRAPH_OBJ_PROP_BACK : this.SetFlagBack(value,false); break; // Background object case GRAPH_OBJ_PROP_ZORDER : this.SetZorder(value,false); break; // Priority of a graphical object for receiving the event of clicking on a chart case GRAPH_OBJ_PROP_HIDDEN : this.SetFlagHidden(value,false); break; // Disable displaying the name of a graphical object in the terminal object list case GRAPH_OBJ_PROP_SELECTED : this.SetFlagSelected(value,false); break; // Object selection case GRAPH_OBJ_PROP_SELECTABLE : this.SetFlagSelectable(value,false); break; // Object availability case GRAPH_OBJ_PROP_TIME : this.SetTime(value,modifier); break; // Time coordinate case GRAPH_OBJ_PROP_COLOR : this.SetColor((color)value); break; // Color case GRAPH_OBJ_PROP_STYLE : this.SetStyle((ENUM_LINE_STYLE)value); break; // Style case GRAPH_OBJ_PROP_WIDTH : this.SetWidth((int)value); break; // Line width case GRAPH_OBJ_PROP_FILL : this.SetFlagFill(value); break; // Filling an object with color case GRAPH_OBJ_PROP_READONLY : this.SetFlagReadOnly(value); break; // Ability to edit text in the Edit object case GRAPH_OBJ_PROP_LEVELS : this.SetLevels((int)value); break; // Number of levels case GRAPH_OBJ_PROP_LEVELCOLOR : this.SetLevelColor((color)value,modifier); break; // Level line color case GRAPH_OBJ_PROP_LEVELSTYLE : this.SetLevelStyle((ENUM_LINE_STYLE)value,modifier); break; // Level line style case GRAPH_OBJ_PROP_LEVELWIDTH : this.SetLevelWidth((int)value,modifier); break; // Level line width case GRAPH_OBJ_PROP_ALIGN : this.SetAlign((ENUM_ALIGN_MODE)value); break; // Horizontal text alignment in the Edit object (OBJ_EDIT) case GRAPH_OBJ_PROP_FONTSIZE : this.SetFontSize((int)value); break; // Font size case GRAPH_OBJ_PROP_RAY_LEFT : this.SetFlagRayLeft(value); break; // Ray goes to the left case GRAPH_OBJ_PROP_RAY_RIGHT : this.SetFlagRayRight(value); break; // Ray goes to the right case GRAPH_OBJ_PROP_RAY : this.SetFlagRay(value); break; // Vertical line goes through all windows of a chart case GRAPH_OBJ_PROP_ELLIPSE : this.SetFlagEllipse(value); break; // Display the full ellipse of the Fibonacci Arc object case GRAPH_OBJ_PROP_ARROWCODE : this.SetArrowCode((uchar)value); break; // Arrow code for the Arrow object case GRAPH_OBJ_PROP_ANCHOR : this.SetAnchor((int)value); break; // Position of the binding point of the graphical object case GRAPH_OBJ_PROP_XDISTANCE : this.SetXDistance((int)value); break; // Distance from the base corner along the X axis in pixels case GRAPH_OBJ_PROP_YDISTANCE : this.SetYDistance((int)value); break; // Distance from the base corner along the Y axis in pixels case GRAPH_OBJ_PROP_DIRECTION : this.SetDirection((ENUM_GANN_DIRECTION)value); break; // Gann object trend case GRAPH_OBJ_PROP_DEGREE : this.SetDegree((ENUM_ELLIOT_WAVE_DEGREE)value); break; // Elliott wave markup level case GRAPH_OBJ_PROP_DRAWLINES : this.SetFlagDrawLines(value); break; // Display lines for Elliott wave markup case GRAPH_OBJ_PROP_STATE : this.SetFlagState(value); break; // Button state (pressed/released) case GRAPH_OBJ_PROP_CHART_OBJ_CHART_ID : this.SetChartObjChartID(value); break; // Chart object ID (OBJ_CHART) case GRAPH_OBJ_PROP_CHART_OBJ_PERIOD : this.SetChartObjPeriod((ENUM_TIMEFRAMES)value); break; // Chart object period case GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE : this.SetChartObjChartScale((int)value); break; // Time scale display flag for the Chart object case GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE : this.SetFlagChartObjPriceScale(value); break; // Price scale display flag for the Chart object case GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE : this.SetFlagChartObjDateScale(value); break; // Chart object scale case GRAPH_OBJ_PROP_XSIZE : this.SetXSize((int)value); break; // Object distance along the X axis in pixels case GRAPH_OBJ_PROP_YSIZE : this.SetYSize((int)value); break; // Object height along the Y axis in pixels case GRAPH_OBJ_PROP_XOFFSET : this.SetXOffset((int)value); break; // X coordinate of the upper-left corner of the visibility area case GRAPH_OBJ_PROP_YOFFSET : this.SetYOffset((int)value); break; // Y coordinate of the upper-left corner of the visibility area case GRAPH_OBJ_PROP_BGCOLOR : this.SetBGColor((color)value); break; // Background color for OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL case GRAPH_OBJ_PROP_CORNER : this.SetCorner((ENUM_BASE_CORNER)value); break; // Chart corner for binding a graphical object case GRAPH_OBJ_PROP_BORDER_TYPE : this.SetBorderType((ENUM_BORDER_TYPE)value); break; // Border type for "Rectangle border" case GRAPH_OBJ_PROP_BORDER_COLOR : this.SetBorderColor((color)value); break; // Border color for the OBJ_EDIT and OBJ_BUTTON objects case GRAPH_OBJ_PROP_ID : // Object ID case GRAPH_OBJ_PROP_TYPE : // Graphical object type (ENUM_OBJECT) case GRAPH_OBJ_PROP_ELEMENT_TYPE : // Graphical element type (ENUM_GRAPH_ELEMENT_TYPE) case GRAPH_OBJ_PROP_SPECIES : // Graphical object species (ENUM_GRAPH_OBJ_SPECIES) case GRAPH_OBJ_PROP_GROUP : // Graphical object group case GRAPH_OBJ_PROP_BELONG : // Graphical object affiliation case GRAPH_OBJ_PROP_CHART_ID : // Chart ID case GRAPH_OBJ_PROP_WND_NUM : // Chart subwindow index case GRAPH_OBJ_PROP_NUM : // Object index in the list case GRAPH_OBJ_PROP_CHANGE_HISTORY : // Flag of storing the change history case GRAPH_OBJ_PROP_CREATETIME : // Object creation time default : break; } } //+------------------------------------------------------------------+ //| Set real property values from the change history | //| for the graphical object | //+------------------------------------------------------------------+ void CGStdGraphObj::SetHistoryDBL(const ENUM_GRAPH_OBJ_PROP_DOUBLE prop,const double value,const int modifier) { switch(prop) { case GRAPH_OBJ_PROP_PRICE : this.SetPrice(value,modifier); break; // Price coordinate case GRAPH_OBJ_PROP_LEVELVALUE : this.SetLevelValue(value,modifier); break; // Level value case GRAPH_OBJ_PROP_SCALE : this.SetScale(value); break; // Scale (property of Gann objects and Fibonacci Arcs objects) case GRAPH_OBJ_PROP_ANGLE : this.SetAngle(value); break; // Angle case GRAPH_OBJ_PROP_DEVIATION : this.SetDeviation(value); break; // Deviation of the standard deviation channel default: break; } } //+------------------------------------------------------------------+ //| Set string property values from the change history | //| for the graphical object | //+------------------------------------------------------------------+ void CGStdGraphObj::SetHistorySTR(const ENUM_GRAPH_OBJ_PROP_STRING prop,const string value,const int modifier) { switch(prop) { case GRAPH_OBJ_PROP_TEXT : this.SetText(value); break; // Object description (the text contained in the object) case GRAPH_OBJ_PROP_TOOLTIP : this.SetTooltip(value); break; // Tooltip text case GRAPH_OBJ_PROP_LEVELTEXT : this.SetLevelText(value,modifier); break; // Level description case GRAPH_OBJ_PROP_FONT : this.SetFont(value); break; // Font case GRAPH_OBJ_PROP_BMPFILE : this.SetBMPFile(value,modifier); break; // BMP file name for the "Bitmap Level" object case GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL : this.SetChartObjSymbol(value); break; // Chart object symbol case GRAPH_OBJ_PROP_NAME : // Object name default : break; } } //+------------------------------------------------------------------+
Here, depending on the property passed to the method, the appropriate method for setting the object property value both in the class object and in the graphical object is selected in the 'switch' operator. The properties that are not necessary for the property change history object do not have their case handler, therefore the code execution reaches the default label and ends with the break operator.
In the graphical element collection class in \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh, add the method returning the list of objects by chart ID and group:
//--- Return (1) the last removed graphical object and (2) the array size of graphical object properties CGStdGraphObj *GetLastDeletedGraphObj(void) const { return this.m_list_deleted_obj.At(this.m_list_deleted_obj.Total()-1); } int GetSizeProperty(const string name,const long chart_id,const int prop) { CGStdGraphObj *obj=this.GetStdGraphObject(name,chart_id); return(obj!=NULL ? obj.Properties().CurrSize(prop) : 0); } //--- Return the list of objects by chart ID and group CArrayObj *GetListStdGraphObjByGroup(const long chart_id,const int group) { CArrayObj *list=GetList(GRAPH_OBJ_PROP_CHART_ID,0,chart_id,EQUAL); return CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_GROUP,0,group,EQUAL); } //--- Constructor
The method receives the list of all graphical objects by chart ID and returns the list of objects with the specified group value from the obtained list.
Using the method in our programs allows us to get the list of graphical objects with a single group assigned to them to handle the objects in the way we need.
To get the entire change history data in library-based programs, we need to make changes in the CEngine library main object in \MQL5\Include\DoEasy\Engine.mqh.
Add the method returning the list of existing graphical objects and the method returning the pointer to the class of the object of the standard graphical object by chart name and ID:
//--- Return the (1) collection of graphical objects, the list of (2) existing and (3) removed graphical objects CGraphElementsCollection *GetGraphicObjCollection(void) { return &this.m_graph_objects; } CArrayObj *GetListStdGraphObj(void) { return this.m_graph_objects.GetListGraphObj(); } CArrayObj *GetListDeletedObj(void) { return this.m_graph_objects.GetListDeletedObj(); } //--- Return (1) the number of removed graphical objects and (2) the size of the property array int TotalDeletedGraphObjects(void) { return this.GetListDeletedObj().Total(); } int GraphGetSizeProperty(const string name,const long chart_id,const int prop) { return this.m_graph_objects.GetSizeProperty(name,chart_id,prop); } //--- 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) { return this.m_graph_objects.GetStdGraphObject(name,chart_id); } //--- Fill in the array with IDs of the charts opened in the terminal
The methods return the result of calling same-name methods of the graphical element collection class.
These are all the changes and improvements of library classes that are necessary to test handling the graphical object change history.
Test
To perform the test, let's use the EA from the previous article and save it to \MQL5\Experts\TestDoEasy\Part92\ as TestDoEasyPart92.mq5.
The flag allowing saving the change history is set for each newly created graphical object on the chart and the group 1 is created. Thus, all graphical objects to be added to the chart fall into one group and are allowed to write their change history. Next, change each graphical object. All changes will be written to its memory.
Set the keys for viewing the change history.
- ">" ("." without Shift) moves the index in the object change list by 1 in the direction of its increase,
- "<" ("," without Shift) moves the index in the object change list by 1 in the direction of its decrease,
- "/" moves the index in the object change list to the very beginning — the object of the graphical object properties is to be stored there with its initial values that were present before its first change.
By pressing the keys, we will see how graphical objects receive all the properties they had every time they were changed.
Set the macro substitutions for the specified buttons:
//+------------------------------------------------------------------+ //| TestDoEasyPart92.mq5 | //| 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" //--- includes #include <DoEasy\Engine.mqh> //--- defines #define FORMS_TOTAL (4) // Number of created forms #define START_X (4) // Initial X coordinate of the shape #define START_Y (4) // Initial Y coordinate of the shape #define KEY_LEFT (188) // Left #define KEY_RIGHT (190) // Right #define KEY_ORIGIN (191) // Initial properties //--- input parameters sinput bool InpMovable = true; // Movable forms flag sinput ENUM_INPUT_YES_NO InpUseColorBG = INPUT_YES; // Use chart background color to calculate shadow color sinput color InpColorForm3 = clrCadetBlue; // Third form shadow color (if not background color) //--- global variables CEngine engine; CArrayObj list_forms; color array_clr[]; //+------------------------------------------------------------------+
In the event handler, add the code block handling keystrokes. In the block handling the graphical object creation event, add setting the flag allowing saving the change history and group 1:
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- If working in the tester, exit if(MQLInfoInteger(MQL_TESTER)) return; //--- If the mouse is moved /* if(id==CHARTEVENT_MOUSE_MOVE) { CForm *form=NULL; datetime time=0; double price=0; int wnd=0; //--- If Ctrl is not pressed, if(!IsCtrlKeyPressed()) { //--- clear the list of created form objects, allow scrolling a chart with the mouse and show the context menu list_forms.Clear(); ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,true); ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,true); return; } //--- If X and Y chart coordinates are successfully converted into time and price, if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price)) { //--- get the bar index the cursor is hovered over int index=iBarShift(Symbol(),PERIOD_CURRENT,time); if(index==WRONG_VALUE) return; //--- Get the bar index by index CBar *bar=engine.SeriesGetBar(Symbol(),Period(),index); if(bar==NULL) return; //--- Convert the coordinates of a chart from the time/price representation of the bar object to the X and Y coordinates int x=(int)lparam,y=(int)dparam; if(!ChartTimePriceToXY(ChartID(),0,bar.Time(),(bar.Open()+bar.Close())/2.0,x,y)) return; //--- Disable moving a chart with the mouse and showing the context menu ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,false); ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,false); //--- Create the form object name and hide all objects except one having such a name string name="FormBar_"+(string)index; HideFormAllExceptOne(name); //--- If the form object with such a name does not exist yet, if(!IsPresentForm(name)) { //--- create a new form object form=bar.CreateForm(index,name,x,y,114,16); if(form==NULL) return; //--- Set activity and unmoveability flags for the form form.SetActive(true); form.SetMovable(false); //--- Set the opacity of 200 form.SetOpacity(200); //--- The form background color is set as the first color from the color array form.SetColorBackground(array_clr[0]); //--- Form outlining frame color form.SetColorFrame(C'47,70,59'); //--- Draw the shadow drawing flag form.SetShadow(true); //--- Calculate the shadow color as the chart background color converted to the monochrome one color clrS=form.ChangeColorSaturation(form.ColorBackground(),-100); //--- If the settings specify the usage of the chart background color, replace the monochrome color with 20 units //--- Otherwise, use the color specified in the settings for drawing the shadow color clr=(InpUseColorBG ? form.ChangeColorLightness(clrS,-20) : InpColorForm3); //--- Draw the form shadow with the right-downwards offset from the form by three pixels along all axes //--- Set the shadow opacity to 200, while the blur radius is equal to 4 form.DrawShadow(2,2,clr,200,3); //--- Fill the form background with a vertical gradient form.Erase(array_clr,form.Opacity()); //--- Draw an outlining rectangle at the edges of the form form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,form.ColorFrame(),form.Opacity()); //--- If failed to add the form object to the list, remove the form and exit the handler if(!list_forms.Add(form)) { delete form; return; } //--- Capture the form appearance form.Done(); } //--- If the form object exists, if(form!=NULL) { //--- draw a text with the bar type description on it and show the form. The description corresponds to the mouse cursor position form.TextOnBG(0,bar.BodyTypeDescription(),form.Width()/2,form.Height()/2-1,FRAME_ANCHOR_CENTER,C'7,28,21'); form.Show(); } //--- Re-draw the chart ChartRedraw(); } } */ //--- If a key is pressed if(id==CHARTEVENT_KEYDOWN) { //---Declare the index of the current graphical object change history object static int index=0; //--- Get the list of all graphical objects with the specified group index (1) CArrayObj *list=engine.GetListStdGraphObj(); list=CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_GROUP,0,1,EQUAL); if(list==NULL || list.Total()==0) return; //--- If "/" is pressed if(lparam==KEY_ORIGIN) { //--- Set the index 0 in the list of the graphical object change history object index=0; //--- In the loop by the number of group 1 objects on the chart for(int i=0;i<list.Total();i++) { //--- Get the next object from the list and set the initial properties from the change history to it CGStdGraphObj *obj=list.At(i); if(obj==NULL) continue; obj.SetPropertiesFromHistory(index); } } //--- If "." is pressed if(lparam==KEY_RIGHT) { //--- Declare the variables to search for the maximum number of changes of all group 1 graphical objects int change_max=0, changes_total=0; //--- Increase the object index in the list of the graphical object change history index++; //--- In the loop by the number of group 1 objects on the chart for(int i=0;i<list.Total();i++) { //--- Get the next object from the list CGStdGraphObj *obj=list.At(i); if(obj==NULL) continue; //--- Calculate the maximum number of changes of all group 1 graphical objects changes_total=obj.HistoryChangesTotal(); if(changes_total>change_max) change_max=changes_total; //--- Set the properties (by 'index' from the list) from the change history obj.SetPropertiesFromHistory(index>obj.HistoryChangesTotal()-1 ? obj.HistoryChangesTotal()-1 : index); } //--- If the change history object index exceeds the maximum number of changes of all objects, //--- set the index equal to the maximum amount of changes of all graphical objects if(index>change_max-1) index=change_max-1; } //--- If "," is pressed if(lparam==KEY_LEFT) { //--- Decrease the object index in the list of the graphical object change history index--; //--- If the index is less than zero, set it to 0 if(index<0) index=0; //--- In the loop by the number of group 1 objects on the chart for(int i=0;i<list.Total();i++) { //--- Get the next object from the list CGStdGraphObj *obj=list.At(i); if(obj==NULL) continue; //--- Set the properties (by 'index' from the list) from the change history obj.SetPropertiesFromHistory(index); } } //--- Re-draw the chart for displaying changes in graphical objects ChartRedraw(); } if(id==CHARTEVENT_CLICK) { if(!IsCtrlKeyPressed()) return; datetime time=0; double price=0; int sw=0; if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,sw,time,price)) { long array[]; engine.GraphGetArrayChartsID(array); for(int i=0;i<ArraySize(array);i++) engine.CreateLineVertical(array[i],"LineVertical",0,time); } } engine.GetGraphicObjCollection().OnChartEvent(id,lparam,dparam,sparam); //--- Handle standard graphical object events ushort idx=ushort(id-CHARTEVENT_CUSTOM); CGStdGraphObj *obj=NULL; if(idx>GRAPH_OBJ_EVENT_NO_EVENT && idx<GRAPH_OBJ_EVENTS_NEXT_CODE) { CChartObjectsControl *chart_ctrl=NULL; int end=0; string evn=""; //--- Depending on the event type, display an appropriate message in the journal switch(idx) { //--- Graphical object creation event case GRAPH_OBJ_EVENT_CREATE : //--- Display the message about creating a new graphical object Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CREATE),":"); //--- Get the pointer to the object by chart name and ID passed to sparam and lparam, respectively //--- display the short description of a newly created object to the journal and set the flag of storing the change history obj=engine.GraphGetStdGraphObject(sparam,lparam); if(obj!=NULL) { obj.PrintShort(); obj.SetAllowChangeMemory(true); obj.SetGroup(1); } break; //--- Event of changing the graphical object property case GRAPH_OBJ_EVENT_CHANGE : //--- Display the message about changing the graphical object property Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_CHANGE),":"); //--- Get the pointer to the object by chart name and ID passed to sparam and lparam, respectively obj=engine.GetGraphicObjCollection().GetStdGraphObject(sparam,lparam); if(obj!=NULL) { //--- Display a short description of the changed object in the journal obj.PrintShort(); //--- calculate the code of the changed property passed to dparam and get the property description if(dparam<GRAPH_OBJ_PROP_INTEGER_TOTAL) evn=obj.GetPropertyDescription((ENUM_GRAPH_OBJ_PROP_INTEGER)dparam); else if(dparam<GRAPH_OBJ_PROP_INTEGER_TOTAL+GRAPH_OBJ_PROP_DOUBLE_TOTAL) evn=obj.GetPropertyDescription((ENUM_GRAPH_OBJ_PROP_DOUBLE)dparam); else evn=obj.GetPropertyDescription((ENUM_GRAPH_OBJ_PROP_STRING)dparam); //--- Display the description of the graphical object's changed property in the journal Print(DFUN,evn); } break; //--- Graphical object renaming event case GRAPH_OBJ_EVENT_RENAME : //--- Display the message about renaming the graphical object Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_RENAME)); //--- Get the pointer to the object by chart name and ID passed to sparam and lparam, respectively obj=engine.GetGraphicObjCollection().GetStdGraphObject(sparam,lparam); if(obj!=NULL) { //--- Display the previous and new object name, as well as its entire renaming history, in the journal Print(DFUN,obj.GetProperty(GRAPH_OBJ_PROP_NAME,obj.Properties().CurrSize(GRAPH_OBJ_PROP_NAME)-1)," >>> ",obj.GetProperty(GRAPH_OBJ_PROP_NAME,0)); obj.PrintRenameHistory(); } break; //--- Graphical object deletion event case GRAPH_OBJ_EVENT_DELETE : //--- Display the message about removing the graphical object Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DELETE),":"); //--- Get the pointer to the removed object by chart name and ID passed to sparam and lparam, respectively //--- and display a short description of the removed object in the journal obj=engine.GetGraphicObjCollection().GetStdDelGraphObject(sparam,lparam); if(obj!=NULL) { obj.PrintShort(); } break; //--- Event of removing the graphical object together with the chart window case GRAPH_OBJ_EVENT_DEL_CHART: //--- Display the message about removing graphical objects together with the chart window, whose ID and symbol are passed to lparam and sparam Print(DFUN,CMessage::Text(MSG_GRAPH_OBJ_EVN_GRAPH_OBJ_DEL_CHART),": #",lparam,", ",sparam,":"); //--- Calculate the end value for the loop by the list of removed graphical objects end=engine.TotalDeletedGraphObjects()-(int)dparam; if(end<0) end=0; //--- In the loop from the end of the removed graphical objects list up to the value calculated in the 'end' variable, for(int i=engine.TotalDeletedGraphObjects()-1;i>=end;i--) { //--- get the next removed graphical object from the list obj=engine.GetListDeletedObj().At(i); if(obj==NULL) continue; //--- and display its brief description in the journal obj.PrintShort(); } break; //--- default: break; } } } //+------------------------------------------------------------------+
The entire logic of handling keystrokes is described in the comments to the code.
Compile the EA and launch it on the chart. Add the graphical objects, modify their properties and press "/" — the objects will take on the values that were before their first modification. Press "." and "," — the objects will take on the properties and appearance corresponding to the property change history list:
What's next?
In the next article, I will start the development of composite graphical objects.
*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
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/10237
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use