//+------------------------------------------------------------------+
//|                                                     TreeView.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "..\Element.mqh"
#include "TreeItem.mqh"
#include "Scrolls.mqh"
#include "Pointer.mqh"
//+------------------------------------------------------------------+
//| Класс для создания древовидного списка                           |
//+------------------------------------------------------------------+
class CTreeView : public CElement
  {
private:
   //--- Объекты для создания элемента
   CTreeItem         m_items[];
   CTreeItem         m_content_items[];
   CScrollV          m_scrollv;
   CScrollV          m_content_scrollv;
   CPointer          m_x_resize;
   //--- Структура элементов закреплённых за каждым пунктом-вкладкой
   struct TVElements
     {
      CElement         *elements[];
      int               list_index;
     };
   TVElements        m_tab_items[];
   //--- Массивы для всех пунктов древовидного списка (полный список)
   int               m_t_list_index[];
   int               m_t_prev_node_list_index[];
   string            m_t_item_text[];
   string            m_t_path_bmp[];
   int               m_t_item_index[];
   int               m_t_node_level[];
   int               m_t_prev_node_item_index[];
   int               m_t_items_total[];
   int               m_t_folders_total[];
   bool              m_t_item_state[];
   bool              m_t_is_folder[];
   //--- Массивы для списка отображаемых пунктов древовидного списка
   int               m_td_list_index[];
   //--- Массивы для списка содержания пунктов выделенных в древовидном списке (полный список)
   int               m_c_list_index[];
   int               m_c_tree_list_index[];
   string            m_c_item_text[];
   //--- Массивы для списка отображаемых пунктов в списке содержания
   int               m_cd_list_index[];
   int               m_cd_tree_list_index[];
   string            m_cd_item_text[];
   //--- Общее количество пунктов и количество в видимой части списков
   int               m_items_total;
   int               m_content_items_total;
   int               m_visible_items_total;
   //--- Индексы выделенных пунктов в списках
   int               m_selected_item_index;
   int               m_selected_content_item_index;
   //--- Текст выделенного пункта в списке.
   //    Только для файлов в случае использования класса для создания файлового навигатора.
   //    Если в списке выбран не файл, то в этом поле должна быть пустая строка "".
   string            m_selected_item_file_name;
   //--- Ширина области древовидного списка
   int               m_treeview_width;
   //--- Высота пунктов
   int               m_item_y_size;
   //--- Режим файлового навигатора
   ENUM_FILE_NAVIGATOR_MODE m_file_navigator_mode;
   //--- Режим подсветки при наведении курсора
   bool              m_lights_hover;
   //--- Режим показа содержания пункта в рабочей области
   bool              m_show_item_content;
   //--- Режим изменения ширины списков
   bool              m_resize_list_mode;
   //--- Режим пунктов-вкладок
   bool              m_tab_items_mode;
   //--- Счётчик таймера для перемотки списка
   int               m_timer_counter;
   //--- (1) Минимальный и (2) максимальный уровень узла
   int               m_min_node_level;
   int               m_max_node_level;
   //--- Количество пунктов в корневом каталоге
   int               m_root_items_total;
   //---
public:
                     CTreeView(void);
                    ~CTreeView(void);
   //--- Методы для создания древовидного списка
   bool              CreateTreeView(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);
   bool              CreateScrollV(void);
   bool              CreateContentItems(void);
   bool              CreateContentScrollV(void);
   bool              CreateXResizePointer(void);
   //---
public:
   //--- Указатели полос прокрутки списков
   CScrollV         *GetScrollVPointer(void)                            { return(::GetPointer(m_scrollv));         }
   CScrollV         *GetContentScrollVPointer(void)                     { return(::GetPointer(m_content_scrollv)); }
   CPointer         *GetMousePointer(void)                              { return(::GetPointer(m_x_resize));        }
   //--- Возвращает (1) указатель пункта древовидного списка, (2) указатель пункта списка содержания, 
   CTreeItem        *ItemPointer(const uint index);
   CTreeItem        *ContentItemPointer(const uint index);
   //--- (1) Режим файлового навигатора, (2) режим подсветки при наведении курсора мыши, 
   //    (3) режим изменения ширины списков, (4) режим пунктов-вкладок
   void              NavigatorMode(const ENUM_FILE_NAVIGATOR_MODE mode) { m_file_navigator_mode=mode;              }
   void              LightsHover(const bool state)                      { m_lights_hover=state;                    }
   void              ResizeListMode(const bool state)                   { m_resize_list_mode=state;                }
   void              TabItemsMode(const bool state)                     { m_tab_items_mode=state;                  }
   bool              TabItemsMode(void)                           const { return(m_tab_items_mode);                }
   //--- Режим показа содержания пункта, 
   void              ShowItemContent(const bool state)                  { m_show_item_content=state;               }
   bool              ShowItemContent(void)                        const { return(m_show_item_content);             }
   //--- Количество пунктов (1) в древовидном списке, (2) в списке содержания и (3) видимое количество пунктов
   int               ItemsTotal(void)                             const { return(::ArraySize(m_items));            }
   int               ContentItemsTotal(void)                      const { return(::ArraySize(m_content_items));    }
   void              VisibleItemsTotal(const int total)                 { m_visible_items_total=total;             }
   //--- (1) Высота пункта, (2) ширина древовидного списка и (3) списка содержания
   void              ItemYSize(const int y_size)                        { m_item_y_size=y_size;                    }
   void              TreeViewWidth(const int x_size)                    { m_treeview_width=x_size;                 }
   //--- (1) Выделяет пункт по индексу и (2) возвращает индекс выделенного пункта, (3) возвращает название файла
   void              SelectedItemIndex(const int index)                 { m_selected_item_index=index;             }
   int               SelectedItemIndex(void)                      const { return(m_selected_item_index);           }
   string            SelectedItemFileName(void)                   const { return(m_selected_item_file_name);       }

   //--- Добавляет пункт в древовидный список
   void              AddItem(const int list_index,const int list_id,const string item_name,const string path_bmp,const int item_index,
                             const int node_number,const int item_number,const int items_total,const int folders_total,const bool item_state,const bool is_folder=true);
   //--- Добавляет элемент в массив пункта-вкладки
   void              AddToElementsArray(const int item_index,CElement &object);
   //--- Показать элементы только выделенного пункта-вкладки
   void              ShowTabElements(void);
   //--- Возвращает полный путь выделенного пункта
   string            CurrentFullPath(void);
   //---
public:
   //--- Обработчик событий графика
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Таймер
   virtual void      OnEventTimer(void);
   //--- Доступность элемента
   virtual void      IsAvailable(const bool state,const bool without_items=false);
   //--- Управление
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Delete(void);
   //--- Рисует элемент
   virtual void      Draw(void);
   //---
private:
   //--- Обработка нажатия на кнопке сворачивания/разворачивания списка пункта
   bool              OnClickItemArrow(const string clicked_object,const int id,const int index);
   //--- Обработка нажатия на пункте древовидного списка
   bool              OnClickTreeItem(const string clicked_object,const int id,const int index);
   //--- Обработка нажатия на пункте в списке содержания
   bool              OnClickContentItem(const string clicked_object,const int id,const int index);

   //--- Формирует массив пунктов-вкладок
   void              GenerateTabItemsArray(void);
   //--- Определение и установка (1) границ узлов и (2) размера корневого каталога
   void              SetNodeLevelBoundaries(void);
   void              SetRootItemsTotal(void);
   //--- Смещение списков
   void              ShiftTreeList(void);
   void              ShiftContentList(void);
   //--- Ускоренная перемотка списка
   void              FastSwitching(void);

   //--- Управляет шириной списков
   void              ResizeListArea(void);
   //--- Проверка готовности для изменения ширины списков
   void              CheckXResizePointer(const int x,const int y);
   //--- Проверка на выход за ограничения
   bool              CheckOutOfArea(const int x,const int y);
   //--- Обновление ширины элементов списка
   void              UpdateXSize(const int x);

   //--- Добавляет пункт в список в области содержания
   void              AddDisplayedTreeItem(const int list_index);
   //--- Формирует (1) древовидный список и (2) список содержания
   void              FormTreeList(void);
   void              FormContentList(void);
   //---
public:
   //--- Перерисовка списков
   void              RedrawTreeList(void);
   void              RedrawContentList(void);
   //--- Обновляет (1) древовидный список и (2) список содержания
   void              UpdateTreeList(void);
   void              UpdateContentList(void);
   //---
private:
   //--- Проверка индекса выделенного пункта на выход из диапазона
   void              CheckSelectedItemIndex(void);

   //--- Рисует границу между областями
   virtual void      DrawResizeBorder(void);

   //--- Изменить ширину по правому краю окна
   virtual void      ChangeWidthByRightWindowSide(void);
  };
//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
CTreeView::CTreeView(void) : m_treeview_width(150),
                             m_item_y_size(20),
                             m_visible_items_total(12),
                             m_tab_items_mode(false),
                             m_lights_hover(false),
                             m_show_item_content(false),
                             m_resize_list_mode(false),
                             m_timer_counter(SPIN_DELAY_MSC),
                             m_selected_item_index(WRONG_VALUE),
                             m_selected_content_item_index(WRONG_VALUE)
  {
//--- Сохраним имя класса элемента в базовом классе
   CElementBase::ClassName(CLASS_NAME);
  }
