⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 faceedit.cpp

📁 类似QQ,MSN表情的richedit,用来测试表情控件的
💻 CPP
📖 第 1 页 / 共 2 页
字号:

/*--------------------------------------
* Copyright (c) 2004
* All rights reserved.
*
* 文件名称: FaceEdit.cpp
* 摘 要: 类 CFaceEdit 实现部分
*
* 当前版本: 1.01 VC6版
* 作 者: Flanker(刘翔)
* 完成日期: 2004年10月26日
* 修改日期: 2004年11月12日
--------------------------------------*/

#include "StdAfx.h"
#include ".\faceedit.h"

#include <vector>
#include <algorithm>
using namespace std;

#include <Richole.h>
#include <afxodlgs.h>
#include <atlconv.h>	//A2W,在VC6下面要包含。

//全局函数
bool less_than(stFace &face1, stFace &face2);	//声明排序的"条件"函数
void InsertBitmap(CRichEditCtrl *pRichEdit, HBITMAP hBitmap);	//底层的一个InsertBitmap

/*-----------------------------------------------------------------------------
*	函数名	:Init
*
*	功能	:初始化函数,用来指定表情的数目,表情符号等信息
*
*	用法	:
				CString pSymbol[] = {":)", ":(", ";)", ":0", ";-)"};
				UINT nIDBmp[] =  {	IDB_BITMAP1, IDB_BITMAP2, IDB_BITMAP3, IDB_BITMAP4, IDB_BITMAP5};
				m_FaceEdit.Init(5, pSymbol, nIDBmp);
*				
-----------------------------------------------------------------------------*/
void CFaceEdit::Init(int nfaceCount, CString *pSymbol, UINT *pIDBmp)
{
	/*
		!!! 发现!!! 由深复制引申的...

		这个函数的原始代码是:
		m_pSymbol = pSymbol;
		m_nfaceCount = nfaceCount;
		m_pIDBmp  = pIDBmp;

		当把Set()函数和SetText()函数分开放在两个不同过程中使用时,将会出现问题,如:
		void proc1()
		{
			...
			CString pSymbol[] = {":)", ":(", ";)", ":0", ";-)"};
			UINT nIDBmp[] =  {	IDB_BITMAP1, IDB_BITMAP2, IDB_BITMAP3, IDB_BITMAP4, IDB_BITMAP5};
			m_FaceEdit.Set(5, pSymbol, nIDBmp);
			...
		}

		void proc2()
		{
			...
			m_FaceEdit.SetText("ha:(ha:0!", TRUE);
		}
		在上面的过程中,proc1()中的两个指针pSymbol和nIDBmp在函数结束时将会被销毁,
		而在Set()内部采用指针传值形式,因此指针成为“野指针”,后果可想而之。
		应用深复制的知识,应该这样...

	*/
	m_nfaceCount = nfaceCount;

	m_pSymbol = new CString[m_nfaceCount];
	m_pIDBmp  = new UINT[m_nfaceCount];
	for(int n=0; n<m_nfaceCount; n++)
	{
		m_pSymbol[n] = pSymbol[n];
		m_pIDBmp[n]  = pIDBmp[n];
	}

	/*
		但这样又产生问题了,现在我把Set()函数和SetText()函数放在同一个过程中使用,
		m_pSymbol和m_pIDBmp指针指向了新的的堆内存,原来的内存块无法析构了,造成了浪费。
		现在的问题就是:如何判断这两个指针指向的区域到底是不是有效区域,
		不是的话就new一块,是的话就直接赋值。
	*/
}

/*-----------------------------------------------------------------------------
*	函数名	:Init
*
*	功能	:初始化函数,用来指定表情的数目,表情符号等信息
*
*	用法	:
				CString pSymbol[] = {":)", ":(", ";)", ":0", ";-)"};
				CString pBmpFile[] =  {	"C:\\a.bmp", "C:\\b.bmp", "C:\\c.bmp", "C:\\d.bmp", "C:\\e.bmp"};
				m_FaceEdit.Init(5, pSymbol, pBmpFile);
*				
-----------------------------------------------------------------------------*/
void CFaceEdit::Init(int nfaceCount, CString *pSymbol, CString *pBmpFile)
{
	m_nfaceCount = nfaceCount;

	m_pSymbol   = new CString[m_nfaceCount];
	m_pBmpFile  = new CString[m_nfaceCount];
	for(int n=0; n<m_nfaceCount; n++)
	{
		m_pSymbol[n]   = pSymbol[n];
		m_pBmpFile[n]  = pBmpFile[n];
	}
}

