diff --git a/src/twc-chat.c b/src/twc-chat.c index 97921b8..f2724cc 100644 --- a/src/twc-chat.c +++ b/src/twc-chat.c @@ -57,6 +57,10 @@ twc_chat_new(struct t_twc_profile *profile, const char *name) chat->profile = profile; chat->friend_number = chat->group_number = -1; chat->nicks = NULL; + chat->ids = NULL; + chat->completion = NULL; + chat->last_search = NULL; + chat->prev_comp = NULL; size_t full_name_size = strlen(profile->name) + 1 + strlen(name) + 1; char *full_name = malloc(full_name_size); @@ -137,6 +141,8 @@ twc_chat_new_group(struct t_twc_profile *profile, int32_t group_number) chat->nicklist_group = weechat_nicklist_add_group(chat->buffer, NULL, NULL, NULL, true); chat->nicks = weechat_list_new(); + chat->ids = weechat_list_new(); + chat->completion = weechat_list_new(); weechat_buffer_set(chat->buffer, "nicklist", "1"); } @@ -395,6 +401,18 @@ twc_chat_free(struct t_twc_chat *chat) weechat_list_remove_all(chat->nicks); weechat_list_free(chat->nicks); } + if (chat->ids) + { + weechat_list_remove_all(chat->ids); + weechat_list_free(chat->ids); + } + if (chat->completion) + { + weechat_list_remove_all(chat->completion); + weechat_list_free(chat->completion); + } + free(chat->last_search); + free(chat->prev_comp); free(chat); } diff --git a/src/twc-chat.h b/src/twc-chat.h index a37176e..8ed87af 100644 --- a/src/twc-chat.h +++ b/src/twc-chat.h @@ -39,6 +39,10 @@ struct t_twc_chat struct t_gui_nick_group *nicklist_group; struct t_weelist *nicks; + struct t_weelist *ids; + struct t_weelist *completion; + char *last_search; + char *prev_comp; }; struct t_twc_chat * diff --git a/src/twc-commands.c b/src/twc-commands.c index a3a0618..3336bc7 100644 --- a/src/twc-commands.c +++ b/src/twc-commands.c @@ -1330,6 +1330,58 @@ twc_cmd_send(const void *pointer, void *data, struct t_gui_buffer *buffer, return WEECHAT_RC_OK; } +/** + * Custom completion for nicknames in groups. + */ +int +twc_input_complete(const void *pointer, void *data, struct t_gui_buffer *buffer, + const char *command) +{ + struct t_twc_chat *chat = twc_chat_search_buffer(buffer); + if (chat && chat->group_number >= 0) + { + const char *input = weechat_buffer_get_string(buffer, "input"); + if (!strcmp(input, "")) + return WEECHAT_RC_OK; + size_t last_search_len = + chat->last_search ? strlen(chat->last_search) : 0; + int cmp = strncmp(chat->last_search, input, last_search_len); + if (cmp || !last_search_len) + { + free(chat->last_search); + chat->last_search = strdup(input); + free(chat->prev_comp); + chat->prev_comp = NULL; + twc_starts_with(chat->nicks, chat->last_search, chat->completion); + } + + const char *comp = + twc_get_next_completion(chat->completion, chat->prev_comp); + if (!comp) + return WEECHAT_RC_OK; + + weechat_buffer_set(buffer, "completion_freeze", "1"); + + char *terminator = ": "; /* Probably put it in a config */ + char temp[strlen(comp) + strlen(terminator) + 1]; + sprintf(temp, "%s%s", comp, terminator); + weechat_buffer_set(buffer, "input", temp); + + int input_pos = weechat_buffer_get_integer(buffer, "input_length"); + int input_pos_str_size = snprintf(NULL, 0, "%d", input_pos); + char input_pos_str[input_pos_str_size + 1]; + snprintf(input_pos_str, input_pos_str_size + 1, "%d", input_pos); + weechat_buffer_set(buffer, "input_pos", input_pos_str); + + weechat_buffer_set(buffer, "completion_freeze", "0"); + + free(chat->prev_comp); + chat->prev_comp = strdup(comp); + return WEECHAT_RC_OK_EAT; + } + return WEECHAT_RC_OK; +} + /** * Register Tox-WeeChat commands. */ @@ -1421,6 +1473,9 @@ twc_commands_init() weechat_hook_command_run("/save", twc_cmd_save, NULL, NULL); + weechat_hook_command_run("/input complete_next", twc_input_complete, NULL, + NULL); + weechat_hook_command("status", "change your Tox status", "online|busy|away", "", NULL, twc_cmd_status, NULL, NULL); diff --git a/src/twc-config.c b/src/twc-config.c index 4b69d5e..2854026 100644 --- a/src/twc-config.c +++ b/src/twc-config.c @@ -38,6 +38,7 @@ struct t_config_section *twc_config_section_profile_default = NULL; struct t_config_option *twc_config_friend_request_message; struct t_config_option *twc_config_short_id_size; +struct t_config_option *twc_config_show_id; char *twc_profile_option_names[TWC_PROFILE_NUM_OPTIONS] = { "save_file", @@ -374,6 +375,11 @@ twc_config_init() NULL, 2, TOX_PUBLIC_KEY_SIZE * 2, "8", NULL, 0, twc_config_check_value_callback, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + twc_config_show_id = weechat_config_new_option( + twc_config_file, twc_config_section_look, "show_id", "boolean", + "show short Tox IDs in message logs and presence logs of group chats", + NULL, 0, 0, "on", NULL, 0, twc_config_check_value_callback, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL); } /** diff --git a/src/twc-config.h b/src/twc-config.h index 80d9418..4095d8c 100644 --- a/src/twc-config.h +++ b/src/twc-config.h @@ -24,6 +24,7 @@ struct t_twc_profile; extern struct t_config_option *twc_config_friend_request_message; extern struct t_config_option *twc_config_short_id_size; +extern struct t_config_option *twc_config_show_id; enum t_twc_proxy { diff --git a/src/twc-tox-callbacks.c b/src/twc-tox-callbacks.c index 6117fb6..be1b0b4 100644 --- a/src/twc-tox-callbacks.c +++ b/src/twc-tox-callbacks.c @@ -28,6 +28,7 @@ #endif /* TOXAV_ENABLED */ #include "twc-chat.h" +#include "twc-config.h" #include "twc-friend-request.h" #include "twc-group-invite.h" #include "twc-message-queue.h" @@ -412,6 +413,9 @@ twc_handle_group_message(Tox *tox, int32_t group_number, int32_t peer_number, char *myname = twc_get_self_name_nt(profile->tox); char *name = twc_get_peer_name_nt(profile->tox, group_number, peer_number); + char *short_id = + twc_get_peer_id_short(profile->tox, group_number, peer_number); + char *full_name = twc_get_peer_name_prefixed(short_id, name); char *tags = "notify_message"; char *message_nt = twc_null_terminate(message, length); @@ -425,10 +429,14 @@ twc_handle_group_message(Tox *tox, int32_t group_number, int32_t peer_number, if (weechat_string_has_highlight(message_nt, myname)) tags = "notify_highlight"; - twc_chat_print_message(chat, tags, nick_color, name, message_nt, - message_type); + twc_chat_print_message( + chat, tags, nick_color, + weechat_config_boolean(twc_config_show_id) ? full_name : name, + message_nt, message_type); free(name); + free(short_id); + free(full_name); free(myname); free(message_nt); } @@ -456,58 +464,88 @@ twc_group_peer_list_changed_callback(Tox *tox, uint32_t group_number, struct t_weelist *new_nicks; struct t_weelist_item *n; + struct t_weelist *new_ids; + struct t_weelist_item *id; npeers = tox_conference_peer_count(profile->tox, group_number, &err); if (err == TOX_ERR_CONFERENCE_PEER_QUERY_OK) { new_nicks = weechat_list_new(); + new_ids = weechat_list_new(); for (i = 0; i < npeers; i++) { char *name = twc_get_peer_name_nt(profile->tox, group_number, i); + char *id = twc_get_peer_id_short(profile->tox, group_number, i); weechat_list_add(new_nicks, name, WEECHAT_LIST_POS_END, NULL); + weechat_list_add(new_ids, id, WEECHAT_LIST_POS_END, NULL); free(name); + free(id); } } else return; + bool changed = false; + /* searching for exits */ n = weechat_list_get(chat->nicks, 0); + id = weechat_list_get(chat->ids, 0); - while (n) + while (id && n) { + const char *short_id = weechat_list_string(id); const char *name = weechat_list_string(n); - if (!weechat_list_search(new_nicks, name)) + if (!weechat_list_search(new_ids, short_id)) { - weechat_printf(chat->buffer, "%s%s just left the group chat", - weechat_prefix("quit"), name); - nick = weechat_nicklist_search_nick(chat->buffer, - chat->nicklist_group, name); + char *full_name = twc_get_peer_name_prefixed(short_id, name); + nick = weechat_nicklist_search_nick( + chat->buffer, chat->nicklist_group, full_name); weechat_nicklist_remove_nick(chat->buffer, nick); + weechat_printf( + chat->buffer, "%s%s just left the group chat", + weechat_prefix("quit"), + weechat_config_boolean(twc_config_show_id) ? full_name : name); + changed = true; + free(full_name); } n = weechat_list_next(n); + id = weechat_list_next(id); } /* searching for joins */ n = weechat_list_get(new_nicks, 0); + id = weechat_list_get(new_ids, 0); - while (n) + while (id && n) { + const char *short_id = weechat_list_string(id); const char *name = weechat_list_string(n); - if (!weechat_list_search(chat->nicks, name)) + if (!weechat_list_search(chat->ids, short_id)) { - weechat_printf(chat->buffer, "%s%s just joined the group chat", - weechat_prefix("join"), name); - weechat_nicklist_add_nick(chat->buffer, chat->nicklist_group, name, - NULL, NULL, NULL, 1); + char *full_name = twc_get_peer_name_prefixed(short_id, name); + weechat_nicklist_add_nick(chat->buffer, chat->nicklist_group, + full_name, NULL, NULL, NULL, 1); + weechat_printf( + chat->buffer, "%s%s just joined the group chat", + weechat_prefix("join"), + weechat_config_boolean(twc_config_show_id) ? full_name : name); + changed = true; + free(full_name); } n = weechat_list_next(n); + id = weechat_list_next(id); } - weechat_list_remove_all(chat->nicks); - weechat_list_free(chat->nicks); - chat->nicks = new_nicks; + if (changed) + { + weechat_list_remove_all(chat->nicks); + weechat_list_free(chat->nicks); + weechat_list_remove_all(chat->ids); + weechat_list_free(chat->ids); + chat->nicks = new_nicks; + chat->ids = new_ids; + } } void @@ -523,10 +561,13 @@ twc_group_peer_name_callback(Tox *tox, uint32_t group_number, struct t_gui_nick *nick = NULL; const char *prev_name; char *name; + const char *short_id; + char *prev_full_name; + char *full_name; bool rc; TOX_ERR_CONFERENCE_PEER_QUERY err = TOX_ERR_CONFERENCE_PEER_QUERY_OK; - struct t_weelist_item *n; + struct t_weelist_item *n, *id; npeers = tox_conference_peer_count(profile->tox, group_number, &err); @@ -550,27 +591,40 @@ twc_group_peer_name_callback(Tox *tox, uint32_t group_number, twc_group_peer_list_changed_callback(tox, group_number, data); return; } - + id = weechat_list_get(chat->ids, peer_number); + short_id = weechat_list_string(id); prev_name = weechat_list_string(n); + prev_full_name = twc_get_peer_name_prefixed(short_id, prev_name); + name = twc_null_terminate(pname, pname_len); + full_name = twc_get_peer_name_prefixed(short_id, name); nick = weechat_nicklist_search_nick(chat->buffer, chat->nicklist_group, - prev_name); - + prev_full_name); weechat_nicklist_remove_nick(chat->buffer, nick); + if (!twc_get_peer_name_count(chat->nicks, prev_name)) + { + nick = weechat_nicklist_search_nick(chat->buffer, chat->nicklist_group, + prev_name); + weechat_nicklist_remove_nick(chat->buffer, nick); + } err = TOX_ERR_CONFERENCE_PEER_QUERY_OK; rc = tox_conference_peer_number_is_ours(tox, group_number, peer_number, &err); + bool show_id = weechat_config_boolean(twc_config_show_id); if ((err == TOX_ERR_CONFERENCE_PEER_QUERY_OK) && (!rc)) - weechat_printf(chat->buffer, "%s%s is now known as %s", - weechat_prefix("network"), prev_name, name); - + weechat_printf( + chat->buffer, "%s%s is now known as %s", weechat_prefix("network"), + show_id ? prev_full_name : prev_name, show_id ? full_name : name); weechat_list_set(n, name); - + weechat_nicklist_add_nick(chat->buffer, chat->nicklist_group, full_name, + NULL, NULL, NULL, 1); weechat_nicklist_add_nick(chat->buffer, chat->nicklist_group, name, NULL, - NULL, NULL, 1); + NULL, NULL, 0); + free(prev_full_name); + free(full_name); free(name); } diff --git a/src/twc-utils.c b/src/twc-utils.c index 8405dfd..74276bb 100644 --- a/src/twc-utils.c +++ b/src/twc-utils.c @@ -178,7 +178,106 @@ twc_get_friend_id_short(Tox *tox, int32_t friend_number) } /** - * Reverse the bytes of a 32-bit integer. + * Return a group peer's Tox ID in short form. Return value must be freed. + */ +char * +twc_get_peer_id_short(Tox *tox, uint32_t conference_number, + uint32_t peer_number) +{ + uint8_t peer_id[TOX_PUBLIC_KEY_SIZE]; + TOX_ERR_CONFERENCE_PEER_QUERY err; + size_t short_id_length = weechat_config_integer(twc_config_short_id_size); + short_id_length = + short_id_length > 4 ? short_id_length : 4; /* Let's use a sane value */ + char *hex_address = malloc(short_id_length + 1); + + tox_conference_peer_get_public_key(tox, conference_number, peer_number, + peer_id, &err); + if (err != TOX_ERR_CONFERENCE_PEER_QUERY_OK) + memset(peer_id, 0, TOX_PUBLIC_KEY_SIZE); + + twc_bin2hex(peer_id, short_id_length / 2, hex_address); + return hex_address; +} + +/** + * Prefix a nickname with Tox ID in short form. Return value must be freed. + */ +char * +twc_get_peer_name_prefixed(const char *id, const char *name) +{ + if (!name) + name = "Tox User"; + size_t full_length = strlen(id) + strlen(name) + 4; + char *full_name = malloc(full_length); + snprintf(full_name, full_length, "[%s] %s", id, name); + return full_name; +} + +/** + * Return amount of times a certain nickname repeats in a t_weelist list. + */ +size_t +twc_get_peer_name_count(struct t_weelist *list, const char *name) +{ + size_t count = 0; + struct t_weelist_item *item = weechat_list_get(list, 0); + for (item = weechat_list_get(list, 0); item; item = weechat_list_next(item)) + { + if (!strcmp(weechat_list_string(item), name)) + count++; + } + return count; +} + +/** + * Return t_weelist* to a list of strings which start with a certain string, + * NULL otherwise if there's no such strings. Must be properly freed after + * usage. + */ +struct t_weelist * +twc_starts_with(struct t_weelist *list, const char *search, + struct t_weelist *result) +{ + size_t length = strlen(search); + weechat_list_remove_all(result); + if (!search || !length) + return result; + struct t_weelist_item *item = weechat_list_get(list, 0); + while (item) + { + const char *string = weechat_list_string(item); + if (strlen(string) >= length && !strncmp(search, string, length)) + weechat_list_add(result, string, WEECHAT_LIST_POS_SORT, NULL); + item = weechat_list_next(item); + } + return result; +} + +/** + * Return next completion string regarding of a previous one. + */ + +const char * +twc_get_next_completion(struct t_weelist *completion_list, + const char *prev_comp) +{ + if (!weechat_list_size(completion_list)) + return NULL; + const char *comp = + weechat_list_string(weechat_list_get(completion_list, 0)); + if (prev_comp) + { + struct t_weelist_item *item = + weechat_list_search(completion_list, prev_comp); + if (item && (item = weechat_list_next(item))) + comp = weechat_list_string(item); + } + return comp; +} + +/** + * reverse the bytes of a 32-bit integer. */ uint32_t twc_uint32_reverse_bytes(uint32_t num) diff --git a/src/twc-utils.h b/src/twc-utils.h index b1d2f3d..6b641f7 100644 --- a/src/twc-utils.h +++ b/src/twc-utils.h @@ -49,6 +49,31 @@ twc_get_self_name_nt(Tox *tox); char * twc_get_friend_id_short(Tox *tox, int32_t friend_number); +char * +twc_get_peer_id_short(Tox *tox, uint32_t conference_number, + uint32_t peer_number); + +char * +twc_get_peer_name_prefixed(const char *id, const char *name); + +char * +twc_get_peer_name_prefixed_and_aligned(const char *id, const char *name, + size_t max); + +size_t +twc_get_max_string_length(struct t_weelist *list); + +size_t +twc_get_peer_name_count(struct t_weelist *list, const char *name); + +struct t_weelist * +twc_starts_with(struct t_weelist *list, const char *search, + struct t_weelist *result); + +const char * +twc_get_next_completion(struct t_weelist *completion_list, + const char *prev_comp); + uint32_t twc_uint32_reverse_bytes(uint32_t num);