/**** ファイル名 : BMemoView.h ****/
#ifndef BMEMOVIEW
#define BMEMOVIEW
//---------------------------------------------------------------------
#include <Be.h>
//---------------------------------------------------------------------
class BMemoView : public BTextView
{
public:
BMemoView(BRect frame,const char *name,uint32 resizingMode);
void MessageReceived(BMessage *msg);
};
//---------------------------------------------------------------------
#endif
/**** ファイル名 : BMemoView.cpp ****/
//---------------------------------------------------------------------
#include "BMemoView.h"
//---------------------------------------------------------------------
BMemoView::BMemoView(BRect frame,const char *name,uint32 resizingMode)
:BTextView(frame,name,BRect(0,0,frame.Width(),frame.Height()),
resizingMode,B_WILL_DRAW)
{
}
//---------------------------------------------------------------------
void BMemoView::MessageReceived(BMessage *msg)
{
switch(msg->what)
{
default:
BTextView::MessageReceived(msg);
}
}
//---------------------------------------------------------------------
/**** ファイル名 : MainWindow.cpp ****/
//---------------------------------------------------------------------
#include "MainWindow.h"
//---------------------------------------------------------------------
BEditorWindow::BEditorWindow(BRect frame,const char *title)
:BWindow(frame,title,MAINWINDOW_WINDOWSTYLE,0)
{
mainview=new BEditorView(Bounds());
AddChild(mainview);
}
//---------------------------------------------------------------------
void BEditorWindow::MessageReceived(BMessage *msg)
{
switch(msg->what)
{
case MSG_OPEN:
break;
case MSG_SAVE:
break;
case MSG_SAVEAS:
break;
default:
BWindow::MessageReceived(msg);
}
}
//---------------------------------------------------------------------
BEditorView::BEditorView(BRect frame)
:BView(frame,"beditorview",B_FOLLOW_ALL,B_WILL_DRAW)
{
BRect viewrect(Bounds());
BMenuBar *mainmenu=new BMenuBar(BRect(0,0,viewrect.right,
be_plain_font->Size()*2),
"mainmenubar");
AddChild(mainmenu);
memo=new BMemoView(BRect(0,be_plain_font->Size()*2,
viewrect.right-B_V_SCROLL_BAR_WIDTH,
viewrect.bottom-B_H_SCROLL_BAR_HEIGHT),
"memo",B_FOLLOW_ALL);
BScrollView *scr=new BScrollView("memoscroll",memo,
B_FOLLOW_ALL,0,true,true);
AddChild(scr);
BMenu *filemenu=new BMenu("File");
filemenu->AddItem(new BMenuItem("Open...",new BMessage(MSG_OPEN)));
filemenu->AddItem(new BMenuItem("Save",new BMessage(MSG_SAVE)));
filemenu->AddItem(new BMenuItem("Save As...",new BMessage(MSG_SAVEAS)));
filemenu->AddSeparatorItem();
filemenu->AddItem(new BMenuItem("Quit",new BMessage(B_QUIT_REQUESTED)));
mainmenu->AddItem(filemenu);
BMenu *editmenu=new BMenu("Edit");
editmenu->AddItem(new BMenuItem("Undo",new BMessage(B_UNDO)));
editmenu->AddSeparatorItem();
editmenu->AddItem(new BMenuItem("Copy",new BMessage(B_COPY)));
editmenu->AddItem(new BMenuItem("Cut",new BMessage(B_CUT)));
editmenu->AddItem(new BMenuItem("Paste",new BMessage(B_PASTE)));
editmenu->AddItem(new BMenuItem("Select All",new BMessage(B_SELECT_ALL)));
mainmenu->AddItem(editmenu);
memo->SetDoesUndo(true);
}
//---------------------------------------------------------------------
BMenuBarは高さを自動調整するそうですので、BMenuBarを作成する際に、be_plain_font->Size()*2といった姑息な方法で高さを指定する必要はないそうです。
ただ、「AddChild(mainmenu);」の後で、「float height = mainmenu->Bounds().Height();」としても、正しい高さは取得できないそうです。
BMenuBarの設定と、高さの取得は次のように行えばよいようです。
BMenuBar *mainmenu=new BMenuBar(BRect(0,0,0,0), "mainmenubar");
AddChild(mainmenu);
float height, width;
mainmenu->GetPreferredSize(&width, &height);
memo=new BMemoView(BRect(0,height + 1,
viewrect.right-B_V_SCROLL_BAR_WIDTH,
viewrect.bottom-B_H_SCROLL_BAR_HEIGHT),
"memo",B_FOLLOW_ALL);
GetPreferredSize関数でBMenuBarの幅と高さを取得し、BMemoViewの表示位置を決定しています。
テキスト入力は普通にできますよね。次にメニューを試してみましょう。
EditメニューのCopyやPasteを操作すると・・・、何も変化がありません。[Alt]+[C]や[Alt]+[V]等のショートカットキーを試してみると、これは正常に動いています。FileメニューのQuitを試すと、アプリケーションが終了します。どうも、B_COPYやB_PASTE等のメッセージの処理が正常に行えていないようです。
なぜこのような動きになるのかを推測すると、B_COPY等のメッセージの送信先がおかしいのではないかと想像できます。
これまで作ってきたアプリケーションは全てBWindowでメッセージを処理してきました(MessageRecievedやQuitRequestedです)。しかし、B_COPY等のメッセージはBTextViewが処理するメッセージです。当然、BTextViewに送らなければならないのではないでしょうか。(正確にはBTextViewを継承しているBMemoViewに送るのだとは思いますが)。
そこで、まずはメニューからとショートカットキーでCopyを行い、B_COPYメッセージがどこに送信されるのかを確かめてみましょう。そのために、BEditorWindowとBMemoViewの両方のMessageRecievedにB_COPYを受けたら、BMemoViewのSetText関数を使用して、画面にどちらのクラスで受け取ったかを表示する処理を作っておきます。
/**** ファイル名 : MainWindow.cpp ****/
//---------------------------------------------------------------------
void BEditorWindow::MessageReceived(BMessage *msg)
{
switch(msg->what)
{
case B_COPY:
((BMemoView *)FindView("memo"))->SetText("BEditorWindow");
break;
case MSG_OPEN:
break;
case MSG_SAVE:
break;
case MSG_SAVEAS:
break;
default:
BWindow::MessageReceived(msg);
}
}
//---------------------------------------------------------------------
/**** ファイル名 : BMemoView.cpp ****/
//---------------------------------------------------------------------
void BMemoView::MessageReceived(BMessage *msg)
{
switch(msg->what)
{
case B_COPY:
SetText("BMemoView");
break;
default:
BTextView::MessageReceived(msg);
}
}
//---------------------------------------------------------------------
実際にこうして試してみると、メニューからはBEditorWindowに送られ、ショートカットキーではBMemoViewに送られている事が確認できました。
後はメニューからメッセージを送信するときに、送信先をBMemoViewにする方法さえわかれば、メニューからテキスト編集する事ができるようになりそうです。
またまたBeBookを見てみると、BMenuやBMenuItemのところにSetTargetForItemsだのSetTargetだのといった記述が見つかります。Targetなんて、いかにも怪しい名前を持ってますね。そこでSetTargetを調べてみると、やはりメッセージの送信先を設定する関数のようです。使い方はメッセージの送信先のBHandlerを指定するようです。BMemoViewはBTextViewを継承しています。BTextViewの継承元を調べていくと、BHandlerもありますので、BMemoViewを送信先にセットする事ができそうです。
/**** ファイル名 : MainWindow.cpp ****/
//---------------------------------------------------------------------
BEditorView::BEditorView(BRect frame)
:BView(frame,"beditorview",B_FOLLOW_ALL,B_WILL_DRAW)
{
BRect viewrect(Bounds());
BMenuBar *mainmenu=new BMenuBar(BRect(0,0,viewrect.right,
be_plain_font->Size()*2),
"mainmenubar");
AddChild(mainmenu);
memo=new BMemoView(BRect(0,be_plain_font->Size()*2,
viewrect.right-B_V_SCROLL_BAR_WIDTH,
viewrect.bottom-B_H_SCROLL_BAR_HEIGHT),
"memo",B_FOLLOW_ALL);
BScrollView *scr=new BScrollView("memoscroll",memo,
B_FOLLOW_ALL,0,true,true);
AddChild(scr);
BMenu *filemenu=new BMenu("File");
filemenu->AddItem(new BMenuItem("Open...",new BMessage(MSG_OPEN)));
filemenu->AddItem(new BMenuItem("Save",new BMessage(MSG_SAVE)));
filemenu->AddItem(new BMenuItem("Save As...",new BMessage(MSG_SAVEAS)));
filemenu->AddSeparatorItem();
filemenu->AddItem(new BMenuItem("Quit",new BMessage(B_QUIT_REQUESTED)));
mainmenu->AddItem(filemenu);
BMenu *editmenu=new BMenu("Edit");
editmenu->AddItem(new BMenuItem("Undo",new BMessage(B_UNDO)));
editmenu->ItemAt(0)->SetTarget(memo);
editmenu->AddSeparatorItem();
editmenu->AddItem(new BMenuItem("Copy",new BMessage(B_COPY)));
editmenu->ItemAt(2)->SetTarget(memo);
editmenu->AddItem(new BMenuItem("Cut",new BMessage(B_CUT)));
editmenu->ItemAt(3)->SetTarget(memo);
editmenu->AddItem(new BMenuItem("Paste",new BMessage(B_PASTE)));
editmenu->ItemAt(4)->SetTarget(memo);
editmenu->AddItem(new BMenuItem("Select All",new BMessage(B_SELECT_ALL)));
editmenu->ItemAt(5)->SetTarget(memo);
mainmenu->AddItem(editmenu);
memo->SetDoesUndo(true);
}
//---------------------------------------------------------------------
これでメニューを選択してみると・・・? BEditorWindowが受け取ってしまいました。
それじゃ今度はSetTargetForItemsを・・・。やはりダメ。
その後も、SetTargetItemを行うタイミングを変えてみたりと何度か試してみたんですが、どれもうまく行きませんでした。(うーん、どこが悪いんだろう・・・?)
ここまできて手詰まりになってしまったので、今回はちょっと別の方法で逃げてしまうことにしました。
ようはBMemoViewにB_COPY等のメッセージが送られてきたと認識させる事ができれば良いのですから、BEditorWindowで受け取ったB_COPY等のメッセージをBMemoViewに転送してしまえば良いのではないでしょうか。後はその方法ですが、これは今までにも散々使ってきた方法で大丈夫でしょう。
今までいくつかのメッセージをMessageRecievedで処理してきましたが、MessageRecievedで処理を準備しておかなかったメッセージを受信したときはどうしていました? そう、継承元のクラスのMessageRecievedを呼び出して、そこで処理していましたよね。今回もこの方法が使えそうです。BEditorWindowのMessageRecievedでB_COPY等のメッセージを受け取ったらBMemoViewのMessageRecievedを呼び出して、そこで処理したらどうでしょう?
BMemoViewの実体はBEditorWindowの中でmemoと定義していますから、BEditorWindowのMessageRecievedを
//---------------------------------------------------------------------
void BEditorWindow::MessageReceived(BMessage *msg)
{
switch(msg->what)
{
case B_UNDO:
case B_COPY:
case B_CUT:
case B_PASTE:
case B_SELECT_ALL:
((BMemoView *)FindView("memo"))->MessageReceived(msg);
break;
case MSG_OPEN:
break;
case MSG_SAVE:
break;
case MSG_SAVEAS:
break;
default:
BWindow::MessageReceived(msg);
}
}
//---------------------------------------------------------------------
としてしまえば良いでしょう。
ここで使っているmemoはメンバ変数のmemoではなく、BMemoViewを作成した時の名称の"memo"です。それをFindViewで探して、あとは取得したポインタをキャストして使用しています。
これで実行してみると、今度はちゃんと、メニューからのCopyも、きちんとBMemoViewまで届きました。
☆2000年10月2日追記☆
これについても情報をいただきました。
SetTargetItemやSetTargetForItemsは、その対象がウィンドウにアタッチされてからでなければ意味をなさないとのことです。AddChild後にSetTargetItemを使用しなければなりません。
もう一度実行して見て、一通りのテキスト編集が行えた事を確認して、今回はおしまいです。
ソースリスト