Lesson 11:ファイルに保存する(2)+


 前回からの続きで、FileメニューのSaveを作成します。前回のソースからの簡単な改造で、今回も修正するのはMainWindow.hとMainWindow.cppのみです。Saveによるファイルの上書きだけでは、ちょっと簡単すぎますので、アイコンをプロジェクトへの追加も行ってみたいと思います。

 Saveの動きは、編集中の文章がファイルから読み込んだものであるか、一旦SaveAsで保存している場合に、そのファイルに対して上書き保存を行います。Save処理ではファイル名が付いているかを調べて、まだファイル名が付いていない場合は、SaveAsに処理を渡すようにすれば効率がよさそうです。
 ファイル名が付いているかを判断するためにも、OpenやSaveAsで指定したファイル名を保持しておく必要があります。保存処理はSaveFile関数がそのまま使えますので、SaveFile関数の引数でもあるBEntryクラスの形式で保存しておくと、Saveの際に再利用しやすいですね。

 BEditorWindowクラスのprivateメンバに「BEntry *editingfile;」を追加しておきましょう。BEditorWindowのコンストラクタではNULLにしておき、OpenFile関数、SaveFile関数で読み込み/書き込み処理が正常に行われた際に、editingfileに各々の引数のentryの内容をコピーしておきます。こうしておけば、editingfileがNULLの場合は、まだファイル名がついていない状態、NULL以外ならば、editingfileにはファイルの情報が入った状態となります。
 ついでといっては何なのですが、折角ファイル名を保持する処理を行うのですから、その処理でウインドウのタブに編集中のファイル名を表示するようにしましょう。

 まずは、editingfileにファイル情報を保存する処理と、ウインドウのタブにファイル名を表示する処理を作ってみましょう。

/**** ファイル名 : MainWindow.cpp ****/

//--------------------------------------------------------------------- 
void BEditorWindow::SetFileName(BEntry *entry)
{
    if(entry==NULL)
    {
        if(editingfile!=NULL)
        {
            delete editingfile;
            editingfile=NULL;
        }
    }
    else
    {
        if(editingfile==NULL)
            editingfile=new BEntry();
        *editingfile=*entry;
    }
    
    if(editingfile!=NULL)
    {
        char nam[B_FILE_NAME_LENGTH];
        
        editingfile->GetName(nam);
        SetTitle(nam);
    }
    else
        SetTitle(MAINWINDOW_TITLE);
}
//--------------------------------------------------------------------- 

 SetFileName関数はentry引数にセットしたいファイル情報を持ったBEntryクラスを受け取ります。もしentryにNULLが渡された場合は、editingfileをNULLにし、SetTitle関数でウインドウのタイトルタブに標準のタイトル(MAINWINDOW_TITLE)をセットします。entryにNULL以外の値が入る場合は、editingfileにentryの内容をコピーし、ウインドウのタイトルタブにeditingfileに格納されたファイル名をセットします。

 このSetFileName関数をOpenFile関数と、SaveFile関数に組み込みます。

/**** ファイル名 : MainWindow.cpp ****/

//--------------------------------------------------------------------- 
void BEditorWindow::OpenFile(BEntry *entry)
{
    if(entry->Exists())
    {
        BFile fil;
        off_t size;
    
        try
        {
            fil.SetTo(entry,B_READ_ONLY);
            fil.GetSize(&size);
            ((BTextView *)FindView("memo"))->SetText(&fil,0,size);
            SetFileName(entry);
        }
        catch(...)
        {
            BAlert *altmsg = new BAlert("Error", 
                                        "File read failed!!","OK",
                                        NULL,NULL,B_WIDTH_AS_USUAL,
                                        B_WARNING_ALERT); 
            altmsg->Go();
        }
    }
}
//--------------------------------------------------------------------- 
void BEditorWindow::SaveFile(BEntry *entry)
{
    BFile fil;
    off_t wsize;
    off_t tsize;

    try
    {
        fil.SetTo(entry,B_READ_WRITE | B_CREATE_FILE);
        tsize=strlen(((BTextView *)FindView("memo"))->Text());
        wsize=fil.WriteAt(0,((BTextView *)FindView("memo"))->Text(),
                          tsize);
        
        if(tsize==wsize)
            SetFileName(entry);
    }
    catch(...)
    {
        BAlert *altmsg = new BAlert("Error", "File write failed!!","OK",
                                    NULL,NULL,B_WIDTH_AS_USUAL,
                                    B_WARNING_ALERT); 
        altmsg->Go();
    }
}
//--------------------------------------------------------------------- 

 残るはメニューからSaveを選択した際の処理です。Saveを選択されると、MSG_SAVEメッセージが送信されますので、MessageReceived関数にMSG_SAVEの処理を追加します。
 ファイル名が付いていればeditingfileはNULL以外になっているはずなので、NULL以外であればSaveFile関数にeditingfileを渡して呼べば、上書き保存が行えます。もしeditingfileはNULLであったなら、そのままMSG_SAVEASの処理に入ってしまえばファイル名を付けての保存になります。

