﻿//+------------------------------------------------------------------+
//|                                                        Panel.mqh |
//|                              Copyright 2026, Christian Benjamin. |
//|                           https://www.mql5.com/en/users/lynchris |
//+------------------------------------------------------------------+
#property copyright "Copyright 2026, Christian Benjamin."
#property link      "https://www.mql5.com/en/users/lynchris"
#property version   "1.0"
#property strict

#ifndef __PANEL_MQH__
#define __PANEL_MQH__

#include "Managers.mqh"

//+------------------------------------------------------------------+
//| CPanel – builds and manages the graphical user interface         |
//+------------------------------------------------------------------+
class CPanel
  {
private:
   //--- Each row in the panel corresponds to one trading symbol
   struct RowData
     {
      string         symbol;
      string         labelName;
      string         buyBtnName;
      string         sellBtnName;
      string         closeBtnName;
      string         profitLabelName;
      string         tpEditName;
      string         slEditName;
      string         bgRectName;
     };

   CSymbolManager    *m_symbolManager;
   CTradeManager     *m_tradeManager;

   string            m_prefix;
   int               m_xOffset;
   int               m_yOffset;
   color             m_bgColor;
   color             m_rowEvenColor;
   color             m_rowOddColor;

   int               m_rowHeight;
   int               m_headerHeight;
   int               m_infoRowHeight;
   int               m_buttonRowHeight;
   int               m_marginLeft;
   int               m_marginTop;
   int               m_spacing;

   int               m_colWidths[7];
   int               m_colOffsets[7];

   RowData           m_rows[];
   bool              m_created;

   //--- Global control names (account info and top buttons)
   string            m_bgName;
   string            m_balanceLabel;
   string            m_equityLabel;
   string            m_marginLabel;
   string            m_floatPLabel;
   string            m_posCountLabel;
   string            m_closeAllBtn;
   string            m_closeWinnersBtn;
   string            m_closeLosersBtn;
   string            m_refreshBtn;

   bool              CreateGlobalControls();
   bool              CreateHeader();
   bool              CreateRows();
   void              UpdateGlobalInfo();
   void              UpdateRow(int rowIndex);
   bool              IsButtonClick(string objName);
   bool              IsEditEnd(string objName);
   string            ExtractSymbolFromObject(string objName);

public:
                     CPanel(CSymbolManager *sm, CTradeManager *tm,
          int xOffset, int yOffset, color bgColor);
                    ~CPanel();

   bool              CreatePanel();
   void              DestroyPanel();
   void              UpdatePanel();
   void              OnChartEvent(const int id, const long &lparam,
                                  const double &dparam, const string &sparam);
  };

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CPanel::CPanel(CSymbolManager *sm, CTradeManager *tm,
               int xOffset, int yOffset, color bgColor)
   : m_symbolManager(sm),
     m_tradeManager(tm),
     m_created(false)
  {
   m_prefix = "MSP_";
   m_xOffset = xOffset;
   m_yOffset = yOffset;

//--- The background colour is a light grey to stand out on the chart
   m_bgColor = clrWhiteSmoke;
   m_rowEvenColor = C'245,245,245';
   m_rowOddColor  = C'235,235,235';

//--- Layout dimensions (tuned for readability)
   m_rowHeight       = 36;
   m_headerHeight    = 28;
   m_infoRowHeight   = 30;
   m_buttonRowHeight = 34;
   m_marginLeft      = 12;
   m_marginTop       = 8;
   m_spacing         = 6;

//--- Column widths: symbol label, buttons, P/L display, and edit fields
   m_colWidths[0] = 90;
   m_colWidths[1] = 60;
   m_colWidths[2] = 60;
   m_colWidths[3] = 65;
   m_colWidths[4] = 80;
   m_colWidths[5] = 75;
   m_colWidths[6] = 75;

//--- Pre‑calculate column offsets to simplify object placement
   int x = m_marginLeft;
   for(int i = 0; i < 7; i++)
     {
      m_colOffsets[i] = x;
      x += m_colWidths[i] + m_spacing;
     }
  }

