// // Created by HuangXin on 2023/4/26. // #include #include "main.h" #include "misc.h" #include "zlog_module.h" #include "dhcp_network.h" #include "sds/sds.h" #include "dhcp_options.h" #include "user_errno.h" typedef struct { U8 op_code; U8 op_size; U8 op_value[256]; } STR_OPT; static GtkWidget *g_detailWnd = NULL; static GtkWidget *g_statusBar = NULL; static GtkTextBuffer *g_pTxtBuf[4]; static GtkWidget *g_ptvDHcp[4]; static GtkWidget *g_pSelVal[4]; static PBUF_INFO g_HexBuf[4]; static PDHCP_INFO g_preInfo = NULL; static U32 g_curPage = 0; static const char *g_mess_info[] = {"NONE", "DISCOVER", "OFFER", "REQUEST", "DECLINE", "ACK", "NAK", "RELEASE", "INFORM"}; static gboolean delete_event(GtkWidget *widget, GdkEventAny *UNUSED(event)) { /* 如果你的 "delete_event" 信号处理函数返回 FALSE,GTK 会发出 "destroy" 信号。 * 返回 TRUE,你不希望关闭窗口。 * 当你想弹出“你确定要退出吗?”对话框时它很有用。*/ //gtk_widget_hide_all(window_test); gtk_widget_hide(widget); return TRUE; //注意必须为TRUE,否则子窗口点击关闭按钮后,就摧毁了,而不是隐藏了。 } static void add_dhcp_tree_colums(GtkWidget *treeView) { GList *cols = gtk_tree_view_get_columns(GTK_TREE_VIEW(treeView)); if (g_list_length(cols) == 0) { GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes("协议", renderer, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(treeView), column); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes("值", renderer, "text", 1, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(treeView), column); } } #define ADD_SUB_STRING(name, s) \ do { \ gtk_tree_store_append(store, &iterSub, &iter); \ gtk_tree_store_set(store, &iterSub, 0, (name), 1, (s), -1); \ } while (0) #define ADD_SUB_INT(name, fmt, v) \ do { \ gtk_tree_store_append(store, &iterSub, &iter); \ s = sdsempty(); \ sprintf(s, (fmt), (v)); \ gtk_tree_store_set(store, &iterSub, 0, (name), 1, s, -1); \ sdsfree(s); \ } while (0) #define ADD_SUB_OPTION(code, len, val) \ do { \ U8 *pVal; \ int count_flag = 0; \ gtk_tree_store_append(store, &iterOpt, &iterSub); \ s = sdsempty(); \ sprintf(s, "%u", (len)); \ gtk_tree_store_set(store, &iterOpt, 0, "Length", 1, s, -1); \ sdsfree(s); \ \ switch (dhcp_get_opType(code)) { \ case 1: \ gtk_tree_store_append(store, &iterOpt, &iterSub); \ gtk_tree_store_set(store, &iterOpt, 0, dhcp_get_opName(code), 1, (val), -1); \ break; \ case 2: \ pVal = val; \ do { \ sds sv = sdsempty(); \ gtk_tree_store_append(store, &iterOpt, &iterSub); \ s = sdsempty(); \ sprintf(s, "%s", dhcp_get_opName(*pVal)); \ sprintf(sv, "Request (%03d)", *pVal); \ gtk_tree_store_set(store, &iterOpt, 0, sv, 1, s, -1); \ sdsfree(s); \ sdsfree(sv); \ count_flag++; \ pVal++; \ } while (count_flag != (len)); \ break; \ case 3: \ do { \ U32 *pIp = (U32 *)(val); \ gtk_tree_store_append(store, &iterOpt, &iterSub); \ s = sdsempty(); \ sprintf(s, "%s", u32_to_str_ip(*pIp)); \ gtk_tree_store_set(store, &iterOpt, 0, dhcp_get_opName(code), 1, s, -1); \ sdsfree(s); \ count_flag += 4; \ } while (count_flag != (len)); \ break; \ case 4: \ gtk_tree_store_append(store, &iterOpt, &iterSub); \ for (int i = 0; i < (len); i++) \ count_flag = count_flag * 256 + (val)[i]; \ s = sdsempty(); \ sprintf(s, "%d(s)", count_flag); \ gtk_tree_store_set(store, &iterOpt, 0, dhcp_get_opName(code), 1, s, -1); \ sdsfree(s); \ break; \ case 6: \ gtk_tree_store_append(store, &iterOpt, &iterSub); \ s = sdsempty(); \ if ((code) == OPT_MESSAGETYPE) { \ gtk_tree_store_set(store, &iterOpt, 0, dhcp_get_opName(code), 1, g_mess_info[(val)[0]], -1); \ } \ sdsfree(s); \ break; \ default: \ gtk_tree_store_append(store, &iterOpt, &iterSub); \ gtk_tree_store_set(store, &iterOpt, 0, dhcp_get_opName(code), 1, "", -1); \ } \ } while (0) static void create_dhcp_tree_mode(PDHCP_PACKAGE p, U32 nBytes, GtkWidget *treeView) { // 填充右上侧 TreeView 协议数据 sds s; GtkTreeIter iter, iterSub, iterOpt; GtkTreeStore *store = NULL; //gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_STRING); const char *itemTitle[] = {"Ethernet II", "802.1Q Virtual LAN", "IP Version 4", "UDP", "DHCP"}; GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(treeView)); //GtkTreeStore *store = GTK_TREE_STORE(); if (model) { store = GTK_TREE_STORE(model); if (gtk_tree_model_get_iter_first(model, &iter)) { gtk_tree_store_clear(store); } } else { store = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_STRING); gtk_tree_view_set_model(GTK_TREE_VIEW(treeView), GTK_TREE_MODEL(store)); } gtk_tree_store_append(store, &iter, NULL); gtk_tree_store_set(store, &iter, 0, itemTitle[0], 1, "", -1); // 添加 Ethernet II 头相关内容 s = sdsempty(); MAC_TO_STR(p->hdr.eth.h_dest, s); ADD_SUB_STRING("Destination", s); sdsfree(s); s = sdsempty(); MAC_TO_STR(p->hdr.eth.h_source, s); ADD_SUB_STRING("Source", s); sdsfree(s); #if VLAN_SUPPORT // 添加 VLan 头相关内容 gtk_tree_store_append(store, &iter, NULL); gtk_tree_store_set(store, &iter, 0, itemTitle[1], 1, "", -1); ADD_SUB_INT("ID", "%u", VLAN_VNI_ID(p->hdr.vlan.id)); ADD_SUB_INT("Type", "0x%04X", ntohs(p->hdr.vlan.type)); #endif // 添加 IP 头 gtk_tree_store_append(store, &iter, NULL); gtk_tree_store_set(store, &iter, 0, itemTitle[2], 1, "", -1); ADD_SUB_INT("Version", "%u", p->hdr.ip.version); s = sdsempty(); sprintf(s, "%u %s (%u)", p->hdr.ip.ihl * 4, "bytes", p->hdr.ip.ihl); ADD_SUB_STRING("Header Length", s); sdsfree(s); ADD_SUB_INT("Differentiated Services Field", "0x%02X", p->hdr.ip.tos); ADD_SUB_INT("Total Length", "%u", ntohs(p->hdr.ip.tot_len)); ADD_SUB_INT("Identification", "0x%04X", ntohs(p->hdr.ip.id)); ADD_SUB_INT("Flags", "0x%04X", ntohs(p->hdr.ip.frag_off)); ADD_SUB_INT("Time to live", "%u", p->hdr.ip.ttl); ADD_SUB_STRING("Protocol", "UDP"); ADD_SUB_INT("Header Checksum", "0x%04X", ntohs(p->hdr.ip.check)); ADD_SUB_STRING("Source", inet_ntoa(*(struct in_addr *)&p->hdr.ip.saddr)); ADD_SUB_STRING("Destination", inet_ntoa(*(struct in_addr *)&p->hdr.ip.daddr)); // 添加 UDP 头 gtk_tree_store_append(store, &iter, NULL); gtk_tree_store_set(store, &iter, 0, itemTitle[3], 1, "", -1); ADD_SUB_INT("Source Port", "%u", ntohs(p->hdr.udp.source)); ADD_SUB_INT("Destination Port", "%u", ntohs(p->hdr.udp.dest)); ADD_SUB_INT("Length", "%u", ntohs(p->hdr.udp.len)); ADD_SUB_INT("Checksum", "0x%04X", ntohs(p->hdr.udp.check)); // 添加 DHCP 内容 gtk_tree_store_append(store, &iter, NULL); gtk_tree_store_set(store, &iter, 0, itemTitle[4], 1, "", -1); switch (p->dhcp.op) { case 1: ADD_SUB_STRING("Message Type", "Boot Request"); break; case 2: ADD_SUB_STRING("Message Type", "Boot Reply"); break; default: ADD_SUB_STRING("Message Type", "Unknown"); } switch (p->dhcp.htype) { case 1: ADD_SUB_STRING("Hardware Type", "Ethernet"); break; default: ADD_SUB_STRING("Hardware Type", "Unknown"); } ADD_SUB_INT("Hardware address length", "%u", p->dhcp.hlen); ADD_SUB_INT("Hops", "%u", p->dhcp.hops); ADD_SUB_INT("Transaction ID", "0x%08X", p->dhcp.xid); ADD_SUB_INT("Seconds elapsed", "%u", p->dhcp.secs); ADD_SUB_INT("Bootp flags", "0x%04X", p->dhcp.flags); ADD_SUB_STRING("Client IP address", inet_ntoa(*(struct in_addr *)&p->dhcp.ciaddr)); ADD_SUB_STRING("Your(client) IP address", inet_ntoa(*(struct in_addr *)&p->dhcp.yiaddr)); ADD_SUB_STRING("Next server IP address", inet_ntoa(*(struct in_addr *)&p->dhcp.siaddr)); ADD_SUB_STRING("Relay agent IP address", inet_ntoa(*(struct in_addr *)&p->dhcp.giaddr)); s = sdsempty(); MAC_TO_STR(p->dhcp.chaddr, s); ADD_SUB_STRING("Client MAC address", s); sdsfree(s); if (strlen((char *)p->dhcp.sname) > 0) { ADD_SUB_STRING("Server host name", p->dhcp.sname); } else { ADD_SUB_STRING("Server host name", "not given"); } if (strlen((char *)p->dhcp.file) > 0) { ADD_SUB_STRING("Boot file name", p->dhcp.file); } else { ADD_SUB_STRING("Boot file name", "not given"); } ADD_SUB_STRING("Magic cookie", "DHCP"); U8 *opPointer, *optEnd = (U8 *)p + nBytes; opPointer = p->dhcp.options; while (*opPointer && opPointer < optEnd) { if (*opPointer == OPT_END) { ADD_SUB_STRING("Option(255)", "End"); opPointer++; } else { STR_OPT opTmp; sds st = sdsempty(); opTmp.op_code = *opPointer; opPointer++; opTmp.op_size = *opPointer; opPointer++; memcpy(opTmp.op_value, opPointer, opTmp.op_size); opPointer += opTmp.op_size; s = sdsempty(); //sprintf(s, "(%u) %s", opTmp.op_code, dhcp_get_opName(opTmp.op_code)); sprintf(st, "Option(%u)", opTmp.op_code); ADD_SUB_STRING(st, dhcp_get_opName(opTmp.op_code)); sdsfree(s); sdsfree(st); ADD_SUB_OPTION(opTmp.op_code, opTmp.op_size, opTmp.op_value); } } } static gboolean upgrade_statusbar_proc(gpointer user_data) { PDHCP_INFO pInfo = (PDHCP_INFO)user_data; int ret; DHCP_OPT opt; char buf[1024] = {0}; S32 tmOffer = 0, tmAck = 0; const char *pIp = NULL, *pNetmask = NULL, *pGw = NULL, *pDns1 = NULL, *pDns2 = NULL; if (pInfo) { tmOffer = MAX(0, (pInfo->pOfferBuf.tm.tv_sec * 1000000 + pInfo->pOfferBuf.tm.tv_usec) - (pInfo->pDiscBuf.tm.tv_sec * 1000000 + pInfo->pDiscBuf.tm.tv_usec)); tmAck = MAX(0, (pInfo->pAckBuf.tm.tv_sec * 1000000 + pInfo->pAckBuf.tm.tv_usec) - (pInfo->pReqBuf.tm.tv_sec * 1000000 + pInfo->pReqBuf.tm.tv_usec)); if (pInfo->pAckBuf.p) { PDHCP_PACKAGE pkg = (PDHCP_PACKAGE)pInfo->pAckBuf.p; U32 optSize = pInfo->pAckBuf.buf_size - sizeof(DHCP_PACKAGE); pIp = u32_to_str_ip_safe(pkg->dhcp.yiaddr); ret = dhcp_get_option(OPT_NETMASK, pkg->dhcp.options, optSize, &opt); if (ret == ERR_SUCCESS && opt.len == sizeof(U32)) { pNetmask = u32_to_str_ip_safe(*((U32 *)opt.pValue)); } ret = dhcp_get_option(OPT_ROUTER, pkg->dhcp.options, optSize, &opt); if (ret == ERR_SUCCESS && opt.len == sizeof(U32)) { pGw = u32_to_str_ip_safe(*((U32 *)opt.pValue)); } ret = dhcp_get_option(OPT_DNS, pkg->dhcp.options, optSize, &opt); if (ret == ERR_SUCCESS && opt.len >= sizeof(U32)) { U32 *pVal = (U32 *)opt.pValue; pDns1 = u32_to_str_ip_safe(*pVal); if (opt.len > sizeof(U32)) { pVal++; pDns2 = u32_to_str_ip_safe(*pVal); } } } } sprintf(buf, "Offer %d.%03ds | ACK %d.%03ds | ip: %s | netmask: %s | gateway: %s | dns1: %s | dns2: %s |", tmOffer / 1000000, (tmOffer % 1000000) / 1000, tmAck / 1000000, (tmAck % 1000000) / 1000, SAFETY_STR_STRING(pIp, ""), SAFETY_STR_STRING(pNetmask, ""), SAFETY_STR_STRING(pGw, ""), SAFETY_STR_STRING(pDns1, ""), SAFETY_STR_STRING(pDns2, "")); gtk_statusbar_pop(GTK_STATUSBAR(g_statusBar), 0); gtk_statusbar_push(GTK_STATUSBAR(g_statusBar), 0, buf); if (pIp) { free((void *)pIp); } if (pDns1) { free((void *)pDns1); } if (pDns2) { free((void *)pDns2); } if (pGw) { free((void *)pGw); } if (pNetmask) { free((void *)pNetmask); } return FALSE; } void details_wnd_show(PDHCP_INFO pInfo) { int i; GtkTextIter iter, iter1; if (pInfo) { if (pInfo != g_preInfo) { g_preInfo = pInfo; for (i = 0; i < 4; i++) { gtk_text_buffer_get_start_iter(g_pTxtBuf[i], &iter); gtk_text_buffer_get_end_iter(g_pTxtBuf[i], &iter1); gtk_text_buffer_delete(g_pTxtBuf[i], &iter, &iter1); } } g_HexBuf[0] = &pInfo->pDiscBuf; g_HexBuf[1] = &pInfo->pOfferBuf; g_HexBuf[2] = &pInfo->pReqBuf; g_HexBuf[3] = &pInfo->pAckBuf; // 填充HEX 窗口数据 for (i = 0; i < 4; i++) { char buf[128] = {0}; U32 offset = 0; if (g_HexBuf[i]->buf_size == 0) { continue; } gtk_text_buffer_get_start_iter(g_pTxtBuf[i], &iter); gtk_text_buffer_get_end_iter(g_pTxtBuf[i], &iter1); if (strlen(gtk_text_buffer_get_text(g_pTxtBuf[i], &iter, &iter1, TRUE)) > 0) { continue; } gtk_text_buffer_get_iter_at_offset(g_pTxtBuf[i], &iter, 0); sprintf(buf, " Offset(h) 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F ASCII Tables\n"); gtk_text_buffer_insert_with_tags_by_name(g_pTxtBuf[i], &iter, buf, -1, "hex_head", NULL); int row = g_HexBuf[i]->buf_size / 16 + ((g_HexBuf[i]->buf_size % 16) > 0 ? 1 : 0); for (int k = 0; k < row; k++) { char bufOffset[24] = {0}; memset(buf, 0, 128); sprintf(bufOffset, " %08X ", offset); gtk_text_buffer_insert_with_tags_by_name(g_pTxtBuf[i], &iter, bufOffset, -1, "hex_offset", NULL); for (int m = 0; m < 16; m++) { char bufStr[4] = {0}; sprintf(bufStr, "%02X ", g_HexBuf[i]->p[k * 16 + m]); strcat(buf, bufStr); if (m == 7 || m == 15) { strcat(buf, " "); } } for (int m = 0; m < 16; m++) { if (isprint(g_HexBuf[i]->p[k * 16 + m])) { char bufStr[2] = {0}; bufStr[0] = (char)g_HexBuf[i]->p[k * 16 + m]; strcat(buf, bufStr); } else { strcat(buf, "."); } } strcat(buf, "\n"); gtk_text_buffer_insert_with_tags_by_name(g_pTxtBuf[i], &iter, buf, -1, "bold", NULL); offset += 16; } // 填充 TreeView add_dhcp_tree_colums(g_ptvDHcp[i]); create_dhcp_tree_mode((PDHCP_PACKAGE)g_HexBuf[i]->p, g_HexBuf[i]->buf_size, g_ptvDHcp[i]); } upgrade_statusbar_proc(pInfo); } gtk_widget_show_all(g_detailWnd); } static gboolean button_release_event(GtkWidget *self, GdkEventButton UNUSED(event), gpointer UNUSED(user_data)) { int row, col; GtkTextIter iter; GtkTreeIter it; GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(g_pSelVal[g_curPage])); GtkListStore *store = GTK_LIST_STORE(model); GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(self)); gtk_text_buffer_get_iter_at_mark(buffer, &iter, gtk_text_buffer_get_insert(buffer)); row = gtk_text_iter_get_line(&iter); col = gtk_text_iter_get_line_offset(&iter); if (gtk_tree_model_get_iter_first(model, &it)) { gtk_list_store_clear(store); } if (col >= 11 && col < 59 && row > 0) { int k; int id; U8 valBuf[8] = {0}; sds s = sdsempty(); const char *binTbl[] = {"0000", "0001", "0010", "0011", "0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011", "1100", "1101", "1110", "1111"}; const char *itemTitle[] = {"Binary", "unsigne char", "unsigne short", "unsigne int", "unsigne long long", "char", "short", "int", "long long"}; if (col >= 35) { k = col - 12; } else { k = col - 11; } id = (row - 1) * 16 + (k + 1) / 3; //printf("Select at(%d, %d), %d, %d, %d of %d\n", row, col, k, (k + 1) / 3, id, g_curPage); memcpy(valBuf, &g_HexBuf[g_curPage]->p[id], MIN(8, g_HexBuf[g_curPage]->buf_size - id)); // printf("%d, %d, %s, %s\n", valBuf[0] >> 4, valBuf[0] & 0x0F, binTbl[(valBuf[0] >> 4) & 0xF], // binTbl[valBuf[0] & 0x0F]); sprintf(s, "%s%s", binTbl[(valBuf[0] >> 4) & 0xF], binTbl[valBuf[0] & 0x0F]); gtk_list_store_append(store, &it); gtk_list_store_set(store, &it, 0, itemTitle[0], 1, s, -1); sdsclear(s); sprintf(s, "%u", valBuf[0]); gtk_list_store_append(store, &it); gtk_list_store_set(store, &it, 0, itemTitle[1], 1, s, -1); sdsclear(s); sprintf(s, "%u", *(unsigned short *)valBuf); gtk_list_store_append(store, &it); gtk_list_store_set(store, &it, 0, itemTitle[2], 1, s, -1); sdsclear(s); sprintf(s, "%u", *(unsigned int *)valBuf); gtk_list_store_append(store, &it); gtk_list_store_set(store, &it, 0, itemTitle[3], 1, s, -1); sdsclear(s); sprintf(s, "%llu", *(unsigned long long *)valBuf); gtk_list_store_append(store, &it); gtk_list_store_set(store, &it, 0, itemTitle[4], 1, s, -1); sdsclear(s); sprintf(s, "%d", valBuf[0]); gtk_list_store_append(store, &it); gtk_list_store_set(store, &it, 0, itemTitle[5], 1, s, -1); sdsclear(s); sprintf(s, "%d", *(short *)valBuf); gtk_list_store_append(store, &it); gtk_list_store_set(store, &it, 0, itemTitle[6], 1, s, -1); sdsclear(s); sprintf(s, "%d", *(int *)valBuf); gtk_list_store_append(store, &it); gtk_list_store_set(store, &it, 0, itemTitle[7], 1, s, -1); sdsclear(s); sprintf(s, "%lld", *(long long *)valBuf); gtk_list_store_append(store, &it); gtk_list_store_set(store, &it, 0, itemTitle[8], 1, s, -1); sdsfree(s); } return FALSE; } void switch_page(GtkNotebook *UNUSED(self), GtkWidget *UNUSED(page), guint(page_num), gpointer UNUSED(user_data)) { if (g_preInfo) { details_wnd_show(g_preInfo); upgrade_statusbar_proc(g_preInfo); } g_curPage = page_num; } void details_wnd_create(GtkBuilder *builder) { // 创建主窗口 GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL); //设置窗口大小 gtk_widget_set_size_request(window, 900, 600); GtkWidget *vBox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2); // 笔记本放进窗口 gtk_container_add(GTK_CONTAINER(window), vBox); // 创建笔记本控件 GtkWidget *notebook = gtk_notebook_new(); // 笔记本放进 VBox //gtk_container_add(GTK_CONTAINER(vBox), notebook); gtk_box_pack_start(GTK_BOX(vBox), notebook, TRUE, TRUE, 0); // 页标签的位置,可以有四种位置:上、下、左或右。 gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP); g_signal_connect(notebook, "switch-page", G_CALLBACK(switch_page), NULL); gtk_widget_set_name(notebook, "nbDhcpInfo"); for (int i = 0; i < 4; i++) { const char *pTabName[] = {"Discover", "Offer", "Request", "ACK/NACK"}; // 第一个页面 GtkWidget *label = gtk_label_new(pTabName[i]); // 水平分割面板 GtkWidget *boxTab = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3); // 左侧 Hex 窗口 GtkWidget *scHex = gtk_scrolled_window_new(NULL, NULL); GtkWidget *scText = gtk_text_view_new(); gtk_container_add(GTK_CONTAINER(scHex), scText); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scHex), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); // 分隔条 GtkWidget *vSepar = gtk_separator_new(GTK_ORIENTATION_VERTICAL); // 右侧 详细信息窗口 GtkWidget *scMsg = gtk_scrolled_window_new(NULL, NULL); // 上下分割面板 GtkWidget *vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL); // 上半部分控件 GtkWidget *tvDhcp = gtk_tree_view_new(); gtk_tree_view_set_grid_lines(GTK_TREE_VIEW(tvDhcp), GTK_TREE_VIEW_GRID_LINES_BOTH); gtk_container_add(GTK_CONTAINER(scMsg), tvDhcp); gtk_paned_pack1(GTK_PANED(vpaned), scMsg, TRUE, FALSE); gtk_widget_set_size_request(tvDhcp, -1, 300); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scMsg), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC); // 下半部分控件 GtkWidget *tvHex = gtk_tree_view_new(); GtkCellRenderer *renderer = gtk_cell_renderer_text_new(); GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes("类型", renderer, "text", 0, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(tvHex), column); g_object_set(column, "min-width", 120, NULL); renderer = gtk_cell_renderer_text_new(); column = gtk_tree_view_column_new_with_attributes("值", renderer, "text", 1, NULL); gtk_tree_view_append_column(GTK_TREE_VIEW(tvHex), column); GtkListStore *store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING); gtk_tree_view_set_model(GTK_TREE_VIEW(tvHex), GTK_TREE_MODEL(store)); gtk_tree_view_set_grid_lines(GTK_TREE_VIEW(tvHex), GTK_TREE_VIEW_GRID_LINES_BOTH); gtk_paned_pack2(GTK_PANED(vpaned), tvHex, FALSE, FALSE); // 控件布局 gtk_box_pack_start(GTK_BOX(boxTab), scHex, TRUE, TRUE, 1); gtk_box_pack_start(GTK_BOX(boxTab), vSepar, FALSE, FALSE, 1); gtk_box_pack_start(GTK_BOX(boxTab), vpaned, TRUE, TRUE, 1); gtk_widget_set_size_request(scHex, 580, -1); gtk_widget_set_size_request(tvDhcp, 300, -1); gtk_notebook_append_page(GTK_NOTEBOOK(notebook), boxTab, label); g_pTxtBuf[i] = gtk_text_view_get_buffer(GTK_TEXT_VIEW(scText)); gtk_text_view_set_editable(GTK_TEXT_VIEW(scText), FALSE); gtk_text_view_set_monospace(GTK_TEXT_VIEW(scText), TRUE); gtk_text_buffer_create_tag(g_pTxtBuf[i], "blue_foreground", "foreground", "blue", NULL); gtk_text_buffer_create_tag(g_pTxtBuf[i], "hex_head", "foreground", "red", "pixels_below_lines", 5, NULL); gtk_text_buffer_create_tag(g_pTxtBuf[i], "hex_offset", "foreground", "red", NULL); gtk_text_buffer_create_tag(g_pTxtBuf[i], "bold", "weight", PANGO_WEIGHT_BOLD, NULL); g_signal_connect(G_OBJECT(scText), "button_release_event", G_CALLBACK(button_release_event), builder); g_ptvDHcp[i] = tvDhcp; g_pSelVal[i] = tvHex; } g_statusBar = gtk_statusbar_new(); gtk_box_pack_start(GTK_BOX(vBox), g_statusBar, FALSE, FALSE, 0); //gtk_widget_set_size_request(vBox, -1, 16); g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), builder); g_detailWnd = window; }