LoginSignup
0
1

More than 5 years have passed since last update.

C++ Builder > (フォームとして)UIとロジック実装 > 複数のプロファイルの切替え > _profileファイルとdetailファイル

Last updated at Posted at 2017-11-02
動作環境
C++ Builder XE4

前回: C++ Builder > ファイル処理検討 > 複数のプロファイルの切替え > _profファイルとdetailファイルでの読書き

上記の処理に関して自分が使いたい例を実装してみた。

使用外部ソース

二つの自作ソースを利用する。
これらの自作ソースがないために、閲覧者の環境では動きません。
コードは参考程度にどうぞ。

  1. UtilSetting.cppと.h
  2. UtilBackupTMemo.cppと.h

詳細は非公開。

処理内容は以下。

  • UtilSetting
    • Form_GetData() : 対象のコンポーネント登録
    • Load() : JSONファイルからの読込み
    • Save() : JSONファイルへの保存
    • Form_SetData_AllKeys() : 読込んだ全ての項目をフォームに反映 (Tagによる制限なし)
    • GetSaveDir() : 保存先フォルダの取得
  • UtilBackupTMemo
    • DoBackup() : TMemoのファイルへの保存
    • DoRecover() : ファイルからTMemoへの読込み
    • GetSaveDir() : 保存先フォルダの取得

(補足: namingがいまいちであるが、すでに使用されているため容易に名前変更できない。最初の実装時に名前をよく考えないとこうなる)。

ファイルの構成

  • Unit1: メイン画面
    • SettingUnitを開くボタンのみある
  • SettingUnit: 設定画面
    • プロファイルの選択、削除ボタンがある
    • 選択プロファイル保存のためのOk, Cancelボタンがある
    • 詳細のTEditがある
  • NewProfileDialog: 新規プロファイル名指定ダイアログ
    • SettingUnitから「Add」にて新規プロファイル追加時に使う
      • プロファイル追加は頻度が低いと思われるため、設定画面と分けた

code

Unit1.h
//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE で管理されるコンポーネント
    TButton *B_setting;
    void __fastcall B_settingClick(TObject *Sender);
private:    // ユーザー宣言
public:     // ユーザー宣言
    __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
Unit1.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
#include "SettingUnit.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::B_settingClick(TObject *Sender)
{
    FormSetting->ShowModal();
}
//---------------------------------------------------------------------------
SettingUnit.h
//---------------------------------------------------------------------------

#ifndef SettingUnitH
#define SettingUnitH
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <Vcl.Controls.hpp>
#include <Vcl.StdCtrls.hpp>
#include <Vcl.Forms.hpp>
#include "editex.h"
//---------------------------------------------------------------------------
class TFormSetting : public TForm
{
__published:    // IDE で管理されるコンポーネント
    TComboBox *CMB_profile;
    TButton *B_add;
    TButton *B_delete;
    TGroupBox *GroupBox1;
    TEdit *E_color;
    TButton *B_ok;
    TButton *B_cancel;
    TButton *B_applyDetail;
    TLabel *Label1;
    TMemo *M_profileItems;
    TEdit *E_profileItemIndex;
    TLabel *Label2;
    TLabel *Label3;
    void __fastcall B_addClick(TObject *Sender);
    void __fastcall CMB_profileChange(TObject *Sender);
    void __fastcall B_deleteClick(TObject *Sender);
    void __fastcall B_okClick(TObject *Sender);
    void __fastcall B_cancelClick(TObject *Sender);
    void __fastcall B_applyDetailClick(TObject *Sender);
    void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
private:    // ユーザー宣言
    void __fastcall Setting_load_profile(void);
    void __fastcall Setting_load_detail(String profName);
    void __fastcall Setting_save_profile(void);
    void __fastcall Setting_save_detail(String profName);
    void __fastcall Setting_delete_detail(String profName);
    void __fastcall SetDefaultDetail(void);
public:     // ユーザー宣言

    // コンストラクタ・デストラクタ
    __fastcall TFormSetting(TComponent* Owner);
    __fastcall ~TFormSetting();
};
//---------------------------------------------------------------------------
extern PACKAGE TFormSetting *FormSetting;
//---------------------------------------------------------------------------
#endif
SettingUnit.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include <IOUtils.hpp>
#include "SettingUnit.h"
#include "NewProfileDialog.h"
//
#include "UtilSetting.h"
#include "UtilBackupTMemo.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "editex"
#pragma resource "*.dfm"
TFormSetting *FormSetting;
//---------------------------------------------------------------------------