//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CPanel::~CPanel()
  {
   DestroyPanel();
  }

//+------------------------------------------------------------------+
//| Creates all graphical objects                                    |
//+------------------------------------------------------------------+
bool CPanel::CreatePanel()
  {
   if(m_created)
      return(true);

//--- Calculate total panel size based on the number of symbols
   int totalWidth = m_colOffsets[6] + m_colWidths[6] + 20;
   int rowCount = m_symbolManager.Count();
   int totalHeight = m_marginTop + m_infoRowHeight + 8 +
                     m_buttonRowHeight + 8 +
                     m_headerHeight + 4 +
                     rowCount * m_rowHeight + 8;

//--- First, create the main background rectangle (serves as the panel container)
   string bgName = m_prefix + "bg";
   if(ObjectCreate(0, bgName, OBJ_RECTANGLE_LABEL, 0, 0, 0))
     {
      ObjectSetInteger(0, bgName, OBJPROP_XDISTANCE, m_xOffset);
      ObjectSetInteger(0, bgName, OBJPROP_YDISTANCE, m_yOffset);
      ObjectSetInteger(0, bgName, OBJPROP_XSIZE, totalWidth);
      ObjectSetInteger(0, bgName, OBJPROP_YSIZE, totalHeight);
      ObjectSetInteger(0, bgName, OBJPROP_BACK, false);
      ObjectSetInteger(0, bgName, OBJPROP_COLOR, m_bgColor);
      ObjectSetInteger(0, bgName, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, bgName, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, bgName, OBJPROP_BORDER_COLOR, clrGray);
      m_bgName = bgName;
     }
   else
     {
      Print("Failed to create panel background");
      return(false);
     }

//--- Now create all child controls (labels, buttons, edit fields)
   if(!CreateGlobalControls())
      return(false);
   if(!CreateHeader())
      return(false);
   if(!CreateRows())
      return(false);

   m_created = true;
   UpdatePanel();
   return(true);
  }

