Interfaces Gráficas VIII: O Controle Navegador de Arquivos (Capítulo 3)
Conteúdo
- Introdução
- O Controle Navegador de Arquivos
- Desenvolvimento da Classe CFileNavigator
- Organização da Estrutura Hierárquica do Sistema de Arquivos
- Métodos para a Criação do Controle
- Manipulador de Eventos
- Integração do Controle para o Motor da Biblioteca
- Teste do Navegador de Arquivos
- Conclusão
Introdução
O primeiro artigo Interfaces gráficas I: Preparação da Estrutura da Biblioteca (Capítulo 1) explica em detalhes a finalidade desta biblioteca. Você irã encontrar uma lista de artigos com os links no final de cada capítulo. Lá, você também pode encontrar e baixar a versão completa da biblioteca, no estágio de desenvolvimento atual. Os arquivos devem estar localizados nas mesmas pastas que o arquivo baixado.
No primeiro e segundo capítulos da oitava parte da série, nossa biblioteca foi reforçada por várias classes para o desenvolvimento de ponteiros para o cursor do mouse (CPointer), calendários (classes CCalendar e CDropCalendar) e as listas hierárquicas (classes CTreeItem e CTreeView). Neste artigo, nós vamos desenvolver ainda mais o assunto do capítulo anterior, bem como examinar o controle navegador de arquivos.
O Controle Navegador de Arquivos
O navegador de arquivos é uma espécie de guia que lhe permite visualizar os elementos do sistema de arquivos de forma hierárquica, através da interface gráfica do programa. Além disso, ele fornece acesso a cada elemento da hierarquia, permitindo que você execute determinadas ações, como abrir um arquivo para visualizar os dados, salvar os dados em um arquivo, mover um arquivo, etc.
Este artigo trata da primeira versão do navegador de arquivos. Ele oferece aos usuários as seguintes opções:
- navegação dos arquivos no "ambiente protegido" do terminal dentro de uma interface gráfica da aplicação MQL;
- encontrar pastas e arquivos necessários tanto na pasta comum dos terminais quanto na pasta local do terminal do cliente;
- salvar o caminho para o acesso programático posterior à uma pasta ou um arquivo selecionado no navegador de arquivos.
Nota:
Por razões de segurança, a manipulação de arquivos é estritamente controlada na linguagem MQL5. Os arquivos que são processados por meio da linguagem MQL5 estão sempre localizados no "ambiente protegido" de arquivos. Um arquivo é aberto na pasta do terminal do cliente em MQL5\Files (ou na pasta_do_agente_de_teste\MQL5\Files no caso de teste). Se o sinalizador FILE_COMMON é especificado entre outras possíveis, o arquivo é aberto na pasta comum de todos os terminais do cliente \Terminal\Common\Files.
Desenvolvimento da Classe CFileNavigator
No atual estágio de desenvolvimento, nós já temos todas as ferramentas necessárias na biblioteca para o desenvolvimento de um navegador de arquivos. O controle lista hierárquica descrito anteriormente serve de base para a criação dos navegadores de arquivos. Além de uma lista hierárquica com uma área de conteúdo, nós precisamos desenvolver uma barra de endereço que contém o caminho completo em do elemento selecionado na lista hierárquica.
Vamos fornecer uma oportunidade de selecionar as pastas para serem exibidas na pasta raiz. Por exemplo, nós podemos usar apenas uma das pastas de arquivos do "ambiente protegido" do terminal ou fornecer acesso aos dois. Para fazer isso, adicione a enumeração ENUM_FILE_NAVIGATOR_CONTENT para o arquivo Enums.mqh (veja o código abaixo):
- FN_BOTH - mostra ambas as pastas.
- FN_ONLY_MQL - mostra apenas a pasta local do terminal do cliente.
- FN_ONLY_COMMON - mostra apenas a pasta comum de todos os terminais instalados.
//+------------------------------------------------------------------+ //| Enumeração do conteúdo do navegador de arquivos | //+------------------------------------------------------------------+ enum ENUM_FILE_NAVIGATOR_CONTENT { FN_BOTH =0, FN_ONLY_MQL =1, FN_ONLY_COMMON =2 };
Cria o arquivo FileNavigator.mqh com a classe CFileNavigator e inclui ele ao motor da biblioteca (arquivo WndContainer.mqh):
//+------------------------------------------------------------------+ //| WndContainer.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "FileNavigator.mqh"
O conjunto padrão de métodos para todos os controles da biblioteca devem ser implementados na classe CFileNavigator, como é exibido no código abaixo:
//+------------------------------------------------------------------+ //| Classe para a criação do navegador de arquivos | //+------------------------------------------------------------------+ class CFileNavigator : public CElement { private: //--- Ponteiro para o formulário que este elemento está anexado CWindow *m_wnd; //--- public: CFileNavigator(void); ~CFileNavigator(void); //--- Armazena o ponteiro do formulário void WindowPointer(CWindow &object) { m_wnd=::GetPointer(object); } //--- public: //--- Manipulador de eventos do gráfico virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- Timer virtual void OnEventTimer(void) {} //--- Move o elemento virtual void Moving(const int x,const int y); //--- (1) Exibe, (2) oculta, (3) reseta, (4) remove virtual void Show(void); virtual void Hide(void); virtual void Reset(void); virtual void Delete(void); //--- (1) Definir (2), resetar as prioridades para o clique esquerdo do mouse virtual void SetZorders(void); virtual void ResetZorders(void); //--- Reseta a cor virtual void ResetColors(void) {} };
Vamos listar as propriedades disponíveis aos usuários da biblioteca para configurar o navegador de arquivos.
- Largura da área da lista hierárquica
- Largura da área de conteúdo
- Cor de fundo da área
- Cor do quadro
- Cor de fundo da barra de endereço
- Cor do texto da barra de endereço
- Altura da barra de endereço
- Imagens para as pastas e arquivos
- Modo do navegador (Mostrar tudo/Somente as pastas)
- Modo do conteúdo do navegador de arquivos (Pasta comum /Local/Tudo)
//+------------------------------------------------------------------+ //| Classe para a criação do navegador de arquivos | //+------------------------------------------------------------------+ class CFileNavigator : public CElement { private: //--- Largura da área da lista hierárquica int m_treeview_area_width; //--- Largura da área de conteúdo int m_content_area_width; //--- Cor de fundo e do quadro de fundo color m_area_color; color m_area_border_color; //--- Cor de fundo da barra de endereço color m_address_bar_back_color; //--- Cor do texto da barra de endereço color m_address_bar_text_color; //--- Altura da barra de endereço int m_address_bar_y_size; //--- Imagens para (1) as pastas e (2) arquivos string m_file_icon; string m_folder_icon; //--- Modo do conteúdo do navegador de arquivos ENUM_FILE_NAVIGATOR_CONTENT m_navigator_content; //--- Prioridades do clique do botão esquerdo do mouse int m_zorder; //--- public: //--- (1) Modo navegador (Mostra tudo/Somente as pastas), (2) o conteúdo do navegador (Pasta Comum/Local/Tudo) 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) Altura da barra de endereço, (2) largura da lista hierárquica e (3) a lista de conteúdo void AddressBarYSize(const int y_size) { m_address_bar_y_size=y_size; } void TreeViewAreaWidth(const int x_size) { m_treeview_area_width=x_size; } void ContentAreaWidth(const int x_size) { m_content_area_width=x_size; } //--- (1) Cor de fundo e do (2) quadro de fundo void AreaBackColor(const color clr) { m_area_color=clr; } void AreaBorderColor(const color clr) { m_area_border_color=clr; } //--- Cor do (1) plano de fundo e (2) do texto da barra de endereço void AddressBarBackColor(const color clr) { m_address_bar_back_color=clr; } void AddressBarTextColor(const color clr) { m_address_bar_text_color=clr; } //--- Define o caminho dos (1) arquivos e (2) pastas void FileIcon(const string file_path) { m_file_icon=file_path; } void FolderIcon(const string file_path) { m_folder_icon=file_path; } };
A inicialização dos campos de propriedades com os valores padrão é realizada no construtor da classe (veja o código abaixo). As imagens para a pasta padrão e o navegador de arquivos estão ligadas à biblioteca como resources - recursos. Eles podem ser baixados a partir do arquivo anexado abaixo.
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\folder.bmp" #resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\text_file.bmp" //+------------------------------------------------------------------+ //| Construtor | //+------------------------------------------------------------------+ CFileNavigator::CFileNavigator(void) : m_address_bar_y_size(20), m_treeview_area_width(300), m_content_area_width(0), m_navigator_content(FN_ONLY_MQL), m_area_border_color(clrLightGray), m_address_bar_back_color(clrWhiteSmoke), m_address_bar_text_color(clrBlack), m_file_icon("Images\\EasyAndFastGUI\\Icons\\bmp16\\text_file.bmp"), m_folder_icon("Images\\EasyAndFastGUI\\Icons\\bmp16\\folder.bmp") { //--- Armazena o nome da classe do elemento na classe base CElement::ClassName(CLASS_NAME); //--- Define as prioridades do botão esquerdo do mouse m_zorder=0; }
A fim de criar o navegador de arquivos, nós precisamos de dois métodos privados e um público. O sistema hierárquico da estrutura do arquivo é implementado utilizando a classe CTreeView descrita no artigo anterior. O arquivo com esta classe deve ser incluído no arquivo FileNavigator.mqh.
//+------------------------------------------------------------------+ //| FileNavigator.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" #include "TreeView.mqh" //+------------------------------------------------------------------+ //| Classe para a criação do navegador de arquivos | //+------------------------------------------------------------------+ class CFileNavigator : public CElement { private: //--- Objetos para criar o controle CRectCanvas m_address_bar; CTreeView m_treeview; //--- public: //--- Métodos para criar o navegador de arquivos bool CreateFileNavigator(const long chart_id,const int subwin,const int x,const int y); //--- private: bool CreateAddressBar(void); bool CreateTreeView(void); };
Antes de descrever os métodos para o desenvolvimento do navegador de arquivos em detalhes, vamos primeiro examinar a organização de estrutura do sistema de arquivos hierárquicos do terminal.
Organização da Estrutura Hierárquica do Sistema de Arquivos
Antes de criar o navegador de arquivos, nós devemos primeiramente varrer o sistema de arquivos e salvar os parâmetros do sistema de todos os elementos para desenvolver os arquivos do terminal Todos esses parâmetros foram examinados em detalhes no artigo anterior. Portanto, é exibido abaixo somente uma lista de arrays dos parâmetros para serem salvos para o desenvolvimento de uma lista hierárquica.
- Índice geral
- Índice geral de um nó anterior
- Nome da pasta/arquivo
- Índice local
- Nível do nó
- Índice local do nó anterior
- Número total de elementos na pasta
- Número de pastas dentro da pasta
- Sinalização da pasta
- Estado do elemento (minimizado/aberto)
A fim de preparar os parâmetros, nós precisamos de duas listas de arrays - principal (prefixo g) e auxiliar (prefixo l):
class CFileNavigator : public CElement { private: //--- Principais arrays para o armazenamento de dados int m_g_list_index[]; // índice geral int m_g_prev_node_list_index[]; // índice geral do nó anterior string m_g_item_text[]; // nome do arquivo/pasta int m_g_item_index[]; // índice local int m_g_node_level[]; // nível do nó int m_g_prev_node_item_index[]; // índice local do nó anterior int m_g_items_total[]; // número total de elementos na pasta int m_g_folders_total[]; // número total de pastas dentro da pasta bool m_g_is_folder[]; // atributo da pasta bool m_g_item_state[]; // estado do elemento(minimizado/aberto) //--- Arrays auxiliares para a coleta dos dados 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[]; };
Nós precisamos de um número de métodos adicionais para varrer o sistema de arquivos do terminal, reunir todos os dados e salvá-lo nos arrays. O método CFileNavigator::AuxiliaryArraysResize() é necessário para trabalhar com os arrays auxiliares. Ele permite alterar o seu volume em relação ao nível do nó atualmente usado (ver o código abaixo). Em outras palavras, o tamanho do array excede o valor do nível do nó atual para ser passado para o método como um único argumento por um elemento. Se o valor do nível do nó atual é 0, o tamanho do array é definido como 1, se o nível do nó é 1, o tamanho do array é 2, etc. Uma vez que o nível do nó durante a recolha de dados pode aumentar ou diminuir, o tamanho do array é alterado em conformidade. O mesmo método é usado para inicializar o elemento do array do nó atual.
class CFileNavigator : public CElement { private: //--- Aumenta o tamanho do array por um elemento void AuxiliaryArraysResize(const int node_level); }; //+------------------------------------------------------------------+ //| Aumenta o tamanho dos arrays adicionais por um elemento | //+------------------------------------------------------------------+ 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); //--- Inicializa o último valor 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; }
O método CFileNavigator::AddItem() é usado para adicionar um elemento com os parâmetros aos arrays principais. Aqui, os arrays são incrementados por um elemento em cada chamada de método. Os valores dos argumentos passados são guardadas para a última célula do elemento.
class CFileNavigator : public CElement { private: //--- Adiciona o elemento para os arrays 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); }; //+------------------------------------------------------------------+ //| Adiciona o elemento com os parâmetros especificados aos arrays | //+------------------------------------------------------------------+ 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) { //--- Aumenta o tamanho do array por um elemento int array_size =::ArraySize(m_g_list_index); int new_size =array_size+1; ::ArrayResize(m_g_prev_node_list_index,new_size); ::ArrayResize(m_g_list_index,new_size); ::ArrayResize(m_g_item_text,new_size); ::ArrayResize(m_g_item_index,new_size); ::ArrayResize(m_g_node_level,new_size); ::ArrayResize(m_g_prev_node_item_index,new_size); ::ArrayResize(m_g_items_total,new_size); ::ArrayResize(m_g_folders_total,new_size); ::ArrayResize(m_g_is_folder,new_size); //--- Armazenar o valor dos parâmetros passados 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; }
Ao varrer o sistema de arquivos, é necessário mover para cada pasta detectada para exibir o seu conteúdo. Para alcançar este objetivo, o método CFileNavigator::IsFolder() é utilizado. Ele define se o elemento atual é uma pasta ou um arquivo. O nome do elemento do sistema de arquivos deve ser passado para ele como um único parâmetro. Se o caractere "\" é detectado no nome do elemento, isto significa que o método retorna true. Se este for um arquivo, o método retorna false.
A função FileIsExist() do sistema é uma outra maneira de verificar se o elemento é um arquivo. A função retorna true se o nome do elemento passado na última busca é um arquivo. Se este é uma pasta, a função gera o erro ERR_FILE_IS_DIRECTORY.
class CFileNavigator : public CElement { private: //--- Determina se o nome do arquivo ou pasta foi passado bool IsFolder(const string file_name); }; //+------------------------------------------------------------------+ //| Determina se um nome de arquivo ou pasta foi passada | //+------------------------------------------------------------------+ bool CFileNavigator::IsFolder(const string file_name) { //--- Se o nome contém os caracteres "\\", então ele é uma pasta if(::StringFind(file_name,"\\",0)>-1) return(true); //--- return(false); }
A fim de formar uma lista hierárquica, especifique o número de elementos no item, assim como o número de elementos que são pastas. Portanto, nós precisamos de métodos adequados para a definição dos valores dos parâmetros. Os métodos são CFileNavigator::ItemsTotal() e CFileNavigator:: FoldersTotal(). Eles são semelhantes, embora no segundo o contador é aumentado somente se um elemento verificado for uma pasta. Dois argumentos são passados para as duas metodologias: (1) caminho e (2) a área de busca. A área de pesquisa é uma pasta comum de todos os terminais ou uma pasta local do terminal. Em seguida, a função do sistema FileFindFirst() é utilizada numa tentativa para receber o manipulador de pesquisa no caminho especificado, bem como o primeiro nome do elemento (se for encontrado) simultaneamente. O manipulador válido e a nome do objeto indicam que o primeiro elemento é encontrado.
Em seguida, a função do sistema FileFindNext() é utilizada em um ciclo, e são feitas tentativas para acessar todos os outros elementos na mesma pasta um de cada vez. O manipulador obtido anteriormente é utilizado como pasta principal. Se o elemento foi encontrado, a função retorna true e o contador é incrementado. A busca é interrompida assim que a função retornar false. O o manipulador da busca deve ser encerrador no final de ambos os métodos. A função do sistema FileFindClose() é utilizada para isso.
class CFileNavigator : public CElement { private: //--- Retorna o número de (1) arquivos e (2) pastas na pasta especificada int ItemsTotal(const string search_path,const int mode); int FoldersTotal(const string search_path,const int mode); }; //+------------------------------------------------------------------+ //| Conta o número de arquivos na pasta atual | //+------------------------------------------------------------------+ int CFileNavigator::ItemsTotal(const string search_path,const int search_area) { int counter =0; // contador de itens string file_name =""; // nome do arquivo long search_handle =INVALID_HANDLE; // manipulador da busca //--- Obtém o primeiro arquivo na pasta atual search_handle=::FileFindFirst(search_path,file_name,search_area); //--- Se a pasta não está vazia if(search_handle!=INVALID_HANDLE && file_name!="") { //--- Conta o número de objetos na pasta atual counter++; while(::FileFindNext(search_handle,file_name)) counter++; } //--- Fecha o manipulador da busca ::FileFindClose(search_handle); return(counter); } //+------------------------------------------------------------------+ //| Conta o número de pastas na pasta atual | //+------------------------------------------------------------------+ int CFileNavigator::FoldersTotal(const string search_path,const int search_area) { int counter =0; // contador de itens string file_name =""; // nome do arquivo long search_handle =INVALID_HANDLE; // manipulador da busca //--- Obtém o primeiro arquivo na pasta atual search_handle=::FileFindFirst(search_path,file_name,search_area); //--- Se não está vazio, conta o número de objetos na pasta atual em um loop if(search_handle!=INVALID_HANDLE && file_name!="") { //--- Se esta é uma pasta, incrementa o contador if(IsFolder(file_name)) counter++; //--- Itera sobre a lista e conta as outras pastas while(::FileFindNext(search_handle,file_name)) { if(IsFolder(file_name)) counter++; } } //--- Fecha o manipulador da busca ::FileFindClose(search_handle); return(counter); }
Ao coletar os parâmetros dos elementos do sistema de arquivos, nós precisamos receber os índices locais dos nós anteriores. O método CFileNavigator::PrevNodeItemIndex() é utilizado para isso. (1) O índice do elemento atual na pasta raiz e (2) o nível do nó atual devem ser passados para ele como argumentos. Os valores de ambos os argumentos são geridos pelos contadores nos loops externos dos métodos principais, dentro do qual o método é chamado.
class CFileNavigator : public CElement { private: //--- Retorna o índice local do nó anterior relativo aos parâmetros passados int PrevNodeItemIndex(const int root_index,const int node_level); }; //+------------------------------------------------------------------+ //| Retorna o índice local do nó anterior | //| relativo aos parâmetros passados | //+------------------------------------------------------------------+ int CFileNavigator::PrevNodeItemIndex(const int root_index,const int node_level) { int prev_node_item_index=0; //--- Se não for a pasta raiz if(node_level>1) prev_node_item_index=m_l_item_index[node_level-1]; else { //--- Se não for o primeiro elemento da lista if(root_index>0) prev_node_item_index=m_l_item_index[node_level-1]; } //--- Retorna o índice local do nó anterior return(prev_node_item_index); }
Cada vez que uma pasta é encontrada, é realizado uma transição para ela (ou seja, para o próximo nível do nó). O método CFileNavigator::ToNextNode() é utilizado para isso. Alguns argumentos dos parâmetros são passados por referência para fornecer a capacidade de gerir os seus valores.
Aqui, o caminho para calcular os elementos da pasta é formado bem no início do método. Em seguida, os parâmetros do elemento são salvos nos arrays auxiliares pelo nível do índice do nó atual. Depois disso, nós precisamos obter o índice do elemento do nó anterior e adicionar o elemento com os parâmetros especificados para os arrays gerais. Em outras palavras, é adicionado aos arrays exatamente o local da pasta onde estamos, tendo os parâmetros prontos para isso.
Depois disso, o contador do nó é aumentado e o tamanho dos arrays auxiliares é corrigido em relação a ele. Em seguida, nós devemos economizar alguns parâmetros para o novo nível do nó: (1) caminho, (2) o número de elementos e (3) o número de pastas. No final do processo, (1) o contador de índices locais é resetado e (2) o manipulador de busca atual é encerrado.
class CFileNavigator : public CElement { private: //--- Vai para o próximo nó 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); }; //+------------------------------------------------------------------+ //| Vai para o próximo nó | //+------------------------------------------------------------------+ 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) { //--- Filtro de busca (* - verifica todos os arquivo/pastas) string filter="*"; //--- Gera o caminho string search_path=m_l_path[node_level]+item_text+filter; //--- Obtém os dados e armazena ele 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; //--- Obtém o índice do item no nó anterior int prev_node_item_index=PrevNodeItemIndex(root_index,node_level); //--- Adiciona o item com os dados especificados nos arrays gerais 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); //--- Incrementa o contador do nó node_level++; //--- Aumenta o tamanho do array por um elemento AuxiliaryArraysResize(node_level); //--- Obtém os dados e armazena ele 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); //--- Zera o contador de índices locais item_index=0; //--- Fecha o manipulador da busca ::FileFindClose(handle); }
Agora, vamos considerar o ciclo principal, onde as principais ações são executadas. Para maior comodidade, o ciclo está localizado em um método separado CFileNavigator::FileSystemScan(). No método, é realizado a leitura do sistema de arquivos do terminal e o armazenamento dos parâmetros dos elementos encontrados no arrays principais. Esses arrays serão usados depois para construir uma lista hierárquica. O ciclo funciona até que o algoritmo atinja o fim da lista ou o programa é removido do gráfico.
O algoritmo funciona da seguinte maneira. A verificação para o início da lista (o primeiro elemento da lista) da pasta atual é realizada no início do ciclo. Se o elemento verificado for realmente novo, nós recebemos um manipulador e um nome do primeiro elemento detectado na área especificada do sistema de arquivos e salvamos o número de elementos e pastas no nível do nó atual nos arrays auxiliares.
Se este não for o primeiro do índice, é verificado a sequência dos índices locais no nó atual. Se o índice do nó já estiver presente, o contador dos índices locais é incrementado e é realizado a transição para o elemento seguinte na pasta seguinte.
Se o algoritmo atingiu o bloco de código seguinte, verifique se não foi excedido o tamanho da lista do elemento relacionado com o nó da raiz. Se este for o caso, o ciclo é interrompido, significando também que o manipulador de busca (fora do bloco do ciclo) é encerrado e o programa sai do método. Se, em vez disso, nós chegamos ao fim de qualquer outra lista de nós, exceto da raiz, então nós temos que passar para o próximo nível. Aqui, (1) o contador é diminuído a um nível de nó, (2) o contador de índices locais é resetado, (3) o manipulador de busca é encerrado e (4) é realizado a transição para o próximo ciclo de iteração.
Se nenhuma condição if-else for satisfeita na construção anterior, verifique se o elemento atual do sistema de arquivos é uma pasta. Se sim, o método CFileNavigator::ToNextNode() anteriormente descrito é utilizado para ir para o próximo nível. Depois disso, o contador total de índices é incrementado e é ativado o comando para passar para a próxima iteração.
Se o elemento do sistema de arquivos é um arquivo, então nós vamos receber primeiro um índice local do nó anterior. Então, nós devemos adicionar o elemento com os parâmetros especificados para os arrays gerais. Incrementa o contador de índices local e total. Como um ciclo de operação final, é realizado a transição para o próximo elemento do sistema de arquivos do terminal. Depois disso, começa a iteração seguinte do ciclo, e o algoritmo passa através de todas as condições descritas acima.
class CFileNavigator : public CElement { private: //--- Lê o sistema de arquivo e escreve os parâmetros para os arrays void FileSystemScan(const int root_index,int &list_index,int &node_level,int &item_index,int search_area); }; //+------------------------------------------------------------------+ //| Lê o sistema de arquivos e escreve os parâmetros do item | //| nos arrays | //+------------------------------------------------------------------+ void CFileNavigator::FileSystemScan(const int root_index,int &list_index,int &node_level,int &item_index,int search_area) { long search_handle =INVALID_HANDLE; // Pasta/manipulador de busca de arquivo string file_name =""; // Nome do elemento encontrado (arquivo/pasta) string filter ="*"; // Filtro de busca (* - verifica todos os arquivos/pastas) //--- Varre as pastas e armazena os dados nos arrays while(!::IsStopped()) { //--- Se este é o início da lista de pastas if(item_index==0) { //--- Caminho para a busca de todos os itens string search_path=m_l_path[node_level]+filter; //--- Obtém o manipulado e o nome do primeiro arquivo search_handle=::FileFindFirst(search_path,file_name,search_area); //--- Obtém o número de arquivos e pastas na pasta especificada m_l_item_total[node_level] =ItemsTotal(search_path,search_area); m_l_folders_total[node_level] =FoldersTotal(search_path,search_area); } //--- Se o índice deste nó já havia sido utilizado, vai para o próximo arquivo if(m_l_item_index[node_level]>-1 && item_index<=m_l_item_index[node_level]) { //--- Incrementa o contador de índices locais item_index++; //--- Vai para o próximo item ::FileFindNext(search_handle,file_name); continue; } //--- Se chegou ao fim da lista no nó raiz, acaba com o loop if(node_level==1 && item_index>=m_l_item_total[node_level]) break; //--- Se chegou ao fim da lista em qualquer nó, exceto no nó raiz else if(item_index>=m_l_item_total[node_level]) { //--- Define o contador do nó em um nível de volta node_level--; //--- Zera o contador de índices locais item_index=0; //--- Fecha o manipulador da busca ::FileFindClose(search_handle); continue; } //--- Se isto é uma pasta if(IsFolder(file_name)) { //--- Vai para o próximo nó ToNextNode(root_index,list_index,node_level,item_index,search_handle,file_name,search_area); //--- Aumenta o contador de índices gerais e inicia uma nova iteração list_index++; continue; } //--- Obtém o índice local do nó anterior int prev_node_item_index=PrevNodeItemIndex(root_index,node_level); //--- Adiciona o item com os dados especificados nos arrays gerais AddItem(list_index,file_name,node_level,prev_node_item_index,item_index,0,0,false); //--- Aumenta o contador de índices gerais list_index++; //--- Incrementa o contador de índices locais item_index++; //--- Vai para o próximo elemento ::FileFindNext(search_handle,file_name); } //--- Fecha o manipulador da busca ::FileFindClose(search_handle); }
Agora, nós vamos considerar o método principal CFileNavigator::FillArraysData(), onde todos os métodos descritos acima são chamados.
Primeiramente, a sequência das listas de pastas locais e comuns do terminal é definido aqui. Esta sequência depende do modo (ENUM_FILE_NAVIGATOR_CONTENT) especificado nas propriedades do navegador de arquivos. Por padrão, a sequência é definida para que a pasta comum do terminal venha em primeiro lugar na lista, enquanto que pasta local do terminal vem em segundo lugar. Isto funciona apenas no caso do modo FN_BOTH ("exibe o conteúdo de ambos os diretórios"). Se o modo "exibir o conteúdo de um diretório" é selecionado, o início (begin) e o fim (end) do intervalo do ciclo é inicializado de forma adequada.
Após ser definido a área de busca no início do corpo do ciclo, é realizado os seguintes passos um após o outro.
- O contador do índices locais são resetados
- O tamanho dos arrays auxiliares é alterado em relação ao nível do nó atual
- O número de elementos e pastas no diretório atual é guardado para o primeiro índice dos mesmos arrays
- O elemento com os parâmetros especificados são adicionados aos arrays principais. Já que aqui ele é utilizado como o diretório raiz, o nome do elemento atual pode ser de uma das duas pastas: Common\\Files\\ ou MQL5\\Files\\
- Os índices gerais e os contadores do nível do nó são incrementados
- O tamanho dos arrays auxiliares são alterados novamente em relação ao nível do nó atual
- Se a área de busca está localizada na pasta local do terminal, os valores dos primeiros índices dos arrays auxiliares são corrigidos: (1) os índices locais e (2) os índices gerais do nó anterior.
Finalmente, é chamado o método CFileNavigator::FileSystemScan() para ler o sistema de arquivos na área de pesquisa especificada e salvar os parâmetros de seus elementos nas arrays principais para a formação da lista hierárquica.
class CFileNavigator : public CElement { private: //--- Preenche os arrays com os parâmetros dos elementos do sistema de arquivos do terminal void FillArraysData(void); }; //+------------------------------------------------------------------+ //| Preenche os arrays com os parâmetros dos elementos do sistema de arquivos| //+------------------------------------------------------------------+ void CFileNavigator::FillArraysData(void) { //--- Contadores dos (1) índices gerais, (2) os níveis do nó, (3) os índices locais int list_index =0; int node_level =0; int item_index =0; //--- Se ambos os diretórios devem ser exibidas (Common (0)/Local (1)) int begin=0,end=1; //--- Se apenas o conteúdo do diretório local deverá ser exibido if(m_navigator_content==FN_ONLY_MQL) begin=1; //--- Se apenas o conteúdo do diretório comum deve ser exibido else if(m_navigator_content==FN_ONLY_COMMON) begin=end=0; //--- Itera sobre os diretórios especificados for(int root_index=begin; root_index<=end; root_index++) { //--- Determina o diretório para varrer a estrutura do arquivo int search_area=(root_index>0)? 0 : FILE_COMMON; //--- Reinicia o contador dos índices locais item_index=0; //--- Aumenta o tamanho dos arrays por um elemento (em relação ao nível do nó) AuxiliaryArraysResize(node_level); //--- Obtém o número de arquivos e pastas no diretório especificado (* - varre todos os arquivos/pastas) 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); //--- Adiciona o item com o nome do diretório raiz para o topo da lista 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); //--- Aumenta os contadores dos índices gerais e níveis de nó list_index++; node_level++; //--- Aumenta o tamanho dos arrays por um elemento (em relação ao nível do nó) AuxiliaryArraysResize(node_level); //--- Inicializa os primeiros itens para o diretório da pasta local do terminal if(root_index>0) { m_l_item_index[0] =root_index; m_l_prev_node_list_index[0] =list_index-1; } //--- Varre as pastas e armazena os dados nos arrays FileSystemScan(root_index,list_index,node_level,item_index,search_area); } }
Métodos para a Criação do Controle
O método CFileNavigator::FillArraysData() é chamado apenas uma vez antes de criar o controle do navegador de arquivos. Na verdade, o usuário da biblioteca não deve estar ciente disso. Só é necessário especificar algumas propriedades comuns que afetam a aparência e o conteúdo do navegador de arquivos.
//+------------------------------------------------------------------+ //| Cria o navegador de arquivos | //+------------------------------------------------------------------+ bool CFileNavigator::CreateFileNavigator(const long chart_id,const int subwin,const int x,const int y) { //--- Retorna se não há nenhum ponteiro do formulário if(::CheckPointer(m_wnd)==POINTER_INVALID) { ::Print(__FUNCTION__," > Antes de criar o navegador de arquivos, " "o ponteiro para o formulário deve ser passado para ele: CFileNavigator::WindowPointer(CWindow &object)."); return(false); } //--- Varre o sistema de arquivos do terminal e salva os dados nos arrays FillArraysData(); //--- Inicializa as variáveis m_id =m_wnd.LastId()+1; m_chart_id =chart_id; m_subwin =subwin; m_x =x; m_y =y; //--- Margens da borda CElement::XGap(CElement::X()-m_wnd.X()); CElement::YGap(CElement::Y()-m_wnd.Y()); //--- Criação de um elemento if(!CreateAddressBar()) return(false); if(!CreateTreeView()) return(false); //--- Oculta o elemento se ele for uma janela de diálogo ou ela está minimizada if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized()) Hide(); //--- return(true); }
Criar a barra de endereços é o primeiro passo no desenvolvimento do navegador de arquivos. Isto será um único objeto do tipo OBJ_BITMAP_LABEL. Seu conteúdo é desenhado na íntegra. Anteriormente, nós já consideramos os exemplos de criação de controles desenhados na tela. Portanto, nós vamos considerar aqui apenas as nuances relacionadas ao controle desenvolvido.
A fim de elaborar uma barra de endereços, nós precisamos de dois métodos:
- O método CFileNavigator::Border() é usado para desenhar o quadro da barra de endereço.
- O CFileNavigator::UpdateAddressBar() é o método principal para desenhar e exibir as últimas alterações, incluindo o diretório selecionado na lista hierárquica.
Vamos considerar apenas o código do método CFileNavigator::UpdateAddressBar() aqui, uma vez que a estrutura já foi descrita no desenvolvimento de outros controles, por exemplo, no artigo Interfaces Gráficas IV: Elementos Informativos da Interface (Capítulo 1).
Nós já mencionamos que os usuários podem especificar a cor de fundo da barra de endereço e seu tamanho através do eixo Y antes de criar o navegador de arquivos. O texto na área da tela será posicionado com a travessão de 5 pixels pelo eixo X a partir da margem esquerda, enquanto que o posicionamento ao longo do eixo Y deve ser centrado. Já que temos o tamanho ao longo do eixo Y, nós simplesmente precisamos dividir a altura da barra de endereço por 2 para obter a coordenada Y. A fim de chamar o método TextOut() para desenhar um texto na tela, nós precisamos também passar as sinalizações para definir o tipo da âncora para a esquerda e no centro.
Durante a primeira instalação do navegador de arquivos, o caminho ainda não foi inicializado, e o campo m_current_path da classe para seu armazenamento contém uma string em branco. Como os elementos do sistema de arquivos podem ser bastante numerosos, pode-se levar um tempo para a formação dos arrays e a criação da lista hierárquica. Quando a barra de endereços for criada pela primeira vez, é possível exibir uma mensagem que solicite aos usuários a aguardarem um pouco. Por exemplo, aqui ela ficará da seguinte forma: "Carregando. Por favor aguarde...".
No final do método, a tela é atualizada a fim de exibir as alterações mais recentes.
class CFileNavigator : public CElement { private: //--- Desenha uma borda para a barra de endereços void Border(void); //--- Exibe o caminho atual na barra de endereços void UpdateAddressBar(void); }; //+------------------------------------------------------------------+ //| Exibe o caminho atual na barra de endereço | //+------------------------------------------------------------------+ void CFileNavigator::UpdateAddressBar(void) { //--- Coordenadas int x=5; int y=m_address_bar_y_size/2; //--- Limpar o fundo m_address_bar.Erase(::ColorToARGB(m_address_bar_back_color,0)); //--- Desenha o quadro de fundo Border(); //--- Propriedades do texto m_address_bar.FontSet("Calibri",14,FW_NORMAL); //--- Se o caminho não foi definido, mostra uma string padrão if(m_current_full_path=="") m_current_full_path="Loading. Please wait..."; //--- Exibe o caminho na barra de endereços do navegador de arquivos m_address_bar.TextOut(x,y,m_current_path,::ColorToARGB(m_address_bar_text_color),TA_LEFT|TA_VCENTER); //--- Atualiza a tela para desenho m_address_bar.Update(); }
A largura da barra de endereço é calculada antes de sua criação no método CFileNavigator::CreateAddressBar(). Se a área de conteúdo é desabilitada nas configurações, a largura da barra de endereços será igual a da lista hierárquica. Em outros casos, ele é calculado usando o princípio implementado na classe da lista hierárquica (CTreeView) para controlar a largura comum.
Depois de criar o objeto, o método CFileNavigator::UpdateAddressBar() é chamado para desenhar o plano de fundo, o quadro e uma mensagem padrão.
//+------------------------------------------------------------------+ //| Cria a barra de endereços | //+------------------------------------------------------------------+ bool CFileNavigator::CreateAddressBar(void) { //--- Elaborando o nome do objeto string name=CElement::ProgramName()+"_file_navigator_address_bar_"+(string)CElement::Id(); //--- Coordenadas int x =CElement::X(); int y =CElement::Y(); //--- Tamanhos: // Calcula a largura int x_size=0; //--- Se não houver nenhuma área de conteúdo if(m_content_area_width<0) x_size=m_treeview_area_width; else { //--- Se foi definido uma largura específica da área de conteúdo if(m_content_area_width>0) x_size=m_treeview_area_width+m_content_area_width-1; //--- Se a borda direita da área de conteúdo deve ser na borda direita do formulário else x_size=m_wnd.X2()-x-2; } //--- Altura int y_size=m_address_bar_y_size; //--- Cria um objeto if(!m_address_bar.CreateBitmapLabel(m_chart_id,m_subwin,name,x,y,x_size,y_size,COLOR_FORMAT_XRGB_NOALPHA)) return(false); //--- Anexa ao gráfico if(!m_address_bar.Attach(m_chart_id,name,m_subwin,1)) return(false); //--- Define as propriedades m_address_bar.Background(false); m_address_bar.Z_Order(m_zorder); m_address_bar.Tooltip("\n"); //--- Armazena o tamanho CElement::X(x); CElement::Y(y); //--- Armazena o tamanho CElement::XSize(x_size); CElement::YSize(y_size); //--- Margens da borda m_address_bar.XGap(x-m_wnd.X()); m_address_bar.YGap(y-m_wnd.Y()); //--- Atualiza a barra de endereços UpdateAddressBar(); //--- Armazena o ponteiro de objeto CElement::AddToArray(m_address_bar); //--- Oculta o elemento se ele for uma janela de diálogo ou ela está minimizada if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized()) m_address_bar.Timeframes(OBJ_NO_PERIODS); //--- return(true); }
Agora, chegámos à fase em que o método CFileNavigator::CreateTreeView() é chamado para definir a lista hierárquica. No artigo anterior, eu salientei que antes de criar o controle do tipo CTreeView, primeiro nós precisamos adicionar os elementos com os parâmetros do array para o array do controle. Nesta fase, todos os parâmetros para os itens são colocados nos arrays da classe principal CFileNavigator. Tudo que nós temos que fazer agora é passá-los para a classe lista hierárquica no ciclo.
A imagem para cada item é definida no mesmo ciclo. Além disso, o último caractere ('\') deve ser removido dos nomes das pastas.
//+------------------------------------------------------------------+ //| Cria a lista hierárquica | //+------------------------------------------------------------------+ bool CFileNavigator::CreateTreeView(void) { //--- Armazena o ponteiro da janela m_treeview.WindowPointer(m_wnd); //--- Define as propriedades m_treeview.Id(CElement::Id()); m_treeview.XSize(CElement::XSize()); m_treeview.YSize(CElement::YSize()); m_treeview.ResizeListAreaMode(true); m_treeview.TreeViewAreaWidth(m_treeview_area_width); m_treeview.ContentAreaWidth(m_content_area_width); //--- Forma os arrays da lista hierárquica int items_total=::ArraySize(m_g_item_text); for(int i=0; i<items_total; i++) { //--- Define o ícone para o item (pasta/arquivo) string icon_path=(m_g_is_folder[i])? m_folder_icon : m_file_icon; //--- Se for uma pasta, excluir o último caractere ( '\') na string if(m_g_is_folder[i]) m_g_item_text[i]=::StringSubstr(m_g_item_text[i],0,::StringLen(m_g_item_text[i])-1); //--- Adiciona os elementos à lista hierárquica m_treeview.AddItem(i,m_g_prev_node_list_index[i],m_g_item_text[i],icon_path,m_g_item_index[i], m_g_node_level[i],m_g_prev_node_item_index[i],m_g_items_total[i],m_g_folders_total[i],false,m_g_is_folder[i]); } //--- Criar a lista hierárquica if(!m_treeview.CreateTreeView(m_chart_id,m_subwin,m_x,m_y+m_address_bar_y_size)) return(false); //--- return(true); }
Manipulador de Eventos
Nos campos da classe são armazenados o (1) O caminho completo do diretório selecionado na lista da árvore (incluindo o rótulo do volume do disco rígido) em relação ao sistema de arquivos, (2) o caminho relativo ao "ambiente protegido" do terminal e (3) a área do diretório atual. Os métodos adequados são necessários para obter esses valores. Além disso, nós teremos o método CFileNavigator::SelectedFile() para obter o elemento selecionado, que é um arquivo.
class CFileNavigator : public CElement { private: //--- Caminho atual relativo ao arquivo do "ambiente protegido" do terminal string m_current_path; //--- Caminho atual em relação ao sistema de arquivos, incluindo o rótulo de volume do disco rígido string m_current_full_path; //--- Área do diretório atual int m_directory_area; //--- public: //--- Retorna (1) o caminho atual e (2) o caminho completo, (3) o arquivo selecionado string CurrentPath(void) const { return(m_current_path); } string CurrentFullPath(void) const { return(m_current_full_path); } //--- Retorna (1) a área do diretório e (2) o arquivo selecionado int DirectoryArea(void) const { return(m_directory_area); } string SelectedFile(void) const { return(m_treeview.SelectedItemFileName()); } };
O manipulador de eventos do navegador de arquivos está configurado para aceitar apenas um evento, tendo o identificador ON_CHANGE_TREE_PATH. Ele é gerado por uma lista hierárquica ao selecionar um item em sua estrutura. O método CFileNavigator::OnChangeTreePath() foi implementado para lidar com a mensagem deste ID.
Em primeiro lugar, nós recebemos o caminho salvo na lista hierárquica Depois disso, dependendo da categoria do caminho (comum ou local), nós (1) recebemos o endereço da pasta de dados da raiz e (2) formamos os caminhos curtos e longos e, então, salvamos a sinalização da área do diretório.
class CFileNavigator : public CElement { private: //--- Manipula o evento de seleção de um novo caminho na lista hierárquica void OnChangeTreePath(void); }; //+------------------------------------------------------------------+ //| Manipula o evento de seleção de um novo caminho na lista hierárquica| //+------------------------------------------------------------------+ void CFileNavigator::OnChangeTreePath(void) { //--- Obtém o caminho atual string path=m_treeview.CurrentFullPath(); //--- Se esta é a pasta comum dos terminais if(::StringFind(path,"Common\\Files\\",0)>-1) { //--- Obtém o endereço da pasta comum dos terminais string common_path=::TerminalInfoString(TERMINAL_COMMONDATA_PATH); //--- Excluir o prefixo "Common\" da string (recebido no evento) path=::StringSubstr(path,7,::StringLen(common_path)-7); //--- Gera o caminho (versão curta e completa) m_current_path =::StringSubstr(path,6,::StringLen(path)-6); m_current_full_path =common_path+"\\"+path; //--- Armazena a área do diretório m_directory_area=FILE_COMMON; } //--- Se esta é a pasta local do terminal else if(::StringFind(path,"MQL5\\Files\\",0)>-1) { //--- Obtém o endereço dos dados na pasta local do terminal string local_path=::TerminalInfoString(TERMINAL_DATA_PATH); //--- Gera o caminho (versão curta e completa) m_current_path =::StringSubstr(path,11,::StringLen(path)-11); m_current_full_path =local_path+"\\"+path; //--- Armazena a área do diretório m_directory_area=0; } //--- Exibe o caminho atual na barra de endereços UpdateAddressBar(); }
Como resultado, quando o evento ON_CHANGE_TREE_PATH chega, o método CFileNavigator::OnChangeTreePath() deve ser chamado no manipulador de eventos no controle como é mostrado no código a seguir:
//+------------------------------------------------------------------+ //| Manipulador de eventos | //+------------------------------------------------------------------+ void CFileNavigator::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Manipula o evento de "mudança no caminho da lista hierárquica" if(id==CHARTEVENT_CUSTOM+ON_CHANGE_TREE_PATH) { OnChangeTreePath(); return; } }
O evento com o mesmo ID pode ser aceite no manipulador da classe personalizada. O exemplo é discutido abaixo.
Integração do Controle para o Motor da Biblioteca
Para o funcionamento correto do controle, ele deve ser integrado no motor da biblioteca. As adições são principalmente para serem inseridas dentro da classe base CWndContainer no arquivo WndContainer.mqh onde os arquivos de todos os outros elementos da biblioteca estão incluídos. Nós devemos adicionar o seguinte:
- array privado para o navegador de arquivos;
- método para receber o número de aplicações do navegador de arquivos deste tipo na interface gráfica (CFileNavigator);
- método para salvar os ponteiros aos elementos do navegador de arquivos para a base de dados.
A versão curta da classe CWndContainer (apenas as coisas que devem ser adicionadas) é fornecido no código a seguir:
#include "FileNavigator.mqh" //+------------------------------------------------------------------+ //| Classe para armazenar todos os objetos da interface | //+------------------------------------------------------------------+ class CWndContainer { protected: //--- Array da janela CWindow *m_windows[]; //--- Estrutura dos arrays de elementos struct WindowElements { //--- Navegadores de arquivos CFileNavigator *m_file_navigators[]; }; //--- Array dos arrays de elemento para cada janela WindowElements m_wnd[]; //--- public: //--- O número de navegadores de arquivos int FileNavigatorsTotal(const int window_index); //--- private: //--- Armazena os ponteiros à lista hierárquica na base bool AddFileNavigatorElements(const int window_index,CElement &object); };
Você pode examinar o código dos métodos em detalhes nos arquivos anexados abaixo.
Teste do Navegador de Arquivos
Agora, tudo está pronto para o teste com o navegador de arquivos. Vamos criar uma cópia do EA do artigo anterior e eliminar todos os elementos exceto o menu principal e a barra de estado da classe do usuário (CProgram). A fim de testar rapidamente os modos principais do conteúdo do navegador de arquivos, nós vamos criar dois parâmetros externos do EA, como é mostrado no código abaixo. Estes tipos de enumerações e modos já foram considerados em detalhes anteriormente neste artigo.
//+------------------------------------------------------------------+ //| Program.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ //--- Parâmetros externos input ENUM_FILE_NAVIGATOR_CONTENT NavigatorContent =FN_BOTH; // Conteúdo do navegador input ENUM_FILE_NAVIGATOR_MODE NavigatorMode =FN_ONLY_FOLDERS; // Modo do Navegador
Agora, nós precisamos declarar a instância da classe CFileNavigator do navegador de arquivos, assim como o método para a criação de um elemento e as margens da borda do formulário que a instância estiver ligada.
class CProgram : public CWndEvents { private: //--- Navegador de arquivos CFileNavigator m_navigator; //--- private: //--- Navegador de arquivos #define NAVIGATOR1_GAP_X (2) #define NAVIGATOR1_GAP_Y (43) bool CreateFileNavigator(void); };
O código do método CProgram::CreateFileNavigator() para criar o navegador de arquivos é fornecido no código abaixo. Nós vamos definir a altura do navegador para 10 pontos. Seu tamanho padrão (20 pixels) é especificado pelo construtor da classe CFileNavigator. Como observado acima, os modos do conteúdo serão gerenciados pelos parâmetros externos. A aparência dos componentes pode ser configurada por meio dos métodos que podem ser recebidos pelo ponteiro. O código a seguir ilustra isso usando um exemplo de receber os ponteiros da da lista hierárquica e suas barras de rolagem. A chamada do método deve ser colocada no método principal para a criação da interface gráfica.
//+------------------------------------------------------------------+ //| Cria um painel do expert | //+------------------------------------------------------------------+ bool CProgram::CreateExpertPanel(void) { //--- Cria um formulário dos controles //--- Criação dos controles: // Menu principal //--- Menus de contexto //--- Criando a barra de status //--- Cria o navegador de arquivos if(!CreateFileNavigator()) return(false); //--- Redesenha o gráfico m_chart.Redraw(); return(true); } //+------------------------------------------------------------------+ //| Cria o navegador de arquivos | //+------------------------------------------------------------------+ bool CProgram::CreateFileNavigator(void) { //--- Salva o ponteiro para o formulário m_navigator.WindowPointer(m_window1); //--- Coordenadas int x=m_window1.X()+NAVIGATOR1_GAP_X; int y=m_window1.Y()+NAVIGATOR1_GAP_Y; //--- Define as propriedades antes da criação m_navigator.TreeViewPointer().VisibleItemsTotal(10); m_navigator.NavigatorMode(NavigatorMode); m_navigator.NavigatorContent(NavigatorContent); m_navigator.TreeViewAreaWidth(250); m_navigator.AddressBarBackColor(clrWhite); m_navigator.AddressBarTextColor(clrSteelBlue); //--- Propriedades da barra de rolagem m_navigator.TreeViewPointer().GetScrollVPointer().AreaBorderColor(clrLightGray); m_navigator.TreeViewPointer().GetContentScrollVPointer().AreaBorderColor(clrLightGray); //--- Cria o controle if(!m_navigator.CreateFileNavigator(m_chart_id,m_subwin,x,y)) return(false); //--- Adiciona o ponteiro para o controle na base de dados CWndContainer::AddToElementsArray(0,m_navigator); return(true); }
Como exemplo, vamos fazer o log do manipulador de eventos para mostrar os caminhos completos e curtos, bem como o nome do arquivo selecionado no momento. Se o arquivo for selecionado, nós vamos abri-lo e ler as três primeiras linhas e enviá-los ao log. Por favor, note que nós usamos o método CFileNavigator::DirectoryArea() para obter a sinalização correspondente ao local do arquivo para que nós sejamos capazes de gerir a área do diretório.
//+------------------------------------------------------------------+ //| Manipulador de eventos | //+------------------------------------------------------------------+ void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- Manipula o evento de "mudança no caminho da lista hierárquica" if(id==CHARTEVENT_CUSTOM+ON_CHANGE_TREE_PATH) { ::Print(__FUNCTION__," > id: ",id,"; file name: ",m_navigator.SelectedFile()); ::Print(__FUNCTION__," > id: ",id,"; path: ",m_navigator.CurrentPath()+m_navigator.SelectedFile()); ::Print(__FUNCTION__," > id: ",id,"; full path: ",m_navigator.CurrentFullPath()+m_navigator.SelectedFile()); //--- Se o arquivo for selecionado, lê ele (as três primeiras linhas) if(m_navigator.SelectedFile()!="") { //--- Forma o caminho para o arquivo string path=m_navigator.CurrentPath()+m_navigator.SelectedFile(); //--- Recebe o identificador do arquivo especificado int filehandle=::FileOpen(path,FILE_READ|FILE_TXT|FILE_ANSI|m_navigator.DirectoryArea(),'\n'); //--- Se o manipulador é recebido, lê as três primeiras linhas if(filehandle!=INVALID_HANDLE) { ::Print(__FUNCTION__," > Arquivo aberto: ",path); ::Print(__FUNCTION__," > Linha 01: ",::FileReadString(filehandle)); ::Print(__FUNCTION__," > Linha 02: ",::FileReadString(filehandle)); ::Print(__FUNCTION__," > Linha 03: ",::FileReadString(filehandle)); } //--- Fecha o arquivo ::FileClose(filehandle); } ::Print("---"); } }
Agora, está na hora de compilar o programa e carregá-lo ao gráfico. O resultado é exibido na imagem abaixo. No seu caso, o conteúdo do navegador de arquivos deve coincidir com o conteúdo do sistema de arquivos do terminal em seu PC.
Fig. 1. Teste do navegador de arquivos
A imagem abaixo exibe a estrutura expandida da lista hierárquica do navegador de arquivos:
Fig. 2. Estrutura expandida da lista hierárquica do navegador de arquivos
Ao lidar com o manipulador de eventos da classe personalizada, construída em nosso EA de teste, o seguinte resultado é obtido após o arquivo ser enviado ao log:
2016.06.16 02:15:29.994 CProgram::OnEvent > Linha 03: 2,155.66,1028.00,1.04,0.30,0.64,0.24,0.01,2,0,10,10,0 2016.06.16 02:15:29.994 CProgram::OnEvent > Linha 02: 1,260.67,498.00,1.13,1.05,0.26,1.00,0.03,3,0,10,10,0 2016.06.16 02:15:29.994 CProgram::OnEvent > Linha 01: №,PROFIT,TOTAL DEALS,PROFIT FACTOR,EXPECTED PAYOFF,EQUITY DD MAX REL%,RECOVERY FACTOR,SHARPE RATIO,AmountBars,TakeProfit,StopLoss,TrailingSL,ReversePosition 2016.06.16 02:15:29.994 CProgram::OnEvent > Arquivo aberto: DATA_OPTIMIZATION\WriteResOptByCriterion\optimization_results2.csv 2016.06.16 02:15:29.994 CProgram::OnEvent > id: 1023; Caminho completo: C:\Users\tol64\AppData\Roaming\MetaQuotes\Terminal\Common\Files\DATA_OPTIMIZATION\WriteResOptByCriterion\optimization_results2.csv 2016.06.16 02:15:29.994 CProgram::OnEvent > id: 1023; caminho: DATA_OPTIMIZATION\WriteResOptByCriterion\optimization_results2.csv 2016.06.16 02:15:29.994 CProgram::OnEvent > id: 1023; nome do arquivo: optimization_results2.csv
Tudo está funcionando muito bem!
Conclusão
A esquemática da biblioteca para a criação das interfaces gráficas no atual estágio de desenvolvimento é parecido com a imagem abaixo:
Fig. 3. Estrutura da biblioteca, no atual estágio de desenvolvimento
Este é o fim da oitava parte da série sobre a criação de interfaces gráficas nos terminais de negociação MetaTrader. Nesta parte, nós consideramos tais controles como o calendário estático e suspenso, a lista hierárquica, o ponteiro do mouse e o navegador de arquivos.
A próxima parte (nono) da série irá considerar os seguintes controles:
- Seleção da cor.
- Barra de progresso.
- Gráfico de linha.
Você pode baixar o material de toda a oitava parte e testar o seu funcionamento. Se você tiver dúvidas sobre a utilização do material a partir desses arquivos, você poderá consultar a descrição detalhada do desenvolvimento da biblioteca em um dos artigos da lista abaixo ou fazer sua pergunta nos comentários deste artigo.
Lista de artigos (capítulos) da parte 8:
- Interfaces Gráficas VIII: O Controle Calendário (Capítulo 1)
- Interfaces Gráficas VIII: O Controle Lista Hierárquica (Capítulo 2)
- Interfaces Gráficas VIII: O Controle Navegador de Arquivos (Capítulo 3)
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/2541
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso