Русский 中文 Español Deutsch 日本語 Português
Graphical Interfaces VIII: the File Navigator Control (Chapter 3)

Graphical Interfaces VIII: the File Navigator Control (Chapter 3)

MetaTrader 5Examples | 18 August 2016, 09:48
7 676 0
Anatoli Kazharski
Anatoli Kazharski

Contents

 


Introduction

The first article Graphical Interfaces I: Preparation of the Library Structure (Chapter 1) explains in detail what this library is for. You will find a list of articles with links at the end of each chapter. There, you can also download a complete version of the library at the current stage of development. The files must be placed in the same directories as they are located in the archive.

In the first and second chapters of the eighth part of the series, our library has been reinforced by several classes for developing mouse pointers (CPointer), calendars (CCalendar and CDropCalendar classes) and tree views (CTreeItem and CTreeView classes). In this article, we further develop the subject of the previous chapter, as well as examine the file navigator control. 

 


The File Navigator Control

File navigator is a kind of guide allowing you to see the elements of the file system hierarchically via a graphical interface of a program. Besides, it provides access to each element of the hierarchy allowing you to perform certain actions, like opening a file to view data, saving data to a file, moving a file, etc.

This article deals with the first version of the file navigator. It provides users with the following options:

  • navigating the terminal's file "sandbox" within a graphical interface of an MQL application;
  • finding necessary folders and files both in the terminals' common folder and the client terminal's local folder;
  • saving a path for the subsequent programmatic access to a folder or a file selected in the file navigator. 

Note:

Working with files is strictly controlled in MQL5 language for security reasons. Files being processed by means of MQL5 language are always located within the file "sandbox". A file is opened in the client terminal's directory in MQL5\Files (or testing_agent_directory\MQL5\Files in case of testing). If the FILE_COMMON flag is specified among other flags, the file is opened in the common folder of all client terminals \Terminal\Common\Files.

 


Developing CFileNavigator Class

At the current stage of development, we already have all necessary tools for developing a file navigator in the library. The previously described tree view control is in fact a basis for creating file navigators. Apart from a tree view with contents area, we need to develop an address bar containing the full path relative to a currently selected element in the tree view.

Let's provide an opportunity to select the folders to be displayed in the root directory. For example, we can use only one of the terminal's file "sandbox" directories or provide access to two of them. To do this, add ENUM_FILE_NAVIGATOR_CONTENT enumeration to the Enums.mqh file (see the code listing below):

  • FN_BOTH – show both directories.
  • FN_ONLY_MQL – show directory of only the client terminal's local folder.
  • FN_ONLY_COMMON – show directory of only the common folder of all installed terminals.

//+------------------------------------------------------------------+
//| Enumeration of the file navigator content                        |
//+------------------------------------------------------------------+
enum ENUM_FILE_NAVIGATOR_CONTENT
  {
   FN_BOTH        =0,
   FN_ONLY_MQL    =1,
   FN_ONLY_COMMON =2
  };

Create the FileNavigator.mqh file with the CFileNavigator class and connect it to the library engine (WndContainer.mqh file):

//+------------------------------------------------------------------+
//|                                                 WndContainer.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "FileNavigator.mqh"

The standard set of all method library elements should be implemented in the CFileNavigator class as shown in the listing below: 

//+------------------------------------------------------------------+
//| Class for creating file navigator                                |
//+------------------------------------------------------------------+
class CFileNavigator : public CElement
  {
private:
   //--- Pointer to the form to which the element is attached
   CWindow          *m_wnd;
   //---
public:
                     CFileNavigator(void);
                    ~CFileNavigator(void);
   //--- Save the form pointer
   void              WindowPointer(CWindow &object)                           { m_wnd=::GetPointer(object);                }
   //---
public:
   //--- Chart event handler
   virtual void      OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam);
   //--- Timer
   virtual void      OnEventTimer(void) {}
   //--- Moving the element
   virtual void      Moving(const int x,const int y);
   //--- (1) Show, (2) hide, (3) reset, (4) delete
   virtual void      Show(void);
   virtual void      Hide(void);
   virtual void      Reset(void);
   virtual void      Delete(void);
   //--- (1) Set, (2) reset priorities of the left mouse button press
   virtual void      SetZorders(void);
   virtual void      ResetZorders(void);
   //--- Reset the color
   virtual void      ResetColors(void) {}
  };

Let's list the properties available to the library users for configuring the file navigator.

  • tree view area width
  • Contents area width
  • Area background color
  • Frame color
  • Address bar background color
  • Address bar text color
  • Address bar height
  • Images for folders and files
  • Navigator mode (Show all/Folders only)
  • File navigator contents mode (Common folder/Local/All)

//+------------------------------------------------------------------+
//| Class for creating file navigator                                |
//+------------------------------------------------------------------+
class CFileNavigator : public CElement
  {
private:
   //--- Tree view area width
   int               m_treeview_area_width;
   //--- Content area width
   int               m_content_area_width;
   //--- Background and background frame color
   color             m_area_color;
   color             m_area_border_color;
   //--- Address bar background color
   color             m_address_bar_back_color;
   //--- Address bar text color
   color             m_address_bar_text_color;
   //--- Address bar height
   int               m_address_bar_y_size;
   //--- Images for (1) folders and (2) files
   string            m_file_icon;
   string            m_folder_icon;
   //--- File navigator content mode
   ENUM_FILE_NAVIGATOR_CONTENT m_navigator_content;
   //--- Priorities of the left mouse button press
   int               m_zorder;
   //---
public:
   //--- (1) Navigator mode (Show all/Folders only), (2) navigator content (Common folder/Local/All)
   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) Address bar height, (2) width of the tree view and (3) the content list
   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) Background and (2) background frame color
   void              AreaBackColor(const color clr)                           { m_area_color=clr;                          }
   void              AreaBorderColor(const color clr)                         { m_area_border_color=clr;                   }
   //--- Color of (1) the background and (2) address bar text
   void              AddressBarBackColor(const color clr)                     { m_address_bar_back_color=clr;              }
   void              AddressBarTextColor(const color clr)                     { m_address_bar_text_color=clr;              }
   //--- Set the file paths to the (1) files and (2) folders
   void              FileIcon(const string file_path)                         { m_file_icon=file_path;                     }
   void              FolderIcon(const string file_path)                       { m_folder_icon=file_path;                   }
  };

