/////////////////////////////
// ECCSkelton
// CREDITクラス定義ファイル
// Copyright (c) August, 2022
//        By Ysama
/////////////////////////////
#include <richedit.h>
#define	MAXBUFF	1024 * 1024
#define	CMASK	CFM_BOLD | CFM_CHARSET | CFM_COLOR | CFM_FACE | CFM_ITALIC | CFM_SIZE | CFM_STRIKEOUT | CFM_UNDERLINE
#define	NOMORE	-1
#define	PMARGIN	1440	//1インチ相当のtwip
#define	INDENT	567		//1センチ相当のtwip

class CREDIT : public CCTRL
{
public:		//メンバー変数
	HINSTANCE m_hRtLib;				//リッチエディットコントロールのインスタンス
	HDC m_Dhdc;						//ディスプレーのデバイスコンテキスト
	int m_FontSize;					//リッチエディットコントロール全体のフォントサイズ
	WCHAR m_FontName[32];			//リッチエディットコントロール全体のフォント名
	CHARFORMATW m_cfm;				//キャラクターフォーマット構造体
	PARAFORMAT m_pf;				//パラグラフフォーマット構造体
	HWND m_hFRDlg;					//検索･置換ダイアログのハンドル
	UINT m_frMsg;					//検索･置換ダイアログのメッセージ
	WCHAR m_FindWhat[MAX_PATH];		//検索文字列バッファー
	WCHAR m_ReplaceWith[MAX_PATH];	//置換文字列バッファー
	HDC m_Phdc;						//プリンターのデバイスコンテキスト
	int m_nPrnW;					//印刷範囲の物理的幅
	int m_nLog;						//印刷範囲の論理的ピクセル数
	int m_RichWidth;				//リッチエディットコントロールの幅(twip)

public:		//メンバー関数
	CREDIT();						//コンストラクター
	~CREDIT();						//デストラクター
	bool Create(LPWSTR, DWORD,
			DWORD, HWND, int, 
			int, int, int, int);	//リッチエディットの作成
	long GetMax();					//リッチエディットの最大文字長を取得
	void SetMax(long);				//リッチエディットの最大文字長を設定
	void FilterMsg(DWORD);			//リッチエディットのWM_NOTIFYメッセージ設定
	static DWORD CALLBACK SaveRichEditProc(DWORD, LPBYTE, LONG, LONG*);
	bool SaveFile(LPWSTR);			//ファイルセーブ関数
	static DWORD CALLBACK ReadRichEditProc(DWORD, LPBYTE, LONG, LONG*);
	bool LoadFile(LPWSTR);			//ファイルロード関数
	long GetSize();					//ファイルサイズを求める
	long GetLines();				//リッチエディットの行数を返す
	LPPOINT GetLinePos(int);		//指定行の最初、最後の桁の位置を返す
	LPPOINT GetPos();				//現在の行、桁の位置を返す
	LPLOGFONT GetFont();			//現在のフォント情報取得
	bool SelFont();					//フォントダイアログによるフォント選択
	void SetFont();					//フォント設定(フォントダイアログ指定）
	void SetFont(int, WCHAR*);		//フォント設定(直接指定）
	void InitCol(int, int);			//文字色の初期化(黒）
	COLORREF GetCol(int, int);		//文字色を取得
	void SetCol(COLORREF, bool);	//文字色設定
	void SetTokenColor(WCHAR*,		//特定Tokenの色付
			COLORREF, int, int, UINT);
	void Jump(int, bool);			//指定行へ移動
	void Align(WCHAR);				//文字寄せ設定
	void Indent(int);				//インデント設定
	void Tab(int);					//タブの設定
	bool SetPrinter();				//プリンターの設定
	int AddPageNo(int);				//ページ付与関数
	bool Print(WCHAR*);				//印刷処理関数
	void SetWYSWYG(bool);			//画面と印刷を合わせる
	FINDREPLACEW* SetFRDlg(WCHAR);	//検索・置換ダイアログの初期化
	bool WasFound(FINDTEXTW*, UINT);//検索・置換関数
	void FindReplace(LPARAM);		//検索・置換ダイアログ処理
};

//コンストラクター
CREDIT::CREDIT() {

	//リッチエディットコントロールのハンドルの初期化
	m_hWnd = 0;
	//コモンコントロールの初期化
	InitCommonControls();
	//リッチエディットコントロールDLLの読込み
	m_hRtLib = LoadLibraryW(L"RICHED20.DLL");
	//検索・置換メーッセージの登録
	m_frMsg = RegisterWindowMessageW(FINDMSGSTRINGW);
	//フォントサイズの初期化
	m_FontSize = 10;
	//フォント名の初期化
	lstrcpyW(m_FontName, L"ＭＳ 明朝");
	//プリンター関係変数の初期化
	m_Phdc = 0;
	m_nPrnW = m_nLog = m_RichWidth = 0;
}

//デストラクター
CREDIT::~CREDIT() {

	ReleaseDC(m_hWnd, m_Dhdc);		//自分のデバイスコンテキストの解放
	if(!m_Phdc) DeleteDC(m_Phdc);	//既存のプリンターデバイスコンテキストがあれば削除
	if(!FreeLibrary(m_hRtLib))
		MessageBoxW(NULL, L"リッチエディットコントロールの開放に失敗しました",
					L"エラー", MB_OK | MB_ICONERROR);
}

