それではIDListProc.hの残りの部分、メニューとダイアログの関連関数について解説してゆきましょう。
/////////////////////////////////
//主ウィンドウCMyWndの関数の定義
//メニュー項目、コントロール関数
/////////////////////////////////
bool CMyWnd::OnOpen() {
//データファイルの読み込み
char* cp = cmndlg.GetFileName(m_hWnd, "datファイル(*.dat)\0*.dat\0全てのファイル(*.*)\0*.*\0\0", TRUE, "dat", ".");
(解説:このCMNDLGクラスで、ファイル、色、フォントやファイルパスのダイアログによる選択ができます。cpにはキャンセルされた場合Null(0)、選択された場合ファイルパス、名のポインターが入ります。)
if(cp)
g_Data.FromFile(cp);
(解説:ものすごく簡単に書いてますが、CSTRクラス変数g_Dataにcpが指す(テキスト)ファイルを読み込ませました。)
else {
MessageBox(m_hWnd, "ファイルが選択されていません", "エラー", MB_OK | MB_ICONERROR);
return FALSE;
}
//データ読み出し-行読み出しを繰り返す
int i = 0;
while(cp = GetDelimtStr(g_Data.ToChar())) {
//一行から語句を読み出し、コラムにセットすることを4回繰り返す
g_ListView.InsertItem(i, cp, -1, i); //LVIF_PARAMをソートの為に追加
cp = GetDelimtStr(g_Data.ToChar());
g_ListView.SetItem(i, 1, cp);
cp = GetDelimtStr(g_Data.ToChar());
g_ListView.SetItem(i, 2, cp);
cp = GetDelimtStr(g_Data.ToChar());
g_ListView.SetItem(i, 3, cp);
i++;
}
(解説:ここでやっているのは、iを行数とし、0行からUser.hの「一行から4列分の文字列を切り出す」GetDelimitStr関数を使ってg_Dataに読み込ませたコンマ区切りファイル(ToChar()関数はCSTRクラスのデータをchar*で返します)を切り出し、終端が来るまで繰り返しリストビューに表示する処理です。)
return TRUE;
}
bool CMyWnd::OnSave() {
//ファイルの保存場所を指定する
char* cp = cmndlg.GetFileName(m_hWnd, "datファイル(*.dat)\0*.dat\00",
FALSE, "dat", ".");
if(!cp) {
MessageBox(m_hWnd, "キャンセルされました", "エラー", MB_OK | MB_ICONERROR);
return FALSE;
}
(解説:これもcpにファイル名を取得します。ファイルを選ぶダイアログがキャンセルされれば処理を中断します。)
//ファイルの保存
g_Data = ""; // 初期化
//行数だけ文字列化を繰り返す
int n = g_ListView.GetItemCount();
int i, j;
for(i = 0; i < n; i++) {
char buff[MAX_PATH];
CSTR row = "";
//g_ListViewの4列のデータを読み出し、コンマ区切の文字列一行とする
for(j = 0; j < 3; j++) {
g_ListView.GetItemText(i, j, buff, MAX_PATH);
row = row + buff;
row = row + ",";
}
g_ListView.GetItemText(i, j, buff, MAX_PATH);
row = row + "\""; //最終項目は
row = row + buff; //文字列中に'\n'があるので
row = row + "\""; //「""」で囲う
//一行データをg_Dataへ足しこんでゆく
g_Data = g_Data + row;
g_Data = g_Data + "\n";
}
(解説:これは読込とは逆にリストビューのデータを0行から(行数-1)行まで文字列としてコンマ区切りで収納し、行末にCR(\n)を入れます。第4列は複数行を許すために「""(ダブルクォテーション)」でくくります。)
//ファイルの書き込み
g_Data.ToFile(cp);
(解説:この一行だけで、cpが指すファイル名でコンマ区切りファイルを書き込みます。)
ErrorMsg(m_hWnd);
(解説:前回ErrorMsg()関数を紹介したので無理やりこれをここに挿入してみました。なお、IDListhファイルの#include文の後にこの関数を入れたError.hファイルを追加するために「#include "Error.h"」を入れること、最後はその#include文とこの行を削除することを忘れないで下さい。この関数を使って実験をしてみる予定です。)
return TRUE;
}
bool CMyWnd::OnExit() {
SendMsg(WM_CLOSE, 0, 0);
(解説:この関数はSendMessage(m_hWnd, ...)と等価です。)
return TRUE;
}
bool CMyWnd::OnAdd() {
//追加の場合は、データ受け渡し用のg_Data(CSTR)を空(NULL)にする
g_Data = "";
(解説:メインウィンドウとダイアログ間のデータ受け渡しにg_Dataを使います。関数の引数で渡すとスマートなんですが、外部変数で渡すこととしました。)
//編集用ダイアログを追加用ダイアログとして使う
if(!editdlg.DoModal(m_hWnd, "IDD_EDIT", editdlgProc) ||
!*g_Data.ToChar()) //キャンセルされた場合、または
return FALSE; //データが空の場合
(解説:コメント通りです。ダイアログがキャンセルされたか、g_Dataが空の場合、FLASE(0)を返すようにします。データ受け渡し用CSTRクラス変数g_Dataのデータをchar*で返すToChar()関数を使い、最初の文字(*g_Data.ToChar())をチェックしています。)
int n = g_ListView.GetItemCount(); //リストの最後尾を確認
(解説:nには行数が入ります。x行の場合は第0行から第(x - 1)行となりますので、第x行は次の新しい行を意味します。)
//CSTR g_Dataのカンマ区切り文字列をEdit1~4までに格納
char *cp = GetDelimtStr(g_Data.ToChar());
g_ListView.InsertItem(n, cp, -1, n); //LVIF_PARAMをソートの為に追加
SBar.SetText(1, cp); //ステータスバーにメンバーシップ表示
cp = GetDelimtStr(g_Data.ToChar());
g_ListView.SetItem(n, 1, cp);
cp = GetDelimtStr(g_Data.ToChar());
g_ListView.SetItem(n, 2, cp);
cp = GetDelimtStr(g_Data.ToChar());
g_ListView.SetItem(n, 3, cp);
(解説:ファイルの読み込みで見た処理です。4列分のデータをGetDelimitStr関数を使って切り出し、リストビューにセットします。)
return TRUE;
}
bool CMyWnd::OnSearch() {
static CSTR ToFind; //検索文字列保存用変数
static int found = 0; //初期値は最初から
char buff[MAX_PATH]; //リストボックス文字列取得用バッファ
int n = g_ListView.GetItemCount(); //リストボックスの行数確認
(解説:以上は検索処理の準備です。内容はコメント通りです。)
//検索文字列取得ダイアログ
SBar.SetText(1, "検索する項目に含まれる文字列を入力してください");
(解説:ステータスバーを使ってユーザーに指示を出します。)
searchdlg.DoModal(m_hWnd, "IDD_SEARCH", searchdlgProc);
(解説:検索用のsearchdlgダイアログから検索文字列をg_Dataに入れて受け取ります。)
//前回の検索と同じ検索文字列であれば検索を継続
if(g_Data != ToFind) { //異なれば
ToFind = g_Data; //検索文字列を更新し
found = 0; //最初から検索する
g_ListView.SetItemState(-1, 0, LVIS_SELECTED); //全行の選択状態の解除
}
(解説:前回の検索文字列(初回の場合はNull)とダイアログからとってきたg_Dataの検索文字列が異なれば、検索処理の初期化を行います。)
for(int i = found; i < n; i++) { //行
for(int j = 0; j < 4; j++) { //列
g_ListView.GetItemText(i, j, buff, MAX_PATH); //文字列取得
if(strstr(buff, ToFind.ToChar())) { //検索文字列が含まれれば
found = i + 1; //次の検索のために、見つかった行の次を指す
SetFocus(g_ListView.m_hWnd); //g_ListViewにフォーカスがないと選択状態が表示されない
g_ListView.SetItemState(i, LVIS_SELECTED, LVIS_SELECTED); //選択状態にする
return TRUE;
}
}
}
MessageBox(m_hWnd, "検索文字は見当たりませんでした", "検索結果", MB_OK | MB_ICONINFORMATION);
g_ListView.SetItemState(-1, 0, LVIS_SELECTED); //全行の選択状態の解除
return TRUE;
(解説:あとは第0行の第0列のセルから横へ検索開始し、なければ次の行に移ります。見つかった時は選択状態をそのままにしてフォーカスを当てて表示します。その状態で再度同じ文字列を検索する場合には次の行(found + 1)から再開します。)
}
bool CMyWnd::OnEdit() {
int n = g_ListView.GetNextItem(-1, LVNI_ALL | LVNI_SELECTED);
(解説:選択行を調べます。)
if (n == -1) {
MessageBox(m_hWnd, "項目が選択されていません", "警告", MB_OK);
return FALSE;
}
(解説:選択されていなければ処理を中断します。)
//CSTR g_Dataに4列の項目データをカンマ区切りで格納
char buff[MAX_PATH];
g_ListView.GetItemText(n, 0, buff, MAX_PATH);
g_Data = buff;
g_Data = g_Data + ",";
g_ListView.GetItemText(n, 1, buff, MAX_PATH);
g_Data = g_Data + buff;
g_Data = g_Data + ",";
g_ListView.GetItemText(n, 2, buff, MAX_PATH);
g_Data = g_Data + buff;
g_Data = g_Data + ",";
g_ListView.GetItemText(n, 3, buff, MAX_PATH);
g_Data = g_Data + "\""; //最終項目は
g_Data = g_Data + buff; //文字列中に'\n'があるので
g_Data = g_Data + "\""; //「""」で囲う
(解説:これは選択行のデータをg_Data変数にカンマ区切りで格納する処理です。)
//編集用ダイアログを呼び出す
if(!editdlg.DoModal(m_hWnd, "IDD_EDIT", editdlgProc))
return FALSE;;
(解説:編集用のeditdlgダイアログがキャンセルされるとFALSE(0)を返すので処理を中断します。)
//CSTR g_Dataに格納されたカンマ区切りデータを4列の項目に格納
char *cp = GetDelimtStr(g_Data.ToChar());
g_ListView.SetItemText(n, 0, cp);
SBar.SetText(1, cp);
cp = GetDelimtStr(g_Data.ToChar());
g_ListView.SetItemText(n, 1, cp);
cp = GetDelimtStr(g_Data.ToChar());
g_ListView.SetItemText(n, 2, cp);
cp = GetDelimtStr(g_Data.ToChar());
g_ListView.SetItemText(n, 3, cp);
(解説:これはg_Data変数を使って編集ダイアログで編集されたカンマ区切りデータを受け取り、それをリストビューに表示する処理です。第1列はステータスバーにも表示されます。)
return TRUE;
}
bool CMyWnd::OnDelete() {
//複数選択に対応
int n = g_ListView.GetNextItem(-1, LVNI_ALL | LVIS_SELECTED);
while(n != -1) {
g_ListView.DeleteItem(n);
n = g_ListView.GetNextItem(n - 1, LVNI_ALL | LVIS_SELECTED);
}
(解説:選択された行がないとGetNextItem関数は-1を返すので、それまで行削除を繰り返します。)
SBar.SetText(1, "選択メンバーシップ"); //ステータスバー表示変更
return TRUE;
}
bool CMyWnd::OnSort() {
sortdlg.DoModal(m_hWnd, "IDD_SORT", sortdlgProc);
(解説:sortdlgダイアログを呼び出します。処理はダイアログで行います。)
return TRUE;
}
bool CMyWnd::OnHelp() {
WinExec(g_HelpFile.ToChar(), SW_SHOWNORMAL); //g_HelpFileがコマンドライン
(解説:これがOnCreate()で用意したg_HelpFile("hh \"(ヘルプファイルパス、名)\"")というコマンド文字列の実行部分です。)
return TRUE;
}
bool CMyWnd::OnVersion() {
versiondlg.DoModal(m_hWnd, "IDD_VERSION", versiondlgProc);
(解説:バージョンダイアログを表示するだけです。)
return TRUE;
}
///////////////////////////////
//ユーザーダイアログの関数定義
//コントロール関数
///////////////////////////////
bool EDITDLG::OnInit(WPARAM wParam, LPARAM lParam) {
(解説:編集ダイアログのWM_INITDLGメッセージ処理です。)
if(!*g_Data.ToChar()) {
SendMsg(WM_SETTEXT, 0, (LPARAM)"IDとパスワードの追加");
SendItemMsg(IDOK, WM_SETTEXT, 0, (LPARAM)"登録終了");
return TRUE;
}
(解説:編集と追加を同じダイアログで行うという手抜きをしたので、データ受け渡し用g_Data変数が空なら、追加ダイアログとしてタイトルとIDOKボタンのテキストを変更しています。なお、以降は編集ダイアログの処理です。)
//GetDelimitStrはソースを短縮してしまうのでg_Dataは保護する
CSTR Data = g_Data;
//CSTR g_Dataのカンマ区切り文字列をEdit1~4までに格納
char *cp = GetDelimtStr(Data.ToChar());
SendItemMsg(IDC_EDIT1, WM_SETTEXT, NULL, (LPARAM)cp);
cp = GetDelimtStr(Data.ToChar());
SendItemMsg(IDC_EDIT2, WM_SETTEXT, NULL, (LPARAM)cp);
cp = GetDelimtStr(Data.ToChar());
SendItemMsg(IDC_EDIT3, WM_SETTEXT, NULL, (LPARAM)cp);
cp = GetDelimtStr(Data.ToChar());
SendItemMsg(IDC_EDIT4, WM_SETTEXT, NULL, (LPARAM)cp);
(解説:g_Dataからのデータはキャンセルで戻った時にそのまま使うので、一旦ローカル変数にコピーし、そのローカル変数をGetDelimitStr関数で切り出し、所定のエディットボックスに格納してゆきます。)
return TRUE;
}
bool EDITDLG::OnIdok() {
//CSTR g_Dataにカンマ区切りでEdit1~4までの文字列を格納
char buff[MAX_PATH];
SendItemMsg(IDC_EDIT1, WM_GETTEXT, (WPARAM)MAX_PATH, (LPARAM)buff);
g_Data = buff;
g_Data = g_Data + ",";
SendItemMsg(IDC_EDIT2, WM_GETTEXT, (WPARAM)MAX_PATH, (LPARAM)buff);
g_Data = g_Data + buff;
g_Data = g_Data + ",";
SendItemMsg(IDC_EDIT3, WM_GETTEXT, (WPARAM)MAX_PATH, (LPARAM)buff);
g_Data = g_Data + buff;
g_Data = g_Data + ",";
SendItemMsg(IDC_EDIT4, WM_GETTEXT, (WPARAM)MAX_PATH, (LPARAM)buff);
g_Data = g_Data + "\""; //最終項目は
g_Data = g_Data + buff; //文字列中に'\n'があるので
g_Data = g_Data + "\""; //「""」で囲う
EndModal(TRUE);
(解説:処理終了ボタンが押されたら、各エディットボックスの内容をbuff配列に読込み、g_Dataにカンマ区切りで追加してゆきます。最終項目(第4列の備考)は括弧で囲みます。)
return TRUE;
}
bool EDITDLG::OnIdcancel() {
EndModal(FALSE);
(解説:これがコントロールにはないIDCANCEL定数の処理です。EndModal関数でFALSE(0)を返します。)
return TRUE;
}
///////////////////////////////
//ユーザーダイアログの関数定義
//コントロール関数
///////////////////////////////
bool SEARCHDLG::OnInit(WPARAM wParam, LPARAM lParam) {
SendItemMsg(IDC_EDIT1, WM_SETTEXT, 0, (LPARAM)m_ToFind);
(解説:既に検索歴があればメンバー変数に保存した検索文字列をエディットボックスに表示します。)
return TRUE;
}
bool SEARCHDLG::OnIdok() {
SendItemMsg(IDC_EDIT1, WM_GETTEXT, MAX_PATH, (LPARAM)m_ToFind);
g_Data = m_ToFind;
EndModal(TRUE);
(解説:エディットボックスの検索文字列はメンバー変数に読込み、g_Dataを介してメインウィンドウ処理に受け渡します。)
return TRUE;
}
bool SEARCHDLG::OnIdcancel() {
EndModal(FALSE);
(解説:EndModal関数でFALSE(0)を返すIDCANCEL定数の処理です。)
return TRUE;
}
///////////////////////////////
//ユーザーダイアログの関数定義
//コントロール関数
///////////////////////////////
bool SORTDLG::OnInit(WPARAM wParam, LPARAM lParam) {
//コンボボックスに文字列を登録
SendItemMsg(IDC_COMBOBOX1, CB_ADDSTRING, 0, (LPARAM)"メンバーシップ");
SendItemMsg(IDC_COMBOBOX1, CB_ADDSTRING, 0, (LPARAM)"ログインID");
SendItemMsg(IDC_COMBOBOX1, CB_ADDSTRING, 0, (LPARAM)"パスワード");
SendItemMsg(IDC_COMBOBOX1, CB_ADDSTRING, 0, (LPARAM)"備考");
SendItemMsg(IDC_COMBOBOX1, CB_SETCURSEL, 0, 0); //一番目を選択状態にする
SendItemMsg(IDC_COMBOBOX2, CB_ADDSTRING, 0, (LPARAM)"昇順");
SendItemMsg(IDC_COMBOBOX2, CB_ADDSTRING, 0, (LPARAM)"降順");
SendItemMsg(IDC_COMBOBOX2, CB_SETCURSEL, 0, 0); //一番目を選択状態にする
(解説:コメントに書いてある通りの初期設定を行います。)
return TRUE;
}
bool SORTDLG::OnIdok() {
//選択番号の取得
int col = SendItemMsg(IDC_COMBOBOX1, CB_GETCURSEL, 0, 0);
int dir = SendItemMsg(IDC_COMBOBOX2, CB_GETCURSEL, 0, 0);
(解説:col変数に列番号、dir変数にUPまたはDOWNを読み込みます。)
//ソート対象項目に昇順、降順のデータを書込む
sortsubno[col] = dir;
//ソート処理
g_ListView.SortItem(CompProc, col);
(解説:ソート処理でデータ受け渡しに使うsortsubno配列にデータをセットし、SortItem関数でソートします。)
EndModal(TRUE);
return TRUE;
}
bool SORTDLG::OnIdcancel() {
EndModal(FALSE);
(解説:EndModal関数でFALSE(0)を返すIDCANCEL定数の処理です。)
return TRUE;
}
///////////////////////////////
//ユーザーダイアログの関数定義
//コントロール関数
///////////////////////////////
bool VERSIONDLG::OnInit(WPARAM wParam, LPARAM lParam) {
//スタティックコントロールに文字列を表示
SendItemMsg(IDC_VERTXT, WM_SETTEXT,
0, (LPARAM)"IDList\nVersion 1.0 by YSAMA");
(解説:バージョンダイアログのテキストをスタティックコントロールにセットする処理です。)
return TRUE;
}
bool VERSIONDLG::OnIdok() {
EndModal(TRUE);
(解説:TRUEでもFALSEでも処理は変わらないのでIDCANCEL定数の処理はしません。)
return TRUE;
}
以上でデータの暗号化なしカンマ久区切りテキストのデータの読み書きができるIDList.exeが出来上がりました。
因みにテストデータを保存したのが次のテキストです。基本カンマ区切り4項目で一行ですが、ダブルクォテーション("")内にコントロール記号があれば複数行になります。
【サンプルIDList.dat】
Amazon,BCCForm@gmail.com,abc123,"2019年加入"
楽天,BCCForm@gmail.com,xyz789,"2016年加入
もういいかなという感じ"
アメーバーブログ,BCCForm@gmail.com,lmn456,"2021年から"
次回はこのファイルを使ってErrorMsgj関数のテストを行いましょう。