// ---------------------------------------------------------------------------
//  M88 - PC-8801 emulator
//	Copyright (C) cisc 2000.
// ---------------------------------------------------------------------------
//	$Id: winmon.cpp,v 1.4 2002/04/07 05:40:11 cisc Exp $

#include "headers.h"
#include <climits>
#include "resource.h"
#include "winmon.h"
#include "misc.h"
#include "88config.h"

// フォントサイズの選択肢（IDM_MON_FONTSIZE_BASE からの連番に対応）
static const int fontsizetable[] = { 10, 12, 14, 16, 18, 20, 24, 32 };
static const int nfontsizes = sizeof(fontsizetable) / sizeof(fontsizetable[0]);

// ---------------------------------------------------------------------------
//	ウインドウが画面外に出ていたら、プライマリモニタの左上寄りに戻す
//
static void ClampToWorkArea(RECT* rect)
{
	POINT center;
	center.x = (rect->left + rect->right) / 2;
	center.y = (rect->top + rect->bottom) / 2;

	HMONITOR hmon = MonitorFromPoint(center, MONITOR_DEFAULTTONULL);
	if (hmon)
		return;     // どこかのモニタ上にある。補正不要

	RECT work;
	SystemParametersInfo(SPI_GETWORKAREA, 0, &work, 0);

	int w = rect->right - rect->left;
	int h = rect->bottom - rect->top;

	rect->left   = work.left + 64;
	rect->top    = work.top + 64;
	rect->right  = rect->left + w;
	rect->bottom = rect->top + h;
}

// ---------------------------------------------------------------------------
//	構築/消滅
//
WinMonitor::WinMonitor()
: txtbuf(0), txpbuf(0), timerinterval(0), timer(0), wheeldelta(0), inisection(0),
  defcols(0), defrows(0)
{
	hwnd = 0;
	hwndowner = 0;
	hwndstatus = 0;
	line = 0;
	hfont = 0;
	hbitmap = 0;
	fontheight = 18;
	fontheightpx = 18;
	wndrect.left = wndrect.top = 0;
	wndrect.right = wndrect.bottom = -1;
}

WinMonitor::~WinMonitor()
{
	if (hbitmap)
		DeleteObject(hbitmap);
	if (hfont)
		DeleteObject(hfont);
	delete[] txtbuf;
}

// ---------------------------------------------------------------------------
//	初期化
//
bool WinMonitor::Init(LPCTSTR tmpl, const char* section, int _defcols, int _defrows)
{
	lptemplate = tmpl;
	nlines = 0;
	txcol = 0xffffff;
	bkcol = 0x400000;
	inisection = section;
	defcols = _defcols;
	defrows = _defrows;
	LoadMonitorConfig();
	return true;
}

// ---------------------------------------------------------------------------
//	ウインドウ位置・サイズを M88.ini から読み込む
//	(inisection が NULL なら何もしない)
//
//	フォントサイズは意図的に保存/復元しない。Ctrl+ホイールでの変更は
//	その場限りとし、次回起動時は常にコンストラクタ既定の 18px から始める
//
void WinMonitor::LoadMonitorConfig()
{
	if (!inisection)
		return;

	const char* ini = PC8801::GetM88IniPath();

	int x  = GetPrivateProfileInt(inisection, "PosX",   INT_MIN, ini);
	int y  = GetPrivateProfileInt(inisection, "PosY",   INT_MIN, ini);
	int w  = GetPrivateProfileInt(inisection, "Width",  0, ini);
	int h  = GetPrivateProfileInt(inisection, "Height", 0, ini);

	if (x != INT_MIN && y != INT_MIN && w > 0 && h > 0)
	{
		wndrect.left   = x;
		wndrect.top    = y;
		wndrect.right  = x + w;
		wndrect.bottom = y + h;
	}
}

// ---------------------------------------------------------------------------
//	ウインドウ位置・サイズを M88.ini へ書き込む
//	(inisection が NULL なら何もしない。フォントサイズは保存しない)
//
void WinMonitor::SaveMonitorConfig()
{
	if (!inisection || !hwnd)
		return;

	RECT rect;
	GetWindowRect(hwnd, &rect);

	const char* ini = PC8801::GetM88IniPath();
	char buf[32];

	wsprintf(buf, "%d", rect.left);                 WritePrivateProfileString(inisection, "PosX",   buf, ini);
	wsprintf(buf, "%d", rect.top);                  WritePrivateProfileString(inisection, "PosY",   buf, ini);
	wsprintf(buf, "%d", rect.right - rect.left);    WritePrivateProfileString(inisection, "Width",  buf, ini);
	wsprintf(buf, "%d", rect.bottom - rect.top);    WritePrivateProfileString(inisection, "Height", buf, ini);
}