The initialization of property fields by default values is performed in the class constructor (see the code listing below). Default folder and navigator file images are connected to the library as resources. They can be downloaded from the archive attached below.

#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\folder.bmp"
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\text_file.bmp"

//+------------------------------------------------------------------+
//| Constructor                                                      |
//+------------------------------------------------------------------+
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")
  {
//--- Store the name of the element class in the base class
   CElement::ClassName(CLASS_NAME);
//--- Set priorities of the left mouse button click
   m_zorder=0;
  }

In order to create the file navigator, we need two private and one public method. The hierarchical system of the file structure is implemented using the CTreeView class described in the previous article. The file with this class should be included in the FileNavigator.mqh file.

//+------------------------------------------------------------------+
//|                                                FileNavigator.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
#include "Element.mqh"
#include "Window.mqh"
#include "TreeView.mqh"
//+------------------------------------------------------------------+
//| Class for creating file navigator                                |
//+------------------------------------------------------------------+
class CFileNavigator : public CElement
  {
private:
   //--- Objects for creating the element
   CRectCanvas       m_address_bar;
   CTreeView         m_treeview;
   //---
public:
   //--- Methods for creating file navigator
   bool              CreateFileNavigator(const long chart_id,const int subwin,const int x,const int y);
   //---
private:
   bool              CreateAddressBar(void);
   bool              CreateTreeView(void);
  };

Before describing the methods for the file navigator development in details, let's first examine the arranging of the terminal's hierarchical file system structure. 

 


Arranging the Hierarchical File System Structure

Before creating the file navigator, we should first scan the terminal's file system and save the parameters of all of its elements to develop a tree view. All these parameters have been examined in details in the previous article. Therefore, only an array list for the parameters to be saved for developing a tree view is displayed below.

  • General index
  • General index of a previous node
  • Folder/file name
  • Local index
  • Node level
  • Local index of the previous node
  • Total number of elements in the folder
  • Number of folders in the folder
  • Folder flag
  • Item status (minimized/unfolded)

In order to prepare the parameters, we need two array lists – main (g prefix) and auxiliary (l prefix):

class CFileNavigator : public CElement
  {
private:
   //--- Main arrays for data storage
   int               m_g_list_index[];           // general index
   int               m_g_prev_node_list_index[]; // general index of the previous node
   string            m_g_item_text[];            // file/folder name
   int               m_g_item_index[];           // local index
   int               m_g_node_level[];           // node level
   int               m_g_prev_node_item_index[]; // local index of the previous node
   int               m_g_items_total[];          // total number of elements in folder
   int               m_g_folders_total[];        // total number of folders in folder
   bool              m_g_is_folder[];            // folder attribute
   bool              m_g_item_state[];           // item state (minimized/open)
   //--- Auxiliary arrays for data collection
   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[];
  };

We need a number of additional methods to scan the terminal file system, gather all the data and save it in the arrays. The CFileNavigator::AuxiliaryArraysResize() method is necessary for working with auxiliary arrays. It allows changing their size relative to the currently used node level (see the code level below). In other words, the array size exceeds the current node level value to be passed to the method as a single argument by one element. If the current node level value is 0, the array size is set to 1, if the node level is 1, the array size is 2, etc. Since the node level during the data collection may increase or decrease, the array size is changed accordingly. The same method is used to initialize the current node's array element. 

class CFileNavigator : public CElement
  {
private:
   //--- Increase the array size by one element
   void              AuxiliaryArraysResize(const int node_level);
  };
//+------------------------------------------------------------------+
//| Increase the size of additional arrays by one element            |
//+------------------------------------------------------------------+
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);
//--- Initialize the last value
   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;
  }

The CFileNavigator::AddItem() method is used to add an element with parameters to the main arrays. Here, the arrays are increased by one element at each method call. The values of the passed arguments are saved to the last cell of the element. 

class CFileNavigator : public CElement
  {
private:
   //--- Adds item to the 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);
  };
//+------------------------------------------------------------------+
//| Add item with the specified parameters to 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)
  {
//--- Increase the array size by one element
   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);
//--- Store the value of passed parameters
   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;
  }

When scanning the file system, it is necessary to move to each detected folder to view its contents. To achieve this, the CFileNavigator::IsFolder() method is used. It defines whether the current element is a folder or a file. The file system element name should be passed to it as a single parameter. If "\" character is detected in the element name, this means the method returns true. If this is a file, the method returns false

The FileIsExist() system function is another way to check whether the element is a file. The function returns true if the passed element name in the last search handle is a file. If this is a directory, the function generates the ERR_FILE_IS_DIRECTORY error.  

class CFileNavigator : public CElement
  {
private:
   //--- Determines if a file or folder name was passed
   bool              IsFolder(const string file_name);
  };
