pidgin/gtkblist.c

Fri, 03 Nov 2023 00:50:51 -0500

author
Gary Kramlich <grim@reaperworld.com>
date
Fri, 03 Nov 2023 00:50:51 -0500
changeset 42475
ce06ca18db9b
parent 42449
bde60654600e
child 42523
7b5d8353a2b3
permissions
-rw-r--r--

Integrate PurplePresenceManager with the core and uis

The original implemenation was just the API and wasn't yet tied into the core
or ui. This fixes that.

Testing Done:
Temporarily added code to add a saved status in the `PurpleUi->get_presence_manager` implementation and verified it was saved to the correct file.

Reviewed at https://reviews.imfreedom.org/r/2771/

/* pidgin
 *
 * Pidgin is the legal property of its developers, whose names are too numerous
 * to list here.  Please refer to the COPYRIGHT file distributed with this
 * source distribution.
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02111-1301  USA
 *
 */

#include <purpleconfig.h>

#include <glib/gi18n-lib.h>

#include <purple.h>

#include "gtkblist.h"
#include "gtkconv.h"
#include "gtkroomlist.h"
#include "gtkutils.h"
#include "pidgin/pidginaccountchooser.h"
#include "pidgin/pidginaccountfilterconnected.h"
#include "pidgin/pidginaddbuddydialog.h"
#include "pidgin/pidginaddchatdialog.h"

typedef struct
{
	PurpleAccount *account;
	GtkWidget *window;
	GtkBox *vbox;
	GtkWidget *account_menu;
	GtkSizeGroup *sg;
} PidginBlistRequestData;

typedef struct
{
	PidginBlistRequestData rq_data;
	gchar *default_chat_name;
	GList *entries;
} PidginChatData;

G_DEFINE_TYPE(PidginBuddyList, pidgin_buddy_list, PURPLE_TYPE_BUDDY_LIST)

/******************************************************************************
 * Helpers
 *****************************************************************************/
static void gtk_blist_join_chat(PurpleChat *chat)
{
	PurpleAccount *account;
	GHashTable *components;

	account = purple_chat_get_account(chat);

	components = purple_chat_get_components(chat);

	purple_serv_join_chat(purple_account_get_connection(account), components);
}

/******************************************************************************
 * Actions
 *****************************************************************************/
static void
do_join_chat(PidginChatData *data)
{
	if (data)
	{
		GHashTable *components =
			g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
		GList *tmp;
		PurpleChat *chat;

		for (tmp = data->entries; tmp != NULL; tmp = tmp->next)
		{
			if (g_object_get_data(tmp->data, "is_spin"))
			{
				g_hash_table_replace(components,
					g_strdup(g_object_get_data(tmp->data, "identifier")),
					g_strdup_printf("%d",
							gtk_spin_button_get_value_as_int(tmp->data)));
			}
			else
			{
				g_hash_table_replace(components,
					g_strdup(g_object_get_data(tmp->data, "identifier")),
					g_strdup(gtk_editable_get_text(GTK_EDITABLE(tmp->data))));
			}
		}

		chat = purple_chat_new(data->rq_data.account, NULL, components);
		gtk_blist_join_chat(chat);
		purple_blist_remove_chat(chat);
	}
}

static void
do_joinchat(GtkWidget *dialog, int id, PidginChatData *info)
{
	switch(id)
	{
		case GTK_RESPONSE_OK:
			do_join_chat(info);
			break;

		case 1:
			pidgin_roomlist_dialog_show_with_account(info->rq_data.account);
			return;

		break;
	}

	gtk_window_destroy(GTK_WINDOW(dialog));
	g_list_free(info->entries);
	g_free(info);
}

/*
 * Check the values of all the text entry boxes.  If any required input
 * strings are empty then don't allow the user to click on "OK."
 */