// ---------------------------------------------------------------------------
//	ダイアログ表示
//
void WinMonitor::Show(HINSTANCE hinstance, HWND hwndparent, bool show)
{
	hwndowner = hwndparent;

	if (show)
	{
		if (!hwnd)
		{
			assert(!hwndstatus);

			RECT rect = wndrect;
			hinst = hinstance;
			hwnd = CreateDialogParam(hinst, lptemplate, hwndparent, DLGPROC((void*)DlgProcGate), (LPARAM)this);
			hwndstatus = 0;

			if (rect.right > rect.left && rect.bottom > rect.top)
			{
				// 画面外に飛んでいたら補正する
				ClampToWorkArea(&rect);

				SetWindowPos(hwnd, 0,
						rect.left, rect.top,
						rect.right - rect.left, rect.bottom - rect.top,
						SWP_NOZORDER | SWP_NOACTIVATE);
			}
			else if (defcols > 0 && defrows > 0 && fontwidth > 0 && fontheightpx > 0)
			{
				// INI に保存された大きさが無いので、
				// 「必要な文字数×行数」が入るサイズにしてやる。
				// フォントサイズを変えてもこの計算が追従する
				ApplyDefaultSize();
			}
			SetLines(nlines);
			Start();
		}
	}
	else
	{
		if (hwnd)
		{
			Stop();
			SendMessage(hwnd, WM_CLOSE, 0, 0);
		}
	}
}

// ---------------------------------------------------------------------------
//	defcols × defrows の文字が収まるウインドウサイズにする
//	(INI に保存されたサイズが無いときだけ呼ばれる)
//
void WinMonitor::ApplyDefaultSize()
{
	RECT r;
	r.left = 0;
	r.top  = 0;
	r.right  = defcols * fontwidth;
	r.bottom = defrows * fontheightpx;

	DWORD style   = GetWindowLong(hwnd, GWL_STYLE);
	DWORD exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
	AdjustWindowRectEx(&r, style, GetMenu(hwnd) != 0, exstyle);

	// AdjustWindowRectEx はスクロールバーを勘定に入れない
	if (style & WS_VSCROLL)
		r.right += GetSystemMetrics(SM_CXVSCROLL);

	SetWindowPos(hwnd, 0, 0, 0, r.right - r.left, r.bottom - r.top,
				 SWP_NOZORDER | SWP_NOMOVE | SWP_NOACTIVATE);
}

// ---------------------------------------------------------------------------
//	フォント高さの変更
//
bool WinMonitor::SetFont(HWND hwnd, int fh)
{
	if (hfont)
		DeleteObject(hfont);

	fontheight = fh;

	// DPI スケールを反映(fontheight 自体はメニュー・INI 用の論理値のまま保つ)
	HDC hdcTmp = GetDC(hwnd);
	int dpi = GetDeviceCaps(hdcTmp, LOGPIXELSY);
	ReleaseDC(hwnd, hdcTmp);
	int fhScaled = MulDiv(fh, dpi, 96);

	hfont = CreateFont(fhScaled, 0, 0, 0, 0, 0, 0, 0,
					SHIFTJIS_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS,
					DEFAULT_QUALITY, FIXED_PITCH, "ＭＳゴシック");
	if (!hfont)
		return false;

	HDC hdc = GetDC(hwnd);
	HFONT hof = (HFONT) SelectObject(hdc, hfont);
	GetCharWidth(hdc, '0', '0', &fontwidth);
	TEXTMETRIC tm;
	GetTextMetrics(hdc, &tm);
	fontheightpx = tm.tmHeight;          // 実測値。ResizeWindow はこちらを使う
	SelectObject(hdc, hof);
	ReleaseDC(hwnd, hdc);
	return true;
}