//+------------------------------------------------------------------+
//| Destructor                                                       |
//+------------------------------------------------------------------+
CTreeView::~CTreeView(void)
  {
  }
//+------------------------------------------------------------------+
//| Обработчик событий                                               |
//+------------------------------------------------------------------+
void CTreeView::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Обработка события перемещения курсора
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      //--- Смещаем древовидный список, если управление ползунком полосы прокрутки в действии
      if(m_scrollv.ScrollBarControl())
        {
         ShiftTreeList();
         m_scrollv.Update(true);
         return;
        }
      //--- Заходим, только если есть список
      if(m_t_items_total[m_selected_item_index]>0)
        {
         //--- Смещаем список содержания, если управление ползунком полосы прокрутки в действии
         if(m_content_scrollv.ScrollBarControl())
           {
            ShiftContentList();
            m_content_scrollv.Update(true);
            return;
           }
        }
      //--- Управление шириной области содержания
      ResizeListArea();
      return;
     }
//--- Обработка события нажатия на кнопках полосы прокрутки 
   if(id==CHARTEVENT_CUSTOM+ON_CLICK_BUTTON)
     {
      //--- Выйти, если в режиме измененения размера области списка содержания
      if(m_x_resize.IsVisible() || m_x_resize.State())
         return;
      //--- Обработка нажатия на стрелке пункта
      if(OnClickItemArrow(sparam,(int)lparam,(int)dparam))
         return;
      //--- Обработка нажатия на пункте древовидного списка
      if(OnClickTreeItem(sparam,(int)lparam,(int)dparam))
         return;
      //--- Обработка нажатия на пункте в списке содержания
      if(OnClickContentItem(sparam,(int)lparam,(int)dparam))
         return;
      //--- Если было нажатие на кнопках полосы прокрутки списка
      if(m_scrollv.OnClickScrollInc((uint)lparam,(uint)dparam) ||
         m_scrollv.OnClickScrollDec((uint)lparam,(uint)dparam))
        {
         ShiftTreeList();
         m_scrollv.Update(true);
         return;
        }
      //--- Если было нажатие на кнопках полосы прокрутки списка
      if(m_content_scrollv.OnClickScrollInc((uint)lparam,(uint)dparam) ||
         m_content_scrollv.OnClickScrollDec((uint)lparam,(uint)dparam))
        {
         ShiftContentList();
         m_content_scrollv.Update(true);
         return;
        }
      return;
     }
  }
//+------------------------------------------------------------------+
//| Таймер                                                           |
//+------------------------------------------------------------------+
void CTreeView::OnEventTimer(void)
  {
//--- Ускоренная перемотка значений
   FastSwitching();
  }
//+------------------------------------------------------------------+
//| Создаёт элемент                                                  |
//+------------------------------------------------------------------+
bool CTreeView::CreateTreeView(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);
   if(!CreateScrollV())
      return(false);
   if(!CreateContentItems())
      return(false);
   if(!CreateContentScrollV())
      return(false);
   if(!CreateXResizePointer())
      return(false);
//--- Сформируем массив пунктов-вкладок
   GenerateTabItemsArray();
//--- Определение и установка (1) границ узлов и (2) размера корневого каталога
   SetNodeLevelBoundaries();
   SetRootItemsTotal();
//--- Обновить списки
   FormTreeList();
   FormContentList();
   return(true);
  }
//+------------------------------------------------------------------+
//| Инициализация свойств                                            |
//+------------------------------------------------------------------+
void CTreeView::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()-CElementBase::X()-m_auto_xresize_right_offset : m_x_size;
   m_y_size =m_item_y_size*m_visible_items_total+2;
//--- Ширина списка
   if(!m_show_item_content)
      m_treeview_width=m_x_size;
   else
      m_treeview_width=(m_treeview_width>=m_x_size)? m_x_size>>1 : m_treeview_width;
//--- Цвета по умолчанию
   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';
//--- Проверка индекса выделенного пункта на выход из диапазона
   CheckSelectedItemIndex();
//--- Отступы от крайней точки
   CElementBase::XGap(x_gap);
   CElementBase::YGap(y_gap);
  }
//+------------------------------------------------------------------+
//| Создаёт объект для рисования                                     |
//+------------------------------------------------------------------+
bool CTreeView::CreateCanvas(void)
  {
//--- Формирование имени объекта
   string name=CElementBase::ElementName("tree_view");
//--- Создание объекта
   if(!CElement::CreateCanvas(name,m_x,m_y,m_x_size,m_y_size))
      return(false);
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Создаёт древовидный список                                       |
//+------------------------------------------------------------------+
bool CTreeView::CreateItems(void)
  {
//--- Координаты
   int x=1,y=1;
//---
   int items_total=::ArraySize(m_items);
   for(int i=0; i<items_total; i++)
     {
      //--- Расчёт координаты Y
      y=(i>0)? y+m_item_y_size : y;
      //--- Сохраним указатель родителя
      m_items[i].MainPointer(this);
      //--- Свойства
      m_items[i].NamePart("tree_item");
      m_items[i].Index(m_t_list_index[i]);
      m_items[i].XSize(m_treeview_width);
      m_items[i].YSize(m_item_y_size);
      m_items[i].IconXGap(m_items[i].ArrowXGap(m_t_node_level[i])+17);
      m_items[i].IconYGap(2);
      m_items[i].IconFile(m_t_path_bmp[i]);
      m_items[i].IsHighlighted(m_lights_hover);
      //--- Определим тип пункта
      ENUM_TYPE_TREE_ITEM type=TI_SIMPLE;
      if(m_file_navigator_mode==FN_ALL)
        {
         type=(m_t_items_total[i]>0)? TI_HAS_ITEMS : TI_SIMPLE;
        }
      else // FN_ONLY_FOLDERS
        {
         type=(m_t_folders_total[i]>0)? TI_HAS_ITEMS : TI_SIMPLE;
        }
      //--- Корректировка начального состояния пункта
      m_t_item_state[i]=(type==TI_HAS_ITEMS)? m_t_item_state[i]: false;
      //--- Создание элемента
      if(!m_items[i].CreateTreeItem(x,y,type,m_t_list_index[i],m_t_node_level[i],m_t_item_text[i],m_t_item_state[i]))
         return(false);
      //--- Добавить элемент в массив
      CElement::AddToArray(m_items[i]);
     }
//--- Установить цвет выделенному пункту
   if(!m_tab_items_mode && m_selected_item_index>=0)
      m_items[m_selected_item_index].IsPressed(true);
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Создаёт вертикальный скролл                                      |
//+------------------------------------------------------------------+
bool CTreeView::CreateScrollV(void)
  {
//--- Сохранить указатель формы
   m_scrollv.MainPointer(this);
//--- Координаты
   int x=m_treeview_width-((m_show_item_content)? 15 : 16);
   int y=1;
//--- Установим свойства
   m_scrollv.Index(0);
   m_scrollv.XSize(m_scrollv.ScrollWidth());
   m_scrollv.YSize(CElementBase::YSize()-2);
//--- Создание полосы прокрутки
   if(!m_scrollv.CreateScroll(x,y,m_items_total,m_visible_items_total))
      return(false);
//--- Добавить элемент в массив
   CElement::AddToArray(m_scrollv);
   return(true);
  }
//+------------------------------------------------------------------+
//| Создаёт список содержания выделенного пункта                     |
//+------------------------------------------------------------------+
bool CTreeView::CreateContentItems(void)
  {
//--- Выйти, если (1) содержание пункта не нужно показывать или (2) включен режим вкладок
   if(!m_show_item_content || m_tab_items_mode)
      return(true);
//--- Резервный размер массива
   int reserve_size=10000;
//--- Координаты и ширина
   int x=m_treeview_width,y=1;
   int w=m_x_size-m_treeview_width-2;
//--- Счётчик количества пунктов
   int c=0;
//--- 
   int items_total=::ArraySize(m_items);
   for(int i=0; i<items_total; i++)
     {
      //--- В этот список не должны попасть пункты из корневого каталога, 
      //    поэтому, если уровень узла меньше 1, перейдём к следующему
      if(m_t_node_level[i]<1)
         continue;
      //--- Увеличить размеры массивов на один элемент
      int new_size=c+1;
      ::ArrayResize(m_content_items,new_size,reserve_size);
      ::ArrayResize(m_c_item_text,new_size,reserve_size);
      ::ArrayResize(m_c_tree_list_index,new_size,reserve_size);
      ::ArrayResize(m_c_list_index,new_size,reserve_size);
      //--- Расчёт координаты Y
      y=(c>0)? y+m_item_y_size : y;
      //--- Передадим объект панели
      m_content_items[c].MainPointer(this);
      //--- Установим свойства перед созданием
      m_content_items[c].NamePart("content_item");
      m_content_items[c].Index(m_t_list_index[i]);
      m_content_items[c].XSize(w);
      m_content_items[c].YSize(m_item_y_size);
      m_content_items[c].IconXGap(7);
      m_content_items[c].IconYGap(2);
      m_content_items[c].IconFile(m_t_path_bmp[i]);
      m_content_items[c].IsHighlighted(m_lights_hover);
      //--- Создание объекта
      if(!m_content_items[c].CreateTreeItem(x,y,TI_SIMPLE,c,0,m_t_item_text[i],false))
         return(false);
      //--- Добавить элемент в массив
      CElement::AddToArray(m_content_items[c]);
      //--- Сохранить (1) индекс общего списка содержания, (2) индекс древовидного списка и (3) текст пункта
      m_c_list_index[c]      =c;
      m_c_tree_list_index[c] =m_t_list_index[i];
      m_c_item_text[c]       =m_t_item_text[i];
      //---
      c++;
     }
//--- Сохранить размер списка
   m_content_items_total=::ArraySize(m_content_items);
   return(true);
  }
//+------------------------------------------------------------------+
//| Создаёт вертикальный скролл для рабочей области                  |
//+------------------------------------------------------------------+
bool CTreeView::CreateContentScrollV(void)
  {
//--- Сохранить указатель формы
   m_content_scrollv.MainPointer(this);
//--- Выйти, если содержание пункта не нужно показывать
   if(!m_show_item_content)
      return(true);
//--- Координаты
   int x=16,y=1;
//--- Свойства
   m_content_scrollv.Index(1);
   m_content_scrollv.XSize(m_content_scrollv.ScrollWidth());
   m_content_scrollv.YSize(CElementBase::YSize()-2);
   m_content_scrollv.AnchorRightWindowSide(true);
//--- Создание полосы прокрутки
   if(!m_content_scrollv.CreateScroll(x,y,m_content_items_total,m_visible_items_total))
      return(false);
//--- Добавить элемент в массив
   CElement::AddToArray(m_content_scrollv);
   return(true);
  }
//+------------------------------------------------------------------+
//| Создаёт указатель курсора изменения ширины                       |
//+------------------------------------------------------------------+
bool CTreeView::CreateXResizePointer(void)
  {
//--- Выйти, если ширину области содержания не нужно изменять или включен режим пунктов-вкладок
   if(!m_resize_list_mode || m_tab_items_mode)
     {
      m_x_resize.State(false);
      m_x_resize.IsVisible(false);
      return(true);
     }
//--- Свойства
   m_x_resize.XGap(12);
   m_x_resize.YGap(9);
   m_x_resize.XSize(25);
   m_x_resize.Type(MP_X_RESIZE);
   m_x_resize.Id(CElementBase::Id());
//--- Создание элемента
   if(!m_x_resize.CreatePointer(m_chart_id,m_subwin))
      return(false);
//---
   return(true);
  }
//+------------------------------------------------------------------+
//| Возвращает указатель пункта древовидного списка по индексу       |
//+------------------------------------------------------------------+
CTreeItem *CTreeView::ItemPointer(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]));
  }
