//+------------------------------------------------------------------+
//|                                                   RadioGroup.mqh |
//|                   Copyright 2009-2016, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "WndClient.mqh"
#include "RadioButton.mqh"
#include <Arrays\ArrayString.mqh>
#include <Arrays\ArrayLong.mqh>
//+------------------------------------------------------------------+
//| Class CRadioGroup                                                |
//| Usage: view and edit radio buttons                               |
//+------------------------------------------------------------------+
class CRadioGroup : public CWndClient
  {
private:
   //--- dependent controls
   CRadioButton      m_rows[];              // array of the row objects
   //--- set up
   int               m_offset;              // index of first visible row in array of rows
   int               m_total_view;          // number of visible rows
   int               m_item_height;         // height of visible row
   //--- data
   CArrayString      m_strings;             // array of rows
   CArrayLong        m_values;              // array of values
   int               m_current;             // index of current row in array of rows

public:
                     CRadioGroup(void);
                    ~CRadioGroup(void);
   //--- create
   virtual bool      Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2);
   virtual void      Destroy(const int reason=0);
   //--- chart event handler
   virtual bool      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- fill
   virtual bool      AddItem(const string item,const long value=0);
   //--- data
   long              Value(void) const { return(m_values.At(m_current)); }
   bool              Value(const long value);
   bool              ValueCheck(long value) const;
   //--- state
   virtual bool      Show(void);
   virtual void      RedrawButtonStates(void);
   //--- methods for working with files
   virtual bool      Save(const int file_handle);
   virtual bool      Load(const int file_handle);

protected:
   //--- create dependent controls
   bool              CreateButton(const int index);
   //--- handlers of the dependent controls events
   virtual bool      OnVScrollShow(void);
   virtual bool      OnVScrollHide(void);
   virtual bool      OnScrollLineDown(void);
   virtual bool      OnScrollLineUp(void);
   virtual bool      OnChangeItem(const int index);
   //--- redraw
   bool              Redraw(void);
   bool              RowState(const int index,const bool select);
   void              Select(const int index);
  };
//+------------------------------------------------------------------+
//| Common handler of chart events                                   |
//+------------------------------------------------------------------+
EVENT_MAP_BEGIN(CRadioGroup)
   ON_INDEXED_EVENT(ON_CHANGE,m_rows,OnChangeItem)
