//+------------------------------------------------------------------+
//|                                                   HotterKeys     |
//|                                                   by Uderfrykte  |
//|                                                                  |
//+------------------------------------------------------------------+
#property indicator_chart_window
#property script_show_inputs
//#property strict

#define KEY_DRAW_RECTANGLE          68       // D
#define KEY_PLACE_TRENDLINE         65       // A
#define KEY_PLACE_LINE              83       // S
#define KEY_MAKE_BLUE               81       // Q
#define KEY_MAKE_RED                87       // W
#define KEY_MAKE_BLACK              69       // E
#define KEY_EXTEND_OBJ              70       // F
#define KEY_REMOVE_OBJ              82       // R
#define KEY_TIMEFRAME_UP            90       // Z
#define KEY_TIMEFRAME_DOWN          88       // X
#define KEY_PLACE_ALERT             86       // V
#define KEY_PLACE_TEXT              66       // B
#define KEY_SCREENSHOT              84       // T
#define KEY_OVERVIEW                9        // TAB
#define KEY_DESELECT                32       // SPACE
#define KEY_CHANGE_STYLE            226      //  "\"
#define KEY_LOAD_M1_DATA            76       // L
#define KEY_RESTORE_SCALING         67       // C





string  Symbols =      "AUDCAD;CADCHF;EURAUD;GBPAUD;NZDCAD;USDCAD;" +
                       "AUDCHF;CADJPY;EURCAD;GBPCAD;NZDCHF;USDCHF;"  +
                       "AUDJPY;CHFJPY;EURCHF;GBPCHF;NZDJPY;USDJPY;" +
                       "AUDNZD;x;EURGBP;GBPJPY;NZDUSD; USDCZK ;" +
                       "AUDUSD;x; EURJPY;GBPNZD;x; XAUUSD;" +
                       "x;x;EURNZD;GBPUSD;x; XAUEUR;" +
                       "x;x;EURUSD;x;x;XTIUSD;" +
                       "x;x;x;x;x;x;"  ;


input bool FreeChart = false;
extern bool saveObjects = True;
int     ButtonsInARow = 6;      // Buttons in a horizontal row
extern int     Corner        = 1;      // Corner
extern int     XShift        = 1200;    // Horizontal shift
extern int     YShift        = 20;     // Vertical shift
extern int     XSize         = 100;     // Width of buttons
extern int     YSize         = 32;     // Height of buttons
extern int     FSize         = 8;     // Font size
extern color   ButtonColor=clrWheat;        // Button color
extern color   OpenPositionColor=clrAquamarine;
extern color   PlacedOrderColor= clrBlueViolet;
extern color   PriceIsCloseColor= clrRed;
extern color   AlertSetColor = clrGainsboro;
extern bool    spaceDeselect = false;

const double DistanceInPoints = 500;
const string  FontType      = "Consolas" ; // Font
const bool ShowLine = true;
const bool zoomRememberPreciseLocation = true;

const string scrFolder = "MyScreenshot/";
const string objPrefix   = "SuperObj_";
const string  buttonPrefix ="changer";
const string ExtendPrefix   = "_EXT";
const string AlertPrefix   = "_ALERT";
const string  Soundfile   = "alert.wav";
const string tfChangeNObjName = "_changeTimeFrameObj55";
const string chartInfoObjName = "_chartInfoObjName55";
const int    ExtendRightOffset = 20;   // in candles
const int timer_tick = 25;



// current state
bool rectDrawingMode = false;
bool drawingRectNow = false;
bool leftClickHolding = false;
bool shiftPressed = false;
bool shootingDown = false;
bool overview_visible = false;
bool alertFileBlocked = false;

// dynamic vars
bool checkAlertsNextTick = false;
int last_cursor_x = 0;
int last_cursor_y = 0;
datetime cursorCandleDate = 0;
string aSymbols[];
int phaseInt = 0;
int curTFnum = 0;
datetime lastTickDate;
string tmpObjName = "";




struct SuperAlert
  {
   string            pair;
   string            currChartName;
   double            price;
   bool              underPrice;
   bool              active;
   string            descript;
  };

SuperAlert alertList[];



//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int OnInit()
  {
   ChartSetInteger(0, CHART_EVENT_MOUSE_MOVE, true);
   ChartSetInteger(0, CHART_SHIFT, true);
   ChartSetInteger(0, CHART_AUTOSCROLL, false);
   ChartSetInteger(0, CHART_SHOW_PRICE_SCALE, true);
   ChartSetInteger(0, CHART_QUICK_NAVIGATION, false);  //
   ChartSetInteger(0, CHART_FOREGROUND, false);
   ChartSetInteger(0, CHART_SHOW_DATE_SCALE, true);
   ChartSetDouble(0, CHART_SHIFT_SIZE, 50);
   ChartSetInteger(0,CHART_SCALEFIX, FreeChart);
   ChartSetInteger(0, CHART_MOUSE_SCROLL, true);
   ChartSetInteger(0, CHART_SHOW_GRID, false);
   ChartSetInteger(0, CHART_SHOW_ASK_LINE, false);
   ChartSetInteger(0, CHART_SHOW_BID_LINE, false);
   ChartSetInteger(0, CHART_EVENT_OBJECT_DELETE, true);
   ChartSetInteger(0, CHART_SHOW_OBJECT_DESCR, true);


   LoadObjectsFromFile(Symbol() + "_saved_objs.txt");
   loadVariablesFromObj();


// adjusting chart position after changing TF
   for(int i = 0; i < 5; ++i) // trying 5 times
     {
      //Print("Anchor: " + ObjectGet(tfChangeNObjName, OBJPROP_TIME1));
      if(ObjectFind(0, tfChangeNObjName) > -1)
        {
         int cursorCandle = candleNumAtMouse();
         int zoomCandleNum = iBarShift(Symbol(), Period(), ObjectGet(tfChangeNObjName, OBJPROP_TIME1), false);
         if(zoomCandleNum < 1)
            zoomCandleNum = 1;

         chartShiftLeft(zoomCandleNum - cursorCandle);
         WindowRedraw();
         Sleep(250);
        }
     }


   if(FreeChart)
      fixChartMinMax();

   extendRects();


// create spread object if there isnt one
   if(ObjectFind(0, "Spread") == -1)
     {
      ObjectCreate("Spread", OBJ_LABEL, 0, 0, 0);
      ObjectSet("Spread", OBJPROP_CORNER, 0);
      ObjectSet("Spread", OBJPROP_XDISTANCE, 15);
      ObjectSet("Spread", OBJPROP_YDISTANCE, 15);
      ObjectSetInteger(0, "Spread", OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS);
     }


   ObjectSetText("Spread", IntegerToString(MarketInfo(Symbol(), MODE_SPREAD)), 96, "Arial", clrRed);
   ObjectSetInteger(0, "Spread", OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS);

   prepareButtons();

   LoadAlertsFromFile();


   setSymbolButtonColor();
   setButtonsVisibility(false);

   EventSetMillisecondTimer(timer_tick);

   return(INIT_SUCCEEDED);
  }



//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnTimer()
  {

   phaseInt++;

   if(phaseInt % 8 == 0)
     {
      extendRects();

      if(!alertFileBlocked)  // checks alerts
        {
         if(DayOfWeek() != 0 && DayOfWeek() != 6)  // ignore on weekends
            checkAlerts();
        }

      if(ObjectFind(0, tfChangeNObjName) != -1)
        {
         ObjectDelete(tfChangeNObjName);
         EventSetMillisecondTimer(timer_tick);
        }
     }

   if(phaseInt % 900 == 0)   // check for alerts change every 900 * timer_tick [ms]
     {
      phaseInt = 1;
      if(!alertFileBlocked)
         LoadAlertsFromFile();  // reloads alerts every minute in case the list changed externally
     }

   if(overview_visible)
      updateSymbolButtonColor();



//EventKillTimer();
  }




