//*****************************************************************************
//
//  TextGridCtrl143.cpp                                    (C)1997-2025 kuzu
//
//*****************************************************************************

// これはMFCのCWndクラスから派生したCTextGridCtrlクラスを定義します。
// ■CWnd
// └■CTextGridCtrl
// これはMicrosoft Visual Studio 2015 Community で作られています。
// このライブラリは、オープンソースです。
// このライブラリは、GNU劣等一般公衆利用許諾契約書(LGPL2.1)に基づき配布されます。
// このライブラリは、Mozilla Public License 2.0 (MPL2.0)に基づき配布されます。

#include <afxwin.h>
#include <afxext.h>
#include <afxdllx.h>
#include "TextGridCtrl143.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

#ifndef CLIP
#define CLIP(A,B,C) ((B)<(A)?(A):((B)>(C)?(C):(B)))
#endif

// これはMFCの拡張DLLである。
// class AFX_EXT_CLASS CXXXX : public CXXX ...
// AFX_EXT_CLASSは_AFXDLL及び_AFXEXTをプリプロセッサに定義した場合に
// MFC により__declspec(dllexport)として定義される。
// #inlcude <afxdllx.h>を忘れぬこと。

extern "C"

static AFX_EXTENSION_MODULE TextGridCtrl143DLL = { NULL, NULL };

int APIENTRY
DllMain (HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) {
	if (dwReason == DLL_PROCESS_ATTACH) {
		TRACE0 ("TEXTGRIDCTRL143.DLL 初期化処理中。\n");
		// 拡張 DLL を１回だけ初期化します。
		AfxInitExtensionModule (TextGridCtrl143DLL, hInstance);
		// この DLL をリソース チェインへ挿入します。
		new CDynLinkLibrary (TextGridCtrl143DLL);
	}
	else if (dwReason == DLL_PROCESS_DETACH) {
		TRACE0 ("TEXTGRIDCTRL143.DLL 終了処理中。\n");
	}
	return 1;   // OK
}

//*****************************************************************************
//
// CTextGridHistoryEventクラス
//
//*****************************************************************************

// コンストラクタ
CTextGridHistoryEvent::CTextGridHistoryEvent () {
	m_nEventType = 0;
	m_nModifiedTopRow = 0;
	m_nModifiedBottomRow = 0;
	m_nModifiedLeftCol = 0;
	m_nModifiedRightCol = 0;
	m_nOldCurRow = 0;
	m_nOldCurCol = 0;
	m_nOldSelectedTopRow = 0;
	m_nOldSelectedBottomRow = 0;
	m_nOldSelectedLeftCol = 0;
	m_nOldSelectedRightCol = 0;
	m_nNewCurRow = 0;
	m_nNewCurCol = 0;
	m_nNewSelectedTopRow = 0;
	m_nNewSelectedBottomRow = 0;
	m_nNewSelectedLeftCol = 0;
	m_nNewSelectedRightCol = 0;

}

// デストラクタ
CTextGridHistoryEvent::~CTextGridHistoryEvent () {
	m_theOldCellStringArray.RemoveAll ();
	m_theOldReadOnlyArray.RemoveAll ();
	m_theOldTextColorArray.RemoveAll ();
	m_theNewCellStringArray.RemoveAll ();
	m_theNewReadOnlyArray.RemoveAll ();
	m_theNewTextColorArray.RemoveAll ();
}

//*****************************************************************************
//
// CTextGridHistoryBlockクラス
//
//*****************************************************************************

// コンストラクタ
CTextGridHistoryBlock::CTextGridHistoryBlock () {
	m_strName = _T ("");
	m_theTime = 0;
}

// デストラクタ
CTextGridHistoryBlock::~CTextGridHistoryBlock () {
	int nNumHistoryEvent = m_theHistoryEventArray.GetCount ();
	for (int i = nNumHistoryEvent - 1; i >= 0; i--) {
		CTextGridHistoryEvent* pHistoryEvent = (CTextGridHistoryEvent*)m_theHistoryEventArray.GetAt (i);
		delete (pHistoryEvent);
		m_theHistoryEventArray.RemoveAt (i);
	}
}

//*****************************************************************************
//
// CTextGridEditクラス
//
//*****************************************************************************

// メッセージマップ
IMPLEMENT_DYNCREATE (CTextGridEdit, CEdit)

BEGIN_MESSAGE_MAP (CTextGridEdit, CEdit)
	ON_WM_KILLFOCUS ()
	ON_WM_KEYDOWN ()
	ON_WM_CHAR ()
END_MESSAGE_MAP()

// コンストラクタ
CTextGridEdit::CTextGridEdit () {
}

// デストラクタ
CTextGridEdit::~CTextGridEdit () {
}

// フォーカスが失われたとき
void CTextGridEdit::OnKillFocus (CWnd* pNewWnd) {
	_RPTF1 (_CRT_WARN, "CInplaceEditBox::OnKillFocus (pNewWnd=0x%08x)\n", pNewWnd);
	// 別のダイアログやフレームウィンドウにフォーカスが直接移動したとき、
	// このコントロールを含む親ウィンドウにフォーカスが失われたことを通知する。
	CWnd* pParentWnd = GetParent ();
	if (pParentWnd != pNewWnd) {
		pParentWnd->SendMessage (WM_KILLFOCUS, (WPARAM)pNewWnd, (LPARAM)0L);
	}
	else {
		//pParentWnd->SetFocus ();
	}
	CEdit::OnKillFocus (pNewWnd); //20080809追加(カレットの消失防止)
}


// キー押し下げ時
void CTextGridEdit::OnKeyDown (UINT nChar, UINT nRepCnt, UINT nFlags) {
	
	CWnd* pParentWnd = GetParent ();
	switch (nChar) {
	case VK_UP:
	case VK_LEFT:
	case VK_RIGHT:
	case VK_DOWN:
		pParentWnd->SetFocus ();
		pParentWnd->PostMessage (WM_KEYDOWN, VK_RETURN, (nRepCnt << 16) | nFlags);
		pParentWnd->PostMessage (WM_KEYDOWN, nChar, (nRepCnt << 16) | nFlags);
		break;
	case VK_RETURN:
		pParentWnd->SetFocus ();	
		pParentWnd->PostMessage (WM_KEYDOWN, VK_RETURN, (nRepCnt << 16) | nFlags);
		break;
	case VK_ESCAPE:
		pParentWnd->SetFocus ();
		pParentWnd->PostMessage (WM_KEYDOWN, VK_ESCAPE, (nRepCnt << 16) | nFlags);
		break;
	}
	CWnd::OnKeyDown (nChar, nRepCnt, nFlags);
}

// 文字入力時
void CTextGridEdit::OnChar (UINT nChar, UINT nRepCnt, UINT nFlags) {
	// Enterを押したときにプープー音が鳴るのを防ぐ
	switch (nChar) {
	case VK_RETURN:
		return;
	}
	CEdit::OnChar (nChar, nRepCnt, nFlags);
}

//*****************************************************************************
//
// CTextGridCtrlクラス
//
//*****************************************************************************

#define TEXTGRIDCTRL_RANGE(A,B,C) ((A)>(B)?(A):((B)>(C)?(C):(B)))

#define IDC_TEXTBOX 3232

IMPLEMENT_DYNCREATE (CTextGridCtrl, CWnd)

BEGIN_MESSAGE_MAP (CTextGridCtrl, CWnd)
	ON_WM_CREATE ()
	ON_WM_DESTROY ()
	ON_WM_SIZE ()
	ON_WM_ENABLE ()
	ON_WM_SETFOCUS ()
	ON_WM_KILLFOCUS ()
	ON_WM_VSCROLL ()
	ON_WM_HSCROLL ()
	ON_WM_ERASEBKGND ()
	ON_WM_PAINT ()
	ON_WM_LBUTTONDOWN ()
	ON_WM_RBUTTONDOWN ()
	ON_WM_MOUSEMOVE ()
	ON_WM_LBUTTONUP ()
	ON_WM_LBUTTONDBLCLK ()
	ON_WM_KEYDOWN ()
	ON_WM_CHAR ()
	ON_WM_MOUSEWHEEL ()
	ON_MESSAGE (WM_IME_STARTCOMPOSITION, OnImeStartComposition)
	ON_COMMAND (IDM_CONTEXT_UNDO, OnContextUndo)
	ON_UPDATE_COMMAND_UI (IDM_CONTEXT_UNDO, OnUpdateContextUndo)
	ON_COMMAND (IDM_CONTEXT_REDO, OnContextRedo)
	ON_UPDATE_COMMAND_UI (IDM_CONTEXT_REDO, OnUpdateContextRedo)
	ON_COMMAND (IDM_CONTEXT_CUT, OnContextCut)
	ON_UPDATE_COMMAND_UI (IDM_CONTEXT_CUT, OnUpdateContextCut)
	ON_COMMAND (IDM_CONTEXT_COPY, OnContextCopy)
	ON_UPDATE_COMMAND_UI (IDM_CONTEXT_COPY, OnUpdateContextCopy)
	ON_COMMAND (IDM_CONTEXT_PASTE, OnContextPaste)
	ON_UPDATE_COMMAND_UI (IDM_CONTEXT_PASTE, OnUpdateContextPaste)
	ON_COMMAND (IDM_CONTEXT_DELETE, OnContextDelete)
	ON_UPDATE_COMMAND_UI (IDM_CONTEXT_DELETE, OnUpdateContextDelete)
END_MESSAGE_MAP ()

// コンストラクタ
CTextGridCtrl::CTextGridCtrl () {
	this->CTextGridCtrl::CTextGridCtrl (1, 1);
}

// 行数と列数を指定したコンストラクタ
CTextGridCtrl::CTextGridCtrl (int nNumRows, int nNumCols) {
	ASSERT (1 <= nNumRows && nNumRows <= 65535);
	ASSERT (1 <= nNumCols && nNumCols <= 65535);
	m_bReadOnly = FALSE;
	m_nNumRows = nNumRows;
	m_nNumCols = nNumCols;
	m_theRowHeightArray.SetSize (m_nNumRows);
	m_theColWidthArray.SetSize (m_nNumCols);
	m_theRowTitleArray.SetSize (m_nNumRows);
	m_theColTitleArray.SetSize (m_nNumCols);
	m_theCellStringArray.SetSize (m_nNumRows * m_nNumCols);
	m_theReadOnlyArray.SetSize (m_nNumRows * m_nNumCols);
	m_theTextColorArray.SetSize (m_nNumRows * m_nNumCols);
	m_nCurRow = 0;
	m_nCurCol = 0;
	m_nVisibleTopRow = 0;
	m_nVisibleLeftCol = 0;
	m_nTitleWidth = 64;
	m_nTitleHeight = 18;
	m_nSelectedTopRow = 0;
	m_nSelectedBottomRow = 0;
	m_nSelectedLeftCol = 0;
	m_nSelectedRightCol = 0;
	m_nMouseMode = 0;
	m_nMouseDownRow = 0;
	m_nMouseDownCol = 0;
	m_nKeyMoveRow = 0;
	m_nKeyMoveCol = 0;
	m_bCut = FALSE;
	for (int i = 0; i < GetNumRows (); i++) {
		SetRowHeight (i, 18);
	}
	for (int j = 0; j < GetNumCols (); j++) {
		SetColWidth (j, 64);
	}
	for (int i = 0; i < GetNumRows (); i++) {
		for (int j = 0; j < GetNumCols (); j++) {
			m_theReadOnlyArray.SetAt (i * m_nNumRows + j, 0);
			m_theTextColorArray.SetAt (i * m_nNumRows + j, 0);
		}
	}
	m_bNotify = FALSE;
	ClearHistory ();
}

// デストラクタ
CTextGridCtrl::~CTextGridCtrl () {
	m_theRowHeightArray.RemoveAll ();
	m_theColWidthArray.RemoveAll ();
	m_theRowTitleArray.RemoveAll ();
	m_theColTitleArray.RemoveAll ();
	m_theCellStringArray.RemoveAll ();
	m_theReadOnlyArray.RemoveAll ();
	m_theTextColorArray.RemoveAll ();
	ClearHistory ();
}


// オペレーション

// 行数の設定(この関数は頻繁に使うべきではない)
BOOL CTextGridCtrl::SetNumRows (int nNumRows) {
	ASSERT (1 <= nNumRows && nNumRows <= 1024);
	if (nNumRows <= 1 || nNumRows > 1024) {
		return FALSE;
	}
	int nOldNumRows = m_nNumRows;
	int nOldNumCols = m_nNumCols;
	CUIntArray theOldRowHeightArray;
	CStringArray theOldRowTitleArray;
	CStringArray theOldCellStringArray;
	CUIntArray theOldReadOnlyArray;
	CUIntArray theOldTextColorArray;
	theOldRowHeightArray.Copy (m_theRowHeightArray);
	theOldRowTitleArray.Copy (m_theRowTitleArray);
	theOldCellStringArray.Copy (m_theCellStringArray);
	theOldReadOnlyArray.Copy (m_theReadOnlyArray);
	theOldTextColorArray.Copy (m_theTextColorArray);
	m_nNumRows = nNumRows;
	m_theRowHeightArray.SetSize (m_nNumRows);
	m_theRowTitleArray.SetSize (m_nNumRows);
	m_theCellStringArray.SetSize (m_nNumRows * m_nNumCols);
	m_theReadOnlyArray.SetSize (m_nNumRows * m_nNumCols);
	m_theTextColorArray.SetSize (m_nNumRows * m_nNumCols);
	int i, j;
	for (i = 0; i < m_nNumRows; i++) {
		if (i < nOldNumRows) {
			int nTemp = theOldRowHeightArray.GetAt (i);
			m_theRowHeightArray.SetAt (i, nTemp);
		}
		else {
			m_theRowHeightArray.SetAt (i, 18);
		}
	}
	for (i = 0; i < m_nNumRows; i++) {
		if (i < nOldNumRows) {
			CString strTemp = theOldRowTitleArray.GetAt (i);
			m_theRowTitleArray.SetAt (i, strTemp);
		}
		else {
			m_theRowTitleArray.SetAt (i, _T(""));
		}
	}
	for (i = 0; i < m_nNumRows; i++) {
		if (i < nOldNumRows) {
			for (j = 0; j < m_nNumCols; j++) {
				if (j < nOldNumCols) {
					CString strTemp = theOldCellStringArray.GetAt (i * nOldNumCols + j);
					m_theCellStringArray.SetAt (i * m_nNumCols + j, strTemp);
					UINT nReadOnly = theOldReadOnlyArray.GetAt (i * nOldNumCols + j);
					m_theReadOnlyArray.SetAt (i * m_nNumCols + j, nReadOnly);
					UINT nTextColor = theOldTextColorArray.GetAt (i * nOldNumCols + j);
					m_theTextColorArray.SetAt (i * m_nNumCols + j, nTextColor);
				}
				else {
					m_theCellStringArray.SetAt (i * m_nNumCols + j, _T (""));
					m_theReadOnlyArray.SetAt (i * m_nNumCols + j, 0);
					m_theTextColorArray.SetAt (i * m_nNumCols + j, RGB (0, 0, 0));
				}
			}
		}
	}
	//  20250913 行数が減少した場合に備えカレントセル・選択範囲の値も有効範囲内に更新
	m_nCurRow = TEXTGRIDCTRL_RANGE (0, m_nCurRow, m_nNumRows - 1); // 20250913 追加
	m_nSelectedTopRow = TEXTGRIDCTRL_RANGE (0, m_nSelectedTopRow, m_nNumRows - 1); // 20250913 追加
	m_nSelectedBottomRow = TEXTGRIDCTRL_RANGE (0, m_nSelectedBottomRow, m_nNumRows - 1); // 20250913 追加

	SetScrollRange (SB_VERT, 0, m_nNumRows - 1, TRUE);

	ClearHistory ();

	return TRUE;
}

// 列数の設定(この関数は頻繁に使うべきではない)
BOOL CTextGridCtrl::SetNumCols (int nNumCols) {
	ASSERT (1 <= nNumCols && nNumCols <= 1024);
	if (nNumCols <= 1 || nNumCols > 1024) {
		return FALSE;
	}
	int nOldNumRows = m_nNumRows;
	int nOldNumCols = m_nNumCols;
	CUIntArray theOldColWidthArray;
	CStringArray theOldColTitleArray;
	CStringArray theOldCellStringArray;
	CUIntArray theOldReadOnlyArray;
	CUIntArray theOldTextColorArray;
	theOldColWidthArray.Copy (m_theColWidthArray);
	theOldColTitleArray.Copy (m_theColTitleArray);
	theOldCellStringArray.Copy (m_theCellStringArray);
	theOldReadOnlyArray.Copy (m_theReadOnlyArray);
	theOldTextColorArray.Copy (m_theTextColorArray);
	m_nNumCols = nNumCols;
	m_theColWidthArray.SetSize (m_nNumCols);
	m_theColTitleArray.SetSize (m_nNumCols);
	m_theCellStringArray.SetSize (m_nNumRows * m_nNumCols);
	m_theReadOnlyArray.SetSize (m_nNumRows * m_nNumCols);
	m_theTextColorArray.SetSize (m_nNumRows * m_nNumCols);
	int i, j;
	for (j = 0; j < m_nNumCols; j++) {
		if (j < nOldNumCols) {
			int nTemp = theOldColWidthArray.GetAt (j);
			m_theColWidthArray.SetAt (j, nTemp);
		}
		else {
			m_theColWidthArray.SetAt (j, 64);
		}
	}
	for (j = 0; j < m_nNumCols; j++) {
		if (j < nOldNumCols) {
			CString strTemp = theOldColTitleArray.GetAt (j);
			m_theColTitleArray.SetAt (j, strTemp);
		}
		else {
			m_theColTitleArray.SetAt (j, _T(""));
		}
	}
	for (i = 0; i < m_nNumRows; i++) {
		if (i < nOldNumRows) {
			for (j = 0; j < m_nNumCols; j++) {
				if (j < nOldNumCols) {
					CString strTemp = theOldCellStringArray.GetAt (i * nOldNumCols + j);
					m_theCellStringArray.SetAt (i * m_nNumCols + j, strTemp);
					UINT nReadOnly = theOldReadOnlyArray.GetAt (i * nOldNumCols + j);
					m_theReadOnlyArray.SetAt (i * m_nNumCols + j, nReadOnly);
					UINT nTextColor = theOldTextColorArray.GetAt (i * nOldNumCols + j);
					m_theTextColorArray.SetAt (i * m_nNumCols + j, nTextColor);
				}
				else {
					m_theCellStringArray.SetAt (i * m_nNumCols + j, _T(""));
					m_theReadOnlyArray.SetAt (i * m_nNumCols + j, 0);
					m_theTextColorArray.SetAt (i * m_nNumCols + j, RGB (0, 0, 0));
				}
			}
		}
	}
	//  20250913 列数が減少した場合に備えカレントセル・選択範囲の値も有効範囲内に更新
	m_nCurCol = TEXTGRIDCTRL_RANGE (0, m_nCurCol, m_nNumCols - 1); // 20250913 追加
	m_nSelectedLeftCol = TEXTGRIDCTRL_RANGE (0, m_nSelectedLeftCol, m_nNumCols - 1); // 20250913 追加
	m_nSelectedRightCol = TEXTGRIDCTRL_RANGE (0, m_nSelectedRightCol, m_nNumCols - 1); // 20250913 追加

	SetScrollRange (SB_HORZ, 0, m_nNumCols - 1, TRUE);

	ClearHistory ();

	return TRUE;
}

// タイトル行の高さの設定
BOOL CTextGridCtrl::SetTitleHeight (int nHeight) {
	ASSERT (0 <= nHeight && nHeight <= 1024);
	if (nHeight <= 0 || nHeight > 1024) {
		return FALSE;
	}
	m_nTitleHeight = nHeight;
	return TRUE;
}

// タイトル列の幅の設定
BOOL CTextGridCtrl::SetTitleWidth (int nWidth) {
	ASSERT (0 <= nWidth && nWidth <= 1024);
	if (nWidth <= 0 || nWidth > 1024) {
		return FALSE;
	}
	m_nTitleWidth = nWidth;
	return TRUE;
}

// 行高さの取得
int CTextGridCtrl::GetRowHeight (int nRow) const {
	ASSERT (0 <= nRow && nRow < m_nNumRows);
	if (nRow < 0 || nRow >= m_nNumRows) {
		return 0;
	}
	return (int)(m_theRowHeightArray.GetAt (nRow));
}

// 列幅の取得
int CTextGridCtrl::GetColWidth (int nCol) const {
	ASSERT (0 <= nCol && nCol < m_nNumCols);
	if (nCol < 0 || nCol >= m_nNumCols) {
		return 0;
	}
	return (int)(m_theColWidthArray.GetAt (nCol));
}

// 行高さの設定
BOOL CTextGridCtrl::SetRowHeight (int nRow, int nHeight) {
	ASSERT (0 <= nRow && nRow < m_nNumRows);
	ASSERT (0 < nHeight && nHeight <= 1024);
	if (nRow < 0 || nRow >= m_nNumRows) {
		return FALSE;
	}
	if (nHeight <= 0 || nHeight > 1024) {
		return FALSE;
	}
	m_theRowHeightArray.SetAt (nRow, nHeight);
	if (m_hWnd != NULL && GetParent () != NULL && m_bNotify == FALSE) {
		m_bNotify = TRUE;
		TextGridNotify theNotify = { 0 };
		theNotify.hdr.code = TGN_ROWHEIGHTCHANGE;
		theNotify.hdr.hwndFrom = (this->m_hWnd);
		theNotify.hdr.idFrom = GetDlgCtrlID ();
		theNotify.m_nRow = nRow;
		GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
		m_bNotify = FALSE;
	}
	return TRUE;
}