static void
set_sensitive_if_input_chat_cb(G_GNUC_UNUSED GtkWidget *entry,
                               gpointer user_data)
{
	PurpleProtocol *protocol;
	PurpleConnection *gc;
	PidginChatData *data;
	GList *tmp;
	const char *text;
	gboolean required;
	gboolean sensitive = TRUE;

	data = user_data;

	for (tmp = data->entries; tmp != NULL; tmp = tmp->next)
	{
		if (!g_object_get_data(tmp->data, "is_spin"))
		{
			required = GPOINTER_TO_INT(g_object_get_data(tmp->data, "required"));
			text = gtk_editable_get_text(GTK_EDITABLE(tmp->data));
			if (required && (*text == '\0'))
				sensitive = FALSE;
		}
	}

	gtk_dialog_set_response_sensitive(GTK_DIALOG(data->rq_data.window), GTK_RESPONSE_OK, sensitive);

	gc = purple_account_get_connection(data->rq_data.account);
	protocol = (gc != NULL) ? purple_connection_get_protocol(gc) : NULL;
	sensitive = (protocol != NULL && PURPLE_PROTOCOL_IMPLEMENTS(protocol, ROOMLIST, get_list));

	gtk_dialog_set_response_sensitive(GTK_DIALOG(data->rq_data.window), 1, sensitive);
}

static gboolean
chat_account_filter_func(gpointer item, G_GNUC_UNUSED gpointer data) {
	PurpleConnection *gc = NULL;
	PurpleProtocol *protocol = NULL;

	if(!PURPLE_IS_ACCOUNT(item)) {
		return FALSE;
	}

	gc = purple_account_get_connection(PURPLE_ACCOUNT(item));
	if(gc == NULL) {
		return FALSE;
	}

	protocol = purple_connection_get_protocol(gc);

	return (PURPLE_PROTOCOL_IMPLEMENTS(protocol, CHAT, info));
}

gboolean
pidgin_blist_joinchat_is_showable(void)
{
	GList *c;
	PurpleConnection *gc;

	for (c = purple_connections_get_all(); c != NULL; c = c->next) {
		gc = c->data;

		if(chat_account_filter_func(purple_connection_get_account(gc), NULL)) {
			return TRUE;
		}
	}

	return FALSE;
}

static void
make_blist_request_dialog(PidginBlistRequestData *data, PurpleAccount *account,
                          const char *title, const char *label_text,
                          GCallback callback_func,
                          GtkCustomFilterFunc filter_func,
                          GCallback response_cb)
{
	GtkWidget *label;
	GtkWidget *img;
	GtkWidget *content_area;
	GtkWidget *hbox;
	GtkWidget *vbox;
	GtkEveryFilter *every = NULL;
	GtkFilter *filter = NULL;

	data->account = account;

	data->window = gtk_dialog_new();
	gtk_window_set_title(GTK_WINDOW(data->window), title);
	gtk_dialog_set_default_response(GTK_DIALOG(data->window), GTK_RESPONSE_OK);
	gtk_window_set_resizable(GTK_WINDOW(data->window), FALSE);
	content_area = gtk_dialog_get_content_area(GTK_DIALOG(data->window));
	gtk_box_set_spacing(GTK_BOX(content_area), 12);

	hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
	gtk_box_append(GTK_BOX(content_area), hbox);

	img = gtk_image_new_from_icon_name("dialog-question");
	gtk_image_set_icon_size(GTK_IMAGE(img), GTK_ICON_SIZE_LARGE);
	gtk_box_append(GTK_BOX(hbox), img);
	gtk_widget_set_halign(img, GTK_ALIGN_START);
	gtk_widget_set_valign(img, GTK_ALIGN_START);

	vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
	gtk_box_append(GTK_BOX(hbox), vbox);

	label = gtk_label_new(label_text);

	gtk_widget_set_size_request(label, 400, -1);
	gtk_label_set_wrap(GTK_LABEL(label), TRUE);
	gtk_label_set_xalign(GTK_LABEL(label), 0);
	gtk_label_set_yalign(GTK_LABEL(label), 0);
	gtk_box_append(GTK_BOX(vbox), label);

	data->sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);

	data->account_menu = pidgin_account_chooser_new();
	every = gtk_every_filter_new();
	filter = pidgin_account_filter_connected_new();
	gtk_multi_filter_append(GTK_MULTI_FILTER(every), filter);
	filter = GTK_FILTER(gtk_custom_filter_new(filter_func, NULL, NULL));
	gtk_multi_filter_append(GTK_MULTI_FILTER(every), filter);
	pidgin_account_chooser_set_filter(
	        PIDGIN_ACCOUNT_CHOOSER(data->account_menu),
	        GTK_FILTER(every));
	g_object_unref(every);

	if(PURPLE_IS_ACCOUNT(account)) {
		pidgin_account_chooser_set_selected(PIDGIN_ACCOUNT_CHOOSER(
			data->account_menu), account);
	}

	pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("A_ccount"), data->sg, data->account_menu, TRUE, NULL);
	g_signal_connect(data->account_menu, "notify::account",
	                 G_CALLBACK(callback_func), data);

	data->vbox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 5));
	gtk_box_append(GTK_BOX(vbox), GTK_WIDGET(data->vbox));

	g_signal_connect(G_OBJECT(data->window), "response", response_cb, data);

	g_object_unref(data->sg);
}