// ---------------------------------------------------------------------------
//	窓の大きさに従いテキストバッファのサイズを変更
//
void WinMonitor::ResizeWindow(HWND hwnd)
{
	RECT rect;

	GetWindowRect(hwnd, &wndrect);
	GetClientRect(hwnd, &rect);

	clientwidth = rect.right;
	clientheight = rect.bottom;
	if (hwndstatus)
	{
		RECT rectstatus;
		GetWindowRect(hwndstatus, &rectstatus);
		clientheight -= rectstatus.bottom - rectstatus.top;
	}
	width = (clientwidth + fontwidth - 1) / fontwidth;
	height = (clientheight + fontheightpx - 1) / fontheightpx;

	// 仮想 TVRAM のセットアップ
	delete[] txtbuf;
	txtbuf = new TXCHAR[width * height * 2];
	txpbuf = txtbuf + width * height;
	ClearText();

	// DDB の入れ替え
	HDC hdc = GetDC(hwnd);
	HDC hmemdc = CreateCompatibleDC(hdc);

	if (hbitmap)
		DeleteObject(hbitmap);
	hbitmap = CreateCompatibleBitmap(hdc, rect.right, rect.bottom);

	// 背景色で塗りつぶし
	HBITMAP holdbmp = (HBITMAP) SelectObject(hmemdc, hbitmap);
	HBRUSH hbrush = (HBRUSH) SelectObject(hmemdc, CreateSolidBrush(bkcol));
	PatBlt(hmemdc, 0, 0, rect.right, rect.bottom, PATCOPY);
	DeleteObject(SelectObject(hmemdc, hbrush));
	SelectObject(hmemdc, holdbmp);
	DeleteDC(hmemdc);

	ReleaseDC(hwnd, hdc);

	// スクロールバーの設定
	SetLines(nlines);
}

// ---------------------------------------------------------------------------
//	画面の中身を消す
//
void WinMonitor::ClearText()
{
	int nch = width * height;
	for (int i=0; i<nch; i++)
	{
		txtbuf[i].ch = ' ';
		txtbuf[i].txcol = txcol;
		txtbuf[i].bkcol = bkcol;
	}
}

static inline bool IsKan(int c)
{
	return (0x81 <= c && c <= 0x9f) || (0xe0 < c && c < 0xfd);
}

// ---------------------------------------------------------------------------
//	画面を書く
//
void WinMonitor::DrawMain(HDC _hdc, bool update)
{
	hdc = _hdc;

	Locate(0, 0);

	assert(height>=0);
	memcpy(txpbuf, txtbuf, sizeof(TXCHAR) * width * height);
	UpdateText();

	char* buf = new char[width];
	int c=0;
	int y=0;
	for (int l=0; l<height; l++)
	{
		if (memcmp(txpbuf + c, txtbuf + c, width * sizeof(TXCHAR)) || update)
		{
			// 1行描画
			for (int x=0; x<width; )
			{
				// 同じ属性で何文字書けるか…？
				int n;

				for (n=x; n<width; n++)
				{
					if (   txtbuf[c+x].txcol != txtbuf[c+n].txcol
						|| txtbuf[c+x].bkcol != txtbuf[c+n].bkcol)
						break;
					buf[n-x] = txtbuf[c+n].ch;
				}

				// 書く
				SetTextColor(hdc, txtbuf[c+x].txcol);
				SetBkColor(hdc, txtbuf[c+x].bkcol);
				TextOut(hdc, x * fontwidth, y, buf, n - x);
				x = n;
			}
		}
		c += width, y += fontheightpx;
	}
	delete[] buf;
}

// ---------------------------------------------------------------------------
//	1行上にスクロール
//
void WinMonitor::ScrollUp()
{
	if (height > 2)
	{
		memmove(txtbuf, txtbuf + width, width * (height - 2) * sizeof(TXCHAR));
		memset(txtbuf+width*(height-1), 0, width * sizeof(TXCHAR));

		HDC hdc = GetDC(hwnd);
		HDC hmemdc = CreateCompatibleDC(hdc);

		HBITMAP holdbmp = (HBITMAP) SelectObject(hmemdc, hbitmap);

		RECT rect;
		GetClientRect(hwnd, &rect);
		BitBlt(hmemdc, 0, 0, rect.right, rect.bottom-fontheightpx,
			   hmemdc, 0, fontheightpx, SRCCOPY);

		SelectObject(hmemdc, holdbmp);
		DeleteDC(hmemdc);

		ReleaseDC(hwnd, hdc);
	}
}

