//+------------------------------------------------------------------+
//|                                                     ComboBox.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "..\Element.mqh"
#include "ListView.mqh"
//+------------------------------------------------------------------+
//| Класс для создания комбинированного списка                       |
//+------------------------------------------------------------------+
class CComboBox : public CElement
  {
private:
   //--- Экземпляры для создания элемента
   CButton           m_button;
   CListView         m_listview;
   //--- Режим элемента с чек-боксом
   bool              m_checkbox_mode;
   //---
public:
                     CComboBox(void);
                    ~CComboBox(void);
   //--- Методы для создания комбо-бокса
   bool              CreateComboBox(const string text,const int x_gap,const int y_gap);
   //---
private:
   void              InitializeProperties(const string text,const int x_gap,const int y_gap);
   bool              CreateCanvas(void);
   bool              CreateButton(void);
   bool              CreateList(void);
   //---
public:
   //--- Возвращает указатели на (1) кнопку, (2) список и (3) полосу прокрутки
   CButton           *GetButtonPointer(void)                 { return(::GetPointer(m_button));         }
   CListView         *GetListViewPointer(void)               { return(::GetPointer(m_listview));       }
   CScrollV          *GetScrollVPointer(void)                { return(m_listview.GetScrollVPointer()); }
   //--- (1) Размер списка (количество пунктов) (2) установка режима элемента с чек-боксом
   void              ItemsTotal(const int items_total)      { m_listview.ListSize(items_total);       }
   void              CheckBoxMode(const bool state)         { m_checkbox_mode=state;                  }
   //--- Состояние элемента (нажат/отжат)
   bool              IsPressed(void) const { return(m_is_pressed); }
   void              IsPressed(const bool state);
   //--- Сохраняет переданное значение в списке по указанному индексу
   void              SetValue(const int item_index,const string item_text);
   //--- Возвращает выбранное значение в списке
   string            GetValue(void);
   //--- Выделение пункта по указанному индексу
   void              SelectItem(const int item_index);
   //--- Изменяет текущее состояние комбо-бокса на противоположное
   void              ChangeComboBoxListState(void);
   //---
public:
   //--- Обработчик событий графика
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Блокировка
   virtual void      IsLocked(const bool state);
   //--- Управление видимостью
   virtual void      Hide(void);
   //--- Рисует элемент
   virtual void      Draw(void);
   //---
private:
   //--- Обработка нажатия на элементе
   bool              OnClickElement(const string pressed_object);
   //--- Обработка нажатия на кнопку
   bool              OnClickButton(const string pressed_object,const int id,const int index);
   //--- Обработка нажатия на пункте списка
   bool              OnClickListItem(const int id);

   //--- Проверка нажатой левой кнопки мыши над кнопкой комбо-бокса
   void              CheckPressedOverButton(void);
   //--- Рисует картинку
   virtual void      DrawImage(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CComboBox::CComboBox(void) : m_checkbox_mode(false)
  {
//--- Сохраним имя класса элемента в базовом классе
   CElementBase::ClassName(CLASS_NAME);
//--- Режим выпадающего списка
   m_listview.IsDropdown(true);
   m_listview.GetScrollVPointer().IsDropdown(true);
   m_listview.GetScrollVPointer().GetIncButtonPointer().IsDropdown(true);
   m_listview.GetScrollVPointer().GetDecButtonPointer().IsDropdown(true);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CComboBox::~CComboBox(void)
  {
  }
//+------------------------------------------------------------------+
//| Обработчик событий                                               |
//+------------------------------------------------------------------+
void CComboBox::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Обработка события перемещения курсора
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- Проверка нажатой левой кнопки мыши над кнопкой
      CheckPressedOverButton();
      //--- Перерисовать элемент
      if(CheckCrossingBorder())
         Update(true);
      //---
      return;
     }
//--- Обработка события нажатия на пункте списка
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_LIST_ITEM)
     {
      if(!OnClickListItem((int)lparam))
         return;
      //---
      return;
     }
//--- Обработка нажатия на элементе
   if(id==CHARTEVENT_OBJECT_CLICK)
     {
      if(OnClickElement(sparam))
         return;
      //---
      return;
     }
//--- Обработка события нажатия на кнопке
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
     {
      if(OnClickButton(sparam,(uint)lparam,(uint)dparam))
         return;
      //---
      return;
     }
//--- Обработка события изменения свойств графика
   if(id==CHARTEVENT_CHART_CHANGE)
     {
      //--- Выйти, если (1) элемент заблокирован или (2) кнопка отжата
      if(CElementBase::IsLocked() || !m_button.IsPressed())
         return;
      //--- Отжать кнопку
      m_button.IsPressed(false);
      //--- Изменить состояние списка
      ChangeComboBoxListState();
      return;
     }
  }