//------------------------------------
// リッチエディットコントロールの作成
//------------------------------------
bool CREDIT::Create(LPWSTR WindowTitle, DWORD Style, DWORD ExStyle,
						HWND hParent, int ID,
						int x = CW_USEDEFAULT, int y = CW_USEDEFAULT,
						int w = CW_USEDEFAULT, int h = CW_USEDEFAULT) {

	m_hInstance = (HINSTANCE)GetWindowLong(hParent, GWL_HINSTANCE);
	m_hWnd =	CreateWindowExW(ExStyle, L"RICHEDIT20W", WindowTitle,
					Style, x, y, w, h, m_hParent = hParent, (HMENU)ID,
					m_hInstance, NULL);
	if(m_hWnd == NULL) return FALSE;
	ShowWindow(m_hWnd, SW_SHOWNORMAL);
	UpdateWindow(m_hWnd);
	m_Dhdc = GetDC(m_hWnd);		//自分のデバイスコンテキストを保存

	//エディットコントロールの最大文字列長
	SetMax(MAXBUFF);
	//フォントの初期化
	SetFont(m_FontSize, m_FontName);
	//文字寄せの初期化
	Align('L');

	return TRUE; 
}

//------------------------------------
// リッチエディットの最大文字長を取得
//------------------------------------
long CREDIT::GetMax() {

	return SendMessageW(m_hWnd, EM_GETLIMITTEXT, 0, 0);
}
//------------------------------------
// リッチエディットの最大文字長を設定
//------------------------------------
void CREDIT::SetMax(long max) {

	SendMessageW(m_hWnd, EM_SETLIMITTEXT, max, 0);
}

//-------------------------------------------
// リッチエディットのWM_NOTIFYメッセージ設定
//-------------------------------------------
void CREDIT::FilterMsg(DWORD msg) {

	DWORD dwEvent = SendMessageW(m_hWnd, EM_GETEVENTMASK, 0, 0);
	dwEvent |= msg;
	SendMessageW(m_hWnd, EM_SETEVENTMASK, 0, (LPARAM)dwEvent);
}

//---------------------------------------------------
// リッチエディットコントロール用書込コールバック関数
//---------------------------------------------------
DWORD CALLBACK CREDIT::SaveRichEditProc(DWORD dwCookie, LPBYTE pbbuf, LONG cb, LONG* pcb) {

	if (WriteFile((HANDLE)dwCookie, pbbuf, cb, (LPDWORD)pcb, NULL))
		return FALSE;
	else
		return TRUE;
}

//--------------------------------------------------------------------------
// リッチエディットコントロール用テキスト、リッチエディットファイル書込関数
// ファイル名 pszFileName のファイルが RichEdit Controlへ書きこまれる
//--------------------------------------------------------------------------
bool CREDIT::SaveFile(LPWSTR pszFileName) {

	EDITSTREAM eds;		//EDITSTREAM構造体
	bool nFileType;		//rtfかtxtかの判別フラグ
	if(StrStrW(pszFileName, L".rtf"))		nFileType = TRUE;	//rtfファイルとして
	else if(StrStrW(pszFileName, L".RTF"))	nFileType = TRUE;	//rtfファイルとして
	else									nFileType = FALSE;	//textファイルとして
	HANDLE hFile = CreateFile(pszFileName, GENERIC_WRITE, 0,
						NULL, CREATE_ALWAYS,
						FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL);
	if (hFile == INVALID_HANDLE_VALUE) {
		MessageBoxW(m_hWnd, L"ファイルが開けませんでした", L"エラー", MB_OK | MB_ICONERROR);
		return FALSE;
	}
	eds.dwCookie = (DWORD)hFile;
	eds.dwError = 0;
	eds.pfnCallback = SaveRichEditProc;
	if(nFileType) {
		int ret = MessageBoxW(m_hWnd, L"UFT-8 RTFで保存しますか\n（「いいえ」はANSI RTF）", L"書き込み確認", MB_YESNO | MB_ICONQUESTION);
		if(ret == IDYES)
			SendMessageW(m_hWnd, EM_STREAMOUT, (WPARAM)(CP_UTF8 << 16) | SF_USECODEPAGE | SF_RTF, (LPARAM)&eds);
		else
			SendMessageW(m_hWnd, EM_STREAMOUT, (WPARAM)SF_RTF, (LPARAM)&eds);
	}
	else {
		int ret = MessageBoxW(m_hWnd, L"BOM付UFT-16で保存しますか\nBOM無UFT-8で保存しますか（「いいえ」）\n（「キャンセル」はANSI）", L"書き込み確認", MB_YESNOCANCEL | MB_ICONQUESTION);
		if(ret == IDYES) {
			//BOMの書き込み - WindowsはIntel系でUTF-16 FF FE（リトルエンディアン）
			BYTE BOM[2] = {0xFF, 0xFE};
			DWORD dwWritten;
			WriteFile(hFile, BOM, 2, &dwWritten, NULL);
			SendMessageW(m_hWnd, EM_STREAMOUT, (WPARAM)SF_TEXT | SF_UNICODE, (LPARAM)&eds);
		}
		else if(ret == IDNO)
			SendMessageW(m_hWnd, EM_STREAMOUT, (WPARAM)(CP_UTF8 << 16) | SF_USECODEPAGE | SF_TEXT, (LPARAM)&eds);
		else
			SendMessageW(m_hWnd, EM_STREAMOUT, (WPARAM)SF_TEXT, (LPARAM)&eds);
	}
	CloseHandle(hFile);
	SendMessageW(m_hWnd, EM_SETMODIFY, (WPARAM)FALSE, 0);
	SetFocus(m_hWnd);
	return TRUE;
}

