/*
Program WinCaml: Graphical User Interface
for interactive use of Caml-Light and Ocaml.
Copyright (C) 2005-2017 Jean Mouric 35700 Rennes France
email: jean.mouric@orange.fr

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

// File Win.cpp

#include "Win.h"
#include <fstream>
#include <locale>
#include <sstream>
#ifndef _MSVC98_
#include <Shlobj.h>
#endif

static FINDREPLACE fr;
static HWND hFindDlg;
static HWND hFindReplaceDlg;     
static WCHAR szFindWhat[FIND_MAXLENGTH];
static WCHAR szReplaceWith[FIND_MAXLENGTH];
static CRichEdit* searchEdit;
CMDIFrame* mainFrame;
const bool liveSplit = false;

static HCURSOR hEastWest;
static HCURSOR hSouthNorth;
static HCURSOR hArrow;

static int* tbCmds;

enum{WIN = 1252, MAC = 10000, UNIX = 28591, UTF8 = 65001};

string convert(const wstring& str);
string toLatin1(const wstring& str);

string convert(const wstring& str)
{
    return toLatin1(str);
}

void aboutMessage(const wstring& msg)
{
	wstring title = L"\xC0 propos de " + appName;
	::MessageBox(mainFrame->hWnd, msg.c_str(), title.c_str(), MB_OK | MB_ICONINFORMATION);
}
void errorMessage(const wstring& msg)
{
	wstring title = L"Message de " + appName;
	::MessageBox(mainFrame->hWnd, msg.c_str(), title.c_str(), MB_OK  | MB_ICONEXCLAMATION);
}
bool yesnoMessage(const wstring& msg)
{
	wstring title = L"Message de " + appName;
	return ::MessageBox(mainFrame->hWnd, msg.c_str(), title.c_str(), MB_YESNO | MB_ICONEXCLAMATION) == IDYES;
}
int yesnocancelMessage(const wstring& msg)
{
	wstring title = L"Message de " + appName;
	return ::MessageBox(mainFrame->hWnd, msg.c_str(), title.c_str(), MB_YESNOCANCEL | MB_ICONEXCLAMATION);
}
#ifdef _MSVC98_
static BOOL CALLBACK findReplaceHookProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
#else
static INT_PTR CALLBACK findReplaceHookProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
#endif
{
	switch(Message)
	{
	case WM_INITDIALOG:
		{
			return TRUE;
		}
	case WM_COMMAND:
		switch(LOWORD(wParam))
		{			
		case chx1:
			fr.Flags = fr.Flags & FR_WHOLEWORD ? fr.Flags &= ~FR_WHOLEWORD : fr.Flags |= FR_WHOLEWORD;
			return TRUE;
		case chx2:
			fr.Flags = fr.Flags & FR_MATCHCASE ? fr.Flags &= ~FR_MATCHCASE : fr.Flags |= FR_MATCHCASE;
			return TRUE;
		case edt1:
			{
				GetDlgItemText(hwnd, edt1, szFindWhat, FIND_MAXLENGTH - 1);
				return TRUE;
			}
		case edt2:
			{
				GetDlgItemText(hwnd, edt2, szReplaceWith, FIND_MAXLENGTH - 1);
				return TRUE;
			}
		default:
			break;
		}
	default:
		return FALSE;
	}
}
static void initFindReplace()
{
    memset(&fr, 0, sizeof(fr));
	fr.lStructSize = sizeof(fr);
	fr.hwndOwner = mainFrame->hWnd;
	fr.lpstrFindWhat = szFindWhat;
	fr.lpstrReplaceWith = szReplaceWith;
	fr.wFindWhatLen = FIND_MAXLENGTH * sizeof(WCHAR);
	fr.wReplaceWithLen = FIND_MAXLENGTH * sizeof(WCHAR);
	fr.Flags |= FR_ENABLEHOOK;
	fr.lpfnHook = (LPFRHOOKPROC)findReplaceHookProc;
	::ZeroMemory(&szFindWhat, FIND_MAXLENGTH * sizeof(WCHAR));
	::ZeroMemory(&szReplaceWith, FIND_MAXLENGTH * sizeof(WCHAR));
	size_t selStart, selEnd;
	searchEdit->getSelection(selStart, selEnd);
	wstring findWhat = searchEdit->getText(selStart, selEnd - selStart);
	size_t len =  findWhat.length();
	size_t sz = len >= FIND_MAXLENGTH ? FIND_MAXLENGTH - 1 : len;
    memmove(szFindWhat, findWhat.c_str(), sz * sizeof(WCHAR));
}
void  findDialog()
{
	if (!searchEdit) return;

	mainFrame->enableMenuItem(EDIT_REPLACE, true);
	mainFrame->enableMenuItem(EDIT_FIND, false);
	if (hFindDlg)
	{
		::SetActiveWindow(hFindDlg);
	}
	else
	{
		if (hFindReplaceDlg)
		{
			::DestroyWindow(hFindReplaceDlg);
			hFindReplaceDlg = NULL;
		}
		else
		{
			initFindReplace();
		}
		hFindDlg = ::FindText(&fr);
		fr.hwndOwner = searchEdit->hWnd;
		::SetWindowText(hFindDlg, L"Rechercher");
	}
}
void setSearchEdit(CRichEdit* edit)
{
	fr.hwndOwner = edit->hWnd;
	searchEdit = edit;
}
void findReplaceDialog()
{
	if (!searchEdit) return;

	mainFrame->enableMenuItem(EDIT_REPLACE, false);
	mainFrame->enableMenuItem(EDIT_FIND, true);
	if (hFindReplaceDlg)
	{
		::SetActiveWindow(hFindReplaceDlg);
	}
	else 
	{
		if (hFindDlg)
		{			
			::DestroyWindow(hFindDlg);
			hFindDlg = NULL;
		}
		else
		{
			initFindReplace();
		}
		hFindReplaceDlg = ::ReplaceText(&fr);
		fr.hwndOwner = searchEdit->hWnd;
		::SetWindowText(hFindDlg, L"Rechercher/remplacer");
	}
}
void closeFindDialog(bool enableMenus)
{
	mainFrame->enableMenuItem(EDIT_REPLACE, enableMenus);
	mainFrame->enableMenuItem(EDIT_FIND, enableMenus);
	if (hFindDlg)
	{
		::DestroyWindow(hFindDlg);
		hFindDlg = NULL;
	}
	if (hFindReplaceDlg)
	{
		::DestroyWindow(hFindReplaceDlg);
		hFindReplaceDlg = NULL;
	}
}
void setCursor(int cs)
{
	if (cs == EAST_WEST) ::SetCursor(hEastWest);
	else if (cs == SOUTH_NORTH) ::SetCursor(hSouthNorth);
	else ::SetCursor(hArrow);
}
void loadCursors()
{
	hEastWest = ::LoadCursor(NULL, IDC_SIZEWE);
	hSouthNorth = ::LoadCursor(NULL, IDC_SIZENS);
	hArrow  = ::LoadCursor(NULL, IDC_ARROW);
}
void unloadCursors()
{
	::DeleteObject((HCURSOR)hEastWest);
	::DeleteObject((HCURSOR)hSouthNorth);
	::DeleteObject((HCURSOR)hArrow);
}
void doSleep(int milliseconds)
{
	::Sleep(milliseconds);
}
static inline int codePage(int encoding)
{
	if (encoding == ENCODING_UNIX) return UNIX;
	if (encoding == ENCODING_MAC) return MAC;
	if (encoding == ENCODING_UTF8) return UTF8;
	return WIN;
}
static string fromUnicode(const wstring& wstr, int enc)
{
	string str = "";
	size_t lenW = wstr.length();
	int codeP = codePage(enc);
	int lenA = ::WideCharToMultiByte(codeP, 0, wstr.c_str(), (int)lenW, 0, 0, NULL, NULL);
	if (lenA > 0)
	{
		LPSTR buffer = new char[lenA + 1];
		::WideCharToMultiByte(codeP, 0, wstr.c_str(), (int)lenW, buffer, lenA, NULL, NULL);
		buffer[lenA] = 0;
		str = buffer;
		::tuneEndOfLines(str, enc);
		delete[] buffer;
	}
	return str;
}
string fromUnicode(const wstring& wstr)
{
	return fromUnicode(wstr, ENCODING_UNIX);
}
string toLatin1(const wstring& wstr)
{
	return fromUnicode(wstr, ENCODING_UNIX);
}
string toBytes(const wstring& wstr)
{
	return fromUnicode(wstr, ENCODING_WIN);
}
static wstring toUnicode(const string& str, int enc)
{
	wstring wstr = L"";
	size_t lenA = str.length();
	int codeP = codePage(enc);
	int lenW = ::MultiByteToWideChar(codeP, MB_ERR_INVALID_CHARS, str.c_str(), (int)lenA, 0, 0);
	if (lenW > 0)
	{
		LPWSTR buffer = new WCHAR[lenW + 1];
		::MultiByteToWideChar(codeP, 0, str.c_str(), (int)lenA, buffer, lenW);
		buffer[lenW]= L'\0';
		wstr = buffer;
		delete [] buffer;
	}
	return wstr;
}
wstring toUnicode(const string& str)
{
	return toUnicode(str, ENCODING_UNIX);
}
wstring fromLatin1(const string& str)
{
	return toUnicode(str, ENCODING_UNIX);
}
wstring fromBytes(const string& str)
{
	return toUnicode(str, ENCODING_WIN);
}
static int guessEncoding(string& data, int preferredEncoding)
{
	if (data.length() == 0) return preferredEncoding;
	size_t i;
	size_t j = 0;
	size_t k = 0;
	size_t l = 0;
	size_t m = 0;
	int enc;
	size_t len = data.length();
	for (i = 0; i < len; i++)
	{
		unsigned char c = data[i];
		if (j == 1)
		{
			j = 0;
			if ( c == 10) {k = 1;}
		}
		if (c == 13) j = 1;
		if (c > 127) m = 1;
		if (c > 223) {l = 1;}
	}
	::tuneEndOfLines(data);
	if (m == 0) enc = k == 0 ? preferredEncoding : ENCODING_WIN;
	else if (fromUnicode(toUnicode(data, ENCODING_UTF8), ENCODING_UTF8) == data) return ENCODING_UTF8;
	else if (k == 0 && l == 0) enc = ENCODING_MAC;
	else if (k == 1) enc = ENCODING_WIN;
	else enc = ENCODING_UNIX;
	return enc;
}
static const int cmdNum = HELP_ABOUT - FILE_NEW + 1;
static HMENU tabCmd[cmdNum];
static inline void setMenu(int cmdID, void* menu)
{
	tabCmd[cmdID - FILE_NEW] = (HMENU)menu;
}
static inline HMENU getMenu(int cmdID)
{
	return tabCmd[cmdID - FILE_NEW];
}
static bool isSeparator(HMENU hMenuItem, int index)
{
	MENUITEMINFO mii;
	memset(&mii, 0, sizeof(mii));
	mii.cbSize = sizeof(mii);
	mii.fMask = MIIM_TYPE;
	::GetMenuItemInfo(hMenuItem, index, MF_BYPOSITION, &mii);
	return ((mii.fType & MF_SEPARATOR) == MF_SEPARATOR);
}
static inline void initMenus(HWND hWnd)
{
	int lastItem = FILE_NEW - 1;

	HMENU hMenu = ::GetMenu(hWnd);
	int menuCount = ::GetMenuItemCount(hMenu);
	int j = FILE_NEW;
	for (int i = 0; i < menuCount; i++)
	{
		HMENU hMenuItem = ::GetSubMenu(hMenu, i);
		int n = ::GetMenuItemCount(hMenuItem);
		int c = 0;
		for (int k = 0; k < n; k++)
			if (isSeparator(hMenuItem, k))
				c++;
		n -= c;
		if (i == 0) n += 8;
		lastItem += n;
		while (j <= lastItem)
		{
			setMenu(j, hMenuItem);
			j++;
		}
	}
}
void removeFileMenuItem(int menuID)
{
	::RemoveMenu(getMenu(FILE_EXIT), menuID, MF_BYCOMMAND);
}
void insertFileMenuItem(int menuID, const wstring& menuTitle) 
{
	::InsertMenu(getMenu(FILE_EXIT), FILE_EXIT, MF_BYCOMMAND, menuID, menuTitle.c_str());
}
static void insertFileMenuSeparator(int separatorID)
{
	::InsertMenu(getMenu(FILE_EXIT), FILE_EXIT, MF_BYCOMMAND | MF_SEPARATOR, separatorID, NULL);
}
void insertFileMenuSeparator()
{
	insertFileMenuSeparator(30000);
}
void removeFileMenuSeparator()
{
	removeFileMenuItem(30000);
}
static int spaceCountPerTab;
#ifdef _MSVC98_
static BOOL CALLBACK tabDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
#else
static INT_PTR CALLBACK tabDlgProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam)
#endif
{
	switch(Message)
	{
	case WM_INITDIALOG:
		{
			HWND updownCtrl = ::GetDlgItem(hwnd, IDC_SPIN1);
			HWND buddy = ::GetDlgItem(hwnd, IDC_EDIT1);
			::SendMessage(updownCtrl, UDM_SETRANGE, 0, MAKELPARAM(8, 1));
			::SendMessage(updownCtrl, UDM_SETPOS, 0, spaceCountPerTab);
			::SendMessage(buddy, EM_SETREADONLY, TRUE, 0);
			return TRUE;
		}
	case WM_COMMAND:
		switch(LOWORD(wParam))
		{			
		case IDOK:
			spaceCountPerTab = (int)::SendMessage(::GetDlgItem(hwnd, IDC_SPIN1), UDM_GETPOS, 0, 0);
		case IDCANCEL:
			::EndDialog(hwnd, LOWORD(wParam));
			return TRUE;
		default:
			break;
		}
	default:
		return FALSE;
	}
}

static void tabDialog(CWindow* win, int& n)
{
	spaceCountPerTab = n;
	::DialogBox(::GetModuleHandle(NULL), MAKEINTRESOURCE(IDD_DIALOG1), win->hWnd, tabDlgProc);
	n = spaceCountPerTab;
}
static void chooseColor(CWindow* window, unsigned long& color)
{
	static unsigned long customColors[16];
	::ZeroMemory(&customColors, sizeof(customColors));
	CHOOSECOLOR cc;
	::ZeroMemory(&cc, sizeof(cc));
	cc.lStructSize = sizeof(cc);
	cc.Flags = CC_RGBINIT | CC_ANYCOLOR;
	cc.hwndOwner = window->hWnd;
	cc.rgbResult = color;
	cc.lpCustColors = customColors;
	if(::ChooseColor(&cc))
	{
		color = cc.rgbResult;
	}
}
static bool openFileDialog(CWindow* window, wstring& fileName)
{
	OPENFILENAME ofn;
	WCHAR szFileName[MAX_PATH] = L"";

	::ZeroMemory(&ofn, sizeof(ofn));
	ofn.lStructSize = sizeof(ofn);
	ofn.hwndOwner = window->hWnd;
	ofn.lpstrFilter = L"Fichiers sources caml (*.ml;*.oml;*.mli)\0*.ml;*.oml;*.mli\0All Files (*.*)\0*.*\0";
	ofn.lpstrFile = (LPWSTR)szFileName;
	ofn.nMaxFile = MAX_PATH;
	ofn.Flags = OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
	ofn.lpstrDefExt = L"ml";
	fileName = szFileName;
	BOOL res = ::GetOpenFileName(&ofn);
	if (res) fileName = szFileName;
	return res ? true : false;
}
static bool saveFileDialog(CWindow* win, wstring& title)
{
	WCHAR szFileName[MAX_PATH] = L"";
	wstring shortName = title.substr(title.find_last_of(L"\\") + 1);
	if (shortName.length() >= MAX_PATH)
	{
		return false; 
	}
	size_t len = shortName.length();
	szFileName[len] = 0;
	for (size_t i = 0; i < len; i++)
	{
		szFileName[i] = shortName[i];
	}

	OPENFILENAME ofn;
	::ZeroMemory(&ofn, sizeof(ofn));
	ofn.lStructSize = sizeof(ofn);
	ofn.hwndOwner = win->hWnd;
	ofn.lpstrFilter = L"Caml Source Files (*.ml;*.oml;*.mli)\0*.ml;*.oml;*.mli\0All Files (*.*)\0*.*\0";
	ofn.lpstrFile = (LPWSTR)szFileName;
	ofn.nMaxFile = MAX_PATH;
	ofn.lpstrDefExt = L"ml";
	ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
	BOOL res = ::GetSaveFileName(&ofn);
	if (res) title = szFileName;
	return res ? true : false;
}
static bool saveFormattedDocumentDialog(CWindow* win, wstring& title)
{
	WCHAR szFileName[MAX_PATH] = L"";
	wstring shortName = title.substr(title.find_last_of(L"\\") + 1);
	if (shortName.length() >= MAX_PATH)
	{
		return false; 
	}
	size_t len = shortName.length();
	szFileName[len] = 0;
	for (size_t i = 0; i < len; i++)
	{
		szFileName[i] = shortName[i];
	}
	OPENFILENAME ofn;
	::ZeroMemory(&ofn, sizeof(ofn));
	ofn.lStructSize = sizeof(ofn);
	ofn.hwndOwner = win->hWnd;
	ofn.lpstrFilter = L"Fichiers rtf (*.rtf)\0*.rtf\0All Files (*.*)\0*.*\0";
	ofn.lpstrFile = (LPWSTR)szFileName;
	ofn.nMaxFile = MAX_PATH;
	ofn.lpstrDefExt = L"rtf";
	ofn.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
	BOOL res = ::GetSaveFileName(&ofn);
	if (res) title = szFileName;
	return res ? true : false;
}
bool createDirectory(const wstring& path)
{
	return ::CreateDirectory(path.c_str(), NULL) || ::GetLastError() == ERROR_ALREADY_EXISTS;
}
#ifdef _MSVC98_
#define CSIDL_LOCAL_APPDATA 0x001c
static bool getConfigFolderParentPath(wstring& path)
{
	WCHAR szPath[MAX_PATH];
	typedef HRESULT __stdcall proc(HWND, int, HANDLE, DWORD, LPWSTR);
	proc* qSHFolderPath;
	HMODULE shFolder = ::LoadLibrary(L"shfolder.dll");
	if (shFolder == NULL) return false;
	qSHFolderPath = (proc*)::GetProcAddress(shFolder, "SHGetFolderPathW");
	if (qSHFolderPath == NULL)
	{
		::FreeLibrary(shFolder);
		return false;
	}
	if (qSHFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, (LPWSTR)szPath))
	{
		::FreeLibrary(shFolder);
		return false;
	}
	path = szPath;
	return true;
}
#else
static bool getConfigFolderParentPath(wstring& path)
{
	WCHAR szPath[MAX_PATH];
	if (::SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, 0, szPath) == S_OK)
	{
		path = szPath;
		return true;
	}
	return false;
}
#endif
bool getConfigFolderPath(wstring& path)
{
	if (getConfigFolderParentPath(path))
	{
		path += L"\\" + appName + L"\\";
		return createDirectory(path);
	}
	return false;
}
wstring applicationDirectoryPath()
{
	WCHAR appPath[MAX_PATH];
	::GetModuleFileName(0, appPath, sizeof(appPath)-1);
	wstring appDirPath = appPath;
	return appDirPath.substr(0, appDirPath.find_last_of(L"\\"));
}
#ifdef _WIN2000_
typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, BOOL*);
LPFN_ISWOW64PROCESS fnIsWow64Process;
static bool IsWow64()
{
	BOOL bIsWow64 = FALSE;
	//IsWow64Process is not available on all supported versions of Windows.
	//Use GetModuleHandle to get a handle to the DLL that contains the function
	//and GetProcAddress to get a pointer to the function if available.
	fnIsWow64Process = (LPFN_ISWOW64PROCESS) ::GetProcAddress(::GetModuleHandle(TEXT("kernel32")),"IsWow64Process");
	if(NULL != fnIsWow64Process)
	{
		if (!fnIsWow64Process(::GetCurrentProcess(),&bIsWow64))
		{
			//handle error
		}
	}
	return bIsWow64 == TRUE;
}
bool architecture64()
{
	return IsWow64();
}
#else
bool architecture64()
{
	SYSTEM_INFO sysInfo;
	::GetNativeSystemInfo(&sysInfo);
	return sysInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64;
}
#endif
void deleteFile(const wstring& fileName)
{
	::DeleteFile(fileName.c_str());
}
static HGLOBAL hDevMode = NULL;
static HGLOBAL hDevNames = NULL;
static void printSetupDlg(CWindow* win, int& left, int& top, int& right, int& bottom)
{
	PAGESETUPDLG psd;
	::ZeroMemory(&psd, sizeof(psd));
	psd.lStructSize = sizeof(psd);
	psd.hwndOwner   = win->hWnd;
	psd.hDevMode = hDevMode;
	psd.hDevNames = hDevNames;
	psd.Flags = PSD_INHUNDREDTHSOFMILLIMETERS | PSD_MARGINS;

	psd.rtMargin.left = left * 100;
	psd.rtMargin.top = top * 100;
	psd.rtMargin.right = right * 100;
	psd.rtMargin.bottom = bottom * 100;

	psd.lpfnPagePaintHook = NULL;
	if (::PageSetupDlg(&psd)==TRUE)
	{
		left = psd.rtMargin.left / 100;
		top = psd.rtMargin.top / 100;
		right = psd.rtMargin.right / 100;
		bottom = psd.rtMargin.bottom / 100;
	}
	if (hDevMode) ::GlobalFree(hDevMode);
	if (hDevNames) ::GlobalFree(hDevNames);
	hDevMode = psd.hDevMode;
	hDevNames = psd.hDevNames;
}
#ifdef _MSVC98_
static HRESULT printProcDlg(HWND hWnd, CRichEdit* re, BOOL (*f)(CRichEdit*, bool, HDC))
{
	HRESULT hResult;
	PRINTDLG pdx = {0};	
	pdx.lStructSize = sizeof(PRINTDLG);
	pdx.hwndOwner = hWnd;
	pdx.hDevMode = hDevMode;
	pdx.hDevNames = hDevNames;
	pdx.hDC = NULL;
	pdx.Flags = PD_RETURNDC | PD_COLLATE | PD_NOPAGENUMS;
	size_t selStart, selEnd;
	re->getSelection(selStart,selEnd);
	if (selStart == selEnd)
		pdx.Flags |= PD_NOSELECTION;
	pdx.nMinPage = 1;
	pdx.nMaxPage = 1000;
	pdx.nCopies = 1;
	pdx.hInstance = 0;
	pdx.lpPrintTemplateName = NULL; 
	hResult = ::PrintDlg(&pdx);
	if (hResult == TRUE) 
	{
		bool printSel = pdx.Flags & PD_SELECTION;
		HDC hdc = pdx.hDC;
		f(re, printSel, hdc);
	}
	if (pdx.hDC != NULL) 
		::DeleteDC(pdx.hDC);
	return hResult;
}
#else
static HRESULT printProcDlg(HWND hWnd, CRichEdit* re, BOOL (*f)(CRichEdit*, bool, HDC))
{
	HRESULT hResult;
	PRINTDLGEX pdx = {0};
	LPPRINTPAGERANGE pPageRanges = NULL;
	pPageRanges = (LPPRINTPAGERANGE) ::GlobalAlloc(GPTR, 10 * sizeof(PRINTPAGERANGE));
	if (!pPageRanges)
		return E_OUTOFMEMORY;
	pdx.lStructSize = sizeof(PRINTDLGEX);
	pdx.hwndOwner = hWnd;
	pdx.hDevMode = hDevMode;
	pdx.hDevNames = hDevNames;
	pdx.hDC = NULL;
	pdx.Flags = PD_RETURNDC | PD_COLLATE | PD_NOPAGENUMS | PD_NOCURRENTPAGE;
	size_t selStart, selEnd;
	re->getSelection(selStart,selEnd);
	if (selStart == selEnd)
		pdx.Flags |= PD_NOSELECTION;
	pdx.Flags2 = 0;
	pdx.ExclusionFlags = 0;
	pdx.nPageRanges = 0;
	pdx.nMaxPageRanges = 10;
	pdx.lpPageRanges = pPageRanges;
	pdx.nMinPage = 1;
	pdx.nMaxPage = 1000;
	pdx.nCopies = 1;
	pdx.hInstance = 0;
	pdx.lpPrintTemplateName = NULL;
	pdx.lpCallback = NULL;
	pdx.nPropertyPages = 0;
	pdx.lphPropertyPages = NULL;
	pdx.nStartPage = START_PAGE_GENERAL;
	pdx.dwResultAction = 0;    
	hResult = ::PrintDlgEx(&pdx);
	if ((hResult == S_OK) && pdx.dwResultAction == PD_RESULT_PRINT) 
	{
		bool printSel = pdx.Flags & PD_SELECTION;
		HDC hdc = pdx.hDC;
		f(re, printSel, hdc);
	}
	if (pdx.hDC != NULL) 
		::DeleteDC(pdx.hDC);
	::GlobalFree(pPageRanges);
	return hResult;
}
#endif
BOOL printRTF(CRichEdit* re, bool printSel, HDC hdc)
{
	re->beginPrint();
	DOCINFO di = { sizeof(di) };    
	if (!::StartDoc(hdc, &di))
	{
		return FALSE;
	}

	int cxPhys1 = ::GetDeviceCaps(hdc, PHYSICALWIDTH); // device units
	int cyPhys1 = ::GetDeviceCaps(hdc, PHYSICALHEIGHT); // device units
	int cxPhys = ::GetDeviceCaps(hdc, HORZSIZE); // millimeters
	int cyPhys = ::GetDeviceCaps(hdc, VERTSIZE); // millimeters

	int cxPhysOffset = ::GetDeviceCaps(hdc, PHYSICALOFFSETX); // device units
	int cyPhysOffset = ::GetDeviceCaps(hdc, PHYSICALOFFSETY); // device units
	cxPhysOffset = ::MulDiv(cxPhysOffset, cxPhys, cxPhys1); // millimeters
	cyPhysOffset = ::MulDiv(cyPhysOffset, cyPhys, cyPhys1); // millimeters
	cxPhys = ::MulDiv(cxPhys - cxPhysOffset - 1 - re->margins.right, 1440, 25); // printable area horizontal size in twips
	cyPhys = ::MulDiv(cyPhys - cyPhysOffset - 1 - re->margins.bottom, 1440, 25); // printable area vertical size in twips

	FORMATRANGE fr;
	fr.hdc       = hdc;
	fr.hdcTarget = hdc;
	fr.rc.left   = ::MulDiv(re->margins.left, 1440, 25);
	fr.rc.right  = cxPhys;
	fr.rc.top   = ::MulDiv(re->margins.top, 1440, 25);
	fr.rc.bottom = cyPhys; // Printing area in twips for GDI page formatting and printing


	if (!printSel) ::SendMessage(re->hEdit, EM_SETSEL, 0, -1);          // Select the entire contents.
	::SendMessage(re->hEdit, EM_EXGETSEL, 0, (LPARAM)&fr.chrg);  // Get the selection into a CHARRANGE.

	// Use GDI to print successive pages.
	BOOL bSuccess = TRUE;
	while (fr.chrg.cpMin < fr.chrg.cpMax && bSuccess) 
	{
		bSuccess = ::StartPage(hdc) > 0;
		if (!bSuccess) break;       
		int cpMin = (int)::SendMessage(re->hEdit, EM_FORMATRANGE, TRUE, (LPARAM)&fr);       
		if (cpMin <= fr.chrg.cpMin) 
		{
			bSuccess = FALSE;
			break;
		}       
		fr.chrg.cpMin = cpMin;
		bSuccess = ::EndPage(hdc) > 0;
	}   
	::SendMessage(re->hEdit, EM_FORMATRANGE, FALSE, 0);   
	if (bSuccess)
	{
		::EndDoc(hdc);
	}   
	else   
	{
		::AbortDoc(hdc);
	}
	re->endPrint();
	return bSuccess;
}
static HRESULT printRTFDlg(HWND hWnd, CRichEdit* re)
{
	return printProcDlg(hWnd, re, printRTF);
}
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
#ifdef _MSVC98_
	CWindow* baseWindow = (CWindow*)::GetWindowLong(hWnd, GWL_USERDATA);
#else
	CWindow* baseWindow = (CWindow*)::GetWindowLongPtr(hWnd, GWLP_USERDATA);
#endif
	if (baseWindow)
		return baseWindow->wndProc(msg, wParam, lParam);
	return ::DefWindowProc(hWnd, msg, wParam, lParam);
}
static HWND CreateRichEdit(HWND hwndOwner, int x, int y, int width, int height, HINSTANCE hinst)
{
	HWND hwndEdit= ::CreateWindowEx(WS_EX_CLIENTEDGE, RICHEDIT_CLASS, L"",
		WS_CHILD | WS_VISIBLE | WS_VSCROLL | WS_HSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_SELECTIONBAR | WS_BORDER | WS_TABSTOP,
		x, y, width, height, 
		hwndOwner, NULL, hinst, NULL);

	return hwndEdit;
}
static BOOL registerRichEditClass(HINSTANCE hInstance)
{
	WNDCLASSEXW wc;

	wc.cbSize		 = sizeof(WNDCLASSEXW);
	wc.style		 = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc	 = WndProc;
	wc.cbClsExtra	 = 0;
	wc.cbWndExtra	 = 0;
	wc.hInstance	 = hInstance;
	wc.hIcon		 = ::LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor		 = ::LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = L"richeditclass";
	wc.hIconSm		 = ::LoadIcon(NULL, IDI_APPLICATION);

	if(!::RegisterClassExW(&wc))
	{
		::MessageBox(NULL, L"L'enregistrement de la classe de fen\xEAtres \"richeditclass\" a \xE9"L"chou\xE9!", appName.c_str(),
			MB_ICONEXCLAMATION | MB_OK);
		return FALSE;
	}
	else
		return TRUE;
}
static HWND makeRichEdit(void* parent)
{
	return ::CreateWindowEx(0, L"richeditclass", L"RichEdit 1", 
		WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN,
		0, 0, 0, 0, (HWND)parent, NULL, (HINSTANCE)::GetModuleHandle(NULL), NULL);
}
static WCHAR g_szChildClassName[] = L"mdiChildFrameClass";
static BOOL registerMDIChildFrameClass(HINSTANCE hInstance)
{
	WNDCLASSEXW wc;

	wc.cbSize		 = sizeof(WNDCLASSEXW);
	wc.style		 = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc	 = WndProc;
	wc.cbClsExtra	 = 0;
	wc.cbWndExtra	 = 0;
	wc.hInstance	 = hInstance;
	wc.hIcon		 = ::LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor		 = ::LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_3DFACE+1);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = g_szChildClassName;
	wc.hIconSm		 = ::LoadIcon(NULL, IDI_APPLICATION);

	if(!::RegisterClassExW(&wc))
	{
		::MessageBox(0, L"L'enregistrement d'une fen\xEAtre a \xE9"L"chou\xE9!", appName.c_str(), MB_ICONEXCLAMATION | MB_OK);
		return FALSE;
	}
	else
		return TRUE;
}
HWND makeMDIChildFrame(void* mdiFrame)
{
	HWND hWnd = NULL;
	MDICREATESTRUCTW mcs;

	mcs.szTitle = L"[Untitled]";
	mcs.szClass = (WCHAR*)g_szChildClassName;
	mcs.hOwner  = ::GetModuleHandle(NULL);
	mcs.x = mcs.cx = CW_USEDEFAULT;
	mcs.y = mcs.cy = CW_USEDEFAULT;
	mcs.style = MDIS_ALLCHILDSTYLES;

	hWnd = (HWND)::SendMessage(((CMDIFrame*)mdiFrame)->hMDIClient, WM_MDICREATE, 0, (LPARAM)&mcs);
	return hWnd;
}
WCHAR g_szClassName[] = L"{321020C5-19B7-4d8d-9237-48EA8F4F4E3E}";
static BOOL registerMDIFrameClass(HINSTANCE hInstance)
{
	WNDCLASSEXW wc;

	wc.cbSize		 = sizeof(WNDCLASSEXW);
	wc.style		 = 0;
	wc.lpfnWndProc	 = WndProc;
	wc.cbClsExtra	 = 0;
	wc.cbWndExtra	 = 0;
	wc.hInstance	 = hInstance;
	wc.hIcon		 = ::LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor		 = ::LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
	wc.lpszMenuName  = (WCHAR*)MAKEINTRESOURCE(IDR_MAINMENU);
	wc.lpszClassName = g_szClassName;
	wc.hIconSm		 = ::LoadIcon(NULL, IDI_APPLICATION);

	if(!::RegisterClassExW(&wc))
	{
		::MessageBox(NULL, L"L'enregistrement de la fen\xEAtre principale a \xE9"L"chou\xE9!", appName.c_str(), MB_ICONEXCLAMATION | MB_OK);
		return FALSE;
	}
	else
		return TRUE;
}
static HWND makeMDIFrame(void* hInstance)
{
	HWND hWnd = NULL;
	if(registerMDIChildFrameClass((HINSTANCE)hInstance)
		&& registerMDIFrameClass((HINSTANCE)hInstance)
		&& registerRichEditClass((HINSTANCE)hInstance))
	{
		hWnd = ::CreateWindowExW(
			0,
			g_szClassName,
			appName.c_str(),
			WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
			CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
			NULL, NULL, (HINSTANCE)hInstance, NULL);
	}
	return hWnd;
}

CWindow::CWindow(void* hInstance, WINDOW_BUILDER wm) 
{
	hWnd = wm(hInstance);
	if(!hWnd)
	{
		::MessageBox(NULL, L"La cr\xE9"L"ation d'une fen\xEAtre a \xE9"L"chou\xE9!", appName.c_str(), MB_ICONEXCLAMATION | MB_OK);
		return;
	}
#ifdef _MSVC98_
	::SetWindowLong(hWnd, GWL_USERDATA, (LONG)this);
#else
	::SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)this);
#endif
}
CWindow::~CWindow()
{
}
void CWindow::stopMessages()
{
#ifdef _MSVC98_
	::SetWindowLong(hWnd, GWL_USERDATA, (LONG)0);
#else
	::SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)0);
#endif
}

CMDIApp::CMDIApp()
{
	name = L"Global\\";
	name += g_szClassName;
	hMutex = ::CreateMutex(NULL, FALSE, name.c_str());
	DWORD dwLastError = ::GetLastError();
	argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
	alreadyExists = dwLastError == ERROR_ALREADY_EXISTS; 
	if (!alreadyExists)
	{
		if (argc == 2 && wstring(argv[1]) == L"init")
		{
			if (::MessageBox(NULL, L"R\xE9tablir les r\xE9glages d'origine ?", appName.c_str(), MB_YESNO | MB_ICONEXCLAMATION) == IDYES)
			{
				wstring path;
				if (getConfigFolderPath(path))
				{
					path +=  appName + version + L".txt";
					::DeleteFile(path.c_str());
				}
			}
		}
	}
	else
	{
		if (argc < 2) return;
		HWND hwnd = ::FindWindow(g_szClassName, NULL);
		wstring path;
		if (getConfigFolderPath(path))
		{
			HANDLE fileHandle = NULL;
			int i = 0;
			wstring str;
			do
			{
				wstringstream format(wstringstream::in | wstringstream::out);
				format << path << ++i;
				str = format.str();
				fileHandle = ::CreateFile(str.c_str(), GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
			}
			while (::GetLastError() == ERROR_FILE_EXISTS);
			if (fileHandle)
			{
				::CloseHandle(fileHandle);
				fstream fs(toBytes(str).c_str(), fstream::out);
				for (int j = 1; j < argc; j++)
				{
					fs << toBytes(wstring(argv[j])) << endl;
				}
				fs.close();
				::PostMessage(hwnd, WM_APP, (WPARAM)i, (LPARAM)0);
			}
		}
		::LocalFree(argv);
	}
}
CMDIApp::~CMDIApp(){}
static UINT uFindReplaceMsg;
#ifdef _MSVC98_
extern "C" WINBASEAPI HWND WINAPI GetConsoleWindow();
#endif
int CMDIApp::run(CMDIFrame& frame)
{
	if (::AllocConsole()) ::ShowWindow(::GetConsoleWindow(), SW_HIDE);
	::InitCommonControls();
	uFindReplaceMsg = ::RegisterWindowMessage(FINDMSGSTRING);
	MSG msg;
	HACCEL hAccel = ::LoadAccelerators(::GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_MAINMENU));
	::LoadLibrary(L"Riched20.dll");
	for (int i = 1; i < argc; i++)
	{
		frame.openFile(argv[i]);
	}
	::LocalFree(argv);
	::ShowWindow(frame.hWnd, SW_SHOW);
	while (::GetMessage(&msg, NULL, 0, 0) > 0)
	{
		if (!(::IsWindow(hFindDlg) && ::IsDialogMessage(hFindDlg, & msg) || ::IsWindow(hFindReplaceDlg) && ::IsDialogMessage(hFindReplaceDlg, & msg)
			|| ::TranslateMDISysAccel(frame.hMDIClient, &msg) || ::TranslateAccelerator(frame.hWnd, hAccel, &msg)))
		{
			::TranslateMessage(&msg);
			::DispatchMessage(&msg);
		}
	}
	::CloseHandle(hMutex);
	return (int)msg.wParam;
}

CMDIFrame::CMDIFrame(): CWindow(GetModuleHandle(NULL), makeMDIFrame)
{
	::SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)::LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_MAINFRAME)));
	mainFrame = this;
	createMDIClient();
	createToolbar();
	createStatusbar();
	ToolbarVisible = true;
	StatusBarVisible = true;
	droppedFiles = new deque<wstring*>();
	::DragAcceptFiles(hWnd, true);
	frameLeft = 50;
	frameTop = 50;
	frameWidth = 800;
	frameHeight = 600;
	cascade = true;
	createMenus();
}    
CMDIFrame::~CMDIFrame()
{
}
#define FIRST_CHILD_WINDOW 50000
#define MDI_CLIENT_WINDOW	101
static const int windowMenuPos = 8;
void CMDIFrame::createMDIClient()
{
	CLIENTCREATESTRUCT ccs;
	ccs.hWindowMenu  = ::GetSubMenu(GetMenu(hWnd), windowMenuPos);
	ccs.idFirstChild = FIRST_CHILD_WINDOW;

	hMDIClient = ::CreateWindowEx(WS_EX_CLIENTEDGE, L"mdiclient", NULL,
		WS_CHILD | WS_CLIPCHILDREN | WS_VSCROLL | WS_HSCROLL | WS_VISIBLE,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
		hWnd, (HMENU)MDI_CLIENT_WINDOW, ::GetModuleHandle(NULL), (LPVOID)&ccs);

	if(hMDIClient == NULL)
		::MessageBox(hWnd, L"La cr\xE9"L"ation de la fen\xEAtre \"MDI client\" a \xE9"L"chou\xE9!", appName.c_str(), MB_OK | MB_ICONERROR);        
}
LRESULT CMDIFrame::wndProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch (msg)
	{
	case WM_APP:
		onOtherInstanceMessage(wParam);
		return 0;
	case WM_DROPFILES:
		dropEvent((void*)wParam);
		break;
	case WM_MOVE:
		onMove((int)LOWORD(lParam), (int)HIWORD(lParam));
	case WM_SIZE:
		onSize();
		break;
	case WM_ACTIVATE:
		{
			if (LOWORD(wParam) != WA_INACTIVE)
			{
				HWND ac = getActiveChild();
				if (ac) ::SendMessage(ac, WM_MDIACTIVATE, 0, 0);
				return 0;
			}
			break;
		}
	case WM_CLOSE:
		if (reviewChanges()) return 0;
		onClose();
		break;
	case WM_DESTROY:
		onDestroy();
		break;
	case WM_COMMAND:
		onCommand((int)LOWORD(wParam));
		if(LOWORD(wParam) >= FIRST_CHILD_WINDOW)
		{
			::DefFrameProc(hWnd, hMDIClient, WM_COMMAND, wParam, lParam);
		}
		else
		{
			HWND hChild = (HWND)::SendMessage(hMDIClient, WM_MDIGETACTIVE,0,0);
			if(hChild)
			{
				::SendMessage(hChild, WM_COMMAND, wParam, lParam);
			}
		}
		break;
	case WM_NOTIFY:
		onNotify((void*)lParam);
		break;
	}
	return ::DefWindowProc(hWnd, msg, wParam, lParam);
}
void CMDIFrame::openFile(const wstring& fileName)
{
}
void CMDIFrame::createMenus()
{
	::initMenus(hWnd);
}
void CMDIFrame::onNotify(void* lParam)
{
	LPNMHDR pnmh = (LPNMHDR) lParam;
	if(pnmh->hwndFrom == hTool)
	{
		LPNMTBCUSTOMDRAW lpNMCustomDraw = (LPNMTBCUSTOMDRAW) lParam;
		RECT rect;
		::GetClientRect(hTool, &rect);
		HBRUSH brush = ::CreateSolidBrush(RGB(230, 230, 230));
		::FillRect(lpNMCustomDraw->nmcd.hdc, &rect, brush);
		::DeleteObject(brush);
	}
}
void CMDIFrame::onOtherInstanceMessage(size_t msg)
{
	wstring path;
	if (!getConfigFolderPath(path)) return;
	wstringstream format(wstringstream::in | wstringstream::out);
	format << path << msg;
	wstring s = format.str();
	fstream fs(toBytes(s).c_str(), fstream::in);
	if (!fs.fail())
	{	
		if (isIconic())
		{
			openIcon();
		}
		char c;
		while (fs.get(c) && c != '\r' && c != '\n')
		{
			string s1;
			while (c != '\r' && c != '\n')
			{
				s1 += c;
				if (!fs.get(c)) break;
			}
			wstring fName = fromBytes(s1);
			filePaths.push_back(fName);
		}
		fs.close();
		deleteFile(s);
		openFiles(filePaths);
	}
}
void CMDIFrame::openFiles(deque<wstring>& fileName)
{
}
void CMDIFrame::dropEvent(void* hDrop)
{
	int droppedFilesNumber = ::DragQueryFile((HDROP)hDrop, 0xFFFFFFFF, NULL, 0);
	for (int i = 0; i < droppedFilesNumber; i++)
	{
		int fileNameSize = ::DragQueryFile((HDROP)hDrop, i, NULL, 0);
		WCHAR * buffer = new WCHAR[fileNameSize + 1];
		::DragQueryFile((HDROP)hDrop, i, buffer, fileNameSize + 1);
		droppedFiles->push_front(new wstring(buffer));
	}
	::DragFinish((HDROP)hDrop);
	onDrop();
}
void CMDIFrame::onDrop()
{
}
void CMDIFrame::onClose()
{
}
void CMDIFrame::onDestroy()
{
	::PostQuitMessage(0);
}
bool CMDIFrame::isIconic()
{
	return ::IsIconic(hWnd) ? true : false;
}
void CMDIFrame::openIcon()
{
	::OpenIcon(hWnd);
}
void CMDIFrame::showMaximized()
{
	::ShowWindow(hWnd, SW_SHOWMAXIMIZED);
}
bool CMDIFrame::reviewChanges()
{
	return false;
}
void CMDIFrame::moveWindow(int left, int top, int width, int height)
{
	::MoveWindow(hWnd, left, top, width, height, TRUE); 
}
void CMDIFrame::update()
{
	::UpdateWindow(hWnd);
}
void CMDIFrame::onCommand(int wParam)
{
}
HWND CMDIFrame::getActiveChild()
{
	return (HWND)::SendMessage(hMDIClient, WM_MDIGETACTIVE,0,0);
}
void CMDIFrame::setActiveChild(CMDIChildFrame *childFrame)
{
	::SendMessage(hMDIClient, WM_MDIACTIVATE, (WPARAM)childFrame->hWnd, (LPARAM)0); 
}
void CMDIFrame::enableMenuItem(int cmd, bool on)
{
	::EnableMenuItem(getMenu(cmd), cmd, MF_BYCOMMAND | (on ? MF_ENABLED : MF_GRAYED));
	int i = 0;
	while (tbCmds[i] != 0)
	{
		if (tbCmds[i] == cmd) break;
		i++;
	}
	if (tbCmds[i] == cmd) enableTBButton(cmd, on);
}
void CMDIFrame::checkMenuItem(int cmd, bool on)
{
	::CheckMenuItem(getMenu(cmd), cmd, on ? MF_CHECKED : MF_UNCHECKED);
}
#ifdef _MSVC98_
#define MIIM_STRING 0x00000040
#endif
void CMDIFrame::setMenuItemText(int cmd,  const wstring& title, bool enabled)
{
	MENUITEMINFO mii;
	memset(&mii, 0, sizeof(mii));
	mii.cbSize = sizeof(mii);
	mii.fMask = MIIM_STRING;
	mii.fType = MIIM_TYPE;
	mii.cch = (UINT)(2* (title.length() + 1));
	mii.dwTypeData = (LPWSTR)title.c_str();
	enableMenuItem(cmd, enabled);       
	::SetMenuItemInfo(getMenu(cmd), cmd, false, &mii);
	::DrawMenuBar(hWnd);
}
void CMDIFrame::setStatus(int index, const wstring& text)
{
	::SendMessage(hStatus, SB_SETTEXT, (WPARAM)index, (LPARAM)text.c_str());
}
void CMDIFrame::onMove(int left, int top)
{
	if ((short)left != -32000)
		frameLeft = left;
	frameTop = top;
}	
void CMDIFrame::onSize()
{
	RECT r;
	::GetWindowRect(hWnd, &r);
	if (r.left != -32000)
	{
		frameLeft = r.left;
		frameTop = r.top;
		frameWidth = r.right - r.left;
		frameHeight = r.bottom - r.top;
	}
	updateFrame();
}
void CMDIFrame::onFileExit()
{
	PostMessage(hWnd, WM_CLOSE, 0, 0);
}
void CMDIFrame::onFileClose()
{
	HWND hChild = (HWND)::SendMessage(hMDIClient, WM_MDIGETACTIVE,0,0);
	if(hChild)
	{
		::SendMessage(hChild, WM_CLOSE, 0, 0);
	}        
}
void CMDIFrame::onTileWindow()
{
	Maximized = false;
	cascade = false;
	::SendMessage(hMDIClient, WM_MDITILE, 0, 0);        
}
void CMDIFrame::onCascadeWindow()
{
	Maximized = false;
	cascade = true;
	::SendMessage(hMDIClient, WM_MDICASCADE, 0, 0);        
}
void CMDIFrame::enableTBButton(WPARAM command, bool enabled)
{
	LPARAM state = (LPARAM)::SendMessage(hTool, TB_GETSTATE, command, (LPARAM)0);
	if (enabled) state |= TBSTATE_ENABLED; else state &= ~TBSTATE_ENABLED;
	::SendMessage(hTool, TB_SETSTATE, command, (LPARAM)state);
	::RedrawWindow(hTool, NULL, NULL, RDW_UPDATENOW);
}
#define TOOLBAR_WINDOW	102
void CMDIFrame::createToolbar()
{
	int toolbarCommands[] = {FILE_NEW, FILE_OPEN1, FILE_SAVE, FILE_PRINT, -1, EDIT_CUT, EDIT_COPY, EDIT_PASTE, -1,
		VIEW_SPLIT, VIEW_BOTH, VIEW_INPUTONLY, VIEW_OUTPUTONLY, VIEW_WRAPINPUT, VIEW_WRAPOUTPUT, -1,
		CAML_SEND, EDIT_INDENT, CAML_INTERRUPT, CAML_STOP, CAML_CLEAROUTPUT, -1, HELP_ABOUT, 0};
	int n = 0;
	int m = 0;
	while (toolbarCommands[n] != 0) {if (toolbarCommands[n] != -1) m++; n++;}
	tbCmds = new int[m];
	for (n = 0, m = 0; toolbarCommands[n] != 0; n++)
	{
		if (toolbarCommands[n] != -1) {tbCmds[m] = toolbarCommands[n]; m++;}
	}
	TBBUTTON* tbb = new TBBUTTON[n];
	::ZeroMemory(tbb, n * sizeof(TBBUTTON));
	int j = 0;
	for (int i = 0; i < n; i++)
	{
		int cmd = toolbarCommands[i];
		if (cmd != -1)
		{
			tbb[i].iBitmap = j++;
			tbb[i].fsState = TBSTATE_ENABLED;
			tbb[i].fsStyle = TBSTYLE_AUTOSIZE;
			tbb[i].idCommand = cmd;
		}
		else
		{
			tbb[i].iBitmap = 0;
			tbb[i].fsState = 0;
			tbb[i].fsStyle = TBSTYLE_SEP;
			tbb[i].idCommand = 0;
		}
	}
	hTool = ::CreateToolbarEx(hWnd,
		WS_VISIBLE | WS_CHILD | WS_BORDER | TBSTYLE_FLAT | TBSTYLE_TRANSPARENT,
		TOOLBAR_WINDOW,
		n,
		::GetModuleHandle(NULL),
		IDR_BITMAP,
		tbb,
		n,
		20, 16, 0, 0,
		sizeof(TBBUTTON));
	if (hTool == NULL)
		::MessageBox(hWnd, L"La cr\xE9"L"ation de la barre d'outils a \xE9"L"chou\xE9!", appName.c_str(), MB_OK | MB_ICONERROR);
	delete [] tbb;
}
#define STATUSBAR_WINDOW	103
void CMDIFrame::createStatusbar()
{
	hStatus = ::CreateWindowEx(0, STATUSCLASSNAME, NULL,
		WS_CHILD | WS_VISIBLE | SBARS_SIZEGRIP, 0, 0, 0, 0,
		hWnd, (HMENU)STATUSBAR_WINDOW, ::GetModuleHandle(NULL), NULL);

	int statwidths[] = {200, -1};
	::SendMessage(hStatus, SB_SETPARTS, sizeof(statwidths)/sizeof(int), (LPARAM)statwidths);
}
void CMDIFrame::updateFrame()
{
	int toolbarHeight;
	int statusBarHeight;

	if (ToolbarVisible)
	{
		hTool = ::GetDlgItem(hWnd, TOOLBAR_WINDOW);
		::SendMessage(hTool, TB_AUTOSIZE, 0, 0);
		RECT toolbarRect;
		::GetWindowRect(hTool, &toolbarRect);
		toolbarHeight = toolbarRect.bottom - toolbarRect.top;
	}
	else
	{
		toolbarHeight = 0;
		::ShowWindow(hTool, SW_HIDE);
	}

	if (StatusBarVisible)
	{
		hStatus = ::GetDlgItem(hWnd, STATUSBAR_WINDOW);
		::SendMessage(hStatus, WM_SIZE, 0, 0);
		RECT statusBarRect;
		::GetWindowRect(hStatus, &statusBarRect); 
		statusBarHeight = statusBarRect.bottom - statusBarRect.top;
	}
	else
	{
		statusBarHeight = 0;
		::ShowWindow(hStatus, SW_HIDE);
	}

	RECT clientRect;
	::GetClientRect(hWnd, &clientRect);
	clientRect.bottom -= toolbarHeight + statusBarHeight;
	::SetWindowPos(GetDlgItem(hWnd, MDI_CLIENT_WINDOW), NULL, 0, toolbarHeight, clientRect.right, clientRect.bottom, SWP_NOZORDER);

	if (toolbarHeight != 0)
		::ShowWindow(hTool, SW_SHOW);
	if (statusBarHeight != 0)
		::ShowWindow(hStatus, SW_SHOW);
}
void CMDIFrame::setTitle(const wstring& title)
{
	::SetWindowText(hWnd, title.c_str());
}
void CMDIFrame::colorDialog(unsigned long& color)
{
	chooseColor(this, color);
}
void CMDIFrame::tabDialog(int& spaceNumPerTab)
{
	::tabDialog(this, spaceNumPerTab);
}
bool CMDIFrame::openFileDialog(wstring& fileName)
{
	return ::openFileDialog(this, fileName);
}
bool CMDIFrame::saveFileDialog(wstring& title)
{
	return ::saveFileDialog(this, title);
}
bool CMDIFrame::saveFormattedDocumentDialog(wstring& title)
{
	return ::saveFormattedDocumentDialog(this, title);
}
void CMDIFrame::printSetUpDialog(int& leftMargin, int& topMargin, int& rightMargin, int& bottomMargin)
{
	::printSetupDlg(this, leftMargin, topMargin, rightMargin, bottomMargin);
}
void CMDIFrame::fontDlg(CFont*& font)
{
	LOGFONT lf;

	if (font->hf) ::GetObject(font->hf, sizeof(LOGFONT), &lf);
	else ::ZeroMemory(&lf, sizeof(lf));

	CHOOSEFONT cf;
	::ZeroMemory(&cf, sizeof(CHOOSEFONT));
	cf.lStructSize = sizeof(CHOOSEFONT); 
	cf.Flags =  CF_BOTH | CF_INITTOLOGFONTSTRUCT | CF_FIXEDPITCHONLY;
	cf.hwndOwner = hWnd;
	cf.nFontType = BOLD_FONTTYPE | ITALIC_FONTTYPE | REGULAR_FONTTYPE | SCREEN_FONTTYPE;
	cf.lpLogFont = &lf;

	if(ChooseFont(&cf))
	{
		{
			HFONT hfAux = ::CreateFontIndirect(&lf);
			if (hfAux)
			{
				wstring fontName = lf.lfFaceName;
				CFont* font1 = new CFont();
				font1->hf = hfAux;
				font1->fntName = new wstring(fontName);
				font1->fntSize = cf.iPointSize / 10;
				font1->bold = lf.lfWeight == FW_BOLD;
				font1->italic = lf.lfItalic == 0xFF;
				font = font1;
			}
		}
	}
}

CMDIChildFrame::CMDIChildFrame(CMDIFrame* mdiFrame, bool) : CWindow(mdiFrame, makeMDIChildFrame)
{
	setDocumentIcon();
	timerID = 0;
	::ShowWindow(hWnd, SW_HIDE);
}
CMDIChildFrame::~CMDIChildFrame()
{
}
LRESULT CMDIChildFrame::wndProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
	case WM_LBUTTONDOWN:
		{
			POINTS pt = MAKEPOINTS(lParam);
			onLeftButtonDown(pt.x, pt.y);
		}
		break;
	case WM_LBUTTONUP:
		onLeftButtonUp();
		break;
	case WM_MOUSEMOVE:
		{
			POINTS pt = MAKEPOINTS(lParam);
			onMouseMove(pt.x, pt.y, wParam == MK_LBUTTON);
		}
		break;
	case WM_MDIACTIVATE:
		onActivate(hWnd == (HWND)lParam);
		break;
	case WM_COMMAND:
		onCommand(LOWORD(wParam));
		break;
	case WM_SIZE:
		onSize();
		break;
	case WM_TIMER:
		onTimer((int)wParam);
		break;
	case WM_CLOSE:
		if (reviewChange()) return 0;
		onClose();
		break;
	case WM_DESTROY:
		onDestroy();
		return 0;
	}
	return ::DefMDIChildProc(hWnd, msg, wParam, lParam);
}
void CMDIChildFrame::drawFocusRect(int left, int top, int right, int bottom)
{
	RECT r;
	::SetRect(&r,left, top, right, bottom);
	HDC hdc = ::GetDC(hWnd);
	::DrawFocusRect(hdc, &r);
	::ReleaseDC(hWnd, hdc);
}
void CMDIChildFrame::setCapture()
{
	::SetCapture(hWnd);
}
void CMDIChildFrame::releaseCapture()
{
	::ReleaseCapture();
}
bool CMDIChildFrame::reviewChange()
{
	return false;
}
void CMDIChildFrame::showNormal()
{
	::ShowWindow(hWnd, SW_SHOW);
}
bool CMDIChildFrame::isMaximized()
{
	return ::IsZoomed(hWnd) ? true : false;
}
void CMDIChildFrame::setWindowModified(bool)
{
}
void CMDIChildFrame::showDefault()
{
	::ShowWindow(hWnd, SW_SHOWMAXIMIZED);
}
void CMDIChildFrame::showMaximized()
{
	::ShowWindow(hWnd, SW_SHOWMAXIMIZED);
}
void CMDIChildFrame::hide()
{
	::ShowWindow(hWnd, SW_HIDE); 
}
void CMDIChildFrame::update()
{
	::UpdateWindow(hWnd);
}
void CMDIChildFrame::setDocumentIcon()
{
	::SendMessage(hWnd, WM_SETICON, (WPARAM)ICON_SMALL, (LPARAM)LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDR_WINCAMLTYPE)));
}
void CMDIChildFrame::onCommand(int wParam)
{
}
void CMDIChildFrame::onSize()
{
}
void CMDIChildFrame::onTimer(int timerID)
{
	if (timerID == WM_USER + 200)
	{
		onTimer();
	}
}
void CMDIChildFrame::onTimer()
{
}
void CMDIChildFrame::onActivate(bool activated)
{
	if (activated)
		fr.hwndOwner = textEdit->hWnd;
	onActivate();
}
void CMDIChildFrame::onActivate()
{
}
void CMDIChildFrame::onClose()
{
}
void CMDIChildFrame::onDestroy()
{
	stopMessages();
	if (timerID) ::KillTimer(hWnd,timerID);
	::DestroyWindow(hWnd);
	hWnd = NULL;
}
void CMDIChildFrame::onFileSaveAs()
{
}
void CMDIChildFrame::onEditCut()
{
}
void CMDIChildFrame::onEditCopy()
{
}
void CMDIChildFrame::onEditPaste()
{
}
void CMDIChildFrame::onEditSelectAll()
{
} 
void CMDIChildFrame::onLeftButtonDown(int x, int y)
{
}
void CMDIChildFrame::onLeftButtonUp()
{
}
void CMDIChildFrame::onMouseMove(int x, int y, bool leftButtonDown)
{
}
void CMDIChildFrame::getClientSize(int& width, int& height)
{
	RECT r;
	::GetClientRect(hWnd, &r);
	width = r.right;
	height = r.bottom;
}
void CMDIChildFrame::setTitle(const wstring& title)
{
	::SetWindowText(hWnd, title.c_str());
}
void CMDIChildFrame::setCursor(int cursor)
{
	::setCursor(cursor);
}
void CMDIChildFrame::startTimer()
{
	timerID = ::SetTimer(hWnd, WM_USER + 200, 5, NULL);
}

CFont::CFont(const wstring& fontName, int fontSize, bool bold, bool italic)
{
	LOGFONT lf;
	::ZeroMemory(&lf, sizeof(LOGFONT));
	size_t len = fontName.length();
	size_t sz = sizeof(lf.lfFaceName);
	if (len >= sz) len = sz - 1;
	lf.lfFaceName[len] = 0;
	for (size_t i = 0; i < len; i++)
		lf.lfFaceName[i] = fontName[i];
	lf.lfHeight = - ::MulDiv(fontSize, ::GetDeviceCaps(::GetDC(NULL), LOGPIXELSY), 72);
	lf.lfCharSet = ANSI_CHARSET;
	if (bold) lf.lfWeight = FW_BOLD;
	lf.lfItalic = italic != 0;
	hf = ::CreateFontIndirect(&lf);
	fntName = new wstring(fontName);
	fntSize = fontSize;
}
CFont::~CFont()
{
	::DeleteObject((HFONT)hf);
	delete(fntName);
}

static WNDPROC savedEditProc;
#ifdef _MSVC98_
static DWORD CALLBACK SaveRTFCallback(unsigned long dwCookie, unsigned char* pbbuf, LONG cb, LONG *pcb) 
#else
static DWORD CALLBACK SaveRTFCallback(DWORD_PTR dwCookie, LPBYTE pbbuf, LONG cb, LONG *pcb) 
#endif
{
	return (DWORD)!::WriteFile((HANDLE)dwCookie, pbbuf, cb, (LPDWORD)pcb, NULL);
}
LRESULT CALLBACK editProcCallback(HWND hEdit, UINT msg, WPARAM wParam, LPARAM lParam)
{
#ifdef _MSVC98_
	CRichEdit* textEdit = (CRichEdit*)::GetWindowLong(hEdit, GWL_USERDATA);
#else
	CRichEdit* textEdit = (CRichEdit*)::GetWindowLongPtr(hEdit, GWLP_USERDATA);
#endif
	if (textEdit) return textEdit->editProc(msg, wParam, lParam);
	return ::CallWindowProc(savedEditProc, hEdit, msg, wParam, lParam);
}
CRichEdit::CRichEdit(CWindow* win): CWindow(win->hWnd, makeRichEdit)
{
	hEdit = ::CreateRichEdit(this->hWnd, 0, 0, 100, 100, ::GetModuleHandle(NULL));
	if(hEdit == NULL)
	{
		::MessageBox(hWnd, L"La cr\xE9"L"ation d'un contr\xF4le \"RichEdit\" a \xF9"L"chou\xF9!", appName.c_str(), MB_OK | MB_ICONERROR);
	}
	else
	{
		::SendMessage(hEdit, EM_SETUNDOLIMIT, 0, 0);
		::SendMessage(hEdit, EM_EXLIMITTEXT, 0, 0x20000);
		::RevokeDragDrop(hEdit);
		margins.left = margins.right = margins.top = margins.bottom = 10;
		hDevMode = NULL;
		hDevNames = NULL;
		HFONT hfDefault = (HFONT)::GetStockObject(DEFAULT_GUI_FONT);
		::SendMessage(hEdit, WM_SETFONT, (WPARAM)hfDefault, MAKELPARAM(FALSE, 0));
		::SendMessage(hEdit, EM_SETEVENTMASK, 0, ENM_MOUSEEVENTS | ENM_KEYEVENTS | ENM_SELCHANGE);
#ifdef _MSVC98_
		savedEditProc = (WNDPROC)::SetWindowLong(hEdit, GWL_WNDPROC, (LONG)editProcCallback);
		::SetWindowLong(hEdit, GWL_USERDATA, (LONG)this);
#else
		savedEditProc = (WNDPROC)::SetWindowLongPtr(hEdit, GWLP_WNDPROC, (UINT_PTR)editProcCallback);
		::SetWindowLongPtr(hEdit, GWLP_USERDATA, (UINT_PTR)this);
#endif
		selectionChanged = true;
	}
}
CRichEdit::~CRichEdit()
{
	::DestroyWindow(hEdit);
	stopMessages();
}
LRESULT CRichEdit::editProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
	switch(msg)
	{
	case WM_KEYDOWN:
		if (wParam == VK_RETURN && (lParam & 0x1000000))
		{
			onEnterKeyDown();
			return 0;
		}
		else if (wParam == VK_RETURN)
		{
			onReturnKeyDown();
			return 0;
		}
	default: break;
	}
	return ::CallWindowProc(savedEditProc, hEdit, msg, wParam, lParam);
}

LRESULT CRichEdit::wndProc(UINT msg, WPARAM wParam, LPARAM lParam)
{
	if (msg == uFindReplaceMsg)
	{
		onFindReplace(lParam);
		return 0;
	}
	switch(msg)
	{
	case WM_NOTIFY:
		onNotify((void*)lParam);
		break;
	}
	return ::DefMDIChildProc(hWnd, msg, wParam, lParam);
}
void CRichEdit::onFindReplace(LPARAM lParam)
{
	LPFINDREPLACE lpfr;
	lpfr = (LPFINDREPLACE)lParam;
	fr.Flags = lpfr->Flags;
	if (lpfr->Flags & FR_DIALOGTERM)
	{
		onFindClose();
		return; 
	} 
	if (lpfr->Flags & FR_FINDNEXT) 
	{
		 onFindNext(lpfr->lpstrFindWhat, lpfr->Flags);
	}
	else if (lpfr->Flags & FR_REPLACE)
	{
		onReplace(lpfr->lpstrReplaceWith);
	}
	else if (lpfr->Flags & FR_REPLACEALL)
	{
		onReplaceAll(lpfr->lpstrFindWhat, lpfr->lpstrReplaceWith, lpfr->Flags);
	}
}
void CRichEdit::onFindNext(const wstring& findWhat, int flags)
{
	this->find(findWhat, flags);
}
void CRichEdit::onReplace(const wstring& replaceWith)
{
}
void CRichEdit::onReplaceAll(const wstring& findWhat, const wstring& replaceWith, unsigned long flags)
{
}
void CRichEdit::onFindClose()
{
	closeFindDialog(true);
}
void CRichEdit::onNotify(void* lParam)
{
	NMHDR* pnmhdr = (NMHDR*)lParam;
	if (pnmhdr->code == EN_MSGFILTER)
	{
		MSGFILTER* msgFilter = (MSGFILTER*)lParam;
		switch(msgFilter->msg)
		{
		case WM_CHAR:
			{
				onChar((int)msgFilter->wParam);
				break;
			}
		case WM_KEYDOWN:
			{
				onKeyDown();
				break;
			}
		case WM_KEYUP:
			{
				onKeyUp();
				break;
			}
		case WM_LBUTTONUP:
			{
				bool ctrlKeyDown = msgFilter->wParam & MK_CONTROL ? true : false;
				onLeftButtonUp(ctrlKeyDown);
				break;
			}
		default:
			break;
		}
	}
}
void CRichEdit::onLeftButtonUp(bool ctrlKeyDown)
{
}
void CRichEdit::onChar(int vKeyCode)
{
}
void CRichEdit::onKeyDown()
{
}
void CRichEdit::onEnterKeyDown()
{
}
void CRichEdit::onReturnKeyDown()
{
}
void CRichEdit::onKeyUp()
{
}
void CRichEdit::getSelection(size_t& selStart, size_t& selEnd)
{
	CHARRANGE cr;
	::SendMessage(hEdit, EM_EXGETSEL, (WPARAM)0, (LPARAM)&cr);
	selStart = cr.cpMin;
	selEnd = cr.cpMax;
}
void CRichEdit::setSelection(size_t selStart, size_t selEnd)
{
	CHARRANGE cr = {(LONG)selStart, (LONG)selEnd};
	::SendMessage(hEdit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr);
}
void CRichEdit::setUseTabs(bool)
{
}
void CRichEdit::selectAll()
{
	CHARRANGE cr = {0, (LONG)getTextLength()};
	::SendMessage(hEdit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr);
}
void CRichEdit::find(const wstring& findWhat, unsigned long flags)
{
	size_t selStart, selEnd;
	getSelection(selStart, selEnd);
	flags &= (FR_DOWN | FR_MATCHCASE | FR_WHOLEWORD);
	findTextEx.chrg.cpMin = flags & FR_DOWN ? (LONG)selEnd : (LONG)selStart;
	findTextEx.chrg.cpMax = flags & FR_DOWN ? (LONG)getTextLength() : 0;
#ifdef _MSVC2010_
	findTextEx.lpstrText = (LPCWSTR)findWhat.c_str();
#else
	findTextEx.lpstrText = (LPWSTR)findWhat.c_str();
#endif
	::SendMessage(hEdit, EM_FINDTEXTEX, flags, (LPARAM)&findTextEx);
	int min = findTextEx.chrgText.cpMin;
	int max = findTextEx.chrgText.cpMax;
	if (min != -1 || max != -1) setSelection(min, max);
	else
	{
		HWND hwnd = hFindDlg ? hFindDlg : hFindReplaceDlg;
		::MessageBox(hwnd, L"Recherche achev\xE9"L"e", appName.c_str(), MB_OK | MB_ICONINFORMATION );
	}
}
void CRichEdit::replace(int a, int l, wstring s)
{
	setText(a, l, s);
}
void CRichEdit::replaceAll(const wstring& findWhat, const wstring& replaceWith, unsigned long flags)
{
	LONG min = 0;
	LONG max = 0;
	FINDTEXTEX ftex;
	while (true)
	{
		ftex.chrg.cpMin = max;
		ftex.chrg.cpMax = (LONG)getTextLength();
#ifdef _MSVC2010_
		ftex.lpstrText = (LPCWSTR)findWhat.c_str();
#else
		ftex.lpstrText = (LPWSTR)findWhat.c_str();
#endif
		::SendMessage(hEdit, EM_FINDTEXTEX, flags, (LPARAM)&ftex);
		min = ftex.chrgText.cpMin;
		max = ftex.chrgText.cpMax;
		if (min == -1 && max == -1) break;
		replace(min, max - min, replaceWith);
	}
}
size_t CRichEdit::getCaretPosition()
{
	POINT pt;
	::GetCaretPos(&pt);
	return (size_t)::SendMessage(hEdit, EM_CHARFROMPOS, 0, (LPARAM)&pt);
}
void CRichEdit::getSelection(CHARRANGE *cr)
{
	::SendMessage(hEdit, EM_EXGETSEL, (WPARAM)0, (LPARAM)cr);
}
void CRichEdit::setSelection(CHARRANGE cr)
{
	::SendMessage(hEdit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr);
}
void CRichEdit::hideSelection()
{
	::SendMessage(hEdit, EM_HIDESELECTION,(WPARAM)1, (LPARAM)0);
}
void CRichEdit::showSelection()
{
	::SendMessage(hEdit, EM_HIDESELECTION,(WPARAM)0, (LPARAM)0);
}
size_t CRichEdit::getTextLength()
{
	GETTEXTLENGTHEX gtl = {GTL_DEFAULT, 1200};
	return (size_t)::SendMessage(hEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, (LPARAM)0);    
}
wstring CRichEdit::getText()
{
	GETTEXTLENGTHEX gtl = {GTL_DEFAULT | GTL_NUMBYTES, 1200};
	size_t length = (size_t) ::SendMessage(hEdit, EM_GETTEXTLENGTHEX, (WPARAM)&gtl, (LPARAM)0);
	WCHAR* buf = new WCHAR[length + 1];
	GETTEXTEX gt;
	gt.cb = (DWORD)(2 * (length + 1));
	gt.flags = GT_DEFAULT;
	gt.codepage = 1200;
	gt.lpDefaultChar = NULL;
	gt.lpUsedDefChar = NULL;
	::SendMessage(hEdit, EM_GETTEXTEX, (WPARAM)&gt, (LPARAM)buf);										
	wstring str(buf);
	delete [] buf;
	return str;
}
#ifdef _MSVC98_
#define GT_SELECTION 2
#endif
wstring CRichEdit::getText(size_t start, size_t length)
{
	CHARRANGE cr;
	::SendMessage(hEdit, EM_EXGETSEL, (WPARAM)0, (LPARAM)&cr);
	::SendMessage(hEdit, EM_HIDESELECTION,(WPARAM)1, (LPARAM)0);
	CHARRANGE cr1 = {(LONG)start, (LONG)(start + length)};
	::SendMessage(hEdit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr1);

	WCHAR* buf = new WCHAR[length + 1];
	GETTEXTEX gt;
	gt.cb = (DWORD)(2 * (length + 1));
	gt.flags = GT_DEFAULT|GT_SELECTION;
	gt.codepage = 1200;
	gt.lpDefaultChar = NULL;
	gt.lpUsedDefChar = NULL;
	::SendMessage(hEdit, EM_GETTEXTEX, (WPARAM)&gt, (LPARAM)buf);							
	wstring str(buf);	
	delete [] buf;

	::SendMessage(hEdit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr);    
	::SendMessage(hEdit, EM_HIDESELECTION,(WPARAM)0, (LPARAM)0);
	return str;
}
#ifdef _MSVC98_
typedef struct _settextex
{
	DWORD flags;
	UINT codepage;
} SETTEXTEX;
#define EM_SETTEXTEX WM_USER + 97
#endif
void CRichEdit::setText(size_t start, size_t length, const wstring& text, unsigned long color)
{
	::SendMessage(hEdit, EM_HIDESELECTION,(WPARAM)1, (LPARAM)0);

	CHARRANGE cr1 = {(LONG)start, (LONG)(start + length)};
	::SendMessage(hEdit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr1);

	SETTEXTEX gt;
	gt.flags = GT_DEFAULT|GT_SELECTION;
	gt.codepage = 1200;
	::SendMessage(hEdit, EM_SETTEXTEX, (WPARAM)&gt, (LPARAM)text.c_str());

	cr1.cpMin = (LONG)start;
	cr1.cpMax = (LONG)(start + text.length());
	::SendMessage(hEdit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr1);

	CHARFORMAT cfm;
	cfm.cbSize = sizeof(cfm);
	::SendMessage(hEdit, EM_GETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfm);
	cfm.dwEffects = 0;
	cfm.crTextColor = color;
	cfm.dwMask = CFM_COLOR;
	::SendMessage(hEdit, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cfm);

	cr1.cpMin = cr1.cpMax;
	::SendMessage(hEdit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr1);    
	::SendMessage(hEdit, EM_HIDESELECTION,(WPARAM)0, (LPARAM)0);
}
void CRichEdit::setSelectedText(const wstring& s)
{
	CHARRANGE cr;
	getSelection(&cr);
	SETTEXTEX gt;
	gt.flags = GT_DEFAULT|GT_SELECTION;
	gt.codepage = 1200;
	::SendMessage(hEdit, EM_SETTEXTEX, (WPARAM)&gt, (LPARAM)s.c_str());							
	cr.cpMax = cr.cpMin + (LONG)s.length();
	::SendMessage(hEdit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr);    
}
void CRichEdit::setTabs(int charSize, int spaceCountPerTab)
{
	PARAFORMAT pf ;
	pf.cbSize = sizeof(PARAFORMAT);
	pf.dwMask = PFM_TABSTOPS ;
	pf.cTabCount = MAX_TAB_STOPS;
	for( int itab = 0 ; itab < pf.cTabCount ; itab++ )
		pf.rgxTabs[itab] = (itab+1) * 12 * charSize * spaceCountPerTab;
	CHARRANGE cr;
	getSelection(&cr);
	hideSelection();
	CHARRANGE cr1 = {0, -1};
	setSelection(cr1);
	::SendMessage(hEdit, EM_SETPARAFORMAT, (WPARAM)0, (LPARAM)&pf);
	setSelection(cr);
	showSelection();
}
size_t CRichEdit::visibleTextOffset()
{
	POINTL pt = {0, 0};
	return (size_t)::SendMessage(this->hEdit, EM_CHARFROMPOS, 0, (LPARAM)&pt);
}
size_t CRichEdit::visibleTextEnd()
{
	RECT r;
	::GetClientRect(this->hEdit, &r);
	POINTL pt ={r.right - 1, r.bottom - 1};
	return (size_t)::SendMessage(this->hEdit, EM_CHARFROMPOS, 0, (LPARAM)&pt);
}
#ifdef _MSVC98_
#define EM_GETSCROLLPOS WM_USER + 221
#define EM_SETSCROLLPOS WM_USER + 222
#endif
void CRichEdit::suspendLayout()
{
	::SendMessage(hEdit, WM_SETREDRAW, (WPARAM)0, (LPARAM)0);
	::SendMessage(hEdit, EM_GETSCROLLPOS, (WPARAM)0, (LPARAM)&scrollPos);
}
void CRichEdit::resumeLayout()
{
	::SendMessage(hEdit, EM_SETSCROLLPOS, (WPARAM)0, (LPARAM)&scrollPos);
	::SendMessage(hEdit, WM_SETREDRAW, (WPARAM)1, (LPARAM)0);
}
void CRichEdit::updateView()
{
	::SendMessage(hEdit, WM_SETREDRAW, (WPARAM)0, (LPARAM)0);
	::SendMessage(hEdit, WM_SETREDRAW, (WPARAM)1, (LPARAM)0);
	RECT r;
	::GetClientRect(hEdit, &r);
	::InvalidateRect(hEdit, &r, true);
}
#ifndef CFM_BACKCOLOR 
#define CFM_BACKCOLOR 0x04000000
#define CFE_AUTOBACKCOLOR CFM_BACKCOLOR
#endif
void CRichEdit::setTextColor(size_t a, size_t l, unsigned long c)
{
	CHARRANGE cr;
	getSelection(&cr);
	if (cr.cpMin != cr.cpMax)
	{
		POINT pt;
		::GetCaretPos(&pt);
		LONG caretCharPos = (LONG)::SendMessage(hEdit, EM_CHARFROMPOS, 0, (LPARAM)&pt);
		if (cr.cpMin - caretCharPos <= 1 && caretCharPos - cr.cpMin <= 1)
		{
			caretCharPos = cr.cpMin;
			cr.cpMin = cr.cpMax;
			cr.cpMax = caretCharPos;
		}
	}

	::SendMessage(this->hEdit, EM_HIDESELECTION,(WPARAM)1, (LPARAM)0);

	CHARRANGE cr1 = {(LONG)a, (LONG)(a + l)};
	::SendMessage(this->hEdit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr1);
	CHARFORMAT2 cfm;
	::memset(&cfm, 0, sizeof(cfm));
	cfm.cbSize = sizeof(CHARFORMAT2);
	cfm.crTextColor = c;
	cfm.dwMask = CFM_COLOR;
	cfm.dwEffects |= CFE_AUTOBACKCOLOR;
	::SendMessage(this->hEdit, EM_SETCHARFORMAT,(WPARAM)SCF_SELECTION,(LPARAM)&cfm);

	::SendMessage(this->hEdit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr);    
	::SendMessage(this->hEdit, EM_HIDESELECTION,(WPARAM)0, (LPARAM)0);
}
void  CRichEdit::setBackground(unsigned long color)
{
	::SendMessage(this->hEdit, EM_SETBKGNDCOLOR, (WPARAM)0, (LPARAM)color);
}
void CRichEdit::setTextBackground(size_t a, size_t l, unsigned long backColor)
{
	CHARRANGE cr1;
	getSelection(&cr1);
	if (cr1.cpMin != cr1.cpMax)
	{
		POINT pt;
		::GetCaretPos(&pt);
		LONG caretCharPos = (LONG)::SendMessage(hEdit, EM_CHARFROMPOS, 0, (LPARAM)&pt);
		if (cr1.cpMin - caretCharPos <= 1 && caretCharPos - cr1.cpMin <= 1)
		{
			caretCharPos = cr1.cpMin;
			cr1.cpMin = cr1.cpMax;
			cr1.cpMax = caretCharPos;
		}
	}

	CHARRANGE cr;
	::SendMessage(this->hEdit, WM_SETREDRAW, (WPARAM)0, (LPARAM)0);
	::SendMessage(this->hEdit, EM_GETSCROLLPOS, (WPARAM)0, (LPARAM)&cr);

	::SendMessage(this->hEdit, EM_HIDESELECTION,(WPARAM)1, (LPARAM)0);
	CHARRANGE cr2 = {(LONG)a, (LONG)(a + l)};
	::SendMessage(this->hEdit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr2);
	CHARFORMAT2 cfm;
	::memset(&cfm, 0, sizeof(cfm));
	cfm.cbSize = sizeof(cfm);
	cfm.dwMask |= CFM_BACKCOLOR;
	cfm.dwEffects = 0;
	cfm.crBackColor = backColor;
	::SendMessage(this->hEdit, EM_SETCHARFORMAT,(WPARAM)SCF_SELECTION, (LPARAM)&cfm);

	::SendMessage(this->hEdit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr1);    
	::SendMessage(this->hEdit, EM_HIDESELECTION,(WPARAM)0, (LPARAM)0);
	::SendMessage(this->hEdit, EM_SETSCROLLPOS, (WPARAM)0, (LPARAM)&cr);
	::SendMessage(this->hEdit, WM_SETREDRAW, (WPARAM)1, (LPARAM)0);
}
void CRichEdit::setTextDefaultBackground(size_t a, size_t l)
{
	CHARRANGE cr1;
	getSelection(&cr1);
	if (cr1.cpMin != cr1.cpMax)
	{
		POINT pt;
		::GetCaretPos(&pt);
		LONG caretCharPos = (LONG)::SendMessage(hEdit, EM_CHARFROMPOS, 0, (LPARAM)&pt);
		if (cr1.cpMin - caretCharPos <= 1 && caretCharPos - cr1.cpMin <= 1)
		{
			caretCharPos = cr1.cpMin;
			cr1.cpMin = cr1.cpMax;
			cr1.cpMax = caretCharPos;
		}
	}

	CHARRANGE cr;
	::SendMessage(this->hEdit, WM_SETREDRAW, (WPARAM)0, (LPARAM)0);
	::SendMessage(this->hEdit, EM_GETSCROLLPOS, (WPARAM)0, (LPARAM)&cr);

	::SendMessage(this->hEdit, EM_HIDESELECTION,(WPARAM)1, (LPARAM)0);
	CHARRANGE cr2 = {(LONG)a, (LONG)(a + l)};
	::SendMessage(this->hEdit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr2);
	CHARFORMAT2 cfm;
	::memset(&cfm, 0, sizeof(cfm));
	cfm.cbSize = sizeof(cfm);
	cfm.dwMask |= CFM_BACKCOLOR;
	cfm.dwEffects |= CFE_AUTOBACKCOLOR;
	::SendMessage(this->hEdit, EM_SETCHARFORMAT,(WPARAM)SCF_SELECTION, (LPARAM)&cfm);

	::SendMessage(this->hEdit, EM_EXSETSEL, (WPARAM)0, (LPARAM)&cr1);    
	::SendMessage(this->hEdit, EM_HIDESELECTION,(WPARAM)0, (LPARAM)0);
	::SendMessage(this->hEdit, EM_SETSCROLLPOS, (WPARAM)0, (LPARAM)&cr);
	::SendMessage(this->hEdit, WM_SETREDRAW, (WPARAM)1, (LPARAM)0);
}
void CRichEdit::setColors()
{
    
}
void CRichEdit::cut()
{
	::SendMessage(hEdit, WM_CUT , 0 , 0);
}
void CRichEdit::copy()
{
	::SendMessage(hEdit, WM_COPY, 0, 0);        
}
void CRichEdit::paste()
{
	::SendMessage(hEdit, WM_PASTE, 0, 0);        
}
bool CRichEdit::loadFile(const wstring& fileName, bool guessEnc, int &preferredEncoding)
{
	HANDLE hFile;
	bool bSuccess = false;

	hFile = ::CreateFile(fileName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,
		OPEN_EXISTING, 0, NULL);
	if(hFile != INVALID_HANDLE_VALUE)
	{
		DWORD dwFileSize;

		dwFileSize = ::GetFileSize(hFile, NULL);
		if(dwFileSize != 0xFFFFFFFF)
		{
			LPSTR pszFileText;

			pszFileText = (LPSTR)::GlobalAlloc(GPTR, dwFileSize + 1);
			if(pszFileText != NULL)
			{
				DWORD dwRead;
				if(::ReadFile(hFile, pszFileText, dwFileSize, &dwRead, NULL))
				{
					string temp = pszFileText;
					pszFileText[dwFileSize] = 0;
					if (guessEnc)
					{
						preferredEncoding = guessEncoding(temp, preferredEncoding);
						setText(0, 0, toUnicode(temp, preferredEncoding).c_str());
					}
					else
					{
						setText(0, 0, toUnicode(temp, preferredEncoding).c_str()); 
					}
					bSuccess = true;
				}
				wstring test = getText();
				::GlobalFree(pszFileText);
			}
		}
		::CloseHandle(hFile);
	}
	return bSuccess;
}
void CRichEdit::updateCursor()
{
}
wstring CRichEdit::status()
{
	return L"";
}
bool CRichEdit::appendFile(const wstring& fileName, int encoding)
{
	HANDLE hFile;
	bool bSuccess = false;

	hFile = ::CreateFile(fileName.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL,
		OPEN_EXISTING, 0, NULL);
	if(hFile != INVALID_HANDLE_VALUE)
	{
		DWORD dwFileSize;

		dwFileSize = ::GetFileSize(hFile, NULL);
		if(dwFileSize != 0xFFFFFFFF)
		{
			LPSTR pszFileText;

			pszFileText = (LPSTR)::GlobalAlloc(GPTR, dwFileSize + 1);
			if(pszFileText != NULL)
			{
				DWORD dwRead;

				if(::ReadFile(hFile, pszFileText, dwFileSize, &dwRead, NULL))
				{
					pszFileText[dwFileSize] = 0;
					string temp = pszFileText;
					wstring uStr = toUnicode(temp, guessEncoding(temp, encoding));
					setText(getTextLength(), 0, uStr);
					bSuccess = true;
				}
				::GlobalFree(pszFileText);
			}
		}
		::CloseHandle(hFile);
	}
	return bSuccess;
}
bool CRichEdit::saveFile(const wstring& fileName, int encoding)
{
	HANDLE hFile;
	bool bSuccess = false;

	hFile = ::CreateFile(fileName.c_str(), GENERIC_WRITE, 0, NULL,
		CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if(hFile != INVALID_HANDLE_VALUE)
	{
		size_t textLength = getTextLength();
		if(textLength > 0)
		{
			{
				DWORD dwWritten;
				string s = fromUnicode(getText(), encoding);
				if(::WriteFile(hFile, s.c_str(), (DWORD)s.length(), &dwWritten, NULL))
					bSuccess = true;
			}
		}
		else bSuccess = true;
		::CloseHandle(hFile);
	}
	return bSuccess;
}
void CRichEdit::focus()
{
	::SetFocus(hEdit);
}
void CRichEdit::setReadOnly(bool b)
{
	if (b)
	{
		::SendMessage(hEdit, EM_SETOPTIONS, ECOOP_OR, ECO_READONLY);
		::ShowScrollBar(hEdit, SB_BOTH, false);
	}
	else
	{
		::SendMessage(hEdit, EM_SETOPTIONS, ECOOP_AND, ~ECO_READONLY);
	}
}
void CRichEdit::saveFormattedDocument(const wstring& fileName)
{
	setTextDefaultBackground(0, getTextLength());
	setBackground(rgb(255,255,255));

	HANDLE hFile;
	EDITSTREAM es;
	hFile = ::CreateFile(fileName.c_str(), GENERIC_WRITE, 0,NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL);
	if (hFile != INVALID_HANDLE_VALUE)
	{
#ifdef _MSVC98_
		es.dwCookie = (DWORD)hFile;
#else
		es.dwCookie = (DWORD_PTR)hFile;
#endif
		es.dwError = 0;
		es.pfnCallback = SaveRTFCallback;
		::SendMessage(hEdit, EM_STREAMOUT, (WPARAM)SF_RTF, (LPARAM)&es);
	}
	::CloseHandle(hFile);

	setBackground(::GetSysColor(COLOR_WINDOW));
	setTextBackground(0, getTextLength(), ::GetSysColor(COLOR_WINDOW));
}
void CRichEdit::beginPrint()
{
	getSelection(&selRange);
}
void CRichEdit::endPrint()
{
	setSelection(selRange);
}
void CRichEdit::printFormattedDialog()
{
	::printRTFDlg(hWnd, this);
}
void CRichEdit::setWrapping(bool on)
{
	LPARAM lp = on ? 0 : 1;
	::SendMessage(hEdit, EM_SETTARGETDEVICE, (WPARAM)0, lp);
}
size_t CRichEdit::lineFromChar(size_t charIndex)
{
	return (size_t)::SendMessage(hEdit, EM_EXLINEFROMCHAR, (WPARAM)0, (LPARAM)charIndex);
}
size_t CRichEdit::lineFirstChar(size_t lineIndex)
{
	return (size_t)::SendMessage(hEdit, EM_LINEINDEX, lineIndex, 0);
}
void CRichEdit::setFont(CFont* font)
{
	::SendMessage(hEdit, WM_SETFONT, (WPARAM)font->hf, (LPARAM)TRUE);
}
void CRichEdit::move(int left, int top, int width, int height)
{
	::MoveWindow(hWnd, left, top, width, height, TRUE);
	::MoveWindow(hEdit, 0, 0, width, height, TRUE);
}

#define BUFFERSIZE 32768
CProcess::CProcess()
{
	::memset(&pi,0,sizeof(PROCESS_INFORMATION));
	::memset(&startInfo,0,sizeof(STARTUPINFO));
	exitCode = 0;
	buffer = new char[BUFFERSIZE];
}
CProcess::~CProcess()
{
	writeToPipe("\015\012");
	Sleep(100);
	delete[] buffer;
}
bool CProcess::start(const wstring& commandLine)
{
	::memset(&pi,0,sizeof(PROCESS_INFORMATION));
	::memset(&startInfo,0,sizeof(STARTUPINFO));
	exitCode = 0;

	SECURITY_ATTRIBUTES saAttr;
	saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
	saAttr.bInheritHandle = TRUE;
	saAttr.lpSecurityDescriptor = NULL;

	if (! ::CreatePipe(&hStdoutRd, &hStdoutWr, &saAttr, 8192))
		return false;

	if (! ::CreatePipe(&hStdinRd, &hStdinWr, &saAttr, 8192))
		return false;

	startInfo.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
	startInfo.wShowWindow = SW_SHOW;
	startInfo.hStdOutput = hStdoutWr;
	startInfo.hStdError = hStdoutWr;
	startInfo.hStdInput = hStdinRd;

	wstring appDirPath = applicationDirectoryPath();

	return ::CreateProcess(NULL,LPWSTR(commandLine.c_str()),NULL,NULL,TRUE,
		CREATE_NEW_PROCESS_GROUP|NORMAL_PRIORITY_CLASS,NULL,LPWSTR(appDirPath.c_str()),&startInfo, &pi
		) ? true : false;
}
bool CProcess::isRunning()
{
	if (pi.hProcess) ::GetExitCodeProcess(pi.hProcess, &exitCode);
	else exitCode = 0;
	return exitCode == STILL_ACTIVE;
}
bool CProcess::interrupt()
{
	if (!isRunning()) return true;
	if (::GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pi.dwProcessId))
	{
		Sleep(100);
		writeToPipe("\015\012");
		return true;
	}
	return false;
}
void CProcess::stop()
{
	if (pi.hProcess)
	{
		::GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, pi.dwProcessId);
		Sleep(100);
		writeToPipe("\015\012");
		Sleep(100);
		::TerminateProcess(pi.hProcess, 0);
		::memset(&pi,0,sizeof(PROCESS_INFORMATION));
		::memset(&startInfo,0,sizeof(STARTUPINFO));
		exitCode = 0;		
	}
}
int CProcess::writeToPipe(const char *data)
{
	DWORD count;
	if (! ::WriteFile(hStdinWr, data, (DWORD)strlen(data), &count, NULL))
		return 0;
	return (int)count;
}
int CProcess::readFromPipe(char *data,int len)
{
	DWORD count;
	::PeekNamedPipe(hStdoutRd,data,len,NULL,&count,NULL);
	if (count == 0)
		return 0;
	if (! ::ReadFile(hStdoutRd, data, len, &count, NULL) || count == 0)
		return 0;
	return (int)count;
}
string CProcess::read()
{
	int count;
	while ((count = readFromPipe(buffer, BUFFERSIZE - 1))== 0);
	buffer[count] = 0;
	string st(buffer);
	return st;
}
string CProcess::readAllStandardOutput()
{
	bA.erase();
	for(;;)
	{
		DWORD count = readFromPipe(buffer, BUFFERSIZE - 1);
		if (count == 0) break;
		buffer[count] ='\0';
		bA.append(buffer);
	}
	return bA;
}
void CProcess::write(const char *data)
{
	writeToPipe(data);
}
