diff options
author | Sébastien Helleu <flashcode@flashtux.org> | 2023-07-14 14:27:23 +0200 |
---|---|---|
committer | Sébastien Helleu <flashcode@flashtux.org> | 2023-08-12 13:05:49 +0200 |
commit | 2f1de098bd3a44eaf0cfccb2c126250bb50f11b9 (patch) | |
tree | 03c6e6e5cf4b233d51b92f3542ed90636c98a202 /src/plugins/irc/irc-list.c | |
parent | d25a4213fecf8abccfecda4229fe85bfcf4a51b7 (diff) | |
download | weechat-2f1de098bd3a44eaf0cfccb2c126250bb50f11b9.zip |
irc: add buffer for /list reply (closes #1972)
New options:
- irc.color.list_buffer_line_selected
- irc.color.list_buffer_line_selected_bg
- irc.look.list_buffer_sort
- irc.look.list_buffer_scroll_horizontal
- irc.look.new_list_position
- irc.look.list_buffer_topic_strip_colors
Diffstat (limited to 'src/plugins/irc/irc-list.c')
-rw-r--r-- | src/plugins/irc/irc-list.c | 1309 |
1 files changed, 1309 insertions, 0 deletions
diff --git a/src/plugins/irc/irc-list.c b/src/plugins/irc/irc-list.c new file mode 100644 index 000000000..e4383b449 --- /dev/null +++ b/src/plugins/irc/irc-list.c @@ -0,0 +1,1309 @@ +/* + * irc-list.c - functions for IRC list buffer + * + * Copyright (C) 2023 Sébastien Helleu <flashcode@flashtux.org> + * + * This file is part of WeeChat, the extensible chat client. + * + * WeeChat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * WeeChat is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with WeeChat. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +#include "../weechat-plugin.h" +#include "irc.h" +#include "irc-list.h" +#include "irc-buffer.h" +#include "irc-color.h" +#include "irc-config.h" +#include "irc-input.h" +#include "irc-message.h" +#include "irc-server.h" + + +struct t_hdata *irc_list_hdata_list_channel = NULL; +struct t_hashtable *irc_list_filter_hashtable_pointers = NULL; +struct t_hashtable *irc_list_filter_hashtable_extra_vars = NULL; +struct t_hashtable *irc_list_filter_hashtable_options = NULL; + + +/* + * Compares two channels in list. + */ + +int +irc_list_compare_cb (void *data, struct t_arraylist *arraylist, + void *pointer1, void *pointer2) +{ + struct t_irc_server *ptr_server; + const char *ptr_field; + int i, reverse, case_sensitive, rc; + + /* make C compiler happy */ + (void) arraylist; + + ptr_server = data; + if (!ptr_server) + return 1; + + for (i = 0; i < ptr_server->list->sort_fields_count; i++) + { + reverse = 1; + case_sensitive = 1; + ptr_field = ptr_server->list->sort_fields[i]; + while ((ptr_field[0] == '-') || (ptr_field[0] == '~')) + { + if (ptr_field[0] == '-') + reverse *= -1; + else if (ptr_field[0] == '~') + case_sensitive ^= 1; + ptr_field++; + } + rc = weechat_hdata_compare (irc_list_hdata_list_channel, + pointer1, pointer2, + ptr_field, + case_sensitive); + rc *= reverse; + if (rc != 0) + return rc; + } + + return 1; +} + +/* + * Frees a channel in list. + */ + +void +irc_list_free_cb (void *data, struct t_arraylist *arraylist, void *pointer) +{ + struct t_irc_list_channel *ptr_channel; + + /* make C compiler happy */ + (void) data; + (void) arraylist; + + ptr_channel = (struct t_irc_list_channel *)pointer; + if (ptr_channel) + { + if (ptr_channel->name) + free (ptr_channel->name); + if (ptr_channel->name2) + free (ptr_channel->name2); + if (ptr_channel->topic) + free (ptr_channel->topic); + free (ptr_channel); + } +} + +/* + * Sets filter for list of channels. + */ + +void +irc_list_set_filter (struct t_irc_server *server, const char *filter) +{ + if (server->list->filter) + { + free (server->list->filter); + server->list->filter = NULL; + } + + server->list->filter = (filter && (strcmp (filter, "*") != 0)) ? + strdup (filter) : NULL; +} + +/* + * Sets sort for list of channels. + * + */ + +void +irc_list_set_sort (struct t_irc_server *server, const char *sort) +{ + if (server->list->sort) + { + free (server->list->sort); + server->list->sort = NULL; + } + if (server->list->sort_fields) + { + weechat_string_free_split (server->list->sort_fields); + server->list->sort_fields = NULL; + } + server->list->sort_fields_count = 0; + + server->list->sort = strdup ( + (sort && sort[0]) ? + sort : weechat_config_string (irc_config_look_list_buffer_sort)); + + if (server->list->sort) + { + server->list->sort_fields = weechat_string_split ( + server->list->sort, + ",", + NULL, + WEECHAT_STRING_SPLIT_STRIP_LEFT + | WEECHAT_STRING_SPLIT_STRIP_RIGHT + | WEECHAT_STRING_SPLIT_COLLAPSE_SEPS, + 0, + &server->list->sort_fields_count); + } +} + +/* + * Adds the properties of an irc list channel in a hashtable + * (keys and values must be strings). + */ + +void +irc_list_add_channel_in_hashtable (struct t_hashtable *hashtable, + struct t_irc_list_channel *channel) +{ + char str_number[32]; + + weechat_hashtable_set (hashtable, "name", channel->name); + weechat_hashtable_set (hashtable, "name2", channel->name2); + snprintf (str_number, sizeof (str_number), "%d", channel->users); + weechat_hashtable_set (hashtable, "users", str_number); + weechat_hashtable_set (hashtable, "topic", channel->topic); +} + +/* + * Checks if a string matches a mask. + * + * If mask has no "*" inside, it just checks if "mask" is inside the "string". + * If mask has at least one "*" inside, the function weechat_string_match is + * used. + * + * Returns: + * 1: string matches mask + * 0: string does not match mask + */ + +int +irc_list_string_match (const char *string, const char *mask) +{ + if (strchr (mask, '*')) + return weechat_string_match (string, mask, 0); + else + return (weechat_strcasestr (string, mask)) ? 1 : 0; +} + +/* + * Checks if a channel matches filter. + * + * Return: + * 1: channel matches filter + * 0: channel does NOT match filter + */ + +int +irc_list_channel_match_filter (struct t_irc_server *server, + struct t_irc_list_channel *channel) +{ + char *error, *result; + long number; + int match; + + /* no filter? then any channel is matching */ + if (!server->list->filter) + return 1; + + if (strncmp (server->list->filter, "c:", 2) == 0) + { + /* filter by evaluated condition */ + weechat_hashtable_set (irc_list_filter_hashtable_pointers, + "irc_list_channel", channel); + irc_list_add_channel_in_hashtable (irc_list_filter_hashtable_extra_vars, + channel); + result = weechat_string_eval_expression ( + server->list->filter + 2, + irc_list_filter_hashtable_pointers, + irc_list_filter_hashtable_extra_vars, + irc_list_filter_hashtable_options); + match = (result && (strcmp (result, "1") == 0)) ? 1 : 0; + if (result) + free (result); + return match; + } + + if (strncmp (server->list->filter, "n:", 2) == 0) + { + /* filter by channel name */ + if (channel->name + && irc_list_string_match (channel->name, server->list->filter + 2)) + { + return 1; + } + } + else if (strncmp (server->list->filter, "t:", 2) == 0) + { + /* filter by topic */ + if (channel->topic + && irc_list_string_match (channel->topic, server->list->filter + 2)) + { + return 1; + } + } + else if (strncmp (server->list->filter, "u:>", 3) == 0) + { + /* filter by users (> N)*/ + number = strtol (server->list->filter + 3, &error, 10); + if (error && !error[0] && channel->users > (int)number) + return 1; + } + else if (strncmp (server->list->filter, "u:<", 3) == 0) + { + /* filter by users (< N)*/ + number = strtol (server->list->filter + 3, &error, 10); + if (error && !error[0] && channel->users < (int)number) + return 1; + } + else if (strncmp (server->list->filter, "u:", 2) == 0) + { + /* filter by users */ + number = strtol (server->list->filter + 2, &error, 10); + if (error && !error[0] && channel->users >= (int)number) + return 1; + } + else + { + if (channel->name + && irc_list_string_match (channel->name, server->list->filter)) + { + return 1; + } + if (channel->topic + && irc_list_string_match (channel->topic, server->list->filter)) + { + return 1; + } + } + + return 0; +} + +/* + * Filters channels: apply filter and use sort to build the list + * "filter_channels" that are pointers to t_irc_list_channel structs + * stored in main list "channels". + */ + +void +irc_list_filter_channels (struct t_irc_server *server) +{ + struct t_irc_list_channel *ptr_channel; + int i, list_size; + + if (server->list->filter_channels) + { + weechat_arraylist_clear (server->list->filter_channels); + } + else + { + server->list->filter_channels = weechat_arraylist_new ( + 16, 1, 0, + &irc_list_compare_cb, server, + NULL, NULL); + } + + if (!server->list->sort) + { + irc_list_set_sort ( + server, + weechat_config_string (irc_config_look_list_buffer_sort)); + } + + list_size = weechat_arraylist_size (server->list->channels); + for (i = 0; i < list_size; i++) + { + ptr_channel = (struct t_irc_list_channel *)weechat_arraylist_get ( + server->list->channels, i); + if (!ptr_channel) + continue; + if (irc_list_channel_match_filter (server, ptr_channel)) + weechat_arraylist_add (server->list->filter_channels, ptr_channel); + } +} + +/* + * Parses output of redirected /list (string with raw IRC messages separated + * by newlines) and returns an arraylist of parsed channels, or NULL if no + * valid message (322) was found in output. + * + * Returns: + * 1: OK + * 0: error + */ + +int +irc_list_parse_messages (struct t_irc_server *server, const char *output) +{ + struct t_irc_list_channel *channel; + char **irc_msgs, *command, **params, *error; + const char *ptr_name; + int i, count_irc_msgs, num_params, length, keep_colors; + long number; + + if (server->list->channels) + { + weechat_arraylist_free (server->list->channels); + server->list->channels = NULL; + } + + irc_msgs = weechat_string_split (output, "\n", NULL, 0, 0, &count_irc_msgs); + if (!irc_msgs) + return 0; + + server->list->channels = weechat_arraylist_new ( + 16, 0, 1, + NULL, NULL, + &irc_list_free_cb, NULL); + if (!server->list->channels) + { + weechat_string_free_split (irc_msgs); + return 0; + } + + server->list->name_max_length = 0; + + keep_colors = (weechat_config_boolean ( + irc_config_look_list_buffer_topic_strip_colors)) ? + 0 : 1; + + for (i = 0; i < count_irc_msgs; i++) + { + irc_message_parse (server, irc_msgs[i], + NULL, /* tags */ + NULL, /* message_without_tags */ + NULL, /* nick */ + NULL, /* user */ + NULL, /* host */ + &command, + NULL, /* channel */ + NULL, /* arguments */ + NULL, /* text */ + ¶ms, + &num_params, + NULL, /* pos_command */ + NULL, /* pos_arguments */ + NULL, /* pos_channel */ + NULL); /* pos_text */ + if (command + && (strcmp (command, "322") == 0) + && params + && (num_params >= 3)) + { + channel = malloc (sizeof (*channel)); + if (channel) + { + channel->name = strdup (params[1]); + ptr_name = params[1] + 1; + while (ptr_name[0] && (ptr_name[0] == params[1][0])) + { + ptr_name++; + } + channel->name2 = strdup (ptr_name); + number = strtol (params[2], &error, 10); + channel->users = (error && !error[0]) ? number : 0; + channel->topic = (num_params > 3) ? + irc_color_decode (params[3], keep_colors) : NULL; + length = weechat_utf8_strlen_screen (channel->name); + if (length > server->list->name_max_length) + server->list->name_max_length = length; + weechat_arraylist_add (server->list->channels, channel); + } + } + if (command) + free (command); + if (params) + weechat_string_free_split (params); + } + + weechat_string_free_split (irc_msgs); + + irc_list_filter_channels (server); + + return 1; +} + +/* + * Sets title of list buffer. + */ + +void +irc_list_buffer_set_title (struct t_irc_server *server) +{ + int num_channels, num_channels_total; + char str_title[8192]; + + if (!server || !server->list->buffer) + return; + + num_channels = (server->list->filter_channels) ? + weechat_arraylist_size (server->list->filter_channels) : 0; + num_channels_total = (server->list->channels) ? + weechat_arraylist_size (server->list->channels) : 0; + + snprintf (str_title, sizeof (str_title), + _("%d channels (total: %d) | Filter: %s | Sort: %s | " + "Key(input): " + "ctrl+j=join channel, " + "($)=refresh, " + "(q)=close buffer"), + num_channels, + num_channels_total, + (server->list->filter) ? server->list->filter : "*", + (server->list->sort) ? server->list->sort : ""); + + weechat_buffer_set (server->list->buffer, "title", str_title); +} + +/* + * Displays a line. + */ + +void +irc_list_display_line (struct t_irc_server *server, int line) +{ + struct t_irc_list_channel *ptr_channel; + const char *ptr_color; + char str_spaces[1024], color[256]; + int num_spaces; + + ptr_channel = (struct t_irc_list_channel *)weechat_arraylist_get ( + server->list->filter_channels, line); + + /* line color */ + if (line == server->list->selected_line) + { + snprintf (color, sizeof (color), + "%s,%s", + weechat_config_string (irc_config_color_list_buffer_line_selected), + weechat_config_string (irc_config_color_list_buffer_line_selected_bg)); + ptr_color = weechat_color (color); + } + else + { + ptr_color = NULL; + } + + /* channel name */ + str_spaces[0] = '\0'; + num_spaces = server->list->name_max_length + - weechat_utf8_strlen_screen (ptr_channel->name); + if (num_spaces > 0) + { + if (num_spaces >= (int)sizeof (str_spaces)) + num_spaces = sizeof (str_spaces) - 1; + memset (str_spaces, ' ', num_spaces); + str_spaces[num_spaces] = '\0'; + } + + /* display the line */ + weechat_printf_y ( + server->list->buffer, + line, + "%s%s%s %7d %s", + (ptr_color) ? ptr_color : "", + ptr_channel->name, + str_spaces, + ptr_channel->users, + ptr_channel->topic); +} + +/* + * Updates list of channels in list buffer. + */ + +void +irc_list_buffer_refresh (struct t_irc_server *server, int clear) +{ + int num_channels, i; + + if (!server || !server->list->buffer) + return; + + num_channels = weechat_arraylist_size (server->list->filter_channels); + + if (clear) + { + weechat_buffer_clear (server->list->buffer); + server->list->selected_line = 0; + } + + for (i = 0; i < num_channels; i++) + { + irc_list_display_line (server, i); + } + + irc_list_buffer_set_title (server); +} + +/* + * Sets current selected line. + */ + +void +irc_list_set_current_line (struct t_irc_server *server, int line) +{ + int old_line; + + if ((line >= 0) && (line < weechat_arraylist_size (server->list->filter_channels))) + { + old_line = server->list->selected_line; + server->list->selected_line = line; + + if (old_line != server->list->selected_line) + irc_list_display_line (server, old_line); + irc_list_display_line (server, server->list->selected_line); + + irc_list_buffer_set_title (server); + } +} + +/* + * Gets info about a window. + */ + +void +irc_list_get_window_info (struct t_gui_window *window, + int *start_line_y, int *chat_height) +{ + struct t_hdata *hdata_window, *hdata_window_scroll, *hdata_line; + struct t_hdata *hdata_line_data; + void *window_scroll, *start_line, *line_data; + + hdata_window = weechat_hdata_get ("window"); + hdata_window_scroll = weechat_hdata_get ("window_scroll"); + hdata_line = weechat_hdata_get ("line"); + hdata_line_data = weechat_hdata_get ("line_data"); + *start_line_y = 0; + window_scroll = weechat_hdata_pointer (hdata_window, window, "scroll"); + if (window_scroll) + { + start_line = weechat_hdata_pointer (hdata_window_scroll, window_scroll, + "start_line"); + if (start_line) + { + line_data = weechat_hdata_pointer (hdata_line, start_line, "data"); + if (line_data) + { + *start_line_y = weechat_hdata_integer (hdata_line_data, + line_data, "y"); + } + } + } + *chat_height = weechat_hdata_integer (hdata_window, window, + "win_chat_height"); +} + +/* + * Checks if current line is outside window and adjusts scroll if needed. + */ + +void +irc_list_check_line_outside_window (struct t_irc_server *server) +{ + struct t_gui_window *window; + int start_line_y, chat_height; + int selected_y; + char str_command[256]; + + window = weechat_window_search_with_buffer (server->list->buffer); + if (!window) + return; + + irc_list_get_window_info (window, &start_line_y, &chat_height); + + selected_y = server->list->selected_line; + + if ((start_line_y > selected_y) + || (start_line_y < selected_y - chat_height + 1)) + { + snprintf (str_command, sizeof (str_command), + "/window scroll -window %d %s%d", + weechat_window_get_integer (window, "number"), + (start_line_y > selected_y) ? "-" : "+", + (start_line_y > selected_y) ? + start_line_y - selected_y : + selected_y - start_line_y - chat_height + 1); + weechat_command (server->list->buffer, str_command); + } +} + +/* + * Callback for signal "window_scrolled". + */ + +int +irc_list_window_scrolled_cb (const void *pointer, void *data, + const char *signal, const char *type_data, + void *signal_data) +{ + struct t_gui_buffer *ptr_buffer; + struct t_irc_server *ptr_server; + int start_line_y, chat_height, line, num_channels; + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) signal; + (void) type_data; + + /* search the /list buffer */ + ptr_buffer = weechat_window_get_pointer (signal_data, "buffer"); + for (ptr_server = irc_servers; ptr_server; + ptr_server = ptr_server->next_server) + { + if (ptr_server->list->buffer == ptr_buffer) + break; + } + if (!ptr_server) + return WEECHAT_RC_OK; + + irc_list_get_window_info (signal_data, &start_line_y, &chat_height); + + line = ptr_server->list->selected_line; + while (line < start_line_y) + { + line += chat_height; + } + while (line >= start_line_y + chat_height) + { + line -= chat_height; + } + if (line < start_line_y) + line = start_line_y + 1; + + num_channels = weechat_arraylist_size (ptr_server->list->filter_channels); + if ((num_channels > 0) && (line >= num_channels)) + line = num_channels - 1; + + irc_list_set_current_line (ptr_server, line); + + return WEECHAT_RC_OK; +} + +/* + * Moves N lines up/down in buffer + * (negative lines = move up, positive lines = move down). + */ + +void +irc_list_move_line_relative (struct t_irc_server *server, int num_lines) +{ + int num_channels, line; + + num_channels = weechat_arraylist_size (server->list->filter_channels); + if (num_channels == 0) + return; + + line = server->list->selected_line + num_lines; + if (line < 0) + line = 0; + if ((num_channels > 0) && (line >= num_channels)) + line = num_channels - 1; + if (line != server->list->selected_line) + { + irc_list_set_current_line (server, line); + irc_list_check_line_outside_window (server); + } +} + +/* + * Moves to line N (0 = first line, -1 = last line). + */ + +void +irc_list_move_line_absolute (struct t_irc_server *server, int line_number) +{ + int num_channels, line; + + num_channels = weechat_arraylist_size (server->list->filter_channels); + if (num_channels == 0) + return; + + line = line_number; + if (line < 0) + line = (num_channels > 0) ? num_channels - 1 : 0; + if ((num_channels > 0) && (line >= num_channels)) + line = num_channels - 1; + if (line != server->list->selected_line) + { + irc_list_set_current_line (server, line); + irc_list_check_line_outside_window (server); + } +} + +/* + * Scrolls horizontally with percent + * (negative: scroll to the left, positive: scroll to the right). + */ + +void +irc_list_scroll_horizontal (struct t_irc_server *server, int percent) +{ + struct t_gui_window *ptr_window; + char str_command[512]; + + if (percent < -100) + percent = -100; + else if (percent > 100) + percent = 100; + + ptr_window = weechat_window_search_with_buffer (server->list->buffer); + if (!ptr_window) + return; + + snprintf (str_command, sizeof (str_command), + "/window scroll_horiz -window %d %d%%", + weechat_window_get_integer (ptr_window, "number"), + percent); + weechat_command (server->list->buffer, str_command); +} + +/* + * Joins channel on current selected line. + */ + +void +irc_list_join_channel (struct t_irc_server *server) +{ + struct t_irc_list_channel *ptr_channel; + int num_channels; + char str_command[1024]; + + num_channels = weechat_arraylist_size (server->list->filter_channels); + + if ((num_channels == 0) || (server->list->selected_line >= num_channels)) + return; + + ptr_channel = (struct t_irc_list_channel *)weechat_arraylist_get ( + server->list->filter_channels, + server->list->selected_line); + if (!ptr_channel) + return; + + snprintf (str_command, sizeof (str_command), + "/join %s", ptr_channel->name); + + weechat_command (server->list->buffer, str_command); +} + +/* + * Callback for input data in list buffer. + */ + +int +irc_list_buffer_input_data (struct t_gui_buffer *buffer, const char *input_data) +{ + struct t_irc_server *ptr_server; + const char *ptr_server_name, *ptr_input; + int i; + char *actions[][2] = { + { "<<", "/list -go 0" }, + { ">>", "/list -go end" }, + { "<", "/list -left" }, + { ">", "/list -right" }, + { NULL, NULL }, + }; + + /* close buffer */ + if (strcmp (input_data, "q") == 0) + { + weechat_buffer_close (buffer); + return WEECHAT_RC_OK; + } + + ptr_server_name = weechat_buffer_get_string (buffer, "localvar_server"); + if (!ptr_server_name) + return WEECHAT_RC_OK; + + ptr_server = irc_server_search (ptr_server_name); + if (!ptr_server) + return WEECHAT_RC_OK; + + /* refresh buffer */ + if (strcmp (input_data, "$") == 0) + { + weechat_command (ptr_server->list->buffer, "/list"); + return WEECHAT_RC_OK; + } + + /* join channel */ + if (strcmp (input_data, "j") == 0) + { + irc_list_join_channel (ptr_server); + return WEECHAT_RC_OK; + } + + /* change sort of channels */ + if (strncmp (input_data, "s:", 2) == 0) + { + irc_list_set_sort (ptr_server, input_data + 2); + irc_list_filter_channels (ptr_server); + irc_list_buffer_refresh (ptr_server, 1); + weechat_buffer_set (buffer, "display", "1"); + return WEECHAT_RC_OK; + } + + /* execute action */ + for (i = 0; actions[i][0]; i++) + { + if (strcmp (input_data, actions[i][0]) == 0) + { + weechat_command (buffer, actions[i][1]); + return WEECHAT_RC_OK; + } + } + + /* filter channels with given text */ + ptr_input = input_data; + while (ptr_input[0] == ' ') + { + ptr_input++; + } + if (ptr_input[0]) + { + irc_list_set_filter (ptr_server, ptr_input); + irc_list_filter_channels (ptr_server); + irc_list_buffer_refresh (ptr_server, 1); + weechat_buffer_set (buffer, "display", "1"); + } + + return WEECHAT_RC_OK; +} + +/* + * Sets keys on list buffer. + */ + +void +irc_list_buffer_set_keys (struct t_gui_buffer *buffer) +{ + char *keys[][2] = { + { "up", "/list -up" }, + { "down", "/list -down" }, + { "meta-home", "/list -go 0" }, + { "meta-end", "/list -go end" }, + { "f11", "/list -left" }, + { "f12", "/list -right" }, + { "ctrl-j", "/list -join" }, + { NULL, NULL }, + }; + char str_key[64]; + int i; + + for (i = 0; keys[i][0]; i++) + { + snprintf (str_key, sizeof (str_key), "key_bind_%s", keys[i][0]); + weechat_buffer_set (buffer, str_key, keys[i][1]); + } +} + +/* + * Creates buffer with list of channels for a server. + * + * Returns pointer to newly created buffer, NULL if error. + */ + +struct t_gui_buffer * +irc_list_create_buffer (struct t_irc_server *server) +{ + struct t_hashtable *buffer_props; + struct t_gui_buffer *buffer; + char buffer_name[1024], str_number[32]; + int buffer_position, current_buffer_number; + + buffer_props = weechat_hashtable_new ( + 32, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_STRING, + NULL, NULL); + if (buffer_props) + { + weechat_hashtable_set (buffer_props, "type", "free"); + weechat_hashtable_set (buffer_props, "localvar_set_type", "irc_list"); + weechat_hashtable_set (buffer_props, "localvar_set_server", server->name); + weechat_hashtable_set (buffer_props, "localvar_set_channel", server->name); + weechat_hashtable_set (buffer_props, "localvar_set_no_log", "1"); + /* disable all highlights on this buffer */ + weechat_hashtable_set (buffer_props, "highlight_words", "-"); + } + + current_buffer_number = weechat_buffer_get_integer ( + weechat_current_buffer (), "number"); + + snprintf (buffer_name, sizeof (buffer_name), "list_%s", server->name); + + buffer = weechat_buffer_new_props ( + buffer_name, + buffer_props, + &irc_input_data_cb, NULL, NULL, + &irc_buffer_close_cb, NULL, NULL); + + if (buffer_props) + weechat_hashtable_free (buffer_props); + + irc_list_buffer_set_keys (buffer); + + if (weechat_buffer_get_integer (buffer, "layout_number") < 1) + { + buffer_position = weechat_config_enum (irc_config_look_new_list_position); + switch (buffer_position) + { + case IRC_CONFIG_LOOK_BUFFER_POSITION_NONE: + /* do nothing */ + break; + case IRC_CONFIG_LOOK_BUFFER_POSITION_NEXT: + /* move buffer to current number + 1 */ + snprintf (str_number, sizeof (str_number), + "%d", current_buffer_number + 1); + weechat_buffer_set (buffer, "number", str_number); + break; + case IRC_CONFIG_LOOK_BUFFER_POSITION_NEAR_SERVER: + /* move buffer after last channel/pv of server */ + irc_buffer_move_near_server ( + server, + 1, /* list_buffer */ + -1, /* channel_type */ + buffer); + break; + } + } + + return buffer; +} + +/* + * Callback for redirected /list command. + */ + +int +irc_list_hsignal_redirect_list_cb (const void *pointer, + void *data, + const char *signal, + struct t_hashtable *hashtable) +{ + struct t_irc_server *ptr_server; + const char *ptr_error, *ptr_server_name, *ptr_output; + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) signal; + + ptr_error = weechat_hashtable_get (hashtable, "error"); + if (ptr_error && ptr_error[0]) + { + weechat_printf ( + NULL, + _("%s%s: error in redirection of /list: %s"), + weechat_prefix ("error"), IRC_PLUGIN_NAME, ptr_error); + return WEECHAT_RC_OK; + } + + ptr_server_name = weechat_hashtable_get (hashtable, "server"); + if (!ptr_server_name) + return WEECHAT_RC_OK; + + ptr_server = irc_server_search (ptr_server_name); + if (!ptr_server || !ptr_server->buffer) + return WEECHAT_RC_OK; + + ptr_output = weechat_hashtable_get (hashtable, "output"); + if (!ptr_output) + return WEECHAT_RC_OK; + + if (!irc_list_hdata_list_channel) + { + irc_list_hdata_list_channel = weechat_hdata_get ("irc_list_channel"); + if (!irc_list_hdata_list_channel) + return WEECHAT_RC_OK; + } + + irc_list_parse_messages (ptr_server, ptr_output); + if (!ptr_server->list->channels) + return WEECHAT_RC_OK; + + irc_list_buffer_refresh (ptr_server, 1); + + return WEECHAT_RC_OK; +} + +/* + * Resets lists used by list buffer. + */ + +void +irc_list_reset (struct t_irc_server *server) +{ + if (!server) + return; + + if (server->list->channels) + weechat_arraylist_clear (server->list->channels); + if (server->list->filter_channels) + weechat_arraylist_clear (server->list->filter_channels); + server->list->name_max_length = 0; + if (!server->list->sort) + { + irc_list_set_sort ( + server, + weechat_config_string (irc_config_look_list_buffer_sort)); + } + server->list->selected_line = 0; +} + +/* + * Frees a list structure in a server. + */ + +struct t_irc_list * +irc_list_alloc () +{ + struct t_irc_list *list; + + list = malloc (sizeof (*list)); + if (!list) + return NULL; + + list->buffer = NULL; + list->channels = NULL; + list->filter_channels = NULL; + list->name_max_length = 0; + list->filter = NULL; + list->sort = NULL; + list->sort_fields = NULL; + list->sort_fields_count = 0; + list->selected_line = 0; + + return list; +} + +/* + * Frees a list structure in a server. + */ + +void +irc_list_free (struct t_irc_server *server) +{ + if (!server || !server->list) + return; + + if (server->list->buffer) + weechat_buffer_close (server->list->buffer); + if (server->list->channels) + weechat_arraylist_free (server->list->channels); + if (server->list->filter_channels) + weechat_arraylist_free (server->list->filter_channels); + if (server->list->filter) + free (server->list->filter); + if (server->list->sort) + free (server->list->sort); + if (server->list->sort_fields) + weechat_string_free_split (server->list->sort_fields); + + free (server->list); + server->list = NULL; +} + +/* + * Returns hdata for irc_list_channel. + */ + +struct t_hdata * +irc_list_hdata_list_channel_cb (const void *pointer, void *data, + const char *hdata_name) +{ + struct t_hdata *hdata; + + /* make C compiler happy */ + (void) pointer; + (void) data; + + hdata = weechat_hdata_new (hdata_name, NULL, NULL, 0, 0, NULL, NULL); + if (hdata) + { + WEECHAT_HDATA_VAR(struct t_irc_list_channel, name, STRING, 0, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_irc_list_channel, name2, STRING, 0, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_irc_list_channel, users, INTEGER, 0, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_irc_list_channel, topic, STRING, 0, NULL, NULL); + } + return hdata; +} + +/* + * Returns hdata for irc_list. + */ + +struct t_hdata * +irc_list_hdata_list_cb (const void *pointer, void *data, const char *hdata_name) +{ + struct t_hdata *hdata; + + /* make C compiler happy */ + (void) pointer; + (void) data; + + hdata = weechat_hdata_new (hdata_name, NULL, NULL, 0, 0, NULL, NULL); + if (hdata) + { + WEECHAT_HDATA_VAR(struct t_irc_list, buffer, POINTER, 0, NULL, "buffer"); + WEECHAT_HDATA_VAR(struct t_irc_list, channels, POINTER, 0, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_irc_list, filter_channels, POINTER, 0, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_irc_list, name_max_length, INTEGER, 0, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_irc_list, filter, STRING, 0, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_irc_list, sort, STRING, 0, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_irc_list, sort_fields, POINTER, 0, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_irc_list, sort_fields_count, INTEGER, 0, NULL, NULL); + WEECHAT_HDATA_VAR(struct t_irc_list, selected_line, INTEGER, 0, NULL, NULL); + } + return hdata; +} + +/* + * Callback called when a mouse action occurs in irc list buffer. + */ + +int +irc_list_mouse_hsignal_cb (const void *pointer, void *data, const char *signal, + struct t_hashtable *hashtable) +{ + const char *ptr_key, *ptr_chat_line_y, *ptr_buffer_pointer; + struct t_gui_buffer *ptr_buffer; + unsigned long value; + char str_command[1024]; + int rc; + + /* make C compiler happy */ + (void) pointer; + (void) data; + (void) signal; + + ptr_key = weechat_hashtable_get (hashtable, "_key"); + ptr_buffer_pointer = weechat_hashtable_get (hashtable, "_buffer"); + ptr_chat_line_y = weechat_hashtable_get (hashtable, "_chat_line_y"); + + if (!ptr_key || !ptr_buffer_pointer || !ptr_chat_line_y) + return WEECHAT_RC_OK; + + rc = sscanf (ptr_buffer_pointer, "%lx", &value); + if ((rc == EOF) || (rc == 0)) + return WEECHAT_RC_OK; + ptr_buffer = (struct t_gui_buffer *)value; + if (!ptr_buffer) + return WEECHAT_RC_OK; + + snprintf (str_command, sizeof (str_command), + "/list -go %s", + ptr_chat_line_y); + weechat_command (ptr_buffer, str_command); + + if (weechat_string_match (ptr_key, "button2*", 1)) + weechat_command (ptr_buffer, "/list -join"); + + return WEECHAT_RC_OK; +} + +/* + * Initializes irc list. + */ + +void +irc_list_init () +{ + struct t_hashtable *keys; + + irc_list_filter_hashtable_pointers = weechat_hashtable_new ( + 8, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_POINTER, + NULL, NULL); + irc_list_filter_hashtable_extra_vars = weechat_hashtable_new ( + 32, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_STRING, + NULL, NULL); + irc_list_filter_hashtable_options = weechat_hashtable_new ( + 8, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_STRING, + NULL, NULL); + if (irc_list_filter_hashtable_options) + { + weechat_hashtable_set (irc_list_filter_hashtable_options, + "type", "condition"); + } + + weechat_hook_hsignal (IRC_LIST_MOUSE_HSIGNAL, + &irc_list_mouse_hsignal_cb, NULL, NULL); + + keys = weechat_hashtable_new (32, + WEECHAT_HASHTABLE_STRING, + WEECHAT_HASHTABLE_STRING, + NULL, NULL); + if (keys) + { + weechat_hashtable_set ( + keys, + "@chat(" IRC_PLUGIN_NAME ".list_*):button1", + "/window ${_window_number};/list -go ${_chat_line_y}"); + weechat_hashtable_set ( + keys, + "@chat(" IRC_PLUGIN_NAME ".list_*):button2*", + "hsignal:" IRC_LIST_MOUSE_HSIGNAL); + weechat_hashtable_set ( + keys, + "@chat(" IRC_PLUGIN_NAME ".list_*):wheelup", + "/list -up 5"); + weechat_hashtable_set ( + keys, + "@chat(" IRC_PLUGIN_NAME ".list_*):wheeldown", + "/list -down 5"); + weechat_hashtable_set (keys, "__quiet", "1"); + weechat_key_bind ("mouse", keys); + weechat_hashtable_free (keys); + } +} + +/* + * Ends irc list. + */ + +void +irc_list_end () +{ + if (irc_list_filter_hashtable_pointers) + { + weechat_hashtable_free (irc_list_filter_hashtable_pointers); + irc_list_filter_hashtable_pointers = NULL; + } + if (irc_list_filter_hashtable_extra_vars) + { + weechat_hashtable_free (irc_list_filter_hashtable_extra_vars); + irc_list_filter_hashtable_extra_vars = NULL; + } + if (irc_list_filter_hashtable_options) + { + weechat_hashtable_free (irc_list_filter_hashtable_options); + irc_list_filter_hashtable_options = NULL; + } +} |