1. RichEdit要嵌入ole objects必须要继承 IRichEditOleCallback 接口,这个接口让richEdit能够增加RichEdit对Ole的嵌入支持。
首先在RichEdit的OnCreate中调用SetOLECallback函数,这样就设置了IRichEditOleCallback的接口实现者。
在OnCreate中,还要记得注册 ole剪贴板格式,这个格式是我们自己定的,在处理复制和粘贴的时候,需要用到它。
- //注册自己的ole 剪贴板格式
- #define STR_OWN_OLE_CLIPBOARD_FORMAT _T("STR_OWN_OLE_CLIPBOARD_FORMAT")
- m_uOwnOleClipboardFormat = RegisterClipboardFormat(STR_OWN_OLE_CLIPBOARD_FORMAT);
这个接口的几个必须实现的接口函数:
它为一个来自剪贴板粘贴的对象提供新的存储。
- STDMETHODIMP CRichEditCtrlEx::GetNewStorage(THIS_ LPSTORAGE FAR * lplpstg)
- {
- //Create a flat storage and steal it from the client item
- //the client item is only used for creating the storage
- COleClientItem item;
- item.GetItemStorageFlat();
- *lplpstg = item.m_lpStorage;
- HRESULT hRes = E_OUTOFMEMORY;
- if (item.m_lpStorage != NULL)
- {
- item.m_lpStorage = NULL;
- hRes = S_OK;
- }
- return hRes;
- }
(2)QueryInsertObject
它处理来自ole object的插入请求,如果同意插入,就返回S_OK,否则返回E_NOTIMPL
在这个里面可以判断是否是自己需要的类型,如果不是,就可以拒绝插入。
- STDMETHODIMP CRichEditCtrlEx::QueryInsertObject(THIS_ LPCLSID lpclsid, LPSTORAGE lpstg,LONG cp)
- {
- if(CLSID_DynamicGif == *lpclsid)
- {
- //如果是CLSID_DynamicGif类型的嵌入对象,则支持,否则不支持
- return S_OK; //此语句用来显示一个嵌入对象
- }
- else
- {
- //否则
- return E_NOTIMPL;
- }
- }
(3) DeleteObject
它处理删除ole obj的请求,直接返回E_NOTIMPL即可。
- STDMETHODIMP CRichEditCtrlEx::DeleteObject(THIS_ LPOLEOBJECT lpoleobj)
- {
- //return S_OK;
- return E_NOTIMPL;
- }
(4) GetClipboardData
在这个地方处理复制或拖拽
创建一个 DataSource对象,将自己处理过的数据,存入ole 剪贴板,最好获得DataSource对象的 IDataObject接口,将它赋值给lpchrg参数。
- STDMETHODIMP CRichEditCtrlEx::GetClipboardData(THIS_ CHARRANGE FAR * lpchrg, DWORD reco,LPDATAOBJECT FAR * lplpdataobj)
- //在这里处理复制,剪切
- if (reco==RECO_COPY || reco==RECO_CUT)
- {
- //获得lpchrg对应的richedit的内容
- CString strText;
- GetTextRange(lpchrg->cpMin,lpchrg->cpMax,strText);
- //code text,存入剪贴板的为string ,通过XML编码string
- string strCodedText=ToCodedString(* lpchrg,strText);
- //创建一个 DataSource
- COleDataSource *pDataSource = new COleDataSource;
- int strBytes= strCodedText.length();
- HGLOBAL hG = GlobalAlloc(GMEM_DDESHARE, strBytes);
- void* pBuffer = GlobalLock(hG);
- {
- memcpy(pBuffer, strCodedText.c_str(), strBytes);
- GlobalUnlock(hG);
- }
- FORMATETC fmt;
- fmt.cfFormat = m_uOwnOleClipboardFormat;
- fmt.dwAspect = DVASPECT_CONTENT;
- fmt.lindex = -1;
- fmt.ptd = NULL;
- fmt.tymed = TYMED_HGLOBAL;
- STGMEDIUM stg;
- stg.tymed = TYMED_HGLOBAL;
- stg.hGlobal = hG;
- stg.pUnkForRelease = NULL;
- pDataSource->CacheData(m_uOwnOleClipboardFormat,&stg, &fmt);
- //将 pDataSource的 IDataObject接口赋值给 lplpdataobj
- *lplpdataobj= (IDataObject *)pDataSource->GetInterface(&IID_IDataObject);
- return S_OK;
- }
- return E_NOTIMPL;
(5) QueryAcceptData
当有粘贴操作或者拖放操作的时候,询问是否应该接受这些操作。
可以在这里处理粘贴和拖放,然后解析来自ole 剪贴板的数据,然后把他输出到richedit中。这些ole 剪贴板中的数据,是在GetClipboardData中写入的。
- STDMETHODIMP CRichEditCtrlEx::QueryAcceptData(THIS_ LPDATAOBJECT lpdataobj, CLIPFORMAT FAR * lpcfFormat, DWORD reco,BOOL bReally, HGLOBAL hMetaPict)
- {
- USES_CONVERSION;
- if (!bReally) // just query
- {
- //return E_NOTIMPL;
- return S_OK;
- }
- //只处理粘贴
- switch(reco)
- {
- case RECO_PASTE:
- case RECO_DROP:
- {
- COleDataObject odo;
- odo.Attach(lpdataobj);
- //如果 m_uOwnOleClipboardFormat 剪贴板格式可用
- if (odo.IsDataAvailable(m_uOwnOleClipboardFormat))
- {
- STGMEDIUM stg;
- VERIFY(odo.GetData(m_uOwnOleClipboardFormat, &stg));
- int nSize = GlobalSize(stg.hGlobal);
- void* pBuffer = GlobalLock(stg.hGlobal);
- {
- //在这个地方复制插入进去......
- string strText= string((char *)pBuffer);
- //解码 XML 元素
- CXmlParser xmlParser;
- xmlParser.BeginDecodeString(strText);
- xmlParser.EndDecodeString();
- vector<XML_OBJ_STRUCT> vec_xml_objs=xmlParser.GetDecodeString();
- //遍历vec_xml_objs ,插入元素
- vector<XML_OBJ_STRUCT>::iterator iter_begin=vec_xml_objs.begin();
- vector<XML_OBJ_STRUCT>::iterator iter_end=vec_xml_objs.end();
- for (;iter_begin!=iter_end;++iter_begin)
- {
- if (iter_begin->obj_type== STR_OBJ_TYPE)
- {
- CString strToInsert=A2CT(iter_begin->str_obj_struct.strText.c_str()) ;
- InsertText(theApp.g_edit_font_,strToInsert,FALSE,TRUE,TRUE,FALSE);
- }
- else if (iter_begin->obj_type==OLE_OBJ_TYPE)
- {
- CString strPathToInsert=A2CT(iter_begin->ole_obj_struct.strOleFilePath.c_str());
- int index= iter_begin->ole_obj_struct.iIndex;
- //插入 ole obj
- if (index>=MAX_EMOTION_INDEX_NUMBER + BMP_INDEX_OFFSET_GAP)
- { //如果是复制的 BMP,那么就重新计算index
- InsertPicImpl(strPathToInsert,0,true,true);
- }
- else
- {
- InsertPicImpl(strPathToInsert,index,false,true);
- }
- }
- }
- GlobalUnlock(stg.hGlobal);
- }
- odo.Detach();
- return S_OK;
- }
- else if (odo.IsDataAvailable(CF_TEXT))
- {
- odo.Detach();
- return S_OK;
- }
- odo.Detach();
- return E_FAIL;
- }
- break;
- case RECO_COPY:
- break;
- case RECO_CUT:
- break;
- case RECO_DRAG:
- break;
- default:
- break;
- }
- return E_NOTIMPL;
- }
(6) GetContextMenu
这个函数处理右键菜单。
- HMENU CRichEditCtrlEx::GetContextMenuInner(WORD seltype, LPOLEOBJECT lpoleobj, CHARRANGE* lpchrg)
- {
- //创建一个弹出式菜单
- CMenu popmenu;
- popmenu.CreatePopupMenu();
- UINT nSel = ((GetSelectionType() != SEL_EMPTY) ? 0 : MF_GRAYED);
- UINT nPaste = ((CanPaste()||IsClipboardFormatAvailable(CF_BITMAP)|| IsClipboardFormatAvailable(m_uOwnOleClipboardFormat)) ? 0 : MF_GRAYED);
- //添加菜单项目
- if(read_only_)
- {
- popmenu.AppendMenu(0, ID_RICH_COPY, TEXT("复制(&C)"));
- popmenu.EnableMenuItem(ID_RICH_COPY, MF_BYCOMMAND|nSel);
- }
- else
- {
- popmenu.AppendMenu(0, ID_RICH_CUT, TEXT("剪切(&X)"));
- popmenu.AppendMenu(0, ID_RICH_COPY, TEXT("复制(&C)"));
- popmenu.AppendMenu(0, ID_RICH_PASTE, TEXT("粘贴(&V)"));
- //popmenu.AppendMenu(MF_SEPARATOR);
- //popmenu.AppendMenu(0, ID_RICH_SETFONT, TEXT("选择字体"));
- popmenu.EnableMenuItem(ID_RICH_CUT, MF_BYCOMMAND|nSel);
- popmenu.EnableMenuItem(ID_RICH_COPY, MF_BYCOMMAND|nSel);
- popmenu.EnableMenuItem(ID_RICH_PASTE, MF_BYCOMMAND|nPaste);
- }
- if(seltype == SEL_OBJECT)
- {
- popmenu.AppendMenu(MF_SEPARATOR);
- popmenu.AppendMenu(MF_STRING, IDM_CHAT_DLG_SAVE_OLE_IMG, TEXT("另存为..."));
- }
- //显示菜单
- POINT pt;
- GetCursorPos(&pt);
- DWORD dwCmd = popmenu.TrackPopupMenu(TPM_LEFTALIGN|TPM_TOPALIGN|TPM_RETURNCMD, pt.x, pt.y, this);
- popmenu.DestroyMenu();
- switch(dwCmd)
- {
- case ID_RICH_COPY:
- {
- Copy();
- break;
- }
- case ID_RICH_CUT:
- {
- Cut();
- break;
- }
- case ID_RICH_PASTE:
- {
- Paste();
- break;
- }
- case IDM_CHAT_DLG_SAVE_OLE_IMG:
- {
- CComPtr<IGGGifCtrl> pGifCtrl;
- HRESULT hr = lpoleobj->QueryInterface(&pGifCtrl);
- if(SUCCEEDED(hr))
- {
- if(pGifCtrl)
- {
- BSTR bstrFile;
- pGifCtrl->GetFilePath(&bstrFile);
- // 保存文件到另外一个文件,这里控件根据控件中文件类型的不同设置
- // 不同的扩展名,如果采用对话框的形式保存文件时注意分析文件的扩展名,来正确的保存文件类型。
- if(_bstr_t(bstrFile).length())
- {
- CString strSrcFilePath = bstrFile;
- SaveOleImgToFile(strSrcFilePath);
- }
- }
- }
- break;
- }
- default:
- break;
- }
- return NULL;
- }
1. RichEdit中,怎么实现对复制的内容中,什么是普通文本,什么是ole对象的识别。
(1) 首先,创建一个Manager类,类里面有个vector,用来管理richedit中的 ole对象对应的结构体列表。
另外一个结构体OleStruct用来存储ole对象的相关的信息。
这些信息包括:ole对象在richedit中的位置nPos,这个很重要,因为在处理复制的时候,需要通过这个来判断,复制的是否是文字还是ole对象。
Ole对象的Index,如果有的话
Ole对象的 path,也就是插入到richedit的 图像的路径,这个是最重要的。
其他的一些信息。
Manager类 提供一个方法,这个方法传入一个位置nPos,如果这个位置是一个ole obj ,那么返回这个ole object对应的vector中的OleStruct对象,否则返回NULL.
在处理复制的时候,就调用这个方法,来将所有的ole obj的数据,替换为编码过的OleStruct对象的数据。然后在处理粘贴的时候,又解码,将对应的Ole object对象插入到
RichEdit中。
(2) 然后,响应richedit的 EN_CHANGE消息
注意在richEdit的OnCreate函数中启用EN_CHANGE消息,否则收不到这个消息:
//设置让 EN_CHANGE 生效
SetEventMask(GetEventMask() | ENM_CHANGE);
在EN_CHANGE消息响应函数中:
首先获得整个richedit的内容,然后遍历内容,将所有的Ole objects 的信息都收集到 ole对象管理器中,这样就可以随时查询ole objects的相关信息了。
(3) 在处理复制和拖拽的时候,首先获得复制的内容,通过查询ole 对象管理器,可以知道对应的内容是否是ole obj对象。
将文本和ole 对象的数据分别用XML文档编码,编码为下面的两种类型:
typedef struct _STR_OBJ_STRUCT
{
string strText; //文本内容
}STR_OBJ_STRUCT;
typedef struct _OLE_OBJ_STRUCT
{
string strOleFilePath; //路径
int iIndex; //index
}OLE_OBJ_STRUCT;
然后再用XML插入这两种类型的结点,最终获得XML 的字符串。
通过ole剪贴板,将这个编码过的字符串保存起来。
(4)在处理粘贴或拖放的时候:
获得编码过的XML文本,然后解析XML文本,获得
STR_OBJ_STRUCT结构体和OLE_OBJ_STRUCT结构体的对象。
依次遍历这些对象,将他们插入到richedit中。这样就让richedit增加了对ole对象的复制粘贴和拖拽的支持。