今回は、簡単なストップウォッチ機能を付けます。
クライアント領域を右クリックすると「ストップウォッチ」というメニュー項目がありまます。
メニューから「ストップウォッチ」を選択すると、左の図のような
ダイアログボックスが出現します。
「スタート」ボタンを押すと本体の方で、計測時間が刻々と表示されます。 また、「スタート」ボタンを押すと「ストップ」ボタンが使用可能となります。 「ストップ」ボタンを押すと計測が止まり、「リセット」ボタンが使用可能となります。
「リセット」ボタンを押すと表示が「0:00:00-000」となります。
左の図は、ストップウォッチで計測中のものです。(一応1/1000秒単位で
表示されますが、それほど精密ではありません。)
では、プログラムを見てみましょう。
// clock08.rcの一部
/////////////////////////////////////////////////////////////////////////////
//
// Menu
//
MYMENU MENU
BEGIN
POPUP "ダミーです"
BEGIN
POPUP "ファイル(&F)"
BEGIN
MENUITEM "終了(&X)...", IDM_END
END
POPUP "オプション(&O)"
BEGIN
MENUITEM "背景色(&B)...", IDM_BACKGROUND
MENUITEM "文字色(&T)...", IDM_TEXT
MENUITEM "文字盤の色(&P)...", IDM_PLATE
MENUITEM "長針の色(&L)...", IDM_LONG
MENUITEM "短針の色(&S)...", IDM_SHORT
MENUITEM "秒針の色(&O)...", IDM_SECOND
END
POPUP "アラーム(&L)"
BEGIN
MENUITEM "時刻設定(&S)...", IDM_ALARMSET
END
POPUP "タイマー(&T)"
BEGIN
MENUITEM "タイマーセット(&T)...", IDM_TIMER
MENUITEM "ストップウォッチ(&S)...", IDM_STOPWATCH
END
POPUP "ストップウォッチ(&S)"
BEGIN
MENUITEM "ストップウォッチ(&S)...", IDM_STOPWATCH
END
END
END
/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//
CAT BITMAP "bitmap1.bmp"
MASK BITMAP "bmp00001.bmp"
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
MYALARM DIALOGEX 0, 0, 115, 73
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION |
WS_SYSMENU
CAPTION "アラーム設定"
FONT 10, "MS ゴシック", 400, 0, 0x80
BEGIN
DEFPUSHBUTTON "OK",IDOK,7,52,50,14
PUSHBUTTON "キャンセル",IDCANCEL,58,52,50,14
CONTROL "アラームをセットする",IDC_CHECK1,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,7,7,101,15
CONTROL "",IDC_DATETIMEPICKER1,"SysDateTimePick32",
DTS_RIGHTALIGN | DTS_UPDOWN | WS_TABSTOP,30,25,54,17
END
MYTIMER DIALOGEX 0, 0, 123, 54
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION |
WS_SYSMENU
CAPTION "タイマー設定"
FONT 10, "MS ゴシック", 400, 0, 0x80
BEGIN
DEFPUSHBUTTON "スタート",IDOK,7,33,50,14
PUSHBUTTON "ストップ",IDCANCEL,65,33,50,14
CONTROL "",IDC_DATETIMEPICKER1,"SysDateTimePick32",
DTS_RIGHTALIGN | DTS_UPDOWN | WS_TABSTOP,35,7,53,19
END
MYSTOPWATCH DIALOGEX 0, 0, 131, 54
STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION |
WS_SYSMENU
CAPTION "ストップウォッチ"
FONT 10, "MS ゴシック", 400, 0, 0x80
BEGIN
DEFPUSHBUTTON "スタート",IDOK,7,7,50,14
PUSHBUTTON "閉じる",IDCANCEL,71,31,50,14
PUSHBUTTON "ストップ",IDC_STOP,71,7,50,14
PUSHBUTTON "リセット",IDC_RESET,7,31,50,14
END
/////////////////////////////////////////////////////////////////////////////
//
// WAVE
//
BELL WAVE "ringin.wav"
リソース・スクリプトですが、メニューに「ストップウォッチ」「ストップウォッチ」が増えました。また、ストップウォッチ用のダイアログが増えました。
// clock08.cpp
#define hParentKey HKEY_CURRENT_USER
#define lpszSubKey "Software\\Kumei\\Clock"
#define PAI 3.14159
#define CLOCK_WIDTH 250 //時計全体のウィンドウ幅
#define CLOCK_HEIGHT 60 //高さ
#define MYTIMER 1 //タイマーID
#define MYSTOP 2
#include <windows.h>
#include <windowsx.h>
#include <math.h>
#include <commctrl.h>
#include <Mmsystem.h>
#include "resource.h"
typedef struct MYDATA {
COLORREF cr_bg; //背景色
COLORREF cr_txt; //文字色
COLORREF cr_plate; //文字盤の色
COLORREF cr_short; //短針の色
COLORREF cr_long; //長針の色
COLORREF cr_second; //秒針の色
int x;
int y;
char szAlarmSet[8];
char szAlarmTime[16];
char szTimerTime[16];
char szStopWatch[8]; //ストップウォッチモードかどうか
char szStopTime[16]; //ストップウォッチモードの時の時間
} INIDATA;
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK MyAlarmProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK MyTimerProc(HWND, UINT, WPARAM, LPARAM);
BOOL CALLBACK MyStopWatchProc(HWND, UINT, WPARAM, LPARAM);
ATOM InitApp(HINSTANCE);
BOOL InitInstance(HINSTANCE, int);
HFONT SetMyFont(LPCTSTR, int);
BOOL GetMyColor(HWND, COLORREF *, COLORREF);
void GetInitialSettings(INIDATA *);
BOOL GetDataDWORD(char *, DWORD *);
BOOL GetDataString(char *, char *);
BOOL SetInitialSettings(INIDATA);
BOOL SetDataDWORD(char *, DWORD);
BOOL SetDataString(char *, char *);
BOOL ShowClock(HDC, char *, INIDATA);
BOOL GetHMS(char *, int *, int *, int *);
BOOL ShowBitmap(HDC, INIDATA);
BOOL MyTimerShow(HDC, int, int, INIDATA, DWORD);
BOOL SetHMS(char *, DWORD);
BOOL SetStopHMS(char *, DWORD);
char szClassName[] = "clock08"; //ウィンドウクラス
char szAppName[] = "猫クロック"; //アプリケーション名
HINSTANCE hInst;
新しくストップウォッチ用タイマーIDのMYSTOPをdefineしました。INIDATA構造体のメンバが増えました。
ストップウォッチ用ダイアログボックスのプロシージャが増えました。
また、システム時間からストップウォッチの時間の文字列を作るSetStopHMS関数が増えました。
int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
LPSTR lpsCmdLine, int nCmdShow)
{
MSG msg;
BOOL bRet;
hInst = hCurInst;
if (!InitApp(hCurInst))
return FALSE;
if (!InitInstance(hCurInst, nCmdShow))
return FALSE;
while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0) {
if (bRet == -1) {
break;
} else {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;
}
//ウィンドウ・クラスの登録
ATOM InitApp(HINSTANCE hInst)
{
WNDCLASSEX wc;
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc; //プロシージャ名
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInst;//インスタンス
wc.hIcon = (HICON)LoadImage(NULL,
MAKEINTRESOURCE(IDI_APPLICATION),
IMAGE_ICON,
0,
0,
LR_DEFAULTSIZE | LR_SHARED);
wc.hCursor = (HCURSOR)LoadImage(NULL,
MAKEINTRESOURCE(IDC_ARROW),
IMAGE_CURSOR,
0,
0,
LR_DEFAULTSIZE | LR_SHARED);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL; //メニュー名
wc.lpszClassName = (LPCSTR)szClassName;
wc.hIconSm = (HICON)LoadImage(NULL,
MAKEINTRESOURCE(IDI_APPLICATION),
IMAGE_ICON,
0,
0,
LR_DEFAULTSIZE | LR_SHARED);
return (RegisterClassEx(&wc));
}
//ウィンドウの生成
BOOL InitInstance(HINSTANCE hInst, int nCmdShow)
{
HWND hWnd;
hWnd = CreateWindowEx(WS_EX_TOPMOST, //拡張ウィンドウスタイル
szClassName,
"猫でもわかるWindowsプログラミング", //タイトルバーにこの名前が表示されます
WS_POPUP, //ウィンドウの種類
0, //X座標
0, //Y座標
CLOCK_WIDTH, //幅
CLOCK_HEIGHT, //高さ
NULL, //親ウィンドウのハンドル、親を作るときはNULL
NULL, //メニューハンドル、クラスメニューを使うときはNULL
hInst, //インスタンスハンドル
NULL);
if (!hWnd)
return FALSE;
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
これらの関数に変更はありません。
//ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp)
{
int id, x, y;
SYSTEMTIME st;
PAINTSTRUCT ps;
HDC hdc;
HFONT hFont;
SIZE s;
HRGN hRgn, hRgn1, hRgn2, hRound1Rgn, hRound2Rgn, hRectRgn;
HBRUSH hBrush;
HMENU hMenu, hSubMenu;
POINT pt;
COLORREF cr;
static INIDATA inidata;
RECT rc;
char szYobi[8];
static char szBuf[64], szBuf2[64]; //時刻表示用
INITCOMMONCONTROLSEX ic;
static BOOL bTimer; //タイマ表示かどうか
static DWORD dwStartMs;
MMTIME mm;
switch (msg) {
case WM_CREATE:
ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
ic.dwICC = ICC_DATE_CLASSES;
InitCommonControlsEx(&ic);
GetInitialSettings(&inidata);
strcpy(inidata.szStopTime, "00:00:00");
MoveWindow(hWnd,
inidata.x,
inidata.y,
CLOCK_WIDTH,
CLOCK_HEIGHT,
TRUE);
SetTimer(hWnd, MYTIMER, 1000, NULL);
hRgn = CreateRectRgn(0, 0, 1, 1);
hRgn1 = CreateRectRgn(0, 0, 1, 1);
hRgn2 = CreateRectRgn(0, 0,1, 1);
hRound1Rgn = CreateEllipticRgn(0, 0, CLOCK_HEIGHT, CLOCK_HEIGHT);
hRectRgn = CreateRectRgn(CLOCK_HEIGHT / 2,
0,
CLOCK_WIDTH - CLOCK_HEIGHT / 2,
CLOCK_HEIGHT);
CombineRgn(hRgn1, hRound1Rgn, hRectRgn, RGN_OR);
hRound2Rgn = CreateEllipticRgn(CLOCK_WIDTH - CLOCK_HEIGHT,
0,
CLOCK_WIDTH,
CLOCK_HEIGHT);
CombineRgn(hRgn2, hRound2Rgn, hRectRgn, RGN_OR);
CombineRgn(hRgn, hRgn1, hRgn2, RGN_OR);
SetWindowRgn(hWnd, hRgn, TRUE);
DeleteObject(hRound1Rgn);
DeleteObject(hRound2Rgn);
DeleteObject(hRectRgn);
DeleteObject(hRgn1);
DeleteObject(hRgn2);
break;
case WM_RBUTTONDOWN:
pt.x = LOWORD(lp);
pt.y = HIWORD(lp);
hMenu = LoadMenu(hInst, "MYMENU");
hSubMenu = GetSubMenu(hMenu, 0);
ClientToScreen(hWnd, &pt);
TrackPopupMenu(hSubMenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hWnd, NULL);
DestroyMenu(hMenu);
break;
case WM_LBUTTONDOWN:
PostMessage(hWnd, WM_NCLBUTTONDOWN, (WPARAM)HTCAPTION, lp);
break;
case WM_TIMER:
if (wp != MYTIMER)
return DefWindowProc(hWnd, msg, wp, lp);
GetLocalTime(&st);
wsprintf(szBuf, "%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);
switch (st.wDayOfWeek) {
case 0:
strcpy(szYobi, "Sun");
break;
case 1:
strcpy(szYobi, "Mon");
break;
case 2:
strcpy(szYobi, "Tue");
break;
case 3:
strcpy(szYobi, "Wed");
break;
case 4:
strcpy(szYobi, "Thu");
break;
case 5:
strcpy(szYobi, "Fri");
break;
case 6:
strcpy(szYobi, "Sat");
break;
}
wsprintf(szBuf2, "%d/%02d/%02d(%s)", st.wYear, st.wMonth, st.wDay, szYobi);
if (strcmp(inidata.szAlarmSet, "Yes") == 0 && strcmp(szBuf, inidata.szAlarmTime) == 0) {
PlaySound("BELL", hInst, SND_RESOURCE | SND_ASYNC | SND_LOOP);
MessageBox(hWnd, "時間ですよ", szAppName, MB_OK);
PlaySound(NULL, hInst, SND_PURGE);
}
InvalidateRect(hWnd, NULL, TRUE);
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
hBrush = CreateSolidBrush(inidata.cr_bg);
SelectObject(hdc, hBrush);
PatBlt(hdc, 0, 0, CLOCK_WIDTH, CLOCK_HEIGHT, PATCOPY);
ShowBitmap(hdc, inidata);
hFont = SetMyFont("MS ゴシック", 30);
SelectObject(hdc, hFont);
GetTextExtentPoint32(hdc, szBuf, (int)strlen(szBuf), &s);
x = (CLOCK_WIDTH - s.cx) / 2 + 4;
y = (CLOCK_HEIGHT - s.cy) / 2 + 10;
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, inidata.cr_txt);
if (strcmp(inidata.szStopWatch, "Yes") == 0) {
hFont = SetMyFont("MS ゴシック", 24);
SelectObject(hdc, hFont);
GetTextExtentPoint32(hdc, szBuf, (int)strlen(szBuf), &s);
x = (CLOCK_WIDTH - s.cx) / 2;
y = (CLOCK_HEIGHT - s.cy) / 2 + 10;
TextOut(hdc, x, y, inidata.szStopTime, (int)strlen(inidata.szStopTime));
DeleteObject(hFont);
} else {
hFont = SetMyFont("MS ゴシック", 30);
SelectObject(hdc, hFont);
GetTextExtentPoint32(hdc, szBuf, (int)strlen(szBuf), &s);
x = (CLOCK_WIDTH - s.cx) / 2 + 4;
y = (CLOCK_HEIGHT - s.cy) / 2 + 10;
TextOut(hdc, x, y, szBuf, (int)strlen(szBuf));
DeleteObject(hFont);
}
hFont = SetMyFont("MS ゴシック", 16);
SelectObject(hdc, hFont);
GetTextExtentPoint32(hdc, szBuf2, (int)strlen(szBuf2), &s);
x = (CLOCK_WIDTH - s.cx) / 2 + 4;
if (!bTimer) {
TextOut(hdc, x, 4, szBuf2, (int)strlen(szBuf2));
} else {
if (MyTimerShow(hdc, x, 4, inidata, dwStartMs)) {
bTimer = FALSE;
PlaySound("BELL", hInst, SND_RESOURCE | SND_ASYNC | SND_LOOP);
MessageBox(hWnd, "時間ですよ", szAppName, MB_OK);
PlaySound(NULL, hInst, SND_PURGE);
}
}
DeleteObject(hFont);
DeleteObject(hBrush);
ShowClock(hdc, szBuf, inidata);
EndPaint(hWnd, &ps);
break;
case WM_COMMAND:
switch (LOWORD(wp)) {
case IDM_END:
SendMessage(hWnd, WM_CLOSE, 0, 0);
break;
case IDM_BACKGROUND:
if (GetMyColor(hWnd, &cr, inidata.cr_bg)) {
inidata.cr_bg = cr;
}
break;
case IDM_TEXT:
if (GetMyColor(hWnd, &cr, inidata.cr_txt)) {
inidata.cr_txt = cr;
}
break;
case IDM_PLATE:
if (GetMyColor(hWnd, &cr, inidata.cr_plate)) {
inidata.cr_plate = cr;
}
break;
case IDM_SHORT:
if (GetMyColor(hWnd, &cr, inidata.cr_short)) {
inidata.cr_short = cr;
}
break;
case IDM_LONG:
if (GetMyColor(hWnd, &cr, inidata.cr_long)) {
inidata.cr_long = cr;
}
break;
case IDM_SECOND:
if (GetMyColor(hWnd, &cr, inidata.cr_second)) {
inidata.cr_second = cr;
}
break;
case IDM_ALARMSET:
DialogBoxParam(hInst, "MYALARM", hWnd, (DLGPROC)MyAlarmProc, (LPARAM)&inidata);
break;
case IDM_TIMER:
if (DialogBoxParam(hInst,
"MYTIMER",
hWnd,
(DLGPROC)MyTimerProc,
(LPARAM)&inidata) == IDOK) {
bTimer = TRUE;
memset(&mm, 0, sizeof(MMTIME));
mm.wType = TIME_MS;
timeGetSystemTime(&mm, sizeof(MMTIME));
dwStartMs = mm.u.ms;
} else {
bTimer = FALSE;
}
break;
case IDM_STOPWATCH:
DialogBoxParam(hInst, "MYSTOPWATCH", hWnd, (DLGPROC)MyStopWatchProc, (LPARAM)&inidata);
break;
}
break;
case WM_CLOSE:
id = MessageBox(hWnd,
"終了してもよろしいですか",
szAppName,
MB_YESNO | MB_ICONQUESTION);
if (id == IDYES) {
GetWindowRect(hWnd, &rc);
inidata.x = rc.left;
inidata.y = rc.top;
DestroyWindow(hWnd);
}
break;
case WM_DESTROY:
SetInitialSettings(inidata);
KillTimer(hWnd, MYTIMER);
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, msg, wp, lp));
}
return 0;
}
メインウィンドウのプロシージャです。WM_PAINTメッセージが来た時の処理がちょっと増えました。
inidata.szStopWatchが"Yes"(現在ストップウォッチモードである)の時と そうでない時で、表示する内容を変えています。
ストップウォッチモードの時は、フォントサイズを小さめにして、 inidata.szStopTimeを表示します。
メニューからIDM_STOPWATCHが選択されたら、ダイアログを出します。
HFONT SetMyFont(LPCTSTR face, int h)
{
HFONT hFont;
hFont = CreateFont(h, //フォント高さ
0, //文字幅
0, //テキストの角度
0, //ベースラインとx軸との角度
FW_REGULAR, //フォントの重さ(太さ)
FALSE, //イタリック体
FALSE, //アンダーライン
FALSE, //打ち消し線
SHIFTJIS_CHARSET, //文字セット
OUT_DEFAULT_PRECIS, //出力精度
CLIP_DEFAULT_PRECIS,//クリッピング精度
PROOF_QUALITY, //出力品質
FIXED_PITCH | FF_MODERN,//ピッチとファミリー
face); //書体名
return hFont;
}
BOOL GetMyColor(HWND hWnd, COLORREF *lpcr, COLORREF org_cr)
{
CHOOSECOLOR cc;
static DWORD dwCustColors[16];
cc.lStructSize = sizeof(CHOOSECOLOR);
cc.hwndOwner = hWnd;
cc.lpCustColors = dwCustColors;
cc.rgbResult = org_cr;
cc.Flags = CC_RGBINIT;
if (ChooseColor(&cc)) {
*lpcr = cc.rgbResult;
return TRUE;
}
return FALSE;
}
これらの関数に変更はありません。
void GetInitialSettings(INIDATA *lpini)
{
DWORD dwData;
char szData[32];
if (GetDataDWORD("background-color", &dwData)) {
lpini->cr_bg = (COLORREF)dwData;
} else {
lpini->cr_bg = RGB(0, 255, 255);
}
if (GetDataDWORD("text-color", &dwData)) {
lpini->cr_txt = (COLORREF)dwData;
} else {
lpini->cr_txt = RGB(0, 0, 0);
}
if (GetDataDWORD("x", &dwData)) {
lpini->x = (int)dwData;
} else {
lpini->x = 0;
}
if (GetDataDWORD("y", &dwData)) {
lpini->y = (int)dwData;
} else {
lpini->y = 0;
}
if (GetDataDWORD("short-color", &dwData)) {
lpini->cr_short = (COLORREF)dwData;
} else {
lpini->cr_short = RGB(0, 0, 255);
}
if (GetDataDWORD("long-color", &dwData)) {
lpini->cr_long = (COLORREF)dwData;
} else {
lpini->cr_long = RGB(0, 0, 0);
}
if (GetDataDWORD("second-color", &dwData)) {
lpini->cr_second = (COLORREF)dwData;
} else {
lpini->cr_second = RGB(255, 0, 0);
}
if (GetDataDWORD("plate-color", &dwData)) {
lpini->cr_plate = (COLORREF)dwData;
} else {
lpini->cr_plate = RGB(0, 255, 255);
}
if (GetDataString("alarm-set", szData)) {
strcpy(lpini->szAlarmSet, szData);
} else {
strcpy(lpini->szAlarmSet, "No");
}
if (GetDataString("alarm-time", szData)) {
strcpy(lpini->szAlarmTime, szData);
} else {
strcpy(lpini->szAlarmTime, "00:00:00");
}
if (GetDataString("timer-time", szData)) {
strcpy(lpini->szTimerTime, szData);
} else {
strcpy(lpini->szTimerTime, "00:00:00");
}
if (GetDataString("stopwatch-mode", szData)) {
strcpy(lpini->szStopWatch, szData);
} else {
strcpy(lpini->szStopWatch, "No");
}
return;
}
レジストリからデータを読み出してINIDATA構造体に書き込む関数です。
stopwatch-modeエントリが増えています。
BOOL GetDataDWORD(char *szName, DWORD *dwValue)
{
HKEY hKey;
DWORD dwPosition;
DWORD dwType = REG_DWORD;
DWORD dwByte = 32;
RegCreateKeyEx(hParentKey,
lpszSubKey,
0,
"",
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
&dwPosition);
if (RegQueryValueEx(hKey,
szName,
NULL,
&dwType,
(BYTE *)dwValue,
&dwByte) != ERROR_SUCCESS) {
RegCloseKey(hKey);
return FALSE;
}
RegCloseKey(hKey);
return TRUE;
}
BOOL GetDataString(char *szName, char *szValue)
{
HKEY hKey;
DWORD dwPosition;
DWORD dwType = REG_SZ;
DWORD dwByte = 32;
RegCreateKeyEx(hParentKey,
lpszSubKey,
0,
"",
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
&dwPosition);
if (RegQueryValueEx(hKey,
szName,
NULL,
&dwType,
(BYTE *)szValue,
&dwByte) != ERROR_SUCCESS) {
return FALSE;
}
RegCloseKey(hKey);
return TRUE;
}
これらの関数に変更はありません。
BOOL SetInitialSettings(INIDATA inidata)
{
SetDataDWORD("background-color", inidata.cr_bg);
SetDataDWORD("text-color", inidata.cr_txt);
SetDataDWORD("x", inidata.x);
SetDataDWORD("y", inidata.y);
SetDataDWORD("short-color", inidata.cr_short);
SetDataDWORD("long-color", inidata.cr_long);
SetDataDWORD("second-color", inidata.cr_second);
SetDataDWORD("plate-color", inidata.cr_plate);
SetDataString("alarm-set", inidata.szAlarmSet);
SetDataString("alarm-time", inidata.szAlarmTime);
SetDataString("timer-time", inidata.szTimerTime);
SetDataString("stopwatch-mode", inidata.szStopWatch);
return TRUE;
}
INIDATA構造体のデータをレジストリに書き込みますが、stopwatch-mode
メンバが増えています。しかし、実際には"No"しか書き込まれません。
BOOL SetDataDWORD(char *szName, DWORD dwData)
{
HKEY hKey;
DWORD dwPosition;
RegCreateKeyEx(hParentKey,
lpszSubKey,
0,
"",
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
&dwPosition);
RegSetValueEx(hKey,
szName,
0,
REG_DWORD,
(CONST BYTE *)&dwData,
sizeof(DWORD));
RegCloseKey(hKey);
return TRUE;
}
BOOL SetDataString(char *szName, char *szData)
{
HKEY hKey;
DWORD dwPosition;
LONG lResult;
RegCreateKeyEx(hParentKey,
lpszSubKey,
0,
"",
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
&dwPosition);
lResult = RegSetValueEx(hKey,
szName,
0,
REG_SZ,
(CONST BYTE *)szData,
lstrlen(szData));
RegCloseKey(hKey);
return TRUE;
}
これらの関数に変更はありません。
BOOL ShowClock(HDC hdc, char *lpszBuf, INIDATA inidata)
{
HBRUSH hBrush;
HPEN hPen;
int h, m, s, x0, y0, x, y;
int l = CLOCK_HEIGHT / 2;
if (strcmp(lpszBuf, "") == 0)
return FALSE;
hBrush = CreateSolidBrush(inidata.cr_plate);
SelectObject(hdc, hBrush);
Ellipse(hdc, 1, 1, CLOCK_HEIGHT - 1, CLOCK_HEIGHT - 1);
Ellipse(hdc, 4, 4, CLOCK_HEIGHT - 4, CLOCK_HEIGHT - 4);
DeleteObject(hBrush);
hPen = CreatePen(PS_SOLID, 0, RGB(0, 0, 0));
SelectObject(hdc, hPen);
MoveToEx(hdc, l, 1, NULL);
LineTo(hdc, l, 4);
MoveToEx(hdc, l, 2 * l -2, NULL);
LineTo(hdc, l, 2 * l - 5);
MoveToEx(hdc, 2*l - 2, l, NULL);
LineTo(hdc, 2 * l - 5, l);
MoveToEx(hdc, 1, l, NULL);
LineTo(hdc, 4, l);
DeleteObject(hPen);
GetHMS(lpszBuf, &h, &m, &s);
x0 = y0 = CLOCK_HEIGHT / 2;
//短針
if (h > 11)
h -= 12;
hPen = CreatePen(PS_SOLID, 4, inidata.cr_short);
SelectObject(hdc, hPen);
MoveToEx(hdc, x0, y0, NULL);
x = (int)(x0 + (l - 16) * sin(h * PAI / 6 + m * PAI / 360));
y = (int)(x0 - (l - 16) * cos(h * PAI / 6 + m * PAI / 360));
LineTo(hdc, x, y);
DeleteObject(hPen);
//長針
hPen = CreatePen(PS_SOLID, 2, inidata.cr_long);
SelectObject(hdc, hPen);
MoveToEx(hdc, x0, y0, NULL);
x = (int)(x0 + (l - 10) * sin(m * PAI / 30 + s * PAI / 1800));
y = (int)(y0 - (l - 10) * cos(m * PAI / 30 + s * PAI / 1800));
LineTo(hdc, x, y);
DeleteObject(hPen);
//秒針
hPen = CreatePen(PS_SOLID, 1, inidata.cr_second);
SelectObject(hdc, hPen);
MoveToEx(hdc, x0, y0, NULL);
x = (int)(x0 + (l - 5) * sin(s * PAI / 30));
y = (int)(y0 - (l - 5) * cos(s * PAI / 30));
LineTo(hdc, x, y);
DeleteObject(hPen);
return TRUE;
}
文字盤と針を描画する関数ですが、文字盤に少しだけ目盛りを付けました。
(12,3,6,9時の位置)
BOOL GetHMS(char *lpszBuf, int *lpH, int *lpM, int *lpS)
{
char szTemp[64], *token, szSep[] = ":";
strcpy(szTemp, lpszBuf);
token = strtok(szTemp, szSep);
*lpH = atoi(token);
token = strtok(NULL, szSep);
*lpM = atoi(token);
token = strtok(NULL, szSep);
*lpS = atoi(token);
return TRUE;
}
BOOL ShowBitmap(HDC hdc, INIDATA inidata)
{
HBITMAP hBmp, hMask;
BITMAP bmp_info;
HDC hdc_mem;
int wx,wy;
HBRUSH hBrush;
hBrush = CreateSolidBrush(inidata.cr_bg);
SelectObject(hdc, hBrush);
hBmp = (HBITMAP)LoadImage(hInst,
"CAT",
IMAGE_BITMAP,
0, 0,
LR_DEFAULTCOLOR);
hMask = (HBITMAP)LoadImage(hInst,
"MASK",
IMAGE_BITMAP,
0, 0,
LR_DEFAULTCOLOR);
if (hMask == NULL) {
MessageBox(NULL, "Error", szAppName, MB_OK);
return FALSE;
}
GetObject(hBmp, (int)sizeof(BITMAP), &bmp_info);
wx = bmp_info.bmWidth;
wy = bmp_info.bmHeight;
hdc_mem = CreateCompatibleDC(hdc);
SelectObject(hdc_mem, hBmp);
MaskBlt(hdc, CLOCK_WIDTH - wx - 5, 5, wx, wy,
hdc_mem, 0, 0,
hMask, 0, 0, MAKEROP4(SRCCOPY, PATCOPY));
DeleteObject(hBmp);
DeleteObject(hMask);
DeleteObject(hBrush);
DeleteDC(hdc_mem);
return TRUE;
}
BOOL CALLBACK MyAlarmProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
{
SYSTEMTIME st;
static HWND hTime, hCheck;
char szBuf[32];
static INIDATA *lpinidata;
int h, m, s;
switch (msg) {
case WM_INITDIALOG:
hTime = GetDlgItem(hDlg, IDC_DATETIMEPICKER1);
hCheck = GetDlgItem(hDlg, IDC_CHECK1);
SendMessage(hTime, DTM_SETFORMAT, 0, (LPARAM)"HH:mm:ss");
lpinidata = (INIDATA *)lp;
if (strcmp(lpinidata->szAlarmSet, "No") == 0) {
EnableWindow(hTime, FALSE);
Button_SetCheck(hCheck, BST_UNCHECKED);
} else {
EnableWindow(hTime, TRUE);
Button_SetCheck(hCheck, BST_CHECKED);
GetHMS(lpinidata->szAlarmTime, &h, &m, &s);
GetLocalTime(&st);
st.wHour = h;
st.wMinute = m;
st.wSecond = s;
DateTime_SetSystemtime(hTime, GDT_VALID, &st);
}
return TRUE;
case WM_COMMAND:
switch (LOWORD(wp)) {
case IDOK:
if (Button_GetCheck(hCheck) == BST_CHECKED) {
strcpy(lpinidata->szAlarmSet, "Yes");
DateTime_GetSystemtime(hTime, &st);
wsprintf(szBuf, "%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);
strcpy(lpinidata->szAlarmTime, szBuf);
} else {
strcpy(lpinidata->szAlarmSet, "No");
strcpy(lpinidata->szAlarmTime, "00:00:00");
}
EndDialog(hDlg, IDOK);
return TRUE;
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
return TRUE;
case IDC_CHECK1:
if (Button_GetCheck(hCheck) == BST_CHECKED) {
EnableWindow(hTime, TRUE);
}
else
EnableWindow(hTime, FALSE);
return TRUE;
}
return FALSE;
}
return FALSE;
}
BOOL CALLBACK MyTimerProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
{
static HWND hTime;
SYSTEMTIME st;
char szBuf[64];
static INIDATA *lpini;
int h, m, s;
switch (msg) {
case WM_INITDIALOG:
hTime = GetDlgItem(hDlg, IDC_DATETIMEPICKER1);
SendMessage(hTime, DTM_SETFORMAT, 0, (LPARAM)"HH:mm:ss");
lpini = (INIDATA *)lp;
GetLocalTime(&st);
GetHMS(lpini->szTimerTime, &h, &m, &s);
st.wHour = h;
st.wMinute = m;
st.wSecond = s;
DateTime_SetSystemtime(hTime, GDT_VALID, &st);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wp)) {
case IDOK:
DateTime_GetSystemtime(hTime, &st);
wsprintf(szBuf, "%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);
strcpy(lpini->szTimerTime, szBuf);
EndDialog(hDlg, IDOK);
return TRUE;
case IDCANCEL:
EndDialog(hDlg, IDCANCEL);
return TRUE;
}
return FALSE;
}
return FALSE;
}
BOOL MyTimerShow(HDC hdc, int x, int y, INIDATA inidata, DWORD dwStart)
{
MMTIME mm;
char szBuf[64];
DWORD dwNow, dwTimerSec;
int h, m, s;
GetHMS(inidata.szTimerTime, &h, &m, &s);
dwTimerSec = h * 60 * 60 + m * 60 + s;
timeGetSystemTime(&mm, (UINT)sizeof(MMTIME));
dwNow = mm.u.ms;
SetHMS(szBuf, dwTimerSec - (dwNow - dwStart) / 1000);
TextOut(hdc, x, y, szBuf, (int)strlen(szBuf));
if (strcmp(szBuf, "00:00:00") == 0)
return TRUE;
return FALSE;
}
BOOL SetHMS(char *lpszBuf, DWORD dwTm)
{
int h, m, s;
h = dwTm / (60 * 60);
m = (dwTm - h * 60 * 60) / 60;
s = dwTm - h * 60 * 60 - m * 60;
if (h > 23) {
h = 0;
m = 0;
s = 0;
}
wsprintf(lpszBuf, "%02d:%02d:%02d", h, m, s);
return TRUE;
}
これらの関数に変更はありません。
BOOL CALLBACK MyStopWatchProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
{
static INIDATA *lpini;
static DWORD dwStart;
static HWND hParent, hStart, hReset, hStop, hClose;
DWORD dwTm;
switch (msg) {
case WM_INITDIALOG:
lpini = (INIDATA *)lp;
strcpy(lpini->szStopWatch, "Yes");
strcpy(lpini->szStopTime, "0:00:00-000");
hParent = GetParent(hDlg);
hStart = GetDlgItem(hDlg, IDOK);
hReset = GetDlgItem(hDlg, IDC_RESET);
hStop = GetDlgItem(hDlg, IDC_STOP);
hClose = GetDlgItem(hDlg, IDCANCEL);
EnableWindow(hStop, FALSE);
EnableWindow(hReset, FALSE);
return TRUE;
case WM_TIMER:
dwTm = timeGetTime() - dwStart;
SetStopHMS(lpini->szStopTime, dwTm);
InvalidateRect(hParent, NULL, TRUE);
return TRUE;
case WM_COMMAND:
switch (LOWORD(wp)) {
case IDOK:
SetTimer(hDlg, MYSTOP, 50, NULL);
dwStart = timeGetTime();
EnableWindow(hStop, TRUE);
EnableWindow(hStart, FALSE);
EnableWindow(hReset, FALSE);
EnableWindow(hClose, FALSE);
SetFocus(hStop);
return TRUE;
case IDCANCEL:
strcpy(lpini->szStopWatch,"No");
KillTimer(hDlg, MYSTOP);
EndDialog(hDlg, IDCANCEL);
return TRUE;
case IDC_STOP:
KillTimer(hDlg, MYSTOP);
EnableWindow(hReset, TRUE);
EnableWindow(hStop, FALSE);
EnableWindow(hClose, TRUE);
SetFocus(hReset);
return TRUE;
case IDC_RESET:
strcpy(lpini->szStopTime, "0:00:00-000");
EnableWindow(hStart, TRUE);
EnableWindow(hReset, FALSE);
SetFocus(hStart);
return TRUE;
}
return FALSE;
}
return FALSE;
}
ストップウォッチ用ダイアログボックスのプロシージャです。WM_INITDIALOGメッセージが来たら、引数のlpからinidata構造体を取得します。
また、このダイアログが出ているということは、ストップウォッチモードであることは 明らかなので、INIDATA構造体のszStopWatchメンバに"Yes"をコピーします。
また、szStopTimeメンバに"0:00:00-000"をセットしておきます。
さらに、ボタン類のウィンドウハンドルを取得してスタティックな変数に格納しておきます。
WM_TIMERメッセージが来たら、現在のシステム時刻とdwStartの差を求めて、この値から ストップウォッチの表示文字列を作っておきます。
IDOK(「スタート」ボタン)が押されたら、SetTimer関数を呼んで計測を開始します。
また、この時のシステム時刻を取得してdwStartに保存しておきます。
IDCANCEL(「キャンセル」ボタン)が押されたら、INIDATA構造体のszStopWatch メンバを"No"に設定します。
その後、タイマを殺しますが、「ストップ」ボタンを押した後に、このボタンを 押した場合はすでにタイマは死んでいるのでKillTimer関数は失敗します。
IDC_STOP(「ストップ」ボタン)が押されたら、KillTimer関数を呼んでタイマを殺します。
IDC_RESET(「リセット」ボタン)が押されたらINIDATA構造体のszStopTime メンバを"0:00:00-000"にします。
BOOL SetStopHMS(char *lpszBuf, DWORD dwTime)
{
int h, m, s, ms;
h = (dwTime /1000) / (60 * 60);
m = (dwTime - h * 60 * 60 * 1000) / (60 * 1000);
s = (dwTime - h * 60 * 60 * 1000 - m * 60 * 1000) / 1000;
ms = dwTime - h * 60 * 60 * 1000 - m * 60 * 1000 - s * 1000;
wsprintf(lpszBuf, "%d:%02d:%02d-%03d", h, m, s, ms);
return TRUE;
}
ミリセコンドから"0:00:00-000"の形の文字列を作る関数です。今回作ったストップウォッチは、一度「ストップ」ボタンを押すと 「リセット」しないと、次の計測ができません。「ストップ」ボタンを押した後、 再度「スタート」ボタンを押すと、ストップした時間から計測を再開できるように 改良してみてください。
Update 03/Feb/2003 By Y.Kumei