限于一直使用微软的richedit控件很多东西有问题都摸不着头脑,而工程使用中其实需要用到的并不高级需求也不高,需求就是快速简单的实现一个图文并茂的富文本显示工具;为此 ,我开始尝试设计一个简单易用的富文本显示和编辑工具,现在初步核心框架已经完成,为此分享给各位我的设计思路,欢迎拍砖~.~。 源代码在文章的结束处可下载。
struct StyleObj { enum StyleType { ST_CHAR = 0, ST_OBJ }; enum StyleRemoveType { SRT_SUCCEED = 0, SRT_ALLDELETE, SRT_NOINRANGE }; int type_;//style樣式,字符串樣式或者obj樣式 int charPos_;//樣式在字符串中起點位置 int charLength_;//樣式覆蓋字符串長度 CharFormat cf_;//樣式 IObj* obj_;//擴展對象 LinkObj* link_;//鏈接對象 HFONT font_;//樣式對應字體 int nRef_;//引用計數器 };鉴于篇幅,我在此只列出我们需要用到的基本属性;
struct LineObj : public IBaseObj { enum PosType{ low = 0, in, beyond }; int beginPos_;//行在字符串中起點位置 int endPos_;//行在字符串中終點位置 int width_;//行寬 int lineIndex_;//行索引 RowObjDetail Details;//行詳細屬性,最高高度,最高高度位置 int indent_;//for paragraph,段落 int yPos_;//行頂部在視口Y軸座標 int nRef_;//對象引用計數 };所有涉及到的辅组数据结构我将在文章结束的时候附上;
/*消息處理類,負責最外層收到消息後的分配和相關響應處理*/ class RichEditor { public: RichEditor(); protected: bool bTraceMouse_; bool bLBButtonDown_; Selection selectRange_; Document document_; DocumentDetails details_; Window window_; CHARFORMAT cfDefault_; CaretPosition caretPosition_; }; /*文檔操作類,負責文檔的插入刪除,更新,查找等功能*/ class Document { public: Document(); protected: SplitVector<MY_CHAR> data;//if is obj, use the '%' as symbol StyleObjManager styleList_; LineObjManager LineObjList_; DocumentDetails* details_; int ident_; Window* window_; Selection* selectionRange_; }; /*窗口類,負責最終繪畫和長度計算,窗口基本屬性存儲*/ class Window { public: Window(); ~Window(); void Attach(HWND hWnd); void Detch(); int GetCharLenght(MY_CHAR c, CHARFORMAT& cf, HFONT font); int GetTextLenght(MY_CHAR* c, int length, CHARFORMAT& cf, HFONT font); void PaintText(MY_CHAR* c, int length, RECT& rcText, CHARFORMAT& cf, HDC hdcPaint, HFONT font); void PaintObj(IObj* obj, RECT& rcObj, HDC hdcPaint, bool bSelect = false); void ReDraw(); HWND GetWnd(); protected: HWND hWnd_; CHARFORMAT cfCur_; bool bSeletionMode_; bool bDoubleBuffering_; HBRUSH m_brushFrame; HBRUSH m_brushSelect; };
void Document::InsertString(const MY_CHAR* c, int pos, int length, CHARFORMAT& cf, MY_CHAR* hyperLinkData) { int begin = 0; int begin1 = 0; int end = length; const MY_CHAR* cTemp = c; const MY_CHAR* cTemp1 = c; styleList_.Log(); styleList_.InsertStyle(pos, length, cf, hyperLinkData); styleList_.Log(); while(begin1 < end) { if (*cTemp == '\n') { int insertLenght = begin1-begin; data.InsertFromArray(pos, cTemp1, 0, insertLenght); UpdateLineManager_insert(pos, insertLenght); pos += begin1; begin = begin1; cTemp1 = cTemp; } cTemp++; begin1++; } if (begin < begin1) { int insertLenght = begin1-begin; data.InsertFromArray(pos, cTemp1, 0, insertLenght); UpdateLineManager_insert(pos, insertLenght); pos += begin1; begin = begin1; cTemp1 = cTemp; } ReDraw(); }
void Document::DeleteRange(int begin, int end) { data.DeleteRange(begin, end - begin + 1); Log(); UpdateStyleManager_delete(begin, end); UpdateLineManager_delete(begin, end); }
struct Selection { Selection():Begin_(-1), End_(-1) {} Selection(int nBegin, int nEnd):Begin_(nBegin), End_(nEnd) {} int Begin() { return Begin_; } int End() { return End_; } int GetSelectionLenght() { return (End_ - Begin_ + 1); } bool IsSelected() const { if ( Begin_ >= 0 && End_ >= 0 && Begin_ <= End_ ) return true; return false; } bool IsBeSelect(int pos) const { if (pos <= End_ && pos >= Begin_) return true; return false; } bool IsBeSelect(int begin, int end) const { if (begin <= End_ && end >= Begin_) return true; return false; } void UpdateSelection(int pos) { if (pos <= Begin_) { End_ = Begin_; Begin_ = pos; } else { End_ = pos; } } void ClearSelection() { Begin_ = -1; End_ = -1; } void Log(MY_CHAR* name) { ATLTRACE("\n%s: Begin_:%d ; End_:%d;\n" , name , Begin_ , End_); } int Begin_; int End_; }; struct IBaseObj { virtual int AddRef() = 0; virtual int Release() = 0; }; struct IObj : public IBaseObj { virtual const SIZE* GetSize() const = 0; virtual void OnDraw(HDC dc, RECT& rcObj) = 0; }; class ContextObj : public IObj { public: virtual const SIZE* GetSize() const { return &sz_; } protected: SIZE sz_; }; class ImageObj : public ContextObj { public: ImageObj():obj_(NULL){} void UpdatePos(int pos, int line) { posInLine_ = pos; ownLine_ = line; } protected: int posInLine_; int ownLine_; void* obj_; }; struct RowObjDetail { RowObjDetail() { maxHeight_ = 0; pos_ = -1; } int maxHeight_; int pos_; }; struct CaretDetail { CaretDetail() { ZeroMemory(&pt, sizeof(pt)); charPos_ = -1; line_ = -1; isLeft = true; } CaretDetail& operator = (const CaretDetail& other) { charPos_ = other.charPos_; line_ = other.line_; pt = other.pt; isLeft = other.isLeft; return *this; } bool IsEqual(const CaretDetail& other) { if (charPos_ == other.charPos_ && line_ == other.line_ && isLeft == other.isLeft) { return true; } return false; } void Log(MY_CHAR* name) { ATLTRACE("\n%s: charPos_:%d ; line_:%d; pt:[x:%d, y:%d] isLeft:%d \n" , name , charPos_ , line_ , pt.x , pt.y , isLeft ? 1 : 0); } int charPos_; int line_; POINT pt; bool isLeft; }; struct LinkObj : public IBaseObj { int charPos_; int nRef_; MY_CHAR* context_; LinkObj(int charPos, MY_CHAR* context) { int len = lstrlen(context); context_ = new MY_CHAR[len + 1]; lstrcpy(context_, context); charPos_ = charPos; nRef_ = 0; AddRef(); } ~LinkObj() { ClearContext(); } void ResetContext(MY_CHAR* context) { ClearContext(); context_ = context; } virtual int AddRef() { nRef_++; return nRef_; } virtual int Release() { nRef_--; if (nRef_ == 0) delete this; return nRef_; } protected: void ClearContext() { if(context_) delete context_; } };
源代码下载地址:http://www.oschina.net/code/snippet_564149_10792