Graphics in DoEasy library (Part 89): Programming standard graphical objects. Basic functionality
Contents
- Concept
- Improving library classes
- Methods for programming standard graphical objects
- Test
- What's next?
Concept
Currently, the library is able to track standard graphical objects on the client terminal chart, including their removal and modification of some of their parameters. Sometimes, it is useful to know about newly set, modified or removed custom graphical objects on a chart directly from your program. However, at the moment, we lack the ability to create standard graphical objects from custom programs. Having the functionality for programming graphical objects and tracking changes in their properties, we are able to create composite graphical objects of any complexity, degree of nesting and the number of controlled pivot points. In this article, I will create the basic functionality for programming standard graphical objects. I will refine the functionality in the coming articles while considering the ability to create custom composite graphical objects based on the standard ones.
Besides, I will gradually make so that library objects use dynamic arrays for storing their properties. In fact, I have already started doing that in the previous article. Here I will fix a logical error made in the previous article causing the inability to track property changes of objects having more than two pivot points. Besides, I will fix and improve the class of a multidimensional dynamic array, so that it can be used as a separate unit of the library, and move it to a separate file.
Improving library classes
The descendant classes of an abstract graphical object need some properties supported by the object so that these properties can be considered when searching, sorting and displaying the properties in the terminal journal. In the GStdFiboArcObj.mqh and GStdGannFanObj.mqh files, add the string for supporting the "Level value" real property by the object:
//+------------------------------------------------------------------+ //| Return 'true' if an object supports a passed | //| real property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CGStdFiboArcObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property) { switch((int)property) { //--- Supported properties case GRAPH_OBJ_PROP_SCALE : case GRAPH_OBJ_PROP_PRICE : case GRAPH_OBJ_PROP_LEVELVALUE : return true; //--- Other properties are not supported //--- Default is 'false' default: break; } return false; } //+------------------------------------------------------------------+
In the GStdExpansionObj.mqh, GStdFiboChannelObj.mqh, GStdFiboFanObj.mqh, GStdFiboObj.mqh, GStdFiboTimesObj.mqh and GStdPitchforkObj.mqh files, make the following changes in the same method to support the same property:
//+------------------------------------------------------------------+ //| Return 'true' if an object supports a passed | //| real property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CGStdPitchforkObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property) { switch((int)property) { //--- Supported properties case GRAPH_OBJ_PROP_PRICE : case GRAPH_OBJ_PROP_LEVELVALUE : return true; //--- Other properties are not supported //--- Default is 'false' default: break; } return false; } //+------------------------------------------------------------------+
In the GStdHLineObj.mqh file of the "Horizontal line" object class, remove the "Pivot point time" property from the method returning the flag of the object supporting the integer property since the object uses only the price for construction:
//+------------------------------------------------------------------+ //| Return 'true' if an object supports a passed | //| integer property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CGStdHLineObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property) { switch((int)property) { //--- Supported properties case GRAPH_OBJ_PROP_ID : case GRAPH_OBJ_PROP_TYPE : case GRAPH_OBJ_PROP_ELEMENT_TYPE : case GRAPH_OBJ_PROP_GROUP : case GRAPH_OBJ_PROP_BELONG : case GRAPH_OBJ_PROP_CHART_ID : case GRAPH_OBJ_PROP_WND_NUM : case GRAPH_OBJ_PROP_NUM : case GRAPH_OBJ_PROP_CREATETIME : case GRAPH_OBJ_PROP_TIMEFRAMES : case GRAPH_OBJ_PROP_BACK : case GRAPH_OBJ_PROP_ZORDER : case GRAPH_OBJ_PROP_HIDDEN : case GRAPH_OBJ_PROP_SELECTED : case GRAPH_OBJ_PROP_SELECTABLE : case GRAPH_OBJ_PROP_TIME : case GRAPH_OBJ_PROP_COLOR : case GRAPH_OBJ_PROP_STYLE : case GRAPH_OBJ_PROP_WIDTH : return true; //--- Other properties are not supported //--- Default is 'false' default: break; } return false; } //+------------------------------------------------------------------+
The "Vertical line" object in the GStdVLineObj.mqh file uses only time for its construction. Therefore, I will remove everything from the method returning the flag of the object supporting a real property — the object does not support real properties:
//+------------------------------------------------------------------+ //| Return 'true' if an object supports a passed | //| real property, otherwise return 'false' | //+------------------------------------------------------------------+ bool CGStdVLineObj::SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property) { return false; } //+------------------------------------------------------------------+
In the previous article, I skipped one logical error preventing us from controlling the changes in the properties of object pivot points and levels in cases when two pivot points are used for the object construction or the object features more than two levels. It was mainly about the method adding the specified number of cells to the end of the array. The method received the pointer to an object created outside, while the array received the specified number of these pointers:
//--- Add the specified number of cells with objects to the end of the array bool AddQuantity(const string source,const int total,CObject *object) { //--- Declare the variable for storing the result of adding objects to the list bool res=true; //--- in the list by the number of added objects passed to the method for(int i=0;i<total;i++) { //--- if failed to add the object to the list if(!this.Add(object)) { //--- display the appropriate message, add 'false' to the variable value //--- and move on to the loop next iteration CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); res &=false; continue; } } //--- Return the total result of adding the specified number of objects to the list return res; }
However, this is the same object! We simply pass the pointer to an object created outside the method and multiply the pointers to it in a loop. Therefore, changing the properties of the object itself affects all the pointers — they also refer to the same object. Thus, we filled the array with instances of the same object property rather than with different properties. If the object featured several reference points, we multiplied the pivot point by the specified number. Instead of managing the real values of the second, third, fourth and fifth points, we added the property of the second pivot point to the array. This prevented us from getting, tracking and changing the values of the real reference point. Changing the second pivot point led these changes to be copied to the third, fourth and fifth object points.
Let's add one more method for creating a new data object. This method will create a new property object. We will add this new property (rather than the pointer) to the array in the AddQuantity() method, in which we simply added the pointer created from the outside in the specified amount to the array.
Since we have three sets of identical classes in \MQL5\Include\DoEasy\Services\XDimArray.mqh — the classes for creating dynamic multidimensional integer, real and string arrays, let's consider this using the classes for creating an integer multidimensional dynamic array as an example.
In the class of one long array dimension, write the method for creating a new data object:
//+------------------------------------------------------------------+ //| Class of a single long array dimension | //+------------------------------------------------------------------+ class CDimLong : public CArrayObj { private: //--- Create a new data object CDataUnitLong *CreateData(const string source,const long value=0) { //--- Create a new long data object CDataUnitLong *data=new CDataUnitLong(); //--- If failed to create an object, inform of that in the journal if(data==NULL) ::Print(source,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_LONG_DATA_OBJ)); //--- Otherwise, set the value passed to the method for the object else data.Value=value; //--- Return the pointer to the object or NULL return data; } //--- Get long data object from the array
Here all is simple: we create a new object of long data and set the value passed to the method for it. If failed to create an object, inform of that in the journal. The method returns the pointer to the created object or NULL in case of an error.
Add the changes to the AddQuantity() method:
//--- Add the specified number of cells with data to the end of the array bool AddQuantity(const string source,const int total,const long value=0) { //--- Declare the variable for storing the result of adding objects to the list bool res=true; //--- in the list by the number of added objects passed to the method for(int i=0;i<total;i++) { //--- Create a new long data object CDataUnitLong *data=this.CreateData(DFUN,value); //--- If failed to create an object, inform of that and move on to the next iteration if(data==NULL) { res &=false; continue; } data.Value=value; //--- if failed to add the object to the list if(!this.Add(data)) { //--- display the appropriate message, remove the object and add 'false' to the variable value //--- and move on to the loop next iteration CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); delete data; res &=false; continue; } } //--- Return the total result of adding the specified number of objects to the list return res; }
Now instead of the pointer to the object, the method receives the value to be assigned to a newly created property. In the loop, create a new object in the loop and add it to the list.
In the Increase() method, in which I have previously created a new object and passed the pointer to it to the AddQuantity() method, simply call the AddQuantity() method since new objects in the loop are now created and added to the array inside the AddQuantity() method, rather than a pointer to a single object previously created in the Increase() method.
Let's remove the following code block from the method:
//--- Increase the number of data cells by the specified value, return the number of added elements int Increase(const int total,const long value=0) { //--- Save the current array size int size_prev=this.Total(); //--- Create a new long data object CDataUnitLong *data=new CDataUnitLong(); //--- If failed to create an object, inform of that and return zero if(data==NULL) { ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_LONG_DATA_OBJ)); return 0; } //--- Set the specified value to a newly created object data.Value=value; //--- Add the specified number of object instances to the list //--- and return the difference between the obtained and previous array size this.AddQuantity(DFUN,total,data); return this.Total()-size_prev; }
The called AddQuantity() method now receives an initial value for a data object newly added to the array rather than a pointer:
//--- Increase the number of data cells by the specified value, return the number of added elements int Increase(const int total,const long value=0) { //--- Save the current array size int size_prev=this.Total(); //--- Add the specified number of object instances to the list //--- and return the difference between the obtained and previous array size this.AddQuantity(DFUN,total,value); return this.Total()-size_prev; }
The same changes were done to the remaining classes of the file. I am not going to repeat them here since they are identical.
You can view all the changes in the files attached below.
In \MQL5\Include\DoEasy\Data.mqh, add new message indices:
//--- CGraphElementsCollection MSG_GRAPH_ELM_COLLECTION_ERR_OBJ_ALREADY_EXISTS, // Error. A chart control object already exists with chart id MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_CREATE_CTRL_OBJ,// Failed to create chart control object with chart ID MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_GET_CTRL_OBJ, // Failed to get chart control object with chart ID MSG_GRAPH_ELM_COLLECTION_ERR_GR_OBJ_ALREADY_EXISTS,// Such graphical object already exists: //--- GStdGraphObj MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ, // Failed to create the class object for a graphical object MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_STD_GRAPH_OBJ, // Failed to create a graphical object MSG_GRAPH_STD_OBJ_ERR_NOT_FIND_SUBWINDOW, // Failed to find the chart subwindow
...
MSG_GRAPH_OBJ_TEXT_BMP_FILE_STATE_ON, // On state MSG_GRAPH_OBJ_TEXT_BMP_FILE_STATE_OFF, // Off state //--- CDataPropObj MSG_DATA_PROP_OBJ_OUT_OF_PROP_RANGE, // Passed property is out of object property range //--- CGraphElementsCollection MSG_GRAPH_OBJ_FAILED_GET_ADDED_OBJ_LIST, // Failed to get the list of newly added objects MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST, // Failed to remove a graphical object from the list MSG_GRAPH_OBJ_CREATE_EVN_CTRL_INDICATOR, // Indicator for controlling and sending events created MSG_GRAPH_OBJ_FAILED_CREATE_EVN_CTRL_INDICATOR, // Failed to create the indicator for controlling and sending events MSG_GRAPH_OBJ_CLOSED_CHARTS, // Chart windows closed: MSG_GRAPH_OBJ_OBJECTS_ON_CLOSED_CHARTS, // Objects removed together with charts: }; //+------------------------------------------------------------------+
and message texts corresponding to newly added indices:
//--- CGraphElementsCollection {"Ошибка. Уже существует объект управления чартами с идентификатором чарта ","Error. A chart control object already exists with chart id "}, {"Не удалось создать объект управления чартами с идентификатором чарта ","Failed to create chart control object with chart id "}, {"Не удалось получить объект управления чартами с идентификатором чарта ","Failed to get chart control object with chart id "}, {"Такой графический объект уже существует: ","Such a graphic object already exists: "}, //--- GStdGraphObj {"Не удалось создать объект класса для графического объекта ","Failed to create class object for graphic object"}, {"Не удалось создать графический объект ","Failed to create graphic object "}, {"Не удалось найти подокно графика","Could not find chart subwindow"},
...
{"Состояние \"On\"","State \"On\""}, {"Состояние \"Off\"","State \"Off\""}, //--- CDataPropObj {"Переданное свойство находится за пределами диапазона свойств объекта","The passed property is outside the range of the object's properties"}, //--- CGraphElementsCollection {"Не удалось получить список вновь добавленных объектов","Failed to get the list of newly added objects"}, {"Не удалось изъять графический объект из списка","Failed to detach graphic object from the list"}, {"Создан индикатор контроля и отправки событий","An indicator for monitoring and sending events has been created"}, {"Не удалось создать индикатор контроля и отправки событий","Failed to create indicator for monitoring and sending events"}, {"Закрыто окон графиков: ","Closed chart windows: "}, {"С ними удалено объектов: ","Objects removed with them: "}, }; //+---------------------------------------------------------------------+
Let's make minor improvements in the class of the base object of the library graphical objects.
The graphical objects feature bool properties returning the flags of some object properties. The abstract graphical object class has the methods returning and setting such flags. The method names indicate that the method sets the flag, for example:
SetFlagDrawLines (Display lines for Elliott wave marking). In the class of the base object of the library graphical objects, a corresponding method is called SetDrawLines(). Thus, if we try to set the flag for an object, we will see two hints for choosing a method. This is confusing. Besides, if we select the method of the base graphical object rather than abstract one, no change set in the object arrays will be made in the properties. Instead, we will simply give a command to change a property in the graphical object itself. The appropriate property in the class object will remain unchanged. This means we need to rename all such methods to avoid mistakes when selecting out of two methods. I believe, later it will be necessary to unify the return types of these methods as well so that the compiler is able to select the necessary method unambiguously.
Let's introduce the necessary fixes in \MQL5\Include\DoEasy\Objects\Graph\GBaseObj.mqh:
//--- Set the "Background object" flag bool SetFlagBack(const bool flag) { ::ResetLastError(); if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_BACK,flag)) { this.m_back=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Set the "Object selection" flag bool SetFlagSelected(const bool flag) { ::ResetLastError(); if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTED,flag)) { this.m_selected=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Set the "Object selection" flag bool SetFlagSelectable(const bool flag) { ::ResetLastError(); if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTABLE,flag)) { this.m_selectable=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; } //--- Set the "Disable displaying the name of a graphical object in the terminal object list" flag bool SetFlagHidden(const bool flag) { ::ResetLastError(); if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTABLE,flag)) { this.m_hidden=flag; return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); return false; }
Let's move the classes for creating a multidimensional dynamic array to a separate file. I will improve them so that they turn into a tool for creating property objects for any existing or planned object of the library applying arrays for storing its properties (integer, real and string ones).
In the \MQL5\Include\DoEasy\Services\ folder of service classes and functions, create a new file Properties.mqh. It is to get all classes for creating a two-dimensional array of object properties and a properties object (both previous and current ones) set in the previous article directly in the class body of the CGStdGraphObj abstract standard graphical object:
//+------------------------------------------------------------------+ //| The class of the abstract standard graphical object | //+------------------------------------------------------------------+ class CGStdGraphObj : public CGBaseObj { private: //--- Object property class class CDataPropObj { private: CArrayObj m_list; // list of property objects int m_total_int; // Number of integer parameters int m_total_dbl; // Number of real parameters int m_total_str; // Number of string parameters //--- Return the index of the array the (1) double and (2) string properties are actually located at int IndexProp(ENUM_GRAPH_OBJ_PROP_DOUBLE property) const { return(int)property-this.m_total_int; } int IndexProp(ENUM_GRAPH_OBJ_PROP_STRING property) const { return(int)property-this.m_total_int-this.m_total_dbl; } public: //--- Return the pointer to (1) the list of property objects, as well as to the object of (2) integer, (3) real and (4) string properties CArrayObj *GetList(void) { return &this.m_list; } CXDimArrayLong *Long() const { return this.m_list.At(0); } CXDimArrayDouble *Double() const { return this.m_list.At(1); } CXDimArrayString *String() const { return this.m_list.At(2); } //--- Set object's (1) integer, (2) real and (3) string properties void Set(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value) { this.Long().Set(property,index,value); } void Set(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value) { this.Double().Set(this.IndexProp(property),index,value); } void Set(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value) { this.String().Set(this.IndexProp(property),index,value); } //--- Return object’s (1) integer, (2) real and (3) string property from the properties array long Get(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index) const { return this.Long().Get(property,index); } double Get(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index) const { return this.Double().Get(this.IndexProp(property),index); } string Get(ENUM_GRAPH_OBJ_PROP_STRING property,int index) const { return this.String().Get(this.IndexProp(property),index); } //--- Return the size of the specified first dimension data array int Size(const int range) const { if(range<this.m_total_int) return this.Long().Size(range); else if(range<this.m_total_int+this.m_total_dbl) return this.Double().Size(this.IndexProp((ENUM_GRAPH_OBJ_PROP_DOUBLE)range)); else if(range<this.m_total_int+this.m_total_dbl+this.m_total_str) return this.String().Size(this.IndexProp((ENUM_GRAPH_OBJ_PROP_STRING)range)); return 0; } //--- Set the array size in the specified dimensionality bool SetSizeRange(const int range,const int size) { if(range<this.m_total_int) return this.Long().SetSizeRange(range,size); else if(range<this.m_total_int+this.m_total_dbl) return this.Double().SetSizeRange(this.IndexProp((ENUM_GRAPH_OBJ_PROP_DOUBLE)range),size); else if(range<this.m_total_int+this.m_total_dbl+this.m_total_str) return this.String().SetSizeRange(this.IndexProp((ENUM_GRAPH_OBJ_PROP_STRING)range),size); return false; } //--- Constructor CDataPropObj(const int prop_total_integer,const int prop_total_double,const int prop_total_string) { this.m_total_int=prop_total_integer; this.m_total_dbl=prop_total_double; this.m_total_str=prop_total_string; this.m_list.Add(new CXDimArrayLong(this.m_total_int, 1)); this.m_list.Add(new CXDimArrayDouble(this.m_total_dbl,1)); this.m_list.Add(new CXDimArrayString(this.m_total_str,1)); } //--- Destructor ~CDataPropObj() { m_list.Clear(); m_list.Shutdown(); } }; //--- Data class of the current and previous properties class CProperty { public: CDataPropObj *Curr; // Pointer to the current properties object CDataPropObj *Prev; // Pointer to the previous properties object //--- Set the array size ('size') in the specified dimension ('range') bool SetSizeRange(const int range,const int size) { return(this.Curr.SetSizeRange(range,size) && this.Prev.SetSizeRange(range,size) ? true : false); } //--- Return the size of the specified array of the (1) current and (2) previous first dimension data int CurrSize(const int range) const { return Curr.Size(range); } int PrevSize(const int range) const { return Prev.Size(range); } //--- Copy the current data to the previous one void CurrentToPrevious(void) { //--- Copy all integer properties for(int i=0;i<this.Curr.Long().Total();i++) for(int r=0;r<this.Curr.Long().Size(i);r++) this.Prev.Long().Set(i,r,this.Curr.Long().Get(i,r)); //--- Copy all real properties for(int i=0;i<this.Curr.Double().Total();i++) for(int r=0;r<this.Curr.Double().Size(i);r++) this.Prev.Double().Set(i,r,this.Curr.Double().Get(i,r)); //--- Copy all string properties for(int i=0;i<this.Curr.String().Total();i++) for(int r=0;r<this.Curr.String().Size(i);r++) this.Prev.String().Set(i,r,this.Curr.String().Get(i,r)); } //--- Constructor CProperty(const int prop_int_total,const int prop_double_total,const int prop_string_total) { this.Curr=new CDataPropObj(prop_int_total,prop_double_total,prop_string_total); this.Prev=new CDataPropObj(prop_int_total,prop_double_total,prop_string_total); } };
We need to get rid of references to any enumerations belonging to a particular class object to make the class universal.
To achieve this, replace all the enumerations used to select the methods returning a real property index with ordinary int variables. To calculate the property indices, pass the maximum property for real and string properties to the class constructors (an integer property does not shift its value and fully corresponds to the property index). Next, simply calculate the real values based on the property value and the maximum value of properties. As usual, the idea looks more clear when displayed as a code.
Add the following classes in the newly added file \MQL5\Include\DoEasy\Services\Properties.mqh:
//+------------------------------------------------------------------+ //| Properties.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "XDimArray.mqh" //+------------------------------------------------------------------+ //| Object property class | //+------------------------------------------------------------------+ //--- Object property class class CDataPropObj : public CObject { private: CArrayObj m_list; // list of property objects int m_total_int; // Number of integer parameters int m_total_dbl; // Number of real parameters int m_total_str; // Number of string parameters int m_prop_max_dbl; // Maximum possible real property value int m_prop_max_str; // Maximum possible string property value //--- Return the index of the array the int, double or string property is actually located at int IndexProp(int property) const { //--- If the passed value is less than the number of integer parameters, //--- this is an integer property. Return the value passed to the method if(property<this.m_total_int) return property; //--- Otherwise if the passed value is less than the maximum possible real property value, //--- then this is a real property - return the calculated index in the array of real properties else if(property<this.m_prop_max_dbl) return property-this.m_total_int; //--- Otherwise if the passed value is less than the maximum possible string property value, //--- then this is a string property - return the calculated index in the array of string properties else if(property<this.m_prop_max_str) return property-this.m_total_int-this.m_total_dbl; //--- Otherwise, if the passed value exceeds the maximum range of all values of all properties, //--- inform of this in the journal and return INT_MAX causing the error //--- accessing the array in XDimArray file classes which send the appropriate warning to the journal CMessage::ToLog(DFUN,MSG_DATA_PROP_OBJ_OUT_OF_PROP_RANGE); return INT_MAX; } public: //--- Return the pointer to (1) the list of property objects, as well as to the object of (2) integer, (3) real and (4) string properties CArrayObj *GetList(void) { return &this.m_list; } CXDimArrayLong *Long() const { return this.m_list.At(0); } CXDimArrayDouble *Double() const { return this.m_list.At(1); } CXDimArrayString *String() const { return this.m_list.At(2); } //--- Set (1) integer, (2) real and (3) string properties in the appropriate property object void SetLong(int property,int index,long value) { this.Long().Set(property,index,value); } void SetDouble(int property,int index,double value) { this.Double().Set(this.IndexProp(property),index,value); } void SetString(int property,int index,string value) { this.String().Set(this.IndexProp(property),index,value); } //--- Return (1) integer, (2) real and (3) string property from the appropriate object long GetLong(int property,int index) const { return this.Long().Get(property,index); } double GetDouble(int property,int index) const { return this.Double().Get(this.IndexProp(property),index); } string GetString(int property,int index) const { return this.String().Get(this.IndexProp(property),index); } //--- Return the size of the specified first dimension data array int Size(const int range) const { if(range<this.m_total_int) return this.Long().Size(range); else if(range<this.m_prop_max_dbl) return this.Double().Size(this.IndexProp(range)); else if(range<this.m_prop_max_str) return this.String().Size(this.IndexProp(range)); return 0; } //--- Set the array size in the specified dimensionality bool SetSizeRange(const int range,const int size) { if(range<this.m_total_int) return this.Long().SetSizeRange(range,size); else if(range<this.m_prop_max_dbl) return this.Double().SetSizeRange(this.IndexProp(range),size); else if(range<this.m_prop_max_str) return this.String().SetSizeRange(this.IndexProp(range),size); return false; } //--- Constructor CDataPropObj(const int prop_total_integer,const int prop_total_double,const int prop_total_string) { //--- Set the passed amounts of integer, real and string properties in the variables this.m_total_int=prop_total_integer; this.m_total_dbl=prop_total_double; this.m_total_str=prop_total_string; //--- Calculate and set the maximum values of real and string properties to the variables this.m_prop_max_dbl=this.m_total_int+this.m_total_dbl; this.m_prop_max_str=this.m_total_int+this.m_total_dbl+this.m_total_str; //--- Add newly created objects of integer, real and string properties to the list this.m_list.Add(new CXDimArrayLong(this.m_total_int, 1)); this.m_list.Add(new CXDimArrayDouble(this.m_total_dbl,1)); this.m_list.Add(new CXDimArrayString(this.m_total_str,1)); } //--- Destructor ~CDataPropObj() { m_list.Clear(); m_list.Shutdown(); } }; //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Data class of the current and previous properties | //+------------------------------------------------------------------+ class CProperties : public CObject { private: CArrayObj m_list; // List for storing the pointers to property objects public: CDataPropObj *Curr; // Pointer to the current properties object CDataPropObj *Prev; // Pointer to the previous properties object //--- Set the array size ('size') in the specified dimension ('range') bool SetSizeRange(const int range,const int size) { return(this.Curr.SetSizeRange(range,size) && this.Prev.SetSizeRange(range,size) ? true : false); } //--- Return the size of the specified array of the (1) current and (2) previous first dimension data int CurrSize(const int range) const { return Curr.Size(range); } int PrevSize(const int range) const { return Prev.Size(range); } //--- Copy the current data to the previous one void CurrentToPrevious(void) { //--- Copy all integer properties for(int i=0;i<this.Curr.Long().Total();i++) for(int r=0;r<this.Curr.Long().Size(i);r++) this.Prev.Long().Set(i,r,this.Curr.Long().Get(i,r)); //--- Copy all real properties for(int i=0;i<this.Curr.Double().Total();i++) for(int r=0;r<this.Curr.Double().Size(i);r++) this.Prev.Double().Set(i,r,this.Curr.Double().Get(i,r)); //--- Copy all string properties for(int i=0;i<this.Curr.String().Total();i++) for(int r=0;r<this.Curr.String().Size(i);r++) this.Prev.String().Set(i,r,this.Curr.String().Get(i,r)); } //--- Constructor CProperties(const int prop_int_total,const int prop_double_total,const int prop_string_total) { //--- Create new objects of the current and previous properties this.Curr=new CDataPropObj(prop_int_total,prop_double_total,prop_string_total); this.Prev=new CDataPropObj(prop_int_total,prop_double_total,prop_string_total); //--- Add newly created objects to the list this.m_list.Add(this.Curr); this.m_list.Add(this.Prev); } //--- Destructor ~CProperties() { this.m_list.Clear(); this.m_list.Shutdown(); } }; //+------------------------------------------------------------------+
All the main explanations are set in the code comments. Compare these classes with the ones I made in the previous article in \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqh.
Now let's improve the abstract standard graphical object class in \MQL5\Include\DoEasy\Objects\Graph\Standard\GStdGraphObj.mqh.
First, include the newly created file of object property classes:
//+------------------------------------------------------------------+ //| GStdGraphObj.mqh | //| Copyright 2021, MetaQuotes Ltd. | //| https://mql5.com/en/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2021, MetaQuotes Ltd." #property link "https://mql5.com/en/users/artmedia70" #property version "1.00" #property strict // Necessary for mql4 //+------------------------------------------------------------------+ //| Include files | //+------------------------------------------------------------------+ #include "..\GBaseObj.mqh" #include "..\..\..\Services\Properties.mqh" //+------------------------------------------------------------------+ //| The class of the abstract standard graphical object | //+------------------------------------------------------------------+ class CGStdGraphObj : public CGBaseObj {
The classes of properties objects are already removed from the private section of the class. The pointer to the properties object is declared in it as well. The appropriate method of the properties object is accessed in each of the Get and Set methods in the public section of the class:
//+------------------------------------------------------------------+ //| The class of the abstract standard graphical object | //+------------------------------------------------------------------+ class CGStdGraphObj : public CGBaseObj { private: CProperties *Prop; // Pointer to the properties object int m_pivots; // Number of object reference points //--- Read and set (1) the time and (2) the price of the specified object pivot point void SetTimePivot(const int index); void SetPricePivot(const int index); //--- Read and set (1) color, (2) style, (3) width, (4) value, (5) text of the specified object level void SetLevelColor(const int index); void SetLevelStyle(const int index); void SetLevelWidth(const int index); void SetLevelValue(const int index); void SetLevelText(const int index); //--- Read and set the BMP file name for the "Bitmap Level" object. Index: 0 - ON, 1 - OFF void SetBMPFile(const int index); public: //--- Set object's (1) integer, (2) real and (3) string properties void SetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value) { this.Prop.Curr.SetLong(property,index,value); } void SetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value) { this.Prop.Curr.SetDouble(property,index,value); } void SetProperty(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value) { this.Prop.Curr.SetString(property,index,value); } //--- Return object’s (1) integer, (2) real and (3) string property from the properties array long GetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index) const { return this.Prop.Curr.GetLong(property,index); } double GetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index) const { return this.Prop.Curr.GetDouble(property,index); } string GetProperty(ENUM_GRAPH_OBJ_PROP_STRING property,int index) const { return this.Prop.Curr.GetString(property,index); } //--- Set object's previous (1) integer, (2) real and (3) string properties void SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index,long value) { this.Prop.Prev.SetLong(property,index,value); } void SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index,double value){ this.Prop.Prev.SetDouble(property,index,value); } void SetPropertyPrev(ENUM_GRAPH_OBJ_PROP_STRING property,int index,string value){ this.Prop.Prev.SetString(property,index,value); } //--- Return object’s (1) integer, (2) real and (3) string property from the previous properties array long GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_INTEGER property,int index) const { return this.Prop.Prev.GetLong(property,index); } double GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_DOUBLE property,int index) const { return this.Prop.Prev.GetDouble(property,index); } string GetPropertyPrev(ENUM_GRAPH_OBJ_PROP_STRING property,int index) const { return this.Prop.Prev.GetString(property,index); } //--- Return itself CGStdGraphObj *GetObject(void) { return &this;}
In the public section, add the destructor of the class the properties object is to be removed in:
//--- Default constructor CGStdGraphObj(){ this.m_type=OBJECT_DE_TYPE_GSTD_OBJ; m_group=WRONG_VALUE; } //--- Destructor ~CGStdGraphObj() { if(this.Prop!=NULL) delete this.Prop; } protected: //--- Protected parametric constructor CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type, const ENUM_GRAPH_OBJ_BELONG belong, const ENUM_GRAPH_OBJ_GROUP group, const long chart_id, const int pivots, const string name);
In the section of the methods of simplified access and setting graphical object properties, improve the methods of setting flag properties:
//--- Background object bool Back(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_BACK,0); } void SetFlagBack(const bool flag) { if(CGBaseObj::SetFlagBack(flag)) this.SetProperty(GRAPH_OBJ_PROP_BACK,0,flag); } //--- Priority of a graphical object for receiving the event of clicking on a chart long Zorder(void) const { return this.GetProperty(GRAPH_OBJ_PROP_ZORDER,0); } void SetZorder(const long value) { if(CGBaseObj::SetZorder(value)) this.SetProperty(GRAPH_OBJ_PROP_ZORDER,0,value); } //--- Disable displaying the name of a graphical object in the terminal object list bool Hidden(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN,0); } void SetFlagHidden(const bool flag) { if(CGBaseObj::SetFlagHidden(flag)) this.SetProperty(GRAPH_OBJ_PROP_HIDDEN,0,flag); } //--- Object selection bool Selected(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTED,0); } void SetFlagSelected(const bool flag) { if(CGBaseObj::SetFlagSelected(flag)) this.SetProperty(GRAPH_OBJ_PROP_SELECTED,0,flag); } //--- Object availability bool Selectable(void) const { return (bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTABLE,0); } void SetFlagSelectable(const bool flag) { if(CGBaseObj::SetFlagSelectable(flag)) this.SetProperty(GRAPH_OBJ_PROP_SELECTABLE,0,flag); } //--- Time coordinate
Move the method copying the current properties to the previous ones from the private section to the public one:
//--- Return the description of the object visibility on timeframes string VisibleOnTimeframeDescription(void); //--- Re-write all graphical object properties void PropertiesRefresh(void); //--- Check object property changes void PropertiesCheckChanged(void); //--- Copy the current data to the previous one void PropertiesCopyToPrevData(void); private: //--- Get and save (1) integer, (2) real and (3) string properties void GetAndSaveINT(void); void GetAndSaveDBL(void); void GetAndSaveSTR(void); }; //+------------------------------------------------------------------+
In the protected parametric constructor, create a new object of the graphical object properties:
//+------------------------------------------------------------------+ //| Protected parametric constructor | //+------------------------------------------------------------------+ CGStdGraphObj::CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type, const ENUM_GRAPH_OBJ_BELONG belong, const ENUM_GRAPH_OBJ_GROUP group, const long chart_id,const int pivots, const string name) { //--- Create the property object with the default values this.Prop=new CProperties(GRAPH_OBJ_PROP_INTEGER_TOTAL,GRAPH_OBJ_PROP_DOUBLE_TOTAL,GRAPH_OBJ_PROP_STRING_TOTAL); //--- Set the number of pivot points and object levels
Pass the number of integer, real and string graphical object properties to the class constructor.
In the method returning integer properties from a graphical and saving them in the class object properties, check whether the number of object levels has changed. If yes, then change the size of arrays of all properties storing the level values.
Otherwise, when setting the level properties, we get the array out of range error:
//--- Properties belonging to different graphical objects this.SetProperty(GRAPH_OBJ_PROP_FILL,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_FILL)); // Fill an object with color this.SetProperty(GRAPH_OBJ_PROP_READONLY,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_READONLY)); // Ability to edit text in the Edit object this.SetProperty(GRAPH_OBJ_PROP_LEVELS,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_LEVELS)); // Number of levels if(this.GetProperty(GRAPH_OBJ_PROP_LEVELS,0)!=this.GetPropertyPrev(GRAPH_OBJ_PROP_LEVELS,0)) // Check if the number of levels has changed { this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELCOLOR,this.Levels()); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELSTYLE,this.Levels()); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELWIDTH,this.Levels()); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELVALUE,this.Levels()); this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELTEXT,this.Levels()); } for(int i=0;i<this.Levels();i++) // Level data { this.SetLevelColor(i); this.SetLevelStyle(i); this.SetLevelWidth(i); } this.SetProperty(GRAPH_OBJ_PROP_ALIGN,0,::ObjectGetInteger(this.ChartID(),this.Name(),OBJPROP_ALIGN)); // Horizontal text alignment in the Edit object (OBJ_EDIT)
Simplify the method checking the changes in object properties:
//+------------------------------------------------------------------+ //| Check object property changes | //+------------------------------------------------------------------+ void CGStdGraphObj::PropertiesCheckChanged(void) { bool changed=false; int begin=0, end=GRAPH_OBJ_PROP_INTEGER_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_INTEGER prop=(ENUM_GRAPH_OBJ_PROP_INTEGER)i; if(!this.SupportProperty(prop)) continue; for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j)) { changed=true; ::Print(DFUN,this.Name(),": ",TextByLanguage(" Изменённое свойство: "," Modified property: "),this.GetPropertyDescription(prop)); } } } begin=end; end+=GRAPH_OBJ_PROP_DOUBLE_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_DOUBLE prop=(ENUM_GRAPH_OBJ_PROP_DOUBLE)i; if(!this.SupportProperty(prop)) continue; for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j)) { changed=true; ::Print(DFUN,this.Name(),": ",TextByLanguage(" Изменённое свойство: "," Modified property: "),this.GetPropertyDescription(prop)); } } } begin=end; end+=GRAPH_OBJ_PROP_STRING_TOTAL; for(int i=begin; i<end; i++) { ENUM_GRAPH_OBJ_PROP_STRING prop=(ENUM_GRAPH_OBJ_PROP_STRING)i; if(!this.SupportProperty(prop)) continue; for(int j=0;j<Prop.CurrSize(prop);j++) { if(this.GetProperty(prop,j)!=this.GetPropertyPrev(prop,j)) { changed=true; ::Print(DFUN,this.Name(),": ",TextByLanguage(" Изменённое свойство: "," Modified property: "),this.GetPropertyDescription(prop)); } } } if(changed) PropertiesCopyToPrevData(); } //+------------------------------------------------------------------+
In the past method implementation, I used the if-else construction to make sure the property has multiple values (such as the anchor point time) and handled each such property in a separate code block. Since we are able to know the property array size for each of the properties, it is sufficient to simply go through the values of a single property in the loop by the size of the array second dimension. For a single property, the size of the second dimension is 1, while for a multiple one, it is equal to the number of values of the property multi-properties. Thus, all can be done in a single loop for each of the object properties. This is what I have done above.
I have also implemented some minor improvements (for example changing method names) in the same file. For instance, LevelColorsDescription() has been renamed to LevelsColorDescription(), which is more in line with the method purpose. I will not consider such renaming here. You can find them in the attached files.
Each of the library objects has a custom object ID in addition to other properties. The collection class of graphical elements features two collections — collection of graphical elements, the development of which I have suspended until the completion of the graphical object collection (featuring manually created standard graphical objects) I am working on right now and standard graphical objects that are created programmatically. Here I will start developing the functionality for programming standard graphical objects.
IDs of graphical objects created programmatically will be located in the range from 1 to 10000 inclusive. IDs of manually created graphical objects start from 10001.
Let's set this threshold value in \MQL5\Include\DoEasy\Defines.mqh:
//--- Pending request type IDs #define PENDING_REQUEST_ID_TYPE_ERR (1) // Type of a pending request created based on the server return code #define PENDING_REQUEST_ID_TYPE_REQ (2) // Type of a pending request created by request //--- Timeseries parameters #define SERIES_DEFAULT_BARS_COUNT (1000) // Required default amount of timeseries data #define PAUSE_FOR_SYNC_ATTEMPTS (16) // Amount of pause milliseconds between synchronization attempts #define ATTEMPTS_FOR_SYNC (5) // Number of attempts to receive synchronization with the server //--- Tick series parameters #define TICKSERIES_DEFAULT_DAYS_COUNT (1) // Required number of days for tick data in default series #define TICKSERIES_MAX_DATA_TOTAL (200000) // Maximum number of stored tick data of a single symbol //--- Parameters of the DOM snapshot series #define MBOOKSERIES_DEFAULT_DAYS_COUNT (1) // The default required number of days for DOM snapshots in the series #define MBOOKSERIES_MAX_DATA_TOTAL (200000) // Maximum number of stored DOM snapshots of a single symbol //--- Canvas parameters #define PAUSE_FOR_CANV_UPDATE (16) // Canvas update frequency #define NULL_COLOR (0x00FFFFFF) // Zero for the canvas with the alpha channel #define OUTER_AREA_SIZE (16) // Size of one side of the outer area around the workspace //--- Graphical object parameters #define PROGRAM_OBJ_MAX_ID (10000) // Maximum value of an ID of a graphical object belonging to a program //+------------------------------------------------------------------+ //| Enumerations | //+------------------------------------------------------------------+
Methods for programming standard graphical objects
We already have the methods tracking manual creation of graphical objects on a chart, creating the appropriate class objects and adding them to the collection list. Of course, it would be good to use them for the current tasks but there are some reasons that forced me to abandon the use of partially ready-made methods. We simply track the appearance of graphical objects in the timer. When creating an object programmatically, I would not want to wait for the next timer tick and define the newly created object, as well as its creation method (programmatic and manual).
Instead, I will create the method in the collection class of graphical elements for constructing standard graphical objects. Immediately after constructing the object, I will create the corresponding class object and place it in the collection. The search for changes in the object properties is performed by the already created functionality. Thus, we will be able to create objects and add them to the collection, while retaining the ability to search for changes with no consideration to the object creation method. In the future, this will simplify the creation of composite graphical objects and managing their properties.
In a programmatically created graphical object, its name will contain the name of the program the object was created from. This will allow us to distinguish our "own" graphical objects from manually created ones.
To achieve this, add the new variable for storing the program name in the private section of the chart object management class in the file of the graphical element collection class \MQL5\Include\DoEasy\Collections\GraphElementsCollection.mqh:
//+------------------------------------------------------------------+ //| Chart object management class | //+------------------------------------------------------------------+ class CChartObjectsControl : public CObject { private: CArrayObj m_list_new_graph_obj; // List of added graphical objects ENUM_TIMEFRAMES m_chart_timeframe; // Chart timeframe long m_chart_id; // Chart ID long m_chart_id_main; // Control program chart ID string m_chart_symbol; // Chart symbol bool m_is_graph_obj_event; // Event flag in the list of graphical objects int m_total_objects; // Number of graphical objects int m_last_objects; // Number of graphical objects during the previous check int m_delta_graph_obj; // Difference in the number of graphical objects compared to the previous check int m_handle_ind; // Event controller indicator handle string m_name_ind; // Short name of the event controller indicator string m_name_program; // Program name //--- Return the name of the last graphical object added to the chart string LastAddedGraphObjName(void); //--- Set the permission to track mouse events and graphical objects void SetMouseEvent(void); public:
In the public section of the class, remove specifying a chart ID from the CreateNewGraphObj() method since the ID is one of the primary properties of the chart objects management object and it can be obtained directly from the object rather than by passing to the method.
Set the program name value to the appropriate variable in the class constructors:
public: //--- Return the variable values ENUM_TIMEFRAMES Timeframe(void) const { return this.m_chart_timeframe; } long ChartID(void) const { return this.m_chart_id; } string Symbol(void) const { return this.m_chart_symbol; } bool IsEvent(void) const { return this.m_is_graph_obj_event; } int TotalObjects(void) const { return this.m_total_objects; } int Delta(void) const { return this.m_delta_graph_obj; } //--- Create a new standard graphical object CGStdGraphObj *CreateNewGraphObj(const ENUM_OBJECT obj_type,const string name); //--- Return the list of newly added objects CArrayObj *GetListNewAddedObj(void) { return &this.m_list_new_graph_obj;} //--- Create the event control indicator bool CreateEventControlInd(const long chart_id_main); //--- Add the event control indicator to the chart bool AddEventControlInd(void); //--- Check the chart objects void Refresh(void); //--- Constructors CChartObjectsControl(void) { this.m_name_program=::MQLInfoString(MQL_PROGRAM_NAME); this.m_chart_id=::ChartID(); this.m_chart_timeframe=(ENUM_TIMEFRAMES)::ChartPeriod(this.m_chart_id); this.m_chart_symbol=::ChartSymbol(this.m_chart_id); this.m_chart_id_main=::ChartID(); this.m_list_new_graph_obj.Clear(); this.m_list_new_graph_obj.Sort(); this.m_is_graph_obj_event=false; this.m_total_objects=0; this.m_last_objects=0; this.m_delta_graph_obj=0; this.m_name_ind=""; this.m_handle_ind=INVALID_HANDLE; this.SetMouseEvent(); } CChartObjectsControl(const long chart_id) { this.m_name_program=::MQLInfoString(MQL_PROGRAM_NAME); this.m_chart_timeframe=(ENUM_TIMEFRAMES)::ChartPeriod(this.m_chart_id); this.m_chart_symbol=::ChartSymbol(this.m_chart_id); this.m_chart_id_main=::ChartID(); this.m_list_new_graph_obj.Clear(); this.m_list_new_graph_obj.Sort(); this.m_chart_id=chart_id; this.m_is_graph_obj_event=false; this.m_total_objects=0; this.m_last_objects=0; this.m_delta_graph_obj=0; this.m_name_ind=""; this.m_handle_ind=INVALID_HANDLE; this.SetMouseEvent(); }
In addition for the empty name check, make sure the object is not created programmatically in the method checking chart objects:
//+------------------------------------------------------------------+ //| CChartObjectsControl: Check objects on a chart | //+------------------------------------------------------------------+ void CChartObjectsControl::Refresh(void) { //--- Graphical objects on the chart this.m_total_objects=::ObjectsTotal(this.ChartID()); this.m_delta_graph_obj=this.m_total_objects-this.m_last_objects; //--- If the number of objects has changed if(this.m_delta_graph_obj!=0) { //--- Create the string and display it in the journal with the chart ID, its symbol and timeframe string txt=", "+(m_delta_graph_obj>0 ? "Added: " : "Deleted: ")+(string)fabs(m_delta_graph_obj)+" obj"; Print(DFUN,"ChartID=",this.ChartID(),", ",this.Symbol(),", ",TimeframeDescription(this.Timeframe()),txt); } //--- If an object is added to the chart if(this.m_delta_graph_obj>0) { //--- find the last added graphical object, select it and write its name string name=this.LastAddedGraphObjName(); if(name!="" && ::StringFind(name,m_name_program)==WRONG_VALUE) { //--- Create the object of the graphical object class corresponding to the added graphical object type ENUM_OBJECT type=(ENUM_OBJECT)::ObjectGetInteger(this.ChartID(),name,OBJPROP_TYPE); ENUM_OBJECT_DE_TYPE obj_type=ENUM_OBJECT_DE_TYPE(type+OBJECT_DE_TYPE_GSTD_OBJ+1); CGStdGraphObj *obj=this.CreateNewGraphObj(type,name); if(obj==NULL) return; //--- Set the object affiliation and add the created object to the list of new objects obj.SetBelong(GRAPH_OBJ_BELONG_NO_PROGRAM); if(this.m_list_new_graph_obj.Search(obj)==WRONG_VALUE) { this.m_list_new_graph_obj.Add(obj); } } } //--- save the index of the last added graphical object and the difference with the last check this.m_last_objects=this.m_total_objects; this.m_is_graph_obj_event=(bool)this.m_delta_graph_obj; } //+------------------------------------------------------------------+
In other words, if an object name has no a substring with a program name, such an object should be handled by the method, otherwise this is a programmatically created graphical object which is added to the collection list by another method.
In the method for creating a new standard graphical object, replace all chart_id, previously passed to the method, with the chart ID set for the object it controls:
//+------------------------------------------------------------------+ //| CChartObjectsControl: | //| Create a new standard graphical object | //+------------------------------------------------------------------+ CGStdGraphObj *CChartObjectsControl::CreateNewGraphObj(const ENUM_OBJECT obj_type,const string name) { CGStdGraphObj *obj=NULL; switch((int)obj_type) { //--- Lines case OBJ_VLINE : return new CGStdVLineObj(this.ChartID(),name); case OBJ_HLINE : return new CGStdHLineObj(this.ChartID(),name); case OBJ_TREND : return new CGStdTrendObj(this.ChartID(),name); case OBJ_TRENDBYANGLE : return new CGStdTrendByAngleObj(this.ChartID(),name); case OBJ_CYCLES : return new CGStdCyclesObj(this.ChartID(),name); case OBJ_ARROWED_LINE : return new CGStdArrowedLineObj(this.ChartID(),name); //--- Channels case OBJ_CHANNEL : return new CGStdChannelObj(this.ChartID(),name); case OBJ_STDDEVCHANNEL : return new CGStdStdDevChannelObj(this.ChartID(),name); case OBJ_REGRESSION : return new CGStdRegressionObj(this.ChartID(),name); case OBJ_PITCHFORK : return new CGStdPitchforkObj(this.ChartID(),name); //--- Gann case OBJ_GANNLINE : return new CGStdGannLineObj(this.ChartID(),name); case OBJ_GANNFAN : return new CGStdGannFanObj(this.ChartID(),name); case OBJ_GANNGRID : return new CGStdGannGridObj(this.ChartID(),name); //--- Fibo case OBJ_FIBO : return new CGStdFiboObj(this.ChartID(),name); case OBJ_FIBOTIMES : return new CGStdFiboTimesObj(this.ChartID(),name); case OBJ_FIBOFAN : return new CGStdFiboFanObj(this.ChartID(),name); case OBJ_FIBOARC : return new CGStdFiboArcObj(this.ChartID(),name); case OBJ_FIBOCHANNEL : return new CGStdFiboChannelObj(this.ChartID(),name); case OBJ_EXPANSION : return new CGStdExpansionObj(this.ChartID(),name); //--- Elliott case OBJ_ELLIOTWAVE5 : return new CGStdElliotWave5Obj(this.ChartID(),name); case OBJ_ELLIOTWAVE3 : return new CGStdElliotWave3Obj(this.ChartID(),name); //--- Shapes case OBJ_RECTANGLE : return new CGStdRectangleObj(this.ChartID(),name); case OBJ_TRIANGLE : return new CGStdTriangleObj(this.ChartID(),name); case OBJ_ELLIPSE : return new CGStdEllipseObj(this.ChartID(),name); //--- Arrows case OBJ_ARROW_THUMB_UP : return new CGStdArrowThumbUpObj(this.ChartID(),name); case OBJ_ARROW_THUMB_DOWN : return new CGStdArrowThumbDownObj(this.ChartID(),name); case OBJ_ARROW_UP : return new CGStdArrowUpObj(this.ChartID(),name); case OBJ_ARROW_DOWN : return new CGStdArrowDownObj(this.ChartID(),name); case OBJ_ARROW_STOP : return new CGStdArrowStopObj(this.ChartID(),name); case OBJ_ARROW_CHECK : return new CGStdArrowCheckObj(this.ChartID(),name); case OBJ_ARROW_LEFT_PRICE : return new CGStdArrowLeftPriceObj(this.ChartID(),name); case OBJ_ARROW_RIGHT_PRICE : return new CGStdArrowRightPriceObj(this.ChartID(),name); case OBJ_ARROW_BUY : return new CGStdArrowBuyObj(this.ChartID(),name); case OBJ_ARROW_SELL : return new CGStdArrowSellObj(this.ChartID(),name); case OBJ_ARROW : return new CGStdArrowObj(this.ChartID(),name); //--- Graphical objects case OBJ_TEXT : return new CGStdTextObj(this.ChartID(),name); case OBJ_LABEL : return new CGStdLabelObj(this.ChartID(),name); case OBJ_BUTTON : return new CGStdButtonObj(this.ChartID(),name); case OBJ_CHART : return new CGStdChartObj(this.ChartID(),name); case OBJ_BITMAP : return new CGStdBitmapObj(this.ChartID(),name); case OBJ_BITMAP_LABEL : return new CGStdBitmapLabelObj(this.ChartID(),name); case OBJ_EDIT : return new CGStdEditObj(this.ChartID(),name); case OBJ_EVENT : return new CGStdEventObj(this.ChartID(),name); case OBJ_RECTANGLE_LABEL : return new CGStdRectangleLabelObj(this.ChartID(),name); default : return NULL; } } //+------------------------------------------------------------------+
In the CGraphElementsCollection collection class of graphical objects, namely, in the method returning the first free graphical object ID, add the flag specifying the ID of the necessary object: false — for a manually created one, true — for a programmatically created one:
//--- Return the first free ID of the graphical (1) object and (2) element on canvas long GetFreeGraphObjID(bool program_object); long GetFreeCanvElmID(void); //--- Add a graphical object to the collection
Declare the private method creating a new standard graphical object:
//--- Remove the object of managing charts from the list bool DeleteGraphObjCtrlObjFromList(CChartObjectsControl *obj); //--- Create a new standard graphical object, return an object name bool CreateNewStdGraphObject(const long chart_id, const string name, const ENUM_OBJECT type, const int subwindow, const datetime time1, const double price1, const datetime time2=0, const double price2=0, const datetime time3=0, const double price3=0, const datetime time4=0, const double price4=0, const datetime time5=0, const double price5=0); public:
In the private section of the class, write the method creating a new graphical object and returning the pointer to the chart management object:
//--- Event handler void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam); private: //--- Create a new graphical object, return the pointer to the chart management object CChartObjectsControl *CreateNewStdGraphObjectAndGetCtrlObj(const long chart_id, const string name, int subwindow, const ENUM_OBJECT type_object, const datetime time1, const double price1, const datetime time2=0, const double price2=0, const datetime time3=0, const double price3=0, const datetime time4=0, const double price4=0, const datetime time5=0, const double price5=0) { //--- If an object with a chart ID and name is already present in the collection, inform of that and return NULL if(this.IsPresentGraphObjInList(chart_id,name)) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_ELM_COLLECTION_ERR_GR_OBJ_ALREADY_EXISTS)," ChartID ",(string)chart_id,", ",name); return NULL; } //--- If failed to create a new standard graphical object, inform of that and return NULL if(!this.CreateNewStdGraphObject(chart_id,name,type_object,subwindow,time1,0)) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_STD_GRAPH_OBJ),StdGraphObjectTypeDescription(type_object)); CMessage::ToLog(::GetLastError(),true); return NULL; } //--- If failed to get a chart management object, inform of that CChartObjectsControl *ctrl=this.GetChartObjectCtrlObj(chart_id); if(ctrl==NULL) ::Print(DFUN,CMessage::Text(MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_GET_CTRL_OBJ),(string)chart_id); //--- Return the pointer to a chart management object or NULL in case of a failed attempt to get it return ctrl; } public:
The method logic is described in the code comments. The method will be used when creating specified standard graphical object types located further in the public section of the class.
The method creating the "Vertical line" graphical object:
public: //--- Create the "Vertical line" graphical object bool CreateLineVertical(const long chart_id,const string name,const int subwindow,const datetime time) { //--- Set the name and type of a created object string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_VLINE; //--- Create a new graphical object and get the pointer to the chart management object CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,0); if(ctrl==NULL) return false; //--- Create a new class object corresponding to the newly created graphical object CGStdVLineObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); //--- If failed to add an object to the collection list, if(!this.m_list_all_graph_obj.Add(obj)) { //--- inform of that, remove the graphical and class object, and return 'false' CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } //--- Redraw the chart and display all object properties in the journal (temporarily, for test purposes only) ::ChartRedraw(chart_id); obj.Print(); return true; }
The method is described in sufficient detail in the code comments. The properties inherent in each specific object and passed in the parameters will be set in each such object. A physical graphical object is created on the specified chart first forming a pointer to the chart management object. Its CreateNewGraphObj() method is used to create the class object corresponding to the created object type. If failed to add the object to the list, the physical graphical object itself and the class object are removed with an error message sent to the journal. If created successfully, the chart is updated and the method returns true.
The remaining methods for creating graphical objects are identical to the one considered above and differ only in their set of parameters defined for each specific graphical object.
Let's have a look at the listing of all other added methods:
//--- Create the "Horizontal line" graphical object bool CreateLineHorizontal(const long chart_id,const string name,const int subwindow,const double price) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_HLINE; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,0,price); if(ctrl==NULL) return false; CGStdHLineObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Trend line" graphical object bool CreateLineTrend(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_TREND; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2); if(ctrl==NULL) return false; CGStdTrendObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Trend line by angle" graphical object bool CreateLineTrendByAngle(const long chart_id,const string name,const int subwindow, const datetime time1,const double price1,const datetime time2,const double price2,const double angle) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_TRENDBYANGLE; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2); if(ctrl==NULL) return false; CGStdTrendByAngleObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.SetAngle(angle); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Cyclic lines" graphical object bool CreateLineCycle(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2,double price2) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_CYCLES; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2); if(ctrl==NULL) return false; CGStdCyclesObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Arrowed line" graphical object bool CreateLineArrowed(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2,double price2) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_ARROWED_LINE; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2); if(ctrl==NULL) return false; CGStdArrowedLineObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Equidistant channel" graphical object bool CreateChannel(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2, double price2,const datetime time3,double price3) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_CHANNEL; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2,time3,price3); if(ctrl==NULL) return false; CGStdChannelObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Standard deviation channel" graphical object bool CreateChannelStdDeviation(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2,double price2,const double deviation=1.5) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_STDDEVCHANNEL; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2); if(ctrl==NULL) return false; CGStdStdDevChannelObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.SetDeviation(deviation); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Linear regression channel" graphical object bool CreateChannelRegression(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2,double price2) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_REGRESSION; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2); if(ctrl==NULL) return false; CGStdRegressionObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Andrews' Pitchfork" graphical object bool CreatePitchforkAndrews(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2,double price2,const datetime time3,double price3) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_PITCHFORK; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2,time3,price3); if(ctrl==NULL) return false; CGStdPitchforkObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Gann line" graphical object bool CreateGannLine(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2,double price2,double angle) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_GANNLINE; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2); if(ctrl==NULL) return false; CGStdGannLineObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.SetAngle(angle); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Gann fan" graphical object bool CreateGannFan(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2,double price2, const ENUM_GANN_DIRECTION direction,const double scale) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_GANNFAN; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2); if(ctrl==NULL) return false; CGStdGannFanObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.SetDirection(direction); obj.SetScale(scale); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Gann grid" graphical object bool CreateGannGrid(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2, const ENUM_GANN_DIRECTION direction,const double scale) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_GANNGRID; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2); if(ctrl==NULL) return false; CGStdGannGridObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.SetDirection(direction); obj.SetScale(scale); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Fibo levels" graphical object bool CreateFiboLevels(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2,double price2) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_FIBO; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2); if(ctrl==NULL) return false; CGStdFiboObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Fibo Time Zones" graphical object bool CreateFiboTimeZones(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2,double price2) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_FIBOTIMES; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2); if(ctrl==NULL) return false; CGStdFiboTimesObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Fibo fan" graphical object bool CreateFiboFan(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2,double price2) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_FIBOFAN; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2); if(ctrl==NULL) return false; CGStdFiboFanObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Fibo arc" graphical object bool CreateFiboArc(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2,double price2, const double scale,const bool ellipse) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_FIBOARC; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2); if(ctrl==NULL) return false; CGStdFiboArcObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.SetScale(scale); obj.SetFlagEllipse(ellipse); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Fibo channel" graphical object bool CreateFiboChannel(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2,double price2,const datetime time3,double price3) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_FIBOCHANNEL; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2,time3,price3); if(ctrl==NULL) return false; CGStdFiboChannelObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Fibo extension" graphical object bool CreateFiboExpansion(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2,double price2,const datetime time3,double price3) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_EXPANSION; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2,time3,price3); if(ctrl==NULL) return false; CGStdExpansionObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Elliott 5 waves" graphical object bool CreateElliothWave5(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2,double price2, const datetime time3,double price3,const datetime time4,double price4, const datetime time5,double price5,const ENUM_ELLIOT_WAVE_DEGREE degree, const bool draw_lines) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_ELLIOTWAVE5; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2,time3,price3,time4,price4,time5,price5); if(ctrl==NULL) return false; CGStdElliotWave5Obj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.SetDegree(degree); obj.SetFlagDrawLines(draw_lines); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Elliott 3 waves" graphical object bool CreateElliothWave3(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2, double price2,const datetime time3,double price3, const ENUM_ELLIOT_WAVE_DEGREE degree,const bool draw_lines) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_ELLIOTWAVE3; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2,time3,price3); if(ctrl==NULL) return false; CGStdElliotWave3Obj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.SetDegree(degree); obj.SetFlagDrawLines(draw_lines); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the Rectangle graphical object bool CreateRectangle(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2,double price2) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_RECTANGLE; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2); if(ctrl==NULL) return false; CGStdRectangleObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the Triangle graphical object bool CreateTriangle(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2,double price2,const datetime time3,double price3) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_TRIANGLE; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2,time3,price3); if(ctrl==NULL) return false; CGStdTriangleObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the Ellipse graphical object bool CreateEllipse(const long chart_id,const string name,const int subwindow, const datetime time1,double price1,const datetime time2,double price2,const datetime time3,double price3) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_ELLIPSE; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time1,price1,time2,price2,time3,price3); if(ctrl==NULL) return false; CGStdEllipseObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Thumb up" graphical object bool CreateThumbUp(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_ARROW_THUMB_UP; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price); if(ctrl==NULL) return false; CGStdArrowThumbUpObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Thumb down" graphical object bool CreateThumbDown(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_ARROW_THUMB_DOWN; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price); if(ctrl==NULL) return false; CGStdArrowThumbDownObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Arrow up" graphical object bool CreateArrowUp(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_ARROW_UP; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price); if(ctrl==NULL) return false; CGStdArrowUpObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Arrow down" graphical object bool CreateArrowDown(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_ARROW_DOWN; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price); if(ctrl==NULL) return false; CGStdArrowDownObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the Stop graphical object bool CreateSignalStop(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_ARROW_STOP; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price); if(ctrl==NULL) return false; CGStdArrowStopObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Check mark" graphical object bool CreateSignalCheck(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_ARROW_CHECK; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price); if(ctrl==NULL) return false; CGStdArrowCheckObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Left price label" graphical object bool CreatePriceLabelLeft(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_ARROW_LEFT_PRICE; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price); if(ctrl==NULL) return false; CGStdArrowLeftPriceObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Right price label" graphical object bool CreatePriceLabelRight(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_ARROW_RIGHT_PRICE; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price); if(ctrl==NULL) return false; CGStdArrowRightPriceObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the Buy graphical object bool CreateSignalBuy(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_ARROW_BUY; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price); if(ctrl==NULL) return false; CGStdArrowBuyObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the Sell graphical object bool CreateSignalSell(const long chart_id,const string name,const int subwindow,const datetime time,const double price) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_ARROW_SELL; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price); if(ctrl==NULL) return false; CGStdArrowSellObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the Arrow graphical object bool CreateArrow(const long chart_id,const string name,const int subwindow,const datetime time,const double price, const uchar arrow_code,const ENUM_ARROW_ANCHOR anchor) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_ARROW; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price); if(ctrl==NULL) return false; CGStdArrowObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.SetArrowCode(arrow_code); obj.SetAnchor(anchor); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the Text graphical object bool CreateText(const long chart_id,const string name,const int subwindow,const datetime time,const double price, const string text,const int size,const ENUM_ANCHOR_POINT anchor_point,const double angle) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_TEXT; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price); if(ctrl==NULL) return false; CGStdTextObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.SetText(text); obj.SetFontSize(size); obj.SetAnchor(anchor_point); obj.SetAngle(angle); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Text label" graphical object bool CreateTextLabel(const long chart_id,const string name,const int subwindow,const int x,const int y, const string text,const int size,const ENUM_BASE_CORNER corner, const ENUM_ANCHOR_POINT anchor_point,const double angle) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_LABEL; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,0,0); if(ctrl==NULL) return false; CGStdLabelObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.SetXDistance(x); obj.SetYDistance(y); obj.SetText(text); obj.SetFontSize(size); obj.SetCorner(corner); obj.SetAnchor(anchor_point); obj.SetAngle(angle); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the Button graphical object bool CreateButton(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h, const ENUM_BASE_CORNER corner,const int font_size,const bool button_state) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_BUTTON; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,0,0); if(ctrl==NULL) return false; CGStdButtonObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.SetXDistance(x); obj.SetYDistance(y); obj.SetXSize(w); obj.SetYSize(h); obj.SetCorner(corner); obj.SetFontSize(font_size); obj.SetFlagState(button_state); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the Chart graphical object bool CreateChart(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h, const ENUM_BASE_CORNER corner,const int scale,const string symbol,const ENUM_TIMEFRAMES timeframe) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_CHART; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,0,0); if(ctrl==NULL) return false; CGStdChartObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); obj.SetXDistance(x); obj.SetYDistance(y); obj.SetXSize(w); obj.SetYSize(h); obj.SetCorner(corner); obj.SetChartObjChartScale(scale); obj.SetChartObjSymbol(symbol); obj.SetChartObjPeriod(timeframe); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the Bitmap graphical object bool CreateBitmap(const long chart_id,const string name,const int subwindow,const datetime time,const double price, const string image1,const string image2,const ENUM_ANCHOR_POINT anchor) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_BITMAP; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,price); if(ctrl==NULL) return false; CGStdBitmapObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.SetAnchor(anchor); obj.SetBMPFile(image1,0); obj.SetBMPFile(image2,1); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Bitmap label" graphical object bool CreateBitmapLabel(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h, const string image1,const string image2,const ENUM_BASE_CORNER corner,const ENUM_ANCHOR_POINT anchor, const bool state) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_BITMAP_LABEL; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,0,0); if(ctrl==NULL) return false; CGStdBitmapLabelObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.SetXDistance(x); obj.SetYDistance(y); obj.SetXSize(w); obj.SetYSize(h); obj.SetCorner(corner); obj.SetAnchor(anchor); obj.SetBMPFile(image1,0); obj.SetBMPFile(image2,1); obj.SetFlagState(state); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Input field" graphical object bool CreateEditField(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h, const int font_size,const ENUM_BASE_CORNER corner,const ENUM_ALIGN_MODE align,const bool readonly) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_EDIT; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,0,0); if(ctrl==NULL) return false; CGStdEditObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.SetXDistance(x); obj.SetYDistance(y); obj.SetXSize(w); obj.SetYSize(h); obj.SetFontSize(font_size); obj.SetCorner(corner); obj.SetAlign(align); obj.SetFlagReadOnly(readonly); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Economic calendar event" graphical object bool CreateCalendarEvent(const long chart_id,const string name,const int subwindow,const datetime time) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_EVENT; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,0); if(ctrl==NULL) return false; CGStdEventObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } //--- Create the "Rectangular label" graphical object bool CreateRectangleLabel(const long chart_id,const string name,const int subwindow,const int x,const int y,const int w,const int h, const ENUM_BASE_CORNER corner,const ENUM_BORDER_TYPE border) { string nm=this.m_name_program+"_"+name; ENUM_OBJECT type_object=OBJ_RECTANGLE_LABEL; CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,0,0); if(ctrl==NULL) return false; CGStdRectangleLabelObj *obj=ctrl.CreateNewGraphObj(type_object,nm); if(obj==NULL) { ::Print(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); return false; } //--- Set the necessary minimal parameters for an object obj.SetBelong(GRAPH_OBJ_BELONG_PROGRAM); obj.SetFlagSelectable(true); obj.SetFlagSelected(true); obj.SetObjectID(this.GetFreeGraphObjID(true)); obj.SetXDistance(x); obj.SetYDistance(y); obj.SetXSize(w); obj.SetYSize(h); obj.SetCorner(corner); obj.SetBorderType(border); obj.PropertiesCopyToPrevData(); if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); ::ObjectDelete(chart_id,nm); delete obj; return false; } ::ChartRedraw(chart_id); obj.Print(); return true; } }; //+------------------------------------------------------------------+
All methods feature their own set of inputs. These parameters are set as minimal object properties sufficient for its creation. All remaining properties can be changed after creating a graphical object.
The method returning the first free graphical object ID:
//+------------------------------------------------------------------+ //| Return the first free graphical object ID | //+------------------------------------------------------------------+ long CGraphElementsCollection::GetFreeGraphObjID(bool program_object) { CArrayObj *list=NULL; int index=WRONG_VALUE; if(program_object) list=CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),GRAPH_OBJ_PROP_ID,0,PROGRAM_OBJ_MAX_ID,EQUAL_OR_LESS); else list=CSelect::ByGraphicStdObjectProperty(this.GetListGraphObj(),GRAPH_OBJ_PROP_ID,0,PROGRAM_OBJ_MAX_ID,MORE); index=CSelect::FindGraphicStdObjectMax(list,GRAPH_OBJ_PROP_ID,0); CGStdGraphObj *obj=list.At(index); int first_id=(program_object ? 1 : PROGRAM_OBJ_MAX_ID+1); return(obj!=NULL ? obj.ObjectID()+1 : first_id); } //+------------------------------------------------------------------+
Now the method considers a necessary ID to be received.
If it is intended for a programmatically created graphical object, get the list of all objects having IDs less or equal to the PROGRAM_OBJ_MAX_ID (10000) constant value.
If it is intended for a manually created graphical object, get the list of all objects having IDs exceeding PROGRAM_OBJ_MAX_ID.
Next, get the index of an object with the maximum ID from the obtained list, as well as a listed object by its index.
Next, calculate the value of the very first ID (for a programmed object — 1, for a manually created object — 10000+1).
If the object with the maximum ID has been received, get the value of its ID+1. Otherwise, the object is not in the list and the calculated value of the first ID (1 or 10001) is returned.
In the method adding the graphical object to the collection, find out whether an object is programmatically or manually created (by object name) to search for an ID and pass the value to the GetFreeGraphObjID() method:
//+------------------------------------------------------------------+ //| Add a graphical object to the collection | //+------------------------------------------------------------------+ bool CGraphElementsCollection::AddGraphObjToCollection(const string source,CChartObjectsControl *obj_control) { //--- Get the list of the last added graphical objects from the class for managing graphical objects CArrayObj *list=obj_control.GetListNewAddedObj(); //--- If failed to obtain the list, inform of that and return 'false' if(list==NULL) { CMessage::ToLog(DFUN_ERR_LINE,MSG_GRAPH_OBJ_FAILED_GET_ADDED_OBJ_LIST); return false; } //--- If the list is empty, return 'false' if(list.Total()==0) return false; //--- Declare the variable for storing the result bool res=true; //--- In the loop by the list of newly added standard graphical objects, for(int i=0;i<list.Total();i++) { //--- retrieve the next object from the list and CGStdGraphObj *obj=list.Detach(i); //--- if failed to retrieve the object, inform of that, add 'false' to the resulting variable and move on to the next one if(obj==NULL) { CMessage::ToLog(source,MSG_GRAPH_OBJ_FAILED_DETACH_OBJ_FROM_LIST); res &=false; continue; } //--- if failed to add the object to the collection list, inform of that, //--- remove the object, add 'false' to the resulting variable and move on to the next one if(!this.m_list_all_graph_obj.Add(obj)) { CMessage::ToLog(source,MSG_LIB_SYS_FAILED_OBJ_ADD_TO_LIST); delete obj; res &=false; continue; } //--- The object has been successfully retrieved from the list of newly added graphical objects and introduced into the collection - //--- find the next free object ID, write it to the property and display the short object description in the journal else { bool program_object=(::StringFind(obj.Name(),this.m_name_program)==0); obj.SetObjectID(this.GetFreeGraphObjID(program_object)); obj.Print(); } } //--- Return the result of adding the object to the collection return res; } //+------------------------------------------------------------------+
Let's simplify the event handler logic by getting rid of if-else and add object click tracking to determine the object selection with a mouse (not implemented yet):
//+------------------------------------------------------------------+ //| Event handler | //+------------------------------------------------------------------+ void CGraphElementsCollection::OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { CGStdGraphObj *obj=NULL; ushort idx=ushort(id-CHARTEVENT_CUSTOM); if(id==CHARTEVENT_OBJECT_CHANGE || id==CHARTEVENT_OBJECT_DRAG || idx==CHARTEVENT_OBJECT_CHANGE || idx==CHARTEVENT_OBJECT_DRAG || id==CHARTEVENT_OBJECT_CLICK || idx==CHARTEVENT_OBJECT_CLICK) { //--- Get the chart ID. If lparam is zero, //--- the event is from the current chart, //--- otherwise, this is a custom event from an indicator long chart_id=(lparam==0 ? ::ChartID() : lparam); //--- Get the object, whose properties were changed or which was relocated, //--- from the collection list by its name set in sparam obj=this.GetStdGraphObject(sparam,chart_id); //--- If failed to get the object by its name, it is not on the list, //--- which means its name has been changed if(obj==NULL) { //--- Let's search the list for the object that is not on the chart obj=this.FindMissingObj(chart_id); //--- If failed to find the object here as well, exit if(obj==NULL) return; //--- Get the name of the renamed graphical object on the chart, which is not in the collection list string name_new=this.FindExtraObj(chart_id); //--- Set a new name for the collection list object, which does not correspond to any graphical object on the chart obj.SetName(name_new); } //--- Update the properties of the obtained object //--- and check their change obj.PropertiesRefresh(); obj.PropertiesCheckChanged(); } } //+------------------------------------------------------------------+
The method creating a new standard graphical object:
//+------------------------------------------------------------------+ //| Create a new standard graphical object | //+------------------------------------------------------------------+ bool CGraphElementsCollection::CreateNewStdGraphObject(const long chart_id, const string name, const ENUM_OBJECT type, const int subwindow, const datetime time1, const double price1, const datetime time2=0, const double price2=0, const datetime time3=0, const double price3=0, const datetime time4=0, const double price4=0, const datetime time5=0, const double price5=0) { ::ResetLastError(); switch(type) { //--- Lines case OBJ_VLINE : return ::ObjectCreate(chart_id,name,OBJ_VLINE,subwindow,time1,0); case OBJ_HLINE : return ::ObjectCreate(chart_id,name,OBJ_HLINE,subwindow,0,price1); case OBJ_TREND : return ::ObjectCreate(chart_id,name,OBJ_TREND,subwindow,time1,price1,time2,price2); case OBJ_TRENDBYANGLE : return ::ObjectCreate(chart_id,name,OBJ_TRENDBYANGLE,subwindow,time1,price1,time2,price2); case OBJ_CYCLES : return ::ObjectCreate(chart_id,name,OBJ_CYCLES,subwindow,time1,price1,time2,price2); case OBJ_ARROWED_LINE : return ::ObjectCreate(chart_id,name,OBJ_ARROWED_LINE,subwindow,time1,price1,time2,price2); //--- Channels case OBJ_CHANNEL : return ::ObjectCreate(chart_id,name,OBJ_CHANNEL,subwindow,time1,price1,time2,price2,time3,price3); case OBJ_STDDEVCHANNEL : return ::ObjectCreate(chart_id,name,OBJ_STDDEVCHANNEL,subwindow,time1,price1,time2,price2); case OBJ_REGRESSION : return ::ObjectCreate(chart_id,name,OBJ_REGRESSION,subwindow,time1,price1,time2,price2); case OBJ_PITCHFORK : return ::ObjectCreate(chart_id,name,OBJ_PITCHFORK,subwindow,time1,price1,time2,price2,time3,price3); //--- Gann case OBJ_GANNLINE : return ::ObjectCreate(chart_id,name,OBJ_GANNLINE,subwindow,time1,price1,time2,price2); case OBJ_GANNFAN : return ::ObjectCreate(chart_id,name,OBJ_GANNFAN,subwindow,time1,price1,time2,price2); case OBJ_GANNGRID : return ::ObjectCreate(chart_id,name,OBJ_GANNGRID,subwindow,time1,price1,time2,price2); //--- Fibo case OBJ_FIBO : return ::ObjectCreate(chart_id,name,OBJ_FIBO,subwindow,time1,price1,time2,price2); case OBJ_FIBOTIMES : return ::ObjectCreate(chart_id,name,OBJ_FIBOTIMES,subwindow,time1,price1,time2,price2); case OBJ_FIBOFAN : return ::ObjectCreate(chart_id,name,OBJ_FIBOFAN,subwindow,time1,price1,time2,price2); case OBJ_FIBOARC : return ::ObjectCreate(chart_id,name,OBJ_FIBOARC,subwindow,time1,price1,time2,price2); case OBJ_FIBOCHANNEL : return ::ObjectCreate(chart_id,name,OBJ_FIBOCHANNEL,subwindow,time1,price1,time2,price2,time3,price3); case OBJ_EXPANSION : return ::ObjectCreate(chart_id,name,OBJ_EXPANSION,subwindow,time1,price1,time2,price2,time3,price3); //--- Elliott case OBJ_ELLIOTWAVE5 : return ::ObjectCreate(chart_id,name,OBJ_ELLIOTWAVE5,subwindow,time1,price1,time2,price2,time3,price3,time4,price4,time5,price5); case OBJ_ELLIOTWAVE3 : return ::ObjectCreate(chart_id,name,OBJ_ELLIOTWAVE3,subwindow,time1,price1,time2,price2,time3,price3); //--- Shapes case OBJ_RECTANGLE : return ::ObjectCreate(chart_id,name,OBJ_RECTANGLE,subwindow,time1,price1,time2,price2); case OBJ_TRIANGLE : return ::ObjectCreate(chart_id,name,OBJ_TRIANGLE,subwindow,time1,price1,time2,price2,time3,price3); case OBJ_ELLIPSE : return ::ObjectCreate(chart_id,name,OBJ_ELLIPSE,subwindow,time1,price1,time2,price2,time3,price3); //--- Arrows case OBJ_ARROW_THUMB_UP : return ::ObjectCreate(chart_id,name,OBJ_ARROW_THUMB_UP,subwindow,time1,price1); case OBJ_ARROW_THUMB_DOWN : return ::ObjectCreate(chart_id,name,OBJ_ARROW_THUMB_DOWN,subwindow,time1,price1); case OBJ_ARROW_UP : return ::ObjectCreate(chart_id,name,OBJ_ARROW_UP,subwindow,time1,price1); case OBJ_ARROW_DOWN : return ::ObjectCreate(chart_id,name,OBJ_ARROW_DOWN,subwindow,time1,price1); case OBJ_ARROW_STOP : return ::ObjectCreate(chart_id,name,OBJ_ARROW_STOP,subwindow,time1,price1); case OBJ_ARROW_CHECK : return ::ObjectCreate(chart_id,name,OBJ_ARROW_CHECK,subwindow,time1,price1); case OBJ_ARROW_LEFT_PRICE : return ::ObjectCreate(chart_id,name,OBJ_ARROW_LEFT_PRICE,subwindow,time1,price1); case OBJ_ARROW_RIGHT_PRICE : return ::ObjectCreate(chart_id,name,OBJ_ARROW_RIGHT_PRICE,subwindow,time1,price1); case OBJ_ARROW_BUY : return ::ObjectCreate(chart_id,name,OBJ_ARROW_BUY,subwindow,time1,price1); case OBJ_ARROW_SELL : return ::ObjectCreate(chart_id,name,OBJ_ARROW_SELL,subwindow,time1,price1); case OBJ_ARROW : return ::ObjectCreate(chart_id,name,OBJ_ARROW,subwindow,time1,price1); //--- Graphical objects case OBJ_TEXT : return ::ObjectCreate(chart_id,name,OBJ_TEXT,subwindow,time1,price1); case OBJ_LABEL : return ::ObjectCreate(chart_id,name,OBJ_LABEL,subwindow,0,0); case OBJ_BUTTON : return ::ObjectCreate(chart_id,name,OBJ_BUTTON,subwindow,0,0); case OBJ_CHART : return ::ObjectCreate(chart_id,name,OBJ_CHART,subwindow,0,0); case OBJ_BITMAP : return ::ObjectCreate(chart_id,name,OBJ_BITMAP,subwindow,time1,price1); case OBJ_BITMAP_LABEL : return ::ObjectCreate(chart_id,name,OBJ_BITMAP_LABEL,subwindow,0,0); case OBJ_EDIT : return ::ObjectCreate(chart_id,name,OBJ_EDIT,subwindow,0,0); case OBJ_EVENT : return ::ObjectCreate(chart_id,name,OBJ_EVENT,subwindow,time1,0); case OBJ_RECTANGLE_LABEL : return ::ObjectCreate(chart_id,name,OBJ_RECTANGLE_LABEL,subwindow,0,0); //--- default: return false; } } //+------------------------------------------------------------------+
The method receives the ID of the chart to feature an object, its name, type, chart subwindow and five pivot point coordinates. The first coordinate (time and price) is mandatory, while the rest have predefined default values allowing users to construct any standard graphical object. In the method, reset the last error code and return the result of the ObjectCreate() function execution depending on an object type. In case of an object creation error, the CreateNewStdGraphObjectAndGetCtrlObj() method considered above and calling the method sends the error message with the error code and its description to the journal.
Now all is ready to test the improvements implemented to classes and program standard graphical objects.
Test
Rather than creating all graphical objects, I will limit myself to a vertical line only. It will be created when left-clicking on a chart while holding down Ctrl. Let's check creation of an object, handling an error when trying to create an object with the same name, handling changes in the time coordinate, as well as tracking changes in the pivot point coordinates of objects having more than two pivot points.
To perform the test, I will use the EA from the previous article and save it in \MQL5\Experts\TestDoEasy\Part89\ as TestDoEasyPart89.mq5.
In the EA's OnChartEvent() handler, disable the code block for creating form objects while holding down Ctrl and add the code block for creating a vertical line with a specified name when clicking on a chart while holding down Ctrl in the mouse click coordinate:
//+------------------------------------------------------------------+ //| ChartEvent function | //+------------------------------------------------------------------+ void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam) { //--- If working in the tester, exit if(MQLInfoInteger(MQL_TESTER)) return; //--- If the mouse is moved /* if(id==CHARTEVENT_MOUSE_MOVE) { CForm *form=NULL; datetime time=0; double price=0; int wnd=0; //--- If Ctrl is not pressed, if(!IsCtrlKeyPressed()) { //--- clear the list of created form objects, allow scrolling a chart with the mouse and show the context menu list_forms.Clear(); ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,true); ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,true); return; } //--- If X and Y chart coordinates are successfully converted into time and price, if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,wnd,time,price)) { //--- get the bar index the cursor is hovered over int index=iBarShift(Symbol(),PERIOD_CURRENT,time); if(index==WRONG_VALUE) return; //--- Get the bar index by index CBar *bar=engine.SeriesGetBar(Symbol(),Period(),index); if(bar==NULL) return; //--- Convert the coordinates of a chart from the time/price representation of the bar object to the X and Y coordinates int x=(int)lparam,y=(int)dparam; if(!ChartTimePriceToXY(ChartID(),0,bar.Time(),(bar.Open()+bar.Close())/2.0,x,y)) return; //--- Disable moving a chart with the mouse and showing the context menu ChartSetInteger(ChartID(),CHART_MOUSE_SCROLL,false); ChartSetInteger(ChartID(),CHART_CONTEXT_MENU,false); //--- Create the form object name and hide all objects except one having such a name string name="FormBar_"+(string)index; HideFormAllExceptOne(name); //--- If the form object with such a name does not exist yet, if(!IsPresentForm(name)) { //--- create a new form object form=bar.CreateForm(index,name,x,y,114,16); if(form==NULL) return; //--- Set activity and unmoveability flags for the form form.SetActive(true); form.SetMovable(false); //--- Set the opacity of 200 form.SetOpacity(200); //--- The form background color is set as the first color from the color array form.SetColorBackground(array_clr[0]); //--- Form outlining frame color form.SetColorFrame(C'47,70,59'); //--- Draw the shadow drawing flag form.SetShadow(true); //--- Calculate the shadow color as the chart background color converted to the monochrome one color clrS=form.ChangeColorSaturation(form.ColorBackground(),-100); //--- If the settings specify the usage of the chart background color, replace the monochrome color with 20 units //--- Otherwise, use the color specified in the settings for drawing the shadow color clr=(InpUseColorBG ? form.ChangeColorLightness(clrS,-20) : InpColorForm3); //--- Draw the form shadow with the right-downwards offset from the form by three pixels along all axes //--- Set the shadow opacity to 200, while the blur radius is equal to 4 form.DrawShadow(2,2,clr,200,3); //--- Fill the form background with a vertical gradient form.Erase(array_clr,form.Opacity()); //--- Draw an outlining rectangle at the edges of the form form.DrawRectangle(0,0,form.Width()-1,form.Height()-1,form.ColorFrame(),form.Opacity()); //--- If failed to add the form object to the list, remove the form and exit the handler if(!list_forms.Add(form)) { delete form; return; } //--- Capture the form appearance form.Done(); } //--- If the form object exists, if(form!=NULL) { //--- draw a text with the bar type description on it and show the form. The description corresponds to the mouse cursor position form.TextOnBG(0,bar.BodyTypeDescription(),form.Width()/2,form.Height()/2-1,FRAME_ANCHOR_CENTER,C'7,28,21'); form.Show(); } //--- Re-draw the chart ChartRedraw(); } } */ if(id==CHARTEVENT_CLICK) { if(!IsCtrlKeyPressed()) return; datetime time=0; double price=0; int sw=0; if(ChartXYToTimePrice(ChartID(),(int)lparam,(int)dparam,sw,time,price)) engine.GetGraphicObjCollection().CreateLineVertical(ChartID(),"LineVertical",0,time); } engine.GetGraphicObjCollection().OnChartEvent(id,lparam,dparam,sparam); } //+------------------------------------------------------------------+
Compile the EA and launch it on the chart.
First, create a vertical line by clicking on a chart while holding down Ctrl, have a look at the line ID and at how object properties change when moving the line along the chart. If we re-create the same line, we get the error message in the journal.
Next, create an equidistant channel, see its ID value and check how changes in the properties of its three pivot points are tracked:
What's next?
In the next article, I will continue working on the functionality for programming graphical objects.
Leave your questions, comments and suggestions in the comments.
*Previous articles within the series:
- Graphics in DoEasy library (Part 83): Class of the abstract standard graphical object
- Graphics in DoEasy library (Part 84): Descendant classes of the abstract standard graphical object
- Graphics in DoEasy library (Part 85): Graphical object collection - adding newly created objects
- Graphics in DoEasy library (Part 86): Graphical object collection - managing property modification
- Graphics in DoEasy library (Part 87): Graphical object collection - managing object property modification on all open charts
- Graphics in DoEasy library (Part 88): Graphical object collection — two-dimensional dynamic array for storing dynamically changing object properties
Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/10119
- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use