// XTPImageEditor.cpp : implementation of the CXTPImageEditorDlg class.
//
// This file is a part of the XTREME COMMANDBARS MFC class library.
// (c)1998-2012 Codejock Software, All Rights Reserved.
//
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
// CONSENT OF CODEJOCK SOFTWARE.
//
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
// IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
// SINGLE COMPUTER.
//
// CONTACT INFORMATION:
// support@codejock.com
// http://www.codejock.com
//
/////////////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Resource.h"
#include "Common/Resource.h"

#include "Common/XTPVC80Helpers.h"
#include "Common/XTPResourceManager.h"
#include "Common/XTPImageManager.h"
#include "Common/XTPDrawHelpers.h"
#include "Common/XTPColorManager.h"
#include "Common/XTPSystemHelpers.h"
#include "Common/XTPHookManager.h"

#include "XTPCommandBarsDefines.h"
#include "XTPCommandBar.h"
#include "XTPToolBar.h"
#include "XTPImageEditor.h"
#include "XTPMouseManager.h"
#include "XTPPaintManager.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


#define UNDO_COUNT 15
#define CLR_TO_RGBQUAD(clr) (RGB(GetBValue(clr), GetGValue(clr), GetRValue(clr)))

void CXTPImageEditorPicture::CAlphaBitmap::CreateEditorBitmap(int nWidth, int nHeight)
{
	Attach(CXTPImageManager::Create32BPPDIBSection(0, nWidth, nHeight));
}

/////////////////////////////////////////////////////////////////////////////
// CXTPImageEditorPicture


CXTPImageEditorPicture::CXTPImageEditorPicture()
{
	m_pCurrentBitmap = NULL;
	m_pPictureBitmap = NULL;

	m_pParentWnd = NULL;

	m_bTracked = FALSE;

	m_clrDraw = 0;

	m_hCursorLine = XTPResourceManager()->LoadCursor(XTP_IDC_COMMANDBARS_LINE);
	m_hCursorFillColor = XTPResourceManager()->LoadCursor(XTP_IDC_COMMANDBARS_FILLCOLOR);
	m_hCursorPencil = XTPResourceManager()->LoadCursor(XTP_IDC_COMMANDBARS_PENCIL);
	m_hCursorPickColor = XTPResourceManager()->LoadCursor(XTP_IDC_COMMANDBARS_PICKCOLOR);

}

CXTPImageEditorPicture::~CXTPImageEditorPicture()
{
	if (m_pPictureBitmap)
	{
		delete m_pPictureBitmap;
	}
	if (m_pCurrentBitmap)
	{
		delete m_pCurrentBitmap;
	}

	while (!m_lstUndo.IsEmpty())
	{
		CBitmap* pBitmap = m_lstUndo.RemoveTail();
		if (pBitmap) delete pBitmap;
	}

	while (!m_lstRedo.IsEmpty())
	{
		CBitmap* pBitmap = m_lstRedo.RemoveTail();
		if (pBitmap) delete pBitmap;
	}

}

void CXTPImageEditorPicture::Init(UINT nID, CXTPImageEditorDlg* pParentWnd)
{
	m_pParentWnd = pParentWnd;

	SubclassDlgItem(nID, pParentWnd);
	m_szPicture = pParentWnd->m_szPicture;

	CRect rc;
	GetWindowRect(rc);

	m_szItem = CSize (rc.Width() / m_szPicture.cx, rc.Height() / m_szPicture.cy);

	CSize sz(m_szItem.cx * m_szPicture.cx, m_szItem.cy * m_szPicture.cy);

	SetWindowPos(&CWnd::wndTop, rc.left, rc.top, sz.cx + 1, sz.cy + 1, SWP_NOMOVE);


	m_dcPicture.CreateCompatibleDC(NULL);
}

XTPImageEditorTools CXTPImageEditorPicture::GetSelectedTool()
{
	ASSERT(m_pParentWnd);
	if (!m_pParentWnd)
		return xtpToolPencil;

	return m_pParentWnd->m_toolSelected;
}
COLORREF CXTPImageEditorPicture::GetSelectedColor()
{
	ASSERT(m_pParentWnd);
	if (m_pParentWnd && m_pParentWnd->m_pSelected)
	{
		return m_pParentWnd->m_pSelected->GetColor();
	}
	return 0;
}

BEGIN_MESSAGE_MAP(CXTPImageEditorPicture, CStatic)
	//{{AFX_MSG_MAP(CXTPImageEditorPicture)
	ON_WM_PAINT()
	ON_WM_LBUTTONDOWN()
	ON_WM_MOUSEMOVE()
	ON_WM_CAPTURECHANGED()
	ON_WM_LBUTTONUP()
	ON_WM_SETCURSOR()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CXTPImageEditorPicture message handlers

BOOL CXTPImageEditorPicture::OnSetCursor(CWnd* /*pWnd*/, UINT /*nHitTest*/, UINT /*message*/)
{
	::SetCursor(
		GetSelectedTool() == xtpToolFill ? m_hCursorFillColor :
		GetSelectedTool() == xtpToolPencil ? m_hCursorPencil :
		GetSelectedTool() == xtpToolGetColor ? m_hCursorPickColor : m_hCursorLine
	);

	return TRUE;
}