//---------------------------------------------------
// リッチエディットコントロール用読込コールバック関数
//---------------------------------------------------
DWORD CALLBACK CREDIT::ReadRichEditProc(DWORD dwCookie, LPBYTE pbBuf, LONG cb, LONG* pcb) {

	ReadFile((HANDLE)dwCookie, pbBuf, cb, (LPDWORD)pcb, NULL);
	return FALSE;
}

//--------------------------------------------------------------------------
// リッチエディットコントロール用テキスト、リッチエディットファイル読込関数
// ファイル名 pszFileName のファイルが RichEdit Controlへ読みこまれる
//--------------------------------------------------------------------------
bool CREDIT::LoadFile(LPWSTR pszFileName) {

	EDITSTREAM eds;		//EDITSTREAM構造体
	bool nFileType;		//rtfかtxtかの判別フラグ
	if(StrStrW(pszFileName, L".rtf"))		nFileType = TRUE;	//rtfファイルとして
	else if(StrStrW(pszFileName, L".RTF"))	nFileType = TRUE;	//rtfファイルとして
	else									nFileType = FALSE;	//textファイルとして
	HANDLE hFile = CreateFile(pszFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING,
						FILE_ATTRIBUTE_NORMAL, NULL);
	if (hFile == INVALID_HANDLE_VALUE) {
		MessageBoxW(m_hWnd, L"ファイルが開けませんでした", L"エラー", MB_OK | MB_ICONERROR);
		return FALSE;
	}
	eds.dwCookie = (DWORD)hFile;
	eds.dwError = 0;
	eds.pfnCallback = ReadRichEditProc;
	if(nFileType) {
		int ret = MessageBoxW(m_hWnd, L"UFT-8 RTFで読み込みますか\n（「いいえ」はANSI RTF）", L"読み込み確認", MB_YESNO | MB_ICONQUESTION);
		if(ret == IDYES)
			SendMessageW(m_hWnd, EM_STREAMIN, (WPARAM)(CP_UTF8 << 16) | SF_USECODEPAGE | SF_RTF, (LPARAM)&eds);
		else
			SendMessageW(m_hWnd, EM_STREAMIN, (WPARAM)SF_RTF, (LPARAM)&eds);
	}
	else {
		int ret = MessageBoxW(m_hWnd, L"BOM付UFT-16で読み込みますか\nBOM無UFT-8にしますか（「いいえ」）\n（「キャンセル」はANSI）", L"読み込み確認", MB_YESNOCANCEL | MB_ICONQUESTION);
		if(ret == IDYES) {
			//BOMの読み込み - WindowsはIntel系でUTF-16 FF FE（リトルエンディアン）
			BYTE BOM[2];
			DWORD dwRead;
			ReadFile(hFile, BOM, 2, &dwRead, NULL);
			if(BOM[0] != 0xFF || BOM[1] != 0xFE) {
				MessageBoxW(m_hWnd, L"BOM付UFT-16ファィルではありません", L"エラー", MB_OK | MB_ICONERROR);
				CloseHandle(hFile);
				return FALSE;
			}
			SendMessageW(m_hWnd, EM_STREAMIN, (WPARAM)SF_TEXT | SF_UNICODE, (LPARAM)&eds);
		}
		else if(ret == IDNO)
			SendMessageW(m_hWnd, EM_STREAMIN, (WPARAM)(CP_UTF8 << 16) | SF_USECODEPAGE | SF_TEXT, (LPARAM)&eds);
		else
			SendMessageW(m_hWnd, EM_STREAMIN, (WPARAM)SF_TEXT, (LPARAM)&eds);
	}
	CloseHandle(hFile);
	SendMessageW(m_hWnd, EM_SETMODIFY, (WPARAM)FALSE, 0);
	SetFocus(m_hWnd);
	return TRUE;
}

//------------------------
// ファイルサイズを求める
//------------------------
long CREDIT::GetSize() {

	long ln = SendMessageW(m_hWnd, EM_GETLINECOUNT, 0, 0);
	long li = SendMessageW(m_hWnd, EM_LINEINDEX, ln - 1, 0);
	long ll = SendMessageW(m_hWnd, EM_LINELENGTH, li, 0);
	return (li + ll);
}

//----------------------------
//リッチエディットの行数を返す
//----------------------------
long CREDIT::GetLines() {

	return SendMessageW(m_hWnd, EM_GETLINECOUNT, 0, 0);
}

//----------------------------------
//指定行の最初、最後の桁の位置を返す
//----------------------------------
LPPOINT CREDIT::GetLinePos(int ln) {

	static POINT pt;
	pt.x = SendMessageW(m_hWnd, EM_LINEINDEX, ln, 0);
	pt.y = pt.x + SendMessageW(m_hWnd, EM_LINELENGTH, pt.x, 0);
	return &pt;
}

//--------------------------
// 現在の行、桁の位置を返す
// 0桁、0行から始まる
//--------------------------
LPPOINT CREDIT::GetPos() {

	static POINT pt;
	CHARRANGE cr;
	SendMessageW(m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr);
	long ln = SendMessageW(m_hWnd, EM_EXLINEFROMCHAR, 0, cr.cpMin);
	long li = SendMessageW(m_hWnd, EM_LINEINDEX, ln, 0);
	pt.x = cr.cpMin - li;
	pt.y = ln;
	return &pt;
}

//------------------------
// 現在のフォント情報取得
//------------------------
LPLOGFONT CREDIT::GetFont() {

	return m_hFont.GetInfo();
}

