DRAGON中edit数据的封装

来源:互联网 发布:mac air 13寸 尺寸 编辑:程序博客网 时间:2024/04/29 06:52

由于操作系统提供的edit存在一系列的问题,如单行不居中,闪烁,无法在layerwnd中使用等,所以,基本上好的界面库都对edit进行了自行绘制。dragon数据使用editdata对数据进行封装。下面对这个类进行分析。

////<span style="white-space:pre"></span>封装编辑框的数据//class EditData{public:<span style="white-space:pre"></span>EditData();<span style="white-space:pre"></span>~EditData();public:<span style="white-space:pre"></span>void    BindToEdit(Edit* pEdit);<span style="white-space:pre"></span>void    SetText(const TCHAR*, bool& bUpdate);<span style="white-space:pre"></span>void    ReplaceChar(const TCHAR& c, bool& bUpdate);<span style="white-space:pre"></span>void    ReplaceString(const String& str, bool& bUpdate);<span style="white-space:pre"></span>void    Delete(bool& bUpdate);<span style="white-space:pre"></span>void    BackSpace(bool& bUpdate);    void    DeleteSelectionText(bool& bUpdate);<span style="white-space:pre"></span>void    SetCaret(int nCaret, bool bSetSelStart, bool& bUpdate);<span style="white-space:pre"></span>void    CutToClipboard();<span style="white-space:pre"></span>void    CopyToClipboard();<span style="white-space:pre"></span>void    PasteFromClipboard();<span style="white-space:pre"></span>void    GetPriorItemPos(int nCP, int* pPrior);<span style="white-space:pre"></span>void    GetNextItemPos(int nCP, int* pNext);<span style="white-space:pre"></span>void    SetMaxChar(int nMax);<span style="white-space:pre"></span>void    SetMaxChar(int nMax, bool bUpdate);<span style="white-space:pre"></span>void    SetInsertMode(bool bInsertOrOverride);<span style="white-space:pre"></span>bool    GetInsertMode() { return m_bInsertMode; }<span style="white-space:pre"></span>void    GetText(String& str) { str = m_strText; }<span style="white-space:pre"></span>const String&  GetTextRef() { return m_strText; }<span style="white-space:pre"></span>const TCHAR*  GetText() { return m_strText.c_str(); }<span style="white-space:pre"></span>int     GetTextLength() { return (int)m_strText.length(); }<span style="white-space:pre"></span>int     GetCaretIndex() { return m_nCaret; }<span style="white-space:pre"></span>int     GetSelectionLength() { return abs(m_nCaret - m_nSelStart); }<span style="white-space:pre"></span>int     GetTextWidth()  { return m_nTextWidth; }<span style="white-space:pre"></span>void    GetSelectionInfo(int& nLeft, int& nRight) const;<span style="white-space:pre"></span>void    SetSelectionInfo(int nStart, int nEnd, bool& bUpdate);<span style="white-space:pre"></span>void    GetSelectionText(String& str);<span style="white-space:pre"></span>bool    IsSelectionExist();    bool    Clear();<span style="white-space:pre"></span>bool    CP2X(int nCP, int* pX);<span style="white-space:pre"></span>bool    X2CP(int nX, int* pnCP, int* pbTrailOrLead);<span style="white-space:pre"></span>void    DestroyStringAnalysis();protected:<span style="white-space:pre"></span>void    DeleteSelectionText();   // 该函数仅用于内部调用,不对数据进行处理,仅删除文本<span style="white-space:pre"></span>void    Fire_Text_Changed(BOOL bSetText=FALSE);<span style="white-space:pre"></span>bool    FilterString(const TCHAR* szSrc, String& strDest);<span style="white-space:pre"></span>bool    FilterChar(const TCHAR& cSrc, TCHAR& cDest);<span style="white-space:pre"></span>bool    StringAnalysis();private:<span style="white-space:pre"></span>String  m_strText;              // 编辑框的内容<span style="white-space:pre"></span>int     m_nMaxChar;             // 允许输入的最大字符数,-1表示无限制<span style="white-space:pre"></span>int<span style="white-space:pre"></span>    m_nSelStart;            // 选择的字符起点,当没有选区时<span style="white-space:pre"></span>m_nSelStart==m_nCaret<span style="white-space:pre"></span><span style="white-space:pre"></span>int     m_nCaret;               // 当前光标的位置,也标志着选区的End pos<span style="white-space:pre"></span>bool    m_bInsertMode;          // 插入/重写模式<span style="white-space:pre"></span>// UniScribe相关变量<span style="white-space:pre"></span>SCRIPT_CONTROL<span style="white-space:pre"></span>m_ScriptControl;<span style="white-space:pre"></span>// For uniscribe<span style="white-space:pre"></span>SCRIPT_STATE<span style="white-space:pre"></span>m_ScriptState;<span style="white-space:pre"></span>// For uniscribe<span style="white-space:pre"></span>SCRIPT_STRING_ANALYSIS<span style="white-space:pre"></span>m_Analysis;     // For cp2x, x2cp...<span style="white-space:pre"></span>int      m_nTextWidth;           // 当前字符的总长度<span style="white-space:pre"></span>Edit*    m_pEdit;};EditData::EditData(){m_pEdit = NULL;m_nMaxChar = -1;m_nSelStart = 0;m_nCaret = 0;ZeroMemory( &m_ScriptControl, sizeof( SCRIPT_CONTROL));ZeroMemory( &m_ScriptState, sizeof( SCRIPT_STATE));<span style="color:#ff6666;">ScriptApplyDigitSubstitution( NULL, &m_ScriptControl, &m_ScriptState); //uniscribe相关,详见后续博文</span>m_Analysis = NULL;m_nTextWidth = 0;m_bInsertMode = true;}
void EditData::BindToEdit(Edit* pEdit){m_pEdit = pEdit;}EditData::~EditData(){if (m_Analysis){ScriptStringFree(&m_Analysis);m_Analysis = NULL;}m_pEdit = NULL;}
void EditData::SetText(const TCHAR* szText, bool& bUpdate){bUpdate = false;String strInput;if (false == this->FilterString(szText, strInput))return;if (0 == m_nMaxChar)return;bUpdate = true;if (m_nMaxChar != -1){if ((int)strInput.length() > m_nMaxChar){m_strText = strInput.substr(0, m_nMaxChar);}else{m_strText = strInput;}}else{m_strText = strInput;}    m_nSelStart = m_nCaret = m_strText.length();this->Fire_Text_Changed(TRUE);}////在当前位置上面插入一个字符//void EditData::ReplaceChar(const TCHAR& c, bool& bUpdate){bUpdate = false;TCHAR cInput = _T('');if (false == this->FilterChar(c, cInput))return;bool bInsertOrOverride = m_bInsertMode;// 如果当前有被选择的文字,那么得覆盖这些文字if (IsSelectionExist()){bInsertOrOverride = true;  // 如果是覆盖模式,在删除完选区之后,应该转为插入,形成覆盖选区的样子bUpdate = true;DeleteSelectionText();}if (m_nCaret >= (int)m_strText.length()){bInsertOrOverride = true;  // 光标在文字末尾时,应该是插入}if (bInsertOrOverride){// 长度限制if ( m_nMaxChar >= 0 && (int)this->m_strText.length() >= (UINT)m_nMaxChar){}else{bUpdate = true;this->m_strText.insert( m_nCaret, 1, cInput);m_nCaret += 1;m_nSelStart = m_nCaret;}}else{// if (cInput != m_strText[m_nCaret]) 即使要覆盖的字符一样,也要发送改变的通知{bUpdate = true; m_strText.replace(m_nCaret,1,1,cInput);m_nCaret += 1;m_nSelStart = m_nCaret;}}if (bUpdate){this->Fire_Text_Changed();}}////在当前位置上面插入字符,如Copy,Paste//void EditData::ReplaceString(const String& str, bool& bUpdate){bUpdate = false;String strInput;if (false == this->FilterString(str.c_str(), strInput))return;bool bInsertOrOverride = m_bInsertMode;// 如果当前有被选择的文字,那么得覆盖这些文字if (IsSelectionExist()){bInsertOrOverride = true;   // 如果是覆盖模式,在删除完选区之后,应该转为插入,形成覆盖选区的样子bUpdate = true;DeleteSelectionText();}if(m_nCaret >= (int)m_strText.length()){bInsertOrOverride = true;  // 光标在文字末尾时,应该是插入}if (bInsertOrOverride){// 长度限制if (m_nMaxChar >= 0){int nRemain = m_nMaxChar - this->m_strText.length();if (0 == nRemain){if (bUpdate){this->Fire_Text_Changed();  // 由于删除了选区导致text changed}return;}if (nRemain < (int)strInput.length()){strInput = strInput.substr(0,nRemain);}}this->m_strText.insert(m_nCaret, strInput);}else{// 长度限制if ( m_nMaxChar >= 0){int nRemain = m_nMaxChar - m_nCaret;if (0 == nRemain){if (bUpdate){this->Fire_Text_Changed();  // 由于删除了选区导致text changed}return;}if (nRemain < (int)strInput.length()){strInput = strInput.substr(0,nRemain);}}this->m_strText.replace( m_nCaret, strInput.length(), strInput.c_str());}bUpdate = true;m_nCaret += strInput.length();m_nSelStart = m_nCaret;if (bUpdate){this->Fire_Text_Changed();}}////往后删除//void EditData::Delete(bool& bUpdate){bUpdate = true;// 删除当前所选择的文字if (IsSelectionExist()){this->DeleteSelectionText();}// 往后删除一个字符else{if (this->m_strText.length() == 0 || m_nCaret >= (int)this->m_strText.length()){bUpdate = false;}else{this->m_strText.erase( m_nCaret, 1);}}m_nSelStart = m_nCaret;if (bUpdate){this->Fire_Text_Changed();}}////往前删除//void EditData::BackSpace(bool& bUpdate){bUpdate = true;// 删除当前所选择的文字if (IsSelectionExist()){this->DeleteSelectionText();}// 往前删除一个字符else{if (m_nCaret <= 0 || this->m_strText.length() == 0){bUpdate = false;}else{m_nCaret--;this->m_strText.erase( m_nCaret, 1);}}m_nSelStart = m_nCaret;if (bUpdate){this->Fire_Text_Changed();}}////设置光标的位置,同时设置选区开始的位置//void EditData::SetCaret(int nCaret, bool bSetSelStart, bool& bUpdate){bUpdate = false;int nOldSelStart = m_nSelStart;int nOldCaret = m_nCaret;if (nCaret < 0){nCaret = 0;}else if (nCaret > (int)m_strText.length()){nCaret = m_strText.length();}m_nCaret = nCaret;if (bSetSelStart){m_nSelStart = m_nCaret;}if (m_nSelStart==m_nCaret && nOldCaret==nOldSelStart)           // 没有选区的光标移动{}else if (m_nSelStart == nOldCaret && m_nCaret == nOldSelStart) // 选择反向{}else{bUpdate = true;}}void EditData::GetPriorItemPos( int nCP, int* pPrior){if (NULL == pPrior)return ;*pPrior = nCP;  // Default is the char itselfif (NULL == m_Analysis){if (false ==this->StringAnalysis())return;}const SCRIPT_LOGATTR* pLogAttr = ScriptString_pLogAttr( m_Analysis);if (!pLogAttr)return;if (!ScriptString_pcOutChars(m_Analysis))return;int nInitial = *ScriptString_pcOutChars(m_Analysis);if (nCP - 1 < nInitial)nInitial = nCP - 1;for (int i = nInitial; i > 0; --i)if (pLogAttr[i].fWordStop ||       // Either the fWordStop flag is set( !pLogAttr[i].fWhiteSpace &&  // Or the previous char is whitespace but this isn't.pLogAttr[i - 1].fWhiteSpace)){*pPrior = i;return;}// We have reached index 0.  0 is always a break point, so simply return it.*pPrior = 0;}void EditData::GetNextItemPos(int nCP, int* pNext) {if (NULL == pNext)return;*pNext = nCP;  // Default is the char itselfif (NULL == m_Analysis){if (false ==this->StringAnalysis())return;}const SCRIPT_LOGATTR* pLogAttr = ScriptString_pLogAttr(m_Analysis);if (!pLogAttr)return;if (!ScriptString_pcOutChars(m_Analysis))return;int nInitial = *ScriptString_pcOutChars(m_Analysis);if (nCP + 1 < nInitial)nInitial = nCP + 1;int i = nInitial;int limit = *ScriptString_pcOutChars(m_Analysis);while (limit > 0 && i < limit - 1){if (pLogAttr[i].fWordStop)      // Either the fWordStop flag is set{*pNext = i;return;}else if (pLogAttr[i].fWhiteSpace &&  // Or this whitespace but the next char isn't.!pLogAttr[i + 1].fWhiteSpace){*pNext = i + 1;  // The next char is a word stopreturn;}++i;limit = *ScriptString_pcOutChars( m_Analysis);}// We have reached the end. It's always a word stop, so simply return it.*pNext = *ScriptString_pcOutChars(m_Analysis) - 1;}////  删除当前选中文本,如果没有选区则不执行//void  EditData::DeleteSelectionText(bool& bUpdate){    // 删除当前所选择的文字    if (IsSelectionExist())    {        bUpdate = true;        this->DeleteSelectionText();        m_nSelStart = m_nCaret;        this->Fire_Text_Changed();    }}   ////删除当前选区内的文字////注:该函数不会触发text changed//void EditData::DeleteSelectionText(){ if (IsSelectionExist()) { if (m_nCaret > m_nSelStart)  // 从前到后选择 { this->m_strText.erase(m_nSelStart, m_nCaret-m_nSelStart); m_nCaret = m_nSelStart; } else                   // 从后往前选择的 { this->m_strText.erase(m_nCaret , m_nSelStart-m_nCaret);m_nSelStart = m_nCaret; } }}bool EditData::IsSelectionExist(){return (m_nCaret!=m_nSelStart);}bool  EditData::Clear(){    if (m_strText.length() == 0)        return false;        m_strText.clear();    m_nSelStart = m_nCaret = 0;    this->Fire_Text_Changed();    return true;}//// BOOL bSetText,表示是否是因为调用Edit.SetText而触发的Change//void EditData::Fire_Text_Changed(BOOL bSetText){this->StringAnalysis();this->CP2X(m_strText.length(), &m_nTextWidth);    UIMSG msg;    msg.pMsgFrom = m_pEdit->GetIEdit();    msg.message = UI_WM_NOTIFY;    msg.nCode = UI_EN_CHANGE;    msg.wParam = (WPARAM)bSetText;    m_pEdit->GetIEdit()->DoNotify(&msg);}bool EditData::FilterString(const TCHAR* szSrc, String& strDest){    if (szSrc)        strDest = szSrc; // TODOreturn true;}bool EditData::FilterChar(const TCHAR& cSrc, TCHAR& cDest){cDest = cSrc;      // TODOreturn true;}void EditData::SetMaxChar(int nMax){bool bUpdate = false;this->SetMaxChar(nMax, bUpdate);}void EditData::SetMaxChar(int nMax, bool bUpdate){bUpdate = false;m_nMaxChar = nMax;if (-1 != m_nMaxChar && (int)m_strText.length() > m_nMaxChar){bUpdate = true;m_strText = m_strText.substr(0,m_nMaxChar);this->Fire_Text_Changed();}}void EditData::SetInsertMode( bool bInsertOrOverride){m_bInsertMode = bInsertOrOverride;}// 当外部字体发生改变时,需要重新创建void EditData::DestroyStringAnalysis(){if (m_Analysis){ScriptStringFree(&m_Analysis);m_Analysis = NULL;}}////初始化当前字符串m_Analysis//bool EditData::StringAnalysis(){if (m_Analysis){ScriptStringFree(&m_Analysis);m_Analysis = NULL;}    IUIApplication* pUIApp = m_pEdit->GetIEdit()->GetUIApplication();HDC    hDC =  pUIApp->GetCacheDC();IRenderFont* pRenderFont = m_pEdit->GetIEdit()->GetRenderFont();HFONT  hFont = pRenderFont->GetHFONT();HFONT  hOldFont = (HFONT)::SelectObject(hDC, hFont);HRESULT hr = ScriptStringAnalyse(hDC,this->m_strText.c_str(),this->m_strText.length() + 1,// 加上NULL.保证光标可以到达最后一个字符的后面。this->m_strText.length()*3/2 + 16,      // MSDN推荐值-1,SSA_BREAK | SSA_GLYPHS | SSA_FALLBACK | SSA_LINK,0,&m_ScriptControl,&m_ScriptState,NULL,NULL,NULL,&m_Analysis);::SelectObject(hDC, hOldFont);pUIApp->ReleaseCacheDC(hDC);if (FAILED(hr) || NULL == m_Analysis)return false;return true;}bool EditData::CP2X(int nCP, int* pX){if (NULL == pX)return false;if (NULL == m_Analysis){if (false ==this->StringAnalysis())return false;}intnX = 0;HRESULT hr = ScriptStringCPtoX(m_Analysis, nCP,           // 要计算第一个字符FALSE,   // 光标在字符前面还是后面pX   // 返回值);if (FAILED(hr))return false;return true;}bool EditData::X2CP(int nX, int* pnCP, int* pbTrailOrLead){if (NULL == pnCP || NULL == pbTrailOrLead)return false;if (NULL == m_Analysis){if (false ==this->StringAnalysis())return false;}HRESULT hr = ScriptStringXtoCP(m_Analysis, nX,pnCP,pbTrailOrLead// 光标在字符前面还是后面);if (FAILED(hr))return false;return true;}// If nStartChar is 0 and nEndChar is –1, all the text in the edit control is selected. // If nStartChar is –1, any current selection is removed.void EditData::SetSelectionInfo(int nStart, int nEnd, bool& bUpdate){if (-1 == nStart){//m_nSelStart = m_nCaret;SetCaret(m_nCaret, true, bUpdate);}else if (-1 == nEnd){// m_nSelStart = nStart;// m_nCaret = m_strText.length();SetCaret(nStart, true, bUpdate);SetCaret(m_strText.length(), false, bUpdate);}else{// m_nSelStart = nStart;// m_nCaret = nEnd;SetCaret(nStart, true, bUpdate);SetCaret(nEnd, false, bUpdate);}}void EditData::GetSelectionInfo(int& nLeft, int& nRight) const{if (m_nSelStart < m_nCaret){nLeft = m_nSelStart;nRight = m_nCaret;}else{nLeft = m_nCaret;nRight = m_nSelStart;}}void EditData::GetSelectionText(String& str){int nLeft = 0, nRight = 0;this->GetSelectionInfo(nLeft, nRight);str = m_strText.substr(nLeft, nRight-nLeft);}void EditData::CutToClipboard(){if (IsSelectionExist()){this->CopyToClipboard();bool bUpdate = false;this->Delete(bUpdate);}}void EditData::CopyToClipboard(){if (IsSelectionExist() && OpenClipboard(NULL)){EmptyClipboard();String strSelectionText;this->GetSelectionText(strSelectionText);int nSize = sizeof(TCHAR) * (strSelectionText.length() + 1);HGLOBAL hBlock = GlobalAlloc(GMEM_MOVEABLE, nSize);if (hBlock){void* p = GlobalLock( hBlock);memcpy(p, strSelectionText.c_str(), nSize);GlobalUnlock( hBlock);}SetClipboardData (CF_UNICODETEXT, hBlock);CloseClipboard();// We must not free the object until CloseClipboard is called.if (hBlock)GlobalFree( hBlock);}}void EditData::PasteFromClipboard(){DeleteSelectionText();if (OpenClipboard(NULL)){HANDLE handle = GetClipboardData( CF_UNICODETEXT);if (handle){// Convert the ANSI string to Unicode, then// insert to our buffer.WCHAR* pwszText = ( WCHAR*)GlobalLock( handle);if (pwszText){// Copy all characters up to null.bool bUpdate = false;ReplaceString(String(pwszText), bUpdate);GlobalUnlock(handle);}}CloseClipboard();}}

这个类并不复杂,但其中可能大家对uniscribe并不熟悉,它其实是操作系统提供的一个组件,它的主要作用就是提供USP支持:USP其实是英语Unicode Scripts Processor的简称,意思就是“Unicode文字系统处理器”。它主要包括以下的部件:

1、把文字从输入次序重排成为显示次序
2、把文字按前文后理作出适当的变换
3、按文字显示的方向作出字符的替换
从代码中来看,通过它来实现字符和字符位置的相互转换,这到省了想法设法去计算。



0 0
原创粉丝点击