void CXTPImageEditorPicture::OnPaint()
{
	CPaintDC dcPaint(this); // device context for painting
	CXTPClientRect rc(this);
	CXTPBufferDC dc(dcPaint, rc);

	dc.FillSolidRect(rc, RGB(255, 255, 255));

	BOOL bAlpha = m_pPictureBitmap != 0 && m_pPictureBitmap->m_bAlpha;

	PINT pBits = 0;
	PBITMAPINFO pbmi = 0;
	UINT nSize;

	if (m_pPictureBitmap)
	{
		if (!CXTPImageManagerIcon::GetBitmapBits(dc, (HBITMAP)m_pPictureBitmap->GetSafeHandle(), pbmi, (LPVOID&)pBits, nSize))
			return;
	}
	if (pbmi == NULL || pBits == NULL)
	{
		return;
	}

	for (int x = 0; x < rc.Width(); x++)
	for (int y = 0; y < rc.Height(); y++)
	{
		CPoint pt = CPoint(m_szPicture.cx * x / rc.Width(), m_szPicture.cy * y / rc.Height());

		COLORREF clr;
		if (bAlpha)
		{
			clr = pBits[(pt.y * pbmi->bmiHeader.biWidth + pt.x)];

			int nAlpha = (clr & 0xFF000000) >> 24;

			int nColor = (((x +3) / 4)+ ((y +3)/ 4)) % 2 == 0 ? 255 : 235;

			clr = RGB(nColor - (nColor -  GetBValue(clr)) * nAlpha / 255,
					nColor -  (nColor - GetGValue(clr)) * nAlpha / 255,
					nColor -  (nColor - GetRValue(clr)) * nAlpha / 255);


		}
		else
		{

			clr = m_pPictureBitmap == NULL ? XTPCOLOR_ERASE : pBits[(pt.y * pbmi->bmiHeader.biWidth + pt.x)];
			clr = CLR_TO_RGBQUAD(clr);

			if (clr == XTPCOLOR_ERASE)
				clr = (((x +3) / 4)+ ((y +3)/ 4)) % 2 == 0 ? RGB(255, 255, 255) : RGB(235, 235, 235);


		}
		dc.SetPixel(x, rc.Height() - y - 1, clr);
	}

	free(pBits);
	free(pbmi);

	dc.Draw3dRect(rc, 0, 0);
}

COLORREF CXTPImageEditorPicture::GetPicturePixel(CPoint point)
{
	CAlphaBitmap bmp;
	bmp.CreateEditorBitmap(m_szPicture.cx, m_szPicture.cy);

	CBitmap* pOldBitmap = m_dcPicture.SelectObject(&bmp);
	m_dcPicture.FillSolidRect(0, 0, m_szPicture.cx, m_szPicture.cy, XTPCOLOR_ERASE);
	if (m_pPictureBitmap)
		m_dcPicture.DrawState(0, m_szPicture, m_pPictureBitmap, DSS_NORMAL, (CBrush*)NULL);
	COLORREF clr = m_dcPicture.GetPixel(point);


	m_dcPicture.SelectObject(pOldBitmap);
	return clr;
}



CPoint CXTPImageEditorPicture::ClientToPicture(CPoint pt)
{
	ASSERT(m_szItem.cx > 0 && m_szItem.cy > 0);
	return CPoint(pt.x / m_szItem.cx, pt.y / m_szItem.cy);
}

void CXTPImageEditorPicture::CopyBitmap(CAlphaBitmap* pBitmapCopyTo, CAlphaBitmap* pBitmapCopyFrom, CSize sz, CPoint ptOffset, COLORREF clrTransparentFrom, COLORREF clrTransparentTo, CDC* pDC)
{
	CDC dc;
	dc.CreateCompatibleDC(pDC);

	pBitmapCopyTo->CreateEditorBitmap(sz.cx, sz.cy);

	CBitmap* pOldBitmap = dc.SelectObject(pBitmapCopyTo);
	dc.FillSolidRect(0, 0, sz.cx, sz.cy, XTPCOLOR_ERASE);

	if (pBitmapCopyFrom)
	{
		dc.DrawState(ptOffset, sz, pBitmapCopyFrom, DSS_NORMAL, (CBrush*)NULL);

		if (clrTransparentFrom != clrTransparentTo && !pBitmapCopyFrom->m_bAlpha)
		{
			for (int x = 0; x < sz.cx; x++)
			{
				for (int y = 0; y < sz.cy; y++)
				{
					if (dc.GetPixel(x, y) == clrTransparentFrom)
						dc.SetPixel(x, y, clrTransparentTo);
				}
			}
		}
	}

	dc.SelectObject(pOldBitmap);

	pBitmapCopyTo->m_bAlpha = pBitmapCopyFrom ? pBitmapCopyFrom->m_bAlpha : FALSE;


}

void CXTPImageEditorPicture::OnPictureChanged()
{
	Invalidate(FALSE);
	m_pParentWnd->OnPictureChanged();
}

void CXTPImageEditorPicture::Apply()
{
	while (!m_lstRedo.IsEmpty())
	{
		CBitmap* pBitmap = m_lstRedo.RemoveTail();
		if (pBitmap) delete pBitmap;
	}

	if (m_lstUndo.GetCount() > UNDO_COUNT)
	{
		CBitmap* pBitmap = m_lstUndo.RemoveHead();
		if (pBitmap) delete pBitmap;
	}

	m_lstUndo.AddTail(m_pCurrentBitmap);
	m_pCurrentBitmap = NULL;

	OnPictureChanged();
}


void CXTPImageEditorPicture::OnLButtonDown(UINT nFlags, CPoint point)
{
	ASSERT(m_pCurrentBitmap == NULL);

	switch (GetSelectedTool())
	{
	case xtpToolFill:
		{
			m_pCurrentBitmap = m_pPictureBitmap;

			m_pPictureBitmap = new CAlphaBitmap();
			CopyBitmap(m_pPictureBitmap, m_pCurrentBitmap, m_szPicture);

			CBitmap* pOldBitmap = m_dcPicture.SelectObject(m_pPictureBitmap);
			CXTPBrushDC brush(m_dcPicture, GetSelectedColor());

			CPoint ptFill(ClientToPicture(point));

			m_dcPicture.ExtFloodFill(ptFill.x, ptFill.y, GetPicturePixel(ptFill), FLOODFILLSURFACE);
			m_dcPicture.SelectObject(pOldBitmap);

			if (m_pCurrentBitmap && m_pCurrentBitmap->m_bAlpha)
				FixAlphaLayer(m_pPictureBitmap, m_pCurrentBitmap);

			Apply();
		}
		break;

	case xtpToolGetColor:
		{
			COLORREF clr = GetPicturePixel(ClientToPicture(point));
			m_pParentWnd->m_wndPicker.SetColor(clr);
			m_pParentWnd->SetSelected(&m_pParentWnd->m_wndPicker);
			m_pParentWnd->m_toolSelected = m_pParentWnd->m_toolPrevious;
		}
		break;
	case xtpToolPencil:
	case xtpToolRectangle:
	case xtpToolCircle:
	case xtpToolLine:
		{

			m_pCurrentBitmap = m_pPictureBitmap;

			m_pPictureBitmap = new CAlphaBitmap();
			CopyBitmap(m_pPictureBitmap, m_pCurrentBitmap, m_szPicture);

			m_clrDraw = GetSelectedColor() != GetPicturePixel(ClientToPicture(point)) ? GetSelectedColor() : XTPCOLOR_ERASE;

			m_bTracked = TRUE;
			m_ptTracked = point;
			SetCapture();

			OnMouseMove(nFlags, point);
		}
		break;
	default :
		ASSERT(FALSE);

	}

	CStatic::OnLButtonDown(nFlags, point);
}