/*-----------------------------------------------------------------------------
*	函数名	:SetText
*
*	功能	:类的主要工作函数。该函数负责将str中的符号翻译成表情,显示到
*				CRichEditCtrl中。参数二bEnableFace是功能开头,当它为FALSE时,
*				本函数将不会翻译表情。
-----------------------------------------------------------------------------*/
bool CFaceEdit::SetText(CString str, BOOL bEnableFace)
{
	if(bEnableFace)
	{
		SetTextWithFace(str);
	}
	else
	{
		SetWindowText(str);
	}

	return true;
}
bool CFaceEdit::SetText()
{

	SetWindowText(_T(""));
	return true;
}
/*-----------------------------------------------------------------------------
*	函数名	:SetTextWithFace
*
*	功能	:实现插入图象的算法函数。
*	实现原理:
				假设:CString pSymbol[] = {":)", ":(", "#", "AK47", ":-)"};
				先将包括表情符号的文本( 如:"haha:)" )直接显示到CRichEditCtrl中,
				然后选定其中的表情符号( 如:":)" ),再调用InsertBitmap函数
				实现插入,详见注释
-----------------------------------------------------------------------------*/
void CFaceEdit::SetTextWithFace(CString str)
{
	CString *pstr = new CString[m_nfaceCount];
	//TRACE("用户定义的表情符号:\n");
	for(int n = 0; n<m_nfaceCount; n++)
	{
		//TRACE("m_pSymbol[%d]:%s\n", n, m_pSymbol[n]);
		pstr[n] = m_pSymbol[n];
	}
	CString strs;
	
	GetWindowText(strs);
	str = strs + str;
//	SetWindowText(str);

	int nFaceCount = 0;		//str中共有多少个表情。
	stFace faceNode;		//faceNode中存储的是在哪个位置插入,插入哪一个表情。
//	vector <stFace> vecFace;	//vecFace[0]表示第一个表情的位置和型号、vecFace[1]表示第二个表情的位置和型号…

	/* *****************************************************************************************
	 *		第一步:
	 *		在str中查找表情字符(pstr)。
	 *
	 *		如str = "我们的:-)明天更美好AK47,一定:-)非常美好#。"。那么以下操作将生成四个
	 *		stFace(定义见FaceEdit.h)结点,它们的值分别为{3, 3, 3}, {15, 3, 4}, {10, 1, 3}, {21, 0, 1}。
	 *		使用vector数组vecFace进行存储。
	 *
	 * ****************************************************************************************/
	for(int i=0, m = -1; i<m_nfaceCount; i++)
	{
		//关键的一步:查找宽字符,汉字算一个字符。放在循环中,就可以查找重复的字符。
		while(1)
		{
			m = (int)str.Find(pstr[i], m + 1);	//循环搜索,不放过重复的表情
			if(m != -1)
			{
				faceNode.nPos = m;
				faceNode.nFaceIndex = i;
				faceNode.nLength = (int)pstr[i].GetLength();

				vecFace.push_back(faceNode);
				//TRACE("push():%d\n", m);
				nFaceCount++;
			}
			else
			{
				break;
			}
		}
	}	//查找完毕
	if(nFaceCount==0)		//在str中没找到一个表情,下面就无需插入表情了。
		return;

	delete []pstr;
	/* *****************************************************************************************
	 *		第二步:
	 *		使用泛型算法sort进行排序。
	 *
	 *		上面的四个结点:A:{3, 3, 3}, B:{15, 3, 4}, C:{10, 1, 3}, D:{21, 0, 1},显然这不是按照
	 *		顺序排的,这里应该按表情在文本中出现的次序依次替换,否则替换算法将会非常麻烦。
	 *
	 * ****************************************************************************************/
	bool less_than(stFace &face1, stFace &face2);	//声明排序的"条件"函数
	sort(vecFace.begin(), vecFace.end(), less_than);	//详见我的ObjectSort工程中的说明。可参见《Essential C++》P84


	/* *****************************************************************************************
	 *		第三步:
	 *		调整各表情字符位置(nPos)。
	 *
	 *		排序之后各结点:A:{3, 3, 3}, C:{10, 1, 3}, B:{15, 3, 4}, D:{21, 0, 1}。
	 *		经过摸索,发现这样一个规律:
	 *		本结点应该向前挪的值(prev) = 上一个表情的长度(prevLength) - 1 + 上一个结点应该向前挪的值(prev)
	 *
	 *		如:
			CString pSymbol[] = {":)", ":(", "#", "AK47", ":-)"};
			序号:                0     1     2     3       4

			"我们的:-)明天更美好AK47,一定:-)非常美好#。"
			位置: 3            11        18         25

			"#"(25, 2, 1)    "AK47"(11, 3, 4)  ":-)"(3, 4, 3)   ":-)"(18, 4, 3) 
				~~					~~			     ~~				  ~~
			排序后:
			":-)"(3, 4, 3)   "AK47"(11,3, 4)   ":-)"(18, 4, 3)    "#"(25, 2, 1)
				 ~~					~~			     ~~				  ~~
			处理后:
			":-)"(3, 4, 3)   "AK47"(9, 3, 4)   ":-)"(13, 4, 3)    "#"(18, 2, 1)
				 ~~					~~			     ~~				  ~~
				少了0			  少了2            少了5          少了7
								  3-1+0            4-1+2          3-1+5
	 *
	 *
	 * ****************************************************************************************/
#ifdef _DEBUG
	TRACE("排序之后:\n");
	for(int x = 0; x<nFaceCount; x++)
	{
		TRACE("vecFace[%d].nPos = %d\n", x, vecFace[x].nPos);
	}
#endif //_DEBUG

	for(int t = 0, prevLength = 0, prev = 0; t<nFaceCount; t++)
	{
		vecFace[t].nPos -= prev;
		prevLength = vecFace[t].nLength;
		prev = prevLength - 1 + prev;
	}

#ifdef _DEBUG
	TRACE("移位处理之后:\n");
	for(int k = 0; k<nFaceCount; k++)
	{
		TRACE("vecFace[%d].nPos = %d\n", k, vecFace[k].nPos);
	}
#endif //_DEBUG

	/* *****************************************************************************************
	 *		第四步:
	 *		下面插入表情。
	 *
	 *		调用InsertBitmap插入各处理完成的各结点A:{3, 3], C:{9, 1}, B:{13, 3}, D:{18, 0}。
	 *
	 * ****************************************************************************************/
	try
	{
		for(int j=0; j<nFaceCount; j++)
		{
			stFace faceNode = vecFace[j];
			InsertBitmap(faceNode);
		}
	}

	catch(char *sError)
	{
		MessageBox(sError, "HBITMAP", MB_OK | MB_ICONERROR);
	}
}