//+------------------------------------------------------------------+
//| Creates account info labels and global buttons                   |
//+------------------------------------------------------------------+
bool CPanel::CreateGlobalControls()
  {
   int y = m_yOffset + m_marginTop;
   int x = m_xOffset + m_marginLeft;
   int infoSpacing = 35;

//--- Account balance
   m_balanceLabel = m_prefix + "balance";
   if(ObjectCreate(0, m_balanceLabel, OBJ_LABEL, 0, 0, 0))
     {
      ObjectSetInteger(0, m_balanceLabel, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, m_balanceLabel, OBJPROP_YDISTANCE, y);
      ObjectSetString(0, m_balanceLabel, OBJPROP_TEXT, "Bal: 0.00");
      ObjectSetInteger(0, m_balanceLabel, OBJPROP_FONTSIZE, 12);
      ObjectSetInteger(0, m_balanceLabel, OBJPROP_COLOR, clrBlack);
      x += 90 + infoSpacing;
     }

//--- Equity
   m_equityLabel = m_prefix + "equity";
   if(ObjectCreate(0, m_equityLabel, OBJ_LABEL, 0, 0, 0))
     {
      ObjectSetInteger(0, m_equityLabel, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, m_equityLabel, OBJPROP_YDISTANCE, y);
      ObjectSetString(0, m_equityLabel, OBJPROP_TEXT, "Eq: 0.00");
      ObjectSetInteger(0, m_equityLabel, OBJPROP_FONTSIZE, 12);
      ObjectSetInteger(0, m_equityLabel, OBJPROP_COLOR, clrBlack);
      x += 90 + infoSpacing;
     }

//--- Free margin
   m_marginLabel = m_prefix + "margin";
   if(ObjectCreate(0, m_marginLabel, OBJ_LABEL, 0, 0, 0))
     {
      ObjectSetInteger(0, m_marginLabel, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, m_marginLabel, OBJPROP_YDISTANCE, y);
      ObjectSetString(0, m_marginLabel, OBJPROP_TEXT, "Mgn: 0.00");
      ObjectSetInteger(0, m_marginLabel, OBJPROP_FONTSIZE, 12);
      ObjectSetInteger(0, m_marginLabel, OBJPROP_COLOR, clrBlack);
      x += 90 + infoSpacing;
     }

//--- Total floating P/L (colour‑coded later in Update)
   m_floatPLabel = m_prefix + "floatpl";
   if(ObjectCreate(0, m_floatPLabel, OBJ_LABEL, 0, 0, 0))
     {
      ObjectSetInteger(0, m_floatPLabel, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, m_floatPLabel, OBJPROP_YDISTANCE, y);
      ObjectSetString(0, m_floatPLabel, OBJPROP_TEXT, "P/L: 0.00");
      ObjectSetInteger(0, m_floatPLabel, OBJPROP_FONTSIZE, 12);
      ObjectSetInteger(0, m_floatPLabel, OBJPROP_COLOR, clrBlack);
      x += 90 + infoSpacing;
     }

//--- Total open positions count
   m_posCountLabel = m_prefix + "poscount";
   if(ObjectCreate(0, m_posCountLabel, OBJ_LABEL, 0, 0, 0))
     {
      ObjectSetInteger(0, m_posCountLabel, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, m_posCountLabel, OBJPROP_YDISTANCE, y);
      ObjectSetString(0, m_posCountLabel, OBJPROP_TEXT, "Pos: 0");
      ObjectSetInteger(0, m_posCountLabel, OBJPROP_FONTSIZE, 12);
      ObjectSetInteger(0, m_posCountLabel, OBJPROP_COLOR, clrBlack);
     }

//--- Second row: action buttons (Close All, Winners, Losers, Refresh)
   y += m_infoRowHeight + 8;
   x = m_xOffset + m_marginLeft;
   int btnW = 80;
   int btnH = m_buttonRowHeight;
   int btnSpacing = 10;

   m_closeAllBtn = m_prefix + "closeAll";
   if(ObjectCreate(0, m_closeAllBtn, OBJ_BUTTON, 0, 0, 0))
     {
      ObjectSetInteger(0, m_closeAllBtn, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, m_closeAllBtn, OBJPROP_YDISTANCE, y);
      ObjectSetInteger(0, m_closeAllBtn, OBJPROP_XSIZE, btnW);
      ObjectSetInteger(0, m_closeAllBtn, OBJPROP_YSIZE, btnH);
      ObjectSetString(0, m_closeAllBtn, OBJPROP_TEXT, "Close All");
      ObjectSetInteger(0, m_closeAllBtn, OBJPROP_COLOR, clrWhite);
      ObjectSetInteger(0, m_closeAllBtn, OBJPROP_BGCOLOR, clrRed);
      x += btnW + btnSpacing;
     }

   m_closeWinnersBtn = m_prefix + "closeWinners";
   if(ObjectCreate(0, m_closeWinnersBtn, OBJ_BUTTON, 0, 0, 0))
     {
      ObjectSetInteger(0, m_closeWinnersBtn, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, m_closeWinnersBtn, OBJPROP_YDISTANCE, y);
      ObjectSetInteger(0, m_closeWinnersBtn, OBJPROP_XSIZE, btnW);
      ObjectSetInteger(0, m_closeWinnersBtn, OBJPROP_YSIZE, btnH);
      ObjectSetString(0, m_closeWinnersBtn, OBJPROP_TEXT, "Winners");
      ObjectSetInteger(0, m_closeWinnersBtn, OBJPROP_COLOR, clrWhite);
      ObjectSetInteger(0, m_closeWinnersBtn, OBJPROP_BGCOLOR, clrGreen);
      x += btnW + btnSpacing;
     }

   m_closeLosersBtn = m_prefix + "closeLosers";
   if(ObjectCreate(0, m_closeLosersBtn, OBJ_BUTTON, 0, 0, 0))
     {
      ObjectSetInteger(0, m_closeLosersBtn, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, m_closeLosersBtn, OBJPROP_YDISTANCE, y);
      ObjectSetInteger(0, m_closeLosersBtn, OBJPROP_XSIZE, btnW);
      ObjectSetInteger(0, m_closeLosersBtn, OBJPROP_YSIZE, btnH);
      ObjectSetString(0, m_closeLosersBtn, OBJPROP_TEXT, "Losers");
      ObjectSetInteger(0, m_closeLosersBtn, OBJPROP_COLOR, clrWhite);
      ObjectSetInteger(0, m_closeLosersBtn, OBJPROP_BGCOLOR, clrRed);
      x += btnW + btnSpacing;
     }

   m_refreshBtn = m_prefix + "refresh";
   if(ObjectCreate(0, m_refreshBtn, OBJ_BUTTON, 0, 0, 0))
     {
      ObjectSetInteger(0, m_refreshBtn, OBJPROP_XDISTANCE, x);
      ObjectSetInteger(0, m_refreshBtn, OBJPROP_YDISTANCE, y);
      ObjectSetInteger(0, m_refreshBtn, OBJPROP_XSIZE, btnW);
      ObjectSetInteger(0, m_refreshBtn, OBJPROP_YSIZE, btnH);
      ObjectSetString(0, m_refreshBtn, OBJPROP_TEXT, "Refresh");
      ObjectSetInteger(0, m_refreshBtn, OBJPROP_COLOR, clrWhite);
      ObjectSetInteger(0, m_refreshBtn, OBJPROP_BGCOLOR, clrDodgerBlue);
     }

   return(true);
  }