void CXTPImageEditorDlg::FixAlphaLayer(LPBYTE pBits, UINT nCount)
{
	if (pBits == 0)
		return;

	LPBYTE pBitsI = pBits;

	for (UINT i = 0; i < nCount; i++)
	{
		if (*(LPDWORD)pBitsI == 0) *(LPDWORD)pBitsI = XTPCOLOR_ERASE;

		int nAlpha = pBitsI[3];
		if (nAlpha != 0)
		{
			pBitsI[0] = BYTE(pBitsI[0] * 255 / nAlpha);
			pBitsI[1] = BYTE(pBitsI[1] * 255 / nAlpha);
			pBitsI[2] = BYTE(pBitsI[2] * 255 / nAlpha);
		}

		pBitsI += 4;
	}
}

void CXTPImageEditorPicture::FixAlphaLayer(CAlphaBitmap* pDest, CAlphaBitmap* pSrc)
{
	if (pSrc == 0 || pDest == 0)
		return;

	CDC dcSrc;
	dcSrc.CreateCompatibleDC(NULL);

	PINT pDestBits = 0, pSrcBits = 0;
	PBITMAPINFO pDestBmi = 0, pSrcBmi = 0;

	UINT nSize;

	if (!CXTPImageManagerIcon::GetBitmapBits(dcSrc, (HBITMAP)pDest->GetSafeHandle(), pDestBmi, (LPVOID&)pDestBits, nSize))
		return;

	if (!CXTPImageManagerIcon::GetBitmapBits(dcSrc, (HBITMAP)pSrc->GetSafeHandle(), pSrcBmi, (LPVOID&)pSrcBits, nSize))
	{
		free(pDestBits);
		free(pDestBmi);
		return;
	}

	PINT pFixedBits = NULL;

	HBITMAP hBitmapAlpha = CreateDIBSection(dcSrc, pDestBmi, DIB_RGB_COLORS, (void**)&pFixedBits, NULL, 0);

	if (hBitmapAlpha && pFixedBits)
	{
		MEMCPY_S(pFixedBits, pDestBits, nSize);

		for (UINT i = 0; i < nSize / 4; i++)
		{
			if (pDestBits[i] != pSrcBits[i])
			{
				if (pDestBits[i] != XTPCOLOR_ERASE)
					pFixedBits[i] |= 0xFF000000;
			}
		}

		pDest->DeleteObject();
		pDest->Attach(hBitmapAlpha);
	}

	free(pDestBits);
	free(pDestBmi);
	free(pSrcBits);
	free(pSrcBmi);

}

void CXTPImageEditorPicture::OnMouseMove(UINT nFlags, CPoint point)
{
	if (m_bTracked)
	{
		if (GetSelectedTool() == xtpToolPencil)
		{
			CAlphaBitmap* pBitmap = new CAlphaBitmap();
			CopyBitmap(pBitmap, m_pPictureBitmap, m_szPicture);

			CBitmap* pOldBitmap = m_dcPicture.SelectObject(pBitmap);

			m_dcPicture.SetPixel(ClientToPicture(point), m_clrDraw);
			m_dcPicture.SelectObject(pOldBitmap);

			if (m_pPictureBitmap->m_bAlpha)
				FixAlphaLayer(pBitmap, m_pPictureBitmap);

			if (m_pPictureBitmap) delete m_pPictureBitmap;

			m_pPictureBitmap = pBitmap;

			Invalidate(FALSE);
			OnPictureChanged();
		}
		else
		{
			CAlphaBitmap* pBitmap = new CAlphaBitmap();
			CopyBitmap(pBitmap, m_pCurrentBitmap, m_szPicture);

			CBitmap* pOldBitmap = m_dcPicture.SelectObject(pBitmap);

			CRect rcReftangle(ClientToPicture(m_ptTracked), ClientToPicture(point));
			rcReftangle.NormalizeRect();

			if (GetSelectedTool() == xtpToolRectangle)
			{
				m_dcPicture.Draw3dRect(rcReftangle, GetSelectedColor(), GetSelectedColor());
			}
			if (GetSelectedTool() == xtpToolCircle)
			{
				CXTPPenDC pen(m_dcPicture, GetSelectedColor());
				CXTPBrushDC brush(m_dcPicture, XTPCOLOR_ERASE);
				m_dcPicture.Ellipse(rcReftangle);
			}
			if (GetSelectedTool() == xtpToolLine)
			{
				CXTPPenDC pen(m_dcPicture, GetSelectedColor());
				m_dcPicture.MoveTo(ClientToPicture(m_ptTracked));
				m_dcPicture.LineTo(ClientToPicture(point));
			}

			m_dcPicture.SelectObject(pOldBitmap);

			if (m_pPictureBitmap->m_bAlpha)
				FixAlphaLayer(pBitmap, m_pCurrentBitmap);

			if (m_pPictureBitmap) delete m_pPictureBitmap;
			m_pPictureBitmap = pBitmap;

			Invalidate(FALSE);
			OnPictureChanged();

		}

	}

	CStatic::OnMouseMove(nFlags, point);
}

void CXTPImageEditorPicture::OnLButtonUp(UINT nFlags, CPoint point)
{
	m_bTracked = FALSE;
	ReleaseCapture();

	CStatic::OnLButtonUp(nFlags, point);
}

void CXTPImageEditorPicture::OnCaptureChanged(CWnd* pWnd)
{
	m_bTracked = FALSE;
	Apply();

	CStatic::OnCaptureChanged(pWnd);
}

void CXTPImageEditorPicture::Undo()
{
	if (!m_lstUndo.IsEmpty())
	{
		CAlphaBitmap* pBitmap = m_lstUndo.RemoveTail();

		m_lstRedo.AddTail(m_pPictureBitmap);

		m_pPictureBitmap = pBitmap;

		OnPictureChanged();
	}
}

