グラフィカルインタフェースVIII: ファイルナビゲータコントロール(チャプター3)
コンテンツ
はじめに
シリーズ第一弾のグラフィカルインタフェース I: ライブラリストラクチャの準備(チャプター 1)ではライブラリの目的を詳細に考察します。記事へのリンクの完全なリストは各章の末尾でみられます。そこではまた、開発の現段階でのライブラリの完全版をダウンロードすることができます。ファイルはアーカイブと同じディレクトリに配置される必要があります。
シリーズの第八部の第1,2章では、ライブラリがマウスポインタ(CPointer)、カレンダー(CCalendar及びCDropCalendarクラス) 、ツリービュー(CTreeItem及び CTreeViewクラス) を開発するためのいくつかのクラスによって強化されました。本稿では、さらに前の章の主題を開発するだけでなく、ファイルナビゲーターコントロールを考察します。
ファイルナビゲータコントロール
ファイルナビゲーターは、プログラムのグラフィカルインタフェースを介して、ファイルシステムの階層の要素を見ることができるガイドのようなものです。また、それは、ファイルを開く、データを表示する、ファイルへデータを保存する、ファイルを移動するといった特定のアクションの実行を可能にする階層の各要素へのアクセスを提供します。
本稿では、ファイルナビゲータの最初のバージョンを扱います。ユーザには次のオプションが提供されます。
- MQLアプリケーションのグラフィカルインターフェース内で端末のファイル「サンドボックス」をナビゲートする
- 端末の共通フォルダ内とクライアント端末のローカルフォルダ内の両方で必要なフォルダとファイルを見つける
- ファイルナビゲータで選択したファイルやフォルダへのパスを後のプログラムによるアクセスのために保存する
注意:
MQL5言語では、ファイル操作はセキュリティ上の理由から厳しく制御されています。MQL5言語で処理されたファイルは常にファイルの「サンドボックス」内に位置します。ファイルはクライアント端末のMQL5\Files(検証の場合はtesting_agent_directory\MQL5\Files)内のディレクトリで開かれます。FILE_COMMONフラグが指定されていると、ファイルはすべてのクライエント端末の共通フォルダである \Terminal\Common\Filesで開かれます。
CFileNavigatorクラスの開発
開発の現段階では、すでに、ライブラリ内のファイルナビゲーターを開発するために必要なすべてのツールがそろっています。先に述べたツリービューコントロールは、実際にはファイルナビゲータを作成するための基礎です。コンテンツ領域を持つツリービューに加えて、ツリービューで現在選択されている要素への完全な相対パスを含むアドレスバーを開発する必要があります。
ルートディレクトリに表示するフォルダを選択する機会を提供してみましょう。例えば、端末のファイル「サンドボックス」のディレクトリのいずれか1つのみを使用するか、2つへのアクセスを提供することができます。これにはENUM_FILE_NAVIGATOR_CONTENT列挙をEnums.mqhファイルに加えます(下のコードを参照)。
- FN_BOTH – 両ディレクトリを表示する
- FN_ONLY_MQL – クライエント端末のローカルフォルダディレクトリのみを表示する
- FN_ONLY_COMMON – すべてのクライアント端末の共通フォルダのみを表示する
//+------------------------------------------------------------------+ //| ファイルナビゲータコンテンツの列挙 | //+------------------------------------------------------------------+ enum ENUM_FILE_NAVIGATOR_CONTENT { FN_BOTH =0, FN_ONLY_MQL =1, FN_ONLY_COMMON =2 };
CFileNavigatorクラスを持ったFileNavigator.mqhファイルを作成しライブラリエンジン(WndContainer.mqhファイル)に接続します。
//+------------------------------------------------------------------+ //| WndContainer.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "FileNavigator.mqh"
以下のリストに示されるように、すべてのメソッドライブラリ要素の標準セットは CFileNavigatorクラスで実装される必要があります。
//+------------------------------------------------------------------+ //| ナビゲータ作成クラス | //+------------------------------------------------------------------+ class CFileNavigator : public CElement { private: //--- 要素が取り付けられるフォームへのポインタ CWindow *m_wnd; //--- public: CFileNavigator(void); ~CFileNavigator(void); //--- フォームポインタを保存する void WindowPointer(CWindow &object) { m_wnd=::GetPointer(object); } //--- public: //--- チャートイベントハンドラ virtual void OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam); //--- タイマー virtual void OnEventTimer(void) {} //--- 要素の移動 virtual void Moving(const int x,const int y); //--- (1)表示 (2)非表示 (3)リセット (4)削除 virtual void Show(void); virtual void Hide(void); virtual void Reset(void); virtual void Delete(void); //--- マウスの左クリックの優先順位の(1)設定と(2)リセット virtual void SetZorders(void); virtual void ResetZorders(void); //--- 色をリセットする virtual void ResetColors(void) {} };
それでは、ファイルナビゲーターを設定するためにライブラリのユーザーが利用でsきるプロパティを一覧表示してみましょう。
- ツリービュー領域の幅
- コンテンツ領域の幅
- 領域の背景色
- 境界の色
- アドレスバーの背景色
- アドレスバーテキストの色
- アドレスバーの高さ
- フォルダとファイルの画像
- ナビゲータモード(すべてを表示/フォルダのみ)
- ファイルナビゲータコンテンツモード(共通フォルダ/ローカル/すべて)
//+------------------------------------------------------------------+ //| ファイルナビゲータ作成クラス | //+------------------------------------------------------------------+ class CFileNavigator : public CElement { private: //--- ツリービュー領域の幅 int m_treeview_area_width; //--- コンテンツ領域の幅 int m_content_area_width; //--- 背景色と背景境界の色 color m_area_color; color m_area_border_color; //--- アドレスバーの背景色 color m_address_bar_back_color; //--- アドレスバーテキストの色 color m_address_bar_text_color; //--- アドレスバーの高さ int m_address_bar_y_size; //--- (1) フォルダと (2) ファイルの画像 string m_file_icon; string m_folder_icon; //--- ファイルナビゲータコンテンツモード ENUM_FILE_NAVIGATOR_CONTENT m_navigator_content; //--- マウスの左クリックの優先順位 int m_zorder; //--- public: //--- (1) ナビゲータモード(すべてを表示/フォルダのみ) (2) ナビゲータコンテンツモード(共通)フォルダ/ローカル/すべて) void NavigatorMode(const ENUM_FILE_NAVIGATOR_MODE mode) { m_treeview.NavigatorMode(mode); } void NavigatorContent(const ENUM_FILE_NAVIGATOR_CONTENT mode) { m_navigator_content=mode; } //--- (1) アドレスバー高さ (2) ツリービュー (3) コンテンツリスト 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)背景色と(2)背景の境界の色 void AreaBackColor(const color clr) { m_area_color=clr; } void AreaBorderColor(const color clr) { m_area_border_color=clr; } //--- (1) 背景色(2) アドレスバーテキストの色 void AddressBarBackColor(const color clr) { m_address_bar_back_color=clr; } void AddressBarTextColor(const color clr) { m_address_bar_text_color=clr; } //--- (1) フィルと (2) フォルダへのファイルパスを設定する void FileIcon(const string file_path) { m_file_icon=file_path; } void FolderIcon(const string file_path) { m_folder_icon=file_path; } };
デフォルト値によるプロパティフィールドの初期化はクラスコンストラクタで行われます(コードは下記参照)。デフォルトのフォルダとナビゲーターのファイルイメージはライブラリにリソースとしてに接続されています。それらは本稿の末尾にあるリンクからのダウンロードが可能です。
#resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\folder.bmp" #resource "\\Images\\EasyAndFastGUI\\Icons\\bmp16\\text_file.bmp" //+------------------------------------------------------------------+ //| コンストラクタ | //+------------------------------------------------------------------+ 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") { //--- 要素クラスの名前を基本クラスに格納する CElement::ClassName(CLASS_NAME); //--- 左マウスクリックの優先順位を設定する m_zorder=0; }
ファイルナビゲータの作成には2つのプライベート及び1つのパブリックメソッドが必要です。ファイル構造の階層システムは前の記事で説明されたCTreeViewクラスで実装されます。このクラスを持ったファイルはFileNavigator.mqhファイルに含まれるべきです。
//+------------------------------------------------------------------+ //| FileNavigator.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ #include "Element.mqh" #include "Window.mqh" #include "TreeView.mqh" //+------------------------------------------------------------------+ //| ファイルナビゲータ作成クラス | //+------------------------------------------------------------------+ class CFileNavigator : public CElement { private: //--- 要素作成オブジェクト CRectCanvas m_address_bar; CTreeView m_treeview; //--- public: //--- ファイルナビゲータ作成メソッド bool CreateFileNavigator(const long chart_id,const int subwin,const int x,const int y); //--- private: bool CreateAddressBar(void); bool CreateTreeView(void); };
ファイルナビゲータ開発の方法を詳細に説明する前に、まずは端末の階層ファイルシステム構造の配置を調べてみましょう。
階層型ファイルシステムの構造のアレンジ
ファイルナビゲーターを作成する前に、まず、端末のファイルシステムをスキャンして、ツリービューを開発するための要素のすべてのパラメータを保存する必要があります。これらのパラメータはすべて、以前の記事で詳細に考察されています。したがって、ツリービューを開発するために保存されるパラメータのための配列リストのみが以下に表示されています。
- 総合インデックス
- 以前のノードの総合インデックス
- フォルダ/ファイル名
- ローカルインデックス
- ノードレベルl
- 1つ前のノードのローカルインデックス
- フォルダ内の要素の総数
- フォルダ内のフォルダの数
- フォルダフラグ
- 項目の状態(最小化/展開)
パラメータを準備するにはmain (g prefix)とauxiliary (l prefix)の2つの配列リストが必要です。
class CFileNavigator : public CElement { private: //--- データ格納のためのメイン配列 int m_g_list_index[]; // 総合インデックス int m_g_prev_node_list_index[]; // 1つ前のノードの総合インデックス string m_g_item_text[]; // フォルダ/ファイル名 int m_g_item_index[]; // ローカルインデックス int m_g_node_level[]; // ノードレベル int m_g_prev_node_item_index[]; // 1つ前のノードのローカルインデックス int m_g_items_total[]; // フォルダでの要素の総数 int m_g_folders_total[]; // フォルダ内のフォルダの数 bool m_g_is_folder[]; // フォルダの属性 bool m_g_item_state[]; // 項目の状態(最小化/展開) //--- データ収集のための補助的な配列 int m_l_prev_node_list_index[]; string m_l_item_text[]; string m_l_path[]; int m_l_item_index[]; int m_l_item_total[]; int m_l_folders_total[]; };
端末のファイルシステムをスキャンし、すべてのデータを収集して配列に保存するための追加のメソッドがいくつか必要になります。補助的な配列の捜査にはCFileNavigator::AuxiliaryArraysResize() メソッドが必要です。これは、現在使用されているノードレベルと比較しそれらのサイズを変更することを可能にします(下のコードを参照)。 つまり、配列サイズは、単一の引数としてメソッドに渡される現在のノードレベル値から1要素分増加されます。現在のノードレベルが0の場合の配列サイズは1、ノードレベルが1の場合の配列サイズは2などです。データコレクション時のノードレベルは増加も減少もできるので、配列のサイズはそれに応じて変更されます。このメソッドは現在のノードの配列要素を初期化するためにも使用されます。
class CFileNavigator : public CElement { private: //--- 配列サイズを1要素で増やす void AuxiliaryArraysResize(const int node_level); }; //+------------------------------------------------------------------+ //| 追加的な配列のサイズを1要素分増加する | //+------------------------------------------------------------------+ void CFileNavigator::AuxiliaryArraysResize(const int node_level) { int new_size=node_level+1; ::ArrayResize(m_l_prev_node_list_index,new_size); ::ArrayResize(m_l_item_text,new_size); ::ArrayResize(m_l_path,new_size); ::ArrayResize(m_l_item_index,new_size); ::ArrayResize(m_l_item_total,new_size); ::ArrayResize(m_l_folders_total,new_size); //--- 最後の値を初期化する m_l_prev_node_list_index[node_level] =0; m_l_item_text[node_level] =""; m_l_path[node_level] =""; m_l_item_index[node_level] =-1; m_l_item_total[node_level] =0; m_l_folders_total[node_level] =0; }
メイン配列にパラメータを持つ要素を追加するにはCFileNavigator::AddItem() メソッドが使用されます。ここでは、配列は、メソッドが呼び出されるごとに1要素によって増加されます。渡された引数の値は、要素の最後のセルに保存されます。
class CFileNavigator : public CElement { private: //--- 配列に項目を追加する void AddItem(const int list_index,const string item_text,const int node_level,const int prev_node_item_index, const int item_index,const int items_total,const int folders_total,const bool is_folder); }; //+------------------------------------------------------------------+ //| 特定のパラメータを持つ項目を配列に追加する | //+------------------------------------------------------------------+ void 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) { //--- 配列サイズを1要素で増やす 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); //--- 渡されたパラメータの値を格納する 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; }
ファイルシステムをスキャンする際には、そのコンテンツを表示するために検出された各フォルダに移動する必要があります。これを達成するにはCFileNavigator::IsFolder() メソッドが使われます。これは、現在の要素がフォルダかファイルであるかを定義します。<ファイルシステム要素の名前は、単一のパラメータとして渡されるべきです。「\」キャラクターが要素名に検知された場合、メソッドはtrueを返すことになります。これがファイルの場合、メソッドはfalseを返します。
FileIsExist()システム関数は要素がファイルかどうかをチェックする後1つの方法です。最後の検索ハンドルで渡された要素名がファイルである場合、この関数はtrueを返します。ディレクトリの場合、この関数はERR_FILE_IS_DIRECTORYエラーを生成します。
class CFileNavigator : public CElement { private: //--- 受け取られたのがファイル名かフォルダ名かを決める bool IsFolder(const string file_name); }; //+------------------------------------------------------------------+ //| 受け取られたのがファイル名かフォルダ名かを決める | //+------------------------------------------------------------------+ bool CFileNavigator::IsFolder(const string file_name) { //--- 名前が 「\\」を含む場合はフォルダである if(::StringFind(file_name,"\\",0)>-1) return(true); //--- return(false); }
ツリービューを形成するには、項目の要素の数だけでなくフォルダである要素の数を指定します。したがって、パラメータ値を定義するための適切なメソッドが必要とされせます。これらはCFileNavigator::ItemsTotal() とCFileNavigator::FoldersTotal()です。両メソッドは、2番目のメソッドではカウンタがチェックされた要素がフォルダである場合にのみ増加することを除いて似ています。両メソッドには (1) パスと (2) 検索領域の2つの引数が渡されます。検索領域は、すべての端末の共有フォルダまたは端末ローカルフォルダです。次に指定されたパスでの検索ハンドルと最初の要素名(見つかった場合)を同時に受け取るためにFileFindFirst()システム関数が使われます。有効なハンドルとオブジェクト名が最初の要素の発見を示します。
次にFileFindNext()システム関数がサイクルで使われて、同じディレクトリの要素への同時アクセスが試みされます。以前に取得されたハンドルがディレクトリキーとして使われます。要素が見つかると、関数はtrueを返しカウンタが増加されます。検索は、関数によってfalseが返された時点で停止します。両メソッドの終わりでは検索ハンドルが閉じられるべきです。このためにはFileFindClose()システム関数が使われます。
class CFileNavigator : public CElement { private: //--- 指定されたディレクトリの (1) 項目と (2) フォルダの数を戻す int ItemsTotal(const string search_path,const int mode); int FoldersTotal(const string search_path,const int mode); }; //+------------------------------------------------------------------+ //| 現在のディレクトリのファイルの数を数える | //+------------------------------------------------------------------+ int CFileNavigator::ItemsTotal(const string search_path,const int search_area) { int counter =0; // 項目カウンタ string file_name =""; // ファイル名 long search_handle =INVALID_HANDLE; // 検索ハンドル //--- 現在のディレクトリでの1番目のファイルを取得する search_handle=::FileFindFirst(search_path,file_name,search_area); //--- ディレクトリがから出ない場合 if(search_handle!=INVALID_HANDLE && file_name!="") { //--- 現在のディレクトリのオブジェクトの数を数える counter++; while(::FileFindNext(search_handle,file_name)) counter++; } //--- 検索ハンドルを閉じる ::FileFindClose(search_handle); return(counter); } //+------------------------------------------------------------------+ //| 現在のディレクトリのフォルダの数を数える | //+------------------------------------------------------------------+ int CFileNavigator::FoldersTotal(const string search_path,const int search_area) { int counter =0; // 項目カウンタ string file_name =""; // ファイル名 long search_handle =INVALID_HANDLE; // 検索ハンドル //--- 現在のディレクトリでの1番目のファイルを取得する search_handle=::FileFindFirst(search_path,file_name,search_area); //--- 空でない場合は、ループ内で現在のディレクトリ内のオブジェクトの数を数える if(search_handle!=INVALID_HANDLE && file_name!="") { //--- フォルダの場合はカウンタを増加する if(IsFolder(file_name)) counter++; //--- さらにリストを反復処理して他のフォルダを数える while(::FileFindNext(search_handle,file_name)) { if(IsFolder(file_name)) counter++; } } //--- 検索ハンドルを閉じる ::FileFindClose(search_handle); return(counter); }
ファイルシステム要素のパラメータを収集するときには以前のノードのローカルインデックスを受信する必要があります。これにはCFileNavigator::PrevNodeItemIndex() メソッドが使われます。(1) ルートディレクトリでの現在の項目のインデックスと(2) 現在のノードレベルは引数として渡されるべきです。両方の引数の値は、メソッドが呼び出さるメインメソッドの外部サイクル内のカウンタによって管理されています。
class CFileNavigator : public CElement { private: //--- 渡されたパラメータに相対した前のノードのローカルインデックスを返す int PrevNodeItemIndex(const int root_index,const int node_level); }; //+------------------------------------------------------------------+ //| 渡されたパラメータに相対した | //| 1つ前のノードのローカルインデックスを返す | //+------------------------------------------------------------------+ int CFileNavigator::PrevNodeItemIndex(const int root_index,const int node_level) { int prev_node_item_index=0; //--- ルートディレクトリ以外の場合 if(node_level>1) prev_node_item_index=m_l_item_index[node_level-1]; else { //--- リストの最初の項目でない場合 if(root_index>0) prev_node_item_index=m_l_item_index[node_level-1]; } //--- 1つ前のノードのローカルインデックスを返す return(prev_node_item_index); }
フォルダが見つかったたびに、それ(すなわち次のノードレベル)に移行します。これにはCFileNavigator::ToNextNode() メソッドが使われますパラメータ引数のいくつかはリンクで渡され値の変更を可能にします。
ここでは、ディレクトリの要素を計算するためのパスがメソッドの開始時に形成されます。そして、要素パラメータは、現在のノードレベルインデックスによって補助配列に保存されます。その後、1つ前のノード項目のインデックスを取得し、指定されたパラメータを使って総合配列に項目を追加する必要があります。言い換えれば、これは現在いるフォルダがそのために準備されたパラメータを有する配列に追加された場所そのものです。
その後、ノードカウンタが増加し、補助配列のサイズがそれに関連して修正されます。次に新しいノードレベルのためのパラメータ(1)パス(2)要素数(3)フォルダ数 を保存する必要があります。メソッドの最後には、(1) ローカルインデックスのカウンタがリセットされて (2) 現在の検索ハンドルが閉じられます。
class CFileNavigator : public CElement { private: //--- 次のノードに進む 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); }; //+------------------------------------------------------------------+ //| 次のノードに進む | //+------------------------------------------------------------------+ void CFileNavigator::ToNextNode(const int root_index,int list_index,int &node_level, int &item_index,long &handle,const string item_text,const int search_area) { //--- 検索フィルタ(* - すべてのファイルトフォルダを確認) string filter="*"; //--- パスを生成する string search_path=m_l_path[node_level]+item_text+filter; //--- データを取得して格納する m_l_item_total[node_level] =ItemsTotal(search_path,search_area); m_l_folders_total[node_level] =FoldersTotal(search_path,search_area); m_l_item_text[node_level] =item_text; m_l_item_index[node_level] =item_index; m_l_prev_node_list_index[node_level] =list_index; //--- 1つ前のノードのインデックスを取得する int prev_node_item_index=PrevNodeItemIndex(root_index,node_level); //--- 特定のデータを持つ項目を総合配列に追加する AddItem(list_index,item_text,node_level,prev_node_item_index, item_index,m_l_item_total[node_level],m_l_folders_total[node_level],true); //--- ノードカウンタを増加する node_level++; //--- 配列サイズを1要素で増やす AuxiliaryArraysResize(node_level); //--- データを取得して格納する m_l_path[node_level] =m_l_path[node_level-1]+item_text; m_l_item_total[node_level] =ItemsTotal(m_l_path[node_level]+filter,search_area); m_l_folders_total[node_level] =FoldersTotal(m_l_path[node_level]+item_text+filter,search_area); //--- ローカルインデックスのカウンタをゼロにする item_index=0; //--- 検索ハンドルを閉じる ::FileFindClose(handle); }
さて、メインアクションが実行されるメインサイクルを考えてみましょう。よりよい利便性のために、サイクルは別の CFileNavigator::FileSystemScan()メソッドに位置します。端末ファイルシステムとメイン配列の検出要素のパラメータを保存する読み取りはメソッドで行われます。これらの配列は、後に、ツリービューを構築するために使用されます。アルゴリズムは、リストの最後に到達したか、プログラムがチャートから削除されるまで機能します。
このアルゴリズムは、次のように動作します。サイクルの初めには、現在のディレクトリのリストの初めのチェック(リストの最初の要素)が行われます。確認された要素が実際に新しいものである場合、ハンドルとファイルシステムの指定された領域に検出された最初の要素の名前が受け取られ、補助配列の現在のノードレベルに要素とフォルダの数を保存します。
これが最初のインデックス出ない場合は現在のノードのローカルインデックスの順番が確認されます。ノードインデックスが既に存在している場合は、ローカルインデックスのカウンタが増加され、次のディレクトリ内の次の要素への移行が行われます。
アルゴリズムが次のコードブロックに到着した場合はルートノードに関した要素リストの範囲が超過されたかどうかを確認します。この場合、サイクルが停止され、これは(サイクルブロックの外)検索ハンドルが閉じれれてプログラムがメソッドを終了することを意味します。代わりに、ルートを除く任意のノードリストの最後に到達した場合、次のレベルへの移動が必要です。ここでは、(1)ノードのカウンタは1レベル戻され(2)ローカルインデックスカウンタがリセットされ(3)検索ハンドルが閉じられ(4)次のサイクルへの遷移が行われます。
1つ前のif-else で条件が1つも満たされていない場合は現在のファイルシステム要素はフォルダです。満たされた場合は前述されたCFileNavigator::ToNextNode() メソッドが使われて次のレベルに進みます。その後、合計インデックスカウンタが増加し、次の反復に移行するためのコマンドが起動されます。
ファイルシステムの要素がファイルである場合は、1つ前のノードのローカルインデックスが最初に受け取られることになります。その後は総合配列に指定されたパラメータを持つ項目を追加する必要があります。合計およびローカルインデックスカウンタを増やします。最終サイクル動作として、次の端末のファイルシステム要素への移行が行われます。その後、次のサイクルの反復処理が開始され、アルゴリズムは、上述したすべての条件を通過します。
class CFileNavigator : public CElement { private: //--- ファイルシステムを読んでパラメータを配列に書きこむ void FileSystemScan(const int root_index,int &list_index,int &node_level,int &item_index,int search_area); }; //+------------------------------------------------------------------+ //| ァイルシステムを読んで項目パラメータを | //| 配列に書き込む | //+------------------------------------------------------------------+ void CFileNavigator::FileSystemScan(const int root_index,int &list_index,int &node_level,int &item_index,int search_area) { long search_handle =INVALID_HANDLE; // Folder/file search handle string file_name =""; // Name of the found item (file/folder) string filter ="*"; // Search filter (* - check all files/folders) //--- ディレクトリをスキャンして配列にデータを格納する while(!::IsStopped()) { //--- これがディレクトリリストの初めにある場合 if(item_index==0) { //--- 全項目検索のパス string search_path=m_l_path[node_level]+filter; //--- 最初のファイルのハンドルと名前を取得する search_handle=::FileFindFirst(search_path,file_name,search_area); //--- 指定されたディレクトリでのファイルとフォルダの数を取得する m_l_item_total[node_level] =ItemsTotal(search_path,search_area); m_l_folders_total[node_level] =FoldersTotal(search_path,search_area); } //--- このノードのインデックスがすでに使われた場合は次のファイルに移る if(m_l_item_index[node_level]>-1 && item_index<=m_l_item_index[node_level]) { //--- ローカルインデックスのカウンタを増加する item_index++; //--- 次の項目に移る ::FileFindNext(search_handle,file_name); continue; } //--- ルートノードの最後が到達した場合はループを終了する if(node_level==1 && item_index>=m_l_item_total[node_level]) break; //--- ルートノード以外の任意のノードでリストの最後が到達した場合 else if(item_index>=m_l_item_total[node_level]) { //--- ノードカウンタを1レベル戻す node_level--; //--- ローカルインデックスのカウンタをゼロにする item_index=0; //--- 検索ハンドルを閉じる ::FileFindClose(search_handle); continue; } //--- これがフォルダの場合 if(IsFolder(file_name)) { //--- 次のノードに進む ToNextNode(root_index,list_index,node_level,item_index,search_handle,file_name,search_area); //--- 総合インデックスのカウンタを増加して新しい反復処理を始める list_index++; continue; } //--- 1つ前のノードのローカルインデックスを取得する int prev_node_item_index=PrevNodeItemIndex(root_index,node_level); //--- 特定のデータを持つ項目を総合配列に追加する AddItem(list_index,file_name,node_level,prev_node_item_index,item_index,0,0,false); //--- 総合インデックスのカウンタを増加する list_index++; //--- ローカルインデックスのカウンタを増加する item_index++; //--- 次の要素に進む ::FileFindNext(search_handle,file_name); } //--- 検索ハンドルを閉じる ::FileFindClose(search_handle); }
ここでメインメソッドであるCFileNavigator::FillArraysData()を考察しましょう。ここでは上記のすべてのメソッドが呼び出されます。
まず、ここでは、端末の共通およびローカルフォルダディレクトリの配列が設定されます。その順番はファイルナビゲータプロパティで指定されたモード(ENUM_FILE_NAVIGATOR_CONTENT)によります。デフォルトでは、共通端末フォルダがリストの初めで端末のローカルフォルダが2番目です。これはFN_BOTH(「display contents of both directories(両ディレクトリのコンテンツを表示する)」)モードが有効な場合のみに動作します。「show the contents of one directory(単一ディレクトリのコンテンツを表示する)」モードが選択されている場合はサイクルの範囲の初め(begin)と(end)終わりは適切に初期化されます。
サイクル本体の先頭で検索領域が定義された後は、次の手順が次々と行われます。
- ローカルインデックスカウンタがリセットされる。
- 補助配列のサイズが現在のノードレベルに対して変更される。
- 現在のディレクトリ内の要素やフォルダの数が同じ配列の最初のインデックスに保存される。
- 指定されたパラメータを持つ要素がメイン配列に追加される。ここではルートディレクトリとして使用されているため、現在の要素の名前はCommon\\Files\\ か MQL5\\Files\\の2ディレクトリのいずれかであり得ます。
- 総合インデックスとノードレベルカウンタが増加される。
- 補助配列のサイズは、再び現在のノードレベルに対して変更される。
- 探索領域が現在の端末のローカルフォルダに配置されている場合、補助配列の最初のインデックスの値(1)ローカルインデックスおよび(2)前のノードの総合インデックスが補正されます。
最後に、ツリービューの形成のために指定された検索領域でファイルシステムを読み込んで要素パラメータをメイン配列に保存するCFileNavigator::FileSystemScan() メソッドが呼ばれます。
class CFileNavigator : public CElement { private: //---端末要素のパラメータで配列を埋める void FillArraysData(void); }; //+------------------------------------------------------------------+ //| 配列にファイルシステム要素のパラメータを書き入れる | //+------------------------------------------------------------------+ void CFileNavigator::FillArraysData(void) { //--- (1) 総合インデックス (2) ノードレベル (3) ローカルインデックスのカウンタ int list_index =0; int node_level =0; int item_index =0; //--- 両ディレクトリが表示されるべきかどうか(共通 (0)/ローカル (1)) int begin=0,end=1; //--- ローカルディレクトリコンテンツのみが表示される場合 if(m_navigator_content==FN_ONLY_MQL) begin=1; //--- 共通ディレクトリコンテンツのみが表示される場合 else if(m_navigator_content==FN_ONLY_COMMON) begin=end=0; //--- 指定されたディレクトリの反復処理 for(int root_index=begin; root_index<=end; root_index++) { //--- ファイル構造をスキャンするディレクトリを決定する int search_area=(root_index>0)?0 : FILE_COMMON; //--- ローカルインデックスのカウンタをリセットする item_index=0; //--- 配列のサイズを1要素で増加する(ノードレベルに早退して) AuxiliaryArraysResize(node_level); //--- 指定されたディレクトリでのファイルとフォルダの数を取得する(* - すべてのファイルとフォルダをスキャンする) string search_path =m_l_path[0]+"*"; m_l_item_total[0] =ItemsTotal(search_path,search_area); m_l_folders_total[0] =FoldersTotal(search_path,search_area); //--- リストの一番上にルートディレクトリ名を持った項目を追加する string item_text=(root_index>0)?"MQL5\\Files\\" : "Common\\Files\\"; AddItem(list_index,item_text,0,0,root_index,m_l_item_total[0],m_l_folders_total[0],true); //--- 総合インデックスとノードレベルのカウンタを増加する list_index++; node_level++; //--- 配列のサイズを1要素で増加する(ノードレベルに早退して) AuxiliaryArraysResize(node_level); //--- 端末のローカルフォルダのディレクトリの最初の項目を初期化する if(root_index>0) { m_l_item_index[0] =root_index; m_l_prev_node_list_index[0] =list_index-1; } //--- ディレクトリをスキャンして配列にデータを格納する FileSystemScan(root_index,list_index,node_level,item_index,search_area); } }
コントロール作成メソッド
CFileNavigator::FillArraysData()メソッドはファイルナビゲータコントロールの作成前に一度のみ呼び出されます。実際には、これはライブラリのユーザにも認識されていない可能性があります。ファイルナビゲータの外観とコンテンツに影響を与えるいくつかの一般的なプロパティを指定するだけで十分です。
//+------------------------------------------------------------------+ //| ファイルナビゲータ | //+------------------------------------------------------------------+ bool CFileNavigator::CreateFileNavigator(const long chart_id,const int subwin,const int x,const int y) { //--- フォームポインタがなければ終了する 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); } //--- 端末のファイルシステムをスキャンして配列にデータを格納する FillArraysData(); //--- 変数の初期化 m_id =m_wnd.LastId()+1; m_chart_id =chart_id; m_subwin =subwin; m_x =x; m_y =y; //--- 端からのマージン CElement::XGap(CElement::X()-m_wnd.X()); CElement::YGap(CElement::Y()-m_wnd.Y()); //--- 要素の作成 if(!CreateAddressBar()) return(false); if(!CreateTreeView()) return(false); //--- ダイアログウィンドウか最小化されたウィンドウの場合は要素を非表示にする if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized()) Hide(); //--- return(true); }
ファイルナビゲーターを開発する際にはアドレスバーの作成が最初のステップです。これは単一のOBJ_BITMAP_LABEL型オブジェクトになります。その内容は完全に描画されます。キャンバスに描かれたコントロールを作成する例は以前に考察されました。そこで、ここでは開発したコントロールに関連するニュアンスのみを検討します。
アドレスバーの描画には2つのメソッドが必要です。
- CFileNavigator::Border()メソッドはアドレスバーのフレームの描画に使われます。
- CFileNavigator::UpdateAddressBar() は、ツリービューで選択されたディレクトリを含む最新の変更の描画と表示のためのメインメソッドです。
フレームは、例えばグラフィカルインタフェース IV:情報インターフェース要素(チャプター1)で他のコントロールの開発時にすでに説明されたので、ここではCFileNavigator::UpdateAddressBar() メソッドコードのみを考察します。
アドレスバーの背景色とファイルナビゲータを作成する前にユーザがY 軸によってそのサイズを指定できることは前述しました。キャンバス領域内のテキストは左マージンからX軸の5画素インデントを持ちY軸方向では中央に配置されるべきです。Y軸に沿ったサイズは分かるため、アドレスバーの高さを2で割ってY 座標を取得します。キャンバスでテキストを描画するTextOut()メソッドを呼び出すためには 左と中央のアンカー型を定義するフラグの受け渡しが必要です。
ファイルナビゲーターの最初のインストール時にはパスがまだ初期化されておれず、その格納のためのm_current_pathには空白の文字列が含まれています。ファイルシステム要素は非常に多数であり得るので、配列を形成しツリービューを作成するには時間がかかることがあります。アドレスバーが最初に作成されているため、ユーザに待機促すメッセージを表示することもできます。ここでの例は「Loading.(読み込み中。)Please wait...(お待ちください)」です。
メソッドの最後では最新の変更を表示するためにキャンバスが更新されます。
class CFileNavigator : public CElement { private: //--- アドレスバーの境界を描画する void Border(void); //--- アドレスバーに現在のパスを表示する void UpdateAddressBar(void); }; //+------------------------------------------------------------------+ //| アドレスバーに現在のパスを表示する | //+------------------------------------------------------------------+ void CFileNavigator::UpdateAddressBar(void) { //--- 座標 int x=5; int y=m_address_bar_y_size/2; //--- 背景をクリアする m_address_bar.Erase(::ColorToARGB(m_address_bar_back_color,0)); //--- 背景フレームを描画する Border(); //--- テキストプロパティ m_address_bar.FontSet("Calibri",14,FW_NORMAL); //--- パスが設定されていない場合にはデフォルトの文字列を表示する if(m_current_full_path=="") m_current_full_path="Loading. Please wait..."; //--- ファイルナビゲータにアドレスバーへのパスを出力する m_address_bar.TextOut(x,y,m_current_path,::ColorToARGB(m_address_bar_text_color),TA_LEFT|TA_VCENTER); //--- キャンバスを描画のために更新する m_address_bar.Update(); }
アドレスバーの幅は作成前にCFileNavigator::CreateAddressBar() メソッドで計算されます。コンテンツ領域が設定で無効になっている場合は、アドレスバーの幅はツリービューの幅に等しいです。他の場合には、共通なコントロール幅のためにツリービュークラス(CTreeView)で実装された原則を使って計算されます。
オブジェクトの作成後には、背景、フレーム及びデフォルトメッセージの描画のために CFileNavigator::UpdateAddressBar() メソッドが呼び出されます。
//+------------------------------------------------------------------+ //| アドレスバーの作成 | //+------------------------------------------------------------------+ bool CFileNavigator::CreateAddressBar(void) { //--- オブジェクト名の形成 string name=CElement::ProgramName()+"_file_navigator_address_bar_"+(string)CElement::Id(); //--- 座標 int x =CElement::X(); int y =CElement::Y(); //--- サイズ // 幅を計算する int x_size=0; //--- コンテンツ領域がない場合 if(m_content_area_width<0) x_size=m_treeview_area_width; else { //--- コンテンツ領域の指定された幅が定義されている場合 if(m_content_area_width>0) x_size=m_treeview_area_width+m_content_area_width-1; //--- コンテンツ領域の右端がフォームの右端と一致しなければない場合 else x_size=m_wnd.X2()-x-2; } //--- 高さ int y_size=m_address_bar_y_size; //--- オブジェクトの作成 if(!m_address_bar.CreateBitmapLabel(m_chart_id,m_subwin,name,x,y,x_size,y_size,COLOR_FORMAT_XRGB_NOALPHA)) return(false); //--- チャートに取り付ける if(!m_address_bar.Attach(m_chart_id,name,m_subwin,1)) return(false); //--- プロパティを設定する m_address_bar.Background(false); m_address_bar.Z_Order(m_zorder); m_address_bar.Tooltip("\n"); //--- サイズを格納する CElement::X(x); CElement::Y(y); //--- サイズを格納する CElement::XSize(x_size); CElement::YSize(y_size); //--- 端からのマージン m_address_bar.XGap(x-m_wnd.X()); m_address_bar.YGap(y-m_wnd.Y()); //--- アドレスバーを更新する UpdateAddressBar(); //--- オブジェクトポインタを格納する CElement::AddToArray(m_address_bar); //--- ダイアログウィンドウか最小化されたウィンドウの場合は要素を非表示にする if(m_wnd.WindowType()==W_DIALOG || m_wnd.IsMinimized()) m_address_bar.Timeframes(OBJ_NO_PERIODS); //--- return(true); }
ここで、ツリービュー設定のためにCFileNavigator::CreateTreeView() メソッドを呼び出すことができます。前回の記事では、 CTreeView型のコントロールを作成する前に、最初にコントロールの配列へ配列パラメータを持った項目を追加する必要があると指摘しました。この段階で、項目のパラメータはすべて CFileNavigatorメインクラス配列に配置されます。ここではサイクルでそれらをツリービュークラスに受け渡すだけです。
各項目の画像は同じサイクルで定義されています。そのうえ、フォルダ名の最後の文字(「\」)は削除されるべきです。
//+------------------------------------------------------------------+ //| ツリービューを作成する | //+------------------------------------------------------------------+ bool CFileNavigator::CreateTreeView(void) { //--- ウィンドウポインタを格納する m_treeview.WindowPointer(m_wnd); //--- プロパティを設定する 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); //--- ツリービュー配列を形成する for(int i=0; i<items_total; i++) { //--- 項目のアイコンを設定する(フォルダ/ファイル) string icon_path=(m_g_is_folder[i])?m_folder_icon : m_file_icon; //--- フォルダの場合は文字列の最後の文字(「\」)を削除する 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); //--- ツリービューに項目を追加する 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]); } //--- ツリービューを作成する if(!m_treeview.CreateTreeView(m_chart_id,m_subwin,m_x,m_y+m_address_bar_y_size)) return(false); //--- return(true); }
イベントハンドラ
(1) ファイルシステムに相対した(ハードドライブのボリュームラベルを含む)ツリーリストで選択されたディレクトリのフルパス(2) 端末サンドボックスに相対したパス及び (3) 現在のディレクトリ領域がクラスフィールドに保存されます。これらの値を得るためには適切なメソッドが必要とされます。加えて、選択された要素すなわちファイルを取得するためのCFileNavigator::SelectedFile() メソッドも必要です。
class CFileNavigator : public CElement { private: //--- 端末の「サンドボックス」に相対した現在のパス string m_current_path; //--- ハードドライブのボリュームラベルを含む、システムに相対した現在のパス string m_current_full_path; //--- 現在のディレクトリの領域 int m_directory_area; //--- public: //--- (1) 現在のパス2) フルパス (3) 選択されたファイルを返す string CurrentPath(void) const { return(m_current_path); } string CurrentFullPath(void) const { return(m_current_full_path); } //--- (1) ディレクトリ領域を (2) 選択されたファイルを返す int DirectoryArea(void) const { return(m_directory_area); } string SelectedFile(void) const { return(m_treeview.SelectedItemFileName()); } };
ファイルナビゲータのイベントハンドラはON_CHANGE_TREE_PATH IDを持つ単一のイベントを受け入れるように設定されています。それは、構造体中の項目を選択するときにツリービューによって生成されます。このIDを持つメッセージの処理にはCFileNavigator::OnChangeTreePath() メソッドが実装されました。
初めに、ツリービューに保存されたパスが受け取られます。その後、パスの種類( 共通またはローカル)によって(1) ルートデータフォルダのアドレスが受け取られ (2) 短いパスと長いパスが掲載されてディレクトリ領域フラグが保存されます。
class CFileNavigator : public CElement { private: //--- ツリービューで新しいパスを選択するイベントの処理 void OnChangeTreePath(void); }; //+------------------------------------------------------------------+ //| ツリービューで新しいパスを選択するイベントの処理 | //+------------------------------------------------------------------+ void CFileNavigator::OnChangeTreePath(void) { //--- 現在のパスを取得する string path=m_treeview.CurrentFullPath(); //--- これが端末の共通フォルダの場合 if(::StringFind(path,"Common\\Files\\",0)>-1) { //--- 端末の共通フォルダのアドレスを取得する string common_path=::TerminalInfoString(TERMINAL_COMMONDATA_PATH); //--- (イベントで受信された)文字列から「Common\」接頭辞を除く path=::StringSubstr(path,7,::StringLen(common_path)-7); //--- パスを生成する(短いパス及びフルパス) m_current_path =::StringSubstr(path,6,::StringLen(path)-6); m_current_full_path =common_path+"\\"+path; //--- ディレクトリ領域を格納する m_directory_area=FILE_COMMON; } //--- これが端末のローカルフォルダの場合 else if(::StringFind(path,"MQL5\\Files\\",0)>-1) { //--- 端末のローカルフォルダでデータのアドレスを取得する string local_path=::TerminalInfoString(TERMINAL_DATA_PATH); //--- パスを生成する(短いパス及びフルパス) m_current_path =::StringSubstr(path,11,::StringLen(path)-11); m_current_full_path =local_path+"\\"+path; //--- ディレクトリ領域を格納する m_directory_area=0; } //--- アドレスバーで現在のパスを表示する UpdateAddressBar(); }
結果的にON_CHANGE_TREE_PATHイベントが到着した場合、下のコードにあるようにCFileNavigator::OnChangeTreePath() メソッドが呼ばれるべきです。
//+------------------------------------------------------------------+ //| イベントハンドラ | //+------------------------------------------------------------------+ void CFileNavigator::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- 「ツリービューでのパス変更」のイベント処理 if(id==CHARTEVENT_CUSTOM+ON_CHANGE_TREE_PATH) { OnChangeTreePath(); return; } }
同じIDを持ったイベントはカスタムクラスハンドラで受け取られることができます。例は以下でお話しします。
ライブラリエンジンへのコントロールの統合
コントロールが正しく動作するためには、ライブラリーエンジンへの統合が必要です。追加は主に、他のすべてのライブラリ要素が接続されているWndContainer.mqhファイルのCWndContainer基本クラスに挿入されます。以下が追加される必要があります。
- ファイルナビゲータのためのプライベート配列
- グラフィックインターフェース(CFileNavigator)でこの型のファイルナビゲータアプリケーションの数を受け取るメソッド
- データベースにファイルナビゲータ要素へのポインタを保存するメソッド
CWndContainerクラスの短縮版(追加されるべきもののみ)は下のコードに表示されます。:
#include "FileNavigator.mqh" //+------------------------------------------------------------------+ //| すべてのインターフェースオブジェクトを格納するクラス | //+------------------------------------------------------------------+ class CWndContainer { protected: //--- ウィンドウ配列 CWindow *m_windows[]; //--- 要素配列の構造体 struct WindowElements { //--- ファイルナビゲータ CFileNavigator *m_file_navigators[]; }; //--- 各ウィンドウの要素配列の配列 WindowElements m_wnd[]; //--- public: //--- ナビゲータの数 int FileNavigatorsTotal(const int window_index); //--- private: //--- ツリービューへのポインタをベースに格納する bool AddFileNavigatorElements(const int window_index,CElement &object); };
メソッドのコードの詳細は以下の添付ファイルで調べることができます。
ファイルナビゲータの検証
ファイルナビゲータのテストの準備がそろいました。以前の記事のEAのコピーを作ってメインメニューとステータスバー以外のすべてをユーザークラス(CProgram)から削除しましょう。メインファイルエディタのコンテンツモードを検証するために、以下のコードに示されたような、2つの外部EAパラメータを作成してみましょう。これらのタイプの列挙とモードは、すでに本稿の上記のセクションで詳細に検討されています。
//+------------------------------------------------------------------+ //| Program.mqh | //| Copyright 2015, MetaQuotes Software Corp. | //| http://www.mql5.com | //+------------------------------------------------------------------+ //--- 外部パラメータ input ENUM_FILE_NAVIGATOR_CONTENT NavigatorContent =FN_BOTH; // Navigator content input ENUM_FILE_NAVIGATOR_MODE NavigatorMode =FN_ONLY_FOLDERS; // Navigator mode
ここでは、CFileNavigatorファイルナビゲータクラスインスタンス宣言とともに要素作成メソッドとインスタンスが接続されるフォームの端からのマージンも必要です。
class CProgram : public CWndEvents { private: //--- ファイルナビゲータ CFileNavigator m_navigator; //--- private: //--- ファイルナビゲータ #define NAVIGATOR1_GAP_X (2) #define NAVIGATOR1_GAP_Y (43) bool CreateFileNavigator(void); };
ファイルナビゲータ作成に使われるCProgram::CreateFileNavigator() メソッドのコードは下に示されています。ナビゲータの高さは10ポイントに設定されます。デフォルトのサイズ(20画素)はCFileNavigatorクラスコンストラクタで指定されます。上記したようにコンテンツモードは外部パラメータで管理されています。コンポーネントの外観は、ポインタによって受信できるメソッドで構成できます。下のコードはツリービューポインタとスクロールバーの受信の例を使ってこれを示します。メソッドの呼び出しはグラフィックインターフェースを作成するためのメインメソッドに配置されるべきです。
//+------------------------------------------------------------------+ //| エキスパートパネルを作成する | //+------------------------------------------------------------------+ bool CProgram::CreateExpertPanel(void) { //--- コントロールのフォーム1 の作成 //--- コントロールの作成 // メインメニュー //--- コンテキストメニュー //--- ステータスバーの作成 //--- ファイルナビゲータの作成 if(!CreateFileNavigator()) return(false); //--- チャートの再描画 m_chart.Redraw(); return(true); } //+------------------------------------------------------------------+ //| ファイルナビゲータを作成する | //+------------------------------------------------------------------+ bool CProgram::CreateFileNavigator(void) { //--- フォームへのポインタを保存する m_navigator.WindowPointer(m_window1); //--- 座標 int x=m_window1.X()+NAVIGATOR1_GAP_X; int y=m_window1.Y()+NAVIGATOR1_GAP_Y; //--- 作成の前にプロパティを設定する 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); //--- スクロールバープロパティ m_navigator.TreeViewPointer().GetScrollVPointer().AreaBorderColor(clrLightGray); m_navigator.TreeViewPointer().GetContentScrollVPointer().AreaBorderColor(clrLightGray); //--- コントロールを作成する if(!m_navigator.CreateFileNavigator(m_chart_id,m_subwin,x,y)) return(false); //--- コントロールへのポインタをデータベースに追加する CWndContainer::AddToElementsArray(0,m_navigator); return(true); }
例として、イベントハンドラログがフルパスと短いパスの両方だけでなく現在選択されているファイル名を表示するようにしてみましょう。ファイルが選択されている場合、開いてログに渡す最初の3行を読み取ります。ファイルの位置に適応するフラグの取得にはCFileNavigator::DirectoryArea()メソッドが使われてディレクトリ領域の管理が可能になります。
//+------------------------------------------------------------------+ //| イベントハンドラ | //+------------------------------------------------------------------+ void CProgram::OnEvent(const int id,const long &lparam,const double &dparam,const string &sparam) { //--- 「ツリービューでのパスの変更」イベントの処理 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()); //--- ファイルが選択されている場合は読み込む(初めの3行) if(m_navigator.SelectedFile()!="") { //--- ファイルへのパスを形成する string path=m_navigator.CurrentPath()+m_navigator.SelectedFile(); //--- 指定されたファイルへのハンドルを受け取る int filehandle=::FileOpen(path,FILE_READ|FILE_TXT|FILE_ANSI|m_navigator.DirectoryArea(),'\n'); //--- ハンドルが受け取られたら初めの3行を読む 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)); } //--- ファイルを閉じる ::FileClose(filehandle); } ::Print("---"); } }
ここで、プログラムをコンパイルしてプログラムをチャートに読み込みます。結果は以下のスクリーンショットに見られます。ファイルナビゲータの内容はお使いのPC上の端末ファイルシステムの内容と一致する必要があります。
図1 ファイルナビゲータの検証
下のスクリーンショットは、折り畳まれていないファイルナビゲーションツリービューを示します。
図2 ファイルナビゲーションツリービューの折りたたまれていない構造
テストEAに組み込まれたカスタムクラスイベントハンドラを扱う場合、ファイルがログに渡された後に次のような結果が得られます。
2016.06.16 02:15:29.994 CProgram::OnEvent > Строка 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 > Строка 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 > Строка 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 > Открыт файл: 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
すべてがうまく動作します。
おわりに
グラフィカルインタフェースを作成するためのライブラリーの開発の現段階での概略は以下に示されます。
図3 開発の現段階でのライブラリの構造
これで、MetaTrader取引ターミナルのグラフィカルインタフェース作成ライブラリの開発に関するシリーズの第八部を終わります。このパートでは、静的なドロップダウンカレンダー、ツリービュー、マウスポインタとファイルナビゲータなどのコントロールが考察されました。
シリーズの次の部分(第九部)では下記のコントロールを考察します。
- 色の選択
- プログレスバー
- 折れ線グラフ
第八部全体の資料は下記からダウンロードされ検証することができます。これらのファイルに含まれている資料の使用についてご質問がある場合は、以下のリストにある記事のいずれかでライブラリの開発の詳細をご参照になるか、本稿へのコメント欄でご質問ください。
第八部の記事(チャプター)のリストは下記の通りです。
- グラフィカルインタフェースVIII:カレンダーコントロール(チャプター1)
- グラフィカルインタフェースVIII: ツリービューコントロール(チャプター2)
- グラフィカルインタフェースVIII: ファイルナビゲータコントロール(チャプター3)
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/2541
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索