//+------------------------------------------------------------------+
//| Determines if a file or folder name was passed                   |
//+------------------------------------------------------------------+
bool CFileNavigator::IsFolder(const string file_name)
  {
//--- If the name contains "\\", characters, then it is a folder
   if(::StringFind(file_name,"\\",0)>-1)
      return(true);
//---
   return(false);
  }

In order to form a tree view, specify the number of elements in the item, as well as the number of elements that are folders. Therefore, we need the appropriate methods for defining the parameter values. The methods are CFileNavigator::ItemsTotal() and CFileNavigator::FoldersTotal(). They are similar, though in the second one the counter is increased only if a checked element is a folder. Two arguments are passed to the both methods: (1) path and (2) search area. The search area is a common folder of all terminals or a terminal local folder. Next, the FileFindFirst() system function is used in an attempt to receive the search handle at the specified path, as well as the first element name (if found) simultaneously. The valid handle and the object name indicate that the first element is found. 

Next, the FileFindNext() system function is used in a cycle, and attempts are made to access all other elements in the same directory one at a time. The previosuly obtained handle is used as the directory key. If the element is found, the function returns true and the counter is increased. The search is stopped as soon as the function returns false. The search handle should be closed at the end of the both methods. The FileFindClose() system function is used for that. 

class CFileNavigator : public CElement
  {
private:
   //--- Returns the number of (1) items and (2) folders in the specified directory
   int               ItemsTotal(const string search_path,const int mode);
   int               FoldersTotal(const string search_path,const int mode);
  };
//+------------------------------------------------------------------+
//| Count the number of files in the current directory               |
//+------------------------------------------------------------------+
int CFileNavigator::ItemsTotal(const string search_path,const int search_area)
  {
   int    counter       =0;              // item counter 
   string file_name     ="";             // file name
   long   search_handle =INVALID_HANDLE; // search handle
//--- Get the first file in the current directory
   search_handle=::FileFindFirst(search_path,file_name,search_area);
//--- If the directory is not empty
   if(search_handle!=INVALID_HANDLE && file_name!="")
     {
      //--- Count the number of objects in the current directory
      counter++;
      while(::FileFindNext(search_handle,file_name))
         counter++;
     }
//--- Close the search handle
   ::FileFindClose(search_handle);
   return(counter);
  }
//+------------------------------------------------------------------+
//| Count the number of folders in the current directory             |
//+------------------------------------------------------------------+
int CFileNavigator::FoldersTotal(const string search_path,const int search_area)
  {
   int    counter       =0;              // item counter 
   string file_name     ="";             // file name
   long   search_handle =INVALID_HANDLE; // search handle
//--- Get the first file in the current directory
   search_handle=::FileFindFirst(search_path,file_name,search_area);
//--- If not empty, count the number of objects in the current directory in a loop
   if(search_handle!=INVALID_HANDLE && file_name!="")
     {
      //--- If this is a folder, increase the counter
      if(IsFolder(file_name))
         counter++;
      //--- Iterate over the list further and count the other folders
      while(::FileFindNext(search_handle,file_name))
        {
         if(IsFolder(file_name))
            counter++;
        }
     }
//--- Close the search handle
   ::FileFindClose(search_handle);
   return(counter);
  }

When collecting the parameters of the file system elements, we need to receive the local indices of the previous nodes. The CFileNavigator::PrevNodeItemIndex() method is used for that. (1) The current item index in the root directory and (2) the current node level should be passed to it as arguments. The values of both arguments are managed by the counters in the external cycles of the main methods, inside which the method is called. 

class CFileNavigator : public CElement
  {
private:
   //--- Return the local index of the previous node relative to the passed parameters
   int               PrevNodeItemIndex(const int root_index,const int node_level);
  };
//+------------------------------------------------------------------+
//| Return the local index of the previous node                      |
//| relative to the passed parameters                                |
//+------------------------------------------------------------------+
int CFileNavigator::PrevNodeItemIndex(const int root_index,const int node_level)
  {
   int prev_node_item_index=0;
//--- If not the root directory
   if(node_level>1)
      prev_node_item_index=m_l_item_index[node_level-1];
   else
     {
      //--- If not the first item in the list
      if(root_index>0)
         prev_node_item_index=m_l_item_index[node_level-1];
     }
//--- Return the local index of the previous node
   return(prev_node_item_index);
  }

Each time a folder is found, transition to it (i.e. on the next node level) is performed. The CFileNavigator::ToNextNode() method is used for that. Some parameter arguments are passed by the link to provide the ability to manage their values

Here, the path for calculating the directory elements is formed at the very start of the method. Then, the element parameters are saved to the auxiliary arrays by the current node level index. After that, we need to obtain the previous node's item index and add the item with the specified parameters to the general arrays. In other words, this is exactly the place where the folder we are in is added to the arrays having the parameters prepared for it.

After that, the node counter is increased and the size of auxiliary arrays is corrected relative to it. Next, we should save a few parameters for the new node level: (1) path, (2) number of elements and (3) number of folders. At the end of the method, (1) the counter of local indices is reset and (2) the current search handle is closed.  

class CFileNavigator : public CElement
  {
private:
   //--- Go to the next node
   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);
  };
//+------------------------------------------------------------------+
//| Go to the next node                                              |
//+------------------------------------------------------------------+
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)
  {
//--- Search filter (* - check all files/folders)
   string filter="*";
//--- Generate the path
   string search_path=m_l_path[node_level]+item_text+filter;
//--- Get and store data
   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;
//--- Get the index of the previous node item
   int prev_node_item_index=PrevNodeItemIndex(root_index,node_level);
//--- Add item with the specified data to the general arrays
   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);
//--- Increase the node counter
   node_level++;
//--- Increase the array size by one element
   AuxiliaryArraysResize(node_level);
