//+------------------------------------------------------------------+
//|                                                InteractiveMA.mq5 |
//|                                                    Forex FanClub |
//|                                           https://fxfan.club/en/ |
//+------------------------------------------------------------------+
//
// Terms of use
// You may use and modify this file for your own trading, free of charge.
// If you share this file or a modified version, keep the author name
// "Forex FanClub" and the link https://fxfan.club/en/ in the file.
// You may not sell this file or a modified version.
// This file is provided "as is", without any warranty.
// Forex FanClub is not responsible for any trading loss or other damage
// from using this file.
//+------------------------------------------------------------------+
//
// InteractiveMA draws one moving average that you change on the chart,
// without opening the settings window.
// - Click the type button (or press W) to switch SMA, EMA, SMMA, LWMA.
// - Click "-" and "+" (or press A and D) to change the period by one.
// - Drag the dark title bar to move the panel.
// - The type, the period and the panel position stay on the chart when
//   you change the timeframe or restart the terminal. Change "Period" or
//   "Moving average type" in the settings to start again from them.
// - Use one InteractiveMA per chart.
//
#property copyright   "Forex FanClub"
#property link        "https://fxfan.club/en/"
#property version     "1.10"
#property description "A moving average you change on the chart: click the panel or press W, A and D."
#ifdef __MQL4__
#property strict
#endif
#property indicator_chart_window
#property indicator_buffers 1
#ifdef __MQL5__
#property indicator_plots   1
#property indicator_type1   DRAW_LINE
#property indicator_label1  "InteractiveMA"
#endif
#property indicator_color1  clrDodgerBlue
#property indicator_width1  2

//--- settings
enum line_style_type
  {
   solid      = 0,                  // Solid
   dash       = 1,                  // Dash
   dot        = 2,                  // Dot
   dashdot    = 3,                  // Dash dot
   dashdotdot = 4                   // Dash dot dot
  };
enum ma_method_type
  {
   mode_sma  = 0,                   // Simple (SMA)
   mode_ema  = 1,                   // Exponential (EMA)
   mode_smma = 2,                   // Smoothed (SMMA)
   mode_lwma = 3                    // Linear weighted (LWMA)
  };
enum corner_type
  {
   corner_left_upper  = 0,          // Top left
   corner_right_upper = 1,          // Top right
   corner_left_lower  = 2,          // Bottom left
   corner_right_lower = 3           // Bottom right
  };

#ifdef __MQL5__
input group "Moving average"
input ma_method_type  InpMAMethod      = mode_sma;           // Moving average type (W key changes it)
input int             InpPeriod        = 14;                 // Period (A and D keys change it)
input int             InpMaxLength     = 200;                // Largest period
input group "Line"
input line_style_type InpLineStyle     = solid;              // Line style
input color           InpLineColor     = clrDodgerBlue;      // Line color
input int             InpLineWidth     = 2;                  // Line width
input group "Panel"
input corner_type     InpPanelPosition = corner_right_lower; // Panel corner
input int             InpFontSize      = 11;                 // Panel font size
#else
//--- MT4: the variable names of version 1.00 are kept, so saved templates keep their values
input string          ma_settings      = "=== Moving average ===";
input ma_method_type  ma_method        = mode_sma;           // Moving average type (W key changes it)
input int             ma_period        = 14;                 // Period (A and D keys change it)
input int             maxLen           = 200;                // Largest period
input string          line_settings    = "=== Line ===";
input line_style_type line_style       = solid;              // Line style
input color           line_color       = clrDodgerBlue;      // Line color
input int             line_width       = 2;                  // Line width
input string          ui_settings      = "=== Panel ===";
input corner_type     panel_position   = corner_right_lower; // Panel corner
input int             UI_FontSize      = 11;                 // Panel font size
#define InpMAMethod      ma_method
#define InpPeriod        ma_period
#define InpMaxLength     maxLen
#define InpLineStyle     line_style
#define InpLineColor     line_color
#define InpLineWidth     line_width
#define InpPanelPosition panel_position
#define InpFontSize      UI_FontSize
#endif

//--- constants
#define MIN_LENGTH  2
#define KEY_A       65
#define KEY_D       68
#define KEY_W       87
#define PANEL_W     168
#define PANEL_H     106

