RichEdit 無窗口(ITextHost)


#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>


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM