精通
英语
和
开源
,
擅长
开发
与
培训
,
胸怀四海
第一信赖
锐英源精品开源,禁止转载和任何形式的非法内容使用,违者必究
文本编辑器是可以处理文本文件的常用应用程序。文本编辑器的主要功能包括“打开/保存文件”,“编辑/查看内容”等。在本教程中,我们将介绍如何构建基于WTL的简单文本编辑器。
我们假设读者有一个支持ATL / WTL的Visual C ++。如果是这样,源代码可以直接用作VC ++项目。如果没有,读者应该做一些额外的工作来准备开发环境。
通过WTL项目向导创建C ++项目,我们可以为文档视图选择基类。CEdit类 和CRichEditCtrl类 都可能是一个简单的文本编辑器的基础。CRichEditCtrl 是Windows ActiveX控件richedt20.dll 的WTL包装 ,而CEdit是一个基于Windows API的WTL作品。作为一个简单的文本编辑程序,CEdit 类还行。我们需要做的是加强CEdit的功能并实现一个简单的文本编辑器。
通过WTL项目向导创建的初始项目提供了一个基本的程序框架。作为一个完整的可执行程序,它提供了基本的GUI,包括窗口、menu、工具栏、工作区和状态栏,以及用户友好的入口点来定制其程序行为。
我们可以在工作区输入文本,但输入结果无法保存,程序也无法打开存在的文本文件。原因是默认文件打开/保存行为为空。我们首先将文件打开并保存功能加到到程序中,如下所示。
DWORD dwSizeLow = GetFileSize(hFile, 0);
char* pbBuff = (char*)malloc(dwSizeLow+1);
DWORD pcb = 0;
DWORD dwRet = ::ReadFile(hFile, pbBuff, dwSizeLow, (LPDWORD)&pcb, NULL);
pbBuff[dwSizeLow] = '\0';
::CloseHandle(hFile);
ATLASSERT(::IsWindow(m_hWnd));
::SendMessage(m_hWnd, WM_SETTEXT, 0, (LPARAM)pbBuff);
delete pbBuff;
ATLASSERT(::IsWindow(m_hWnd));
DWORD dwSizeLow = ::GetWindowTextLength(m_hWnd);
char* pbBuff = (char*)malloc(dwSizeLow+1);
::SendMessage(m_hWnd, WM_GETTEXT, (WPARAM)dwSizeLow, (LPARAM)pbBuff);
pbBuff[dwSizeLow] = '\0';
DWORD pcb = 0;
DWORD dwRet = ::WriteFile(hFile, pbBuff, dwSizeLow, (LPDWORD)&pcb, NULL);
::CloseHandle(hFile);
CEdit 类支持基本的文本编辑操作,我们可以输入文本,复制/粘贴/剪切在工作区中的文本。然而, CEdit 编辑操作是通过操纵它的内部数据完成。因为我们的文本编辑程序需要做一些自定义操作,所以我们基于我们自己的数据结构来实现基本的编辑操作。
我们使用STL列表string 来存储内容。
vector<string> m_contents;
每个string 包含一个文本行,默认情况下,我们使用两个字符'\ r'\ n'来完成一行并开始一个新行。我们知道,与我们的方法不同,CEdit 类将内容视为长字符序列。如果我们在工作区域中选择文本,CEdit 函数GetSel()将返回当前选择的位置。我们必须将通过GetSel() 函数获得的位置映射到我们m_contents变量内的位置。例如,内容...
foo
]
...被默认处理为序列“Hello world!\ r \ n \ r \ n foo”,我们将它视为字符串“Hello world!\ r \ n”“\ r \ n” “富”。序列中的位置17为“f”,而其对应位置为[2,0],即char 第三个位置的第一位string。映射函数如下所示:
位置映射后,仍然需要处理文本编辑的消息。这些消息包括WM_CHAR,WM_KEYDOWN,...
当编辑器 通过WM_CHAR 消息捕获用户输入char时,CEdit 该类将更改保存到其内部数据结构中,并将其反映在工作区上。但是,我们可以通过重叠来自定义程序行为,或者使用函数“ OnChar”来处理WM_CHAR 消息。
OnChar主要过程 首先在工作区定位当前选定的内容,然后通过输入char替换所选内容。重要的是编辑器应该识别控件的字符并正确处理它们。例如,当输入为“ return” VK_RETURN,程序应将string 对象分成两个string 对象,但不能简单地替换所选内容。
if(chNewChar==VK_RETURN)
{
string line1 = m_rawText[nRow].substr(0, nCol);
line1.append(1, '\r').append(1, '\n');
string line2 = m_rawText[nRow].substr(nCol, m_rawText[nRow].size());
m_rawText[nRow] = line2;
m_rawText.insert(m_rawText.begin() + nRow, line1);
nInsertionPoint++;
}
else if (chNewChar == VK_TAB)
{
for(int i=0;i<m_TabCount;i++) {
nInsertionPoint = nCol++;
m_rawText[nRow].insert(nInsertionPoint, 1, _T(' '));
}
}
else
m_rawText[nRow].insert(nCol, &chNewChar);
return nInsertionPoint;
}
WM_KEYDOWN 这里的消息用于处理“ insert” VK_INSERT。每按一次VK_INSERT 键,编辑模式应在INSERT 模式和REPLACE 模式之间切换。
BOOL bIsShiftKeyDown=::GetAsyncKeyState(VK_SHIFT)<0;
BOOL bIsCtrlKeyDown=::GetAsyncKeyState(VK_CONTROL)<0;
if(nChar == VK_INSERT) {
if (!bIsShiftKeyDown && !bIsCtrlKeyDown)
{
// The standard CEdit control does not support over-typing.
// This flag is used to manage over-typing internally.
SetInsertMode(!GetInsertMode());
}
}
return 0;
}