//--- panel objects (one InteractiveMA per chart)
#define OBJ_BACK    "IMA_Back"
#define OBJ_TITLE   "IMA_Title"
#define OBJ_METHOD  "IMA_Method"
#define OBJ_MINUS   "IMA_Minus"
#define OBJ_PERIOD  "IMA_Period"
#define OBJ_PLUS    "IMA_Plus"

//--- panel colors: the panel has its own background, so it reads on dark and light charts
#define CLR_TITLE_BG   C'29,17,12'
#define CLR_TITLE_TEXT clrWhite
#define CLR_BUTTON_BG  C'248,248,248'
#define CLR_BUTTON_TXT C'50,50,50'
#define CLR_BUTTON_BRD C'169,169,169'

double         g_ma[];
ma_method_type g_method;
int            g_period;
int            g_max_period;
int            g_rates      = 0;          // bars calculated in the last OnCalculate
bool           g_ready      = false;      // the panel and the saved state are read
int            g_margin_x   = 6;          // panel distance from the chart corner
int            g_margin_y   = 6;
bool           g_mouse_down = false;
bool           g_dragging   = false;
bool           g_scroll_was = true;
int            g_drag_dx    = 0;
int            g_drag_dy    = 0;

//+------------------------------------------------------------------+
//| Initialization                                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   g_max_period = MathMax(InpMaxLength, MIN_LENGTH);
   g_method     = InpMAMethod;
   g_period     = ClampPeriod(InpPeriod);

   SetIndexBuffer(0, g_ma, INDICATOR_DATA);
   ArraySetAsSeries(g_ma, false);
#ifdef __MQL5__
   PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_LINE);
   PlotIndexSetInteger(0, PLOT_LINE_STYLE, LineStyle());
   PlotIndexSetInteger(0, PLOT_LINE_WIDTH, InpLineWidth);
   PlotIndexSetInteger(0, PLOT_LINE_COLOR, InpLineColor);
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);
#else
   SetIndexStyle(0, DRAW_LINE, LineStyle(), InpLineWidth, InpLineColor);
   SetIndexEmptyValue(0, EMPTY_VALUE);
#endif
   IndicatorSetString(INDICATOR_SHORTNAME, "InteractiveMA");
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
   UpdateLabel();

   //--- the panel and the saved type and period are read one second later:
   //--- after a restart the chart's objects are not there yet during OnInit
   EventSetTimer(1);
   Print("InteractiveMA: click the panel, or press W for the next moving average type and A or D to change the period.");
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Deinitialization                                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   EventKillTimer();
   if(g_dragging)
      ChartSetInteger(0, CHART_MOUSE_SCROLL, g_scroll_was);
   //--- the panel holds the type, the period and its position. Remove it only when InteractiveMA itself is removed.
   if(reason == REASON_REMOVE || reason == REASON_PROGRAM || reason == REASON_INITFAILED)
     {
      DeletePanel();
      ChartRedraw();
     }
  }

//+------------------------------------------------------------------+
//| Calculation                                                      |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   ArraySetAsSeries(close, false);
   //--- the last bar is calculated again on every tick, so each bar ends with its final close
   int start = (prev_calculated > 0 && g_rates == prev_calculated) ? prev_calculated - 1 : 0;
   Calculate(close, rates_total, start);
   g_rates = rates_total;
   return(rates_total);
  }

//+------------------------------------------------------------------+
//| Timer: set up once, then keep the panel complete                 |
//+------------------------------------------------------------------+
void OnTimer()
  {
   if(!g_ready)
     {
      SetUp();
      return;
     }
   if(ObjectFind(0, OBJ_BACK) < 0 || ObjectFind(0, OBJ_TITLE) < 0 || ObjectFind(0, OBJ_METHOD) < 0 ||
      ObjectFind(0, OBJ_MINUS) < 0 || ObjectFind(0, OBJ_PERIOD) < 0 || ObjectFind(0, OBJ_PLUS) < 0)
     {
      CreatePanel();
      ChartRedraw();
     }
  }

