//+------------------------------------------------------------------+ //| ObjSync.mqh | //| Copyright 2026, Allan NiƱal | //| https://www.allanninal.dev/mql5 | //+------------------------------------------------------------------+ //| Chart objects that survive a redraw. A product that redraws on | //| every new bar used to delete all its objects and create them | //| again; here each object is created once, its points and | //| properties are changed only when they differ, and only the | //| objects the pass did not ask for are deleted: | //| OsBegin(); ... OsRect / OsTrend / OsText / OsLabel ... | //| OsEnd(prefix); | //| An object is marked "used in this pass" in its z-order property | //| (a pass number; the objects are not selectable). Works the same | //| in MQL5 and MQL4. | //+------------------------------------------------------------------+ // SPDX-License-Identifier: LicenseRef-PolyForm-Noncommercial-1.0.0 (see LICENSE) #ifndef OBJSYNC_MQH #define OBJSYNC_MQH int g_os_gen=0; void OsBegin(void) { g_os_gen=(g_os_gen>=2000000000 ? 1 : g_os_gen+1); } void OsInt(const string name,const ENUM_OBJECT_PROPERTY_INTEGER prop,const long v) { if(ObjectGetInteger(0,name,prop)!=v) ObjectSetInteger(0,name,prop,v); } void OsStr(const string name,const ENUM_OBJECT_PROPERTY_STRING prop,const string v) { if(ObjectGetString(0,name,prop)!=v) ObjectSetString(0,name,prop,v); } //--- the object `name`: created once, moved only when a point differs, marked as used in this pass. //--- npts is 1 or 2. Returns false when the chart refuses the object. bool OsPlace(const string name,const ENUM_OBJECT type,const int npts,const datetime t1,const double p1, const datetime t2,const double p2) { if(ObjectFind(0,name)<0) { bool ok=(npts==1 ? ObjectCreate(0,name,type,0,t1,p1) : ObjectCreate(0,name,type,0,t1,p1,t2,p2)); if(!ok) return(false); ObjectSetInteger(0,name,OBJPROP_SELECTABLE,false); ObjectSetInteger(0,name,OBJPROP_HIDDEN,true); } else { if(ObjectGetInteger(0,name,OBJPROP_TIME,0)!=(long)t1 || ObjectGetDouble(0,name,OBJPROP_PRICE,0)!=p1) ObjectMove(0,name,0,t1,p1); if(npts>1 && (ObjectGetInteger(0,name,OBJPROP_TIME,1)!=(long)t2 || ObjectGetDouble(0,name,OBJPROP_PRICE,1)!=p2)) ObjectMove(0,name,1,t2,p2); } ObjectSetInteger(0,name,OBJPROP_ZORDER,g_os_gen); return(true); } //--- delete every object with `prefix` that this pass did not ask for void OsEnd(const string prefix) { for(int i=ObjectsTotal(0,-1,-1)-1; i>=0; i--) { string nm=ObjectName(0,i); if(StringFind(nm,prefix)==0 && ObjectGetInteger(0,nm,OBJPROP_ZORDER)!=g_os_gen) ObjectDelete(0,nm); } } #endif //+------------------------------------------------------------------+