void CXTPImageEditorPicture::Redo()
{
	if (!m_lstRedo.IsEmpty())
	{
		CAlphaBitmap* pBitmap = m_lstRedo.RemoveTail();

		m_lstUndo.AddTail(m_pPictureBitmap);

		m_pPictureBitmap = pBitmap;

		OnPictureChanged();
	}
}


void CXTPImageEditorPicture::Clear()
{
	m_pCurrentBitmap = m_pPictureBitmap;
	m_pPictureBitmap = NULL;
	Apply();
}

void CXTPImageEditorPicture::MovePicture(CPoint ptOffset)
{
	m_pCurrentBitmap = m_pPictureBitmap;

	m_pPictureBitmap = new CAlphaBitmap();
	CopyBitmap(m_pPictureBitmap, m_pCurrentBitmap, m_szPicture, ptOffset);

	Apply();
}


void CXTPImageEditorPicture::Copy()
{
	if (!OpenClipboard ())
	{
		return;
	}

	if (!::EmptyClipboard ())
	{
		::CloseClipboard ();
		return;
	}

	CAlphaBitmap bitmapCopy;
	CopyBitmap(&bitmapCopy, m_pPictureBitmap, m_szPicture, 0, XTPCOLOR_ERASE, GetXtremeColor(COLOR_3DFACE));

	HANDLE hclipData = ::SetClipboardData (CF_BITMAP, bitmapCopy.Detach ());
	if (hclipData == NULL)
	{
		TRACE (_T ("CXTPImageEditorPicture::Copy() error. Error code = %x\n"), GetLastError ());
	}

	if (bitmapCopy.m_bAlpha)
	{
		SetClipboardData(XTPImageManager()->m_nAlphaClipFormat, GlobalAlloc(GMEM_MOVEABLE, 1));
	}

	::CloseClipboard ();
}

void CXTPImageEditorPicture::Paste()
{
	COleDataObject data;
	if (!data.AttachClipboard ())
	{
		return;
	}
	if (!data.IsDataAvailable (CF_BITMAP))
	{
		return;
	}

	tagSTGMEDIUM dataMedium;
	if (!data.GetData (CF_BITMAP, &dataMedium))
	{
		return;
	}

	if (!dataMedium.hBitmap)
		return;

	CAlphaBitmap bmpClip;

	BOOL bAlpha = FALSE;
	FORMATETC fetc;
	static TCHAR sz[256] ;

	data.BeginEnumFormats();
	while (data.GetNextFormat(&fetc))
	{
		if (GetClipboardFormatName((UINT)fetc.cfFormat, sz, 254))
		{
			CString str(sz);
			if (str.Find(_T("DIB32")) > 0)
			{
				bAlpha = TRUE;
				break;
			}
		}
	}

	bmpClip.m_bAlpha = bAlpha ? bAlpha : data.IsDataAvailable(XTPImageManager()->m_nAlphaClipFormat);
	bmpClip.Attach(dataMedium.hBitmap);


	m_pCurrentBitmap = m_pPictureBitmap;

	m_pPictureBitmap = new CAlphaBitmap();
	CopyBitmap(m_pPictureBitmap, &bmpClip, m_szPicture, 0, GetXtremeColor(COLOR_3DFACE), XTPCOLOR_ERASE);

	bmpClip.Detach();

	Apply();

}


void CXTPImageEditorPicture::Load()
{
	CString strFilter;
	CXTPResourceManager::AssertValid(XTPResourceManager()->LoadString(&strFilter, XTP_IDS_IMAGEEDITOR_FILTER));
	CFileDialog fd(TRUE, NULL, NULL, OFN_HIDEREADONLY, strFilter);

	if (fd.DoModal() == IDOK)
	{

		CString strExtension = fd.GetFileExt();
		strExtension.MakeLower();

		if (strExtension == _T("ico"))
		{

			HBITMAP hBitmap = CXTPImageManagerIcon::LoadAlphaIcon(fd.GetPathName(), m_szPicture.cx);

			if (!hBitmap)
			{
				HICON hIcon = (HICON)LoadImage(0, fd.GetPathName(),
					IMAGE_ICON, m_szPicture.cx, m_szPicture.cy, LR_CREATEDIBSECTION | LR_LOADFROMFILE);

				if (!hIcon)
					return;

				m_pCurrentBitmap = m_pPictureBitmap;
				m_pPictureBitmap = new CAlphaBitmap();

				CDC dc;
				dc.CreateCompatibleDC(NULL);

				m_pPictureBitmap->CreateEditorBitmap(m_szPicture.cx, m_szPicture.cy);

				CBitmap* pOldBitmap = dc.SelectObject(m_pPictureBitmap);
				dc.FillSolidRect(0, 0, m_szPicture.cx, m_szPicture.cy, XTPCOLOR_ERASE);

				dc.DrawState(0, CSize(0, 0), hIcon, DSS_NORMAL, (CBrush*)NULL);

				dc.SelectObject(pOldBitmap);

				DestroyIcon(hIcon);
			}
			else
			{

				m_pCurrentBitmap = m_pPictureBitmap;
				m_pPictureBitmap = new CAlphaBitmap();


				CAlphaBitmap bmpFile(TRUE);
				bmpFile.Attach(hBitmap);

				CopyBitmap(m_pPictureBitmap, &bmpFile, m_szPicture);
			}

			Apply();


		}
		else if (strExtension == _T("bmp") || strExtension == _T("png"))
		{

			BOOL bAlphaBitmap = FALSE;

			HBITMAP hBmp = CXTPImageManagerIcon::LoadBitmapFromFile(fd.GetPathName(), &bAlphaBitmap);

			if (!hBmp)
				return;

			CAlphaBitmap bmpFile(bAlphaBitmap);
			bmpFile.Attach(hBmp);

			m_pCurrentBitmap = m_pPictureBitmap;
			m_pPictureBitmap = new CAlphaBitmap();

			CopyBitmap(m_pPictureBitmap, &bmpFile, m_szPicture);

			Apply();
		}
		else
		{
			LPPICTURE pPict = NULL;

			if (OleLoadPicturePath((LPOLESTR)XTP_CT2CW(fd.GetPathName()), NULL, 0, 0, IID_IPicture, (LPVOID*)&pPict) == S_OK)
			{

				m_pCurrentBitmap = m_pPictureBitmap;
				m_pPictureBitmap = new CAlphaBitmap();

				CDC dc;
				dc.CreateCompatibleDC(NULL);

				m_pPictureBitmap->CreateEditorBitmap(m_szPicture.cx, m_szPicture.cy);

				CBitmap* pOldBitmap = dc.SelectObject(m_pPictureBitmap);
				dc.FillSolidRect(0, 0, m_szPicture.cx, m_szPicture.cy, XTPCOLOR_ERASE);

				long hmWidth;
				long hmHeight;

				pPict->get_Width(&hmWidth);
				pPict->get_Height(&hmHeight);

				pPict->Render(dc, 0, 0,
					m_szPicture.cx, m_szPicture.cy, 0, hmHeight-1,
					hmWidth, -hmHeight, 0);

				dc.SelectObject(pOldBitmap);

				Apply();

				pPict->Release();

			}


		}
	}

}