//+------------------------------------------------------------------+
//| Создаёт группу объектов типа комбо-бокс                          |
//+------------------------------------------------------------------+
bool CComboBox::CreateComboBox(const string text,const int x_gap,const int y_gap)
  {
//--- Выйти, если нет указателя на главный элемент
   if(!CElement::CheckMainPointer())
      return(false);
//--- Инициализация свойств
   InitializeProperties(text,x_gap,y_gap);
//--- Создание элемента
   if(!CreateCanvas())
      return(false);
   if(!CreateButton())
      return(false);
   if(!CreateList())
      return(false);
//--- Установить текст в кнопку
   m_button.LabelText(m_listview.SelectedItemText());
   return(true);
  }
//+------------------------------------------------------------------+
//| Инициализация свойств                                            |
//+------------------------------------------------------------------+
void CComboBox::InitializeProperties(const string text,const int x_gap,const int y_gap)
  {
   m_x          =CElement::CalculateX(x_gap);
   m_y          =CElement::CalculateY(y_gap);
   m_x_size     =(m_x_size<1)? 50 : m_x_size;
   m_y_size     =(m_y_size<1)? 20 : m_y_size;
   m_label_text =text;
//--- Цвет фона и отступы для картинки/чек-бокса
   m_back_color=(m_back_color!=clrNONE)? m_back_color : m_main.BackColor();
//--- Отступы и цвет текстовой метки
   m_icon_y_gap          =(m_icon_y_gap!=WRONG_VALUE)? m_icon_y_gap : 4;
   m_label_x_gap         =(m_label_x_gap!=WRONG_VALUE)? m_label_x_gap : (m_checkbox_mode)? 20 : 0;
   m_label_y_gap         =(m_label_y_gap!=WRONG_VALUE)? m_label_y_gap : 0;
   m_label_color         =(m_label_color!=clrNONE)? m_label_color : clrBlack;
   m_label_color_hover   =(m_label_color_hover!=clrNONE)? m_label_color_hover : C'0,120,215';
   m_label_color_locked  =(m_label_color_locked!=clrNONE)? m_label_color_locked : clrSilver;
   m_label_color_pressed =(m_label_color_pressed!=clrNONE)? m_label_color_pressed : clrBlack;
//--- Отступы от крайней точки
   CElementBase::XGap(x_gap);
   CElementBase::YGap(y_gap);
  }
