如何实现一个可视化的文字编辑器(C语言版)?
一、软件安装
Visual Studio 2022
Visual Studio 2022 是微软提供的强大集成开发环境(IDE),广泛用于C/C++、C#、Python等多种编程语言的开发。它提供了许多强大的工具,帮助开发者编写、调试和优化代码。
1.下载 Visual Studio 2022:访问Visual Studio 官网
2.点击 Download Visual Studio,选择 Community Edition(免费)或其他版本,根据需求选择。
2.安装 Visual Studio:运行下载的安装程序,启动 Visual Studio 安装程序。
在安装界面选择 Desktop development with C++,开发 C++ 程序所需的工作负载。
可以根据需要选择其他组件,比如 Windows 10 SDK、C++ CMake tools 等,帮助进行图形和图像处理的开发,完成安装后,启动 Visual Studio 2022,选择 Create a new project 来创建新的C++项目。(不会安装的可以搜一下相关教程)
安装 EasyX 图形库
1.下载 EasyX 图形库:EasyX官网,根据需要选择合适版本。
2.安装 EasyX:
二、效果演示
1.界面样式
2.读入文件
3.查找
4.插入文字
5.替换
三、代码实现
Text.h:该文件是 Text.cpp 的头文件,声明了文本操作相关的结构体和函数接口。
-
Node
结构体:表示链表中的一个字符节点。包含字符内容、字符在屏幕上的位置、是否为普通字符、是否被选中等信息。 -
Text
结构体:表示文本内容,包含头指针、尾指针和当前光标位置。 -
File
结构体:表示文件,包含文件路径和文本内容。 -
函数声明:如
readFile()
、writeFile()
、insertText()
、deleteCharacter()
等。
#pragma once
/*
该模块用于文本的操作
*/#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>// 该结构体表示链表结点
typedef struct Node Node;
struct Node {char content[3]; // 表示一个普通字符或中午字符int rect[4]; // 字符在屏幕上所占的区域bool normal; // 是否是普通字符bool selected; // 是否被选中Node* next;Node* prev;
};// 该结构体表示文本
typedef struct Text Text;
struct Text {Node* head; // 头指针Node* tail; // 尾指针Node* cursor; // 当前指针位置
};// 该结构体表示文件
typedef struct File File;
struct File {char path[256]; // 文件的路径Text* text; // 文件的内容
};// 读入文件
bool readFile(char* path);// 写入文件
void writeFile();// 新建文件
bool createFile(char* path);// 保存文件
bool saveFile(char* path);// 获取当前文件
File* getCurrentFile();// 释放相关内存
void freeResources();// 插入文字
void insertText(char* mContent);// 删除字符
void deleteCharacter(bool isAfter);// 重置选中标志
void resetSelected();// 查找字符串
void findString(char* searchText);// 替换字符串
void replaceString(char* srcText, char* dstText);
Text.cpp:实现了文本内容的管理与操作,包括文件的读取、保存、文本的插入、删除、查找和替换等。它使用了链表结构来组织文本内容。
-
readFile()
:-
读取指定路径的文件,并将文件内容解析为字符。读取的字符根据其字节数判断是普通字符还是中文字符,并将其存储到链表中。
-
使用
fread()
函数按字节读取文件内容,若字符为普通字符,则将其存储在content[]
中,若为中文字符,则按两个字节读取。
-
-
writeFile()
:-
将当前内存中的文本内容(链表结构)保存到文件中。通过
fopen()
打开文件,并逐个节点将文本内容写入。
-
-
createFile()
:-
创建新文件并为其分配内存。调用
writeFile()
保存当前文件内容,释放内存,创建新文件,并初始化新的文本结构。
-
-
insertText()
:-
在当前光标位置插入文本。通过
appendString()
函数将新的文本插入到链表的对应位置。
-
-
deleteCharacter()
:-
删除当前光标位置的字符。通过检查光标的位置(
isAfter
参数决定删除前或后字符)来删除指定的字符节点。
-
-
findString()
:-
查找指定的字符串,并将找到的部分高亮显示。通过遍历链表中的节点,并检查每个节点的
content
是否与目标字符串匹配。
-
-
replaceString()
:-
查找并替换文本中的指定字符串。通过遍历链表查找目标字符串,若找到则替换为指定的新字符串。
-
#include "Text.h"// 在某个节点后添加字符串
void appendString(Node** beforeNode, char* mContent, bool modifyBeforeNode);// 当前的文件
static File* curFile = NULL;// 为新文件分配内容
static void initFile(File* file, char* path) {strcpy(curFile->path, path);curFile->text = (Text*)calloc(1, sizeof(Text));curFile->text->head = (Node*)calloc(1, sizeof(Node));curFile->text->tail = (Node*)calloc(1, sizeof(Node));curFile->text->head->next = curFile->text->tail;curFile->text->tail->prev = curFile->text->head;curFile->text->cursor = curFile->text->head;
}// 释放文件占用的内存
static void destroyFile(File** file) {while ((*file)->text->head->next != (*file)->text->tail) {Node* node = (*file)->text->head->next;(*file)->text->head->next = node->next;free(node);}free((*file)->text->head);free((*file)->text->tail);free(*file);*file = NULL;
}// 在Text中添加一个Node
static void insertNode(Node* beforeNode, char content[], bool normal) {Node* node = (Node*)calloc(1, sizeof(Node));strcpy(node->content, content);node->normal = normal;node->selected = false;node->next = beforeNode->next;node->prev = beforeNode;beforeNode->next->prev = node;beforeNode->next = node;
}// 读入文件
bool readFile(char* path) {// 确保文件存在FILE* file = fopen(path, "rb");if (!file) {return false;}// 保存当前文件writeFile();// 释放内存if (curFile) {destroyFile(&curFile);}// 为新文件分配内存curFile = (File*)calloc(1, sizeof(File));initFile(curFile, path);// 读取新的文件char byte;char content[3] = {0};bool normal;while (fread(&byte, 1, 1, file) == 1) {if (byte >= 0) {// 这是普通字符content[0] = byte;if (byte == '\r') {char nextByte;fread(&nextByte, 1, 1, file);content[1] = nextByte;normal = false;}else {content[1] = '\0';normal = true;}}else {// 这是中文,其占2个字节char nextByte;fread(&nextByte, 1, 1, file);content[0] = byte;content[1] = nextByte;normal = false;}// 添加当前普通字符或者中文字符到文件的文本中insertNode(curFile->text->tail->prev, content, normal);}fclose(file);return true;
}// 写入文件
void writeFile() {if (!curFile) return;// 把新内容写入文件FILE* fp = fopen(curFile->path, "w");Node* node = curFile->text->head->next;while (node != curFile->text->tail) {fprintf(fp, "%s", node->content);node = node->next;}fclose(fp);
}// 新建文件
bool createFile(char* path) {// 保存当前文件writeFile();// 释放内存if (curFile) {destroyFile(&curFile);}FILE* fp = fopen(path, "w");if (!fp) return false;fclose(fp);// 为新文件分配内存curFile = (File*)calloc(1, sizeof(File));initFile(curFile, path);return true;
}// 保存文件
bool saveFile(char* path) {if (!curFile) return false;// 把内容写入新的文件FILE* fp = fopen(path, "w");if (!fp) return false;Node* node = curFile->text->head->next;while (node != curFile->text->tail) {fprintf(fp, "%s", node->content);node = node->next;}fclose(fp);return true;
}// 获取当前文件
File* getCurrentFile() {return curFile;
}// 释放相关内存
void freeResources() {if (curFile) {destroyFile(&curFile);}
}// 插入文字
void insertText(char* mContent) {if (!curFile || !mContent || strlen(mContent) == 0) return;// 添加字符串到当前指针后面appendString(&curFile->text->cursor, mContent, true);
}// 删除当前字符
void deleteCharacter(bool isAfter) {if (curFile) {if (isAfter) {// 删除指针后面的字符Node* node = curFile->text->cursor->next;if (node != curFile->text->tail) {curFile->text->cursor->next = node->next;node->next->prev = curFile->text->cursor;free(node);}}else {// 删除指针指向的字符Node* node = curFile->text->cursor;if (node != curFile->text->head) {curFile->text->cursor = node->prev;curFile->text->cursor->next = node->next;node->next->prev = curFile->text->cursor;free(node);}}}
}// 重置选中标志
void resetSelected() {if (!curFile) return;Node* node = curFile->text->head->next;while (node != curFile->text->tail) {node->selected = false;node = node->next;}
}// 查找字符串
void findString(char* searchText) {if (!curFile) return;char content[3] = { 0 };char byte;size_t len = strlen(searchText);Node* curNode = curFile->text->head->next;while (curNode != curFile->text->tail) {Node* tmpNode = curNode;bool isFound = true;// 查找子串for (int i = 0; i < len && tmpNode != curFile->text->tail; i++) {byte = searchText[i];if (byte >= 0) {// 这是普通字符content[0] = byte;if (byte == '\r') {char nextByte = searchText[++i];content[1] = nextByte;}else {content[1] = '\0';}}else {// 这是中文,其占2个字节char nextByte = searchText[++i];content[0] = byte;content[1] = nextByte;}// 判断两个字符是否相同if (strcmp(tmpNode->content, content) != 0) {isFound = false;break;}tmpNode = tmpNode->next;}if (isFound) {// 设置对应的字符序列为选中Node* node = curNode;while (node != tmpNode) {node->selected = true;node = node->next;}}curNode = curNode->next;}
}// 在某个节点后添加字符串
void appendString(Node** beforeNode, char* mContent, bool modifyBeforeNode) {int i = 0;char byte;bool normal;char content[3] = { 0 };size_t len = strlen(mContent);Node* prev = *beforeNode;while (i < len) {byte = mContent[i];if (byte >= 0) {// 这是普通字符content[0] = byte;if (byte == '\r') {char nextByte = mContent[++i];content[1] = nextByte;normal = false;}else {content[1] = '\0';normal = true;}}else {// 这是中文,其占2个字节char nextByte = mContent[++i];content[0] = byte;content[1] = nextByte;normal = false;}// 添加当前普通字符或者中文字符到该指针后面insertNode(prev, content, normal);prev = prev->next;if (modifyBeforeNode) {*beforeNode = prev;}i++;}
}// 替换字符串
void replaceString(char* srcText, char* dstText) {if (!curFile) return;char content[3] = { 0 };char byte;size_t len = strlen(srcText);Node* curNode = curFile->text->head->next;while (curNode != curFile->text->tail) {Node* tmpNode = curNode;bool isFound = true;// 查找子串for (int i = 0; i < len && tmpNode != curFile->text->tail; i++) {byte = srcText[i];if (byte >= 0) {// 这是普通字符content[0] = byte;if (byte == '\r') {char nextByte = srcText[++i];content[1] = nextByte;}else {content[1] = '\0';}}else {// 这是中文,其占2个字节char nextByte = srcText[++i];content[0] = byte;content[1] = nextByte;}// 判断两个字符是否相同if (strcmp(tmpNode->content, content) != 0) {isFound = false;break;}tmpNode = tmpNode->next;}if (isFound) {// 首先删除原字符串Node* beforeNode = curNode->prev;while (beforeNode->next != tmpNode) {Node* node = beforeNode->next;beforeNode->next = node->next;node->next->prev = beforeNode;free(node);}curNode = tmpNode;// 添加替换字符串appendString(&beforeNode, dstText, false);}else {curNode = curNode->next;}}
}
Editor.h:是 Editor.cpp 的头文件,声明了文本编辑器相关的函数接口,确保其他文件能够调用 Editor.cpp 中定义的函数。
声明了文本编辑器相关的函数,如:
-
initTextEditor()
:初始化编辑器。 -
runTextEditor()
:运行编辑器的主循环。 -
destroyTextEditor()
:销毁编辑器。
#pragma once
/*
该模块用于显示文本编辑器的界面和用户交互
*/// 初始化文本编辑器
void initTextEditor();// 运行文本编辑器
void runTextEditor();// 销毁文本编辑器
void destroyTextEditor();
Editor.cpp:文本编辑器的核心,负责创建并显示图形界面,同时处理文件操作、文本编辑、用户输入事件等。它利用 EasyX 图形库来绘制窗口和菜单,支持文件的读写操作、文本的插入与删除、文本的查找和替换。
-
initTextEditor()
:-
初始化文本编辑器,创建窗口并设置背景色。
-
使用
initgraph()
函数来初始化窗口,setbkcolor()
设置背景色为白色,SetWindowText()
设置窗口的标题为 "文本编辑器"。 -
还通过
readResources()
函数加载图像资源(如editor.png
),并设置图形界面的直线样式。
-
-
runTextEditor()
:-
启动文本编辑器的主循环。在这个循环中,程序不断更新窗口,清除设备并重新绘制所有组件(如菜单和文本区域)。
-
处理键盘事件(如删除字符)和鼠标点击事件(如选择菜单项):
-
键盘事件:当按下删除键(
VK_BACK
)时,删除当前光标位置的字符。 -
鼠标事件:检测鼠标点击的区域,根据用户点击的菜单项执行不同操作,如读入文件、写入文件、新建文件等。
-
-
-
drawWindow()
:-
用于绘制编辑器窗口,包括背景图像、菜单区域和文本区域。
-
使用
putimage()
函数绘制背景图像,并调用drawMenu()
和drawText()
来绘制菜单和文本区域。
-
-
drawMenu()
:-
绘制顶部的菜单区域,包括文件操作(如读入、写入、保存等)和文本编辑操作(如插入文本、查找、替换)。
-
使用
outtextxy()
函数绘制菜单项文本,并用roundrect()
绘制按钮。
-
-
InsertText()
:-
提示用户输入要插入的文字,并调用
insertText()
函数将文字插入到当前光标位置。
-
-
FindString()
:-
查找字符串。调用
findString()
函数查找指定的字符串并将找到的文本高亮显示。
-
-
ReplaceString()
:-
替换字符串。通过
replaceString()
函数,查找指定的原字符串并将其替换为新字符串。
-
#include "Editor.h"
#include <graphics.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <time.h>
#include <math.h>
#include "Text.h"// 窗口大小
static int WINDOW_WIDTH = 1000;
static int WINDOW_HEIGHT = 700;// 设置每帧的时间
static double FPS = 30;
static double PER_FRAME_TIME = 1000.0 / FPS;// 查找字符串
static char searchText[256] = {0};// 原字符串
static char sourceText[256] = { 0 };// 替换字符串
static char replaceText[256] = { 0 };// 设置菜单区域的位置
static int MENU_LEFT_TOP[] = { 10, 80 };
static int MENU_RIGHT_BOTTOM[] = { 900, 220 };
static int PADDING[] = {50, 16};// 读入文件菜单位置
static int READ_FILE_MENU[] = { MENU_LEFT_TOP[0] + PADDING[0], MENU_LEFT_TOP[1] + PADDING[1],MENU_LEFT_TOP[0] + PADDING[0] + 104, MENU_LEFT_TOP[1] + PADDING[1] + 25 };
// 写入文件菜单位置
static int WRITE_FILE_MENU[] = { READ_FILE_MENU[2] + PADDING[0], MENU_LEFT_TOP[1] + PADDING[1],READ_FILE_MENU[2] + PADDING[0] + 104, MENU_LEFT_TOP[1] + PADDING[1] + 25 };
// 新建文件菜单位置
static int CREATE_FILE_MENU[] = { WRITE_FILE_MENU[2] + PADDING[0], MENU_LEFT_TOP[1] + PADDING[1],WRITE_FILE_MENU[2] + PADDING[0] + 104, MENU_LEFT_TOP[1] + PADDING[1] + 25 };
// 保存文件菜单位置
static int SAVE_FILE_MENU[] = { CREATE_FILE_MENU[2] + PADDING[0], MENU_LEFT_TOP[1] + PADDING[1],CREATE_FILE_MENU[2] + PADDING[0] + 104, MENU_LEFT_TOP[1] + PADDING[1] + 25 };
// 退出菜单位置
static int EXIT_MENU[] = { SAVE_FILE_MENU[2] + PADDING[0], MENU_LEFT_TOP[1] + PADDING[1],SAVE_FILE_MENU[2] + PADDING[0] + 52, MENU_LEFT_TOP[1] + PADDING[1] + 25 };
// 编辑文件菜单位置
static int EDIT_FILE_MENU[] = { MENU_LEFT_TOP[0] + PADDING[0], READ_FILE_MENU[3] + PADDING[1],MENU_LEFT_TOP[0] + PADDING[0] + 104, READ_FILE_MENU[3] + PADDING[1] + 25 };
// 插入菜单位置
static int INSERT_TEXT_MENU[] = { MENU_LEFT_TOP[0] + PADDING[0], EDIT_FILE_MENU[3] + PADDING[1],MENU_LEFT_TOP[0] + PADDING[0] + 104, EDIT_FILE_MENU[3] + PADDING[1] + 25 };
// 查找文本位置
static int SEARCH_TEXT[] = { EDIT_FILE_MENU[2] + PADDING[0], READ_FILE_MENU[3] + PADDING[1],EDIT_FILE_MENU[2] + PADDING[0] + 208, READ_FILE_MENU[3] + PADDING[1] + 25 };
// 查找菜单位置
static int FIND_MENU[] = { SEARCH_TEXT[2] + PADDING[0], READ_FILE_MENU[3] + PADDING[1],SEARCH_TEXT[2] + PADDING[0] + 52, READ_FILE_MENU[3] + PADDING[1] + 25 };
// 清除高亮菜单位置
static int CLEAR_MENU[] = { FIND_MENU[2] + PADDING[0], READ_FILE_MENU[3] + PADDING[1],FIND_MENU[2] + PADDING[0] + 104, READ_FILE_MENU[3] + PADDING[1] + 25 };
// 原文本位置
static int SOURCE_TEXT[] = { EDIT_FILE_MENU[2] + PADDING[0], FIND_MENU[3] + PADDING[1],EDIT_FILE_MENU[2] + PADDING[0] + 208, FIND_MENU[3] + PADDING[1] + 25 };
// 替换文本位置
static int REPLACE_TEXT[] = { SOURCE_TEXT[2] + PADDING[0], FIND_MENU[3] + PADDING[1],SOURCE_TEXT[2] + PADDING[0] + 208, FIND_MENU[3] + PADDING[1] + 25 };
// 替换菜单位置
static int REPLACE_MENU[] = { REPLACE_TEXT[2] + PADDING[0], FIND_MENU[3] + PADDING[1],REPLACE_TEXT[2] + PADDING[0] + 52, FIND_MENU[3] + PADDING[1] + 25 };
// 文本区域位置
static int TEXT_AREA[] = { 10, MENU_RIGHT_BOTTOM[1] + PADDING[1], WINDOW_WIDTH-20, WINDOW_HEIGHT - 20 };// 图片资源
static IMAGE editorImage;// 读取图片资源
static void readResources();// 绘制窗口
static void drawWindow();// 绘制菜单区域
static void drawMenu();// 绘制文本区域
static void drawText();// 读入文件
static void ReadFile();// 写入文件
static void WriteFile();// 新建文件
static void CreateNewFile();// 保存文件
static void SaveFile();// 插入文字
static void InsertText();// 查找
static void FindString();// 清除输入
static void ClearInput();// 替换
static void ReplaceString();// 更新文件指针位置
static void updateCursor(int x, int y);// 初始化文本编辑器
void initTextEditor() {// 创建窗口 initgraph(WINDOW_WIDTH, WINDOW_HEIGHT);setbkcolor(RGB(255, 255, 255));// 设置窗口名称HWND hWnd = GetHWnd();SetWindowText(hWnd, "文本编辑器");// 读取图片资源readResources();// 设置直线样式LINESTYLE style;style.thickness = 2;setlinestyle(&style);
}// 运行文本编辑器
void runTextEditor() {bool exitCode = false;while (!exitCode) {int startTime = clock();// 双缓冲 BeginBatchDraw();cleardevice();// 绘制窗口drawWindow();EndBatchDraw();// 处理消息static ExMessage msg;while (peekmessage(&msg, EM_KEY | EM_MOUSE)) {// 键盘按下事件if (msg.message == WM_KEYDOWN) {if (msg.vkcode == VK_BACK) {// 删除指针指向的字符deleteCharacter(false);}else if (msg.vkcode == VK_DELETE) {// 删除指针后面的字符deleteCharacter(true);}}// 鼠标左键按下事件else if (msg.message == WM_LBUTTONDOWN) {int x = msg.x;int y = msg.y;// 点击了读入文件按钮if (x > READ_FILE_MENU[0] && x < READ_FILE_MENU[2] &&y > READ_FILE_MENU[1] && y < READ_FILE_MENU[3]) {ReadFile();}// 点击了写入文件按钮else if (x > WRITE_FILE_MENU[0] && x < WRITE_FILE_MENU[2] &&y > WRITE_FILE_MENU[1] && y < WRITE_FILE_MENU[3]) {WriteFile();}// 点击了创建文件按钮else if (x > CREATE_FILE_MENU[0] && x < CREATE_FILE_MENU[2] &&y > CREATE_FILE_MENU[1] && y < CREATE_FILE_MENU[3]) {CreateNewFile();}// 点击了保存文件按钮else if (x > SAVE_FILE_MENU[0] && x < SAVE_FILE_MENU[2] &&y > SAVE_FILE_MENU[1] && y < SAVE_FILE_MENU[3]) {SaveFile();}// 点击了退出按钮else if (x > EXIT_MENU[0] && x < EXIT_MENU[2] &&y > EXIT_MENU[1] && y < EXIT_MENU[3]) {// 释放相关内存freeResources();exitCode = true;break;}// 点击了插入文字按钮else if (x > INSERT_TEXT_MENU[0] && x < INSERT_TEXT_MENU[2] &&y > INSERT_TEXT_MENU[1] && y < INSERT_TEXT_MENU[3]) {InsertText();}// 点击了查找按钮else if (x > FIND_MENU[0] && x < FIND_MENU[2] &&y > FIND_MENU[1] && y < FIND_MENU[3]) {FindString();}// 点击了清楚高亮按钮else if (x > CLEAR_MENU[0] && x < CLEAR_MENU[2] &&y > CLEAR_MENU[1] && y < CLEAR_MENU[3]) {ClearInput();}// 点击了替换按钮else if (x > REPLACE_MENU[0] && x < REPLACE_MENU[2] &&y > REPLACE_MENU[1] && y < REPLACE_MENU[3]) {ReplaceString();}// 点击了文本区域else if (x > TEXT_AREA[0] && x < TEXT_AREA[2] &&y > TEXT_AREA[1] && y < TEXT_AREA[3]) {updateCursor(x, y);}// 输入查找字符串else if (x > SEARCH_TEXT[0] && x < SEARCH_TEXT[2] &&y > SEARCH_TEXT[1] && y < SEARCH_TEXT[3]) {InputBox(searchText, 255, "输入查找字符串:");}// 输入原字符串else if (x > SOURCE_TEXT[0] && x < SOURCE_TEXT[2] &&y > SOURCE_TEXT[1] && y < SOURCE_TEXT[3]) {InputBox(sourceText, 255, "输入原字符串:");}// 输入替换字符串else if (x > REPLACE_TEXT[0] && x < REPLACE_TEXT[2] &&y > REPLACE_TEXT[1] && y < REPLACE_TEXT[3]) {InputBox(replaceText, 255, "输入替换字符串:");}}}// 控制每帧时间 int duration = clock() - startTime;if (duration < PER_FRAME_TIME) {Sleep(PER_FRAME_TIME - duration);}}
}// 销毁文本编辑器
void destroyTextEditor() {closegraph();
}// 读取图片资源
static void readResources() {loadimage(&editorImage, "editor.png");
}// 绘制窗口
static void drawWindow() {// 绘制编辑器图片putimage(0, 0, &editorImage);// 绘制菜单区域drawMenu();// 绘制文本区域drawText();
}// 绘制菜单区域
static void drawMenu() {// 设置字体LOGFONT f;gettextstyle(&f);f.lfHeight = 25;_tcscpy(f.lfFaceName, _T("黑体"));f.lfQuality = ANTIALIASED_QUALITY;f.lfWeight = 800;settextstyle(&f);settextcolor(RGB(0, 0, 0));// 绘制文件编辑器的菜单setlinecolor(RGB(0, 0, 0));rectangle(MENU_LEFT_TOP[0], MENU_LEFT_TOP[1], MENU_RIGHT_BOTTOM[0], MENU_RIGHT_BOTTOM[1]);// 读入文件菜单outtextxy(READ_FILE_MENU[0], READ_FILE_MENU[1], "读入文件");roundrect(READ_FILE_MENU[0], READ_FILE_MENU[1], READ_FILE_MENU[2], READ_FILE_MENU[3], 5, 5);// 写入文件菜单outtextxy(WRITE_FILE_MENU[0], WRITE_FILE_MENU[1], "写入文件");roundrect(WRITE_FILE_MENU[0], WRITE_FILE_MENU[1], WRITE_FILE_MENU[2], WRITE_FILE_MENU[3], 5, 5);// 新建文件菜单outtextxy(CREATE_FILE_MENU[0], CREATE_FILE_MENU[1], "新建文件");roundrect(CREATE_FILE_MENU[0], CREATE_FILE_MENU[1], CREATE_FILE_MENU[2], CREATE_FILE_MENU[3], 5, 5);// 保存文件菜单outtextxy(SAVE_FILE_MENU[0], SAVE_FILE_MENU[1], "保存文件");roundrect(SAVE_FILE_MENU[0], SAVE_FILE_MENU[1], SAVE_FILE_MENU[2], SAVE_FILE_MENU[3], 5, 5);// 退出菜单outtextxy(EXIT_MENU[0], EXIT_MENU[1], "退出");roundrect(EXIT_MENU[0], EXIT_MENU[1], EXIT_MENU[2], EXIT_MENU[3], 5, 5);// 编辑文件菜单outtextxy(EDIT_FILE_MENU[0], EDIT_FILE_MENU[1], "编辑文件");// 插入菜单outtextxy(INSERT_TEXT_MENU[0], INSERT_TEXT_MENU[1], "插入文字");roundrect(INSERT_TEXT_MENU[0], INSERT_TEXT_MENU[1], INSERT_TEXT_MENU[2], INSERT_TEXT_MENU[3], 5, 5);// 查找菜单outtextxy(FIND_MENU[0], FIND_MENU[1], "查找");roundrect(FIND_MENU[0], FIND_MENU[1], FIND_MENU[2], FIND_MENU[3], 5, 5);// 清除输入菜单outtextxy(CLEAR_MENU[0], CLEAR_MENU[1], "清除输入");roundrect(CLEAR_MENU[0], CLEAR_MENU[1], CLEAR_MENU[2], CLEAR_MENU[3], 5, 5);// 替换菜单outtextxy(REPLACE_MENU[0], REPLACE_MENU[1], "替换");roundrect(REPLACE_MENU[0], REPLACE_MENU[1], REPLACE_MENU[2], REPLACE_MENU[3], 5, 5);// 设置字体gettextstyle(&f);f.lfHeight = 18;_tcscpy(f.lfFaceName, _T("宋体"));f.lfQuality = ANTIALIASED_QUALITY;f.lfWeight = 800;settextstyle(&f);settextcolor(RGB(0, 0, 0));// 查找文本if (strlen(searchText) > 0) {outtextxy(SEARCH_TEXT[0], SEARCH_TEXT[1]+3, searchText);}roundrect(SEARCH_TEXT[0], SEARCH_TEXT[1], SEARCH_TEXT[2], SEARCH_TEXT[3], 5, 5);// 原文本if (strlen(sourceText) > 0) {outtextxy(SOURCE_TEXT[0], SOURCE_TEXT[1] + 3, sourceText);}roundrect(SOURCE_TEXT[0], SOURCE_TEXT[1], SOURCE_TEXT[2], SOURCE_TEXT[3], 5, 5);// 替换文本if (strlen(replaceText) > 0) {outtextxy(REPLACE_TEXT[0], REPLACE_TEXT[1] + 3, replaceText);}roundrect(REPLACE_TEXT[0], REPLACE_TEXT[1], REPLACE_TEXT[2], REPLACE_TEXT[3], 5, 5);
}// 绘制文本区域
static void drawText() {// 设置字体LOGFONT f;gettextstyle(&f);f.lfHeight = 20;f.lfWeight = 600;_tcscpy(f.lfFaceName, _T("宋体"));f.lfQuality = ANTIALIASED_QUALITY;settextstyle(&f);settextcolor(RGB(0, 0, 0));// 显示文本区域框rectangle(TEXT_AREA[0], TEXT_AREA[1], TEXT_AREA[2], TEXT_AREA[3]);// 显示文件内容File* file = getCurrentFile();if (!file) return;int leftX = TEXT_AREA[0] + 5;int x = leftX;int y = TEXT_AREA[1] + 5;int w = textwidth("a");int h = f.lfHeight;Node* node = file->text->head->next;// 设置头指针的位置file->text->head->rect[0] = TEXT_AREA[0]+4;file->text->head->rect[1] = y;file->text->head->rect[2] = TEXT_AREA[0]+4;file->text->head->rect[3] = y;// 如果文件是空的,则在开头显示指针if (node == file->text->tail) {line(node->prev->rect[2], node->prev->rect[1], node->prev->rect[2], node->prev->rect[1] + h);return;}while (node != file->text->tail) {// 设置字符所占的区域node->rect[0] = x;node->rect[1] = y;node->rect[2] = x + (node->normal ? w : w*2);node->rect[3] = y + h;// 判断是否需要换行if (strcmp(node->content, "\r\n") == 0 || strcmp(node->content, "\n") == 0 || node->rect[2] >= TEXT_AREA[2]) {// 需要换行,更新位置x = leftX;y += f.lfHeight;node->rect[0] = x;node->rect[1] = y;node->rect[2] = x + (node->normal ? w : w * 2);node->rect[3] = y + h;}if (strcmp(node->content, "\r\n") != 0 && strcmp(node->content, "\n") != 0) {// 如果字符被选中,则更改颜色if (node->selected) {setcolor(RED);}else {setcolor(BLACK);}// 显示该字符outtextxy(node->rect[0], node->rect[1], node->content);// 更新x值x = node->rect[2];}// 绘制当前位置指针if (file->text->cursor == node->prev) {line(node->prev->rect[2], node->prev->rect[1], node->prev->rect[2], node->prev->rect[1] + h);}else if (file->text->cursor == node && node->next == file->text->tail) {line(node->rect[2], node->rect[1], node->rect[2], node->rect[1] + h);}node = node->next;}
}// 更新文件指针位置
static void updateCursor(int x, int y) {File* file = getCurrentFile();if (!file) return;// 设置文件指针到最靠近鼠标的文字的位置(距离不能超过26)int minDistance = INT_MAX;Node* node = file->text->head->next;while (node != file->text->tail) {if (x > node->rect[0] && x < node->rect[2] &&y > node->rect[1] && y < node->rect[3]) {file->text->cursor = node;break;}else {// 判断该文字是否更加靠近鼠标点击的位置int dx = (node->rect[0] + node->rect[2]) / 2 - x;int dy = (node->rect[1] + node->rect[3]) / 2 - y;double dist = sqrt(dx * dx + dy * dy);if (dist < 26 && dist < minDistance) {minDistance = dist;file->text->cursor = node;}}node = node->next;}
}// 读入文件
static void ReadFile() {// 提示用户输入文件路径char path[256];InputBox(path, 255, "输入文件路径:");// 读入文件内容if (!readFile(path)) {MessageBox(GetHWnd(), "该文件不存在", "提示", MB_OK);}
}// 写入文件
static void WriteFile() {if (getCurrentFile()) {writeFile();MessageBox(GetHWnd(), "内容已被写入文件", "提示", MB_OK);}
}// 新建文件
static void CreateNewFile() {// 提示用户输入文件路径char path[256];InputBox(path, 255, "输入文件路径:");// 创建新的文件if (!createFile(path)) {MessageBox(GetHWnd(), "创建文件失败", "提示", MB_OK);}
}// 保存文件
static void SaveFile() {// 提示用户输入文件路径char path[256];InputBox(path, 255, "输入文件路径:");if (!saveFile(path)) {MessageBox(GetHWnd(), "保存文件失败", "提示", MB_OK);}else {MessageBox(GetHWnd(), "文件已被保存", "提示", MB_OK);}
}// 插入文字
static void InsertText() {// 提示输入要插入的文字char content[256];InputBox(content, 255, "输入要插入的文字:");// 把该文字插入指针所在位置insertText(content);
}// 查找
static void FindString() {resetSelected();if (strlen(searchText) > 0) {findString(searchText);}
}// 清除输入
static void ClearInput() {memset(searchText, 0, sizeof(searchText));memset(sourceText, 0, sizeof(sourceText));memset(replaceText, 0, sizeof(replaceText));// 重置选中标志resetSelected();
}// 替换
static void ReplaceString() {if (strlen(sourceText) > 0 && strlen(replaceText) > 0) {replaceString(sourceText, replaceText);}
}
Main.cpp:程序的入口文件,主要用于启动文本编辑器的初始化、运行和销毁。
main()
:
-
程序启动时首先调用
initTextEditor()
函数初始化文本编辑器。 -
接着调用
runTextEditor()
启动文本编辑器的主循环,允许用户进行各种操作(如文件操作、文本编辑等)。 -
最后通过
destroyTextEditor()
销毁编辑器,释放资源。
#include "Editor.h"int main() {// 初始化文本编辑器initTextEditor();// 运行文本编辑器runTextEditor();// 销毁文本编辑器destroyTextEditor();return 0;
}