動作環境
C++ Builder XE4
前回: C++ Builder > ファイル処理検討 > 複数のプロファイルの切替え > _profファイルとdetailファイルでの読書き
上記の処理に関して自分が使いたい例を実装してみた。
使用外部ソース
二つの自作ソースを利用する。
これらの自作ソースがないために、閲覧者の環境では動きません。
コードは参考程度にどうぞ。
- UtilSetting.cppと.h
- 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」にて新規プロファイル追加時に使う
- プロファイル追加は頻度が低いと思われるため、設定画面と分けた
- 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"";
}
//---------------------------------------------------------------------------
実行例
ファイル例
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