//----------------------------------------------
// 現在選択されている文字列のフォントをフォント
// ダイアログを使って変更する
//----------------------------------------------
bool CREDIT::SelFont() {

	CHOOSEFONT cf;		//ChooseFont構造体
	ZeroMemory(&cf, sizeof(CHOOSEFONT));
	LOGFONTW lf;		//Font構造体
    ZeroMemory(&lf, sizeof(LOGFONTW));

	//現在のフォンと情報を構造体CHOOSEFONTに読み込む
	m_cfm.cbSize = sizeof(CHARFORMAT);
	SendMessageW(m_hWnd, EM_GETCHARFORMAT, TRUE, (LPARAM)&m_cfm);
	lf.lfHeight = MulDiv(m_cfm.yHeight,
		GetDeviceCaps(m_Dhdc, LOGPIXELSY), -1440);
	m_cfm.dwMask = CMASK;
	if(m_cfm.dwEffects & CFE_BOLD)
		lf.lfWeight = FW_BOLD;
	else
		lf.lfWeight = FW_NORMAL;
	if(m_cfm.dwEffects & CFE_ITALIC)
		lf.lfItalic = TRUE;
	if(m_cfm.dwEffects & CFE_UNDERLINE)
		lf.lfUnderline = TRUE;
	if(m_cfm.dwEffects & CFE_STRIKEOUT)
		lf.lfStrikeOut = TRUE;
	lf.lfCharSet = m_cfm.bCharSet;
	lf.lfQuality = DEFAULT_QUALITY;
	lf.lfPitchAndFamily = m_cfm.bPitchAndFamily;
	lstrcpyW(lf.lfFaceName, m_cfm.szFaceName);
	cf.rgbColors = m_cfm.crTextColor;
	cf.lStructSize = sizeof(CHOOSEFONT);
	cf.hwndOwner = m_hWnd;
	cf.lpLogFont = &lf;
	cf.Flags = CF_SCREENFONTS | CF_EFFECTS | CF_INITTOLOGFONTSTRUCT;
	//フォント情報をChooseFontダイアログ構造体から得る
	if(ChooseFont(&cf)) {	//成功
		m_cfm.cbSize = sizeof(CHARFORMAT);
		m_cfm.dwMask = CMASK;
		m_cfm.yHeight = 2 * cf.iPointSize;
		m_cfm.dwEffects = 0;
		if(lf.lfWeight >= FW_BOLD)
			m_cfm.dwEffects |= CFE_BOLD;
		if(lf.lfItalic)
			m_cfm.dwEffects |= CFE_ITALIC;
		if(lf.lfUnderline)
			m_cfm.dwEffects |= CFE_UNDERLINE;
		if(lf.lfStrikeOut)
			m_cfm.dwEffects |= CFE_STRIKEOUT;
		m_cfm.crTextColor = (COLORREF)cf.rgbColors;
		m_cfm.bPitchAndFamily = lf.lfPitchAndFamily;
		m_cfm.bCharSet = lf.lfCharSet;
		lstrcpyW(m_cfm.szFaceName, lf.lfFaceName);
		if(!SendMessageW(m_hWnd, EM_SETCHARFORMAT,
			SCF_SELECTION, (LPARAM)&m_cfm)) {
			MessageBoxW(m_hWnd, L"フォントの設定ができませんでした", L"エラー", MB_OK | MB_ICONERROR);
			return FALSE;
		}
	}
	SetFocus(m_hWnd);
	return TRUE;
}

//--------------------------------------
// フォント設定(フォントダイアログ指定）
//--------------------------------------
void CREDIT::SetFont() {

	CHOOSEFONT cf;		//ChooseFont構造体
	ZeroMemory(&cf, sizeof(CHOOSEFONT));
	LOGFONTW lf;		//Font構造体
    ZeroMemory(&lf, sizeof(LOGFONTW));
	lf = *GetFont();
	cf.rgbColors = m_cfm.crTextColor;
	cf.lStructSize = sizeof(CHOOSEFONT);
	cf.hwndOwner = m_hWnd;
	cf.lpLogFont = &lf;
	cf.Flags = CF_SCREENFONTS | CF_EFFECTS | CF_INITTOLOGFONTSTRUCT;
	//フォント情報をChooseFontダイアログ構造体から得る
	if(ChooseFont(&cf)) {	//成功
		m_FontSize = cf.iPointSize / 10;
		lstrcpyW(m_FontName, lf.lfFaceName);
		m_hFont.SetFont(m_hWnd, m_FontSize, m_FontName);
	}
}

//------------------------
// フォント設定(直接指定）
//------------------------
void CREDIT::SetFont(int size, WCHAR* FontName) {

	m_FontSize = size;
	lstrcpyW(m_FontName, FontName);
	m_hFont.SetFont(m_hWnd, size, FontName);
}

//--------------------------------------------------------------------------
// 文字色の初期化(黒）flag = 0（全文選択）、1（1行選択）、その他（1文字選択）
//--------------------------------------------------------------------------
void CREDIT::InitCol(int min = 0, int max = -1) {

	CHARRANGE cr;
	SendMessageW(m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cr);
	SendMessageW(m_hWnd, EM_SETSEL, min, max);
	m_cfm.cbSize = sizeof(CHARFORMAT);
	m_cfm.dwMask = CFM_COLOR;			//CMASK;
	m_cfm.dwEffects = CFE_AUTOCOLOR;	//|= CFE_AUTOCOLOR;
	SendMessageW(m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&m_cfm);
	SendMessageW(m_hWnd, EM_EXSETSEL, 0, (LPARAM)&cr);
	SetFocus(m_hWnd);
}

