// 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 | チェックが入ってない状態 |
さて、ビューがウィンドウに取り付けられた時(ウィンドウに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型で、コントロールが動作した時間のデータを追加しています。
ただここら辺はキーとなる文字列がバッティングしたりすると、動作がおかしくなったりするので、注意が必要です。
あまりにも一般的な単語をキーにするのは避けた方がいいかもしれません。
CheckBox以外にもBMessageのデータベース機構を紹介したので結構量が多くなってしまいましたがいかがでしたでしょうか?
BMessage関連ではデータベース機能によるアーカイビングや、オブジェクトをバイト列に変換するフラット化といった機能もあるので、
それらについてもおいおい説明したいと思っています。
|
|||
|
|