//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   shootingDown = true;
   saveVariablesToObj();


   if(saveObjects)
      SaveObjectsToFile();

   for(int i=ObjectsTotal()-1; i>=0; i--)
      ObjectDelete(ObjectName(i));

//SaveAlertsToFile();
  }




//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
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[])
  {

   ObjectSetText("Spread", IntegerToString(MarketInfo(Symbol(), MODE_SPREAD)), 96, "Arial", clrRed);  // update spread

   if(time[1] - lastTickDate > 3600 && lastTickDate != 0)
      Print("Tick received after a long time: " + close[0] + "   - Spread: " + spread[0]);
   lastTickDate = time[1];


   return(rates_total);
  }





//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   datetime dt_1     = 0;
   double   price_1  = 0;
   datetime dt_2     = 0;
   double   price_2  = 0;
   int      window   = 0;
   int      x        = 0;
   int      y        = 0;
   int i = 0;
   double   tmpPrice  = 0;

////////////////////////////////////////////////////////////////////////

   curTFnum = periodToTFnum(Period());


   if(id == CHARTEVENT_MOUSE_MOVE)
     {
      leftClickHolding = (((uint)sparam)& 1) == 1;
      shiftPressed = ((uint)sparam& 4) == 4;

      if(shiftPressed && FreeChart)
         verticalScale(1.0 + (dparam - last_cursor_y) * 0.001);

      last_cursor_x = lparam;
      last_cursor_y = dparam;

      ChartXYToTimePrice(0, last_cursor_x, 0, window, cursorCandleDate, price_1);
     }



   if(id == CHARTEVENT_OBJECT_DELETE)
     {
      if(StringFind(sparam, AlertPrefix) == 0 && (!shootingDown))
        {
         for(i = 0; i < ArraySize(alertList); i++)
            if(alertList[i].currChartName == sparam)
              {
               alertList[i].active = false;
               //Print("Alert removed: " + alertList[i].currChartName);
              }

         if(!alertFileBlocked)
            SaveAlertsToFile();
         setSymbolButtonColor();
         updateSymbolButtonColor();
        }
     }


   if(id == CHARTEVENT_OBJECT_DRAG)
     {
      if(StringFind(sparam, AlertPrefix) == 0)  // save alert levels after dragging
        {
         for(i = 0; i < ArraySize(alertList); i++)
            if(alertList[i].currChartName == sparam)
              {
               alertList[i].underPrice = ObjectGet(sparam, OBJPROP_PRICE1) < Bid;
               alertList[i].price = ObjectGet(sparam, OBJPROP_PRICE1);
               if(alertList[i].underPrice)
                  ObjectSet(sparam, OBJPROP_COLOR, clrRed);
               else
                  ObjectSet(sparam, OBJPROP_COLOR, clrBlue);

               //Print("Alert price adjusted: " + alertList[i].price);
              }
         SaveAlertsToFile();
        }

     }


   if(id == CHARTEVENT_OBJECT_CHANGE)
     {
      string desc = ObjectDescription(sparam);

      if(StringFind(sparam, AlertPrefix) == 0)   // save alerts when changing description
        {
         for(i = 0; i < ArraySize(alertList); i++)
            if(alertList[i].currChartName == sparam)
               alertList[i].descript = desc;
         SaveAlertsToFile();
        }

     }


   if(rectDrawingMode && leftClickHolding)   // left mouse button pressed
     {
      Comment("");
      rectDrawingMode = false;
      drawingRectNow = true;
      // name(tmpObjName) is set when pressing the hotkey

      ChartXYToTimePrice(0, last_cursor_x, last_cursor_y, window, dt_2, price_2);

      if(ObjectFind(0, tmpObjName) == -1)
        {
         ChartXYToTimePrice(0, last_cursor_x, last_cursor_y, window, dt_1, price_1);
         RectangleCreate(0,tmpObjName,0,dt_1,price_1,dt_2,price_2,clrBlue,STYLE_SOLID,2,false,false,false,true,false,0);
        }

      ChartSetInteger(0, CHART_MOUSE_SCROLL, false);
     }



   if(drawingRectNow)   // following mouse to draw rectangle
     {
      ChartXYToTimePrice(0, MathMin(last_cursor_x, ChartGetInteger(0, CHART_WIDTH_IN_PIXELS,0)), MathMin(last_cursor_y, ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS,0)), window, dt_2, price_2);


      ObjectSet(tmpObjName, OBJPROP_PRICE2, price_2);
      ObjectSet(tmpObjName, OBJPROP_TIME2, dt_2);

      if(!leftClickHolding)    // mouse button release - stop drawing
        {
         drawingRectNow = false;
         ChartSetInteger(0, CHART_MOUSE_SCROLL, true);  // allow chart to move again
         ObjectSet(tmpObjName, OBJPROP_SELECTED, true);  //select the object
         tmpObjName = "";
        }
     }


   if(id==CHARTEVENT_OBJECT_CLICK && ObjectGet(sparam,OBJPROP_TYPE)==OBJ_BUTTON)
     {
      if(StringFind(sparam,buttonPrefix + ":symbol:",0)==0)    // pair button clicked
        {
         shootingDown = true;
         ObjectSet(sparam,OBJPROP_STATE,false);
         string pair_str = sparam;
         StringReplace(pair_str, buttonPrefix + ":symbol:", "");  // get the pair name

         if(StringCompare(Symbol(), pair_str) != 0)    // ignore if it's the current pair
            ChartSetSymbolPeriod(0, pair_str, Period());


         setButtonsVisibility(false);
         fixChartMinMax();
         ChartRedraw(0);
        }
     }



   if(id==CHARTEVENT_KEYDOWN)   // hotkeys code here
     {
      int objTotals = ObjectsTotal();
      string name = "";
      rectDrawingMode = false;
      Comment("");

      if(lparam == KEY_SCREENSHOT)
        {
         string scrFileName = scrFolder + Symbol() + "_" + DateTimeReformat(TimeToStr(TimeLocal(), TIME_DATE|TIME_MINUTES|TIME_SECONDS));
         if(!WindowScreenShot(scrFileName + ".png", 1920, 1080, ChartGetInteger(0,CHART_FIRST_VISIBLE_BAR,0), -1, -1))
            Print(GetLastError());
         //Comment("");
        }

      if(lparam == KEY_CHANGE_STYLE)
        {
         for(i = 0; i < objTotals; i++)
           {
            name = ObjectName(i);
            if(ObjectGet(name, OBJPROP_SELECTED) > 0)
              {
               ObjectSetInteger(0,name, OBJPROP_STYLE, (ObjectGetInteger(0, name, OBJPROP_STYLE) + 1) % 5);
               if(ObjectGetInteger(0, name, OBJPROP_STYLE) == 0)
                  ObjectSetInteger(0,name, OBJPROP_WIDTH, 2);
               else
                  ObjectSetInteger(0,name, OBJPROP_WIDTH, 0);
              }
           }
        }


      if(lparam == KEY_PLACE_TEXT)
        {
         name = generateName();
         ChartXYToTimePrice(0, last_cursor_x, last_cursor_y, window, dt_1, price_1);
         ChartXYToTimePrice(0, last_cursor_x, last_cursor_y, window, dt_2, price_2);
         TextCreate(0, name, 0, dt_1, price_1, "ENG", "Arial", 10, clrBlue, 0, ANCHOR_CENTER, false, true, false);
        }



      if(lparam == KEY_PLACE_LINE)
        {
         name = generateName();
         ChartXYToTimePrice(0, last_cursor_x, last_cursor_y, window, dt_1, price_1);
         ObjectCreate(name, OBJ_HLINE, 0,Time[0], price_1);
         ObjectSet(name, OBJPROP_STYLE, STYLE_SOLID);
         ObjectSet(name, OBJPROP_COLOR, clrBlack);
         ObjectSet(name, OBJPROP_WIDTH, 2);
         ObjectSet(name, OBJPROP_SELECTED, true);
        }


      if(lparam == KEY_DRAW_RECTANGLE)
        {
         rectDrawingMode = true;
         tmpObjName = generateName();
         Comment("Draw a rectangle... ");
        }


      if(lparam == KEY_PLACE_TRENDLINE)
        {
         name = generateName();
         ChartXYToTimePrice(0, last_cursor_x - 155, last_cursor_y, window, dt_1, price_1);
         ChartXYToTimePrice(0, last_cursor_x + 155, last_cursor_y, window, dt_2, price_2);
         TrendCreate(0,name,0,dt_1,price_1,dt_2,price_2,clrBlue,STYLE_SOLID,2,false,true,false,false,false,0);
        }


      if(lparam == KEY_MAKE_BLUE)      // set blue color
        {

         if(ObjectFind(0, tmpObjName) >= 0)
            ObjectSet(tmpObjName, OBJPROP_COLOR, clrBlue);

         for(i = 0; i <= objTotals - 1; i++)
           {
            name = ObjectName(i);
            if(ObjectGet(name, OBJPROP_SELECTED) > 0)
               ObjectSet(name, OBJPROP_COLOR, clrBlue);
           }
        }


      if(lparam == KEY_MAKE_RED)       // set red color
        {
         if(ObjectFind(0, tmpObjName) >= 0)
            ObjectSet(tmpObjName, OBJPROP_COLOR, clrRed);

         for(i = 0; i < objTotals; i++)
           {
            name = ObjectName(i);
            if(ObjectGet(name, OBJPROP_SELECTED) > 0)
               ObjectSet(name, OBJPROP_COLOR, clrRed);
           }
        }


      if(lparam == KEY_MAKE_BLACK)    // set black color
        {
         if(ObjectFind(0, tmpObjName) >= 0)
            ObjectSet(tmpObjName, OBJPROP_COLOR, clrBlack);


         for(i = 0; i < objTotals; i++)
           {
            name = ObjectName(i);
            if(ObjectGet(name, OBJPROP_SELECTED) > 0)
               ObjectSet(name, OBJPROP_COLOR, clrBlack);
           }
        }


      if(lparam == KEY_EXTEND_OBJ)    // toggle auto extending
        {
         int s_counter = 0;
         string selectedObjs[];

         for(i = 0; i < objTotals; i++)
            if(ObjectGet(ObjectName(i), OBJPROP_SELECTED) > 0)
               s_counter++; // number of selected objs

         ArrayResize(selectedObjs,s_counter) ;

         s_counter = 0;
         for(i = 0; i < objTotals; i++)
            if(ObjectGet(ObjectName(i), OBJPROP_SELECTED) > 0)
              {
               selectedObjs[s_counter] = ObjectName(i);
               s_counter++;
              }



         for(i = 0; i < s_counter; i++)
           {
            name = selectedObjs[i];
            if(StringFind(name, AlertPrefix) < 0)     // not alert line
               if(StringFind(name, ExtendPrefix)== 0)  // already extended
                 {
                  string newName = name;
                  StringReplace(newName, ExtendPrefix, "");  // remove the EXT_ prefix
                  renameObj(name, newName);

                  //ObjectSet(newName,OBJPROP_TIME2,ObjectGet(newName,OBJPROP_TIME3));       // jumping back after canceling extending
                  if(ObjectGet(newName,OBJPROP_TIME2) < ObjectGet(newName,OBJPROP_TIME1))
                     ObjectSet(newName,OBJPROP_TIME2, 0);
                  ObjectSet(newName,OBJPROP_TIME3, 0);
                  ObjectSet(newName, OBJPROP_SELECTED, true);
                 }
               else // not extended
                 {
                  string nName = ExtendPrefix + name;
                  renameObj(name, nName);

                  ObjectSet(nName,OBJPROP_TIME3,ObjectGet(nName,OBJPROP_TIME2));
                  ObjectSet(nName,OBJPROP_TIME2, TimeCurrent() + Period() * ExtendRightOffset * 60);
                  ObjectSet(nName, OBJPROP_SELECTED, true);
                 }
           }
        }


      if(lparam == KEY_REMOVE_OBJ)    // delete selected objects
        {
         for(i = objTotals - 1; i >= 0; i--)
           {
            name = ObjectName(i);
            if(ObjectGet(name, OBJPROP_SELECTED) > 0)
               ObjectDelete(name);
           }
        }


      if(lparam == KEY_PLACE_ALERT)
        {
         ChartXYToTimePrice(0, last_cursor_x, last_cursor_y, window, dt_1, price_1);
         bool UnderCurPrice = price_1 < Bid;

         for(i=0; i < ArraySize(alertList); i++)
            if(alertList[i].active && alertList[i].pair == Symbol() && MathAbs(alertList[i].price - price_1) < 0.00000001)     // check whether similar alert already exists
               return;

         if(UnderCurPrice)
            name = AlertPrefix + "un" +  IntegerToString(MathRand() + 100,0,' ');
         else
            name = AlertPrefix + "ov" + IntegerToString(MathRand() + 100,0,' ');
         ObjectCreate(name, OBJ_HLINE, 0,Time[0], price_1);
         ObjectSet(name, OBJPROP_STYLE, STYLE_DOT);
         ObjectSet(name, OBJPROP_WIDTH, 1);
         ObjectSet(name, OBJPROP_SELECTED, true);
         ObjectSet(name, OBJPROP_SELECTABLE, true);
         ObjectSet(name, OBJPROP_BACK, false);
         desc = periodToText(Period()) + " " + (UnderCurPrice ? "Buy " : "Sell ") + "alert";
         ObjectSetText(name, desc);
         if(UnderCurPrice)
            ObjectSet(name, OBJPROP_COLOR, clrRed);
         else
            ObjectSet(name, OBJPROP_COLOR, clrBlue);

         int lst = ArraySize(alertList);
         ArrayResize(alertList, lst + 1);

         alertList[lst].pair = Symbol();
         alertList[lst].price = price_1;
         alertList[lst].underPrice = UnderCurPrice;
         alertList[lst].active = true;
         alertList[lst].currChartName = name;
         alertList[lst].descript = desc;




         Print("Alert placed");
         SaveAlertsToFile();
         setSymbolButtonColor();
         updateSymbolButtonColor();
        }


      if(lparam == KEY_RESTORE_SCALING)
        {
         if(FreeChart)
            fixChartMinMax();
        }


      if(lparam == KEY_DESELECT)      // unselect all objects
        {
         bool slctd = false;
         objTotals = ObjectsTotal();

         if(spaceDeselect)
            for(i = 0; i < objTotals; i++)
              {
               string nname = ObjectName(i);
               if(ObjectGet(nname, OBJPROP_SELECTED) > 0)
                 {
                  ObjectSet(nname, OBJPROP_SELECTED, false);
                  slctd = true;
                 }
              }

        }



      if(lparam == KEY_OVERVIEW)     // TAB - show pairs
        {
         if(overview_visible)
            ObjectSetInteger(0, "Spread", OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS);
         else
            ObjectSetInteger(0, "Spread", OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);

         updateSymbolButtonColor();
         setButtonsVisibility(!overview_visible);
        }

      if(lparam == KEY_LOAD_M1_DATA)   // loads M1 data for all listed pairs
        {
         Print("Start fetching M1 data");
         //double Highs[];
         //ArraySetAsSeries(Highs,true);

         for(i = 0; i < ArraySize(aSymbols); ++i)
           {
            iHighest(aSymbols[i], PERIOD_M1, MODE_HIGH,20,0);

            /*
            iHigh(aSymbols[i], PERIOD_M15, 1);
            iHigh(aSymbols[i], PERIOD_M5, 1);
            iHigh(aSymbols[i], PERIOD_M1, 1);  // loads latest 1 min data

            double high=0;
            double Highs[];
            ArraySetAsSeries(Highs,true);

            CopyHigh(aSymbols[i], PERIOD_M1,0 , 10, Highs);
            ArrayFree(Highs);

            iHighest(aSymbols[i], PERIOD_M1, MODE_HIGH,2000,0);


            for(int t = 0; t < 50; ++t)
              {
               if(GetLastError() == 0)
                  break;

               iHigh(aSymbols[i], PERIOD_M15, 1);
               iHigh(aSymbols[i], PERIOD_M5, 1);
               iHigh(aSymbols[i], PERIOD_M1, 1);  // loads latest 1 min data

               //CopyHigh(aSymbols[i], PERIOD_M1,0 , 10, Highs);
               //ArrayFree(Highs);

               Print(iHighest(aSymbols[i], PERIOD_M1, MODE_HIGH,20,0) + aSymbols[i] );
              }

            //ArrayFree(Highs); */
           }

         Print("Done fetching M1 data");
        }


      if(lparam == KEY_TIMEFRAME_UP)  // change timeframe
        {
         curTFnum += 1;
         if(curTFnum > 9)
            curTFnum = 9;

         if(!zoomRememberPreciseLocation)
            ChartXYToTimePrice(0, last_cursor_x, 0, window, cursorCandleDate, price_1);

         TrySetTimeframe(curTFnum);
        }


      if(lparam == KEY_TIMEFRAME_DOWN)  // change timeframe
        {
         curTFnum -= 1;
         if(curTFnum < 1)
            curTFnum = 1;

         if(!zoomRememberPreciseLocation)
            ChartXYToTimePrice(0, last_cursor_x, 0, window, cursorCandleDate, price_1);

         TrySetTimeframe(curTFnum);
        }

      switch(lparam)  // change candle width
        {
         case 52:
            setScale(3);
            break;    // 4
         case 51:
            setScale(2);
            break;    // 3
         case 50:
            setScale(1);
            break;    // 2
         case 49:
            setScale(0);
            break;    // 1
        }
     }

   WindowRedraw();
  }


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void checkAlerts()
  {

   int alert_count = ArraySize(alertList);

   for(int i=0; i < alert_count; i++)
     {
      if(!alertList[i].active)
         continue;

      double bidPrice = MarketInfo(alertList[i].pair, MODE_BID);

      if((alertList[i].underPrice && (bidPrice < alertList[i].price)) || (!alertList[i].underPrice && (bidPrice > alertList[i].price)))
        {
         string alert_msg = "Alert triggered: \"" + alertList[i].descript + "\"  " + alertList[i].pair + "@" + DoubleToStr(bidPrice, Digits) + ", Current price: " + bidPrice;

         Alert(alert_msg);
         SendNotification(alert_msg);
         SendMail("Flippety flop PA detected", alert_msg);

         alertList[i].active = false;
         if(alertList[i].pair == Symbol())
            ObjectDelete(0, alertList[i].currChartName); // this will get saved instantly anyway
         else
            SaveAlertsToFile();

         Print(alert_msg);
         //PlaySound(Soundfile);   // sound
        }
     }

  }



