開発環境 Microsoft Visual C++ 2010 Express (SP1)
実行環境 Microsoft Windows XP Home Edition (SP3)
プロジェクトの種類 Win32 プロジェクト
プロジェクト名 MultiTimer
アプリケーションの種類 Windows アプリケーション
追加のオプション 空のプロジェクト

ダウンロード
MultiTimer_20130706.zip
MultiTimer_20130731.zip

参考

MultiTimer.cpp
#pragma comment(lib, "winmm")
 
#include <Windows.h>
#include <WindowsX.h>
#include <CommCtrl.h>
#include <wchar.h>
#include <time.h>
#include "resource.h"
 
#define TIMER_MAX	20
#define SECT		TEXT("General")
#define APP_NAME	TEXT("MultiTimer")
#define IDC_CLOCK	1000
#define IDC_EDIT	1100
#define IDC_TIMER	1200
 
enum STATUS {
	OFF = 0,
	ON,
	WARN1,
	WARN2,
	END,
	COUNTUP,
};
 
typedef struct {
	STATUS status;
	time_t time;
//	int sec;
	BOOL tool;
} TimerList;
 
// 外部変数
HINSTANCE hInst;
TCHAR iniFile[MAX_PATH];
HFONT hFontEdit;
HFONT hFontTimer;
HBRUSH hBrush[COUNTUP+1];
HWND hTool;
HWND hClock;
INT_PTR uIDEvent;
time_t timer;
TimerList tl[TIMER_MAX];
size_t timerNum;
int nearest = -1;
int warnSec[2];
 
UINT uTime;
 
// 関数プロトタイプ宣言
void GetIniFileName();
void Trace(LPCTSTR format, ...);
 