//+------------------------------------------------------------------+
//| Chart events: keys, panel buttons, panel drag                    |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
  {
   if(!g_ready)
      return;

   if(id == CHARTEVENT_KEYDOWN)
     {
      if(lparam == KEY_W)      NextMethod();
      else if(lparam == KEY_D) ChangePeriod(1);
      else if(lparam == KEY_A) ChangePeriod(-1);
     }
   else if(id == CHARTEVENT_OBJECT_CLICK)
     {
      if(sparam == OBJ_METHOD)     NextMethod();
      else if(sparam == OBJ_PLUS)  ChangePeriod(1);
      else if(sparam == OBJ_MINUS) ChangePeriod(-1);
      if(sparam == OBJ_TITLE || sparam == OBJ_METHOD || sparam == OBJ_MINUS || sparam == OBJ_PERIOD || sparam == OBJ_PLUS)
        {
         ObjectSetInteger(0, sparam, OBJPROP_STATE, false);   // buttons, not switches
         ChartRedraw();
        }
     }
   else if(id == CHARTEVENT_MOUSE_MOVE)
     {
      DragPanel((int)lparam, (int)dparam, (((int)StringToInteger(sparam)) & 1) == 1);
     }
   else if(id == CHARTEVENT_CHART_CHANGE)
     {
      if(KeepPanelInside())                                  // the chart became smaller: bring the panel back inside
        {
         PlacePanel();
         ChartRedraw();
        }
     }
  }

//+------------------------------------------------------------------+
//| Read the saved state and build the panel                         |
//+------------------------------------------------------------------+
void SetUp()
  {
   //--- panel objects of version 1.00 that are no longer used
   ObjectDelete(0, "panelma_MAbTn");
   ObjectDelete(0, "Sma_MAbTn");
   ObjectDelete(0, "Smadata_MAbTn");
   ObjectDelete(0, "body_ReCt");

   //--- type and period: keep what was chosen on the chart unless the settings were changed since then
   if(ObjectFind(0, OBJ_BACK) >= 0)
     {
      int m, p, im, ip;
      if(ReadState(ObjectGetString(0, OBJ_BACK, OBJPROP_TEXT), m, p, im, ip) &&
         im == (int)InpMAMethod && ip == InpPeriod && m >= 0 && m <= 3)
        {
         g_method = (ma_method_type)m;
         g_period = ClampPeriod(p);
        }
     }
   if(ObjectFind(0, OBJ_TITLE) >= 0)
      ReadPanelPosition();
   CreatePanel();
   UpdateLabel();
   Recalculate();

   g_ready = true;
   ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);       // for dragging the panel by its title bar
   ChartRedraw();
  }

//--- the state is kept in the description of the panel background, which the chart saves
string StateText()
  {
   return(StringFormat("InteractiveMA m=%d p=%d im=%d ip=%d", (int)g_method, g_period, (int)InpMAMethod, InpPeriod));
  }

bool ReadState(string s, int &m, int &p, int &im, int &ip)
  {
   string parts[];
   if(StringSplit(s, ' ', parts) != 5 || parts[0] != "InteractiveMA")
      return(false);
   m  = (int)StringToInteger(StringSubstr(parts[1], 2));
   p  = (int)StringToInteger(StringSubstr(parts[2], 2));
   im = (int)StringToInteger(StringSubstr(parts[3], 3));
   ip = (int)StringToInteger(StringSubstr(parts[4], 3));
   return(StringSubstr(parts[1], 0, 2) == "m=" && StringSubstr(parts[2], 0, 2) == "p=" && p > 0);
  }

int ClampPeriod(int p)
  {
   if(p < MIN_LENGTH) return(MIN_LENGTH);
   if(p > g_max_period) return(g_max_period);
   return(p);
  }

