EasyAndFastMod/Controls/TreeView.mqh
2026-01-15 07:23:17 -05:00

1828 строки
150 КиБ
MQL5

//+------------------------------------------------------------------+
//| 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();
}
//+------------------------------------------------------------------+