// 列幅の設定
BOOL CTextGridCtrl::SetColWidth (int nCol, int nWidth) {
	ASSERT (0 <= nCol && nCol < m_nNumCols);
	ASSERT (0 < nWidth && nWidth <= 1024);
	m_theColWidthArray.SetAt (nCol, nWidth);
	if (nCol < 0 || nCol >= m_nNumCols) {
		return FALSE;
	}
	if (nWidth <= 0 || nWidth > 1024) {
		return FALSE;
	}
	if (m_hWnd != NULL && GetParent () != NULL && m_bNotify == FALSE) {
		m_bNotify = TRUE;
		TextGridNotify theNotify = {0};
		theNotify.hdr.code = TGN_COLWIDTHCHANGE;
		theNotify.hdr.hwndFrom = (this->m_hWnd);
		theNotify.hdr.idFrom = GetDlgCtrlID ();
		theNotify.m_nCol = nCol;
		GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
		m_bNotify = FALSE;
	}
	return TRUE;
}

// セル部合計高さの取得
int CTextGridCtrl::GetCellSumHeight () const {
	int nHeight = 0;
	for (int i = 0; i < m_nNumRows; i++) {
		nHeight += GetRowHeight (i);
	}
	return nHeight;
}

// セル部合計幅の取得
int CTextGridCtrl::GetCellSumWidth () const {
	int nWidth = 0;
	for (int j = 0; j < m_nNumCols; j++) {
		nWidth += GetColWidth (j);
	}
	return nWidth;
}
	
// 行タイトルの取得
CString CTextGridCtrl::GetRowTitle (int nRow) const {
	ASSERT (0 <= nRow && nRow < m_nNumRows);
	if (nRow < 0 || nRow >= m_nNumRows) {
		return _T("");
	}
	return m_theRowTitleArray.GetAt (nRow);	
}

// 列タイトルの取得
CString CTextGridCtrl::GetColTitle (int nCol) const {
	ASSERT (0 <= nCol && nCol < m_nNumCols);
	if (nCol < 0 || nCol >= m_nNumCols) {
		return _T("");
	}
	return m_theColTitleArray.GetAt (nCol);	
}

// グリッド文字列の取得
CString CTextGridCtrl::GetCellString (int nRow, int nCol) const {
	ASSERT (0 <= nRow && nRow < m_nNumRows);
	ASSERT (0 <= nCol && nCol < m_nNumCols);
	if (nRow < 0 || nRow >= m_nNumRows) {
		return _T("");
	}
	if (nCol < 0 || nCol >= m_nNumCols) {
		return _T("");
	}
	return m_theCellStringArray.GetAt (nRow * m_nNumCols + nCol);	
}

// グリッドの文字列をdoubleに取得
double CTextGridCtrl::GetCellDouble (int nRow, int nCol) const {
	CString strValue = GetCellString (nRow, nCol);
	return _ttof (strValue);
}

// 行タイトルの設定
BOOL CTextGridCtrl::SetRowTitle (int nRow, CString strRowTitle) {
	ASSERT (0 <= nRow && nRow < m_nNumRows);
	if (nRow < 0 || nRow >= m_nNumRows) {
		return FALSE;
	}
	m_theRowTitleArray.SetAt (nRow, strRowTitle);	
	return TRUE;
}

// 行タイトルの設定
BOOL CTextGridCtrl::SetRowTitleWithNotify (int nRow, CString strRowTitle) {
	BOOL bRet = SetRowTitle (nRow, strRowTitle);
	if (m_hWnd != NULL && GetParent () != NULL && m_bNotify == FALSE) {
		m_bNotify = TRUE;
		TextGridNotify theNotify = { 0 };
		theNotify.hdr.code = TGN_ROWTITLECHANGE;
		theNotify.hdr.hwndFrom = (this->m_hWnd);
		theNotify.hdr.idFrom = GetDlgCtrlID ();
		theNotify.m_nRow = nRow;
		GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
		m_bNotify = FALSE;
	}
	return bRet;
}

// 列タイトルの設定
BOOL CTextGridCtrl::SetColTitle (int nCol, CString strColTitle) {
	ASSERT (0 <= nCol && nCol < m_nNumCols);
	if (nCol < 0 || nCol >= m_nNumCols) {
		return FALSE;
	}
	m_theColTitleArray.SetAt (nCol, strColTitle);	
	return TRUE;
}

// 列タイトルの設定
BOOL CTextGridCtrl::SetColTitleWithNotify (int nCol, CString strColTitle) {
	BOOL bRet = SetColTitle (nCol, strColTitle);
	if (m_hWnd != NULL && GetParent () != NULL && m_bNotify == FALSE) {
		m_bNotify = TRUE;
		TextGridNotify theNotify = { 0 };
		theNotify.hdr.code = TGN_COLTITLECHANGE;
		theNotify.hdr.hwndFrom = (this->m_hWnd);
		theNotify.hdr.idFrom = GetDlgCtrlID ();
		theNotify.m_nCol = nCol;
		GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
		m_bNotify = FALSE;
	}
	return bRet;
}


// グリッド文字列の設定
BOOL CTextGridCtrl::SetCellString (int nRow, int nCol, CString strGridString) {
	ASSERT (0 <= nRow && nRow < m_nNumRows);
	ASSERT (0 <= nCol && nCol < m_nNumCols);
	if (nRow < 0 || nRow >= m_nNumRows) {
		return FALSE;
	}
	if (nCol < 0 || nCol >= m_nNumCols) {
		return FALSE;
	}

	// 履歴登録
	if (m_nCurHistoryIndex >= 0) {
		CTextGridHistoryEvent* pHistoryEvent = new CTextGridHistoryEvent ();
		if (pHistoryEvent == NULL) {
			AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
			return FALSE;
		}
		pHistoryEvent->m_nEventType = TEXTGRIDCTRL_MODIFYCELLSTRING;
		pHistoryEvent->m_nModifiedTopRow = nRow;
		pHistoryEvent->m_nModifiedBottomRow = nRow;
		pHistoryEvent->m_nModifiedLeftCol = nCol;
		pHistoryEvent->m_nModifiedRightCol = nCol;
		CString strOldString = m_theCellStringArray.GetAt (nRow * m_nNumCols + nCol);
		pHistoryEvent->m_theOldCellStringArray.Add (strOldString);
		pHistoryEvent->m_theNewCellStringArray.Add (strGridString);
		AddHistoryEvent (pHistoryEvent);
	}

	// 設定
	m_theCellStringArray.SetAt (nRow * m_nNumCols + nCol, strGridString);	

	return TRUE;
}

// グリッド文字列の設定(親への通知あり)
BOOL CTextGridCtrl::SetCellStringWithNotify (int nRow, int nCol, CString strGridString) {
	BOOL bRet = SetCellString (nRow, nCol, strGridString);
	if (bRet) {
		if (m_hWnd != NULL && GetParent () != NULL && m_bNotify == FALSE) {
			m_bNotify = TRUE;
			TextGridNotify theNotify = { 0 };
			theNotify.hdr.code = TGN_CELLSTRINGCHANGE;
			theNotify.hdr.hwndFrom = (this->m_hWnd);
			theNotify.hdr.idFrom = GetDlgCtrlID ();
			theNotify.m_nRow = nRow;
			theNotify.m_nCol = nCol;
			GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
			m_bNotify = FALSE;
		}
	}
	return bRet;
}

// WMCOMMANによる親ウィンドウへの通知で、どのセルが変更されたのかを取得する。
/*BOOL CTextGridCtrl::GetLastNotifyInfo (int* pRow, int* pCol, int* pLeft, int* pTop, int* pRight, int* pBottom) const {
	if (pRow != NULL) {
		*pRow = m_nLastNotifyRow;
	}
	if (pCol != NULL) {
		*pCol = m_nLastNotifyCol;
	}
	if (pLeft != NULL) {
		*pLeft = m_nLastNotifyLeftCol;
	}
	if (pTop != NULL) {
		*pTop = m_nLastNotifyTopRow;
	}
	if (pRight != NULL) {
		*pRight = m_nLastNotifyRightCol;
	}
	if (pBottom != NULL) {
		*pBottom = m_nLastNotifyBottomRow;
	}
	return TRUE;
}*/

// グリッドの文字列をdouble値から設定
BOOL CTextGridCtrl::SetCellDouble (int nRow, int nCol, double dValue, LPCTSTR lpszFormat) {
	CString strFormat = lpszFormat != NULL ? lpszFormat : _T("%1.15g");
	CString strValue;
	strValue.Format (strFormat, dValue);
	return SetCellString (nRow, nCol, strValue);
}

// グリッドの文字列をdouble値から設定
BOOL CTextGridCtrl::SetCellDoubleWithNotify (int nRow, int nCol, double dValue, LPCTSTR lpszFormat) {
	CString strFormat = lpszFormat != NULL ? lpszFormat : _T ("%1.15g");
	CString strValue;
	strValue.Format (strFormat, dValue);
	return SetCellStringWithNotify (nRow, nCol, strValue);
}

// すべての行タイトル(左端)を削除
void CTextGridCtrl::DeleteAllRowTitle () {
	for (int i = 0; i < m_nNumRows; i++) {
		m_theRowTitleArray.SetAt (i, _T(""));
	}
}

// すべての行タイトル(左端)を削除
void CTextGridCtrl::DeleteAllRowTitleWithNotify () {
	DeleteAllRowTitle ();
	if (m_hWnd != NULL && GetParent () != NULL && m_bNotify == FALSE) {
		m_bNotify = TRUE;
		TextGridNotify theNotify = { 0 };
		theNotify.hdr.code = TGN_ROWTITLECHANGE;
		theNotify.hdr.hwndFrom = (this->m_hWnd);
		theNotify.hdr.idFrom = GetDlgCtrlID ();
		theNotify.m_nRow = -1;
		theNotify.m_nCol = -1;
		GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
		m_bNotify = FALSE;
	}
}

// すべての列タイトル(上端)を削除
void CTextGridCtrl::DeleteAllColTitle () {
	for (int j = 0; j < m_nNumCols; j++) {
		m_theColTitleArray.SetAt (j, _T(""));
	}
}

// すべての列タイトル(上端)を削除
void CTextGridCtrl::DeleteAllColTitleWithNotify () {
	DeleteAllColTitle ();
	if (m_hWnd != NULL && GetParent () != NULL && m_bNotify == FALSE) {
		m_bNotify = TRUE;
		TextGridNotify theNotify = { 0 };
		theNotify.hdr.code = TGN_COLTITLECHANGE;
		theNotify.hdr.hwndFrom = (this->m_hWnd);
		theNotify.hdr.idFrom = GetDlgCtrlID ();
		theNotify.m_nRow = -1;
		theNotify.m_nCol = -1;
		GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
		m_bNotify = FALSE;
	}
}



// すべてのセルを削除
void CTextGridCtrl::DeleteAllCellString () {
	for (int i = 0; i < m_nNumRows; i++) {
		for (int j = 0; j < m_nNumCols; j++) {
			m_theCellStringArray.SetAt (i * m_nNumCols + j, _T(""));
		}
	}
}

// すべてのセルを削除
void CTextGridCtrl::DeleteAllCellStringWithNotify () {
	DeleteAllCellString ();
	if (m_hWnd != NULL && GetParent () != NULL && m_bNotify == FALSE) {
		m_bNotify = TRUE;
		TextGridNotify theNotify = { 0 };
		theNotify.hdr.code = TGN_CELLSTRINGCHANGE;
		theNotify.hdr.hwndFrom = (this->m_hWnd);
		theNotify.hdr.idFrom = GetDlgCtrlID ();
		theNotify.m_nRow = -1;
		theNotify.m_nCol = -1;
		GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
		m_bNotify = FALSE;
	}
}


// 行の挿入
BOOL CTextGridCtrl::InsertRow (int nRow) {
	return FALSE;
}

// 列の挿入
BOOL CTextGridCtrl::InsertCol (int nCol) {
	return FALSE;
}

// 行の削除
BOOL CTextGridCtrl::RemoveRow (int nRow) {
	return FALSE;
}

// 列の削除
BOOL CTextGridCtrl::RemoveCol (int nCol) {
	return FALSE;
}

// 下端に表示される行番号を取得する。
int CTextGridCtrl::GetVisibleBottomRow (long lLogPixelsX, long lLogPixelsY) const {
	CRect rcClient;
	GetClientRect (&rcClient);
	int nSumY = m_nTitleHeight * lLogPixelsY / 96;
	int i;
	for (i = m_nVisibleTopRow; i < m_nNumRows; i++) {
		nSumY += GetRowHeight (i) * lLogPixelsY / 96;
		if (nSumY > rcClient.Height ()) {
			break;
		}
	}
	return i;
}

// 右端に表示される行番号を取得する。
int CTextGridCtrl::GetVisibleRightCol (long lLogPixelsX, long lLogPixelsY) const {
	CRect rcClient;
	GetClientRect (&rcClient);
	int nSumX = m_nTitleWidth * lLogPixelsX / 96;
	int j;
	for (j = m_nVisibleLeftCol; j < m_nNumCols; j++) {
		nSumX += GetColWidth (j) * lLogPixelsX / 96;
		if (nSumX > rcClient.Width ()) {
			break;
		}
	}
	return j;
}

// 上端に表示される行番号を設定する(ウィンドウがあるときのみ)
BOOL CTextGridCtrl::SetVisibleTopRow (int nVisibleTopRow, long lLogPixelsX, long lLogPixelsY) {
	ASSERT (0 <= nVisibleTopRow && nVisibleTopRow < m_nNumRows);
	ASSERT (m_hWnd);
	if (nVisibleTopRow < 0 || nVisibleTopRow >= m_nNumRows) {
		return FALSE;
	}
	if (m_hWnd == NULL) {
		return FALSE;
	}
	if (m_nVisibleTopRow != nVisibleTopRow) {
		m_nVisibleTopRow = nVisibleTopRow;
		SetScrollPos (SB_VERT, m_nVisibleTopRow, TRUE);
	}
	return TRUE;
}

// 左端に表示される列番号を設定する(ウィンドウがあるときのみ)
BOOL CTextGridCtrl::SetVisibleLeftCol (int nVisibleLeftCol, long lLogPixelsX, long lLogPixelsY) {
	ASSERT (0 <= nVisibleLeftCol && nVisibleLeftCol < m_nNumCols);
	ASSERT (m_hWnd);
	if (nVisibleLeftCol < 0 || nVisibleLeftCol >= m_nNumCols) {
		return FALSE;
	}
	if (m_hWnd == NULL) {
		return FALSE;
	}
	if (m_nVisibleLeftCol != nVisibleLeftCol) {
		m_nVisibleLeftCol = nVisibleLeftCol;
		SetScrollPos (SB_HORZ, m_nVisibleLeftCol, TRUE);
	}
	return TRUE;
}

// 下端に表示される行番号を設定する(ウィンドウがあるときのみ)
BOOL CTextGridCtrl::SetVisibleBottomRow (int nVisibleBottomRow, long lLogPixelsX, long lLogPixelsY) {
	ASSERT (0 <= nVisibleBottomRow && nVisibleBottomRow < m_nNumRows);
	ASSERT (m_hWnd);
	if (nVisibleBottomRow < 0 || nVisibleBottomRow >= m_nNumRows) {
		return FALSE;
	}
	if (m_hWnd == NULL) {
		return FALSE;
	}
	CRect rcClient;
	GetClientRect (&rcClient);
	if (GetRowHeight (nVisibleBottomRow) + m_nTitleHeight > rcClient.Height ()) {
		VERIFY (SetVisibleTopRow (nVisibleBottomRow, lLogPixelsX, lLogPixelsY));
	}
	else {
		int nSumY = m_nTitleHeight * lLogPixelsY / 96;
		int i;
		for (i = nVisibleBottomRow; i >= 0; i--) {
			nSumY += GetRowHeight (TEXTGRIDCTRL_RANGE (0, i, m_nNumRows - 1)) * lLogPixelsY / 96;
			if (nSumY > rcClient.Height ()) {
				break;
			}
		}
		VERIFY (SetVisibleTopRow (TEXTGRIDCTRL_RANGE (0, i + 1, m_nNumRows - 1), lLogPixelsX, lLogPixelsY));
	}
	return TRUE;
}

// 右端に表示される列番号を設定する(ウィンドウがあるときのみ)
BOOL CTextGridCtrl::SetVisibleRightCol (int nVisibleRightCol, long lLogPixelsX, long lLogPixelsY) {
	ASSERT (0 <= nVisibleRightCol && nVisibleRightCol < m_nNumCols);
	ASSERT (m_hWnd);
	if (nVisibleRightCol < 0 || nVisibleRightCol >= m_nNumCols) {
		return FALSE;
	}
	if (m_hWnd == NULL) {
		return FALSE;
	}
	CRect rcClient;
	GetClientRect (&rcClient);
	if (GetColWidth (nVisibleRightCol) + m_nTitleWidth > rcClient.Width ()) {
		VERIFY (SetVisibleLeftCol (nVisibleRightCol, lLogPixelsX, lLogPixelsY));
	}
	else {
		int nSumX = m_nTitleWidth * lLogPixelsX / 96;
		int j;
		for (j = nVisibleRightCol; j >= 0; j--) {
			nSumX += GetColWidth (TEXTGRIDCTRL_RANGE (0, j, m_nNumCols - 1)) * lLogPixelsX / 96;
			if (nSumX > rcClient.Width ()) {
				break;
			}
		}
		VERIFY (SetVisibleLeftCol (TEXTGRIDCTRL_RANGE (0, j + 1, m_nNumCols - 1), lLogPixelsX, lLogPixelsY));
	}
	return TRUE;
}



// 選択範囲関係

// 選択範囲の取得
BOOL CTextGridCtrl::GetSelectedArea (int* pRow1, int* pRow2, int* pCol1, int* pCol2) const {
	ASSERT (pRow1);
	ASSERT (pRow2);
	ASSERT (pCol1);
	ASSERT (pCol2);
	*pRow1 = m_nSelectedTopRow;
	*pRow2 = m_nSelectedBottomRow;
	*pCol1 = m_nSelectedLeftCol;
	*pCol2 = m_nSelectedRightCol;
	return TRUE;
}


// 単一のセルを選択
BOOL CTextGridCtrl::SelectCell (int nRow, int nCol) {
	ASSERT (0 <= nRow && nRow < m_nNumRows);
	ASSERT (0 <= nCol && nCol < m_nNumCols);
	if (nRow < 0 || nRow >= m_nNumRows) {
		return FALSE;
	}
	if (nCol < 0 || nCol >= m_nNumCols) {
		return FALSE;
	}
	if (m_nSelectedTopRow != nRow ||
		m_nSelectedBottomRow != nRow ||
		m_nSelectedLeftCol != nCol ||
		m_nSelectedRightCol != nCol) {
		
		// 履歴登録
		if (m_nCurHistoryIndex >= 0) {
			CTextGridHistoryEvent* pHistoryEvent = new CTextGridHistoryEvent ();
			if (pHistoryEvent == NULL) {
				AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
				return FALSE;
			}
			pHistoryEvent->m_nEventType = TEXTGRIDCTRL_MODIFYSELECT;
			pHistoryEvent->m_nOldSelectedTopRow = m_nSelectedTopRow;
			pHistoryEvent->m_nOldSelectedBottomRow = m_nSelectedBottomRow;
			pHistoryEvent->m_nOldSelectedLeftCol = m_nSelectedLeftCol;
			pHistoryEvent->m_nOldSelectedRightCol = m_nSelectedRightCol;
			pHistoryEvent->m_nNewSelectedTopRow = nRow;
			pHistoryEvent->m_nNewSelectedBottomRow = nRow;
			pHistoryEvent->m_nNewSelectedLeftCol = nCol;
			pHistoryEvent->m_nNewSelectedRightCol = nCol;
			AddHistoryEvent (pHistoryEvent);
		}
		
		// 設定
		m_nSelectedTopRow = nRow;
		m_nSelectedBottomRow = nRow;
		m_nSelectedLeftCol = nCol;
		m_nSelectedRightCol = nCol;

		return TRUE;
	}
	return FALSE;
}

BOOL CTextGridCtrl::SelectCellWithNotify (int nRow, int nCol) {
	BOOL bRet = SelectCell (nRow, nCol);

	if (bRet) {
		if (m_hWnd != NULL && GetParent () != NULL && m_bNotify == FALSE) {
			m_bNotify = TRUE;
			TextGridNotify theNotify = { 0 };
			theNotify.hdr.code = TGN_SELECTCHANGE;
			theNotify.hdr.hwndFrom = (this->m_hWnd);
			theNotify.hdr.idFrom = GetDlgCtrlID ();
			theNotify.m_nTopRow = nRow;
			theNotify.m_nBottomRow = nRow;
			theNotify.m_nLeftCol = nCol;
			theNotify.m_nRightCol = nCol;
			GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
			m_bNotify = FALSE;
		}
	}

	return bRet;
}

// 指定範囲を選択
BOOL CTextGridCtrl::SelectArea (int nRow1, int nRow2, int nCol1, int nCol2) {
	ASSERT (0 <= nRow1 && nRow1 < m_nNumRows);
	ASSERT (0 <= nRow2 && nRow2 < m_nNumRows);
	ASSERT (0 <= nCol1 && nCol1 < m_nNumCols);
	ASSERT (0 <= nCol2 && nCol2 < m_nNumCols);
	if (nRow1 < 0 || nRow1 >= m_nNumRows) {
		return FALSE;
	}
	if (nRow2 < 0 || nRow2 >= m_nNumRows) {
		return FALSE;
	}
	if (nCol1 < 0 || nCol1 >= m_nNumCols) {
		return FALSE;
	}
	if (nCol2 < 0 || nCol2 >= m_nNumCols) {
		return FALSE;
	}
	if (m_nSelectedTopRow != __min (nRow1, nRow2) ||
		m_nSelectedBottomRow != __max (nRow1, nRow2) ||
		m_nSelectedLeftCol != __min (nCol1, nCol2) ||
		m_nSelectedRightCol != __max (nCol1, nCol2)) {

		// 履歴登録
		if (m_nCurHistoryIndex >= 0) {
			CTextGridHistoryEvent* pHistoryEvent = new CTextGridHistoryEvent ();
			if (pHistoryEvent == NULL) {
				AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
				return FALSE;
			}
			pHistoryEvent->m_nEventType = TEXTGRIDCTRL_MODIFYSELECT;
			pHistoryEvent->m_nOldSelectedTopRow = m_nSelectedTopRow;
			pHistoryEvent->m_nOldSelectedBottomRow = m_nSelectedBottomRow;
			pHistoryEvent->m_nOldSelectedLeftCol = m_nSelectedLeftCol;
			pHistoryEvent->m_nOldSelectedRightCol = m_nSelectedRightCol;
			pHistoryEvent->m_nNewSelectedTopRow = __min (nRow1, nRow2);
			pHistoryEvent->m_nNewSelectedBottomRow = __max (nRow1, nRow2);
			pHistoryEvent->m_nNewSelectedLeftCol = __min (nCol1, nCol2);
			pHistoryEvent->m_nNewSelectedRightCol = __max (nCol1, nCol2);
			AddHistoryEvent (pHistoryEvent);
		}

		// 設定
		m_nSelectedTopRow = __min (nRow1, nRow2);
		m_nSelectedBottomRow = __max (nRow1, nRow2);
		m_nSelectedLeftCol = __min (nCol1, nCol2);
		m_nSelectedRightCol = __max (nCol1, nCol2);

		return TRUE;

	}
	return FALSE;
}

BOOL CTextGridCtrl::SelectAreaWithNotify (int nRow1, int nRow2, int nCol1, int nCol2) {
	BOOL bRet = SelectArea (nRow1, nRow2, nCol1, nCol2);
	if (bRet) {
		if (m_hWnd != NULL && GetParent () != NULL && m_bNotify == FALSE) {
			m_bNotify = TRUE;
			TextGridNotify theNotify = { 0 };
			theNotify.hdr.code = TGN_SELECTCHANGE;
			theNotify.hdr.hwndFrom = (this->m_hWnd);
			theNotify.hdr.idFrom = GetDlgCtrlID ();
			theNotify.m_nTopRow = __min (nRow1, nRow2);
			theNotify.m_nBottomRow = __max (nRow1, nRow2);
			theNotify.m_nLeftCol = __min (nCol1, nCol2);
			theNotify.m_nRightCol = __max (nCol1, nCol2);
			GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
			m_bNotify = FALSE;
		}
	}
	return bRet;
}

// 行全体を選択(nRow1=開始行、nRow2=終了行、順不同)
BOOL CTextGridCtrl::SelectRow (int nRow1, int nRow2) {
	ASSERT (0 <= nRow1 && nRow1 < m_nNumRows);
	ASSERT (0 <= nRow2 && nRow2 < m_nNumRows);
	if (nRow1 < 0 || nRow1 >= m_nNumRows) {
		return FALSE;
	}
	if (nRow2 < 0 || nRow2 >= m_nNumRows) {
		return FALSE;
	}
	if (m_nSelectedTopRow != __min (nRow1, nRow2) ||
		m_nSelectedBottomRow != __max (nRow1, nRow2) ||
		m_nSelectedLeftCol != 0 || 
		m_nSelectedRightCol != m_nNumCols - 1) {

		// 履歴登録
		if (m_nCurHistoryIndex >= 0) {
			CTextGridHistoryEvent* pHistoryEvent = new CTextGridHistoryEvent ();
			if (pHistoryEvent == NULL) {
				AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
				return FALSE;
			}
			pHistoryEvent->m_nEventType = TEXTGRIDCTRL_MODIFYSELECT;
			pHistoryEvent->m_nOldSelectedTopRow = m_nSelectedTopRow;
			pHistoryEvent->m_nOldSelectedBottomRow = m_nSelectedBottomRow;
			pHistoryEvent->m_nOldSelectedLeftCol = m_nSelectedLeftCol;
			pHistoryEvent->m_nOldSelectedRightCol = m_nSelectedRightCol;
			pHistoryEvent->m_nNewSelectedTopRow = __min (nRow1, nRow2);
			pHistoryEvent->m_nNewSelectedBottomRow = __max (nRow1, nRow2);
			pHistoryEvent->m_nNewSelectedLeftCol = 0;
			pHistoryEvent->m_nNewSelectedRightCol = m_nNumCols - 1;
			AddHistoryEvent (pHistoryEvent);
		}

		// 設定
		m_nSelectedTopRow = __min (nRow1, nRow2);
		m_nSelectedBottomRow = __max (nRow1, nRow2);
		m_nSelectedLeftCol = 0;
		m_nSelectedRightCol = m_nNumCols - 1;

		return TRUE;

	}
	return FALSE;

}

BOOL CTextGridCtrl::SelectRowWithNotify (int nRow1, int nRow2) {
	BOOL bRet = SelectRow (nRow1, nRow2);
	if (bRet) {
		if (m_hWnd != NULL && GetParent () != NULL && m_bNotify == FALSE) {
			m_bNotify = TRUE;
			TextGridNotify theNotify = { 0 };
			theNotify.hdr.code = TGN_SELECTCHANGE;
			theNotify.hdr.hwndFrom = (this->m_hWnd);
			theNotify.hdr.idFrom = GetDlgCtrlID ();
			theNotify.m_nTopRow = __min (nRow1, nRow2);
			theNotify.m_nBottomRow = __max (nRow1, nRow2);
			theNotify.m_nLeftCol = 0;
			theNotify.m_nRightCol = m_nNumCols - 1;
			GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
			m_bNotify = FALSE;
		}
	}
	return bRet;
}

// 列全体を選択(nCol1=開始列、nCol2=終了列、順不同)
BOOL CTextGridCtrl::SelectCol (int nCol1, int nCol2) {
	ASSERT (0 <= nCol1 && nCol1 < m_nNumCols);
	ASSERT (0 <= nCol2 && nCol2 < m_nNumCols);
	if (nCol1 < 0 || nCol1 >= m_nNumCols) {
		return FALSE;
	}
	if (nCol2 < 0 || nCol2 >= m_nNumCols) {
		return FALSE;
	}
	if (m_nSelectedTopRow != 0 || 
		m_nSelectedBottomRow != m_nNumRows -1 ||
		m_nSelectedLeftCol != __min (nCol1, nCol2) || 
		m_nSelectedRightCol != __max (nCol1, nCol2)) {

		// 履歴登録
		if (m_nCurHistoryIndex >= 0) {
			CTextGridHistoryEvent* pHistoryEvent = new CTextGridHistoryEvent ();
			if (pHistoryEvent == NULL) {
				AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
				return FALSE;
			}
			pHistoryEvent->m_nEventType = TEXTGRIDCTRL_MODIFYSELECT;
			pHistoryEvent->m_nOldSelectedTopRow = m_nSelectedTopRow;
			pHistoryEvent->m_nOldSelectedBottomRow = m_nSelectedBottomRow;
			pHistoryEvent->m_nOldSelectedLeftCol = m_nSelectedLeftCol;
			pHistoryEvent->m_nOldSelectedRightCol = m_nSelectedRightCol;
			pHistoryEvent->m_nNewSelectedTopRow = 0;
			pHistoryEvent->m_nNewSelectedBottomRow = m_nNumRows - 1;
			pHistoryEvent->m_nNewSelectedLeftCol = __min (nCol1, nCol2);
			pHistoryEvent->m_nNewSelectedRightCol = __max (nCol1, nCol2);
			AddHistoryEvent (pHistoryEvent);
		}
		
		// 設定
		m_nSelectedTopRow = 0;
		m_nSelectedBottomRow = m_nNumRows - 1;
		m_nSelectedLeftCol = __min (nCol1, nCol2);
		m_nSelectedRightCol = __max (nCol1, nCol2);
		
		return TRUE;
	}
	return FALSE;
}

BOOL CTextGridCtrl::SelectColWithNotify (int nCol1, int nCol2) {
	BOOL bRet = SelectCol (nCol1, nCol2);
	if (bRet) {
		if (m_hWnd != NULL && GetParent () != NULL && m_bNotify == FALSE) {
			m_bNotify = TRUE;
			TextGridNotify theNotify = { 0 };
			theNotify.hdr.code = TGN_SELECTCHANGE;
			theNotify.hdr.hwndFrom = (this->m_hWnd);
			theNotify.hdr.idFrom = GetDlgCtrlID ();
			theNotify.m_nTopRow = 0;
			theNotify.m_nBottomRow = m_nNumRows - 1;
			theNotify.m_nLeftCol = __min (nCol1, nCol2);
			theNotify.m_nRightCol = __max (nCol1, nCol2);
			GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
			m_bNotify = FALSE;
		}
	}
	return bRet;
}

// 表全体を選択
BOOL CTextGridCtrl::SelectAll () {
	if (m_nSelectedTopRow != 0 || 
		m_nSelectedBottomRow != m_nNumRows -1 ||
		m_nSelectedLeftCol != 0 || 
		m_nSelectedRightCol != m_nNumCols -1) {

		// 履歴登録
		if (m_nCurHistoryIndex >= 0) {
			CTextGridHistoryEvent* pHistoryEvent = new CTextGridHistoryEvent ();
			if (pHistoryEvent == NULL) {
				AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
				return FALSE;
			}
			pHistoryEvent->m_nEventType = TEXTGRIDCTRL_MODIFYSELECT;
			pHistoryEvent->m_nOldSelectedTopRow = m_nSelectedTopRow;
			pHistoryEvent->m_nOldSelectedBottomRow = m_nSelectedBottomRow;
			pHistoryEvent->m_nOldSelectedLeftCol = m_nSelectedLeftCol;
			pHistoryEvent->m_nOldSelectedRightCol = m_nSelectedRightCol;
			pHistoryEvent->m_nNewSelectedTopRow = 0;
			pHistoryEvent->m_nNewSelectedBottomRow = m_nNumRows - 1;
			pHistoryEvent->m_nNewSelectedLeftCol = 0;
			pHistoryEvent->m_nNewSelectedRightCol = m_nNumCols - 1;
			AddHistoryEvent (pHistoryEvent);
		}

		// 設定
		m_nSelectedTopRow = 0;
		m_nSelectedBottomRow = m_nNumRows - 1;
		m_nSelectedLeftCol = 0;
		m_nSelectedRightCol = m_nNumCols - 1;


	}
	return TRUE;
}

BOOL CTextGridCtrl::SelectAllWithNotify () {
	BOOL bRet = SelectAll ();
	if (bRet) {
		if (m_hWnd != NULL && GetParent () != NULL && m_bNotify == FALSE) {
			m_bNotify = TRUE;
			TextGridNotify theNotify = { 0 };
			theNotify.hdr.code = TGN_SELECTCHANGE;
			theNotify.hdr.hwndFrom = (this->m_hWnd);
			theNotify.hdr.idFrom = GetDlgCtrlID ();
			theNotify.m_nTopRow = 0;
			theNotify.m_nBottomRow = m_nNumRows - 1;
			theNotify.m_nLeftCol = 0;
			theNotify.m_nRightCol = m_nNumCols - 1;
			GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
			m_bNotify = FALSE;
		}
	}
	return bRet;
}

// 指定セルが選択されているかどうか調べる。
BOOL CTextGridCtrl::IsSelectedCell (int nRow, int nCol) const {
	ASSERT (0 <= nRow && nRow < m_nNumRows);
	ASSERT (0 <= nCol && nCol < m_nNumCols);
	if (m_nSelectedTopRow <= nRow && nRow <= m_nSelectedBottomRow &&
		m_nSelectedLeftCol <= nCol && nCol <= m_nSelectedRightCol) {
		return TRUE;
	}
	return FALSE;
}

// 行全体が選択されているかどうか調べる。
BOOL CTextGridCtrl::IsSelectedRow (int nRow) const {
	ASSERT (0 <= nRow && nRow < m_nNumRows);
	if (m_nSelectedTopRow <= nRow && nRow <= m_nSelectedBottomRow &&
		m_nSelectedLeftCol == 0 && m_nSelectedRightCol == m_nNumCols - 1) {
		return TRUE;
	}
	return FALSE;
}

// 列全体が選択されているかどうか調べる。
BOOL CTextGridCtrl::IsSelectedCol (int nCol) const {
	ASSERT (0 <= nCol && nCol < m_nNumCols);
	if (m_nSelectedTopRow == 0 && m_nSelectedBottomRow == m_nNumRows - 1 &&
		m_nSelectedLeftCol <= nCol && nCol <= m_nSelectedRightCol) {
		return TRUE;
	}
	return FALSE;
}

// 表全体が選択されているかどうか調べる。
BOOL CTextGridCtrl::IsSelectedAll () const {
	if (m_nSelectedTopRow == 0 && m_nSelectedBottomRow == m_nNumRows - 1 &&
		m_nSelectedLeftCol == 0 && m_nSelectedRightCol == m_nNumCols - 1) {
		return TRUE;
	}
	return FALSE;
}

// カレントセルの設定(オペレーション用)
BOOL CTextGridCtrl::SetCurCell (int nRow, int nCol) {
	// 画面のdpi取得
	HDC hDC = ::GetDC (this->GetSafeHwnd ());
	long lLogPixelsX = ::GetDeviceCaps (hDC, LOGPIXELSX);
	long lLogPixelsY = ::GetDeviceCaps (hDC, LOGPIXELSY);
	::ReleaseDC (this->GetSafeHwnd (), hDC);
	return SetCurCell (nRow, nCol, lLogPixelsX, lLogPixelsY);
}

// カレントセルの設定(オペレーション用)
BOOL CTextGridCtrl::SetCurCellWithNotify (int nRow, int nCol) {
	// 画面のdpi取得
	HDC hDC = ::GetDC (this->GetSafeHwnd ());
	long lLogPixelsX = ::GetDeviceCaps (hDC, LOGPIXELSX);
	long lLogPixelsY = ::GetDeviceCaps (hDC, LOGPIXELSY);
	::ReleaseDC (this->GetSafeHwnd (), hDC);
	return SetCurCellWithNotify (nRow, nCol, lLogPixelsX, lLogPixelsY);
}

// カレントセルの設定(内部呼び出し用)
BOOL CTextGridCtrl::SetCurCell (int nRow, int nCol, long lLogPixelsX, long lLogPixelsY) {
	ASSERT (0 <= nRow && nRow < m_nNumRows);
	ASSERT (0 <= nCol && nCol < m_nNumCols);
	if (nRow < 0 || nRow >= m_nNumRows) {
		return FALSE;
	}
	if (nCol < 0 || nCol >= m_nNumCols) {
		return FALSE;
	}

	// 履歴登録
	if (m_nCurHistoryIndex >= 0) {
		CTextGridHistoryEvent* pHistoryEvent = new CTextGridHistoryEvent ();
		if (pHistoryEvent == NULL) {
			AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
			return FALSE;
		}
		pHistoryEvent->m_nEventType = TEXTGRIDCTRL_MODIFYCURCELL;
		pHistoryEvent->m_nOldCurRow = m_nCurRow;
		pHistoryEvent->m_nOldCurCol = m_nCurCol;
		pHistoryEvent->m_nNewCurRow = nRow;
		pHistoryEvent->m_nNewCurCol = nCol;
		AddHistoryEvent (pHistoryEvent);
	}

	// 設定
	m_nCurRow = nRow;
	m_nCurCol = nCol;
	m_nKeyMoveRow = nRow;
	m_nKeyMoveCol = nCol;
	
	if (m_hWnd != NULL) {
		AutoScrolltoShowCell (m_nCurRow, m_nCurCol, lLogPixelsX, lLogPixelsY);
		MoveTextBox (lLogPixelsX, lLogPixelsY);
	}

	return TRUE;
}

// カレントセルの設定(内部呼び出し用)
BOOL CTextGridCtrl::SetCurCellWithNotify (int nRow, int nCol, long lLogPixelsX, long lLogPixelsY) {
	BOOL bRet = SetCurCell (nRow, nCol, lLogPixelsX, lLogPixelsY);
	if (bRet) {
		if (m_hWnd != NULL && GetParent () != NULL && m_bNotify == FALSE) {
			m_bNotify = TRUE;
			TextGridNotify theNotify = { 0 };
			theNotify.hdr.code = TGN_CURCELLCHANGE;
			theNotify.hdr.hwndFrom = (this->m_hWnd);
			theNotify.hdr.idFrom = GetDlgCtrlID ();
			theNotify.m_nRow = nRow;
			theNotify.m_nCol = nCol;
			GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
			m_bNotify = FALSE;
		}
	}
	return bRet;
}

// 指定セルがクライアント領域からはみ出している場合の自動スクロール
BOOL CTextGridCtrl::AutoScrolltoShowCell (int nRow, int nCol, long lLogPixelsX, long lLogPixelsY) {
	ASSERT (0 <= nRow && nRow < m_nNumRows);
	ASSERT (0 <= nCol && nCol < m_nNumCols);
	ASSERT (m_hWnd);
	if (nRow < 0 || nRow >= m_nNumRows) {
		return FALSE;
	}
	if (nCol < 0 || nCol >= m_nNumCols) {
		return FALSE;
	}
	if (m_hWnd == NULL) {
		return FALSE;
	}
	VERIFY (AutoScrolltoShowRow (nRow, lLogPixelsX, lLogPixelsY));
	VERIFY (AutoScrolltoShowCol (nCol, lLogPixelsX, lLogPixelsY));
	return TRUE;
}

// 指定行がクライアント領域からはみ出した場合の自動スクロール
BOOL CTextGridCtrl::AutoScrolltoShowRow (int nRow, long lLogPixelsX, long lLogPixelsY) {
	ASSERT (0 <= nRow && nRow < m_nNumRows);
	ASSERT (m_hWnd);
	if (nRow < 0 || nRow >= m_nNumRows) {
		return FALSE;
	}
	if (m_hWnd == NULL) {
		return FALSE;
	}
	CRect rcClient;
	GetClientRect (&rcClient);
	// セルがビューの上にはみ出した場合の処理
	if (nRow < m_nVisibleTopRow) {
		VERIFY (SetVisibleTopRow (nRow, lLogPixelsX, lLogPixelsY));
	}
	// セルがビューの下にはみ出した場合の処理
	else if (nRow >= GetVisibleBottomRow (lLogPixelsX, lLogPixelsY)) {
		VERIFY (SetVisibleBottomRow (nRow, lLogPixelsX, lLogPixelsY));
	}
	return TRUE;
}

// 指定列がクライアント領域からはみ出した場合の自動スクロール
BOOL CTextGridCtrl::AutoScrolltoShowCol (int nCol, long lLogPixelsX, long lLogPixelsY) {
	ASSERT (0 <= nCol && nCol < m_nNumCols);
	ASSERT (m_hWnd);
	if (nCol < 0 || nCol >= m_nNumCols) {
		return FALSE;
	}
	if (m_hWnd == NULL) {
		return FALSE;
	}
	CRect rcClient;
	GetClientRect (&rcClient);
	// セルがビューの左にはみ出した場合の処理
	if (nCol < m_nVisibleLeftCol) {
		VERIFY (SetVisibleLeftCol (nCol, lLogPixelsX, lLogPixelsY));
	}
	// セルがビューの右にはみ出した場合の処理
	else if (nCol >= GetVisibleRightCol (lLogPixelsX, lLogPixelsY)) {
		VERIFY (SetVisibleRightCol (nCol, lLogPixelsX, lLogPixelsY));
	}
	return TRUE;
}


// ウィンドウ状態関数

// 読み取り専用状態を取得する
BOOL CTextGridCtrl::IsReadOnly () const {
	//return (GetStyle () & TGS_READONLY) == 0 ? FALSE : TRUE;
	return m_bReadOnly;
}

// 読み取り専用状態を取得する
BOOL CTextGridCtrl::IsCellReadOnly (int nRow, int nCol) const {
	ASSERT (0 <= nRow && nRow < m_nNumRows);
	ASSERT (0 <= nCol && nCol < m_nNumCols);
	if (nRow < 0 || nRow >= m_nNumRows) {
		return FALSE;
	}
	if (nCol < 0 || nCol >= m_nNumCols) {
		return FALSE;
	}
	return m_theReadOnlyArray.GetAt (nRow * m_nNumCols + nCol) == 0 ? FALSE : TRUE;
}