INT_PTR CALLBACK DlgMain(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
void OnInitDialog(HWND hDlg, LPARAM lParam);
void OnDestroy(HWND hDlg);
INT_PTR OnCtlColorStatic(WPARAM wParam, LPARAM lParam);
void OnTimer(HWND hDlg);
void OnXButtonDown(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
void OnLButtonDown(HWND hDlg, size_t i);
void SetTimerList(HWND hDlg, size_t i, STATUS status, time_t time);
void Nearest();
void AddTool(HWND hDlg, size_t i);
 
INT_PTR CALLBACK DlgInput(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
 
//==============================================================================
int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow)
{
	hInst = hInstance;
	GetIniFileName();
 
	DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, DlgMain);
	return 0;
}
 
void GetIniFileName()
{
	TCHAR path	[_MAX_PATH];
	TCHAR drive	[_MAX_DRIVE];
	TCHAR dir	[_MAX_DIR];
	TCHAR fname	[_MAX_FNAME];
	TCHAR ext	[_MAX_EXT];
 
	GetModuleFileName(NULL, path, _countof(path));
	_wsplitpath_s(path, drive, dir, fname, ext);
	_wmakepath_s(iniFile, drive, dir, fname, L"ini");
}
 
void Trace(LPCTSTR format, ...)
{
	va_list arg_ptr;
	va_start(arg_ptr, format);
	TCHAR buffer[256];
	int size = _vsnwprintf_s(buffer, _TRUNCATE, format, arg_ptr);
	va_end(arg_ptr);
	OutputDebugString(buffer);
	if (size < 0) {
		OutputDebugString(L"...\n");
	}
}
 
//==============================================================================
INT_PTR CALLBACK DlgMain(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	INT_PTR res = TRUE;	// メッセージを処理した
 
	switch (uMsg) {
	case WM_TIMER:
		OnTimer(hDlg);
		break;
	case WM_CTLCOLORSTATIC:
		return OnCtlColorStatic(wParam, lParam);
	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
		OnXButtonDown(hDlg, uMsg, wParam, lParam);
		break;
	case WM_INITDIALOG:
		OnInitDialog(hDlg, lParam);
		//res = FALSE;	// SetFocusでフォーカスを設定した場合はFALSE
		break;
	case WM_CLOSE:
		EndDialog(hDlg, 0);
		break;
	case WM_ENDSESSION:
		if (wParam == TRUE) {
			EndDialog(hDlg, 0);
		}
		break;
	case WM_DESTROY:
		OnDestroy(hDlg);
		break;
	default:
		res = FALSE;	// メッセージを処理しなかった
	}
	return res;
}
 
void OnInitDialog(HWND hDlg, LPARAM lParam)
{
//	LPCREATESTRUCT pcs = (LPCREATESTRUCT)lParam;
 
	timerNum	= GetPrivateProfileInt(SECT, L"TimerNum", 0, iniFile);
	warnSec[0]	= GetPrivateProfileInt(SECT, L"WarningSec1", 120, iniFile);
	warnSec[1]	= GetPrivateProfileInt(SECT, L"WarningSec2", 65, iniFile);
	int X		= GetPrivateProfileInt(SECT, L"X", 100, iniFile);
	int Y		= GetPrivateProfileInt(SECT, L"Y", 100, iniFile);
 
	if (timerNum < 1) timerNum = 11;
	if (TIMER_MAX < timerNum) timerNum = TIMER_MAX;
 
	RECT rw;
	RECT rc;
	GetWindowRect(hDlg, &rw);
	GetClientRect(hDlg, &rc);
	int cx = (rw.right - rw.left) - rc.right + 210;
	int cy = (rw.bottom - rw.top) - rc.bottom + 35 + 25 * timerNum;
	SetWindowPos(hDlg, HWND_TOP, X, Y, cx, cy, 0);
 
	// フォント作成
	LOGFONT lf;
	ZeroMemory(&lf, sizeof lf);
	lf.lfCharSet	= DEFAULT_CHARSET;
	lf.lfHeight	= 16;
	wcscpy_s(lf.lfFaceName, L"MS Pゴシック");
	hFontEdit = CreateFontIndirect(&lf);
 
	lf.lfHeight	= 24;
	wcscpy_s(lf.lfFaceName, L"Arial");
	hFontTimer = CreateFontIndirect(&lf);
 
	// ブラシ
	hBrush[OFF]	= (HBRUSH)GetStockObject(LTGRAY_BRUSH);
	hBrush[ON]	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	hBrush[WARN1]	= CreateSolidBrush(RGB(0x00,0xff,0xff));
	hBrush[WARN2]	= CreateSolidBrush(RGB(0x00,0xff,0x00));
	hBrush[END]	= CreateSolidBrush(RGB(0x00,0xff,0x00));
	hBrush[COUNTUP]	= CreateSolidBrush(RGB(0xff,0xff,0x7f));
 
	// 時計
	hClock = CreateWindowEx(WS_EX_CLIENTEDGE, WC_STATIC, NULL,
		WS_CHILD | WS_VISIBLE | SS_CENTER,
		105, 5, 100, 25, hDlg, (HMENU)IDC_CLOCK, hInst, NULL);
	SendMessage(hClock, WM_SETFONT, (WPARAM)hFontTimer, MAKELPARAM(FALSE, 0));
 
	// ツールチップ
	hTool = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, TTS_ALWAYSTIP,
		CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, hDlg, NULL, hInst, NULL);
 
	// タイマー
	for (size_t i = 0; i < timerNum; i++) {
		HWND hWnd;
		int y = 30 + 25 * i;
 
		hWnd = CreateWindowEx(WS_EX_CLIENTEDGE, WC_EDIT, NULL,
			WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL,
			5, y, 100, 25, hDlg, HMENU(IDC_EDIT + i), hInst, NULL);
		SendMessage(hWnd, WM_SETFONT, (WPARAM)hFontEdit, MAKELPARAM(FALSE, 0));
 
		hWnd = CreateWindowEx(WS_EX_CLIENTEDGE, WC_STATIC, NULL,
			WS_CHILD | WS_VISIBLE | SS_CENTER/* | SS_NOTIFY*/,
			105, y, 100, 25, hDlg, HMENU(IDC_TIMER + i), hInst, NULL);
		SendMessage(hWnd, WM_SETFONT, (WPARAM)hFontTimer, MAKELPARAM(FALSE, 0));
 
		TCHAR key[16];
		TCHAR buf[256];
		swprintf_s(key, L"Edit%d", i);
		GetPrivateProfileString(SECT, key, NULL, buf, _countof(buf), iniFile);
		SetDlgItemText(hDlg, IDC_EDIT + i, buf);
 
		swprintf_s(key, L"Timer%d", i);
		GetPrivateProfileString(SECT, key, NULL, buf, _countof(buf), iniFile);
		time_t time = 0LL;
		swscanf_s(buf, L"%lld", &time);
		if (time) {
			tl[i].status	= ON;
			tl[i].time	= time;
			AddTool(hDlg, i);
		}
	}
	Nearest();
 
	time(&timer);
	uIDEvent = SetTimer(hDlg, 1, 1000, NULL);
}
 
