weechat/src/plugins/buflist/buflist.c

541 lines
18 KiB
C

/*
* buflist.c - Bar with list of buffers
*
* Copyright (C) 2003-2020 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 <libgen.h>
#include "../weechat-plugin.h"
#include "buflist.h"
#include "buflist-bar-item.h"
#include "buflist-command.h"
#include "buflist-config.h"
#include "buflist-info.h"
#include "buflist-mouse.h"
WEECHAT_PLUGIN_NAME(BUFLIST_PLUGIN_NAME);
WEECHAT_PLUGIN_DESCRIPTION(N_("Buffers list"));
WEECHAT_PLUGIN_AUTHOR("Sébastien Helleu <flashcode@flashtux.org>");
WEECHAT_PLUGIN_VERSION(WEECHAT_VERSION);
WEECHAT_PLUGIN_LICENSE(WEECHAT_LICENSE);
WEECHAT_PLUGIN_PRIORITY(9000);
struct t_weechat_plugin *weechat_buflist_plugin = NULL;
struct t_hdata *buflist_hdata_window = NULL;
struct t_hdata *buflist_hdata_buffer = NULL;
struct t_hdata *buflist_hdata_hotlist = NULL;
struct t_hdata *buflist_hdata_bar = NULL;
struct t_hdata *buflist_hdata_bar_item = NULL;
struct t_hdata *buflist_hdata_bar_window = NULL;
/*
* Adds the buflist bar.
*/
void
buflist_add_bar ()
{
weechat_bar_new (BUFLIST_BAR_NAME, "off", "0", "root", "", "left",
"columns_vertical", "vertical", "0", "0",
"default", "default", "default", "on",
BUFLIST_BAR_ITEM_NAME);
}
/*
* Gets IRC server and channel pointers for a buffer.
*
* According to buffer:
* - non IRC buffer: both are NULL
* - IRC server/private: server is set, channel is NULL
* - IRC channel: server and channel are set
*/
void
buflist_buffer_get_irc_pointers (struct t_gui_buffer *buffer,
void **irc_server, void **irc_channel)
{
const char *ptr_server_name, *ptr_channel_name, *ptr_name;
struct t_hdata *hdata_irc_server, *hdata_irc_channel;
*irc_server = NULL;
*irc_channel = NULL;
/* check if the buffer belongs to IRC plugin */
if (strcmp (weechat_buffer_get_string (buffer, "plugin"), "irc") != 0)
return;
/* get server name from buffer local variable */
ptr_server_name = weechat_buffer_get_string (buffer, "localvar_server");
if (!ptr_server_name || !ptr_server_name[0])
return;
/* get hdata "irc_server" (can be NULL if irc plugin is not loaded) */
hdata_irc_server = weechat_hdata_get ("irc_server");
if (!hdata_irc_server)
return;
/* search the server by name in list of servers */
*irc_server = weechat_hdata_get_list (hdata_irc_server, "irc_servers");
while (*irc_server)
{
ptr_name = weechat_hdata_string (hdata_irc_server, *irc_server, "name");
if (strcmp (ptr_name, ptr_server_name) == 0)
break;
*irc_server = weechat_hdata_move (hdata_irc_server, *irc_server, 1);
}
if (!*irc_server)
return;
/* get channel name from buffer local variable */
ptr_channel_name = weechat_buffer_get_string (buffer,
"localvar_channel");
if (!ptr_channel_name || !ptr_channel_name[0])
return;
/* get hdata "irc_channel" (can be NULL if irc plugin is not loaded) */
hdata_irc_channel = weechat_hdata_get ("irc_channel");
if (!hdata_irc_channel)
return;
/* search the channel by name in list of channels on the server */
*irc_channel = weechat_hdata_pointer (hdata_irc_server,
*irc_server, "channels");
while (*irc_channel)
{
ptr_name = weechat_hdata_string (hdata_irc_channel,
*irc_channel, "name");
if (strcmp (ptr_name, ptr_channel_name) == 0)
break;
*irc_channel = weechat_hdata_move (hdata_irc_channel, *irc_channel, 1);
}
}
/*
* Compares two inactive merged buffers.
*
* Buffers are sorted so that the active buffer and buffers immediately after
* this one are first in list, followed by the buffers before the active one.
* This sort respects the order of next active buffers that can be selected
* with ctrl-X.
*
* For example with such list of merged buffers:
*
* weechat
* freenode
* oftc (active)
* test
* another
*
* Buffers will be sorted like that:
*
* oftc (active)
* test
* another
* weechat
* freenode
*
* Returns:
* -1: buffer1 must be sorted before buffer2
* 0: no sort (buffer2 will be after buffer1 by default)
* 1: buffer2 must be sorted after buffer1
*/
int
buflist_compare_inactive_merged_buffers (struct t_gui_buffer *buffer1,
struct t_gui_buffer *buffer2)
{
int number1, number, priority, priority1, priority2, active;
struct t_gui_buffer *ptr_buffer;
number1 = weechat_hdata_integer (buflist_hdata_buffer,
buffer1, "number");
priority = 20000;
priority1 = 0;
priority2 = 0;
ptr_buffer = weechat_hdata_get_list (buflist_hdata_buffer,
"gui_buffers");
while (ptr_buffer)
{
number = weechat_hdata_integer (buflist_hdata_buffer,
ptr_buffer, "number");
if (number > number1)
break;
if (number == number1)
{
active = weechat_hdata_integer (buflist_hdata_buffer,
ptr_buffer,
"active");
if (active > 0)
priority += 20000;
if (ptr_buffer == buffer1)
priority1 = priority;
if (ptr_buffer == buffer2)
priority2 = priority;
priority--;
}
ptr_buffer = weechat_hdata_move (buflist_hdata_buffer,
ptr_buffer, 1);
}
return (priority1 > priority2) ?
1 : ((priority1 < priority2) ? -1 : 0);
}
/*
* Compares two buffers in order to add them in the sorted arraylist.
*
* The comparison is made using the list of fields defined in the option
* "buflist.look.sort".
*
* Returns:
* -1: buffer1 < buffer2
* 0: buffer1 == buffer2
* 1: buffer1 > buffer2
*/
int
buflist_compare_buffers (void *data, struct t_arraylist *arraylist,
void *pointer1, void *pointer2)
{
int i, item_number, reverse, case_sensitive, rc;
const char *ptr_field;
struct t_gui_hotlist *ptr_hotlist1, *ptr_hotlist2;
void *ptr_server1, *ptr_server2, *ptr_channel1, *ptr_channel2;
struct t_hdata *hdata_irc_server, *hdata_irc_channel;
struct t_gui_bar_item *ptr_item;
/* make C compiler happy */
(void) arraylist;
ptr_item = (struct t_gui_bar_item *)data;
item_number = buflist_bar_item_get_index_with_pointer (ptr_item);
if (item_number < 0)
item_number= 0;
hdata_irc_server = weechat_hdata_get ("irc_server");
hdata_irc_channel = weechat_hdata_get ("irc_channel");
for (i = 0; i < buflist_config_sort_fields_count[item_number]; i++)
{
rc = 0;
reverse = 1;
case_sensitive = 1;
ptr_field = buflist_config_sort_fields[item_number][i];
while ((ptr_field[0] == '-') || (ptr_field[0] == '~'))
{
if (ptr_field[0] == '-')
reverse *= -1;
else if (ptr_field[0] == '~')
case_sensitive ^= 1;
ptr_field++;
}
if (strncmp (ptr_field, "hotlist.", 8) == 0)
{
ptr_hotlist1 = weechat_hdata_pointer (buflist_hdata_buffer,
pointer1, "hotlist");
ptr_hotlist2 = weechat_hdata_pointer (buflist_hdata_buffer,
pointer2, "hotlist");
if (!ptr_hotlist1 && !ptr_hotlist2)
rc = 0;
else if (ptr_hotlist1 && !ptr_hotlist2)
rc = 1;
else if (!ptr_hotlist1 && ptr_hotlist2)
rc = -1;
else
{
rc = weechat_hdata_compare (buflist_hdata_hotlist,
pointer1, pointer2,
ptr_field + 8,
case_sensitive);
}
}
else if (strncmp (ptr_field, "irc_server.", 11) == 0)
{
if (hdata_irc_server)
{
buflist_buffer_get_irc_pointers (pointer1,
&ptr_server1, &ptr_channel1);
buflist_buffer_get_irc_pointers (pointer2,
&ptr_server2, &ptr_channel2);
rc = weechat_hdata_compare (hdata_irc_server,
ptr_server1, ptr_server2,
ptr_field + 11,
case_sensitive);
}
}
else if (strncmp (ptr_field, "irc_channel.", 12) == 0)
{
if (hdata_irc_channel)
{
buflist_buffer_get_irc_pointers (pointer1,
&ptr_server1, &ptr_channel1);
buflist_buffer_get_irc_pointers (pointer2,
&ptr_server2, &ptr_channel2);
rc = weechat_hdata_compare (hdata_irc_channel,
ptr_channel1, ptr_channel2,
ptr_field + 12,
case_sensitive);
}
}
else
{
rc = weechat_hdata_compare (buflist_hdata_buffer,
pointer1, pointer2,
ptr_field,
case_sensitive);
/*
* In case we are sorting on "active" flag and that both
* buffers have same value (it should be 0),
* we sort buffers so that the buffers immediately after the
* active one is first in list, followed by the next ones, followed
* by the buffers before the active one.
*/
if ((rc == 0)
&& (strcmp (ptr_field, "active") == 0)
&& (weechat_hdata_integer (buflist_hdata_buffer,
pointer1, "number") ==
weechat_hdata_integer (buflist_hdata_buffer,
pointer2, "number")))
{
rc = buflist_compare_inactive_merged_buffers (pointer1,
pointer2);
}
}
rc *= reverse;
if (rc != 0)
return rc;
}
return 0;
}
/*
* Builds a list of pointers to buffers, sorted according to option
* "buflist.look.sort".
*
* Returns an arraylist that must be freed by weechat_arraylist_free after use.
*/
struct t_arraylist *
buflist_sort_buffers (struct t_gui_bar_item *item)
{
struct t_arraylist *buffers;
struct t_gui_buffer *ptr_buffer;
buffers = weechat_arraylist_new (128, 1, 1,
&buflist_compare_buffers, item,
NULL, NULL);
ptr_buffer = weechat_hdata_get_list (buflist_hdata_buffer, "gui_buffers");
while (ptr_buffer)
{
weechat_arraylist_add (buffers, ptr_buffer);
ptr_buffer = weechat_hdata_move (buflist_hdata_buffer, ptr_buffer, 1);
}
return buffers;
}
/*
* Callback called when a Perl script is loaded: if the script is buffers.pl,
* then we display a warning.
*/
int
buflist_script_loaded_cb (const void *pointer, void *data, const char *signal,
const char *type_data, void *signal_data)
{
char *base_name, *base_name2;
/* make C compiler happy */
(void) pointer;
(void) data;
(void) signal;
(void) type_data;
/* display a warning only if buflist is enabled */
if (!weechat_config_boolean (buflist_config_look_enabled))
return WEECHAT_RC_OK;
if (!signal_data)
return WEECHAT_RC_OK;
base_name = basename (signal_data);
if (!base_name)
return WEECHAT_RC_OK;
base_name2 = strdup (base_name);
if (!base_name2)
return WEECHAT_RC_OK;
if (strcmp (base_name2, "buffers.pl") == 0)
{
weechat_printf (NULL,
_("%sbuflist: warning: the script buffers.pl is "
"loaded and provides a bar with list of buffers "
"similar to the buflist plugin; you may want to "
"uninstall the script buffers.pl "
"(/script remove buffers.pl) or disable/unload the "
"buflist plugin; see WeeChat release notes for more "
"information"),
weechat_prefix ("error"));
}
free (base_name2);
return WEECHAT_RC_OK;
}
/*
* Initializes buflist plugin.
*/
int
weechat_plugin_init (struct t_weechat_plugin *plugin, int argc, char *argv[])
{
struct t_hashtable *keys;
int i;
char str_key[256];
char *default_keys[][2] = {
{ /* <f1> */ "meta-OP", "/bar scroll buflist * -100%" },
{ /* <f1> */ "meta2-11~", "/bar scroll buflist * -100%" },
{ /* <f2> */ "meta-OQ", "/bar scroll buflist * +100%" },
{ /* <f2> */ "meta2-12~", "/bar scroll buflist * +100%" },
{ /* c-<f1> */ "meta2-11^", "/bar scroll buflist * -100%" },
{ /* c-<f1> */ "meta2-1;5P", "/bar scroll buflist * -100%" },
{ /* c-<f2> */ "meta2-12^", "/bar scroll buflist * +100%" },
{ /* c-<f2> */ "meta2-1;5Q", "/bar scroll buflist * +100%" },
{ /* m-<f1> */ "meta-meta-OP", "/bar scroll buflist * b" },
{ /* m-<f1> */ "meta-meta2-11~", "/bar scroll buflist * b" },
{ /* m-<f1> */ "meta2-1;3P", "/bar scroll buflist * b" },
{ /* m-<f2> */ "meta-meta-OQ", "/bar scroll buflist * e" },
{ /* m-<f2> */ "meta-meta2-12~", "/bar scroll buflist * e" },
{ /* m-<f2> */ "meta2-1;3Q", "/bar scroll buflist * e" },
{ NULL, NULL },
};
/* make C compiler happy */
(void) argc;
(void) argv;
weechat_plugin = plugin;
buflist_hdata_window = weechat_hdata_get ("window");
buflist_hdata_buffer = weechat_hdata_get ("buffer");
buflist_hdata_hotlist = weechat_hdata_get ("hotlist");
buflist_hdata_bar = weechat_hdata_get ("bar");
buflist_hdata_bar_item = weechat_hdata_get ("bar_item");
buflist_hdata_bar_window = weechat_hdata_get ("bar_window");
if (!buflist_config_init ())
return WEECHAT_RC_ERROR;
buflist_config_read ();
if (!buflist_bar_item_init ())
return WEECHAT_RC_ERROR;
buflist_config_change_sort (NULL, NULL, NULL);
buflist_command_init ();
if (weechat_config_boolean (buflist_config_look_enabled))
buflist_add_bar ();
buflist_bar_item_update (0);
buflist_mouse_init ();
/* default keys and mouse actions */
keys = weechat_hashtable_new (32,
WEECHAT_HASHTABLE_STRING,
WEECHAT_HASHTABLE_STRING,
NULL, NULL);
if (keys)
{
/* default keys */
for (i = 0; default_keys[i][0]; i++)
{
weechat_hashtable_set (keys,
default_keys[i][0], default_keys[i][1]);
}
weechat_key_bind ("default", keys);
/* default mouse actions */
weechat_hashtable_remove_all (keys);
for (i = 0; i < BUFLIST_BAR_NUM_ITEMS; i++)
{
snprintf (str_key, sizeof (str_key),
"@item(%s):button1*",
buflist_bar_item_get_name (i));
weechat_hashtable_set (keys,
str_key, "hsignal:" BUFLIST_MOUSE_HSIGNAL);
snprintf (str_key, sizeof (str_key),
"@item(%s):button2*",
buflist_bar_item_get_name (i));
weechat_hashtable_set (keys,
str_key, "hsignal:" BUFLIST_MOUSE_HSIGNAL);
}
weechat_hashtable_set (keys,
"@bar(" BUFLIST_BAR_NAME "):ctrl-wheelup",
"hsignal:" BUFLIST_MOUSE_HSIGNAL);
weechat_hashtable_set (keys,
"@bar(" BUFLIST_BAR_NAME "):ctrl-wheeldown",
"hsignal:" BUFLIST_MOUSE_HSIGNAL);
weechat_hashtable_set (keys, "__quiet", "1");
weechat_key_bind ("mouse", keys);
}
weechat_hashtable_free (keys);
weechat_hook_signal ("perl_script_loaded",
&buflist_script_loaded_cb, NULL, NULL);
buflist_info_init ();
return WEECHAT_RC_OK;
}
/*
* Ends buflist plugin.
*/
int
weechat_plugin_end (struct t_weechat_plugin *plugin)
{
/* make C compiler happy */
(void) plugin;
buflist_mouse_end ();
buflist_bar_item_end ();
buflist_config_write ();
buflist_config_free ();
return WEECHAT_RC_OK;
}