//泛型算法sort()的排序条件函数,实现按stFace.nPos域排序
bool less_than(stFace &face1, stFace &face2)
{
	return face1.nPos < face2.nPos ? true : false;
}

//应用层的InsertBitmap
void CFaceEdit::InsertBitmap(stFace &face)
{
	int nBegin = face.nPos;
	int nEnd = nBegin + face.nLength;
	SetSel(nBegin, nEnd);                //设置选中位置 用来更改

	HBITMAP bmp = NULL;
	if(m_pIDBmp != NULL)
		//从资源创建HBITMAP
		bmp = ::LoadBitmap(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(m_pIDBmp[face.nFaceIndex]));
	else
		//从文件创建HBITMAP
		bmp = (HBITMAP)::LoadImage(NULL, m_pBmpFile[face.nFaceIndex], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
	
	bmp = (HBITMAP)::LoadImage(NULL, "res\\kid.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);



	if(bmp==NULL)
	{
		throw "无效的HBITMAP类型。\r\n\r\n可能的原因是:\r\n位图文件的路径不正确。";
	}
	
	 picture.Load("11.gif");
	//  WCHAR wchar[10] = {'1','1','.','g','i','f'};
	::InsertBitmap(this,bmp);
}

/*--------------------------------------------------------------------------
*	函数名:InsertBitmap
*
*	功能  :底层的一个InsertBitmap,使用OLE容器,向CRichEditCtrl中插入表情。
*			根据位图句柄创建OleCreateStaticFromData();用这个函数可以把资源中的图片插入到文本框中
*
*	头文件:<Richole.h>、<afxodlgs.h>
--------------------------------------------------------------------------*/

/*
FORMATETC接口(发音“format et cetera”)用来表示IDataObject提供(或接收)的数据类型,是标准window粘贴板格式(CF_TEXT等)的扩展,因此除了基本的粘贴板格式之外,还包含了数据怎么样rendered和存储。
typedef struct
{
CLIPFORMAT      cfFormat;   // 粘贴板格式 
DVTARGETDEVICE *ptd;        // (NULL)  rendering的目标设备
DWORD           dwAspect;    // (DV_CONTENT) rendering的详细程度

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -