mirror of
https://github.com/LHY0125/PathEditor.git
synced 2026-05-10 19:19:47 +08:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3302132644 | |||
| 59db3dc33b | |||
| 197b318c61 | |||
| b35fac5358 | |||
| 7db190306c | |||
| 575fcca5c4 | |||
| 39d06e20e0 |
@@ -18,9 +18,9 @@ CFLAGS = -Wall -O2 -I$(INCLUDE_DIR) -I$(LOCAL_INCLUDE_DIR) -D_WIN32 -DUNICODE -D
|
|||||||
LDFLAGS = -L$(LIB_DIR) -liup -liupcd -lgdi32 -lcomdlg32 -lcomctl32 -luuid -lole32 -ladvapi32 -mwindows
|
LDFLAGS = -L$(LIB_DIR) -liup -liupcd -lgdi32 -lcomdlg32 -lcomctl32 -luuid -lole32 -ladvapi32 -mwindows
|
||||||
|
|
||||||
# Source
|
# Source
|
||||||
SRC = src/main.c src/utils.c src/registry.c src/callbacks.c
|
SRC = src/main.c src/utils.c src/registry.c src/ui.c src/ui_utils.c src/cb_edit.c src/cb_file.c src/cb_main.c src/globals.c
|
||||||
RES = ico/resources.rc
|
RES = ico/resources.rc
|
||||||
OBJ = $(OBJ_DIR)/main.o $(OBJ_DIR)/utils.o $(OBJ_DIR)/registry.o $(OBJ_DIR)/callbacks.o $(OBJ_DIR)/resources.o
|
OBJ = $(OBJ_DIR)/main.o $(OBJ_DIR)/utils.o $(OBJ_DIR)/registry.o $(OBJ_DIR)/ui.o $(OBJ_DIR)/ui_utils.o $(OBJ_DIR)/cb_edit.o $(OBJ_DIR)/cb_file.o $(OBJ_DIR)/cb_main.o $(OBJ_DIR)/globals.o $(OBJ_DIR)/resources.o
|
||||||
EXE = $(BIN_DIR)/PathEditor.exe
|
EXE = $(BIN_DIR)/PathEditor.exe
|
||||||
|
|
||||||
all: $(BIN_DIR) $(OBJ_DIR) $(EXE)
|
all: $(BIN_DIR) $(OBJ_DIR) $(EXE)
|
||||||
@@ -43,7 +43,22 @@ $(OBJ_DIR)/utils.o: src/utils.c
|
|||||||
$(OBJ_DIR)/registry.o: src/registry.c
|
$(OBJ_DIR)/registry.o: src/registry.c
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
$(OBJ_DIR)/callbacks.o: src/callbacks.c
|
$(OBJ_DIR)/ui.o: src/ui.c
|
||||||
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
$(OBJ_DIR)/ui_utils.o: src/ui_utils.c
|
||||||
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
$(OBJ_DIR)/cb_edit.o: src/cb_edit.c
|
||||||
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
$(OBJ_DIR)/cb_file.o: src/cb_file.c
|
||||||
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
$(OBJ_DIR)/cb_main.o: src/cb_main.c
|
||||||
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
$(OBJ_DIR)/globals.o: src/globals.c
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
$(OBJ_DIR)/resources.o: ico/resources.rc
|
$(OBJ_DIR)/resources.o: ico/resources.rc
|
||||||
|
|||||||
@@ -1,18 +1,51 @@
|
|||||||
# Path Editor (系统环境变量编辑器)
|
# Path Editor (系统环境变量编辑器)
|
||||||
|
|
||||||
一个简单、轻量级的 Windows 系统环境变量(PATH)编辑器,基于 C 语言和 IUP 图形库开发。
|
* Path Editor 是一个专为 Windows 用户设计的系统环境变量(PATH)管理工具。它基于原生 C 语言和 IUP 图形库开发,旨在替代 Windows 自带的简陋编辑界面。
|
||||||
|
* 相比系统自带的编辑器,Path Editor 提供了更加直观的双视图(系统/用户变量)界面、智能的路径有效性检测、自动备份机制以及便捷的拖拽操作,让环境变量的管理变得安全、高效且轻松。无论您是开发者还是系统管理员,它都是您配置开发环境的得力助手。
|
||||||
|
|
||||||
## ✨ 功能特点
|
## ✨ 功能特点
|
||||||
|
|
||||||
* **可视化编辑**:直观地查看和管理系统 PATH 环境变量。
|
* **🛡️ 安全第一**:
|
||||||
* **安全操作**:必须以管理员身份运行才能保存更改,防止误操作。
|
* **自动备份**:每次保存前自动备份注册表,防止意外。
|
||||||
|
* **只读模式**:非管理员运行时自动切换到只读模式,防止误操作。
|
||||||
|
* **权限检测**:智能检测当前运行权限。
|
||||||
|
|
||||||
|
* **📑 双视图与预览**:
|
||||||
|
* **分离管理**:完美支持 **System (系统变量)** 和 **User (用户变量)** 的分离查看与编辑。
|
||||||
|
* **合并预览**:新增标签页,展示最终解析后的完整 PATH 顺序(系统在前,用户在后),方便排查冲突。
|
||||||
|
* **变量展开**:鼠标悬停在包含变量(如 `%JAVA_HOME%`)的路径上时,自动显示解析后的绝对路径。
|
||||||
|
|
||||||
|
* **🔴 智能诊断与维护**:
|
||||||
|
* **无效路径高亮**:自动检测路径是否存在,不存在的显示为红色。
|
||||||
|
* **重复路径高亮**:自动检测重复项,重复的显示为橙色。
|
||||||
|
* **一键清理**:智能移除所有无效和重复的路径,保持环境整洁。
|
||||||
|
|
||||||
|
* **📂 高级交互**:
|
||||||
|
* **多选支持**:支持使用 `Ctrl` 或 `Shift` 进行多选,批量删除或移动路径。
|
||||||
|
* **撤销/重做**:支持 `Ctrl+Z` 撤销和 `Ctrl+Y` 重做,防止误操作,并提供直观的工具栏按钮。
|
||||||
|
* **拖拽支持**:直接将文件夹拖入窗口即可添加(支持管理员模式下的 UIPI 穿透)。
|
||||||
|
* **实时搜索**:顶部搜索框支持不区分大小写的实时过滤查找(支持快捷键 `Ctrl+F`)。
|
||||||
|
* **全局快捷键**:支持 `Ctrl+N` 新建、`Ctrl+S` 保存、`Delete` 删除等快捷操作。
|
||||||
|
|
||||||
|
* **🎨 个性化**:
|
||||||
|
* **深色模式**:一键切换深色主题,保护视力。
|
||||||
|
|
||||||
* **便捷管理**:
|
* **便捷管理**:
|
||||||
* ➕ **新建**:添加新路径到列表。
|
* ➕ **新建**:添加新路径到列表。
|
||||||
* 📂 **浏览**:直接从文件资源管理器选择目录添加。
|
* 📂 **浏览**:直接从文件资源管理器选择目录添加。
|
||||||
* ✏️ **编辑**:修改现有路径。
|
* ✏️ **编辑**:双击或点击按钮修改现有路径。
|
||||||
* 🗑️ **删除**:移除不需要的路径。
|
* 🗑️ **删除**:移除不需要的路径。
|
||||||
* ⬆️⬇️ **排序**:上移/下移调整路径优先级。
|
* ⬆️⬇️ **排序**:上移/下移调整路径优先级。
|
||||||
* **轻量级**:原生 C 语言编写,运行速度快,占用资源少。
|
* 📥/📤 **导入导出**:支持将环境变量导出为 `.txt` 文本文件备份,或从文件导入恢复。
|
||||||
|
|
||||||
|
* **轻量级**:原生 C 语言编写,无臃肿依赖,运行速度极快。
|
||||||
|
|
||||||
|
## 🏗️ 架构与二次开发
|
||||||
|
|
||||||
|
本项目注重代码的模块化和可维护性,非常适合作为 C 语言桌面程序开发的参考:
|
||||||
|
|
||||||
|
* **统一配置中心**:所有的 UI 尺寸、间距、颜色等常量配置均提取在 `include/config.h` 中,只需修改宏定义即可轻松定制属于你的专属界面风格。
|
||||||
|
* **清晰的全局状态**:全局变量和常量被独立分离在 `src/globals.c` / `include/globals.h` 中管理,使得核心业务逻辑更加整洁。
|
||||||
|
|
||||||
## 📦 下载与安装
|
## 📦 下载与安装
|
||||||
|
|
||||||
@@ -30,6 +63,7 @@
|
|||||||
* GCC 编译器 (推荐 MinGW-w64)
|
* GCC 编译器 (推荐 MinGW-w64)
|
||||||
* Make 工具
|
* Make 工具
|
||||||
* IUP 库 (已包含在 `libs` 目录下)
|
* IUP 库 (已包含在 `libs` 目录下)
|
||||||
|
* Inno Setup 6 (仅打包需要)
|
||||||
|
|
||||||
### 编译步骤
|
### 编译步骤
|
||||||
|
|
||||||
@@ -54,16 +88,26 @@
|
|||||||
本项目使用 Inno Setup 生成安装包。
|
本项目使用 Inno Setup 生成安装包。
|
||||||
|
|
||||||
1. 确保已安装 [Inno Setup 6](https://jrsoftware.org/isdl.php)。
|
1. 确保已安装 [Inno Setup 6](https://jrsoftware.org/isdl.php)。
|
||||||
2. 编译项目生成 exe 文件。
|
2. 运行根目录下的 `build_installer.bat` 脚本。
|
||||||
3. 使用 Inno Setup 编译 `dist/installer.iss` 脚本。
|
3. 生成的安装包将位于 `dist/dist/PathEditorSetup.exe`。
|
||||||
|
|
||||||
## 📝 使用说明
|
## 📝 使用说明
|
||||||
|
|
||||||
1. **启动**:右键点击程序图标,选择“以管理员身份运行”。
|
1. **启动**:右键点击程序图标,选择“以管理员身份运行”。
|
||||||
2. **查看**:程序启动后会自动加载当前的系统 PATH 变量。
|
2. **查看**:程序启动后会自动加载当前的系统 PATH 变量。
|
||||||
3. **修改**:使用右侧按钮栏进行添加、删除、移动等操作。
|
* **红色**条目表示路径不存在。
|
||||||
4. **保存**:操作完成后,务必点击底部的【确定】按钮保存更改。
|
* **橙色**条目表示路径重复。
|
||||||
5. **生效**:保存后,某些正在运行的程序可能需要重启才能识别新的环境变量。CMD 或 PowerShell 窗口需要重新打开。
|
* **变量预览**:鼠标悬停在带 `%` 的变量上可查看实际路径。
|
||||||
|
3. **搜索**:在顶部输入关键词或按 `Ctrl+F` 可快速筛选。
|
||||||
|
4. **修改**:
|
||||||
|
* **拖拽**:将文件夹拖入列表可直接添加。
|
||||||
|
* **多选**:按住 `Ctrl` 或 `Shift` 可选中多项进行批量删除。
|
||||||
|
* **撤销/重做**:误操作时可使用 `Ctrl+Z` / `Ctrl+Y` 或工具栏按钮回退。
|
||||||
|
* **常规操作**:使用右侧按钮栏进行新建、编辑、移动等操作。
|
||||||
|
* **清理**:点击“一键清理”可自动删除无效和重复项。
|
||||||
|
* **导入/导出**:使用导入导出功能备份或恢复配置。
|
||||||
|
5. **保存**:操作完成后,务必点击底部的【确定】按钮(或按 `Ctrl+S`)保存更改。
|
||||||
|
6. **生效**:保存后,某些正在运行的程序可能需要重启才能识别新的环境变量。CMD 或 PowerShell 窗口需要重新打开。
|
||||||
|
|
||||||
## 👤 作者信息
|
## 👤 作者信息
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,15 @@
|
|||||||
|
@echo off
|
||||||
|
echo Copying DLLs...
|
||||||
|
if not exist bin mkdir bin
|
||||||
|
copy /Y "libs\iup-3.31_Win64_dllw6_lib\*.dll" bin\
|
||||||
|
|
||||||
|
echo Building Installer...
|
||||||
|
"D:\Program Files (x86)\Inno Setup 6\ISCC.exe" "dist\installer.iss"
|
||||||
|
|
||||||
|
if %ERRORLEVEL% NEQ 0 (
|
||||||
|
echo Installer build failed!
|
||||||
|
exit /b %ERRORLEVEL%
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Done! Installer is in dist\dist\
|
||||||
|
pause
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
#ifndef CALLBACKS_H
|
|
||||||
#define CALLBACKS_H
|
|
||||||
|
|
||||||
#include <iup.h>
|
|
||||||
|
|
||||||
// 按钮回调
|
|
||||||
int btn_new_cb(Ihandle* self);
|
|
||||||
int btn_edit_cb(Ihandle* self);
|
|
||||||
int btn_browse_cb(Ihandle* self);
|
|
||||||
int btn_del_cb(Ihandle* self);
|
|
||||||
int btn_up_cb(Ihandle* self);
|
|
||||||
int btn_down_cb(Ihandle* self);
|
|
||||||
int btn_ok_cb(Ihandle* self);
|
|
||||||
int btn_cancel_cb(Ihandle* self);
|
|
||||||
int btn_help_cb(Ihandle* self);
|
|
||||||
|
|
||||||
// 双击回调
|
|
||||||
int list_dblclick_cb(Ihandle* self, int item, char* text);
|
|
||||||
|
|
||||||
#endif // CALLBACKS_H
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#ifndef CB_EDIT_H
|
||||||
|
#define CB_EDIT_H
|
||||||
|
|
||||||
|
#include <iup.h>
|
||||||
|
|
||||||
|
// 编辑相关回调
|
||||||
|
int btn_new_cb(Ihandle *self);
|
||||||
|
int btn_edit_cb(Ihandle *self);
|
||||||
|
int list_dblclick_cb(Ihandle *self, int item, char *text);
|
||||||
|
int btn_del_cb(Ihandle *self);
|
||||||
|
int btn_up_cb(Ihandle *self);
|
||||||
|
int btn_down_cb(Ihandle *self);
|
||||||
|
int btn_clean_cb(Ihandle *self);
|
||||||
|
|
||||||
|
#endif // CB_EDIT_H
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#ifndef CB_FILE_H
|
||||||
|
#define CB_FILE_H
|
||||||
|
|
||||||
|
#include <iup.h>
|
||||||
|
|
||||||
|
// 文件和历史记录回调
|
||||||
|
int btn_browse_cb(Ihandle *self);
|
||||||
|
int btn_undo_cb(Ihandle *self);
|
||||||
|
int btn_redo_cb(Ihandle *self);
|
||||||
|
int btn_export_cb(Ihandle *self);
|
||||||
|
int btn_import_cb(Ihandle *self);
|
||||||
|
int list_dropfiles_cb(Ihandle *self, const char *filename, int num, int x, int y);
|
||||||
|
|
||||||
|
#endif // CB_FILE_H
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef CB_MAIN_H
|
||||||
|
#define CB_MAIN_H
|
||||||
|
|
||||||
|
#include <iup.h>
|
||||||
|
|
||||||
|
// 主界面交互回调
|
||||||
|
int txt_search_cb(Ihandle *self);
|
||||||
|
int list_k_any_cb(Ihandle *self, int c);
|
||||||
|
int list_motion_cb(Ihandle *self, int x, int y, char *status);
|
||||||
|
int dialog_k_any_cb(Ihandle *self, int c);
|
||||||
|
int btn_ok_cb(Ihandle *self);
|
||||||
|
int btn_cancel_cb(Ihandle *self);
|
||||||
|
int btn_help_cb(Ihandle *self);
|
||||||
|
int tabs_tabchange_cb(Ihandle *self, int new_pos, int old_pos);
|
||||||
|
int btn_theme_cb(Ihandle *self);
|
||||||
|
|
||||||
|
#endif // CB_MAIN_H
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#ifndef CONFIG_H
|
||||||
|
#define CONFIG_H
|
||||||
|
|
||||||
|
// UI 常量定义
|
||||||
|
#define UI_WINDOW_TITLE "Path Editor" // 窗口标题
|
||||||
|
#define UI_WINDOW_SIZE "800x800" // 窗口默认大小 (像素)
|
||||||
|
|
||||||
|
// 按钮尺寸
|
||||||
|
#define UI_BUTTON_SIZE "100x32" // 按钮默认大小 (像素)
|
||||||
|
#define UI_BUTTON_SMALL_SIZE "80x32" // 小按钮大小 (像素)
|
||||||
|
|
||||||
|
// 布局间距
|
||||||
|
#define UI_MARGIN_MAIN "10x10" // 主布局外边距 (像素)
|
||||||
|
#define UI_GAP_MAIN "10" // 主布局间距 (像素)
|
||||||
|
#define UI_GAP_BUTTONS "5" // 按钮间距 (像素)
|
||||||
|
#define UI_GAP_BOTTOM "10" // 底部间距 (像素)
|
||||||
|
|
||||||
|
// 列表属性
|
||||||
|
#define UI_LIST_ITEM_PADDING "5x5" // 列表项内边距 (像素)
|
||||||
|
#define UI_LIST_BGCOLOR "255 255 255" // 列表背景颜色 (RGB)
|
||||||
|
#define UI_LIST_MERGED_BGCOLOR "240 240 240"// 合并列表背景颜色 (RGB)
|
||||||
|
|
||||||
|
#endif // CONFIG_H
|
||||||
+54
-2
@@ -4,12 +4,16 @@
|
|||||||
#include <iup.h>
|
#include <iup.h>
|
||||||
|
|
||||||
// 注册表路径常量
|
// 注册表路径常量
|
||||||
#define REG_PATH L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"
|
#define REG_PATH_SYS L"SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment"
|
||||||
|
#define REG_PATH_USER L"Environment"
|
||||||
#define REG_VALUE L"Path"
|
#define REG_VALUE L"Path"
|
||||||
|
|
||||||
// 全局控件句柄声明
|
// 全局控件句柄声明
|
||||||
extern Ihandle *dlg; // 主对话框句柄
|
extern Ihandle *dlg; // 主对话框句柄
|
||||||
extern Ihandle *list_path; // 路径列表控件句柄
|
extern Ihandle *tabs_main; // 标签页容器
|
||||||
|
extern Ihandle *list_sys; // 系统变量列表
|
||||||
|
extern Ihandle *list_user; // 用户变量列表
|
||||||
|
extern Ihandle *list_merged; // 合并变量列表
|
||||||
extern Ihandle *lbl_status; // 状态标签句柄
|
extern Ihandle *lbl_status; // 状态标签句柄
|
||||||
extern Ihandle *btn_new; // 新增按钮句柄
|
extern Ihandle *btn_new; // 新增按钮句柄
|
||||||
extern Ihandle *btn_edit; // 编辑按钮句柄
|
extern Ihandle *btn_edit; // 编辑按钮句柄
|
||||||
@@ -17,8 +21,56 @@ extern Ihandle *btn_browse; // 浏览按钮句柄
|
|||||||
extern Ihandle *btn_del; // 删除按钮句柄
|
extern Ihandle *btn_del; // 删除按钮句柄
|
||||||
extern Ihandle *btn_up; // 上移按钮句柄
|
extern Ihandle *btn_up; // 上移按钮句柄
|
||||||
extern Ihandle *btn_down; // 下移按钮句柄
|
extern Ihandle *btn_down; // 下移按钮句柄
|
||||||
|
extern Ihandle *btn_clean; // 一键清理按钮句柄
|
||||||
|
extern Ihandle *btn_undo; // 撤销按钮句柄
|
||||||
|
extern Ihandle *btn_redo; // 重做按钮句柄
|
||||||
|
extern Ihandle *btn_import; // 导入按钮句柄
|
||||||
|
extern Ihandle *btn_export; // 导出按钮句柄
|
||||||
|
extern Ihandle *btn_theme; // 主题切换按钮句柄
|
||||||
extern Ihandle *btn_ok; // 确认按钮句柄
|
extern Ihandle *btn_ok; // 确认按钮句柄
|
||||||
|
extern int is_dark_mode; // 深色模式状态
|
||||||
extern Ihandle *btn_cancel; // 取消按钮句柄
|
extern Ihandle *btn_cancel; // 取消按钮句柄
|
||||||
extern Ihandle *btn_help; // 帮助按钮句柄
|
extern Ihandle *btn_help; // 帮助按钮句柄
|
||||||
|
extern Ihandle *txt_search; // 搜索框
|
||||||
|
|
||||||
|
// 简单字符串列表结构,用于搜索缓存
|
||||||
|
typedef struct {
|
||||||
|
char **items;
|
||||||
|
int count;
|
||||||
|
int capacity;
|
||||||
|
} StringList;
|
||||||
|
|
||||||
|
extern StringList raw_sys_paths;
|
||||||
|
extern StringList raw_user_paths;
|
||||||
|
|
||||||
|
// 历史记录节点
|
||||||
|
typedef struct HistoryNode {
|
||||||
|
StringList sys_paths;
|
||||||
|
StringList user_paths;
|
||||||
|
struct HistoryNode *next;
|
||||||
|
} HistoryNode;
|
||||||
|
|
||||||
|
// 历史记录栈
|
||||||
|
typedef struct {
|
||||||
|
HistoryNode *top;
|
||||||
|
int count;
|
||||||
|
} HistoryStack;
|
||||||
|
|
||||||
|
extern HistoryStack undo_stack;
|
||||||
|
extern HistoryStack redo_stack;
|
||||||
|
extern Ihandle *btn_undo;
|
||||||
|
extern Ihandle *btn_redo;
|
||||||
|
|
||||||
|
// 缓存操作函数声明
|
||||||
|
void init_string_list(StringList *list);
|
||||||
|
void add_string_list(StringList *list, const char *str);
|
||||||
|
void clear_string_list(StringList *list);
|
||||||
|
void copy_string_list(StringList *dest, StringList *src);
|
||||||
|
|
||||||
|
// 历史记录操作
|
||||||
|
void init_history_stack(HistoryStack *stack);
|
||||||
|
void push_history(HistoryStack *stack, StringList *sys, StringList *user);
|
||||||
|
int pop_history(HistoryStack *stack, StringList *out_sys, StringList *out_user);
|
||||||
|
void clear_history_stack(HistoryStack *stack);
|
||||||
|
|
||||||
#endif // GLOBALS_H
|
#endif // GLOBALS_H
|
||||||
+3
-3
@@ -1,10 +1,10 @@
|
|||||||
#ifndef REGISTRY_H
|
#ifndef REGISTRY_H
|
||||||
#define REGISTRY_H
|
#define REGISTRY_H
|
||||||
|
|
||||||
// 从注册表加载PATH到列表控件
|
// 从注册表加载所有PATH到列表控件
|
||||||
void load_path();
|
void load_all_paths();
|
||||||
|
|
||||||
// 将列表控件中的PATH保存回注册表
|
// 将列表控件中的PATH保存回注册表
|
||||||
void save_path();
|
void save_all_paths();
|
||||||
|
|
||||||
#endif // REGISTRY_H
|
#endif // REGISTRY_H
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef UI_H
|
||||||
|
#define UI_H
|
||||||
|
|
||||||
|
#include <iup.h>
|
||||||
|
|
||||||
|
// 创建列表控件
|
||||||
|
Ihandle *create_path_list();
|
||||||
|
|
||||||
|
// 创建右侧功能按钮区域
|
||||||
|
Ihandle *create_main_buttons();
|
||||||
|
|
||||||
|
// 创建底部按钮区域
|
||||||
|
Ihandle *create_bottom_buttons();
|
||||||
|
|
||||||
|
Ihandle *create_main_dialog();
|
||||||
|
|
||||||
|
#endif // UI_H
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
#ifndef UI_UTILS_H
|
||||||
|
#define UI_UTILS_H
|
||||||
|
|
||||||
|
#include <iup.h>
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
|
// 辅助函数声明
|
||||||
|
int get_first_selected_index(Ihandle *list);
|
||||||
|
void set_single_selection(Ihandle *list, int index);
|
||||||
|
void refresh_ui_from_raw(Ihandle *list, StringList *raw);
|
||||||
|
void record_history();
|
||||||
|
int custom_input_dialog(const char *title, const char *label_text, char *buffer, int buffer_size);
|
||||||
|
Ihandle *get_current_list();
|
||||||
|
void remove_from_raw_data(StringList *list, const char *str);
|
||||||
|
void toggle_edit_buttons(int enable);
|
||||||
|
void apply_theme();
|
||||||
|
|
||||||
|
#endif // UI_UTILS_H
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
|
#include <iup.h>
|
||||||
|
|
||||||
// 宽字符转UTF-8
|
// 宽字符转UTF-8
|
||||||
char* wide_to_utf8(const wchar_t* wstr);
|
char* wide_to_utf8(const wchar_t* wstr);
|
||||||
@@ -13,7 +14,20 @@ wchar_t* utf8_to_wide(const char* str);
|
|||||||
// 检查管理员权限
|
// 检查管理员权限
|
||||||
int check_admin();
|
int check_admin();
|
||||||
|
|
||||||
|
// 展开环境变量
|
||||||
|
char* expand_env_vars(const char* path);
|
||||||
|
|
||||||
|
// 检查路径是否有效(存在且为目录)
|
||||||
|
int is_path_valid(const char *path);
|
||||||
|
|
||||||
// 刷新列表样式(斑马纹)
|
// 刷新列表样式(斑马纹)
|
||||||
void refresh_list_style();
|
void refresh_list_style();
|
||||||
|
void refresh_single_list_style(Ihandle *list);
|
||||||
|
|
||||||
|
// 备份注册表
|
||||||
|
void backup_registry();
|
||||||
|
|
||||||
|
// 不区分大小写的字符串查找
|
||||||
|
char *stristr(const char *haystack, const char *needle);
|
||||||
|
|
||||||
#endif // UTILS_H
|
#endif // UTILS_H
|
||||||
-327
@@ -1,327 +0,0 @@
|
|||||||
#include "callbacks.h"
|
|
||||||
#include "globals.h"
|
|
||||||
#include "registry.h"
|
|
||||||
#include "utils.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
// 简单的自定义输入对话框,支持更宽的输入框
|
|
||||||
// 返回 1 表示确定,0 表示取消
|
|
||||||
int show_custom_input_dialog(const char *title, const char *label_text, char *buffer, int buffer_size)
|
|
||||||
{
|
|
||||||
Ihandle *text = IupText(NULL);
|
|
||||||
IupSetAttribute(text, "VALUE", buffer);
|
|
||||||
IupSetAttribute(text, "EXPAND", "HORIZONTAL");
|
|
||||||
IupSetAttribute(text, "RASTERSIZE", "600x"); // 设置宽度为600像素
|
|
||||||
IupSetAttribute(text, "VISIBLECOLUMNS", "80"); // 设置可见列数
|
|
||||||
|
|
||||||
// 如果是编辑模式,全选文本
|
|
||||||
if (strlen(buffer) > 0)
|
|
||||||
{
|
|
||||||
IupSetAttribute(text, "SELECTION", "ALL");
|
|
||||||
}
|
|
||||||
|
|
||||||
Ihandle *dlg = IupDialog(
|
|
||||||
IupVbox(
|
|
||||||
IupLabel(label_text),
|
|
||||||
text,
|
|
||||||
IupHbox(
|
|
||||||
IupFill(),
|
|
||||||
IupButton("确定", "1"), // "1" will be returned by IupPopup
|
|
||||||
IupButton("取消", "0"), // "0" will be returned by IupPopup
|
|
||||||
NULL),
|
|
||||||
NULL));
|
|
||||||
|
|
||||||
// 布局设置
|
|
||||||
IupSetAttribute(dlg, "TITLE", title);
|
|
||||||
IupSetAttribute(dlg, "MINBOX", "NO");
|
|
||||||
IupSetAttribute(dlg, "MAXBOX", "NO");
|
|
||||||
IupSetAttribute(dlg, "RESIZE", "NO");
|
|
||||||
IupSetAttribute(dlg, "MARGIN", "10x10");
|
|
||||||
IupSetAttribute(dlg, "GAP", "10");
|
|
||||||
|
|
||||||
// 按钮响应
|
|
||||||
// 这是一个简单的 hack:IupPopup 的参数 x, y 如果是 IUP_CENTER 等,它是一个模态循环。
|
|
||||||
// 但是标准的 IupPopup 不返回按钮值。
|
|
||||||
// 我们使用 IupAlarm 类似的逻辑,或者使用 IUP 提供的 IupGetParam。
|
|
||||||
// 为了最简单实现,我们使用 IUP 的 IupGetParam,但是它很难调整宽度。
|
|
||||||
// 所以还是手动构建对话框。
|
|
||||||
|
|
||||||
// 为了获取返回值,我们需要设置按钮回调。
|
|
||||||
// 但为了避免定义额外的全局函数,我们可以使用 IupPopup 阻塞特性。
|
|
||||||
// 我们需要定义两个简单的回调函数。
|
|
||||||
// 为了简化,这里定义两个静态辅助函数。
|
|
||||||
|
|
||||||
// 由于 C 语言闭包限制,我们需要用全局或静态变量传递状态,或者使用 Dialog 的 Attribute。
|
|
||||||
IupSetAttribute(dlg, "MY_STATUS", "0");
|
|
||||||
|
|
||||||
// 注册回调 (使用 IupSetCallback 注册 lambda 类似的逻辑比较难,这里用名字)
|
|
||||||
// 必须定义全局函数。为了避免污染,我们在文件顶部定义静态函数。
|
|
||||||
// 见下文的 static int on_dialog_ok...
|
|
||||||
|
|
||||||
// 由于不能在函数内部定义函数,我们需要调整代码结构。
|
|
||||||
// 见下文重构。
|
|
||||||
|
|
||||||
return 0; // 占位
|
|
||||||
}
|
|
||||||
|
|
||||||
// 静态辅助函数:对话框确定
|
|
||||||
static int on_dialog_ok(Ihandle *self)
|
|
||||||
{
|
|
||||||
Ihandle *dlg = IupGetDialog(self);
|
|
||||||
IupSetAttribute(dlg, "MY_STATUS", "1");
|
|
||||||
return IUP_CLOSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 静态辅助函数:对话框取消
|
|
||||||
static int on_dialog_cancel(Ihandle *self)
|
|
||||||
{
|
|
||||||
Ihandle *dlg = IupGetDialog(self);
|
|
||||||
IupSetAttribute(dlg, "MY_STATUS", "0");
|
|
||||||
return IUP_CLOSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 真正的实现函数
|
|
||||||
int custom_input_dialog(const char *title, const char *label_text, char *buffer, int buffer_size)
|
|
||||||
{
|
|
||||||
Ihandle *text = IupText(NULL);
|
|
||||||
IupSetAttribute(text, "VALUE", buffer);
|
|
||||||
IupSetAttribute(text, "EXPAND", "HORIZONTAL");
|
|
||||||
IupSetAttribute(text, "RASTERSIZE", "500x");
|
|
||||||
IupSetAttribute(text, "NAME", "INPUT_TEXT");
|
|
||||||
|
|
||||||
Ihandle *btn_ok = IupButton("确定", NULL);
|
|
||||||
IupSetCallback(btn_ok, "ACTION", on_dialog_ok);
|
|
||||||
IupSetAttribute(btn_ok, "RASTERSIZE", "100x32");
|
|
||||||
|
|
||||||
Ihandle *btn_cancel = IupButton("取消", NULL);
|
|
||||||
IupSetCallback(btn_cancel, "ACTION", on_dialog_cancel);
|
|
||||||
IupSetAttribute(btn_cancel, "RASTERSIZE", "100x32");
|
|
||||||
|
|
||||||
Ihandle *vbox = IupVbox(
|
|
||||||
IupLabel(label_text),
|
|
||||||
text,
|
|
||||||
IupHbox(IupFill(), btn_ok, btn_cancel, NULL),
|
|
||||||
NULL);
|
|
||||||
IupSetAttribute(vbox, "MARGIN", "15x15");
|
|
||||||
IupSetAttribute(vbox, "GAP", "10");
|
|
||||||
|
|
||||||
Ihandle *dlg = IupDialog(vbox);
|
|
||||||
IupSetAttribute(dlg, "TITLE", title);
|
|
||||||
IupSetAttribute(dlg, "MINBOX", "NO");
|
|
||||||
IupSetAttribute(dlg, "MAXBOX", "NO");
|
|
||||||
IupSetAttribute(dlg, "RESIZE", "NO");
|
|
||||||
|
|
||||||
IupSetAttributeHandle(dlg, "DEFAULTENTER", btn_ok);
|
|
||||||
IupSetAttributeHandle(dlg, "DEFAULTESC", btn_cancel);
|
|
||||||
|
|
||||||
IupPopup(dlg, IUP_CENTER, IUP_CENTER);
|
|
||||||
|
|
||||||
int result = IupGetInt(dlg, "MY_STATUS");
|
|
||||||
if (result == 1)
|
|
||||||
{
|
|
||||||
char *val = IupGetAttribute(text, "VALUE");
|
|
||||||
if (val)
|
|
||||||
{
|
|
||||||
strncpy(buffer, val, buffer_size);
|
|
||||||
buffer[buffer_size - 1] = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IupDestroy(dlg);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按钮回调:新建
|
|
||||||
int btn_new_cb(Ihandle *self)
|
|
||||||
{
|
|
||||||
char buffer[1024] = "";
|
|
||||||
if (custom_input_dialog("新建环境变量", "请输入路径:", buffer, sizeof(buffer)))
|
|
||||||
{
|
|
||||||
if (strlen(buffer) > 0)
|
|
||||||
{
|
|
||||||
int count = IupGetInt(list_path, "COUNT");
|
|
||||||
count++;
|
|
||||||
IupSetAttributeId(list_path, "", count, buffer);
|
|
||||||
IupSetInt(list_path, "COUNT", count);
|
|
||||||
IupSetInt(list_path, "VALUE", count);
|
|
||||||
|
|
||||||
refresh_list_style();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return IUP_DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按钮回调:编辑
|
|
||||||
int btn_edit_cb(Ihandle *self)
|
|
||||||
{
|
|
||||||
int selected = IupGetInt(list_path, "VALUE");
|
|
||||||
if (selected == 0)
|
|
||||||
return IUP_DEFAULT;
|
|
||||||
|
|
||||||
char *current_val = IupGetAttributeId(list_path, "", selected);
|
|
||||||
char buffer[4096]; // 假设单个路径不超过4096
|
|
||||||
if (current_val)
|
|
||||||
{
|
|
||||||
strncpy(buffer, current_val, 4096);
|
|
||||||
buffer[4095] = '\0';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
buffer[0] = '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (custom_input_dialog("编辑环境变量", "编辑路径:", buffer, sizeof(buffer)))
|
|
||||||
{
|
|
||||||
if (strlen(buffer) > 0)
|
|
||||||
{
|
|
||||||
IupSetAttributeId(list_path, "", selected, buffer);
|
|
||||||
refresh_list_style();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return IUP_DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 双击回调
|
|
||||||
int list_dblclick_cb(Ihandle *self, int item, char *text)
|
|
||||||
{
|
|
||||||
// 这里的 item 是点击的行号
|
|
||||||
if (item > 0)
|
|
||||||
{
|
|
||||||
// 选中该行
|
|
||||||
IupSetInt(list_path, "VALUE", item);
|
|
||||||
// 调用编辑逻辑
|
|
||||||
btn_edit_cb(NULL);
|
|
||||||
}
|
|
||||||
return IUP_DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按钮回调:浏览
|
|
||||||
int btn_browse_cb(Ihandle *self)
|
|
||||||
{
|
|
||||||
Ihandle *filedlg = IupFileDlg();
|
|
||||||
IupSetAttribute(filedlg, "DIALOGTYPE", "DIR");
|
|
||||||
IupSetAttribute(filedlg, "TITLE", "选择目录");
|
|
||||||
|
|
||||||
IupPopup(filedlg, IUP_CENTER, IUP_CENTER);
|
|
||||||
|
|
||||||
if (IupGetInt(filedlg, "STATUS") != -1)
|
|
||||||
{
|
|
||||||
char *value = IupGetAttribute(filedlg, "VALUE");
|
|
||||||
if (value)
|
|
||||||
{
|
|
||||||
int count = IupGetInt(list_path, "COUNT");
|
|
||||||
count++;
|
|
||||||
IupSetAttributeId(list_path, "", count, value);
|
|
||||||
IupSetInt(list_path, "COUNT", count);
|
|
||||||
IupSetInt(list_path, "VALUE", count);
|
|
||||||
|
|
||||||
refresh_list_style();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
IupDestroy(filedlg);
|
|
||||||
return IUP_DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按钮回调:删除
|
|
||||||
int btn_del_cb(Ihandle *self)
|
|
||||||
{
|
|
||||||
int selected = IupGetInt(list_path, "VALUE");
|
|
||||||
if (selected == 0)
|
|
||||||
return IUP_DEFAULT;
|
|
||||||
|
|
||||||
IupSetAttribute(list_path, "REMOVEITEM", "SELECTED");
|
|
||||||
|
|
||||||
// 重新刷新,因为删除了中间项,后面的奇偶性变了
|
|
||||||
refresh_list_style();
|
|
||||||
return IUP_DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按钮回调:上移
|
|
||||||
int btn_up_cb(Ihandle *self)
|
|
||||||
{
|
|
||||||
int selected = IupGetInt(list_path, "VALUE");
|
|
||||||
if (selected <= 1)
|
|
||||||
return IUP_DEFAULT; // 已经是第一个或未选中
|
|
||||||
|
|
||||||
char *current = IupGetAttributeId(list_path, "", selected);
|
|
||||||
char *prev = IupGetAttributeId(list_path, "", selected - 1);
|
|
||||||
|
|
||||||
// 交换内容
|
|
||||||
char buf_curr[4096], buf_prev[4096];
|
|
||||||
strncpy(buf_curr, current, 4096);
|
|
||||||
buf_curr[4095] = '\0';
|
|
||||||
strncpy(buf_prev, prev, 4096);
|
|
||||||
buf_prev[4095] = '\0';
|
|
||||||
|
|
||||||
IupSetAttributeId(list_path, "", selected, buf_prev);
|
|
||||||
IupSetAttributeId(list_path, "", selected - 1, buf_curr);
|
|
||||||
|
|
||||||
IupSetInt(list_path, "VALUE", selected - 1);
|
|
||||||
|
|
||||||
// 刷新样式(虽然颜色不需要变,但为了保险)
|
|
||||||
refresh_list_style();
|
|
||||||
return IUP_DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按钮回调:下移
|
|
||||||
int btn_down_cb(Ihandle *self)
|
|
||||||
{
|
|
||||||
int selected = IupGetInt(list_path, "VALUE");
|
|
||||||
int count = IupGetInt(list_path, "COUNT");
|
|
||||||
if (selected == 0 || selected >= count)
|
|
||||||
return IUP_DEFAULT;
|
|
||||||
|
|
||||||
char *current = IupGetAttributeId(list_path, "", selected);
|
|
||||||
char *next = IupGetAttributeId(list_path, "", selected + 1);
|
|
||||||
|
|
||||||
char buf_curr[4096], buf_next[4096];
|
|
||||||
strncpy(buf_curr, current, 4096);
|
|
||||||
buf_curr[4095] = '\0';
|
|
||||||
strncpy(buf_next, next, 4096);
|
|
||||||
buf_next[4095] = '\0';
|
|
||||||
|
|
||||||
IupSetAttributeId(list_path, "", selected, buf_next);
|
|
||||||
IupSetAttributeId(list_path, "", selected + 1, buf_curr);
|
|
||||||
|
|
||||||
IupSetInt(list_path, "VALUE", selected + 1);
|
|
||||||
|
|
||||||
refresh_list_style();
|
|
||||||
return IUP_DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按钮回调:确定
|
|
||||||
int btn_ok_cb(Ihandle *self)
|
|
||||||
{
|
|
||||||
save_path();
|
|
||||||
return IUP_DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按钮回调:取消
|
|
||||||
int btn_cancel_cb(Ihandle *self)
|
|
||||||
{
|
|
||||||
IupExitLoop();
|
|
||||||
return IUP_DEFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 按钮回调:帮助
|
|
||||||
int btn_help_cb(Ihandle *self)
|
|
||||||
{
|
|
||||||
IupMessage("使用说明",
|
|
||||||
"1. 本程序用于编辑系统环境变量 PATH。\n"
|
|
||||||
"2. 必须以【管理员身份】运行才能保存更改。\n"
|
|
||||||
"3. 操作说明:\n"
|
|
||||||
" - 新建:添加新路径到列表末尾。\n"
|
|
||||||
" - 编辑:修改选中的路径。\n"
|
|
||||||
" - 浏览:从文件系统选择目录添加。\n"
|
|
||||||
" - 删除:移除选中的路径。\n"
|
|
||||||
" - 上移/下移:调整路径优先级。\n"
|
|
||||||
"4. 点击【确定】保存更改并生效。\n"
|
|
||||||
"5. 注意:某些正在运行的程序可能需要重启才能识别新的环境变量。\n\n"
|
|
||||||
"--------------------------------------------------\n"
|
|
||||||
"作者:LHY\n"
|
|
||||||
"邮箱:3364451258@qq.com\n"
|
|
||||||
"GitHub:https://github.com/LHY0125/PathEditor\n"
|
|
||||||
"记得给我的项目点个star!");
|
|
||||||
return IUP_DEFAULT;
|
|
||||||
}
|
|
||||||
+368
@@ -0,0 +1,368 @@
|
|||||||
|
#include "cb_edit.h"
|
||||||
|
#include "ui_utils.h"
|
||||||
|
#include "globals.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// 按钮回调:新建
|
||||||
|
int btn_new_cb(Ihandle *self)
|
||||||
|
{
|
||||||
|
char buffer[1024] = "";
|
||||||
|
if (custom_input_dialog("新建环境变量", "请输入路径:", buffer, sizeof(buffer)))
|
||||||
|
{
|
||||||
|
if (strlen(buffer) > 0)
|
||||||
|
{
|
||||||
|
// 记录历史
|
||||||
|
record_history();
|
||||||
|
|
||||||
|
Ihandle *current_list = get_current_list();
|
||||||
|
int count = IupGetInt(current_list, "COUNT");
|
||||||
|
count++;
|
||||||
|
IupSetAttributeId(current_list, "", count, buffer);
|
||||||
|
IupSetInt(current_list, "COUNT", count);
|
||||||
|
|
||||||
|
// 更新选中状态
|
||||||
|
set_single_selection(current_list, count);
|
||||||
|
|
||||||
|
// 同时添加到 raw_data
|
||||||
|
int pos = IupGetInt(tabs_main, "VALUEPOS");
|
||||||
|
StringList *raw_data = (pos == 0) ? &raw_sys_paths : &raw_user_paths;
|
||||||
|
if (raw_data) {
|
||||||
|
add_string_list(raw_data, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh_single_list_style(current_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按钮回调:编辑
|
||||||
|
int btn_edit_cb(Ihandle *self)
|
||||||
|
{
|
||||||
|
Ihandle *current_list = get_current_list();
|
||||||
|
// 获取第一个选中的项
|
||||||
|
int selected = get_first_selected_index(current_list);
|
||||||
|
if (selected == 0)
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
|
||||||
|
char *current_val = IupGetAttributeId(current_list, "", selected);
|
||||||
|
char buffer[4096]; // 假设单个路径不超过4096
|
||||||
|
if (current_val)
|
||||||
|
{
|
||||||
|
strncpy(buffer, current_val, 4096);
|
||||||
|
buffer[4095] = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (custom_input_dialog("编辑环境变量", "编辑路径:", buffer, sizeof(buffer)))
|
||||||
|
{
|
||||||
|
if (strlen(buffer) > 0)
|
||||||
|
{
|
||||||
|
// 记录历史
|
||||||
|
record_history();
|
||||||
|
|
||||||
|
// 更新 UI
|
||||||
|
IupSetAttributeId(current_list, "", selected, buffer);
|
||||||
|
|
||||||
|
// 更新 raw_data
|
||||||
|
int pos = IupGetInt(tabs_main, "VALUEPOS");
|
||||||
|
StringList *raw_data = (pos == 0) ? &raw_sys_paths : &raw_user_paths;
|
||||||
|
|
||||||
|
char *filter = IupGetAttribute(txt_search, "VALUE");
|
||||||
|
if (!filter || strlen(filter) == 0) {
|
||||||
|
if (raw_data && selected <= raw_data->count) {
|
||||||
|
free(raw_data->items[selected-1]);
|
||||||
|
raw_data->items[selected-1] = _strdup(buffer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 搜索状态下,忽略同步问题,或者编辑后清除搜索。
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh_single_list_style(current_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 双击回调
|
||||||
|
int list_dblclick_cb(Ihandle *self, int item, char *text)
|
||||||
|
{
|
||||||
|
// 这里的 self 就是触发双击的列表控件
|
||||||
|
if (item > 0)
|
||||||
|
{
|
||||||
|
// 选中该行 (单选)
|
||||||
|
set_single_selection(self, item);
|
||||||
|
// 调用编辑逻辑
|
||||||
|
btn_edit_cb(NULL);
|
||||||
|
}
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按钮回调:删除 (支持多选)
|
||||||
|
int btn_del_cb(Ihandle *self)
|
||||||
|
{
|
||||||
|
Ihandle *current_list = get_current_list();
|
||||||
|
char *value = IupGetAttribute(current_list, "VALUE");
|
||||||
|
if (!value)
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
|
||||||
|
int len = strlen(value);
|
||||||
|
int has_selection = 0;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (value[i] == '+') {
|
||||||
|
has_selection = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_selection)
|
||||||
|
{
|
||||||
|
IupMessage("提示", "请先选择要删除的项");
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录历史
|
||||||
|
record_history();
|
||||||
|
|
||||||
|
// 获取 raw_data 缓存
|
||||||
|
int pos = IupGetInt(tabs_main, "VALUEPOS");
|
||||||
|
StringList *raw_data = (pos == 0) ? &raw_sys_paths : &raw_user_paths;
|
||||||
|
|
||||||
|
// 从后往前遍历删除,避免索引错位
|
||||||
|
for (int i = len - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (value[i] == '+')
|
||||||
|
{
|
||||||
|
int item_index = i + 1; // IUP 索引从 1 开始
|
||||||
|
char *val = IupGetAttributeId(current_list, "", item_index);
|
||||||
|
|
||||||
|
// 从缓存删除
|
||||||
|
if (val && raw_data)
|
||||||
|
{
|
||||||
|
char *val_copy = _strdup(val);
|
||||||
|
remove_from_raw_data(raw_data, val_copy);
|
||||||
|
free(val_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从界面删除
|
||||||
|
IupSetInt(current_list, "REMOVEITEM", item_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新刷新
|
||||||
|
refresh_single_list_style(current_list);
|
||||||
|
|
||||||
|
// 更新状态栏
|
||||||
|
IupSetAttribute(lbl_status, "TITLE", "状态: 已删除选中项");
|
||||||
|
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按钮回调:上移 (支持多选)
|
||||||
|
int btn_up_cb(Ihandle *self)
|
||||||
|
{
|
||||||
|
Ihandle *current_list = get_current_list();
|
||||||
|
char *value = IupGetAttribute(current_list, "VALUE");
|
||||||
|
if (!value)
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
|
||||||
|
int len = strlen(value);
|
||||||
|
char *new_value = _strdup(value);
|
||||||
|
int moved = 0;
|
||||||
|
|
||||||
|
// 预检查是否有移动
|
||||||
|
for (int i = 1; i < len; i++) {
|
||||||
|
if (new_value[i] == '+' && new_value[i - 1] == '-') {
|
||||||
|
moved = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moved) {
|
||||||
|
// 记录历史
|
||||||
|
record_history();
|
||||||
|
|
||||||
|
// 同步 raw_data (假设非搜索状态,raw_data 与 UI 一致)
|
||||||
|
int pos = IupGetInt(tabs_main, "VALUEPOS");
|
||||||
|
StringList *raw_data = (pos == 0) ? &raw_sys_paths : &raw_user_paths;
|
||||||
|
|
||||||
|
// 从前往后遍历,如果当前项被选中且前一项未选中,则交换
|
||||||
|
for (int i = 1; i < len; i++)
|
||||||
|
{
|
||||||
|
if (new_value[i] == '+' && new_value[i - 1] == '-')
|
||||||
|
{
|
||||||
|
// 交换列表项内容
|
||||||
|
char *curr_text = IupGetAttributeId(current_list, "", i + 1);
|
||||||
|
char *prev_text = IupGetAttributeId(current_list, "", i);
|
||||||
|
|
||||||
|
// 需要复制,防止指针失效
|
||||||
|
char *curr_copy = curr_text ? _strdup(curr_text) : NULL;
|
||||||
|
char *prev_copy = prev_text ? _strdup(prev_text) : NULL;
|
||||||
|
|
||||||
|
IupSetAttributeId(current_list, "", i, curr_copy);
|
||||||
|
IupSetAttributeId(current_list, "", i + 1, prev_copy);
|
||||||
|
|
||||||
|
if (curr_copy) free(curr_copy);
|
||||||
|
if (prev_copy) free(prev_copy);
|
||||||
|
|
||||||
|
// 交换 raw_data
|
||||||
|
if (raw_data && i < raw_data->count) {
|
||||||
|
char *temp = raw_data->items[i];
|
||||||
|
raw_data->items[i] = raw_data->items[i-1];
|
||||||
|
raw_data->items[i-1] = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 交换选中状态
|
||||||
|
new_value[i] = '-';
|
||||||
|
new_value[i - 1] = '+';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新选中状态
|
||||||
|
IupSetAttribute(current_list, "VALUE", new_value);
|
||||||
|
refresh_single_list_style(current_list);
|
||||||
|
}
|
||||||
|
free(new_value);
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按钮回调:下移 (支持多选)
|
||||||
|
int btn_down_cb(Ihandle *self)
|
||||||
|
{
|
||||||
|
Ihandle *current_list = get_current_list();
|
||||||
|
char *value = IupGetAttribute(current_list, "VALUE");
|
||||||
|
if (!value)
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
|
||||||
|
int len = strlen(value);
|
||||||
|
char *new_value = _strdup(value);
|
||||||
|
int moved = 0;
|
||||||
|
|
||||||
|
// 预检查
|
||||||
|
for (int i = len - 2; i >= 0; i--) {
|
||||||
|
if (new_value[i] == '+' && new_value[i + 1] == '-') {
|
||||||
|
moved = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moved) {
|
||||||
|
// 记录历史
|
||||||
|
record_history();
|
||||||
|
|
||||||
|
// 同步 raw_data
|
||||||
|
int pos = IupGetInt(tabs_main, "VALUEPOS");
|
||||||
|
StringList *raw_data = (pos == 0) ? &raw_sys_paths : &raw_user_paths;
|
||||||
|
|
||||||
|
// 从后往前遍历,如果当前项被选中且后一项未选中,则交换
|
||||||
|
for (int i = len - 2; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (new_value[i] == '+' && new_value[i + 1] == '-')
|
||||||
|
{
|
||||||
|
// 交换列表项内容
|
||||||
|
char *curr_text = IupGetAttributeId(current_list, "", i + 1);
|
||||||
|
char *next_text = IupGetAttributeId(current_list, "", i + 2);
|
||||||
|
|
||||||
|
// 需要复制
|
||||||
|
char *curr_copy = curr_text ? _strdup(curr_text) : NULL;
|
||||||
|
char *next_copy = next_text ? _strdup(next_text) : NULL;
|
||||||
|
|
||||||
|
IupSetAttributeId(current_list, "", i + 2, curr_copy);
|
||||||
|
IupSetAttributeId(current_list, "", i + 1, next_copy);
|
||||||
|
|
||||||
|
if (curr_copy) free(curr_copy);
|
||||||
|
if (next_copy) free(next_copy);
|
||||||
|
|
||||||
|
// 交换 raw_data
|
||||||
|
if (raw_data && i + 1 < raw_data->count) {
|
||||||
|
char *temp = raw_data->items[i];
|
||||||
|
raw_data->items[i] = raw_data->items[i+1];
|
||||||
|
raw_data->items[i+1] = temp;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 交换选中状态
|
||||||
|
new_value[i] = '-';
|
||||||
|
new_value[i + 1] = '+';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新选中状态
|
||||||
|
IupSetAttribute(current_list, "VALUE", new_value);
|
||||||
|
refresh_single_list_style(current_list);
|
||||||
|
}
|
||||||
|
free(new_value);
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按钮回调:一键清理
|
||||||
|
int btn_clean_cb(Ihandle *self)
|
||||||
|
{
|
||||||
|
Ihandle *current_list = get_current_list();
|
||||||
|
int count = IupGetInt(current_list, "COUNT");
|
||||||
|
if (count == 0)
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
|
||||||
|
// 弹出确认对话框
|
||||||
|
if (IupAlarm("确认清理", "此操作将移除当前列表中所有【无效路径】和【重复路径】。\n确定要继续吗?", "确定", "取消", NULL) != 1)
|
||||||
|
{
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录历史 (放在循环外,一次操作)
|
||||||
|
record_history();
|
||||||
|
|
||||||
|
// 获取 raw_data 用于同步删除
|
||||||
|
int pos = IupGetInt(tabs_main, "VALUEPOS");
|
||||||
|
StringList *raw_data = (pos == 0) ? &raw_sys_paths : &raw_user_paths;
|
||||||
|
|
||||||
|
// 从后往前遍历删除,避免索引错位
|
||||||
|
for (int i = count; i >= 1; i--)
|
||||||
|
{
|
||||||
|
char *item = IupGetAttributeId(current_list, "", i);
|
||||||
|
if (!item)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int should_remove = 0;
|
||||||
|
|
||||||
|
// 1. 检查有效性
|
||||||
|
if (!is_path_valid(item))
|
||||||
|
{
|
||||||
|
should_remove = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 2. 检查重复 (检查当前项之前是否出现过)
|
||||||
|
// 注意:这里需要再次遍历,性能稍低但最稳妥
|
||||||
|
for (int j = 1; j < i; j++)
|
||||||
|
{
|
||||||
|
char *prev_item = IupGetAttributeId(current_list, "", j);
|
||||||
|
if (prev_item && _stricmp(item, prev_item) == 0)
|
||||||
|
{
|
||||||
|
should_remove = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_remove)
|
||||||
|
{
|
||||||
|
// 从 raw_data 删除
|
||||||
|
if (raw_data) {
|
||||||
|
char *val_copy = _strdup(item);
|
||||||
|
remove_from_raw_data(raw_data, val_copy);
|
||||||
|
free(val_copy);
|
||||||
|
}
|
||||||
|
|
||||||
|
IupSetAttributeId(current_list, "REMOVEITEM", i, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh_single_list_style(current_list);
|
||||||
|
IupMessage("提示", "清理完成!");
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
+260
@@ -0,0 +1,260 @@
|
|||||||
|
#include "cb_file.h"
|
||||||
|
#include "ui_utils.h"
|
||||||
|
#include "globals.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <windows.h> // for GetFileAttributesA
|
||||||
|
|
||||||
|
// 按钮回调:浏览
|
||||||
|
int btn_browse_cb(Ihandle *self)
|
||||||
|
{
|
||||||
|
Ihandle *filedlg = IupFileDlg();
|
||||||
|
IupSetAttribute(filedlg, "DIALOGTYPE", "DIR");
|
||||||
|
IupSetAttribute(filedlg, "TITLE", "选择目录");
|
||||||
|
|
||||||
|
IupPopup(filedlg, IUP_CENTER, IUP_CENTER);
|
||||||
|
|
||||||
|
if (IupGetInt(filedlg, "STATUS") != -1)
|
||||||
|
{
|
||||||
|
char *value = IupGetAttribute(filedlg, "VALUE");
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
// 记录历史
|
||||||
|
record_history();
|
||||||
|
|
||||||
|
Ihandle *current_list = get_current_list();
|
||||||
|
int count = IupGetInt(current_list, "COUNT");
|
||||||
|
count++;
|
||||||
|
IupSetAttributeId(current_list, "", count, value);
|
||||||
|
IupSetInt(current_list, "COUNT", count);
|
||||||
|
|
||||||
|
// 更新选中状态
|
||||||
|
set_single_selection(current_list, count);
|
||||||
|
|
||||||
|
// 同步 raw_data
|
||||||
|
int pos = IupGetInt(tabs_main, "VALUEPOS");
|
||||||
|
StringList *raw_data = (pos == 0) ? &raw_sys_paths : &raw_user_paths;
|
||||||
|
if (raw_data) {
|
||||||
|
add_string_list(raw_data, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh_single_list_style(current_list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IupDestroy(filedlg);
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 撤销回调
|
||||||
|
int btn_undo_cb(Ihandle *self)
|
||||||
|
{
|
||||||
|
StringList sys = {0}, user = {0};
|
||||||
|
if (pop_history(&undo_stack, &sys, &user)) {
|
||||||
|
// Push current state to redo
|
||||||
|
push_history(&redo_stack, &raw_sys_paths, &raw_user_paths);
|
||||||
|
|
||||||
|
// Restore
|
||||||
|
clear_string_list(&raw_sys_paths);
|
||||||
|
clear_string_list(&raw_user_paths);
|
||||||
|
raw_sys_paths = sys;
|
||||||
|
raw_user_paths = user;
|
||||||
|
|
||||||
|
// Refresh UI
|
||||||
|
refresh_ui_from_raw(list_sys, &raw_sys_paths);
|
||||||
|
refresh_ui_from_raw(list_user, &raw_user_paths);
|
||||||
|
|
||||||
|
IupSetAttribute(lbl_status, "TITLE", "状态: 已撤销");
|
||||||
|
} else {
|
||||||
|
IupSetAttribute(lbl_status, "TITLE", "状态: 没有可撤销的操作");
|
||||||
|
}
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重做回调
|
||||||
|
int btn_redo_cb(Ihandle *self)
|
||||||
|
{
|
||||||
|
StringList sys = {0}, user = {0};
|
||||||
|
if (pop_history(&redo_stack, &sys, &user)) {
|
||||||
|
// Push current state to undo
|
||||||
|
push_history(&undo_stack, &raw_sys_paths, &raw_user_paths);
|
||||||
|
|
||||||
|
// Restore
|
||||||
|
clear_string_list(&raw_sys_paths);
|
||||||
|
clear_string_list(&raw_user_paths);
|
||||||
|
raw_sys_paths = sys;
|
||||||
|
raw_user_paths = user;
|
||||||
|
|
||||||
|
// Refresh UI
|
||||||
|
refresh_ui_from_raw(list_sys, &raw_sys_paths);
|
||||||
|
refresh_ui_from_raw(list_user, &raw_user_paths);
|
||||||
|
|
||||||
|
IupSetAttribute(lbl_status, "TITLE", "状态: 已重做");
|
||||||
|
} else {
|
||||||
|
IupSetAttribute(lbl_status, "TITLE", "状态: 没有可重做的操作");
|
||||||
|
}
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出配置
|
||||||
|
int btn_export_cb(Ihandle *self)
|
||||||
|
{
|
||||||
|
Ihandle *current_list = get_current_list();
|
||||||
|
int count = IupGetInt(current_list, "COUNT");
|
||||||
|
if (count == 0) {
|
||||||
|
IupMessage("提示", "当前列表为空,无法导出");
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ihandle *filedlg = IupFileDlg();
|
||||||
|
IupSetAttribute(filedlg, "DIALOGTYPE", "SAVE");
|
||||||
|
IupSetAttribute(filedlg, "TITLE", "导出配置");
|
||||||
|
IupSetAttribute(filedlg, "FILTER", "*.txt");
|
||||||
|
IupSetAttribute(filedlg, "FILTERINFO", "Text Files (*.txt)");
|
||||||
|
|
||||||
|
IupPopup(filedlg, IUP_CENTER, IUP_CENTER);
|
||||||
|
|
||||||
|
if (IupGetInt(filedlg, "STATUS") != -1) {
|
||||||
|
char *filename = IupGetAttribute(filedlg, "VALUE");
|
||||||
|
if (filename) {
|
||||||
|
char final_path[1024];
|
||||||
|
strncpy(final_path, filename, sizeof(final_path));
|
||||||
|
final_path[sizeof(final_path)-1] = '\0';
|
||||||
|
|
||||||
|
// 检查是否以 .txt 结尾 (不区分大小写)
|
||||||
|
size_t len = strlen(final_path);
|
||||||
|
if (len < 4 || _stricmp(final_path + len - 4, ".txt") != 0) {
|
||||||
|
if (len + 4 < sizeof(final_path)) {
|
||||||
|
strcat(final_path, ".txt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *fp = fopen(final_path, "w");
|
||||||
|
if (fp) {
|
||||||
|
for (int i = 1; i <= count; i++) {
|
||||||
|
char *item = IupGetAttributeId(current_list, "", i);
|
||||||
|
if (item) fprintf(fp, "%s\n", item);
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
IupMessage("提示", "导出成功!");
|
||||||
|
} else {
|
||||||
|
IupMessage("错误", "无法打开文件进行写入");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IupDestroy(filedlg);
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入配置
|
||||||
|
int btn_import_cb(Ihandle *self)
|
||||||
|
{
|
||||||
|
Ihandle *filedlg = IupFileDlg();
|
||||||
|
IupSetAttribute(filedlg, "DIALOGTYPE", "OPEN");
|
||||||
|
IupSetAttribute(filedlg, "TITLE", "导入配置");
|
||||||
|
IupSetAttribute(filedlg, "FILTER", "*.txt");
|
||||||
|
IupSetAttribute(filedlg, "FILTERINFO", "Text Files (*.txt)");
|
||||||
|
|
||||||
|
IupPopup(filedlg, IUP_CENTER, IUP_CENTER);
|
||||||
|
|
||||||
|
if (IupGetInt(filedlg, "STATUS") != -1) {
|
||||||
|
char *filename = IupGetAttribute(filedlg, "VALUE");
|
||||||
|
if (filename) {
|
||||||
|
FILE *fp = fopen(filename, "r");
|
||||||
|
if (fp) {
|
||||||
|
// Record history
|
||||||
|
record_history();
|
||||||
|
|
||||||
|
Ihandle *current_list = get_current_list();
|
||||||
|
int pos = IupGetInt(tabs_main, "VALUEPOS");
|
||||||
|
StringList *raw_data = (pos == 0) ? &raw_sys_paths : &raw_user_paths;
|
||||||
|
|
||||||
|
char line[4096];
|
||||||
|
int imported_count = 0;
|
||||||
|
while (fgets(line, sizeof(line), fp)) {
|
||||||
|
// Trim newline
|
||||||
|
size_t len = strlen(line);
|
||||||
|
while (len > 0 && (line[len-1] == '\n' || line[len-1] == '\r')) {
|
||||||
|
line[len-1] = '\0';
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
if (len > 0) {
|
||||||
|
// Add to UI
|
||||||
|
int count = IupGetInt(current_list, "COUNT");
|
||||||
|
count++;
|
||||||
|
IupSetAttributeId(current_list, "", count, line);
|
||||||
|
IupSetInt(current_list, "COUNT", count);
|
||||||
|
|
||||||
|
// Add to raw_data
|
||||||
|
if (raw_data) add_string_list(raw_data, line);
|
||||||
|
|
||||||
|
imported_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
refresh_single_list_style(current_list);
|
||||||
|
|
||||||
|
char msg[64];
|
||||||
|
snprintf(msg, sizeof(msg), "导入成功!共导入 %d 条路径。", imported_count);
|
||||||
|
IupMessage("提示", msg);
|
||||||
|
} else {
|
||||||
|
IupMessage("错误", "无法打开文件进行读取");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
IupDestroy(filedlg);
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 拖拽回调
|
||||||
|
int list_dropfiles_cb(Ihandle *self, const char *filename, int num, int x, int y)
|
||||||
|
{
|
||||||
|
// 获取当前列表和原始数据
|
||||||
|
// 注意:拖拽的目标列表可能是 list_sys 或 list_user,由 self 参数决定
|
||||||
|
// 但为了确保数据一致性,我们还是重新获取一下
|
||||||
|
Ihandle *current_list = self;
|
||||||
|
StringList *raw_data = NULL;
|
||||||
|
if (self == list_sys)
|
||||||
|
raw_data = &raw_sys_paths;
|
||||||
|
else if (self == list_user)
|
||||||
|
raw_data = &raw_user_paths;
|
||||||
|
else
|
||||||
|
return IUP_DEFAULT; // 异常情况
|
||||||
|
|
||||||
|
// 检查拖入的是否为目录
|
||||||
|
DWORD attr = GetFileAttributesA(filename);
|
||||||
|
if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
|
||||||
|
{
|
||||||
|
// 记录历史
|
||||||
|
record_history();
|
||||||
|
|
||||||
|
// 如果正在搜索,先清空搜索框
|
||||||
|
IupSetAttribute(txt_search, "VALUE", "");
|
||||||
|
|
||||||
|
// 添加到列表末尾
|
||||||
|
int count = IupGetInt(current_list, "COUNT");
|
||||||
|
count++;
|
||||||
|
IupSetAttributeId(current_list, "", count, filename);
|
||||||
|
IupSetInt(current_list, "COUNT", count);
|
||||||
|
|
||||||
|
// 更新选中状态
|
||||||
|
set_single_selection(current_list, count);
|
||||||
|
|
||||||
|
// 同时添加到原始数据缓存,确保搜索时能搜到
|
||||||
|
if (raw_data)
|
||||||
|
{
|
||||||
|
add_string_list(raw_data, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh_single_list_style(current_list);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 如果拖入的不是文件夹,可以在状态栏提示
|
||||||
|
IupSetAttribute(lbl_status, "TITLE", "提示: 只能拖拽文件夹添加到 PATH");
|
||||||
|
}
|
||||||
|
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
+214
@@ -0,0 +1,214 @@
|
|||||||
|
#include "cb_main.h"
|
||||||
|
#include "ui_utils.h"
|
||||||
|
#include "globals.h"
|
||||||
|
#include "registry.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include "cb_edit.h"
|
||||||
|
#include "cb_file.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// 搜索回调
|
||||||
|
int txt_search_cb(Ihandle *self)
|
||||||
|
{
|
||||||
|
char *filter = IupGetAttribute(self, "VALUE");
|
||||||
|
if (!filter)
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
|
||||||
|
// 获取当前选中的 Tab 索引
|
||||||
|
int pos = IupGetInt(tabs_main, "VALUEPOS");
|
||||||
|
Ihandle *current_list = (pos == 0) ? list_sys : list_user;
|
||||||
|
StringList *raw_data = (pos == 0) ? &raw_sys_paths : &raw_user_paths;
|
||||||
|
|
||||||
|
// 清空列表
|
||||||
|
IupSetAttribute(current_list, "REMOVEITEM", "ALL");
|
||||||
|
|
||||||
|
// 重新填充
|
||||||
|
int count = 0;
|
||||||
|
for (int i = 0; i < raw_data->count; i++)
|
||||||
|
{
|
||||||
|
// 如果 filter 为空,或包含 filter (不区分大小写)
|
||||||
|
if (strlen(filter) == 0 || stristr(raw_data->items[i], filter) != NULL)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
IupSetAttributeId(current_list, "", count, raw_data->items[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IupSetInt(current_list, "COUNT", count);
|
||||||
|
refresh_single_list_style(current_list);
|
||||||
|
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 键盘按键回调
|
||||||
|
int list_k_any_cb(Ihandle *self, int c)
|
||||||
|
{
|
||||||
|
// 处理 Delete 键
|
||||||
|
if (c == K_DEL)
|
||||||
|
{
|
||||||
|
btn_del_cb(NULL);
|
||||||
|
return IUP_IGNORE; // 阻止默认处理
|
||||||
|
}
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 鼠标移动回调
|
||||||
|
int list_motion_cb(Ihandle *self, int x, int y, char *status)
|
||||||
|
{
|
||||||
|
int pos = IupConvertXYToPos(self, x, y);
|
||||||
|
if (pos > 0)
|
||||||
|
{
|
||||||
|
char *item = IupGetAttributeId(self, "", pos);
|
||||||
|
if (item)
|
||||||
|
{
|
||||||
|
char *expanded = expand_env_vars(item);
|
||||||
|
if (expanded)
|
||||||
|
{
|
||||||
|
IupSetAttribute(self, "TIP", expanded);
|
||||||
|
free(expanded);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IupSetAttribute(self, "TIP", item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IupSetAttribute(self, "TIP", NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IupSetAttribute(self, "TIP", NULL);
|
||||||
|
}
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对话框全局按键回调
|
||||||
|
int dialog_k_any_cb(Ihandle *self, int c)
|
||||||
|
{
|
||||||
|
switch (c)
|
||||||
|
{
|
||||||
|
case K_cN: // Ctrl+N 新建
|
||||||
|
btn_new_cb(NULL);
|
||||||
|
return IUP_IGNORE;
|
||||||
|
case K_cS: // Ctrl+S 保存
|
||||||
|
btn_ok_cb(NULL);
|
||||||
|
return IUP_IGNORE;
|
||||||
|
case K_cF: // Ctrl+F 搜索
|
||||||
|
if (txt_search)
|
||||||
|
{
|
||||||
|
IupSetFocus(txt_search);
|
||||||
|
}
|
||||||
|
return IUP_IGNORE;
|
||||||
|
case K_cZ: // Ctrl+Z 撤销
|
||||||
|
btn_undo_cb(NULL);
|
||||||
|
return IUP_IGNORE;
|
||||||
|
case K_cY: // Ctrl+Y 重做
|
||||||
|
btn_redo_cb(NULL);
|
||||||
|
return IUP_IGNORE;
|
||||||
|
}
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按钮回调:确定
|
||||||
|
int btn_ok_cb(Ihandle *self)
|
||||||
|
{
|
||||||
|
save_all_paths();
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按钮回调:取消
|
||||||
|
int btn_cancel_cb(Ihandle *self)
|
||||||
|
{
|
||||||
|
IupExitLoop();
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 按钮回调:帮助
|
||||||
|
int btn_help_cb(Ihandle *self)
|
||||||
|
{
|
||||||
|
IupMessage("使用说明",
|
||||||
|
"1. 本程序用于编辑系统环境变量 PATH。\n"
|
||||||
|
"2. 必须以【管理员身份】运行才能保存更改。\n"
|
||||||
|
"3. 操作说明:\n"
|
||||||
|
" - 新建:添加新路径到列表末尾。\n"
|
||||||
|
" - 编辑:修改选中的路径。\n"
|
||||||
|
" - 浏览:从文件系统选择目录添加。\n"
|
||||||
|
" - 删除:移除选中的路径。\n"
|
||||||
|
" - 上移/下移:调整路径优先级。\n"
|
||||||
|
" - 导入/导出:备份和恢复配置。\n"
|
||||||
|
" - 快捷键:\n"
|
||||||
|
" Ctrl+N: 新建路径\n"
|
||||||
|
" Ctrl+S: 保存更改\n"
|
||||||
|
" Ctrl+F: 聚焦搜索框\n"
|
||||||
|
" Ctrl+Z: 撤销\n"
|
||||||
|
" Ctrl+Y: 重做\n"
|
||||||
|
"4. 点击【确定】保存更改并生效。\n"
|
||||||
|
"5. 注意:某些正在运行的程序可能需要重启才能识别新的环境变量。\n\n"
|
||||||
|
"--------------------------------------------------\n"
|
||||||
|
"作者:LHY\n"
|
||||||
|
"邮箱:3364451258@qq.com\n"
|
||||||
|
"GitHub:https://github.com/LHY0125/PathEditor\n"
|
||||||
|
"记得给我的项目点个star!");
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标签页切换回调
|
||||||
|
int tabs_tabchange_cb(Ihandle *self, int new_pos, int old_pos)
|
||||||
|
{
|
||||||
|
if (new_pos == 2)
|
||||||
|
{
|
||||||
|
// 合并预览模式
|
||||||
|
IupSetAttribute(list_merged, "REMOVEITEM", "ALL");
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
// 添加系统变量
|
||||||
|
for (int i = 0; i < raw_sys_paths.count; i++)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
IupSetAttributeId(list_merged, "", count, raw_sys_paths.items[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加用户变量
|
||||||
|
for (int i = 0; i < raw_user_paths.count; i++)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
IupSetAttributeId(list_merged, "", count, raw_user_paths.items[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
IupSetInt(list_merged, "COUNT", count);
|
||||||
|
refresh_single_list_style(list_merged);
|
||||||
|
|
||||||
|
// 禁用编辑按钮
|
||||||
|
toggle_edit_buttons(0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 编辑模式 (检查管理员权限)
|
||||||
|
if (check_admin())
|
||||||
|
{
|
||||||
|
toggle_edit_buttons(1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
toggle_edit_buttons(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主题切换回调
|
||||||
|
int btn_theme_cb(Ihandle *self)
|
||||||
|
{
|
||||||
|
is_dark_mode = !is_dark_mode;
|
||||||
|
if (is_dark_mode)
|
||||||
|
IupSetAttribute(btn_theme, "TITLE", "浅色模式");
|
||||||
|
else
|
||||||
|
IupSetAttribute(btn_theme, "TITLE", "深色模式");
|
||||||
|
|
||||||
|
apply_theme();
|
||||||
|
return IUP_DEFAULT;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
#include "globals.h"
|
||||||
|
|
||||||
|
// 全局控件定义
|
||||||
|
Ihandle *dlg = NULL; // 主对话框
|
||||||
|
Ihandle *tabs_main = NULL; // 主选项卡
|
||||||
|
Ihandle *list_sys = NULL, *list_user = NULL, *list_merged = NULL; // 列表控件
|
||||||
|
Ihandle *lbl_status = NULL; // 状态栏
|
||||||
|
Ihandle *btn_new = NULL, *btn_edit = NULL, *btn_browse = NULL, *btn_del = NULL, *btn_up = NULL, *btn_down = NULL; // 右侧按钮
|
||||||
|
Ihandle *btn_undo = NULL, *btn_redo = NULL; // 撤销重做按钮
|
||||||
|
Ihandle *btn_import = NULL, *btn_export = NULL; // 导入导出按钮
|
||||||
|
Ihandle *btn_ok = NULL, *btn_cancel = NULL, *btn_help = NULL; // 确认取消帮助按钮
|
||||||
|
Ihandle *btn_clean = NULL; // 一键清理按钮
|
||||||
|
Ihandle *btn_theme = NULL; // 主题切换按钮
|
||||||
|
Ihandle *txt_search = NULL; // 搜索框
|
||||||
|
|
||||||
|
// 历史记录栈
|
||||||
|
HistoryStack undo_stack = {0};
|
||||||
|
HistoryStack redo_stack = {0};
|
||||||
|
|
||||||
|
// 全局变量定义
|
||||||
|
StringList raw_sys_paths = {0};
|
||||||
|
StringList raw_user_paths = {0};
|
||||||
|
int is_dark_mode = 0; // 默认浅色模式
|
||||||
+42
-101
@@ -1,130 +1,71 @@
|
|||||||
#include <windows.h>
|
|
||||||
#include <iup.h>
|
#include <iup.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <wchar.h>
|
|
||||||
#include "globals.h"
|
#include "globals.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "registry.h"
|
#include "registry.h"
|
||||||
#include "callbacks.h"
|
#include "ui.h"
|
||||||
|
#include "cb_main.h"
|
||||||
|
|
||||||
// 全局控件定义
|
/*
|
||||||
Ihandle *dlg, *list_path, *lbl_status;
|
编译命令:
|
||||||
Ihandle *btn_new, *btn_edit, *btn_browse, *btn_del, *btn_up, *btn_down;
|
Remove-Item -Path "obj\*.o" -Force -ErrorAction SilentlyContinue && mingw32-make
|
||||||
Ihandle *btn_ok, *btn_cancel, *btn_help;
|
打包命令:
|
||||||
|
build_installer.bat
|
||||||
|
*/
|
||||||
|
|
||||||
// 主函数
|
// 主函数
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
// 强制设置 UTF8MODE 环境变量,必须在 IupOpen 之前
|
// 初始化 IUP
|
||||||
putenv("IUP_UTF8MODE=YES");
|
if (IupOpen(&argc, &argv) == IUP_ERROR)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
IupOpen(&argc, &argv);
|
// 开启 UTF-8 支持
|
||||||
IupSetGlobal("UTF8MODE", "YES");
|
IupSetGlobal("UTF8MODE", "YES");
|
||||||
|
|
||||||
// 创建列表控件
|
// 启用 UIPI 绕过,允许拖拽
|
||||||
list_path = IupFlatList();
|
HMODULE hUser32 = LoadLibraryA("user32.dll");
|
||||||
IupSetAttribute(list_path, "EXPAND", "YES");
|
if (hUser32)
|
||||||
IupSetAttribute(list_path, "ITEMPADDING", "5x5");
|
{
|
||||||
IupSetAttribute(list_path, "BACKCOLOR", "255 255 255");
|
typedef BOOL(WINAPI * ChangeWindowMessageFilterProc)(UINT, DWORD);
|
||||||
IupSetAttribute(list_path, "BORDER", "YES");
|
ChangeWindowMessageFilterProc pChangeWindowMessageFilter = (ChangeWindowMessageFilterProc)GetProcAddress(hUser32, "ChangeWindowMessageFilter");
|
||||||
IupSetAttribute(list_path, "CANFOCUS", "YES");
|
if (pChangeWindowMessageFilter)
|
||||||
IupSetAttribute(list_path, "HLINE", "NO"); // 禁用横线,使用斑马纹
|
{
|
||||||
// IupFlatList 不支持 VISIBLELINES,高度由 EXPAND 和布局决定
|
// WM_DROPFILES = 0x0233, WM_COPYDATA = 0x004A, MSGFLT_ADD = 1
|
||||||
|
pChangeWindowMessageFilter(0x0233, 1);
|
||||||
|
pChangeWindowMessageFilter(0x004A, 1);
|
||||||
|
}
|
||||||
|
FreeLibrary(hUser32);
|
||||||
|
}
|
||||||
|
|
||||||
// 创建右侧按钮
|
// 初始化历史栈
|
||||||
btn_new = IupButton("新建(N)", NULL);
|
init_history_stack(&undo_stack);
|
||||||
btn_edit = IupButton("编辑(E)", NULL);
|
init_history_stack(&redo_stack);
|
||||||
btn_browse = IupButton("浏览(B)...", NULL);
|
|
||||||
btn_del = IupButton("删除(D)", NULL);
|
|
||||||
btn_up = IupButton("上移(U)", NULL);
|
|
||||||
btn_down = IupButton("下移(O)", NULL);
|
|
||||||
|
|
||||||
// 设置按钮回调
|
// 创建主界面
|
||||||
IupSetCallback(btn_new, "ACTION", (Icallback)btn_new_cb);
|
dlg = create_main_dialog();
|
||||||
IupSetCallback(btn_edit, "ACTION", (Icallback)btn_edit_cb);
|
|
||||||
IupSetCallback(btn_browse, "ACTION", (Icallback)btn_browse_cb);
|
|
||||||
IupSetCallback(btn_del, "ACTION", (Icallback)btn_del_cb);
|
|
||||||
IupSetCallback(btn_up, "ACTION", (Icallback)btn_up_cb);
|
|
||||||
IupSetCallback(btn_down, "ACTION", (Icallback)btn_down_cb);
|
|
||||||
|
|
||||||
// 设置双击回调
|
// 设置全局按键回调 (如果在 ui.c 中未设置)
|
||||||
IupSetCallback(list_path, "DBLCLICK_CB", (Icallback)list_dblclick_cb);
|
IupSetCallback(dlg, "K_ANY", (Icallback)dialog_k_any_cb);
|
||||||
|
|
||||||
// 设置按钮大小 (宽度和高度都增加约1/4)
|
|
||||||
IupSetAttribute(btn_new, "RASTERSIZE", "100x32");
|
|
||||||
IupSetAttribute(btn_edit, "RASTERSIZE", "100x32");
|
|
||||||
IupSetAttribute(btn_browse, "RASTERSIZE", "100x32");
|
|
||||||
IupSetAttribute(btn_del, "RASTERSIZE", "100x32");
|
|
||||||
IupSetAttribute(btn_up, "RASTERSIZE", "100x32");
|
|
||||||
IupSetAttribute(btn_down, "RASTERSIZE", "100x32");
|
|
||||||
|
|
||||||
Ihandle *vbox_btns = IupVbox(
|
|
||||||
btn_new, btn_edit, btn_browse, btn_del,
|
|
||||||
IupFill(), // 间隔
|
|
||||||
btn_up, btn_down,
|
|
||||||
NULL);
|
|
||||||
IupSetAttribute(vbox_btns, "GAP", "5");
|
|
||||||
IupSetAttribute(vbox_btns, "MARGIN", "0x0");
|
|
||||||
|
|
||||||
// 上部布局:列表 + 按钮
|
|
||||||
Ihandle *hbox_main = IupHbox(list_path, vbox_btns, NULL);
|
|
||||||
IupSetAttribute(hbox_main, "GAP", "10");
|
|
||||||
IupSetAttribute(hbox_main, "MARGIN", "10x10");
|
|
||||||
|
|
||||||
// 状态标签
|
|
||||||
lbl_status = IupLabel("状态: 就绪");
|
|
||||||
IupSetAttribute(lbl_status, "EXPAND", "HORIZONTAL");
|
|
||||||
|
|
||||||
// 底部按钮
|
|
||||||
btn_ok = IupButton("确定", NULL);
|
|
||||||
btn_cancel = IupButton("取消", NULL);
|
|
||||||
btn_help = IupButton("帮助(?)", NULL);
|
|
||||||
|
|
||||||
IupSetCallback(btn_ok, "ACTION", (Icallback)btn_ok_cb);
|
|
||||||
IupSetCallback(btn_cancel, "ACTION", (Icallback)btn_cancel_cb);
|
|
||||||
IupSetCallback(btn_help, "ACTION", (Icallback)btn_help_cb);
|
|
||||||
|
|
||||||
IupSetAttribute(btn_ok, "RASTERSIZE", "100x32");
|
|
||||||
IupSetAttribute(btn_cancel, "RASTERSIZE", "100x32");
|
|
||||||
IupSetAttribute(btn_help, "RASTERSIZE", "100x32");
|
|
||||||
|
|
||||||
Ihandle *hbox_bottom = IupHbox(lbl_status, IupFill(), btn_help, btn_ok, btn_cancel, NULL);
|
|
||||||
IupSetAttribute(hbox_bottom, "GAP", "10");
|
|
||||||
IupSetAttribute(hbox_bottom, "MARGIN", "10x10");
|
|
||||||
IupSetAttribute(hbox_bottom, "ALIGNMENT", "ACENTER");
|
|
||||||
|
|
||||||
// 总体布局
|
|
||||||
Ihandle *vbox_all = IupVbox(
|
|
||||||
IupLabel("系统变量 Path:"),
|
|
||||||
hbox_main,
|
|
||||||
hbox_bottom,
|
|
||||||
NULL);
|
|
||||||
IupSetAttribute(vbox_all, "MARGIN", "10x10");
|
|
||||||
IupSetAttribute(vbox_all, "GAP", "5");
|
|
||||||
|
|
||||||
// 创建对话框
|
|
||||||
dlg = IupDialog(vbox_all);
|
|
||||||
IupSetAttribute(dlg, "TITLE", "编辑环境变量 (IUP版)");
|
|
||||||
IupSetAttribute(dlg, "SIZE", "450x350");
|
|
||||||
IupSetAttribute(dlg, "MINBOX", "NO");
|
|
||||||
IupSetAttribute(dlg, "MAXBOX", "NO");
|
|
||||||
|
|
||||||
// 加载数据
|
// 加载数据
|
||||||
if (!check_admin())
|
if (!check_admin())
|
||||||
{
|
{
|
||||||
IupMessage("警告", "程序未以管理员身份运行,您只能查看,无法保存更改!");
|
IupMessage("警告", "未检测到管理员权限!\n您可能无法保存更改。\n请右键以【管理员身份运行】。");
|
||||||
IupSetAttribute(dlg, "TITLE", "编辑环境变量 (只读模式)");
|
|
||||||
IupSetAttribute(lbl_status, "TITLE", "状态: 只读模式 (权限不足)");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
load_all_paths();
|
||||||
|
|
||||||
|
// 显示对话框
|
||||||
IupShowXY(dlg, IUP_CENTER, IUP_CENTER);
|
IupShowXY(dlg, IUP_CENTER, IUP_CENTER);
|
||||||
|
|
||||||
// IUP List APPEND 属性需要在控件 Map 之后才能生效
|
// 进入主循环
|
||||||
// IupShowXY 会触发 Map
|
|
||||||
load_path();
|
|
||||||
|
|
||||||
IupMainLoop();
|
IupMainLoop();
|
||||||
|
|
||||||
|
// 清理资源
|
||||||
IupClose();
|
IupClose();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
+80
-98
@@ -6,17 +6,23 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
|
|
||||||
// 从注册表加载PATH
|
// 内部辅助函数:加载单个列表
|
||||||
void load_path()
|
static void load_single_path(HKEY hKeyRoot, const wchar_t *regPath, Ihandle *list, StringList *cache)
|
||||||
{
|
{
|
||||||
|
// 清空旧缓存
|
||||||
|
clear_string_list(cache);
|
||||||
|
|
||||||
HKEY hKey;
|
HKEY hKey;
|
||||||
LONG res = RegOpenKeyExW(HKEY_LOCAL_MACHINE, REG_PATH, 0, KEY_READ, &hKey);
|
LONG res = RegOpenKeyExW(hKeyRoot, regPath, 0, KEY_READ, &hKey);
|
||||||
if (res != ERROR_SUCCESS)
|
if (res != ERROR_SUCCESS)
|
||||||
|
{
|
||||||
|
// 只有 HKLM 失败才提示需要管理员,HKCU 失败可能是其他原因
|
||||||
|
if (hKeyRoot == HKEY_LOCAL_MACHINE)
|
||||||
{
|
{
|
||||||
char msg[512];
|
char msg[512];
|
||||||
snprintf(msg, sizeof(msg), "无法打开注册表键 (HKLM)。\n路径: %ls\n错误码: %ld\n\n请尝试右键点击程序 -> '以管理员身份运行'。", REG_PATH, res);
|
snprintf(msg, sizeof(msg), "无法打开注册表键 (HKLM)。\n路径: %ls\n错误码: %ld\n\n请尝试右键点击程序 -> '以管理员身份运行'。", regPath, res);
|
||||||
IupMessage("错误", msg);
|
IupMessage("错误", msg);
|
||||||
IupSetAttribute(lbl_status, "TITLE", "状态: 注册表读取失败");
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,172 +30,148 @@ void load_path()
|
|||||||
res = RegQueryValueExW(hKey, REG_VALUE, NULL, &type, NULL, &size);
|
res = RegQueryValueExW(hKey, REG_VALUE, NULL, &type, NULL, &size);
|
||||||
if (res == ERROR_SUCCESS)
|
if (res == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
// 安全分配内存:size 是字节数,多分配 2 个字节给 null 终止符
|
|
||||||
wchar_t *buffer = (wchar_t *)malloc(size + 2);
|
wchar_t *buffer = (wchar_t *)malloc(size + 2);
|
||||||
if (!buffer)
|
if (buffer)
|
||||||
{
|
{
|
||||||
IupMessage("错误", "内存分配失败!");
|
|
||||||
RegCloseKey(hKey);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 初始化内存
|
|
||||||
memset(buffer, 0, size + 2);
|
memset(buffer, 0, size + 2);
|
||||||
|
|
||||||
if (RegQueryValueExW(hKey, REG_VALUE, NULL, &type, (LPBYTE)buffer, &size) == ERROR_SUCCESS)
|
if (RegQueryValueExW(hKey, REG_VALUE, NULL, &type, (LPBYTE)buffer, &size) == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
// 重新实现分割逻辑,避免 wcstok 的兼容性问题
|
|
||||||
wchar_t *current = buffer;
|
wchar_t *current = buffer;
|
||||||
wchar_t *next_semicolon = NULL;
|
wchar_t *next_semicolon = NULL;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
|
|
||||||
// 清空列表
|
IupSetAttribute(list, "REMOVEITEM", "ALL");
|
||||||
IupSetAttribute(list_path, "REMOVEITEM", "ALL");
|
|
||||||
|
|
||||||
// 检查内容是否为空
|
|
||||||
if (wcslen(buffer) == 0)
|
|
||||||
{
|
|
||||||
IupMessage("提示", "读取到的 PATH 变量为空!");
|
|
||||||
}
|
|
||||||
|
|
||||||
while (*current)
|
while (*current)
|
||||||
{
|
{
|
||||||
// 查找下一个分号
|
|
||||||
next_semicolon = wcschr(current, L';');
|
next_semicolon = wcschr(current, L';');
|
||||||
if (next_semicolon)
|
if (next_semicolon)
|
||||||
{
|
*next_semicolon = L'\0';
|
||||||
*next_semicolon = L'\0'; // 临时截断
|
|
||||||
}
|
|
||||||
|
|
||||||
// 跳过空项
|
|
||||||
if (wcslen(current) > 0)
|
if (wcslen(current) > 0)
|
||||||
{
|
{
|
||||||
char *utf8_str = wide_to_utf8(current);
|
char *utf8_str = wide_to_utf8(current);
|
||||||
|
|
||||||
|
// 添加到列表
|
||||||
count++;
|
count++;
|
||||||
IupSetAttributeId(list_path, "", count, utf8_str);
|
IupSetAttributeId(list, "", count, utf8_str);
|
||||||
|
|
||||||
|
// 添加到缓存
|
||||||
|
add_string_list(cache, utf8_str);
|
||||||
|
|
||||||
free(utf8_str);
|
free(utf8_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (next_semicolon)
|
if (next_semicolon)
|
||||||
{
|
|
||||||
current = next_semicolon + 1;
|
current = next_semicolon + 1;
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
IupSetInt(list_path, "COUNT", count); // 显式设置列表项数量
|
IupSetInt(list, "COUNT", count);
|
||||||
IupSetInt(list_path, "VALUE", 1); // 选中第一项
|
IupSetInt(list, "VALUE", 1);
|
||||||
|
|
||||||
// 刷新斑马纹样式
|
|
||||||
refresh_list_style();
|
|
||||||
|
|
||||||
char status_msg[100];
|
|
||||||
sprintf(status_msg, "状态: 已加载 %d 个条目", count);
|
|
||||||
IupSetAttribute(lbl_status, "TITLE", status_msg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IupMessage("错误", "读取 PATH 值内容失败!");
|
|
||||||
IupSetAttribute(lbl_status, "TITLE", "状态: 读取值内容失败");
|
|
||||||
}
|
}
|
||||||
free(buffer);
|
free(buffer);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
char msg[256];
|
|
||||||
sprintf(msg, "查询 PATH 值大小失败。错误码: %ld", res);
|
|
||||||
IupMessage("错误", msg);
|
|
||||||
IupSetAttribute(lbl_status, "TITLE", "状态: 查询值失败");
|
|
||||||
}
|
}
|
||||||
RegCloseKey(hKey);
|
RegCloseKey(hKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存PATH到注册表
|
// 加载所有PATH
|
||||||
void save_path()
|
void load_all_paths()
|
||||||
{
|
{
|
||||||
if (!check_admin())
|
load_single_path(HKEY_LOCAL_MACHINE, REG_PATH_SYS, list_sys, &raw_sys_paths);
|
||||||
{
|
load_single_path(HKEY_CURRENT_USER, REG_PATH_USER, list_user, &raw_user_paths);
|
||||||
IupMessage("错误", "需要管理员权限才能保存更改!\n请重新以管理员身份运行程序。");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int count = IupGetInt(list_path, "COUNT");
|
refresh_list_style();
|
||||||
if (count == 0)
|
IupSetAttribute(lbl_status, "TITLE", "状态: 已加载变量");
|
||||||
{
|
}
|
||||||
// 警告:清空PATH是很危险的
|
|
||||||
if (IupAlarm("警告", "PATH 为空!这可能导致系统命令无法使用。\n确定要保存吗?", "确定", "取消", NULL) != 1)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算所需缓冲区大小
|
// 内部辅助函数:保存单个列表
|
||||||
|
static int save_single_path(HKEY hKeyRoot, const wchar_t *regPath, Ihandle *list)
|
||||||
|
{
|
||||||
|
int count = IupGetInt(list, "COUNT");
|
||||||
|
|
||||||
|
// 计算大小
|
||||||
size_t total_len = 0;
|
size_t total_len = 0;
|
||||||
for (int i = 1; i <= count; i++)
|
for (int i = 1; i <= count; i++)
|
||||||
{
|
{
|
||||||
char *item = IupGetAttributeId(list_path, "", i);
|
char *item = IupGetAttributeId(list, "", i);
|
||||||
if (item)
|
if (item)
|
||||||
{
|
{
|
||||||
wchar_t *witem = utf8_to_wide(item);
|
wchar_t *witem = utf8_to_wide(item);
|
||||||
total_len += wcslen(witem) + 1; // +1 for ';'
|
total_len += wcslen(witem) + 1;
|
||||||
free(witem);
|
free(witem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
total_len += 1; // null terminator
|
total_len += 1;
|
||||||
|
|
||||||
wchar_t *buffer = (wchar_t *)malloc(total_len * sizeof(wchar_t));
|
wchar_t *buffer = (wchar_t *)malloc(total_len * sizeof(wchar_t));
|
||||||
if (!buffer)
|
if (!buffer)
|
||||||
{
|
return 0;
|
||||||
IupMessage("错误", "内存分配失败 (保存时)!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
buffer[0] = L'\0';
|
|
||||||
|
|
||||||
|
buffer[0] = L'\0';
|
||||||
for (int i = 1; i <= count; i++)
|
for (int i = 1; i <= count; i++)
|
||||||
{
|
{
|
||||||
char *item = IupGetAttributeId(list_path, "", i);
|
char *item = IupGetAttributeId(list, "", i);
|
||||||
if (item)
|
if (item)
|
||||||
{
|
{
|
||||||
wchar_t *witem = utf8_to_wide(item);
|
wchar_t *witem = utf8_to_wide(item);
|
||||||
wcscat(buffer, witem);
|
wcscat(buffer, witem);
|
||||||
if (i < count)
|
if (i < count)
|
||||||
{
|
|
||||||
wcscat(buffer, L";");
|
wcscat(buffer, L";");
|
||||||
}
|
|
||||||
free(witem);
|
free(witem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 写入注册表
|
|
||||||
HKEY hKey;
|
HKEY hKey;
|
||||||
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, REG_PATH, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS)
|
int success = 0;
|
||||||
|
if (RegOpenKeyExW(hKeyRoot, regPath, 0, KEY_WRITE, &hKey) == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
// 使用 REG_EXPAND_SZ 类型,因为 PATH 可能包含 %SystemRoot%
|
|
||||||
DWORD size = (wcslen(buffer) + 1) * sizeof(wchar_t);
|
DWORD size = (wcslen(buffer) + 1) * sizeof(wchar_t);
|
||||||
if (RegSetValueExW(hKey, REG_VALUE, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size) == ERROR_SUCCESS)
|
if (RegSetValueExW(hKey, REG_VALUE, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size) == ERROR_SUCCESS)
|
||||||
{
|
{
|
||||||
// 发送系统广播通知环境变量已更改
|
success = 1;
|
||||||
SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)L"Environment", SMTO_ABORTIFHUNG, 5000, NULL);
|
|
||||||
IupMessage("成功", "PATH 环境变量已更新!");
|
|
||||||
IupSetAttribute(lbl_status, "TITLE", "状态: 保存成功");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
IupMessage("错误", "写入注册表失败!");
|
|
||||||
IupSetAttribute(lbl_status, "TITLE", "状态: 保存失败");
|
|
||||||
}
|
}
|
||||||
RegCloseKey(hKey);
|
RegCloseKey(hKey);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
IupMessage("错误", "无法打开注册表进行写入。请检查权限!");
|
|
||||||
IupSetAttribute(lbl_status, "TITLE", "状态: 打开注册表失败");
|
|
||||||
}
|
|
||||||
|
|
||||||
free(buffer);
|
free(buffer);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存所有PATH
|
||||||
|
void save_all_paths()
|
||||||
|
{
|
||||||
|
if (!check_admin())
|
||||||
|
{
|
||||||
|
IupMessage("错误", "需要管理员权限才能保存更改!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 备份
|
||||||
|
backup_registry();
|
||||||
|
|
||||||
|
int sys_ok = save_single_path(HKEY_LOCAL_MACHINE, REG_PATH_SYS, list_sys);
|
||||||
|
int user_ok = save_single_path(HKEY_CURRENT_USER, REG_PATH_USER, list_user);
|
||||||
|
|
||||||
|
if (sys_ok && user_ok)
|
||||||
|
{
|
||||||
|
SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)L"Environment", SMTO_ABORTIFHUNG, 5000, NULL);
|
||||||
|
IupMessage("成功", "系统和用户 PATH 环境变量均已更新!");
|
||||||
|
IupSetAttribute(lbl_status, "TITLE", "状态: 全部保存成功");
|
||||||
|
}
|
||||||
|
else if (sys_ok)
|
||||||
|
{
|
||||||
|
IupMessage("提示", "系统变量保存成功,但用户变量保存失败。");
|
||||||
|
}
|
||||||
|
else if (user_ok)
|
||||||
|
{
|
||||||
|
IupMessage("提示", "用户变量保存成功,但系统变量保存失败。");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
IupMessage("错误", "保存失败!");
|
||||||
|
IupSetAttribute(lbl_status, "TITLE", "状态: 保存失败");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
#include "ui.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "globals.h"
|
||||||
|
#include "ui_utils.h"
|
||||||
|
#include "cb_edit.h"
|
||||||
|
#include "cb_file.h"
|
||||||
|
#include "cb_main.h"
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// 创建列表控件
|
||||||
|
Ihandle *create_path_list()
|
||||||
|
{
|
||||||
|
Ihandle *list = IupFlatList();
|
||||||
|
IupSetAttribute(list, "EXPAND", "YES");
|
||||||
|
IupSetAttribute(list, "MULTIPLE", "YES");
|
||||||
|
IupSetAttribute(list, "ITEMPADDING", UI_LIST_ITEM_PADDING);
|
||||||
|
IupSetAttribute(list, "BACKCOLOR", UI_LIST_BGCOLOR);
|
||||||
|
IupSetAttribute(list, "BORDER", "YES");
|
||||||
|
IupSetAttribute(list, "CANFOCUS", "YES");
|
||||||
|
IupSetAttribute(list, "HLINE", "NO");
|
||||||
|
IupSetCallback(list, "DBLCLICK_CB", (Icallback)list_dblclick_cb);
|
||||||
|
IupSetCallback(list, "DROPFILES_CB", (Icallback)list_dropfiles_cb);
|
||||||
|
IupSetCallback(list, "K_ANY", (Icallback)list_k_any_cb);
|
||||||
|
IupSetCallback(list, "MOTION_CB", (Icallback)list_motion_cb);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建右侧功能按钮区域
|
||||||
|
Ihandle *create_main_buttons()
|
||||||
|
{
|
||||||
|
// 创建右侧按钮
|
||||||
|
btn_new = IupButton("新建(N)", NULL);
|
||||||
|
btn_edit = IupButton("编辑(E)", NULL);
|
||||||
|
btn_browse = IupButton("浏览(B)...", NULL);
|
||||||
|
btn_del = IupButton("删除(D)", NULL);
|
||||||
|
btn_undo = IupButton("撤销(Z)", NULL);
|
||||||
|
btn_redo = IupButton("重做(Y)", NULL);
|
||||||
|
btn_up = IupButton("上移(U)", NULL);
|
||||||
|
btn_down = IupButton("下移(O)", NULL);
|
||||||
|
btn_clean = IupButton("一键清理", NULL);
|
||||||
|
|
||||||
|
// 设置按钮回调
|
||||||
|
IupSetCallback(btn_new, "ACTION", (Icallback)btn_new_cb);
|
||||||
|
IupSetCallback(btn_edit, "ACTION", (Icallback)btn_edit_cb);
|
||||||
|
IupSetCallback(btn_browse, "ACTION", (Icallback)btn_browse_cb);
|
||||||
|
IupSetCallback(btn_del, "ACTION", (Icallback)btn_del_cb);
|
||||||
|
IupSetCallback(btn_undo, "ACTION", (Icallback)btn_undo_cb);
|
||||||
|
IupSetCallback(btn_redo, "ACTION", (Icallback)btn_redo_cb);
|
||||||
|
IupSetCallback(btn_up, "ACTION", (Icallback)btn_up_cb);
|
||||||
|
IupSetCallback(btn_down, "ACTION", (Icallback)btn_down_cb);
|
||||||
|
IupSetCallback(btn_clean, "ACTION", (Icallback)btn_clean_cb);
|
||||||
|
|
||||||
|
// 设置按钮大小
|
||||||
|
IupSetAttribute(btn_new, "RASTERSIZE", UI_BUTTON_SIZE);
|
||||||
|
IupSetAttribute(btn_edit, "RASTERSIZE", UI_BUTTON_SIZE);
|
||||||
|
IupSetAttribute(btn_browse, "RASTERSIZE", UI_BUTTON_SIZE);
|
||||||
|
IupSetAttribute(btn_del, "RASTERSIZE", UI_BUTTON_SIZE);
|
||||||
|
IupSetAttribute(btn_undo, "RASTERSIZE", UI_BUTTON_SIZE);
|
||||||
|
IupSetAttribute(btn_redo, "RASTERSIZE", UI_BUTTON_SIZE);
|
||||||
|
IupSetAttribute(btn_up, "RASTERSIZE", UI_BUTTON_SIZE);
|
||||||
|
IupSetAttribute(btn_down, "RASTERSIZE", UI_BUTTON_SIZE);
|
||||||
|
IupSetAttribute(btn_clean, "RASTERSIZE", UI_BUTTON_SIZE);
|
||||||
|
|
||||||
|
Ihandle *vbox_btns = IupVbox(
|
||||||
|
btn_new, btn_edit, btn_browse, btn_del,
|
||||||
|
IupFill(), // 间隔
|
||||||
|
btn_undo, btn_redo,
|
||||||
|
IupFill(),
|
||||||
|
btn_clean, // 放在上移下移之前,或者最下面,这里放在中间偏下
|
||||||
|
IupFill(),
|
||||||
|
btn_up, btn_down,
|
||||||
|
NULL);
|
||||||
|
IupSetAttribute(vbox_btns, "GAP", UI_GAP_BUTTONS);
|
||||||
|
IupSetAttribute(vbox_btns, "MARGIN", "0x0");
|
||||||
|
|
||||||
|
return vbox_btns;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建底部按钮区域
|
||||||
|
Ihandle *create_bottom_buttons()
|
||||||
|
{
|
||||||
|
// 创建底部按钮
|
||||||
|
btn_help = IupButton("帮助(H)", NULL);
|
||||||
|
IupSetCallback(btn_help, "ACTION", (Icallback)btn_help_cb);
|
||||||
|
IupSetAttribute(btn_help, "RASTERSIZE", UI_BUTTON_SMALL_SIZE);
|
||||||
|
|
||||||
|
btn_theme = IupButton("深色模式", NULL);
|
||||||
|
IupSetCallback(btn_theme, "ACTION", (Icallback)btn_theme_cb);
|
||||||
|
IupSetAttribute(btn_theme, "RASTERSIZE", UI_BUTTON_SMALL_SIZE);
|
||||||
|
|
||||||
|
lbl_status = IupLabel("就绪");
|
||||||
|
IupSetAttribute(lbl_status, "EXPAND", "HORIZONTAL");
|
||||||
|
|
||||||
|
btn_import = IupButton("导入配置", NULL);
|
||||||
|
IupSetCallback(btn_import, "ACTION", (Icallback)btn_import_cb);
|
||||||
|
IupSetAttribute(btn_import, "RASTERSIZE", UI_BUTTON_SIZE);
|
||||||
|
|
||||||
|
btn_export = IupButton("导出配置", NULL);
|
||||||
|
IupSetCallback(btn_export, "ACTION", (Icallback)btn_export_cb);
|
||||||
|
IupSetAttribute(btn_export, "RASTERSIZE", UI_BUTTON_SIZE);
|
||||||
|
|
||||||
|
btn_ok = IupButton("确定(O)", NULL);
|
||||||
|
IupSetCallback(btn_ok, "ACTION", (Icallback)btn_ok_cb);
|
||||||
|
IupSetAttribute(btn_ok, "RASTERSIZE", UI_BUTTON_SIZE);
|
||||||
|
|
||||||
|
btn_cancel = IupButton("取消(C)", NULL);
|
||||||
|
IupSetCallback(btn_cancel, "ACTION", (Icallback)btn_cancel_cb);
|
||||||
|
IupSetAttribute(btn_cancel, "RASTERSIZE", UI_BUTTON_SIZE);
|
||||||
|
|
||||||
|
Ihandle *hbox_bottom = IupHbox(
|
||||||
|
btn_help,
|
||||||
|
btn_theme,
|
||||||
|
lbl_status,
|
||||||
|
IupFill(),
|
||||||
|
btn_import,
|
||||||
|
btn_export,
|
||||||
|
btn_ok,
|
||||||
|
btn_cancel,
|
||||||
|
NULL);
|
||||||
|
IupSetAttribute(hbox_bottom, "GAP", UI_GAP_BOTTOM);
|
||||||
|
IupSetAttribute(hbox_bottom, "ALIGNMENT", "ACENTER");
|
||||||
|
IupSetAttribute(hbox_bottom, "MARGIN", "0x0");
|
||||||
|
|
||||||
|
return hbox_bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建主对话框
|
||||||
|
Ihandle *create_main_dialog()
|
||||||
|
{
|
||||||
|
// 创建两个列表
|
||||||
|
list_sys = create_path_list();
|
||||||
|
list_user = create_path_list();
|
||||||
|
list_merged = create_path_list();
|
||||||
|
|
||||||
|
IupSetAttribute(list_merged, "READONLY", "YES");
|
||||||
|
IupSetAttribute(list_merged, "MULTIPLE", "NO");
|
||||||
|
IupSetAttribute(list_merged, "BGCOLOR", UI_LIST_MERGED_BGCOLOR); // 灰色背景
|
||||||
|
|
||||||
|
// 创建标签页
|
||||||
|
tabs_main = IupTabs(list_sys, list_user, list_merged, NULL);
|
||||||
|
IupSetAttribute(tabs_main, "TABTITLE0", "系统变量 (System PATH)");
|
||||||
|
IupSetAttribute(tabs_main, "TABTITLE1", "用户变量 (User PATH)");
|
||||||
|
IupSetAttribute(tabs_main, "TABTITLE2", "合并预览 (Merged PATH)");
|
||||||
|
|
||||||
|
// 设置标签页切换回调
|
||||||
|
IupSetCallback(tabs_main, "TABCHANGEPOS_CB", (Icallback)tabs_tabchange_cb);
|
||||||
|
|
||||||
|
// 搜索框
|
||||||
|
txt_search = IupText(NULL);
|
||||||
|
IupSetAttribute(txt_search, "NAME", "TXT_SEARCH");
|
||||||
|
IupSetAttribute(txt_search, "CUEBANNER", "搜索...");
|
||||||
|
IupSetCallback(txt_search, "VALUECHANGED_CB", (Icallback)txt_search_cb);
|
||||||
|
IupSetAttribute(txt_search, "EXPAND", "HORIZONTAL");
|
||||||
|
|
||||||
|
// 布局
|
||||||
|
Ihandle *btns = create_main_buttons();
|
||||||
|
Ihandle *hbox_mid = IupHbox(tabs_main, btns, NULL);
|
||||||
|
IupSetAttribute(hbox_mid, "GAP", UI_GAP_MAIN);
|
||||||
|
IupSetAttribute(hbox_mid, "MARGIN", "0x0");
|
||||||
|
|
||||||
|
Ihandle *bottom = create_bottom_buttons();
|
||||||
|
|
||||||
|
Ihandle *vbox_main = IupVbox(txt_search, hbox_mid, bottom, NULL);
|
||||||
|
IupSetAttribute(vbox_main, "GAP", UI_GAP_MAIN);
|
||||||
|
IupSetAttribute(vbox_main, "MARGIN", UI_MARGIN_MAIN);
|
||||||
|
|
||||||
|
Ihandle *dlg = IupDialog(vbox_main);
|
||||||
|
IupSetAttribute(dlg, "TITLE", UI_WINDOW_TITLE);
|
||||||
|
IupSetAttribute(dlg, "RASTERSIZE", UI_WINDOW_SIZE);
|
||||||
|
IupSetAttribute(dlg, "MINSIZE", UI_WINDOW_SIZE);
|
||||||
|
|
||||||
|
return dlg;
|
||||||
|
}
|
||||||
+225
@@ -0,0 +1,225 @@
|
|||||||
|
#include "ui_utils.h"
|
||||||
|
#include "globals.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
// 获取第一个选中的索引(1-based),如果没有选中则返回 0
|
||||||
|
int get_first_selected_index(Ihandle *list)
|
||||||
|
{
|
||||||
|
char *value = IupGetAttribute(list, "VALUE");
|
||||||
|
if (!value)
|
||||||
|
return 0;
|
||||||
|
int len = strlen(value);
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
if (value[i] == '+')
|
||||||
|
return i + 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置单选(1-based)
|
||||||
|
void set_single_selection(Ihandle *list, int index)
|
||||||
|
{
|
||||||
|
int count = IupGetInt(list, "COUNT");
|
||||||
|
if (count <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char *new_val = (char *)malloc(count + 1);
|
||||||
|
if (!new_val)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
new_val[i] = '-';
|
||||||
|
}
|
||||||
|
new_val[count] = '\0';
|
||||||
|
|
||||||
|
if (index >= 1 && index <= count)
|
||||||
|
{
|
||||||
|
new_val[index - 1] = '+';
|
||||||
|
}
|
||||||
|
|
||||||
|
IupSetAttribute(list, "VALUE", new_val);
|
||||||
|
free(new_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从原始数据刷新UI
|
||||||
|
void refresh_ui_from_raw(Ihandle *list, StringList *raw)
|
||||||
|
{
|
||||||
|
IupSetAttribute(list, "REMOVEITEM", "ALL");
|
||||||
|
for (int i = 0; i < raw->count; i++)
|
||||||
|
{
|
||||||
|
IupSetAttributeId(list, "", i + 1, raw->items[i]);
|
||||||
|
}
|
||||||
|
IupSetInt(list, "COUNT", raw->count);
|
||||||
|
refresh_single_list_style(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录历史
|
||||||
|
void record_history()
|
||||||
|
{
|
||||||
|
push_history(&undo_stack, &raw_sys_paths, &raw_user_paths);
|
||||||
|
clear_history_stack(&redo_stack);
|
||||||
|
// 更新按钮状态(可选)
|
||||||
|
// IupSetAttribute(btn_undo, "ACTIVE", "YES");
|
||||||
|
// IupSetAttribute(btn_redo, "ACTIVE", "NO");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 静态辅助函数:对话框确定
|
||||||
|
static int on_dialog_ok(Ihandle *self)
|
||||||
|
{
|
||||||
|
Ihandle *dlg = IupGetDialog(self);
|
||||||
|
IupSetAttribute(dlg, "MY_STATUS", "1");
|
||||||
|
return IUP_CLOSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 静态辅助函数:对话框取消
|
||||||
|
static int on_dialog_cancel(Ihandle *self)
|
||||||
|
{
|
||||||
|
Ihandle *dlg = IupGetDialog(self);
|
||||||
|
IupSetAttribute(dlg, "MY_STATUS", "0");
|
||||||
|
return IUP_CLOSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义输入对话框
|
||||||
|
int custom_input_dialog(const char *title, const char *label_text, char *buffer, int buffer_size)
|
||||||
|
{
|
||||||
|
Ihandle *text = IupText(NULL);
|
||||||
|
IupSetAttribute(text, "VALUE", buffer);
|
||||||
|
IupSetAttribute(text, "EXPAND", "HORIZONTAL");
|
||||||
|
IupSetAttribute(text, "RASTERSIZE", "500x");
|
||||||
|
IupSetAttribute(text, "NAME", "INPUT_TEXT");
|
||||||
|
|
||||||
|
Ihandle *btn_ok = IupButton("确定", NULL);
|
||||||
|
IupSetCallback(btn_ok, "ACTION", on_dialog_ok);
|
||||||
|
IupSetAttribute(btn_ok, "RASTERSIZE", "100x32");
|
||||||
|
|
||||||
|
Ihandle *btn_cancel = IupButton("取消", NULL);
|
||||||
|
IupSetCallback(btn_cancel, "ACTION", on_dialog_cancel);
|
||||||
|
IupSetAttribute(btn_cancel, "RASTERSIZE", "100x32");
|
||||||
|
|
||||||
|
Ihandle *vbox = IupVbox(
|
||||||
|
IupLabel(label_text),
|
||||||
|
text,
|
||||||
|
IupHbox(IupFill(), btn_ok, btn_cancel, NULL),
|
||||||
|
NULL);
|
||||||
|
IupSetAttribute(vbox, "MARGIN", "15x15");
|
||||||
|
IupSetAttribute(vbox, "GAP", "10");
|
||||||
|
|
||||||
|
Ihandle *dlg = IupDialog(vbox);
|
||||||
|
IupSetAttribute(dlg, "TITLE", title);
|
||||||
|
IupSetAttribute(dlg, "MINBOX", "NO");
|
||||||
|
IupSetAttribute(dlg, "MAXBOX", "NO");
|
||||||
|
IupSetAttribute(dlg, "RESIZE", "NO");
|
||||||
|
|
||||||
|
IupSetAttributeHandle(dlg, "DEFAULTENTER", btn_ok);
|
||||||
|
IupSetAttributeHandle(dlg, "DEFAULTESC", btn_cancel);
|
||||||
|
|
||||||
|
// 应用主题到对话框
|
||||||
|
if (is_dark_mode) {
|
||||||
|
IupSetAttribute(dlg, "BGCOLOR", "50 50 50");
|
||||||
|
IupSetAttribute(text, "BGCOLOR", "30 30 30");
|
||||||
|
IupSetAttribute(text, "FGCOLOR", "255 255 255");
|
||||||
|
IupSetAttribute(IupGetChild(vbox, 0), "FGCOLOR", "255 255 255"); // Label
|
||||||
|
}
|
||||||
|
|
||||||
|
IupPopup(dlg, IUP_CENTER, IUP_CENTER);
|
||||||
|
|
||||||
|
int result = IupGetInt(dlg, "MY_STATUS");
|
||||||
|
if (result == 1)
|
||||||
|
{
|
||||||
|
char *val = IupGetAttribute(text, "VALUE");
|
||||||
|
if (val)
|
||||||
|
{
|
||||||
|
strncpy(buffer, val, buffer_size);
|
||||||
|
buffer[buffer_size - 1] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IupDestroy(dlg);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前选中的列表
|
||||||
|
Ihandle *get_current_list()
|
||||||
|
{
|
||||||
|
// 获取当前选中的 Tab 索引
|
||||||
|
int pos = IupGetInt(tabs_main, "VALUEPOS");
|
||||||
|
if (pos == 0)
|
||||||
|
return list_sys;
|
||||||
|
if (pos == 1)
|
||||||
|
return list_user;
|
||||||
|
if (pos == 2)
|
||||||
|
return list_merged;
|
||||||
|
return list_sys; // 默认
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从 raw_data 中删除指定字符串
|
||||||
|
void remove_from_raw_data(StringList *list, const char *str)
|
||||||
|
{
|
||||||
|
if (!list || !str)
|
||||||
|
return;
|
||||||
|
for (int i = 0; i < list->count; i++)
|
||||||
|
{
|
||||||
|
if (strcmp(list->items[i], str) == 0)
|
||||||
|
{
|
||||||
|
free(list->items[i]);
|
||||||
|
// 移动后续元素
|
||||||
|
for (int j = i; j < list->count - 1; j++)
|
||||||
|
{
|
||||||
|
list->items[j] = list->items[j + 1];
|
||||||
|
}
|
||||||
|
list->count--;
|
||||||
|
break; // 假设没有重复,只删除第一个匹配
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换编辑按钮状态
|
||||||
|
void toggle_edit_buttons(int enable)
|
||||||
|
{
|
||||||
|
char *val = enable ? "YES" : "NO";
|
||||||
|
IupSetAttribute(btn_new, "ACTIVE", val); // 新建按钮
|
||||||
|
IupSetAttribute(btn_edit, "ACTIVE", val); // 编辑按钮
|
||||||
|
IupSetAttribute(btn_browse, "ACTIVE", val); // 浏览按钮
|
||||||
|
IupSetAttribute(btn_del, "ACTIVE", val); // 删除按钮
|
||||||
|
IupSetAttribute(btn_clean, "ACTIVE", val); // 清除按钮
|
||||||
|
IupSetAttribute(btn_up, "ACTIVE", val); // 上移按钮
|
||||||
|
IupSetAttribute(btn_down, "ACTIVE", val); // 下移按钮
|
||||||
|
IupSetAttribute(btn_import, "ACTIVE", val); // 导入按钮
|
||||||
|
IupSetAttribute(btn_export, "ACTIVE", "YES"); // 导出按钮始终可用
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用主题
|
||||||
|
void apply_theme()
|
||||||
|
{
|
||||||
|
char *bg_color = is_dark_mode ? "50 50 50" : "240 240 240";
|
||||||
|
char *fg_color = is_dark_mode ? "255 255 255" : "0 0 0";
|
||||||
|
char *list_bg = is_dark_mode ? "60 60 60" : "255 255 255";
|
||||||
|
char *text_bg = is_dark_mode ? "30 30 30" : "255 255 255";
|
||||||
|
|
||||||
|
// 主对话框
|
||||||
|
IupSetAttribute(dlg, "BGCOLOR", bg_color);
|
||||||
|
|
||||||
|
// 列表
|
||||||
|
IupSetAttribute(list_sys, "BACKCOLOR", list_bg);
|
||||||
|
IupSetAttribute(list_sys, "FGCOLOR", fg_color);
|
||||||
|
IupSetAttribute(list_user, "BACKCOLOR", list_bg);
|
||||||
|
IupSetAttribute(list_user, "FGCOLOR", fg_color);
|
||||||
|
IupSetAttribute(list_merged, "BACKCOLOR", list_bg);
|
||||||
|
IupSetAttribute(list_merged, "FGCOLOR", fg_color);
|
||||||
|
|
||||||
|
// 文本框
|
||||||
|
IupSetAttribute(txt_search, "BGCOLOR", text_bg);
|
||||||
|
IupSetAttribute(txt_search, "FGCOLOR", fg_color);
|
||||||
|
|
||||||
|
// 标签
|
||||||
|
IupSetAttribute(lbl_status, "FGCOLOR", fg_color);
|
||||||
|
|
||||||
|
// 刷新列表样式
|
||||||
|
refresh_list_style();
|
||||||
|
refresh_single_list_style(list_merged);
|
||||||
|
}
|
||||||
+287
-9
@@ -3,6 +3,9 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <iup.h>
|
#include <iup.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <direct.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
// 宽字符转UTF-8 (用于IUP显示)
|
// 宽字符转UTF-8 (用于IUP显示)
|
||||||
char *wide_to_utf8(const wchar_t *wstr)
|
char *wide_to_utf8(const wchar_t *wstr)
|
||||||
@@ -39,23 +42,298 @@ int check_admin()
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新列表样式(斑马纹)
|
// 展开环境变量
|
||||||
void refresh_list_style()
|
char* expand_env_vars(const char* path)
|
||||||
{
|
{
|
||||||
if (!list_path)
|
if (!path) return NULL;
|
||||||
|
|
||||||
|
// 先转换为宽字符,因为ExpandEnvironmentStringsW不支持UTF-8
|
||||||
|
wchar_t *wpath = utf8_to_wide(path);
|
||||||
|
if (!wpath) return NULL;
|
||||||
|
|
||||||
|
DWORD size = ExpandEnvironmentStringsW(wpath, NULL, 0);
|
||||||
|
if (size == 0) {
|
||||||
|
free(wpath);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t *wexpanded = (wchar_t *)malloc(size * sizeof(wchar_t));
|
||||||
|
ExpandEnvironmentStringsW(wpath, wexpanded, size);
|
||||||
|
free(wpath);
|
||||||
|
|
||||||
|
char *expanded = wide_to_utf8(wexpanded);
|
||||||
|
free(wexpanded);
|
||||||
|
|
||||||
|
return expanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查路径是否存在
|
||||||
|
static int path_exists(const char *path)
|
||||||
|
{
|
||||||
|
char *expanded_path = expand_env_vars(path);
|
||||||
|
if (!expanded_path)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
wchar_t *wpath = utf8_to_wide(expanded_path);
|
||||||
|
free(expanded_path);
|
||||||
|
|
||||||
|
if (!wpath)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
DWORD attr = GetFileAttributesW(wpath);
|
||||||
|
free(wpath);
|
||||||
|
|
||||||
|
if (attr == INVALID_FILE_ATTRIBUTES)
|
||||||
|
return 0;
|
||||||
|
return (attr & FILE_ATTRIBUTE_DIRECTORY); // 必须是目录
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查路径是否存在(公开给外部使用)
|
||||||
|
int is_path_valid(const char *path)
|
||||||
|
{
|
||||||
|
return path_exists(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 刷新列表样式(斑马纹 + 有效性检查)
|
||||||
|
void refresh_single_list_style(Ihandle *list)
|
||||||
|
{
|
||||||
|
if (!list)
|
||||||
return;
|
return;
|
||||||
int count = IupGetInt(list_path, "COUNT");
|
int count = IupGetInt(list, "COUNT");
|
||||||
|
|
||||||
|
// 用于查重的简单数组(实际项目可以用哈希表)
|
||||||
|
// 为了简单,我们只用双重循环检查重复,性能在几百个条目下没问题
|
||||||
|
|
||||||
for (int i = 1; i <= count; i++)
|
for (int i = 1; i <= count; i++)
|
||||||
{
|
{
|
||||||
// 奇数行:白色
|
char *item = IupGetAttributeId(list, "", i);
|
||||||
// 偶数行:极浅灰色 (245 245 245)
|
if (!item)
|
||||||
if (i % 2 == 0)
|
continue;
|
||||||
|
|
||||||
|
// 默认颜色:黑字
|
||||||
|
char fg_color[32];
|
||||||
|
if (is_dark_mode)
|
||||||
|
strcpy(fg_color, "255 255 255");
|
||||||
|
else
|
||||||
|
strcpy(fg_color, "0 0 0");
|
||||||
|
|
||||||
|
// 1. 检查有效性
|
||||||
|
if (!path_exists(item))
|
||||||
{
|
{
|
||||||
IupSetAttributeId(list_path, "ITEMBGCOLOR", i, "245 245 245");
|
// 无效路径:红色
|
||||||
|
sprintf(fg_color, "255 0 0");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 检查重复 (只检查当前项之前的项,如果之前出现过,当前项标橙)
|
||||||
|
for (int j = 1; j < i; j++)
|
||||||
|
{
|
||||||
|
char *prev_item = IupGetAttributeId(list, "", j);
|
||||||
|
if (prev_item && _stricmp(item, prev_item) == 0) // Windows 路径不区分大小写
|
||||||
|
{
|
||||||
|
// 重复路径:橙色
|
||||||
|
sprintf(fg_color, "255 128 0");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IupSetAttributeId(list, "ITEMFGCOLOR", i, fg_color);
|
||||||
|
|
||||||
|
// 斑马纹背景
|
||||||
|
if (is_dark_mode)
|
||||||
|
{
|
||||||
|
if (i % 2 == 0)
|
||||||
|
IupSetAttributeId(list, "ITEMBGCOLOR", i, "60 60 60");
|
||||||
|
else
|
||||||
|
IupSetAttributeId(list, "ITEMBGCOLOR", i, "50 50 50");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
IupSetAttributeId(list_path, "ITEMBGCOLOR", i, "255 255 255");
|
if (i % 2 == 0)
|
||||||
|
IupSetAttributeId(list, "ITEMBGCOLOR", i, "245 245 245");
|
||||||
|
else
|
||||||
|
IupSetAttributeId(list, "ITEMBGCOLOR", i, "255 255 255");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 刷新所有列表样式
|
||||||
|
void refresh_list_style()
|
||||||
|
{
|
||||||
|
refresh_single_list_style(list_sys);
|
||||||
|
refresh_single_list_style(list_user);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 备份注册表
|
||||||
|
void backup_registry()
|
||||||
|
{
|
||||||
|
// 创建 records 目录
|
||||||
|
if (_mkdir("records") == -1)
|
||||||
|
{
|
||||||
|
// 目录可能已存在,忽略错误
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取当前时间
|
||||||
|
time_t t = time(NULL);
|
||||||
|
struct tm *tm_info = localtime(&t);
|
||||||
|
char time_str[64];
|
||||||
|
strftime(time_str, sizeof(time_str), "%Y%m%d_%H%M%S", tm_info);
|
||||||
|
|
||||||
|
// 构造备份文件名
|
||||||
|
char filename[256];
|
||||||
|
snprintf(filename, sizeof(filename), "records\\backup_%s.reg", time_str);
|
||||||
|
|
||||||
|
// 构造 reg export 命令
|
||||||
|
char full_cmd[1024];
|
||||||
|
snprintf(full_cmd, sizeof(full_cmd), "reg.exe export \"HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\" \"%s\" /y", filename);
|
||||||
|
|
||||||
|
// 使用 CreateProcess 隐藏窗口执行
|
||||||
|
STARTUPINFOA si;
|
||||||
|
PROCESS_INFORMATION pi;
|
||||||
|
memset(&si, 0, sizeof(si));
|
||||||
|
si.cb = sizeof(si);
|
||||||
|
si.dwFlags = STARTF_USESHOWWINDOW;
|
||||||
|
si.wShowWindow = SW_HIDE; // 隐藏窗口
|
||||||
|
memset(&pi, 0, sizeof(pi));
|
||||||
|
|
||||||
|
if (CreateProcessA(NULL, full_cmd, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi))
|
||||||
|
{
|
||||||
|
// 等待进程结束
|
||||||
|
WaitForSingleObject(pi.hProcess, INFINITE);
|
||||||
|
|
||||||
|
DWORD exit_code = 0;
|
||||||
|
GetExitCodeProcess(pi.hProcess, &exit_code);
|
||||||
|
|
||||||
|
CloseHandle(pi.hProcess);
|
||||||
|
CloseHandle(pi.hThread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化字符串列表
|
||||||
|
void init_string_list(StringList *list)
|
||||||
|
{
|
||||||
|
list->items = NULL;
|
||||||
|
list->count = 0;
|
||||||
|
list->capacity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不区分大小写的字符串查找 (简单实现)
|
||||||
|
char *stristr(const char *haystack, const char *needle)
|
||||||
|
{
|
||||||
|
if (!haystack || !needle)
|
||||||
|
return NULL;
|
||||||
|
if (*needle == '\0')
|
||||||
|
return (char *)haystack;
|
||||||
|
|
||||||
|
const char *h = haystack;
|
||||||
|
const char *n = needle;
|
||||||
|
const char *h_start = haystack;
|
||||||
|
|
||||||
|
while (*h)
|
||||||
|
{
|
||||||
|
h_start = h;
|
||||||
|
n = needle;
|
||||||
|
while (*h && *n && (tolower((unsigned char)*h) == tolower((unsigned char)*n)))
|
||||||
|
{
|
||||||
|
h++;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
if (*n == '\0')
|
||||||
|
return (char *)h_start;
|
||||||
|
h = h_start + 1;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加字符串到列表
|
||||||
|
void add_string_list(StringList *list, const char *str)
|
||||||
|
{
|
||||||
|
if (list->count >= list->capacity)
|
||||||
|
{
|
||||||
|
list->capacity = (list->capacity == 0) ? 16 : list->capacity * 2;
|
||||||
|
list->items = (char **)realloc(list->items, list->capacity * sizeof(char *));
|
||||||
|
}
|
||||||
|
list->items[list->count] = _strdup(str); // 复制字符串
|
||||||
|
list->count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空字符串列表
|
||||||
|
void clear_string_list(StringList *list)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < list->count; i++)
|
||||||
|
{
|
||||||
|
free(list->items[i]);
|
||||||
|
}
|
||||||
|
free(list->items);
|
||||||
|
list->items = NULL;
|
||||||
|
list->count = 0;
|
||||||
|
list->capacity = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制字符串列表
|
||||||
|
void copy_string_list(StringList *dest, StringList *src)
|
||||||
|
{
|
||||||
|
init_string_list(dest);
|
||||||
|
if (!src || src->count == 0)
|
||||||
|
return;
|
||||||
|
for (int i = 0; i < src->count; i++)
|
||||||
|
{
|
||||||
|
add_string_list(dest, src->items[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化历史栈
|
||||||
|
void init_history_stack(HistoryStack *stack)
|
||||||
|
{
|
||||||
|
stack->top = NULL;
|
||||||
|
stack->count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 压入历史
|
||||||
|
void push_history(HistoryStack *stack, StringList *sys, StringList *user)
|
||||||
|
{
|
||||||
|
HistoryNode *node = (HistoryNode *)malloc(sizeof(HistoryNode));
|
||||||
|
if (!node)
|
||||||
|
return;
|
||||||
|
|
||||||
|
copy_string_list(&node->sys_paths, sys);
|
||||||
|
copy_string_list(&node->user_paths, user);
|
||||||
|
|
||||||
|
node->next = stack->top;
|
||||||
|
stack->top = node;
|
||||||
|
stack->count++;
|
||||||
|
|
||||||
|
// 简单限制:如果超过 50 个,就不处理底部了(太麻烦),反正内存够用
|
||||||
|
}
|
||||||
|
|
||||||
|
// 弹出历史
|
||||||
|
int pop_history(HistoryStack *stack, StringList *out_sys, StringList *out_user)
|
||||||
|
{
|
||||||
|
if (!stack->top)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
HistoryNode *node = stack->top;
|
||||||
|
stack->top = node->next;
|
||||||
|
stack->count--;
|
||||||
|
|
||||||
|
// 转移所有权,避免复制
|
||||||
|
*out_sys = node->sys_paths;
|
||||||
|
*out_user = node->user_paths;
|
||||||
|
|
||||||
|
free(node);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空历史栈
|
||||||
|
void clear_history_stack(HistoryStack *stack)
|
||||||
|
{
|
||||||
|
while (stack->top)
|
||||||
|
{
|
||||||
|
HistoryNode *node = stack->top;
|
||||||
|
stack->top = node->next;
|
||||||
|
|
||||||
|
clear_string_list(&node->sys_paths);
|
||||||
|
clear_string_list(&node->user_paths);
|
||||||
|
free(node);
|
||||||
|
}
|
||||||
|
stack->count = 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user