//------------------------------------------------
// リッチエディットコントロールの文字色を取得する
//------------------------------------------------
COLORREF CREDIT::GetCol(int min, int max) {

	SendMessageW(m_hWnd, EM_SETSEL, min, max);
	m_cfm.cbSize = sizeof(CHARFORMAT);
	m_cfm.dwMask = CFM_COLOR;	//CMASK;
	SendMessageW(m_hWnd, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&m_cfm);
	return m_cfm.crTextColor;
}

//------------------------------------------------
// リッチエディットコントロールの文字色を設定する
//------------------------------------------------
void CREDIT::SetCol(COLORREF col, bool sign = TRUE) {

	if(sign) {
		m_cfm.cbSize = sizeof(CHARFORMAT);
		m_cfm.dwMask = CFM_COLOR;	//CMASK;
		m_cfm.crTextColor = col;
		m_cfm.dwEffects = 0;		//^= CFE_AUTOCOLOR;
		SendMessageW(m_hWnd, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&m_cfm);
	}
	else
		SendMessageW(m_hWnd, EM_SETBKGNDCOLOR, 0, (LPARAM)col);
	SetFocus(m_hWnd);
}

//-----------------
// 特定Tokenの色付
//-----------------
void CREDIT::SetTokenColor(WCHAR* target, COLORREF col, int min = 0, int max = -1, UINT flag = FR_MATCHCASE | FR_WHOLEWORD) {

	//FINDTEXT構造体
	FINDTEXTW ft;
	//検索範囲の設定
	ft.chrg.cpMin = min;
	ft.chrg.cpMax = max;
	//文字列の長さ
	long wl = lstrlenW(target);
	//検索文字列へのポインターを設定
	ft.lpstrText = target;
	while(WasFound(&ft, flag)) {
		SendMessageW(m_hWnd, EM_SETSEL, ft.chrg.cpMin, ft.chrg.cpMin + wl);
		SetCol(col);
		ft.chrg.cpMin += wl;
	}
}

//--------------
// 指定行へ移動
//--------------
void CREDIT::Jump(int line, bool cover = TRUE) {

	CHARRANGE cr;
	cr.cpMin = cr.cpMax = SendMessageW(m_hWnd, EM_LINEINDEX, line, 0);
	if(cover)	cr.cpMax += SendMessageW(m_hWnd, EM_LINELENGTH, cr.cpMin, 0);
	SendMessageW(m_hWnd, EM_EXSETSEL, 0, (LPARAM)&cr);
	SendMessageW(m_hWnd, EM_SCROLLCARET, 0, 0);
	SetFocus(m_hWnd);
}

//------------------------------------------
// リッチエディットコントロール内の文字寄せ
// flag = R(ight), C(enter) or L(eft)
//------------------------------------------
void CREDIT::Align(WCHAR flag) {

	ZeroMemory(&m_pf, sizeof(PARAFORMAT));
	m_pf.cbSize = sizeof(PARAFORMAT);
	m_pf.dwMask = PFM_ALIGNMENT;
	switch(flag) {		//フラッグによってpf.wAlignmentに書き込み、マークをつける
		case L'R':
			m_pf.wAlignment = PFA_RIGHT;
			break;
		case L'C':
			m_pf.wAlignment = PFA_CENTER;
			break;
		case L'L':
			m_pf.wAlignment = PFA_LEFT;
			break;
		default:
			m_pf.wAlignment = PFA_LEFT;
			break;
	}
	SendMessageW(m_hWnd, EM_SETPARAFORMAT, 0, (LPARAM)&m_pf);
	SetFocus(m_hWnd);
}

//--------------------------------------------
// リッチエディットコントロール内のインデント
// flag = 1-左／3-右の設定、2-左／4-右の解消
//--------------------------------------------
void CREDIT::Indent(int flag) {

	ZeroMemory(&m_pf, sizeof(PARAFORMAT));
	m_pf.cbSize = sizeof(PARAFORMAT);
	m_pf.dwMask = PFM_STARTINDENT | PFM_RIGHTINDENT;
	SendMessage(m_hWnd, EM_GETPARAFORMAT, 0, (LPARAM)&m_pf);
	switch(flag) {		//フラッグによってpf.dxStart/RightOffsetに書き込む
		case 1:
			m_pf.dxStartIndent += INDENT;
			break;
		case 2:
			m_pf.dxStartIndent -= INDENT;
			if(m_pf.dxStartIndent < 0)
				m_pf.dxStartIndent = 0;
			break;
		case 3:
			m_pf.dxRightIndent += INDENT;
			break;
		case 4:
			m_pf.dxRightIndent -= INDENT;
			if(m_pf.dxRightIndent < 0)
				m_pf.dxRightIndent = 0;
			break;
	}
	SendMessageW(m_hWnd, EM_SETPARAFORMAT, 0, (LPARAM)&m_pf);
	SetFocus(m_hWnd);
}

