第7回 BCheckBoxをチェケラッチョ!


今回取り扱う内容はBCheckBoxです。
まぁ、いわばチェックボックスなんですが、ただチェックボックスを紹介するだけなのも面白くないので、
BMessageのより突っ込んだ使い方についても紹介しています。
このサンプルでいうと結果的には不要なものなんですが、まぁサンプルということなのでお願いします。

// sample06.cpp

#include <Application.h>        // BApplicationを使うために必要
#include <Window.h>             // BWindowを使うために必要
#include <View.h>               // BViewを使うために必要
#include <Alert.h>			  // BAlertを使うために必要
#include <Button.h>             // BButtonを使うために必要
#include <CheckBox.h>           // BCheckBoxを使うために必要

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

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

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();	// 終了が要求されたときに呼ばれる関数
	virtual void MessageReceived(BMessage* message); // メッセージが送られたときに呼ばれる関数
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* _msg);
private:
	BButton* m_button;			// 貼り付けるボタン
	BCheckBox* m_checkbox;        // 貼り付けるチェックボックス
};

MyApp::MyApp()
      :BApplication("application/x-vnd.big56-MyApp")
{
	m_win = new MyWindow(BRect(100,100,270,170),"チェックボックスのテスト",B_TITLED_WINDOW,0);
	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;
}

// ウィンドウのメッセージ処理関数は、ボタンが押されたときの処理を記述する
void MyWindow::MessageReceived(BMessage* message)
{
	BAlert* alert;
	switch(message->what)
	{
	case BUTTON_CLICKED:
		alert = new BAlert("test","ボタンが押されました","OK");
		alert->Go();
		break;
	default:
		BWindow::MessageReceived(message);
		break;
	}
}

MyView::MyView(BRect frame,const char* title,uint32 resizingMode,uint32 flags)
	  :BView(frame,title,resizingMode,flags)
{
	m_button = new BButton(BRect(25,30,125,50),"theButton","初めてのボタン",new BMessage(BUTTON_CLICKED));
	AddChild(m_button);

	m_checkbox = new BCheckBox(BRect(15,5,135,25),"theCheckBox","ボタンを有効にする",new BMessage(CHECK_CLICKED));
	m_checkbox->SetValue(B_CONTROL_ON);
	AddChild(m_checkbox);
}

MyView::~MyView()
{
}

void MyView::AttachedToWindow()
{
	m_checkbox->SetTarget(this);		// チェックボックスのメッセージの送り先をMyViewに指定する
}

// ビューの処理関数はチェックボックスが押されたときの処理を記述する
void MyView::MessageReceived(BMessage* message)
{
	BCheckBox* checkbox;
	switch(message->what)
	{
	case CHECK_CLICKED:
		// メッセージからメッセージが発進された元のコントロールへのポインタを得る
		// これはm_checkboxと同じ値になるはず
		message->FindPointer("source",reinterpret_cast<void**>(&checkbox));

		// チェックボックスの値からボタンのEnabledを変更する		
		switch (checkbox->Value())
		{
		case B_CONTROL_ON:   // チェックがあったら
			m_button->SetEnabled(true); // ボタンを有効にする
			break;
		case B_CONTROL_OFF:  // チェックがなかったら
			m_button->SetEnabled(false); // ボタンを無効にする
			break;
		}
		break;
	default: 
		BView::MessageReceived(message);
		break;
	}
}


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

結果は以下の通りです。
実行結果
前回のサンプルにチェックボックスを足しただけです。
このチェックボックスをオフにすると、ボタンが押せなくなります。

まず、CheckBoxの構築部分の解説です。

m_checkbox = new BCheckBox(BRect(15,5,135,25),"theCheckBox","ボタンを有効にする",new BMessage(CHECK_CLICKED));
m_checkbox->SetValue(B_CONTROL_ON);
AddChild(m_checkbox);

BCheckBoxの構築時に指定するパラメータはBButtonと全く同じなので省略します。第6回を参考にしてください。
次にSetValue()関数で、チェックボックスを最初にオフにしています。
これをしないと、最初の状態がチェックボックスがオフでボタンが押せるということになってしまいますので、
つじつま合わせのためにやってます。
このSetValue()関数の引数は一つなのですが、BCheckBoxの場合、とれる引数の表は次の通りです。

SetEnabled()関数の引数意味
B_CONTROL_ONチェックが入っている状態
B_CONTROL_OFFチェックが入ってない状態
これは、MyView::MessageReceived()関数の中で、ボタンを有効にする・しないの所のswitch文で
分岐する値として使用しています。

さて、ビューがウィンドウに取り付けられた時(ウィンドウにAddChild()された時)に呼ばれるフック関数
MyView::AttachedToWindow()関数の中身を見てみましょう。

void MyView::AttachedToWindow()
{
	m_checkbox->SetTarget(this);		// チェックボックスのメッセージの送り先をMyViewに指定する
}

