今回は、タイマーとデバイスコンテキストを使って別の時計を作ろうと思います。
MyProject_res.rcをRisohEditorで開いて下さい。 これからコマンドIDの「ID_EXIT」と「ID_TEST」を追加します。
「リソースIDの一覧」で「追加...」を選んで下さい。 「リソースIDの追加」ダイアログが表示されますね。 「IDの名前」に「ID_EXIT」と入力して、「自動」ボタンを押して下さい。 次のようになりますね。
OKボタンをクリックします。同様にして「ID_TEST」を追加して下さい。 「リソースIDの一覧」が次のようになりますね。
次は、メニューを編集します。RisohEditorの左側のツリービューで 「RT_MENU」→「1」→「日本語 (日本) (1041)」 を順番に開きます。 「日本語 (日本) (1041)」をクリックして、リソーステキストを次のようにします。
LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT
1 MENU
{
POPUP "ファイル(&F)"
{
MENUITEM "テスト(&T)", ID_TEST
MENUITEM SEPARATOR
MENUITEM "終了(&X)", ID_EXIT
}
}
編集が終わったら「コンパイル」して下さい。
さらに、アクセスキーを追加します。アクセスキー (Accelerators)というのは、 特定のキーの組み合わせを入力すると、コマンドが発生する仕組みです。
RisohEditorの「編集」メニューから「追加」→「アクセスキーを追加」を選んで下さい。 「リソースの追加」ダイアログが表示されますので、「リソースの名前」に「1」(いち)を入力して、 OKボタンをクリックして下さい。
名前が1のRT_ACCELERATORが追加されます。
RT_ACCELERATORの「日本語 (日本) (1041)」をダブルクリックして下さい。 次のような「アクセスキーの編集」ダイアログが表示されます。
左下の「すべて削除」ボタンをクリックして下さい。中身が消えましたね。 その後、右上の「追加」ボタンをクリックして下さい。「キーの追加」ダイアログが表示されますので、 「キー:」に「"T"」(二重引用符、ティー、二重引用符)と入力して、 「コマンドID:」に「ID_TEST」と入力して下さい。 チェックボックス「CONTROL」にチェックを入れます。
OKボタンをクリックします。次のようになりますね。
OKボタンをクリックします。 MyProject_res.rcにエクスポートして下さい。これでリソースの編集は終わりです。
MyProject.cppを開いて下さい。 "resource.h"と<cmath>のインクルードを追加して下さい。
#include "targetver.h"
#include "MWindowBase.hpp"
#include "resource.h"
#include <cmath>
<cmath>は、C言語の数学関数を使うためにインクルードします。 アナログ時計を描くには、三角関数が必要だからです。
OnCommandメソッドを次のようにします。
void OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
{
switch (id)
{
case ID_EXIT:
DestroyWindow(hwnd);
break;
case ID_TEST:
MsgBoxDx(TEXT("TEST"), MB_ICONINFORMATION);
break;
}
}
MsgCrackでWM_TIMERのテキストをコピーしてMMainWnd内部に貼り付けます。 貼り付けたOnTimerを次のようにします。
void OnTimer(HWND hwnd, UINT id)
{
InvalidateRect(hwnd, NULL, TRUE);
}
InvalidateRect APIは、再描画を発生させる関数です。 OnCreateメソッドを次のようにします。
BOOL OnCreate(HWND hwnd, LPCREATESTRUCT lpCreateStruct)
{
SetTimer(hwnd, 999, 500, NULL);
return TRUE;
}
WindowProcDxにWM_TIMER/OnTimerを登録して下さい。
virtual LRESULT CALLBACK
WindowProcDx(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
HANDLE_MSG(hwnd, WM_CREATE, OnCreate);
HANDLE_MSG(hwnd, WM_COMMAND, OnCommand);
HANDLE_MSG(hwnd, WM_DESTROY, OnDestroy);
HANDLE_MSG(hwnd, WM_PAINT, OnPaint);
HANDLE_MSG(hwnd, WM_TIMER, OnTimer);
default:
return DefaultProcDx();
}
}
これで、0.5秒ごとに再描画されます。 次は、OnPaintメソッドに時計を描画する処理を書きます。
void DrawClockHand(HDC hDC, INT cx, INT cy, LONG nRadius, double e60thValue)
{
const double PI = 3.14159;
INT x = cx + nRadius * std::cos((PI / 2) - e60thValue * (2 * PI) / 60);
INT y = cy - nRadius * std::sin((PI / 2) - e60thValue * (2 * PI) / 60);
MoveToEx(hDC, cx, cy, NULL);
LineTo(hDC, x, y);
}
void OnPaint(HWND hwnd)
{
RECT rc;
GetClientRect(hwnd, &rc);
PAINTSTRUCT ps;
if (HDC hDC = BeginPaint(hwnd, &ps))
{
FillRect(hDC, &rc, GetStockBrush(GRAY_BRUSH));
INT cx = (rc.left + rc.right) / 2;
INT cy = (rc.top + rc.bottom) / 2;
SIZE siz = SizeFromRectDx(&rc);
LONG nDiameter;
if (siz.cx < siz.cy)
nDiameter = siz.cx;
else
nDiameter = siz.cy;
LONG nRadius = nDiameter / 2;
SYSTEMTIME st;
GetLocalTime(&st);
Ellipse(hDC, cx - nRadius, cy - nRadius, cx + nRadius, cy + nRadius);
{
TCHAR sz[64];
StringCchPrintf(sz, _countof(sz), TEXT("%02u:%02u:%02u"), st.wHour, st.wMinute, st.wSecond);
SIZE siz;
GetTextExtentPoint32(hDC, sz, lstrlen(sz), &siz);
TextOut(hDC, cx - siz.cx / 2, cy + nRadius / 2, sz, lstrlen(sz));
}
if (HPEN hPen = CreatePen(PS_SOLID, 8, RGB(255, 0, 0)))
{
HPEN hPenOld = SelectPen(hDC, hPen);
DrawClockHand(hDC, cx, cy, nRadius * 2 / 3, st.wHour * (60 / 12) + st.wMinute / 60.0);
SelectPen(hDC, hPenOld);
DeleteObject(hPen);
}
if (HPEN hPen = CreatePen(PS_SOLID, 4, RGB(0, 255, 0)))
{
HPEN hPenOld = SelectPen(hDC, hPen);
DrawClockHand(hDC, cx, cy, nRadius * 8 / 9, st.wMinute + st.wSecond / 60.0);
SelectPen(hDC, hPenOld);
DeleteObject(hPen);
}
SelectPen(hDC, GetStockPen(BLACK_PEN));
DrawClockHand(hDC, cx, cy, nRadius, st.wSecond);
EndPaint(hwnd, &ps);
}
}
ややこしいですが、少しずつかみ砕いていきましょう。
DrawClockHandメソッドは、時計の針を書くメソッドです。 三角関数、std::cosとstd::sinを使って針の位置を計算しています。 MoveToExとLineToで実際に針を描いています。
void DrawClockHand(HDC hDC, INT cx, INT cy, LONG nRadius, double e60thValue)
{
const double PI = 3.14159;
INT x = cx + nRadius * std::cos((PI / 2) - e60thValue * (2 * PI) / 60);
INT y = cy - nRadius * std::sin((PI / 2) - e60thValue * (2 * PI) / 60);
MoveToEx(hDC, cx, cy, NULL);
LineTo(hDC, x, y);
}
OnPaintメソッドを見てみましょう。
FillRect(hDC, &rc, GetStockBrush(GRAY_BRUSH));
FillRect APIは、指定された領域を塗りつぶす関数です。 ブラシというものでクライアント領域全体を塗りつぶしています。 GetStockBrush(GRAY_BRUSH)は、グレー(灰色)のストックブラシです。 ストックブラシはDeleteObjectで解放する必要はありません。
INT cx = (rc.left + rc.right) / 2;
INT cy = (rc.top + rc.bottom) / 2;
(cx, cy)は、クライアント領域の中心点です。
SIZE siz = SizeFromRectDx(&rc);
SIZEはサイズを表す構造体です。SizeFromRectDxは、RECTからSIZEを求めるMZC4の関数です。
LONG nDiameter;
if (siz.cx < siz.cy)
nDiameter = siz.cx;
else
nDiameter = siz.cy;
LONG nRadius = nDiameter / 2;
縦と横のうち、短い方を直径nDiameterに設定しています。半径nRadiusは、直径nDiameterの半分です。
SYSTEMTIME st;
GetLocalTime(&st);
GetLocalTime APIは、ローカルの日時を取得する関数です。
Ellipse(hDC, cx - nRadius, cy - nRadius, cx + nRadius, cy + nRadius);
Ellipse APIで円を描いています。
{
TCHAR sz[64];
StringCchPrintf(sz, _countof(sz), TEXT("%02u:%02u:%02u"), st.wHour, st.wMinute, st.wSecond);
SIZE siz;
GetTextExtentPoint32(hDC, sz, lstrlen(sz), &siz);
TextOut(hDC, cx - siz.cx / 2, cy + nRadius / 2, sz, lstrlen(sz));
}
ここは、デジタル時計を描いています。 GetTextExtentPoint32 APIは、テキストのサイズを取得する関数です。 lstrlen APIは、テキストの長さを取得する関数です。 TextOut APIは、テキストを描画する関数です。
if (HPEN hPen = CreatePen(PS_SOLID, 8, RGB(255, 0, 0)))
{
HPEN hPenOld = SelectPen(hDC, hPen);
DrawClockHand(hDC, cx, cy, nRadius * 2 / 3, st.wHour * (60 / 12) + st.wMinute / 60.0);
SelectPen(hDC, hPenOld);
DeleteObject(hPen);
}
ここでは、時計の時針を描いています。
if (HPEN hPen = CreatePen(PS_SOLID, 4, RGB(0, 255, 0)))
{
HPEN hPenOld = SelectPen(hDC, hPen);
DrawClockHand(hDC, cx, cy, nRadius * 8 / 9, st.wMinute + st.wSecond / 60.0);
SelectPen(hDC, hPenOld);
DeleteObject(hPen);
}
ここでは、時計の分針を描いています。
SelectPen(hDC, GetStockPen(BLACK_PEN));
DrawClockHand(hDC, cx, cy, nRadius, st.wSecond);
ここでは、時計の秒針を描いています。GetStockPen(BLACK_PEN)は、太さ1の黒いペンです。 ストックペンはDeleteObjectで解放する必要はありません。
ビルドして動作を確認しましょう。
メニューが日本語になりましたね。Ctrl+Tを押したり、「ファイル」→「テスト」を選ぶと、ID_TESTコマンドが実行されます。
時間に余裕があれば、自由に改造してみて下さい。