//+------------------------------------------------------------------+
//| Создаёт объект для рисования                                     |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Controls\\checkbox_off.bmp"
#resource "\\Images\\EasyAndFastGUI\\Controls\\checkbox_off_locked.bmp"
#resource "\\Images\\EasyAndFastGUI\\Controls\\checkbox_on_g.bmp"
#resource "\\Images\\EasyAndFastGUI\\Controls\\checkbox_on_locked.bmp"
//---
bool CComboBox::CreateCanvas(void)
  {
//--- Формирование имени объекта
   string name=CElementBase::ElementName("combobox");
//--- Если нужен элемент с чек-боксом
   if(m_checkbox_mode)
     {
      IconFile("Images\\EasyAndFastGUI\\Controls\\checkbox_off.bmp");
      IconFileLocked("Images\\EasyAndFastGUI\\Controls\\checkbox_off_locked.bmp");
      IconFilePressed("Images\\EasyAndFastGUI\\Controls\\checkbox_on_g.bmp");
      IconFilePressedLocked("Images\\EasyAndFastGUI\\Controls\\checkbox_on_locked.bmp");
     }
//--- Создание объекта
   if(!CElement::CreateCanvas(name,m_x,m_y,m_x_size,m_y_size))
      return(false);
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Создаёт кнопку                                                   |
//+------------------------------------------------------------------+
#resource "\\Images\\EasyAndFastGUI\\Controls\\up_thin_black.bmp"
#resource "\\Images\\EasyAndFastGUI\\Controls\\down_thin_black.bmp"
//---
bool CComboBox::CreateButton(void)
  {
//--- Сохраним указатель на родительский элемент
   m_button.MainPointer(this);
//--- Размеры
   int x_size=(m_button.XSize()<1)? 80 : m_button.XSize();
//--- Координаты
   int x =(m_button.XGap()<1)? x_size : m_button.XGap();
   int y =0;
//--- Отступы для картинки
   int icon_x_gap =(m_button.IconXGap()<1)? x_size-18 : m_button.IconXGap();
   int icon_y_gap =(m_button.IconYGap()<1)? 2 : m_button.IconYGap();
//--- Отступы для текста
   int label_x_gap =(m_button.LabelXGap()<1)? 7 : m_button.LabelXGap();
   int label_y_gap =(m_button.LabelYGap()<1)? 4 : m_button.LabelYGap();
//--- Свойства
   m_button.NamePart("combobox_button");
   m_button.Index(0);
   m_button.TwoState(true);
   m_button.XSize(x_size);
   m_button.YSize(m_y_size);
   m_button.IconXGap(icon_x_gap);
   m_button.IconYGap(icon_y_gap);
   m_button.LabelXGap(label_x_gap);
   m_button.LabelYGap(label_y_gap);
   m_button.IsDropdown(CElementBase::IsDropdown());
   m_button.IconFile("Images\\EasyAndFastGUI\\Controls\\down_thin_black.bmp");
   m_button.IconFileLocked("Images\\EasyAndFastGUI\\Controls\\down_thin_black.bmp");
   m_button.IconFilePressed("Images\\EasyAndFastGUI\\Controls\\up_thin_black.bmp");
   m_button.IconFilePressedLocked("Images\\EasyAndFastGUI\\Controls\\up_thin_black.bmp");
//--- Создадим элемент управления
   if(!m_button.CreateButton("",x,y))
      return(false);
//--- Добавить элемент в массив
   CElement::AddToArray(m_button);
   return(true);
  }
//+------------------------------------------------------------------+
//| Создаёт список                                                   |
//+------------------------------------------------------------------+
bool CComboBox::CreateList(void)
  {
//--- Сохраним указатель на главный элемент
   m_listview.MainPointer(this);
//--- Координаты
   int x =m_button.XGap();
   int y =m_button.YSize();
//--- Размеры
   int x_size =(m_listview.XSize()<1)? m_button.XSize() : m_listview.XSize();
   int y_size =(m_listview.YSize()<1)? 93 : m_listview.YSize();
//--- Свойства
   m_listview.XSize(x_size);
   m_listview.YSize(y_size);
   m_listview.AnchorRightWindowSide(m_button.AnchorRightWindowSide());
//--- Создадим элемент управления
   if(!m_listview.CreateListView(x,y))
      return(false);
//--- Скрыть список
   m_listview.Hide();
//--- Добавить элемент в массив
   CElement::AddToArray(m_listview);
   return(true);
  }
//+------------------------------------------------------------------+
//| Установка состояния элемента (нажат/отжат)                       |
//+------------------------------------------------------------------+
void CComboBox::IsPressed(const bool state)
  {
//--- Выйти, если (1) элемент заблокирован или (2) элемент уже в таком состоянии
   if(CElementBase::IsLocked() || m_is_pressed==state)
      return;
//--- Установка состояния
   m_is_pressed=state;
//--- Установить соответствующую картинку
   CElement::ChangeImage(0,!m_is_pressed? 0 : 2);
  }
//+------------------------------------------------------------------+
//| Сохраняет переданное значение в списке по указанному индексу     |
//+------------------------------------------------------------------+
void CComboBox::SetValue(const int item_index,const string item_text)
  {
   m_listview.SetValue(item_index,item_text);
  }
//+------------------------------------------------------------------+
//| Возвращает выбранное в списке значение                           |
//+------------------------------------------------------------------+
string CComboBox::GetValue(void)
  {
   return(m_listview.SelectedItemText());
  }
//+------------------------------------------------------------------+
//| Выделение пункта по указанному индексу                           |
//+------------------------------------------------------------------+
void CComboBox::SelectItem(const int item_index)
  {
//--- Выбрать пункт в списке
   m_listview.SelectItem(item_index);
//--- Установить текст в кнопку
   m_button.LabelText(m_listview.SelectedItemText());
  }
//+------------------------------------------------------------------+
//| Блокировка                                                       |
//+------------------------------------------------------------------+
void CComboBox::IsLocked(const bool state)
  {
   CElement::IsLocked(state);
//--- Установить соответствующую картинку
   CElement::ChangeImage(0,(m_is_locked)? !m_is_pressed? 1 : 3 : !m_is_pressed? 0 : 2);
  }
//+------------------------------------------------------------------+
//| Скрытие                                                          |
//+------------------------------------------------------------------+
void CComboBox::Hide(void)
  {
   CElement::Hide();
//--- Отжать кнопку
   m_button.IsPressed(false);
  }
//+------------------------------------------------------------------+
//| Изменяет текущее состояние комбо-бокса на противоположное        |
//+------------------------------------------------------------------+
void CComboBox::ChangeComboBoxListState(void)
  {
//--- Если кнопка нажата
   if(m_button.IsPressed())
     {
      //--- Показать список
      m_listview.Show();
      //--- Отправим сообщение на определение доступных элементов
      ::EventChartCustom(m_chart_id,ON_SET_AVAILABLE,CElementBase::Id(),0,"");
      //--- Отправим сообщение об изменении в графическом интерфейсе
      ::EventChartCustom(m_chart_id,ON_CHANGE_GUI,CElementBase::Id(),0,"");
     }
   else
     {
      //--- Скрыть список
      m_listview.Hide();
      //--- Отправим сообщение на восстановление элементов
      ::EventChartCustom(m_chart_id,ON_SET_AVAILABLE,CElementBase::Id(),1,"");
      //--- Отправим сообщение об изменении в графическом интерфейсе
      ::EventChartCustom(m_chart_id,ON_CHANGE_GUI,CElementBase::Id(),0,"");
     }
  }
//+------------------------------------------------------------------+
//| Нажатие на чек-боксе                                             |
//+------------------------------------------------------------------+
bool CComboBox::OnClickElement(const string pressed_object)
  {
//--- Выйдем, если (1) элемент заблокирован или (2) чужое имя объекта
   if(CElementBase::IsLocked() || m_canvas.ChartObjectName()!=pressed_object)
      return(false);
//--- Если чек-бокс включен
   if(m_checkbox_mode)
     {
      //--- Переключить на противоположный режим
      IsPressed(!(IsPressed()));
      //--- Отправим сообщение об этом
      ::EventChartCustom(m_chart_id,ON_CLICK_CHECKBOX,CElementBase::Id(),CElementBase::Index(),"");
     }
//--- Нарисовать элемент
   CElement::Update(true);
   return(true);
  }
//+------------------------------------------------------------------+
//| Нажатие на кнопку комбо-бокса                                    |
//+------------------------------------------------------------------+
bool CComboBox::OnClickButton(const string pressed_object,const int id,const int index)
  {
//--- Выйти, если нажатие было на другом элементе
   if(!m_button.CheckElementName(pressed_object))
      return(false);
//--- Выйти, если значения не совпадают
   if(id!=m_button.Id() || index!=m_button.Index())
      return(false);
//--- Изменить состояние списка
   ChangeComboBoxListState();
//--- Отправим сообщение об этом
   ::EventChartCustom(m_chart_id,ON_CLICK_COMBOBOX_BUTTON,CElementBase::Id(),0,"");
   return(true);
  }
//+------------------------------------------------------------------+
//| Обработка нажатия на пункте списка                               |
//+------------------------------------------------------------------+
bool CComboBox::OnClickListItem(const int id)
  {
//--- Выйти, если значения не совпадают
   if(id!=CElementBase::Id())
      return(false);
//--- Отжать кнопку
   m_button.IsPressed(false);
//--- Установить текст в кнопку
   m_button.LabelText(m_listview.SelectedItemText());
   m_button.Update(true);
//--- Изменить состояние списка
   ChangeComboBoxListState();
//--- Отправим сообщение об этом
   ::EventChartCustom(m_chart_id,ON_CLICK_COMBOBOX_ITEM,CElementBase::Id(),0,m_label_text);
   return(true);
  }
//+------------------------------------------------------------------+
//| Проверка нажатой левой кнопки мыши над кнопкой                   |
//+------------------------------------------------------------------+
void CComboBox::CheckPressedOverButton(void)
  {
//--- Выйти, если кнопка уже отжата
   if(!m_button.IsPressed())
      return;
//--- Выйти, если (1) элемент заблокирован или (2) левая кнопка мыши отжата
   if(CElementBase::IsLocked() || !m_mouse.LeftButtonState())
      return;
//--- Если фокуса нет
   if(!CElementBase::MouseFocus() && !m_button.MouseFocus())
     {
      //--- Выйти, если фокус на списке или полоса прокрутки списка в действии
      if(m_listview.MouseFocus() || m_listview.ScrollState())
         return;
      //--- Скрыть список
      m_listview.Hide();
      m_button.IsPressed(false);
      m_button.Update(true);
      //--- Нарисовать элемент
      Update(true);
      //--- Отправим сообщение на восстановление элементов
      ::EventChartCustom(m_chart_id,ON_SET_AVAILABLE,CElementBase::Id(),1,"");
      //--- Отправим сообщение об изменении в графическом интерфейсе
      ::EventChartCustom(m_chart_id,ON_CHANGE_GUI,CElementBase::Id(),0.0,"");
     }
  }
//+------------------------------------------------------------------+
//| Рисует элемент                                                   |
//+------------------------------------------------------------------+
void CComboBox::Draw(void)
  {
//--- Нарисовать фон
   CElement::DrawBackground();
//--- Нарисовать картинку
   CElement::DrawImage();
//--- Нарисовать текст
   CElement::DrawText();
  }
//+------------------------------------------------------------------+
//| Рисует картинку                                                  |
//+------------------------------------------------------------------+
void CComboBox::DrawImage(void)
  {
//--- Выйти, если (1) чек-бокс не нужен или (2) картинка не определена
   if(!m_checkbox_mode || CElement::IconFile()=="")
      return;
//--- Определим индекс
   uint image_index=(m_is_pressed)? 2 : 0;
//--- Если элемент не заблокирован
   if(!CElementBase::IsLocked())
     {
      if(CElementBase::MouseFocus())
         image_index=(m_is_pressed)? 2 : 0;
     }
   else
      image_index=(m_is_pressed)? 3 : 1;
//--- Установить соответствующую картинку
   CElement::ChangeImage(0,image_index);
//--- Рисуем картинку
   CElement::DrawImage();
  }
//+------------------------------------------------------------------+