//------------
// タブの設定
//------------
void CREDIT::Tab(int unit) {

	//twips 単位(20 twips = 1 points)
	//RichEditの初期値は高さが200twips(10 points x 20)、108twipsが良いのだが。
	SIZE size;		//メンバーはcx、cy
	WCHAR temp[95];
	for(int i = 0; i < 95; i++)	temp[i] = i + 32;
	GetTextExtentPoint32(m_Dhdc, temp, 95, &size);
	unit *= (size.cx / 95) * m_FontSize * 144 / GetDeviceCaps(m_Dhdc, LOGPIXELSX);
	//現在の位置を記録
	CHARRANGE cur;
	SendMessageW(m_hWnd, EM_EXGETSEL, 0, (LPARAM)&cur);
	//タブを設定
	ZeroMemory(&m_pf, sizeof(PARAFORMAT));
	m_pf.cbSize = sizeof(PARAFORMAT);
	SendMessageW(m_hWnd, EM_GETPARAFORMAT, 0, (LPARAM)&m_pf);
	m_pf.dwMask |= PFM_TABSTOPS;
	m_pf.cTabCount = MAX_TAB_STOPS;			//タブストップの点の数
	for (int i = 0; i < MAX_TAB_STOPS; i++)
		m_pf.rgxTabs[i] = unit * (i + 1);	//各タブストップの点を記録
	//全体をカバーする
	CHARRANGE set;
	set.cpMin = 0;	set.cpMax = -1;
	SendMessageW(m_hWnd, EM_EXSETSEL, 0, (LPARAM)&set);
	SendMessageW(m_hWnd, EM_SETPARAFORMAT, 0, (LPARAM)&m_pf);
	//元に戻す
	SendMessageW(m_hWnd, EM_EXSETSEL, 0, (LPARAM)&cur);
	SetFocus(m_hWnd);
}

//--------------------
// プリンター設定関数
//--------------------
bool CREDIT::SetPrinter() {

	if(!m_Phdc)		//既存のプリンターデバイスコンテキストがあれば先ず削除
		DeleteDC(m_Phdc);
	//印刷ダイアログをプリンター設定で開く
	PRINTDLG pd;
	ZeroMemory(&pd, sizeof(PRINTDLG));
	pd.lStructSize = sizeof(PRINTDLG);
	pd.hwndOwner = m_hWnd;
	pd.Flags = PD_PRINTSETUP | PD_RETURNDC;
	//プリンターのデバイスコンテキストを取得
	if(PrintDlg(&pd))
		m_Phdc = pd.hDC;
	else {
		MessageBoxW(NULL, L"プリンターのデバイスコンテキストを取得できません", L"エラー", MB_OK | MB_ICONERROR);
		return FALSE;
	}
	//プリンター用紙の物理的幅
	m_nPrnW = GetDeviceCaps(m_Phdc, PHYSICALWIDTH);
	//同幅中の論理的ピクセル数
	m_nLog = GetDeviceCaps(m_Phdc, LOGPIXELSX);
	//左右に1インチ(1440 twip)のマージンを設定
	m_RichWidth = MulDiv(m_nPrnW, 1440, m_nLog) - PMARGIN * 2;
	return TRUE;
}

//--------------------
// ページ番号付加関数
//--------------------
int CREDIT::AddPageNo(int nPage) {

	int wx, wy, x, y;
	WCHAR szPage[64];
	const WCHAR *szPage_Org = L"- %d -";
	int nTextW;		//文書幅
	TEXTMETRIC tm;
	wx = GetDeviceCaps(m_Phdc, PHYSICALWIDTH);
	wy = GetDeviceCaps(m_Phdc, PHYSICALHEIGHT);
	wsprintfW(szPage, szPage_Org, nPage);
	GetTextMetrics(m_Phdc, &tm);
	nTextW = (int)tm.tmAveCharWidth * lstrlen(szPage);
	x = (wx - nTextW) / 2;	//水平位置
	y = wy - PMARGIN / 4;	//垂直位置
	TextOutW(m_Phdc, x, y, szPage, lstrlenW(szPage));
	return nPage;
}