//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void extendRects()
  {
   int objTotals = ObjectsTotal();
   for(int i = objTotals - 1; i >= 0; i--)
     {
      string nname = ObjectName(i);

      if(StringFind(nname, AlertPrefix) < 0)
         if(StringFind(nname, ExtendPrefix) == 0)
           {

            ObjectSet(nname,OBJPROP_TIME2, TimeCurrent() + Period() * ExtendRightOffset * 60);     // auto extending
            if(ObjectGet(nname,OBJPROP_TIME1) > ObjectGet(nname,OBJPROP_TIME2))
               ObjectSet(nname,OBJPROP_TIME2,ObjectGet(nname,OBJPROP_TIME1));

           }
     }
  }




//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool candleDataAvailable(int m_tf, datetime m_date)
  {
   if(m_date > TimeCurrent())
      return true;
   if(shiftPressed)
      return true;
   return iBarShift(Symbol(), m_tf, m_date, m_tf < 1440) >= 0;
  }


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int periodToTFnum(int mins)
  {
   switch(mins)
     {
      case PERIOD_M1:
         return 1;

      case PERIOD_M5:
         return 2;

      case PERIOD_M15:
         return 3;

      case PERIOD_M30:
         return 4;

      case PERIOD_H1:
         return 5;

      case PERIOD_H4:
         return 6;

      case PERIOD_D1:
         return 7;

      case PERIOD_W1:
         return 8;

      case PERIOD_MN1:
         return 9;

      default:
         return 0;
     }
  }


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int TFnumToPeriod(int tf)
  {
   switch(tf)
     {
      case 9:
         return PERIOD_MN1;

      case 8:
         return PERIOD_W1;

      case 7:
         return PERIOD_D1;

      case 6:
         return PERIOD_H4;

      case 5:
         return PERIOD_H1;

      case 4:
         return PERIOD_M30;

      case 3:
         return PERIOD_M15;

      case 2:
         return PERIOD_M5;

      case 1:
         return PERIOD_M1;

      default:
         return 0;
     }
  }


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string periodToText(int mins)
  {
   switch(periodToTFnum(mins))
     {
      case 9:
         return "MN1";
      case 8:
         return "W1";
      case 7:
         return "D1";
      case 6:
         return "H4";
      case 5:
         return "H1";
      case 4:
         return "M30";
      case 3:
         return "M15";
      case 2:
         return "M5";
      case 1:
         return "M1";
      default:
         return "";
     }
  }