// 設定保存関連
static const String kSettingFileExt = L".json";
static String s_settingProfileFileName = L""; // コンストラクタで設定すること
static String s_settingDetailFilePrefix = L""; // コンストラクタで設定すること
// TComboBox->Items保存用TMemo
static const String kSuffix_pofileItems = L"_profileItems.csv";
static CUtilBackupTMemo *s_bkMemo;
static String s_filename_profileItems; // コンストラクタで設定すること

//---------------------------------------------------------------------------
__fastcall TFormSetting::TFormSetting(TComponent* Owner)
    : TForm(Owner)
{
    s_settingProfileFileName = this->Name + L"_profile" + kSettingFileExt; // フォーム名を設定ファイル名に用いる
    s_settingDetailFilePrefix = this->Name + L"_detail"; // フォーム名を設定ファイル名に用いる
    s_filename_profileItems = this->Name + kSuffix_pofileItems;

    // Items保存用
    String dstDir = CUtilBackupTMemo::GetSaveDir();
    s_bkMemo = new CUtilBackupTMemo(M_profileItems, dstDir, s_filename_profileItems);

    // 読込み
    Setting_load_profile();
    Setting_load_detail(CMB_profile->Text);
}

__fastcall TFormSetting::~TFormSetting()
{
    // Items保存用
    delete s_bkMemo;
    s_bkMemo = NULL;
}
//---------------------------------------------------------------------------
void __fastcall TFormSetting::B_addClick(TObject *Sender)
{
    // 1. プロファイルの追加
    if (DialogNewProfile->ShowModal() != mrOk) {
        return;
    }
    String newItem = DialogNewProfile->GetProfileName();
    if (newItem.Length() == 0) {
        return; // error
    }
    CMB_profile->Items->Add(newItem);
    int cnt = CMB_profile->Items->Count;
    CMB_profile->ItemIndex = cnt - 1;

    // 2. 詳細をデフォルトにする
    SetDefaultDetail();

    // 3. 保存
    Setting_save_profile();
    Setting_save_detail(CMB_profile->Text);
}

void __fastcall TFormSetting::CMB_profileChange(TObject *Sender)
{
    Setting_load_detail(CMB_profile->Text);
}

void __fastcall TFormSetting::B_deleteClick(TObject *Sender)
{
    if (CMB_profile->Items->Count == 0) {
        return;
    }
    String cnfmsg = L"選択したプロファイル[" + CMB_profile->Text + L"]を削除しますか?";
    if (IDOK != MessageBox(Handle, cnfmsg.c_str(), L"確認",
        MB_ICONQUESTION | MB_OKCANCEL | MB_DEFBUTTON2) ) {
            return;
    }

    // 1. 現在のプロファイルの詳細を削除
    Setting_delete_detail(CMB_profile->Text);

    // 2. 現在のプロファイルをコンボボックスから削除
    String name = CMB_profile->Text;
    int idx = CMB_profile->Items->IndexOf(name);
    CMB_profile->Items->Delete(idx);

    // TODO: 0a > 1つの場合の削除テスト

    // 3. 既存プロファイルに切替え
    CMB_profile->ItemIndex = 0;
    Setting_load_detail(CMB_profile->Text);

    // 4. プロファイル保存
    Setting_save_profile();
}

void __fastcall TFormSetting::B_applyDetailClick(TObject *Sender)
{
    if (CMB_profile->Items->Count == 0) {
        return;
    }
    Setting_save_detail(CMB_profile->Text);
}

//---------------------------------------------------------------------------
// OkとCancel、閉じる処理
//
void __fastcall TFormSetting::B_okClick(TObject *Sender)
{
    if (CMB_profile->Items->Count == 0) {
        ShowMessage(L"プロファイルが設定されていません。");
        return;
    }

    Setting_save_profile();
    Setting_save_detail(CMB_profile->Text);

    Close();
}

void __fastcall TFormSetting::B_cancelClick(TObject *Sender)
{
    // 前のプロファイル読込み (FormCloseでも同じ処理をしている)
    Setting_load_profile();
    Setting_load_detail(CMB_profile->Text);

    Close();
}

