Lesson 18:フォントの種類も変えてみよう
随分とながいことテキストエディタの作成をしてきましたが、今回で一応の終わりとしたいと思います。
最後にやるのも、やはりカスタマイズです。今回は、テキストを表示する際に使用するフォントの種類と大きさを変更できるようにします。
フォントを扱うクラスは当然BFontクラスですが、BeBookでBFontの説明をみると、get_font_family関数とget_font_style関数を使う事で、フォントの種類とスタイルを求める事ができるようです。これを利用して、Setting(設定)メニューにフォントを選択するための項目を追加しましょう。
フォントを設定するメニューですが、SettingメニューにFontという項目を追加、Fontを選択するとフォントの種類の一覧が表示され、フォントの種類を選ぶと、そのフォントのスタイルの一覧が表示されていくようにします。スタイルを選ぶと、そのフォントとスタイルを使用するようにします。
また、フォントの大きさを変更する事が出来るように、SettingメニューにSizeメニューを作り、Sizeメニューの中には、6から24まで1刻みでフォントの大きさを指定するメニューを追加しておきます。
それでは、まずメニューを作成してみましょう。Settingメニューに追加する、Font、Sizeはやはり日本語と英語の両方を表示させたいので、リソースファイル「strings.rsrc」に「Font」「Size」「フォント」「サイズ」と追加しておきます。FontのIDが1037、Sizeが1038となるようにします。対応する日本語は1000番違いに登録しますので、2037と2038になります。resmsg.hのRESMSGID列挙型の定義に、rmFontとrmSizeを追加しておいてください。
メニューの登録は、BEditorViewクラスのコンストラクタで行ってきましたので、こんかいもそこに追加します。
Settingメニューを追加していた部分を次のように書き換えます。
/**** ファイル名 : resmsg.h ****/
BMenu *settingmenu=new BMenu(resmsg(rmSetting));
settingmenu->AddItem(new BMenuItem(resmsg(rmLanguageChange),
new BMessage(MSG_LANGCHANGE)));
settingmenu->ItemAt(0)->SetTarget(be_app);
fontmenu=new BMenu(resmsg(rmFont));
for(int32 i=0;i<count_font_families();i++)
{
font_family memofont_family;
font_style memofont_style;
get_font_family(i,&memofont_family);
fontmenu->AddItem(new BMenu((char *)memofont_family));
for(int j=0;j<count_font_styles(memofont_family);j++)
{
get_font_style(memofont_family,j,&memofont_style);
fontmenu->SubmenuAt(i)->AddItem(new BMenuItem((char *)memofont_style,
new BMessage(MSG_FONTMENU)));
fontmenu->SubmenuAt(i)->ItemAt(j)->
Message()->AddString("family",(char *)memofont_family);
fontmenu->SubmenuAt(i)->ItemAt(j)->
Message()->AddString("style",(char *)memofont_style);
}
fontmenu->SubmenuAt(i)->SetTargetForItems(be_app);
}
settingmenu->AddItem(fontmenu);
sizemenu=new BMenu(resmsg(rmSize));
for(int32 i=6;i<=24;i++)
{
char wk[3];
sprintf(wk,"%d",(int)i);
sizemenu->AddItem(new BMenuItem(wk,new BMessage(MSG_SIZEMENU)));
sizemenu->ItemAt(i-6)->Message()->AddInt32("size",i);
}
sizemenu->SetTargetForItems(be_app);
sizemenu->SetRadioMode(true);
settingmenu->AddItem(sizemenu);
mainmenu->AddItem(settingmenu);
Fontメニューを追加している部分ですが、count_font_families関数で種類の数分だけループするようにしています。ループの中では、get_font_family関数で種類を取得し、fontmenuにAddItem関数で取得した種類memofont_styleを追加しています。font_family型もfont_style型もchar型の配列をtypedefで定義しただけのものですので、char *へのキャストができます。
同じように今度は取得した種類memofont_styleで使用できるスタイルの一覧をメニューに登録します。
count_font_styles関数の引数にmemofont_styleを渡し、memofont_styleで使用できるスタイルの数分だけループし、get_font_style関数でスタイルを取得します。
メニューを選んだ際に発行されるメッセージはMSG_FONTMENUとして定義し、送り先はbe_app、メッセージに付加される情報はフォントの種類とスタイルとします。送り先はbe_appとしているのは、フォントやスタイルは、全てのウィンドウで共通した設定にするためです。
次にSizeメニューですが、こちらは、6から24までループしてその数字を持つ項目を作成しています。MSG_FONTMENUメッセージと同じようにMSG_SIZEMENUメッセージを定義しておき、メッセージには、サイズの情報を付加しておきます。
sizemenu->SetRadioMode(true)としているのは、sizemenuの項目でチェックのマークを複数の項目につける事の出来ないようにするための処理です。
次に、メニューからフォントの種類とスタイルを変更する部分を作成します。フォントやスタイル、サイズの設定を共通して持つため、BTinyEditorAppクラスに設定値を保持するための変数、memofont_family、memofont_style、memofont_sizeを追加します。
font_family memofont_family;
font_style memofont_style;
int32 memofont_size;
これらの変数をBTinyEditorAppクラスのコンストラクタで初期化します。初期化する値は、be_plain_fontから求めます。
be_plain_font->GetFamilyAndStyle(&memofont_family, &memofont_style);
memofont_size=(int32)be_plain_font->Size();
GetFamilyAndStyle関数でフォントとスタイルを、Size関数でサイズを取得します。
フォントの種類やサイズを変更するための関数として、BEditorWindowクラスにSetMemoFont関数を作成します。
SetMemoFont関数では、BMemoViewクラスのフォントを変更すると同時に、FontメニューとSizeメニューのチェックを変更します。
/**** ファイル名 : MainWindow.cpp ****/
//--------------------------------------------------------------------
void BEditorWindow::SetMemoFont(const font_family ffamily,
const font_style fstyle,
const int32 size)
{
BFont memofont;
char wk[3];
BMenuItem *target;
memofont.SetFamilyAndStyle(ffamily,fstyle);
memofont.SetSize((float)size);
for(int32 i=0;i<mainview->fontmenu->CountItems();i++)
{
bool fonttype;
fonttype=(strcmp(mainview->fontmenu->SubmenuAt(i)->Name(),
(char *)ffamily)==0)?true:false;
for(int32 j=0;
j<mainview->fontmenu->SubmenuAt(i)->CountItems();j++)
{
bool styletype;
if(fonttype)
styletype=(strcmp(mainview->fontmenu->SubmenuAt(i)->
ItemAt(j)->Label(),
(char *)fstyle)==0)?true:false;
mainview->fontmenu->SubmenuAt(i)->ItemAt(j)->
SetMarked(fonttype && styletype);
}
}
sprintf(wk,"%d",(int)size);
target=mainview->sizemenu->FindItem(wk);
if(target!=NULL)
target->SetMarked(true);
mainview->memo->SetFontAndColor(&memofont);
}
//--------------------------------------------------------------------
memofontにSetFamilyAndStyle関数とSetSize関数で引数に指定した設定を反映します。それをmainviewのmemoにSetFontAndColor関数でmemofontの設定を反映します。
ループでfontmenuに登録されている項目数分だけ繰り返し、その中のループでその項目に登録されている項目分だけループします。ループ内では、strcmp関数でその項目のNameやLabelが引数で指定したフォントやスタイルと同じかを比較し、同一のものだけ項目をマークします。
Sizeメニューはもっと簡単で、FindItem関数で指定されたサイズと同じ項目を検索し、その項目をマークします。Sizeメニューは作成したときにSetRadioMode関数にtrueを設定しています。このため、マークできる項目が一つだけに限定されますので、SetMarked関数でtrueを設定すると、他の項目は全てマークが外れます。
memofont_family等に保持している値でウィンドウのフォントを変更するタイミングとしては、ウィンドウを作成したときと、フォントの設定が変更されたときです。
ウィンドウの作成はAddEdit関数で行ないますので、mainwndを作成した後で、
mainwnd->Lock();
mainwnd->SetMemoFont(memofont_family,memofont_style,memofont_size);
mainwnd->Unlock();
として、SetMemoFont関数を呼び出します。
残るはもう一つのタイミング、フォントの設定が変更されたときの処理です。これは、BTinyEditorAppクラスのMessageReceived関数で、MSG_FONTMENUメッセージか、MSG_SIZEMENUメッセージを受け取ったときです。
/**** ファイル名 : BTinyEditor.cpp ****/
case MSG_FONTMENU:
case MSG_SIZEMENU:
{
BString str;
if(msg->what==MSG_FONTMENU)
{
msg->FindString("family",&str);
strncpy(memofont_family,str.String(),
B_FONT_FAMILY_LENGTH + 1);
msg->FindString("style",&str);
strncpy(memofont_style,str.String(),
B_FONT_STYLE_LENGTH + 1);
}
if(msg->what==MSG_SIZEMENU)
msg->FindInt32("size",&memofont_size);
for(int i=EditorList->CountItems()-1;i>=0;i--)
{
BEditorWindow *targetwnd=
((BEditorWindow *)EditorList->ItemAt(i));
targetwnd->Lock();
targetwnd->SetMemoFont(memofont_family,memofont_style,
memofont_size);
targetwnd->Unlock();
}
}
break;
MSG_FONTMENUメッセージの場合は、メッセージからフォントの種類とスタイルを取得します。MSG_SIZEMENUの場合は、同じようにフォントのサイズを取得します。取得した値はそれぞれのメンバ変数に代入し、全てのウィンドウに対してSetMemoFont関数を実施します。
ここまで作成すれば、フォントの種類等を変更する事が出来るようになっているはずです。動作を確認したら、最後の仕上げに、フォントの設定もリソースに保存し、次回に再利用できるようにしましょう。
設定を保持しているのは、BTinyEditorAppクラスですので、BTinyEditorAppクラスのコンストラクタとデストラクタで、それぞれ読み込みと保存を行ないます。
/**** ファイル名 : BTinyEditor.cpp ****/
//---------------------------------------------------------------------
BTinyEditor::BTinyEditorApp()
:BApplication(APPLICATION_SIGNATURE)
{
BResources res;
BFile file;
be_plain_font->GetFamilyAndStyle(&memofont_family, &memofont_style);
memofont_size=(int32)be_plain_font->Size();
usedjapanese=false;
if(file.SetTo(RESOURCEFILEPATH, B_READ_ONLY)==B_OK)
{
size_t siz;
int32 *wk;
char *pwk;
res.SetTo(&file);
siz=sizeof(int32);
wk=(int32 *)res.LoadResource(B_INT32_TYPE,R_JAPANESE,&siz);
if(wk!=NULL)
usedjapanese=(bool)*wk;
siz=B_FONT_FAMILY_LENGTH + 1;
pwk=(char *)res.LoadResource(B_INT32_TYPE,R_FONTFAMILY,&siz);
if(pwk!=NULL)
strncpy(memofont_family,pwk,B_FONT_FAMILY_LENGTH + 1);
siz=B_FONT_STYLE_LENGTH + 1;
pwk=(char *)res.LoadResource(B_INT32_TYPE,R_FONTSTYLE,&siz);
if(pwk!=NULL)
strncpy(memofont_style,pwk,B_FONT_STYLE_LENGTH + 1);
siz=sizeof(int32);
wk=(int32 *)res.LoadResource(B_INT32_TYPE,R_FONTSIZE,&siz);
if(wk!=NULL)
memofont_size=*wk;
file.Unset();
}
EditorList=new BList();
open_filepanel= new BFilePanel();
findword="";
replaceword="";
ignorecase=false;
}
//---------------------------------------------------------------------
BTinyEditorApp::~BTinyEditorApp()
{
BResources res;
BFile file(RESOURCEFILEPATH, B_READ_WRITE | B_CREATE_FILE);
int32 wk;
res.SetTo(&file);
wk=(int32)usedjapanese;
res.AddResource(B_INT32_TYPE,R_JAPANESE,&wk,sizeof(wk));
res.AddResource(B_STRING_TYPE,R_FONTFAMILY,
(char *)memofont_family,B_FONT_FAMILY_LENGTH + 1);
res.AddResource(B_STRING_TYPE,R_FONTSTYLE,
(char *)memofont_style,B_FONT_STYLE_LENGTH + 1);
res.AddResource(B_INT32_TYPE,R_FONTSIZE,&memofont_size,
sizeof(memofont_size));
res.Sync();
file.Unset();
delete open_filepanel;
for(int i=EditorList->CountItems()-1;i>=0;i--)
{
BEditorWindow *targetwnd=
((BEditorWindow *)EditorList->ItemAt(i));
targetwnd->Lock();
targetwnd->Quit();
EditorList->RemoveItem(targetwnd);
}
delete EditorList;
}
//---------------------------------------------------------------------
もう今更リソースファイルの説明は要りませんよね。
これで、とりあえずテキストエディタの作成を終わりとします。数週間、この企画を休ませてもらって、すこしサイト全体を手直しします。その後、もう少しだけ学習記を続けたいと思います。まだ、次のネタは考えていませんが、これまで作ったテキストエディタを活かすようなものにしようかとも思っています。(未定ですが)
☆☆8/1追記☆☆
SaveFile関数でファイルを保存する際に、ファイルサイズを補正しなかったため、もとの文章よりも短い文章を保存した場合に、ファイルの末尾に前の情報が残ってしまっていました。
この対策として、SaveFile関数で、ファイル保存する際に、SetSize関数で、ファイルの大きさを正しい大きさになおすようにしてください。
ソースリスト