English Русский 中文 Español Deutsch 日本語
Interfaces Gráficas VIII: O Controle Navegador de Arquivos (Capítulo 3)

Interfaces Gráficas VIII: O Controle Navegador de Arquivos (Capítulo 3)

MetaTrader 5Exemplos | 4 novembro 2016, 12:21
791 0
Anatoli Kazharski
Anatoli Kazharski

Conteúdo

 


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

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

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 

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:


Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/2541

Arquivos anexados |
Interfaces Gráficas IX: O Controle Seletor de Cores (Capítulo 1) Interfaces Gráficas IX: O Controle Seletor de Cores (Capítulo 1)
Com este artigo, nós começamos o capítulo nove da série de artigos dedicados à criação das interfaces gráficas nos terminais de negociação MetaTrader. Ele consiste de dois capítulos onde são apresentados os novos elementos do controle da interface, tais como o seletor de cores, o botão do seletor de cores, a barra de progresso e o gráfico de linha.
Sistema de negociação '80-20' Sistema de negociação '80-20'
Este artigo trata de como criar instrumentos (indicador e Expert Advisor) para estudo sobre a Estratégia de Negociação '80-20' descrita no livro "Street Smarts: High Probability Short-Term Trading Strategies" de Linda Raschke e Laurence Connors. Na linguagem MQL5, estão estabelecidas as regras desta estratégia, e seu principal indicador e Expert Advisor estão testados com base no histórico atual de mercado.
Distribuição Estatística no MQL5 - tirando o melhor de R e o fazendo mais rápido Distribuição Estatística no MQL5 - tirando o melhor de R e o fazendo mais rápido
As funções para trabalhar com as distribuições estatísticas básicas implementadas na linguagem R são consideradas. as distribuições de Cauchy, Weibull, normal, log-normal, logistic, exponential, uniform, gamma, beta central e não-central, qui-quadrado, F de Fisher-Snedecor, t de Student, assim como as distribuições binomiais discretas e binomiais negativas, distribuições geométricas, hipergeométricas e de Poisson. Existem funções para o cálculo de momentos teóricos de distribuições, que permitem avaliar o grau de conformidade da distribuição real com o modelado.
Interfaces Gráficas VIII: O Controle Lista Hierárquica (Capítulo 2) Interfaces Gráficas VIII: O Controle Lista Hierárquica (Capítulo 2)
O capítulo anterior da parte VIII da série Interfaces Gráficas, nós focamos sobre os elementos do calendário estático e suspenso. O segundo capítulo será dedicado a um elemento igualmente complexo - uma lista hierárquica, que está incluída em cada biblioteca multifuncional, usada para a criação de interfaces gráficas. A lista hierárquica implementada neste artigo contém várias configurações flexíveis e modos, permitindo assim ajustar este elemento de controle às suas necessidades.