FaceEdit.cpp
上传用户:dgvc2008
上传日期:2021-01-21
资源大小:65k
文件大小:12k
源码类别:

RichEdit

开发平台:

Visual C++

  1. /*--------------------------------------
  2. * Copyright (c) 2004
  3. * All rights reserved.
  4. * Download by http://www.codefans.net
  5. * 文件名称: FaceEdit.cpp
  6. * 摘 要: 类 CFaceEdit 实现部分
  7. *
  8. * 当前版本: 1.01 VC6版
  9. * 作 者: Flanker(刘翔)
  10. * 完成日期: 2004年10月26日
  11. * 修改日期: 2004年11月12日
  12. --------------------------------------*/
  13. #include "StdAfx.h"
  14. #include ".faceedit.h"
  15. #include <vector>
  16. #include <algorithm>
  17. using namespace std;
  18. #include <Richole.h>
  19. #include <afxodlgs.h>
  20. #include <atlconv.h> //A2W,在VC6下面要包含。
  21. //全局函数
  22. bool less_than(stFace &face1, stFace &face2); //声明排序的"条件"函数
  23. void InsertBitmap(CRichEditCtrl *pRichEdit, HBITMAP hBitmap); //底层的一个InsertBitmap
  24. /*-----------------------------------------------------------------------------
  25. * 函数名 :Init
  26. *
  27. * 功能 :初始化函数,用来指定表情的数目,表情符号等信息
  28. *
  29. * 用法 :
  30. CString pSymbol[] = {":)", ":(", ";)", ":0", ";-)"};
  31. UINT nIDBmp[] =  { IDB_BITMAP1, IDB_BITMAP2, IDB_BITMAP3, IDB_BITMAP4, IDB_BITMAP5};
  32. m_FaceEdit.Init(5, pSymbol, nIDBmp);
  33. *
  34. -----------------------------------------------------------------------------*/
  35. void CFaceEdit::Init(int nfaceCount, CString *pSymbol, UINT *pIDBmp)
  36. {
  37. /*
  38. !!! 发现!!! 由深复制引申的...
  39. 这个函数的原始代码是:
  40. m_nfaceCount = nfaceCount;
  41. m_pSymbol = pSymbol;
  42. m_pIDBmp  = pIDBmp;
  43. 当把Set()函数和SetText()函数分开放在两个不同过程中使用时,将会出现问题,如:
  44. void proc1()
  45. {
  46. ...
  47. CString pSymbol[] = {":)", ":(", ";)", ":0", ";-)"};
  48. UINT nIDBmp[] =  { IDB_BITMAP1, IDB_BITMAP2, IDB_BITMAP3, IDB_BITMAP4, IDB_BITMAP5};
  49. m_FaceEdit.Set(5, pSymbol, nIDBmp);
  50. ...
  51. }
  52. void proc2()
  53. {
  54. ...
  55. m_FaceEdit.SetText("ha:(ha:0!", TRUE);
  56. }
  57. 在上面的过程中,proc1()中的两个指针pSymbol和nIDBmp在函数结束时将会被销毁,
  58. 而在Set()内部采用指针传值形式,因此指针成为“野指针”,后果可想而之。
  59. 应用深复制的知识,应该这样...
  60. */
  61. m_nfaceCount = nfaceCount;
  62. m_pSymbol = new CString[m_nfaceCount];
  63. m_pIDBmp  = new UINT[m_nfaceCount];
  64. for(int n=0; n<m_nfaceCount; n++)
  65. {
  66. m_pSymbol[n] = pSymbol[n];
  67. m_pIDBmp[n]  = pIDBmp[n];
  68. }
  69. /*
  70. 但这样又产生问题了,现在我把Set()函数和SetText()函数放在同一个过程中使用,
  71. m_pSymbol和m_pIDBmp指针指向了新的的堆内存,原来的内存块无法析构了,造成了浪费。
  72. 现在的问题就是:如何判断这两个指针指向的区域到底是不是有效区域,
  73. 不是的话就new一块,是的话就直接赋值。
  74. */
  75. }
  76. /*-----------------------------------------------------------------------------
  77. * 函数名 :Init
  78. *
  79. * 功能 :初始化函数,用来指定表情的数目,表情符号等信息
  80. *
  81. * 用法 :
  82. CString pSymbol[] = {":)", ":(", ";)", ":0", ";-)"};
  83. CString pBmpFile[] =  { "C:\a.bmp", "C:\b.bmp", "C:\c.bmp", "C:\d.bmp", "C:\e.bmp"};
  84. m_FaceEdit.Init(5, pSymbol, pBmpFile);
  85. *
  86. -----------------------------------------------------------------------------*/
  87. void CFaceEdit::Init(int nfaceCount, CString *pSymbol, CString *pBmpFile)
  88. {
  89. m_nfaceCount = nfaceCount;
  90. m_pSymbol   = new CString[m_nfaceCount];
  91. m_pBmpFile  = new CString[m_nfaceCount];
  92. for(int n=0; n<m_nfaceCount; n++)
  93. {
  94. m_pSymbol[n]   = pSymbol[n];
  95. m_pBmpFile[n]  = pBmpFile[n];
  96. }
  97. }
  98. /*-----------------------------------------------------------------------------
  99. * 函数名 :SetText
  100. *
  101. * 功能 :类的主要工作函数。该函数负责将str中的符号翻译成表情,显示到
  102. * CRichEditCtrl中。参数二bEnableFace是功能开头,当它为FALSE时,
  103. * 本函数将不会翻译表情。
  104. -----------------------------------------------------------------------------*/
  105. bool CFaceEdit::SetText(CString str, BOOL bEnableFace)
  106. {
  107. if(bEnableFace)
  108. {
  109. SetTextWithFace(str);
  110. }
  111. else
  112. {
  113. SetWindowText(str);
  114. }
  115. return true;
  116. }
  117. /*-----------------------------------------------------------------------------
  118. * 函数名 :SetTextWithFace
  119. *
  120. * 功能 :实现插入图象的算法函数。
  121. * 实现原理:
  122. 假设:CString pSymbol[] = {":)", ":(", "#", "AK47", ":-)"};
  123. 先将包括表情符号的文本( 如:"haha:)" )直接显示到CRichEditCtrl中,
  124. 然后选定其中的表情符号( 如:":)" ),再调用InsertBitmap函数
  125. 实现插入,详见注释
  126. -----------------------------------------------------------------------------*/
  127. void CFaceEdit::SetTextWithFace(CString str)
  128. {
  129. CString *pstr = new CString[m_nfaceCount];
  130. TRACE("用户定义的表情符号:n");
  131. for(int n = 0; n<m_nfaceCount; n++)
  132. {
  133. TRACE("m_pSymbol[%d]:%sn", n, m_pSymbol[n]);
  134. pstr[n] = m_pSymbol[n];
  135. }
  136. SetWindowText(str);
  137. int nFaceCount = 0; //str中共有多少个表情。
  138. stFace faceNode; //faceNode中存储的是在哪个位置插入,插入哪一个表情。
  139. vector <stFace> vecFace; //vecFace[0]表示第一个表情的位置和型号、vecFace[1]表示第二个表情的位置和型号…
  140. /* *****************************************************************************************
  141.  * 第一步:
  142.  * 在str中查找表情字符(pstr)。
  143.  *
  144.  * 如str = "我们的:-)明天更美好AK47,一定:-)非常美好#。"。那么以下操作将生成四个
  145.  * stFace(定义见FaceEdit.h)结点,它们的值分别为{3, 3, 3}, {15, 3, 4}, {10, 1, 3}, {21, 0, 1}。
  146.  * 使用vector数组vecFace进行存储。
  147.  *
  148.  * ****************************************************************************************/
  149. for(int i=0, m = -1; i<m_nfaceCount; i++)
  150. {
  151. //关键的一步:查找宽字符,汉字算一个字符。放在循环中,就可以查找重复的字符。
  152. while(1)
  153. {
  154. m = (int)str.Find(pstr[i], m + 1); //循环搜索,不放过重复的表情
  155. if(m != -1)
  156. {
  157. faceNode.nPos = m;
  158. faceNode.nFaceIndex = i;
  159. faceNode.nLength = (int)pstr[i].GetLength();
  160. vecFace.push_back(faceNode);
  161. //TRACE("push():%dn", m);
  162. nFaceCount++;
  163. }
  164. else
  165. {
  166. break;
  167. }
  168. }
  169. } //查找完毕
  170. if(nFaceCount==0) //在str中没找到一个表情,下面就无需插入表情了。
  171. return;
  172. delete []pstr;
  173. /* *****************************************************************************************
  174.  * 第二步:
  175.  * 使用泛型算法sort进行排序。
  176.  *
  177.  * 上面的四个结点:A:{3, 3, 3}, B:{15, 3, 4}, C:{10, 1, 3}, D:{21, 0, 1},显然这不是按照
  178.  * 顺序排的,这里应该按表情在文本中出现的次序依次替换,否则替换算法将会非常麻烦。
  179.  *
  180.  * ****************************************************************************************/
  181. bool less_than(stFace &face1, stFace &face2); //声明排序的"条件"函数
  182. sort(vecFace.begin(), vecFace.end(), less_than); //详见我的ObjectSort工程中的说明。可参见《Essential C++》P84
  183. /* *****************************************************************************************
  184.  * 第三步:
  185.  * 调整各表情字符位置(nPos)。
  186.  *
  187.  * 排序之后各结点:A:{3, 3, 3}, C:{10, 1, 3}, B:{15, 3, 4}, D:{21, 0, 1}。
  188.  * 经过摸索,发现这样一个规律:
  189.  * 本结点应该向前挪的值(prev) = 上一个表情的长度(prevLength) - 1 + 上一个结点应该向前挪的值(prev)
  190.  *
  191.  * 如:
  192. CString pSymbol[] = {":)", ":(", "#", "AK47", ":-)"};
  193. 序号:                0     1     2     3       4
  194. "我们的:-)明天更美好AK47,一定:-)非常美好#。"
  195. 位置: 3            11        18         25
  196. "#"(25, 2, 1)    "AK47"(11, 3, 4)  ":-)"(3, 4, 3)   ":-)"(18, 4, 3) 
  197. ~~ ~~      ~~   ~~
  198. 排序后:
  199. ":-)"(3, 4, 3)   "AK47"(11,3, 4)   ":-)"(18, 4, 3)    "#"(25, 2, 1)
  200.  ~~ ~~      ~~   ~~
  201. 处理后:
  202. ":-)"(3, 4, 3)   "AK47"(9, 3, 4)   ":-)"(13, 4, 3)    "#"(18, 2, 1)
  203.  ~~ ~~      ~~   ~~
  204. 少了0   少了2            少了5          少了7
  205.   3-1+0            4-1+2          3-1+5
  206.  *
  207.  *
  208.  * ****************************************************************************************/
  209. #ifdef _DEBUG
  210. TRACE("排序之后:n");
  211. for(int x = 0; x<nFaceCount; x++)
  212. {
  213. TRACE("vecFace[%d].nPos = %dn", x, vecFace[x].nPos);
  214. }
  215. #endif //_DEBUG
  216. for(int t = 0, prevLength = 0, prev = 0; t<nFaceCount; t++)
  217. {
  218. vecFace[t].nPos -= prev;
  219. prevLength = vecFace[t].nLength;
  220. prev = prevLength - 1 + prev;
  221. }
  222. #ifdef _DEBUG
  223. TRACE("移位处理之后:n");
  224. for(int k = 0; k<nFaceCount; k++)
  225. {
  226. TRACE("vecFace[%d].nPos = %dn", k, vecFace[k].nPos);
  227. }
  228. #endif //_DEBUG
  229. /* *****************************************************************************************
  230.  * 第四步:
  231.  * 下面插入表情。
  232.  *
  233.  * 调用InsertBitmap插入各处理完成的各结点A:{3, 3], C:{9, 1}, B:{13, 3}, D:{18, 0}。
  234.  *
  235.  * ****************************************************************************************/
  236. try
  237. {
  238. for(int j=0; j<nFaceCount; j++)
  239. {
  240. stFace faceNode = vecFace[j];
  241. InsertBitmap(faceNode);
  242. }
  243. }
  244. catch(char *sError)
  245. {
  246. MessageBox(sError, "HBITMAP", MB_OK | MB_ICONERROR);
  247. }
  248. }
  249. //泛型算法sort()的排序条件函数,实现按stFace.nPos域排序
  250. bool less_than(stFace &face1, stFace &face2)
  251. {
  252. return face1.nPos < face2.nPos ? true : false;
  253. }
  254. //应用层的InsertBitmap
  255. void CFaceEdit::InsertBitmap(stFace &face)
  256. {
  257. int nBegin = face.nPos;
  258. int nEnd = nBegin + face.nLength;
  259. SetSel(nBegin, nEnd);
  260. HBITMAP bmp = NULL;
  261. if(m_pIDBmp != NULL)
  262. //从资源创建HBITMAP
  263. bmp = ::LoadBitmap(AfxGetApp()->m_hInstance, MAKEINTRESOURCE(m_pIDBmp[face.nFaceIndex]));
  264. else
  265. //从文件创建HBITMAP
  266. bmp = (HBITMAP)::LoadImage(NULL, m_pBmpFile[face.nFaceIndex], IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
  267. if(bmp==NULL)
  268. {
  269. throw "无效的HBITMAP类型。rnrn可能的原因是:rn位图文件的路径不正确。";
  270. }
  271. ::InsertBitmap(this, bmp);
  272. }
  273. /*--------------------------------------------------------------------------
  274. * 函数名:InsertBitmap
  275. *
  276. * 功能  :底层的一个InsertBitmap,使用OLE容器,向CRichEditCtrl中插入表情。
  277. * 根据位图句柄创建OleCreateStaticFromData();用这个函数可以把资源中的图片插入到文本框中
  278. *
  279. * 头文件:<Richole.h>、<afxodlgs.h>
  280. --------------------------------------------------------------------------*/
  281. void InsertBitmap(CRichEditCtrl *pRichEdit, HBITMAP hBitmap) //底层的一个InsertBitmap
  282. {
  283. STGMEDIUM stgm;
  284. stgm.tymed = TYMED_GDI;    // Storage medium = HBITMAP handle
  285. stgm.hBitmap = hBitmap;
  286. stgm.pUnkForRelease = NULL; // Use ReleaseStgMedium
  287. FORMATETC fm;
  288. fm.cfFormat = CF_BITMAP;    // Clipboard format = CF_BITMAP
  289. fm.ptd = NULL;       // Target Device = Screen
  290. fm.dwAspect = DVASPECT_CONTENT;   // Level of detail = Full content
  291. fm.lindex = -1;       // Index = Not applicaple
  292. fm.tymed = TYMED_GDI;  
  293. //创建输入数据源
  294. IStorage *pStorage; 
  295. //分配内存
  296. LPLOCKBYTES lpLockBytes = NULL;
  297. SCODE sc = ::CreateILockBytesOnHGlobal(NULL, TRUE, &lpLockBytes);
  298. if (sc != S_OK)
  299. AfxThrowOleException(sc);
  300. ASSERT(lpLockBytes != NULL);
  301. sc = ::StgCreateDocfileOnILockBytes(lpLockBytes,
  302. STGM_SHARE_EXCLUSIVE|STGM_CREATE|STGM_READWRITE, 0, &pStorage);
  303. if (sc != S_OK)
  304. {
  305. VERIFY(lpLockBytes->Release() == 0);
  306. lpLockBytes = NULL;
  307. AfxThrowOleException(sc);
  308. }
  309. ASSERT(pStorage != NULL);
  310. COleDataSource *pDataSource = new COleDataSource;
  311. pDataSource->CacheData(CF_BITMAP, &stgm);
  312. LPDATAOBJECT lpDataObject = 
  313. (LPDATAOBJECT)pDataSource->GetInterface(&IID_IDataObject);
  314. //获取RichEdit的OLEClientSite
  315. LPOLECLIENTSITE lpClientSite;
  316. pRichEdit->GetIRichEditOle()->GetClientSite( &lpClientSite );
  317. //创建OLE对象
  318. IOleObject *pOleObject;
  319. sc = OleCreateStaticFromData(lpDataObject,IID_IOleObject,OLERENDER_FORMAT,
  320. &fm,lpClientSite,pStorage,(void **)&pOleObject);
  321. if(sc!=S_OK)
  322. AfxThrowOleException(sc);
  323. //插入OLE对象
  324. REOBJECT reobject;
  325. ZeroMemory(&reobject, sizeof(REOBJECT));
  326. reobject.cbStruct = sizeof(REOBJECT);
  327. CLSID clsid;
  328. sc = pOleObject->GetUserClassID(&clsid);
  329. if (sc != S_OK)
  330. AfxThrowOleException(sc);
  331. reobject.clsid = clsid;
  332. reobject.cp = REO_CP_SELECTION;
  333. reobject.dvaspect = DVASPECT_CONTENT;
  334. reobject.poleobj = pOleObject;
  335. reobject.polesite = lpClientSite;
  336. reobject.pstg = pStorage;
  337. HRESULT hr = pRichEdit->GetIRichEditOle()->InsertObject( &reobject );
  338. delete pDataSource;
  339. }