//+------------------------------------------------------------------+
//| Moving average                                                   |
//+------------------------------------------------------------------+
void Calculate(const double &close[], int total, int start)
  {
   int p = g_period;
   if(total < p)
     {
      for(int i = 0; i < total; i++) g_ma[i] = EMPTY_VALUE;
      return;
     }
   if(start < p)
     {
      start = p;
      for(int i = 0; i < p - 1; i++) g_ma[i] = EMPTY_VALUE;
      double sum = 0.0;
      for(int j = 0; j < p; j++) sum += close[j];
      g_ma[p - 1] = (g_method == mode_lwma) ? Lwma(close, p - 1, p) : sum / p;   // SMA seeds EMA and SMMA
     }
   double k = 2.0 / (p + 1.0);
   for(int i = start; i < total; i++)
     {
      switch(g_method)
        {
         case mode_ema:  g_ma[i] = close[i] * k + g_ma[i - 1] * (1.0 - k);   break;
         case mode_smma: g_ma[i] = (g_ma[i - 1] * (p - 1) + close[i]) / p;   break;
         case mode_lwma: g_ma[i] = Lwma(close, i, p);                        break;
         default:
           {
            double sum = 0.0;
            for(int j = 0; j < p; j++) sum += close[i - j];
            g_ma[i] = sum / p;
           }
        }
     }
#ifdef __MQL5__
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, p - 1);
#else
   SetIndexDrawBegin(0, p - 1);
#endif
  }

double Lwma(const double &close[], int i, int p)
  {
   double sum = 0.0;
   for(int j = 0; j < p; j++) sum += close[i - j] * (p - j);
   return(sum / (p * (p + 1) / 2.0));
  }

//--- full calculation outside OnCalculate, so a click or a key changes the line at once (also when no tick comes)
void Recalculate()
  {
   int total = ArraySize(g_ma);
   if(g_rates > 0 && g_rates < total) total = g_rates;
   if(total <= 0) return;
   double c[];
   ArraySetAsSeries(c, false);
   if(CopyClose(_Symbol, _Period, 0, total, c) != total)
     {
      g_rates = 0;                                           // not ready: OnCalculate calculates everything on the next tick
      return;
     }
   Calculate(c, total, 0);
   ChartRedraw();
  }

void NextMethod()
  {
   g_method = (ma_method_type)(((int)g_method + 1) % 4);
   Changed();
  }

void ChangePeriod(int step)
  {
   int p = ClampPeriod(g_period + step);
   if(p == g_period) return;
   g_period = p;
   Changed();
  }

void Changed()
  {
   UpdatePanel();
   UpdateLabel();
   Recalculate();
  }

string MethodName()
  {
   switch(g_method)
     {
      case mode_ema:  return("EMA");
      case mode_smma: return("SMMA");
      case mode_lwma: return("LWMA");
     }
   return("SMA");
  }

//--- name in the Data Window and the tooltip of the line, for example "InteractiveMA EMA(20)"
void UpdateLabel()
  {
   string label = "InteractiveMA " + MethodName() + "(" + (string)g_period + ")";
#ifdef __MQL5__
   PlotIndexSetString(0, PLOT_LABEL, label);
#else
   SetIndexLabel(0, label);
#endif
  }

ENUM_LINE_STYLE LineStyle()
  {
   switch(InpLineStyle)
     {
      case dash:       return(STYLE_DASH);
      case dot:        return(STYLE_DOT);
      case dashdot:    return(STYLE_DASHDOT);
      case dashdotdot: return(STYLE_DASHDOTDOT);
     }
   return(STYLE_SOLID);
  }

//+------------------------------------------------------------------+
//| Panel                                                            |
//+------------------------------------------------------------------+
ENUM_BASE_CORNER PanelCorner()
  {
   switch(InpPanelPosition)
     {
      case corner_left_upper:  return(CORNER_LEFT_UPPER);
      case corner_right_upper: return(CORNER_RIGHT_UPPER);
      case corner_left_lower:  return(CORNER_LEFT_LOWER);
     }
   return(CORNER_RIGHT_LOWER);
  }

bool CornerIsRight() { return(InpPanelPosition == corner_right_upper || InpPanelPosition == corner_right_lower); }
bool CornerIsLower() { return(InpPanelPosition == corner_left_lower || InpPanelPosition == corner_right_lower); }

//--- distance of an object (ox, oy from the panel's top-left corner) from the chart corner.
//--- MT4 and MT5 measure the distance to the object's left and top edge for every corner,
//--- so for the right and bottom corners the object extends toward the chart corner.
int ObjX(int ox) { return(g_margin_x + (CornerIsRight() ? PANEL_W - ox : ox)); }
int ObjY(int oy) { return(g_margin_y + (CornerIsLower() ? PANEL_H - oy : oy)); }

