ウィンドウズプログラミング講座第12回:ツリービュー連携
概要
ツリービューとドキュメントビューの連携処理を実装します。
ということはつまり、ツリービューでファイルを選択するとドキュメントビューで表示します。そういうことです。
但し、間違ってシステムファイルをこわすと危ないので保存機能は禁止してあります。表示も禁止してます。
これらを解除するにはちゃんとしたチェックをしてからでないといけません。
とりあえず選択されたファイルをバイナリモードで表示します。
但し大きすぎるファイルを表示しようとすると止まってしまう可能性があるため、最大でも頭の1メガだけ読込むようにしました。
アイテム選択イベント
ツリービューでアイテムが選択されると TVN_SELCHANGED 通知が来ます。
以下の関数はそれに対応するやつです。
LRESULT TreeView :: OnTvnSelChanged() { TVITEM tvItem; TCHAR path[ _MAX_PATH ]; WIN32_FIND_DATA ffd; HANDLE hFind; tvItem.mask = TVIF_HANDLE | TVIF_TEXT | TVIF_PARAM; tvItem.hItem = lpnmtv->itemNew.hItem; tvItem.pszText = path; tvItem.cchTextMax = sizeof path; TreeView_GetItem( hTree, & tvItem ); if ( tvItem.lParam != 0 ) {
ウィンドウクラスで LPARAM 引数を LPNMTREEVIEW 型として共用体宣言してある lpnmtv を使用します。
lpnmtv->itemNew はいま選択されたアイテムのことで、このハンドルから TreeView_GetItem() 関数で選択されたアイテム情報を取得します。
tvItem.lParam にはファイル属性が入ってるはずですが、これが 0 ということはおそらくルートアイテムです。ルートアイテムなら名前しかありません。
ルート以外のアイテムならもっとファイル情報を調べます。
MakeFullPath( path, tvItem.hItem ); hFind = FindFirstFile( path, & ffd ); if ( hFind != INVALID_HANDLE_VALUE ) { FindClose( hFind ); app.docView.FileClose(); if ( ( ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) == 0 ) { app.docView.SetPath( path ); app.docView.Load(); app.UpdateMenu();
選択されたファイルがフォルダでないならドキュメントに表示します。
ファイル情報を取得する関数ですが、できるだけファイルをオープンしたくないので DOS の stat() 関数的なやつを探したんですが Win32 関数には無かったので仕方なく FindFirstFile() 関数を使いました。
これもハンドルをクローズしなくちゃならないので面倒ですが、オープンするわけじゃないので若干ましかと。
sprintf( path + strlen( path ), " %d bytes", ffd.nFileSizeLow ); } else { strcat( path, " [DIR]" ); } } } SetWindowText( app.hStatBar, path ); return 0; }
選択項目のフルパス名を取得してステータスバーに表示しています。フォルダ以外ならファイルサイズも表示しました。
起動時のアイテム選択
ドキュメントビュー側ではプロファイルで最後にオープンしたファイル名を保存しているので、再起動時にこれを読み込んでいます。
このファイルをツリービューの初期状態で選択させる処理を実装しました。
BOOL DocView :: Init( Window * parent, UINT id ) { GetProfString( sectionName, keyFilePath, filePath, sizeof filePath ); if ( strlen( filePath ) > 0 ) { if ( app.treeView.SelectPath( filePath ) && Load() ) { UpdateSize(); }
直前に開いていたファイル名が filePath です。これに合致するツリーアイテムを SelectPath() 関数で選択させます。
BOOL TreeView :: SelectPath( LPCTSTR path ) { HTREEITEM hItem = SearchPathItem( TreeView_GetRoot( hTree ), path ); if ( hItem != NULL ) { TreeView_Select( hTree, hItem, TVGN_FIRSTVISIBLE ); return TRUE; } return FALSE; }
与えられたパスに合致するアイテムを SearchPathItem() 関数で検索し、見つかったら選択してツリービューの先頭に表示します。
HTREEITEM TreeView :: SearchPathItem( HTREEITEM hParent, LPCTSTR path ) { LPCTSTR s = strchr( path, '\\' ); INT len; TVITEM tvItem; TCHAR text[ _MAX_PATH ]; if ( s == NULL ) { len = ( INT ) strlen( path ); } else { len = ( INT ) ( s - path ); } tvItem.mask = TVIF_HANDLE | TVIF_CHILDREN; tvItem.hItem = hParent; TreeView_GetItem( hTree, & tvItem ); tvItem.mask = TVIF_HANDLE | TVIF_TEXT; tvItem.hItem = TreeView_GetChild( hTree, hParent ); tvItem.pszText = text; tvItem.cchTextMax = sizeof text; if ( tvItem.hItem != NULL ) { TreeView_GetItem( hTree, & tvItem ); for ( ; strnicmp( text, path, len ) != 0; ) { tvItem.hItem = TreeView_GetNextSibling( hTree, tvItem.hItem ); if ( tvItem.hItem == NULL ) { return NULL; } TreeView_GetItem( hTree, & tvItem ); } if ( s == NULL ) { return tvItem.hItem; } else { return SearchPathItem( tvItem.hItem, path + len + 1 ); } } return NULL; }
与えられたパス文字列の円マークまでが親ディレクトリです。円マークが無ければ探しているファイル名です。
んで、ディレクトリならばさらに子アイテムを検索し、ファイルならばこれを返します。
その他
両者のフォーカスを移動するために、タブキーでツリービューとドキュメントビューとを行き来できるようにしました。
これもフォーカスをコントロールが持っているため、最上層でフックしました。