mirror of
https://github.com/LHY0125/PathEditor.git
synced 2026-05-10 02:09:46 +08:00
feat(backup): 支持自定义备份目录和备份跳过选项
- 在 Lua 配置中新增 backup.dir 配置项,允许设置默认备份路径 - 修改 backup_registry() 函数,接受自定义备份路径参数,优先级为:传入参数 > Lua 配置 > 默认 %APPDATA% 路径 - 在保存操作前,通过对话框让用户选择“使用默认路径”、“自定义目录”或“跳过备份” - 更新 README 文档,说明新的备份功能和多语言支持等架构细节
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
## ✨ 功能特点
|
||||
|
||||
* **🛡️ 安全第一**:
|
||||
* **自动备份**:每次保存前自动备份注册表,防止意外。
|
||||
* **自动备份**:每次保存前自动备份,支持自定义备份目录(默认 `%APPDATA%/PathEditor/backups/`)。
|
||||
* **只读模式**:非管理员运行时自动切换到只读模式,防止误操作。
|
||||
* **权限检测**:智能检测当前运行权限。
|
||||
|
||||
@@ -29,6 +29,10 @@
|
||||
* **导入恢复**:从 JSON 文件导入路径配置。
|
||||
* **格式兼容**:支持旧版 TXT 格式导入。
|
||||
|
||||
* **🌍 多语言支持**:
|
||||
* 内置中文和英文界面,支持运行时切换。
|
||||
* 基于 gettext 国际化框架,易于扩展其他语言。
|
||||
|
||||
* **便捷管理**:
|
||||
* ➕ **新建**:添加新路径到列表。
|
||||
* 📂 **浏览**:直接从文件资源管理器选择目录添加。
|
||||
@@ -45,14 +49,23 @@
|
||||
* **分层设计**:
|
||||
* `src/core/` (Model): 核心数据与业务逻辑,完全脱离 UI 框架(无任何 `<iup.h>` 依赖)。
|
||||
* `src/ui/` (View): 负责界面布局与组件的纯视觉展示。
|
||||
* `src/controller/` (Controller): 负责连接用户交互与底层数据。
|
||||
* `src/controller/` (Controller): 负责连接用户交互与底层数据,已按功能拆分为 6 个模块:
|
||||
* `callbacks.c` - 辅助函数与上下文管理
|
||||
* `callbacks_basic.c` - 基础 CRUD 操作(新建、编辑、浏览、删除)
|
||||
* `callbacks_nav.c` - 导航操作(上移、下移、清理)
|
||||
* `callbacks_search.c` - 搜索过滤与拖拽
|
||||
* `callbacks_io.c` - 导入导出
|
||||
* `callbacks_sys.c` - 系统操作(保存、取消、帮助、语言切换)
|
||||
* `src/utils/` (Utils): 纯粹的底层工具类封装(系统级调用、字符串处理)。
|
||||
* **热配置系统**:所有 UI 参数(窗口大小、按钮文本、布局间距等)均通过 `lua/config.lua` 配置,修改无需重新编译即可生效。
|
||||
* **国际化支持**:基于 gettext 框架,支持中英文运行时切换,语言文件位于 `locale/` 目录。
|
||||
* **清晰的应用状态**:摒弃了脆弱的全局变量模式,采用 `AppContext` 统一管理应用运行时的上下文状态,通过指针传递,安全可靠。
|
||||
* **开发工具库**:
|
||||
* 统一错误码系统 (`utils/error_code.h`)
|
||||
* 统一错误码系统 (`utils/error_code.h`) - 11 种细分错误码
|
||||
* 安全字符串函数 (`utils/safe_string.h`)
|
||||
* 日志系统 (`utils/logger.h`)
|
||||
* 字符串列表封装 (`utils/string_ext.h`) - 带访问器函数的安全动态数组
|
||||
* 日志系统 (`utils/logger.h`) - 支持 DEBUG/INFO/WARN/ERROR 四个级别
|
||||
* 控件名称常量 (`utils/ui_constants.h`) - 集中管理所有 IUP 控件名称
|
||||
|
||||
## 📦 下载与安装
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ int check_admin(void);
|
||||
int is_path_valid(const char *path);
|
||||
|
||||
// 备份注册表
|
||||
ErrorCode backup_registry(void);
|
||||
// 参数 backup_path: 自定义备份目录路径,传 NULL 使用 Lua 配置中的默认路径
|
||||
ErrorCode backup_registry(const char *backup_path);
|
||||
|
||||
#endif // OS_ENV_H
|
||||
|
||||
@@ -15,6 +15,11 @@ local config = {
|
||||
select_dir = "Select Directory"
|
||||
},
|
||||
|
||||
-- 备份设置
|
||||
backup = {
|
||||
dir = "", -- 默认备份目录,留空使用 %APPDATA%/PathEditor/backups/
|
||||
},
|
||||
|
||||
-- 列表控件设置
|
||||
list = {
|
||||
item_padding = "5x5",
|
||||
|
||||
@@ -28,23 +28,71 @@ int btn_ok_cb(Ihandle *self)
|
||||
return IUP_DEFAULT;
|
||||
}
|
||||
|
||||
ErrorCode backup_result = backup_registry();
|
||||
if (backup_result != ERR_OK)
|
||||
{
|
||||
log_error("Backup failed: error code %d", backup_result);
|
||||
const char *reason = "未知错误";
|
||||
if (backup_result == ERR_FAILED)
|
||||
reason = "无法获取 AppData 路径";
|
||||
else if (backup_result == ERR_FILE_NOT_FOUND)
|
||||
reason = "无法创建备份目录或文件";
|
||||
else if (backup_result == ERR_REGISTRY_FAILED)
|
||||
reason = "无法读取注册表中的 PATH 值";
|
||||
// 询问用户是否自定义备份目录
|
||||
char custom_backup_dir[MAX_PATH] = "";
|
||||
int do_backup = 1; // 是否执行备份
|
||||
|
||||
char msg[512];
|
||||
snprintf(msg, sizeof(msg), "备份失败!原因:%s\n\n是否继续保存?\n(继续保存可能导致无法恢复)", reason);
|
||||
int choice = IupAlarm("警告", msg, "继续保存", "取消", NULL);
|
||||
if (choice != 1)
|
||||
return IUP_DEFAULT;
|
||||
int backup_choice = IupAlarm("备份设置",
|
||||
"是否自定义备份目录?\n\n"
|
||||
"选择「使用默认」将备份到 %APPDATA%/PathEditor/backups/\n"
|
||||
"选择「自定义目录」可选择其他位置",
|
||||
"使用默认", "自定义目录", "跳过备份");
|
||||
|
||||
if (backup_choice == 2) // 自定义目录
|
||||
{
|
||||
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)
|
||||
strncpy(custom_backup_dir, value, MAX_PATH - 1);
|
||||
}
|
||||
IupDestroy(filedlg);
|
||||
|
||||
if (strlen(custom_backup_dir) == 0)
|
||||
{
|
||||
IupMessage("提示", "未选择目录,将使用默认备份路径。");
|
||||
}
|
||||
}
|
||||
else if (backup_choice == 3) // 跳过备份
|
||||
{
|
||||
int skip_confirm = IupAlarm("确认", "确定跳过备份吗?\n跳过备份可能导致无法恢复!",
|
||||
"确定跳过", "返回备份", NULL);
|
||||
if (skip_confirm != 1)
|
||||
{
|
||||
// 用户反悔,重新询问
|
||||
return btn_ok_cb(self);
|
||||
}
|
||||
do_backup = 0;
|
||||
}
|
||||
|
||||
// 执行备份(如果用户没有跳过)
|
||||
if (do_backup)
|
||||
{
|
||||
const char *backup_path = strlen(custom_backup_dir) > 0 ? custom_backup_dir : NULL;
|
||||
ErrorCode backup_result = backup_registry(backup_path);
|
||||
if (backup_result != ERR_OK)
|
||||
{
|
||||
log_error("Backup failed: error code %d", backup_result);
|
||||
const char *reason = "未知错误";
|
||||
if (backup_result == ERR_FAILED)
|
||||
reason = "无法获取 AppData 路径";
|
||||
else if (backup_result == ERR_FILE_NOT_FOUND)
|
||||
reason = "无法创建备份目录或文件";
|
||||
else if (backup_result == ERR_REGISTRY_FAILED)
|
||||
reason = "无法读取注册表中的 PATH 值";
|
||||
|
||||
char msg[512];
|
||||
snprintf(msg, sizeof(msg), "备份失败!原因:%s\n\n是否继续保存?\n(继续保存可能导致无法恢复)", reason);
|
||||
int choice = IupAlarm("警告", msg, "继续保存", "取消", NULL);
|
||||
if (choice != 1)
|
||||
return IUP_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
ErrorCode sys_ok = save_system_paths(&ctx->sys_paths);
|
||||
|
||||
+50
-9
@@ -1,6 +1,7 @@
|
||||
#include "utils/os_env.h"
|
||||
#include "utils/string_ext.h"
|
||||
#include "utils/logger.h"
|
||||
#include "core/lua_config.h"
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -47,25 +48,65 @@ int is_path_valid(const char *path)
|
||||
}
|
||||
|
||||
// 备份注册表
|
||||
// 备份到 %APPDATA%/PathEditor/backups/ 目录下
|
||||
// 参数 backup_path: 自定义备份目录路径,传 NULL 使用 Lua 配置中的默认路径
|
||||
// 返回值:
|
||||
// ERR_OK - 备份成功(系统和用户 PATH 都已备份)
|
||||
// ERR_FAILED - AppData 路径获取失败
|
||||
// ERR_FILE_NOT_FOUND - 备份文件创建失败
|
||||
// ERR_REGISTRY_FAILED - 系统和用户 PATH 都读取失败
|
||||
ErrorCode backup_registry(void)
|
||||
ErrorCode backup_registry(const char *backup_path)
|
||||
{
|
||||
// 获取 AppData 路径
|
||||
wchar_t appdata_path[MAX_PATH];
|
||||
if (SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, appdata_path) != S_OK)
|
||||
wchar_t backup_dir[MAX_PATH];
|
||||
|
||||
if (backup_path && strlen(backup_path) > 0)
|
||||
{
|
||||
log_error("Failed to get AppData path");
|
||||
return ERR_FAILED;
|
||||
// 使用用户指定的路径
|
||||
wchar_t *wpath = utf8_to_wide(backup_path);
|
||||
if (wpath)
|
||||
{
|
||||
wcsncpy(backup_dir, wpath, MAX_PATH - 1);
|
||||
backup_dir[MAX_PATH - 1] = L'\0';
|
||||
free(wpath);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_error("Failed to convert backup path to wide string");
|
||||
return ERR_FAILED;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 使用默认路径:Lua 配置 > %APPDATA%/PathEditor/backups/
|
||||
const char *lua_dir = lua_config_get_string("backup", "dir");
|
||||
if (lua_dir && strlen(lua_dir) > 0)
|
||||
{
|
||||
wchar_t *wpath = utf8_to_wide(lua_dir);
|
||||
if (wpath)
|
||||
{
|
||||
wcsncpy(backup_dir, wpath, MAX_PATH - 1);
|
||||
backup_dir[MAX_PATH - 1] = L'\0';
|
||||
free(wpath);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_error("Failed to convert Lua backup dir to wide string");
|
||||
return ERR_FAILED;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 获取 AppData 路径作为默认值
|
||||
wchar_t appdata_path[MAX_PATH];
|
||||
if (SHGetFolderPathW(NULL, CSIDL_APPDATA, NULL, 0, appdata_path) != S_OK)
|
||||
{
|
||||
log_error("Failed to get AppData path");
|
||||
return ERR_FAILED;
|
||||
}
|
||||
swprintf(backup_dir, MAX_PATH, L"%s\\PathEditor\\backups", appdata_path);
|
||||
}
|
||||
}
|
||||
|
||||
// 创建备份目录(递归创建中间目录)
|
||||
wchar_t backup_dir[MAX_PATH];
|
||||
swprintf(backup_dir, MAX_PATH, L"%s\\PathEditor\\backups", appdata_path);
|
||||
if (SHCreateDirectoryExW(NULL, backup_dir, NULL) != ERROR_SUCCESS)
|
||||
{
|
||||
log_error("Failed to create backup directory: %ls", backup_dir);
|
||||
|
||||
Reference in New Issue
Block a user