//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void TrySetTimeframe(int tf)
  {
   int mins = TFnumToPeriod(tf);

   ObjectCreate(0,tfChangeNObjName,OBJ_VLINE,0,cursorCandleDate,0);
   ObjectSet(tfChangeNObjName, OBJPROP_TIME1, cursorCandleDate);
   ObjectSet(tfChangeNObjName, OBJPROP_WIDTH, 4);
   ObjectSet(tfChangeNObjName, OBJPROP_SELECTABLE, false);
   ObjectSet(tfChangeNObjName, OBJPROP_COLOR, clrGreen);
   if(!ShowLine)
      ObjectSet(tfChangeNObjName, OBJPROP_TIMEFRAMES, -1);
   ObjectSet(tfChangeNObjName, OBJPROP_HIDDEN, true);

   EventSetMillisecondTimer(timer_tick);


   if(mins == Period())
      return;

   if(!candleDataAvailable(mins, cursorCandleDate) && mins < Period())
      return;

   ChartSetSymbolPeriod(0,NULL,mins);
  }






//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int candleNumAtMouse()
  {

//int res = iBarShift(Symbol(), Period(), cursorCandleDate, true);
   int res = MathRound(ChartGetInteger(0,CHART_FIRST_VISIBLE_BAR,0) - WindowBarsPerChart() * last_cursor_x / (double)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS));

   if(res < 0)
      res = 0;
   return res;
  }





