如何實現一個可視化的文字編輯器(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;
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/79288.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/79288.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/79288.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

ArrayList的特點及應用場景

ArrayList的特點及應用場景 一、ArrayList核心特點 基于動態數組實現 底層使用Object[]數組存儲元素 默認初始容量為10 擴容機制&#xff1a;每次擴容為原來的1.5倍&#xff08;int newCapacity oldCapacity (oldCapacity >> 1)&#xff09; 快速隨機訪問 實現了R…

深挖Java基礎之:變量與類型

今天我要介紹的是在Java中對變量和類型的一些相關知識點的介紹&#xff0c;包括對基本數據類型&#xff0c;引用類型&#xff0c;變量命名規則和類型轉換以及其注意事項的解明。 java變量與類型&#xff1a;Java 是靜態類型語言&#xff0c;變量必須先聲明類型后使用。變量是存…

數據結構與算法學習筆記(Acwing提高課)----動態規劃·背包模型(一)

數據結構與算法學習筆記----動態規劃背包模型(一) author: 明月清了個風 first publish time: 2025.5.1 ps??背包模型是動態規劃中的重要模型&#xff0c;基礎課中已對背包模型的幾種模版題有了講解&#xff0c;[鏈接在這](數據結構與算法學習筆記----背包問題_有 n 件物品…

Java關鍵字解析

Java關鍵字是編程語言中具有特殊含義的保留字&#xff0c;不能用作標識符&#xff08;如變量名、類名等&#xff09;。Java共有50多個關鍵字&#xff08;不同版本略有差異&#xff09;&#xff0c;下面我將分類詳細介紹這些關鍵字及其使用方式。 一、數據類型相關關鍵字 1. 基…

vue自定義表頭內容excel表格導出

1、安裝 npm install xlsx file-saver 2、使用 import * as XLSX from xlsx import { saveAs } from file-saverconst exportAccounts (data) > {// 將對象數組轉換為 worksheetconst worksheet XLSX.utils.json_to_sheet(data)// 創建 workbook 并附加 sheetconst wor…

鴻蒙NEXT開發組件截圖和窗口截圖工具類SnapshotUtil(ArkTs)

import { image } from kit.ImageKit; import { componentSnapshot, window } from kit.ArkUI; import { AppUtil } from ./AppUtil; import { ArrayUtil } from ./ArrayUtil;/*** 組件截圖和窗口截圖工具類* author 鴻蒙布道師* since 2025/04/28*/ export class SnapshotUtil…

C#與SVN的深度集成:實現版本控制自動化管理?

目錄 1. 環境準備 2. 創建 C# 工程 3. 引用 SharpSvn 庫 4. 編寫代碼 1. 環境準備 2. 創建 C# 工程 3. 引用 SharpSvn 庫 4. 編寫代碼 5. 代碼說明 6. 注意事項 1. 環境準備 首先&#xff0c;需要安裝 SharpSvn 庫。可以從 SharpSvn 官方網站 下載適合 .NET Framewor…

本文不定期更新,用于收錄各種怪異的python腳本

1.計算自然數對數底 a b 1 for n in range(1, 1001):a a * n 1b b * n t a % br . for i in range(1, 1001):t 10if t < b:r 0else:r str(t // b)t % bprint(str(a//b) r) 得到 2.7182818284590452353602874713526624977572470936999595749669676277240766303…

日志之ClickHouse部署及替換ELK中的Elasticsearch

文章目錄 1 ELK替換1.1 Elasticsearch vs ClickHouse1.2 環境部署1.2.1 zookeeper 集群部署1.2.2 Kafka 集群部署1.2.3 FileBeat 部署1.2.4 clickhouse 部署1.2.4.1 準備步驟1.2.4.2 添加官方存儲庫1.2.4.3 部署&啟動&連接1.2.4.5 基本配置服務1.2.4.6 測試創建數據庫和…

2025年大一ACM訓練-搜索

2025年大一ACM訓練-搜索 前期知識&#xff1a;DFS&#xff0c;本文搜索題解法以深度優先搜索為主 1.1 DFS 的定義 深度優先搜索&#xff08;Depth-First Search&#xff09;是一種用于遍歷樹或圖的算法。核心思想是盡可能“深入”訪問圖的每個節點&#xff0c;直到無法繼續前進…

Nginx核心功能02

目錄 一&#xff0c;正向代理 1&#xff0c;編譯安裝Nginx &#xff08;1&#xff09;安裝支持軟件 &#xff08;2&#xff09;創建運行用戶&#xff0c;組和日志目錄 &#xff08;3&#xff09;編譯安裝Nginx &#xff08;4&#xff09;添加Nginx系統服務 2&#xff0c…

rk3568安全啟動功能實踐

本文主要講述筆者在rk3568芯片上開發安全啟動功能實踐的流程。其中主要參考瑞芯微官方文檔《Rockchip_Developer_Guide_Secure_Boot_for_UBoot_Next_Dev_CN.pdf》。文檔中描述邏輯不是很清晰而且和當前瑞芯微的sdk中安全啟動的流程匹配度不高。本文就不再對瑞芯微官方文檔的內容…

[操作系統] 線程互斥

文章目錄 背景概念線程互斥的引出互斥量鎖的操作初始化 (Initialization)靜態初始化動態初始化 加鎖 (Locking)阻塞式加鎖非阻塞式加鎖 (嘗試加鎖/一般不考慮) 解鎖 (Unlocking)銷毀 (Destruction)設置屬性 (Setting Attributes - 通過 pthread_mutex_init) 鎖本身的保護互斥鎖…

【神經網絡與深度學習】兩種加載 pickle 文件方式(joblib、pickle)的差異

引言 從深度學習應用到數據分析的多元化需求出發&#xff0c;Python 提供了豐富的工具和模塊&#xff0c;其中 pickle 和 joblib 兩種方式在加載數據文件方面表現尤為突出。不同場景對性能、兼容性以及后續處理的要求不盡相同&#xff0c;使得這兩種方式各顯優勢。本文將通過深…

Electron 入門指南

Electron 入門指南 Electron 是一個使用 JavaScript、HTML 和 CSS 構建跨平臺桌面應用的框架。通過 Electron&#xff0c;你可以利用 Web 技術開發出功能強大的桌面應用程序&#xff0c;并且能夠運行在 Windows、Mac 和 Linux 系統上。 本文將帶你從零開始構建一個簡單的 Ele…

編程中如何與AI交互-結構化輸入和理解確認機制

一 結構化輸入是什么 &#x1f4cc; 結構化輸入的定義&#xff1a; 結構化輸入是指以清晰、分層、有邏輯的格式向 AI 輸入信息&#xff0c;使其更容易解析內容、抓住重點&#xff0c;并準確回答問題。 &#x1f4e6; 舉個例子&#xff08;編程場景&#xff09;&#xff1a; 非…

13:傅里葉變換

傅立葉變換(FT, Fourier Transform)的作用是將一個信號由時域變換到頻域。其實就是把數據由橫坐標時間、縱坐標采樣值的波形圖格式&#xff0c;轉換為橫坐標頻率、縱坐標振幅(或相位)的頻譜格式。換后可以很明顯地看出一些原先不易察覺的特征。 有些信號在時域上是很難看出什么…

基于單片機的音頻信號處理系統設計(一)

項目名稱:基于單片機的音頻信號處理系統設計學院名稱:信息學院學生姓名:學號專業年級:指導教師:教師職稱:教授企業導師:目 錄 摘 要 Abstract 1 前言 1.1研究背景與意義 <

機器學習實操 第一部分 機器學習基礎 第8章 降維技術

機器學習實操 第一部分 機器學習基礎 第8章 降維技術 內容概要 第8章探討了降維技術&#xff0c;這些技術在處理高維數據時至關重要。高維數據不僅會使訓練過程變得極其緩慢&#xff0c;還會增加找到良好解決方案的難度&#xff0c;這就是所謂的維度災難問題。幸運的是&#…

微信小程序 XSS 防護知識整理

場景1&#xff1a;用戶輸入表單&#xff08;如評論框&#xff09; 錯誤做法&#xff1a;直接渲染未過濾的用戶輸入 // WXML <view>{{ userInput }}</view>// JS&#xff08;用戶輸入了惡意內容&#xff09; Page({data: { userInput: <script>alert("…