//+------------------------------------------------------------------+
//| Header row                                                       |
//+------------------------------------------------------------------+
bool CPanel::CreateHeader()
  {
   int y = m_yOffset + m_marginTop + m_infoRowHeight + 8 + m_buttonRowHeight + 8;
   int x = m_xOffset + m_marginLeft;

   string headerLabels[] = {"Symbol", "Buy", "Sell", "Close", "P/L", "TP", "SL"};
   for(int i = 0; i < 7; i++)
     {
      string name = m_prefix + "hdr_" + IntegerToString(i);
      if(ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0))
        {
         ObjectSetInteger(0, name, OBJPROP_XDISTANCE, x + m_colOffsets[i]);
         ObjectSetInteger(0, name, OBJPROP_YDISTANCE, y);
         ObjectSetString(0, name, OBJPROP_TEXT, headerLabels[i]);
         ObjectSetInteger(0, name, OBJPROP_FONTSIZE, 11);
         ObjectSetInteger(0, name, OBJPROP_COLOR, clrBlack);
        }
      else
        {
         Print("Failed to create header label ", name);
         return(false);
        }
     }
   return(true);
  }

//+------------------------------------------------------------------+
//| Creates rows with alternating backgrounds                        |
//+------------------------------------------------------------------+
bool CPanel::CreateRows()
  {
   int count = m_symbolManager.Count();
   ArrayResize(m_rows, count);

   int headerY = m_yOffset + m_marginTop + m_infoRowHeight + 8 + m_buttonRowHeight + 8;
   int y = headerY + m_headerHeight + 4;

   for(int r = 0; r < count; r++)
     {
      string symbol = m_symbolManager.GetSymbol(r);
      RowData row;
      row.symbol = symbol;

      int x = m_xOffset + m_marginLeft;

      //--- Row background (alternating colours make it easier to read)
      row.bgRectName = m_prefix + "rowbg_" + symbol;
      if(ObjectCreate(0, row.bgRectName, OBJ_RECTANGLE_LABEL, 0, 0, 0))
        {
         int rectWidth = m_colOffsets[6] + m_colWidths[6] + 10;
         int rectHeight = m_rowHeight;
         ObjectSetInteger(0, row.bgRectName, OBJPROP_XDISTANCE, m_xOffset + m_marginLeft - 4);
         ObjectSetInteger(0, row.bgRectName, OBJPROP_YDISTANCE, y);
         ObjectSetInteger(0, row.bgRectName, OBJPROP_XSIZE, rectWidth);
         ObjectSetInteger(0, row.bgRectName, OBJPROP_YSIZE, rectHeight);
         ObjectSetInteger(0, row.bgRectName, OBJPROP_BACK, false);
         color bgColor = (r % 2 == 0) ? m_rowEvenColor : m_rowOddColor;
         ObjectSetInteger(0, row.bgRectName, OBJPROP_COLOR, bgColor);
         ObjectSetInteger(0, row.bgRectName, OBJPROP_STYLE, STYLE_SOLID);
         ObjectSetInteger(0, row.bgRectName, OBJPROP_WIDTH, 0);
        }
      else
        {
         Print("Failed to create row background");
         return(false);
        }

      //--- Symbol name label
      row.labelName = m_prefix + "sym_" + symbol;
      if(ObjectCreate(0, row.labelName, OBJ_LABEL, 0, 0, 0))
        {
         ObjectSetInteger(0, row.labelName, OBJPROP_XDISTANCE, x + m_colOffsets[0]);
         ObjectSetInteger(0, row.labelName, OBJPROP_YDISTANCE, y + 8);
         ObjectSetString(0, row.labelName, OBJPROP_TEXT, symbol);
         ObjectSetInteger(0, row.labelName, OBJPROP_FONTSIZE, 11);
         ObjectSetInteger(0, row.labelName, OBJPROP_COLOR, clrBlack);
        }
      else
        {
         Print("Failed to create symbol label");
         return(false);
        }

      //--- Buy button (green)
      row.buyBtnName = m_prefix + "buy_" + symbol;
      if(ObjectCreate(0, row.buyBtnName, OBJ_BUTTON, 0, 0, 0))
        {
         int btnW = m_colWidths[1] - 4;
         int btnH = m_rowHeight - 6;
         ObjectSetInteger(0, row.buyBtnName, OBJPROP_XDISTANCE, x + m_colOffsets[1] + 2);
         ObjectSetInteger(0, row.buyBtnName, OBJPROP_YDISTANCE, y + 2);
         ObjectSetInteger(0, row.buyBtnName, OBJPROP_XSIZE, btnW);
         ObjectSetInteger(0, row.buyBtnName, OBJPROP_YSIZE, btnH);
         ObjectSetString(0, row.buyBtnName, OBJPROP_TEXT, "Buy");
         ObjectSetInteger(0, row.buyBtnName, OBJPROP_COLOR, clrWhite);
         ObjectSetInteger(0, row.buyBtnName, OBJPROP_BGCOLOR, clrGreen);
        }
      else
        {
         Print("Failed to create Buy button");
         return(false);
        }

      //--- Sell button (red)
      row.sellBtnName = m_prefix + "sell_" + symbol;
      if(ObjectCreate(0, row.sellBtnName, OBJ_BUTTON, 0, 0, 0))
        {
         int btnW = m_colWidths[2] - 4;
         int btnH = m_rowHeight - 6;
         ObjectSetInteger(0, row.sellBtnName, OBJPROP_XDISTANCE, x + m_colOffsets[2] + 2);
         ObjectSetInteger(0, row.sellBtnName, OBJPROP_YDISTANCE, y + 2);
         ObjectSetInteger(0, row.sellBtnName, OBJPROP_XSIZE, btnW);
         ObjectSetInteger(0, row.sellBtnName, OBJPROP_YSIZE, btnH);
         ObjectSetString(0, row.sellBtnName, OBJPROP_TEXT, "Sell");
         ObjectSetInteger(0, row.sellBtnName, OBJPROP_COLOR, clrWhite);
         ObjectSetInteger(0, row.sellBtnName, OBJPROP_BGCOLOR, clrRed);
        }
      else
        {
         Print("Failed to create Sell button");
         return(false);
        }

      //--- Close button (grey) – closes all positions for this symbol
      row.closeBtnName = m_prefix + "close_" + symbol;
      if(ObjectCreate(0, row.closeBtnName, OBJ_BUTTON, 0, 0, 0))
        {
         int btnW = m_colWidths[3] - 4;
         int btnH = m_rowHeight - 6;
         ObjectSetInteger(0, row.closeBtnName, OBJPROP_XDISTANCE, x + m_colOffsets[3] + 2);
         ObjectSetInteger(0, row.closeBtnName, OBJPROP_YDISTANCE, y + 2);
         ObjectSetInteger(0, row.closeBtnName, OBJPROP_XSIZE, btnW);
         ObjectSetInteger(0, row.closeBtnName, OBJPROP_YSIZE, btnH);
         ObjectSetString(0, row.closeBtnName, OBJPROP_TEXT, "Close");
         ObjectSetInteger(0, row.closeBtnName, OBJPROP_COLOR, clrWhite);
         ObjectSetInteger(0, row.closeBtnName, OBJPROP_BGCOLOR, clrGray);
        }
      else
        {
         Print("Failed to create Close button");
         return(false);
        }

      //--- Floating P/L label (colour‑coded)
      row.profitLabelName = m_prefix + "pl_" + symbol;
      if(ObjectCreate(0, row.profitLabelName, OBJ_LABEL, 0, 0, 0))
        {
         ObjectSetInteger(0, row.profitLabelName, OBJPROP_XDISTANCE, x + m_colOffsets[4]);
         ObjectSetInteger(0, row.profitLabelName, OBJPROP_YDISTANCE, y + 8);
         ObjectSetString(0, row.profitLabelName, OBJPROP_TEXT, "0.00");
         ObjectSetInteger(0, row.profitLabelName, OBJPROP_FONTSIZE, 11);
         ObjectSetInteger(0, row.profitLabelName, OBJPROP_COLOR, clrBlack);
        }
      else
        {
         Print("Failed to create P/L label");
         return(false);
        }

      //--- Take Profit edit field (user enters a price level)
      row.tpEditName = m_prefix + "tp_" + symbol;
      if(ObjectCreate(0, row.tpEditName, OBJ_EDIT, 0, 0, 0))
        {
         int editW = m_colWidths[5] - 4;
         int editH = m_rowHeight - 8;
         ObjectSetInteger(0, row.tpEditName, OBJPROP_XDISTANCE, x + m_colOffsets[5] + 2);
         ObjectSetInteger(0, row.tpEditName, OBJPROP_YDISTANCE, y + 2);
         ObjectSetInteger(0, row.tpEditName, OBJPROP_XSIZE, editW);
         ObjectSetInteger(0, row.tpEditName, OBJPROP_YSIZE, editH);
         ObjectSetString(0, row.tpEditName, OBJPROP_TEXT, "0");
         ObjectSetInteger(0, row.tpEditName, OBJPROP_COLOR, clrBlack);
         ObjectSetInteger(0, row.tpEditName, OBJPROP_BGCOLOR, clrWhite);
        }
      else
        {
         Print("Failed to create TP edit");
         return(false);
        }

      //--- Stop Loss edit field
      row.slEditName = m_prefix + "sl_" + symbol;
      if(ObjectCreate(0, row.slEditName, OBJ_EDIT, 0, 0, 0))
        {
         int editW = m_colWidths[6] - 4;
         int editH = m_rowHeight - 8;
         ObjectSetInteger(0, row.slEditName, OBJPROP_XDISTANCE, x + m_colOffsets[6] + 2);
         ObjectSetInteger(0, row.slEditName, OBJPROP_YDISTANCE, y + 2);
         ObjectSetInteger(0, row.slEditName, OBJPROP_XSIZE, editW);
         ObjectSetInteger(0, row.slEditName, OBJPROP_YSIZE, editH);
         ObjectSetString(0, row.slEditName, OBJPROP_TEXT, "0");
         ObjectSetInteger(0, row.slEditName, OBJPROP_COLOR, clrBlack);
         ObjectSetInteger(0, row.slEditName, OBJPROP_BGCOLOR, clrWhite);
        }
      else
        {
         Print("Failed to create SL edit");
         return(false);
        }

      m_rows[r] = row;
      y += m_rowHeight;
     }
   return(true);
  }

