DRAGON中edit数据的封装

由于操作系统提供的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 itself

	if (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 itself

	if (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 stop
			return;
		}

		++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; // TODO

	return true;
}
bool EditData::FilterChar(const TCHAR& cSrc, TCHAR& cDest)
{
	cDest = cSrc;      // TODO
	return 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;
	}

	int	nX = 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值