// ---------------------------------------------------------------------------
//	1行下にスクロール
//
void WinMonitor::ScrollDown()
{
	if (height > 2)
	{
		memmove(txtbuf + width, txtbuf, width * (height - 1) * sizeof(TXCHAR));

		HDC hdc = GetDC(hwnd);
		HDC hmemdc = CreateCompatibleDC(hdc);

		HBITMAP holdbmp = (HBITMAP) SelectObject(hmemdc, hbitmap);

		RECT rect;
		GetClientRect(hwnd, &rect);
		BitBlt(hmemdc, 0, fontheightpx, rect.right, rect.bottom-fontheightpx,
			   hmemdc, 0, 0, SRCCOPY);

		SelectObject(hmemdc, holdbmp);
		DeleteDC(hmemdc);

		ReleaseDC(hwnd, hdc);
	}
}

// ---------------------------------------------------------------------------
//	テスト用
//
void WinMonitor::UpdateText()
{
	Puts("???\n");
}

// ---------------------------------------------------------------------------
//	書き込み位置の変更
//
void WinMonitor::Locate(int x, int y)
{
	txp.x = Min(x, width);
	txp.y = Min(y, height);
	txtbufptr = txtbuf + txp.x + txp.y * width;
}

// ---------------------------------------------------------------------------
//	文字列書き込み
//
void WinMonitor::Puts(const char* text)
{
	if (txp.y >= height)
		return;

	int c;
	char* txx;
	while (c = *text++)
	{
		if (c == '\n')
		{
			while (txp.x++ < width)
			{
				txtbufptr->ch = ' ';
				txtbufptr->txcol = txcol;
				txtbufptr->bkcol = bkcol;
				txtbufptr++;
			}
			txp.x = 0, txp.y++;
			if (txp.y >= height)
				return;
		}
		else if (c == '\a')
		{
			// set fgcolor
			if (*text != ';')
			{
				txcolprev = txcol;
				txcol = strtoul(text, &txx, 0);
				text = txx + (*txx == ';');
			}
			else
			{
				txcol = txcolprev;
				text++;
			}
		}
		else if (c == '\b')
		{
			// set bkcolor
			if (*text != ';')
			{
				bkcolprev = bkcol;
				bkcol = strtoul(text, &txx, 0);
				text = txx + (*txx == ';');
			}
			else
			{
				bkcol = bkcolprev;
				text++;
			}
		}
		else
		{
			if (txp.x < width)
			{
				txtbufptr->ch = c;
				txtbufptr->txcol = txcol;
				txtbufptr->bkcol = bkcol;
				txtbufptr++;
				txp.x++;
			}
		}
	}
}

// ---------------------------------------------------------------------------
//	文字列書き込み(書式付)
//
void WinMonitor::Putf(const char* msg, ...)
{
	char buf[512];
	va_list marker;
	va_start(marker, msg);
	wvsprintf(buf, msg, marker);
	va_end(marker);
	Puts(buf);
}

// ---------------------------------------------------------------------------
//	窓書き換え
//
void WinMonitor::Draw(HWND hwnd, HDC hdc)
{
	if (!hbitmap)
		return;

	HDC hmemdc = CreateCompatibleDC(hdc);

	HBITMAP holdbmp = (HBITMAP) SelectObject(hmemdc, hbitmap);

	SetTextColor(hmemdc, 0xffffff);
	SetBkColor(hmemdc, bkcol);
	HFONT holdfont = (HFONT) SelectObject(hmemdc, hfont);

	DrawMain(hmemdc);

	SelectObject(hmemdc, holdfont);

	BitBlt(hdc, 0, 0, clientwidth, clientheight, hmemdc, 0, 0, SRCCOPY);

	SelectObject(hmemdc, holdbmp);
	DeleteDC(hmemdc);
}


void WinMonitor::Update()
{
	if (hwnd)
	{
		InvalidateRect(hwnd, 0, false);
		RedrawWindow(hwnd, 0, 0, RDW_UPDATENOW);
	}
}

// ---------------------------------------------------------------------------
//	ライン数を設定
//
void WinMonitor::SetLines(int nl)
{
	nlines = nl;
	line = Limit(line, nlines, 0);
	if (nlines)
	{
		SCROLLINFO si;
		si.cbSize = sizeof(SCROLLINFO);
		si.fMask = SIF_PAGE | SIF_POS | SIF_RANGE | SIF_DISABLENOSCROLL;
		si.nMin = 0;
		si.nMax = Max(1, nlines+height-2);
		si.nPage = height;
		si.nPos = line;
		SetScrollInfo(hwnd, SB_VERT, &si, true);
	}
}

