//+------------------------------------------------------------------+
//|                                                     Calendar.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "..\Element.mqh"
#include "TextEdit.mqh"
#include "ComboBox.mqh"
#include "Button.mqh"
#include "ButtonsGroup.mqh"
#include <Tools\DateTime.mqh>
//--- Количество дней в таблице
#define DAYS_TOTAL 42
//+------------------------------------------------------------------+
//| Класс для создания календаря                                     |
//+------------------------------------------------------------------+
class CCalendar : public CElement
  {
private:
   //--- Элементы для создания календаря
   CButton           m_month_dec;
   CButton           m_month_inc;
   CComboBox         m_months;
   CTextEdit         m_years;
   CButtonsGroup     m_days;
   CButton           m_button_today;
   //--- Экзепляры структуры для работы с датами и временем:
   CDateTime         m_date;      // выделенная пользователем дата
   CDateTime         m_today;     // текущая (локальная на компьютере пользователя) дата
   CDateTime         m_temp_date; // экземпляр для расчётов и проверок
   //--- Цвет пункта текущего дня
   color             m_today_color;
   //--- Цвет разделительной линии
   color             m_sepline_color;
   //--- Счётчик таймера для перемотки списка
   int               m_timer_counter;
   //---
public:
                     CCalendar(void);
                    ~CCalendar(void);
   //--- Методы для создания календаря
   bool              CreateCalendar(const int x_gap,const int y_gap);
   //---
private:
   void              InitializeProperties(const int x_gap,const int y_gap);
   bool              CreateCanvas(void);
   bool              CreateMonthArrow(CButton &button_obj,const int index);
   bool              CreateMonthsList(void);
   bool              CreateYearsSpinEdit(void);
   bool              CreateDaysMonth(void);
   bool              CreateButtonToday(void);
   //---
public:
   //--- Возвращает указатели на элементы календаря
   CButton          *GetMonthDecPointer(void)              { return(::GetPointer(m_month_dec));    }
   CButton          *GetMonthIncPointer(void)              { return(::GetPointer(m_month_inc));    }
   CComboBox        *GetComboBoxPointer(void)              { return(::GetPointer(m_months));       }
   CTextEdit        *GetSpinEditPointer(void)              { return(::GetPointer(m_years));        }
   CButton          *GetTodayButtonPointer(void)           { return(::GetPointer(m_button_today)); }
   CButtonsGroup    *GetDayButtonsPointer(void)            { return(::GetPointer(m_days));         }
   //--- (1) получить текущую дату в календаре, (2) Установить (выделить) и (3) получить выделенную дату
   datetime          Today(void)                           { return(m_today.DateTime());           }
   datetime          SelectedDate(void)                    { return(m_date.DateTime());            }
   void              SelectedDate(const datetime date);
   //--- Отображение последних изменений в календаре
   void              UpdateCalendar(void);
   //--- Обновляет элементы календаря
   void              UpdateElements(void);
   //--- Обновление текущей даты
   void              UpdateCurrentDate(void);
   //---
public:
   //--- Обработчик событий графика
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Таймер
   virtual void      OnEventTimer(void);
   //--- Показ
   virtual void      Show(void);
   //--- Рисует элемент
   virtual void      Draw(void);
   //---
private:
   //--- Обработка нажатия на кнопке перехода к предыдущему месяцу
   bool              OnClickMonthDec(const string clicked_object,const int id,const int index);
   //--- Обработка нажатия на кнопке перехода к следующему месяцу
   bool              OnClickMonthInc(const string clicked_object,const int id,const int index);
   //--- Обработка выбора месяца в списке
   bool              OnClickMonthList(const int id);
   //--- Обработка ввода значения в поле ввода лет
   bool              OnEndEnterYear(const string edited_object,const int id);
   //--- Обработка нажатия на кнопке перехода к следующему году
   bool              OnClickYearInc(const string clicked_object,const int id,const int index);
   //--- Обработка нажатия на кнопке перехода к предыдущему году
   bool              OnClickYearDec(const string clicked_object,const int id,const int index);
   //--- Обработка нажатия на дне месяца
   bool              OnClickDayOfMonth(const string clicked_object,const int id,const int index);
   //--- Обработка нажатия на кнопке перехода к текущей дате
   bool              OnClickTodayButton(const string clicked_object,const int id,const int index);

   //--- Корректировка выделенного дня по количеству дней в месяце
   void              CorrectingSelectedDay(void);
   //--- Определение разницы от первого пункта таблицы календаря до пункта первого дня текущего месяца
   int               OffsetFirstDayOfMonth(void);
   //--- Отображает последние изменения в таблице календаря
   void              SetCalendar(void);
   //--- Ускоренная перемотка значений календаря
   void              FastSwitching(void);
   //--- Подсветка текущего дня и выбранного пользователем дня
   void              HighlightDate(void);
   //--- Сброс времени на начало суток
   void              ResetTime(void);

   //--- Рисует названия дней недели
   void              DrawDaysWeek(void);
   //--- Рисует разделительную линию
   void              DrawSeparateLine(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CCalendar::CCalendar(void) : m_today_color(C'0,102,204')
  {
//--- Сохраним имя класса элемента в базовом классе
   CElementBase::ClassName(CLASS_NAME);
//--- Инициализация структур времени
   m_date.DateTime(::TimeLocal());
   m_today.DateTime(::TimeLocal());
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CCalendar::~CCalendar(void)
  {
  }
//+------------------------------------------------------------------+
//| Обработчик событий                                               |
//+------------------------------------------------------------------+
void CCalendar::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Обработка события выбора пункта в выпадающем списке
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_COMBOBOX_ITEM)
     {
      //--- Обработка ввода значения в поле ввода лет
      if(OnClickMonthList((int)lparam))
         return;
      //---
      return;
     }
//--- Обработка события ввода значения в поле ввода
   if(id==CHARTEVENT_CUSTOM+ON_END_EDIT)
     {
      //--- Обработка ввода значения в поле ввода лет
      if(OnEndEnterYear(sparam,(int)lparam))
         return;
      //---
      return;
     }
//--- Обработка события нажатия на кнопке в группе
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_GROUP_BUTTON)
     {
      //--- Обработка нажатия на дне календаря
      if(OnClickDayOfMonth(sparam,(int)lparam,(int)dparam))
         return;
      //---
      return;
     }
//--- Обработка события нажатия на кнопке
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
     {
      //--- Обработка нажатия на кнопках переключения месяцев
      if(OnClickMonthDec(sparam,(int)lparam,(int)dparam))
         return;
      if(OnClickMonthInc(sparam,(int)lparam,(int)dparam))
         return;
      //--- Обработка нажатия на кнопках перехода по годам
      if(OnClickYearInc(sparam,(int)lparam,(int)dparam))
         return;
      if(OnClickYearDec(sparam,(int)lparam,(int)dparam))
         return;
      //--- Обработка нажатия на кнопке перехода к текущей дате
      if(OnClickTodayButton(sparam,(int)lparam,(int)dparam))
         return;
      //---
      return;
     }
  }