//--- Get and store data
   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);
//--- Zero the counter of local indices
   item_index=0;
//--- Close the search handle
   ::FileFindClose(handle);
  }

Now, let's consider the main cycle, in which the main actions are performed. For more convenience, the cycle is located in the separate method CFileNavigator::FileSystemScan(). The reading of the terminal file system and saving the parameters of the detected elements to the main arrays are performed in the method. These arrays will be later used to construct a tree view. The cycle works till the algorithm reaches the end of the list or the program is deleted from the chart. 

The algorithm works the following way. The check for the beginning of the list (the first element of the list) of the current directory is performed at the cycle start. If the checked element is actually new, we receive a handle and a name of the first element detected in the specified area of the file system and save the number of elements and folders in the current node level of the auxiliary arrays. 

If this is not the first index, the sequence of the local indices in the current node is checked. If the node index has already been present, the counter of local indices is increased and transition to the next element in the next directory is performed.

If the algorithm has reached the next code block, check if we exceed the range of the element list related to the root node. If this is the case, the cycle is stopped also meaning that the search handle (outside of the cycle block) closes and the program exits the method. If, instead, we have reached the end of any other node list except the root one, then we need to move to the next level. Here, (1) the node counter is decreased one level back, (2) the local indices counter is reset, (3) the search handle is closed and (4) transition to the next cycle iteration is performed.

If not a single condition in the previous if-else construction is fulfilled, check if the current file system element is a folder. If yes, the previously described CFileNavigator::ToNextNode() method is used to move to the next level. After that, the total indices counter is increased and command for moving to the next iteration is activated. 

If the file system element is a file, then first we will receive a local index of the previous node. Then we should add the item with the specified parameters to the general arrays. Increase the total and local indices counters. As a final cycle operation, transition to the next terminal file system element is performed. After that, the next cycle iteration starts, and the algorithm passes through all the conditions described above. 

class CFileNavigator : public CElement
  {
private:
   //--- Read the file system and write parameters to arrays
   void              FileSystemScan(const int root_index,int &list_index,int &node_level,int &item_index,int search_area);
  };
//+------------------------------------------------------------------+
//| Reads the file system and writes item parameters                 |
//| in 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; // Folder/file search handle
   string file_name     ="";             // Name of the found item (file/folder)
   string filter        ="*";            // Search filter (* - check all files/folders)
//--- Scan the directories and store data in arrays
   while(!::IsStopped())
     {
      //--- If this is the beginning of the directory list
      if(item_index==0)
        {
         //--- Path for searching for all items
         string search_path=m_l_path[node_level]+filter;
         //--- Get the handle and name of the first file
         search_handle=::FileFindFirst(search_path,file_name,search_area);
         //--- Get the number of files and folders in the specified directory
         m_l_item_total[node_level]    =ItemsTotal(search_path,search_area);
         m_l_folders_total[node_level] =FoldersTotal(search_path,search_area);
        }
      //--- If the index of this node had already been used, go to the next file
      if(m_l_item_index[node_level]>-1 && item_index<=m_l_item_index[node_level])
        {
         //--- Increase the counter of local indices
         item_index++;
         //--- Go to the next item
         ::FileFindNext(search_handle,file_name);
         continue;
        }
      //--- If reached the end of list in the root node, end the loop
      if(node_level==1 && item_index>=m_l_item_total[node_level])
         break;
      //--- If reached the end of list in any node, except the root node
      else if(item_index>=m_l_item_total[node_level])
        {
         //--- Set the node counter one level back
         node_level--;
         //--- Zero the counter of local indices
         item_index=0;
         //--- Close the search handle
         ::FileFindClose(search_handle);
         continue;
        }
      //--- If this is folder
      if(IsFolder(file_name))
        {
         //--- Go to the next node
         ToNextNode(root_index,list_index,node_level,item_index,search_handle,file_name,search_area);
         //--- Increase the counter of general indices and start a new iteration
         list_index++;
         continue;
        }
      //--- Get the local index of the previous node
      int prev_node_item_index=PrevNodeItemIndex(root_index,node_level);
      //--- Add item with the specified data to the general arrays
      AddItem(list_index,file_name,node_level,prev_node_item_index,item_index,0,0,false);
      //--- Increase the counter of general indices
      list_index++;
      //--- Increase the counter of local indices
      item_index++;
      //--- Go to the next element
      ::FileFindNext(search_handle,file_name);
     }
//--- Close the search handle
   ::FileFindClose(search_handle);
  }

Now, let's consider the main method CFileNavigator::FillArraysData(), in which all methods described above are called.

First of all, the sequence of the terminal's common and local folder directories is set here. This sequence depends on the mode (ENUM_FILE_NAVIGATOR_CONTENT) specified in the file navigator properties. By default, the sequence is set so that the common terminal folder comes first in the list, while the terminal's local folder comes second. This works only in case FN_BOTH ("display contents of both directories") mode is set. If the mode "show the contents of one directory" is selected, the beginning (begin) and the end (end) of the cycle range are initialized appropriately.

After the search area at the beginning of the cycle body is defined, the following steps are performed one after another.

  • The local indices counter is reset
  • The size of the auxiliary arrays is changed relative to the current node level
  • The number of elements and folders in the current directory is saved to the first index of the same arrays
  • The element with the specified parameters is added to the main arrays. Since it is used as the root directory here, the name of the current element can be from one of the two directories: Common\\Files\\ or MQL5\\Files\\
  • The general indices and node level counters are increased
  • The size of the auxiliary arrays is again changed relative to the current node level
  • If the search area is currently located in the terminal's local folder, the values for the first indices of the auxiliary arrays: (1) local indices and (2) the previous node's general indices, are corrected.

