// フォルダツリーコントロールクラス // (C)2003 Yutaka Wada, AirparkLab // ツリーコントロールクラス CTreeCtrl を基本クラスに持ち // 与えられたパスのフォルダツリーを作成するクラス。 // ツリーアイテムが選択されると親ウインドウに指定したメッセージを通知する。 /* 説明 ダイアログにフォルダツリーコントロールを実装する手順は以下の通り ・ダイアログメンバにフォルダツリーコントロールとアイコンイメージリストを追加(ダイアログ.h) #include "FolderTreeCtrl.h" // フォルダツリーコントロール定義 private: CFolderTreeCtrl m_FolderTreeCtrl; // フォルダツリーコントロール CImageList m_ImageList; // アイコンイメージリスト ・ツリーアイテムが選択された時に通知されるユーザメッセージを定義(ダイアログ.h) #define USER_MESSAGE_SELECTED_ITEM (WM_APP+100) // アイテム選択メッセージ ・ダイアログの初期化処理(OnInitDialog()@ダイアログ.cpp) // リソースエディタで作成した非可視のスタティックピクチャーから // フォルダツリーコントロールを配置する位置とサイズを取得 CRect Rect; GetDlgItem( IDC_STATIC_TREE_AREA )->GetWindowRect( Rect ); this->ScreenToClient( &Rect ); // フォルダツリーコントロールの作成 m_FolderTreeCtrl.Create( WS_VISIBLE|TVS_HASLINES|TVS_LINESATROOT|TVS_HASBUTTONS, Rect, this, 2000, USER_MESSAGE_SELECTED_ITEM ); m_FolderTreeCtrl.ModifyStyleEx( NULL, WS_EX_CLIENTEDGE, SWP_DRAWFRAME ); // フォルダツリーコントロールにアイコンイメージを登録 m_ImageList.Create( 16, 16, TRUE, 2, 0 ); m_ImageList.Add( AfxGetApp()->LoadIcon( IDI_ICON0 ) ); // リソースエディタで作成した閉アイテムアイコン m_ImageList.Add( AfxGetApp()->LoadIcon( IDI_ICON1 ) ); // リソースエディタで作成した開アイテムアイコン m_FolderTreeCtrl.SetImageList( &m_ImageList, TVSIL_NORMAL ); ・フォルダツリーを表示するタイミング(ダイアログ.cpp) // フォルダツリーを表示する CString Path = "c:\\windows\\"; m_FolderTreeCtrl.SetFolder( Path ); ・フォルダツリーコントロールからアイテムの選択メッセージを受け取る(ダイアログ.cpp) // WindowProc() をオーバライドする LRESULT C????Dlg::WindowProc(UINT message, WPARAM wParam, LPARAM lParam) { // フォルダツリーコントロールからアイテム選択メッセージ処理 if( message == USER_MESSAGE_SELECTED_ITEM ){ CString Item = (char *)wParam; // 選択アイテム文字列 CString Path = (char *)lParam; // 選択パス文字列 // m_Disp.SetWindowText( Path ); // エディットボックスに表示 } return CDialog::WindowProc(message, wParam, lParam); } パブリックメンバ関数は以下の通り // コンストラクタ CFolderTreeCtrl() // デストラクタ ~CFolderTreeCtrl() // フォルダツリーコントロールの作成 BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, UINT nMessage ); 引数 dwStyle コントロールのスタイル(CTreeCtrl互換) rect コントロールのサイズと位置 pParentWnd コントロールの親ウィンドウ nID コントロールID nMessage アイテムが選択された時に親ウィンドウに通知するメッセージ 戻値 0:失敗, それ以外:正常終了 // 表示するフォルダの設定 BOOL SetFolder( CString Path ) 引数 Path ツリー表示するトップフォルダのフルパス文字列 戻値 0:失敗, それ以外:正常終了 */ #ifndef __FOLDER_TREE_CTRL_H__ #define __FOLDER_TREE_CTRL_H__ #include "direct.h" class CFolderTreeCtrl: public CTreeCtrl { UINT m_nMessage; // アイテムが選択されたときのメッセージ CString m_RootPath; // ルートアイテムのパス // 親ウインドウに通知したメッセージを処理するハンドラ BOOL OnChildNotify(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pLResult) { // ツリーコントロール関連メッセージは WM_NOTIFY で通知されている if( message == WM_NOTIFY ){ NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)lParam; // アイテムが選択されたとき if( pNMTreeView->hdr.code == TVN_SELCHANGED ){ // 未知による操作は何もしない(デフォルトハンドラを実行して抜ける) if( pNMTreeView->action == TVC_UNKNOWN ) return CTreeCtrl::OnChildNotify(message, wParam, lParam, pLResult); // 選択されたアイテムが有効である場合 HTREEITEM hItem = pNMTreeView->itemNew.hItem; if( hItem ){ // 親ウインドウに選択メッセージを通知する CString Item = this->GetItemText( hItem ); CString Path = GetPath( hItem ); GetParent()->SendMessage( m_nMessage, (WPARAM)(LPCTSTR)Item, (LPARAM)(LPCTSTR)Path ); } } // 展開されようとするとき else if( pNMTreeView->hdr.code == TVN_ITEMEXPANDING ){ // 未知による操作は何もしない(デフォルトハンドラを実行して抜ける) if( pNMTreeView->action == TVC_UNKNOWN ) return CTreeCtrl::OnChildNotify(message, wParam, lParam, pLResult); // 展開されようとするアイテムが有効である場合 HTREEITEM hParent = pNMTreeView->itemNew.hItem; if( hParent ){ // 既に展開されている場合は何もしない(デフォルトハンドラを実行して抜ける) if( TVIS_EXPANDED & this->GetItemState( hParent, TVIS_EXPANDED ) ) return CTreeCtrl::OnChildNotify(message, wParam, lParam, pLResult); // 各子アイテムに孫アイテムを追加する HTREEITEM hChild = this->GetChildItem( hParent ); for( ; ; ){ // 子アイテムが無ければ何もしない if( hChild == NULL ) break; // 孫アイテムを追加し直すために一旦子アイテムを削除し、再追加する CString Item = this->GetItemText( hChild ); this->DeleteItem( hChild ); hChild = this->InsertItem( (LPCTSTR)Item, 0, 1, hParent, TVI_SORT ); // 子アイテムに孫アイテムを追加する CString Path = GetPath( hChild ); AddChildren( Path, hChild ); // 次の子アイテムを検索する hChild = GetNextSiblingItem( hChild ); } } } } // デフォルトハンドラの実行 return CTreeCtrl::OnChildNotify(message, wParam, lParam, pLResult); } // アイテムハンドルからフォルダフルパスを返す CString GetPath( HTREEITEM hItem ) { // ルートアイテムであればルートフォルダを返して終了 CString Path; if( hItem == this->GetRootItem() ){ Path = m_RootPath + "\\"; return( Path ); } // 子アイテムであれば親アイテムへ順次さかのぼってフルパスを作成する CString Child = this->GetItemText( hItem ); HTREEITEM hParent; for( ; ; ){ hParent = this->GetParentItem( hItem ); if( hParent && hParent != this->GetRootItem() ){ Path = this->GetItemText( hParent ); Child = Path + "\\" + Child; hItem = hParent; } else { Path = m_RootPath + "\\" + Child + "\\"; break; } } return( Path ); } // フォルダの検索 // pFolder で指定したフォルダが実在するかどうか調査し、 // 実在すれば TRUE 、無ければ FALSE を返す。 BOOL FolderSearch( const char *pFolder ) { BOOL ret; // カレントディレクトリの取得 char szCurrent[MAX_PATH]; GetCurrentDirectory( MAX_PATH, szCurrent ); // カレントディレクトリとして移動できるか調査 if( ::_chdir( pFolder ) == -1 ) ret = FALSE; else ret = TRUE; // カレントディレクトリの復帰 ::_chdir( szCurrent ); return( ret ); } // フルパスから最下位フォルダ名またはファイル名を戻す // pPath で与えられたフルパスファイル(またはフォルダ)名からファイル名のみ、 // または最下位フォルダ名へのアドレスを戻す。 // 戻り値のアドレスは引数である pPath 内部のアドレスであることに注意すること。 // また pPath の末尾が'\'である場合は pPath そのものが戻り値となる。 const char* GetElementName( const char *pPath ) { int nLen = strlen( pPath ); if( nLen <= 3 ) return( pPath ); // セパレータ文字'\'の検索(SJISコード対応) for( int i=nLen-1; i>=2; i-- ){ if( (unsigned char)pPath[i-1] < 0x81 && pPath[i] == '\\' ){ i++; break; } else if( (unsigned char)pPath[i-2] >= 0x81 && pPath[i] == '\\' ){ i++; break; } } if( i >= nLen || i <= 1 ) return( pPath ); else if( pPath[i] == '\0' ) return( pPath ); else return( &pPath[i] ); } // pPath で与えられたパス名の末尾が'\'であればNULL文字に変更し削除する。 // 与えられたアドレスの内容を変更するので注意すること。 // 存在するフォルダであれば TRUE を戻し、存在しないフォルダであれば FALSE を返す。 BOOL RemoveTailSeparater( char *pPath ) { int nLen = strlen( pPath ); // 末尾の'\'処理(SJISコード対応) if( nLen >= 2 && (unsigned char)pPath[nLen-2] < 0x81 && pPath[nLen-1] == '\\' ) pPath[nLen-1] = '\0'; else if( nLen >= 3 && (unsigned char)pPath[nLen-3] >= 0x81 && pPath[nLen-1] == '\\' ) pPath[nLen-1] = '\0'; // 実在するフォルダかどうかのチェックしてその結果を戻す BOOL bRet = FolderSearch( pPath ); return( bRet ); } // 与えられたパスの下にある子フォルダをツリーコントロールアイテムとして追加する int AddChildren( CString Path, HTREEITEM hItem ) { HANDLE hFile; // 検索ハンドル WIN32_FIND_DATA data; // 発見ファイル情報を受ける変数 int nItem = 0; // 追加アイテム数 // 末尾の'\'処理(SJISコード対応) int nLen = Path.GetLength(); if( nLen >= 2 && (unsigned char)Path[nLen-2] < 0x81 && Path[nLen-1] == '\\' ) Path += "*.*"; else if( nLen >= 3 && (unsigned char)Path[nLen-3] >= 0x81 && Path[nLen-1] == '\\' ) Path += "*.*"; else Path += "\\*.*"; // フォルダを検索 hFile = FindFirstFile( (LPCSTR)Path, &data ); if( hFile != INVALID_HANDLE_VALUE ){ do{ // 特殊フォルダ ".", ".." の場合は次へ if( strcmp( ".", data.cFileName ) == 0 || strcmp( "..", data.cFileName ) == 0 ) continue; // フォルダならアイテムを作成 if( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ){ this->InsertItem( data.cFileName, 0, 1, hItem, TVI_SORT ); nItem++; } } while( FindNextFile( hFile, &data ) ); // 次を検索 } FindClose( hFile ); // ハンドルの後始末 return( nItem ); } public: // コンストラクタ CFolderTreeCtrl() { } // デストラクタ ~CFolderTreeCtrl() { } // 作成 BOOL Create( DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, UINT nMessage ) { // アイテムが選択されたときのメッセージを保存 m_nMessage = nMessage; // 基本クラスの作成関数を実行 return( CTreeCtrl::Create( dwStyle, rect, pParentWnd, nID ) ); } // 表示するフォルダの設定 BOOL SetFolder( CString Path, BOOL ExpandFlag = FALSE ) { // 指定されたパスが現存するフォルダでなければエラー if( FolderSearch( (LPCTSTR)Path ) != TRUE ) return( FALSE ); // 一旦全てのアイテムを削除 this->DeleteAllItems(); // ルートアイテムの追加 RemoveTailSeparater( Path.GetBuffer( MAX_PATH ) ); Path.ReleaseBuffer(); m_RootPath = Path; HTREEITEM hItem = this->InsertItem( GetElementName( (LPCTSTR)Path ), 0, 1, TVI_ROOT, TVI_SORT ); // 直属の子ディレクトリをツリーアイテムとして追加 AddChildren( Path, hItem ); // フラグ指定があればツリーを展開 if( ExpandFlag ) this->Expand( hItem, TVE_EXPAND ); return( TRUE ); } }; #endif