refactor: 重构项目结构,拆分回调函数并新增功能模块

- 将 callbacks.c 拆分为 cb_edit.c、cb_file.c、cb_main.c 三个模块,提高代码可维护性
- 新增 ui_utils.c 提供通用 UI 辅助函数
- 新增历史记录功能(撤销/重做)和主题切换支持
- 增加合并预览标签页,优化路径有效性检查和环境变量展开
- 更新 Makefile 以支持新的源文件结构
This commit is contained in:
2026-03-17 21:18:21 +08:00
parent 7db190306c
commit b35fac5358
20 changed files with 1453 additions and 714 deletions
+13 -4
View File
@@ -18,9 +18,9 @@ CFLAGS = -Wall -O2 -I$(INCLUDE_DIR) -I$(LOCAL_INCLUDE_DIR) -D_WIN32 -DUNICODE -D
LDFLAGS = -L$(LIB_DIR) -liup -liupcd -lgdi32 -lcomdlg32 -lcomctl32 -luuid -lole32 -ladvapi32 -mwindows
# Source
SRC = src/main.c src/utils.c src/registry.c src/callbacks.c src/ui.c
SRC = src/main.c src/utils.c src/registry.c src/ui.c src/ui_utils.c src/cb_edit.c src/cb_file.c src/cb_main.c
RES = ico/resources.rc
OBJ = $(OBJ_DIR)/main.o $(OBJ_DIR)/utils.o $(OBJ_DIR)/registry.o $(OBJ_DIR)/callbacks.o $(OBJ_DIR)/ui.o $(OBJ_DIR)/resources.o
OBJ = $(OBJ_DIR)/main.o $(OBJ_DIR)/utils.o $(OBJ_DIR)/registry.o $(OBJ_DIR)/ui.o $(OBJ_DIR)/ui_utils.o $(OBJ_DIR)/cb_edit.o $(OBJ_DIR)/cb_file.o $(OBJ_DIR)/cb_main.o $(OBJ_DIR)/resources.o
EXE = $(BIN_DIR)/PathEditor.exe
all: $(BIN_DIR) $(OBJ_DIR) $(EXE)
@@ -43,10 +43,19 @@ $(OBJ_DIR)/utils.o: src/utils.c
$(OBJ_DIR)/registry.o: src/registry.c
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJ_DIR)/callbacks.o: src/callbacks.c
$(OBJ_DIR)/ui.o: src/ui.c
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJ_DIR)/ui.o: src/ui.c
$(OBJ_DIR)/ui_utils.o: src/ui_utils.c
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJ_DIR)/cb_edit.o: src/cb_edit.c
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJ_DIR)/cb_file.o: src/cb_file.c
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJ_DIR)/cb_main.o: src/cb_main.c
$(CC) $(CFLAGS) -c -o $@ $<
$(OBJ_DIR)/resources.o: ico/resources.rc
Binary file not shown.
Binary file not shown.
-30
View File
@@ -1,30 +0,0 @@
#ifndef CALLBACKS_H
#define CALLBACKS_H
#include <iup.h>
// 按钮回调
int btn_new_cb(Ihandle *self);
int btn_edit_cb(Ihandle *self);
int btn_browse_cb(Ihandle *self);
int btn_del_cb(Ihandle *self);
int btn_up_cb(Ihandle *self);
int btn_down_cb(Ihandle *self);
int btn_clean_cb(Ihandle *self);
int btn_ok_cb(Ihandle *self);
int btn_cancel_cb(Ihandle *self);
int btn_help_cb(Ihandle *self);
// 搜索回调
int txt_search_cb(Ihandle *self);
// 双击回调
int list_dblclick_cb(Ihandle *self, int item, char *text);
// 拖拽回调
int list_dropfiles_cb(Ihandle *self, const char *filename, int num, int x, int y);
// 键盘按键回调
int list_k_any_cb(Ihandle *self, int c);
#endif // CALLBACKS_H
+15
View File
@@ -0,0 +1,15 @@
#ifndef CB_EDIT_H
#define CB_EDIT_H
#include <iup.h>
// 编辑相关回调
int btn_new_cb(Ihandle *self);
int btn_edit_cb(Ihandle *self);
int list_dblclick_cb(Ihandle *self, int item, char *text);
int btn_del_cb(Ihandle *self);
int btn_up_cb(Ihandle *self);
int btn_down_cb(Ihandle *self);
int btn_clean_cb(Ihandle *self);
#endif // CB_EDIT_H
+14
View File
@@ -0,0 +1,14 @@
#ifndef CB_FILE_H
#define CB_FILE_H
#include <iup.h>
// 文件和历史记录回调
int btn_browse_cb(Ihandle *self);
int btn_undo_cb(Ihandle *self);
int btn_redo_cb(Ihandle *self);
int btn_export_cb(Ihandle *self);
int btn_import_cb(Ihandle *self);
int list_dropfiles_cb(Ihandle *self, const char *filename, int num, int x, int y);
#endif // CB_FILE_H
+17
View File
@@ -0,0 +1,17 @@
#ifndef CB_MAIN_H
#define CB_MAIN_H
#include <iup.h>
// 主界面交互回调
int txt_search_cb(Ihandle *self);
int list_k_any_cb(Ihandle *self, int c);
int list_motion_cb(Ihandle *self, int x, int y, char *status);
int dialog_k_any_cb(Ihandle *self, int c);
int btn_ok_cb(Ihandle *self);
int btn_cancel_cb(Ihandle *self);
int btn_help_cb(Ihandle *self);
int tabs_tabchange_cb(Ihandle *self, int new_pos, int old_pos);
int btn_theme_cb(Ihandle *self);
#endif // CB_MAIN_H
+32
View File
@@ -13,6 +13,7 @@ extern Ihandle *dlg; // 主对话框句柄
extern Ihandle *tabs_main; // 标签页容器
extern Ihandle *list_sys; // 系统变量列表
extern Ihandle *list_user; // 用户变量列表
extern Ihandle *list_merged; // 合并变量列表
extern Ihandle *lbl_status; // 状态标签句柄
extern Ihandle *btn_new; // 新增按钮句柄
extern Ihandle *btn_edit; // 编辑按钮句柄
@@ -21,7 +22,13 @@ extern Ihandle *btn_del; // 删除按钮句柄
extern Ihandle *btn_up; // 上移按钮句柄
extern Ihandle *btn_down; // 下移按钮句柄
extern Ihandle *btn_clean; // 一键清理按钮句柄
extern Ihandle *btn_undo; // 撤销按钮句柄
extern Ihandle *btn_redo; // 重做按钮句柄
extern Ihandle *btn_import; // 导入按钮句柄
extern Ihandle *btn_export; // 导出按钮句柄
extern Ihandle *btn_theme; // 主题切换按钮句柄
extern Ihandle *btn_ok; // 确认按钮句柄
extern int is_dark_mode; // 深色模式状态
extern Ihandle *btn_cancel; // 取消按钮句柄
extern Ihandle *btn_help; // 帮助按钮句柄
extern Ihandle *txt_search; // 搜索框
@@ -36,9 +43,34 @@ typedef struct {
extern StringList raw_sys_paths;
extern StringList raw_user_paths;
// 历史记录节点
typedef struct HistoryNode {
StringList sys_paths;
StringList user_paths;
struct HistoryNode *next;
} HistoryNode;
// 历史记录栈
typedef struct {
HistoryNode *top;
int count;
} HistoryStack;
extern HistoryStack undo_stack;
extern HistoryStack redo_stack;
extern Ihandle *btn_undo;
extern Ihandle *btn_redo;
// 缓存操作函数声明
void init_string_list(StringList *list);
void add_string_list(StringList *list, const char *str);
void clear_string_list(StringList *list);
void copy_string_list(StringList *dest, StringList *src);
// 历史记录操作
void init_history_stack(HistoryStack *stack);
void push_history(HistoryStack *stack, StringList *sys, StringList *user);
int pop_history(HistoryStack *stack, StringList *out_sys, StringList *out_user);
void clear_history_stack(HistoryStack *stack);
#endif // GLOBALS_H
+2
View File
@@ -12,4 +12,6 @@ Ihandle *create_main_buttons();
// 创建底部按钮区域
Ihandle *create_bottom_buttons();
Ihandle *create_main_dialog();
#endif // UI_H
+18
View File
@@ -0,0 +1,18 @@
#ifndef UI_UTILS_H
#define UI_UTILS_H
#include <iup.h>
#include "globals.h"
// 辅助函数声明
int get_first_selected_index(Ihandle *list);
void set_single_selection(Ihandle *list, int index);
void refresh_ui_from_raw(Ihandle *list, StringList *raw);
void record_history();
int custom_input_dialog(const char *title, const char *label_text, char *buffer, int buffer_size);
Ihandle *get_current_list();
void remove_from_raw_data(StringList *list, const char *str);
void toggle_edit_buttons(int enable);
void apply_theme();
#endif // UI_UTILS_H
+3
View File
@@ -14,6 +14,9 @@ wchar_t* utf8_to_wide(const char* str);
// 检查管理员权限
int check_admin();
// 展开环境变量
char* expand_env_vars(const char* path);
// 检查路径是否有效(存在且为目录)
int is_path_valid(const char *path);
-547
View File
@@ -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");
// 按钮响应
// 这是一个简单的 hackIupPopup 的参数 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"
"GitHubhttps://github.com/LHY0125/PathEditor\n"
"记得给我的项目点个star");
return IUP_DEFAULT;
}
+368
View File
@@ -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
View File
@@ -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
View File
@@ -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"
"GitHubhttps://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
View File
@@ -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
View File
@@ -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", "状态: 已加载变量");
}
// 内部辅助函数:保存单个列表
+96 -12
View File
@@ -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,6 +63,8 @@ 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,
@@ -65,23 +78,94 @@ Ihandle *create_main_buttons()
// 创建底部按钮区域
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
View File
@@ -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);
}
+113 -7
View File
@@ -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(expanded_path);
free(expanded_path);
wchar_t *wpath = utf8_to_wide(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,12 +139,18 @@ 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
{
if (i % 2 == 0)
IupSetAttributeId(list, "ITEMBGCOLOR", i, "245 245 245");
else
IupSetAttributeId(list, "ITEMBGCOLOR", i, "255 255 255");
}
}
@@ -231,3 +268,72 @@ void clear_string_list(StringList *list)
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;
}