static void
rebuild_chat_entries(PidginChatData *data, const char *default_chat_name)
{
	PurpleConnection *gc;
	PurpleProtocol *protocol;
	GtkWidget *child = NULL;
	GList *list = NULL, *tmp;
	GHashTable *defaults = NULL;
	PurpleProtocolChatEntry *pce;
	gboolean focus = TRUE;

	g_return_if_fail(data->rq_data.account != NULL);

	gc = purple_account_get_connection(data->rq_data.account);
	protocol = purple_connection_get_protocol(gc);

	child = gtk_widget_get_first_child(GTK_WIDGET(data->rq_data.vbox));
	while (child != NULL) {
		gtk_widget_unparent(child);
		child = gtk_widget_get_first_child(GTK_WIDGET(data->rq_data.vbox));
	}

	g_clear_list(&data->entries, NULL);

	if(!PURPLE_IS_PROTOCOL_CHAT(protocol)) {
		return;
	}

	list = purple_protocol_chat_info(PURPLE_PROTOCOL_CHAT(protocol), gc);
	defaults = purple_protocol_chat_info_defaults(PURPLE_PROTOCOL_CHAT(protocol),
	                                              gc, default_chat_name);

	for (tmp = list; tmp; tmp = tmp->next)
	{
		GtkWidget *input;

		pce = tmp->data;

		if (pce->is_int)
		{
			GtkAdjustment *adjust;
			adjust = GTK_ADJUSTMENT(gtk_adjustment_new(pce->min,
			                                           pce->min, pce->max,
			                                           1, 10, 10));
			input = gtk_spin_button_new(adjust, 1, 0);
			gtk_widget_set_size_request(input, 50, -1);
			pidgin_add_widget_to_vbox(GTK_BOX(data->rq_data.vbox), pce->label, data->rq_data.sg, input, FALSE, NULL);
		}
		else
		{
			char *value;
			input = gtk_entry_new();
			gtk_entry_set_activates_default(GTK_ENTRY(input), TRUE);
			value = g_hash_table_lookup(defaults, pce->identifier);
			if(value != NULL) {
				gtk_editable_set_text(GTK_EDITABLE(input), value);
			}
			if (pce->secret)
			{
				gtk_entry_set_visibility(GTK_ENTRY(input), FALSE);
			}
			pidgin_add_widget_to_vbox(data->rq_data.vbox, pce->label, data->rq_data.sg, input, TRUE, NULL);
			g_signal_connect(G_OBJECT(input), "changed",
							 G_CALLBACK(set_sensitive_if_input_chat_cb), data);
		}

		/* Do the following for any type of input widget */
		if (focus)
		{
			gtk_widget_grab_focus(input);
			focus = FALSE;
		}
		g_object_set_data(G_OBJECT(input), "identifier", (gpointer)pce->identifier);
		g_object_set_data(G_OBJECT(input), "is_spin", GINT_TO_POINTER(pce->is_int));
		g_object_set_data(G_OBJECT(input), "required", GINT_TO_POINTER(pce->required));
		data->entries = g_list_append(data->entries, input);

		g_free(pce);
	}

	g_list_free(list);
	g_hash_table_destroy(defaults);

	/* Set whether the "OK" button should be clickable initially */
	set_sensitive_if_input_chat_cb(NULL, data);
}