/////////////////////////////////////////////////////////////////////////////
// CXTPImageEditorPicker

IMPLEMENT_DYNAMIC(CXTPImageEditorPicker, CStatic)

CXTPImageEditorPicker::CXTPImageEditorPicker()
{
	m_clr = 0;
	m_bSelected = FALSE;
}


void CXTPImageEditorPicker::SetColor(COLORREF clr)
{
	m_clr = clr;
	if (m_hWnd) Invalidate(FALSE);
}
COLORREF CXTPImageEditorPicker::GetColor()
{
	return m_clr;
}

void CXTPImageEditorPicker::SetSelected(BOOL bSelected)
{
	m_bSelected = bSelected;
	if (m_hWnd) Invalidate(FALSE);
}


BEGIN_MESSAGE_MAP(CXTPImageEditorPicker, CStatic)
	//{{AFX_MSG_MAP(CXTPImageEditorPicker)
	ON_WM_PAINT()
	ON_WM_LBUTTONDOWN()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CXTPImageEditorPicker message handlers


void CXTPImageEditorPicker::OnPaint()
{
	CPaintDC dcPaint(this); // device context for painting
	CXTPClientRect rc(this);
	CXTPBufferDC dc(dcPaint, rc);

	if (m_clr != XTPCOLOR_ERASE)
	{
		dc.FillSolidRect(rc, m_clr);
	}
	else
	{

		CRect rcItem(rc);
		dc.FillSolidRect(rc, RGB(0xFF, 0xFF, 0xFF));
		dc.FillSolidRect(rcItem.left, rcItem.top, rcItem.Width() / 2, rcItem.Height() / 2, RGB(235, 235, 235));
		dc.FillSolidRect(rcItem.left + rcItem.Width() / 2, rcItem.top + rcItem.Height() / 2,
			rcItem.Width() - (rcItem.Width() / 2), rcItem.Height() - (rcItem.Height() / 2), RGB(235, 235, 235));

	}
	if (!m_bSelected)
	{
		dc.Draw3dRect(rc, GetXtremeColor(COLOR_3DDKSHADOW), GetXtremeColor(COLOR_3DDKSHADOW));
	}
	else
	{
		dc.Draw3dRect(rc, 0, 0);
		rc.DeflateRect(1, 1);
		dc.Draw3dRect(rc, RGB(0xFF, 0xFF, 0xFF), RGB(0xFF, 0xFF, 0xFF));
		rc.DeflateRect(1, 1);
		dc.Draw3dRect(rc, 0, 0);
	}

}

void CXTPImageEditorPicker::OnLButtonDown(UINT /*nFlags*/, CPoint /*point*/)
{
	NMHDR mHDR;
	mHDR.hwndFrom = m_hWnd;
	mHDR.code = NM_CLICK;
	mHDR.idFrom = GetDlgCtrlID();

	GetParent()->SendMessage(WM_NOTIFY,
		(WPARAM)mHDR.idFrom, (LPARAM)&mHDR);
}

/////////////////////////////////////////////////////////////////////////////
// CXTPImageEditorPreview

CXTPImageEditorPreview::CXTPImageEditorPreview()
{
	m_pParentWnd = 0;
}

void CXTPImageEditorPreview::Init(UINT nID, CXTPImageEditorDlg* pParentWnd)
{
	m_pParentWnd = pParentWnd;

	SubclassDlgItem(nID, pParentWnd);
	m_szPicture = pParentWnd->m_szPicture;

	ModifyStyleEx(WS_EX_STATICEDGE, 0);

}


BEGIN_MESSAGE_MAP(CXTPImageEditorPreview, CStatic)
	//{{AFX_MSG_MAP(CXTPImageEditorPreview)
	ON_WM_PAINT()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CXTPImageEditorPreview message handlers

void CXTPImageEditorPreview::OnPaint()
{
	CPaintDC dcPaint(this); // device context for painting
	CXTPClientRect rc(this);
	CXTPBufferDC dc(dcPaint, rc);
	dc.FillSolidRect(rc, GetXtremeColor(COLOR_3DFACE));


	CRect rcButton (10, (rc.Height() - m_szPicture.cy) / 2, 10 + m_szPicture.cx, (rc.Height() - m_szPicture.cy) / 2 + m_szPicture.cy);
	CRect rcFrame(rcButton);
	rcFrame.InflateRect(3, 3);

	dc.Draw3dRect(rcFrame, RGB(0xFF, 0xFF, 0xFF), GetXtremeColor(COLOR_3DSHADOW));
	rcFrame.InflateRect(1, 1);
	dc.Draw3dRect(rcFrame, GetXtremeColor(COLOR_3DFACE), GetXtremeColor(COLOR_3DDKSHADOW));

	ASSERT(m_pParentWnd);
	if (!m_pParentWnd)
		return;
	CXTPImageEditorPicture::CAlphaBitmap* pBitmap = m_pParentWnd->m_wndPicture.m_pPictureBitmap;

	if (pBitmap)
	{
		if (pBitmap->m_bAlpha)
		{
			HBITMAP hBitmap = CXTPImageManagerIcon::PreMultiplyAlphaBitmap((HBITMAP)pBitmap->GetSafeHandle());
			if (hBitmap)
			{
				CXTPImageManagerIcon::DrawAlphaBitmap(&dc, hBitmap, rcButton.TopLeft(), m_szPicture);

				DeleteObject(hBitmap);
			}
		}
		else
		{
			CXTPImageEditorPicture::CAlphaBitmap bmpClient;
			CXTPImageEditorPicture::CopyBitmap(&bmpClient, pBitmap, m_szPicture, 0, XTPCOLOR_ERASE, GetXtremeColor(COLOR_3DFACE), &dc);

			dc.DrawState(rcButton.TopLeft(), m_szPicture, &bmpClient, DSS_NORMAL, (CBrush*)NULL);
		}
	}
}


