第16回 階層表示への道


参考 第14回(リスト表示)、第15回(スクロールバー)


今回は、あるディレクトリをドロップしたら、そのディレクトリ以下を階層的にすべて表示するプログラムを作りたいと思います。
今回のサンプルを利用する上で注意することは、ルートに近い(例:/boot/home/)ディレクトリをドロップした場合、
すべてのファイルを網羅するまで、ファイルをリストに追加しようとするため、アプリケーションがいわゆる、応答がない状態になってしまいます。
別の言葉で言えば、あっちの世界へ旅立たれてしまいます。
こんなタコな実装は実際のアプリではやめましょう(^^;

今回のサンプルです。

// sample14.cpp

#include <Application.h>        // BApplicationを使うために必要
#include <Window.h>             // BWindowを使うために必要
#include <View.h>               // BViewを使うために必要
#include <Alert.h>			  // BAlertを使うために必要
#include <Button.h>             // BButtonを使うために必要
#include <Box.h>			 // BBoxを使うために必要
#include <StringView.h>		  // BStringViewを使うために必要
#include <ListItem.h>		  // BStringItemを使うために必要
#include <Directory.h>		  // BDirectoryを使うために必要
#include <Entry.h>			  // entry_refを使うために必要
#include <ScrollView.h>		  // BScrollViewを使うために必要
#include <OutlineListView.h>    // BOutlineListViewを使うために必要

// クラスの前方宣言
class MyApp;
class MyWindow;
class MyView;

// メッセージ定数
const uint32 BUTTON_CLICKED = 'bucl';

class MyApp : public BApplication
{
public:
	MyApp();
private:
	MyWindow* m_win;
};

class MyWindow : public BWindow
{
public:
	MyWindow(BRect frame,const char* title,window_type type,uint32 flags);
	virtual bool QuitRequested();	// 終了が要求されたときに呼ばれる関数
private:
	MyView* m_view;
};
 
class MyView : public BView
{
public:
	MyView(BRect frame,const char *name,uint32 resizingMode,uint32 flags);
	~MyView();
	virtual void AttachedToWindow();	// ビューがウィンドウに取り付けられたとき(子供として登録された後)呼ばれる関数
	virtual void MessageReceived(BMessage* message);
private:
	BStringView* m_string_view;  // 左上の文字列
	BScrollView* m_scroll_view;  // スクロールビュー
	BBox*		 m_box;		     // 装飾としてのBBox
	BButton*   m_button;		//  ボタン
	BOutlineListView* m_outline_list_view; // ディレクトリの内容一覧をあらわす、OutlineListView

	void AddAllFiles(BDirectory* entry,int level); // すべてのファイルを階層的にリストに追加
};

MyApp::MyApp()
      :BApplication("application/x-vnd.big56-MyApp")
{
	m_win = new MyWindow(BRect(100,100,350,380),"アウトラインビュー",B_TITLED_WINDOW,B_ASYNCHRONOUS_CONTROLS);
	m_win->Show();
}

MyWindow::MyWindow(BRect frame,const char* title,window_type type,uint32 flags)
		:BWindow(frame,title,type,flags)
{
	frame.OffsetTo(0,0);  // 与えられたBRectの左上が(0,0)になるようにする

	m_view = new MyView(frame,"theView",B_FOLLOW_ALL_SIDES,B_WILL_DRAW);
	AddChild(m_view);	
}

bool MyWindow::QuitRequested()
{
// アプリケーションを終了する
	be_app->Quit();
	return true;
}

MyView::MyView(BRect frame,const char* title,uint32 resizingMode,uint32 flags)
	  :BView(frame,title,resizingMode,flags)
{
	m_string_view = new BStringView(BRect(10,5,240,25),"theStringView","ディレクトリをドロップしてください");
	AddChild(m_string_view);
	
	m_box = new BBox(BRect(10,30,240,240));
	m_box->SetLabel("ファイル一覧");		
	
	m_outline_list_view = new BOutlineListView(BRect(20-B_V_SCROLL_BAR_WIDTH/2,20,210-B_V_SCROLL_BAR_WIDTH/2,190),"theListView");
	
	m_scroll_view = new BScrollView("theScrollView",m_outline_list_view,B_FOLLOW_LEFT | B_FOLLOW_TOP,
								0,false,true);
	m_box->AddChild(m_scroll_view); // スクロールビューの方を子供に加える

	m_button = new BButton(BRect(140,250,240,270),"theButton",
						"選択文字列の表示",new BMessage(BUTTON_CLICKED));
	AddChild(m_box);	
	AddChild(m_button);
	
	SetViewColor(200,200,200);
}


MyView::~MyView()
{
}

// ラジオボタンとボタンのターゲット変更
void MyView::AttachedToWindow()
{
	m_button->SetTarget(this);
}

// メッセージ処理
void MyView::MessageReceived(BMessage* message)
{
	
	switch(message->what)
	{
	case B_SIMPLE_DATA:
		{
		entry_ref ref;
		message->FindRef("refs",&ref);
		BDirectory dir(&ref);
		
		// 受け取ったファイルがディレクトリで無かったら何もしない
		if (dir.InitCheck() == B_OK)
		{
			BListItem* item;	// リストの内容をすべて削除
			while((item = m_outline_list_view->RemoveItem(static_cast<int32>(0) ) ) != NULL)
				 delete item;
			
			AddAllFiles(&dir,0); // 階層構造のすべてのファイルをリストに追加

		}
		break;
		}
	case BUTTON_CLICKED:
		{
		if (m_outline_list_view->IsEmpty() ) break;  // リストビューが空だったら何もしない
		
		int32 index;
		index = m_outline_list_view->CurrentSelection(); // 現在選択されている場所の位置を得る
		if (index < 0) break;			// なにも選択されていなかったら何もしない
		
		BStringItem* selected_item;
		selected_item = dynamic_cast<BStringItem*>(m_outline_list_view->ItemAt(index));

		// ダウンキャストした結果が失敗したらなにもしない
		if (selected_item != NULL)
		{
			// 現在選択されているものを表示
			(new BAlert("Test",selected_item->Text(),"OK"))->Go();
		}
	
		break;
		}
	default: 
		BView::MessageReceived(message);
		break;
	}
}


// 階層構造のすべてのファイルを追加
void MyView::AddAllFiles(BDirectory* dir,int level)
{
	char buffer[B_FILE_NAME_LENGTH]; // ファイル名を格納するバッファ

	BEntry entry;
	// ディレクトリの中のファイルを順々に得る
	while (dir->GetNextEntry(&entry) == B_OK) 
	{
		entry.GetName(buffer); // ファイル名の取得
		if (entry.IsDirectory() ) // ディレクトリだったら、再帰呼び出し
		{
			// ディレクトリは、三角印をつけて名前を追加
			m_outline_list_view->AddItem(new BStringItem(buffer,level,true));
			
			//レベル階層を一段深くして、ディレクトリの内容を追加する再帰呼び出し
			BDirectory dir2(&entry);
			AddAllFiles(&dir2,level+1); 
		}		
		else  //通常のファイルの処理
		{
			// ファイルの名前を追加
			m_outline_list_view->AddItem(new BStringItem(buffer,level,false));
		}
	}

}

int main(int argc,char** argv)
{
 	MyApp app;
	app.Run();
	return 0;
}

今回の実行結果です。

実行結果初期画面

画面では、筆者の/boot/home/config/settings/をドロップした直後の画面です。
途中のディレクトリに対しては、折り畳んで表示しないことも可能です。
ちょうど、WindowsのExplorerのファイル一覧を思い浮かべてくれればいいかと思います。
下の画像は折り畳んだところです。

ディレクトリを折り畳んだところ

まず、OutlineListViewの構築についての話です。

m_outline_list_view = new BOutlineListView(BRect(20-B_V_SCROLL_BAR_WIDTH/2,20,210-B_V_SCROLL_BAR_WIDTH/2,190),"theListView");

例によって実際のコンストラクタの宣言はこのようになっています。

BOutlineListView(BRect frame,
	const char * name,
	list_view_type type = B_SINGLE_SELECTION_LIST,
	uint32 resizeMask = B_FOLLOW_LEFT | B_FOLLOW_TOP,
	uint32 flags = B_WILL_DRAW | B_FRAME_EVENTS 
				| B_NAVIGABLE);

コンストラクタで指定出来るものは、

frame、name、resizeMask、flagsについては第4回のBViewの説明、
typeについては第14回のListViewをごらんください。


OutlineListViewは、基本的な使い方はBListViewと同じです。
今回のサンプルでも、基本的には前回とまったく同じ使い方をしています。
では、階層構造の深さ(レベル)や、折り畳める場所などはどうやってしていするのかというと、
BListItem(このサンプルではBStringItem)で指定します。
このオプションに関しては、第14回のBStringItemのコンストラクタの引数、
levelとexpandedが重要になってきます。
expandedは、折畳み矢印をつけるかどうかのフラグ、levelは階層レベルです。(一番上のレベルは0、以下1,2,3と増えていく)

さて、実際にリストにファイルをどんどん追加しているコードの部分を見てみましょう。

// 階層構造のすべてのファイルを追加
void MyView::AddAllFiles(BDirectory* dir,int level)
{
	char buffer[B_FILE_NAME_LENGTH]; // ファイル名を格納するバッファ

	BEntry entry;
	// ディレクトリの中のファイルを順々に得る
	while (dir->GetNextEntry(&entry) == B_OK) 
	{
		entry.GetName(buffer); // ファイル名の取得
		if (entry.IsDirectory() ) // ディレクトリだったら、再帰呼び出し
		{
			// ディレクトリは、三角印をつけて名前を追加
			m_outline_list_view->AddItem(new BStringItem(buffer,level,true));
			
			//レベル階層を一段深くして、ディレクトリの内容を追加する再帰呼び出し
			BDirectory dir2(&entry);
			AddAllFiles(&dir2,level+1); 
		}		
		else  //通常のファイルの処理
		{
			// ファイルの名前を追加
			m_outline_list_view->AddItem(new BStringItem(buffer,level,false));
		}
	}
}

このコードでは、ディレクトリであるか同化の判定を、BStatable::IsDirectory()関数で判定しています。
BStatableはBEntryの基底クラスで、以下のような操作が出来ます。

宣言説明
GetCreationTime(time_t* ctime)
SetCreationTime(time_t ctime)
ファイルの作成時間の取得と設定
GetModificationTime(time_t* mtime)
SetModificationTime(time_t mtime)
ファイルの最終更新時間の取得と設定
GetOwner(uid_t *owner)
SetOwner(uid_t owner)
ファイルの所有者のIDの取得と設定
GetGroup(gid_t *group)
SetGroup(gid_t group)
ファイルのグループIDの取得と設定
GetPermissions(mode_t *perms)
SetPermissions(mode_t perms)
ファイルのパーミッションの取得と設定
GetSize(off_t *size)ファイルサイズの取得
GetStat(struct stat* st)stat構造体の取得
GetNodeRef(node_ref* nref)node_ref構造体の取得
IsFile()
IsDirectory()
IsSymLink()
ファイルかディレクトリかシンボリックリンクかを調べ、結果をtrueまたはfalseで返す

次に、ディレクトリだったら、expandフラグをtrueにしてBStringItemを構築し、
そのディレクトリ以下を調べるために、レベルを一段深くして再帰呼び出しを用いています。

ディレクトリでなかったら、ファイルの名前を普通に追加するだけです。
しかし、この時はexpandフラグはfalseにします。


今回の内容は、BOutlineListViewとBStatableについてでした。
本来であればBOutlineListViewは階層構造をうまくコントロールするいろんなメンバ関数があるのですが、
興味があればBeBook等で調べて見てください。
今回のサンプルはルートに近いディレクトリをドロップすると、応答がない状態になるなどの問題点を抱えていますので、
改善点なども考えてみるのもいいかもしれません。

[<<前へ] [次へ>>] [戻る]

big56 big56@anet.ne.jp
海外旅行保険の加入はコチラ! 過払い金の回収ならこちら あなたの悩み解決します
[PR] | ヒーリング会社案内 作成se 転職川口栃木荻窪池袋中国SEO対策消費者金融車 買取テンプレート沖縄旅行免許合宿二輪引越しプレゼントゴルフ会員権留学レーシックマッサージFXアフィリエイトFXホームページ制作デイトレードハワイ旅行タイバンコクハワイ レンタカーベスト ハワイ ホテル レーツバリ島Hawaii hotelsHawaii Activitiesbhhrハワイホテルテキスト広告
【運営会社「パラダイムシフト」サービス】 ハワイ現地オプショナルツアーリラックマ) - ビジネスクラス航空券 - 格安航空券(1) - 格安航空券(2) - 海外ホテル - 韓国旅行 - タイムシェア - ホテル 予約
無料ホームページ - 携帯ホームページ - 無料ホームページ作成 - レンタルサーバー - ブログ - ヴィラ - ハワイ コンドミニアム - バリ島 ホテル - プーケット ホテル - 旅行 口コミ - 旅行情報 - 国際電話 - ホノルルマラソン - 掲示板監視 - 風評被害 - ホテル比較 - ノースウェスト航空 - ファイナルチェッカー