//--------------
// 印刷処理関数
//--------------
bool CREDIT::Print(WCHAR* szFileName) {

	static FORMATRANGE fr;
	DOCINFOW di;
	DWORD lTextLen;
	DWORD lNextMin = 0;
	int nPage = 1; //ページ番号
	ZeroMemory(&fr, sizeof(FORMATRANGE));
	ZeroMemory(&di, sizeof(DOCINFOW));
	//タイトルは szFileName
	di.cbSize = sizeof(DOCINFO);
	di.lpszDocName = szFileName;
	if(!m_Phdc) {
		MessageBoxW(m_hWnd, L"プリンターが見当たりません", L"エラー",
			MB_OK | MB_ICONERROR);
		return FALSE;
	}
	fr.hdc = m_Phdc;
	fr.hdcTarget = m_Phdc;
	fr.rcPage.left = 0;
	fr.rcPage.top = 0;
	fr.rcPage.right = MulDiv(GetDeviceCaps(m_Phdc, PHYSICALWIDTH),
		1440, GetDeviceCaps(m_Phdc, LOGPIXELSX));
	fr.rcPage.bottom = MulDiv(GetDeviceCaps(m_Phdc, PHYSICALHEIGHT),
		1440, GetDeviceCaps(m_Phdc, LOGPIXELSY));
	//上下左右に1インチ(1440twip)のマージンを設定
	fr.rc.top = PMARGIN;
	fr.rc.left = PMARGIN;
	fr.rc.right = fr.rcPage.right - PMARGIN;
	fr.rc.bottom = fr.rcPage.bottom - PMARGIN;
//	lTextLen = SendMessage(m_hWnd, WM_GETTEXTLENGTH, 0, 0);	//無限ループが発生時のコード
	GETTEXTLENGTHEX gtl;	//GETTEXTLENEX構造体
	lTextLen = PtrToInt(SendMessageW(m_hWnd, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, 0));
	fr.chrg.cpMin = 0;
	fr.chrg.cpMax = lTextLen;
	StartDoc(m_Phdc, &di);
/*  Oct 2, 2020 - サンプルの"Visual Studio 2019 Image Library EULA.rtf"ファイルを
	印刷すると、lTextLenが11,661であるのに対し、fr.chrg.cpMinは11,649のあと11,647と
	なり、無限ループとなるために、次のwhile内に次のような処理を追加した。
	if(lNextMin <= fr.chrg.cpMin)	break;	//無限ループ回避コード
	しかし、後にリッチエディットのデータ長をWM_GETTEXTLENGTHでとることが誤りではないか、
	と考え、調べた結果、EM_GETTEXTLENGTHEXを採用した。（正しく動くが、正解か否か不詳）
	//検証コード1
	char Buff[128];
	wsprintf(Buff, "fr.chrg.cpMax = %d\nfr.chrg.cpMin = %d", fr.chrg.cpMax, fr.chrg.cpMin);
	MessageBox(m_hWnd, Buff, "", MB_OK | MB_ICONINFORMATION);
	//検証コード2
	char Buff[128];
	wsprintf(Buff, "WM_GETTEXTLENGTH= %d, EM_GETTEXTLENGTHEX, = %d", lTextLen, nLength);
	MessageBox(m_hWnd, Buff, "Test", MB_OK | MB_ICONINFORMATION);
*/
	while (fr.chrg.cpMin < lTextLen) {
		lNextMin = SendMessageW(m_hWnd, EM_FORMATRANGE, TRUE, (LPARAM)&fr);
		StartPage(m_Phdc);
		SendMessageW(m_hWnd, EM_DISPLAYBAND, 0, (LPARAM)&fr.rc);
		AddPageNo(nPage);
		EndPage(m_Phdc);
		fr.chrg.cpMin = lNextMin;
		nPage++;
	}
	SendMessageW(m_hWnd, EM_FORMATRANGE, TRUE, NULL);
	return (EndDoc(m_Phdc) > 0);
}

//--------------------------------------------------------
// リッチエディットコントロールの幅をプリンターに合わせる
//--------------------------------------------------------
void CREDIT::SetWYSWYG(bool sign = TRUE) {

	if(sign) {
		if(!(SendMessageW(m_hWnd, EM_SETTARGETDEVICE, (WPARAM)m_Phdc, (LPARAM)m_RichWidth)))
			MessageBoxW(m_hWnd, L"リッチエディットコントロール幅を設定できませんでした",
						L"エラー", MB_OK | MB_ICONERROR);
	}
	else {
		SendMessageW(m_hWnd, EM_SETTARGETDEVICE, (WPARAM)m_Dhdc,
			(LPARAM)MulDiv(GetDeviceCaps(m_Dhdc, PHYSICALWIDTH), 1440,
			GetDeviceCaps(m_Dhdc, LOGPIXELSX)));
	}
}

//--------------------------------------
// 検索･置換用FINDREPLACE構造体の初期化
//--------------------------------------
FINDREPLACEW* CREDIT::SetFRDlg(WCHAR sign) {

	DWORD flag;
	switch(sign) {
	case L'F':
	case L'f':
		flag = FR_DOWN | FR_FINDNEXT;
		break;
	case L'R':
	case L'r':
		flag = FR_DOWN | FR_REPLACE;
		break;
	default :
		return 0;
	}

	static FINDREPLACEW fr;
	ZeroMemory(&fr, sizeof(FINDREPLACEW));
	fr.lStructSize = sizeof(FINDREPLACEW);
	fr.hwndOwner = m_hParent;
	fr.hInstance = NULL;
	fr.Flags = flag;
	fr.lpstrFindWhat = m_FindWhat;
	fr.lpstrReplaceWith = m_ReplaceWith;
	fr.wFindWhatLen = MAX_PATH;
	fr.wReplaceWithLen = MAX_PATH;
	fr.lCustData = NULL;
	fr.lpfnHook = NULL;
	fr.lpTemplateName = NULL;
	return &fr;
}

//----------------
// 検索・置換関数
//----------------
bool CREDIT::WasFound(FINDTEXTW* ft, UINT flag) {

	return ((ft->chrg.cpMin = SendMessageW(m_hWnd, EM_FINDTEXTW,
								flag, (LPARAM)ft)) != NOMORE);
}