Finally, the CFileNavigator::FileSystemScan() method for reading the file system in the specified search area and saving the parameters of its elements to the main arrays for forming a tree view is called.  

class CFileNavigator : public CElement
  {
private:
   //--- Fills arrays with parameters of the terminal file system elements
   void              FillArraysData(void);
  };
//+------------------------------------------------------------------+
//| Fills arrays with parameters of the file system elements         |
//+------------------------------------------------------------------+
void CFileNavigator::FillArraysData(void)
  {
//--- Counters of (1) general indices, (2) node levels, (3) local indices
   int list_index =0;
   int node_level =0;
   int item_index =0;
//--- If both directories should be displayed (Common (0)/Local (1))
   int begin=0,end=1;
//--- If only the local directory content should be displayed
   if(m_navigator_content==FN_ONLY_MQL)
      begin=1;
//--- If only the common directory contents should be displayed
   else if(m_navigator_content==FN_ONLY_COMMON)
      begin=end=0;
//--- Iterate over the specified directories
   for(int root_index=begin; root_index<=end; root_index++)
     {
      //--- Determine the directory for scanning the file structure
      int search_area=(root_index>0)? 0 : FILE_COMMON;
      //--- Reset the counter of the local indices
      item_index=0;
      //--- Increase the array size by one element (relative to the node level)
      AuxiliaryArraysResize(node_level);
      //--- Get the number of files and folders in the specified directory (* - scan all files/folders)
      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);
      //--- Add item with the name of the root directory to the top of the list
      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);
      //--- Increase the counters of general indices and node levels
      list_index++;
      node_level++;
      //--- Increase the array size by one element (relative to the node level)
      AuxiliaryArraysResize(node_level);
      //--- Initialize the first items for the directory of the local folder of the terminal
      if(root_index>0)
        {
         m_l_item_index[0]           =root_index;
         m_l_prev_node_list_index[0] =list_index-1;
        }
      //--- Scan the directories and store data in arrays
      FileSystemScan(root_index,list_index,node_level,item_index,search_area);
     }
  }

 


Methods for Creating the Control

The CFileNavigator::FillArraysData() method is called only once before creating the File navigator control. In fact, a library user may not even be aware of it. It is only necessary to specify some common properties affecting the look and contents of the file navigator.

//+------------------------------------------------------------------+
//| Create file navigator                                            |
//+------------------------------------------------------------------+
bool CFileNavigator::CreateFileNavigator(const long chart_id,const int subwin,const int x,const int y)
  {
//--- Leave, if there is no form pointer
   if(::CheckPointer(m_wnd)==POINTER_INVALID)
     {
      ::Print(__FUNCTION__," > Before creating the file navigator, "
              "the pointer to the form should be passed to it: CFileNavigator::WindowPointer(CWindow &object).");
      return(false);
     }
//--- Scan the file system of the terminal and store data in arrays
   FillArraysData();
//--- Initializing variables
   m_id       =m_wnd.LastId()+1;
   m_chart_id =chart_id;
   m_subwin   =subwin;
   m_x        =x;
   m_y        =y;
//--- Margins from the edge
   CElement::XGap(CElement::X()-m_wnd.X());
   CElement::YGap(CElement::Y()-m_wnd.Y());
//--- Creating an element
   if(!CreateAddressBar())
      return(false);
   if(!CreateTreeView())
      return(false);
//--- Hide the element if the window is a dialog one or is minimized
   if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized())
      Hide();
//---
   return(true);
  }

Creating the address bar is the first step when developing the file navigator. This will be a single object of OBJ_BITMAP_LABEL type. Its contents is drawn in full. Previously, we have already considered examples of creating controls drawn on canvas. Therefore, here we will consider only the nuances related to the developed control. 

In order to draw an address bar, we need two methods:

  • The CFileNavigator::Border() method is used to draw the address bar frame.
  • CFileNavigator::UpdateAddressBar() is the main method for drawing and displaying the latest changes, including the directory selected in the tree view.

Let's consider only the CFileNavigator::UpdateAddressBar() method code here since the frame has already been described when developing other controls, for example, in the article Graphical Interfaces IV: Informational Interface Elements (Chapter 1)

We have already mentioned that users can specify the address bar background color and its size by the Y axis before creating the file navigator. The text in the canvas area will be positioned with the 5 pixel indent by the X axis from the left margin, while positioning along the Y axis should be centered. Since we have the size along the Y axis, we simply need to divide the address bar height by 2 to obtain the Y coordinate. In order to call the TextOut() method to draw a text on the canvas, we also need to pass the flags for defining the anchor type to the left and at the center

During the first installation of the file navigator, the path is not initialized yet, and the m_current_path class field for its storage contains the blank string. Since the file system elements can be quite numerous, forming the arrays and creating the tree view may take some time. Since the address bar is created first, it can feature a message that prompts users to wait a bit. For example, here it will look as follows: "Loading. Please wait...". 

In the end of the method, the canvas is updated in order to display the most recent changes. 

class CFileNavigator : public CElement
  {
private:
   //--- Draws a border for the address bar
   void              Border(void);
   //--- Displays the current path in the address bar
   void              UpdateAddressBar(void);
  };