//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void fixChartMinMax()
  {

   int startingCandle = WindowFirstVisibleBar() - WindowBarsPerChart();
   if(startingCandle < 0)
      startingCandle = 0;

   int m_shift = WindowFirstVisibleBar() - startingCandle;

   double lowest_price = iLow(NULL, 0, iLowest(NULL, 0, MODE_LOW, m_shift, startingCandle));
   double highest_price = iHigh(NULL, 0, iHighest(NULL, 0, MODE_HIGH, m_shift, startingCandle));

//ChartSetInteger(0,CHART_SCALEFIX,true);
   ChartSetDouble(0, CHART_FIXED_MIN, lowest_price - (highest_price-lowest_price) * 0.05);
   ChartSetDouble(0, CHART_FIXED_MAX, highest_price + (highest_price-lowest_price) * 0.05);
  }


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void verticalScale(double direction)
  {
   double lowest_price = ChartGetDouble(0, CHART_FIXED_MIN);
   double highest_price = ChartGetDouble(0, CHART_FIXED_MAX);
   double chart_center_price = (lowest_price + highest_price) * 0.5;

   double radius = highest_price - chart_center_price;
   radius *= direction;

   ChartSetDouble(0, CHART_FIXED_MIN, chart_center_price - radius);
   ChartSetDouble(0, CHART_FIXED_MAX, chart_center_price + radius);
  }


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void verticalShift(double Ypixels)
  {
   double lowest_price = ChartGetDouble(0, CHART_FIXED_MIN);
   double highest_price = ChartGetDouble(0, CHART_FIXED_MAX);

   double pricePerPixel = (highest_price - lowest_price) / (double)ChartGetInteger(0, CHART_HEIGHT_IN_PIXELS, 0);
   ChartSetDouble(0, CHART_FIXED_MIN, lowest_price + Ypixels * pricePerPixel);
   ChartSetDouble(0, CHART_FIXED_MAX, highest_price + Ypixels * pricePerPixel);
  }



//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void horizontalShift(double Xpixels)
  {
   double candleSize = ChartGetInteger(0,CHART_WIDTH_IN_PIXELS) / (double)WindowBarsPerChart();

   int x_shift = Xpixels / candleSize;
   ChartNavigate(0, CHART_CURRENT_POS, -x_shift);
  }




//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void chartShiftLeft(int candles)
  {
   if(candles == 0)
      return;
   ChartRedraw();
   ChartNavigate(0,CHART_END, WindowBarsPerChart() - ChartGetInteger(0,CHART_FIRST_VISIBLE_BAR,0) - ChartGetDouble(0,CHART_SHIFT_SIZE,0) * WindowBarsPerChart() * 0.01 - candles - 2);
  }


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void setScale(int sc)
  {
   double mXp = last_cursor_x / (double)ChartGetInteger(0,CHART_WIDTH_IN_PIXELS);
   int anchorCandleIndex = ChartGetInteger(0,CHART_FIRST_VISIBLE_BAR,0) - MathRound(WindowBarsPerChart() * mXp);

   ChartSetInteger(0, CHART_SCALE, sc);
   int offset = ChartGetInteger(0,CHART_FIRST_VISIBLE_BAR,0) - MathRound(WindowBarsPerChart() * mXp) - anchorCandleIndex;
   ChartNavigate(0,CHART_END, WindowBarsPerChart() - ChartGetInteger(0,CHART_FIRST_VISIBLE_BAR,0) - ChartGetDouble(0,CHART_SHIFT_SIZE,0) * WindowBarsPerChart() * 0.01 - 2 + offset);
  }




//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string DateTimeReformat(string dat_0)
  {
   string dat_8;
   string dat_ret_16 = "";
   dat_0 = " " + dat_0;
   int dat_len_24 = StringLen(dat_0);
   for(int dat_28 = 0; dat_28 < dat_len_24; dat_28++)
     {
      dat_8 = StringSetChar(dat_8, 0, StringGetChar(dat_0, dat_28));
      if(dat_8 != ":" && dat_8 != " " && dat_8 != ".")
         dat_ret_16 = dat_ret_16 + dat_8;
     }
   return (dat_ret_16);
  }
//+------------------------------------------------------------------+






//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string generateName()
  {
   string m_name = objPrefix + IntegerToString(MathRand() + 100,0,' ');
   while(ObjectFind(0, m_name) >= 0)
      m_name = objPrefix + IntegerToString(MathRand() + 100,0,' ');

   return (m_name);
  }




//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void renameObj(string n, string nn)
  {
   int objType = ObjectType(n);

   datetime ot1 = ObjectGet(n, OBJPROP_TIME1);
   datetime ot2 = ObjectGet(n, OBJPROP_TIME2);
   datetime ot3 = ObjectGet(n, OBJPROP_TIME3);

   double op1 = ObjectGet(n, OBJPROP_PRICE1);
   double op2 = ObjectGet(n, OBJPROP_PRICE2);
   double op3 = ObjectGet(n, OBJPROP_PRICE3);

   int style = ObjectGet(n, OBJPROP_STYLE);
   int width = ObjectGet(n, OBJPROP_WIDTH);
   color pc = ObjectGet(n, OBJPROP_COLOR);

   bool fill = ObjectGet(n, OBJPROP_FILL);
   bool back = ObjectGet(n, OBJPROP_BACK);
   bool rayleft = ObjectGet(n, OBJPROP_RAY_LEFT);
   bool rayright = ObjectGet(n, OBJPROP_RAY_RIGHT);

   ObjectDelete(n);

   ObjectCreate(0,nn,objType,0,ot1,op1,ot2,op2);
   ObjectSetInteger(0,nn,OBJPROP_COLOR,pc);
   ObjectSetInteger(0,nn,OBJPROP_STYLE,style);
   ObjectSetInteger(0,nn,OBJPROP_WIDTH,width);
   ObjectSetInteger(0,nn,OBJPROP_FILL,fill);
   ObjectSetInteger(0,nn,OBJPROP_BACK,back);
   ObjectSetInteger(0,nn,OBJPROP_RAY_LEFT,rayleft);
   ObjectSetInteger(0,nn,OBJPROP_RAY_RIGHT,rayright);
   ObjectSet(nn,OBJPROP_TIME3,ot3);
   ObjectSet(nn,OBJPROP_PRICE3,op3);
   return;
  }
//+------------------------------------------------------------------+