static void
chat_select_account_cb(GObject *obj, G_GNUC_UNUSED GParamSpec *pspec,
                       gpointer user_data)
{
	PidginChatData *data = user_data;
	PidginAccountChooser *chooser = PIDGIN_ACCOUNT_CHOOSER(obj);
	PurpleAccount *account = pidgin_account_chooser_get_selected(chooser);

	g_return_if_fail(data != NULL);
	if(account == NULL) {
		return;
	}

	if (purple_strequal(purple_account_get_protocol_id(data->rq_data.account),
	                    purple_account_get_protocol_id(account)))
	{
		data->rq_data.account = account;
	}
	else
	{
		data->rq_data.account = account;
		rebuild_chat_entries(data, data->default_chat_name);
	}
}

void
pidgin_blist_joinchat_show(void)
{
	PidginChatData *data = NULL;
	PidginAccountChooser *chooser = NULL;

	data = g_new0(PidginChatData, 1);

	make_blist_request_dialog((PidginBlistRequestData *)data, NULL,
		_("Join a Chat"),
		_("Please enter the appropriate information about the chat "
			"you would like to join.\n"),
		G_CALLBACK(chat_select_account_cb),
		chat_account_filter_func, (GCallback)do_joinchat);
	gtk_dialog_add_buttons(GTK_DIALOG(data->rq_data.window),
		_("Room _List"), 1,
		_("Cancel"), GTK_RESPONSE_CANCEL,
		_("_Join"), GTK_RESPONSE_OK, NULL);
	gtk_dialog_set_default_response(GTK_DIALOG(data->rq_data.window),
		GTK_RESPONSE_OK);
	data->default_chat_name = NULL;

	chooser = PIDGIN_ACCOUNT_CHOOSER(data->rq_data.account_menu);
	data->rq_data.account = pidgin_account_chooser_get_selected(chooser);

	rebuild_chat_entries(data, NULL);

	gtk_widget_set_visible(data->rq_data.window, TRUE);
}

static void
pidgin_blist_request_add_buddy(G_GNUC_UNUSED PurpleBuddyList *list,
                               PurpleAccount *account, const char *username,
                               const char *group, const char *alias)
{
	GtkWidget *dialog = NULL;

	dialog = pidgin_add_buddy_dialog_new(account, username, alias, NULL, group);

	gtk_widget_set_visible(dialog, TRUE);
}

static void
pidgin_blist_request_add_chat(G_GNUC_UNUSED PurpleBuddyList *list,
                              PurpleAccount *account, PurpleGroup *group,
                              const char *alias, const char *name)
{
	GtkWidget *dialog = NULL;

	dialog = pidgin_add_chat_dialog_new(account, group, alias, name);

	gtk_widget_set_visible(dialog, TRUE);
}

static void
add_group_cb(G_GNUC_UNUSED PurpleConnection *connection,
             const char *group_name)
{
	PurpleGroup *group;

	if ((group_name == NULL) || (*group_name == '\0'))
		return;

	group = purple_group_new(group_name);
	purple_blist_add_group(group, NULL);
}

static void
pidgin_blist_request_add_group(G_GNUC_UNUSED PurpleBuddyList *list)
{
	purple_request_input(NULL, _("Add Group"), NULL,
					   _("Please enter the name of the group to be added."),
					   NULL, FALSE, FALSE, NULL,
					   _("Add"), G_CALLBACK(add_group_cb),
					   _("Cancel"), NULL,
					   NULL, NULL);
}

/**************************************************************************
 * GTK Buddy list GObject code
 **************************************************************************/
static void
pidgin_buddy_list_init(G_GNUC_UNUSED PidginBuddyList *self)
{
}

static void
pidgin_buddy_list_class_init(PidginBuddyListClass *klass) {
	PurpleBuddyListClass *purple_blist_class = PURPLE_BUDDY_LIST_CLASS(klass);

	purple_blist_class->request_add_buddy = pidgin_blist_request_add_buddy;
	purple_blist_class->request_add_chat = pidgin_blist_request_add_chat;
	purple_blist_class->request_add_group = pidgin_blist_request_add_group;
}

mercurial