//+------------------------------------------------------------------+
//| Displays the current path in the address bar                     |
//+------------------------------------------------------------------+
void CFileNavigator::UpdateAddressBar(void)
  {
//--- Coordinates
   int x=5;
   int y=m_address_bar_y_size/2;
//--- Clear background
   m_address_bar.Erase(::ColorToARGB(m_address_bar_back_color,0));
//--- Draw the background frame
   Border();
//--- Text properties
   m_address_bar.FontSet("Calibri",14,FW_NORMAL);
//--- If the path is not set, show the default string
   if(m_current_full_path=="")
      m_current_full_path="Loading. Please wait...";
//--- Output the path to the address bar of the file navigator
   m_address_bar.TextOut(x,y,m_current_path,::ColorToARGB(m_address_bar_text_color),TA_LEFT|TA_VCENTER);
//--- Update the canvas for drawing
   m_address_bar.Update();
  }

The address bar width is calculated before its creation in the CFileNavigator::CreateAddressBar() method. If the contents area is disabled in the settings, the address bar width is equal to the tree view one. In other cases, it is calculated using the principle implemented in the tree view class (CTreeView) for the common control width.

After creating the object, the CFileNavigator::UpdateAddressBar() method is called for drawing the background, frame and default message. 

//+------------------------------------------------------------------+
//| Create address bar                                               |
//+------------------------------------------------------------------+
bool CFileNavigator::CreateAddressBar(void)
  {
//--- Forming the object name
   string name=CElement::ProgramName()+"_file_navigator_address_bar_"+(string)CElement::Id();
//--- Coordinates
   int x =CElement::X();
   int y =CElement::Y();
//--- Sizes:
//    Calculate the width
   int x_size=0;
//--- If there is no content area
   if(m_content_area_width<0)
      x_size=m_treeview_area_width;
   else
     {
      //--- If a specific width of the content area is defined
      if(m_content_area_width>0)
         x_size=m_treeview_area_width+m_content_area_width-1;
      //--- If the right edge of the content area must be at the right edge of the form
      else
         x_size=m_wnd.X2()-x-2;
     }
//--- Height
   int y_size=m_address_bar_y_size;
//--- Creating the object
   if(!m_address_bar.CreateBitmapLabel(m_chart_id,m_subwin,name,x,y,x_size,y_size,COLOR_FORMAT_XRGB_NOALPHA))
      return(false);
//--- Attach to the chart
   if(!m_address_bar.Attach(m_chart_id,name,m_subwin,1))
      return(false);
//--- Set the properties
   m_address_bar.Background(false);
   m_address_bar.Z_Order(m_zorder);
   m_address_bar.Tooltip("\n");
//--- Store the size
   CElement::X(x);
   CElement::Y(y);
//--- Store the size
   CElement::XSize(x_size);
   CElement::YSize(y_size);
//--- Margins from the edge
   m_address_bar.XGap(x-m_wnd.X());
   m_address_bar.YGap(y-m_wnd.Y());
//--- Update the address bar
   UpdateAddressBar();
//--- Store the object pointer
   CElement::AddToArray(m_address_bar);
//--- Hide the element if the window is a dialog one or is minimized
   if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized())
      m_address_bar.Timeframes(OBJ_NO_PERIODS);
//---
   return(true);
  }

Now, we have reached the stage when the CFileNavigator::CreateTreeView() method is called for setting the tree view. In the previous article, I pointed out that before creating the CTreeView type control, we first need to add the items with the array parameters to the control's arrays. At this stage, all parameters for the items are placed to the CFileNavigator main class arrays. All we have to do now is to pass them to the tree view class in the cycle. 

The image for each item is defined in the same cycle. Besides, the last character (‘\’) should be deleted in the names of the folders. 

//+------------------------------------------------------------------+
//| Creates the tree view                                            |
//+------------------------------------------------------------------+
bool CFileNavigator::CreateTreeView(void)
  {
//--- Store the window pointer
   m_treeview.WindowPointer(m_wnd);
//--- set properties
   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);
//--- Form the tree view arrays
   int items_total=::ArraySize(m_g_item_text);
   for(int i=0; i<items_total; i++)
     {
      //--- Set icon for the item (folder/file)
      string icon_path=(m_g_is_folder[i])? m_folder_icon : m_file_icon;
      //--- If it is a folder, delete the last character ('\') in the 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);
      //--- Add item to the tree view
      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]);
     }
//--- Create the tree view
   if(!m_treeview.CreateTreeView(m_chart_id,m_subwin,m_x,m_y+m_address_bar_y_size))
      return(false);
//---
   return(true);
  }

 

 


Event Handler

(1) The full path of the directory selected in tree list (including a hard drive volume label) relative to the file system, (2) the path relative to the terminal sandbox and (3) the current directory area are saved in the class fields. The appropriate methods are needed to obtain these values. Besides, we will need the CFileNavigator::SelectedFile() method to obtain the selected element which is a file.

class CFileNavigator : public CElement
  {
private:
   //--- Current path relative to the file "sandbox" of the terminal
   string            m_current_path;
   //--- Current path relative to the file system, including the hard drive volume label
   string            m_current_full_path;
   //--- Area of the current directory
   int               m_directory_area;
   //---
public:
   //--- Returns (1) the current path and (2) the full path, (3) the selected file
   string            CurrentPath(void)                                  const { return(m_current_path);                    }
   string            CurrentFullPath(void)                              const { return(m_current_full_path);               }
   //--- Returns (1) directory area and (2) the selected file
   int               DirectoryArea(void)                                const { return(m_directory_area);                  }
   string            SelectedFile(void)                                 const { return(m_treeview.SelectedItemFileName()); }
  };

The file navigator event handler is configured to accept only one event having the ON_CHANGE_TREE_PATH ID. It is generated by a tree view when selecting an item in its structure. The CFileNavigator::OnChangeTreePath() method has been implemented to handle a message with this ID. 