void OnDestroy(HWND hDlg)
{
	WINDOWPLACEMENT wp;
	wp.length = sizeof wp;
	GetWindowPlacement(hDlg, &wp);
 
	TCHAR buf[256];
	swprintf_s(buf, L"%d", wp.rcNormalPosition.left);
	WritePrivateProfileString(SECT, L"X", buf, iniFile);
	swprintf_s(buf, L"%d", wp.rcNormalPosition.top);
	WritePrivateProfileString(SECT, L"Y", buf, iniFile);
 
	for (size_t i = 0; i < timerNum; i++) {
		TCHAR key[16];
		swprintf_s(key, L"Edit%d", i);
		GetDlgItemText(hDlg, IDC_EDIT + i, buf, _countof(buf));
		WritePrivateProfileString(SECT, key, buf, iniFile);
 
		swprintf_s(key, L"Timer%d", i);
		swprintf_s(buf, L"%lld", (tl[i].status == ON) ? tl[i].time : 0LL);
		WritePrivateProfileString(SECT, key, buf, iniFile);
	}
 
	DeleteObject(hFontEdit);
	DeleteObject(hFontTimer);
	for (int i = 0; i < _countof(hBrush); i++) {
		DeleteObject(hBrush[i]);
	}
	KillTimer(hDlg, uIDEvent);
}
 
INT_PTR OnCtlColorStatic(WPARAM wParam, LPARAM lParam)
{
	LONG id = GetWindowLong((HWND)lParam, GWL_ID);
	if (id == IDC_CLOCK) {
		return (INT_PTR)hBrush[ON];
	}
	size_t i = id - IDC_TIMER;
	if (i < timerNum) {
		HDC hdc = (HDC)wParam;
		SetBkMode(hdc, TRANSPARENT);
		if (i == nearest) {
			SetTextColor(hdc, RGB(0xff,0x00,0x00));
		}
		return (INT_PTR)hBrush[tl[i].status];
	}
	return FALSE;
}
 