//+------------------------------------------------------------------+
//| Возвращает указатель пункта области содержания по индексу        |
//+------------------------------------------------------------------+
CTreeItem *CTreeView::ContentItemPointer(const uint index)
  {
   uint array_size=::ArraySize(m_content_items);
//--- Если нет ни одного пункта в контекстном меню, сообщить об этом
   if(array_size<1)
     {
      ::Print(__FUNCTION__," > Вызов этого метода нужно осуществлять, "
              "когда в контекстном меню есть хотя бы один пункт!");
     }
//--- Корректировка в случае выхода из диапазона
   uint i=(index>=array_size)? array_size-1 : index;
//--- Вернуть указатель
   return(::GetPointer(m_content_items[i]));
  }
//+------------------------------------------------------------------+
//| Добавляет пункт в общий массив древовидного списка               |
//+------------------------------------------------------------------+
void CTreeView::AddItem(const int list_index,const int prev_node_list_index,const string item_text,const string path_bmp,const int item_index,
                        const int node_level,const int prev_node_item_index,const int items_total,const int folders_total,const bool item_state,const bool is_folder)
  {
//--- Резервный размер массива
   int reserve_size=10000;
//--- Увеличим размер массивов на один элемент
   int array_size =::ArraySize(m_items);
   m_items_total  =array_size+1;
   ::ArrayResize(m_items,m_items_total,reserve_size);
   ::ArrayResize(m_t_list_index,m_items_total,reserve_size);
   ::ArrayResize(m_t_prev_node_list_index,m_items_total,reserve_size);
   ::ArrayResize(m_t_item_text,m_items_total,reserve_size);
   ::ArrayResize(m_t_path_bmp,m_items_total,reserve_size);
   ::ArrayResize(m_t_item_index,m_items_total,reserve_size);
   ::ArrayResize(m_t_node_level,m_items_total,reserve_size);
   ::ArrayResize(m_t_prev_node_item_index,m_items_total,reserve_size);
   ::ArrayResize(m_t_items_total,m_items_total,reserve_size);
   ::ArrayResize(m_t_folders_total,m_items_total,reserve_size);
   ::ArrayResize(m_t_item_state,m_items_total,reserve_size);
   ::ArrayResize(m_t_is_folder,m_items_total,reserve_size);
//--- Сохраним значения переданных параметров
   m_t_list_index[array_size]           =list_index;
   m_t_prev_node_list_index[array_size] =prev_node_list_index;
   m_t_item_text[array_size]            =item_text;
   m_t_path_bmp[array_size]             =path_bmp;
   m_t_item_index[array_size]           =item_index;
   m_t_node_level[array_size]           =node_level;
   m_t_prev_node_item_index[array_size] =prev_node_item_index;
   m_t_items_total[array_size]          =items_total;
   m_t_folders_total[array_size]        =folders_total;
   m_t_item_state[array_size]           =item_state;
   m_t_is_folder[array_size]            =is_folder;
  }
//+------------------------------------------------------------------+
//| Добавляет элемент в массив указанной вкладки                     |
//+------------------------------------------------------------------+
void CTreeView::AddToElementsArray(const int tab_index,CElement &object)
  {
//--- Выйти, если режим пунктов-вкладок отключен
   if(!m_tab_items_mode)
      return;
//--- Проверка на выход из диапазона
   int array_size=::ArraySize(m_tab_items);
   if(array_size<1 || tab_index<0 || tab_index>=array_size)
      return;
//--- Добавим указатель переданного элемента в массив указанной вкладки
   int size=::ArraySize(m_tab_items[tab_index].elements);
   ::ArrayResize(m_tab_items[tab_index].elements,size+1);
   m_tab_items[tab_index].elements[size]=::GetPointer(object);
  }
//+------------------------------------------------------------------+
//| Показывает элементы только выделенного пункта-вкладки            |
//+------------------------------------------------------------------+
void CTreeView::ShowTabElements(void)
  {
//--- Выйти, если (1) элемент скрыт или (2) режим пунктов-вкладок отключен
   if(!CElementBase::IsVisible() || !m_tab_items_mode)
      return;
//--- Индекс выделенной вкладки
   int tab_index=WRONG_VALUE;
//--- Определим индекс выделенной вкладки
   int tab_items_total=::ArraySize(m_tab_items);
   for(int i=0; i<tab_items_total; i++)
     {
      if(m_tab_items[i].list_index==m_selected_item_index)
        {
         tab_index=i;
         break;
        }
     }
//--- Покажем элементы только выделенной вкладки
   for(int i=0; i<tab_items_total; i++)
     {
      //--- Получим количество элементов присоединённых к вкладке
      int tab_elements_total=::ArraySize(m_tab_items[i].elements);
      //--- Если выделен этот пункт-вкладка
      if(i==tab_index)
        {
         //--- Показать элементы
         for(int j=0; j<tab_elements_total; j++)
            m_tab_items[i].elements[j].Reset();
        }
      else
        {
         //--- Скрыть элементы
         for(int j=0; j<tab_elements_total; j++)
            m_tab_items[i].elements[j].Hide();
        }
     }
  }
