#pragma once
#include <richedit.h>
#include <textserv.h>
#include <RichOle.h>
#include "CaretWindow.h"
#include "richeditolemgr.h"
#pragma comment(lib, "Riched20.lib")
class RichEditBase;
// RichEdit及其Callback的实现,可以参考MFC CRichEditView
// RichEidt的ole view的实现,可以参考ATL插入一个控件对象的实现代码
//
//
// Q1. 无窗口RE是怎么创建的?
// a. 调用CreateTextSevices()
// b. ??
//
// Q2. 无窗口RE是怎么绘制到窗口上面的?
// a. 拿到一个内存DC?
// b. 调用itextservices->txdraw方法,随便提供一个clientrect,这样就能在该窗口区域上显示一个白色背景了
// c.
//
// Q3. 无窗口RE是怎么决定自己在窗口上的范围的?
// a. Draw的时候给出的clientrect就是显示的范围
// b. 然后在其它时候判断当前鼠标是否位于窗口内或者返回TxGetClientRect
//
// Q4. 怎么输入文字?
// a. 将WM_CHAR消息传递给service text即可。需要注意的是模态对话框是收不到WM_CHAR消息的。
//
// Q5. 输入文字后是怎么刷新显示的?
// a. 内部将调用TxInvalidateRect接口,在该接口中,我们自己去实现为::InvalidateRect
//
// Q6. 怎么设置它的样式?例如多行编辑功能?
// a. TxGetPropertyBits 返回的
//
// Q7. 当有选中区域时,发现光标的位置显示不太准确,怎么才能在有选中区域时不显示光标?
// a. 其实这个时候TxCreateCaret(HBITMAP hbmp 这个hbmp是不为空的,使用它创建的caret就不会显示。why?
//
// Q8. 为什么我用CCaretWindow实现光标时,不能做到输入文字时将系统光标隐藏?
//
//
// Q9. 光标是怎么销毁的?
// a. 向text service传递kill focus消息,而且还得传递set focus消息,否则再次得到焦点时光标不显示。
//
// Q10.为什么我向textservice发送SETFOCUS消息后,却不会响应TxCreateCaret消息
// a. 因为没有调用OnTxInPlaceActivate
//
// Q11.在控件位置改变后,怎么去同步光标的位置?
// a. 向txt service发送一个通知:m_spTextServices->OnTxPropertyBitsChange(TXTBIT_CLIENTRECTCHANGE, TRUE);
//
// Q12.为什么拖拽时,刷新会出现异常?先添加后删除,但是分开刷新的
//
// Q13.如何显示滚动条?
// 在TxGetScrollBars中,返回你的m_dwStyle样式。因此需要在m_dwStyle中指定
// WM_HSCROLL,WM_VSCROLL...
//
// Q14.OLE的生命周期.
// 因为richedit的undo功能,对象会一直保存在内存中,直到richedit关闭。
//
// 1. WM_KEYDOWN delete键删除对象时,IRichEditOleCallback::DeleteObject。这里不能调用Release
// > UIDLL.dll!IRichEditOleCallbackImpl<WindowlessRichEdit>::DeleteObject(IOleObject * lpoleobj=0x0011fc90) 行251 C++
// riched20.dll!CObjectMgr::ReplaceRange() + 0x9a 字节
// riched20.dll!CRchTxtPtr::ReplaceRange() + 0x18dde 字节
// riched20.dll!CTxtRange::ReplaceRange() + 0x80 字节
// riched20.dll!CTxtSelection::ReplaceRange() + 0x1f9 字节
// riched20.dll!CTxtSelection::Backspace() + 0xc0 字节
// riched20.dll!CTxtEdit::OnTxKeyDown() + 0x5d4 字节
// riched20.dll!CTxtEdit::TxSendMessage() + 0x21f3 字节
// UIDLL.dll!WindowlessRichEdit::OnPostHandleMsg(HWND__ * hWnd=0x00220428, unsigned int Msg=256, unsigned int wParam=8, long lParam=917505) 行196 + 0x39 字节 C++
//
// 2. DeleteObject之后,接着会响应IOleObject::Close。 这里不能调用Release
// 将一个OLE对象用delete键给删除掉时,会调用。然后将对象用ctrl+z再还原,然后再删除时,又会调用一次
//
// > UIDLL.dll!CGifOleObject::Close(unsigned long dwSaveOption=0) 行50 C++
// riched20.dll!COleObject::Close() + 0x2d 字节
// riched20.dll!CReplaceObjectAE::OnCommit() + 0xa 字节
// riched20.dll!CommitAEList() + 0x21 字节
// riched20.dll!CGenUndoBuilder::Done() + 0xf276 字节
// riched20.dll!CTxtEdit::TxSendMessage() + 0x11b 字节
// UIDLL.dll!WindowlessRichEdit::OnPostHandleMsg(HWND__ * hWnd=0x001504fc, unsigned int Msg=256, unsigned int wParam=8, long lParam=917505) 行196 + 0x39 字节 C++
//
// 3. 销毁. 在ole对象已不在undo范围时,richedit才会将该对象release掉,此时CGifOleObject被释放
// 堆栈如下:
// > UIDLL.dll!CGifOleObject::~CGifOleObject() 行13 C++
// UIDLL.dll!CGifOleObject::`scalar deleting destructor'() + 0x2b 字节 C++
// UIDLL.dll!CGifOleObject::Release() 行66 + 0x2b 字节 C++
// riched20.dll!SafeReleaseAndNULL() + 0x17 字节
// riched20.dll!COleObject::DisconnectObject() + 0x95 字节
// riched20.dll!COleObject::CleanupState() + 0x33 字节
// riched20.dll!COleObject::MakeZombie() + 0xa 字节
// riched20.dll!CReplaceObjectAE::Destroy() + 0x1a 字节
// riched20.dll!DestroyAEList() + 0x1d 字节
// riched20.dll!CUndoStack::ClearAll() + 0x2c 字节
// riched20.dll!CGenUndoBuilder::Done() + 0x1c56d 字节
// riched20.dll!CTxtEdit::TxSendMessage() + 0x11b 字节
// UIDLL.dll!WindowlessRichEdit::OnChar(unsigned int uMsg=258, unsigned int wParam=97, long lParam=1966081) 行297 + 0x39 字节 C++
//
// Q15. Rich Edit 剪贴板操作
// http://zjqzy03080312.blog.163.com/blog/static/1857428072012124112714348/
//
// 应用程序可以粘贴剪贴板中内容到一个Rich Edit控件中,采用最佳可用剪贴板格式或者指定的剪贴板格式。
// 你也可以决定是否一个Rich Edit控件可以粘贴某种剪贴板格式。
//
// 对于一个Edit控件而言,你可以使用WM_COPY或者WM_CUT消息来拷贝或者剪切当前选中内容。同样的,你可
// 以使用WM_PASTE消息将这些剪贴板内容粘贴到一个Rich Edit控件中。控件将粘贴它所识别的第一个可用格式,
// 这大概是最具描述性的格式。
//
// 你可以使用EM_PASTESPECIAL消息来粘贴指定的剪贴板格式。这个消息对具有"特殊粘贴"命令的应用程序很有
// 用,该命令可以让用户选择剪贴板格式。你可以使用EM_CANPASTE消息来决定控件是否识别某种指定的格式。
//
// 你也可以使用EM_CANPASTE消息来决定Rich Edit控件是否识别所有可用的剪贴板格式。该消息在处理WM_INITMENUPOPUP
// 消息时很有用。应用程序可以启用或者屏蔽"粘贴"命令,取决于控件是否可以粘贴任一个可用类型。
//
// Rich Edit控件注册两种剪贴板格式:"富文本格式(RTF)"和一种叫做"RichEdit 文本与对象"的格式。应用程序
// 可以使用RegisterClipboardFormat函数来注册这些格式,其取值为CF_RTF与CF_RETEXTOBJ
//
class UIAPI ITextHostImpl : public ITextHost
{
public:
ITextHostImpl();
~ITextHostImpl(){};
// ITextHost Interface
virtual HDC TxGetDC();
virtual INT TxReleaseDC(HDC hdc);
// virtual BOOL TxShowScrollBar(INT fnBar, BOOL fShow);
// virtual BOOL TxEnableScrollBar (INT fuSBFlags, INT fuArrowflags);
// virtual BOOL TxSetScrollRange(INT fnBar,LONG nMinPos,INT nMaxPos,BOOL fRedraw);
// virtual BOOL TxSetScrollPos (INT fnBar, INT nPos, BOOL fRedraw);
// virtual void TxInvalidateRect(LPCRECT prc, BOOL fMode);
virtual void TxViewChange(BOOL fUpdate);
virtual BOOL TxCreateCaret(HBITMAP hbmp, INT xWidth, INT yHeight);
virtual BOOL TxShowCaret(BOOL fShow);
virtual BOOL TxSetCaretPos(INT x, INT y);
virtual BOOL TxSetTimer(UINT idTimer, UINT uTimeout);
virtual void TxKillTimer(UINT idTimer);
virtual void TxScrollWindowEx (INT dx,INT dy,LPCRECT lprcScroll,LPCRECT lprcClip,HRGN hrgnUpdate,LPRECT lprcUpdate,UINT fuScroll);
virtual void TxSetCapture(BOOL fCapture);
virtual void TxSetFocus();
virtual void TxSetCursor(HCURSOR hcur, BOOL fText);
virtual BOOL TxScreenToClient (LPPOINT lppt);
virtual BOOL TxClientToScreen (LPPOINT lppt);
virtual HRESULT TxActivate( LONG * plOldState );
virtual HRESULT TxDeactivate( LONG lNewState );
// virtual HRESULT TxGetClientRect(LPRECT prc);
// virtual HRESULT TxGetViewInset(LPRECT prc);
virtual HRESULT TxGetCharFormat(const CHARFORMATW **ppCF );
virtual HRESULT TxGetParaFormat(const PARAFORMAT **ppPF);
virtual COLORREF TxGetSysColor(int nIndex);
// virtual HRESULT TxGetBackStyle(TXTBACKSTYLE *pstyle);
virtual HRESULT TxGetMaxLength(DWORD *plength);
virtual HRESULT TxGetScrollBars(DWORD *pdwScrollBar);
virtual HRESULT TxGetPasswordChar(TCHAR *pch);
virtual HRESULT TxGetAcceleratorPos(LONG *pcp);
virtual HRESULT TxGetExtent(LPSIZEL lpExtent);
virtual HRESULT OnTxCharFormatChange (const CHARFORMATW * pcf);
virtual HRESULT OnTxParaFormatChange (const PARAFORMAT * ppf);
virtual HRESULT TxGetPropertyBits(DWORD dwMask, DWORD *pdwBits);
virtual HRESULT TxNotify(DWORD iNotify, void *pv);
// Far East Methods for getting the Input Context
//#ifdef WIN95_IME
virtual HIMC TxImmGetContext();
virtual void TxImmReleaseContext( HIMC himc );
//#endif
virtual HRESULT TxGetSelectionBarWidth (LONG *lSelBarWidth);
// 外部设置方法 (部分参考microsoft windowlessRE工程)
bool IsPasswordMode() { return m_fPassword; }
void SetPasswordMode(bool b);
WCHAR SetPasswordChar(WCHAR chPasswordChar);
LONG SetAccelPos(LONG l_accelpos);
bool SetFont(LOGFONT* plf);
void InitDefaultParaFormat();
bool IsWordWrap() { return m_fWordWrap; }
void SetWordWrap(bool fWordWrap);
bool IsReadOnly() { return (m_dwStyle & ES_READONLY) != 0; }
void SetReadOnly(bool fReadOnly);
DWORD GetMaxLength() { return m_dwMaxLength; }
void SetMaxLegnth(DWORD dw);
LONG GetSelBarWidth();
LONG SetSelBarWidth(LONG l_SelBarWidth);
bool GetRichTextFlag() { return m_fRich; }
void SetRichTextFlag(bool b);
void RevokeDragDrop(void);
void RegisterDragDrop(void);
bool SetText(const TCHAR* szText);
IRichEditOle* GetRichEditOle() { return m_spOle; }
protected:
// unknown attribute
// SIZE m_sizeExtent; // text service 用来实现缩放的参数。Each HIMETRIC unit corresponds to 0.01 millimeter.
int m_nxPerInch;
int m_nyPerInch;
LONG m_laccelpos; // Accelerator position
// 已知属性
DWORD m_dwStyle; // 编辑框样式
WCHAR m_chPasswordChar; // Password character, TODO: 该接口未测试过
DWORD m_dwMaxLength; // 最大输入内容长度
LONG m_lSelBarWidth; // 类似于VS的左侧(显示行数字),专门用于点击选中一行的东东
unsigned m_fWordWrap:1; // Whether control should word wrap
unsigned m_fRich:1; // Whether control is rich text
unsigned m_fRegisteredForDrop:1; // whether host has registered for drop
unsigned m_fPassword:1; //
CHARFORMAT2W m_cf; // Default character format
PARAFORMAT m_pf; // Default paragraph format
// 其它资源、数据
CComPtr<ITextServices> m_spTextServices;
CComPtr<IRichEditOle> m_spOle;
HWND m_hParentWnd; // 所在的窗口句柄
CCaret m_caret; // 光标(集成了系统光标和分层窗口模拟光标两套)
};
interface ITextEditControl
{
virtual BOOL ProcessWindowMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT& lResult, DWORD dwMsgMapID = 0) = 0;
};
// 条件判断
#define PRE_HANDLE_MSG() \
{ \
SetMsgHandled(TRUE); \
lResult = OnPreHandleMsg(hWnd,uMsg,wParam,lParam); \
if(IsMsgHandled()) \
return FALSE; \
}
// 默认处理
#define POST_HANDLE_MSG() \
{ \
SetMsgHandled(TRUE); \
lResult = OnPostHandleMsg(hWnd,uMsg,wParam,lParam); \
if(IsMsgHandled()) \
return TRUE; \
}
class UIAPI WindowlessRichEdit : public ITextHostImpl,
public ITextEditControl
, public IRichEditOleCallback
{
public:
WindowlessRichEdit(RichEditBase*);
~WindowlessRichEdit(void);
public:
//////////////////////////////////////////////////////////////////////////
// 处理从父窗口中转发过来的消息
BEGIN_MSG_MAP_EX(WindowlessRichEdit)
PRE_HANDLE_MSG()
MSG_WM_SETCURSOR(OnSetCursor)
MSG_WM_KILLFOCUS(OnKillFocus)
MSG_WM_WINDOWPOSCHANGED(OnWindowPosChanged)
MESSAGE_HANDLER_EX(WM_KEYDOWN, OnDefaultHandle)
MESSAGE_HANDLER_EX(WM_CHAR, OnChar)
MESSAGE_RANGE_HANDLER_EX(WM_MOUSEFIRST,WM_MOUSELAST, OnDefaultHandle)
MESSAGE_HANDLER_EX(WM_SETFOCUS, OnDefaultHandle)
MESSAGE_HANDLER_EX(WM_VSCROLL, OnDefaultHandle)
MESSAGE_HANDLER_EX(WM_HSCROLL, OnDefaultHandle)
// POST_HANDLE_MSG()
END_MSG_MAP()
protected:
LRESULT OnPreHandleMsg( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
LRESULT OnPostHandleMsg( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam );
BOOL OnSetCursor(HWND wnd, UINT nHitTest, UINT message);
LRESULT OnDefaultHandle(UINT uMsg, WPARAM wParam, LPARAM lParam);
void OnKillFocus(HWND wndFocus);
void OnObjectPosChanged(UINT fwSide, LPRECT pRect);
LRESULT OnChar(UINT uMsg, WPARAM wParam, LPARAM lParam);
void OnWindowPosChanged(LPWINDOWPOS);
protected:
// *** IRichEditOleCallback methods ***
virtual HRESULT __stdcall GetNewStorage(LPSTORAGE FAR * lplpstg);
virtual HRESULT __stdcall GetInPlaceContext(LPOLEINPLACEFRAME FAR * lplpFrame, LPOLEINPLACEUIWINDOW FAR * lplpDoc, LPOLEINPLACEFRAMEINFO lpFrameInfo) ;
virtual HRESULT __stdcall ShowContainerUI(BOOL fShow) ;
virtual HRESULT __stdcall QueryInsertObject(LPCLSID lpclsid, LPSTORAGE lpstg, LONG cp) ;
virtual HRESULT __stdcall DeleteObject(LPOLEOBJECT lpoleobj) ;
virtual HRESULT __stdcall QueryAcceptData(LPDATAOBJECT lpdataobj, CLIPFORMAT FAR * lpcfFormat, DWORD reco, BOOL fReally, HGLOBAL hMetaPict) ;
virtual HRESULT __stdcall ContextSensitiveHelp(BOOL fEnterMode) ;
virtual HRESULT __stdcall GetClipboardData(CHARRANGE FAR * lpchrg, DWORD reco, LPDATAOBJECT FAR * lplpdataobj) ;
virtual HRESULT __stdcall GetDragDropEffect(BOOL fDrag, DWORD grfKeyState, LPDWORD pdwEffect) ;
virtual HRESULT __stdcall GetContextMenu(WORD seltype, LPOLEOBJECT lpoleobj, CHARRANGE FAR * lpchrg, HMENU FAR * lphmenu) ;
public:
// Helper
WORD GetSelectionType() const
{
LRESULT lr = 0;
m_spTextServices->TxSendMessage(EM_SELECTIONTYPE, 0, 0L, &lr);
return (WORD)lr;
}
bool GetSelectionOleObject(RichEditOleObjectItem** ppItem);
// Function
bool Create(HWND hWndParent);
void Draw(HDC);
bool HitTest(POINT pt);
//
bool InsertOleObject(RichEditOleObjectItem* pItem);
bool InsertGif(const TCHAR* szGifPath);
// Call this function to paste the OLE item in dataobj into this rich edit document/view.
void DoPaste(LPDATAOBJECT pDataObject, CLIPFORMAT cf, HMETAFILEPICT hMetaPict);
protected:
// Creation IDataObject from IDataObject (used for drag-drop, paste)
IOleObject* CreateOleObjectFromData(LPDATAOBJECT pDataObject, bool bOleCreateFromDataOrOleCreateStaticFromData = true, OLERENDER render = OLERENDER_DRAW, CLIPFORMAT cfFormat = 0, LPFORMATETC lpFormatEtc = NULL);
int GetObjectTypeByOleObject(LPOLEOBJECT pOleObject);
public:
// IUnknown Interface
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,void **ppvObject);
virtual ULONG STDMETHODCALLTYPE AddRef(void);
virtual ULONG STDMETHODCALLTYPE Release(void);
// ITextHost Interface
// 需要根据控件属性进行定制的接口放在这里实现,其它接口接口放在ITextHostImpl中实现
virtual BOOL TxShowScrollBar(INT fnBar, BOOL fShow);
virtual BOOL TxEnableScrollBar (INT fuSBFlags, INT fuArrowflags);
virtual BOOL TxSetScrollRange(INT fnBar,LONG nMinPos,INT nMaxPos,BOOL fRedraw);
virtual BOOL TxSetScrollPos (INT fnBar, INT nPos, BOOL fRedraw);
virtual HRESULT TxGetClientRect(LPRECT prc);
virtual void TxInvalidateRect(LPCRECT prc, BOOL fMode);
virtual HRESULT TxGetBackStyle(TXTBACKSTYLE *pstyle);
virtual HRESULT TxGetViewInset(LPRECT prc);
protected:
void ClearOleObjects();
protected:
RichEditBase* m_pRichEditBase;
RichEditOleObjectManager m_olemgr;
protected:
// 非windowless richedit要调用的初始化函数
static LPCTSTR GetLibraryName();
void InitRichEidtDll();
void ReleaseRichEidtDll();
static HMODULE s_RichEditDll;
static LONG s_refDll;
public:
//RichEdit formats
static UINT s_cfRichTextFormat; // CLIPFORMAT
static UINT s_cfRichTextAndObjects; // CLIPFORMAT
};
//#include <atlctl.h>