mirror of
https://github.com/LHY0125/PathEditor.git
synced 2026-05-10 02:09:46 +08:00
refactor: 重构项目结构,拆分回调函数并新增功能模块
- 将 callbacks.c 拆分为 cb_edit.c、cb_file.c、cb_main.c 三个模块,提高代码可维护性 - 新增 ui_utils.c 提供通用 UI 辅助函数 - 新增历史记录功能(撤销/重做)和主题切换支持 - 增加合并预览标签页,优化路径有效性检查和环境变量展开 - 更新 Makefile 以支持新的源文件结构
This commit is contained in:
-547
@@ -1,547 +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" 会被返回
|
||||
IupButton("取消", "0"), // "0" 会被返回
|
||||
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;
|
||||
}
|
||||
|
||||
// 辅助函数:获取当前选中的列表
|
||||
Ihandle *get_current_list()
|
||||
{
|
||||
// 获取当前选中的 Tab 索引
|
||||
// 注意:IupTabs 的 VALUE 属性在某些版本可能返回 handle,某些版本返回 pos
|
||||
// 这里使用 IupGetInt(tabs_main, "VALUEPOS") 更稳妥
|
||||
int pos = IupGetInt(tabs_main, "VALUEPOS");
|
||||
if (pos == 0)
|
||||
return list_sys;
|
||||
if (pos == 1)
|
||||
return list_user;
|
||||
return list_sys; // 默认
|
||||
}
|
||||
|
||||
// 按钮回调:新建
|
||||
int btn_new_cb(Ihandle *self)
|
||||
{
|
||||
char buffer[1024] = "";
|
||||
if (custom_input_dialog("新建环境变量", "请输入路径:", buffer, sizeof(buffer)))
|
||||
{
|
||||
if (strlen(buffer) > 0)
|
||||
{
|
||||
Ihandle *current_list = get_current_list();
|
||||
int count = IupGetInt(current_list, "COUNT");
|
||||
count++;
|
||||
IupSetAttributeId(current_list, "", count, buffer);
|
||||
IupSetInt(current_list, "COUNT", count);
|
||||
IupSetInt(current_list, "VALUE", count);
|
||||
|
||||
refresh_single_list_style(current_list);
|
||||
}
|
||||
}
|
||||
return IUP_DEFAULT;
|
||||
}
|
||||
|
||||
// 按钮回调:编辑
|
||||
int btn_edit_cb(Ihandle *self)
|
||||
{
|
||||
Ihandle *current_list = get_current_list();
|
||||
int selected = IupGetInt(current_list, "VALUE");
|
||||
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)
|
||||
{
|
||||
IupSetAttributeId(current_list, "", selected, buffer);
|
||||
refresh_single_list_style(current_list);
|
||||
}
|
||||
}
|
||||
return IUP_DEFAULT;
|
||||
}
|
||||
|
||||
// 双击回调
|
||||
int list_dblclick_cb(Ihandle *self, int item, char *text)
|
||||
{
|
||||
// 这里的 self 就是触发双击的列表控件
|
||||
if (item > 0)
|
||||
{
|
||||
// 选中该行
|
||||
IupSetInt(self, "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)
|
||||
{
|
||||
Ihandle *current_list = get_current_list();
|
||||
int count = IupGetInt(current_list, "COUNT");
|
||||
count++;
|
||||
IupSetAttributeId(current_list, "", count, value);
|
||||
IupSetInt(current_list, "COUNT", count);
|
||||
IupSetInt(current_list, "VALUE", count);
|
||||
|
||||
refresh_single_list_style(current_list);
|
||||
}
|
||||
}
|
||||
IupDestroy(filedlg);
|
||||
return IUP_DEFAULT;
|
||||
}
|
||||
|
||||
// 辅助函数:从 raw_data 中删除指定字符串
|
||||
static 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; // 假设没有重复,只删除第一个匹配
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 按钮回调:删除
|
||||
int btn_del_cb(Ihandle *self)
|
||||
{
|
||||
Ihandle *current_list = get_current_list();
|
||||
int selected = IupGetInt(current_list, "VALUE");
|
||||
|
||||
if (selected == 0)
|
||||
{
|
||||
IupMessage("提示", "请先选择要删除的项");
|
||||
return IUP_DEFAULT;
|
||||
}
|
||||
|
||||
// 获取当前要删除的内容
|
||||
char *val = IupGetAttributeId(current_list, "", selected);
|
||||
|
||||
// 确认删除
|
||||
// char msg[1024];
|
||||
// snprintf(msg, sizeof(msg), "确定要删除以下路径吗?\n\n%s", val ? val : "(空)");
|
||||
// if (IupAlarm("确认删除", msg, "是", "否", NULL) != 1)
|
||||
// return IUP_DEFAULT;
|
||||
|
||||
// 从 raw_data 缓存中同步删除
|
||||
int pos = IupGetInt(tabs_main, "VALUEPOS");
|
||||
StringList *raw_data = (pos == 0) ? &raw_sys_paths : &raw_user_paths;
|
||||
|
||||
// 注意:必须先保存 val 的副本,因为 REMOVEITEM 可能会导致 val 指针失效(如果它是指向列表内部缓冲区的)
|
||||
char *val_copy = val ? _strdup(val) : NULL;
|
||||
|
||||
// 先从界面删除
|
||||
// IupSetAttribute(current_list, "REMOVEITEM", "SELECTED");
|
||||
// 改为按索引删除,防止失去焦点导致 SELECTED 失效
|
||||
IupSetInt(current_list, "REMOVEITEM", selected);
|
||||
|
||||
// 再从缓存删除
|
||||
if (val_copy && raw_data)
|
||||
{
|
||||
remove_from_raw_data(raw_data, val_copy);
|
||||
free(val_copy);
|
||||
}
|
||||
|
||||
// 重新刷新
|
||||
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();
|
||||
int selected = IupGetInt(current_list, "VALUE");
|
||||
if (selected <= 1)
|
||||
return IUP_DEFAULT; // 已经是第一个或未选中
|
||||
|
||||
char *current = IupGetAttributeId(current_list, "", selected);
|
||||
char *prev = IupGetAttributeId(current_list, "", 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(current_list, "", selected, buf_prev);
|
||||
IupSetAttributeId(current_list, "", selected - 1, buf_curr);
|
||||
|
||||
IupSetInt(current_list, "VALUE", selected - 1);
|
||||
|
||||
// 刷新样式(虽然颜色不需要变,但为了保险)
|
||||
refresh_single_list_style(current_list);
|
||||
return IUP_DEFAULT;
|
||||
}
|
||||
|
||||
// 按钮回调:下移
|
||||
int btn_down_cb(Ihandle *self)
|
||||
{
|
||||
Ihandle *current_list = get_current_list();
|
||||
int selected = IupGetInt(current_list, "VALUE");
|
||||
int count = IupGetInt(current_list, "COUNT");
|
||||
if (selected == 0 || selected >= count)
|
||||
return IUP_DEFAULT;
|
||||
|
||||
char *current = IupGetAttributeId(current_list, "", selected);
|
||||
char *next = IupGetAttributeId(current_list, "", 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(current_list, "", selected, buf_next);
|
||||
IupSetAttributeId(current_list, "", selected + 1, buf_curr);
|
||||
|
||||
IupSetInt(current_list, "VALUE", selected + 1);
|
||||
|
||||
refresh_single_list_style(current_list);
|
||||
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;
|
||||
}
|
||||
|
||||
// 从后往前遍历删除,避免索引错位
|
||||
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)
|
||||
{
|
||||
IupSetAttributeId(current_list, "REMOVEITEM", i, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
refresh_single_list_style(current_list);
|
||||
IupMessage("提示", "清理完成!");
|
||||
return IUP_DEFAULT;
|
||||
}
|
||||
|
||||
// 搜索回调
|
||||
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_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))
|
||||
{
|
||||
// 如果正在搜索,先清空搜索框
|
||||
IupSetAttribute(txt_search, "VALUE", "");
|
||||
|
||||
// 添加到列表末尾
|
||||
int count = IupGetInt(current_list, "COUNT");
|
||||
count++;
|
||||
IupSetAttributeId(current_list, "", count, filename);
|
||||
IupSetInt(current_list, "COUNT", count);
|
||||
IupSetInt(current_list, "VALUE", 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;
|
||||
}
|
||||
|
||||
// 键盘按键回调
|
||||
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 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"
|
||||
"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;
|
||||
}
|
||||
+43
-94
@@ -1,137 +1,86 @@
|
||||
#include <windows.h>
|
||||
#include <iup.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <wchar.h>
|
||||
#include "globals.h"
|
||||
#include "utils.h"
|
||||
#include "registry.h"
|
||||
#include "callbacks.h"
|
||||
#include "ui.h"
|
||||
#include "cb_main.h"
|
||||
|
||||
// 定义 Windows 消息常量
|
||||
#ifndef WM_COPYGLOBALDATA
|
||||
#define WM_COPYGLOBALDATA 0x0049
|
||||
#endif
|
||||
// 全局控件定义
|
||||
Ihandle *dlg; // 主对话框
|
||||
Ihandle *tabs_main; // 主选项卡
|
||||
Ihandle *list_sys, *list_user, *list_merged; // 列表控件
|
||||
Ihandle *lbl_status; // 状态栏
|
||||
Ihandle *btn_new, *btn_edit, *btn_browse, *btn_del, *btn_up, *btn_down; // 右侧按钮
|
||||
Ihandle *btn_undo, *btn_redo; // 撤销重做按钮
|
||||
Ihandle *btn_import, *btn_export; // 导入导出按钮
|
||||
Ihandle *btn_ok, *btn_cancel, *btn_help; // 确认取消帮助按钮
|
||||
Ihandle *btn_clean; // 一键清理按钮
|
||||
Ihandle *btn_theme; // 主题切换按钮
|
||||
Ihandle *txt_search; // 搜索框
|
||||
|
||||
#ifndef MSGFLT_ADD
|
||||
#define MSGFLT_ADD 1
|
||||
#endif
|
||||
// 历史记录栈
|
||||
HistoryStack undo_stack = {0};
|
||||
HistoryStack redo_stack = {0};
|
||||
|
||||
// 全局变量定义
|
||||
StringList raw_sys_paths = {0};
|
||||
StringList raw_user_paths = {0};
|
||||
|
||||
// 全局控件定义
|
||||
Ihandle *dlg, *tabs_main, *list_sys, *list_user, *lbl_status; // 主窗口、标签页、系统路径列表、用户路径列表、状态标签
|
||||
Ihandle *btn_new, *btn_edit, *btn_browse, *btn_del, *btn_up, *btn_down; // 右侧按钮
|
||||
Ihandle *btn_ok, *btn_cancel, *btn_help; // 确认取消帮助按钮
|
||||
Ihandle *btn_clean; // 一键清理按钮
|
||||
Ihandle *txt_search; // 搜索框
|
||||
int is_dark_mode = 0; // 默认浅色模式
|
||||
|
||||
// 主函数
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
// 强制设置 UTF8MODE 环境变量,必须在 IupOpen 之前
|
||||
putenv("IUP_UTF8MODE=YES");
|
||||
// 初始化 IUP
|
||||
if (IupOpen(&argc, &argv) == IUP_ERROR)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
IupOpen(&argc, &argv);
|
||||
// 开启 UTF-8 支持
|
||||
IupSetGlobal("UTF8MODE", "YES");
|
||||
|
||||
// 在管理员模式下,解决无法拖拽文件到列表框的问题 (UIPI)
|
||||
// 需要加载 User32.dll 获取 ChangeWindowMessageFilter 函数
|
||||
HMODULE hUser32 = LoadLibraryW(L"user32.dll");
|
||||
// 启用 UIPI 绕过,允许拖拽
|
||||
HMODULE hUser32 = LoadLibraryA("user32.dll");
|
||||
if (hUser32)
|
||||
{
|
||||
typedef BOOL(WINAPI * ChangeWindowMessageFilterProc)(UINT, DWORD);
|
||||
ChangeWindowMessageFilterProc pChangeWindowMessageFilter =
|
||||
(ChangeWindowMessageFilterProc)GetProcAddress(hUser32, "ChangeWindowMessageFilter");
|
||||
|
||||
ChangeWindowMessageFilterProc pChangeWindowMessageFilter = (ChangeWindowMessageFilterProc)GetProcAddress(hUser32, "ChangeWindowMessageFilter");
|
||||
if (pChangeWindowMessageFilter)
|
||||
{
|
||||
pChangeWindowMessageFilter(WM_DROPFILES, MSGFLT_ADD);
|
||||
pChangeWindowMessageFilter(WM_COPYDATA, MSGFLT_ADD);
|
||||
pChangeWindowMessageFilter(WM_COPYGLOBALDATA, MSGFLT_ADD);
|
||||
// WM_DROPFILES = 0x0233, WM_COPYDATA = 0x004A, MSGFLT_ADD = 1
|
||||
pChangeWindowMessageFilter(0x0233, 1);
|
||||
pChangeWindowMessageFilter(0x004A, 1);
|
||||
}
|
||||
FreeLibrary(hUser32);
|
||||
}
|
||||
|
||||
// 创建两个列表控件
|
||||
list_sys = create_path_list();
|
||||
list_user = create_path_list();
|
||||
// 初始化历史栈
|
||||
init_history_stack(&undo_stack);
|
||||
init_history_stack(&redo_stack);
|
||||
|
||||
// 创建搜索框
|
||||
txt_search = IupText(NULL);
|
||||
IupSetAttribute(txt_search, "EXPAND", "HORIZONTAL");
|
||||
IupSetAttribute(txt_search, "CUEBANNER", "输入关键词搜索...");
|
||||
IupSetCallback(txt_search, "VALUECHANGED_CB", (Icallback)txt_search_cb);
|
||||
// 创建主界面
|
||||
dlg = create_main_dialog();
|
||||
|
||||
// 创建 Tabs
|
||||
tabs_main = IupTabs(
|
||||
IupVbox(list_sys, NULL),
|
||||
IupVbox(list_user, NULL),
|
||||
NULL);
|
||||
IupSetAttribute(tabs_main, "TABTITLE0", "系统变量 (System)");
|
||||
IupSetAttribute(tabs_main, "TABTITLE1", "用户变量 (User)");
|
||||
IupSetAttribute(tabs_main, "TABTYPE", "TOP");
|
||||
|
||||
// 创建右侧按钮区域
|
||||
Ihandle *vbox_btns = create_main_buttons();
|
||||
|
||||
// 上部布局:Tabs + 按钮
|
||||
Ihandle *hbox_main = IupHbox(tabs_main, vbox_btns, NULL);
|
||||
IupSetAttribute(hbox_main, "GAP", "10");
|
||||
IupSetAttribute(hbox_main, "MARGIN", "10x10");
|
||||
|
||||
// 状态标签
|
||||
lbl_status = IupLabel("状态: 就绪");
|
||||
IupSetAttribute(lbl_status, "EXPAND", "HORIZONTAL");
|
||||
|
||||
// 创建底部按钮区域
|
||||
Ihandle *hbox_bottom = create_bottom_buttons();
|
||||
|
||||
// 总体布局
|
||||
Ihandle *vbox_all = IupVbox(
|
||||
IupLabel("环境变量编辑器:"),
|
||||
txt_search,
|
||||
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", "500x400"); // 稍微调大一点
|
||||
IupSetAttribute(dlg, "MINBOX", "NO");
|
||||
IupSetAttribute(dlg, "MAXBOX", "NO");
|
||||
// 设置全局按键回调 (如果在 ui.c 中未设置)
|
||||
IupSetCallback(dlg, "K_ANY", (Icallback)dialog_k_any_cb);
|
||||
|
||||
// 加载数据
|
||||
if (!check_admin())
|
||||
{
|
||||
IupMessage("警告", "程序未以管理员身份运行,您只能查看,无法保存更改!");
|
||||
IupSetAttribute(dlg, "TITLE", "编辑环境变量 (只读模式)");
|
||||
IupSetAttribute(lbl_status, "TITLE", "状态: 只读模式 (权限不足)");
|
||||
|
||||
// 禁用修改类按钮
|
||||
IupSetAttribute(btn_new, "ACTIVE", "NO");
|
||||
IupSetAttribute(btn_edit, "ACTIVE", "NO");
|
||||
IupSetAttribute(btn_browse, "ACTIVE", "NO");
|
||||
IupSetAttribute(btn_del, "ACTIVE", "NO");
|
||||
IupSetAttribute(btn_up, "ACTIVE", "NO");
|
||||
IupSetAttribute(btn_down, "ACTIVE", "NO");
|
||||
IupSetAttribute(btn_clean, "ACTIVE", "NO");
|
||||
IupSetAttribute(btn_ok, "ACTIVE", "NO");
|
||||
IupMessage("警告", "未检测到管理员权限!\n您可能无法保存更改。\n请右键以【管理员身份运行】。");
|
||||
}
|
||||
|
||||
IupShowXY(dlg, IUP_CENTER, IUP_CENTER);
|
||||
|
||||
// IUP List APPEND 属性需要在控件 Map 之后才能生效
|
||||
// IupShowXY 会触发 Map
|
||||
load_all_paths();
|
||||
|
||||
// 显示对话框
|
||||
IupShowXY(dlg, IUP_CENTER, IUP_CENTER);
|
||||
|
||||
// 进入主循环
|
||||
IupMainLoop();
|
||||
|
||||
// 清理资源
|
||||
IupClose();
|
||||
return 0;
|
||||
}
|
||||
+1
-1
@@ -84,7 +84,7 @@ void load_all_paths()
|
||||
load_single_path(HKEY_CURRENT_USER, REG_PATH_USER, list_user, &raw_user_paths);
|
||||
|
||||
refresh_list_style();
|
||||
IupSetAttribute(lbl_status, "TITLE", "状态: 已加载系统和用户变量");
|
||||
IupSetAttribute(lbl_status, "TITLE", "状态: 已加载变量");
|
||||
}
|
||||
|
||||
// 内部辅助函数:保存单个列表
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
#include "ui.h"
|
||||
#include "globals.h"
|
||||
#include "callbacks.h"
|
||||
#include "ui_utils.h"
|
||||
#include "cb_edit.h"
|
||||
#include "cb_file.h"
|
||||
#include "cb_main.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
// 创建列表控件
|
||||
@@ -8,6 +11,7 @@ Ihandle *create_path_list()
|
||||
{
|
||||
Ihandle *list = IupFlatList();
|
||||
IupSetAttribute(list, "EXPAND", "YES");
|
||||
IupSetAttribute(list, "MULTIPLE", "YES");
|
||||
IupSetAttribute(list, "ITEMPADDING", "5x5");
|
||||
IupSetAttribute(list, "BACKCOLOR", "255 255 255");
|
||||
IupSetAttribute(list, "BORDER", "YES");
|
||||
@@ -16,6 +20,7 @@ Ihandle *create_path_list()
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -27,6 +32,8 @@ Ihandle *create_main_buttons()
|
||||
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);
|
||||
@@ -36,6 +43,8 @@ Ihandle *create_main_buttons()
|
||||
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);
|
||||
@@ -45,6 +54,8 @@ Ihandle *create_main_buttons()
|
||||
IupSetAttribute(btn_edit, "RASTERSIZE", "100x32");
|
||||
IupSetAttribute(btn_browse, "RASTERSIZE", "100x32");
|
||||
IupSetAttribute(btn_del, "RASTERSIZE", "100x32");
|
||||
IupSetAttribute(btn_undo, "RASTERSIZE", "100x32");
|
||||
IupSetAttribute(btn_redo, "RASTERSIZE", "100x32");
|
||||
IupSetAttribute(btn_up, "RASTERSIZE", "100x32");
|
||||
IupSetAttribute(btn_down, "RASTERSIZE", "100x32");
|
||||
IupSetAttribute(btn_clean, "RASTERSIZE", "100x32");
|
||||
@@ -52,36 +63,109 @@ Ihandle *create_main_buttons()
|
||||
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", "5");
|
||||
IupSetAttribute(vbox_btns, "MARGIN", "0x0");
|
||||
|
||||
|
||||
return vbox_btns;
|
||||
}
|
||||
|
||||
// 创建底部按钮区域
|
||||
Ihandle *create_bottom_buttons()
|
||||
{
|
||||
// 底部按钮
|
||||
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);
|
||||
// 创建底部按钮
|
||||
btn_help = IupButton("帮助(H)", NULL);
|
||||
IupSetCallback(btn_help, "ACTION", (Icallback)btn_help_cb);
|
||||
IupSetAttribute(btn_help, "RASTERSIZE", "80x32");
|
||||
|
||||
btn_theme = IupButton("深色模式", NULL);
|
||||
IupSetCallback(btn_theme, "ACTION", (Icallback)btn_theme_cb);
|
||||
IupSetAttribute(btn_theme, "RASTERSIZE", "80x32");
|
||||
|
||||
lbl_status = IupLabel("就绪");
|
||||
IupSetAttribute(lbl_status, "EXPAND", "HORIZONTAL");
|
||||
|
||||
btn_import = IupButton("导入配置", NULL);
|
||||
IupSetCallback(btn_import, "ACTION", (Icallback)btn_import_cb);
|
||||
IupSetAttribute(btn_import, "RASTERSIZE", "100x32");
|
||||
|
||||
btn_export = IupButton("导出配置", NULL);
|
||||
IupSetCallback(btn_export, "ACTION", (Icallback)btn_export_cb);
|
||||
IupSetAttribute(btn_export, "RASTERSIZE", "100x32");
|
||||
|
||||
btn_ok = IupButton("确定(O)", NULL);
|
||||
IupSetCallback(btn_ok, "ACTION", (Icallback)btn_ok_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);
|
||||
btn_cancel = IupButton("取消(C)", NULL);
|
||||
IupSetCallback(btn_cancel, "ACTION", (Icallback)btn_cancel_cb);
|
||||
IupSetAttribute(btn_cancel, "RASTERSIZE", "100x32");
|
||||
|
||||
Ihandle *hbox_bottom = IupHbox(
|
||||
btn_help,
|
||||
btn_theme,
|
||||
lbl_status,
|
||||
IupFill(),
|
||||
btn_import,
|
||||
btn_export,
|
||||
btn_ok,
|
||||
btn_cancel,
|
||||
NULL);
|
||||
IupSetAttribute(hbox_bottom, "GAP", "10");
|
||||
IupSetAttribute(hbox_bottom, "MARGIN", "10x10");
|
||||
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", "240 240 240"); // 灰色背景
|
||||
|
||||
// 创建标签页
|
||||
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", "10");
|
||||
IupSetAttribute(hbox_mid, "MARGIN", "0x0");
|
||||
|
||||
Ihandle *bottom = create_bottom_buttons();
|
||||
|
||||
Ihandle *vbox_main = IupVbox(txt_search, hbox_mid, bottom, NULL);
|
||||
IupSetAttribute(vbox_main, "GAP", "10");
|
||||
IupSetAttribute(vbox_main, "MARGIN", "10x10");
|
||||
|
||||
Ihandle *dlg = IupDialog(vbox_main);
|
||||
IupSetAttribute(dlg, "TITLE", "Path Editor");
|
||||
IupSetAttribute(dlg, "SIZE", "480x400");
|
||||
|
||||
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);
|
||||
}
|
||||
+114
-8
@@ -42,14 +42,41 @@ int check_admin()
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 展开环境变量
|
||||
char* expand_env_vars(const char* 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)
|
||||
{
|
||||
// 如果包含 %,说明是变量,无法直接检查存在性,默认视为有效
|
||||
if (strchr(path, '%'))
|
||||
return 1;
|
||||
char *expanded_path = expand_env_vars(path);
|
||||
if (!expanded_path)
|
||||
return 0;
|
||||
|
||||
wchar_t *wpath = utf8_to_wide(path);
|
||||
wchar_t *wpath = utf8_to_wide(expanded_path);
|
||||
free(expanded_path);
|
||||
|
||||
if (!wpath)
|
||||
return 0;
|
||||
|
||||
@@ -84,7 +111,11 @@ void refresh_single_list_style(Ihandle *list)
|
||||
continue;
|
||||
|
||||
// 默认颜色:黑字
|
||||
char fg_color[32] = "0 0 0";
|
||||
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))
|
||||
@@ -108,13 +139,19 @@ void refresh_single_list_style(Ihandle *list)
|
||||
IupSetAttributeId(list, "ITEMFGCOLOR", i, fg_color);
|
||||
|
||||
// 斑马纹背景
|
||||
if (i % 2 == 0)
|
||||
if (is_dark_mode)
|
||||
{
|
||||
IupSetAttributeId(list, "ITEMBGCOLOR", i, "245 245 245");
|
||||
if (i % 2 == 0)
|
||||
IupSetAttributeId(list, "ITEMBGCOLOR", i, "60 60 60");
|
||||
else
|
||||
IupSetAttributeId(list, "ITEMBGCOLOR", i, "50 50 50");
|
||||
}
|
||||
else
|
||||
{
|
||||
IupSetAttributeId(list, "ITEMBGCOLOR", i, "255 255 255");
|
||||
if (i % 2 == 0)
|
||||
IupSetAttributeId(list, "ITEMBGCOLOR", i, "245 245 245");
|
||||
else
|
||||
IupSetAttributeId(list, "ITEMBGCOLOR", i, "255 255 255");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -230,4 +267,73 @@ void clear_string_list(StringList *list)
|
||||
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