void __fastcall TFormSetting::FormClose(TObject *Sender, TCloseAction &Action)
{
    // OK, Cancelボタンでない閉じる場合に対応
    Setting_load_profile();
    Setting_load_detail(CMB_profile->Text);
}

//---------------------------------------------------------------------------
// デフォルト設定
//
void __fastcall TFormSetting::SetDefaultDetail(void)
{
    // 例
    E_color->Text = L"Blue";
}

//---------------------------------------------------------------------------
// 設定の読込と保存
//
void __fastcall TFormSetting::Setting_load_profile(void)
{
    // 1. 設定の読込み (ItemIndex)
    TForm *formPtr = this;

    CUtilSetting *setPtr = new CUtilSetting();
    setPtr->Load(/* absPath=*/false, s_settingProfileFileName, formPtr->Name); // absPath=falseの場合、保存先フォルダを自動指定
    setPtr->Form_SetData_AllKeys(formPtr);

    delete setPtr;
    setPtr = NULL;

    // 2. 設定の読込み (Items)
    s_bkMemo->DoRecover();

    // 3. ItemsとItemIndexの読込み
    CMB_profile->Items->Assign(M_profileItems->Lines);
    CMB_profile->ItemIndex = StrToIntDef(E_profileItemIndex->Text, -1);
}

void __fastcall TFormSetting::Setting_save_profile(void)
{
    // 1. TComboBoxのItemsとItemIndexの退避
    M_profileItems->Lines->Assign(CMB_profile->Items);
    E_profileItemIndex->Text = IntToStr(CMB_profile->ItemIndex);

    // 2. 設定の保存 (ItemIndex)
    CUtilSetting *setPtr = new CUtilSetting();

    TForm *formPtr = this;
    setPtr->Form_GetData(E_profileItemIndex);
    setPtr->Save(/* absPath=*/false, s_settingProfileFileName, formPtr->Name); // absPath=falseの場合、保存先フォルダを自動指定

    delete setPtr;
    setPtr = NULL;

    // 3. 設定の保存 (Items)
    s_bkMemo->DoBackup();
}

void __fastcall TFormSetting::Setting_load_detail(String profName)
{
    if (profName.Length() == 0) {
        E_color->Text = L"";
        return;
    }

    // debug
    String msg = L"Setting_load_detail";
    OutputDebugString(msg.c_str());

    // 
    TForm *formPtr = this;

    CUtilSetting *setPtr = new CUtilSetting();

    String filename = s_settingDetailFilePrefix + L"." + profName;
    setPtr->Load(/* absPath=*/false, filename, formPtr->Name); // absPath=falseの場合、保存先フォルダを自動指定
    setPtr->Form_SetData_AllKeys(formPtr);

    delete setPtr;
    setPtr = NULL;
}

void __fastcall TFormSetting::Setting_save_detail(String profName)
{
    if (profName.Length() == 0) {
        return;
    }

    CUtilSetting *setPtr = new CUtilSetting();

    String filename = s_settingDetailFilePrefix + L"." + profName;
    TForm *formPtr = this;
    setPtr->Form_GetData(E_color);
    setPtr->Save(/* absPath=*/false, filename, formPtr->Name); // absPath=falseの場合、保存先フォルダを自動指定

    delete setPtr;
    setPtr = NULL;
}

void __fastcall TFormSetting::Setting_delete_detail(String profName)
{
    if (profName.Length() == 0) {
        return;
    }

    // 削除処理
    String trgDir = CUtilSetting::GetSaveDir();
    String filepath = trgDir + L"\\" + s_settingDetailFilePrefix + L"." + profName;
    if (FileExists(filepath) == false) {
        return;
    }

    try {
        TFile::Delete(filepath);
    } catch (Exception &exc) {
        OutputDebugString(exc.Message.c_str());
    }
}
//---------------------------------------------------------------------------
NewProfileDialog.h
//----------------------------------------------------------------------------
#ifndef NewProfileDialogH
#define NewProfileDialogH
//----------------------------------------------------------------------------
#include <Buttons.hpp>
#include <StdCtrls.hpp>
#include <Controls.hpp>
#include <Forms.hpp>
#include <Graphics.hpp>
#include <Classes.hpp>
#include <SysUtils.hpp>
#include <Windows.hpp>
#include <System.hpp>
//----------------------------------------------------------------------------
class TDialogNewProfile : public TForm
{
__published:
    TLabel *Label1;
    TEdit *E_name;
    TButton *OKBtn;
    TButton *CancelBtn;
    void __fastcall FormShow(TObject *Sender);
private:
public:
    String __fastcall GetProfileName(void);
    virtual __fastcall TDialogNewProfile(TComponent* AOwner);
};
//----------------------------------------------------------------------------
extern PACKAGE TDialogNewProfile *DialogNewProfile;
//----------------------------------------------------------------------------
#endif    
NewProfileDialog.cpp
//---------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "NewProfileDialog.h"
//--------------------------------------------------------------------- 
#pragma resource "*.dfm"
TDialogNewProfile *DialogNewProfile;
//---------------------------------------------------------------------
__fastcall TDialogNewProfile::TDialogNewProfile(TComponent* AOwner)
    : TForm(AOwner)
{
}