//+------------------------------------------------------------------+
//| Возвращает полный текущий путь                                   |
//+------------------------------------------------------------------+
string CTreeView::CurrentFullPath(void)
  {
//--- Для формирования директории к выделенному пункту
   string path="";
//--- Индекс выделенного пункта
   int li=m_selected_item_index;
//--- Массив для формирования директории
   string path_parts[];
//--- Получим описание (текст) выделенного пункта древовидного списка,
//    но только, если это папка
   if(m_t_is_folder[li])
     {
      ::ArrayResize(path_parts,1);
      path_parts[0]=m_t_item_text[li];
     }
//--- Пройдёмся по всему списку
   int total=::ArraySize(m_t_list_index);
   for(int i=0; i<total; i++)
     {
      //--- Рассматриваем только папки.
      //    Если файл, переходим к следующему пункту.
      if(!m_t_is_folder[i])
         continue;
      //--- Если (1) индекс общего списка совпадает с индексом общего списка предыдущего узла и
      //    (2) индекс пункта локального списка совпадает с индексом пункта предыдущего узла и
      //    (3) соблюдается последовательность уровней узлов
      if(m_t_list_index[i]==m_t_prev_node_list_index[li] &&
         m_t_item_index[i]==m_t_prev_node_item_index[li] &&
         m_t_node_level[i]==m_t_node_level[li]-1)
        {
         //--- Увеличим массив на один элемент и сохраним описание пункта
         int sz=::ArraySize(path_parts);
         ::ArrayResize(path_parts,sz+1);
         path_parts[sz]=m_t_item_text[i];
         //--- Запомним индекс для следующей проверки
         li=i;
         //--- Если дошли до нулевого уровня узла, выходим из цикла
         if(m_t_node_level[i]==0 || i<=0)
            break;
         //--- Сбросить счётчик цикла
         i=-1;
        }
     }
//--- Сформировать строку - полный путь к выделенному пункту в древовидном списке
   total=::ArraySize(path_parts);
   for(int i=total-1; i>=0; i--)
      ::StringAdd(path,path_parts[i]+"\\");
//--- Если выделенный в древовидном списке пункт - папка
   if(m_t_is_folder[m_selected_item_index])
     {
      m_selected_item_file_name="";
      //--- Если пункт в области содержания выделен
      if(m_selected_content_item_index>0)
        {
         //--- Если выделенный пункт - файл, сохраним его название
         if(!m_t_is_folder[m_c_tree_list_index[m_selected_content_item_index]])
            m_selected_item_file_name=m_c_item_text[m_selected_content_item_index];
        }
     }
//--- Если выделенный в древовидном списке пункт - файл
   else
//--- Сохраним его название
      m_selected_item_file_name=m_t_item_text[m_selected_item_index];
//--- Вернуть директорию
   return(path);
  }
//+------------------------------------------------------------------+
//| Доступность элемента                                             |
//+------------------------------------------------------------------+
void CTreeView::IsAvailable(const bool state,const bool without_items=false)
  {
//--- Если без пунктов
   if(without_items)
     {
      m_is_available=state;
      return;
     }
//--- Если с пунктами
   else
     {
      m_is_available=state;
      int elements_total=CElement::ElementsTotal();
      for(int i=0; i<elements_total; i++)
         m_elements[i].IsAvailable(state);
      //---
      if(state)
         SetZorders();
      else
         ResetZorders();
     }
  }
//+------------------------------------------------------------------+
//| Показывает элемент                                               |
//+------------------------------------------------------------------+
void CTreeView::Show(void)
  {
//--- Выйти, если элемент уже видим
   if(CElementBase::IsVisible())
      return;
//--- Показать элемент
   CElement::Show();
//--- Обновить координаты и размеры списков
   ShiftTreeList();
   ShiftContentList();
  }
//+------------------------------------------------------------------+
//| Скрывает элемент                                                 |
//+------------------------------------------------------------------+
void CTreeView::Hide(void)
  {
//--- Выйти, если элемент уже скрыт
   if(!CElementBase::IsVisible())
      return;
//--- Скрыть элемент
   CElement::Hide();
//--- Скрыть пункты древовидного списка
   int total=::ArraySize(m_items);
   for(int i=0; i<total; i++)
      m_items[i].Hide();
//--- Скрыть пункты списка содержания
   total=::ArraySize(m_content_items);
   for(int i=0; i<total; i++)
      m_content_items[i].Hide();
//--- Скрыть полосы прокрутки
   m_scrollv.Hide();
   m_content_scrollv.Hide();
//--- Скорректируем размер полосы прокрутки
   m_scrollv.ChangeThumbSize(m_items_total,m_visible_items_total);
  }
//+------------------------------------------------------------------+
//| Удаление                                                         |
//+------------------------------------------------------------------+
void CTreeView::Delete(void)
  {
   CElement::Delete();
   m_x_resize.Delete();
//--- Освобождение массивов элемента
   ::ArrayFree(m_items);
   ::ArrayFree(m_content_items);
//---
   int total=::ArraySize(m_tab_items);
   for(int i=0; i<total; i++)
      ::ArrayFree(m_tab_items[i].elements);
   ::ArrayFree(m_tab_items);
//---
   ::ArrayFree(m_t_prev_node_list_index);
   ::ArrayFree(m_t_list_index);
   ::ArrayFree(m_t_item_text);
   ::ArrayFree(m_t_path_bmp);
   ::ArrayFree(m_t_item_index);
   ::ArrayFree(m_t_node_level);
   ::ArrayFree(m_t_prev_node_item_index);
   ::ArrayFree(m_t_items_total);
   ::ArrayFree(m_t_folders_total);
   ::ArrayFree(m_t_item_state);
   ::ArrayFree(m_t_is_folder);
//---
   ::ArrayFree(m_td_list_index);
//---
   ::ArrayFree(m_c_list_index);
   ::ArrayFree(m_c_item_text);
//---
   ::ArrayFree(m_cd_item_text);
   ::ArrayFree(m_cd_list_index);
   ::ArrayFree(m_cd_tree_list_index);
//--- Инициализация переменных значениями по умолчанию
   m_selected_item_index=WRONG_VALUE;
   m_selected_content_item_index=WRONG_VALUE;
  }
//+------------------------------------------------------------------+
//| Рисует элемент                                                   |
//+------------------------------------------------------------------+
void CTreeView::Draw(void)
  {
//--- Нарисовать фон
   DrawBackground();
//--- Нарисовать рамку
   DrawBorder();
//--- Нарисовать границу областей
   DrawResizeBorder();
  }
//+------------------------------------------------------------------+
//| Нажатие на кнопку сворачивания/разворачивания списка пункта      |
//+------------------------------------------------------------------+
bool CTreeView::OnClickItemArrow(const string clicked_object,const int id,const int index)
  {
//--- Выйдем, если чужое имя объекта
   if(::StringFind(clicked_object,CElementBase::ProgramName()+"_tree_item_",0)<0)
      return(false);
//--- Выйти, если идентификаторы не совпадают
   if(id!=CElementBase::Id())
      return(false);
//--- Получим индекс пункта в общем списке
   int list_index=CElementBase::IndexFromObjectName(clicked_object);
//--- Выйти, если этот пункт без выпадающего списка
   if(m_items[list_index].Type()!=TI_HAS_ITEMS)
      return(false);
//--- Получим относительные координаты под курсором мыши
   int x=m_mouse.RelativeX(m_canvas);
//--- Выйти, если нажатие было не на стрелке
   if(x<m_items[list_index].ArrowXGap() || x>m_items[list_index].ArrowXGap()+16)
      return(false);
//--- Получим состояние стрелки пункта и установим противоположное
   m_t_item_state[list_index]=!m_t_item_state[list_index];
//--- Подсветить выделенный пункт
   m_items[list_index].ItemState(m_t_item_state[list_index]);
   m_items[list_index].IsPressed((list_index==m_selected_item_index)? true : false);
   m_items[list_index].Update(true);
//--- Сформировать древовидный список
   FormTreeList();
//--- Обновить
   UpdateTreeList();
//--- Рассчитать положение ползунка полосы прокрутки
   m_scrollv.MovingThumb(m_scrollv.CurrentPos());
//--- Показать элементы выделенного пункта-вкладки
   ShowTabElements();
//--- Отправим сообщение об изменении в графическом интерфейсе
   ::EventChartCustom(m_chart_id,ON_CHANGE_GUI,CElementBase::Id(),0,"");
   return(true);
  }
//+------------------------------------------------------------------+
//| Нажатие на пункте в древовидном списке                           |
//+------------------------------------------------------------------+
bool CTreeView::OnClickTreeItem(const string clicked_object,const int id,const int index)
  {
//--- Выйдем, если полоса прокрутки в активном режиме
   if(m_scrollv.State() || m_content_scrollv.State())
      return(false);
//--- Выйдем, если чужое имя объекта
   if(::StringFind(clicked_object,CElementBase::ProgramName()+"_tree_item_",0)<0)
      return(false);
//--- Выйти, если идентификаторы не совпадают
   if(id!=CElementBase::Id())
      return(false);
//--- Получим текущую позицию ползунка полосы прокрутки
   int v=m_scrollv.CurrentPos();
//--- Пройдёмся по списку
   for(int r=0; r<m_visible_items_total; r++)
     {
      //--- Проверка для предотвращения выхода из диапазона
      if(v>=0 && v<m_items_total)
        {
         //--- Получим общий индекс пункта
         int li=m_td_list_index[v];
         //--- Если выбран этот пункт в списке
         if(m_items[li].CanvasPointer().ChartObjectName()==clicked_object)
           {
            //--- Выйдем, если этот пункт уже выделен
            if(li==m_selected_item_index)
              {
               m_items[li].IsPressed(true);
               m_items[li].Update(true);
               return(false);
              }
            //--- Если включен режим пунктов-вкладок и отключен режим показа содержания,
            //    не будем выделять пункты без списка
            if(m_tab_items_mode && !m_show_item_content)
              {
               //--- Если текущий пункт не содержит в себе списка, остановим цикл
               if(m_t_items_total[li]>0)
                 {
                  m_items[li].IsPressed(false);
                  m_items[li].Update(true);
                  break;
                 }
              }
            //--- Установим цвет предыдущему выделенному пункту
            m_items[m_selected_item_index].IsPressed(false);
            m_items[m_selected_item_index].Update(true);
            //--- Запомним индекс для текущего и изменим его цвет
            m_selected_item_index=li;
            m_items[li].IsPressed(true);
            m_items[li].Update(true);
            break;
           }
         v++;
        }
     }
//--- Сбросить цвета в области содержания
   if(m_selected_content_item_index>=0)
     {
      m_content_items[m_selected_content_item_index].IsPressed(false);
      m_content_items[m_selected_content_item_index].Update();
     }
//--- Сброс выделенного пункта
   m_selected_content_item_index=WRONG_VALUE;
//--- Обновить список содержания
   FormContentList();
   UpdateContentList();
//--- Рассчитать положение ползунка полосы прокрутки
   m_content_scrollv.MovingThumb(m_content_scrollv.CurrentPos());
//--- Показать элементы выделенного пункта-вкладки
   ShowTabElements();
//--- Отправить сообщение о выборе новой директории в древовидном списке
   ::EventChartCustom(m_chart_id,ON_CHANGE_TREE_PATH,0,0,"");
//--- Отправим сообщение об изменении в графическом интерфейсе
   ::EventChartCustom(m_chart_id,ON_CHANGE_GUI,CElementBase::Id(),0,"");
   return(true);
  }
//+------------------------------------------------------------------+
//| Нажатие на пункте в списке содержания                            |
//+------------------------------------------------------------------+
bool CTreeView::OnClickContentItem(const string clicked_object,const int id,const int index)
  {
//--- Выйти, если область содержания отключена
   if(!m_show_item_content)
      return(false);
//--- Выйдем, если полоса прокрутки в активном режиме
   if(m_scrollv.State() || m_content_scrollv.State())
      return(false);
//--- Выйдем, если чужое имя объекта
   if(::StringFind(clicked_object,CElementBase::ProgramName()+"_content_item_",0)<0)
      return(false);
//--- Выйти, если идентификаторы не совпадают
   if(id!=CElementBase::Id())
      return(false);
//--- Получим количество пунктов в списке содержания
   int content_items_total=::ArraySize(m_cd_list_index);
//--- Получим текущую позицию ползунка полосы прокрутки
   int v=m_content_scrollv.CurrentPos();
//--- Пройдёмся по списку
   for(int r=0; r<m_visible_items_total; r++)
     {
      //--- Проверка для предотвращения выхода из диапазона
      if(v>=0 && v<content_items_total)
        {
         //--- Получим общий индекс списка
         int li=m_cd_list_index[v];
         //--- Если выбран этот пункт в списке
         if(m_content_items[li].CanvasPointer().ChartObjectName()==clicked_object)
           {
            //--- Установить цвет предыдущему выделенному пункту
            if(m_selected_content_item_index>=0)
              {
               m_content_items[m_selected_content_item_index].IsPressed(false);
               m_content_items[m_selected_content_item_index].Update(true);
              }
            //--- Запомним индекс для текущего и изменим цвет
            m_selected_content_item_index=li;
            m_content_items[li].IsPressed(true);
            m_content_items[li].Update(true);
           }
         v++;
        }
     }
//--- Отправить сообщение о выборе новой директории в древовидном списке
   ::EventChartCustom(m_chart_id,ON_CHANGE_TREE_PATH,0,0,"");
   return(true);
  }
//+------------------------------------------------------------------+
//| Формирует массив пунктов-вкладок                                 |
//+------------------------------------------------------------------+
void CTreeView::GenerateTabItemsArray(void)
  {
//--- Выйти, если режим пунктов-вкладок отключен
   if(!m_tab_items_mode)
      return;
//--- Добавим в массив пунктов-вкладок только пустые пункты
   int items_total=::ArraySize(m_items);
   for(int i=0; i<items_total; i++)
     {
      //--- Если в этом пункте есть другие пункты, перейдём к следующему
      if(m_t_items_total[i]>0)
         continue;
      //--- Увеличим размер массива пунктов-вкладок на один элемент
      int array_size=::ArraySize(m_tab_items);
      ::ArrayResize(m_tab_items,array_size+1);
      //--- Сохраним общий индекс пункта
      m_tab_items[array_size].list_index=i;
     }
//--- Если отключен показ содержания пунктов
   if(!m_show_item_content)
     {
      //--- Получим размер массива пунктов-вкладок
      int tab_items_total=::ArraySize(m_tab_items);
      //--- Скорректируем индекс, если выход из диапазона
      if(m_selected_item_index>=tab_items_total)
         m_selected_item_index=tab_items_total-1;
      //--- Индекс выделенной вкладки
      int tab_index=m_tab_items[m_selected_item_index].list_index;
      m_selected_item_index=tab_index;
      m_items[tab_index].IsPressed(true);
      m_items[tab_index].Update();
     }
  }
//+------------------------------------------------------------------+
//| Определение и установка границ узлов                             |
//+------------------------------------------------------------------+
void CTreeView::SetNodeLevelBoundaries(void)
  {
//--- Определим минимальный и максимальный уровень узлов
   m_min_node_level =m_t_node_level[::ArrayMinimum(m_t_node_level)];
   m_max_node_level =m_t_node_level[::ArrayMaximum(m_t_node_level)];
  }
//+------------------------------------------------------------------+
//| Определение и установка размера корневого каталога               |
//+------------------------------------------------------------------+
void CTreeView::SetRootItemsTotal(void)
  {
//--- Определим количество пунктов в корневом каталоге
   int items_total=::ArraySize(m_items);
   for(int i=0; i<items_total; i++)
     {
      //--- Если это минимальный уровень, увеличим счётчик
      if(m_t_node_level[i]==m_min_node_level)
         m_root_items_total++;
     }
  }
//+------------------------------------------------------------------+
//| Сдвигает древовидный список относительно полосы прокрутки        |
//+------------------------------------------------------------------+
void CTreeView::ShiftTreeList(void)
  {
//--- Выйти, если элемент скрыт
   if(!CElementBase::IsVisible())
      return;
//--- Скрыть все пункты в древовидном списке
   int items_total=ItemsTotal();
   for(int i=0; i<items_total; i++)
      m_items[i].Hide();
//--- Получим количество отображаемых пунктов в списке
   int total=::ArraySize(m_td_list_index);
//--- Расчёт ширины пунктов списка
   int w=(m_scrollv.IsScroll())? CElementBase::XSize()-m_scrollv.ScrollWidth()-2 : CElementBase::XSize();
//--- Определение позиции скролла
   int v=(m_scrollv.IsScroll())? m_scrollv.CurrentPos() : 0;
   m_scrollv.CurrentPos(v);
//--- Координаты
   int x=1,y=1;
//---
   for(int r=0; r<m_visible_items_total; r++)
     {
      //--- Проверка для предотвращения выхода из диапазона
      if(v>=0 && v<total)
        {
         //--- Рассчитаем координату Y
         y=(r>0)? y+m_item_y_size : y;
         //--- Получим общий индекс пункта древовидного списка
         int li=m_td_list_index[v];
         //--- Установить координаты и ширину
         m_items[li].UpdateX(x);
         m_items[li].UpdateY(y);
         //--- Показать пункт
         m_items[li].Show();
         v++;
        }
     }
//--- Перерисовать полосу прокрутки
   if(m_scrollv.IsScroll())
      m_scrollv.Show();
  }
//+------------------------------------------------------------------+
//| Сдвигает список содержания относительно полосы прокрутки         |
//+------------------------------------------------------------------+
void CTreeView::ShiftContentList(void)
  {
//--- Выйти, если (1) содержание пункта не нужно показывать или (2) элемент скрыт
   if(!m_show_item_content || !CElementBase::IsVisible())
      return;
//--- Скрыть все пункты в списке содержания
   m_content_items_total=ContentItemsTotal();
   for(int i=0; i<m_content_items_total; i++)
      m_content_items[i].Hide();
//--- Получим количество отображаемых пунктов в списке содержания
   int total=::ArraySize(m_cd_list_index);
//--- Если нужна полоса прокрутки
   bool is_scroll=total>m_visible_items_total;
//--- Расчёт ширины пунктов списка
   int w=(is_scroll)? m_x_size-m_treeview_width-m_content_scrollv.ScrollWidth()-2 : m_x_size-m_treeview_width-2;
//--- Определение позиции скролла
   int v=(is_scroll)? m_content_scrollv.CurrentPos() : 0;
   m_content_scrollv.CurrentPos(v);
//--- Координата X
   int x=m_treeview_width+1;
//--- Координата Y первого пункта древовидного списка
   int y=1;
//--- 
   for(int r=0; r<m_visible_items_total; r++)
     {
      //--- Проверка для предотвращения выхода из диапазона
      if(v>=0 && v<total)
        {
         //--- Рассчитаем координату Y
         y=(r>0)? y+m_item_y_size : y;
         //--- Получим общий индекс пункта древовидного списка
         int li=m_cd_list_index[v];
         //--- Установить координаты и ширину
         m_content_items[li].UpdateX(x);
         m_content_items[li].UpdateY(y);
         //--- Показать пункт
         m_content_items[li].Show();
         v++;
        }
     }
//--- Перерисовать полосу прокрутки
   if(is_scroll)
      m_content_scrollv.Show();
  }