//+------------------------------------------------------------------+
//| Destroys all panel objects                                       |
//+------------------------------------------------------------------+
void CPanel::DestroyPanel()
  {
//--- Delete all objects belonging to the panel
//--- Reverse iteration avoids skipping objects as they are removed
   for(int i = ObjectsTotal(0) - 1; i >= 0; i--)
     {
      string name = ObjectName(0, i);

      //--- Only delete objects created by this panel
      if(StringFind(name, m_prefix) == 0)
         ObjectDelete(0, name);
     }

//--- Mark the panel as no longer created
   m_created = false;

//--- Refresh the chart immediately so the deleted objects disappear
   ChartRedraw();
  }

//+------------------------------------------------------------------+
//| Updates the panel                                                |
//+------------------------------------------------------------------+
void CPanel::UpdatePanel()
  {
   if(!m_created)
      return;

//--- Update the global account info row
   UpdateGlobalInfo();

//--- Then update each symbol row
   int count = m_symbolManager.Count();
   for(int r = 0; r < count; r++)
     {
      UpdateRow(r);
     }

   ChartRedraw(0);
  }

//+------------------------------------------------------------------+
//| Updates global info                                              |
//+------------------------------------------------------------------+
void CPanel::UpdateGlobalInfo()
  {
   double balance = AccountInfoDouble(ACCOUNT_BALANCE);
   double equity = AccountInfoDouble(ACCOUNT_EQUITY);
   double margin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
   double totalPL = m_tradeManager.GetTotalProfit();
   int totalPos = m_tradeManager.GetTotalPositions();

   ObjectSetString(0, m_balanceLabel, OBJPROP_TEXT, StringFormat("Bal: %.2f", balance));
   ObjectSetString(0, m_equityLabel, OBJPROP_TEXT, StringFormat("Eq: %.2f", equity));
   ObjectSetString(0, m_marginLabel, OBJPROP_TEXT, StringFormat("Mgn: %.2f", margin));

//--- Format the total P/L with a '+' sign for positive values
   string plText = (totalPL >= 0) ? StringFormat("P/L: +%.2f", totalPL)
                   : StringFormat("P/L: %.2f", totalPL);
   ObjectSetString(0, m_floatPLabel, OBJPROP_TEXT, plText);
   color plColor = (totalPL > 0) ? clrLimeGreen : (totalPL < 0) ? clrRed : clrBlack;
   ObjectSetInteger(0, m_floatPLabel, OBJPROP_COLOR, plColor);

   ObjectSetString(0, m_posCountLabel, OBJPROP_TEXT, StringFormat("Pos: %d", totalPos));
  }