/**** ファイル名 : MainWindow.cpp ****/

//--------------------------------------------------------------------- 
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_SAVE:
            if(editingfile!=NULL) 
            {
                SaveFile(editingfile);
                break;
            } 
        case MSG_SAVEAS:
            save_filepanel->SetTarget(this);
            save_filepanel->Show(); 
            break;
        case B_SAVE_REQUESTED:
            {
                entry_ref ref;
                BString nam;
                BDirectory dir;
                BEntry entry;

                msg->FindRef("directory",&ref);
                msg->FindString("name",&nam);
                dir.SetTo(&ref);

                if(entry.SetTo(&dir,nam.String())==B_OK)
                {
                    SaveFile(&entry);
                    return;
                }
            }
            break;    
        default: 
            BWindow::MessageReceived(msg); 
    } 
}
//--------------------------------------------------------------------- 

 MSG_SAVEの処理で、editingfileがNULL以外の場合のみbreak命令が使用されますので、NULLの場合は、そのままMSG_SAVEASの処理に入ります。もっとも、あまりきれいな方法ではありませんが。

 実際に動きを見て、Saveと、SaveAsの両方が正しく動作する事を確認してみてください。

 これで今回のプログラム修正は終わりですが、かなり内容が少ないので、プロジェクトにアイコンを付加する作業もやってみましょう。(タイトルに+を付けた理由はこれですね。)

 Signatureとアイコンをプロジェクトに設定する作業を行います。
 TrackerのPreferencesから、FileTypesを起動します。FileメニューのNew Resource Fileを選択すると、設定画面が表示されます。Signature欄にBTinyEditor.hでAPPLICATION_SIGNATUREに定義してある内容「application/x-vnd.vow-betinyeditor」を記述します。
 Signature欄の右隣に四角いくぼみがありますので、ここをダブルクリックすると、「The large or mini icon for the '[untitled]' mime type is missing.」とアラートが表示されます。New Iconを選択すると、アイコン編集画面となります。
 大小2種類のアイコンをデザインできますので、それぞれのアイコンを作成したら、編集画面を閉じます。この際にSaveしておいてください。設定画面のSignature欄の横のくぼみに作成したアイコンが表示されているはずです。
 画面中央付近のAddボタンを押して選択画面を表示、選択画面からtextをSupported Typesに追加します。

 ここまで行ったら、FileメニューのSave into Resouce Fileを選択し、BTinyEditorのプロジェクトのフォルダに、BTinyEditor.rsrcの名前で保存します。保存したらFileTypesを終了します。

 プロジェクトのProjectメニューのAdd FilesでBTinyEditor.rsrcをプロジェクトに追加します。ProjectをLinkすると、実行ファイルにSignatureとアイコンが反映されます。

 今まではSignatureをBApplicationのコンストラクタで設定してきましたが、リソースで設定して実行ファイルにうめこんでしまいました。BApplicationのコンストラクタで設定する場合と実行ファイルにうめこんで設定する場合、この二つには大きな違いが存在します。それはBApplicationのコンストラクタで設定する場合はプログラムが実行されてはじめてTrackerがSignatureを判定できるようになるのですが、実行ファイルにうめこんで設定する場合はプログラムが起動していない状態でもTrackerはSignatureを判定できるのです。
 このことを簡単に確認するには、BTinyEditorが起動していない状態で、何かテキストファイルをBTinyEditorのアイコンにドロップしてみてください。するとBTinyEditorが起動してテキストファイルが読み込まれるはずです。
 なぜこのような動きをするのか。それはBTinyEditorのアイコンにファイルがドロップされた際に、TrackerがSignatureからBTinyEditorを判定しBTinyEditorを起動、ファイルがドロップされたということで、B_REFS_RECEIVEDメッセージが送信されたためです。

 これでアイコンとSignatureをプロジェクトに付加する事ができました。
 非常に低機能ですが、一応テキストエディタらしくはなってきました。次回何をやろうかはまだ考えていません。小さな事をやろうか、それとも大きな事をやろうか・・・。どちらにしても、もうしばらくBTinyEditorを素材にしてすすめたいと思います。

ソースリスト
圧縮ファイル
R5 Intel環境で確認
BTinyEditor20000414.zip
ソースファイル BTinyEditor.h
BMemoView.cpp
BMemoView.h
main.cpp
MainWindow.cpp
MainWindow.h

次の項目へ

トップページへ戻る