今回は、前回作成したリソースを使ってドンガラのスケルトンを作ります。以下作成プロセスを少し細かく記述します。

 

作成したリソースファイル(IDList.icoとToolBar.bmp)、IDList.rcファイル(とResIDList.hファイル)を用意し、プログラム作成用フォールダー(例:C:\IDList)へ入れます。次に(アイコンをドラッグして持って行ってもよいですが、面倒なので)SkeltonWizard.exeのショートカットを入れ、それにリソースファイル(IDList.rc)を落とします。すると、SkeltonWizardが立ち上がるので、各画面を次のように設定してください。(分からないところはBCFEditorのヘルプ「BCCSkeltonについて」を読んでね!)

 

「プロジェクト名」がIDListですね。「SDIウィンドウ」になっていることを確認し、ツールバーがグレーになっているので「メニュー選択」で「IDM_MAIN」を選択し、「ツールバー」、「ステータスバー」、「コモンダイアログ」にチェックを入れます。アイコンも「IDI_ICON」にしてください。「次へ」ボタンを押します。

 

「ツールバー」がスタンダード選択の状態になっているので「コモンコントロールのスタンダードビットマップ?」のチェックを二度クリックして外します。するとプルダウンが有効化されますので、「IDI_TOOLBAR」を選択します。

後はToolBar.bmpの順(0~9)までをメニューアイテム(IDM_なんちゃら)に、それぞれクリックすることで関連付けます。("TBar.m_id + ~"のリストアイテムをクリックし、次にIDM_なんちゃらのリストアイテムをクリックして、「→」のボタンを押す)、セパレーターを挿入したい場合は"TBar.m_id + ~"のリストの一番上にある<SEPARATOR>をクリックする。間違った場合はIDM_なんちゃらのリストでアイテムを選択し、「←」ボタンを押すと元に戻るのでやり直す。)

「ステータスバー」は枠の数を決めて、その初期値を入力します。(枠が複数の場合はカンマ区切りにしますが、空白文字等が入る場合は例のように""で囲んでください。)
「次へ」ボタンを押します。

 

ウィンドウメッセージやメニュー、コントロールによる割り込み関数の設定です。

ウィンドウメッセージは設計上使うものを選んでください。(例ではWM_CREATE、WM_NOTIFY、WM_SIZEとWM_CLOSEを選んでいますが、これらは本当によく使うメッセージです。)

メニューやコントロールは、後で追加するよりも削除する方が楽なので決められなければ「すべて選択」を選んでください。

なお、「→」のボタンで追加、「←」ボタンで削除するのは前タブと同じです。

 

最後に選択内容が表示されますので間違いが無いか確認します。

なお、下段の「WYSWYG」にチェックを入れるとBCFEditorに連動するコードを挿入し、「CPPファイルを書き換えない」にチェックを入れると.hとProc.hファイルのみ書き換えます。(チェックを入れなくとも、書き換える際には警告が出ますので心配いりません。)これは慣れてから使うとよいでしょう。

 

こうしてSkeltonWizardを使うことによってIDList.bdp、IDList.h、IDListProc.h、IDList.cppという4つのIDLisファイルが自動生成され、プログラム作成用フォールダーは次のようになります。

 

これらのファイルをビルドする(注)と何もしないスケルトンウィンドウができます。

 

注:IDListファイルをビルドするには、同梱のBCCMaker、または(私が愛用する)BCC Developerを使います。

 

1.BCCMakerの場合

(1)BCCMakerを立ち上げる。

(2)メニューの「ファイル」「新規」でプログラム作成用フォールダー、DebugまたはReleaseを指定し、makeファイル名を"IDList.mak"とする。

(3)cppファイルフォールダーアイコンをダブルクリック(または「追加」ボタンを押)してIDList.cppを選択し、同様にしてrcファイルもIDList.rcを選択する。

(4)「*.makフィル」の「詳細」ボタンを押して、設定ダイアログの「インクルードパス」にBCCForm&Skeltonの(BCCSkelton.hの入っている)フォールダーを指定する。

(5)メニューの「ツール」「Make」でビルドしてから「実行」選択するか、またはMake実行ボタンを押す。

 

2.BCC Developerの場合

(1)SkeltonWIzardが作成したIDList.bdpでBCC Developerを立ち上げる(または立ち上げてからbdpファイル開く。)