// 読み取り専用状態を設定する
BOOL CTextGridCtrl::SetReadOnly (BOOL bReadOnly) {
//	VERIFY (m_theGridPen.DeleteObject ());
//	VERIFY (m_theWindowBackBrush.DeleteObject ());
/*	if (bReadOnly || IsWindowEnabled () == FALSE) {
		VERIFY (m_theGridPen.CreatePen (PS_SOLID, 1, GetSysColor (COLOR_APPWORKSPACE)));
		VERIFY (m_theWindowBackBrush.CreateSolidBrush (GetSysColor (COLOR_BTNFACE)));
	}
	else {
		VERIFY (m_theGridPen.CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNFACE)));
		VERIFY (m_theWindowBackBrush.CreateSolidBrush (GetSysColor (COLOR_WINDOW)));
	}
	if (bReadOnly) {
		return ModifyStyle (0, TGS_READONLY);
	}
	else {
		return ModifyStyle (TGS_READONLY, 0);
	}*/
	m_bReadOnly = bReadOnly;
	return TRUE;
}

// 読み取り専用状態を設定する
BOOL CTextGridCtrl::SetCellReadOnly (int nRow, int nCol, BOOL bReadOnly) {
//	VERIFY (m_theGridPen.DeleteObject ());
//	VERIFY (m_theWindowBackBrush.DeleteObject ());
	ASSERT (0 <= nRow && nRow < m_nNumRows);
	ASSERT (0 <= nCol && nCol < m_nNumCols);
	if (nRow < 0 || nRow >= m_nNumRows) {
		return FALSE;
	}
	if (nCol < 0 || nCol >= m_nNumCols) {
		return FALSE;
	}

	// 履歴登録
	if (m_nCurHistoryIndex >= 0) {
		CTextGridHistoryEvent* pHistoryEvent = new CTextGridHistoryEvent ();
		if (pHistoryEvent == NULL) {
			AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
			return FALSE;
		}
		pHistoryEvent->m_nEventType = TEXTGRIDCTRL_MODIFYREADONLY;
		pHistoryEvent->m_nModifiedTopRow = nRow;
		pHistoryEvent->m_nModifiedBottomRow = nRow;
		pHistoryEvent->m_nModifiedLeftCol = nCol;
		pHistoryEvent->m_nModifiedRightCol = nCol;
		BOOL bOldReadOnly = m_theReadOnlyArray.GetAt (nRow * m_nNumCols + nCol);
		pHistoryEvent->m_theOldReadOnlyArray.Add (bOldReadOnly);
		pHistoryEvent->m_theNewReadOnlyArray.Add (bReadOnly);
		AddHistoryEvent (pHistoryEvent);
	}

	// 設定
	m_theReadOnlyArray.SetAt (nRow * m_nNumCols + nCol, bReadOnly);
	return TRUE;
}

COLORREF CTextGridCtrl::GetCellTextColor (int nRow, int nCol) const {
	ASSERT (0 <= nRow && nRow < m_nNumRows);
	ASSERT (0 <= nCol && nCol < m_nNumCols);
	if (nRow < 0 || nRow >= m_nNumRows) {
		return FALSE;
	}
	if (nCol < 0 || nCol >= m_nNumCols) {
		return FALSE;
	}
	return m_theTextColorArray.GetAt (nRow * m_nNumCols + nCol);
}

BOOL CTextGridCtrl::SetCellTextColor (int nRow, int nCol, COLORREF nColor) {
	ASSERT (0 <= nRow && nRow < m_nNumRows);
	ASSERT (0 <= nCol && nCol < m_nNumCols);
	if (nRow < 0 || nRow >= m_nNumRows) {
		return FALSE;
	}
	if (nCol < 0 || nCol >= m_nNumCols) {
		return FALSE;
	}

	// 履歴登録
	if (m_nCurHistoryIndex >= 0) {
		CTextGridHistoryEvent* pHistoryEvent = new CTextGridHistoryEvent ();
		if (pHistoryEvent == NULL) {
			AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
			return FALSE;
		}
		pHistoryEvent->m_nEventType = TEXTGRIDCTRL_MODIFYTEXTCOLOR;
		pHistoryEvent->m_nModifiedTopRow = nRow;
		pHistoryEvent->m_nModifiedBottomRow = nRow;
		pHistoryEvent->m_nModifiedLeftCol = nCol;
		pHistoryEvent->m_nModifiedRightCol = nCol;
		COLORREF nOldColor = m_theTextColorArray.GetAt (nRow * m_nNumCols + nCol);
		pHistoryEvent->m_theOldTextColorArray.Add (nOldColor);
		pHistoryEvent->m_theNewTextColorArray.Add (nColor);
		AddHistoryEvent (pHistoryEvent);
	}

	// 設定
	m_theTextColorArray.SetAt (nRow * m_nNumCols + nCol, nColor);
	return TRUE;
}


// 行の高さ固定状態を取得する
BOOL CTextGridCtrl::IsRowHeightFixed () const {
	return (GetStyle () & TGS_ROWHEIGHTFIXED) == 0 ? FALSE : TRUE;
}

// 行の高さ固定状態を設定する
BOOL CTextGridCtrl::SetRowHeightFixed (BOOL bRowHeightFixed) {
	if (bRowHeightFixed) {
		return ModifyStyle (0, TGS_ROWHEIGHTFIXED);
	}
	else {
		return ModifyStyle (TGS_ROWHEIGHTFIXED, 0);
	}
}

// 列の幅固定状態を取得する
BOOL CTextGridCtrl::IsColWidthFixed () const {
	return (GetStyle () & TGS_COLWIDTHFIXED) == 0 ? FALSE : TRUE;
}

// 列の幅固定状態を設定する
BOOL CTextGridCtrl::SetColWidthFixed (BOOL bColWidthFixed) {
	if (bColWidthFixed) {
		return ModifyStyle (0, TGS_COLWIDTHFIXED);
	}
	else {
		return ModifyStyle (TGS_COLWIDTHFIXED, 0);
	}
}

// スクロールバー関係

// 垂直スクロールバーの調整
BOOL CTextGridCtrl::AdjustVScrollBar () {
	SCROLLINFO si;
	si.fMask = SIF_PAGE | SIF_RANGE;
	si.nMin = 0;
	si.nMax = m_nNumRows - 1;
	si.nPage = TEXTGRIDCTRL_RANGE (0, 1, m_nNumRows - 1);
	VERIFY (SetScrollInfo (SB_VERT, &si, TRUE));
	return TRUE;
}

// 水平スクロールバーの調整
BOOL CTextGridCtrl::AdjustHScrollBar () {
	SCROLLINFO si;
	si.fMask = SIF_PAGE | SIF_RANGE;
	si.nMin = 0;
	si.nMax = m_nNumCols - 1;
	si.nPage = TEXTGRIDCTRL_RANGE (0, 1, m_nNumCols - 1);
	VERIFY (SetScrollInfo (SB_HORZ, &si, TRUE));
	return TRUE;
}

// 座標変換関係

// 指定座標の行番号と列番号を求める。
// *pRow==-1はタイトル行、0<=*pRow<m_nNumRowは行番号、*pRow==m_nNumRowは下空欄。
// *pCol==-1はタイトル列、0<=*pCol<m_nNumColは列番号、*pCol==m_nNumColは右空欄。
BOOL CTextGridCtrl::PointToCell (CPoint point, int* pRow, int* pCol, long lLogPixelsX, long lLogPixelsY) const {
	// 行方向
	int i, j;
	int y = m_nTitleHeight * lLogPixelsY / 96;
	if (point.y < y) {
		*pRow = -1;
	}
	else {
		for (i = m_nVisibleTopRow; i < m_nNumRows; i++) {
			if (y <= point.y && point.y < y + GetRowHeight (i) * lLogPixelsY / 96) {
				break;
			}
			y += GetRowHeight (i) * lLogPixelsY / 96;
		}
		*pRow = i;
	}
	// 列方向
	int x = m_nTitleWidth * lLogPixelsX / 96;
	if (point.x < x) {
		*pCol = -1;
	}
	else {
		for (j = m_nVisibleLeftCol; j < m_nNumCols; j++) {
			if (x <= point.x && point.x < x + GetColWidth (j) * lLogPixelsX / 96) {
				break;
			}
			x += GetColWidth (j) * lLogPixelsX / 96;
		}
		*pCol = j;
	}
	return TRUE;
}

// 指定座標の行境界線番号と列境界線番号を求める(セルの右側の線及び下の線を境界線とする)。
// *pRowBorder==-1は境界線上にない、-1<=*pRowBorder<m_nNumRowは行境界線番号。
// *pColBorder==-1は境界線上にない、-1<=*pColBorder<m_nNumColは列境界線番号。
BOOL CTextGridCtrl::PointToBorder (CPoint point, int* pRowBorder, int* pColBorder, long lLogPixelsX, long lLogPixelsY) const {
	// 行方向
	*pRowBorder = -1;
	int y = m_nTitleHeight * lLogPixelsY / 96;
	for (int i = m_nVisibleTopRow; i < m_nNumRows; i++) {
		y += GetRowHeight (i) * lLogPixelsY / 96;
		if (y - 2 <= point.y && point.y <= y + 2) {
			*pRowBorder = i;
			break;
		}
	}
	// 列方向
	*pColBorder = -1;
	int x = m_nTitleWidth * lLogPixelsX / 96;
	for (int j = m_nVisibleLeftCol; j < m_nNumCols; j++) {
		x += GetColWidth (j) * lLogPixelsX / 96;
		if (x - 2 <= point.x && point.x <= x + 2) {
			*pColBorder = j;
			break;
		}
	}
	return TRUE;
}


// 指定行列の占有する矩形座標を求める。画面上のどこも占有してない場合FALSEを返す。
BOOL CTextGridCtrl::CellToRect (int nRow, int nCol, CRect* pRect, long lLogPixelsX, long lLogPixelsY) const {
	ASSERT (0 <= nRow && nRow < m_nNumRows);
	ASSERT (0 <= nCol && nCol < m_nNumCols);
	BOOL bRet = TRUE;
	int i, j;
	// 行方向
	int nVisibleBottomRow = GetVisibleBottomRow (lLogPixelsX, lLogPixelsY);
	if (nRow < 0) {
		(*pRect).top = 0;	
		(*pRect).bottom = m_nTitleHeight * lLogPixelsY / 96;
	}
	else if (0 <= nRow && nRow < m_nVisibleTopRow || nRow > nVisibleBottomRow) {
		(*pRect).top = 0;	
		(*pRect).bottom = 0;
		bRet = FALSE;
	}
	else {
		int y = m_nTitleHeight * lLogPixelsY / 96;
		for (i = m_nVisibleTopRow; i < nRow; i++) {
			y += GetRowHeight (i) * lLogPixelsY / 96;
		}
		(*pRect).top = y;
		(*pRect).bottom = y + GetRowHeight (i) * lLogPixelsY / 96;
	}
	// 列方向
	int nVisibleRightCol = GetVisibleRightCol (lLogPixelsX, lLogPixelsY);
	if (nCol < 0) {
		(*pRect).left = 0;
		(*pRect).right = m_nTitleWidth * lLogPixelsX / 96;
	}
	else if (0 <= nCol && nCol < m_nVisibleLeftCol || nCol > nVisibleRightCol) {
		(*pRect).left = 0;
		(*pRect).right = 0;
		bRet = FALSE;
	}
	else {
		int x = m_nTitleWidth * lLogPixelsX / 96;
		for (j = m_nVisibleLeftCol; j < nCol; j++) {
			x += GetColWidth (j) * lLogPixelsX / 96;
		}
		(*pRect).left = x;
		(*pRect).right = x + GetColWidth (j) * lLogPixelsX / 96;
	}
	return bRet;
}


// テキストボックス処理

// 現在テキストボックスで編集中かどうか返す。
BOOL CTextGridCtrl::IsTextEditing () const {
	ASSERT (m_theTextBox.m_hWnd != NULL);
	if (m_theTextBox.m_hWnd == NULL) {
		return FALSE;
	}
	return (m_theTextBox.GetStyle () & WS_VISIBLE) ? TRUE : FALSE;
}

// テキストボックスでの編集を開始する。
BOOL CTextGridCtrl::BeginTextEditing () {
	ASSERT (m_theTextBox.m_hWnd != NULL);
	if (m_theTextBox.m_hWnd == NULL) {
		return FALSE;
	}
	m_theTextBox.SetWindowText (CString (""));
	m_theTextBox.EmptyUndoBuffer ();
	m_theTextBox.ShowWindow (SW_SHOW);
	m_theTextBox.SetFocus ();
	m_theTextBox.UpdateWindow ();
	return TRUE;
}

// テキストボックスでの編集を継続開始する。
BOOL CTextGridCtrl::BeginTextEditingContinue () {
	ASSERT (m_theTextBox.m_hWnd != NULL);
	if (m_theTextBox.m_hWnd == NULL) {
		return FALSE;
	}
	CString strTemp;
	strTemp = GetCellString (m_nCurRow, m_nCurCol);
	m_theTextBox.SetWindowText (strTemp);
	m_theTextBox.SetSel (strTemp.GetLength (), strTemp.GetLength (), TRUE);
	m_theTextBox.EmptyUndoBuffer ();
	m_theTextBox.ShowWindow (SW_SHOW);
	m_theTextBox.SetFocus ();
	m_theTextBox.UpdateWindow ();
	return TRUE;
}

// テキストボックスでの編集を終了し、新しい値を格納する。
BOOL CTextGridCtrl::EndTextEditingOK () {
	ASSERT (m_theTextBox.m_hWnd != NULL);
	if (m_theTextBox.m_hWnd == NULL) {
		return FALSE;
	}
	// 履歴準備
	DeleteHistoryBlockAfter (m_nCurHistoryIndex);
	CTextGridHistoryBlock* pHistoryBlock = new CTextGridHistoryBlock ();
	if (pHistoryBlock == NULL) {
		AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
		return 0;
	}
	pHistoryBlock->m_strName = _T ("テキスト編集");
	m_theHistoryBlockArray.Add (pHistoryBlock);
	m_nCurHistoryIndex++;

	// 現在のセルの編集テキストを取得
	CString strBuf;
	m_theTextBox.GetWindowText (strBuf);
	// 値の反映
	int nRow = GetCurRow ();
	int nCol = GetCurCol ();
	SetCellStringWithNotify (nRow, nCol, strBuf);
	// 編集終了	
	m_theTextBox.ShowWindow (SW_HIDE);
	this->SetFocus ();
	return TRUE;
}

// テキストボックスでの編集を終了し、新しい値を格納しない。
BOOL CTextGridCtrl::EndTextEditingCancel () {
	ASSERT (m_theTextBox.m_hWnd != NULL);
	if (m_theTextBox.m_hWnd == NULL) {
		return FALSE;
	}
	m_theTextBox.ShowWindow (SW_HIDE);
	this->SetFocus ();
	return TRUE;
}

// テキストボックスの移動(VISIBLE==FALSE時含む)
BOOL CTextGridCtrl::MoveTextBox (long lLogPixelsX, long lLogPixelsY) {
	ASSERT (m_theTextBox.m_hWnd != NULL);
	if (m_theTextBox.m_hWnd == NULL) {
		return FALSE;
	}
	CRect rcCurCell;
	ASSERT (0 <= m_nCurRow && m_nCurRow < m_nNumRows);
	ASSERT (0 <= m_nCurCol && m_nCurCol < m_nNumCols);
	this->CellToRect (m_nCurRow, m_nCurCol, &rcCurCell, lLogPixelsX, lLogPixelsY);
	m_theTextBox.MoveWindow 
		(rcCurCell.left, rcCurCell.top + rcCurCell.Height () / 2 - 6 * lLogPixelsY / 96, 
		rcCurCell.Width (), 13 * lLogPixelsY / 96);
	return TRUE;
}


// 編集処理

// 切り取り(親ウィンドウへの通知機能付き)
BOOL CTextGridCtrl::CutWithNotify () {
	BOOL bRet = Cut ();
	if (bRet) {
		if (m_hWnd != NULL && GetParent () != NULL && m_bNotify == FALSE) {
			m_bNotify = TRUE;
			TextGridNotify theNotify = { 0 };
			theNotify.hdr.code = TGN_CUT;
			theNotify.hdr.hwndFrom = (this->m_hWnd);
			theNotify.hdr.idFrom = GetDlgCtrlID ();
			theNotify.m_nTopRow = m_nSelectedTopRow;
			theNotify.m_nBottomRow = m_nSelectedBottomRow;
			theNotify.m_nLeftCol = m_nSelectedLeftCol;
			theNotify.m_nRightCol = m_nSelectedRightCol;
			GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
			m_bNotify = FALSE;
		}
	}
	return bRet;
}

// 切り取り
BOOL CTextGridCtrl::Cut () {
	m_bCut = TRUE;
	if (Copy () == FALSE) {
		m_bCut = FALSE;
		return FALSE;
	}
	if (Delete () == FALSE) {
		m_bCut = FALSE;
		return FALSE;
	}
	m_bCut = FALSE;
	return TRUE;
}

// コピー(親ウィンドウへの通知機能付き)
BOOL CTextGridCtrl::CopyWithNotify () {
	BOOL bRet = Copy ();
	if (bRet) {
		if (m_hWnd != NULL && GetParent () != NULL && m_bNotify == FALSE) {
			m_bNotify = TRUE;
			TextGridNotify theNotify = { 0 };
			theNotify.hdr.code = TGN_COPY;
			theNotify.hdr.hwndFrom = (this->m_hWnd);
			theNotify.hdr.idFrom = GetDlgCtrlID ();
			theNotify.m_nTopRow = m_nSelectedTopRow;
			theNotify.m_nBottomRow = m_nSelectedBottomRow;
			theNotify.m_nLeftCol = m_nSelectedLeftCol;
			theNotify.m_nRightCol = m_nSelectedRightCol;
			GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
			m_bNotify = FALSE;
		}
	}
	return bRet;
}

// コピー
BOOL CTextGridCtrl::Copy () {
	// 転送用文字列作成
	CString strTextCopy;
	for (int i = m_nSelectedTopRow; i <= m_nSelectedBottomRow; i++) {
		CString strTextLine;
		for (int j = m_nSelectedLeftCol; j <= m_nSelectedRightCol; j++) {
			strTextLine += GetCellString (i, j);
			if (j == m_nSelectedRightCol) {
				strTextLine += _T("\r\n");
			}
			else {
				strTextLine += _T("\t");
			}
		}
		strTextCopy += strTextLine;
	}
	// クリップボード転送処理
	TCHAR* p = NULL;
	HGLOBAL hGlobalMem = NULL;
	if ((hGlobalMem = GlobalAlloc(GHND, (strTextCopy.GetLength() + 1)  * sizeof (TCHAR))) == NULL) {
		_RPT0 (_CRT_WARN, "ｸﾞﾛｰﾊﾞﾙﾒﾓﾘ確保不能。ｺﾋﾟｰは失敗しました。");
		return FALSE;
	}
	if ((p = (TCHAR*)GlobalLock(hGlobalMem)) == NULL) {
		_RPT0 (_CRT_WARN, "ｸﾞﾛｰﾊﾞﾙﾒﾓﾘ固定不能。ｺﾋﾟｰは失敗しました。");
		GlobalFree (hGlobalMem);
		return FALSE;
	}
	_tcsncpy (p, (LPCTSTR)strTextCopy, strTextCopy.GetLength ());
	if (GlobalUnlock (hGlobalMem) == TRUE) {
		//まだﾛｯｸされてればTRUE,解放されたorｴﾗｰはFALSE。
		_RPT0 (_CRT_WARN, "ｸﾞﾛｰﾊﾞﾙﾒﾓﾘ固定異常。ｺﾋﾟｰは失敗しました。");
		GlobalFree (hGlobalMem);
		return FALSE;
	}
	if (::OpenClipboard (AfxGetMainWnd()->m_hWnd) == FALSE) {
		_RPT0 (_CRT_WARN, "ｸﾘｯﾌﾟﾎﾞｰﾄﾞｵｰﾌﾟﾝ不能。ｺﾋﾟｰは失敗しました。");
		GlobalFree (hGlobalMem);
		return FALSE;
	}
	if (::EmptyClipboard() == FALSE) {
		::CloseClipboard();//先に閉じないとﾀﾞｲｱﾛｸﾞ表示時にいじられる。
		_RPT0 (_CRT_WARN, "ｸﾘｯﾌﾟﾎﾞｰﾄﾞ初期化不能。ｺﾋﾟｰは失敗しました。");
		GlobalFree (hGlobalMem);
		return FALSE;
	}
	if (::SetClipboardData (CF_UNICODETEXT, hGlobalMem) == FALSE) {
		::CloseClipboard ();
		_RPT0 (_CRT_WARN, "ｸﾘｯﾌﾟﾎﾞｰﾄﾞ転送不能。ｺﾋﾟｰは失敗しました。");
		GlobalFree (hGlobalMem);
		return FALSE;
	}
	CloseClipboard ();
	return TRUE;

}

// 貼り付け(親ウィンドウへの通知機能付き)
BOOL CTextGridCtrl::PasteWithNotify () {
	BOOL bRet = Paste ();
	if (bRet) {
		if (m_hWnd != NULL && GetParent () != NULL && m_bNotify == FALSE) {
			m_bNotify = TRUE;
			TextGridNotify theNotify = { 0 };
			theNotify.hdr.code = TGN_PASTE;
			theNotify.hdr.hwndFrom = (this->m_hWnd);
			theNotify.hdr.idFrom = GetDlgCtrlID ();
			theNotify.m_nTopRow = m_nSelectedTopRow;
			theNotify.m_nBottomRow = m_nSelectedBottomRow;
			theNotify.m_nLeftCol = m_nSelectedLeftCol;
			theNotify.m_nRightCol = m_nSelectedRightCol;
			GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
			m_bNotify = FALSE;
		}
	}
	return bRet;
}