// ---------------------------------------------------------------------------
//	ライン数を設定
//
void WinMonitor::SetLine(int nl)
{
	line = nl;
	if (nlines)
	{
		SCROLLINFO si;
		si.cbSize = sizeof(SCROLLINFO);
		si.fMask = SIF_POS | SIF_DISABLENOSCROLL;
		si.nMin = 0;
		si.nMax = Max(1, nlines+height-2);
		si.nPage = height;
		si.nPos = line;
		SetScrollInfo(hwnd, SB_VERT, &si, true);
	}
}



// ---------------------------------------------------------------------------
//	スクロールバー処理
//
int WinMonitor::VerticalScroll(int msg)
{
	switch (msg)
	{
	case SB_LINEUP:
		if (--line < 0)
			line = 0;
		else
			ScrollDown();
		break;

	case SB_LINEDOWN:
		if (++line >= nlines)
			line = Max(0, nlines - 1);
		else
			ScrollUp();
		break;

	case SB_PAGEUP:
		line = Max(line - Max(1, height - 1), 0);
		break;

	case SB_PAGEDOWN:
		line = Min(line + Max(1, height - 1), nlines - 1);
		break;

	case SB_TOP:
		line = 0;
		break;

	case SB_BOTTOM:
		line = Max(0, nlines - 1);
		break;

	case SB_THUMBTRACK:
	case SB_THUMBPOSITION:
		line = Limit(GetScrPos(msg == SB_THUMBTRACK), nlines-1, 0);
		break;
	}
	return line;
}

// ---------------------------------------------------------------------------
//	現在のスクロールタブの位置を得る
//
int WinMonitor::GetScrPos(bool track)
{
	SCROLLINFO si;
	memset(&si, 0, sizeof(si));
	si.cbSize = sizeof(si);
	si.fMask = track ? SIF_TRACKPOS : SIF_POS;
	GetScrollInfo(hwnd, SB_VERT, &si);
	return track ? si.nTrackPos : si.nPos;
}

// ---------------------------------------------------------------------------
//	クライエント領域座標を文字座標に変換
//
bool WinMonitor::GetTextPos(POINT* p)
{
	if (fontwidth && fontheightpx)
	{
		p->x /= fontwidth;
		p->y /= fontheightpx;
		return true;
	}
	return false;
}

// ---------------------------------------------------------------------------
//	マウスホイール処理
//	・delta を累積して WHEEL_DELTA 単位で消化する（高精細ホイール対応）
//	・OS のホイールスクロール行数設定を反映する
//
bool WinMonitor::ProcMouseWheel(WPARAM wp)
{
	if (LOWORD(wp) & MK_CONTROL)
	{
		int notch = static_cast<short>(HIWORD(wp)) / WHEEL_DELTA;
		if (notch)
		{
			ChangeFontSize(notch > 0 ? +1 : -1);
			return true;
		}
	}

	if (!nlines)
		return false;

	// OS の設定行数を取得（0 = 設定なし、WHEEL_PAGESCROLL = ページ単位）
	UINT lines = 3;
	SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &lines, 0);

	wheeldelta += static_cast<short>(HIWORD(wp));

	int notches = wheeldelta / WHEEL_DELTA;
	if (!notches)
		return true;                    // 端数を貯めただけ。処理はした扱い
	wheeldelta -= notches * WHEEL_DELTA;

	if (lines == WHEEL_PAGESCROLL)
	{
		int msg = notches > 0 ? SB_PAGEUP : SB_PAGEDOWN;
		int n = notches > 0 ? notches : -notches;
		for (int i = 0; i < n; i++)
			SendMessage(hwnd, WM_VSCROLL, MAKELONG(msg, 0), 0L);
	}
	else
	{
		if (lines < 1)
			lines = 1;
		int msg = notches > 0 ? SB_LINEUP : SB_LINEDOWN;
		int n = (notches > 0 ? notches : -notches) * static_cast<int>(lines);

		// 暴走防止（1回のホイールで動かす上限）
		if (n > 256)
			n = 256;

		for (int i = 0; i < n; i++)
			SendMessage(hwnd, WM_VSCROLL, MAKELONG(msg, 0), 0L);
	}
	return true;
}

