//+------------------------------------------------------------------+
//|                                                      MenuBar.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "..\Element.mqh"
#include "MenuItem.mqh"
#include "ContextMenu.mqh"
//+------------------------------------------------------------------+
//| Класс для создания главного меню                                 |
//+------------------------------------------------------------------+
class CMenuBar : public CElement
  {
private:
   //--- Объекты для создания пункта меню
   CMenuItem         m_items[];
   //--- Массив указателей на контекстные меню
   CContextMenu     *m_contextmenus[];
   //--- Состояние главного меню
   bool              m_menubar_state;
   //--- Индекс предыдущего активированного пункта
   int               m_prev_active_item_index;
   //---
public:
                     CMenuBar(void);
                    ~CMenuBar(void);
   //--- Методы для создания элемента
   bool              CreateMenuBar(const int x_gap,const int y_gap);
   //---
private:
   void              InitializeProperties(const int x_gap,const int y_gap);
   bool              CreateCanvas(void);
   bool              CreateItems(void);
   //---
public:
   //--- (1) Получение указателя указанного пункта меню, (2) получение указателя указанного контекстного меню
   CMenuItem        *GetItemPointer(const uint index);
   CContextMenu     *GetContextMenuPointer(const uint index);
   //--- Количество (1) пунктов и (2) контекстных меню, (3) состояние главного меню
   int               ItemsTotal(void)               const { return(::ArraySize(m_items));        }
   int               ContextMenusTotal(void)        const { return(::ArraySize(m_contextmenus)); }
   bool              State(void)                    const { return(m_menubar_state);             }
   void              State(const bool state);
   //--- Добавляет пункт меню с указанными свойствами до создания главного меню
   void              AddItem(const int width,const string text);
   //--- Присоединяет переданное контекстное меню к указанному пункту главного меню
   void              AddContextMenuPointer(const uint index,CContextMenu &object);
   //---
public:
   //--- Обработчик событий графика
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Удаление
   virtual void      Delete(void);
   //--- Рисует элемент
   virtual void      Draw(void);
   //---
private:
   //--- Обработка нажатия на пункте меню
   bool              OnClickMenuItem(const int id,const int index);
   //--- Возвращает активный пункт главного меню
   int               ActiveItemIndex(void);
   //--- Переключает контекстные меню главного меню, наведением курсора
   void              SwitchContextMenuByFocus(void);

   //--- Изменить ширину по правому краю окна
   virtual void      ChangeWidthByRightWindowSide(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CMenuBar::CMenuBar(void) : m_menubar_state(false),
                           m_prev_active_item_index(WRONG_VALUE)
  {
//--- Сохраним имя класса элемента в базовом классе
   CElementBase::ClassName(CLASS_NAME);
//--- Центрировать текст в пунктах меню
   CElement::IsCenterText(true);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CMenuBar::~CMenuBar(void)
  {
  }
//+------------------------------------------------------------------+
//| Обработчик событий                                               |
//+------------------------------------------------------------------+
void CMenuBar::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Обработка события смены фокуса на кнопках меню
   if(id==CHARTEVENT_CUSTOM+ON_MOUSE_FOCUS)
     {
      //--- Выйти, если (2) главное меню не активировано или (2) идентификаторы не совпадают
      if(!m_menubar_state || lparam!=CElementBase::Id())
         return;
      //--- Переключить контекстное меню по активированному пункту главного меню
      SwitchContextMenuByFocus();
      return;
     }
//--- Обработка события нажатия левой кнопки мыши на пункте главного меню
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
     {
      if(OnClickMenuItem((uint)lparam,(uint)dparam))
         return;
      //---
      return;
     }
  }
//+------------------------------------------------------------------+
//| Создаёт главное меню                                             |
//+------------------------------------------------------------------+
bool CMenuBar::CreateMenuBar(const int x_gap,const int y_gap)
  {
//--- Выйти, если нет указателя на главный элемент
   if(!CElement::CheckMainPointer())
      return(false);
//--- Инициализация свойств
   InitializeProperties(x_gap,y_gap);
//--- Создание элемента
   if(!CreateCanvas())
      return(false);
   if(!CreateItems())
      return(false);
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Инициализация свойств                                            |
//+------------------------------------------------------------------+
void CMenuBar::InitializeProperties(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 || m_auto_xresize_mode)? m_main.X2()-m_x-m_auto_xresize_right_offset : m_x_size;
   m_y_size   =(m_y_size<1)? 22 : m_y_size;
//--- Свойства по умолчанию
   m_back_color           =(m_back_color!=clrNONE)? m_back_color : C'225,225,225';
   m_back_color_hover     =(m_back_color_hover!=clrNONE)? m_back_color_hover : C'51,153,255';
   m_back_color_pressed   =(m_back_color_pressed!=clrNONE)? m_back_color_pressed : m_back_color_hover;
   m_border_color         =(m_border_color!=clrNONE)? m_border_color : m_back_color;
   m_border_color_hover   =(m_border_color_hover!=clrNONE)? m_border_color_hover : m_back_color;
   m_border_color_pressed =(m_border_color_pressed!=clrNONE)? m_border_color_pressed : m_back_color;
   m_label_y_gap          =(m_label_y_gap!=WRONG_VALUE)? m_label_y_gap : 3;
   m_label_color          =(m_label_color!=clrNONE)? m_label_color : clrBlack;
   m_label_color_hover    =(m_label_color_hover!=clrNONE)? m_label_color_hover : clrWhite;
   m_label_color_pressed  =(m_label_color_pressed!=clrNONE)? m_label_color_pressed : clrWhite;
//--- Отступы от крайней точки
   CElementBase::XGap(x_gap);
   CElementBase::YGap(y_gap);
  }
//+------------------------------------------------------------------+
//| Создаёт объект для рисования                                     |
//+------------------------------------------------------------------+
bool CMenuBar::CreateCanvas(void)
  {
//--- Формирование имени объекта
   string name=CElementBase::ElementName("menubar");
//--- Создание объекта
   if(!CElement::CreateCanvas(name,m_x,m_y,m_x_size,m_y_size))
      return(false);
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Создаёт список пунктов меню                                      |
//+------------------------------------------------------------------+
bool CMenuBar::CreateItems(void)
  {
//--- Координаты
   int x=0,y=0;
//---
   int items_total=ItemsTotal();
   for(int i=0; i<items_total; i++)
     {
      //--- Расчёт координаты X
      x=(i>0)? x+m_items[i-1].XSize() : x;
      //--- Сохраним указатель на главный элемент
      m_items[i].MainPointer(this);
      //--- Установим свойства перед созданием
      m_items[i].Index(i);
      m_items[i].NamePart("menu_item");
      m_items[i].TwoState(true);
      m_items[i].TypeMenuItem(MI_HAS_CONTEXT_MENU);
      m_items[i].ShowRightArrow(false);
      m_items[i].XSize(m_items[i].XSize());
      m_items[i].YSize(m_y_size);
      m_items[i].BackColor(m_back_color);
      m_items[i].BackColorHover(m_back_color_hover);
      m_items[i].BackColorPressed(m_back_color_pressed);
      m_items[i].BorderColor(m_border_color);
      m_items[i].BorderColorHover(m_border_color_hover);
      m_items[i].BorderColorPressed(m_border_color_pressed);
      m_items[i].IconXGap(3);
      m_items[i].IconYGap(4);
      m_items[i].LabelXGap(m_label_x_gap);
      m_items[i].LabelYGap(m_label_y_gap);
      m_items[i].LabelColor(m_label_color);
      m_items[i].LabelColorHover(m_label_color_hover);
      m_items[i].LabelColorPressed(m_label_color_pressed);
      m_items[i].IsCenterText(CElement::IsCenterText());
      //--- Создание пункта меню
      if(!m_items[i].CreateMenuItem(m_items[i].LabelText(),x,y))
         return(false);
      //--- Добавить элемент в массив
      CElement::AddToArray(m_items[i]);
     }
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Установка состояния главного меню                                |
//+------------------------------------------------------------------+
void CMenuBar::State(const bool state)
  {
   if(state)
      m_menubar_state=true;
   else
     {
      m_menubar_state=false;
      //--- Пройтись по всем пунктам главного меню для установки статуса отключенных контекстных меню
      int items_total=ItemsTotal();
      for(int i=0; i<items_total; i++)
        {
         m_items[i].IsPressed(false);
         m_items[i].Update(true);
        }
     }
  }
//+------------------------------------------------------------------+
//| Возвращает указатель пункта меню по индексу                      |
//+------------------------------------------------------------------+
CMenuItem *CMenuBar::GetItemPointer(const uint index)
  {
   uint array_size=::ArraySize(m_items);
//--- Если нет ни одного пункта в главном меню, сообщить об этом
   if(array_size<1)
     {
      ::Print(__FUNCTION__," > Вызов этого метода нужно осуществлять, "
              "когда в главном меню есть хотя бы один пункт!");
     }
//--- Корректировка в случае выхода из диапазона
   uint i=(index>=array_size)? array_size-1 : index;
//--- Вернуть указатель
   return(::GetPointer(m_items[i]));
  }
//+------------------------------------------------------------------+
//| Возвращает указатель контекстного меню по индексу                |
//+------------------------------------------------------------------+
CContextMenu *CMenuBar::GetContextMenuPointer(const uint index)
  {
   uint array_size=::ArraySize(m_contextmenus);
//--- Если нет ни одного пункта в главном меню, сообщить об этом
   if(array_size<1)
     {
      ::Print(__FUNCTION__," > Вызов этого метода нужно осуществлять, "
              "когда в главном меню есть хотя бы один пункт!");
     }
//--- Корректировка в случае выхода из диапазона
   uint i=(index>=array_size)? array_size-1 : index;
//--- Вернуть указатель
   return(::GetPointer(m_contextmenus[i]));
  }
//+------------------------------------------------------------------+
//| Добавляет пункт меню                                             |
//+------------------------------------------------------------------+
void CMenuBar::AddItem(const int width,const string text)
  {
//--- Увеличим размер массивов на один элемент  
   int array_size=::ArraySize(m_items);
   ::ArrayResize(m_items,array_size+1);
   ::ArrayResize(m_contextmenus,array_size+1);
//--- Сохраним значения переданных параметров
   m_items[array_size].XSize(width);
   m_items[array_size].LabelText(text);
  }
//+------------------------------------------------------------------+
//| Добавляет указатель контекстного меню                            |
//+------------------------------------------------------------------+
void CMenuBar::AddContextMenuPointer(const uint index,CContextMenu &object)
  {
//--- Проверка на выход из диапазона
   uint size=::ArraySize(m_contextmenus);
   if(size<1 || index>=size)
      return;
//--- Сохранить указатель
   m_contextmenus[index]=::GetPointer(object);
  }
//+------------------------------------------------------------------+
//| Удаление                                                         |
//+------------------------------------------------------------------+
void CMenuBar::Delete(void)
  {
//--- Удаление объектов  
   CElement::Delete();
//--- Освобождение массивов элемента
   ::ArrayFree(m_items);
   ::ArrayFree(m_contextmenus);
  }
//+------------------------------------------------------------------+
//| Нажатие на пункте главного меню                                  |
//+------------------------------------------------------------------+
bool CMenuBar::OnClickMenuItem(const int id,const int index)
  {
//--- Выйти, если (1) идентификаторы не совпадают или (2) элемент заблокирован
   if(id!=CElementBase::Id() || CElementBase::IsLocked())
      return(false);
//--- Если есть указатель на контекстное меню
   if(::CheckPointer(m_contextmenus[index])!=POINTER_INVALID)
     {
      //--- Состояние главного меню зависит от видимости контекстного меню
      m_menubar_state=(m_contextmenus[index].IsVisible())? false : true;
      //--- Определим выделенный пункт
      m_prev_active_item_index=(m_menubar_state)? index : WRONG_VALUE;
     }
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Возвращает индекс активированного пункта меню                    |
//+------------------------------------------------------------------+
int CMenuBar::ActiveItemIndex(void)
  {
   int active_item_index=WRONG_VALUE;
//---
   int items_total=ItemsTotal();
   for(int i=0; i<items_total; i++)
     {
      //--- Если пункт в фокусе
      if(m_items[i].MouseFocus())
        {
         //--- Запомним индекс и остановим цикл
         active_item_index=i;
         break;
        }
     }
//---
   return(active_item_index);
  }
//+------------------------------------------------------------------+
//| Переключает контекстные меню главного меню, наведением курсора   |
//+------------------------------------------------------------------+
void CMenuBar::SwitchContextMenuByFocus(void)
  {
//--- Получим индекс активиронного пункта главного меню
   int active_item_index=ActiveItemIndex();
//--- Выйти, если (1) меню не активировано или (2) это тот же пункт меню
   if(active_item_index==WRONG_VALUE || active_item_index==m_prev_active_item_index)
      return;
//--- Перейти к следующему, если в этом пункте нет контекстного меню
   if(::CheckPointer(m_contextmenus[active_item_index])!=POINTER_INVALID)
     {
      //--- Сделать контекстное меню видимым
      m_contextmenus[active_item_index].Show();
      m_items[active_item_index].IsPressed(true);
     }
//--- Получим указатель на предыдущий выделенный пункт
   CContextMenu *cm=m_contextmenus[m_prev_active_item_index];
//--- Скрыть контекстные меню, которые открыты из других контекстных меню.
//    Пройдёмся в цикле по пунктам текущего контекстного меню, чтобы выяснить, есть ли такие.
   int cm_items_total=cm.ItemsTotal();
   for(int c=0; c<cm_items_total; c++)
     {
      CMenuItem *mi=cm.GetItemPointer(c);
      //--- Перейти к следующему, если указатель на пункт некорректный
      if(::CheckPointer(mi)==POINTER_INVALID)
         continue;
      //--- Перейти к следующему, если этот пункт не содержит в себе контекстное меню
      if(mi.TypeMenuItem()!=MI_HAS_CONTEXT_MENU)
         continue;
      //--- Если контекстное меню активировано
      if(mi.IsPressed())
        {
         //--- Отправить сигнал на закрытие всех контекстных меню, которые открыты из этого
         ::EventChartCustom(m_chart_id,ON_HIDE_BACK_CONTEXTMENUS,CElementBase::Id(),0,"");
         break;
        }
     }
//--- Скрыть контекстное меню главного меню
   m_contextmenus[m_prev_active_item_index].Hide();
   m_items[m_prev_active_item_index].IsPressed(false);
   m_items[m_prev_active_item_index].Update(true);
//--- Запомним индекс текущего активированного меню
   m_prev_active_item_index=active_item_index;
//--- Отправим сообщение на определение доступных элементов
   ::EventChartCustom(m_chart_id,ON_SET_AVAILABLE,CElementBase::Id(),0,"");
//--- Отправим сообщение об изменении в графическом интерфейсе
   ::EventChartCustom(m_chart_id,ON_CHANGE_GUI,CElementBase::Id(),0.0,"");
  }
//+------------------------------------------------------------------+
//| Изменить ширину по правому краю формы                            |
//+------------------------------------------------------------------+
void CMenuBar::ChangeWidthByRightWindowSide(void)
  {
//--- Выйти, если включен режим фиксации к правому краю формы
   if(m_anchor_right_window_side)
      return;
//--- Размеры
   int x_size=0;
//--- Рассчитать и установить новый размер фону элемента
   x_size=m_main.X2()-m_canvas.X()-m_auto_xresize_right_offset;
   CElementBase::XSize(x_size);
   m_canvas.XSize(x_size);
   m_canvas.Resize(x_size,m_y_size);
//--- Перерисовать элемент
   Draw();
//--- Обновить положение объектов
   Moving();
  }
//+------------------------------------------------------------------+
//| Рисует элемент                                                   |
//+------------------------------------------------------------------+
void CMenuBar::Draw(void)
  {
//--- Нарисовать фон
   CElement::DrawBackground();
  }
//+------------------------------------------------------------------+