//+------------------------------------------------------------------+
//| Creating Text object                                             |
//+------------------------------------------------------------------+
bool TextCreate(const long              chart_ID=0,               // chart's ID
                const string            nname="Text",              // object name
                const int               sub_window=0,             // subwindow index
                datetime                time=0,                   // anchor point time
                double                  price=0,                  // anchor point price
                const string            text="Text",              // the text itself
                const string            font="Arial",             // font
                const int               font_size=10,             // font size
                const color             clr=clrBlack,               // color
                const double            angle=0.0,                // text slope
                const ENUM_ANCHOR_POINT anchor=ANCHOR_LEFT_UPPER, // anchor type
                const bool              back=false,               // in the background
                const bool              selection=false,          // highlight to move
                const bool              hidden=false,              // hidden in the object list
                const long              z_order=0)                // priority for mouse click
  {
   ResetLastError();

   if(!ObjectCreate(chart_ID,nname,OBJ_TEXT,sub_window,time,price))
     {
      Print(__FUNCTION__,
            ": failed to create \"Text\" object! Error code = ",GetLastError());
      return(false);
     }

   ObjectSetString(chart_ID,nname,OBJPROP_TEXT,text);
   ObjectSetString(chart_ID,nname,OBJPROP_FONT,font);
   ObjectSetInteger(chart_ID,nname,OBJPROP_FONTSIZE,font_size);
   ObjectSetDouble(chart_ID,nname,OBJPROP_ANGLE,angle);
   ObjectSetInteger(chart_ID,nname,OBJPROP_ANCHOR,anchor);
   ObjectSetInteger(chart_ID,nname,OBJPROP_COLOR,clr);
   ObjectSetInteger(chart_ID,nname,OBJPROP_BACK,back);
   ObjectSetInteger(chart_ID,nname,OBJPROP_SELECTABLE,selection);
   ObjectSetInteger(chart_ID,nname,OBJPROP_SELECTED,selection);
   ObjectSetInteger(chart_ID,nname,OBJPROP_HIDDEN,hidden);
   ObjectSetInteger(chart_ID,nname,OBJPROP_ZORDER,z_order);
   return(true);
  }
//+------------------------------------------------------------------+



//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool TrendCreate(const long            chart_ID=0,
                 const string          nname="TrendLine",
                 const int             sub_window=0,
                 datetime              time1=0,
                 double                price1=0,
                 datetime              time2=0,
                 double                price2=0,
                 const color           clr=clrRed,
                 const ENUM_LINE_STYLE style=STYLE_SOLID,
                 const int             width=1,
                 const bool            back=false,
                 const bool            selection=true,
                 const bool            ray_left=false,
                 const bool            ray_right=false,
                 const bool            hidden=true,
                 const long            z_order=0)
  {

   ResetLastError();

   if(ObjectFind(nname) == -1)
      ObjectCreate(chart_ID,nname,OBJ_TREND,sub_window,time1,price1,time2,price2);

   ObjectSetInteger(chart_ID,nname,OBJPROP_COLOR,clr);
   ObjectSetInteger(chart_ID,nname,OBJPROP_STYLE,style);
   ObjectSetInteger(chart_ID,nname,OBJPROP_WIDTH,width);
   ObjectSetInteger(chart_ID,nname,OBJPROP_BACK,back);
   ObjectSetInteger(chart_ID,nname,OBJPROP_SELECTABLE,selection);
   ObjectSetInteger(chart_ID,nname,OBJPROP_SELECTED,selection);


   ObjectSetInteger(chart_ID,nname,OBJPROP_RAY_LEFT,ray_left);
   ObjectSetInteger(chart_ID,nname,OBJPROP_RAY_RIGHT,ray_right);
   ObjectSetInteger(chart_ID,nname,OBJPROP_HIDDEN,hidden);
   ObjectSetInteger(chart_ID,nname,OBJPROP_ZORDER,z_order);

   return(true);
  }



//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool RectangleCreate(const long            chart_ID=0,
                     const string          nname="Rectangle",
                     const int             sub_window=0,
                     datetime              time1=0,
                     double                price1=0,
                     datetime              time2=0,
                     double                price2=0,
                     const color           clr=clrRed,
                     const ENUM_LINE_STYLE style=STYLE_SOLID,
                     const int             width=1,
                     const bool            fill=false,
                     const bool            back=false,
                     const bool            selection=true,
                     const bool            selectable=true,
                     const bool            hidden=true,
                     const long            z_order=0)
  {

   ResetLastError();

   if(ObjectFind(nname) == -1)
      ObjectCreate(chart_ID,nname,OBJ_RECTANGLE,sub_window,time1,price1,time2,price2);

   ObjectSetInteger(chart_ID,nname,OBJPROP_COLOR,clr);
   ObjectSetInteger(chart_ID,nname,OBJPROP_STYLE,style);
   ObjectSetInteger(chart_ID,nname,OBJPROP_WIDTH,width);
   ObjectSetInteger(chart_ID,nname,OBJPROP_FILL,fill);
   ObjectSetInteger(chart_ID,nname,OBJPROP_BACK,back);
   ObjectSetInteger(chart_ID,nname,OBJPROP_SELECTABLE,selectable);
   ObjectSetInteger(chart_ID,nname,OBJPROP_SELECTED,selection);


   ObjectSetInteger(chart_ID,nname,OBJPROP_HIDDEN,hidden);
   ObjectSetInteger(chart_ID,nname,OBJPROP_ZORDER,z_order);

   return(true);
  }
//+------------------------------------------------------------------+


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void createButton(string name,string caption,int xpos,int ypos)
  {
   if(ObjectFind(name)!=0)
      ObjectCreate(name,OBJ_BUTTON,0,0,0);
   ObjectSet(name,OBJPROP_CORNER,0);
   ObjectSet(name,OBJPROP_XDISTANCE,xpos);
   ObjectSet(name,OBJPROP_YDISTANCE,ypos);
   ObjectSet(name,OBJPROP_XSIZE,XSize);
   ObjectSet(name,OBJPROP_YSIZE,YSize);
   ObjectSetText(name,caption,FSize,FontType,clrBlack);
   ObjectSet(name,OBJPROP_FONTSIZE,FSize);
   ObjectSet(name,OBJPROP_COLOR,clrBlack);

//int clr = 125 + ypos / 1.5;
//color button_clr = StringToColor(clr + "," + clr + "," + clr);


   ObjectSet(name,OBJPROP_BGCOLOR,ButtonColor);
   ObjectSet(name,OBJPROP_BORDER_COLOR,ButtonColor);
   ObjectSet(name,OBJPROP_STATE,false);
   ObjectSet(name,OBJPROP_HIDDEN,!overview_visible);
   ObjectSet(name,OBJPROP_CORNER,Corner);
   ObjectSet(name,OBJPROP_BACK,false);

  }
//+------------------------------------------------------------------+
void setSymbolButtonColor()
  {
   string lookFor       = buttonPrefix + ":symbol:";
   int    lookForLength = StringLen(lookFor);
   for(int i=ObjectsTotal()-1; i>=0; i--)
     {
      string objectName=ObjectName(i);
      if(StringSubstr(objectName,0,lookForLength)==lookFor)
        {
         ObjectSet(objectName,OBJPROP_COLOR,clrBlack);
         ObjectSet(objectName,OBJPROP_BGCOLOR,ButtonColor);
         ObjectSet(objectName,OBJPROP_BORDER_COLOR,ButtonColor);

         string btn_pair = StringSubstr(objectName, lookForLength, StringLen(objectName) - lookForLength);

         for(int k=0; k < ArraySize(alertList); k++)
            if(alertList[k].active && alertList[k].pair == btn_pair)
              {
               ObjectSet(objectName,OBJPROP_BGCOLOR,AlertSetColor);
               ObjectSet(objectName,OBJPROP_BORDER_COLOR,AlertSetColor);
              }
        }
     }
  }
//+------------------------------------------------------------------+