//+------------------------------------------------------------------+
//| Ускоренная перемотка списков                                     |
//+------------------------------------------------------------------+
void CTreeView::FastSwitching(void)
  {
//--- Определение фокуса на одной из кнопок полос прокрутки
   bool spin_buttons_focus=m_scrollv.GetIncButtonPointer().MouseFocus() || 
                           m_scrollv.GetDecButtonPointer().MouseFocus() ||
                           m_content_scrollv.GetIncButtonPointer().MouseFocus() ||
                           m_content_scrollv.GetDecButtonPointer().MouseFocus();
//--- Выйти, если (1) вне области элемента или (2) активирован режим изменения ширины области содержания
   if((!CElementBase::MouseFocus() && !spin_buttons_focus) || m_x_resize.State())
     {
      //--- Отправим сообщение об изменении в графическом интерфейсе
      if(m_timer_counter!=SPIN_DELAY_MSC)
         ::EventChartCustom(m_chart_id,ON_CHANGE_GUI,CElementBase::Id(),0,"");
      //--- Вернём счётчик к первоначальному значению
      m_timer_counter=SPIN_DELAY_MSC;
      return;
     }
//--- Если кнопка мыши отжата
   if(!m_mouse.LeftButtonState())
     {
      //--- Отправим сообщение об изменении в графическом интерфейсе
      if(m_timer_counter!=SPIN_DELAY_MSC)
         ::EventChartCustom(m_chart_id,ON_CHANGE_GUI,CElementBase::Id(),0,"");
      //--- Вернём счётчик к первоначальному значению
      m_timer_counter=SPIN_DELAY_MSC;
     }
//--- Если же кнопка мыши нажата
   else
     {
      //--- Выйти, если ни одна кнопка не зажата
      if(!spin_buttons_focus)
         return;
      //--- Увеличим счётчик на установленный интервал
      m_timer_counter+=TIMER_STEP_MSC;
      //--- Выйдем, если меньше нуля
      if(m_timer_counter<0)
         return;
      //--- Флаг перемотки
      bool scroll_v=false;
      //--- Если прокрутка вверх
      if(m_scrollv.GetIncButtonPointer().MouseFocus())
        {
         m_scrollv.OnClickScrollInc((uint)Id(),0);
         scroll_v=true;
        }
      //--- Если прокрутка вниз
      else if(m_scrollv.GetDecButtonPointer().MouseFocus())
        {
         m_scrollv.OnClickScrollDec((uint)Id(),1);
         scroll_v=true;
        }
      //--- Если кнопка нажата
      if(scroll_v)
        {
         //--- Обновить список
         ShiftTreeList();
         m_scrollv.Update(true);
         return;
        }
      //--- Выйти, если область содержания отключена
      if(!m_show_item_content)
         return;
      //--- Если прокрутка вверх
      if(m_content_scrollv.GetIncButtonPointer().MouseFocus())
        {
         m_content_scrollv.OnClickScrollInc((uint)Id(),2);
         scroll_v=true;
        }
      //--- Если прокрутка вниз
      else if(m_content_scrollv.GetDecButtonPointer().MouseFocus())
        {
         m_content_scrollv.OnClickScrollDec((uint)Id(),3);
         scroll_v=true;
        }
      //--- Если кнопка нажата
      if(scroll_v)
        {
         //--- Обновить список
         ShiftContentList();
         m_content_scrollv.Update(true);
        }
     }
  }
//+------------------------------------------------------------------+
//| Управляет шириной списков                                        |
//+------------------------------------------------------------------+
void CTreeView::ResizeListArea(void)
  {
//--- Выйти, (1) если ширину области содержания не нужно изменять или 
//    (2) включен режим пунктов-вкладок или (3) полоса прокрутки активна
   if(!m_resize_list_mode || !m_show_item_content || m_tab_items_mode || m_scrollv.State())
      return;
//--- Координаты
   int x =m_mouse.RelativeX(m_canvas);
   int y =m_mouse.RelativeY(m_canvas);
//--- Проверка готовности для изменения ширины списков
   CheckXResizePointer(x,y);
//--- Выйти, если курсор отключен
   if(!m_x_resize.State())
      return;
//--- Проверка на выход за установленные ограничения 
   if(!CheckOutOfArea(x,y))
      return;
//--- Установим X-координату объекту по центру курсора мыши
   m_x_resize.UpdateX(m_mouse.X());
//--- Y-координату устанавливаем, только если не вышли за область элемента
   if(y>0 && y<m_y_size)
      m_x_resize.UpdateY(m_mouse.Y());
//--- Обновление ширины элементов списка
   UpdateXSize(x);
//--- Перерисовать указатель
   m_x_resize.Reset();
  }
//+------------------------------------------------------------------+
//| Проверка готовности для изменения ширины списков                 |
//+------------------------------------------------------------------+
void CTreeView::CheckXResizePointer(const int x,const int y)
  {
//--- Если указатель не активирован, но курсор мыши в его области
   if(!m_x_resize.State() && 
      y>0 && y<m_y_size && x>m_treeview_width && x<m_treeview_width+3)
     {
      //--- Обновить координаты указателя и сделать его видимым
      m_x_resize.Moving(m_mouse.X(),m_mouse.Y());
      m_x_resize.Reset();
      //--- Если левая кнопка мыши нажата, активируем указатель
      if(m_mouse.LeftButtonState())
        {
         m_x_resize.State(true);
         m_x_resize.Update(true);
         //--- Отправим сообщение на определение доступных элементов
         ::EventChartCustom(m_chart_id,ON_SET_AVAILABLE,CElementBase::Id(),0,"");
         //--- Отправим сообщение об изменении в графическом интерфейсе
         ::EventChartCustom(m_chart_id,ON_CHANGE_GUI,CElementBase::Id(),0,"");
        }
     }
   else
     {
      //--- Если левая кнопка мыши отжата
      if(!m_mouse.LeftButtonState())
        {
         //--- Выйти, если указатель курсора уже скрыт
         if(!m_x_resize.IsVisible())
            return;
         //--- Если указатель курсора активен
         if(m_x_resize.State())
           {
            //--- Дезактивировать указатель
            m_x_resize.State(false);
            //--- Коррекция ширины пунктов списков
            RedrawTreeList();
            RedrawContentList();
            UpdateTreeList();
            UpdateContentList();
            //--- Отправим сообщение на определение доступных элементов
            ::EventChartCustom(m_chart_id,ON_SET_AVAILABLE,CElementBase::Id(),1,"");
            //--- Отправим сообщение об изменении в графическом интерфейсе
            ::EventChartCustom(m_chart_id,ON_CHANGE_GUI,CElementBase::Id(),0,"");
           }
         //--- Скрыть указатель
         m_x_resize.Hide();
        }
     }
  }
//+------------------------------------------------------------------+
//| Проверка на выход за ограничения                                 |
//+------------------------------------------------------------------+
bool CTreeView::CheckOutOfArea(const int x,const int y)
  {
//--- Ограничение
   int area_limit=80;
//--- Если выходим за границы элемента по горизонтали ...
   if(x<area_limit || x>m_x_size-area_limit)
     {
      // ... перемещаем указатель только по вертикали, не выходя за границы
      if(y>0 && y<m_y_size)
         m_x_resize.UpdateY(m_mouse.Y());
      //--- Не изменять ширину списков
      return(false);
     }
//--- Изменить ширину списков
   return(true);
  }