EVENT_MAP_END(CWndClient)
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CRadioGroup::CRadioGroup(void) : m_offset(0),
                                 m_total_view(0),
                                 m_item_height(CONTROLS_LIST_ITEM_HEIGHT),
                                 m_current(CONTROLS_INVALID_INDEX)
  {
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CRadioGroup::~CRadioGroup(void)
  {
  }
//+------------------------------------------------------------------+
//| Create a control                                                 |
//+------------------------------------------------------------------+
bool CRadioGroup::Create(const long chart,const string name,const int subwin,const int x1,const int y1,const int x2,const int y2)
  {
//--- determine the number of visible rows
   m_total_view=(y2-y1)/m_item_height;
//--- check the number of visible rows
   if(m_total_view<1)
      return(false);
//--- call method of the parent class
   if(!CWndClient::Create(chart,name,subwin,x1,y1,x2,y2))
      return(false);
//--- set up
   if(!m_background.ColorBackground(CONTROLS_RADIOGROUP_COLOR_BG))
      return(false);
   if(!m_background.ColorBorder(CONTROLS_RADIOGROUP_COLOR_BORDER))
      return(false);
//--- create dependent controls
   ArrayResize(m_rows,m_total_view);
   for(int i=0;i<m_total_view;i++)
      if(!CreateButton(i))
         return(false);
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Delete group of controls                                         |
//+------------------------------------------------------------------+
void CRadioGroup::Destroy(const int reason)
  {
//--- call of the method of the parent class
   CWndClient::Destroy(reason);
//--- clear items
   m_strings.Clear();
   m_values.Clear();
  }
//+------------------------------------------------------------------+
//| Create "row"                                                     |
//+------------------------------------------------------------------+
bool CRadioGroup::CreateButton(const int index)
  {
//--- calculate coordinates
   int x1=CONTROLS_BORDER_WIDTH;
   int y1=CONTROLS_BORDER_WIDTH+m_item_height*index;
   int x2=Width()-CONTROLS_BORDER_WIDTH;
   int y2=y1+m_item_height;
//--- create
   if(!m_rows[index].Create(m_chart_id,m_name+"Item"+IntegerToString(index),m_subwin,x1,y1,x2,y2))
      return(false);
   if(!m_rows[index].Text(""))
      return(false);
   if(!Add(m_rows[index]))
      return(false);
   m_rows[index].Hide();
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Add item (row)                                                   |
//+------------------------------------------------------------------+
bool CRadioGroup::AddItem(const string item,const long value)
  {
//--- check value
   if(value!=0 && !ValueCheck(value))
      return(false);
//--- add
   if(!m_strings.Add(item))
      return(false);
   if(!m_values.Add(value))
      return(false);
//--- number of items
   int total=m_strings.Total();
//--- exit if number of items does not exceed the size of visible area
   if(total<m_total_view+1)
     {
      if(IS_VISIBLE && total!=0)
         m_rows[total-1].Show();
      return(Redraw());
     }
//--- if number of items exceeded the size of visible area
   if(total==m_total_view+1)
     {
      //--- enable vertical scrollbar
      if(!VScrolled(true))
         return(false);
      //--- and immediately make it invisible (if needed)
      if(!IS_VISIBLE)
         m_scroll_v.Visible(false);

     }
//--- set up the scrollbar
   m_scroll_v.MaxPos(m_strings.Total()-m_total_view);
//--- redraw
   return(Redraw());
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CRadioGroup::Value(const long value)
  {
//--- значение должно присутствовать в наборе
   int total=m_values.Total();
//---
   for(int i=0;i<total;i++)
      if(m_values.At(i)==value)
         Select(i+m_offset);
//---
   return(true);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CRadioGroup::ValueCheck(long value) const
  {
//--- проверяемое значение не должно дублировать уже существующее
   int total=m_values.Total();
//---
   for(int i=0;i<total;i++)
      if(m_values.At(i)==value)
         return(false);
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Makes the group visible                                          |
//+------------------------------------------------------------------+
bool CRadioGroup::Show(void)
  {
//--- call of the method of the parent class
   if(!CWndClient::Show())
      return(false);
//--- loop by rows
   int total=m_values.Total();
   for(int i=total;i<m_total_view;i++)
      m_rows[i].Hide();
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
//| Recheck button object state                                      |
//+------------------------------------------------------------------+
void CRadioGroup::RedrawButtonStates(void)
  {
//--- loop by "rows"
   for(int i=0;i<m_total_view;i++)
     {
      if(i>=ArraySize(m_rows))
         break;
      //--- select/unselect
      m_rows[i].State(m_current==i+m_offset);
     }
//---
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CRadioGroup::Save(const int file_handle)
  {
//--- check
   if(file_handle==INVALID_HANDLE)
      return(false);
//---
   FileWriteLong(file_handle,Value());
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CRadioGroup::Load(const int file_handle)
  {
//--- check
   if(file_handle==INVALID_HANDLE)
      return(false);
//---
   if(!FileIsEnding(file_handle))
      Value(FileReadLong(file_handle));
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Sett current item                                                |
//+------------------------------------------------------------------+
void CRadioGroup::Select(const int index)
  {
//--- disable the "ON" state
   if(m_current!=-1)
      RowState(m_current-m_offset,false);
//--- enable the "ON" state
   if(index!=-1)
      RowState(index-m_offset,true);
//--- save value
   m_current=index;
  }
//+------------------------------------------------------------------+
//| Redraw                                                           |
//+------------------------------------------------------------------+
bool CRadioGroup::Redraw(void)
  {
//--- loop by "rows"
   for(int i=0;i<m_total_view;i++)
     {
      //--- copy text
      if(!m_rows[i].Text(m_strings.At(i+m_offset)))
         return(false);
      //--- select
      if(!RowState(i,(m_current==i+m_offset)))
         return(false);
     }
//--- succeed
   return(true);
  }
//+------------------------------------------------------------------+
//| Change state                                                     |
//+------------------------------------------------------------------+
bool CRadioGroup::RowState(const int index,const bool select)
  {
//--- check index
   if(index<0 || index>=ArraySize(m_rows))
      return(true);
//--- change state
   return(m_rows[index].State(select));
  }
//+------------------------------------------------------------------+
//| Handler of the "Show vertical scrollbar" event                   |
//+------------------------------------------------------------------+
bool CRadioGroup::OnVScrollShow(void)
  {
//--- loop by "rows"
   for(int i=0;i<m_total_view;i++)
     {
      //--- resize "rows" according to shown vertical scrollbar
      m_rows[i].Width(Width()-(CONTROLS_SCROLL_SIZE+CONTROLS_BORDER_WIDTH));
     }
//--- check visibility
   if(!IS_VISIBLE)
     {
      m_scroll_v.Visible(false);
      return(true);
     }
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "Hide vertical scrollbar" event                   |
//+------------------------------------------------------------------+
bool CRadioGroup::OnVScrollHide(void)
  {
//--- check visibility
   if(!IS_VISIBLE)
      return(true);
//--- loop by "rows"
   for(int i=0;i<m_total_view;i++)
     {
      //--- resize "rows" according to hidden vertical scroll bar
      m_rows[i].Width(Width()-CONTROLS_BORDER_WIDTH);
     }
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Handler of the "Scroll up for one row" event                     |
//+------------------------------------------------------------------+
bool CRadioGroup::OnScrollLineUp(void)
  {
//--- get new offset
   m_offset=m_scroll_v.CurrPos();
//--- redraw
   return(Redraw());
  }
//+------------------------------------------------------------------+
//| Handler of the "Scroll down for one row" event                   |
//+------------------------------------------------------------------+
bool CRadioGroup::OnScrollLineDown(void)
  {
//--- get new offset
   m_offset=m_scroll_v.CurrPos();
//--- redraw
   return(Redraw());
  }
//+------------------------------------------------------------------+
//| Handler of changing a "row" state                                |
//+------------------------------------------------------------------+
bool CRadioGroup::OnChangeItem(const int index)
  {
//--- select "row"
   Select(index+m_offset);
//--- send notification
   EventChartCustom(INTERNAL_EVENT,ON_CHANGE,m_id,0.0,m_name);
//--- handled
   return(true);
  }
//+------------------------------------------------------------------+