// 貼り付け
BOOL CTextGridCtrl::Paste () {
	// 画面のdpi取得
	HDC hDC = ::GetDC (this->GetSafeHwnd ());
	long lLogPixelsX = ::GetDeviceCaps (hDC, LOGPIXELSX);
	long lLogPixelsY = ::GetDeviceCaps (hDC, LOGPIXELSY);
	::ReleaseDC (this->GetSafeHwnd (), hDC);

	// クリップボードからデータ転送
	if (IsClipboardFormatAvailable(CF_UNICODETEXT) == FALSE) {
		_RPT0 (_CRT_WARN, "ｸﾘｯﾌﾟﾎﾞｰﾄﾞが有効な形式ではありません。貼り付けに失敗しました。");
		return FALSE;
	}
	if (::OpenClipboard(AfxGetMainWnd()->m_hWnd) == FALSE) {
		_RPT0 (_CRT_WARN, "ｸﾘｯﾌﾟﾎﾞｰﾄﾞが開けません。貼り付けに失敗しました。");
		return FALSE;
	}
	HGLOBAL hData = NULL;
	if ((hData = GetClipboardData(CF_UNICODETEXT)) == NULL) {
		CloseClipboard ();//先に閉じないとﾀﾞｲｱﾛｸﾞ表示中にいじられる。
		_RPT0 (_CRT_WARN, "ｸﾘｯﾌﾟﾎﾞｰﾄﾞから転送不能。貼り付けに失敗しました。");
		return FALSE;
	}
	TCHAR* p = NULL;//転送元ﾊﾞｯﾌｧ。
	if ((p = (TCHAR*)GlobalLock(hData)) == NULL) {
		CloseClipboard();
		_RPT0 (_CRT_WARN, "ｸﾞﾛｰﾊﾞﾙﾒﾓﾘ固定不能。貼り付けに失敗しました。");
		return FALSE;
	}
	CString strTextPaste (p);
	if ((GlobalUnlock(hData)) == TRUE) {
		//まだﾛｯｸされてればTRUE,解放されたorｴﾗｰはFALSE。
		CloseClipboard();
		_RPT0 (_CRT_WARN, "ｸﾞﾛｰﾊﾞﾙﾒﾓﾘ固定異常(致命的)。貼り付けに失敗しました。");
		//return FALSE;
	}
	//重要！プログラミングWindows95(アスキー) P.929　に以下のように記述。
	//『ｸﾘｯﾌﾟﾎﾞｰﾄﾞをｸﾛｰｽﾞする前にﾃﾞｰﾀﾌﾞﾛｯｸのﾛｯｸをはずす。』
	if (::CloseClipboard() == FALSE) {
		_RPT0 (_CRT_WARN, "ｸﾘｯﾌﾟﾎﾞｰﾄﾞを閉じられません。しかし処理を続けます。");
	}
	// 文字列のペースト
	if (strTextPaste == _T("")) {
		_RPT0 (_CRT_WARN, "貼り付ける文字列がありません。貼り付けに失敗しました。");
		return FALSE;
	}


	int i = m_nSelectedTopRow;
	int j = m_nSelectedLeftCol;
	int jMax = 0;
	CString strTextLine;
	CString strTextCell;
	while (1) {
		j = m_nSelectedLeftCol;
		int nn = strTextPaste.Find (_T("\r\n"));
		if (nn < 0) {
			strTextLine = strTextPaste;
			if (strTextLine == _T("")) {
				i--;
				break;
			}
			strTextPaste = _T ("");
		}
		else {
			strTextLine = strTextPaste.Left (nn);
			strTextPaste = strTextPaste.Mid (nn + 2);	
		}
		while (1) {
			int nt = strTextLine.Find (_T("\t"));
			if (nt < 0) {
				strTextCell = strTextLine;
				strTextLine = _T("");
			}
			else {
				strTextCell = strTextLine.Left (nt);
				strTextLine = strTextLine.Mid (nt + 1);
			}
			if (0 <= i && i <= m_nNumRows - 1 && 0 <= j && j <= m_nNumCols - 1) {
				if (IsReadOnly () == FALSE && IsCellReadOnly (i, j) == FALSE) {

					// 履歴登録
					if (m_nCurHistoryIndex >= 0) {
						CTextGridHistoryEvent* pHistoryEvent = new CTextGridHistoryEvent ();
						if (pHistoryEvent == NULL) {
							AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
							break;
						}
						pHistoryEvent->m_nEventType = TEXTGRIDCTRL_MODIFYCELLSTRING;
						pHistoryEvent->m_nModifiedTopRow = i;
						pHistoryEvent->m_nModifiedBottomRow = i;
						pHistoryEvent->m_nModifiedLeftCol = j;
						pHistoryEvent->m_nModifiedRightCol = j;
						CString strOldTextCell = m_theCellStringArray.GetAt (i * m_nNumCols + j);
						pHistoryEvent->m_theOldCellStringArray.Add (strOldTextCell);
						pHistoryEvent->m_theNewCellStringArray.Add (strTextCell);
						AddHistoryEvent (pHistoryEvent);
					}

					// 設定
					m_theCellStringArray.SetAt (i * m_nNumCols + j, strTextCell);
				}
			}
			if (j > jMax) {
				jMax = j;
			}
			if (nt < 0) {
				break;
			}
			j++;
		}
		if (nn < 0) {
			break;
		}
		i++;
	}

	// 履歴登録
	if (m_nCurHistoryIndex >= 0) {
		CTextGridHistoryEvent* pHistoryEvent2 = new CTextGridHistoryEvent ();
		if (pHistoryEvent2 == NULL) {
			AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
			return FALSE;
		}
		pHistoryEvent2->m_nEventType = TEXTGRIDCTRL_MODIFYCURCELL;
		pHistoryEvent2->m_nOldCurRow = GetCurRow ();
		pHistoryEvent2->m_nOldCurCol = GetCurCol ();
		pHistoryEvent2->m_nNewCurRow = m_nSelectedTopRow;
		pHistoryEvent2->m_nNewCurCol = m_nSelectedLeftCol;
		AddHistoryEvent (pHistoryEvent2);
	}

	// 設定
	SetCurCell (m_nSelectedTopRow, m_nSelectedLeftCol, lLogPixelsX, lLogPixelsY);

	// 履歴登録
	if (m_nCurHistoryIndex >= 0) {
		CTextGridHistoryEvent* pHistoryEvent3 = new CTextGridHistoryEvent ();
		if (pHistoryEvent3 == NULL) {
			AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
			return FALSE;
		}
		pHistoryEvent3->m_nEventType = TEXTGRIDCTRL_MODIFYSELECT;
		pHistoryEvent3->m_nOldSelectedTopRow = m_nSelectedTopRow;
		pHistoryEvent3->m_nOldSelectedBottomRow = m_nSelectedBottomRow;
		pHistoryEvent3->m_nOldSelectedLeftCol = m_nSelectedLeftCol;
		pHistoryEvent3->m_nOldSelectedRightCol = m_nSelectedRightCol;
		pHistoryEvent3->m_nOldSelectedTopRow = m_nSelectedTopRow;
		pHistoryEvent3->m_nOldSelectedBottomRow = TEXTGRIDCTRL_RANGE (0, i, m_nNumRows - 1);
		pHistoryEvent3->m_nOldSelectedLeftCol = m_nSelectedLeftCol;
		pHistoryEvent3->m_nOldSelectedRightCol = TEXTGRIDCTRL_RANGE (0, jMax, m_nNumCols - 1);
		AddHistoryEvent (pHistoryEvent3);
	}

	// 設定
	SelectArea (m_nSelectedTopRow, TEXTGRIDCTRL_RANGE (0, i, m_nNumRows - 1),
		m_nSelectedLeftCol, TEXTGRIDCTRL_RANGE (0, jMax, m_nNumCols - 1));
	return TRUE;
}

// 削除(親ウィンドウへの通知機能付き)
BOOL CTextGridCtrl::DeleteWithNotify () {
	BOOL bRet = Delete ();
	if (bRet) {
		if (m_hWnd != NULL && GetParent () != NULL && m_bCut == FALSE && m_bNotify == FALSE) {
			m_bNotify = TRUE;
			TextGridNotify theNotify = { 0 };
			theNotify.hdr.code = TGN_DELETE;
			theNotify.hdr.hwndFrom = (this->m_hWnd);
			theNotify.hdr.idFrom = GetDlgCtrlID ();
			theNotify.m_nTopRow = m_nSelectedTopRow;
			theNotify.m_nBottomRow = m_nSelectedBottomRow;
			theNotify.m_nLeftCol = m_nSelectedLeftCol;
			theNotify.m_nRightCol = m_nSelectedRightCol;
			GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
			m_bNotify = FALSE;
		}
	}
	return bRet;
}

// 削除
BOOL CTextGridCtrl::Delete () {
	for (int i = m_nSelectedTopRow; i <= m_nSelectedBottomRow; i++) {
		for (int j = m_nSelectedLeftCol; j <= m_nSelectedRightCol; j++) {

			// 履歴登録
			if (m_nCurHistoryIndex >= 0) {
				CTextGridHistoryEvent* pHistoryEvent = new CTextGridHistoryEvent ();
				if (pHistoryEvent == NULL) {
					AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
					break;
				}
				pHistoryEvent->m_nEventType = TEXTGRIDCTRL_MODIFYCELLSTRING;
				pHistoryEvent->m_nModifiedTopRow = i;
				pHistoryEvent->m_nModifiedBottomRow = i;
				pHistoryEvent->m_nModifiedLeftCol = j;
				pHistoryEvent->m_nModifiedRightCol = j;
				CString strOldCellString = m_theCellStringArray.GetAt (i * m_nNumCols + j);
				pHistoryEvent->m_theOldCellStringArray.Add (strOldCellString);
				pHistoryEvent->m_theNewCellStringArray.Add (_T (""));
				AddHistoryEvent (pHistoryEvent);
			}

			// 設定
			m_theCellStringArray.SetAt (i * m_nNumCols + j, _T(""));
		}
	}
	return TRUE;
}

// 元に戻す
BOOL CTextGridCtrl::Undo () {
	if (m_nCurHistoryIndex < 0) {
		return FALSE;
	}
	CTextGridHistoryBlock* pHistoryBlock = (CTextGridHistoryBlock*)(m_theHistoryBlockArray.GetAt (m_nCurHistoryIndex));
	int nNumHistoryEvent = pHistoryBlock->m_theHistoryEventArray.GetCount ();
	for (int j = nNumHistoryEvent - 1; j >= 0; j--) {
		CTextGridHistoryEvent* pHistoryEvent = (CTextGridHistoryEvent*)(pHistoryBlock->m_theHistoryEventArray.GetAt (j));
		switch (pHistoryEvent->m_nEventType) {
		case TEXTGRIDCTRL_MODIFYCURCELL:
			m_nCurRow = pHistoryEvent->m_nOldCurRow;
			m_nCurCol = pHistoryEvent->m_nOldCurCol;
			break;
		case TEXTGRIDCTRL_MODIFYSELECT:
			m_nSelectedTopRow = pHistoryEvent->m_nOldSelectedTopRow;
			m_nSelectedBottomRow = pHistoryEvent->m_nOldSelectedBottomRow;
			m_nSelectedLeftCol = pHistoryEvent->m_nOldSelectedLeftCol;
			m_nSelectedRightCol = pHistoryEvent->m_nOldSelectedRightCol;
			break;
		case TEXTGRIDCTRL_MODIFYCELLSTRING:
			for (int ii = pHistoryEvent->m_nModifiedTopRow; ii <= pHistoryEvent->m_nModifiedBottomRow; ii++) {
				for (int jj = pHistoryEvent->m_nModifiedLeftCol; jj <= pHistoryEvent->m_nModifiedRightCol; jj++) {
					int nDestIndex = ii * m_nNumCols + jj;
					int nSrcIndex = (ii - pHistoryEvent->m_nModifiedTopRow) * m_nNumCols +
						(jj - pHistoryEvent->m_nModifiedLeftCol);
					m_theCellStringArray.SetAt (nDestIndex, pHistoryEvent->m_theOldCellStringArray.GetAt (nSrcIndex));
				}
			}
			break;
		case TEXTGRIDCTRL_MODIFYREADONLY:
			for (int ii = pHistoryEvent->m_nModifiedTopRow; ii <= pHistoryEvent->m_nModifiedBottomRow; ii++) {
				for (int jj = pHistoryEvent->m_nModifiedLeftCol; jj <= pHistoryEvent->m_nModifiedRightCol; jj++) {
					int nDestIndex = ii * m_nNumCols + jj;
					int nSrcIndex = (ii - pHistoryEvent->m_nModifiedTopRow) * m_nNumCols +
						(jj - pHistoryEvent->m_nModifiedLeftCol);
					m_theReadOnlyArray.SetAt (nDestIndex, pHistoryEvent->m_theOldReadOnlyArray.GetAt (nSrcIndex));
				}
			}
			break;
		case TEXTGRIDCTRL_MODIFYTEXTCOLOR:
			for (int ii = pHistoryEvent->m_nModifiedTopRow; ii <= pHistoryEvent->m_nModifiedBottomRow; ii++) {
				for (int jj = pHistoryEvent->m_nModifiedLeftCol; jj <= pHistoryEvent->m_nModifiedRightCol; jj++) {
					int nDestIndex = ii * m_nNumCols + jj;
					int nSrcIndex = (ii - pHistoryEvent->m_nModifiedTopRow) * m_nNumCols +
						(jj - pHistoryEvent->m_nModifiedLeftCol);
					m_theTextColorArray.SetAt (nDestIndex, pHistoryEvent->m_theOldTextColorArray.GetAt (nSrcIndex));
				}
			}
			break;
		default:
			ASSERT (FALSE);
		}
	}
	m_nCurHistoryIndex--;

	return TRUE;
}

// 元に戻す
BOOL CTextGridCtrl::UndoWithNotify () {
	BOOL bRet = Undo ();
	if (bRet) {
		if (m_hWnd != NULL && GetParent () != NULL && m_bCut == FALSE && m_bNotify == FALSE) {
			m_bNotify = TRUE;
			TextGridNotify theNotify = { 0 };
			theNotify.hdr.code = TGN_UNDO;
			theNotify.hdr.hwndFrom = (this->m_hWnd);
			theNotify.hdr.idFrom = GetDlgCtrlID ();
			GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
			m_bNotify = FALSE;
		}
	}
	return bRet;
}

// やり直し
BOOL CTextGridCtrl::Redo () {
	if (m_nCurHistoryIndex + 1 > m_theHistoryBlockArray.GetCount () - 1) {
		return FALSE;
	}
	m_nCurHistoryIndex++;

	CTextGridHistoryBlock* pHistoryBlock = (CTextGridHistoryBlock*)(m_theHistoryBlockArray.GetAt (m_nCurHistoryIndex));
	int nNumHistoryEvent = pHistoryBlock->m_theHistoryEventArray.GetCount ();
	for (int j = 0; j < nNumHistoryEvent; j++) {
		CTextGridHistoryEvent* pHistoryEvent = (CTextGridHistoryEvent*)(pHistoryBlock->m_theHistoryEventArray.GetAt (j));
		switch (pHistoryEvent->m_nEventType) {
		case TEXTGRIDCTRL_MODIFYCURCELL:
			m_nCurRow = pHistoryEvent->m_nNewCurRow;
			m_nCurCol = pHistoryEvent->m_nNewCurCol;
			break;
		case TEXTGRIDCTRL_MODIFYSELECT:
			m_nSelectedTopRow = pHistoryEvent->m_nNewSelectedTopRow;
			m_nSelectedBottomRow = pHistoryEvent->m_nNewSelectedBottomRow;
			m_nSelectedLeftCol = pHistoryEvent->m_nNewSelectedLeftCol;
			m_nSelectedRightCol = pHistoryEvent->m_nNewSelectedRightCol;
			break;
		case TEXTGRIDCTRL_MODIFYCELLSTRING:
			for (int ii = pHistoryEvent->m_nModifiedTopRow; ii <= pHistoryEvent->m_nModifiedBottomRow; ii++) {
				for (int jj = pHistoryEvent->m_nModifiedLeftCol; jj <= pHistoryEvent->m_nModifiedRightCol; jj++) {
					int nDestIndex = ii * m_nNumCols + jj;
					int nSrcIndex = (ii - pHistoryEvent->m_nModifiedTopRow) * m_nNumCols +
						(jj - pHistoryEvent->m_nModifiedLeftCol);
					m_theCellStringArray.SetAt (nDestIndex, pHistoryEvent->m_theNewCellStringArray.GetAt (nSrcIndex));
				}
			}
			break;
		case TEXTGRIDCTRL_MODIFYREADONLY:
			for (int ii = pHistoryEvent->m_nModifiedTopRow; ii <= pHistoryEvent->m_nModifiedBottomRow; ii++) {
				for (int jj = pHistoryEvent->m_nModifiedLeftCol; jj <= pHistoryEvent->m_nModifiedRightCol; jj++) {
					int nDestIndex = ii * m_nNumCols + jj;
					int nSrcIndex = (ii - pHistoryEvent->m_nModifiedTopRow) * m_nNumCols +
						(jj - pHistoryEvent->m_nModifiedLeftCol);
					m_theReadOnlyArray.SetAt (nDestIndex, pHistoryEvent->m_theNewReadOnlyArray.GetAt (nSrcIndex));
				}
			}

			break;
		case TEXTGRIDCTRL_MODIFYTEXTCOLOR:
			for (int ii = pHistoryEvent->m_nModifiedTopRow; ii <= pHistoryEvent->m_nModifiedBottomRow; ii++) {
				for (int jj = pHistoryEvent->m_nModifiedLeftCol; jj <= pHistoryEvent->m_nModifiedRightCol; jj++) {
					int nDestIndex = ii * m_nNumCols + jj;
					int nSrcIndex = (ii - pHistoryEvent->m_nModifiedTopRow) * m_nNumCols +
						(jj - pHistoryEvent->m_nModifiedLeftCol);
					m_theTextColorArray.SetAt (nDestIndex, pHistoryEvent->m_theNewTextColorArray.GetAt (nSrcIndex));
				}
			}

			break;
		default:
			ASSERT (FALSE);
		}
	}
	return TRUE;
}

// 元に戻す
BOOL CTextGridCtrl::RedoWithNotify () {
	BOOL bRet = Redo ();
	if (bRet) {
		if (m_hWnd != NULL && GetParent () != NULL && m_bCut == FALSE && m_bNotify == FALSE) {
			m_bNotify = TRUE;
			TextGridNotify theNotify = { 0 };
			theNotify.hdr.code = TGN_REDO;
			theNotify.hdr.hwndFrom = (this->m_hWnd);
			theNotify.hdr.idFrom = GetDlgCtrlID ();
			GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
			m_bNotify = FALSE;
		}
	}
	return bRet;
}

// 履歴関係

// ひとつの操作履歴を追加
BOOL CTextGridCtrl::AddHistoryBlock (CTextGridHistoryBlock* pHistoryBlock) {
	int nRet = m_theHistoryBlockArray.Add (pHistoryBlock);
	m_nCurHistoryIndex = m_theHistoryBlockArray.GetCount () - 1;
	return nRet;
}

// 最後の操作履歴の中にイベントを追加
BOOL CTextGridCtrl::AddHistoryEvent (CTextGridHistoryEvent* pHistoryEvent) {
	int nNumHistoryBlock = m_theHistoryBlockArray.GetCount ();
	if (nNumHistoryBlock <= 0) {
		return FALSE;
	}
	CTextGridHistoryBlock* pHistoryBlock = (CTextGridHistoryBlock*)m_theHistoryBlockArray.GetAt (nNumHistoryBlock - 1);
	return pHistoryBlock->m_theHistoryEventArray.Add (pHistoryEvent);
}

// nHisotryIndex以降の操作履歴を削除する
BOOL CTextGridCtrl::DeleteHistoryBlockAfter (int nHistoryIndex) {
	int nNumHistoryBlock = m_theHistoryBlockArray.GetCount ();
	for (int i = nNumHistoryBlock - 1; i >= nHistoryIndex + 1; i--) {
		CTextGridHistoryBlock* pHistoryBlock = (CTextGridHistoryBlock*)(m_theHistoryBlockArray.GetAt (i));
		int nNumHistoryEvent = pHistoryBlock->m_theHistoryEventArray.GetCount ();
		for (int j = nNumHistoryEvent - 1; j >= 0; j--) {
			CTextGridHistoryEvent* pHistoryEvent = (CTextGridHistoryEvent*)(pHistoryBlock->m_theHistoryEventArray.GetAt (j));
			pHistoryEvent->m_theOldCellStringArray.RemoveAll ();
			pHistoryEvent->m_theOldReadOnlyArray.RemoveAll ();
			pHistoryEvent->m_theOldTextColorArray.RemoveAll ();
			pHistoryEvent->m_theNewCellStringArray.RemoveAll ();
			pHistoryEvent->m_theNewReadOnlyArray.RemoveAll ();
			pHistoryEvent->m_theNewTextColorArray.RemoveAll ();
			delete pHistoryEvent;
			pHistoryBlock->m_theHistoryEventArray.RemoveAt (j);
		}
		delete pHistoryBlock;
		pHistoryBlock = NULL;
		m_theHistoryBlockArray.RemoveAt (i);
	}
	return TRUE;
}