//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
void CCalendar::OnEventTimer(void)
  {
//--- Ускоренная перемотка значений
   FastSwitching();
//--- Обновление текущей даты календаря
   UpdateCurrentDate();
  }
//+------------------------------------------------------------------+
//| Создаёт контекстное меню                                         |
//+------------------------------------------------------------------+
bool CCalendar::CreateCalendar(const int x_gap,const int y_gap)
  {
//--- Выйти, если нет указателя на главный элемент
   if(!CElement::CheckMainPointer())
      return(false);
//--- Инициализация свойств
   InitializeProperties(x_gap,y_gap);
//--- Создание элемента
   if(!CreateCanvas())
      return(false);
   if(!CreateMonthArrow(m_month_dec,0))
      return(false);
   if(!CreateMonthArrow(m_month_inc,1))
      return(false);
   if(!CreateMonthsList())
      return(false);
   if(!CreateYearsSpinEdit())
      return(false);
   if(!CreateDaysMonth())
      return(false);
   if(!CreateButtonToday())
      return(false);
//--- Обновить календарь
   UpdateCalendar();
   return(true);
  }
//+------------------------------------------------------------------+
//| Инициализация свойств                                            |
//+------------------------------------------------------------------+
void CCalendar::InitializeProperties(const int x_gap,const int y_gap)
  {
   m_x      =CElement::CalculateX(x_gap);
   m_y      =CElement::CalculateY(y_gap);
   m_x_size =161;
   m_y_size =158;
//--- Цвета умолчанию
   m_back_color         =(m_back_color!=clrNONE)? m_back_color : clrWhite;
   m_border_color       =(m_border_color!=clrNONE)? m_border_color : C'150,170,180';
   m_label_color        =(m_label_color!=clrNONE)? m_label_color : clrBlack;
   m_label_color_locked =(m_label_color_locked!=clrNONE)? m_label_color_locked : C'200,200,200';
//--- Отступы от крайней точки
   CElementBase::XGap(x_gap);
   CElementBase::YGap(y_gap);
  }
//+------------------------------------------------------------------+
//| Создаёт объект для рисования                                     |
//+------------------------------------------------------------------+
bool CCalendar::CreateCanvas(void)
  {
//--- Формирование имени объекта
   string name=CElementBase::ElementName("calendar");
//--- Создание объекта
   if(!CElement::CreateCanvas(name,m_x,m_y,m_x_size,m_y_size))
      return(false);
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Создаёт переключатель месяцев влево                              |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Controls\\left_thin_black.bmp"
#resource "\\Images\\EasyAndFastGUI\\Controls\\right_thin_black.bmp"
//---
bool CCalendar::CreateMonthArrow(CButton &button_obj,const int index)
  {
//--- Сохраним указатель на главный элемент
   button_obj.MainPointer(this);
//--- Размеры
   int x_size=12;
   int y_size=18;
//--- Отступ
   int offset=2;
//--- Координаты
   int x =(index<1)? offset : x_size+offset;
   int y =offset;
//--- Свойства
   button_obj.Index(index);
   button_obj.XSize(x_size);
   button_obj.YSize(y_size);
   button_obj.IconXGap(-2);
   button_obj.IconYGap(1);
   button_obj.IsDropdown(CElementBase::IsDropdown());
//--- Ярлыки для кнопок
   if(index<1)
     {
      button_obj.IconFile("Images\\EasyAndFastGUI\\Controls\\left_thin_black.bmp");
      button_obj.IconFileLocked("Images\\EasyAndFastGUI\\Controls\\left_thin_black.bmp");
     }
   else
     {
      button_obj.IconFile("Images\\EasyAndFastGUI\\Controls\\right_thin_black.bmp");
      button_obj.IconFileLocked("Images\\EasyAndFastGUI\\Controls\\right_thin_black.bmp");
      button_obj.AnchorRightWindowSide(true);
     }
//--- Создадим элемент управления
   if(!button_obj.CreateButton("",x,y))
      return(false);
//--- Добавить элемент в массив
   CElement::AddToArray(button_obj);
   return(true);
  }
//+------------------------------------------------------------------+
//| Создаёт комбо-бокс с месяцами                                    |
//+------------------------------------------------------------------+
bool CCalendar::CreateMonthsList(void)
  {
//--- Сохраним указатель на главный элемент
   m_months.MainPointer(this);
//--- Координаты
   int x=14,y=2;
//--- Свойства
   m_months.XSize(50);
   m_months.YSize(18);
   m_months.ItemsTotal(12);
   m_months.GetButtonPointer().XGap(1);
   m_months.GetButtonPointer().LabelYGap(3);
   m_months.IsDropdown(CElementBase::IsDropdown());
//--- Получим указатель на список
   CListView *lv=m_months.GetListViewPointer();
//--- Установим свойства списка
   lv.YSize(93);
   lv.LightsHover(true);
//--- Занесём значения в список (названия месяцев)
   for(int i=0; i<12; i++)
      m_months.SetValue(i,m_date.MonthName(i+1));
//--- Выделим в списке текущий месяц
   m_months.SelectItem(m_date.mon-1);
//--- Создадим элемент управления
   if(!m_months.CreateComboBox("",x,y))
      return(false);
//--- Добавить элемент в массив
   CElement::AddToArray(m_months);
   return(true);
  }
//+------------------------------------------------------------------+
//| Создаёт поле ввода года                                          |
//+------------------------------------------------------------------+
bool CCalendar::CreateYearsSpinEdit(void)
  {
//--- Сохраним указатель на главный элемент
   m_years.MainPointer(this);
//--- Координаты
   int x=95,y=2;
//--- Свойства
   m_years.Index(m_is_dropdown? 1 : 0);
   m_years.XSize(50);
   m_years.YSize(18);
   m_years.MaxValue(2099);
   m_years.MinValue(1970);
   m_years.StepValue(1);
   m_years.SetDigits(0);
   m_years.SpinEditMode(true);
   m_years.GetTextBoxPointer().AutoSelectionMode(true);
   m_years.SetValue((string)m_date.year);
   m_years.GetTextBoxPointer().XGap(1);
   m_years.GetTextBoxPointer().XSize(50);
   m_years.GetIncButtonPointer().NamePart("cal_spin_inc");
   m_years.GetDecButtonPointer().NamePart("cal_spin_dec");
//--- Создадим элемент управления
   if(!m_years.CreateTextEdit("",x,y))
      return(false);
//--- Отступ текста в поле ввода
   m_years.GetTextBoxPointer().TextYOffset(4);
//--- Добавить элемент в массив
   CElement::AddToArray(m_years);
   return(true);
  }
//+------------------------------------------------------------------+
//| Создаёт таблицу дней месяца                                      |
//+------------------------------------------------------------------+
bool CCalendar::CreateDaysMonth(void)
  {
//--- Счётчик дней
   int i=0;
//--- Координаты и отступы
   int x=0,y=0;
   int x_offset=7,y_offset=44;
//--- Размеры
   int x_size=21,y_size=15;
//--- Сохраним указатель на главный элемент
   m_days.MainPointer(this);
//---
   int    buttons_x_offset[DAYS_TOTAL]={};
   int    buttons_y_offset[DAYS_TOTAL]={};
   string buttons_text[DAYS_TOTAL]={};
//--- Установим объекты таблицы дней календаря
   for(int r=0; r<6; r++)
     {
      //--- Расчёт координаты Y
      y=(r>0)? y+y_size : 0;
      //---
      for(int c=0; c<7; c++)
        {
         //--- Расчёт координаты X
         x=(c>0)? x+x_size : 0;
         //--
         buttons_text[i]     =string(i);
         buttons_x_offset[i] =x;
         buttons_y_offset[i] =y;
         //---
         i++;
        }
     }
//--- Свойства
   m_days.NamePart("day");
   m_days.ButtonYSize(y_size);
   m_days.LabelYGap(1);
   m_days.IsCenterText(true);
   m_days.RadioButtonsMode(true);
   m_days.IsDropdown(CElementBase::IsDropdown());
//--- Добавим кнопки в группу
   for(int j=0; j<DAYS_TOTAL; j++)
      m_days.AddButton(buttons_x_offset[j],buttons_y_offset[j],buttons_text[j],x_size);
//--- Создать группу кнопок
   x=x_offset;
   y=y_offset;
   if(!m_days.CreateButtonsGroup(x,y))
      return(false);
//--- Свойства
   for(int j=0; j<DAYS_TOTAL; j++)
     {
      m_days.GetButtonPointer(j).BackColor(m_back_color);
      m_days.GetButtonPointer(j).BorderColor(m_back_color);
     }
//--- Добавить элемент в массив
   CElement::AddToArray(m_days);
   return(true);
  }
//+------------------------------------------------------------------+
//| Создаёт кнопку для перехода на текущую дату                      |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Controls\\calendar_today.bmp"
//---
bool CCalendar::CreateButtonToday(void)
  {
//--- Сохраним указатель на главный элемент
   m_button_today.MainPointer(this);
//--- Координаты
   int x=22,y=YSize()-23;
//--- Свойства
   m_button_today.NamePart("today_button");
   m_button_today.Index(2);
   m_button_today.XSize(120);
   m_button_today.YSize(20);
   m_button_today.IconXGap(1);
   m_button_today.IconYGap(1);
   m_button_today.LabelXGap(25);
   m_button_today.LabelYGap(4);
   m_button_today.BackColor(m_back_color);
   m_button_today.BackColorHover(m_back_color);
   m_button_today.BackColorLocked(m_back_color);
   m_button_today.BackColorPressed(m_back_color);
   m_button_today.BorderColor(m_back_color);
   m_button_today.BorderColorHover(m_back_color);
   m_button_today.BorderColorLocked(m_back_color);
   m_button_today.BorderColorPressed(m_back_color);
   m_button_today.LabelColorHover(C'0,102,250');
   m_button_today.IsDropdown(CElementBase::IsDropdown());
   m_button_today.IconFile("Images\\EasyAndFastGUI\\Controls\\calendar_today.bmp");
   m_button_today.IconFileLocked("Images\\EasyAndFastGUI\\Controls\\calendar_today.bmp");
   m_button_today.IconFilePressed("Images\\EasyAndFastGUI\\Controls\\calendar_today.bmp");
   m_button_today.IconFilePressedLocked("Images\\EasyAndFastGUI\\Controls\\calendar_today.bmp");
//--- Создадим элемент управления
   if(!m_button_today.CreateButton("Today: "+::TimeToString(::TimeLocal(),TIME_DATE),x,y))
      return(false);
//--- Добавить элемент в массив
   CElement::AddToArray(m_button_today);
   return(true);
  }
//+------------------------------------------------------------------+
//| Выбор новой даты                                                 |
//+------------------------------------------------------------------+
void CCalendar::SelectedDate(const datetime date)
  {
//--- Сохранение даты в структуре и поле класса
   m_date.DateTime(date);
//--- Отображение последних изменений в календаре
   UpdateCalendar();
  }
//+------------------------------------------------------------------+
//| Отображение последних изменений в календаре                      |
//+------------------------------------------------------------------+
void CCalendar::UpdateCalendar(void)
  {
//--- Отобразить изменения в таблице календаря
   SetCalendar();
//--- Подсветка текущего дня и выбранного пользователем дня
   HighlightDate();
//--- Установим год в поле ввода
   m_years.SetValue((string)m_date.year,false);
//--- Установим месяц в списке комбо-бокса
   m_months.SelectItem(m_date.mon-1);
  }
//+------------------------------------------------------------------+
//| Обновляет элементы календаря                                     |
//+------------------------------------------------------------------+
void CCalendar::UpdateElements(void)
  {
   m_years.GetTextBoxPointer().Update(true);
   m_months.GetButtonPointer().Update(true);
   m_days.Update(true);
  }
//+------------------------------------------------------------------+
//| Обновление текущей даты                                          |
//+------------------------------------------------------------------+
void CCalendar::UpdateCurrentDate(void)
  {
//--- Счётчик
   static int count=0;
//--- Выйти, если прошло меньше секунды
   if(count<1000)
     {
      count+=TIMER_STEP_MSC;
      return;
     }
//--- Обнулить счётчик
   count=0;
//--- Получим текущее (локальное) время
   MqlDateTime local_time;
   ::TimeToStruct(::TimeLocal(),local_time);
//--- Если наступил новый день
   if(local_time.day!=m_today.day)
     {
      //--- Обновить дату в календаре
      m_today.DateTime(::TimeLocal());
      m_button_today.LabelText(::TimeToString(m_today.DateTime()));
      //--- Отобразить последние изменения в календаре
      UpdateCalendar();
      return;
     }
//--- Обновить дату в календаре
   m_today.DateTime(::TimeLocal());
  }
//+------------------------------------------------------------------+
//| Показывает календарь                                             |
//+------------------------------------------------------------------+
void CCalendar::Show(void)
  {
//--- Если календарь не выпадающий, сделать видимыми все его элементы
   CElement::Show();
//--- Если календарь выпадающий
   if(CElementBase::IsDropdown())
     {
      int elements_total=ElementsTotal();
      for(int i=0; i<elements_total; i++)
         m_elements[i].Show();
     }
  }
//+------------------------------------------------------------------+
//| Нажатие на стрелку влево. Переход к предыдущему месяцу.          |
//+------------------------------------------------------------------+
bool CCalendar::OnClickMonthDec(const string clicked_object,const int id,const int index)
  {
//--- Выйдем, если нажатие было не на этом элементе
   if(!m_month_dec.CheckElementName(clicked_object))
      return(false);
//--- Выйти, если (1) идентификаторы не совпадают или (2) элемент заблокирован
   if(id!=CElementBase::Id() || index!=m_month_dec.Index() || CElementBase::IsLocked())
      return(false);
//--- Если текущий год в календаре равен минимальному указанному и текущий месяц "Январь"
   if(m_date.year==m_years.MinValue() && m_date.mon==1)
      return(true);
//--- Перейти к предыдущему месяцу
   m_date.MonDec();
//--- Установить первое число месяца
   m_date.day=1;
//--- Установить время на начало суток
   ResetTime();
//--- Отображение последних изменений в календаре
   UpdateCalendar();
//--- Обновляет элементы календаря
   UpdateElements();
//--- Отправим сообщение об этом
   ::EventChartCustom(m_chart_id,ON_CHANGE_DATE,CElementBase::Id(),m_date.DateTime(),"");
   return(true);
  }
//+------------------------------------------------------------------+
//| Нажатие на стрелку влево. Переход к следующему месяцу.           |
//+------------------------------------------------------------------+
bool CCalendar::OnClickMonthInc(const string clicked_object,const int id,const int index)
  {
//--- Выйдем, если нажатие было не на этом элементе
   if(!m_month_inc.CheckElementName(clicked_object))
      return(false);
//--- Выйти, если (1) идентификаторы не совпадают или (2) элемент заблокирован
   if(id!=CElementBase::Id() || index!=m_month_inc.Index() || CElementBase::IsLocked())
      return(false);
//--- Если текущий год в календаре равен максимальному указанному и текущий месяц "Декабрь"
   if(m_date.year==m_years.MaxValue() && m_date.mon==12)
      return(true);
//--- Перейти к следующему месяцу
   m_date.MonInc();
//--- Установить первое число месяца
   m_date.day=1;
//--- Установить время на начало суток
   ResetTime();
//--- Отображение последних изменений в календаре
   UpdateCalendar();
//--- Обновляет элементы календаря
   UpdateElements();
//--- Отправим сообщение об этом
   ::EventChartCustom(m_chart_id,ON_CHANGE_DATE,CElementBase::Id(),m_date.DateTime(),"");
   return(true);
  }
//+------------------------------------------------------------------+
//| Обработка выбора месяца в списке                                 |
//+------------------------------------------------------------------+
bool CCalendar::OnClickMonthList(const int id)
  {
//--- Выйти, если идентификаторы элементов не совпадают
   if(id!=CElementBase::Id())
      return(false);
//--- Получим выбранный месяц в списке
   int month=m_months.GetListViewPointer().SelectedItemIndex()+1;
   m_date.Mon(month);
//--- Корректировка выделенного дня по количеству дней в месяце
   CorrectingSelectedDay();
//--- Установить время на начало суток
   ResetTime();
//--- Отобразить изменения в таблице календаря
   UpdateCalendar();
//--- Обновляет элементы календаря
   UpdateElements();
//--- Отправим сообщение об этом
   ::EventChartCustom(m_chart_id,ON_CHANGE_DATE,CElementBase::Id(),m_date.DateTime(),"");
   return(true);
  }
//+------------------------------------------------------------------+
//| Обработка ввода значения в поле ввода лет                        |
//+------------------------------------------------------------------+
bool CCalendar::OnEndEnterYear(const string edited_object,const int id)
  {
//--- Выйти, если (1) идентификаторы не совпадают или (2) элемент заблокирован
   if(id!=CElementBase::Id() || CElementBase::IsLocked())
      return(false);
//--- Выйдем, если значение не изменилось
   string value=m_years.GetValue();
   if(m_date.year==(int)value)
     {
      //--- Обновляет поле ввода
      m_years.GetTextBoxPointer().Update(true);
      return(false);
     }
//--- Скорректируем значение в случае выхода за установленные ограничения
   if((int)value<m_years.MinValue())
      value=(string)int(m_years.MinValue());
   if((int)value>m_years.MaxValue())
      value=(string)int(m_years.MaxValue());
//--- Определим количество дней в текущем месяце
   string year  =value;
   string month =string(m_date.mon);
   string day   =string(1);
   m_temp_date.DateTime(::StringToTime(year+"."+month+"."+day));
//--- Если значение выделенного дня больше, чем количество дней в месяце,
//    установить текущее количество дней в месяце в качестве выделенного дня
   if(m_date.day>m_temp_date.DaysInMonth())
      m_date.day=m_temp_date.DaysInMonth();
//--- Установим дату в структуру
   m_date.DateTime(::StringToTime(year+"."+month+"."+string(m_date.day)));
//--- Отобразим изменения в таблице календаря
   UpdateCalendar();
//--- Обновляет элементы календаря
   UpdateElements();
//--- Отправим сообщение об этом
   ::EventChartCustom(m_chart_id,ON_CHANGE_DATE,CElementBase::Id(),m_date.DateTime(),"");
   return(true);
  }
//+------------------------------------------------------------------+
//| Обработка нажатия на кнопке перехода к следующему году           |
//+------------------------------------------------------------------+
bool CCalendar::OnClickYearInc(const string clicked_object,const int id,const int index)
  {
//--- Выйдем, если нажатие было не на этом элементе
   if(!m_years.GetIncButtonPointer().CheckElementName(clicked_object))
      return(false);
//--- Если список месяцев открыт, закроем его
   if(m_months.GetListViewPointer().IsVisible())
      m_months.ChangeComboBoxListState();
//--- Выйти, если идентификаторы элементов не совпадают
   if(id!=CElementBase::Id())
      return(false);
//--- Если год меньше максимального указанного, увеличить значение на один
   if(m_date.year<m_years.MaxValue())
      m_date.YearInc();
//--- Корректировка выделенного дня по количеству дней в месяце
   CorrectingSelectedDay();
//--- Отобразить изменения в таблице календаря
   UpdateCalendar();
//--- Обновляет элементы календаря
   UpdateElements();
//--- Отправим сообщение об этом
   ::EventChartCustom(m_chart_id,ON_CHANGE_DATE,CElementBase::Id(),m_date.DateTime(),"");
   return(true);
  }
//+------------------------------------------------------------------+
//| Обработка нажатия на кнопке перехода к предыдущему году          |
//+------------------------------------------------------------------+
bool CCalendar::OnClickYearDec(const string clicked_object,const int id,const int index)
  {
//--- Выйдем, если нажатие было не на этом элементе
   if(!m_years.GetDecButtonPointer().CheckElementName(clicked_object))
      return(false);
//--- Если список месяцев открыт, закроем его
   if(m_months.GetListViewPointer().IsVisible())
      m_months.ChangeComboBoxListState();
//--- Выйти, если идентификаторы элементов не совпадают
   if(id!=CElementBase::Id())
      return(false);
//--- Если год больше минимального указанного, уменьшить значение на один
   if(m_date.year>m_years.MinValue())
      m_date.YearDec();
//--- Корректировка выделенного дня по количеству дней в месяце  
   CorrectingSelectedDay();
//--- Отобразить изменения в таблице календаря
   UpdateCalendar();
//--- Обновляет элементы календаря
   UpdateElements();
//--- Отправим сообщение об этом
   ::EventChartCustom(m_chart_id,ON_CHANGE_DATE,CElementBase::Id(),m_date.DateTime(),"");
   return(true);
  }
//+------------------------------------------------------------------+
//| Обработка нажатия на дне месяца календаря                        |
//+------------------------------------------------------------------+
bool CCalendar::OnClickDayOfMonth(const string clicked_object,const int id,const int index)
  {
//--- Выйти, если (1) идентификаторы не совпадают или (2) элемент заблокирован
   if(id!=CElementBase::Id() || CElementBase::IsLocked())
      return(false);
//--- Определение разницы от первого пункта таблицы календаря до пункта первого дня текущего месяца
   OffsetFirstDayOfMonth();
//--- Пройдёмся в цикле по пунктам таблицы календаря
   int items_total=m_days.ButtonsTotal();
   for(int i=0; i<items_total; i++)
     {
      //--- Если дата текущего пункта меньше, чем установленный в системе минимум
      if(m_temp_date.DateTime()<datetime(D'01.01.1970'))
        {
         //--- Если это объект, на который нажали
         if(i==index)
            return(false);
         //--- Перейти к следующей дате
         m_temp_date.DayInc();
         continue;
        }
      //--- Если это объект, на который нажали
      if(i==index)
        {
         //--- Сохраним дату
         m_date.DateTime(m_temp_date.DateTime());
         //--- Отображение последних изменений в календаре
         UpdateCalendar();
         break;
        }
      //--- Перейти к следующей дате
      m_temp_date.DayInc();
      //--- Проверка выхода за установленный в системе максимум
      if(m_temp_date.year>m_years.MaxValue())
         return(false);
     }
//--- Обновляет элементы календаря
   UpdateElements();
//--- Отправим сообщение об этом
   ::EventChartCustom(m_chart_id,ON_CHANGE_DATE,CElementBase::Id(),m_date.DateTime(),"");
   return(true);
  }
//+------------------------------------------------------------------+
//| Обработка нажатия на кнопке перехода к текущей дате              |
//+------------------------------------------------------------------+
bool CCalendar::OnClickTodayButton(const string clicked_object,const int id,const int index)
  {
//--- Выйти, если чужое имя объекта
   if(::StringFind(clicked_object,m_button_today.NamePart(),0)<0)
      return(false);
//--- Выйти, если идентификаторы элементов не совпадают
   if(id!=CElementBase::Id())
      return(false);
//--- Если список месяцев открыт, закроем его
   if(m_months.GetListViewPointer().IsVisible())
      m_months.ChangeComboBoxListState();
//--- Установить текущую дату
   m_date.DateTime(::TimeLocal());
//--- Отображение последних изменений в календаре
   UpdateCalendar();
//--- Обновляет элементы календаря
   UpdateElements();
//--- Отправим сообщение об этом
   ::EventChartCustom(m_chart_id,ON_CHANGE_DATE,CElementBase::Id(),m_date.DateTime(),"");
   return(true);
  }
//+------------------------------------------------------------------+
//| Определение первого дня месяца                                   |
//+------------------------------------------------------------------+
void CCalendar::CorrectingSelectedDay(void)
  {
//--- Установить текущее количество дней в месяце, если значение выделенного дня больше
   if(m_date.day>m_date.DaysInMonth())
      m_date.day=m_date.DaysInMonth();
  }
//+------------------------------------------------------------------+
//| Определение разницы от первого пункта таблицы календаря          |
//| до пункта первого дня текущего месяца                            |
//+------------------------------------------------------------------+
int CCalendar::OffsetFirstDayOfMonth(void)
  {
//--- Получим дату первого дня выделенного года и месяца в виде строки
   string date=string(m_date.year)+"."+string(m_date.mon)+"."+string(1);
//--- Установим эту дату в структуру для расчётов
   m_temp_date.DateTime(::StringToTime(date));
//--- Если результат вычитания единицы от текущего номера дня недели больше либо равен нулю,
//    вернуть результат, иначе вернуть значение 6
   int diff=(m_temp_date.day_of_week-1>=0) ? m_temp_date.day_of_week-1 : 6;
//--- Запомним дату, которая приходится на первый пункт таблицы
   m_temp_date.DayDec(diff);
   return(diff);
  }
//+------------------------------------------------------------------+
//| Установка значений календаря                                     |
//+------------------------------------------------------------------+
void CCalendar::SetCalendar(void)
  {
//--- Определение разницы от первого пункта таблицы календаря до пункта первого дня текущего месяца
   int diff=OffsetFirstDayOfMonth();
//--- Пройдёмся в цикле по всем пунктам таблицы календаря
   int items_total=m_days.ButtonsTotal();
   for(int i=0; i<items_total; i++)
     {
      //--- Установка дня в текущий пункт таблицы
      m_days.GetButtonPointer(i).LabelText(string(m_temp_date.day));
      //--- Перейти к следующей дате
      m_temp_date.DayInc();
     }
  }
//+------------------------------------------------------------------+
//| Ускоренная промотка календаря                                    |
//+------------------------------------------------------------------+
void CCalendar::FastSwitching(void)
  {
//--- Выйдем, если нет фокуса на элементе
   if(!CElementBase::MouseFocus())
      return;
//--- Вернём счётчик к первоначальному значению, если кнопка мыши отжата
   if(!m_mouse.LeftButtonState())
      m_timer_counter=SPIN_DELAY_MSC;
//--- Если же кнопка мыши нажата
   else
     {
      //--- Увеличим счётчик на установленный интервал
      m_timer_counter+=TIMER_STEP_MSC;
      //--- Выйдем, если меньше нуля
      if(m_timer_counter<0)
         return;
      //--- Если левая стрелка нажата
      if(m_month_dec.MouseFocus())
        {
         //--- Если текущий год в календаре больше/равен минимального указанного
         if(m_date.year>=m_years.MinValue())
           {
            //--- Если текущий год в календаре уже равен минимальному указанному и
            //    текущий месяц "Январь"
            if(m_date.year==m_years.MinValue() && m_date.mon==1)
               return;
            //--- Перейти к следующему месяцу (в сторону уменьшения)
            m_date.MonDec();
            //--- Установить первое число месяца
            m_date.day=1;
           }
        }
      //--- Если правая стрелка нажата
      else if(m_month_inc.MouseFocus())
        {
         //--- Если текущий в календаре год меньше/равен максимального указанного
         if(m_date.year<=m_years.MaxValue())
           {
            //--- Если текущий год в календаре уже равен максимальному указанному и
            //    текущий месяц "Декабрь"
            if(m_date.year==m_years.MaxValue() && m_date.mon==12)
               return;
            //--- Перейти к следующему месяцу (в сторону увеличения)
            m_date.MonInc();
            //--- Установить первое число месяца
            m_date.day=1;
           }
        }
      //--- Если кнопка инкремента поля ввода лет нажата
      else if(m_years.GetIncButtonPointer().MouseFocus())
        {
         //--- Если меньше максимального указанного года,
         //    перейти к следующему году (в сторону увеличения)
         if(m_date.year<m_years.MaxValue())
            m_date.YearInc();
         else
            return;
        }
      //--- Если кнопка декремента поля ввода лет нажата
      else if(m_years.GetDecButtonPointer().MouseFocus())
        {
         //--- Если больше минимального указанного года,
         //    перейти к следующему году (в сторону уменьшения)
         if(m_date.year>m_years.MinValue())
            m_date.YearDec();
         else
            return;
        }
      else
         return;
      //--- Отображение последних изменений в календаре
      UpdateCalendar();
      //--- Обновляет элементы календаря
      UpdateElements();
      //--- Отправим сообщение об этом
      ::EventChartCustom(m_chart_id,ON_CHANGE_DATE,CElementBase::Id(),m_date.DateTime(),"");
     }
  }
//+------------------------------------------------------------------+
//| Подсветка текущего дня и выбранного пользователем дня            |
//+------------------------------------------------------------------+
void CCalendar::HighlightDate(void)
  {
//--- Определение разницы от первого пункта таблицы календаря до пункта первого дня текущего месяца
   OffsetFirstDayOfMonth();
//--- Пройдёмся в цикле по пунктам таблицы календаря
   int items_total=m_days.ButtonsTotal();
   for(int i=0; i<items_total; i++)
     {
      //--- Если месяц пункта совпадает с текущим месяцем и 
      //    день пункта совпадает с выделенным днём
      if(m_temp_date.mon==m_date.mon &&
         m_temp_date.day==m_date.day)
        {
         //--- Выделить эту кнопку
         m_days.SelectButton(i);
         //--- Перейти к следующему пункту таблицы
         m_temp_date.DayInc();
         continue;
        }
      //--- Если это текущая дата (сегодня)
      if(m_temp_date.year==m_today.year && 
         m_temp_date.mon==m_today.mon &&
         m_temp_date.day==m_today.day)
        {
         m_days.GetButtonPointer(i).LabelColor(m_today_color);
         m_days.GetButtonPointer(i).BorderColor(m_today_color);
         //--- Перейти к следующему пункту таблицы
         m_temp_date.DayInc();
         continue;
        }
      //---
      m_days.GetButtonPointer(i).BorderColor(m_back_color);
      m_days.GetButtonPointer(i).LabelColor((m_temp_date.mon==m_date.mon)? m_label_color : m_label_color_locked);
      //--- Перейти к следующему пункту таблицы
      m_temp_date.DayInc();
     }
  }
//+------------------------------------------------------------------+
//| Сброс времени на начало суток                                    |
//+------------------------------------------------------------------+
void CCalendar::ResetTime(void)
  {
   m_date.hour =0;
   m_date.min  =0;
   m_date.sec  =0;
  }
//+------------------------------------------------------------------+
//| Рисует элемент                                                   |
//+------------------------------------------------------------------+
void CCalendar::Draw(void)
  {
//--- Нарисовать фон
   CElement::DrawBackground();
//--- Нарисовать рамку
   CElement::DrawBorder();
//--- Рисует названия дней недели
   DrawDaysWeek();
//--- Рисует разделительную линию
   DrawSeparateLine();
  }
//+------------------------------------------------------------------+
//| Рисует названия дней недели                                      |
//+------------------------------------------------------------------+
void CCalendar::DrawDaysWeek(void)
  {
//--- Координаты
   int x=17,y=26;
//--- Размеры
   int x_size =21;
   int y_size =16;
//--- Счётчик дней недели (для массива объектов)
   int w=0;
//--- Установим объекты отображающие сокращённые названия дней недели
   for(int i=1; i<7; i++,w++)
     {
      //--- Расчёт координаты X
      x=(w>0)? x+x_size : x;
      //--- Свойства шрифта
      m_canvas.FontSet(CElement::Font(),-CElement::FontSize()*10,FW_NORMAL);
      //--- Вывести текст
      m_canvas.TextOut(x,y,m_date.ShortDayName(i),::ColorToARGB(clrBlack),TA_CENTER);
      //--- Если был сброс, выйти
      if(i==0)
         break;
      //--- Сброс, если прошли все дни недели
      if(i>=6)
         i=-1;
     }
  }
//+------------------------------------------------------------------+
//| Рисует разделительную линию                                      |
//+------------------------------------------------------------------+
void CCalendar::DrawSeparateLine(void)
  {
//--- Координаты
   int x1=7,x2=154,y=42;
//--- Нарисовать линию
   m_canvas.Line(x1,y,x2,y,::ColorToARGB(m_border_color));
  }
//+------------------------------------------------------------------+