// ---------------------------------------------------------------------------
//	Ctrl+ホイールでのフォントサイズ変更
//
void WinMonitor::ChangeFontSize(int dir)
{
	// 現在値に最も近いインデックスを求める
	int idx = 0;
	for (int i = 0; i < nfontsizes; i++)
	{
		if (fontsizetable[i] == fontheight) { idx = i; break; }
		if (fontsizetable[i] < fontheight)  idx = i;
	}
	idx += dir;
	if (idx < 0) idx = 0;
	if (idx >= nfontsizes) idx = nfontsizes - 1;

	if (fontheight != fontsizetable[idx])
	{
		fontheight = fontsizetable[idx];
		SetFont(hwnd, fontheight);
		ResizeWindow(hwnd);
		Update();
	}
}

// ---------------------------------------------------------------------------
//	自動更新頻度を設定
//
void WinMonitor::SetUpdateTimer(int t)
{
	timerinterval = t;
	if (hwnd)
	{
		if (timer)
			KillTimer(hwnd, timer), timer = 0;
		if (t)
			timer = SetTimer(hwnd, 1, timerinterval, 0);
	}
}

// ---------------------------------------------------------------------------
//
//
bool WinMonitor::EnableStatus(bool s)
{
	if (s)
	{
		if (!hwndstatus)
		{
			hwndstatus = CreateStatusWindow(WS_CHILD | WS_VISIBLE, 0, GetHWnd(), 1);
			if (!hwndstatus)
				return false;
		}
	}
	else
	{
		if (hwndstatus)
		{
			DestroyWindow(hwndstatus);
			hwndstatus = 0;
		}
	}
	ResizeWindow(hwnd);
	return true;
}

bool WinMonitor::PutStatus(const char* text, ...)
{
	if (!hwndstatus)
		return false;

	if (!text)
	{
		SendMessage(hwndstatus, SB_SETTEXT, SBT_OWNERDRAW, 0);
		return true;
	}

	va_list a;
	va_start(a, text);
	vsprintf_s(statusbuf, sizeof(statusbuf), text, a);
	va_end(a);

	SendMessage(hwndstatus, SB_SETTEXT, SBT_OWNERDRAW, (LPARAM) statusbuf);
	return true;
}