First, we receive the path saved in the tree view. After that, depending on the path category (common or local), we (1) receive the address of the root data folder and (2) form short and long paths and save the directory area flag

class CFileNavigator : public CElement
  {
private:
   //--- Handle event of selecting a new path in the tree view
   void              OnChangeTreePath(void);
  };
//+------------------------------------------------------------------+
//| Handle event of selecting a new path in the tree view            |
//+------------------------------------------------------------------+
void CFileNavigator::OnChangeTreePath(void)
  {
//--- Get the current path
   string path=m_treeview.CurrentFullPath();
//--- If this is the terminals common folder
   if(::StringFind(path,"Common\\Files\\",0)>-1)
     {
      //--- Get the address of the terminals common folder
      string common_path=::TerminalInfoString(TERMINAL_COMMONDATA_PATH);
      //--- Delete the "Common\" prefix in the string (received in the event)
      path=::StringSubstr(path,7,::StringLen(common_path)-7);
      //--- Generate the path (short and full version)
      m_current_path      =::StringSubstr(path,6,::StringLen(path)-6);
      m_current_full_path =common_path+"\\"+path;
      //--- Store the directory area
      m_directory_area=FILE_COMMON;
     }
//--- If this is the local folder of the terminal
   else if(::StringFind(path,"MQL5\\Files\\",0)>-1)
     {
      //--- Get the address of data in the local folder of the terminal
      string local_path=::TerminalInfoString(TERMINAL_DATA_PATH);
      //--- Generate the path (short and full version)
      m_current_path      =::StringSubstr(path,11,::StringLen(path)-11);
      m_current_full_path =local_path+"\\"+path;
      //--- Store the directory area
      m_directory_area=0;
     }
//--- Display the current path in the address bar
   UpdateAddressBar();
  }

 

As a result, when the ON_CHANGE_TREE_PATH event arrives, the CFileNavigator::OnChangeTreePath() method should be called in the control event handler as shown in the code listing below:

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CFileNavigator::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Handle event of "Change in the path of the tree view"
   if(id==CHARTEVENT_CUSTOM+ON_CHANGE_TREE_PATH)
     {
      OnChangeTreePath();
      return;
     }
  }

The event with the same ID can be accepted in the custom class handler. The example is discussed below.

 


Integrating the Control into the Library Engine

For the correct control operation, it should be integrated into the library engine. The additions are mainly to be inserted into the CWndContainer base class in the WndContainer.mqh file the files of all other library elements are connected to. We should add the following: 

  • private array for the file navigator;
  • method for receiving the number of file navigator applications of this type in the graphical interface (CFileNavigator);
  • method for saving the pointers to the file navigator elements to the database.

The short version of the CWndContainer class (only the things that should be added) is provided in the code listing below:

#include "FileNavigator.mqh"
//+------------------------------------------------------------------+
//| Class for storing all interface objects                          |
//+------------------------------------------------------------------+
class CWndContainer
  {
protected:
   //--- Window array
   CWindow          *m_windows[];
   //--- Structure of element arrays
   struct WindowElements
     {
      //--- File navigators
      CFileNavigator   *m_file_navigators[];
     };
   //--- Array of element arrays for each window
   WindowElements    m_wnd[];
   //---
public:
   //--- The number of file navigators
   int               FileNavigatorsTotal(const int window_index);
   //---
private:
   //--- Stores pointers to the tree view controls in the base
   bool              AddFileNavigatorElements(const int window_index,CElement &object);
  };

You can examine the code of the methods in details in the attached files below. 



Testing the File Navigator

Now, everything is ready for testing the file navigator. Let's create a copy of the EA from the previous article and delete all elements except the main menu and the status bar from the user class (CProgram). In order to quickly test the main file editor contents modes, let's create two external EA parameters like shown in the code listing below. These types of enumerations and modes have already been considered in details in the article sections above.

//+------------------------------------------------------------------+
//|                                                      Program.mqh |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                              http://www.mql5.com |
//+------------------------------------------------------------------+
//--- External parameters
input ENUM_FILE_NAVIGATOR_CONTENT NavigatorContent =FN_BOTH;         // Navigator content
input ENUM_FILE_NAVIGATOR_MODE    NavigatorMode    =FN_ONLY_FOLDERS; // Navigator mode

Now, we need to declare the CFileNavigator file navigator class instance, as well as the method for creating an element and the margins from the edge of the form the instance is to be connected to. 

class CProgram : public CWndEvents
  {
private:
   //--- File navigator
   CFileNavigator    m_navigator;
   //---
private:
   //--- File navigator
#define NAVIGATOR1_GAP_X      (2)
#define NAVIGATOR1_GAP_Y      (43)
   bool              CreateFileNavigator(void);
  };

The CProgram::CreateFileNavigator() method code for creating the file navigator is provided in the code listing below. We will set the navigator height to 10 points. Their default size (20 pixels) is specified by CFileNavigator class constructor. As noted above, the contents modes will be managed by external parameters. The look of the components can be configured by methods that can be received by the pointer. The code below illustrates this by using the example of receiving the tree view pointers and its scroll bars. The method call should be placed in the main method for creating the graphical interface

//+------------------------------------------------------------------+
//| Create an expert panel                                           |
//+------------------------------------------------------------------+
bool CProgram::CreateExpertPanel(void)
  {
//--- Creating form 1 of the controls
//--- Creating controls:
//    Main menu
//--- Context menus
//--- Creating the status bar
//--- Creating the file navigator
   if(!CreateFileNavigator())
      return(false);
//--- Redrawing the chart
   m_chart.Redraw();
   return(true);
  }
