summaryrefslogtreecommitdiff
path: root/src/plugins/irc/irc-list.c
diff options
context:
space:
mode:
authorSébastien Helleu <flashcode@flashtux.org>2023-07-14 14:27:23 +0200
committerSébastien Helleu <flashcode@flashtux.org>2023-08-12 13:05:49 +0200
commit2f1de098bd3a44eaf0cfccb2c126250bb50f11b9 (patch)
tree03c6e6e5cf4b233d51b92f3542ed90636c98a202 /src/plugins/irc/irc-list.c
parentd25a4213fecf8abccfecda4229fe85bfcf4a51b7 (diff)
downloadweechat-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.c1309
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 */
+ &params,
+ &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;
+ }
+}