// すべての履歴を削除する(オペレーションとして呼びだし可能)
BOOL CTextGridCtrl::ClearHistory () {
	int nNumHistoryBlock = m_theHistoryBlockArray.GetCount ();
	for (int i = nNumHistoryBlock - 1; i >= 0; i--) {
		CTextGridHistoryBlock* pHistoryBlock = (CTextGridHistoryBlock*)(m_theHistoryBlockArray.GetAt (i));
		int nNumHistoryEvent = pHistoryBlock->m_theHistoryEventArray.GetCount ();
		for (int j = nNumHistoryEvent - 1; j >= 0; j--) {
			CTextGridHistoryEvent* pHistoryEvent = (CTextGridHistoryEvent*)(pHistoryBlock->m_theHistoryEventArray.GetAt (j));
			pHistoryEvent->m_theOldCellStringArray.RemoveAll ();
			pHistoryEvent->m_theOldReadOnlyArray.RemoveAll ();
			pHistoryEvent->m_theOldTextColorArray.RemoveAll ();
			pHistoryEvent->m_theNewCellStringArray.RemoveAll ();
			pHistoryEvent->m_theNewReadOnlyArray.RemoveAll ();
			pHistoryEvent->m_theNewTextColorArray.RemoveAll ();
			delete pHistoryEvent;
			pHistoryBlock->m_theHistoryEventArray.RemoveAt (j);
		}
		delete pHistoryBlock;
		pHistoryBlock = NULL;
		m_theHistoryBlockArray.RemoveAt (i);
	}
	m_theHistoryBlockArray.RemoveAll ();
	m_nCurHistoryIndex = -1;
	return TRUE;
}

BOOL CTextGridCtrl::UpdateContextMenuState () {
	BOOL bCanUndo = !m_bReadOnly && (m_nCurHistoryIndex >= 0 ? TRUE : FALSE);
	BOOL bCanRedo = !m_bReadOnly && (m_nCurHistoryIndex + 1 <= m_theHistoryBlockArray.GetCount () - 1 ? TRUE : FALSE);
	BOOL bCanCut = !m_bReadOnly;
	BOOL bCanPaste = !m_bReadOnly && (IsClipboardFormatAvailable (CF_UNICODETEXT) == TRUE);
	BOOL bCanDelete = !m_bReadOnly;
	m_theContextMenu.EnableMenuItem (IDM_CONTEXT_UNDO, MF_BYCOMMAND | (bCanUndo ? MF_ENABLED : MF_DISABLED));
	m_theContextMenu.EnableMenuItem (IDM_CONTEXT_REDO, MF_BYCOMMAND | (bCanRedo ? MF_ENABLED : MF_DISABLED));
	m_theContextMenu.EnableMenuItem (IDM_CONTEXT_CUT, MF_BYCOMMAND | (bCanCut ? MF_ENABLED : MF_DISABLED));
	m_theContextMenu.EnableMenuItem (IDM_CONTEXT_PASTE, MF_BYCOMMAND | (bCanPaste ? MF_ENABLED : MF_DISABLED));
	m_theContextMenu.EnableMenuItem (IDM_CONTEXT_DELETE, MF_BYCOMMAND | (bCanDelete ? MF_ENABLED : MF_DISABLED));
	return TRUE;
}


// タブキーダウン以外のメッセージのオーバーライド
BOOL CTextGridCtrl::PreTranslateMessage (MSG* msg) {
	if (msg->message == WM_KEYDOWN && msg->wParam == VK_TAB) {
		return CWnd::PreTranslateMessage (msg);
	}
	::TranslateMessage (msg);
	::DispatchMessage (msg);
	return TRUE;
}

// メッセージマップ

// ウィンドウ作成時
int CTextGridCtrl::OnCreate (LPCREATESTRUCT lpCreateStruct) {
	// 画面のdpi取得
	HDC hDC = ::GetDC (this->GetSafeHwnd ());
	long lLogPixelsX = ::GetDeviceCaps (hDC, LOGPIXELSX);
	long lLogPixelsY = ::GetDeviceCaps (hDC, LOGPIXELSY);
	::ReleaseDC (this->GetSafeHwnd (), hDC);

	// グリッド描画用のペン設定
	//if (IsReadOnly () == TRUE || IsWindowEnabled () == FALSE) {
	VERIFY (m_theGridPen.CreatePen (PS_SOLID, 1, GetSysColor (COLOR_APPWORKSPACE)));
	VERIFY (m_theWindowBackBrush.CreateSolidBrush (GetSysColor (COLOR_BTNFACE)));
	//}
	//else {
	//	VERIFY (m_theGridPen.CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNFACE)));
	//	VERIFY (m_theWindowBackBrush.CreateSolidBrush (GetSysColor (COLOR_WINDOW)));
	//}
	VERIFY (m_theCurCellPen.CreatePen (PS_SOLID, 3, RGB (0, 0, 0)));
	VERIFY (m_theNullBrush.CreateStockObject (NULL_BRUSH));
	// テキスト描画用のフォント設定
	LOGFONT lf;
	CFont* pFont = CFont::FromHandle 
		((HFONT)(::GetStockObject (DEFAULT_GUI_FONT)));
	pFont->GetLogFont (&lf);
	VERIFY (m_theTextFont.CreateFontIndirect (&lf));
	//VERIFY (m_theTextFont.CreateFont (12, 0, 0, 0, FW_THIN, FALSE, FALSE, FALSE,
	//	DEFAULT_CHARSET, OUT_CHARACTER_PRECIS, CLIP_CHARACTER_PRECIS, DEFAULT_QUALITY,
	//	DEFAULT_PITCH, "ＭＳ Ｐゴシック"));
	// テキストボックスの生成
	VERIFY (m_theTextBox.Create (WS_CHILD | ES_AUTOHSCROLL, 
		CRect (0, 0, 0, 0), this, IDC_TEXTBOX));
	m_theTextBox.SetFont (&m_theTextFont);
	m_theTextBox.ShowWindow (SW_HIDE);
	MoveTextBox (lLogPixelsX, lLogPixelsY);
	// コンテキストメニューの作成
	m_theContextMenu.CreatePopupMenu ();
	m_theContextMenu.AppendMenu (MF_STRING, IDM_CONTEXT_UNDO, _T ("元に戻す(&U)"));
	m_theContextMenu.AppendMenu (MF_STRING, IDM_CONTEXT_REDO, _T ("やり直し(&R)"));
	m_theContextMenu.AppendMenu (MF_SEPARATOR);
	m_theContextMenu.AppendMenu (MF_STRING, IDM_CONTEXT_CUT, _T("切り取り(&T)"));
	m_theContextMenu.AppendMenu (MF_STRING, IDM_CONTEXT_COPY, _T("コピー(&C)"));
	m_theContextMenu.AppendMenu (MF_STRING, IDM_CONTEXT_PASTE, _T("貼り付け(&P)"));
	m_theContextMenu.AppendMenu (MF_STRING, IDM_CONTEXT_DELETE, _T("削除(&D)"));
	return CWnd::OnCreate (lpCreateStruct);
}

// ウィンドウ破棄時
void CTextGridCtrl::OnDestroy () {
	if (m_theTextBox.m_hWnd) {
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
	}
	VERIFY (m_theGridPen.DeleteObject ());
	VERIFY (m_theCurCellPen.DeleteObject ());
	VERIFY (m_theNullBrush.DeleteObject ());
	VERIFY (m_theWindowBackBrush.DeleteObject ());
	VERIFY (m_theTextFont.DeleteObject ());
	VERIFY (m_theTextBox.DestroyWindow ());
	VERIFY (m_theContextMenu.DestroyMenu ());
	CWnd::OnDestroy ();
}

// ウィンドウサイズ変更時
void CTextGridCtrl::OnSize (UINT nType, int cx, int cy) {
	this->AdjustVScrollBar ();
	this->AdjustHScrollBar ();
	CWnd::OnSize (nType, cx, cy);
}

// イネーブルが変更された時
void CTextGridCtrl::OnEnable (BOOL bEnable) {
	/*m_theGridPen.DeleteObject ();
	m_theWindowBackBrush.DeleteObject ();
	if (IsReadOnly () == TRUE || bEnable == FALSE) {
		VERIFY (m_theGridPen.CreatePen (PS_SOLID, 1, GetSysColor (COLOR_APPWORKSPACE)));
		VERIFY (m_theWindowBackBrush.CreateSolidBrush (GetSysColor (COLOR_BTNFACE)));
	}
	else {
		VERIFY (m_theGridPen.CreatePen (PS_SOLID, 1, GetSysColor (COLOR_BTNFACE)));
		VERIFY (m_theWindowBackBrush.CreateSolidBrush (GetSysColor (COLOR_WINDOW)));
	}*/
	CWnd::OnEnable (bEnable);
}

// フォーカスを受け取ったとき
void CTextGridCtrl::OnSetFocus (CWnd* pOldWnd) {
	if (m_hWnd != NULL && GetParent () != NULL && pOldWnd != &m_theTextBox && m_bNotify == FALSE) {
		m_bNotify = TRUE;
		TextGridNotify theNotify = { 0 };
		theNotify.hdr.code = TGN_SETFOCUS;
		theNotify.hdr.hwndFrom = (this->m_hWnd);
		theNotify.hdr.idFrom = GetDlgCtrlID ();
		GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
		m_bNotify = FALSE;
	}
	CWnd::OnSetFocus (pOldWnd);
}

// フォーカスを失ったとき
void CTextGridCtrl::OnKillFocus (CWnd* pNewWnd) {
	if (m_theTextBox.m_hWnd) {
		if (IsTextEditing () && pNewWnd != &m_theTextBox) {
			EndTextEditingOK ();
		}
	}
	if (m_hWnd != NULL && GetParent () != NULL && pNewWnd != &m_theTextBox && m_bNotify == FALSE) {
		m_bNotify = TRUE;
		TextGridNotify theNotify = { 0 };
		theNotify.hdr.code = TGN_KILLFOCUS;
		theNotify.hdr.hwndFrom = (this->m_hWnd);
		theNotify.hdr.idFrom = GetDlgCtrlID ();
		GetParent ()->SendMessage (WM_NOTIFY, (WPARAM)GetDlgCtrlID (), (LPARAM)(&theNotify));
		m_bNotify = FALSE;
	}
	CWnd::OnKillFocus (pNewWnd);
}

// 垂直スクロール時
void CTextGridCtrl::OnVScroll (UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) {
	// 画面のdpi取得
	HDC hDC = ::GetDC (this->GetSafeHwnd ());
	long lLogPixelsX = ::GetDeviceCaps (hDC, LOGPIXELSX);
	long lLogPixelsY = ::GetDeviceCaps (hDC, LOGPIXELSY);
	::ReleaseDC (this->GetSafeHwnd (), hDC);

	if (IsTextEditing ()) {
		EndTextEditingOK ();
	}
	switch (nSBCode) {
	case SB_LINEUP:
		SetVisibleTopRow
			(TEXTGRIDCTRL_RANGE (0, GetVisibleTopRow () - 1, m_nNumRows - 1), lLogPixelsX, lLogPixelsY);
		break;
	case SB_LINEDOWN:
		SetVisibleTopRow
			(TEXTGRIDCTRL_RANGE (0, GetVisibleTopRow () + 1, m_nNumRows - 1), lLogPixelsX, lLogPixelsY);
		break;
	case SB_PAGEUP:
		SetVisibleBottomRow
			(TEXTGRIDCTRL_RANGE (0, GetVisibleTopRow (), m_nNumRows - 1), lLogPixelsX, lLogPixelsY);
		break;
	case SB_PAGEDOWN:
		SetVisibleTopRow
			(TEXTGRIDCTRL_RANGE (0, GetVisibleBottomRow (lLogPixelsX, lLogPixelsY), m_nNumRows - 1), lLogPixelsX, lLogPixelsY);
		break;
	case SB_THUMBPOSITION:
	case SB_THUMBTRACK:
		SetVisibleTopRow
			(TEXTGRIDCTRL_RANGE (0, (int)nPos, m_nNumRows - 1), lLogPixelsX, lLogPixelsY);
		break;
	}
	MoveTextBox (lLogPixelsX, lLogPixelsY);
	Invalidate ();
}

// 水平スクロール時
void CTextGridCtrl::OnHScroll (UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) {
	// 画面のdpi取得
	HDC hDC = ::GetDC (this->GetSafeHwnd ());
	long lLogPixelsX = ::GetDeviceCaps (hDC, LOGPIXELSX);
	long lLogPixelsY = ::GetDeviceCaps (hDC, LOGPIXELSY);
	::ReleaseDC (this->GetSafeHwnd (), hDC);

	if (IsTextEditing ()) {
		EndTextEditingOK ();
	}
	switch (nSBCode) {
	case SB_LINELEFT:
		SetVisibleLeftCol
			(TEXTGRIDCTRL_RANGE (0, GetVisibleLeftCol () - 1, m_nNumCols - 1), lLogPixelsX, lLogPixelsY);
		break;
	case SB_LINERIGHT:
		SetVisibleLeftCol
			(TEXTGRIDCTRL_RANGE (0, GetVisibleLeftCol () + 1, m_nNumCols - 1), lLogPixelsX, lLogPixelsY);
		break;
	case SB_PAGELEFT:
		SetVisibleRightCol
			(TEXTGRIDCTRL_RANGE (0, GetVisibleLeftCol (), m_nNumCols - 1), lLogPixelsX, lLogPixelsY);
		break;
	case SB_PAGERIGHT:
		SetVisibleLeftCol
			(TEXTGRIDCTRL_RANGE (0, GetVisibleRightCol (lLogPixelsX, lLogPixelsY), m_nNumCols - 1), lLogPixelsX, lLogPixelsY);
		break;
	case SB_THUMBPOSITION:
	case SB_THUMBTRACK:
		SetVisibleLeftCol
			(TEXTGRIDCTRL_RANGE (0, (int)nPos, m_nNumCols - 1), lLogPixelsX, lLogPixelsY);
		break;
	}
	MoveTextBox (lLogPixelsX, lLogPixelsY);
	Invalidate ();
}

// 背景の消去 
BOOL CTextGridCtrl::OnEraseBkgnd (CDC* pDC) {
	CRect rcClient;
	GetClientRect (&rcClient);

	// 画面のdpi取得
	HDC hDC = ::GetDC (this->GetSafeHwnd ());
	long lLogPixelsX = ::GetDeviceCaps (hDC, LOGPIXELSX);
	long lLogPixelsY = ::GetDeviceCaps (hDC, LOGPIXELSY);
	::ReleaseDC (this->GetSafeHwnd (), hDC);


	int nGridHeight = 0;
	for (int i = m_nVisibleTopRow; i < m_nNumRows; i++) {
		nGridHeight += GetRowHeight (i) * lLogPixelsY / 96;
	}
	int nGridWidth = 0;
	for (int j = m_nVisibleLeftCol; j < m_nNumCols; j++) {
		nGridWidth += GetColWidth (j) * lLogPixelsX / 96;
	}
	// 右部に余白がある
	if (m_nTitleWidth + nGridWidth < rcClient.right) {
		CRect rcFillRect (rcClient);
		rcFillRect.left = m_nTitleWidth * lLogPixelsX / 96 + nGridWidth;
		pDC->FillSolidRect (rcFillRect, GetSysColor (COLOR_APPWORKSPACE));
	}
	// 下部に余白がある
	if (m_nTitleHeight + nGridHeight < rcClient.bottom) {
		CRect rcFillRect (rcClient);
		rcFillRect.top = m_nTitleHeight * lLogPixelsY / 96 + nGridHeight;
		pDC->FillSolidRect (rcFillRect, GetSysColor (COLOR_APPWORKSPACE));
	}
	return TRUE;
}

// ウィンドウ描画時
void CTextGridCtrl::OnPaint () {
	// 画面のdpi取得
	HDC hDC = ::GetDC (this->GetSafeHwnd ());
	long lLogPixelsX = ::GetDeviceCaps (hDC, LOGPIXELSX);
	long lLogPixelsY = ::GetDeviceCaps (hDC, LOGPIXELSY);
	::ReleaseDC (this->GetSafeHwnd (), hDC);

	CPaintDC dc (this);
	int nVisibleRightCol = TEXTGRIDCTRL_RANGE (0, GetVisibleRightCol (lLogPixelsX, lLogPixelsY), m_nNumCols - 1);
	int nVisibleBottomRow = TEXTGRIDCTRL_RANGE (0, GetVisibleBottomRow (lLogPixelsX, lLogPixelsY), m_nNumRows - 1);

	int i, j;
	int nGridHeight = 0;
	for (i = m_nVisibleTopRow; i <= nVisibleBottomRow; i++) {
		nGridHeight += GetRowHeight (i) * lLogPixelsY / 96;
	}
	int nGridWidth = 0;
	for (j = m_nVisibleLeftCol; j <= nVisibleRightCol; j++) {
		nGridWidth += GetColWidth (j) * lLogPixelsX / 96;
	}

	dc.SetBkMode (TRANSPARENT);
	CFont* pOldFont = dc.SelectObject (&m_theTextFont);
	CBrush* pOldBrush = dc.SelectObject (&m_theWindowBackBrush);
	if (IsWindowEnabled () == FALSE) {
		dc.SetTextColor (GetSysColor (COLOR_GRAYTEXT));
	}
	else {
		dc.SetTextColor (GetSysColor (COLOR_WINDOWTEXT));
	}
	int x, y;
	// 左上タイトル交差部の描画
	dc.FillSolidRect (0, 0, m_nTitleWidth * lLogPixelsX / 96, m_nTitleHeight * lLogPixelsY / 96,
		GetSysColor (COLOR_BTNFACE));
	dc.Draw3dRect (0, 0, m_nTitleWidth * lLogPixelsX / 96, m_nTitleHeight * lLogPixelsY / 96, 
		GetSysColor (COLOR_BTNHIGHLIGHT), GetSysColor (COLOR_BTNSHADOW));
	if (IsSelectedAll ()) {
		dc.PatBlt (0, 0, m_nTitleWidth * lLogPixelsX / 96, m_nTitleHeight * lLogPixelsY / 96, DSTINVERT);
	}
	// 上タイトル部の描画
	x = m_nTitleWidth * lLogPixelsX / 96;
	for (j = m_nVisibleLeftCol; j <= nVisibleRightCol; j++) {
		dc.FillSolidRect (x, 0, GetColWidth (j) * lLogPixelsX / 96, m_nTitleHeight * lLogPixelsY / 96,
			GetSysColor (COLOR_BTNFACE));
		dc.Draw3dRect (x, 0, GetColWidth (j) * lLogPixelsX / 96, m_nTitleHeight * lLogPixelsY / 96,
			GetSysColor (COLOR_BTNHIGHLIGHT), GetSysColor (COLOR_BTNSHADOW));
		dc.DrawText (GetColTitle (j), CRect (x, 0, x + GetColWidth (j) * lLogPixelsX / 96, m_nTitleHeight * lLogPixelsY / 96),
			DT_LEFT | DT_VCENTER | DT_SINGLELINE);
		if (IsSelectedCol (j)) {
			dc.PatBlt (x, 0, GetColWidth (j) * lLogPixelsX / 96, m_nTitleHeight * lLogPixelsY / 96, DSTINVERT);
		}
		x += GetColWidth (j) * lLogPixelsX / 96;
	}
	// 左タイトル部の描画
	y = m_nTitleHeight * lLogPixelsY / 96;
	for (i = m_nVisibleTopRow; i <= nVisibleBottomRow; i++) {
		dc.FillSolidRect (0, y, m_nTitleWidth * lLogPixelsX / 96, GetRowHeight (i) * lLogPixelsY / 96,
			GetSysColor (COLOR_BTNFACE));
		dc.Draw3dRect (0, y, m_nTitleWidth * lLogPixelsX / 96, GetRowHeight (i) * lLogPixelsY / 96, 
			GetSysColor (COLOR_BTNHIGHLIGHT), GetSysColor (COLOR_BTNSHADOW));
		dc.DrawText (GetRowTitle (i), CRect (0, y, m_nTitleWidth * lLogPixelsX / 96, y + GetRowHeight (i) * lLogPixelsY / 96),
			DT_LEFT | DT_VCENTER | DT_SINGLELINE);
		if (IsSelectedRow (i)) {
			dc.PatBlt (0, y, m_nTitleWidth * lLogPixelsX / 96, GetRowHeight (i) * lLogPixelsY / 96, DSTINVERT);
		}
		y += GetRowHeight (i) * lLogPixelsY / 96;
	}

	// 背景色の描画
	y = m_nTitleHeight * lLogPixelsY / 96;
	for (i = m_nVisibleTopRow; i <= nVisibleBottomRow; i++) {
		x = m_nTitleWidth * lLogPixelsX / 96;
		for (j = m_nVisibleLeftCol; j <= nVisibleRightCol; j++) {
			// リードオンリー又はディスエーブルの場合
			if (IsReadOnly () == TRUE || IsCellReadOnly (i, j) == TRUE ||
				IsWindowEnabled () == FALSE) {
				dc.FillSolidRect (
					x, y, GetColWidth (j) * lLogPixelsX / 96, GetRowHeight (i) * lLogPixelsY / 96,
					GetSysColor (COLOR_BTNFACE));
			}
			// 書き込み可の場合
			else {
				dc.FillSolidRect (
					x, y,GetColWidth (j) * lLogPixelsX / 96, GetRowHeight (i) * lLogPixelsY / 96,
					GetSysColor (COLOR_WINDOW));
			}
			x += GetColWidth (j) * lLogPixelsX / 96;
		}
		y += GetRowHeight (i) * lLogPixelsY / 96;
	}
	//if (IsReadOnly () == TRUE || IsWindowEnabled () == FALSE) {
	//	dc.FillSolidRect (m_nTitleWidth * lLogPixelsX / 96, m_nTitleHeight * lLogPixelsY / 96, nGridWidth, nGridHeight, 
	//		GetSysColor (COLOR_BTNFACE));
	//}
	//else {
	//	dc.FillSolidRect (m_nTitleWidth * lLogPixelsX / 96, m_nTitleHeight * lLogPixelsY / 96, nGridWidth, nGridHeight, 
	//		GetSysColor (COLOR_WINDOW));
	//}
	// 横グリッド線の描画
	CPen* pOldPen = dc.SelectObject (&m_theGridPen);
	y = 0;
	for (i = m_nVisibleTopRow; i <= nVisibleBottomRow; i++) {
		y += GetRowHeight (i) * lLogPixelsY / 96;
		dc.MoveTo (m_nTitleWidth * lLogPixelsX / 96, m_nTitleHeight * lLogPixelsY / 96 + y - 1);
		dc.LineTo (m_nTitleWidth * lLogPixelsX / 96 + nGridWidth, m_nTitleHeight * lLogPixelsY / 96 + y - 1);
	}
	// 縦グリッド線の描画
	x = 0;
	for (j = m_nVisibleLeftCol; j <= nVisibleRightCol; j++) {
		x += GetColWidth (j) * lLogPixelsX / 96;
		dc.MoveTo (m_nTitleWidth * lLogPixelsX / 96 + x - 1, m_nTitleHeight * lLogPixelsY / 96);
		dc.LineTo (m_nTitleWidth * lLogPixelsX / 96 + x - 1, m_nTitleHeight * lLogPixelsY / 96 + nGridHeight);
	}
	dc.SelectObject (pOldPen);
	// カレントセルの枠描画
	if (m_nVisibleTopRow <= m_nCurRow && m_nCurRow <= nVisibleBottomRow &&
		m_nVisibleLeftCol <= m_nCurCol && m_nCurCol <= nVisibleRightCol) {
		y = m_nTitleHeight * lLogPixelsY / 96;
		for (i = m_nVisibleTopRow; i < m_nCurRow; i++) {
			y += GetRowHeight (i) * lLogPixelsY / 96;
		}
		x = m_nTitleWidth * lLogPixelsX / 96;
		for (j = m_nVisibleLeftCol; j < m_nCurCol; j++) {
			x += GetColWidth (j) * lLogPixelsX / 96;
		}
		pOldPen = dc.SelectObject (&m_theCurCellPen);
		pOldBrush = dc.SelectObject (&m_theNullBrush);
		dc.Rectangle (x, y, x + GetColWidth (j) * lLogPixelsX / 96, y + GetRowHeight (i) * lLogPixelsY / 96);
		dc.SelectObject (pOldPen);
		dc.SelectObject (pOldBrush);
	}
	// 各セルの文字の描画
	y = m_nTitleHeight * lLogPixelsY / 96;
	for (i = m_nVisibleTopRow; i <= nVisibleBottomRow; i++) {\
		x = m_nTitleWidth * lLogPixelsX / 96;
		for (j = m_nVisibleLeftCol; j <= nVisibleRightCol; j++) {
			dc.SetTextColor (m_theTextColorArray.GetAt (i * m_nNumCols + j));
			dc.DrawText (GetCellString (i, j), 
				CRect (x, y, x + GetColWidth (j) * lLogPixelsX / 96, y + GetRowHeight (i) * lLogPixelsY / 96),
				DT_LEFT | DT_VCENTER | DT_SINGLELINE);
			if (IsSelectedCell (i, j) && (m_nCurRow != i || m_nCurCol != j)) {
				dc.PatBlt (x, y, GetColWidth (j) * lLogPixelsX / 96, GetRowHeight (i) * lLogPixelsY / 96, DSTINVERT);
			}
			x += GetColWidth (j) * lLogPixelsX / 96;
		}
		y += GetRowHeight (i) * lLogPixelsY / 96;
	}

	dc.SelectObject (pOldFont);
	dc.SelectObject (pOldBrush);
}