//------------------------------
// 検索・置換ダイアログ処理関数
//------------------------------
void CREDIT::FindReplace(LPARAM lParam) {

	static int fcount = 0;	//検索回数のカウンター
	static int rcount = 0;	//置換回数のカウンター
	static int fc, rc;		//表示用のカウンター（上記が0に初期化されるので）
	static FINDTEXTW ft;	//FINDTEXTW構造体−メンバーにchrgとlpstrTextを持つ
	WCHAR FRMsg[MAX_PATH];	//出力メッセージ用文字配列
	LPFINDREPLACEW fr = (LPFINDREPLACEW)lParam;	//構造体FINDREPLACEWへのポインター
	//検索範囲の設定
	if(!fcount) {								//初回だけ実行
		SendMessageW(m_hWnd, EM_EXGETSEL, 0,
					(LPARAM)&ft.chrg);			//選択範囲を取得
		if(fr->Flags & FR_DOWN) {				//下方検索なら
			ft.chrg.cpMin = ft.chrg.cpMax;		//選択範囲の末尾（ft.chrg.cpMax）から
			ft.chrg.cpMax = -1;					//文末までを検索
		}
		else									//上方検索なら選択範囲の先頭（ft.chrg.cpMin）から
			ft.chrg.cpMax = 0;					//文頭迄（cpMax < Minでなければならない）を検索
	}
	//検索文字列の設定
	ft.lpstrText = m_FindWhat;
	//検索･置換処理
	if(fr->Flags & FR_DIALOGTERM) {		//検索・置換ダイアログが終了した場合
		fc = fcount;	rc = rcount;	//表示用に記録する
		m_hFRDlg = 0;					//ハンドルを初期化
		fcount = 0;						//検索カウンターを初期化
		rcount = 0;						//置換カウンターを初期化
		SetFocus(m_hWnd);				//リッチエディットコントロールにフォーカスを当てる
	}
	else if(fr->Flags & FR_FINDNEXT) {	//「次を検索」の場合
		if(WasFound(&ft, (fr->Flags & FR_DOWN) | (fr->Flags & FR_MATCHCASE) | 
					(fr->Flags & FR_WHOLEWORD))) {				//見つかった場合
			fcount++;											//回数を記録
			SendMessageW(m_hWnd, EM_SETSEL, ft.chrg.cpMin,
						ft.chrg.cpMin + lstrlenW(m_FindWhat));	//検索文字列を選択
			SendMessageW(m_hWnd, EM_SCROLLCARET, 0, 0);			//キャレットの位置を表示する
			SetFocus(m_hWnd);			//リッチエディットコントロールにフォーカスを当てる
			if(fr->Flags & FR_DOWN)		//下方向検索の場合
				ft.chrg.cpMin += lstrlenW(m_FindWhat);			//次の検索開始位置に進める
		}
		else {													//もう見つからない場合
			SendMessageW(m_hFRDlg, WM_CLOSE, 0, 0);				//検索・置換ダイアログを閉じる
			wsprintfW(FRMsg, L"検索文字列は %d回見つかりました", fc);
			MessageBoxW(m_hParent, FRMsg, L"メッセージ", MB_OK | MB_ICONINFORMATION);
		}
	}
	else if(fr->Flags & FR_REPLACE) {	//「置換して次に」の場合
		if(WasFound(&ft, (fr->Flags & FR_DOWN) | (fr->Flags & FR_MATCHCASE) | 
					(fr->Flags & FR_WHOLEWORD))) {				//見つかった場合
			SendMessageW(m_hWnd, EM_SETSEL, ft.chrg.cpMin, ft.chrg.cpMin + lstrlenW(m_FindWhat));
			SendMessageW(m_hWnd, EM_SCROLLCARET, 0, 0);			//キャレット位置を表示する
			//メッセージボックスで確認して検索文字列を置換文字列に置換する
			if(MessageBoxW(m_hParent, L"置換しますか？", L"確  認",
							MB_YESNO | MB_ICONQUESTION) == IDYES) {
				SendMessageW(m_hWnd, EM_REPLACESEL, TRUE, (LPARAM)m_ReplaceWith);
				rcount++;										//置換回数を記録
			}
			fcount++;											//回数を記録
			SendMessageW(m_hWnd, EM_SETSEL, ft.chrg.cpMin, ft.chrg.cpMin + lstrlenW(m_ReplaceWith));
			SendMessageW(m_hWnd, EM_SCROLLCARET, 0, 0);			//キャレット位置を表示する
			SetFocus(m_hWnd);			//リッチエディットコントロールにフォーカスを当てる
			if(fr->Flags & FR_DOWN)		//下方向検索の場合
				ft.chrg.cpMin += lstrlenW(m_ReplaceWith);		//次の検索開始位置に進める
		}
		else {													//もう見つからない場合
			SendMessageW(m_hFRDlg, WM_CLOSE, 0, 0);				//検索・置換ダイアログを閉じる
			wsprintfW(FRMsg, L"検索文字列を %d検索し、%d回置換しました", fc, rc);
			MessageBoxW(m_hParent, FRMsg, L"メッセージ", MB_OK | MB_ICONINFORMATION);
		}
	}
	else if(fr->Flags & FR_REPLACEALL) {//「すべて置換」の場合
		ft.chrg.cpMin = 0;		//検索開始位置を文頭とし、
		ft.chrg.cpMax = -1;		//検索終了位置を文末とする
		while(WasFound(&ft, (fr->Flags & FR_DOWN) | (fr->Flags & FR_MATCHCASE) |
					(fr->Flags & FR_WHOLEWORD))) {
			fcount++;											//回数を記録
			SendMessageW(m_hWnd, EM_SETSEL, ft.chrg.cpMin, ft.chrg.cpMin + lstrlen(m_FindWhat));
			SendMessageW(m_hWnd, EM_SCROLLCARET, 0, 0);			//キャレット位置を表示する
			SendMessageW(m_hWnd, EM_REPLACESEL, TRUE, (LPARAM)m_ReplaceWith);
			rcount++;											//置換回数を記録
			if(fr->Flags & FR_DOWN)		//下方向検索の場合
				ft.chrg.cpMin += lstrlenW(m_ReplaceWith);		//次の検索開始位置に進める
		}
		SendMessageW(m_hFRDlg, WM_CLOSE, 0, 0);					//検索・置換ダイアログを閉じる
		wsprintfW(FRMsg, L"検索文字列を %d回置換しました", rc);
		MessageBoxW(m_hParent, FRMsg, L"メッセージ", MB_OK | MB_ICONINFORMATION);
	}
	SetFocus(m_hWnd);		//リッチエディットコントロールにフォーカスを当てる
}