// ---------------------------------------------------------------------------
//	ダイアログ処理
//
BOOL WinMonitor::DlgProc(HWND hdlg, UINT msg, WPARAM wp, LPARAM lp)
{
	bool r = false;
	int sn;
	SCROLLINFO si;

	switch (msg)
	{
	case WM_INITDIALOG:
		SetFont(hdlg, fontheight);
		ResizeWindow(hdlg);

		if (timerinterval && !timer)
			timer = SetTimer(hdlg, 1, timerinterval, 0);
		break;

	case WM_ACTIVATE:
		if (wp == WA_INACTIVE)
			EnableStatus(false);
		break;

	case WM_INITMENU:
		{
			HMENU hmenu = (HMENU) wp;
			for (int i = 0; i < nfontsizes; i++)
			{
				CheckMenuItem(hmenu, IDM_MON_FONTSIZE_BASE + i,
					(fontheight == fontsizetable[i]) ? MF_CHECKED : MF_UNCHECKED);
			}
		}
		break;

	case WM_COMMAND:
		{
			UINT id = LOWORD(wp);
			if (IDM_MON_FONTSIZE_BASE <= id && id <= IDM_MON_FONTSIZE_LAST)
			{
				int idx = id - IDM_MON_FONTSIZE_BASE;
				if (idx < nfontsizes)
				{
					fontheight = fontsizetable[idx];
					SetFont(hdlg, fontheight);
					ResizeWindow(hdlg);
					Update();
					r = true;
				}
			}
		}
		break;

	case WM_CLOSE:
		SaveMonitorConfig();
		EnableStatus(false);
		if (timer)
			KillTimer(hdlg, timer), timer = 0;

		DestroyWindow(hdlg);
		hwnd = 0;
		r = true;
		break;

	case WM_PAINT:
		{
			PAINTSTRUCT ps;
			HDC hdc = BeginPaint(hdlg, &ps);
			Draw(hdlg, hdc);
			EndPaint(hdlg, &ps);
		}
		r = true;
		break;

	case WM_TIMER:
		Update();
		return true;

	case WM_SIZE:
		ResizeWindow(hdlg);
		if (hwndstatus)
			PostMessage(hwndstatus, WM_SIZE, wp, lp);
		Update();

		r = true;
		break;

	case WM_VSCROLL:
		line = VerticalScroll(LOWORD(wp));

		memset(&si, 0, sizeof(si));
		si.cbSize = sizeof(si);
		si.fMask = SIF_POS;
		si.nPos = line;
		SetScrollInfo(hdlg, SB_VERT, &si, true);

		Update();
		return true;

	case WM_MOUSEWHEEL:
		if (ProcMouseWheel(wp))
			r = true;
		break;

	case WM_KEYDOWN:
	case WM_SYSKEYDOWN:
		// デバッグ用ファンクションキーは、どのモニタ窓にフォーカスがあっても
		// 効くようにする。アクセラレータ(TranslateAccelerator)経由だと
		// メニュー項目の状態やアクティブウインドウの状態に左右されて
		// 取りこぼすため、ここで拾ってメインウインドウへ直接投げる
		//
		// ★ F10 は Windows では「メニューバーを開くキー」なので、単独で押すと
		//    WM_KEYDOWN ではなく WM_SYSKEYDOWN で飛んでくる。WM_KEYDOWN しか
		//    見ていないと DefDlgProc へ流れてメニューモードに入ってしまい
		//    (メニュー項目に下線が出る。F10 のたびに ON/OFF する)、
		//    以降のキー入力がメニュー側に食われて効かなくなる。
		//    ここで受け取って TRUE を返し、メニューモードに入れないこと
		if (hwndowner && !(GetKeyState(VK_MENU) & 0x8000))
		{
			uint id = 0;
			switch (wp)
			{
			case VK_F5:  id = IDM_DBG_PLAY; break;
			case VK_F10: id = IDM_DBG_STEPOVER; break;
			case VK_F11:
				id = (GetKeyState(VK_SHIFT) & 0x8000) ? IDM_DBG_STEPOUT : IDM_DBG_STEPIN;
				break;
			}
			if (id)
			{
				PostMessage(hwndowner, WM_COMMAND, id, 0);
				return TRUE;
			}
		}
		// スクロールは通常のキー入力のときだけ (Alt 併用は対象外)
		if (msg == WM_KEYDOWN && nlines)
		{
			sn = -1;
			switch (wp)
			{
			case VK_UP:		sn = SB_LINEUP; break;
			case VK_PRIOR:	sn = SB_PAGEUP;	break;
			case VK_NEXT:	sn = SB_PAGEDOWN; break;
			case VK_DOWN:	sn = SB_LINEDOWN; break;
			case VK_HOME:	sn = SB_TOP; break;
			case VK_END:	sn = SB_BOTTOM; break;
			}
			if (sn != -1)
				SendMessage(hwnd, WM_VSCROLL, MAKELONG(sn, 0), 0L);
		}
		break;

	case WM_SYSKEYUP:
		// F10 の離した側も捨てる。DefDlgProc に渡すとここでメニューモードへ
		// 入ることがある (WM_SYSKEYDOWN を握り潰しただけでは足りない)
		if (wp == VK_F10 && !(GetKeyState(VK_MENU) & 0x8000))
			return TRUE;
		break;

	case WM_DRAWITEM:
		if ((UINT) wp == 1)
		{
			DRAWITEMSTRUCT* dis = (DRAWITEMSTRUCT*)lp;
			SetBkColor(dis->hDC, GetSysColor(COLOR_3DFACE));
//			SetTextColor(dis->hDC, RGB(255, 0, 0));
			char* text = reinterpret_cast<char*>(dis->itemData);
			if (text)
				TextOut(dis->hDC, dis->rcItem.left, dis->rcItem.top, text, strlen(text));
		}
		break;
	}
	return r;
}

BOOL CALLBACK WinMonitor::DlgProcGate
(HWND hwnd, UINT m, WPARAM w, LPARAM l)
{
	WinMonitor* winmon = 0;

	if ( m == WM_INITDIALOG ) {
		winmon = reinterpret_cast<WinMonitor*>(l);
		if (winmon) {
			::SetWindowLongPtr( hwnd, GWLP_USERDATA, (LONG_PTR)winmon );
		}
	} else {
		winmon = (WinMonitor*)::GetWindowLongPtr( hwnd, GWLP_USERDATA );
	}

	if (winmon) {
		return winmon->DlgProc(hwnd, m, w, l);
	} else {
		return FALSE;
	}
}