//+------------------------------------------------------------------+
//| Create file navigator                                            |
//+------------------------------------------------------------------+
bool CProgram::CreateFileNavigator(void)
  {
//--- Save the pointer to the form
   m_navigator.WindowPointer(m_window1);
//--- Coordinates
   int x=m_window1.X()+NAVIGATOR1_GAP_X;
   int y=m_window1.Y()+NAVIGATOR1_GAP_Y;
//--- Set the properties before creating
   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);
//--- Scroll bar properties
   m_navigator.TreeViewPointer().GetScrollVPointer().AreaBorderColor(clrLightGray);
   m_navigator.TreeViewPointer().GetContentScrollVPointer().AreaBorderColor(clrLightGray);
//--- Create the control
   if(!m_navigator.CreateFileNavigator(m_chart_id,m_subwin,x,y))
      return(false);
//--- Add the pointer to the control to the database
   CWndContainer::AddToElementsArray(0,m_navigator);
   return(true);
  }

As an example, let's make the event handler log to show both full and short paths, as well as the name of a currently selected file. If the file is selected, we will open it and read the first three lines passing them to the log. Please note that we use the CFileNavigator::DirectoryArea() method to obtain the flag corresponding to the file location so that we are able to manage the directory area.  

//+------------------------------------------------------------------+
//| Event handler                                                    |
//+------------------------------------------------------------------+
void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam)
  {
//--- Process the event "Changing the path in the tree view"
   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());
      //--- If the file is selected, read it (the first three lines)
      if(m_navigator.SelectedFile()!="")
        {
         //--- Form the path to the file
         string path=m_navigator.CurrentPath()+m_navigator.SelectedFile();
         //--- Receive the handle of the specified file
         int filehandle=::FileOpen(path,FILE_READ|FILE_TXT|FILE_ANSI|m_navigator.DirectoryArea(),'\n');
         //--- If the handle is received, read the first three lines
         if(filehandle!=INVALID_HANDLE)
           {
            ::Print(__FUNCTION__," > Opened file: ",path);
            ::Print(__FUNCTION__," > Line 01: ",::FileReadString(filehandle));
            ::Print(__FUNCTION__," > Line 02: ",::FileReadString(filehandle));
            ::Print(__FUNCTION__," > Line 03: ",::FileReadString(filehandle));
           }
         //--- Close the file
         ::FileClose(filehandle);
        }
      ::Print("---");
     }
  }

Now, it is time to compile the program and load it to the chart. The result is shown on the screenshot below. In your case, the contents of the file navigator should match the contents of the terminal file system on your PC.

 Fig. 1. Testing the file navigator

Fig. 1. Testing the file navigator


The screenshot below shows the unfolded file navigator tree view: 

 Fig. 2. Unfolded structure of the file navigator tree view

Fig. 2. Unfolded structure of the file navigator tree view


When dealing with the custom class event handler built into our test EA, the following result is obtained after the file is passed to the log: 

2016.06.16 02:15:29.994         CProgram::OnEvent > Line 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 > Line 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 > Line 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 > Opened file: DATA_OPTIMIZATION\WriteResOptByCriterion\optimization_results2.csv
2016.06.16 02:15:29.994         CProgram::OnEvent > id: 1023; full path: 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; path: DATA_OPTIMIZATION\WriteResOptByCriterion\optimization_results2.csv
2016.06.16 02:15:29.994         CProgram::OnEvent > id: 1023; file name: optimization_results2.csv

All works fine! 



Conclusion

Schematic of the library for creating graphical interfaces at the current stage of development looks as shown below:

Fig. 3. Library structure at the current stage of development 

Fig. 3. Library structure at the current stage of development

This is the end of the eighth part of the series about creating graphical interfaces in the MetaTrader trading terminals. In this part, we have considered such controls as static and drop down calendar, tree view, mouse pointer and file navigator.

The next (ninth) part of the series will consider the following controls:

  • Color selection.
  • Progress bar.
  • Line chart.

You can download the material of the entire eighth part below to perform tests. If you have questions on using the material presented in those files, you can refer to the detailed description of the library development in one of the articles from the list below or ask your question in the comments to this article.

List of articles (chapters) of part 8:


Translated from Russian by MetaQuotes Ltd.
Original article: https://www.mql5.com/ru/articles/2541

Attached files |
LifeHack for trader: four backtests are better than one LifeHack for trader: four backtests are better than one
Before the first single test, every trader faces the same question — "Which of the four modes to use?" Each of the provided modes has its advantages and features, so we will do it the easy way - run all four modes at once with a single button! The article shows how to use the Win API and a little magic to see all four testing chart at the same time.
Cross-Platform Expert Advisor: Introduction Cross-Platform Expert Advisor: Introduction
This article details a method by which cross-platform expert advisors can be developed faster and easier. The proposed method consolidates the features shared by both versions into a single class, and splits the implementation on derived classes for incompatible features.
Cross-Platform Expert Advisor: Reuse of Components from the MQL5 Standard Library Cross-Platform Expert Advisor: Reuse of Components from the MQL5 Standard Library
There exists some components in the MQL5 Standard Library that may prove to be useful in the MQL4 version of cross-platform expert advisors. This article deals with a method of making certain components of the MQL5 Standard Library compatible with the MQL4 compiler.
Graphical Interfaces VIII: The Tree View Control (Chapter 2) Graphical Interfaces VIII: The Tree View Control (Chapter 2)
The previous chapter of part VIII on graphical interfaces has focused on the elements of static and drop-down calendar. The second chapter will be dedicated to an equally complex element — a tree view, that is included in every complete library used for creating graphical interfaces. A tree view implemented in this article contains multiple flexible settings and modes, thus allowing to adjust this element of control to your needs.