//+------------------------------------------------------------------+
//| Updates a single row                                             |
//+------------------------------------------------------------------+
void CPanel::UpdateRow(int rowIndex)
  {
   RowData row = m_rows[rowIndex];
   string symbol = row.symbol;

   double profit = m_tradeManager.GetSymbolProfit(symbol);
   color profitColor = (profit > 0) ? clrLimeGreen : (profit < 0) ? clrRed : clrBlack;
   string profitText = (profit > 0) ? StringFormat("+%.2f", profit)
                       : StringFormat("%.2f", profit);
   if(profit == 0)
      profitText = "0.00";

   ObjectSetString(0, row.profitLabelName, OBJPROP_TEXT, profitText);
   ObjectSetInteger(0, row.profitLabelName, OBJPROP_COLOR, profitColor);
  }

//+------------------------------------------------------------------+
//| Chart event handler                                              |
//+------------------------------------------------------------------+
void CPanel::OnChartEvent(const int id, const long &lparam,
                          const double &dparam, const string &sparam)
  {
//--- Handle button clicks (CHARTEVENT_OBJECT_CLICK)
   if(id == CHARTEVENT_OBJECT_CLICK)
     {
      if(IsButtonClick(sparam))
        {
         //--- Global buttons (top row)
         if(sparam == m_closeAllBtn)
           {
            m_tradeManager.CloseAllPositions();
            UpdatePanel();
            return;
           }
         else
            if(sparam == m_closeWinnersBtn)
              {
               m_tradeManager.CloseWinners();
               UpdatePanel();
               return;
              }
            else
               if(sparam == m_closeLosersBtn)
                 {
                  m_tradeManager.CloseLosers();
                  UpdatePanel();
                  return;
                 }
               else
                  if(sparam == m_refreshBtn)
                    {
                     UpdatePanel();
                     return;
                    }

         //--- Row‑specific buttons (Buy, Sell, Close) – extract the symbol
         string symbol = ExtractSymbolFromObject(sparam);
         if(symbol != "")
           {
            if(StringFind(sparam, "buy_") != -1)
              {
               m_tradeManager.OpenBuy(symbol);
               UpdatePanel();
              }
            else
               if(StringFind(sparam, "sell_") != -1)
                 {
                  m_tradeManager.OpenSell(symbol);
                  UpdatePanel();
                 }
               else
                  if(StringFind(sparam, "close_") != -1)
                    {
                     m_tradeManager.CloseAllPositions(symbol);
                     UpdatePanel();
                    }
           }
        }
     }
//--- Handle edit field completion (CHARTEVENT_OBJECT_ENDEDIT)
   else
      if(id == CHARTEVENT_OBJECT_ENDEDIT)
        {
         if(IsEditEnd(sparam))
           {
            string symbol = ExtractSymbolFromObject(sparam);
            if(symbol != "")
              {
               string text = ObjectGetString(0, sparam, OBJPROP_TEXT);
               double price = StringToDouble(text);
               if(price <= 0)
                 {
                  Print("Invalid price entered: ", text);
                  return;
                 }

               //--- Apply the entered price as TP or SL for all positions of that symbol
               if(StringFind(sparam, "tp_") != -1)
                  m_tradeManager.SetTakeProfit(symbol, price);
               else
                  if(StringFind(sparam, "sl_") != -1)
                     m_tradeManager.SetStopLoss(symbol, price);
               UpdatePanel();
              }
           }
        }
  }

//+------------------------------------------------------------------+
//| Checks if object is a clickable button                           |
//+------------------------------------------------------------------+
bool CPanel::IsButtonClick(string objName)
  {
   return (StringFind(objName, m_prefix) != -1 &&
           (StringFind(objName, "buy_") != -1 ||
            StringFind(objName, "sell_") != -1 ||
            StringFind(objName, "close_") != -1 ||
            objName == m_closeAllBtn ||
            objName == m_closeWinnersBtn ||
            objName == m_closeLosersBtn ||
            objName == m_refreshBtn));
  }

//+------------------------------------------------------------------+
//| Checks if object is an edit field ended                          |
//+------------------------------------------------------------------+
bool CPanel::IsEditEnd(string objName)
  {
   return (StringFind(objName, m_prefix) != -1 &&
           (StringFind(objName, "tp_") != -1 ||
            StringFind(objName, "sl_") != -1));
  }

//+------------------------------------------------------------------+
//| Extracts symbol name from object name                            |
//+------------------------------------------------------------------+
string CPanel::ExtractSymbolFromObject(string objName)
  {
//--- The symbol is the part after the last underscore prefix (tp_, sl_, buy_, sell_, close_)
   int pos = StringFind(objName, "tp_");
   if(pos != -1)
      return(StringSubstr(objName, pos + 3));
   pos = StringFind(objName, "sl_");
   if(pos != -1)
      return(StringSubstr(objName, pos + 3));
   pos = StringFind(objName, "buy_");
   if(pos != -1)
      return(StringSubstr(objName, pos + 4));
   pos = StringFind(objName, "sell_");
   if(pos != -1)
      return(StringSubstr(objName, pos + 5));
   pos = StringFind(objName, "close_");
   if(pos != -1)
      return(StringSubstr(objName, pos + 6));
   return("");
  }

#endif // __PANEL_MQH__
//+------------------------------------------------------------------+