/////////////////////////////////////////////////////////////////////////////
// CXTPImageEditorDlg dialog

CXTPImageEditorDlg::CXTPImageEditorDlg(CWnd* pParent /*= NULL*/, BOOL bCommandBarsEditor)
	: m_bCommandBarsEditor(bCommandBarsEditor)
{
	InitModalIndirect(XTPResourceManager()->LoadDialogTemplate(XTP_IDD_IMAGEEDITOR_DIALOG), pParent);

	m_toolSelected = xtpToolPencil;

	m_pSelected = NULL;
	m_szPicture = CSize(16, 16);
	m_toolPrevious = XTPImageEditorTools(-1);
}

void CXTPImageEditorDlg::GetIcon(CXTPImageManagerIconHandle* pHandle)
{
	ASSERT(pHandle);
	if (!pHandle)
		return;

	if (m_wndPicture.m_pPictureBitmap)
	{
		if (m_wndPicture.m_pPictureBitmap->m_bAlpha)
		{
			pHandle->CopyHandle((HBITMAP)m_wndPicture.m_pPictureBitmap->GetSafeHandle());
		}
		else
		{

			CImageList imgList;
			imgList.Create(m_szPicture.cx, m_szPicture.cy, ILC_COLOR24 | ILC_MASK, 0, 1);
			imgList.Add(m_wndPicture.m_pPictureBitmap, XTPCOLOR_ERASE);

			if (imgList.GetImageCount() != 1)
				return;

			*pHandle = imgList.ExtractIcon(0);
		}
	}
}

void CXTPImageEditorDlg::SetIconSize(CSize szIcon)
{
	m_szPicture = szIcon;
}
void CXTPImageEditorDlg::SetIcon(CXTPImageManagerIconHandle& icon)
{
	if (icon.IsEmpty())
		return;

	ASSERT(m_wndPicture.m_pPictureBitmap == NULL);
	m_szPicture = icon.GetExtent();

	CXTPImageManagerIconHandle iconHandle;
	iconHandle.CopyHandle(icon);

	if (iconHandle.IsAlpha())
	{
		iconHandle.PreMultiply();

		CXTPImageEditorPicture::CAlphaBitmap* pBitmap = new CXTPImageEditorPicture::CAlphaBitmap(TRUE);

		LPBYTE lpBits;
		pBitmap->Attach(CXTPImageManagerIcon::CopyAlphaBitmap(iconHandle.GetBitmap(), &lpBits));

		FixAlphaLayer(lpBits, m_szPicture.cx * m_szPicture.cy);
		m_wndPicture.m_pPictureBitmap = pBitmap;

	}
	else
	{
		CXTPImageEditorPicture::CAlphaBitmap* pBitmap = new CXTPImageEditorPicture::CAlphaBitmap();

		CDC dc;
		dc.CreateCompatibleDC(NULL);

		pBitmap->CreateEditorBitmap(m_szPicture.cx, m_szPicture.cy);

		CBitmap* pOldBitmap = dc.SelectObject(pBitmap);
		dc.FillSolidRect(0, 0, m_szPicture.cx, m_szPicture.cy, XTPCOLOR_ERASE);

		dc.DrawState(0, CSize(0, 0), iconHandle.GetIcon(), DSS_NORMAL, (CBrush*)NULL);

		dc.SelectObject(pOldBitmap);

		m_wndPicture.m_pPictureBitmap = pBitmap;
	}

}

void CXTPImageEditorDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CXTPImageEditorDlg)
	DDX_Control(pDX, XTP_IDC_STATIC_TOOLS, m_gboxTools);
	DDX_Control(pDX, XTP_IDC_STATIC_MOVE, m_gboxMove);
	//}}AFX_DATA_MAP

}

BEGIN_MESSAGE_MAP(CXTPImageEditorDlg, CDialog)
	//{{AFX_MSG_MAP(CXTPImageEditorDlg)
	ON_MESSAGE(WM_KICKIDLE, OnKickIdle)
	ON_WM_DESTROY()

	ON_NOTIFY(NM_CLICK, XTP_IDC_CLR_PICKER, OnPickerClick)
	ON_NOTIFY_RANGE(NM_CLICK, XTP_IDS_CLR_BLACK, XTP_IDS_CLR_FUCHSIA, OnSelectorClick)
	ON_NOTIFY(NM_CLICK, XTP_IDC_CLR_ERASE, OnEraseClick)

	ON_UPDATE_COMMAND_UI_RANGE(XTP_ID_BUTTON_PENCIL, XTP_ID_BUTTON_CIRCLE, OnUpdateButtonTool)
	ON_COMMAND_RANGE(XTP_ID_BUTTON_PENCIL, XTP_ID_BUTTON_CIRCLE, OnButtonTool)

	ON_UPDATE_COMMAND_UI(XTP_ID_BUTTON_UNDO, OnUpdateButtonUndo)
	ON_COMMAND(XTP_ID_BUTTON_UNDO, OnButtonUndo)

	ON_UPDATE_COMMAND_UI(XTP_ID_BUTTON_REDO, OnUpdateButtonRedo)
	ON_COMMAND(XTP_ID_BUTTON_REDO, OnButtonRedo)

	ON_COMMAND(XTP_ID_BUTTON_CLEAR, OnButtonClear)
	ON_COMMAND(XTP_ID_BUTTON_COPY, OnButtonCopy)
	ON_COMMAND(XTP_ID_BUTTON_PASTE, OnButtonPaste)
	ON_COMMAND(XTP_ID_BUTTON_OPEN, OnButtonOpen)
	ON_COMMAND_RANGE(XTP_IDC_BUTTON_LEFT, XTP_IDC_BUTTON_RIGHT, OnMoveButton)
	ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTW, 0, 0xFFFF, OnToolTipText)
	ON_NOTIFY_EX_RANGE(TTN_NEEDTEXTA, 0, 0xFFFF, OnToolTipText)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CXTPImageEditorDlg message handlers

BOOL CXTPImageEditorDlg::OnInitDialog()
{
	UINT nButtons[] =
	{
		XTP_ID_BUTTON_PENCIL,
		XTP_ID_BUTTON_FILL,
		XTP_ID_BUTTON_UNDO,
		XTP_ID_BUTTON_REDO,
		XTP_ID_BUTTON_GETCOLOR,
		XTP_ID_BUTTON_LINE,
		XTP_ID_BUTTON_COPY,
		XTP_ID_BUTTON_PASTE,
		XTP_ID_BUTTON_RECTANGLE,
		XTP_ID_BUTTON_CIRCLE,
		XTP_ID_BUTTON_CLEAR,
		XTP_ID_BUTTON_OPEN,

		XTP_IDC_BUTTON_LEFT,
		XTP_IDC_BUTTON_DOWN,
		XTP_IDC_BUTTON_UP,
		XTP_IDC_BUTTON_RIGHT
	};

	UINT* nButtonsTools = nButtons;
	UINT* nButtonsMove = nButtons + 12;

	const struct
	{
		COLORREF clr;
		UINT nID;
		POINT ptOffset;
	}
	colors[] =
	{
		RGB(0x00, 0x00, 0x00), XTP_IDS_CLR_BLACK           , {0, 0},
		RGB(0x80, 0x00, 0x00), XTP_IDS_CLR_DARK_RED        , {2, 0},
		RGB(0x80, 0x80, 0x00), XTP_IDS_CLR_DARK_YELLOW     , {3, 0},
		RGB(0x00, 0x00, 0x80), XTP_IDS_CLR_DARK_BLUE       , {6, 0},
		RGB(0x00, 0x80, 0x00), XTP_IDS_CLR_GREEN           , {4, 0},
		RGB(0x00, 0x80, 0x80), XTP_IDS_CLR_TEAL            , {5, 0},
		RGB(0x00, 0x00, 0xFF), XTP_IDS_CLR_BLUE            , {6, 1},
		RGB(0x80, 0x80, 0x80), XTP_IDS_CLR_GRAY50          , {1, 0},
		RGB(0xFF, 0x00, 0x00), XTP_IDS_CLR_RED             , {2, 1},
		RGB(0x80, 0x00, 0x80), XTP_IDS_CLR_VIOLET          , {7, 0},
		RGB(0xFF, 0x00, 0xFF), XTP_IDS_CLR_PINK            , {7, 1},
		RGB(0xFF, 0xFF, 0x00), XTP_IDS_CLR_YELLOW          , {3, 1},
		RGB(0x00, 0xFF, 0x00), XTP_IDS_CLR_BRIGHT_GREEN    , {4, 1},
		RGB(0x00, 0xFF, 0xFF), XTP_IDS_CLR_TURQUOISE       , {5, 1},
		RGB(0xC0, 0xC0, 0xC0), XTP_IDS_CLR_GRAY25          , {1, 1},
		RGB(0xFF, 0xFF, 0xFF), XTP_IDS_CLR_WHITE           , {0, 1}
	};

	CDialog::OnInitDialog();

	CXTPResourceManager::CManageState manageState; // Switch to Toolkit resources

	// Set Icons
	CXTPImageManager* pImageManager = new CXTPImageManager();
	pImageManager->InternalAddRef();

	pImageManager->SetIcons(XTP_IDB_TOOLBAR_TOOLS, nButtons, 12 + 4, CSize(0, 0));

	CXTPPaintManager* pPaintManager = CXTPPaintManager::CreateTheme(xtpThemeOfficeXP);
	pPaintManager->InternalAddRef();
	pPaintManager->GetIconsInfo()->bUseFadedIcons = FALSE;
	pPaintManager->GetIconsInfo()->bIconsWithShadow = FALSE;
	pPaintManager->m_bFlatToolBar = TRUE;

	CRect rcTools;
	m_gboxTools.GetWindowRect(&rcTools);
	ScreenToClient(rcTools);

	m_wndToolbarTools.SetPaintManager(pPaintManager);
	m_wndToolbarTools.SetImageManager(pImageManager);
	m_wndToolbarTools.CreateToolBar(WS_CHILD | WS_VISIBLE | CBRS_TOOLTIPS, this);
	m_wndToolbarTools.SetButtons(nButtonsTools, 12);

	CSize sz = m_wndToolbarTools.CalcDockingLayout(23 * 4 + 4, LM_HORZDOCK | LM_HORZ | LM_COMMIT);
	m_wndToolbarTools.SetWindowPos(&CWnd::wndTop, rcTools.left - 7, rcTools.top - 4, sz.cx, sz.cy, 0);

	CRect rcMove;
	m_gboxMove.GetWindowRect(&rcMove);
	ScreenToClient(rcMove);

	m_wndToolbarMove.SetPaintManager(pPaintManager);
	m_wndToolbarMove.SetImageManager(pImageManager);
	m_wndToolbarMove.CreateToolBar(WS_CHILD | WS_VISIBLE | CBRS_TOOLTIPS, this);
	m_wndToolbarMove.SetButtons(nButtonsMove, 4);

	sz = m_wndToolbarMove.CalcDockingLayout(23 * 4 + 4, LM_HORZDOCK | LM_HORZ | LM_COMMIT);
	m_wndToolbarMove.SetWindowPos(&CWnd::wndTop, rcMove.left - 7, rcMove.top - 4, sz.cx, sz.cy, 0);

	CRect rcColors;
	GetDlgItem(XTP_IDC_STATIC_COLORS)->GetWindowRect(&rcColors);
	ScreenToClient(rcColors);

	for (int i = 0; i < _countof(colors); i++)
	{
		CRect rcItem(CPoint(rcColors.left, rcColors.top - 4), CSize(11, 11));
		rcItem.OffsetRect(colors[i].ptOffset.x * 13 , colors[i].ptOffset.y * 13);

		m_wndSelector[i].Create(NULL, WS_CHILD | WS_VISIBLE | SS_NOTIFY, rcItem, this, colors[i].nID);
		m_wndSelector[i].SetColor(colors[i].clr);

	}
	SetSelected(&m_wndSelector[0]);

	m_wndSelectorErase.SubclassDlgItem(XTP_IDC_CLR_ERASE, this);
	m_wndSelectorErase.SetColor(XTPCOLOR_ERASE);

	m_wndPicker.SubclassDlgItem(XTP_IDC_CLR_PICKER, this);


	m_wndPicture.Init(XTP_IDC_STATIC_PICTURE, this);
	m_wndPreview.Init(XTP_IDC_STATIC_PREVIEW, this);

	if (m_bCommandBarsEditor)
	{
		XTPMouseManager()->AddTrustedWindow(m_hWnd);
	}

	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CXTPImageEditorDlg::OnDestroy()
{
	if (m_bCommandBarsEditor)
	{
		XTPMouseManager()->RemoveTrustedWindow(m_hWnd);
	}

	CDialog::OnDestroy();
}

LRESULT CXTPImageEditorDlg::OnKickIdle(WPARAM, LPARAM)
{
	SendMessageToDescendants(WM_IDLEUPDATECMDUI);
	return 0;
}

void CXTPImageEditorDlg::SetSelected(CXTPImageEditorPicker* pPicker)
{
	if (m_pSelected != NULL)
		m_pSelected->SetSelected(FALSE);

	if (pPicker)
		pPicker->SetSelected(TRUE);

	m_pSelected = pPicker;
}

void CXTPImageEditorDlg::OnPickerClick(NMHDR* /*pNMHDR*/, LRESULT* /*pResult*/)
{
	CColorDialog cd(m_wndPicker.GetColor(), 0, this);
	if (cd.DoModal())
	{
		m_wndPicker.SetColor(cd.GetColor());
		SetSelected(&m_wndPicker);
	}

}
void CXTPImageEditorDlg::OnEraseClick(NMHDR* pNMHDR, LRESULT* /*pResult*/)
{
	CXTPImageEditorPicker* pWnd = DYNAMIC_DOWNCAST(CXTPImageEditorPicker, CWnd::FromHandlePermanent(pNMHDR->hwndFrom));
	ASSERT(pWnd);
	SetSelected(pWnd);

}

void CXTPImageEditorDlg::OnSelectorClick(UINT /*id*/, NMHDR* pNMHDR, LRESULT* /*pResult*/)
{
	CXTPImageEditorPicker* pWnd = DYNAMIC_DOWNCAST(CXTPImageEditorPicker, CWnd::FromHandlePermanent(pNMHDR->hwndFrom));
	ASSERT(pWnd);
	SetSelected(pWnd);
}


void CXTPImageEditorDlg::OnUpdateButtonTool(CCmdUI* pCmdUI)
{
	pCmdUI->SetCheck(m_toolSelected == (int)pCmdUI->m_nID - XTP_ID_BUTTON_PENCIL);
}

void CXTPImageEditorDlg::OnButtonTool(UINT nID)
{
	m_toolPrevious = m_toolSelected == xtpToolGetColor ? m_toolPrevious : m_toolSelected;
	m_toolSelected = (XTPImageEditorTools)(nID - XTP_ID_BUTTON_PENCIL);
}


void CXTPImageEditorDlg::OnPictureChanged()
{
	m_wndPreview.Invalidate(FALSE);
}

void CXTPImageEditorDlg::OnUpdateButtonUndo(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(!m_wndPicture.m_lstUndo.IsEmpty());
}

void CXTPImageEditorDlg::OnButtonUndo()
{
	m_wndPicture.Undo();
}

void CXTPImageEditorDlg::OnUpdateButtonRedo(CCmdUI* pCmdUI)
{
	pCmdUI->Enable(!m_wndPicture.m_lstRedo.IsEmpty());
}

void CXTPImageEditorDlg::OnButtonRedo()
{
	m_wndPicture.Redo();
}

void CXTPImageEditorDlg::OnButtonClear()
{
	m_wndPicture.Clear();
}
void CXTPImageEditorDlg::OnButtonCopy()
{
	m_wndPicture.Copy();
}
void CXTPImageEditorDlg::OnButtonPaste()
{
	m_wndPicture.Paste();
}
void CXTPImageEditorDlg::OnButtonOpen()
{
	m_wndPicture.Load();
}

void CXTPImageEditorDlg::OnMoveButton(UINT nID)
{
	m_wndPicture.MovePicture(
		nID == XTP_IDC_BUTTON_LEFT ? CPoint(-1, 0) :
		nID == XTP_IDC_BUTTON_RIGHT ? CPoint(+1, 0) :
		nID == XTP_IDC_BUTTON_UP ? CPoint(0, -1) : CPoint(0, +1));
}


BOOL CXTPImageEditorDlg::OnToolTipText(UINT, NMHDR* pNMHDR, LRESULT* pResult)
{
	ASSERT(pNMHDR->code == TTN_NEEDTEXTA || pNMHDR->code == TTN_NEEDTEXTW);

	// need to handle both ANSI and UNICODE versions of the message
	TOOLTIPTEXTA* pTTTA = (TOOLTIPTEXTA*)pNMHDR;
	TOOLTIPTEXTW* pTTTW = (TOOLTIPTEXTW*)pNMHDR;
	TCHAR szFullText[256];
	CString strTipText;
	UINT nID = (UINT)pNMHDR->idFrom;

	if (nID != 0) // will be zero on a separator
	{
		// don't handle the message if no string resource found
		if (AfxLoadString(nID, szFullText) == 0)
			return FALSE;

		// this is the command id, not the button index
		AfxExtractSubString(strTipText, szFullText, 1, '\n');
	}
#ifndef _UNICODE
	if (pNMHDR->code == TTN_NEEDTEXTA)
		lstrcpyn(pTTTA->szText, strTipText, _countof(pTTTA->szText));
	else
		_mbstowcsz(pTTTW->szText, strTipText, _countof(pTTTW->szText));
#else
	if (pNMHDR->code == TTN_NEEDTEXTA)
		_wcstombsz(pTTTA->szText, strTipText, _countof(pTTTA->szText));
	else
		lstrcpyn(pTTTW->szText, strTipText, _countof(pTTTW->szText));
#endif
	*pResult = 0;

	// bring the tooltip window above other popup windows
	::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0,
		SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER);

	return TRUE;    // message was handled
}