From 97534b067012b647fed114edd7d3f074e42c78eb Mon Sep 17 00:00:00 2001 From: deepend-tildeclub Date: Wed, 10 Jun 2026 15:05:00 -0600 Subject: [PATCH 1/3] Add option to hide join/part hostmasks --- src/common/cfgfiles.c | 1 + src/common/text.c | 87 +++++++++++++++++++++++++++++++++++++++++- src/common/zoitechat.h | 1 + src/fe-gtk/setup.c | 1 + 4 files changed, 89 insertions(+), 1 deletion(-) diff --git a/src/common/cfgfiles.c b/src/common/cfgfiles.c index c8fcf760..fbf73c6a 100644 --- a/src/common/cfgfiles.c +++ b/src/common/cfgfiles.c @@ -518,6 +518,7 @@ const struct prefs vars[] = {"irc_conf_mode", P_OFFINT (hex_irc_conf_mode), TYPE_BOOL}, {"irc_extra_hilight", P_OFFSET (hex_irc_extra_hilight), TYPE_STR}, {"irc_hide_nickchange", P_OFFINT (hex_irc_hide_nickchange), TYPE_BOOL}, + {"irc_hide_join_part_hostmask", P_OFFINT (hex_irc_hide_join_part_hostmask), TYPE_BOOL}, {"irc_hide_version", P_OFFINT (hex_irc_hide_version), TYPE_BOOL}, {"irc_hidehost", P_OFFINT (hex_irc_hidehost), TYPE_BOOL}, {"irc_id_ntext", P_OFFSET (hex_irc_id_ntext), TYPE_STR}, diff --git a/src/common/text.c b/src/common/text.c index 3dfaade5..3e1e758c 100644 --- a/src/common/text.c +++ b/src/common/text.c @@ -1809,12 +1809,97 @@ format_event (session *sess, int index, char **args, char *o, gsize sizeofo, uns o[0] = 0; } +static char * +text_event_without_hostmask_format (const char *format, int host_arg) +{ + char token[3]; + const char *arg; + const char *open; + const char *close; + const char *start; + char *out; + gsize prefix_len; + + g_snprintf (token, sizeof (token), "$%d", host_arg); + arg = strstr (format, token); + if (!arg) + return NULL; + + open = arg; + while (open > format && *open != '(' && *open != '\n') + open--; + + close = arg + strlen (token); + while (*close && *close != ')' && *close != '\n') + close++; + + if (*open != '(' || *close != ')') + return NULL; + + start = open; + if (start > format && start[-1] == ' ') + start--; + + prefix_len = start - format; + out = g_malloc (prefix_len + strlen (close + 1) + 1); + memcpy (out, format, prefix_len); + strcpy (out + prefix_len, close + 1); + return out; +} + +static void +display_event_string (session *sess, int event, char **args, char *format, + unsigned int stripcolor_args, time_t timestamp) +{ + char *compiled; + char *saved; + char o[4096]; + int max_arg; + + if (pevt_build_string (format, &compiled, &max_arg) != 0) + return; + + saved = pntevts[event]; + pntevts[event] = compiled; + format_event (sess, event, args, o, sizeof (o), stripcolor_args); + pntevts[event] = saved; + g_free (compiled); + + if (o[0]) + PrintTextTimeStamp (sess, o, timestamp); +} + static void display_event (session *sess, int event, char **args, unsigned int stripcolor_args, time_t timestamp) { char o[4096]; - format_event (sess, event, args, o, sizeof (o), stripcolor_args); + char *format; + char *host; + int host_arg; + + if (prefs.hex_irc_hide_join_part_hostmask && + (event == XP_TE_JOIN || event == XP_TE_PART || event == XP_TE_PARTREASON)) + { + host_arg = event == XP_TE_JOIN ? 3 : 2; + format = text_event_without_hostmask_format (pntevts_text[event], host_arg); + if (format) + { + display_event_string (sess, event, args, format, stripcolor_args, timestamp); + g_free (format); + return; + } + + host = args[host_arg]; + args[host_arg] = ""; + format_event (sess, event, args, o, sizeof (o), stripcolor_args); + args[host_arg] = host; + } + else + { + format_event (sess, event, args, o, sizeof (o), stripcolor_args); + } + if (o[0]) PrintTextTimeStamp (sess, o, timestamp); } diff --git a/src/common/zoitechat.h b/src/common/zoitechat.h index 6f8591c3..ac3e78a4 100644 --- a/src/common/zoitechat.h +++ b/src/common/zoitechat.h @@ -188,6 +188,7 @@ struct zoitechatprefs unsigned int hex_irc_conf_mode; unsigned int hex_irc_hidehost; unsigned int hex_irc_hide_nickchange; + unsigned int hex_irc_hide_join_part_hostmask; unsigned int hex_irc_hide_version; unsigned int hex_irc_invisible; unsigned int hex_irc_logging; diff --git a/src/fe-gtk/setup.c b/src/fe-gtk/setup.c index d2dc0882..6b61f12f 100644 --- a/src/fe-gtk/setup.c +++ b/src/fe-gtk/setup.c @@ -546,6 +546,7 @@ static const setting general_settings[] = {ST_TOGGLE, N_("WHOIS on notify"), P_OFFINTNL(hex_notify_whois_online), N_("Sends a /WHOIS when a user comes online in your notify list."), 0, 0}, {ST_TOGGLE, N_("Hide join and part messages"), P_OFFINTNL(hex_irc_conf_mode), N_("Hide channel join/part messages by default."), 0, 0}, {ST_TOGGLE, N_("Hide nick change messages"), P_OFFINTNL(hex_irc_hide_nickchange), 0, 0, 0}, + {ST_TOGGLE, N_("Hide hostmasks in join and part messages"), P_OFFINTNL(hex_irc_hide_join_part_hostmask), 0, 0, 0}, {ST_TOGGLE, N_("Enable Ctrl+Q to quit"), P_OFFINTNL(hex_gui_ctrlq_quit), 0, 0, 0}, {ST_END, 0, 0, 0, 0, 0} From 900066b9d43cba27899b6275e9dd07021970e910 Mon Sep 17 00:00:00 2001 From: deepend-tildeclub Date: Wed, 10 Jun 2026 15:09:47 -0600 Subject: [PATCH 2/3] Add option to hide join/part hostmasks --- src/fe-gtk/fkeys.c | 196 ++++++++++++++++++++++++++++++++++++--------- src/fe-gtk/menu.c | 111 ++++++++++++++----------- src/fe-gtk/menu.h | 1 + 3 files changed, 224 insertions(+), 84 deletions(-) diff --git a/src/fe-gtk/fkeys.c b/src/fe-gtk/fkeys.c index 5add39a9..3c7095d6 100644 --- a/src/fe-gtk/fkeys.c +++ b/src/fe-gtk/fkeys.c @@ -80,7 +80,7 @@ void key_action_tab_clean (void); */ /* Remember that the *number* of actions is this *plus* 1 --AGL */ -#define KEY_MAX_ACTIONS 14 +#define KEY_MAX_ACTIONS 16 struct key_binding { @@ -142,6 +142,13 @@ static int key_action_move_tab_family_right (GtkWidget * wid, GdkEventKey * evt, static int key_action_put_history (GtkWidget * wid, GdkEventKey * evt, char *d1, char *d2, struct session *sess); +static int key_action_menu_shortcut (GtkWidget * wid, GdkEventKey * evt, + char *d1, char *d2, + struct session *sess); +static int key_action_reopen_closed_tab (GtkWidget * wid, GdkEventKey * evt, + char *d1, char *d2, + struct session *sess); + static GSList *keybind_list = NULL; @@ -287,6 +294,10 @@ static const struct key_action key_actions[KEY_MAX_ACTIONS + 1] = { N_("This command moves the current tab family to the right")}, {key_action_put_history, "Push input line into history", N_("Push input line into history but doesn't send to server")}, + {key_action_menu_shortcut, "Menu Shortcut", + N_("Runs one of the built-in menu shortcuts. Set Data 1 to: network-list, new-server-tab, new-server-window, close, quit, menu-toggle, user-list-toggle, fullscreen-toggle, away-toggle, reset-marker, move-marker, copy-selection, search-text, search-next, search-previous or contents.")}, + {key_action_reopen_closed_tab, "Reopen Closed Tab", + N_("Reopens the most recently closed channel tab")}, }; #define default_kb_cfg \ @@ -329,7 +340,86 @@ static const struct key_action key_actions[KEY_MAX_ACTIONS + 1] = { "ACCEL=Right\nMove front tab right\nD1!\nD2!\n\n"\ "ACCEL=Page_Up\nMove tab family left\nD1!\nD2!\n\n"\ "ACCEL=Page_Down\nMove tab family right\nD1!\nD2!\n\n"\ - "ACCEL=F9\nRun Command\nD1:/GUI MENU TOGGLE\nD2!\n\n" + "ACCEL=s\nMenu Shortcut\nD1:network-list\nD2!\n\n"\ + "ACCEL=t\nMenu Shortcut\nD1:new-server-tab\nD2!\n\n"\ + "ACCEL=n\nMenu Shortcut\nD1:new-server-window\nD2!\n\n"\ + "ACCEL=w\nMenu Shortcut\nD1:close\nD2!\n\n"\ + "ACCEL=q\nMenu Shortcut\nD1:quit\nD2!\n\n"\ + "ACCEL=F9\nMenu Shortcut\nD1:menu-toggle\nD2!\n\n"\ + "ACCEL=F7\nMenu Shortcut\nD1:user-list-toggle\nD2!\n\n"\ + "ACCEL=F11\nMenu Shortcut\nD1:fullscreen-toggle\nD2!\n\n"\ + "ACCEL=a\nMenu Shortcut\nD1:away-toggle\nD2!\n\n"\ + "ACCEL=m\nMenu Shortcut\nD1:reset-marker\nD2!\n\n"\ + "ACCEL=M\nMenu Shortcut\nD1:move-marker\nD2!\n\n"\ + "ACCEL=C\nMenu Shortcut\nD1:copy-selection\nD2!\n\n"\ + "ACCEL=f\nMenu Shortcut\nD1:search-text\nD2!\n\n"\ + "ACCEL=g\nMenu Shortcut\nD1:search-next\nD2!\n\n"\ + "ACCEL=G\nMenu Shortcut\nD1:search-previous\nD2!\n\n"\ + "ACCEL=F1\nMenu Shortcut\nD1:contents\nD2!\n\n"\ + "ACCEL=T\nReopen Closed Tab\nD1!\nD2!\n\n" + +static gboolean +key_builtin_data_match (char *line, char *data) +{ + if (line[2] == '!') + return data == NULL || data[0] == 0; + + if (line[2] == ':') + return !strcmp (&line[3], data ? data : ""); + + return FALSE; +} + +static gboolean +key_binding_is_builtin (struct key_binding *kb) +{ + char *buf, *ibuf; + char *action; + int pnt = 0; + int state = 0; + gboolean match = FALSE; + gboolean d1_match = FALSE; + off_t size; + + if (kb->action < 0 || kb->action > KEY_MAX_ACTIONS) + return FALSE; + + action = key_actions[kb->action].name; + ibuf = g_strdup (default_kb_cfg); + size = strlen (default_kb_cfg); + + while (buf_get_line (ibuf, &buf, &pnt, size)) + { + if (strlen (buf) == 0) + continue; + + switch (state) + { + case 0: + state = 1; + break; + case 1: + match = !strcmp (buf, action); + state = 2; + break; + case 2: + d1_match = match && key_builtin_data_match (buf, kb->data1); + state = 3; + break; + case 3: + if (d1_match && key_builtin_data_match (buf, kb->data2)) + { + g_free (ibuf); + return TRUE; + } + state = 0; + break; + } + } + + g_free (ibuf); + return FALSE; +} void key_init () @@ -443,28 +533,6 @@ key_handle_key_press (GtkWidget *wid, GdkEventKey *evt, session *sess) if (!list) return FALSE; current_sess = sess; - if ((evt->state & GDK_CONTROL_MASK) && - !(evt->state & (GDK_MOD1_MASK | GDK_META_MASK))) - { - if (!(evt->state & GDK_SHIFT_MASK) && - (evt->keyval == GDK_KEY_w || evt->keyval == GDK_KEY_W)) - { - if (sess->type == SESS_CHANNEL) - { - fe_close_window (sess); - g_signal_stop_emission_by_name (G_OBJECT (wid), "key-press-event"); - return 1; - } - } - if ((evt->state & GDK_SHIFT_MASK) && - (evt->keyval == GDK_KEY_t || evt->keyval == GDK_KEY_T)) - { - mg_reopen_closed_channel_tab (); - g_signal_stop_emission_by_name (G_OBJECT (wid), "key-press-event"); - return 1; - } - } - if (plugin_emit_keypress (sess, evt->state, evt->keyval, gdk_keyval_to_unicode (evt->keyval))) return 1; @@ -518,6 +586,7 @@ enum ACTION_COLUMN, D1_COLUMN, D2_COLUMN, + CUSTOM_COLUMN, N_COLUMNS }; @@ -642,16 +711,32 @@ key_dialog_keypress (GtkWidget *wid, GdkEventKey *evt, gpointer userdata) if (handled) { + gboolean custom1, custom2; + sel = gtk_tree_view_get_selection (view); - gtk_tree_selection_get_selected (sel, &store, &iter1); + if (!gtk_tree_selection_get_selected (sel, &store, &iter1)) + return FALSE; + path = gtk_tree_model_get_path (store, &iter1); if (delta == 1) gtk_tree_path_next (path); - else - gtk_tree_path_prev (path); - gtk_tree_model_get_iter (store, &iter2, path); + else if (!gtk_tree_path_prev (path)) + { + gtk_tree_path_free (path); + return FALSE; + } + + if (!gtk_tree_model_get_iter (store, &iter2, path)) + { + gtk_tree_path_free (path); + return FALSE; + } + gtk_tree_path_free (path); - gtk_list_store_swap (GTK_LIST_STORE (store), &iter1, &iter2); + gtk_tree_model_get (store, &iter1, CUSTOM_COLUMN, &custom1, -1); + gtk_tree_model_get (store, &iter2, CUSTOM_COLUMN, &custom2, -1); + if (custom1 && custom2) + gtk_list_store_swap (GTK_LIST_STORE (store), &iter1, &iter2); } return handled; @@ -663,14 +748,23 @@ key_dialog_selection_changed (GtkTreeSelection *sel, gpointer userdata) GtkTreeModel *model; GtkTreeIter iter; GtkXText *xtext; + GtkWidget *delete_button; char *actiontext; + gboolean custom; int action; + delete_button = g_object_get_data (G_OBJECT (key_dialog), "delete_button"); if (!gtk_tree_selection_get_selected (sel, &model, &iter) || model == NULL) + { + if (delete_button) + gtk_widget_set_sensitive (delete_button, FALSE); return; + } xtext = GTK_XTEXT (g_object_get_data (G_OBJECT (key_dialog), "xtext")); - gtk_tree_model_get (model, &iter, ACTION_COLUMN, &actiontext, -1); + gtk_tree_model_get (model, &iter, ACTION_COLUMN, &actiontext, CUSTOM_COLUMN, &custom, -1); + if (delete_button) + gtk_widget_set_sensitive (delete_button, custom); if (actiontext) { @@ -758,6 +852,7 @@ key_dialog_add (GtkWidget *wid, gpointer userdata) GtkTreePath *path; gtk_list_store_append (store, &iter); + gtk_list_store_set (store, &iter, CUSTOM_COLUMN, TRUE, -1); /* make sure the new row is visible and selected */ path = gtk_tree_model_get_path (GTK_TREE_MODEL (store), &iter); @@ -774,9 +869,14 @@ key_dialog_delete (GtkWidget *wid, gpointer userdata) GtkListStore *store = GTK_LIST_STORE (gtk_tree_view_get_model (view)); GtkTreeIter iter; GtkTreePath *path; + gboolean custom; if (gtkutil_treeview_get_selected (view, &iter, -1)) { + gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, CUSTOM_COLUMN, &custom, -1); + if (!custom) + return; + /* delete this row, select next one */ if (gtk_list_store_remove (store, &iter)) { @@ -803,13 +903,13 @@ key_dialog_treeview_new (GtkWidget *box) gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN); store = gtk_list_store_new (N_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING); + G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN); g_return_val_if_fail (store != NULL, NULL); view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store)); gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (view), TRUE); gtk_tree_view_set_enable_search (GTK_TREE_VIEW (view), FALSE); - gtk_tree_view_set_reorderable (GTK_TREE_VIEW (view), TRUE); + gtk_tree_view_set_reorderable (GTK_TREE_VIEW (view), FALSE); g_signal_connect (G_OBJECT (view), "key-press-event", G_CALLBACK (key_dialog_keypress), NULL); @@ -879,7 +979,8 @@ key_dialog_treeview_new (GtkWidget *box) G_CALLBACK (key_dialog_combo_changed), combostore); gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), ACTION_COLUMN, "Action", render, - "text", ACTION_COLUMN, + "text", ACTION_COLUMN, + "editable", CUSTOM_COLUMN, NULL); render = gtk_cell_renderer_text_new (); @@ -890,6 +991,7 @@ key_dialog_treeview_new (GtkWidget *box) GTK_TREE_VIEW (view), D1_COLUMN, "Data1", render, "text", D1_COLUMN, + "editable", CUSTOM_COLUMN, NULL); render = gtk_cell_renderer_text_new (); @@ -900,6 +1002,7 @@ key_dialog_treeview_new (GtkWidget *box) GTK_TREE_VIEW (view), D2_COLUMN, "Data2", render, "text", D2_COLUMN, + "editable", CUSTOM_COLUMN, NULL); col = gtk_tree_view_get_column (GTK_TREE_VIEW (view), KEY_COLUMN); @@ -945,7 +1048,8 @@ key_dialog_load (GtkListStore *store) ACCEL_COLUMN, accel_text, ACTION_COLUMN, key_actions[kb->action].name, D1_COLUMN, kb->data1, - D2_COLUMN, kb->data2, -1); + D2_COLUMN, kb->data2, + CUSTOM_COLUMN, !key_binding_is_builtin (kb), -1); g_free (accel_text); g_free (label_text); @@ -958,7 +1062,7 @@ void key_dialog_show () { GtkWidget *vbox, *box; - GtkWidget *view, *xtext; + GtkWidget *view, *xtext, *delete_button; GtkListStore *store; XTextColor xtext_palette[XTEXT_COLS]; char buf[128]; @@ -992,8 +1096,10 @@ key_dialog_show () gtkutil_button (box, ICON_FKEYS_NEW, NULL, key_dialog_add, NULL, _("Add")); - gtkutil_button (box, ICON_FKEYS_DELETE, NULL, key_dialog_delete, + delete_button = gtkutil_button (box, ICON_FKEYS_DELETE, NULL, key_dialog_delete, NULL, _("Delete")); + g_object_set_data (G_OBJECT (key_dialog), "delete_button", delete_button); + gtk_widget_set_sensitive (delete_button, FALSE); gtkutil_button (box, ICON_FKEYS_CANCEL, NULL, key_dialog_close, NULL, _("Cancel")); gtkutil_button (box, ICON_FKEYS_SAVE, NULL, key_dialog_save, @@ -1289,6 +1395,24 @@ key_action_handle_command (GtkWidget * wid, GdkEventKey * evt, char *d1, return 0; } +static int +key_action_menu_shortcut (GtkWidget * wid, GdkEventKey * evt, char *d1, + char *d2, struct session *sess) +{ + if (menu_key_action (d1, evt->keyval, evt->state)) + return 2; + + return 0; +} + +static int +key_action_reopen_closed_tab (GtkWidget * wid, GdkEventKey * evt, char *d1, + char *d2, struct session *sess) +{ + mg_reopen_closed_channel_tab (); + return 2; +} + /* * Check if the given session is inside the main window. This predicate * is passed to lastact_getfirst() as a way to filter out detached sessions. diff --git a/src/fe-gtk/menu.c b/src/fe-gtk/menu.c index 6f7e2f09..fd9e8f88 100644 --- a/src/fe-gtk/menu.c +++ b/src/fe-gtk/menu.c @@ -1758,39 +1758,6 @@ menu_change_layout (void) void menu_update_quit_accel (void) { - GSList *list; - - list = sess_list; - while (list) - { - session *sess = list->data; - session_gui *gui = sess->gui; - GtkWidget *item; - GtkAccelGroup *accel_group; - int enabled; - - list = list->next; - if (!gui) - continue; - - item = gui->menu_item[MENU_ID_QUIT]; - if (!item) - continue; - - enabled = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "zc-ctrlq-enabled")); - if (enabled == (int)prefs.hex_gui_ctrlq_quit) - continue; - - accel_group = g_object_get_data (G_OBJECT (item), "zc-quit-accel-group"); - if (!accel_group) - continue; - - if (prefs.hex_gui_ctrlq_quit) - gtk_widget_add_accelerator (item, "activate", accel_group, GDK_KEY_q, STATE_CTRL, GTK_ACCEL_VISIBLE); - else - gtk_widget_remove_accelerator (item, accel_group, GDK_KEY_q, STATE_CTRL); - g_object_set_data (G_OBJECT (item), "zc-ctrlq-enabled", GINT_TO_POINTER (prefs.hex_gui_ctrlq_quit)); - } } static void @@ -1939,13 +1906,13 @@ menu_about (GtkWidget *wid, gpointer sess) static struct mymenu mymenu[] = { {N_("_ZoiteChat"), 0, 0, M_NEWMENU, MENU_ID_ZOITECHAT, 0, 1}, - {N_("Network Li_st"), menu_open_server_list, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_s}, + {N_("Network Li_st"), menu_open_server_list, 0, M_MENUITEM, 0, 0, 1}, {0, 0, 0, M_SEP, 0, 0, 0}, {N_("_New"), 0, 0, M_MENUSUB, 0, 0, 1}, - {N_("Server Tab"), menu_newserver_tab, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_t}, + {N_("Server Tab"), menu_newserver_tab, 0, M_MENUITEM, 0, 0, 1}, {N_("Channel Tab"), menu_newchannel_tab, 0, M_MENUITEM, 0, 0, 1}, - {N_("Server Window"), menu_newserver_window, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_n}, + {N_("Server Window"), menu_newserver_window, 0, M_MENUITEM, 0, 0, 1}, {N_("Channel Window"), menu_newchannel_window, 0, M_MENUITEM, 0, 0, 1}, {0, 0, 0, M_END, 0, 0, 0}, {0, 0, 0, M_SEP, 0, 0, 0}, @@ -1957,13 +1924,13 @@ static struct mymenu mymenu[] = { #define CLOSE_OFFSET (13) {0, menu_close, 0, M_MENUITEM, 0, 0, 1}, {0, 0, 0, M_SEP, 0, 0, 0}, - {N_("_Quit"), menu_quit, 0, M_MENUITEM, MENU_ID_QUIT, 0, 1, GDK_KEY_q}, /* 15 */ + {N_("_Quit"), menu_quit, 0, M_MENUITEM, MENU_ID_QUIT, 0, 1}, /* 15 */ {N_("_View"), 0, 0, M_NEWMENU, 0, 0, 1}, #define MENUBAR_OFFSET (17) - {N_("_Menu Bar"), menu_bar_toggle_cb, 0, M_MENUTOG, MENU_ID_MENUBAR, 0, 1, GDK_KEY_F9}, + {N_("_Menu Bar"), menu_bar_toggle_cb, 0, M_MENUTOG, MENU_ID_MENUBAR, 0, 1}, {N_("_Topic Bar"), menu_topicbar_toggle, 0, M_MENUTOG, MENU_ID_TOPICBAR, 0, 1}, - {N_("_User List"), menu_userlist_toggle, 0, M_MENUTOG, MENU_ID_USERLIST, 0, 1, GDK_KEY_F7}, + {N_("_User List"), menu_userlist_toggle, 0, M_MENUTOG, MENU_ID_USERLIST, 0, 1}, {N_("U_ser List Buttons"), menu_ulbuttons_toggle, 0, M_MENUTOG, MENU_ID_ULBUTTONS, 0, 1}, {N_("M_ode Buttons"), menu_cmbuttons_toggle, 0, M_MENUTOG, MENU_ID_MODEBUTTONS, 0, 1}, {0, 0, 0, M_SEP, 0, 0, 0}, @@ -1980,7 +1947,7 @@ static struct mymenu mymenu[] = { {N_("Both"), menu_metres_both, 0, M_MENURADIO, 0, 0, 1}, {0, 0, 0, M_END, 0, 0, 0}, /* 32 */ { 0, 0, 0, M_SEP, 0, 0, 0 }, - {N_ ("_Fullscreen"), menu_fullscreen_toggle, 0, M_MENUTOG, MENU_ID_FULLSCREEN, 0, 1, GDK_KEY_F11}, + {N_ ("_Fullscreen"), menu_fullscreen_toggle, 0, M_MENUTOG, MENU_ID_FULLSCREEN, 0, 1}, {N_("_Server"), 0, 0, M_NEWMENU, 0, 0, 1}, {N_("_Disconnect"), menu_disconnect, 0, M_MENUITEM, MENU_ID_DISCONNECT, 0, 1}, @@ -1989,7 +1956,7 @@ static struct mymenu mymenu[] = { {N_("Channel _List"), menu_chanlist, 0, M_MENUITEM, 0, 0, 1}, {0, 0, 0, M_SEP, 0, 0, 0}, #define AWAY_OFFSET (41) - {N_("Marked _Away"), menu_away_toggle, 0, M_MENUITEM, MENU_ID_AWAY, 0, 1, GDK_KEY_a}, + {N_("Marked _Away"), menu_away_toggle, 0, M_MENUITEM, MENU_ID_AWAY, 0, 1}, {N_("_Usermenu"), 0, 0, M_NEWMENU, MENU_ID_USERMENU, 0, 1}, /* 40 */ @@ -2017,25 +1984,73 @@ static struct mymenu mymenu[] = { {N_("_Raw Log"), menu_rawlog, 0, M_MENUITEM, 0, 0, 1}, /* 61 */ {N_("_URL Grabber"), url_opengui, 0, M_MENUITEM, 0, 0, 1}, {0, 0, 0, M_SEP, 0, 0, 0}, - {N_("Reset Marker Line"), menu_resetmarker, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_m}, - {N_("Move to Marker Line"), menu_movetomarker, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_M}, - {N_("_Copy Selection"), menu_copy_selection, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_C}, + {N_("Reset Marker Line"), menu_resetmarker, 0, M_MENUITEM, 0, 0, 1}, + {N_("Move to Marker Line"), menu_movetomarker, 0, M_MENUITEM, 0, 0, 1}, + {N_("_Copy Selection"), menu_copy_selection, 0, M_MENUITEM, 0, 0, 1}, {N_("C_lear Text"), menu_flushbuffer, 0, M_MENUITEM, 0, 0, 1}, {N_("Save Text" ELLIPSIS), menu_savebuffer, 0, M_MENUITEM, 0, 0, 1}, #define SEARCH_OFFSET (70) {N_("Search"), 0, 0, M_MENUSUB, 0, 0, 1}, - {N_("Search Text" ELLIPSIS), menu_search, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_f}, - {N_("Search Next" ), menu_search_next, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_g}, - {N_("Search Previous" ), menu_search_prev, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_G}, + {N_("Search Text" ELLIPSIS), menu_search, 0, M_MENUITEM, 0, 0, 1}, + {N_("Search Next" ), menu_search_next, 0, M_MENUITEM, 0, 0, 1}, + {N_("Search Previous" ), menu_search_prev, 0, M_MENUITEM, 0, 0, 1}, {0, 0, 0, M_END, 0, 0, 0}, {N_("_Help"), 0, 0, M_NEWMENU, 0, 0, 1}, /* 74 */ - {N_("_Contents"), menu_docs, 0, M_MENUITEM, 0, 0, 1, GDK_KEY_F1}, + {N_("_Contents"), menu_docs, 0, M_MENUITEM, 0, 0, 1}, {N_("_About"), menu_about, 0, M_MENUITEM, 0, 0, 1}, {0, 0, 0, M_END, 0, 0, 0}, }; +gboolean +menu_key_action (const char *name, guint keyval, GdkModifierType state) +{ + if (!name) + return FALSE; + + if (!strcmp (name, "network-list")) + menu_open_server_list (NULL, NULL); + else if (!strcmp (name, "new-server-tab")) + menu_newserver_tab (NULL, NULL); + else if (!strcmp (name, "new-server-window")) + menu_newserver_window (NULL, NULL); + else if (!strcmp (name, "close")) + menu_close (NULL, NULL); + else if (!strcmp (name, "quit")) + { + if (!prefs.hex_gui_ctrlq_quit && keyval == GDK_KEY_q && (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK | GDK_MOD1_MASK)) == STATE_CTRL) + return FALSE; + menu_quit (NULL, NULL); + } + else if (!strcmp (name, "menu-toggle")) + menu_bar_toggle_cb (); + else if (!strcmp (name, "user-list-toggle")) + menu_userlist_toggle (NULL, NULL); + else if (!strcmp (name, "fullscreen-toggle")) + menu_fullscreen_toggle (NULL, NULL); + else if (!strcmp (name, "away-toggle")) + menu_away_toggle (NULL, NULL); + else if (!strcmp (name, "reset-marker")) + menu_resetmarker (NULL, NULL); + else if (!strcmp (name, "move-marker")) + menu_movetomarker (NULL, NULL); + else if (!strcmp (name, "copy-selection")) + menu_copy_selection (NULL, NULL); + else if (!strcmp (name, "search-text")) + menu_search (); + else if (!strcmp (name, "search-next")) + menu_search_next (NULL); + else if (!strcmp (name, "search-previous")) + menu_search_prev (NULL); + else if (!strcmp (name, "contents")) + menu_docs (NULL, NULL); + else + return FALSE; + + return TRUE; +} + void menu_set_away (session_gui *gui, int away) { diff --git a/src/fe-gtk/menu.h b/src/fe-gtk/menu.h index 52454f58..50c70b9a 100644 --- a/src/fe-gtk/menu.h +++ b/src/fe-gtk/menu.h @@ -39,6 +39,7 @@ void menu_bar_toggle (void); void menu_add_plugin_items (GtkWidget *menu, char *root, char *target); void menu_change_layout (void); void menu_update_quit_accel (void); +gboolean menu_key_action (const char *name, guint keyval, GdkModifierType state); void menu_set_away (session_gui *gui, int away); void menu_set_fullscreen (session_gui *gui, int fullscreen); From 9f839579e27a3868940261deae2c3636025ff838 Mon Sep 17 00:00:00 2001 From: deepend-tildeclub Date: Wed, 10 Jun 2026 15:57:23 -0600 Subject: [PATCH 3/3] Show live menu accelerators from keybinds --- src/fe-gtk/fkeys.c | 28 ++++++++++ src/fe-gtk/fkeys.h | 1 + src/fe-gtk/menu.c | 127 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 151 insertions(+), 5 deletions(-) diff --git a/src/fe-gtk/fkeys.c b/src/fe-gtk/fkeys.c index 3c7095d6..c5f05ea9 100644 --- a/src/fe-gtk/fkeys.c +++ b/src/fe-gtk/fkeys.c @@ -576,6 +576,31 @@ key_handle_key_press (GtkWidget *wid, GdkEventKey *evt, session *sess) return 0; } +gboolean +key_get_menu_accel (const char *name, guint *keyval, GdkModifierType *mod) +{ + struct key_binding *kb; + GSList *list; + + if (!name) + return FALSE; + + list = keybind_list; + while (list) + { + kb = (struct key_binding*)list->data; + if (kb->action >= 0 && kb->action <= KEY_MAX_ACTIONS && kb->keyval != 0 && !strcmp (key_actions[kb->action].name, "Menu Shortcut") && kb->data1 && !strcmp (kb->data1, name)) + { + *keyval = kb->keyval; + *mod = kb->mod; + return TRUE; + } + list = g_slist_next (list); + } + + return FALSE; +} + /* ***** GUI code here ******************* */ @@ -839,7 +864,10 @@ key_dialog_save (GtkWidget *wid, gpointer userdata) } if (key_save_kbs () == 0) + { + menu_update_quit_accel (); key_dialog_close (wid, NULL); + } } static void diff --git a/src/fe-gtk/fkeys.h b/src/fe-gtk/fkeys.h index ff57a565..224b45dd 100644 --- a/src/fe-gtk/fkeys.h +++ b/src/fe-gtk/fkeys.h @@ -35,5 +35,6 @@ int key_handle_key_press (GtkWidget * wid, GdkEventKey * evt, session *sess); int key_action_insert (GtkWidget * wid, GdkEventKey * evt, char *d1, char *d2, session *sess); void key_check_replace_on_change (GtkEditable *editable, gpointer data); +gboolean key_get_menu_accel (const char *name, guint *keyval, GdkModifierType *mod); #endif diff --git a/src/fe-gtk/menu.c b/src/fe-gtk/menu.c index fd9e8f88..b0b1a2c8 100644 --- a/src/fe-gtk/menu.c +++ b/src/fe-gtk/menu.c @@ -1755,11 +1755,6 @@ menu_change_layout (void) } } -void -menu_update_quit_accel (void) -{ -} - static void menu_layout_cb (GtkWidget *item, gpointer none) { @@ -2003,6 +1998,124 @@ static struct mymenu mymenu[] = { {0, 0, 0, M_END, 0, 0, 0}, }; +static const char * +menu_get_key_action_name (int index) +{ + switch (index) + { + case 1: + return "network-list"; + case 4: + return "new-server-tab"; + case 6: + return "new-server-window"; + case CLOSE_OFFSET: + return "close"; + case 15: + return "quit"; + case MENUBAR_OFFSET: + return "menu-toggle"; + case MENUBAR_OFFSET + 2: + return "user-list-toggle"; + case 34: + return "fullscreen-toggle"; + case AWAY_OFFSET: + return "away-toggle"; + case 65: + return "reset-marker"; + case 66: + return "move-marker"; + case 67: + return "copy-selection"; + case SEARCH_OFFSET + 1: + return "search-text"; + case SEARCH_OFFSET + 2: + return "search-next"; + case SEARCH_OFFSET + 3: + return "search-previous"; + case 75: + return "contents"; + } + + return NULL; +} + +static void +menu_add_keybinding_accel (GtkWidget *item, GtkAccelGroup *accel_group, const char *name) +{ + guint keyval; + GdkModifierType mod; + + if (!accel_group || !key_get_menu_accel (name, &keyval, &mod)) + return; + + if (!strcmp (name, "quit") && !prefs.hex_gui_ctrlq_quit && keyval == GDK_KEY_q && mod == STATE_CTRL) + return; + + gtk_widget_add_accelerator (item, "activate", accel_group, keyval, mod, GTK_ACCEL_VISIBLE); + g_object_set_data (G_OBJECT (item), "zc-key-accel-key", GUINT_TO_POINTER (keyval)); + g_object_set_data (G_OBJECT (item), "zc-key-accel-mod", GUINT_TO_POINTER (mod)); +} + +static void +menu_refresh_keybinding_accels (GtkWidget *widget, gpointer data) +{ + GtkAccelGroup *accel_group = data; + const char *name; + guint keyval; + GdkModifierType mod; + GtkWidget *submenu; + GList *children, *list; + + if (GTK_IS_MENU_ITEM (widget)) + { + keyval = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), "zc-key-accel-key")); + mod = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (widget), "zc-key-accel-mod")); + if (keyval != 0) + { + gtk_widget_remove_accelerator (widget, accel_group, keyval, mod); + g_object_set_data (G_OBJECT (widget), "zc-key-accel-key", NULL); + g_object_set_data (G_OBJECT (widget), "zc-key-accel-mod", NULL); + } + + name = g_object_get_data (G_OBJECT (widget), "zc-key-action"); + menu_add_keybinding_accel (widget, accel_group, name); + + submenu = gtk_menu_item_get_submenu (GTK_MENU_ITEM (widget)); + if (submenu) + menu_refresh_keybinding_accels (submenu, data); + } + + if (GTK_IS_CONTAINER (widget)) + { + children = gtk_container_get_children (GTK_CONTAINER (widget)); + for (list = children; list; list = g_list_next (list)) + menu_refresh_keybinding_accels (list->data, data); + g_list_free (children); + } +} + +void +menu_update_quit_accel (void) +{ + session *sess; + GSList *list; + GtkAccelGroup *accel_group; + + list = sess_list; + while (list) + { + sess = list->data; + if (sess && sess->gui && sess->gui->menu) + { + accel_group = g_object_get_data (G_OBJECT (sess->gui->menu), "accel"); + if (accel_group) + menu_refresh_keybinding_accels (sess->gui->menu, accel_group); + } + list = g_slist_next (list); + } +} + gboolean menu_key_action (const char *name, guint keyval, GdkModifierType state) { @@ -2656,6 +2769,8 @@ menu_create_main (void *accel_group, int bar, int away, int toplevel, case M_MENUITEM: item = gtk_menu_item_new_with_mnemonic (_(mymenu[i].text)); normalitem: + g_object_set_data (G_OBJECT (item), "zc-key-action", (gpointer) menu_get_key_action_name (i)); + menu_add_keybinding_accel (item, accel_group, menu_get_key_action_name (i)); if (mymenu[i].key != 0 && !(mymenu[i].id == MENU_ID_QUIT && !prefs.hex_gui_ctrlq_quit)) gtk_widget_add_accelerator (item, "activate", accel_group, mymenu[i].key, @@ -2684,6 +2799,8 @@ normalitem: case M_MENUTOG: item = gtk_check_menu_item_new_with_mnemonic (_(mymenu[i].text)); togitem: + g_object_set_data (G_OBJECT (item), "zc-key-action", (gpointer) menu_get_key_action_name (i)); + menu_add_keybinding_accel (item, accel_group, menu_get_key_action_name (i)); /* must avoid callback for Radio buttons */ gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), mymenu[i].state); /*gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item),