//+------------------------------------------------------------------+
void updateSymbolButtonColor()
  {
   RefreshRates();
   setSymbolButtonColor();

   for(int i=(OrdersTotal()-1); i>=0; i--)
     {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false)
        {
         Print("ERROR - Unable to select the order - ",GetLastError());
         continue;
        }

      int ordType = OrderType();

      if(ordType == OP_BUYSTOP || ordType == OP_SELLSTOP)
         continue;

      string tgOrderSymbol = OrderSymbol();
      string buttonName = buttonPrefix + ":symbol:" + tgOrderSymbol;

      if(ordType == OP_BUY || ordType == OP_SELL)
        {
         ObjectSet(buttonName,OBJPROP_BGCOLOR,OpenPositionColor);
         ObjectSet(buttonName,OBJPROP_BORDER_COLOR,OpenPositionColor);
        }

      if(ordType == OP_BUYLIMIT || ordType == OP_SELLLIMIT)
        {
         ObjectSet(buttonName,OBJPROP_COLOR,PlacedOrderColor);
         ObjectSet(buttonName,OBJPROP_BORDER_COLOR,PlacedOrderColor);

         if(getDistanceToNearestPendingEntry(tgOrderSymbol) < DistanceInPoints)
           {
            ObjectSet(buttonName,OBJPROP_COLOR,PriceIsCloseColor);
            ObjectSet(buttonName,OBJPROP_BORDER_COLOR,PriceIsCloseColor);
           }

        }

     }
  }
//+------------------------------------------------------------------+




//+------------------------------------------------------------------+
void setButtonsVisibility(bool vis)
  {
   overview_visible = vis;
   int    lookForLength = StringLen(buttonPrefix);
   for(int i=ObjectsTotal()-1; i>=0; i--)
     {
      string objectName=ObjectName(i);
      if(StringSubstr(objectName,0,lookForLength) == buttonPrefix)
        {
         string symbol=ObjectGetString(0,objectName,OBJPROP_TEXT);
         if(vis)
            ObjectSetInteger(0, objectName,OBJPROP_TIMEFRAMES,OBJ_ALL_PERIODS);
         else
            ObjectSetInteger(0, objectName,OBJPROP_TIMEFRAMES,OBJ_NO_PERIODS);
        }
     }
  }
//+------------------------------------------------------------------+




//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
double getDistanceToNearestPendingEntry(string sym)
  {
   double nearestDist = 999999999;

   for(int i=(OrdersTotal()-1); i>=0; i--)
     {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES)==false)
        {
         Print("ERROR - Unable to select the order - ",GetLastError());
         continue;
        }

      if(OrderSymbol() != sym)
         continue;

      double curDist = MathAbs(OrderOpenPrice() - MarketInfo(sym,MODE_BID)) * MathPow(10, MarketInfo(sym,MODE_DIGITS));
      if(curDist < nearestDist)
         nearestDist = curDist;
     }
   return nearestDist;
  }




//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void SaveObjectsToFile()
  {

// save chart objects
   int hndl = FileOpen(Symbol() + "_saved_objs.txt",FILE_WRITE|FILE_TXT);

   for(int i=0; i < ObjectsTotal(); i++)
     {

      string nm = ObjectName(i);

      if(StringFind(nm, buttonPrefix + ":symbol:",0) == 0)
         continue;    // no need to save buttons
      if(StringFind(nm,"Spread",0) == 0)
         continue;               // neither spread
      if(StringFind(nm, AlertPrefix, 0) == 0)
         continue;
      //if(StringFind(nm, chartInfoObjName, 0) == 0)
      //   continue;



      string newObjName = nm;
      StringReplace(newObjName, " ", "_");

      string infoStr = newObjName + " ";
      infoStr = infoStr + ObjectType(nm)                + " ";
      infoStr = infoStr + ObjectGet(nm, OBJPROP_TIME1)  + " ";
      infoStr = infoStr + ObjectGet(nm, OBJPROP_PRICE1) + " ";
      infoStr = infoStr + ObjectGet(nm, OBJPROP_TIME2)  + " ";
      infoStr = infoStr + ObjectGet(nm, OBJPROP_PRICE2) + " ";
      infoStr = infoStr + ObjectGet(nm, OBJPROP_TIME3)  + " ";
      infoStr = infoStr + ObjectGet(nm, OBJPROP_PRICE3) + " ";
      infoStr = infoStr + ObjectGet(nm, OBJPROP_STYLE)  + " ";
      infoStr = infoStr + ObjectGet(nm, OBJPROP_WIDTH)  + " ";
      infoStr = infoStr + ObjectGet(nm, OBJPROP_COLOR)  + " ";
      infoStr = infoStr + ObjectGet(nm, OBJPROP_FILL)   + " ";
      infoStr = infoStr + ObjectGet(nm, OBJPROP_BACK)   + " ";
      infoStr = infoStr + ObjectGet(nm, OBJPROP_RAY_LEFT) + " ";
      infoStr = infoStr + ObjectGet(nm, OBJPROP_RAY_RIGHT) + " ";

      string txt = ObjectGetString(0, nm, OBJPROP_TEXT);
      StringReplace(txt, " ", "_");
      txt = "_" + txt;

      infoStr = infoStr + txt + " " ;
      infoStr = infoStr + ObjectGetInteger(0, nm, OBJPROP_ARROWCODE)+ " ";
      infoStr = infoStr + ObjectGet(nm, OBJPROP_SELECTED) + " ";

      infoStr = infoStr + " \n";


      FileWriteString(hndl,infoStr);

     }
   FileClose(hndl);
//Print("Saved to file");

  }
//+------------------------------------------------------------------+


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void LoadObjectsFromFile(string fileName)
  {

   int hndl = FileOpen(fileName,FILE_READ|FILE_TXT);

   while(!FileIsEnding(hndl))
     {
      string params[];
      StringSplit(FileReadString(hndl), ' ', params);

      if(ArraySize(params) < 17)
         continue;

      string nm = params[0];

      ObjectCreate(0, nm, params[1], 0, params[2], params[3]);

      ObjectSet(nm, OBJPROP_TIME2, params[4]);
      ObjectSet(nm, OBJPROP_PRICE2, params[5]);
      ObjectSet(nm, OBJPROP_TIME3, params[6]);
      ObjectSet(nm, OBJPROP_PRICE3, params[7]);

      ObjectSet(nm, OBJPROP_STYLE, params[8]);
      ObjectSet(nm, OBJPROP_WIDTH, params[9]);
      ObjectSet(nm, OBJPROP_COLOR, params[10]);
      ObjectSet(nm, OBJPROP_FILL, params[11]);
      ObjectSet(nm, OBJPROP_BACK, params[12]);
      ObjectSet(nm, OBJPROP_RAY_LEFT, params[13]);
      ObjectSet(nm, OBJPROP_RAY_RIGHT, params[14]);

      string txt = params[15];

      ObjectSetInteger(0, nm, OBJPROP_ARROWCODE, params[16]);

      if(ArraySize(params) >= 18)
         ObjectSet(nm, OBJPROP_SELECTED, params[17]);

      StringReplace(txt, "_", " ");
      StringReplace(txt, ",,,", "");

      ObjectSetString(0, nm, OBJPROP_TEXT, StringTrimLeft(txt));

     }
   FileClose(hndl);

  }
//+------------------------------------------------------------------+

//+------------------------------------------------------------------+