まぁ、コメントを見ればわかると思うんですが、チェックボックスのSetTarget()関数を呼び出して
メッセージの送り先をMyVIewにしています。
MyViewのコンストラクタ内では、SetTarget()関数は失敗するので注意してください。 私はこれで、1時間程はまりました(^^;

なぜこんなことをするかというと、メッセージを処理する場所をある程度分散させたいことがあるからです。
この場合は、処理しているメッセージはただの2種類しかないのですが、大規模のアプリケーションになってくると
膨大な量のメッセージを処理する場合も出てきます。
それをすべてMyWindow::MessageReceived()関数の中で処理すると、メンテナンス性に劣ったソースができ上がってしまいます。
今回チェックボックスを別のMessageReceived()関数に分けた必然性はありませんが、主に見た目の変更に関するメッセージをビュー側で
中身の振る舞いに関するメッセージをウィンドウ、またはBApplicationで処理するというのが、いい方法だと個人的に思っています。

さて、MyView::MessageReceived()関数では今までとは少し違ったやりかたをしています。

message->FindPointer("source",reinterpret_cast<void**>(&checkbox));

この文では、文字列"source"というキーに対応する、ポインタを探しだしています。
ポインタの差す先は任意のオブジェクトなので、本来ならもっと厳密なチェックが必要だと思いますが、
今回、CHECK_CLICKEDメッセージを発行するのはm_checkboxだけなので、省略しています。

この処理は厳密には必要ではなく、直接m_checkboxを参照しても同じことです。
ただ、BMessageの機能説明のサンプルでもあるので、あえてこのような形にしてあります。

BMessageには、メッセージ定数(whatフィールド)以外にも、メッセージの中に任意のデータを追加することが出来ます。
任意のデータは文字列のキーに関連づけられ、Find〜()関数を利用してデータを取り出すことが出来ます。
これが、第6回でBMessageがデータベース的な側面を持っているといったことです。

FindPointerの引数は次の様になっています。

status_t	FindPointer(const char *name, void **ptr) const;

戻り値はエラーコード。nameには、キーとなる文字列。ptrには、見つかったデータを受け取るポインタへのポインタを指定します。
基本的に戻り値のエラーコードはB_OKが帰ってくれば大丈夫だという認識でいいです。
ポインタに慣れてないと、難しいかもしれません。

Find〜()関数というのは、以下にあげるものがあります。

関数名データの値
FindBool()bool
FindInt8()int8(8ビット整数)
FindInt16()int16(16ビット整数)
FindInt32()int32(32ビット整数)
FindInt64()int64(64ビット整数)
FindFloat()float
FindDouble()double
FindPointer()void*(任意のオブジェクトを指すポインタ)
FindMessenger()BMessengerオブジェクト
FindMessage()BMessageオブジェクト
FindFlat()BFlattenableから継承したオブジェクト
FinRect()BRectオブジェクト
FindPoint()BPointオブジェクト
FindString()char*(文字列)
FindRef()entry_ref構造体(ファイルを指すときに使用)

これらの関数は、FindPointer()関数と同じように、第1引数にキーとなる文字列、第2引数にデータを受け取るためのポインタを指定します。
例えば、FindBool()の引数は、

status_t	FindBool(const char* name,bool* val) const;

となります。
また、これらの関数はすべてFindData()関数で代用ができます。
FindData()関数では、引数にtype_codeを追加で指定する必要があります。

Find〜()関数はデータを探しだす関数ですが、データを追加する関数はAdd〜()関数になります。
例えば、bool型ならAddBool()関数が、int32ならAddInt32()関数があります。

さて、文字列"source"にデータを追加するコードを書いていませんが、このデータはどこで追加されたものなのでしょう?
実は、コントロール全般の基底クラスBControlが、メッセージを送るときに勝手に追加しています。
「なにを出すぎた真似を」と思う人もいるかも知れませんが、
これにより、どのコントロールが送ったメッセージでも、"source"にあるポインタを参照すれば、
メッセージの送信元のオブジェクトへのポインタが取得出来るというメリットもあります。
もちろん、それぞれのコントロールに特有の操作をするときには、ポインタを慎重にキャストする必要があります。

ちなみに、BControlは文字列"when"にint64型で、コントロールが動作した時間のデータを追加しています。

ただここら辺はキーとなる文字列がバッティングしたりすると、動作がおかしくなったりするので、注意が必要です。
あまりにも一般的な単語をキーにするのは避けた方がいいかもしれません。

その後のコードは、Value()関数でCheckBoxの値を取得して、それによりBButtonのSetEnabled()関数を呼び出して
ボタンを有効にしたり無効にしたりしています。
SetEnabled()関数の引数はbool型のもの一つだけで、trueで有効、falseで無効になります。

CheckBox以外にもBMessageのデータベース機構を紹介したので結構量が多くなってしまいましたがいかがでしたでしょうか?
BMessage関連ではデータベース機能によるアーカイビングや、オブジェクトをバイト列に変換するフラット化といった機能もあるので、
それらについてもおいおい説明したいと思っています。

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

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