String __fastcall TDialogNewProfile::GetProfileName(void)
{
    return E_name->Text;
}
//---------------------------------------------------------------------
void __fastcall TDialogNewProfile::FormShow(TObject *Sender)
{
    E_name->Text = L"";
}
//---------------------------------------------------------------------------

実行例

メイン画面
qiita.png

設定画面
qiita.png

新規プロファイル名指定ダイアログ表示
qiita.png

作成されたファイル
qiita.png

ファイル例

FormSetting_detail.DeltaFlyer
{"E_color":"Blue1"}
FormSetting_profile.json
{"E_profileItemIndex":"1"}
FormSetting_profileItems.csv
DeltaFlyer
DeltaFlyer2
Voyager
Runabout

注意点: 再利用性

フォームで実装しているため、複数プロジェクト間での再利用性は低い。

ロジックを分離することで、再利用性を高める方が良い。

このまま使うと将来のメンテナンスコストが跳ね上がる。
(フォームをコピーして、プロジェクト独自のコンポーネント追加、をすると似たようなコードが氾濫する。可読性も悪く、バグ対処もしにくくなる)。

処理の外部ソース化

(追記 2017/11/05)

上記の処理を外部ソース化した。
結果としてSettingUnit.cpp内が読みやすくなり、複数プロジェクトへの適用作業を軽減することができるだろう。

上記で「動くようにした」上で「正しく(使いまわししやすく)」した。
参考: 動くようにする、正しくする、速くする。

SettingUnit.cpp
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include <IOUtils.hpp>
#include "SettingUnit.h"
#include "NewProfileDialog.h"
//
#include "UtilSetting.h"
#include "UtilProfileSetting.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma link "editex"
#pragma resource "*.dfm"
TFormSetting *FormSetting;
//---------------------------------------------------------------------------

// UtilProfileSetting
static CUtilProfileSetting *s_utilProfileSettting; // コンストラクタで設定すること

//---------------------------------------------------------------------------
__fastcall TFormSetting::TFormSetting(TComponent* Owner)
    : TForm(Owner)
{
    Profile_setup();
}

__fastcall TFormSetting::~TFormSetting()
{
    Profile_delete();
}

//---------------------------------------------------------------------------
// ProfileSetting
//
void __fastcall TFormSetting::Profile_setup(void)
{
    // UtilProfileSetting
    s_utilProfileSettting = new CUtilProfileSetting(CMB_profile, M_profileItems, E_profileItemIndex, this);
    TObject *objList[] = {
        E_color,
        E_pilot,
    };
    int numObject = sizeof(objList) / sizeof(objList[0]);
    s_utilProfileSettting->RegisterDetailComponents(objList, numObject);
    s_utilProfileSettting->SetFunction_detailDefault(SetDefaultDetail);
    s_utilProfileSettting->SetFunction_detailClear(ClearDetail);
    //

    // 読込み
    s_utilProfileSettting->Profile_Load();
    s_utilProfileSettting->Detail_Load();
}

void __fastcall TFormSetting::Profile_delete(void)
{
    delete s_utilProfileSettting;
    s_utilProfileSettting = NULL;
}
//---------------------------------------------------------------------------
void __fastcall TFormSetting::B_addClick(TObject *Sender)
{
    if (DialogNewProfile->ShowModal() != mrOk) {
        return;
    }
    String newItem = DialogNewProfile->GetProfileName();
    s_utilProfileSettting->Profile_Add(newItem);
}

