pidgin/pidgin

Parents 3820d0d16092
Children 3f497be65b2e
Create a new PidginContactList widget that shows the PurplePersons that PurpleContactManager is tracking

This has a few caveats with it.

First of all, we're using closures just to make stuff work for now since we can't bind properties. A side effect of that, is that the list won't update when the property changes on the Person/Contact. That will be fixed later as we clean up a bunch of stuff like porting PidginAvatar to PurpleAvatar.

For the text, I skipped the status messages and idle times for now because they won't update as their not bound properties and will require cleaning up the status API to make that work at all.

Person/Contact don't have a `name-for-display` property, so right now that's implemented as a closure as well.

I also skipped search and sorting for now, to keep the review size small.

Regardless, this looks pretty snazzy if I do say so!

Testing Done:
Ran the program and verified stuff was working.

Reviewed at https://reviews.imfreedom.org/r/2146/
--- a/libpurple/purplecontactmanager.c Thu Jan 12 17:23:37 2023 -0600
+++ b/libpurple/purplecontactmanager.c Fri Jan 13 00:55:50 2023 -0600
@@ -580,6 +580,7 @@
PurpleAccount *account = NULL;
PurpleContact *contact = NULL;
PurpleContactInfo *info = NULL;
+ PurplePerson *person = NULL;
PurplePresence *buddy_presence = NULL;
PurplePresence *contact_presence = NULL;
const gchar *id = NULL;
@@ -593,6 +594,10 @@
contact = purple_contact_new(account, id);
info = PURPLE_CONTACT_INFO(contact);
+ person = purple_person_new();
+ purple_contact_info_set_person(info, person);
+ purple_person_add_contact_info(person, info);
+
/* Bind all of the properties. */
g_object_bind_property(buddy, "name", contact, "username",
G_BINDING_SYNC_CREATE | G_BINDING_BIDIRECTIONAL);
--- a/meson.build Thu Jan 12 17:23:37 2023 -0600
+++ b/meson.build Fri Jan 13 00:55:50 2023 -0600
@@ -235,7 +235,7 @@
# Check Pidgin dependencies
#######################################################################
if get_option('gtkui')
- gtk = dependency('gtk4', version : '>= 4.0.0')
+ gtk = dependency('gtk4', version : '>= 4.6.0')
libadwaita = dependency('libadwaita-1', version : '>= 1.2')
talkatu_dep = dependency('talkatu',
--- a/pidgin/meson.build Thu Jan 12 17:23:37 2023 -0600
+++ b/pidgin/meson.build Fri Jan 13 00:55:50 2023 -0600
@@ -34,6 +34,7 @@
'pidginavatar.c',
'pidgincolor.c',
'pidgincommands.c',
+ 'pidgincontactlist.c',
'pidgincontactlistwindow.c',
'pidgindebug.c',
'pidgindisplaywindow.c',
@@ -98,6 +99,7 @@
'pidginattachment.h',
'pidginavatar.h',
'pidgincolor.h',
+ 'pidgincontactlist.h',
'pidgincontactlistwindow.h',
'pidgincore.h',
'pidgindisplaywindow.h',
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidgincontactlist.c Fri Jan 13 00:55:50 2023 -0600
@@ -0,0 +1,121 @@
+/*
+ * Pidgin - Internet Messenger
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n-lib.h>
+
+#include <purple.h>
+
+#include "pidgin/pidgincontactlist.h"
+
+struct _PidginContactList {
+ AdwBin parent;
+
+ GtkWidget *view;
+};
+
+G_DEFINE_TYPE(PidginContactList, pidgin_contact_list, ADW_TYPE_BIN)
+
+/******************************************************************************
+ * Callbacks
+ *****************************************************************************/
+static GdkTexture *
+pidgin_contact_list_avatar_cb(G_GNUC_UNUSED GObject *self,
+ PurplePerson *person,
+ G_GNUC_UNUSED gpointer data)
+{
+ PurpleContactInfo *info = NULL;
+ PurpleContact *contact = NULL;
+ PurpleBuddyIcon *icon = NULL;
+ GdkTexture *texture = NULL;
+
+ info = purple_person_get_priority_contact_info(person);
+
+ /* All of the contact info in the manager are PurpleContact's so this cast
+ * is fine.
+ */
+ contact = PURPLE_CONTACT(info);
+
+ icon = purple_buddy_icons_find(purple_contact_get_account(contact),
+ purple_contact_info_get_username(info));
+
+ if(icon != NULL) {
+ GBytes *bytes = NULL;
+ GError *error = NULL;
+ gsize size;
+ gconstpointer data = NULL;
+
+ data = purple_buddy_icon_get_data(icon, &size);
+ bytes = g_bytes_new(data, size);
+
+ texture = gdk_texture_new_from_bytes(bytes, &error);
+ g_bytes_unref(bytes);
+
+ if(error != NULL) {
+ g_warning("Failed to create texture: %s", error->message);
+
+ g_clear_error(&error);
+ }
+ }
+
+ return texture;
+}
+
+/******************************************************************************
+ * GObject Implementation
+ *****************************************************************************/
+static void
+pidgin_contact_list_init(PidginContactList *list) {
+ PurpleContactManager *manager = NULL;
+ GtkSingleSelection *selection = NULL;
+
+ gtk_widget_init_template(GTK_WIDGET(list));
+
+ manager = purple_contact_manager_get_default();
+ selection = gtk_single_selection_new(G_LIST_MODEL(manager));
+
+ gtk_list_view_set_model(GTK_LIST_VIEW(list->view),
+ GTK_SELECTION_MODEL(selection));
+}
+
+static void
+pidgin_contact_list_class_init(PidginContactListClass *klass) {
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
+
+ gtk_widget_class_set_template_from_resource(
+ widget_class,
+ "/im/pidgin/Pidgin3/ContactList/widget.ui"
+ );
+
+ gtk_widget_class_bind_template_child(widget_class, PidginContactList,
+ view);
+
+ gtk_widget_class_bind_template_callback(widget_class,
+ pidgin_contact_list_avatar_cb);
+}
+
+/******************************************************************************
+ * API
+ *****************************************************************************/
+GtkWidget *
+pidgin_contact_list_new(void) {
+ return g_object_new(PIDGIN_TYPE_CONTACT_LIST, NULL);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/pidgincontactlist.h Fri Jan 13 00:55:50 2023 -0600
@@ -0,0 +1,64 @@
+/*
+ * Pidgin - Internet Messenger
+ * Copyright (C) Pidgin Developers <devel@pidgin.im>
+ *
+ * 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, see <https://www.gnu.org/licenses/>.
+ */
+
+#if !defined(PIDGIN_GLOBAL_HEADER_INSIDE) && !defined(PIDGIN_COMPILATION)
+# error "only <pidgin.h> may be included directly"
+#endif
+
+#ifndef PIDGIN_CONTACT_LIST_H
+#define PIDGIN_CONTACT_LIST_H
+
+#include <glib.h>
+
+#include <adwaita.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+/**
+ * PidginContactList:
+ *
+ * #PidginContactList is a widget that displays the [iface@Gio.ListModel] of
+ * [class@Purple.Person] from [class@Purple.ContactManager].
+ *
+ * Since: 3.0.0
+ */
+
+#define PIDGIN_TYPE_CONTACT_LIST (pidgin_contact_list_get_type())
+G_DECLARE_FINAL_TYPE(PidginContactList, pidgin_contact_list, PIDGIN,
+ CONTACT_LIST, AdwBin)
+
+/**
+ * pidgin_contact_list_new:
+ *
+ * Creates a new #PidginContactList instance that will display
+ * [iface@Gio.ListModel] of [class@Purple.Person] from
+ * [class@Purple.ContactManager].
+ *
+ * Returns: (transfer full): The new #PidginContactList instance.
+ */
+GtkWidget *pidgin_contact_list_new(void);
+
+G_END_DECLS
+
+#endif /* PIDGIN_CONTACT_LIST_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/pidgin/resources/ContactList/widget.ui Fri Jan 13 00:55:50 2023 -0600
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+Pidgin - Internet Messenger
+Copyright (C) Pidgin Developers <devel@pidgin.im>
+
+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 library; if not, see <https://www.gnu.org/licenses/>.
+-->
+<interface domain="pidgin">
+ <requires lib="gtk" version="4.0"/>
+ <requires lib="Adw" version="1.0"/>
+ <requires lib="pidgin" version="3.0"/>
+ <!-- interface-license-type gplv2 -->
+ <!-- interface-name Pidgin -->
+ <!-- interface-description Internet Messenger -->
+ <!-- interface-copyright Pidgin Developers <devel@pidgin.im> -->
+ <template class="PidginContactList" parent="AdwBin">
+ <child>
+ <object class="GtkListView" id="view">
+ <property name="factory">
+ <object class="GtkBuilderListItemFactory">
+ <property name="bytes">
+<![CDATA[
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+ <template class="GtkListItem">
+ <property name="child">
+ <object class="GtkBox">
+ <property name="orientation">horizontal</property>
+ <property name="spacing">6</property>
+ <child>
+ <!-- switch to a PidginAvatar when that can take a PurpleAvatar -->
+ <object class="GtkImage" id="avatar">
+ <property name="icon-size">large</property>
+ <binding name="paintable">
+ <closure type="GdkTexture" function="pidgin_contact_list_avatar_cb">
+ <lookup name="item">GtkListItem</lookup>
+ </closure>
+ </binding>
+ </object>
+ </child>
+ <child>
+ <object class="GtkLabel">
+ <property name="xalign">0</property>
+ <property name="use-markup">1</property>
+ <binding name="label">
+ <lookup name="name-for-display" type="PurpleContactInfo">
+ <lookup name="priority-contact-info" type="PurplePerson">
+ <lookup name="item">GtkListItem</lookup>
+ </lookup>
+ </lookup>
+ </binding>
+ </object>
+ </child>
+ </object>
+ </property>
+ </template>
+</interface>
+]]>
+ </property>
+ </object>
+ </property>
+ </object>
+ </child>
+ </template>
+</interface>
--- a/pidgin/resources/Display/window.ui Thu Jan 12 17:23:37 2023 -0600
+++ b/pidgin/resources/Display/window.ui Fri Jan 13 00:55:50 2023 -0600
@@ -110,10 +110,7 @@
<object class="AdwViewStackPage">
<property name="name">__contacts__</property>
<property name="child">
- <object class="AdwStatusPage" id="contact_list">
- <property name="icon-name">system-users-symbolic</property>
- <property name="title" translatable="1">Contacts</property>
- <property name="description" translatable="1">This is just a placeholder until we build the new ContactList widget.</property>
+ <object class="PidginContactList" id="contact_list">
</object>
</property>
</object>
--- a/pidgin/resources/pidgin.gresource.xml Thu Jan 12 17:23:37 2023 -0600
+++ b/pidgin/resources/pidgin.gresource.xml Fri Jan 13 00:55:50 2023 -0600
@@ -13,6 +13,7 @@
<file compressed="true" preprocess="xml-stripblanks">Conversations/infopane.ui</file>
<file compressed="true" preprocess="xml-stripblanks">Conversations/invite_dialog.ui</file>
<file compressed="true">Conversations/tab-label.css</file>
+ <file compressed="true" preprocess="xml-stripblanks">ContactList/widget.ui</file>
<file compressed="true" preprocess="xml-stripblanks">Debug/debug.ui</file>
<file compressed="true" preprocess="xml-stripblanks">Dialogs/addbuddy.ui</file>
<file compressed="true" preprocess="xml-stripblanks">Dialogs/addchat.ui</file>