//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void saveVariablesToObj()
  {
   TrendCreate(0,chartInfoObjName,0,cursorCandleDate,ChartGetDouble(0, CHART_PRICE_MIN),555555,ChartGetDouble(0, CHART_PRICE_MAX),clrBlue,STYLE_SOLID,2,true,true,false,false,true,0);
   ObjectSetText(chartInfoObjName,last_cursor_x,10,"Times New Roman",clrNONE);
   ObjectSet(chartInfoObjName, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);   // make invisible
//Print("Saved cursor date: " + cursorCandleDate);
  }


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void loadVariablesFromObj()
  {
   string desc = ObjectDescription(chartInfoObjName);
   cursorCandleDate = ObjectGet(chartInfoObjName, OBJPROP_TIME1);
   last_cursor_x = desc;

   int m_shift = WindowBarsPerChart();
   if(WindowFirstVisibleBar() - WindowBarsPerChart() < 0)
      m_shift = WindowFirstVisibleBar();

   double lowest_price = iLow(NULL, 0, iLowest(NULL, 0, MODE_LOW, m_shift, MathMax(WindowFirstVisibleBar() - WindowBarsPerChart(), 0)));
   double highest_price = iHigh(NULL, 0, iHighest(NULL, 0, MODE_HIGH, m_shift, MathMax(WindowFirstVisibleBar() - WindowBarsPerChart(), 0)));


   if(FreeChart)
      if(highest_price > ObjectGet(chartInfoObjName, OBJPROP_PRICE1) && lowest_price < ObjectGet(chartInfoObjName, OBJPROP_PRICE2))
        {
         ChartSetInteger(0,CHART_SCALEFIX,true);
         double vertical_shift = (ObjectGet(chartInfoObjName, OBJPROP_PRICE2) - ObjectGet(chartInfoObjName, OBJPROP_PRICE1)) * 0.0033;
         ChartSetDouble(0, CHART_FIXED_MIN, ObjectGet(chartInfoObjName, OBJPROP_PRICE1) + vertical_shift);
         ChartSetDouble(0, CHART_FIXED_MAX, ObjectGet(chartInfoObjName, OBJPROP_PRICE2));
        }
      else
         fixChartMinMax();

   ObjectDelete(chartInfoObjName);
  }





//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void SaveAlertsToFile()
  {
   sortAlerts();
   int hndl = FileOpen("alerts.txt",FILE_WRITE|FILE_TXT);

   for(int i=0; i < ArraySize(alertList); i++)
     {
      if(!alertList[i].active)
         continue;

      string record = alertList[i].pair + " " + alertList[i].price + " \"" + alertList[i].descript + "\" " + alertList[i].underPrice + "\n";
      FileWriteString(hndl, record);
     }

//Print("Alerts saved");
   FileClose(hndl);

  }




//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void LoadAlertsFromFile()
  {

   if(!FileIsExist("alerts.txt"))
      return;

   bool fileModified = false;
   alertFileBlocked = true;
   int hndl = FileOpen("alerts.txt", FILE_READ|FILE_TXT);
   int alertNum = 0;


   for(int k = ObjectsTotal() - 1; k >= 0; k--)
      if(StringFind(ObjectName(k), AlertPrefix) == 0)   // remove existing alerts from the chart
         ObjectDelete(0, ObjectName(k));


//Print("Loading alerts");
   while(!FileIsEnding(hndl))
     {
      string params[];
      string dataStr = StringTrimLeft(StringTrimRight(FileReadString(hndl)));

      int descStartPos = StringFind(dataStr, "\"") + 1;
      int descEndPos = StringFind(dataStr, "\"", descStartPos);
      string desc = StringSubstr(dataStr, descStartPos, descEndPos - descStartPos); // get description
      if(descEndPos - descStartPos == 0 || descStartPos == 0)
         desc = ""; // empty description

      StringReplace(dataStr, " \"" + desc + "\"", "");  // remove description because it can contain spaces
      dataStr = StringTrimLeft(StringTrimRight(dataStr));
      StringSplit(dataStr, ' ', params);

      if(ArraySize(params) < 2) // data missing - ignore
         continue;

      ArrayResize(alertList, alertNum + 1);

      alertList[alertNum].pair = params[0];
      alertList[alertNum].price = params[1];
      alertList[alertNum].descript = desc;
      alertList[alertNum].active = true;

      if(ArraySize(params) >= 3)
         alertList[alertNum].underPrice = StringCompare(params[2], "1") == 0;
      else
        {
         alertList[alertNum].underPrice = alertList[alertNum].price < MarketInfo(alertList[alertNum].pair, MODE_BID);   // if direction is not specified
         fileModified = true;
        }






      // place alert levels on current symbol
      if(alertList[alertNum].pair == Symbol())
        {
         bool alertAlreadyExists = false;
         int objTotals = ObjectsTotal();

         for(k = objTotals - 1; k >= 0; k--)
            if(StringFind(ObjectName(k), AlertPrefix) == 0)
               if(MathAbs(alertList[alertNum].price - ObjectGet(ObjectName(k), OBJPROP_PRICE1)) < 0.000001)  // floating point comparison
                 {
                  alertAlreadyExists = true;
                  alertList[alertNum].currChartName = ObjectName(k);
                 }


         if(!alertAlreadyExists)
           {

            string name;
            if(alertList[alertNum].underPrice)
               name = AlertPrefix + "un" +  IntegerToString(MathRand() + 100,0,' ');
            else
               name = AlertPrefix + "ov" + IntegerToString(MathRand() + 100,0,' ');

            alertList[alertNum].currChartName = name;

            ObjectCreate(name, OBJ_HLINE, 0, Time[0], alertList[alertNum].price);
            ObjectSet(name, OBJPROP_STYLE, STYLE_DOT);
            ObjectSet(name, OBJPROP_WIDTH, 1);
            ObjectSet(name, OBJPROP_SELECTED, false);
            ObjectSet(name, OBJPROP_SELECTABLE, true);
            ObjectSet(name, OBJPROP_BACK, false);

            ObjectSetText(name, alertList[alertNum].descript);

            if(alertList[alertNum].underPrice)
               ObjectSet(name, OBJPROP_COLOR, clrRed);
            else
               ObjectSet(name, OBJPROP_COLOR, clrBlue);
           }
        }



      alertNum++;
     }

   FileClose(hndl);
   alertFileBlocked = false;


   SaveAlertsToFile();

  }
//+------------------------------------------------------------------+


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void prepareButtons()
  {
   Symbols=StringTrimLeft(StringTrimRight(Symbols));
   if(StringSubstr(Symbols,StringLen(Symbols)-1,1)!=";")
      Symbols=StringConcatenate(Symbols,";");

   int s=0;
   int i=StringFind(Symbols,";",s);
   string current;
   while(i>0)
     {
      current=StringSubstr(Symbols,s,i-s);
      ArrayResize(aSymbols,ArraySize(aSymbols)+1);
      aSymbols[ArraySize(aSymbols)-1]=current;
      s = i + 1;
      i = StringFind(Symbols,";",s);
     }


   int xpos=0,ypos=0,maxx=0,maxy=0;
   for(i=0; i<ArraySize(aSymbols); i++)
     {
      if(i>0 && MathMod(i,ButtonsInARow)==0)
        {
         xpos=0;
         ypos+=YSize+1;
        }

      if(StringTrimLeft(StringTrimRight(aSymbols[i])) != "x")
         createButton(buttonPrefix + ":symbol:"+StringTrimLeft(StringTrimRight(aSymbols[i])),aSymbols[i],XShift-xpos,YShift+ypos);
      xpos+=XSize+1;
     }
  }
//+------------------------------------------------------------------+


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void sortAlerts()  // good old bubble sort
  {
   bool swapMade = true;

   while(swapMade)
     {
      swapMade = false;

      for(int i=0; i < ArraySize(alertList)-1; i++)
        {

         string a1 = alertList[i].pair + alertList[i].price + alertList[i].descript;
         string a2 = alertList[i+1].pair + alertList[i+1].price + alertList[i+1].descript;

         if(StringCompare(a1, a2) > 0)
           {
            SuperAlert tmpAlert = alertList[i];
            alertList[i] = alertList[i+1];
            alertList[i+1] = tmpAlert;

            swapMade = true;
           }
        }
     }

  }
//+------------------------------------------------------------------+