//+------------------------------------------------------------------+
//| Обновление ширины древовидного списка                            |
//+------------------------------------------------------------------+
void CTreeView::UpdateXSize(const int x)
  {
//--- Координаты
   int y1=1,y2=m_y_size-2;
//--- Стереть границу
   m_canvas.LineVertical(m_treeview_width,y1,y2,::ColorToARGB(m_back_color));
//--- Рассчитаем и установим ширину области древовидного списка
   m_treeview_width=x-3;
//--- Рассчитаем и установим ширину для пунктов в древовидном списке с учётом полосы прокрутки
   int w=(m_scrollv.IsScroll())? m_treeview_width-m_scrollv.ScrollWidth()-2 : m_treeview_width-1;
//--- Определение позиции скролла
   int v=(m_scrollv.IsScroll())? m_scrollv.CurrentPos() : 0;
   m_scrollv.CurrentPos(v);
//--- Получим количество отображаемых пунктов в списке
   int total=::ArraySize(m_td_list_index);
   for(int r=0; r<m_visible_items_total; r++)
     {
      //--- Проверка для предотвращения выхода из диапазона
      if(v>=0 && v<total)
        {
         //--- Получим общий индекс пункта древовидного списка
         int li=m_td_list_index[v];
         //--- Установить координаты и ширину
         m_items[li].UpdateWidth(w);
         m_items[li].Update(true);
         v++;
        }
     }
//--- Рассчитаем ширину для пунктов в списке
   w=(m_content_scrollv.IsScroll())? m_x_size-m_treeview_width-m_content_scrollv.ScrollWidth()-3 : m_x_size-m_treeview_width-2;
//--- Определение позиции скролла
   v=(m_content_scrollv.IsScroll())? m_content_scrollv.CurrentPos() : 0;
   m_content_scrollv.CurrentPos(v);
//--- Получим количество отображаемых пунктов в списке содержания
   total=::ArraySize(m_cd_list_index);
   for(int r=0; r<m_visible_items_total; r++)
     {
      //--- Проверка для предотвращения выхода из диапазона
      if(v>=0 && v<total)
        {
         //--- Получим общий индекс пункта древовидного списка
         int li=m_cd_list_index[v];
         //--- Установить координаты и ширину
         m_content_items[li].MouseFocus(false);
         m_content_items[li].UpdateWidth(w);
         m_content_items[li].UpdateX(m_treeview_width+1);
         m_content_items[li].Moving();
         m_content_items[li].Update(true);
         v++;
        }
     }
//--- Нарисовать границу
   m_canvas.LineVertical(m_treeview_width,y1,y2,::ColorToARGB(m_border_color));
//--- Рассчитаем и установим координаты для полосы прокрутки древовидного списка
   m_scrollv.XDistance(m_treeview_width-15);
//--- Обновить элемент
   m_canvas.Update();
  }
//+------------------------------------------------------------------+
//| Добавляет пункт в массив отображаемых пунктов                    |
//| в древовидном списке                                             |
//+------------------------------------------------------------------+
void CTreeView::AddDisplayedTreeItem(const int list_index)
  {
//--- Увеличим размер массивов на один элемент
   int array_size=::ArraySize(m_td_list_index);
   ::ArrayResize(m_td_list_index,array_size+1);
//--- Сохраним значения переданных параметров
   m_td_list_index[array_size]=list_index;
  }
//+------------------------------------------------------------------+
//| Формирует древовидный список                                     |
//+------------------------------------------------------------------+
void CTreeView::FormTreeList(void)
  {
//--- Массивы для контроля последовательности пунктов:
   int l_prev_node_list_index[]; // общий индекс списка предыдущего узла
   int l_item_index[];           // локальный индекс пункта
   int l_items_total[];          // количество пунктов в узле
   int l_folders_total[];        // количество папок в узле
//--- Зададим начальный размер массивов
   int begin_size=m_max_node_level+2;
   ::ArrayResize(l_prev_node_list_index,begin_size);
   ::ArrayResize(l_item_index,begin_size);
   ::ArrayResize(l_items_total,begin_size);
   ::ArrayResize(l_folders_total,begin_size);
//--- Инициализация массивов
   ::ArrayInitialize(l_prev_node_list_index,-1);
   ::ArrayInitialize(l_item_index,-1);
   ::ArrayInitialize(l_items_total,-1);
   ::ArrayInitialize(l_folders_total,-1);
//--- Освобождаем массив отображаемых пунктов древовидного списка
   ::ArrayFree(m_td_list_index);
//--- Счётчик локальных индексов пунктов
   int ii=0;
//--- Для установки флага последнего пункта в корневом каталоге
   bool end_list=false;
//--- Собираем отображаемые пункты в массив. Цикл будет работать до тех пор, пока: 
//    1: счётчик узлов не больше максимального;
//    2: не дошли до последнего пункта (после проверки всех вложенных в него пунктов);
//    3: пользователь не удалил программу.
   int items_total=::ArraySize(m_items);
   for(int nl=m_min_node_level; nl<=m_max_node_level && !end_list; nl++)
     {
      for(int i=0; i<items_total && !::IsStopped(); i++)
        {
         //--- Если включен режим "Отображать только папки"
         if(m_file_navigator_mode==FN_ONLY_FOLDERS)
           {
            //--- Если это файл, перейти к следующему пункту
            if(!m_t_is_folder[i])
               continue;
           }
         //--- Если (1) это не наш узел или (2) последовальность локальных индексов пунктов не соблюдается,
         //    перейдём к следующему
         if(nl!=m_t_node_level[i] || m_t_item_index[i]<=l_item_index[nl])
            continue;
         //--- Перейдём к следующему пункту, если (1) сейчас не в корневом каталоге и 
         //    (2) общий индекс списка предыдущего узла не равен аналогичному в памяти
         if(nl>m_min_node_level && m_t_prev_node_list_index[i]!=l_prev_node_list_index[nl])
            continue;
         //--- Запомним локальный индекс пункта, если следующий будет не меньше размера локального списка
         if(m_t_item_index[i]+1>=l_items_total[nl])
            ii=m_t_item_index[i];
         //--- Если список текущего пункта развёрнут
         if(m_t_item_state[i])
           {
            //--- Добавим пункт в массив отображаемых пунктов в древовидном списке
            AddDisplayedTreeItem(i);
            //--- Запомним текущие значения и перейдём к следующему узлу
            int n=nl+1;
            l_prev_node_list_index[n] =m_t_list_index[i];
            l_item_index[nl]          =m_t_item_index[i];
            l_items_total[n]          =m_t_items_total[i];
            l_folders_total[n]        =m_t_folders_total[i];
            //--- Обнулим счётчик локальных индексов пунктов
            ii=0;
            //--- Перейдём к следующему узлу
            break;
           }
         //--- Добавим пункт в массив отображаемых пунктов в древовидном списке
         AddDisplayedTreeItem(i);
         //--- Увеличим счётчик локальных индексов пунктов
         ii++;
         //--- Если дошли до последнего пункта в корневом каталоге
         if(nl==m_min_node_level && ii>=m_root_items_total)
           {
            //--- Установим флаг и завершим текущий цикл
            end_list=true;
            break;
           }
         //--- Если до последнего пункта в корневом каталоге ещё не дошли
         else if(nl>m_min_node_level)
           {
            //--- Получим количество пунктов в текущем узле
            int total=(m_file_navigator_mode==FN_ONLY_FOLDERS)? l_folders_total[nl]: l_items_total[nl];
            //--- Если это не последний локальный индекс пункта, перейдём к следующему
            if(ii<total)
               continue;
            //--- Если дошли до последнего локального индекса, то 
            //    нужно вернуться на предыдущий узел и продолжить с пункта, на котором остановились
            while(true)
              {
               //--- Сбросим значения текущего узла в перечисленных ниже массивах
               l_prev_node_list_index[nl] =-1;
               l_item_index[nl]           =-1;
               l_items_total[nl]          =-1;
               //--- Уменьшаем счётчик узлов, пока соблюдается равенство в количестве пунктов в локальных списках 
               //    или не дошли до корневого каталога
               if(l_item_index[nl-1]+1>=l_items_total[nl-1])
                 {
                  if(nl-1==m_min_node_level)
                     break;
                  //---
                  nl--;
                  continue;
                 }
               //---
               break;
              }
            //--- Перейдём на предыдущий узел
            nl=nl-2;
            //--- Обнулим счётчик локальных индексов пунктов и перейдём к следующему узлу
            ii=0;
            break;
           }
        }
     }
//--- Перерисовка списка
   RedrawTreeList();
  }
//+------------------------------------------------------------------+
//| Формирует список содержания                                      |
//+------------------------------------------------------------------+
void CTreeView::FormContentList(void)
  {
//--- Индекс выделенного пункта
   int li=m_selected_item_index;
//--- Освободим массивы списка содержания
   ::ArrayFree(m_cd_item_text);
   ::ArrayFree(m_cd_list_index);
   ::ArrayFree(m_cd_tree_list_index);
//--- Сформируем список содержания
   int items_total=::ArraySize(m_items);
   for(int i=0; i<items_total; i++)
     {
      //--- Если совпадают (1) уровни узлов и (2) локальные индексы пунктов, а также
      //    (3) индекс предыдущего узла с индексом выделенного пункта
      if(m_t_node_level[i]==m_t_node_level[li]+1 && 
         m_t_prev_node_item_index[i]==m_t_item_index[li] &&
         m_t_prev_node_list_index[i]==li)
        {
         //--- Увеличим массивы отображаемых пунктов списка содержания
         int size     =::ArraySize(m_cd_list_index);
         int new_size =size+1;
         ::ArrayResize(m_cd_item_text,new_size);
         ::ArrayResize(m_cd_list_index,new_size);
         ::ArrayResize(m_cd_tree_list_index,new_size);
         //--- Сохраним в массивах текст пункта и общий индекс древовидного списка
         m_cd_item_text[size]       =m_t_item_text[i];
         m_cd_tree_list_index[size] =m_t_list_index[i];
        }
     }
//--- Если в итоге список не пустой, заполним массив общих индексов списка содержания
   int cd_items_total=::ArraySize(m_cd_list_index);
   if(cd_items_total>0)
     {
      //--- Счётчик пунктов
      int c=0;
      //--- Пройдёмся по списку
      int c_items_total=::ArraySize(m_c_list_index);
      for(int i=0; i<c_items_total; i++)
        {
         //--- Если описание и общие индексы пунктов древовидного списка совпадают
         if(m_c_item_text[i]==m_cd_item_text[c] && 
            m_c_tree_list_index[i]==m_cd_tree_list_index[c])
           {
            //--- Сохраним общий индекс списка содержания и перейдём к следующему
            m_cd_list_index[c]=m_c_list_index[i];
            c++;
            //--- Выйти из цикла, если дошли конца отображаемого списка
            if(c>=cd_items_total)
               break;
           }
        }
     }
//--- Перерисовка списка
   RedrawContentList();
  }
