//+------------------------------------------------------------------+ //| FileNavigator.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "..\Element.mqh" #include "TreeView.mqh" //+------------------------------------------------------------------+ //| Класс для создания файлового навигатора | //+------------------------------------------------------------------+ class CFileNavigator : public CElement { private: //--- Объекты для создания элемента CTreeView m_treeview; //--- Основные массивы для хранения данных int m_g_list_index[]; // общий индекс int m_g_prev_node_list_index[]; // общий индекс предыдущего узла string m_g_item_text[]; // имя папки/файла int m_g_item_index[]; // локальный индекс int m_g_node_level[]; // уровень узла int m_g_prev_node_item_index[]; // локальный индекс предыдущего узла int m_g_items_total[]; // всего элементов в папке int m_g_folders_total[]; // количество папок в папке bool m_g_is_folder[]; // признак папки bool m_g_item_state[]; // состояние пункта (свёрнут/открыт) //--- Вспомогательные массивы для сбора данных int m_l_prev_node_list_index[]; string m_l_item_text[]; string m_l_path[]; int m_l_item_index[]; int m_l_item_total[]; int m_l_folders_total[]; //--- Ширина области древовидного списка int m_treeview_width; //--- Картинки для (1) папок и (2) файлов string m_file_icon; string m_folder_icon; //--- Текущий путь относительно файловой "песочницы" терминала string m_current_path; //--- Текущий полный путь относительно файловой системы включая метку тома жёсткого диска string m_current_full_path; //--- Область текущей директории int m_directory_area; //--- Режим содержания файлового навигатора ENUM_FILE_NAVIGATOR_CONTENT m_navigator_content; //--- public: CFileNavigator(void); ~CFileNavigator(void); //--- Методы для создания файлового навигатора bool CreateFileNavigator(const int x_gap,const int y_gap); //--- private: void InitializeProperties(const int x_gap,const int y_gap); bool CreateCanvas(void); bool CreateTreeView(void); //--- public: //--- (1) Возвращает указатель древовидного списка, // (2) режим навигатора (Показывать все/Только папки), (3) содержание навигатора (Общая папка/Локальная/Всё) CTreeView *GetTreeViewPointer(void) { return(::GetPointer(m_treeview)); } void NavigatorMode(const ENUM_FILE_NAVIGATOR_MODE mode) { m_treeview.NavigatorMode(mode); } void NavigatorContent(const ENUM_FILE_NAVIGATOR_CONTENT mode) { m_navigator_content=mode; } //--- (1) ширина древовидного списка, (2) установка пути к файлам для файлов и папок void TreeViewWidth(const int x_size) { m_treeview_width=x_size; } void FileIcon(const string file_path) { m_file_icon=file_path; } void FolderIcon(const string file_path) { m_folder_icon=file_path; } //--- Возвращает (1) текущий путь и (2) полный путь, (3) выделенный файл string CurrentPath(void) const { return(m_current_path); } string CurrentFullPath(void) const { return(m_current_full_path); } //--- Возвращает (1) область директории и (2) выделенный файл int DirectoryArea(void) const { return(m_directory_area); } string SelectedFile(void) const { return(m_treeview.SelectedItemFileName()); } //--- public: //--- Обработчик событий графика virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- Управление virtual void Delete(void); //--- Рисует элемент virtual void Draw(void); //--- private: //--- Обработка события выбора нового пути в древовидном списке void OnChangeTreePath(void); //--- Заполняет массивы параметрами элементов файловой системы терминала void FillArraysData(void); //--- Читает файловую систему и записывает параметры в массивы void FileSystemScan(const int root_index,int &list_index,int &node_level,int &item_index,int search_area); //--- Изменяет размер вспомогательных массивов относительно текущего уровня узла void AuxiliaryArraysResize(const int node_level); //--- Определяет, передано имя папки или файла bool IsFolder(const string file_name); //--- Возвращает количество (1) элементов и (2) папок в указанной директории int ItemsTotal(const string search_path,const int mode); int FoldersTotal(const string search_path,const int mode); //--- Возвращает локальный индекс предыдущего узла относительно переданных параметров int PrevNodeItemIndex(const int root_index,const int node_level); //--- Добавляет пункт в массивы void AddItem(const int list_index,const string item_text,const int node_level,const int prev_node_item_index, const int item_index,const int items_total,const int folders_total,const bool is_folder); //--- Переход на следующий узел void ToNextNode(const int root_index,int list_index,int &node_level, int &item_index,long &handle,const string item_text,const int search_area); virtual void DrawText(void); //--- Изменить ширину по правому краю окна virtual void ChangeWidthByRightWindowSide(void); }; //+------------------------------------------------------------------+ //| Constructor | //+------------------------------------------------------------------+ CFileNavigator::CFileNavigator(void) : m_current_path(""), m_current_full_path(""), m_treeview_width(200), m_directory_area(FILE_COMMON), m_navigator_content(FN_ONLY_MQL), m_file_icon("Images\\EasyAndFastGUI\\Icons\\bmp16\\text_file_w10.bmp"), m_folder_icon("Images\\EasyAndFastGUI\\Icons\\bmp16\\folder_w10.bmp") { //--- Сохраним имя класса элемента в базовом классе CElementBase::ClassName(CLASS_NAME); } //+------------------------------------------------------------------+ //| Destructor | //+------------------------------------------------------------------+ CFileNavigator::~CFileNavigator(void) { } //+------------------------------------------------------------------+ //| Обработчик событий | //+------------------------------------------------------------------+ void CFileNavigator::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Обработка события "Изменение пути в древовидном списке" if(id==CHARTEVENT_CUSTOM+ON_CHANGE_TREE_PATH) { OnChangeTreePath(); //--- Отобразим текущий путь в адресной строке Update(true); return; } } //+------------------------------------------------------------------+ //| Создаёт файловый навигатор | //+------------------------------------------------------------------+ bool CFileNavigator::CreateFileNavigator(const int x_gap,const int y_gap) { //--- Выйти, если нет указателя на главный элемент if(!CElement::CheckMainPointer()) return(false); //--- Сканируем файловую систему терминала и заносим данные в массивы FillArraysData(); //--- Инициализация свойств InitializeProperties(x_gap,y_gap); //--- Создание элемента if(!CreateTreeView()) return(false); if(!CreateCanvas()) return(false); //--- Отобразим текущий путь в адресной строке OnChangeTreePath(); return(true); } //+------------------------------------------------------------------+ //| Инициализация свойств | //+------------------------------------------------------------------+ void CFileNavigator::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_y_size<1)? 20 : m_y_size; //--- Цвета по умолчанию m_border_color =(m_border_color!=clrNONE)? m_border_color : C'150,170,180'; m_label_color =(m_label_color!=clrNONE)? m_label_color : clrBlack; //--- Отступы от крайней точки CElementBase::XGap(x_gap); CElementBase::YGap(y_gap); } //+------------------------------------------------------------------+ //| Создаёт объект для рисования | //+------------------------------------------------------------------+ bool CFileNavigator::CreateCanvas(void) { //--- Формирование имени объекта string name=CElementBase::ElementName("file_navigator"); //--- Создание объекта if(!CElement::CreateCanvas(name,m_x,m_y,m_treeview.XSize(),m_y_size)) return(false); //--- return(true); } //+------------------------------------------------------------------+ //| Создаёт древовидный список | //+------------------------------------------------------------------+ #resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\folder_w10.bmp" #resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\text_file_w10.bmp" //--- bool CFileNavigator::CreateTreeView(void) { //--- Сохраним указатель на окно m_treeview.MainPointer(this); //--- Координаты int x=0,y=m_y_size-1; //--- Свойства m_treeview.XSize(m_x_size); m_treeview.TreeViewWidth(m_treeview_width); m_treeview.ResizeListMode(true); m_treeview.ShowItemContent(true); m_treeview.AutoXResizeMode(CElementBase::AutoXResizeMode()); m_treeview.AutoXResizeRightOffset(0); m_treeview.AnchorRightWindowSide(CElementBase::AnchorRightWindowSide()); m_treeview.AnchorBottomWindowSide(CElementBase::AnchorBottomWindowSide()); //--- Формируем массивы древовидного списка int items_total=::ArraySize(m_g_item_text); for(int i=0; i-1) { //--- Получим адрес общей папки терминалов string common_path=::TerminalInfoString(TERMINAL_COMMONDATA_PATH); //--- Удалим в строке (принятой в событии) префикс "Common\" path=::StringSubstr(path,7,::StringLen(common_path)-7); //--- Сформируем путь (краткую и полную версию) m_current_path =::StringSubstr(path,6,::StringLen(path)-6); m_current_full_path =common_path+"\\"+path; //--- Сохраним область директории m_directory_area=FILE_COMMON; } //--- Если это локальная папка терминала else if(::StringFind(path,"MQL5\\Files\\",0)>-1) { //--- Получим адрес данных в локальной папке терминала string local_path=::TerminalInfoString(TERMINAL_DATA_PATH); //--- Сформируем путь (краткую и полную версию) m_current_path =::StringSubstr(path,11,::StringLen(path)-11); m_current_full_path =local_path+"\\"+path; //--- Сохраним область директории m_directory_area=0; } } //+------------------------------------------------------------------+ //| Заполняет массивы параметрами элементов файловой системы | //+------------------------------------------------------------------+ void CFileNavigator::FillArraysData(void) { //--- Счётчики (1) общих индексов, (2) уровней узлов, (3) локальных индексов int list_index =0; int node_level =0; int item_index =0; //--- Если нужно отображать обе директории (Общая (0)/Локальная (1)) int begin=0,end=1; //--- Если нужно отображать содержимое только локальной директории if(m_navigator_content==FN_ONLY_MQL) begin=1; //--- Если нужно отображать содержимое только общей директории else if(m_navigator_content==FN_ONLY_COMMON) begin=end=0; //--- Пройдёмся по указанным директориям for(int root_index=begin; root_index<=end; root_index++) { //--- Определим директорию для сканирования файловой структуры int search_area=(root_index>0) ? 0 : FILE_COMMON; //--- Обнулим счётчик локальных индексов item_index=0; //--- Увеличим размер массивов на один элемент (относительно уровня узла) AuxiliaryArraysResize(node_level); //--- Получим кол-во файлов и папок в указанной директории (* - проверить все файлы/папки) string search_path =m_l_path[0]+"*"; m_l_item_total[0] =ItemsTotal(search_path,search_area); m_l_folders_total[0] =FoldersTotal(search_path,search_area); //--- Добавим пункт с названием корневого каталога в начало списка string item_text=(root_index>0)? "MQL5\\Files\\" : "Common\\Files\\"; AddItem(list_index,item_text,0,0,root_index,m_l_item_total[0],m_l_folders_total[0],true); //--- Увеличим счётчики общих индексов и уровней узлов list_index++; node_level++; //--- Увеличим размер массивов на один элемент (относительно уровня узла) AuxiliaryArraysResize(node_level); //--- Инициализация первых элементов для директории локальной папки терминала if(root_index>0) { m_l_item_index[0] =root_index; m_l_prev_node_list_index[0] =list_index-1; } //--- Сканируем директории и заносим данные в массивы FileSystemScan(root_index,list_index,node_level,item_index,search_area); } } //+------------------------------------------------------------------+ //| Читает файловую систему терминала и записывает | //| параметры элементов в массивы | //+------------------------------------------------------------------+ void CFileNavigator::FileSystemScan(const int root_index,int &list_index,int &node_level,int &item_index,int search_area) { long search_handle =INVALID_HANDLE; // Хэндл поиска папки/файла string file_name =""; // Имя найденного элемента (файла/папки) string filter ="*"; // Фильтр поиска (* - проверить все файлы/папки) //--- Сканируем директории и заносим данные в массивы while(!::IsStopped()) { //--- Если это начало списка директории if(item_index==0) { //--- Путь для поиска всех элементов string search_path=m_l_path[node_level]+filter; //--- Получаем хэндл и имя первого файла search_handle=::FileFindFirst(search_path,file_name,search_area); //--- Получим кол-во файлов и папок в указанной директории m_l_item_total[node_level] =ItemsTotal(search_path,search_area); m_l_folders_total[node_level] =FoldersTotal(search_path,search_area); } //--- Если индекс этого узла уже был, перейдём к следующему файлу if(m_l_item_index[node_level]>-1 && item_index<=m_l_item_index[node_level]) { //--- Увеличим счётчик локальных индексов item_index++; //--- Переходим к следующему элементу ::FileFindNext(search_handle,file_name); continue; } //--- Если дошли до конца списка в корневом узле, закончим цикл if(node_level==1 && item_index>=m_l_item_total[node_level]) break; //--- Если дошли до конца списка в любом узле, кроме корневого else if(item_index>=m_l_item_total[node_level]) { //--- Перевести счётчик узлов на один уровень назад node_level--; //--- Обнулить счётчик локальных индексов item_index=0; //--- Закрываем хэндл поиска ::FileFindClose(search_handle); continue; } //--- Если это папка if(IsFolder(file_name)) { //--- Перейдём на следующий узел ToNextNode(root_index,list_index,node_level,item_index,search_handle,file_name,search_area); //--- Увеличить счётчик общих индексов и начать новую итерацию list_index++; continue; } //--- Получим локальный индекс предыдущего узла int prev_node_item_index=PrevNodeItemIndex(root_index,node_level); //--- Добавим пункт с указанными данными в общие массивы AddItem(list_index,file_name,node_level,prev_node_item_index,item_index,0,0,false); //--- Увеличим счётчик общих индексов list_index++; //--- Увеличим счётчик локальных индексов item_index++; //--- Переходим к следующему элементу ::FileFindNext(search_handle,file_name); } //--- Закрываем хэндл поиска ::FileFindClose(search_handle); } //+------------------------------------------------------------------+ //| Изменяет размер вспомогательных массивов | //| относительно текущего уровня узла | //+------------------------------------------------------------------+ void CFileNavigator::AuxiliaryArraysResize(const int node_level) { //--- Изменить размер массивов int new_size=node_level+1; ::ArrayResize(m_l_prev_node_list_index,new_size); ::ArrayResize(m_l_item_text,new_size); ::ArrayResize(m_l_path,new_size); ::ArrayResize(m_l_item_index,new_size); ::ArrayResize(m_l_item_total,new_size); ::ArrayResize(m_l_folders_total,new_size); //--- Инициализация последнего значения m_l_prev_node_list_index[node_level] =0; m_l_item_text[node_level] =""; m_l_path[node_level] =""; m_l_item_index[node_level] =-1; m_l_item_total[node_level] =0; m_l_folders_total[node_level] =0; } //+------------------------------------------------------------------+ //| Определяет, передано имя папки или файла | //+------------------------------------------------------------------+ bool CFileNavigator::IsFolder(const string file_name) { //--- Если в имени есть символы "\\", то это папка if(::StringFind(file_name,"\\",0)>-1) return(true); //--- return(false); } //+------------------------------------------------------------------+ //| Считает количество файлов в текущей директории | //+------------------------------------------------------------------+ int CFileNavigator::ItemsTotal(const string search_path,const int search_area) { int counter =0; // счётчик элементов string file_name =""; // имя файла long search_handle =INVALID_HANDLE; // хэндл поиска //--- Получаем первый файл в текущей директории search_handle=::FileFindFirst(search_path,file_name,search_area); //--- Если директория не пуста if(search_handle!=INVALID_HANDLE && file_name!="") { //--- Посчитаем количество объектов в текущей директории counter++; while(::FileFindNext(search_handle,file_name)) counter++; } //--- Закрываем хэндл поиска ::FileFindClose(search_handle); return(counter); } //+------------------------------------------------------------------+ //| Считает количество папок в текущей директории | //+------------------------------------------------------------------+ int CFileNavigator::FoldersTotal(const string search_path,const int search_area) { int counter =0; // счётчик элементов string file_name =""; // имя файла long search_handle =INVALID_HANDLE; // хэндл поиска //--- Получаем первый файл в текущей директории search_handle=::FileFindFirst(search_path,file_name,search_area); //--- Если не путо, то в цикле считаем кол-во объектов в текущей директории if(search_handle!=INVALID_HANDLE && file_name!="") { //--- Если это папка, увеличим счётчик if(IsFolder(file_name)) counter++; //--- Пройдёмся далее по списку и посчитаем другие папки while(::FileFindNext(search_handle,file_name)) { if(IsFolder(file_name)) counter++; } } //--- Закрываем хэндл поиска ::FileFindClose(search_handle); return(counter); } //+------------------------------------------------------------------+ //| Возвращает локальный индекс предыдущего узла | //| относительно переданных параметров | //+------------------------------------------------------------------+ int CFileNavigator::PrevNodeItemIndex(const int root_index,const int node_level) { int prev_node_item_index=0; //--- Если не в корневом каталоге if(node_level>1) prev_node_item_index=m_l_item_index[node_level-1]; else { //--- Если не первый элемент списка if(root_index>0) prev_node_item_index=m_l_item_index[node_level-1]; } //--- Вернём локальный индекс предыдущего узла return(prev_node_item_index); } //+------------------------------------------------------------------+ //| Добавляет пункт с указанными параметрами в массивы | //+------------------------------------------------------------------+ void CFileNavigator::AddItem(const int list_index,const string item_text,const int node_level,const int prev_node_item_index, const int item_index,const int items_total,const int folders_total,const bool is_folder) { //--- Резервный размер массива int reserve_size=100000; //--- Увеличим размер массивов на один элемент int array_size =::ArraySize(m_g_list_index); int new_size =array_size+1; ::ArrayResize(m_g_prev_node_list_index,new_size,reserve_size); ::ArrayResize(m_g_list_index,new_size,reserve_size); ::ArrayResize(m_g_item_text,new_size,reserve_size); ::ArrayResize(m_g_item_index,new_size,reserve_size); ::ArrayResize(m_g_node_level,new_size,reserve_size); ::ArrayResize(m_g_prev_node_item_index,new_size,reserve_size); ::ArrayResize(m_g_items_total,new_size,reserve_size); ::ArrayResize(m_g_folders_total,new_size,reserve_size); ::ArrayResize(m_g_is_folder,new_size,reserve_size); //--- Сохраним значения переданных параметров m_g_prev_node_list_index[array_size] =(node_level==0)? -1 : m_l_prev_node_list_index[node_level-1]; m_g_list_index[array_size] =list_index; m_g_item_text[array_size] =item_text; m_g_item_index[array_size] =item_index; m_g_node_level[array_size] =node_level; m_g_prev_node_item_index[array_size] =prev_node_item_index; m_g_items_total[array_size] =items_total; m_g_folders_total[array_size] =folders_total; m_g_is_folder[array_size] =is_folder; } //+------------------------------------------------------------------+ //| Переход на следующий узел | //+------------------------------------------------------------------+ void CFileNavigator::ToNextNode(const int root_index,int list_index,int &node_level, int &item_index,long &handle,const string item_text,const int search_area) { //--- Фильтр поиска (* - проверить все файлы/папки) string filter="*"; //--- Сформируем путь string search_path=m_l_path[node_level]+item_text+filter; //--- Получим и сохраним данные m_l_item_total[node_level] =ItemsTotal(search_path,search_area); m_l_folders_total[node_level] =FoldersTotal(search_path,search_area); m_l_item_text[node_level] =item_text; m_l_item_index[node_level] =item_index; m_l_prev_node_list_index[node_level] =list_index; //--- Получим индекс пункта предыдущего узла int prev_node_item_index=PrevNodeItemIndex(root_index,node_level); //--- Добавим пункт с указанными данными в общие массивы AddItem(list_index,item_text,node_level,prev_node_item_index, item_index,m_l_item_total[node_level],m_l_folders_total[node_level],true); //--- Увеличим счётчик узлов node_level++; //--- Увеличим размер массивов на один элемент AuxiliaryArraysResize(node_level); //--- Получим и сохраним данные m_l_path[node_level] =m_l_path[node_level-1]+item_text; m_l_item_total[node_level] =ItemsTotal(m_l_path[node_level]+filter,search_area); m_l_folders_total[node_level] =FoldersTotal(m_l_path[node_level]+item_text+filter,search_area); //--- Обнулить счётчик локальных индексов item_index=0; //--- Закрываем хэндл поиска ::FileFindClose(handle); } //+------------------------------------------------------------------+ //| Удаление | //+------------------------------------------------------------------+ void CFileNavigator::Delete(void) { CElement::Delete(); //--- Освобождение массивов элемента ::ArrayFree(m_g_prev_node_list_index); ::ArrayFree(m_g_list_index); ::ArrayFree(m_g_item_text); ::ArrayFree(m_g_item_index); ::ArrayFree(m_g_node_level); ::ArrayFree(m_g_prev_node_item_index); ::ArrayFree(m_g_items_total); ::ArrayFree(m_g_folders_total); ::ArrayFree(m_g_item_state); //--- ::ArrayFree(m_l_prev_node_list_index); ::ArrayFree(m_l_item_text); ::ArrayFree(m_l_path); ::ArrayFree(m_l_item_index); ::ArrayFree(m_l_item_total); ::ArrayFree(m_l_folders_total); //--- Обнулить переменные m_current_path=""; } //+------------------------------------------------------------------+ //| Рисует элемент | //+------------------------------------------------------------------+ void CFileNavigator::Draw(void) { //--- Нарисовать фон CElement::DrawBackground(); //--- Нарисовать рамку CElement::DrawBorder(); //--- Нарисовать текст DrawText(); } //+------------------------------------------------------------------+ //| Рисует текст | //+------------------------------------------------------------------+ void CFileNavigator::DrawText(void) { //--- Координаты int x=5,y=m_y_size>>1; //--- Свойства текста m_canvas.FontSet(CElement::Font(),-CElement::FontSize()*10,FW_NORMAL); //--- Если путь ещё не установлен, показать строку по умолчанию if(m_current_full_path=="") m_current_full_path="Loading. Please wait..."; //--- Выведем путь в адресную строку файлового навигатора m_canvas.TextOut(x,y,m_current_full_path,::ColorToARGB(m_label_color),TA_LEFT|TA_VCENTER); } //+------------------------------------------------------------------+ //| Изменить ширину по правому краю формы | //+------------------------------------------------------------------+ void CFileNavigator::ChangeWidthByRightWindowSide(void) { //--- Выйти, если включен режим привязки к правой части окна if(m_anchor_right_window_side) return; //--- Рассчитать и установить новый размер фону элемента int x_size=m_main.X2()-CElementBase::X()-m_auto_xresize_right_offset; //--- Установить новый размер CElementBase::XSize(x_size); m_canvas.XSize(x_size); m_canvas.Resize(x_size,m_y_size); //--- Перерисовать элемент Draw(); } //+------------------------------------------------------------------+