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