diff --git a/CMakeLists.txt b/CMakeLists.txt index fe18570..bab2d56 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,11 @@ set(SOURCES src/core/lua_config.c src/core/import_export.c src/controller/callbacks.c + src/controller/callbacks_basic.c + src/controller/callbacks_nav.c + src/controller/callbacks_search.c + src/controller/callbacks_io.c + src/controller/callbacks_sys.c ico/resources.rc ) diff --git a/include/controller/callbacks_internal.h b/include/controller/callbacks_internal.h new file mode 100644 index 0000000..d3aac5f --- /dev/null +++ b/include/controller/callbacks_internal.h @@ -0,0 +1,22 @@ +#ifndef CALLBACKS_INTERNAL_H +#define CALLBACKS_INTERNAL_H + +#include +#include "core/app_context.h" + +// 内部辅助函数声明(供各 callbacks_*.c 文件共享) +// 这些函数不对外暴露,仅在 controller 层内部使用 + +// 获取主对话框句柄 +Ihandle *get_main_dlg(void); + +// 从对话框获取应用上下文 +AppContext *get_app_context_from_dlg(Ihandle *dlg); + +// 获取当前活动的数据列表(根据 Tab 页切换) +StringList *get_current_raw_data(Ihandle *dlg); + +// 获取当前活动的列表 UI 控件 +Ihandle *get_current_list(Ihandle *dlg); + +#endif // CALLBACKS_INTERNAL_H diff --git a/include/utils/string_ext.h b/include/utils/string_ext.h index 96fbd47..ab43b49 100644 --- a/include/utils/string_ext.h +++ b/include/utils/string_ext.h @@ -16,6 +16,12 @@ void init_string_list(StringList *list); void add_string_list(StringList *list, const char *str); void clear_string_list(StringList *list); +// 访问器函数 - 安全访问内部数据 +// 获取指定索引的字符串(只读),越界返回 NULL +const char *string_list_get(const StringList *list, int index); +// 设置指定索引的字符串(会复制新字符串并释放旧字符串),越界返回 -1,成功返回 0 +int string_list_set(StringList *list, int index, const char *str); + // 字符串转换函数 char *wide_to_utf8(const wchar_t *wstr); wchar_t *utf8_to_wide(const char *str); diff --git a/src/controller/callbacks.c b/src/controller/callbacks.c index 7a2026d..cafb36e 100644 --- a/src/controller/callbacks.c +++ b/src/controller/callbacks.c @@ -1,32 +1,17 @@ #include "controller/callbacks.h" +#include "controller/callbacks_internal.h" #include "core/app_context.h" -#include "core/registry_service.h" -#include "core/path_manager.h" -#include "core/lua_config.h" -#include "core/import_export.h" -#include "utils/string_ext.h" -#include "utils/os_env.h" -#include "utils/error_code.h" -#include "utils/safe_string.h" -#include "utils/logger.h" -#include "utils/i18n.h" #include "utils/ui_constants.h" -#include "ui/ui_utils.h" -#include "ui/dialogs.h" -#include "ui/main_window.h" -#include -#include -#include -#include +#include // 辅助函数:获取主对话框 -static Ihandle *get_main_dlg() +Ihandle *get_main_dlg(void) { return IupGetHandle("MAIN_DIALOG"); } // 辅助函数:从对话框获取应用上下文(core层不依赖IUP,因此在此实现) -static AppContext *get_app_context_from_dlg(Ihandle *dlg) +AppContext *get_app_context_from_dlg(Ihandle *dlg) { if (!dlg) return NULL; @@ -34,7 +19,7 @@ static AppContext *get_app_context_from_dlg(Ihandle *dlg) } // 获取当前的缓存数据列表 -static StringList *get_current_raw_data(Ihandle *dlg) +StringList *get_current_raw_data(Ihandle *dlg) { AppContext *ctx = get_app_context_from_dlg(dlg); if (!ctx) @@ -50,7 +35,7 @@ static StringList *get_current_raw_data(Ihandle *dlg) } // 辅助函数:获取当前选中的列表UI控件 -static Ihandle *get_current_list(Ihandle *dlg) +Ihandle *get_current_list(Ihandle *dlg) { Ihandle *tabs_main = IupGetDialogChild(dlg, CTRL_TABS_MAIN); int pos = IupGetInt(tabs_main, "VALUEPOS"); @@ -60,558 +45,3 @@ static Ihandle *get_current_list(Ihandle *dlg) return IupGetDialogChild(dlg, CTRL_LIST_USER); return IupGetDialogChild(dlg, CTRL_LIST_SYS); } - -// 按钮回调:新建 -int btn_new_cb(Ihandle *self) -{ - Ihandle *dlg = IupGetDialog(self); - char buffer[PATH_BUFFER_SIZE] = ""; - if (custom_input_dialog("新建环境变量", "请输入路径:", buffer, sizeof(buffer))) - { - if (strlen(buffer) > 0) - { - StringList *raw_data = get_current_raw_data(dlg); - add_string_list(raw_data, buffer); - - Ihandle *current_list = get_current_list(dlg); - sync_string_list_to_ui(current_list, raw_data); - - int count = IupGetInt(current_list, "COUNT"); - IupSetInt(current_list, "VALUE", count); - } - } - return IUP_DEFAULT; -} - -// 按钮回调:编辑 -int btn_edit_cb(Ihandle *self) -{ - Ihandle *dlg = IupGetDialog(self); - Ihandle *current_list = get_current_list(dlg); - int selected = IupGetInt(current_list, "VALUE"); - if (selected == 0) - return IUP_DEFAULT; - - StringList *raw_data = get_current_raw_data(dlg); - if (selected - 1 >= raw_data->count) - return IUP_DEFAULT; - - char buffer[PATH_BUFFER_SIZE]; - safe_strcpy(buffer, sizeof(buffer), raw_data->items[selected - 1]); - - if (custom_input_dialog("编辑环境变量", "编辑路径:", buffer, sizeof(buffer))) - { - if (strlen(buffer) > 0) - { - free(raw_data->items[selected - 1]); - raw_data->items[selected - 1] = _strdup(buffer); - - sync_string_list_to_ui(current_list, raw_data); - IupSetInt(current_list, "VALUE", selected); - } - } - return IUP_DEFAULT; -} - -// 双击回调 -int list_dblclick_cb(Ihandle *self, int item, char *text) -{ - if (item > 0) - { - IupSetInt(self, "VALUE", item); - btn_edit_cb(self); - } - return IUP_DEFAULT; -} - -// 按钮回调:浏览 -int btn_browse_cb(Ihandle *self) -{ - Ihandle *dlg = IupGetDialog(self); - Ihandle *filedlg = IupFileDlg(); - IupSetAttribute(filedlg, "DIALOGTYPE", "DIR"); - IupSetAttribute(filedlg, "TITLE", lua_config_get_string("dialog", "select_dir")); - - IupPopup(filedlg, IUP_CENTER, IUP_CENTER); - - if (IupGetInt(filedlg, "STATUS") != -1) - { - char *value = IupGetAttribute(filedlg, "VALUE"); - if (value) - { - StringList *raw_data = get_current_raw_data(dlg); - add_string_list(raw_data, value); - - Ihandle *current_list = get_current_list(dlg); - sync_string_list_to_ui(current_list, raw_data); - - int count = IupGetInt(current_list, "COUNT"); - IupSetInt(current_list, "VALUE", count); - } - } - IupDestroy(filedlg); - return IUP_DEFAULT; -} - -// 按钮回调:删除 -int btn_del_cb(Ihandle *self) -{ - Ihandle *dlg = IupGetDialog(self); - Ihandle *current_list = get_current_list(dlg); - int selected = IupGetInt(current_list, "VALUE"); - - if (selected == 0) - { - IupMessage("提示", "请先选择要删除的项"); - return IUP_DEFAULT; - } - - StringList *raw_data = get_current_raw_data(dlg); - ErrorCode result = path_manager_remove_at(raw_data, selected - 1); - if (result != ERR_OK) - { - log_error("Failed to remove path at index %d", selected - 1); - } - - sync_string_list_to_ui(current_list, raw_data); - - Ihandle *lbl_status = IupGetDialogChild(dlg, CTRL_LBL_STATUS); - if (lbl_status) - IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "deleted")); - - return IUP_DEFAULT; -} - -// 按钮回调:上移 -int btn_up_cb(Ihandle *self) -{ - Ihandle *dlg = IupGetDialog(self); - Ihandle *current_list = get_current_list(dlg); - int selected = IupGetInt(current_list, "VALUE"); - if (selected <= 1) - return IUP_DEFAULT; - - StringList *raw_data = get_current_raw_data(dlg); - ErrorCode result = path_manager_move_up(raw_data, selected - 1); - if (result != ERR_OK) - { - log_error("Failed to move path up at index %d", selected - 1); - } - - sync_string_list_to_ui(current_list, raw_data); - IupSetInt(current_list, "VALUE", selected - 1); - - return IUP_DEFAULT; -} - -// 按钮回调:下移 -int btn_down_cb(Ihandle *self) -{ - Ihandle *dlg = IupGetDialog(self); - Ihandle *current_list = get_current_list(dlg); - int selected = IupGetInt(current_list, "VALUE"); - StringList *raw_data = get_current_raw_data(dlg); - - if (selected == 0 || selected >= raw_data->count) - return IUP_DEFAULT; - - ErrorCode result = path_manager_move_down(raw_data, selected - 1); - if (result != ERR_OK) - { - log_error("Failed to move path down at index %d", selected - 1); - } - - sync_string_list_to_ui(current_list, raw_data); - IupSetInt(current_list, "VALUE", selected + 1); - - return IUP_DEFAULT; -} - -// 按钮回调:一键清理 -int btn_clean_cb(Ihandle *self) -{ - Ihandle *dlg = IupGetDialog(self); - StringList *raw_data = get_current_raw_data(dlg); - if (!raw_data || raw_data->count == 0) - return IUP_DEFAULT; - - if (IupAlarm("确认清理", "此操作将移除当前列表中所有【无效路径】和【重复路径】。\n确定要继续吗?", "确定", "取消", NULL) != 1) - { - return IUP_DEFAULT; - } - - int before_count = raw_data->count; - path_manager_clean(raw_data); - int removed = before_count - raw_data->count; - - Ihandle *current_list = get_current_list(dlg); - sync_string_list_to_ui(current_list, raw_data); - - char msg[128]; - snprintf(msg, sizeof(msg), "清理完成!共移除了 %d 个无效或重复路径。", removed); - IupMessage("提示", msg); - return IUP_DEFAULT; -} - -// 搜索回调 -int txt_search_cb(Ihandle *self) -{ - Ihandle *dlg = IupGetDialog(self); - char *filter = IupGetAttribute(self, "VALUE"); - if (!filter) - return IUP_DEFAULT; - - Ihandle *current_list = get_current_list(dlg); - StringList *raw_data = get_current_raw_data(dlg); - - IupSetAttribute(current_list, "REMOVEITEM", "ALL"); - - int count = 0; - for (int i = 0; i < raw_data->count; i++) - { - 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_dropfiles_cb(Ihandle *self, const char *filename, int num, int x, int y) -{ - Ihandle *dlg = IupGetDialog(self); - Ihandle *current_list = self; - AppContext *ctx = get_app_context_from_dlg(dlg); - if (!ctx) - return IUP_DEFAULT; - - StringList *raw_data = NULL; - if (self == IupGetDialogChild(dlg, CTRL_LIST_SYS)) - raw_data = &ctx->sys_paths; - else if (self == IupGetDialogChild(dlg, CTRL_LIST_USER)) - raw_data = &ctx->user_paths; - else - return IUP_DEFAULT; - - DWORD attr = GetFileAttributesA(filename); - if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY)) - { - Ihandle *txt_search = IupGetDialogChild(dlg, CTRL_TXT_SEARCH); - if (txt_search) - IupSetAttribute(txt_search, "VALUE", ""); - - add_string_list(raw_data, filename); - sync_string_list_to_ui(current_list, raw_data); - - IupSetInt(current_list, "VALUE", raw_data->count); - } - else - { - Ihandle *lbl_status = IupGetDialogChild(dlg, CTRL_LBL_STATUS); - if (lbl_status) - IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "drag_folder_only")); - } - - return IUP_DEFAULT; -} - -// 键盘按键回调 -int list_k_any_cb(Ihandle *self, int c) -{ - if (c == K_DEL) - { - btn_del_cb(self); - return IUP_IGNORE; - } - return IUP_DEFAULT; -} - -// 按钮回调:确定 (保存所有) -int btn_ok_cb(Ihandle *self) -{ - Ihandle *dlg = IupGetDialog(self); - AppContext *ctx = get_app_context_from_dlg(dlg); - if (!ctx) - return IUP_DEFAULT; - - if (!check_admin()) - { - IupMessage("错误", "需要管理员权限才能保存更改!"); - return IUP_DEFAULT; - } - - backup_registry(); - - ErrorCode sys_ok = save_system_paths(&ctx->sys_paths); - ErrorCode user_ok = save_user_paths(&ctx->user_paths); - - Ihandle *lbl_status = IupGetDialogChild(dlg, CTRL_LBL_STATUS); - - if (sys_ok == ERR_OK && user_ok == ERR_OK) - { - log_info("Saved system paths: %d, user paths: %d", ctx->sys_paths.count, ctx->user_paths.count); - SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)L"Environment", SMTO_ABORTIFHUNG, 5000, NULL); - IupMessage("成功", "系统和用户 PATH 环境变量均已更新!"); - if (lbl_status) - IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "saved")); - } - else if (sys_ok == ERR_OK) - { - IupMessage("提示", "系统变量保存成功,但用户变量保存失败。"); - } - else if (user_ok == ERR_OK) - { - IupMessage("提示", "用户变量保存成功,但系统变量保存失败。"); - } - else - { - log_error("Failed to save paths: sys=%d, user=%d", sys_ok, user_ok); - IupMessage("错误", "保存失败!"); - if (lbl_status) - IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "error")); - } - return IUP_DEFAULT; -} - -// 按钮回调:取消 -int btn_cancel_cb(Ihandle *self) -{ - IupExitLoop(); - return IUP_DEFAULT; -} - -// 按钮回调:导入 -int btn_import_cb(Ihandle *self) -{ - Ihandle *dlg = IupGetDialog(self); - AppContext *ctx = get_app_context_from_dlg(dlg); - if (!ctx) - return IUP_DEFAULT; - - if (!check_admin()) - { - IupMessage("错误", "需要管理员权限才能导入 PATH!"); - return IUP_DEFAULT; - } - - Ihandle *filedlg = IupFileDlg(); - IupSetAttribute(filedlg, "DIALOGTYPE", "OPEN"); - IupSetAttribute(filedlg, "TITLE", lua_config_get_string("label", "import_title")); - IupSetAttribute(filedlg, "FILTER", "json"); - IupSetAttribute(filedlg, "EXTFILTER", "JSON 文件 (*.json)|*.json|文本文件 (*.txt)|*.txt|所有文件 (*.*)|*.*"); - - IupPopup(filedlg, IUP_CENTER, IUP_CENTER); - - if (IupGetInt(filedlg, "STATUS") != -1) - { - char *filepath = IupGetAttribute(filedlg, "VALUE"); - if (filepath) - { - ExportData imported; - ErrorCode import_result = import_paths_from_file(filepath, &imported); - if (import_result == ERR_OK) - { - int has_system = imported.system.count > 0; - int has_user = imported.user.count > 0; - - if (!has_system && !has_user) - { - IupMessage("错误", "文件中没有找到有效的路径!"); - return IUP_DEFAULT; - } - - int choice = 0; - if (has_system && has_user) - { - choice = IupAlarm("导入选项", "请选择导入目标:", - "仅系统变量", "仅用户变量", "全部导入"); - } - else if (has_system) - { - choice = 3; - } - else - { - choice = 2; - } - - int total_imported = 0; - - if (choice == 1 || choice == 3) - { - clear_string_list(&ctx->sys_paths); - for (int i = 0; i < imported.system.count; i++) - { - add_string_list(&ctx->sys_paths, imported.system.items[i]); - } - Ihandle *list_sys = IupGetDialogChild(dlg, CTRL_LIST_SYS); - sync_string_list_to_ui(list_sys, &ctx->sys_paths); - total_imported += imported.system.count; - } - - if (choice == 2 || choice == 3) - { - clear_string_list(&ctx->user_paths); - for (int i = 0; i < imported.user.count; i++) - { - add_string_list(&ctx->user_paths, imported.user.items[i]); - } - Ihandle *list_user = IupGetDialogChild(dlg, CTRL_LIST_USER); - sync_string_list_to_ui(list_user, &ctx->user_paths); - total_imported += imported.user.count; - } - - char msg[256]; - snprintf(msg, sizeof(msg), "成功导入 %d 个路径!", total_imported); - IupMessage("导入成功", msg); - - Ihandle *lbl_status = IupGetDialogChild(dlg, CTRL_LBL_STATUS); - if (lbl_status) - IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "loaded")); - } - else - { - log_error("Import failed: error code %d", import_result); - IupMessage("错误", "导入失败,请检查文件格式是否正确!"); - } - } - } - IupDestroy(filedlg); - return IUP_DEFAULT; -} - -// 按钮回调:导出 -int btn_export_cb(Ihandle *self) -{ - Ihandle *dlg = IupGetDialog(self); - AppContext *ctx = get_app_context_from_dlg(dlg); - if (!ctx) - return IUP_DEFAULT; - - ExportData data; - data.system = ctx->sys_paths; - data.user = ctx->user_paths; - - Ihandle *filedlg = IupFileDlg(); - IupSetAttribute(filedlg, "DIALOGTYPE", "SAVE"); - IupSetAttribute(filedlg, "TITLE", lua_config_get_string("label", "export_title")); - IupSetAttribute(filedlg, "FILTER", "json"); - IupSetAttribute(filedlg, "EXTFILTER", "JSON 文件 (*.json)|*.json"); - IupSetAttribute(filedlg, "DEFAULTEXT", "json"); - - char default_name[64]; - snprintf(default_name, sizeof(default_name), "path_all.json"); - IupSetAttribute(filedlg, "VALUE", default_name); - - IupPopup(filedlg, IUP_CENTER, IUP_CENTER); - - if (IupGetInt(filedlg, "STATUS") != -1) - { - char *filepath = IupGetAttribute(filedlg, "VALUE"); - if (filepath) - { - char final_path[MAX_PATH]; - if (strchr(filepath, '.') == NULL) - { - snprintf(final_path, sizeof(final_path), "%s.json", filepath); - } - else - { - safe_strcpy(final_path, sizeof(final_path), filepath); - } - filepath = final_path; - - ErrorCode export_result = export_paths_to_file(&data, filepath); - if (export_result == ERR_OK) - { - char msg[512]; - snprintf(msg, sizeof(msg), "成功导出!\n系统变量: %d 个\n用户变量: %d 个\n\n保存位置: %s", - data.system.count, data.user.count, filepath); - IupMessage("导出成功", msg); - } - else - { - log_error("Export failed: error code %d", export_result); - IupMessage("错误", "导出失败!"); - } - } - } - IupDestroy(filedlg); - return IUP_DEFAULT; -} - -// 载入所有路径 -void load_all_paths(void) -{ - Ihandle *dlg = get_main_dlg(); - if (!dlg) - return; - AppContext *ctx = get_app_context_from_dlg(dlg); - if (!ctx) - return; - - if (load_system_paths(&ctx->sys_paths) != ERR_OK) - { - log_error("Failed to load system paths"); - IupMessage("错误", "无法打开系统环境变量注册表键,请尝试以管理员身份运行。"); - } - else - { - log_info("Loaded system paths: %d", ctx->sys_paths.count); - } - - ErrorCode user_result = load_user_paths(&ctx->user_paths); - if (user_result == ERR_OK) - { - log_info("Loaded user paths: %d", ctx->user_paths.count); - } - - Ihandle *list_sys = IupGetDialogChild(dlg, CTRL_LIST_SYS); - Ihandle *list_user = IupGetDialogChild(dlg, CTRL_LIST_USER); - - sync_string_list_to_ui(list_sys, &ctx->sys_paths); - sync_string_list_to_ui(list_user, &ctx->user_paths); - - Ihandle *lbl_status = IupGetDialogChild(dlg, CTRL_LBL_STATUS); - if (lbl_status) - IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "loaded")); -} - -// 按钮回调:语言切换 -int btn_lang_cb(Ihandle *self) -{ - Ihandle *dlg = IupGetDialog(self); - language_select_dialog(); - refresh_main_window_ui(dlg); - return IUP_DEFAULT; -} - -// 按钮回调:帮助 -int btn_help_cb(Ihandle *self) -{ - IupMessage("使用说明", - "1. 本程序用于编辑系统环境变量 PATH。\n" - "2. 必须以【管理员身份】运行才能保存更改。\n" - "3. 操作说明:\n" - " - 新建:添加新路径到列表末尾。\n" - " - 编辑:修改选中的路径。\n" - " - 浏览:从文件系统选择目录添加。\n" - " - 删除:移除选中的路径。\n" - " - 上移/下移:调整路径优先级。\n" - " - 导入/导出:备份和恢复 PATH 配置。\n" - "4. 点击【确定】保存更改并生效。\n" - "5. 注意:某些正在运行的程序可能需要重启才能识别新的环境变量。\n\n" - "--------------------------------------------------\n" - "作者:LHY\n" - "邮箱:3364451258@qq.com\n" - "GitHub:https://github.com/LHY0125/PathEditor\n" - "记得给我的项目点个star!"); - - return IUP_DEFAULT; -} \ No newline at end of file diff --git a/src/controller/callbacks_basic.c b/src/controller/callbacks_basic.c new file mode 100644 index 0000000..39868e0 --- /dev/null +++ b/src/controller/callbacks_basic.c @@ -0,0 +1,134 @@ +#include "controller/callbacks.h" +#include "controller/callbacks_internal.h" +#include "core/path_manager.h" +#include "core/lua_config.h" +#include "utils/string_ext.h" +#include "utils/safe_string.h" +#include "utils/error_code.h" +#include "utils/logger.h" +#include "utils/ui_constants.h" +#include "ui/ui_utils.h" +#include "ui/dialogs.h" +#include +#include +#include + +// 按钮回调:新建 +int btn_new_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + char buffer[PATH_BUFFER_SIZE] = ""; + if (custom_input_dialog("新建环境变量", "请输入路径:", buffer, sizeof(buffer))) + { + if (strlen(buffer) > 0) + { + StringList *raw_data = get_current_raw_data(dlg); + add_string_list(raw_data, buffer); + + Ihandle *current_list = get_current_list(dlg); + sync_string_list_to_ui(current_list, raw_data); + + int count = IupGetInt(current_list, "COUNT"); + IupSetInt(current_list, "VALUE", count); + } + } + return IUP_DEFAULT; +} + +// 按钮回调:编辑 +int btn_edit_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + Ihandle *current_list = get_current_list(dlg); + int selected = IupGetInt(current_list, "VALUE"); + if (selected == 0) + return IUP_DEFAULT; + + StringList *raw_data = get_current_raw_data(dlg); + if (selected - 1 >= raw_data->count) + return IUP_DEFAULT; + + char buffer[PATH_BUFFER_SIZE]; + safe_strcpy(buffer, sizeof(buffer), string_list_get(raw_data, selected - 1)); + + if (custom_input_dialog("编辑环境变量", "编辑路径:", buffer, sizeof(buffer))) + { + if (strlen(buffer) > 0) + { + string_list_set(raw_data, selected - 1, buffer); + + sync_string_list_to_ui(current_list, raw_data); + IupSetInt(current_list, "VALUE", selected); + } + } + return IUP_DEFAULT; +} + +// 双击回调 +int list_dblclick_cb(Ihandle *self, int item, char *text) +{ + if (item > 0) + { + IupSetInt(self, "VALUE", item); + btn_edit_cb(self); + } + return IUP_DEFAULT; +} + +// 按钮回调:浏览 +int btn_browse_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + Ihandle *filedlg = IupFileDlg(); + IupSetAttribute(filedlg, "DIALOGTYPE", "DIR"); + IupSetAttribute(filedlg, "TITLE", lua_config_get_string("dialog", "select_dir")); + + IupPopup(filedlg, IUP_CENTER, IUP_CENTER); + + if (IupGetInt(filedlg, "STATUS") != -1) + { + char *value = IupGetAttribute(filedlg, "VALUE"); + if (value) + { + StringList *raw_data = get_current_raw_data(dlg); + add_string_list(raw_data, value); + + Ihandle *current_list = get_current_list(dlg); + sync_string_list_to_ui(current_list, raw_data); + + int count = IupGetInt(current_list, "COUNT"); + IupSetInt(current_list, "VALUE", count); + } + } + IupDestroy(filedlg); + return IUP_DEFAULT; +} + +// 按钮回调:删除 +int btn_del_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + Ihandle *current_list = get_current_list(dlg); + int selected = IupGetInt(current_list, "VALUE"); + + if (selected == 0) + { + IupMessage("提示", "请先选择要删除的项"); + return IUP_DEFAULT; + } + + StringList *raw_data = get_current_raw_data(dlg); + ErrorCode result = path_manager_remove_at(raw_data, selected - 1); + if (result != ERR_OK) + { + log_error("Failed to remove path at index %d", selected - 1); + } + + sync_string_list_to_ui(current_list, raw_data); + + Ihandle *lbl_status = IupGetDialogChild(dlg, CTRL_LBL_STATUS); + if (lbl_status) + IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "deleted")); + + return IUP_DEFAULT; +} diff --git a/src/controller/callbacks_io.c b/src/controller/callbacks_io.c new file mode 100644 index 0000000..9632812 --- /dev/null +++ b/src/controller/callbacks_io.c @@ -0,0 +1,176 @@ +#include "controller/callbacks.h" +#include "controller/callbacks_internal.h" +#include "core/app_context.h" +#include "core/import_export.h" +#include "core/lua_config.h" +#include "utils/string_ext.h" +#include "utils/safe_string.h" +#include "utils/error_code.h" +#include "utils/os_env.h" +#include "utils/logger.h" +#include "utils/ui_constants.h" +#include "ui/ui_utils.h" +#include +#include +#include + +// 按钮回调:导入 +int btn_import_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + AppContext *ctx = get_app_context_from_dlg(dlg); + if (!ctx) + return IUP_DEFAULT; + + if (!check_admin()) + { + IupMessage("错误", "需要管理员权限才能导入 PATH!"); + return IUP_DEFAULT; + } + + Ihandle *filedlg = IupFileDlg(); + IupSetAttribute(filedlg, "DIALOGTYPE", "OPEN"); + IupSetAttribute(filedlg, "TITLE", lua_config_get_string("label", "import_title")); + IupSetAttribute(filedlg, "FILTER", "json"); + IupSetAttribute(filedlg, "EXTFILTER", "JSON 文件 (*.json)|*.json|文本文件 (*.txt)|*.txt|所有文件 (*.*)|*.*"); + + IupPopup(filedlg, IUP_CENTER, IUP_CENTER); + + if (IupGetInt(filedlg, "STATUS") != -1) + { + char *filepath = IupGetAttribute(filedlg, "VALUE"); + if (filepath) + { + ExportData imported; + ErrorCode import_result = import_paths_from_file(filepath, &imported); + if (import_result == ERR_OK) + { + int has_system = imported.system.count > 0; + int has_user = imported.user.count > 0; + + if (!has_system && !has_user) + { + IupMessage("错误", "文件中没有找到有效的路径!"); + IupDestroy(filedlg); + return IUP_DEFAULT; + } + + int choice = 0; + if (has_system && has_user) + { + choice = IupAlarm("导入选项", "请选择导入目标:", + "仅系统变量", "仅用户变量", "全部导入"); + } + else if (has_system) + { + choice = 3; + } + else + { + choice = 2; + } + + int total_imported = 0; + + if (choice == 1 || choice == 3) + { + clear_string_list(&ctx->sys_paths); + for (int i = 0; i < imported.system.count; i++) + { + add_string_list(&ctx->sys_paths, imported.system.items[i]); + } + Ihandle *list_sys = IupGetDialogChild(dlg, CTRL_LIST_SYS); + sync_string_list_to_ui(list_sys, &ctx->sys_paths); + total_imported += imported.system.count; + } + + if (choice == 2 || choice == 3) + { + clear_string_list(&ctx->user_paths); + for (int i = 0; i < imported.user.count; i++) + { + add_string_list(&ctx->user_paths, imported.user.items[i]); + } + Ihandle *list_user = IupGetDialogChild(dlg, CTRL_LIST_USER); + sync_string_list_to_ui(list_user, &ctx->user_paths); + total_imported += imported.user.count; + } + + char msg[256]; + snprintf(msg, sizeof(msg), "成功导入 %d 个路径!", total_imported); + IupMessage("导入成功", msg); + + Ihandle *lbl_status = IupGetDialogChild(dlg, CTRL_LBL_STATUS); + if (lbl_status) + IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "loaded")); + } + else + { + log_error("Import failed: error code %d", import_result); + IupMessage("错误", "导入失败,请检查文件格式是否正确!"); + } + } + } + IupDestroy(filedlg); + return IUP_DEFAULT; +} + +// 按钮回调:导出 +int btn_export_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + AppContext *ctx = get_app_context_from_dlg(dlg); + if (!ctx) + return IUP_DEFAULT; + + ExportData data; + data.system = ctx->sys_paths; + data.user = ctx->user_paths; + + Ihandle *filedlg = IupFileDlg(); + IupSetAttribute(filedlg, "DIALOGTYPE", "SAVE"); + IupSetAttribute(filedlg, "TITLE", lua_config_get_string("label", "export_title")); + IupSetAttribute(filedlg, "FILTER", "json"); + IupSetAttribute(filedlg, "EXTFILTER", "JSON 文件 (*.json)|*.json"); + IupSetAttribute(filedlg, "DEFAULTEXT", "json"); + + char default_name[64]; + snprintf(default_name, sizeof(default_name), "path_all.json"); + IupSetAttribute(filedlg, "VALUE", default_name); + + IupPopup(filedlg, IUP_CENTER, IUP_CENTER); + + if (IupGetInt(filedlg, "STATUS") != -1) + { + char *filepath = IupGetAttribute(filedlg, "VALUE"); + if (filepath) + { + char final_path[MAX_PATH]; + if (strchr(filepath, '.') == NULL) + { + snprintf(final_path, sizeof(final_path), "%s.json", filepath); + } + else + { + safe_strcpy(final_path, sizeof(final_path), filepath); + } + filepath = final_path; + + ErrorCode export_result = export_paths_to_file(&data, filepath); + if (export_result == ERR_OK) + { + char msg[512]; + snprintf(msg, sizeof(msg), "成功导出!\n系统变量: %d 个\n用户变量: %d 个\n\n保存位置: %s", + data.system.count, data.user.count, filepath); + IupMessage("导出成功", msg); + } + else + { + log_error("Export failed: error code %d", export_result); + IupMessage("错误", "导出失败!"); + } + } + } + IupDestroy(filedlg); + return IUP_DEFAULT; +} diff --git a/src/controller/callbacks_nav.c b/src/controller/callbacks_nav.c new file mode 100644 index 0000000..f9407a7 --- /dev/null +++ b/src/controller/callbacks_nav.c @@ -0,0 +1,91 @@ +#include "controller/callbacks.h" +#include "controller/callbacks_internal.h" +#include "core/path_manager.h" +#include "core/lua_config.h" +#include "utils/error_code.h" +#include "utils/logger.h" +#include "utils/ui_constants.h" +#include "ui/ui_utils.h" +#include + +// 按钮回调:上移 +int btn_up_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + Ihandle *current_list = get_current_list(dlg); + int selected = IupGetInt(current_list, "VALUE"); + if (selected <= 1) + return IUP_DEFAULT; + + StringList *raw_data = get_current_raw_data(dlg); + ErrorCode result = path_manager_move_up(raw_data, selected - 1); + if (result != ERR_OK) + { + log_error("Failed to move path up at index %d", selected - 1); + } + + sync_string_list_to_ui(current_list, raw_data); + IupSetInt(current_list, "VALUE", selected - 1); + + return IUP_DEFAULT; +} + +// 按钮回调:下移 +int btn_down_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + Ihandle *current_list = get_current_list(dlg); + int selected = IupGetInt(current_list, "VALUE"); + StringList *raw_data = get_current_raw_data(dlg); + + if (selected == 0 || selected >= raw_data->count) + return IUP_DEFAULT; + + ErrorCode result = path_manager_move_down(raw_data, selected - 1); + if (result != ERR_OK) + { + log_error("Failed to move path down at index %d", selected - 1); + } + + sync_string_list_to_ui(current_list, raw_data); + IupSetInt(current_list, "VALUE", selected + 1); + + return IUP_DEFAULT; +} + +// 按钮回调:一键清理 +int btn_clean_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + StringList *raw_data = get_current_raw_data(dlg); + if (!raw_data || raw_data->count == 0) + return IUP_DEFAULT; + + if (IupAlarm("确认清理", "此操作将移除当前列表中所有【无效路径】和【重复路径】。\n确定要继续吗?", "确定", "取消", NULL) != 1) + { + return IUP_DEFAULT; + } + + int before_count = raw_data->count; + path_manager_clean(raw_data); + int removed = before_count - raw_data->count; + + Ihandle *current_list = get_current_list(dlg); + sync_string_list_to_ui(current_list, raw_data); + + char msg[128]; + snprintf(msg, sizeof(msg), "清理完成!共移除了 %d 个无效或重复路径。", removed); + IupMessage("提示", msg); + return IUP_DEFAULT; +} + +// 键盘按键回调 +int list_k_any_cb(Ihandle *self, int c) +{ + if (c == K_DEL) + { + btn_del_cb(self); + return IUP_IGNORE; + } + return IUP_DEFAULT; +} diff --git a/src/controller/callbacks_search.c b/src/controller/callbacks_search.c new file mode 100644 index 0000000..ccc2ad3 --- /dev/null +++ b/src/controller/callbacks_search.c @@ -0,0 +1,78 @@ +#include "controller/callbacks.h" +#include "controller/callbacks_internal.h" +#include "core/app_context.h" +#include "core/lua_config.h" +#include "utils/string_ext.h" +#include "utils/ui_constants.h" +#include "ui/ui_utils.h" +#include +#include + +// 搜索回调 +int txt_search_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + char *filter = IupGetAttribute(self, "VALUE"); + if (!filter) + return IUP_DEFAULT; + + Ihandle *current_list = get_current_list(dlg); + StringList *raw_data = get_current_raw_data(dlg); + + IupSetAttribute(current_list, "REMOVEITEM", "ALL"); + + int count = 0; + for (int i = 0; i < raw_data->count; i++) + { + const char *item = string_list_get(raw_data, i); + if (strlen(filter) == 0 || stristr(item, filter) != NULL) + { + count++; + IupSetAttributeId(current_list, "", count, item); + } + } + + IupSetInt(current_list, "COUNT", count); + refresh_single_list_style(current_list); + + return IUP_DEFAULT; +} + +// 拖拽回调 +int list_dropfiles_cb(Ihandle *self, const char *filename, int num, int x, int y) +{ + Ihandle *dlg = IupGetDialog(self); + Ihandle *current_list = self; + AppContext *ctx = get_app_context_from_dlg(dlg); + if (!ctx) + return IUP_DEFAULT; + + StringList *raw_data = NULL; + if (self == IupGetDialogChild(dlg, CTRL_LIST_SYS)) + raw_data = &ctx->sys_paths; + else if (self == IupGetDialogChild(dlg, CTRL_LIST_USER)) + raw_data = &ctx->user_paths; + else + return IUP_DEFAULT; + + DWORD attr = GetFileAttributesA(filename); + if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY)) + { + Ihandle *txt_search = IupGetDialogChild(dlg, CTRL_TXT_SEARCH); + if (txt_search) + IupSetAttribute(txt_search, "VALUE", ""); + + add_string_list(raw_data, filename); + sync_string_list_to_ui(current_list, raw_data); + + IupSetInt(current_list, "VALUE", raw_data->count); + } + else + { + Ihandle *lbl_status = IupGetDialogChild(dlg, CTRL_LBL_STATUS); + if (lbl_status) + IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "drag_folder_only")); + } + + return IUP_DEFAULT; +} diff --git a/src/controller/callbacks_sys.c b/src/controller/callbacks_sys.c new file mode 100644 index 0000000..879b014 --- /dev/null +++ b/src/controller/callbacks_sys.c @@ -0,0 +1,139 @@ +#include "controller/callbacks.h" +#include "controller/callbacks_internal.h" +#include "core/app_context.h" +#include "core/registry_service.h" +#include "core/lua_config.h" +#include "utils/error_code.h" +#include "utils/os_env.h" +#include "utils/logger.h" +#include "utils/i18n.h" +#include "utils/ui_constants.h" +#include "ui/ui_utils.h" +#include "ui/dialogs.h" +#include "ui/main_window.h" +#include +#include + +// 按钮回调:确定 (保存所有) +int btn_ok_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + AppContext *ctx = get_app_context_from_dlg(dlg); + if (!ctx) + return IUP_DEFAULT; + + if (!check_admin()) + { + IupMessage("错误", "需要管理员权限才能保存更改!"); + return IUP_DEFAULT; + } + + backup_registry(); + + ErrorCode sys_ok = save_system_paths(&ctx->sys_paths); + ErrorCode user_ok = save_user_paths(&ctx->user_paths); + + Ihandle *lbl_status = IupGetDialogChild(dlg, CTRL_LBL_STATUS); + + if (sys_ok == ERR_OK && user_ok == ERR_OK) + { + log_info("Saved system paths: %d, user paths: %d", ctx->sys_paths.count, ctx->user_paths.count); + SendMessageTimeoutW(HWND_BROADCAST, WM_SETTINGCHANGE, 0, (LPARAM)L"Environment", SMTO_ABORTIFHUNG, 5000, NULL); + IupMessage("成功", "系统和用户 PATH 环境变量均已更新!"); + if (lbl_status) + IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "saved")); + } + else if (sys_ok == ERR_OK) + { + IupMessage("提示", "系统变量保存成功,但用户变量保存失败。"); + } + else if (user_ok == ERR_OK) + { + IupMessage("提示", "用户变量保存成功,但系统变量保存失败。"); + } + else + { + log_error("Failed to save paths: sys=%d, user=%d", sys_ok, user_ok); + IupMessage("错误", "保存失败!"); + if (lbl_status) + IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "error")); + } + return IUP_DEFAULT; +} + +// 按钮回调:取消 +int btn_cancel_cb(Ihandle *self) +{ + IupExitLoop(); + return IUP_DEFAULT; +} + +// 载入所有路径 +void load_all_paths(void) +{ + Ihandle *dlg = get_main_dlg(); + if (!dlg) + return; + AppContext *ctx = get_app_context_from_dlg(dlg); + if (!ctx) + return; + + if (load_system_paths(&ctx->sys_paths) != ERR_OK) + { + log_error("Failed to load system paths"); + IupMessage("错误", "无法打开系统环境变量注册表键,请尝试以管理员身份运行。"); + } + else + { + log_info("Loaded system paths: %d", ctx->sys_paths.count); + } + + ErrorCode user_result = load_user_paths(&ctx->user_paths); + if (user_result == ERR_OK) + { + log_info("Loaded user paths: %d", ctx->user_paths.count); + } + + Ihandle *list_sys = IupGetDialogChild(dlg, CTRL_LIST_SYS); + Ihandle *list_user = IupGetDialogChild(dlg, CTRL_LIST_USER); + + sync_string_list_to_ui(list_sys, &ctx->sys_paths); + sync_string_list_to_ui(list_user, &ctx->user_paths); + + Ihandle *lbl_status = IupGetDialogChild(dlg, CTRL_LBL_STATUS); + if (lbl_status) + IupSetAttribute(lbl_status, "TITLE", lua_config_get_string("status", "loaded")); +} + +// 按钮回调:语言切换 +int btn_lang_cb(Ihandle *self) +{ + Ihandle *dlg = IupGetDialog(self); + language_select_dialog(); + refresh_main_window_ui(dlg); + return IUP_DEFAULT; +} + +// 按钮回调:帮助 +int btn_help_cb(Ihandle *self) +{ + IupMessage("使用说明", + "1. 本程序用于编辑系统环境变量 PATH。\n" + "2. 必须以【管理员身份】运行才能保存更改。\n" + "3. 操作说明:\n" + " - 新建:添加新路径到列表末尾。\n" + " - 编辑:修改选中的路径。\n" + " - 浏览:从文件系统选择目录添加。\n" + " - 删除:移除选中的路径。\n" + " - 上移/下移:调整路径优先级。\n" + " - 导入/导出:备份和恢复 PATH 配置。\n" + "4. 点击【确定】保存更改并生效。\n" + "5. 注意:某些正在运行的程序可能需要重启才能识别新的环境变量。\n\n" + "--------------------------------------------------\n" + "作者:LHY\n" + "邮箱:3364451258@qq.com\n" + "GitHub:https://github.com/LHY0125/PathEditor\n" + "记得给我的项目点个star!"); + + return IUP_DEFAULT; +} diff --git a/src/ui/ui_utils.c b/src/ui/ui_utils.c index c1075e6..8ec9dfc 100644 --- a/src/ui/ui_utils.c +++ b/src/ui/ui_utils.c @@ -61,7 +61,7 @@ void sync_string_list_to_ui(Ihandle *list_ui, const StringList *str_list) for (int i = 0; i < str_list->count; i++) { - IupSetAttributeId(list_ui, "", i + 1, str_list->items[i]); + IupSetAttributeId(list_ui, "", i + 1, string_list_get(str_list, i)); } IupSetInt(list_ui, "COUNT", str_list->count); diff --git a/src/utils/string_ext.c b/src/utils/string_ext.c index 6ea5080..6a3d772 100644 --- a/src/utils/string_ext.c +++ b/src/utils/string_ext.c @@ -85,6 +85,24 @@ void add_string_list(StringList *list, const char *str) list->count++; } +// 获取指定索引的字符串(只读) +const char *string_list_get(const StringList *list, int index) +{ + if (!list || index < 0 || index >= list->count) + return NULL; + return list->items[index]; +} + +// 设置指定索引的字符串(复制新字符串并释放旧字符串) +int string_list_set(StringList *list, int index, const char *str) +{ + if (!list || index < 0 || index >= list->count || !str) + return -1; + free(list->items[index]); + list->items[index] = _strdup(str); + return 0; +} + // 清空字符串列表 void clear_string_list(StringList *list) {