#define TEXTGRIDCTRL_MOUSEMODENONE 0
#define TEXTGRIDCTRL_MOUSEMODECELLSELECT 1
#define TEXTGRIDCTRL_MOUSEMODEROWSELECT 2
#define TEXTGRIDCTRL_MOUSEMODECOLSELECT 3
#define TEXTGRIDCTRL_MOUSEMODEROWHEIGHTCHANGE 4
#define TEXTGRIDCTRL_MOUSEMODECOLWIDTHCHANGE 5


// マウス左ボタン押し下げ時
void CTextGridCtrl::OnLButtonDown (UINT nFlags, CPoint point) {
	this->SetFocus ();

	// 画面のdpi取得
	HDC hDC = ::GetDC (this->GetSafeHwnd ());
	long lLogPixelsX = ::GetDeviceCaps (hDC, LOGPIXELSX);
	long lLogPixelsY = ::GetDeviceCaps (hDC, LOGPIXELSY);
	::ReleaseDC (this->GetSafeHwnd (), hDC);

	int nRow, nCol;
	this->PointToCell (point, &nRow, &nCol, lLogPixelsX, lLogPixelsY);
	int nRowBorder, nColBorder;
	this->PointToBorder (point, &nRowBorder, &nColBorder, lLogPixelsX, lLogPixelsY);
	
	// 行高さの変更
	if (nRowBorder >= 0 && nCol == -1 && !IsRowHeightFixed ()) {
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		SetCapture ();
		m_nMouseMode = TEXTGRIDCTRL_MOUSEMODEROWHEIGHTCHANGE;
		m_nMouseDownRowHeight = GetRowHeight (nRowBorder);
	}
	// 列高さの変更
	else if (nColBorder >= 0 && nRow == -1 && !IsColWidthFixed ()) {
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		SetCapture ();
		m_nMouseMode = TEXTGRIDCTRL_MOUSEMODECOLWIDTHCHANGE;
		m_nMouseDownColWidth = GetColWidth (nColBorder);
	}
	// セル選択
	else if (0 <= nRow && nRow < m_nNumRows && 0 <= nCol && nCol < m_nNumCols) {
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		if (nFlags & MK_SHIFT) {
			SelectArea (m_nCurRow, nRow, m_nCurCol, nCol);
		}
		else {
			SetCurCell (nRow, nCol, lLogPixelsX, lLogPixelsY);
			SelectCell (nRow, nCol);
		}
		Invalidate ();
		SetCapture ();
		m_nMouseMode = TEXTGRIDCTRL_MOUSEMODECELLSELECT;
	}
	// 行選択
	else if (nCol == -1 && 0 <= nRow && nRow < m_nNumRows) {
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		if (nFlags & MK_SHIFT) {
			SelectRow (m_nCurRow, nRow);
		}
		else {
			SetCurCell (nRow, m_nVisibleLeftCol, lLogPixelsX, lLogPixelsY);
			SelectRow (nRow, nRow);
		}
		Invalidate ();
		SetCapture ();
		m_nMouseMode = TEXTGRIDCTRL_MOUSEMODEROWSELECT;
	}
	// 列選択
	else if (nRow == -1 && 0 <= nCol && nCol < m_nNumCols) {		
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		if (nFlags & MK_SHIFT) {
			SelectCol (m_nCurCol, nCol);
		}
		else {
			SetCurCell (m_nVisibleTopRow, nCol, lLogPixelsX, lLogPixelsY);
			SelectCol (nCol, nCol);
		}
		Invalidate ();
		SetCapture ();
		m_nMouseMode = TEXTGRIDCTRL_MOUSEMODECOLSELECT;
	}
	// 全体選択
	else if (nRow == -1 && nCol == -1) {
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		SetCurCell (m_nVisibleTopRow, m_nVisibleLeftCol, lLogPixelsX, lLogPixelsY);
		SelectArea (0, m_nNumRows - 1, 0, m_nNumCols - 1);
		Invalidate ();
	}
	// マウスカーソル形状の変更
	HCURSOR hCursor = NULL;
	if (m_nMouseMode == TEXTGRIDCTRL_MOUSEMODEROWHEIGHTCHANGE) {
		VERIFY (hCursor = ::LoadCursor (NULL, IDC_SIZENS));
	}
	else if (m_nMouseMode == TEXTGRIDCTRL_MOUSEMODECOLWIDTHCHANGE) {
		VERIFY (hCursor = ::LoadCursor (NULL, IDC_SIZEWE));
	}
	else if (nCol >= m_nNumCols || nRow >= m_nNumRows) {
		VERIFY (hCursor = ::LoadCursor (NULL, IDC_NO));
	}
	else {
		VERIFY (hCursor = ::LoadCursor (NULL, IDC_ARROW));
	}
	::SetCursor (hCursor);
	m_ptMouseDown = point;
	m_ptMouseMove = point;
	m_nMouseDownRow = nRow;
	m_nMouseDownCol = nCol;
	m_nMouseDownRowBorder = nRowBorder;
	m_nMouseDownColBorder = nColBorder;
}

// マウス右ボタン押し下げ時
void CTextGridCtrl::OnRButtonDown (UINT nFlags, CPoint point) {
	this->SetFocus ();

	// 画面のdpi取得
	HDC hDC = ::GetDC (this->GetSafeHwnd ());
	long lLogPixelsX = ::GetDeviceCaps (hDC, LOGPIXELSX);
	long lLogPixelsY = ::GetDeviceCaps (hDC, LOGPIXELSY);
	::ReleaseDC (this->GetSafeHwnd (), hDC);

	int nRow, nCol;
	this->PointToCell (point, &nRow, &nCol, lLogPixelsX, lLogPixelsY);
	// セル領域
	if (0 <= nRow && nRow < m_nNumRows && 0 <= nCol && nCol < m_nNumCols) {
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		// 選択範囲外が右クリックされた
		if (nRow < m_nSelectedTopRow || nRow > m_nSelectedBottomRow ||
			nCol < m_nSelectedLeftCol || nCol > m_nSelectedRightCol) {
			SetCurCell (nRow, nCol, lLogPixelsX, lLogPixelsY);
			SelectCell (nRow, nCol);
			Invalidate ();
			m_nMouseMode = TEXTGRIDCTRL_MOUSEMODENONE;
		}
		CPoint ptScreen (point);
		ClientToScreen (&ptScreen);
		UpdateContextMenuState ();
		m_theContextMenu.TrackPopupMenu (TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
			ptScreen.x, ptScreen.y, this);
	}
	// 行領域
	else if (nCol < 0 && 0 <= nRow && nRow < m_nNumRows) {
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		// 選択範囲外が右クリックされた
		if (IsSelectedRow (nRow) == FALSE) {
			SetCurCell (nRow, m_nVisibleLeftCol, lLogPixelsX, lLogPixelsY);
			SelectRow (nRow, nRow);
			Invalidate ();
			m_nMouseMode = TEXTGRIDCTRL_MOUSEMODENONE;
		}
		CPoint ptScreen (point);
		ClientToScreen (&ptScreen);
		UpdateContextMenuState ();
		m_theContextMenu.TrackPopupMenu (TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
			ptScreen.x, ptScreen.y, this);
	}
	// 列領域
	else if (nRow < 0 && 0 <= nCol && nCol < m_nNumCols) {		
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		// 選択範囲外が右クリックされた
		if (IsSelectedCol (nCol) == FALSE) {
			SetCurCell (m_nVisibleTopRow, nCol, lLogPixelsX, lLogPixelsY);
			SelectCol (nCol, nCol);
			Invalidate ();
			m_nMouseMode = TEXTGRIDCTRL_MOUSEMODENONE;
		}
		CPoint ptScreen (point);
		ClientToScreen (&ptScreen);
		UpdateContextMenuState ();
		m_theContextMenu.TrackPopupMenu (TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
			ptScreen.x, ptScreen.y, this);
	}
	// 行列交差域
	else if (nRow < 0 && nCol < 0) {
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		SetCurCell (m_nVisibleTopRow, m_nVisibleLeftCol, lLogPixelsX, lLogPixelsY);
		SelectArea (0, m_nNumRows - 1, 0, m_nNumCols - 1);
		Invalidate ();
		m_nMouseMode = TEXTGRIDCTRL_MOUSEMODENONE;
		CPoint ptScreen (point);
		ClientToScreen (&ptScreen);
		UpdateContextMenuState ();
		m_theContextMenu.TrackPopupMenu (TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON,
			ptScreen.x, ptScreen.y, this);
	}
	m_ptMouseDown = point;
	m_ptMouseMove = point;
}



// マウスムーブ時
void CTextGridCtrl::OnMouseMove (UINT nFlags, CPoint point) {
	// 画面のdpi取得
	HDC hDC = ::GetDC (this->GetSafeHwnd ());
	long lLogPixelsX = ::GetDeviceCaps (hDC, LOGPIXELSX);
	long lLogPixelsY = ::GetDeviceCaps (hDC, LOGPIXELSY);
	::ReleaseDC (this->GetSafeHwnd (), hDC);

	CRect rcClient;
	GetClientRect (&rcClient);
	int nRow, nCol;
	this->PointToCell (point, &nRow, &nCol, lLogPixelsX, lLogPixelsY);
	int nRowBorder, nColBorder;
	this->PointToBorder (point, &nRowBorder, &nColBorder, lLogPixelsX, lLogPixelsY);
	// アンキャプチャー中の処理
	if (GetCapture () != this) {
		// マウスカーソル形状の変更
		HCURSOR hCursor = NULL;
		if (nRowBorder >= 0 && nCol == -1 && !IsRowHeightFixed ()) {
			VERIFY (hCursor = ::LoadCursor (NULL, IDC_SIZENS));
		}
		else if (nColBorder >= 0 && nRow == -1 && !IsColWidthFixed ()) {
			VERIFY (hCursor = ::LoadCursor (NULL, IDC_SIZEWE));
		}
		else if (nCol >= m_nNumCols || nRow >= m_nNumRows) {
			VERIFY (hCursor = ::LoadCursor (NULL, IDC_NO));
		}
		else {
			VERIFY (hCursor = ::LoadCursor (NULL, IDC_ARROW));
		}
		::SetCursor (hCursor);
	}
	// キャプチャー中の処理
	if (GetCapture () == this && point != m_ptMouseDown) {
		// マウスカーソル形状の変更
		HCURSOR hCursor = NULL;
		if (m_nMouseMode == TEXTGRIDCTRL_MOUSEMODEROWHEIGHTCHANGE) {
			VERIFY (hCursor = ::LoadCursor (NULL, IDC_SIZENS));
		}
		else if (m_nMouseMode == TEXTGRIDCTRL_MOUSEMODECOLWIDTHCHANGE) {
			VERIFY (hCursor = ::LoadCursor (NULL, IDC_SIZEWE));
		}
		else {
			VERIFY (hCursor = ::LoadCursor (NULL, IDC_ARROW));
		}
		::SetCursor (hCursor);
		// 選択系事前処理
		int nRow2 = TEXTGRIDCTRL_RANGE 
			(GetVisibleTopRow () - 1, nRow, GetVisibleBottomRow (lLogPixelsX, lLogPixelsY) + 1);
		int nCol2 =	TEXTGRIDCTRL_RANGE 
			(GetVisibleLeftCol () - 1, nCol, GetVisibleRightCol (lLogPixelsX, lLogPixelsY) + 1);				
		int nRow3 = TEXTGRIDCTRL_RANGE (0, nRow2, m_nNumRows - 1);
		int nCol3 = TEXTGRIDCTRL_RANGE (0, nCol2, m_nNumCols - 1);		
		int nOldSelectedTopRow = m_nSelectedTopRow;
		int nOldSelectedBottomRow = m_nSelectedBottomRow;
		int nOldSelectedLeftCol = m_nSelectedLeftCol;
		int nOldSelectedRightCol = m_nSelectedRightCol;
		// セル選択
		if (m_nMouseMode == TEXTGRIDCTRL_MOUSEMODECELLSELECT) {
			SelectArea (m_nCurRow, nRow3, m_nCurCol, nCol3);
			AutoScrolltoShowCell (nRow3, nCol3, lLogPixelsX, lLogPixelsY);
			if (nOldSelectedTopRow != m_nSelectedTopRow ||
				nOldSelectedBottomRow != m_nSelectedBottomRow ||
				nOldSelectedLeftCol != m_nSelectedLeftCol ||
				nOldSelectedRightCol != m_nSelectedRightCol) {
				Invalidate ();
			}
		}
		// 行選択
		else if (m_nMouseMode == TEXTGRIDCTRL_MOUSEMODEROWSELECT) {
			SelectRow (m_nCurRow, nRow3);
			AutoScrolltoShowRow (nRow3, lLogPixelsX, lLogPixelsY);
			if (nOldSelectedTopRow != m_nSelectedTopRow ||
				nOldSelectedBottomRow != m_nSelectedBottomRow) {
				Invalidate ();
			}
		}
		// 列選択
		else if (m_nMouseMode == TEXTGRIDCTRL_MOUSEMODECOLSELECT) {
			SelectCol (m_nCurCol, nCol3);
			AutoScrolltoShowCol (nCol3, lLogPixelsX, lLogPixelsY);
			if (nOldSelectedLeftCol != m_nSelectedLeftCol ||
				nOldSelectedRightCol != m_nSelectedRightCol) {
				Invalidate ();
			}
		}
		// 行高さの変更
		else if (m_nMouseMode == TEXTGRIDCTRL_MOUSEMODEROWHEIGHTCHANGE) {
			int nDeltaY = (point.y - m_ptMouseDown.y) * 96 / lLogPixelsY;
			if (0 <= m_nMouseDownRowBorder && m_nMouseDownRowBorder < GetNumRows ()) {
				int nHeight = GetRowHeight (m_nMouseDownRowBorder);
				nHeight = TEXTGRIDCTRL_RANGE 
					(1, m_nMouseDownRowHeight + nDeltaY, rcClient.Height () - m_nTitleHeight);
				if (IsSelectedRow (m_nMouseDownRowBorder)) {
					for (int i = m_nSelectedTopRow; i <= m_nSelectedBottomRow; i++) {
						SetRowHeight (i, nHeight);
					}
				}
				else {
					SetRowHeight (m_nMouseDownRowBorder, nHeight);
				}
				Invalidate ();
			}
		}
		// 列幅の変更
		else if (m_nMouseMode == TEXTGRIDCTRL_MOUSEMODECOLWIDTHCHANGE) {
			int nDeltaX = (point.x - m_ptMouseDown.x) * 96 / lLogPixelsX;
			if (0 <= m_nMouseDownColBorder && m_nMouseDownColBorder < GetNumCols ()) {
				int nWidth = GetColWidth (m_nMouseDownColBorder);
				nWidth = TEXTGRIDCTRL_RANGE 
					(1, m_nMouseDownColWidth + nDeltaX, rcClient.Width () - m_nTitleWidth);
				if (IsSelectedCol (m_nMouseDownColBorder)) {
					for (int j = m_nSelectedLeftCol; j <= m_nSelectedRightCol; j++) {
						SetColWidth (j, nWidth);
					}
				}
				else {
					SetColWidth (m_nMouseDownColBorder, nWidth);
				}
				Invalidate ();
			}
		}
	}
	m_ptMouseMove = point;
}

// マウス左ボタン押し上げ時
void CTextGridCtrl::OnLButtonUp (UINT nFlags, CPoint point) {
	// 画面のdpi取得
	HDC hDC = ::GetDC (this->GetSafeHwnd ());
	long lLogPixelsX = ::GetDeviceCaps (hDC, LOGPIXELSX);
	long lLogPixelsY = ::GetDeviceCaps (hDC, LOGPIXELSY);
	::ReleaseDC (this->GetSafeHwnd (), hDC);

	int nRow, nCol;
	this->PointToCell (point, &nRow, &nCol, lLogPixelsX, lLogPixelsY);
	if (GetCapture () == this) {
		VERIFY (ReleaseCapture ());
	}
	// マウスカーソル形状の変更
	HCURSOR hCursor = NULL;
	if (m_nMouseMode == TEXTGRIDCTRL_MOUSEMODEROWHEIGHTCHANGE) {
		VERIFY (hCursor = ::LoadCursor (NULL, IDC_SIZENS));
	}
	else if (m_nMouseMode == TEXTGRIDCTRL_MOUSEMODECOLWIDTHCHANGE) {
		VERIFY (hCursor = ::LoadCursor (NULL, IDC_SIZEWE));
	}
	else if (nCol >= m_nNumCols || nRow >= m_nNumRows) {
		VERIFY (hCursor = ::LoadCursor (NULL, IDC_NO));
	}
	else {
		VERIFY (hCursor = ::LoadCursor (NULL, IDC_ARROW));
	}
	::SetCursor (hCursor);
}

// マウス左ボタンダブルクリック時
void CTextGridCtrl::OnLButtonDblClk (UINT nFlags, CPoint point) {
	// 画面のdpi取得
	HDC hDC = ::GetDC (this->GetSafeHwnd ());
	long lLogPixelsX = ::GetDeviceCaps (hDC, LOGPIXELSX);
	long lLogPixelsY = ::GetDeviceCaps (hDC, LOGPIXELSY);
	::ReleaseDC (this->GetSafeHwnd (), hDC);

	int nRow, nCol;
	this->PointToCell (point, &nRow, &nCol, lLogPixelsX, lLogPixelsY);
	if (0 <= nRow && nRow < m_nNumRows &&
		0 <= nCol && nCol < m_nNumCols) {
		if (IsReadOnly () == FALSE && IsCellReadOnly (nRow, nCol) == FALSE) {
			BeginTextEditingContinue ();
		}
	}
}

