mql5/Experts/_FrameWork/Graphics/ZoneObject.mqh
younglubah-gmail a4b935d8d8
2026-09-30 09:06:59 +02:00

533 lines
18 KiB
MQL5

//+------------------------------------------------------------------+
//| ZoneObject.mqh |
//| Copyright 2025, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link "https://www.mql5.com"
#property version "1.00"
#include "../Market/TimeHelpers.mqh"
#include <ABCD/chartObjects.mqh>
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class ZoneObject
{
protected:
int chartId;
ENUM_TIMEFRAMES tf;
string name;
string symbol;
string suffix;
string zoneText;
string zoneObjName;
string zoneTextObjName;
string _zoneText;
string _zoneObjName;
string _zoneTextObjName;
color levelColor;
color textColor;
int pxDiff;
bool isDrawExt;
TimePrice a;
TimePrice b;
TimePrice c, d;
bool isBull;
bool isBroken;
// --- Dirty tracking ---
bool m_dirty, txt_dirty, _txt_dirty;
bool m_lastVisible;
bool m_lastExtVisible;
datetime m_from_time, m_to_time;
double m_min_price, m_max_price;
virtual void Draw(int zoomLevel, int minZoom) = 0;
virtual void DrawExt(int zoomLevel, int minZoom);
virtual void DrawText(string name, string text, TimePrice &_a, double angle = 0);
// Object deletion
virtual bool IsExtVisible(datetime fromTime, datetime toTime, double minPrice, double maxPrice);
virtual bool IsVisible(datetime fromTime, datetime toTime, double minPrice, double maxPrice)
{
m_from_time = fromTime;
m_to_time = toTime;
m_min_price = minPrice;
m_max_price = maxPrice;
return false;
}
void MarkDirty() { m_dirty = true; }
public:
ZoneObject(string _name, string _symbol, ENUM_TIMEFRAMES _tf, bool _isBull, string _zoneTxt0, int _chartId = 0) : suffix("visuals_"),
m_dirty(true), txt_dirty(true), _txt_dirty(true), name(_name), symbol(_symbol), isBull(_isBull), zoneText(_zoneTxt0), tf(_tf), chartId(_chartId), pxDiff(6),
levelColor(clrRosyBrown), textColor(clrRosyBrown), m_lastExtVisible(false), isDrawExt(true)
{
zoneObjName = suffix + name + "_" + GetTimeframeDescription(tf);
zoneTextObjName = suffix + name + "_txt_" + GetTimeframeDescription(tf);
_zoneObjName = suffix + name + "_ext_" + GetTimeframeDescription(tf);
_zoneTextObjName = suffix + name + "_txt_ext_" + GetTimeframeDescription(tf);
}
~ZoneObject() {Delete();}
void UpdateDraw(int zoomLevel, datetime fromTime, datetime toTime, double minPrice, double maxPrice, int minZoom = 2);
virtual void SetFontSize(int fontSize);
// Property setters (call MarkDirty when changed)
void SetText(string txt) { if(zoneText != txt) { zoneText = txt; MarkDirty(); } }
void SetExtText(string txt) { if(_zoneText != txt) { _zoneText = txt; MarkDirty(); } }
void SetColor(color clr) { if(levelColor != clr) { levelColor = clr; MarkDirty(); } }
void SetBothColor(color clr) { if(levelColor != clr) { levelColor = clr; textColor = clr; MarkDirty(); } }
// void SetExtEnd(TimePrice &_a) {c = _a; MarkDirty();}
void SetBull(bool _isBull, TimePrice &_a);
void SetDrawExt(bool _isDrawExt);
// void SetFontSize(int fontSize);
void SetAtBack(bool back = true);
void SetFill(bool fill = true);
virtual void SetA(TimePrice &_a) {a = _a; MarkDirty();}
virtual void SetB(TimePrice &_b) {b = _b; MarkDirty();}
void Delete();
void DeleteAllText();
void DeleteLevel();
void DeleteLevelText();
void DeleteExtender();
void DeleteExtenderText();
string GetText() { return zoneText;}
string GetName() { return name;}
ENUM_TIMEFRAMES GetTimeFrame() { return tf;}
string GetSymbol() { return symbol;}
TimePrice GetA() { return a;}
TimePrice GetB() { return b;}
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
bool ZoneObject:: IsExtVisible(datetime fromTime, datetime toTime, double minPrice, double maxPrice)
{
// Print("[PointLevel]::IsVisible() ",fromTime," :: ", a.x,"-- ", a.x >= fromTime);
return (d.x >= fromTime && c.x <= toTime && d.y >= minPrice && c.y <= maxPrice);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneObject::SetDrawExt(bool _isDrawExt = false)
{
isDrawExt = _isDrawExt;
MarkDirty();
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneObject::UpdateDraw(int zoomLevel, datetime fromTime, datetime toTime, double minPrice, double maxPrice, int minZoom = 2)
{
// 1. Check visibility for main level
bool isVisible = IsVisible(fromTime, toTime, minPrice, maxPrice);
bool visibilityChanged = (isVisible != m_lastVisible);
m_lastVisible = isVisible;
// 2. Check visibility for extender
bool isExtVisible = IsExtVisible(fromTime, toTime, minPrice, maxPrice);
bool visibilityExtChanged = (isExtVisible != m_lastExtVisible);
m_lastExtVisible = isExtVisible;
// 3. Handle MAIN level
if(!isVisible)
{
if(visibilityChanged)
DeleteLevel(); // main left visible area → delete objects
}
else
{
// main is visible – redraw if dirty OR just became visible
if(visibilityChanged || m_dirty || txt_dirty)
Draw(zoomLevel, minZoom);
}
// 4. Handle EXTENDER level (same dirty flag)
if(!isExtVisible)
{
if(visibilityExtChanged)
DeleteExtender(); // ext left visible area → delete objects
}
else
{
// ext is visible – redraw if dirty OR just became visible
if(visibilityExtChanged || m_dirty || _txt_dirty)
{
DrawExt(zoomLevel, minZoom);
}
}
// 5. Clear the shared dirty flag only after both components have been processed
if(m_dirty)
m_dirty = false;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneObject::DrawExt(int zoomLevel, int minZoom)
{
if(!isDrawExt)
{
return;
}
if(ObjectFind(chartId, _zoneObjName) < 0)
{
ObjectCreate(chartId, _zoneObjName, OBJ_TREND, 0, c.x, c.y, d.x, d.y);
GetVisibilityMaskManual(_zoneObjName, tf);
}
else
{
ObjectMove(chartId, _zoneObjName, 0, c.x, c.y);
ObjectMove(chartId, _zoneObjName, 1, d.x, d.y);
}
ObjectSetInteger(chartId, _zoneObjName, OBJPROP_COLOR, levelColor);
ObjectSetInteger(chartId, _zoneObjName, OBJPROP_STYLE, STYLE_DOT);
if(_zoneText != "" && zoomLevel >= minZoom)
{
double angle;
CalcLineAngleDeg(chartId, c.x, c.y, d.x, d.y, angle);
DrawText(_zoneTextObjName, _zoneText, c.CenterCoord(d), angle);
}
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneObject::DeleteExtender()
{
DeleteExtenderText();
ObjectDelete(chartId, _zoneObjName);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneObject::DeleteExtenderText()
{
ObjectDelete(chartId, _zoneTextObjName);
_txt_dirty = true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneObject::SetFontSize(int fontSize)
{
ObjectSetInteger(chartId, _zoneTextObjName, OBJPROP_FONTSIZE, fontSize);
ObjectSetInteger(chartId, zoneTextObjName, OBJPROP_FONTSIZE, fontSize);
MarkDirty();
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneObject::Delete()
{
DeleteLevel();
DeleteExtender();
MarkDirty();
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneObject::DeleteLevel()
{
bool res1 = ObjectDelete(chartId, zoneObjName);
// Print("Deleting main object: ", zoneObjName, " on chart ", chartId, " -> Result: ", res1);
txt_dirty = true;
DeleteLevelText(); // also modify this one
}
void ZoneObject::DeleteLevelText()
{
bool res2 = ObjectDelete(chartId, zoneTextObjName);
// Print("Deleting text object: ", zoneTextObjName, " on chart ", chartId, " -> Result: ", res2);
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneObject:: DeleteAllText()
{
DeleteExtenderText();
DeleteLevelText();
txt_dirty = true;
_txt_dirty = true;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
void ZoneObject::DrawText(string _name, string text, TimePrice &_a, double angle = 0)
{
int x = 0, y = 0;
ChartTimePriceToXY(chartId, 0, _a.x, _a.y, x, y);
if(!isBull)
y += pxDiff;
else
y -= pxDiff;
int sub_window = 0;
datetime newTime = 0;
double newPrice = 0.0;
bool result = ChartXYToTimePrice(chartId, x, y, sub_window, newTime, newPrice);
if(ObjectFind(chartId, _name) < 0)
{
ObjectCreate(chartId, _name, OBJ_TEXT, 0, _a.x, newPrice);
GetVisibilityMaskManual(_name, tf);
}
else
ObjectMove(chartId, _name, 0, _a.x, _a.y);
ObjectSetString(chartId, _name, OBJPROP_TEXT, text);
ObjectSetInteger(chartId, _name, OBJPROP_COLOR, textColor);
ObjectSetDouble(chartId, _name, OBJPROP_ANGLE, angle);
if(!isBull)
ObjectSetInteger(chartId, _name, OBJPROP_ANCHOR, ANCHOR_UPPER);
else
ObjectSetInteger(chartId, _name, OBJPROP_ANCHOR, ANCHOR_LOWER);
if(txt_dirty)
txt_dirty = false;
}
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class PointLevel : public ZoneObject
{
private:
public:
PointLevel(string _name, string _symbol, ENUM_TIMEFRAMES _tf, bool _isBull, TimePrice &_point, string txt = ""):
ZoneObject(_name, _symbol, _tf, _isBull, txt)
{
a.Set(_point);
b.Set(_point);
c.Set(_point);
d.Set(TimeCurrent(), c.y);
}
virtual void SetFontSize(int fontSize)
{
ObjectSetInteger(chartId, _zoneTextObjName, OBJPROP_FONTSIZE, fontSize);
ObjectSetInteger(chartId, zoneTextObjName, OBJPROP_FONTSIZE, fontSize);
ObjectSetInteger(chartId, zoneObjName, OBJPROP_FONTSIZE, (long)MathMax(fontSize * 0.5, 8));
MarkDirty();
}
virtual void SetA(TimePrice &_point)
{
a = _point;
b.Set(_point);
c.Set(_point);
d.Set(TimeCurrent(), c.y);
MarkDirty();
}
virtual bool IsVisible(datetime fromTime, datetime toTime, double minPrice, double maxPrice)
{
ZoneObject::IsVisible(fromTime, toTime, minPrice, maxPrice);
return (a.x >= fromTime && a.x <= toTime && a.y >= minPrice && a.y <= maxPrice);
}
virtual void Draw(int zoomLevel, int minZoom)
{
if(ObjectFind(chartId, zoneObjName) < 0)
{
ObjectCreate(chartId, zoneObjName, OBJ_TEXT, 0, a.x, a.y, b.x, b.y);
GetVisibilityMaskManual(zoneObjName, tf);
}
else
ObjectMove(chartId, zoneObjName, 0, a.x, a.y);
ObjectSetInteger(chartId, zoneObjName, OBJPROP_COLOR, levelColor);
ObjectSetString(chartId, zoneObjName, OBJPROP_TEXT, "●");
ObjectSetInteger(chartId, zoneObjName, OBJPROP_ANCHOR, ANCHOR_CENTER);
if(zoneText != "" && zoomLevel >= minZoom)
{
double angle;
CalcLineAngleDeg(chartId, a.x, a.y, b.x, b.y, angle);
DrawText(zoneTextObjName, zoneText, a.CenterCoord(b), angle);
}
if(isDrawExt)
{
DrawExt(zoomLevel, minZoom);
}
}
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class TrendLevel : public ZoneObject
{
private:
public:
TrendLevel(string _name, string _symbol, ENUM_TIMEFRAMES _tf, bool _isBull, TimePrice &_point, TimePrice &_pointB, string txt = ""):
ZoneObject(_name, _symbol, _tf, _isBull, txt)
{
a.Set(_point);
b.Set(_pointB);
c.Set(_point.MaxT(_pointB));
d.Set(TimeCurrent(), c.y);
}
virtual void SetB(TimePrice &_point)
{
b.Set(_point);
c.Set(a.MaxT(b));
d.Set(TimeCurrent(), c.y);
MarkDirty();
}
virtual bool IsVisible(datetime fromTime, datetime toTime, double minPrice, double maxPrice)
{
ZoneObject::IsVisible(fromTime, toTime, minPrice, maxPrice);
return MathMax(a.x, b.x) >= fromTime && MathMin(a.x, b.x) <= toTime &&
MathMax(a.y, b.y) >= minPrice && MathMin(a.y, b.y) <= maxPrice;
}
virtual void Draw(int zoomLevel, int minZoom)
{
if(ObjectFind(chartId, zoneObjName) < 0)
{
ObjectCreate(chartId, zoneObjName, OBJ_TREND, 0, a.x, a.y, b.x, b.y);
GetVisibilityMaskManual(zoneObjName, tf);
}
ObjectSetInteger(chartId, zoneObjName, OBJPROP_COLOR, levelColor);
if(zoneText != "" && zoomLevel >= minZoom)
{
double angle;
CalcLineAngleDeg(chartId, a.x, a.y, b.x, b.y, angle);
DrawText(zoneTextObjName, zoneText, a.CenterCoord(b), angle);
if(isDrawExt)
{
DrawExt(zoomLevel, minZoom);
}
}
}
};
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
class RectLevel : public ZoneObject
{
private:
public:
RectLevel(string _name, string _symbol, ENUM_TIMEFRAMES _tf, bool _isBull, TimePrice &_point, TimePrice &_pointB, string txt = ""):
ZoneObject(_name, _symbol, _tf, _isBull, txt)
{
a.Set(_point);
b.Set(_pointB);
c.Set(_point.MaxT(_pointB).x, _point.Max(_pointB).y);
d.Set(TimeCurrent(), _point.Min(_pointB).y);
}
virtual bool IsVisible(datetime fromTime, datetime toTime, double minPrice, double maxPrice)
{
ZoneObject::IsVisible(fromTime, toTime, minPrice, maxPrice);
return MathMax(a.x, b.x) >= fromTime && MathMin(a.x, b.x) <= toTime &&
MathMax(a.y, b.y) >= minPrice && MathMin(a.y, b.y) <= maxPrice;
}
virtual void DrawText(string _name, string text, TimePrice &_a, double angle = 0)
{
if(ObjectFind(chartId, _name) < 0)
{
ObjectCreate(chartId, _name, OBJ_TEXT, 0, _a.x, _a.y);
GetVisibilityMaskManual(_name, tf);
}
ObjectSetString(chartId, _name, OBJPROP_TEXT, text);
ObjectSetInteger(chartId, _name, OBJPROP_COLOR, textColor);
ObjectSetDouble(chartId, _name, OBJPROP_ANGLE, angle);
ObjectSetInteger(chartId, _name, OBJPROP_ANCHOR, ANCHOR_CENTER);
if(txt_dirty)
txt_dirty = false;
}
virtual void DrawExt(int zoomLevel, int minZoom)
{
if(ObjectFind(chartId, _zoneObjName) < 0)
{
ObjectCreate(chartId, _zoneObjName, OBJ_RECTANGLE, 0, c.x, c.y, d.x, d.y);
GetVisibilityMaskManual(_zoneObjName, tf);
}
ObjectSetInteger(chartId, _zoneObjName, OBJPROP_COLOR, levelColor);
ObjectSetInteger(chartId, _zoneObjName, OBJPROP_STYLE, STYLE_DOT);
if(_zoneText != "" && zoomLevel >= minZoom)
{
DrawText(_zoneTextObjName, _zoneText, c.CenterCoord(d));
}
}
virtual void Draw(int zoomLevel, int minZoom)
{
if(ObjectFind(chartId, zoneObjName) < 0)
{
ObjectCreate(chartId, zoneObjName, OBJ_RECTANGLE, 0, a.x, a.y, b.x, b.y);
GetVisibilityMaskManual(zoneObjName, tf);
}
ObjectSetInteger(chartId, zoneObjName, OBJPROP_COLOR, levelColor);
if(zoneText != "" && zoomLevel >= minZoom)
{
DrawText(zoneTextObjName, zoneText, a.CenterCoord(b));
if(isDrawExt)
{
DrawExt(zoomLevel, minZoom);
}
}
}
};
//+------------------------------------------------------------------+