(2)メニューの「プロジェクト」「プロジェクトの設定」(または「プロジェクトの設定」ボタン)でダイアログを開き、「リソース」タブで、リソースファイルが正しく「IDList.rc」となっているか、確認する。(時々「"IDList.rc」とダブルクォーテーションが入ってリソースが読めなくなるので注意!)また、念のため「アプリケーション」タブで「Windowsアプリケーション」になっているか、「コンパイル3」タブでBCCSkelton.hのフォールダーにパスが通っているか確認しましょう。

(3)メニューの「プロジェクト」「再構築」でビルドし、「実行」「実行」で実行するか、「再構築」ボタンでビルドし、「実行」ボタンで実行します。

 

どうでしょうか?

 

ドンガラウィンドウを作るだけならこんなに簡単です。次回からはユーザーインターフェースを通じて行う処理を実装します。なお、進め方としてまず暗号化なしのIDListを作成し、その後データを暗号化(高度セキュリティの場合はパスワードも)を行えるようにしましょう。

 

では、また。

 

BCCFormandBCCSkeltonのプログラミング作法は、完成イメージに基づき、まずリソースを作成することから始めます。

ということで、まずはアプリのアイコンを作ります。お好みでよいのですが、添付ツールのEZImageを使って次のようにしてみました。

 

また、他のイメージリソースとして、ツールバービットマップ(ここでは"ToolBar.bmp"としておきます)が必要です。メニュー構成に合わせてTBEditorで作る必要があります。以下に作成例を載せます。

 

これらを元にBCCFormでメニュー、ダイアログなどのリソースを作ります。

以下は作成したリソースのコードです。(解説:)文も載せます。

//-----------------------------------------
//             BCCForm Ver 2.41
//    An Easy Resource Editor for BCC
//  Copyright (c) February 2002 by ysama
//-----------------------------------------
#include    "ResIDList.h"

//----------------------------------
// ダイアログ (IDD_EDIT)

//(解説:表の列項目である「メンーバシップ」、「ログインID」、「パスワード」、「備考(任意)」の入力用ダイアログです。

//夫々のエディットコントロールとラベルを付けています。)
//----------------------------------
IDD_EDIT DIALOG DISCARDABLE 0, 0, 270, 150
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP | WS_DLGFRAME | WS_CAPTION | WS_SYSMENU | DS_SETFONT | DS_CENTER
CAPTION "IDとパスワードの編集"
FONT 8, "MS 明朝"
{
 CONTROL "", IDC_EDIT1, "EDIT", WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | ES_LEFT, 87, 9, 171, 15, WS_EX_CLIENTEDGE
 CONTROL "", IDC_EDIT2, "EDIT", WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | ES_LEFT, 87, 30, 171, 15, WS_EX_CLIENTEDGE
 CONTROL "", IDC_EDIT3, "EDIT", WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | ES_LEFT, 87, 48, 171, 15, WS_EX_CLIENTEDGE
 CONTROL "", IDC_EDIT4, "EDIT", WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | ES_MULTILINE | WS_VSCROLL | ES_LEFT, 87, 66, 171, 48
 CONTROL "編集終了", IDOK, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON, 198, 123, 60, 18
 CONTROL "メンバーシップ", IDC_LABEL1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_SUNKEN, 6, 9, 75, 15
 CONTROL "ログインID", IDC_LABEL2, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_SUNKEN, 6, 30, 75, 15
 CONTROL "パスワード", IDC_LABEL3, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_SUNKEN, 6, 48, 75, 15
 CONTROL "備考(任意)", IDC_LABEL4, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY | SS_SUNKEN, 6, 66, 75, 15
}

//----------------------------------
// ダイアログ (IDD_SEARCH)
//(解説:検索文字列の入力用ダイアログです。)
//----------------------------------
IDD_SEARCH DIALOG DISCARDABLE 0, 0, 135, 48
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU | DS_SETFONT | DS_CENTER
CAPTION "検索"
FONT 8, "MS 明朝"
{
 CONTROL "", IDC_EDIT1, "EDIT", WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | ES_LEFT, 3, 15, 126, 12, WS_EX_CLIENTEDGE
 CONTROL "OK", IDOK, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON, 45, 30, 45, 15
 CONTROL "検索する文字列", IDC_LABEL1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 6, 3, 123, 9
}

//----------------------------------
// ダイアログ (IDD_SORT)
//(解説:表の並べ替え用ダイアログです。サブアイテム(列)の指定、昇順・降順の指定を行います。)
//----------------------------------
IDD_SORT DIALOG DISCARDABLE 0, 0, 144, 87
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU | DS_SETFONT | DS_CENTER
CAPTION "データの並べ替え"
FONT 8, "MS 明朝"
{
 CONTROL "", IDC_COMBOBOX1, "COMBOBOX", WS_CHILD | WS_VISIBLE | WS_TABSTOP | CBS_DROPDOWNLIST | WS_VSCROLL, 12, 18, 120, 60
 CONTROL "", IDC_COMBOBOX2, "COMBOBOX", WS_CHILD | WS_VISIBLE | WS_TABSTOP | CBS_DROPDOWNLIST | WS_VSCROLL, 12, 48, 120, 60
 CONTROL "サブアイテムの選択", IDC_LABEL1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 15, 6, 93, 9
 CONTROL "並べ替え方法", IDC_LABEL2, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 15, 36, 93, 9
 CONTROL "完了", IDOK, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 51, 66, 39, 15
}

//----------------------------------
// ダイアログ (IDD_VERSION)
//(解説:バージョン表示用ダイアログです。)
//----------------------------------
IDD_VERSION DIALOG DISCARDABLE 0, 0, 160, 40
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION | DS_MODALFRAME | DS_3DLOOK | DS_CENTER
CAPTION "バージョン情報"
FONT 9, "Times New Roman"
{
 CONTROL IDI_ICON, 0, "STATIC", SS_SUNKEN | SS_ICON | WS_CHILD | WS_VISIBLE, 12, 10, 32, 32
 CONTROL "", IDC_VERTXT, "STATIC", SS_CENTER | SS_SUNKEN | WS_CHILD | WS_VISIBLE, 42, 8, 80, 24
 CONTROL "OK", IDOK, "BUTTON", BS_PUSHBUTTON | BS_CENTER | WS_CHILD | WS_VISIBLE | WS_TABSTOP, 130, 14, 20, 12
}

//-------------------------
// メニュー(IDM_MAIN)
//(解説:メニューです。ツールバービットマップはこれらに対応しています。)
//-------------------------
IDM_MAIN MENU DISCARDABLE
{
    POPUP "ファイル(&F)"
    {
        MENUITEM "データを開く(&O)", IDM_OPEN
        MENUITEM SEPARATOR
        MENUITEM "データの保存(&S)", IDM_SAVE
        MENUITEM SEPARATOR
        MENUITEM "終了(&X)", IDM_EXIT
    }
    POPUP "編集(&E)"
    {
        MENUITEM "追加(&A)", IDM_ADD
        MENUITEM "検索(&S)", IDM_SEARCH
        MENUITEM "編集(&E)", IDM_EDIT
        MENUITEM "削除(&D)", IDM_DELETE
    }
    POPUP "表示(&V)"
    {
        MENUITEM "並び替え(&S)", IDM_SORT
    }
    POPUP "ヘルプ(&H)"
    {
        MENUITEM "本ソフトの使い方(&H)", IDM_HELP
        MENUITEM SEPARATOR
        MENUITEM "バージョン情報(&V)", IDM_VERSION
    }
}

//--------------------------
// イメージ(IDI_ICON)
//(解説:作成したアプリケーションアイコンです。実際の<ファイルパス>は各自の開発環境で変わります。)
//--------------------------
IDI_ICON    ICON    DISCARDABLE    "C:\<ファイルパス>\IDList\IDList.ico"

//--------------------------
// イメージ(IDI_TOOLBAR)
//(解説:作成したツールバービットマップです。)
//--------------------------
IDI_TOOLBAR    BITMAP    DISCARDABLE    "C:\<ファイルパス>\IDList\ToolBar.bmp"

 

今日はここまで、です。

長らくネタ探しをしていましたが、実用的なプログラムとして自分自身が使っているのが、「様々なログインIDとパスワードの管理」ソフトです。

会員、買い物、eBankingその他webを利用するサービスではIDとパスワードが必須。その全てを覚えられれば簡単ですが、時には失念してしまう。(特に67となった管理人はそうです。)その時の為の記録として使うのがIDとパスワードの一覧です。

女房などは未だに紙媒体(要すればノートとペン)を使っていますが、プログラミングを趣味にする私が紙にペンでは不味いでしょう?ということで、最初は金融機関のeBanking用に簡単なものを作り、その後汎用のものを作りましたが、暗号化や初期パスワードは自分のアルゴリズムだったので公開できませんでした。しかし、Windowsに、

CryptProtectData関数

CryptUnProtectData関数

があるということに気が付き、この汎用APIを使ってプログラムを組みなおし、公開しようと思います。名付けて

IDList

としました。

ゆっくり作ってゆくので、また正常動作するまで試行錯誤で開発するのでリアルではありますが、長くなりますので、その点はご了解ください。

 

【IDList開発仕様】

・20年前のBorland C++ Compiler 5.5(Win32)を使用(ここから

・BCCFormandBCCSkeltonを使用

・SDIベースでListViewコントロールを使った「表」表示とし、入力はダイアログを使用

・暗号化(更にパスワード-これはIDListには入れられませんけどね-汗;)はDPAPIの関数を使用

・見た目は次のようにする

 

まぁ、これ位の緩い縛りでぼちぼちやりましょう。

 

今回はCalenderの最後で、ユーザー定義関数と年月入力ダイアログについて解説します。

 

///////////////////////////////
//ユーザー定義関数
///////////////////////////////
bool CMyWnd::SetMCC(HWND hMC) {

    int wd, ht, margin = 10;                //ウィンドウクライアントエリアの余白
    RECT rw, rc;                            //サイズ取得のための変数
    //MCコントロールの表示サイズ取得と表示
    SendMessage(hMC, MCM_GETMINREQRECT, NULL, (LPARAM)&rw);    //最小限のサイズ取得
    wd = rw.right - rw.left;                //MCコントロールの最小幅
    ht = rw.bottom - rw.top;                //MCコントロールの最小高さ
    MoveWindow(hMC, margin, margin, wd, ht, TRUE);    //x, y, 幅, 高さ, 再描画
    //MCコントロールに合わせてウィンドウサイズを調整
    GetWindowRect(m_hWnd, &rw);                //ウィンドウのサイズ
    GetClientRect(m_hWnd, &rc);                //ウィンドウクライアント領域のサイズ
    wd += rw.right - rw.left - rc.right + rc.left + margin * 2;
    ht += rw.bottom - rw.top - rc.bottom + rc.top + margin * 2;
    GetWindowRect(SBar.m_hWnd, &rw);        //ステータスバーのサイズ
    ht += rw.bottom - rw.top;                //ステータスバーの高さを追加
    SetWindowPos(m_hWnd, HWND_TOP, 0, 0, wd, ht, SWP_NOMOVE | SWP_NOZORDER);
    return TRUE;
}
//(解説:まずMCCの最小サイズをMCM_GETMINREQRECTでローカル変数のRECT構造体rwに取得し、ローカル変数のwdとhtに記録し、これを使って最小サイズのMCCとします。次にローカル変数のRECT構造体rwとrcを使ってウィンドウの外周径と内周径からその幅と高さの差を求め、ローカル変数のwdとhtに加算します。最後にローカル変数のRECT構造体rwを使ってステータスバーの高さを求めてこれをhtに更に加算し、ウィンドウサイズを求めてSetWindowPosでサイズを変更します。)


bool CMyWnd::SBarInfo(bool notified) {

    char buff[BSIZE];    //最大256文字
    if(notified)
        wsprintf(buff, "%d年%d月%d日を選択しました", st.wYear, st.wMonth, st.wDay);
    else
        wsprintf(buff, "%d年%d月を表示", st.wYear, st.wMonth);
    SBar.SetText(0, buff);
    return TRUE;
}
//(解説:定数定義をしたBSIZEを使い、文字列バッファを宣言した後、notified引数に応じて日付の選択表示か、カレンダー月をステータスバーに選択表示します。)


bool CMyWnd::ModifyMenu(HMENU hMenu, int oldid, int newid, char* item) {

    MENUITEMINFO mi;
    ZeroMemory(&mi, sizeof(MENUITEMINFO));
    mi.cbSize = sizeof(MENUITEMINFO);
    mi.fMask = MIIM_TYPE | MIIM_ID;
    mi.fType = MFT_STRING;
    mi.fState = MFS_ENABLED;
    mi.wID = newid;
    mi.dwTypeData = (LPTSTR)item;
    mi.cch = strlen(item);
    if (!SetMenuItemInfo(hMenu, oldid, FALSE, &mi))
        MessageBox(m_hWnd, "メニューの変更に失敗しました", "エラー", MB_OK | MB_ICONERROR);
    return TRUE;
}
//(解説:ローカル変数の MENUITEMINFO構造体miをゼロで初期化して、cbSizeに構造体のサイズ、fMaskにタイプとIDを使うこと、fTypeにタイプは文字列であること、fStateで有効にすること、wIDにID(値-newid引数)とcchに引数itemの文字列長をセットして、SetMenuItemInfo関数でメニューアイテムを変更します。)


///////////////////////////////
//ユーザーダイアログの関数定義
//コントロール関数
///////////////////////////////
bool SPECDLG::OnButton() {

    char buff[BSIZE];

    int i;
    MonthCal_GetCurSel(hMC, &st);    //現在MCCで指定された年月日を取得
//(解説:ここでも定義された定数BSIZEを使って256バイトの文字列バッファ―buffと整数 i をローカル変数として宣言しています。また、外部SYSTEMTIME変数 stを使ってMCCの示す日時を取得します。)

    if(!GetDlgItemText(m_hWnd, IDC_EDIT1, buff, BSIZE - 1)) {
        MessageBox(m_hWnd, "年を読み取れませんでした", "エラー", MB_OK | MB_ICONERROR);
        specdlg.EndModal(TRUE);
        return FALSE;
    }
//(解説:まずエラー処理ですね。)

    else {
        i = atoi(buff);

//(解説:取得した年の文字データを整数化します。)
        if(i < 1753 || i > 9998) {    //仕様外データエラー
            MessageBox(m_hWnd, "この年は表示できません", "エラー", MB_OK | MB_ICONERROR);
            specdlg.EndModal(TRUE);
            return FALSE;
        }

//(解説:それが許容される数値外であればエラーとします。)
        else
            st.wYear = i;
    }

//(解説:そうでなければシステムタイム変数の年にセットします。)
    if(!GetDlgItemText(m_hWnd, IDC_EDIT2, buff, BSIZE - 1)) {
        MessageBox(m_hWnd, "月を読み取れませんでした", "エラー", MB_OK | MB_ICONERROR);
        specdlg.EndModal(TRUE);
        return FALSE;
    }
    else {
        i = atoi(buff);
        if(i < 1 || i > 12) {        //仕様外データエラー
            MessageBox(m_hWnd, "この月は表示できません", "エラー", MB_OK | MB_ICONERROR);
            specdlg.EndModal(TRUE);
            return FALSE;
        }
        else
            st.wMonth = i;
    }
//(解説:同じことを月についても同様に実施します。)

    MonthCal_SetCurSel(hMC, &st);    //指定年月での表示

//(解説:変更された年月にもとづいてMCCの表示を変更します。)
    specdlg.EndModal(TRUE);
//(解説:ダイアログを終了します。

    return TRUE;
}

 

今回は西暦の歴史と、それに基づくMCCコントロールの基本的な使い方についてサンプルのCalenderを使って解説しました。日付データの入出力(表示)を行う場合、MCCコントロールを使うととても簡単になることが分かります。
 

では、また次のネタが思いつきましたらお会いしましょう。

今回はメニュー処理です。

 

/////////////////////////////////
//主ウィンドウCMyWndの関数の定義
//メニュー項目、コントロール関数
/////////////////////////////////
bool CMyWnd::OnExplanation() {

    MessageBox(m_hWnd,
                "西暦はローマ歴から始まり、シーザーがエジプト歴による修正を命じた後、何度かの修正を経て、現在のグレゴリオ歴に至りました。\n1752年9月に修正を行った英国では過去の誤差修正(閏年調整)の為に9月2日の後が14日となりました。\n残念ながら、MCコントロールは1753年から9998年までしか表示できません。",
                "西暦解説", MB_OK | MB_ICONINFORMATION);
    return TRUE;
}
//(解説:これは西暦の解説したね。)

bool CMyWnd::OnVersion() {

    MessageBox(m_hWnd, "Calender Ver. 1.0", "バージョン情報",
                MB_OK | MB_ICONINFORMATION);
    return TRUE;
}
//(解説:定番のバージョン情報です。ダイアログを使わず、メッセージボックスで対応しています。)

bool CMyWnd::OnExit() {

    SendMessage(m_hWnd, WM_CLOSE, 0, 0);
    return TRUE;
}
//(解説:これも定番の終了処理です。)

bool CMyWnd::OnSpecym() {

    specdlg.DoModal(m_hWnd, "IDD_SPEC", specdlgProc);
    SBarInfo(FALSE);
    return TRUE;
}
//(解説:IDD_SPECダイアログを使った年月特定処理-Specifying year and month-とステータスバーへの情報書き込みです。)

bool CMyWnd::OnWeekno() {

    LONG lStyle;            //MCコントロールのスタイル変更用変数
    lStyle = GetWindowLong(hMC, GWL_STYLE);

//(解説:現行のMCCのスタイルをlStyle変数に取り込みます。)
    lStyle |= MCS_WEEKNUMBERS;
//(解説:現行のMCCのスタイルに週数表示スタイルを付加します。)
    SetWindowLong(hMC, GWL_STYLE, lStyle);
//(解説:現行のMCCのスタイルを新しいスタイルに変更します。)
    ModifyMenu(hMenu, IDM_WEEKNO, IDM_NOWEEKNO, "週数を表示しない(&W)");
    SetMCC(hMC);
//(解説:メニューアイテムを書き換えます。)
    InvalidateRect(m_hWnd, NULL, TRUE);
//(解説:ウィンドウ表示をリフレッシュします。)

    return TRUE;
}

bool CMyWnd::OnNotoday() {

    LONG lStyle;            //MCコントロールのスタイル変更用変数
    lStyle = GetWindowLong(hMC, GWL_STYLE);
//(解説:現行のMCCのスタイルをlStyle変数に取り込みます。)
    lStyle |= MCS_NOTODAY;
//(解説:本日表示をしないスタイルを付加します。)
    SetWindowLong(hMC, GWL_STYLE, lStyle);
//(解説:新しいスタイルに変更します。)
    ModifyMenu(hMenu, IDM_NOTODAY, IDM_TODAY, "今日を表示する(&T)");
    SetMCC(hMC);
//(解説:メニューアイテムを書き換えます。)
    InvalidateRect(m_hWnd, NULL, TRUE);
//(解説:ウィンドウ表示をリフレッシュします。)
    return TRUE;
}

bool CMyWnd::OnNoWeekno() {

    LONG lStyle;            //MCコントロールのスタイル変更用変数
    lStyle = GetWindowLong(hMC, GWL_STYLE);
//(解説:現行のMCCのスタイルをlStyle変数に取り込みます。)
    lStyle &= ~MCS_WEEKNUMBERS;
//(解説:週数表示スタイルをXORで除去します。)
    SetWindowLong(hMC, GWL_STYLE, lStyle);
//(解説:新しいスタイルに変更します。)
    ModifyMenu(hMenu, IDM_NOWEEKNO, IDM_WEEKNO, "週数を表示する(&W)");
    SetMCC(hMC);
//(解説:メニューアイテムを書き換えます。)
    InvalidateRect(m_hWnd, NULL, TRUE);
//(解説:ウィンドウ表示をリフレッシュします。)
    return TRUE;
}

bool CMyWnd::OnToday() {

    LONG lStyle;            //MCコントロールのスタイル変更用変数
    lStyle = GetWindowLong(hMC, GWL_STYLE);
//(解説:現行のMCCのスタイルをlStyle変数に取り込みます。)
    lStyle &= ~MCS_NOTODAY;
//(解説:本日表示スタイルをXORで除去します。)
    SetWindowLong(hMC, GWL_STYLE, lStyle);
//(解説:新しいスタイルに変更します。)
    ModifyMenu(hMenu, IDM_TODAY, IDM_NOTODAY, "今日を表示しない(&T)");
    SetMCC(hMC);
//(解説:メニューアイテムを書き換えます。)
    InvalidateRect(m_hWnd, NULL, TRUE);
//(解説:ウィンドウ表示をリフレッシュします。)
    return TRUE;
}

 

こうやって見ると西暦の解説の他は、メニュー処理は同様の処理のバリエーションだと分かります。

では具体的にどうやっているのか、は次回で。

今日は、メニューとユーザー定義関数は次回にして、CalenderProc.hのウィンドウメッセージ処理を解説します。

 

//////////////////////////////////////////
// CalenderProc.h
// Copyright (c) 04/24/2020 by BCCSkelton
//////////////////////////////////////////
#define        BSIZE 256
//(解説:定数定義です。)


/////////////////////////////////
//主ウィンドウCMyWndの関数の定義
//ウィンドウメッセージ関数
/////////////////////////////////
bool CMyWnd::OnCreate(WPARAM wParam, LPARAM lParam) {

    //コモンコントロールの初期化
    INITCOMMONCONTROLSEX ic;
    ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
    ic.dwICC = ICC_BAR_CLASSES | ICC_DATE_CLASSES;    //StatusBarとMonthCalenderコントロール
    InitCommonControlsEx(&ic);                        //必要なものだけをロード

    //MCコントロールの作成
    hMC = CreateWindowEx(0, MONTHCAL_CLASS, "",
        WS_VISIBLE | WS_CHILD | WS_BORDER | MCS_DAYSTATE,
        0, 0, 0, 0, m_hWnd, NULL, m_hInstance, NULL);
//(解説:CCTRLクラスを使ってもよかったのですが、ここはWin32APIで記述しています。)

    //ステータスバー登録-Init(hWnd, hIinstance, ID, (以下省略可)Style)
    SBar.Init(m_hWnd, m_hInstance, STATUSBAR);
    //ステータスバー文字列設定
    MonthCal_GetCurSel(hMC, &st);    //MCCからシステムタイムを取得
    SBarInfo(FALSE);                //システムタイムの日付表示(ユーザー関数)
//(解説:ステータスア―の初期設定で、ユーザー定義化数を使ってシステムタイムの日付を表示します。)

    //MCCの最小サイズを求め、MCCと主ウィンドウのサイズを設定
    SetMCC(hMC);
//(解説:MCCとウィンドウのサイズ初期化です。)

    //メニューハンドルの取得
    hMenu = GetMenu(m_hWnd);
//(解説:操作途中でメニューアイテムを変更するので、外部変数にメニューハンドルを取得しています。)

    return TRUE;
}

bool CMyWnd::OnNotify(WPARAM wParam, LPARAM lParam) {

    LPNMSELCHANGE lpSChange = (LPNMSELCHANGE)lParam;
    if (lpSChange->nmhdr.hwndFrom != hMC || lpSChange->nmhdr.code != MCN_SELCHANGE)
        //MCコントロールではないか、選択変更がなければウインドウズにコントロールを戻す。
        return FALSE;
    else {                                //MCCから変更通知が入った場合
        MonthCal_GetCurSel(hMC, &st);    //システムタイムを取得し、
        SBarInfo(TRUE);                    //指定日時をステータスバーに表示
        InvalidateRect(m_hWnd, NULL, TRUE);
        return TRUE;
    }
}
//(解説:コメントの通りです。InvalidateRectでウィンドウ表示をリフレッシュします。)

bool CMyWnd::OnSize(WPARAM wParam, LPARAM lParam) {

    //ステータスバーの自動調整
    SBar.AutoSize();
    //MCCのサイズ調整
    int wd, ht, margin = 10;        //ウィンドウクライアントエリアの余白
    RECT rc;                        //ウィンドウサイズ取得のための変数
    //ウィンドウに合わせてMCCサイズを調整
    GetClientRect(m_hWnd, &rc);        //ウィンドウクライアント領域のサイズ
    wd = rc.right - rc.left - margin * 2;
    ht = rc.bottom - rc.top - margin * 2;
    GetWindowRect(SBar.m_hWnd, &rc);    //ステータスバーのサイズ
    ht -= rc.bottom - rc.top;            //ステータスバーの高さを除算
    MoveWindo

//(解説:コメントの通りです。ウィンドウのサイズを変えると、それに合わせてMCCのサイズを同じマージンを取って変更します。)

    return TRUE;
}

bool CMyWnd::OnClose(WPARAM wParam, LPARAM lParam) {

    if(MessageBox(m_hWnd, "終了しますか?", "終了確認",
                    MB_YESNO | MB_ICONQUESTION) == IDYES)
        //処理をするとDestroyWindow、PostQuitMessageが呼ばれる
        return TRUE;
    else
        //そうでなければウィンドウではDefWindowProc関数をreturn、ダイアログではreturn FALSEとなる。
        return FALSE;
}

bool CMyWnd::OnDestroy(WPARAM, LPARAM) {

    return TRUE;
}

//(解説:終了処理はいつも通りです。)
 

では、また。

今日は日曜なので、手抜きして負荷の少ないCalender.cppを。(要すれば、SkeltonWizardの作成したファイルをそのまま使っています。)

 

//////////////////////////////////////////
// Calender.cpp
//Copyright (c) 04/24/2020 by BCCSkelton
//////////////////////////////////////////
#include    "Calender.h"
#include    "CalenderProc.h"

////////////////
// WinMain関数
////////////////
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine, int nCmdShow) {

    //2重起動防止
    if(!Calender.IsOnlyOne()) {
        //ここに2重起動時の処理を書く(下記は1例)
        HWND hWnd = FindWindow("MainWnd", "Calender");
        if(IsIconic(hWnd))
            ShowWindow(hWnd, SW_RESTORE);
        SetForegroundWindow(hWnd);
        return 0L;
    }

    //ウィンドウ登録 - Init(ClassName, hInstance, WndProc, "IDM_MAIN",
    //                        (以下省略可)MAKEINTRESOURCE(IDI_ICON), IDC_ARROW, Brush)
    Calender.Init("MainWnd", hInstance, SDIPROC, "IDM_MENU", MAKEINTRESOURCE(IDI_ICON));

    //ウィンドウ作成と表示-Create(WindowTitle, (以下省略可)Style,
    //                        ExStyle, hParent, hMenu, x, y, w, h)
    if(!Calender.Create("Calender"))
        return 0L;

    //メッセージループに入る
    return Calender.Loop();
}
 

閑話休題。今回は 少し最近の私事を書きましょう。

 

還暦を迎えた際に、自分のご褒美にと無理なく買える範囲で「ミッドシップスポーツカーフェチ」の私がロータスを探していて巡り合ったのが旧いポルシェ Boxster S。(Boxster = Boxer-水平対向-エンジン + Roadster-オープンカー)

以降サーキットや峠ドライブを満喫してきましたが、ちょうど年齢的に同じような車も運転手も不具合(幌開閉の不具合と、右手腱鞘炎および腰痛症)が発生し、ちゃんと乗り続けて修理してくれるオーナーへ送り出すことにしました。

頭ではわかっていても、また機械と人間といっても「別れは別れ」。今も時々一緒に走っていた際の光景が突然フラッシュバックして悲しい気持ちになります。

こんな気持ちを書き、どなたかに読んで(共感して)いただけるだけで少し癒されるのではないかと期待しています。

今回はCalender.hの解説をしますが、その前にプログラムの「ファイル」「西暦の解説」で述べている月次カレンダーコントロール(MCC)の限界を述べておきますね。

「西暦はローマ歴から始まり、シーザーがエジプト歴による修正を命じた後、何度かの修正を経て、現在のグレゴリオ歴に至りました。1752年9月に修正を行った英国では過去の誤差修正(閏年調整)の為に9月2日の後が14日となりました。残念ながら、MCコントロールは1753年から9998年までしか表示できません。,」(Got it? アラッチ?)

 

//////////////////////////////////////////
// Calender.h
// Copyright (c) 04/24/2020 by BCCSkelton
// Ref: http://www.kumei.ne.jp/c_lang/sdk2/sdk_181.htm
//////////////////////////////////////////
//BCCSkeltonのヘッダー-これに必要なヘッダーが入っている
#include    "BCCSkelton.h"
//リソースIDのヘッダー
#include    "ResCalender.h"
//(解説:SkeltonWizard通りの内容です。なお、プログラミングにあたり「猫でも」を参照したのでReferenceにしています。)

/////////////////////////////////////////////////////////////////////
//CMyWndクラスをCSDIクラスから派生させ、メッセージ用の関数を宣言する
/////////////////////////////////////////////////////////////////////
class CMyWnd : public CSDI
{
public:    //以下はコールバック関数マクロと関連している
    //2重起動防止用のMutex用ID名称
    CMyWnd(char* UName) : CSDI(UName) {}
    //メニュー項目、ダイアログコントロール関連
    bool OnExplanation();
    bool OnVersion();
    bool OnExit();
    bool OnSpecym();
    bool OnWeekno();
    bool OnNotoday();
    bool OnNoWeekno();
    bool OnToday();
    //ウィンドウメッセージ関連
    bool OnCreate(WPARAM, LPARAM);
    bool OnNotify(WPARAM, LPARAM);
    bool OnSize(WPARAM, LPARAM);
    bool OnClose(WPARAM, LPARAM);
    bool OnDestroy(WPARAM, LPARAM);
    //ユーザー定義関数
    bool SetMCC(HWND);
    bool SBarInfo(bool);
    bool ModifyMenu(HMENU, int, int, char*);
};
//(解説:メニューはリソースファイルの通りです。ウィンドウはSDIでサイズを変更するとMCCもサイズを変える仕様にして、どのように形態変化するかが分かるようにしています。あと、MCCの初期化やステータスバーへの情報表示や、メニューアイテムの変更などをユーザー定義しています。)


////////////////////////////////////////////////////////////////////////
//派生させたCMyWndクラスのインスタンスとコールバック関数(マクロ)の作成
//主ウィンドウはダイアログと違い、コールバック関数は一つしか作れない
////////////////////////////////////////////////////////////////////////
CMyWnd Calender("Calender");    //ウィンドウクラスインスタンスの生成

BEGIN_SDIMSG(Calender)    //ダイアログと違い、コールバック関数名を特定しない
    //メニュー項目、ダイアログコントロール関連
    ON_COMMAND(Calender, IDM_EXPLANATION, OnExplanation())
    ON_COMMAND(Calender, IDM_VERSION, OnVersion())
    ON_COMMAND(Calender, IDM_EXIT, OnExit())
    ON_COMMAND(Calender, IDM_SPECYM, OnSpecym())
    ON_COMMAND(Calender, IDM_WEEKNO, OnWeekno())
    ON_COMMAND(Calender, IDM_NOTODAY, OnNotoday())
    ON_COMMAND(Calender, IDM_NOWEEKNO, OnNoWeekno())
    ON_COMMAND(Calender, IDM_TODAY, OnToday())
    //ウィンドウメッセージ関連
    ON_CREATE(Calender)
    ON_NOTIFY(Calender)
    ON_SIZE(Calender)
    ON_CLOSE(Calender)
    ON_DESTROY(Calender)
END_WNDMSG
//(解説:ここはSkeltonWizardが作成した通りです。)


///////////////////////
//ステータスバーの作成
///////////////////////
CSBAR SBar;

//(解説:ステータスバーを追加します。)


///////////////////////
//仮想ウィンドウの作成
///////////////////////
CANVAS cvs;

//(解説:ウ~ン、最初はSDIウィンドウに書き込むつもりだったのでしょうか?このCANVASインスタンスは使用されていません。汗;)


///////////////////////////////////////////
// CDLGクラスからDLGDLGクラスを派生
// 複数の同一ダイアログ変数とダイアログも作
// れるが、一つのダイアログに一つの派生ダイ
// アログクラスを作成するのが基本
///////////////////////////////////////////
class SPECDLG : public CDLG {
public:
    bool OnButton();
};

//(解説:ユーザー入力用のダイアログです。決定ボタンを押すだけの処理しかありません。)


////////////////////////////////////////////////////////////////////////////
// SPECDLGクラスダイアログ変数の生成とそのコールバック関数(マクロ)を定義
// 複数同一クラスのダイアログを作成することを予期してコールバック関数を明記
////////////////////////////////////////////////////////////////////////////
SPECDLG specdlg;

BEGIN_MODALDLGMSG(specdlgProc, specdlg)    //第1引数がコールバック関数の名前
    ON_COMMAND(specdlg, IDC_BUTTON, OnButton())
END_DLGMSG

////////////////////
// ユーザー外部変数
////////////////////
static HWND hMC;        //MCコントロールのハンドル
static SYSTEMTIME st;    //MCコントロールから取得する為の変数
static HMENU hMenu;        //メニューハンドルの取得用変数

//(解説:ユーザー定義外部変数です。MCCのハンドル、システム時間用変数とメニュー変更用のメニューハンドル変数を宣言しています。)

 

日付取得コントロール、月次カレンダーコントロール(MCC)共に、BCCFormで標準装備していますが、単にカレンダーから日付を取るだけの日付取得コントロールよりも、見た目それだけでカレンダーのMCCの方が楽しい、ということでサンプルにはこっちを選んでいます。しかし、どちらも専用のクラスでカプセル化はしていないのでWin32APIをつかったプログラムとなっています。

基本仕様はメニュー付SDIクラスのウィンドウに//ステータスバーを使っています。

 

では、早速リソースファイルから見てゆきます。

//-----------------------------------------
//             BCCForm Ver 2.41
//    An Easy Resource Editor for BCC
//  Copyright (c) February 2002 by ysama
//-----------------------------------------
#include    "ResCalender.h"

//----------------------------------
// ダイアログ (IDD_DLG)
//----------------------------------
IDD_SPEC DIALOG DISCARDABLE 0, 0, 150, 78
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP | WS_DLGFRAME | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_SETFONT
CAPTION "指定"
FONT 8, "MS 明朝"
{
 CONTROL "", IDC_EDIT1, "EDIT", WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | ES_LEFT, 80, 12, 60, 12, WS_EX_CLIENTEDGE
 CONTROL "", IDC_EDIT2, "EDIT", WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | ES_LEFT, 80, 30, 60, 12, WS_EX_CLIENTEDGE
 CONTROL "決定", IDC_BUTTON, "BUTTON", WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON, 80, 52, 60, 18
 CONTROL "年(西暦4桁)", IDC_LABEL1, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 12, 12, 66, 12
 CONTROL "月(西暦2桁)", IDC_LABEL2, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 12, 30, 66, 12
}
//(解説:これは西暦で年と月の入力を求めるダイアログです。)

//-------------------------
// メニュー(IDM_MENU)
//-------------------------
IDM_MENU MENU DISCARDABLE
{
    POPUP "ファイル(&F)"
    {
        MENUITEM "西暦解説(&E)", IDM_EXPLANATION
        MENUITEM "バージョン情報(&V)", IDM_VERSION
        MENUITEM SEPARATOR
        MENUITEM "終了(&X)", IDM_EXIT
    }
    POPUP "表示(&V)"
    {
        MENUITEM "年月指定(&Y)", IDM_SPECYM
        MENUITEM "週数を表示(&W)", IDM_WEEKNO
        MENUITEM "本日を表示しない(&T)", IDM_NOTODAY
    }
}
//(解説:これはメインメニューです。最初の西暦解説はためになりました。また通常Helpにするバージョン情報はここにもtt来ています。また、表示の「年月指定」は上記のダイアログを使用し、下の「週数を表示」と「本日を表示しない」はMCCの表示オプション用です。)

//--------------------------
// イメージ(IDI_ICON)
//--------------------------
IDI_ICON    ICON    DISCARDABLE    "C:\Users\ysama\Programing\Borland C++\Calender\Icon.ico"

 

ではまた次回。

夏に入るころから、右手腱鞘炎で右手が使えなくなり、更に腰痛症も発症して寝返りすら打てなくなるという「芋虫生活」をしていて、大分更新をさぼってしまいました。ごめんなさい。

 

さて、日常生活で使うソフトを開発するという点では、結構そのような需要は少なく、(プログラム開発系以外の)自作ソフトで使っているのは「銀行情報一覧」(*非公開-簡易暗号化を使っているので公開できません。)、「IDとパスワード管理」(*非公開)、「FileHandler」、「RTEdutir」、「TextSpeech」くらいですね。なので、開発しながらのブログは未だ考え中。

 

ということで、またサンプルの紹介ということで暦(カレンダー)について書いてみます。

 

大昔、MSX PCを買って、MSX BASICを使っているときに特定の日付から閏年か否か、とか曜日を算出する初心者向けのプログラムがありました。以下は閏年判定のC(++)サンプルです。

 

/* Check and judge if the year is a leap year */
#include    <stdio.h>

/* Function prototype */
int chkleap(int);

void main() {

    int    y, i;
    char*    ans;
    
    printf("年を入力してください:");
    scanf("%d4", &y);
    ans = chkleap(y) ? "leap" : "non-leap";
    printf("The year is a %s year.\n\n", ans);
    
    printf("For your reference, following are the leap years since 1954.\n");
    for(i = 1954; i <2024; i++) {
        if(chkleap(i)) {
            printf("%4d ", i);
            if(i % 5 == 2)
                printf("\n");
        }
    }     
}

int chkleap(int y) {
    /* 閏年(leap year)は、4で割り切れ且つ100で割り切れないか、または400で割り切れるか */
    return (!(y % 4) && (y % 100)) || !(y % 400) ? 1 : 0;
}

 

1992年にBorland C++の入門書を買った際にも、「カレンダーをウィンドウ上に描画し、印刷できる」サンプルが載っていました。しかし、現在ではわざわざ印刷するなんて、しないですよね?また、Windowsでは「日付取得コントロール」や「月次カレンダーコントロール(MCコントロール)」があるので、このようなアルゴリズム自体を考える必要もなくなってしまいました。

と、いうことで、(実用性はありませんが)MCコントロールの習作ということで、サンプルにCalenderを載せていますので次回以降で解説します。