void OnTimer(HWND hDlg)
{
	TCHAR buf[8+1];
 
	time(&timer);
 
	tm tm;
	localtime_s(&tm, &timer);
	swprintf_s(buf, L"%2d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);
	SetWindowText(hClock, buf);
 
	for (size_t i = 0; i < timerNum; i++) {
		switch (tl[i].status) {
		case ON:
		case WARN1:
		case WARN2:
			{
				int sec = int(tl[i].time - timer);
				if (tl[i].status == ON && sec <= warnSec[0]) {
					tl[i].status = WARN1;
					Nearest();
				}
				if (tl[i].status == WARN1 && sec <= warnSec[1]) {
					tl[i].status = WARN2;
					TCHAR path[MAX_PATH];
					GetPrivateProfileString(SECT, L"WaveFile", L"sample.wav",
						path, _countof(path), iniFile);
					PlaySound(path, NULL, SND_FILENAME | SND_ASYNC);
				}
				if (sec <= 0) {
					sec = 0;
					tl[i].status = END;
				}
//				tl[i].sec = sec;
				swprintf_s(buf, L"%2d:%02d:%02d", sec / 3600, (sec / 60) % 60, sec % 60);
				SetDlgItemText(hDlg, IDC_TIMER + i, buf);
			}
			break;
		case COUNTUP:
			{
				int sec = int(timer - tl[i].time);
				if (99 * 3600 + 59 * 60 + 59 < sec) {
					sec = 0;
					tl[i].status = END;
				}
				swprintf_s(buf, L"%2d:%02d:%02d", sec / 3600, (sec / 60) % 60, sec % 60);
				SetDlgItemText(hDlg, IDC_TIMER + i, buf);
			}
			break;
		}
	}
}
 
void OnXButtonDown(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	POINT pt;
	pt.x = GET_X_LPARAM(lParam);
	pt.y = GET_Y_LPARAM(lParam);
	HWND hChild = ChildWindowFromPoint(hDlg, pt);
	LONG id = GetWindowLong(hChild, GWL_ID);
//	Trace(L"OnXButtonDown(%d, %d, %d)\n", pt.x, pt.y, id);
 
	size_t i = id - IDC_TIMER;
	if (i < timerNum) {
		switch (uMsg) {
		case WM_LBUTTONDOWN:
			OnLButtonDown(hDlg, i);
			break;
		case WM_RBUTTONDOWN:
			tl[i].status = OFF;
			SetDlgItemText(hDlg, IDC_TIMER + i, NULL);
			Nearest();
			break;
		}
	}
}
 
void OnLButtonDown(HWND hDlg, size_t i)
{
	INT_PTR ret = DialogBox(hInst, MAKEINTRESOURCE(IDD_INPUT), hDlg, DlgInput);
	switch (ret) {
	case IDC_TIME1:		// 残り時間
		{
			int sec = (uTime / 10000) * 3600 + ((uTime / 100) % 100) * 60 + (uTime % 100);
			SetTimerList(hDlg, i, ON, timer + sec);
		}
		break;
	case IDC_TIME2:		// 時刻指定
		{
			tm tm;
			localtime_s(&tm, &timer);
			tm.tm_hour	= uTime / 100;
			tm.tm_min	= uTime % 100;
			tm.tm_sec	= 0;
			time_t time = mktime(&tm);
			if (time <= timer) {
				time += 24 * 60 * 60;
			}
			SetTimerList(hDlg, i, ON, time);
		}
		break;
	case IDC_COUNTUP:	// カウントアップ
		{
			SetTimerList(hDlg, i, COUNTUP, timer);
		}
		break;
	}
}
 
void SetTimerList(HWND hDlg, size_t i, STATUS status, time_t time)
{
	tl[i].status	= status;
	tl[i].time	= time;
	AddTool(hDlg, i);
	Nearest();
}
 
void Nearest()
{
	time_t time;
	nearest = -1;
	for (size_t i = 0; i < timerNum; i++) {
		if (tl[i].status == ON) {
			if (nearest < 0 || tl[i].time < time) {
				nearest = i;
				time = tl[i].time;
			}
		}
	}
}
 
void AddTool(HWND hDlg, size_t i)
{
	RECT rc;
	GetWindowRect(GetDlgItem(hDlg, IDC_TIMER + i), &rc);
	MapWindowPoints(NULL, hDlg, (LPPOINT)&rc, 2);
 
	TCHAR buf[8+1];
	tm tm;
	localtime_s(&tm, &tl[i].time);
	swprintf_s(buf, L"%d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);
 
	TOOLINFO ti;
	ti.cbSize	= sizeof ti - 4;	// ComCtl32.dll ver 5.0
	ti.uFlags	= TTF_SUBCLASS;
	ti.hwnd		= hDlg;
	ti.uId		= i;
	ti.rect		= rc;
	ti.lpszText	= buf;
	if (tl[i].tool) {
		SendMessage(hTool, TTM_DELTOOL, 0, (LPARAM)&ti);
	} else {
		tl[i].tool = TRUE;
	}
	SendMessage(hTool, TTM_ADDTOOL, 0, (LPARAM)&ti);
}
 
//==============================================================================
INT_PTR CALLBACK DlgInput(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	INT_PTR res = TRUE;	// メッセージを処理した
 
	switch (uMsg) {
	case WM_COMMAND:
		{
			WORD wCtrlId = LOWORD(wParam);
			switch (wCtrlId) {
			case IDOK:
				{
					BOOL trans;
					uTime = GetDlgItemInt(hDlg, IDC_TIME1, &trans, FALSE);
					if (trans) {
						if (uTime <= 995959) {
							EndDialog(hDlg, IDC_TIME1);
							break;
						}
					}
					uTime = GetDlgItemInt(hDlg, IDC_TIME2, &trans, FALSE);
					if (trans) {
						if (uTime <= 2359 && uTime % 100 < 60) {
							EndDialog(hDlg, IDC_TIME2);
							break;
						}
					}
					MessageBeep(MB_ICONWARNING);
				}
				break;
			case IDC_COUNTUP:
			case IDCANCEL:
				EndDialog(hDlg, wCtrlId);
				break;
			}
		}
		break;
	case WM_INITDIALOG:
//		res = FALSE;	// SetFocusでフォーカスを設定した場合はFALSE
		break;
	case WM_CLOSE:
		EndDialog(hDlg, IDCANCEL);
		break;
	default:
		res = FALSE;	// メッセージを処理しなかった
	}
	return res;
}
 

resource.h
#define IDD_MAIN	100
#define IDD_INPUT	101
 
#define IDC_STATIC	-1
#define IDC_TIME1	1000
#define IDC_TIME2	1001
#define IDC_COUNTUP	1002
 

MultiTimer.rc
// resource script
#include <Windows.h>
#include "resource.h"
 
IDD_MAIN DIALOGEX 0, 0, 0, 0
STYLE WS_POPUPWINDOW | WS_MINIMIZEBOX
EXSTYLE WS_EX_APPWINDOW
CAPTION "MultiTimer"
BEGIN
END
 
IDD_INPUT DIALOG 0, 0, 115, 60
STYLE WS_POPUPWINDOW
CAPTION "タイマー設定"
FONT 9, "MS Pゴシック"
BEGIN
	LTEXT		"残り時間 (hhmmss)",IDC_STATIC,5,8,75,15
	EDITTEXT	IDC_TIME1,80,6,30,13
	LTEXT		"時刻指定 (hhmm)",IDC_STATIC,5,23,75,15
	EDITTEXT	IDC_TIME2,80,21,30,13
	PUSHBUTTON	"OK",IDOK,5,40,50,15
	PUSHBUTTON	"カウントアップ",IDC_COUNTUP,60,40,50,15
END
 
最終更新:2013年07月31日 09:05