BOOL CTextGridCtrl::OnMouseWheel (UINT nFlags, short zDelta, CPoint point) {
	// 注意：point=画面左上からのピクセル数
	// 画面のdpi取得
	HDC hDC = ::GetDC (this->GetSafeHwnd ());
	long lLogPixelsX = ::GetDeviceCaps (hDC, LOGPIXELSX);
	long lLogPixelsY = ::GetDeviceCaps (hDC, LOGPIXELSY);
	::ReleaseDC (this->GetSafeHwnd (), hDC);

	int nMinHorzPos, nMaxHorzPos, nMinVertPos, nMaxVertPos;
	GetScrollRange (SB_HORZ, &nMinHorzPos, &nMaxHorzPos);
	GetScrollRange (SB_VERT, &nMinVertPos, &nMaxVertPos);

	if (IsTextEditing ()) {
		EndTextEditingOK ();
	}

	// CTRL+SHIFTキーが押されていた場合(20220903追加)
	if ((nFlags & MK_CONTROL) && (nFlags & MK_SHIFT)) {
		// 左右スクロール
		int nDelta = zDelta > 0 ? -4 : 4;
		int nOldPos = GetScrollPos (SB_HORZ);
		if (1) {
			int nNewPos = TEXTGRIDCTRL_RANGE (0, m_nVisibleLeftCol + nDelta, m_nNumCols - 1);
			SetVisibleLeftCol (nNewPos, lLogPixelsX, lLogPixelsY);
			//SetScrollPos (SB_HORZ, nNewPos, TRUE);
			//nDelta = nNewPos - m_sizScrollPosition.cx;
			//m_sizScrollPosition.cx = GetScrollPos (SB_HORZ);
			//ScrollWindow (-nDelta, 0);
			Invalidate ();
		}
	}
	// CTRLキーが押されていた場合
	else if (nFlags & MK_CONTROL) {
		// マウス位置を中心に拡大縮小
		/*double dCurScale = m_dCurScale;
		if (zDelta > 0) {
			dCurScale /= 2;
		}
		else {
			dCurScale *= 2;
		}
		dCurScale = CLIP (10, dCurScale, 1000);
		CPoint ptDevPoint (point);
		CRect rcWindowRect;
		GetWindowRect (&rcWindowRect);
		CPoint ptNoMove (point.x - rcWindowRect.left, point.y - rcWindowRect.top);
		CalcTotalAndOffset (dCurScale, ptNoMove, FALSE, lLogPixelsX, lLogPixelsY);
		m_strCurSpecialScale = _T ("");
		Invalidate ();*/
	}
	// その他の場合
	else {
		// 上下スクロール
		//int nDelta = zDelta > 0 ? rcClient.Height () / 2 : -rcClient.Height () / 2;*/
		int nDelta = zDelta > 0 ? -4 : 4;
		int nOldPos = GetScrollPos (SB_VERT);
		if (1) {
			int nNewPos = TEXTGRIDCTRL_RANGE (0, m_nVisibleTopRow + nDelta, m_nNumRows - 1);
			SetVisibleTopRow (nNewPos, lLogPixelsX, lLogPixelsY);
			//SetScrollPos (SB_VERT, nNewPos, TRUE);
			//nDelta = nNewPos - m_sizScrollPosition.cy;
			//m_sizScrollPosition.cy = GetScrollPos (SB_VERT);
			//ScrollWindow (0, -nDelta);
			Invalidate ();
		}
	}
	return TRUE;
}


// キーダウン時
void CTextGridCtrl::OnKeyDown (UINT nChar, UINT nRepCnt, UINT nFlags) {
	// 画面のdpi取得
	HDC hDC = ::GetDC (this->GetSafeHwnd ());
	long lLogPixelsX = ::GetDeviceCaps (hDC, LOGPIXELSX);
	long lLogPixelsY = ::GetDeviceCaps (hDC, LOGPIXELSY);
	::ReleaseDC (this->GetSafeHwnd (), hDC);

	int nVisibleRows = __max (1, (GetVisibleBottomRow (lLogPixelsX, lLogPixelsY) - GetVisibleTopRow ()));
	int nVisibleCols = __max (1, (GetVisibleRightCol (lLogPixelsX, lLogPixelsY) - GetVisibleLeftCol ()));
	switch (nChar) {
	//左キー
	case VK_LEFT:
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		if (::GetKeyState (VK_SHIFT) < 0) {
			if (::GetKeyState (VK_CONTROL) < 0) {
				m_nKeyMoveCol = 0;
			}
			else {
				m_nKeyMoveCol = TEXTGRIDCTRL_RANGE (0, m_nKeyMoveCol - 1, m_nNumCols - 1);
			}
			SelectArea (m_nSelectedTopRow, m_nSelectedBottomRow, m_nKeyMoveCol, m_nCurCol);
			AutoScrolltoShowCol (m_nKeyMoveCol, lLogPixelsX, lLogPixelsY);
		}
		else {
			if (::GetKeyState (VK_CONTROL) < 0) {
				SetCurCell (m_nCurRow, 0, lLogPixelsX, lLogPixelsY);
			}
			else {
				SetCurCell (m_nCurRow, TEXTGRIDCTRL_RANGE (0, m_nCurCol - 1, m_nNumCols - 1), lLogPixelsX, lLogPixelsY);
			}
			SelectCell (m_nCurRow, m_nCurCol);
		}
		Invalidate ();
		break;
	// 上キー
	case VK_UP:
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		if (::GetKeyState (VK_SHIFT) < 0) {
			if (::GetKeyState (VK_CONTROL) < 0) {
				m_nKeyMoveRow = 0;
			}
			else {
				m_nKeyMoveRow = TEXTGRIDCTRL_RANGE (0, m_nKeyMoveRow - 1, m_nNumRows - 1);
			}
			SelectArea (m_nCurRow, m_nKeyMoveRow, m_nSelectedLeftCol, m_nSelectedRightCol);
			AutoScrolltoShowRow (m_nKeyMoveRow, lLogPixelsX, lLogPixelsY);
		}
		else {
			if (::GetKeyState (VK_CONTROL) < 0) {
				SetCurCell (0, m_nCurCol, lLogPixelsX, lLogPixelsY);
			}
			else {
				SetCurCell (TEXTGRIDCTRL_RANGE (0, m_nCurRow - 1, m_nNumRows - 1), m_nCurCol, lLogPixelsX, lLogPixelsY);
			}
			SelectCell (m_nCurRow, m_nCurCol);
		}
		Invalidate ();
		break;
	// 右キー
	case VK_RIGHT:
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		if (::GetKeyState (VK_SHIFT) < 0) {
			if (::GetKeyState (VK_CONTROL) < 0) {
				m_nKeyMoveCol = m_nNumCols - 1;	
			}
			else {
				m_nKeyMoveCol = TEXTGRIDCTRL_RANGE (0, m_nKeyMoveCol + 1, m_nNumCols - 1);
			}
			SelectArea (m_nSelectedTopRow, m_nSelectedBottomRow, m_nKeyMoveCol, m_nCurCol);
			AutoScrolltoShowCol (m_nKeyMoveCol, lLogPixelsX, lLogPixelsY);
		}
		else {
			if (::GetKeyState (VK_CONTROL) < 0) {
				SetCurCell (m_nCurRow, m_nNumCols - 1, lLogPixelsX, lLogPixelsY);
			}
			else {
				SetCurCell (m_nCurRow, TEXTGRIDCTRL_RANGE (0, m_nCurCol + 1, m_nNumCols - 1), lLogPixelsX, lLogPixelsY);
			}
			SelectCell (m_nCurRow, m_nCurCol);
		}
		Invalidate ();
		break;
	// 下キー
	case VK_DOWN:
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		if (::GetKeyState (VK_SHIFT) < 0) {
			if (::GetKeyState (VK_CONTROL) < 0) {
				m_nKeyMoveRow = m_nNumRows - 1;
			}
			else {
				m_nKeyMoveRow = TEXTGRIDCTRL_RANGE (0, m_nKeyMoveRow + 1, m_nNumRows - 1);
			}
			SelectArea (m_nCurRow, m_nKeyMoveRow, m_nSelectedLeftCol, m_nSelectedRightCol);
			AutoScrolltoShowRow (m_nKeyMoveRow, lLogPixelsX, lLogPixelsY);
		}
		else {
			if (::GetKeyState (VK_CONTROL) < 0) {
				SetCurCell (m_nNumRows - 1, m_nCurCol, lLogPixelsX, lLogPixelsY);
			}
			else {
				SetCurCell (TEXTGRIDCTRL_RANGE (0, m_nCurRow + 1, m_nNumRows - 1), m_nCurCol, lLogPixelsX, lLogPixelsY);
			}
			SelectCell (m_nCurRow, m_nCurCol);
		}
		Invalidate ();
		break;
	// Homeキー
	case VK_HOME:
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		if (::GetKeyState (VK_SHIFT) < 0) {
			m_nKeyMoveCol = TEXTGRIDCTRL_RANGE (0, m_nKeyMoveCol - nVisibleCols, m_nNumCols - 1);
			SelectArea (m_nSelectedTopRow, m_nSelectedBottomRow, m_nKeyMoveCol, m_nCurCol);
			AutoScrolltoShowCol (m_nKeyMoveCol, lLogPixelsX, lLogPixelsY);
		}
		else {
			SetCurCell (m_nCurRow, TEXTGRIDCTRL_RANGE (0, m_nCurCol - nVisibleCols, m_nNumCols - 1), lLogPixelsX, lLogPixelsY);
			SelectCell (m_nCurRow, m_nCurCol);
		}
		Invalidate ();
		break;
	// PageUpキー
	case VK_PRIOR:
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		if (::GetKeyState (VK_SHIFT) < 0) {
			m_nKeyMoveRow = TEXTGRIDCTRL_RANGE (0, m_nKeyMoveRow - nVisibleRows, m_nNumRows - 1);
			SelectArea (m_nCurRow, m_nKeyMoveRow, m_nSelectedLeftCol, m_nSelectedRightCol);
			AutoScrolltoShowRow (m_nKeyMoveRow, lLogPixelsX, lLogPixelsY);
		}
		else {
			SetCurCell (TEXTGRIDCTRL_RANGE (0, m_nCurRow - nVisibleRows, m_nNumRows - 1), m_nCurCol, lLogPixelsX, lLogPixelsY);
			SelectCell (m_nCurRow, m_nCurCol);
		}
		Invalidate ();
		break;
	// Endキー
	case VK_END:
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		if (::GetKeyState (VK_SHIFT) < 0) {
			m_nKeyMoveCol = TEXTGRIDCTRL_RANGE (0, m_nKeyMoveCol + nVisibleCols, m_nNumCols - 1);
			SelectArea (m_nSelectedTopRow, m_nSelectedBottomRow, m_nKeyMoveCol, m_nCurCol);
			AutoScrolltoShowCol (m_nKeyMoveCol, lLogPixelsX, lLogPixelsY);
		}
		else {
			SetCurCell (m_nCurRow, TEXTGRIDCTRL_RANGE (0, m_nCurCol + nVisibleCols, m_nNumCols - 1), lLogPixelsX, lLogPixelsY);
			SelectCell (m_nCurRow, m_nCurCol);
		}
		Invalidate ();
		break;
	// PageDownキー
	case VK_NEXT:
		if (IsTextEditing ()) {
			EndTextEditingOK ();
		}
		if (::GetKeyState (VK_SHIFT) < 0) {
			m_nKeyMoveRow = TEXTGRIDCTRL_RANGE (0, m_nKeyMoveRow + nVisibleRows, m_nNumRows - 1);
			SelectArea (m_nCurRow, m_nKeyMoveRow, m_nSelectedLeftCol, m_nSelectedRightCol);
			AutoScrolltoShowRow (m_nKeyMoveRow, lLogPixelsX, lLogPixelsY);
		}
		else {
			SetCurCell (TEXTGRIDCTRL_RANGE (0, m_nCurRow + nVisibleRows, m_nNumRows - 1), m_nCurCol, lLogPixelsX, lLogPixelsY);
			SelectCell (m_nCurRow, m_nCurCol);
		}
		Invalidate ();
		break;
	// Ctrl+Zキー(元に戻す)
	case 'Z':
		if (::GetKeyState (VK_CONTROL) < 0) {
			if (IsTextEditing ()) {
				EndTextEditingOK ();
			}

			UndoWithNotify ();
			Invalidate ();
		}
		break;
	// Ctrl+Yキー(やり直し)
	case 'Y':
		if (::GetKeyState (VK_CONTROL) < 0) {
			if (IsTextEditing ()) {
				EndTextEditingOK ();
			}

			RedoWithNotify ();
			Invalidate ();
		}
		break;
	// Ctrl+Aキー(すべて選択)
	case 'A':
		if (::GetKeyState (VK_CONTROL) < 0) {
			if (IsTextEditing ()) {
				EndTextEditingOK ();
			}

			SelectAll ();
			Invalidate ();
		}
		break;
	// Ctrl+Xキー(切り取り)
	case 'X':
		if (::GetKeyState (VK_CONTROL) < 0) {
			if (IsTextEditing ()) {
				EndTextEditingOK ();
			}

			DeleteHistoryBlockAfter (m_nCurHistoryIndex);
			CTextGridHistoryBlock* pHistoryBlock = new CTextGridHistoryBlock ();
			if (pHistoryBlock == NULL) {
				AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
				return;
			}
			pHistoryBlock->m_strName = _T ("切り取り");
			m_theHistoryBlockArray.Add (pHistoryBlock);
			m_nCurHistoryIndex++;

			CutWithNotify ();
			Invalidate ();
		}
		break;
	// Ctrl+Cキー(コピー)
	case 'C':
		if (::GetKeyState(VK_CONTROL) < 0) {
			if (IsTextEditing ()) {
				EndTextEditingOK();
			}

			CopyWithNotify ();
			Invalidate ();
		}
		break;
	// Ctrl+Vキー(貼り付け)
	case 'V':
		if (::GetKeyState (VK_CONTROL) < 0) {
			if (IsTextEditing ()) {
				EndTextEditingOK ();
			}

			DeleteHistoryBlockAfter (m_nCurHistoryIndex);
			CTextGridHistoryBlock* pHistoryBlock = new CTextGridHistoryBlock ();
			if (pHistoryBlock == NULL) {
				AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
				return;
			}
			pHistoryBlock->m_strName = _T ("貼り付け");
			m_theHistoryBlockArray.Add (pHistoryBlock);
			m_nCurHistoryIndex++;

			PasteWithNotify ();
			Invalidate ();
		}
		break;
	// 改行(Enter)キー
	case VK_RETURN:
		// テキスト編集中でない場合
		if (IsTextEditing () == FALSE && IsReadOnly () == FALSE) {
			BeginTextEditingContinue ();
		}
		// テキスト編集中の場合(CTextGridEditクラスからPostMessageされる。)
		else {
			EndTextEditingOK ();
			Invalidate ();
		}
		break;
	// エスケープ(Esc)キー(CTextGridEditクラスからPostMessageされる。)
	case VK_ESCAPE:
		if (IsTextEditing () == FALSE) {
			GetParent ()->PostMessage (WM_KEYDOWN, VK_ESCAPE, nFlags);
		}
		else {
			EndTextEditingCancel ();
			Invalidate ();
		}
		break;
	// Deleteキー
	case VK_DELETE:
		if (1) {
			CTextGridHistoryBlock* pHistoryBlock = new CTextGridHistoryBlock ();
			if (pHistoryBlock == NULL) {
				AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
				return;
			}
			pHistoryBlock->m_strName = _T ("削除");
			m_theHistoryBlockArray.Add (pHistoryBlock);
			m_nCurHistoryIndex++;

			DeleteWithNotify ();
			Invalidate ();
		}
		break;
	// どれにも該当しない。
	default :
		CWnd::OnKeyDown (nChar, nRepCnt, nFlags);
		break;
	}
}


// 文字入力時
void CTextGridCtrl::OnChar (UINT nChar, UINT nRepCnt, UINT nFlags) {
	// 画面のdpi取得
	HDC hDC = ::GetDC (this->GetSafeHwnd ());
	long lLogPixelsX = ::GetDeviceCaps (hDC, LOGPIXELSX);
	long lLogPixelsY = ::GetDeviceCaps (hDC, LOGPIXELSY);
	::ReleaseDC (this->GetSafeHwnd (), hDC);
	int nCurRow = GetCurRow ();
	int nCurCol = GetCurCol ();
	if (IsReadOnly () == FALSE && IsCellReadOnly (nCurRow, nCurCol) == 0) {
		if (32 <= nChar) {
			MoveTextBox (lLogPixelsX, lLogPixelsY);
			BeginTextEditing ();
			//m_theTextBox.SendMessage (WM_CHAR, nChar, (nRepCnt << 16) | nFlags);
			m_theTextBox.SendMessage (WM_CHAR, nChar, (nFlags << 16) | (nRepCnt & 0x00FF));
		}
	}
}

// 文字入力時
LRESULT CTextGridCtrl::OnImeStartComposition (WPARAM wParam, LPARAM lParam) {
	// 画面のdpi取得
	HDC hDC = ::GetDC (this->GetSafeHwnd ());
	long lLogPixelsX = ::GetDeviceCaps (hDC, LOGPIXELSX);
	long lLogPixelsY = ::GetDeviceCaps (hDC, LOGPIXELSY);
	::ReleaseDC (this->GetSafeHwnd (), hDC);
	int nCurRow = GetCurRow ();
	int nCurCol = GetCurCol ();
	if (IsReadOnly () == FALSE && IsCellReadOnly (nCurRow, nCurCol) == 0) {
		MoveTextBox (lLogPixelsX, lLogPixelsY);
		BeginTextEditing ();
		m_theTextBox.SendMessage (WM_IME_STARTCOMPOSITION, wParam, lParam);
	}
	return 1;
}

// 元に戻す
void CTextGridCtrl::OnContextUndo() {
	this->UndoWithNotify ();
	Invalidate ();
}

// 元に戻す
void CTextGridCtrl::OnUpdateContextUndo (CCmdUI* pCmdUI) {
	pCmdUI->Enable (IsReadOnly () == FALSE && m_nCurHistoryIndex >= 0);
}

// やり直し
void CTextGridCtrl::OnContextRedo () {
	this->RedoWithNotify ();
	Invalidate ();
}

// やり直し
void CTextGridCtrl::OnUpdateContextRedo (CCmdUI* pCmdUI) {
	pCmdUI->Enable (IsReadOnly () == FALSE && m_nCurHistoryIndex + 1 <= m_theHistoryBlockArray.GetCount () - 1);
}


// 切り取り
void CTextGridCtrl::OnContextCut () {
	DeleteHistoryBlockAfter (m_nCurHistoryIndex);
	CTextGridHistoryBlock* pHistoryBlock = new CTextGridHistoryBlock ();
	if (pHistoryBlock == NULL) {
		AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
		return;
	}
	pHistoryBlock->m_strName = _T ("切り取り");
	m_theHistoryBlockArray.Add (pHistoryBlock);
	m_nCurHistoryIndex++;

	this->CutWithNotify ();
	Invalidate ();
}

// 切り取り
void CTextGridCtrl::OnUpdateContextCut (CCmdUI* pCmdUI) {
	pCmdUI->Enable (IsReadOnly () == FALSE);
}


// コピー
void CTextGridCtrl::OnContextCopy () {

	this->CopyWithNotify ();
	Invalidate ();
}

// コピー
void CTextGridCtrl::OnUpdateContextCopy (CCmdUI* pCmdUI) {
}

// 貼り付け
void CTextGridCtrl::OnContextPaste () {
	DeleteHistoryBlockAfter (m_nCurHistoryIndex);
	CTextGridHistoryBlock* pHistoryBlock = new CTextGridHistoryBlock ();
	if (pHistoryBlock == NULL) {
		AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
		return;
	}
	pHistoryBlock->m_strName = _T ("貼り付け");
	m_theHistoryBlockArray.Add (pHistoryBlock);
	m_nCurHistoryIndex++;

	this->PasteWithNotify ();
	Invalidate ();
}

// 貼り付け
void CTextGridCtrl::OnUpdateContextPaste (CCmdUI* pCmdUI) {
	pCmdUI->Enable (IsReadOnly () == FALSE);
}

// 削除
void CTextGridCtrl::OnContextDelete () {
	DeleteHistoryBlockAfter (m_nCurHistoryIndex);
	CTextGridHistoryBlock* pHistoryBlock = new CTextGridHistoryBlock ();
	if (pHistoryBlock == NULL) {
		AfxMessageBox (_T ("メモリ不足です。"), MB_ICONSTOP);
		return;
	}
	pHistoryBlock->m_strName = _T ("削除");
	m_theHistoryBlockArray.Add (pHistoryBlock);
	m_nCurHistoryIndex++;

	this->DeleteWithNotify ();
	Invalidate ();
}
// 削除
void CTextGridCtrl::OnUpdateContextDelete (CCmdUI* pCmdUI) {
	pCmdUI->Enable (IsReadOnly () == FALSE);
}