//+------------------------------------------------------------------+
//| Перерисовка списка                                               |
//+------------------------------------------------------------------+
void CTreeView::RedrawTreeList(void)
  {
//--- Скрыть пункты списка
   int items_total=::ArraySize(m_items);
   for(int i=0; i<items_total; i++)
      m_items[i].Hide();
//--- Скрыть полосу прокрутки
   m_scrollv.Hide();
//--- Координата Y первого пункта древовидного списка
   int y=1;
//--- Получим количество пунктов
   m_items_total=::ArraySize(m_td_list_index);
//--- Скорректировать размеры полосы прокрутки
   m_scrollv.Reinit(m_items_total,m_visible_items_total);
   m_scrollv.ChangeYSize(m_y_size-2);
   m_scrollv.Update(true);
//--- Расчёт ширины пунктов древовидного списка
   int w=0;
   if(m_show_item_content)
      w=(m_scrollv.IsScroll()) ? m_treeview_width-m_scrollv.ScrollWidth()-2 : m_treeview_width-1;
   else
      w=(m_scrollv.IsScroll()) ? m_treeview_width-m_scrollv.ScrollWidth()-3 : m_treeview_width-2;
//--- Установим новые значения
   for(int i=0; i<m_items_total; i++)
     {
      //--- Расчёт координаты Y для каждого пункта
      y=(i>0)? y+m_item_y_size : y;
      //--- Получим общий индекс пункта в списке
      int li=m_td_list_index[i];
      //--- Обновим координаты и размер
      m_items[li].UpdateY(y);
      m_items[li].UpdateWidth(w);
     }
//--- Показать пункты списка
   for(int i=0; i<items_total; i++)
      m_items[i].Show();
//--- Обновить координаты и размер списка
   ShiftTreeList();
  }
//+------------------------------------------------------------------+
//| Перерисовка списка содержания                                    |
//+------------------------------------------------------------------+
void CTreeView::RedrawContentList(void)
  {
//--- Выйти, если (1) содержание пункта не нужно показывать или (2) включен режим вкладок
   if(!m_show_item_content || m_tab_items_mode)
      return;
//--- Количество пунктов списка
   int content_items_total=::ArraySize(m_content_items);
//--- Если элемент не скрыт, скрыть пункты списка
   if(CElementBase::IsVisible())
     {
      for(int i=0; i<content_items_total; i++)
         m_content_items[i].Hide();
      //--- Скрыть полосу прокрутки
      m_content_scrollv.Hide();
     }
//--- Координата Y первого пункта древовидного списка
   int y=1;
//--- Получим количество пунктов
   int items_total=::ArraySize(m_cd_list_index);
//--- Скорректировать размеры полосы прокрутки
   m_content_scrollv.Reinit(items_total,m_visible_items_total);
   m_content_scrollv.ChangeYSize(m_y_size-2);
   m_content_scrollv.Update(true);
//--- Расчёт ширины пунктов древовидного списка
   int w=(m_content_scrollv.IsScroll()) ? m_x_size-m_treeview_width-m_scrollv.ScrollWidth()-3 : m_x_size-m_treeview_width-2;
//--- Установим новые значения
   for(int i=0; i<items_total; i++)
     {
      //--- Расчёт координаты Y для каждого пункта
      y=(i>0)? y+m_item_y_size : y;
      //--- Получим общий индекс пункта в списке
      int li=m_cd_list_index[i];
      //--- Обновим координаты и размер
      m_content_items[li].UpdateY(y);
      m_content_items[li].UpdateWidth(w);
     }
//--- Если элемент не скрыт, показать пункты списка
   if(CElementBase::IsVisible())
     {
      for(int i=0; i<content_items_total; i++)
         m_content_items[i].Show();
     }
//--- Обновить координаты и размер списка
   ShiftContentList();
  }
//+------------------------------------------------------------------+
//| Обновляет список                                                 |
//+------------------------------------------------------------------+
void CTreeView::UpdateTreeList(void)
  {
   int items_total=::ArraySize(m_td_list_index);
   for(int i=0; i<items_total; i++)
     {
      //--- Получим общий индекс пункта в списке
      int li=m_td_list_index[i];
      //--- Обновим
      m_items[li].Update(true);
     }
  }
//+------------------------------------------------------------------+
//| Обновляет список содержания                                      |
//+------------------------------------------------------------------+
void CTreeView::UpdateContentList(void)
  {
   int items_total=::ArraySize(m_cd_list_index);
   for(int i=0; i<items_total; i++)
     {
      //--- Получим общий индекс пункта в списке
      int li=m_cd_list_index[i];
      //--- Обновим
      m_content_items[li].Update(true);
     }
  }
//+------------------------------------------------------------------+
//| Проверка индекса выделенного пункта на выход из диапазона        |
//+------------------------------------------------------------------+
void CTreeView::CheckSelectedItemIndex(void)
  {
//--- Если индекс не определён
   if(m_selected_item_index==WRONG_VALUE)
     {
      //--- Будет выделен первый пункт в списке
      m_selected_item_index=0;
      return;
     }
//--- Проверка на выход из диапазона
   int array_size=::ArraySize(m_items);
   if(array_size<1 || m_selected_item_index<0 || m_selected_item_index>=array_size)
     {
      //--- Будет выделен первый пункт в списке
      m_selected_item_index=0;
      return;
     }
  }
//+------------------------------------------------------------------+
//| Рисует границу между областями                                   |
//+------------------------------------------------------------------+
void CTreeView::DrawResizeBorder(void)
  {
//--- Выйти, если режим отключен
   if(!m_show_item_content)
      return;
//--- Координаты
   int x=m_treeview_width;
   int y1=0,y2=m_y_size;
//--- Нарисовать линию
   m_canvas.LineVertical(x,y1,y2,::ColorToARGB(m_border_color));
  }
//+------------------------------------------------------------------+
//| Изменить ширину по правому краю формы                            |
//+------------------------------------------------------------------+
void CTreeView::ChangeWidthByRightWindowSide(void)
  {
//--- Выйти, если включен режим фиксации к правому краю формы
   if(m_anchor_right_window_side)
      return;
//--- Координаты и ширина
   int x=0,w=0;
//--- Размеры
   int x_size =m_main.X2()-CElementBase::X()-m_auto_xresize_right_offset;
   int y_size =(m_auto_yresize_mode)? m_main.Y2()-CElementBase::Y()-m_auto_yresize_bottom_offset : m_y_size;
//--- Установить новый размер
   CElementBase::XSize(x_size);
   m_canvas.XSize(x_size);
   m_canvas.Resize(x_size,y_size);
//--- Если без списка содержания
   if(!m_show_item_content)
     {
      //--- Рассчитаем и установим ширину для пунктов в списке
      w=(m_scrollv.IsScroll())? CElementBase::XSize()-m_scrollv.ScrollWidth()-3 : CElementBase::XSize()-2;
      //---
      int v=(m_scrollv.IsScroll())? m_scrollv.CurrentPos() : 0;
      //--- Получим количество отображаемых пунктов в списке содержания
      int total=::ArraySize(m_td_list_index);
      for(int r=0; r<m_visible_items_total; r++)
        {
         //--- Проверка для предотвращения выхода из диапазона
         if(v>=0 && v<total)
           {
            //--- Получим общий индекс пункта древовидного списка
            int li=m_td_list_index[v];
            //--- Установить координаты и ширину
            m_items[li].UpdateWidth(w);
            m_items[li].Draw();
            v++;
           }
        }
     }
//--- Если есть список содержания
   else
     {
      w=(m_content_scrollv.IsScroll())? m_x_size-m_treeview_width-m_content_scrollv.ScrollWidth()-2 : m_x_size-m_treeview_width-2;
      //---
      int v=(m_content_scrollv.IsScroll())? m_content_scrollv.CurrentPos() : 0;
      //--- Получим количество отображаемых пунктов в списке содержания
      int total=::ArraySize(m_cd_list_index);
      for(int r=0; r<m_visible_items_total; r++)
        {
         //--- Проверка для предотвращения выхода из диапазона
         if(v>=0 && v<total)
           {
            //--- Получим общий индекс пункта древовидного списка
            int li=m_cd_list_index[v];
            //--- Установить координаты и ширину
            m_content_items[li].UpdateWidth(w);
            m_content_items[li].Draw();
            v++;
           }
        }
     }
//--- Перерисовать элемент
   Draw();
  }
//+------------------------------------------------------------------+