void Place(string name, int ox, int oy, int w, int h)
  {
   ObjectSetInteger(0, name, OBJPROP_CORNER, PanelCorner());
   ObjectSetInteger(0, name, OBJPROP_XDISTANCE, ObjX(ox));
   ObjectSetInteger(0, name, OBJPROP_YDISTANCE, ObjY(oy));
   ObjectSetInteger(0, name, OBJPROP_XSIZE, w);
   ObjectSetInteger(0, name, OBJPROP_YSIZE, h);
  }

void Button(string name, string text, int ox, int oy, int w, int h, color bg, color fg, color border, string tip)
  {
   if(ObjectFind(0, name) < 0)
      ObjectCreate(0, name, OBJ_BUTTON, 0, 0, 0);
   Place(name, ox, oy, w, h);
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetString(0, name, OBJPROP_FONT, "Segoe UI");
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, InpFontSize);
   ObjectSetInteger(0, name, OBJPROP_COLOR, fg);
   ObjectSetInteger(0, name, OBJPROP_BGCOLOR, bg);
   ObjectSetInteger(0, name, OBJPROP_BORDER_COLOR, border);
   ObjectSetInteger(0, name, OBJPROP_STATE, false);
   ObjectSetInteger(0, name, OBJPROP_BACK, false);
   ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, name, OBJPROP_SELECTED, false);
   ObjectSetInteger(0, name, OBJPROP_HIDDEN, true);
   ObjectSetInteger(0, name, OBJPROP_ZORDER, 10);
   ObjectSetInteger(0, name, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);
   ObjectSetString(0, name, OBJPROP_TOOLTIP, tip);
  }

void CreatePanel()
  {
   if(ObjectFind(0, OBJ_BACK) < 0)
      ObjectCreate(0, OBJ_BACK, OBJ_RECTANGLE_LABEL, 0, 0, 0);
   Place(OBJ_BACK, 0, 0, PANEL_W, PANEL_H);
   ObjectSetInteger(0, OBJ_BACK, OBJPROP_BGCOLOR, clrWhite);
   ObjectSetInteger(0, OBJ_BACK, OBJPROP_BORDER_TYPE, BORDER_FLAT);
   ObjectSetInteger(0, OBJ_BACK, OBJPROP_COLOR, C'160,160,160');
   ObjectSetInteger(0, OBJ_BACK, OBJPROP_BACK, false);
   ObjectSetInteger(0, OBJ_BACK, OBJPROP_SELECTABLE, false);
   ObjectSetInteger(0, OBJ_BACK, OBJPROP_SELECTED, false);
   ObjectSetInteger(0, OBJ_BACK, OBJPROP_HIDDEN, true);
   ObjectSetInteger(0, OBJ_BACK, OBJPROP_ZORDER, 5);
   ObjectSetInteger(0, OBJ_BACK, OBJPROP_TIMEFRAMES, OBJ_ALL_PERIODS);

   Button(OBJ_TITLE,  "InteractiveMA", 4, 4,   160, 28, CLR_TITLE_BG,  CLR_TITLE_TEXT, CLR_TITLE_BG,   "Drag to move the panel");
   Button(OBJ_METHOD, "",              4, 38,  160, 30, CLR_BUTTON_BG, CLR_BUTTON_TXT, CLR_BUTTON_BRD, "Next type: click or press W (SMA, EMA, SMMA, LWMA)");
   Button(OBJ_MINUS,  "-",             4, 72,  36,  30, CLR_BUTTON_BG, CLR_BUTTON_TXT, CLR_BUTTON_BRD, "Period -1: click or press A");
   Button(OBJ_PERIOD, "",              44, 72, 80,  30, clrWhite,      CLR_BUTTON_TXT, clrWhite,       "Moving average period");
   Button(OBJ_PLUS,   "+",             128, 72, 36, 30, CLR_BUTTON_BG, CLR_BUTTON_TXT, CLR_BUTTON_BRD, "Period +1: click or press D");
   UpdatePanel();
  }

void UpdatePanel()
  {
   ObjectSetString(0, OBJ_METHOD, OBJPROP_TEXT, MethodName());
   ObjectSetString(0, OBJ_PERIOD, OBJPROP_TEXT, "MA " + (string)g_period);
   ObjectSetString(0, OBJ_BACK, OBJPROP_TEXT, StateText());
   ChartRedraw();
  }