void __fastcall TFormSetting::CMB_profileChange(TObject *Sender)
{
    s_utilProfileSettting->Detail_Load();
}

void __fastcall TFormSetting::B_deleteClick(TObject *Sender)
{
    s_utilProfileSettting->Profile_Delete(Handle, /*english=*/false);
}

void __fastcall TFormSetting::B_applyDetailClick(TObject *Sender)
{
    if (CMB_profile->Items->Count == 0) {
        return;
    }
    s_utilProfileSettting->Detail_Save();
}

//---------------------------------------------------------------------------
// OkとCancel、閉じる処理
//
void __fastcall TFormSetting::B_okClick(TObject *Sender)
{
    if (CMB_profile->Items->Count == 0) {
        ShowMessage(L"プロファイルが設定されていません。");
        return;
    }

    s_utilProfileSettting->Profile_Save();
    s_utilProfileSettting->Detail_Save();

    Close();
}

void __fastcall TFormSetting::B_cancelClick(TObject *Sender)
{
    // 前のプロファイル読込み (FormCloseでも同じ処理をしている)
    s_utilProfileSettting->Profile_Load();
    s_utilProfileSettting->Detail_Load();

    Close();
}

void __fastcall TFormSetting::FormClose(TObject *Sender, TCloseAction &Action)
{
    // OK, Cancelボタンでない閉じる場合に対応
    s_utilProfileSettting->Profile_Load();
    s_utilProfileSettting->Detail_Load();
}

//---------------------------------------------------------------------------
// 設定のデフォルト代入・クリア
//
void __fastcall TFormSetting::SetDefaultDetail(void)
{
    // 例
    E_color->Text = L"Blue";
    E_pilot->Text = L"Someone";
}
void __fastcall TFormSetting::ClearDetail(void)
{
    // 例
    E_color->Text = L"";
    E_pilot->Text = L"";
}
//---------------------------------------------------------------------------
UtilProfileSetting.h
//---------------------------------------------------------------------------

#ifndef UtilProfileSettingH
#define UtilProfileSettingH

//---------------------------------------------------------------------------
#include "UtilBackupTMemo.h"

/*
以下の設定項目の保存と読込みを行う
- プロファイル
- 詳細

依存ライブラリ: [UtilSetting]
依存ライブラリ: [UtilBackupTMemo]
*/

class CUtilProfileSetting {
private:
    TComboBox *m_comboPtr; // profileを選択するコンボボックスのポインタ
    TMemo *m_itemsPtr; // TComboBoxのItemsを保持するTMemo
    TEdit *m_itemindexPtr; // TComboBoxのItemIndexを保持するTEdit
    //
    TForm *m_formPtr; // 設定の読込み、保存対象フォーム
    TObjectList *m_objList; // 詳細のコンポーネントを登録する
    int m_numObject; // 詳細のコンポーネント数
    CUtilBackupTMemo *m_backupTmemo; // TComboBoxのItemsの保存と読込みに使う
    String m_filename_profile; // profileのJSONファイル名
    // デフォルト値の設定関数ポインタ
    typedef void __fastcall (__closure *TMethodDefault)(void);
    TMethodDefault m_funcDefaultPtr; 
    typedef void __fastcall (__closure *TMethodClear)(void);
    TMethodClear m_funcClearPtr;
    //
    String getDetailFilename(String profName);
public:
    // プロファイル
    void __fastcall Profile_Save(void);
    void __fastcall Profile_Load(void);
    void __fastcall Profile_Add(String newItem);
    void __fastcall Profile_Delete(HWND handle, bool english);
    // 詳細
    void __fastcall Detail_Save(void);
    void __fastcall Detail_Load(void);
    void __fastcall Detail_Delete(void);

    // コンストラクタ・デストラクタ、セットアップ関連
    __fastcall CUtilProfileSetting(TComboBox *cmbPtr, TMemo *itemsPtr, TEdit *itemindexPtr, TForm *formPtr);
    void __fastcall RegisterDetailComponents(TObject *objs[], int numObject);
    void __fastcall SetFunction_detailDefault(TMethodDefault ptr);
    void __fastcall SetFunction_detailClear(TMethodClear ptr);
    __fastcall ~CUtilProfileSetting();
};

#endif
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1