void PlacePanel()
  {
   Place(OBJ_BACK, 0, 0, PANEL_W, PANEL_H);
   Place(OBJ_TITLE, 4, 4, 160, 28);
   Place(OBJ_METHOD, 4, 38, 160, 30);
   Place(OBJ_MINUS, 4, 72, 36, 30);
   Place(OBJ_PERIOD, 44, 72, 80, 30);
   Place(OBJ_PLUS, 128, 72, 36, 30);
  }

void DeletePanel()
  {
   ObjectDelete(0, OBJ_BACK);
   ObjectDelete(0, OBJ_TITLE);
   ObjectDelete(0, OBJ_METHOD);
   ObjectDelete(0, OBJ_MINUS);
   ObjectDelete(0, OBJ_PERIOD);
   ObjectDelete(0, OBJ_PLUS);
  }

//--- top-left corner of the panel in chart pixels
int PanelLeft() { return(CornerIsRight() ? (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS) - g_margin_x - PANEL_W : g_margin_x); }
int PanelTop()  { return(CornerIsLower() ? (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS) - g_margin_y - PANEL_H : g_margin_y); }

//--- drag the panel: press the left button on the title bar and move the mouse
void DragPanel(int x, int y, bool left_down)
  {
   if(left_down && !g_mouse_down)
     {
      int left = PanelLeft() + 4, top = PanelTop() + 4;
      if(x >= left && x <= left + 160 && y >= top && y <= top + 28)
        {
         g_dragging = true;
         g_drag_dx  = x - PanelLeft();
         g_drag_dy  = y - PanelTop();
         //--- keep the chart still while the panel moves
         g_scroll_was = (bool)ChartGetInteger(0, CHART_MOUSE_SCROLL);
         ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
        }
     }
   if(g_dragging && left_down)
     {
      int cw = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
      int ch = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
      int nl = x - g_drag_dx, nt = y - g_drag_dy;
      if(nl > cw - PANEL_W) nl = cw - PANEL_W;
      if(nt > ch - PANEL_H) nt = ch - PANEL_H;
      if(nl < 0) nl = 0;
      if(nt < 0) nt = 0;
      g_margin_x = CornerIsRight() ? cw - PANEL_W - nl : nl;
      g_margin_y = CornerIsLower() ? ch - PANEL_H - nt : nt;
      PlacePanel();
      ChartRedraw();
     }
   if(!left_down && g_dragging)
     {
      g_dragging = false;
      ChartSetInteger(0, CHART_MOUSE_SCROLL, g_scroll_was);
     }
   g_mouse_down = left_down;
  }

//--- read the panel's distance from the chart corner back from the title bar (from a saved chart)
void ReadPanelPosition()
  {
   if((ENUM_BASE_CORNER)ObjectGetInteger(0, OBJ_TITLE, OBJPROP_CORNER) != PanelCorner())
      return;                                                // panel from another corner setting: use the default
   int xd = (int)ObjectGetInteger(0, OBJ_TITLE, OBJPROP_XDISTANCE);
   int yd = (int)ObjectGetInteger(0, OBJ_TITLE, OBJPROP_YDISTANCE);
   int mx = xd - (CornerIsRight() ? PANEL_W - 4 : 4);
   int my = yd - (CornerIsLower() ? PANEL_H - 4 : 4);
   g_margin_x = mx;
   g_margin_y = my;
   KeepPanelInside();
  }

//--- keep the whole panel inside the chart. Returns true when the position was changed.
bool KeepPanelInside()
  {
   int cw = (int)ChartGetInteger(0, CHART_WIDTH_IN_PIXELS);
   int ch = (int)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS);
   int mx = g_margin_x, my = g_margin_y;
   if(cw > PANEL_W && mx > cw - PANEL_W) mx = cw - PANEL_W;
   if(ch > PANEL_H && my > ch - PANEL_H) my = ch - PANEL_H;
   if(mx < 0) mx = 0;
   if(my < 0) my = 0;
   bool moved = (mx != g_margin_x || my != g_margin_y);
   g_margin_x = mx;
   g_margin_y = my;
   return(moved);
  }
//+------------------------------------------------------------------+
