LCOV - code coverage report
Current view: top level - src/plugins/gnome - valent-messages-window.c (source / functions) Coverage Total Hit
Test: Code Coverage Lines: 27.9 % 358 100
Test Date: 2025-11-28 01:07:15 Functions: 35.7 % 28 10
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 11.5 % 182 21

             Branch data     Line data    Source code
       1                 :             : // SPDX-License-Identifier: GPL-3.0-or-later
       2                 :             : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
       3                 :             : 
       4                 :             : #define G_LOG_DOMAIN "valent-messages-window"
       5                 :             : 
       6                 :             : #include "config.h"
       7                 :             : 
       8                 :             : #include <inttypes.h>
       9                 :             : 
      10                 :             : #include <adwaita.h>
      11                 :             : #include <glib/gi18n.h>
      12                 :             : #include <gtk/gtk.h>
      13                 :             : #include <libebook-contacts/libebook-contacts.h>
      14                 :             : #include <valent.h>
      15                 :             : 
      16                 :             : #include "valent-contact-page.h"
      17                 :             : #include "valent-contact-row.h"
      18                 :             : #include "valent-conversation-page.h"
      19                 :             : #include "valent-ui-utils-private.h"
      20                 :             : #include "valent-message-row.h"
      21                 :             : 
      22                 :             : #include "valent-messages-window.h"
      23                 :             : 
      24                 :             : 
      25                 :             : struct _ValentMessagesWindow
      26                 :             : {
      27                 :             :   AdwApplicationWindow    parent_instance;
      28                 :             : 
      29                 :             :   GListModel             *contacts;
      30                 :             :   ValentContactsAdapter  *contacts_adapter;
      31                 :             :   GListModel             *messages;
      32                 :             :   ValentMessagesAdapter  *messages_adapter;
      33                 :             :   GCancellable           *search;
      34                 :             : 
      35                 :             :   /* template */
      36                 :             :   AdwNavigationSplitView *main_view;
      37                 :             :   AdwHeaderBar           *sidebar_header;
      38                 :             :   GtkListBox             *sidebar_list;
      39                 :             :   AdwNavigationView      *content_view;
      40                 :             :   AdwNavigationPage      *search_page;
      41                 :             :   GtkWidget              *search_entry;
      42                 :             :   GtkListBox             *search_list;
      43                 :             :   AdwNavigationPage      *contact_page;
      44                 :             :   AdwDialog              *details_dialog;
      45                 :             :   GtkListBox             *medium_list;
      46                 :             : };
      47                 :             : 
      48                 :             : void   valent_messages_window_set_active_message (ValentMessagesWindow *window,
      49                 :             :                                                   ValentMessage        *message);
      50                 :             : void   valent_messages_window_set_active_thread  (ValentMessagesWindow *window,
      51                 :             :                                                   const char           *iri);
      52                 :             : 
      53   [ +  +  +  - ]:           5 : G_DEFINE_FINAL_TYPE (ValentMessagesWindow, valent_messages_window, ADW_TYPE_APPLICATION_WINDOW)
      54                 :             : 
      55                 :             : typedef enum {
      56                 :             :   PROP_MESSAGES = 1,
      57                 :             : } ValentMessagesWindowProperty;
      58                 :             : 
      59                 :             : static GParamSpec *properties[PROP_MESSAGES + 1] = { NULL, };
      60                 :             : 
      61                 :             : 
      62                 :             : /*
      63                 :             :  * Contact Lookup
      64                 :             :  */
      65                 :             : static void
      66                 :           0 : lookup_contact_cb (ValentContactsAdapter *adapter,
      67                 :             :                    GAsyncResult          *result,
      68                 :             :                    ValentMessageRow      *row)
      69                 :             : {
      70                 :           0 :   g_autoptr (EContact) contact = NULL;
      71                 :           0 :   g_autoptr (GError) error = NULL;
      72                 :             : 
      73                 :           0 :   contact = valent_contacts_adapter_reverse_lookup_finish (adapter, result, &error);
      74         [ #  # ]:           0 :   if (contact == NULL)
      75                 :             :     {
      76         [ #  # ]:           0 :       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
      77                 :           0 :         g_warning ("%s(): %s", G_STRFUNC, error->message);
      78                 :             : 
      79         [ #  # ]:           0 :       return;
      80                 :             :     }
      81                 :             : 
      82         [ #  # ]:           0 :   valent_message_row_set_contact (row, contact);
      83                 :             : }
      84                 :             : 
      85                 :             : static void
      86                 :           0 : search_contacts_cb (ValentContactsAdapter *adapter,
      87                 :             :                     GAsyncResult          *result,
      88                 :             :                     ValentMessagesWindow  *self)
      89                 :             : {
      90                 :           0 :   g_autoptr (GListModel) contacts = NULL;
      91                 :           0 :   unsigned int n_contacts;
      92   [ #  #  #  # ]:           0 :   g_autoptr (GError) error = NULL;
      93                 :             : 
      94                 :           0 :   contacts = valent_contacts_adapter_search_finish (adapter, result, &error);
      95         [ #  # ]:           0 :   if (error != NULL)
      96                 :             :     {
      97         [ #  # ]:           0 :       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
      98                 :           0 :         g_warning ("%s(): %s", G_STRFUNC, error->message);
      99                 :             : 
     100         [ #  # ]:           0 :       return;
     101                 :             :     }
     102                 :             : 
     103                 :           0 :   n_contacts = g_list_model_get_n_items (contacts);
     104         [ #  # ]:           0 :   for (unsigned int i = 0; i < n_contacts; i++)
     105                 :             :     {
     106                 :           0 :       g_autoptr (EContact) contact = g_list_model_get_item (contacts, i);
     107                 :             : #if EDS_CHECK_VERSION (3, 59, 0)
     108                 :             :       g_autoptr (GList) attrs = NULL;
     109                 :             : #else
     110   [ #  #  #  # ]:           0 :       g_autolist (EVCardAttribute) attrs = NULL;
     111                 :             : #endif
     112                 :           0 :       g_autofree char *number = NULL;
     113                 :           0 :       unsigned int n_attrs;
     114                 :           0 :       GtkWidget *row;
     115                 :             : 
     116                 :             : #if EDS_CHECK_VERSION (3, 59, 0)
     117                 :             :       attrs = e_vcard_get_attributes_by_name (E_VCARD (contact), EVC_TEL);
     118                 :             : #else
     119                 :           0 :       attrs = e_contact_get_attributes (contact, E_CONTACT_TEL);
     120                 :             : #endif
     121                 :           0 :       n_attrs = g_list_length (attrs);
     122         [ #  # ]:           0 :       if (n_attrs == 0)
     123                 :           0 :         continue;
     124                 :             : 
     125                 :           0 :       g_object_get (contact, "primary-phone", &number, NULL);
     126   [ #  #  #  # ]:           0 :       if (number == NULL || *number == '\0')
     127                 :             :         {
     128                 :           0 :           g_free (number);
     129                 :           0 :           number = e_vcard_attribute_get_value ((EVCardAttribute *)attrs->data);
     130                 :             :         }
     131                 :             : 
     132         [ #  # ]:           0 :       if (n_attrs > 1)
     133                 :             :         {
     134                 :           0 :           g_autofree char *tmp = g_steal_pointer (&number);
     135                 :             : 
     136                 :           0 :           number = g_strdup_printf (ngettext ("%s and %u more…",
     137                 :             :                                               "%s and %u more…",
     138                 :             :                                               n_attrs - 1),
     139                 :             :                                     tmp, n_attrs - 1);
     140                 :             :         }
     141                 :             : 
     142                 :           0 :       row = g_object_new (VALENT_TYPE_CONTACT_ROW,
     143                 :             :                           "contact",        contact,
     144                 :             :                           "contact-medium", number,
     145                 :             :                           NULL);
     146                 :             : 
     147         [ #  # ]:           0 :       if (n_attrs > 1)
     148                 :             :         {
     149                 :           0 :           gtk_accessible_update_state (GTK_ACCESSIBLE (row),
     150                 :             :                                        GTK_ACCESSIBLE_STATE_EXPANDED, FALSE,
     151                 :             :                                        -1);
     152                 :             :         }
     153                 :             : 
     154                 :           0 :       gtk_list_box_insert (self->search_list, row, -1);
     155                 :             :     }
     156                 :             : }
     157                 :             : 
     158                 :             : static void
     159                 :           0 : search_messages_cb (ValentMessagesAdapter *adapter,
     160                 :             :                     GAsyncResult          *result,
     161                 :             :                     ValentMessagesWindow  *self)
     162                 :             : {
     163                 :           0 :   g_autoptr (GListModel) messages = NULL;
     164                 :           0 :   unsigned int n_messages;
     165                 :           0 :   g_autoptr (GError) error = NULL;
     166                 :             : 
     167                 :           0 :   messages = valent_messages_adapter_search_finish (adapter, result, &error);
     168         [ #  # ]:           0 :   if (messages == NULL)
     169                 :             :     {
     170         [ #  # ]:           0 :       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
     171                 :           0 :         g_warning ("%s(): %s", G_STRFUNC, error->message);
     172                 :             : 
     173         [ #  # ]:           0 :       return;
     174                 :             :     }
     175                 :             : 
     176                 :           0 :   n_messages = g_list_model_get_n_items (messages);
     177         [ #  # ]:           0 :   for (unsigned int i = 0; i < n_messages; i++)
     178                 :             :     {
     179                 :           0 :       g_autoptr (ValentMessage) message = g_list_model_get_item (messages, i);
     180                 :           0 :       GtkWidget *row;
     181                 :           0 :       const char *medium;
     182                 :             : 
     183                 :           0 :       row = g_object_new (VALENT_TYPE_MESSAGE_ROW,
     184                 :             :                           "message", message,
     185                 :             :                           NULL);
     186                 :           0 :       gtk_list_box_insert (self->search_list, row, -1);
     187                 :             : 
     188                 :           0 :       medium = valent_message_get_sender (message);
     189   [ #  #  #  # ]:           0 :       if (medium == NULL || *medium == '\0')
     190                 :             :         {
     191                 :           0 :           const char * const *recipients = NULL;
     192                 :             : 
     193                 :           0 :           recipients = valent_message_get_recipients (message);
     194         [ #  # ]:           0 :           if (recipients != NULL)
     195                 :           0 :             medium = recipients[0];
     196                 :             :         }
     197                 :             : 
     198   [ #  #  #  # ]:           0 :       if (medium != NULL && *medium != '\0')
     199                 :             :         {
     200         [ #  # ]:           0 :           g_autoptr (GCancellable) cancellable = NULL;
     201                 :             : 
     202                 :           0 :           cancellable = g_cancellable_new ();
     203                 :           0 :           g_signal_connect_object (row,
     204                 :             :                                    "destroy",
     205                 :             :                                    G_CALLBACK (g_cancellable_cancel),
     206                 :             :                                    cancellable,
     207                 :             :                                    G_CONNECT_SWAPPED);
     208         [ #  # ]:           0 :           valent_contacts_adapter_reverse_lookup (self->contacts_adapter,
     209                 :             :                                                   medium,
     210                 :             :                                                   cancellable,
     211                 :             :                                                   (GAsyncReadyCallback) lookup_contact_cb,
     212                 :             :                                                   row);
     213                 :             :         }
     214                 :             :     }
     215                 :             : }
     216                 :             : 
     217                 :             : /*
     218                 :             :  * Message Search
     219                 :             :  */
     220                 :             : static void
     221                 :           0 : search_header_func (GtkListBoxRow *row,
     222                 :             :                     GtkListBoxRow *before,
     223                 :             :                     gpointer       user_data)
     224                 :             : {
     225         [ #  # ]:           0 :   if (VALENT_IS_MESSAGE_ROW (row))
     226                 :             :     {
     227   [ #  #  #  # ]:           0 :       if (before == NULL || !VALENT_IS_MESSAGE_ROW (before))
     228                 :             :         {
     229                 :           0 :           GtkWidget *label;
     230                 :             : 
     231                 :           0 :           label = g_object_new (GTK_TYPE_LABEL,
     232                 :             :                                 "label",        _("Conversations"),
     233                 :             :                                 "halign",       GTK_ALIGN_START,
     234                 :             :                                 "margin-bottom", 6,
     235                 :             :                                 "margin-end",    6,
     236                 :             :                                 "margin-start",  6,
     237                 :             :                                 "margin-top",    6,
     238                 :             :                                 NULL);
     239                 :           0 :           gtk_widget_add_css_class (label, "dim-label");
     240                 :           0 :           gtk_widget_add_css_class (label, "caption-heading");
     241                 :           0 :           gtk_list_box_row_set_header (row, label);
     242                 :             :         }
     243                 :             :     }
     244         [ #  # ]:           0 :   else if (!VALENT_IS_CONTACT_ROW (before))
     245                 :             :     {
     246                 :           0 :       GtkWidget *label;
     247                 :             : 
     248                 :           0 :       label = g_object_new (GTK_TYPE_LABEL,
     249                 :             :                             "label",        _("Contacts"),
     250                 :             :                             "halign",       GTK_ALIGN_START,
     251                 :             :                             "margin-bottom", 6,
     252                 :             :                             "margin-end",    6,
     253                 :             :                             "margin-start",  6,
     254                 :             :                             "margin-top",    12, // +6 for section spacing
     255                 :             :                             NULL);
     256                 :           0 :       gtk_widget_add_css_class (label, "dim-label");
     257                 :           0 :       gtk_widget_add_css_class (label, "caption-heading");
     258                 :           0 :       gtk_list_box_row_set_header (row, label);
     259                 :             :     }
     260                 :           0 : }
     261                 :             : 
     262                 :             : static void
     263                 :           0 : on_search_changed (GtkSearchEntry       *entry,
     264                 :             :                    ValentMessagesWindow *self)
     265                 :             : {
     266                 :           0 :   GtkWidget *child;
     267                 :           0 :   const char *search_query;
     268                 :             : 
     269                 :             :   /* Clear previous results
     270                 :             :    */
     271                 :           0 :   g_cancellable_cancel (self->search);
     272         [ #  # ]:           0 :   g_clear_object (&self->search);
     273                 :             : 
     274         [ #  # ]:           0 :   while ((child = gtk_widget_get_first_child (GTK_WIDGET (self->search_list))))
     275                 :           0 :     gtk_list_box_remove (self->search_list, child);
     276                 :             : 
     277                 :           0 :   search_query = gtk_editable_get_text (GTK_EDITABLE (entry));
     278   [ #  #  #  # ]:           0 :   if (search_query == NULL || *search_query == '\0')
     279                 :             :     return;
     280                 :             : 
     281                 :             :   /* Search messages
     282                 :             :    */
     283                 :           0 :   self->search = g_cancellable_new ();
     284                 :           0 :   valent_messages_adapter_search (self->messages_adapter,
     285                 :             :                                   search_query,
     286                 :             :                                   self->search,
     287                 :             :                                   (GAsyncReadyCallback)search_messages_cb,
     288                 :             :                                   self);
     289                 :           0 :   valent_contacts_adapter_search (self->contacts_adapter,
     290                 :             :                                   search_query,
     291                 :             :                                   self->search,
     292                 :             :                                   (GAsyncReadyCallback)search_contacts_cb,
     293                 :             :                                   self);
     294                 :             : }
     295                 :             : 
     296                 :             : static void
     297                 :           0 : lookup_thread_cb (ValentMessagesAdapter *adapter,
     298                 :             :                   GAsyncResult          *result,
     299                 :             :                   ValentMessagesWindow  *self)
     300                 :             : {
     301                 :           0 :   g_autofree char *iri = NULL;
     302                 :           0 :   g_autoptr (GError) error = NULL;
     303                 :             : 
     304                 :           0 :   iri = g_task_propagate_pointer (G_TASK (result), &error);
     305         [ #  # ]:           0 :   if (iri == NULL)
     306                 :             :     {
     307         [ #  # ]:           0 :       if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
     308                 :           0 :         g_warning ("%s(): %s", G_STRFUNC, error->message);
     309                 :             :       else
     310                 :           0 :         g_debug ("%s(): no thread found for contact", G_STRFUNC);
     311                 :             : 
     312         [ #  # ]:           0 :       return;
     313                 :             :     }
     314                 :             : 
     315                 :           0 :   adw_navigation_view_pop (self->content_view);
     316         [ #  # ]:           0 :   valent_messages_window_set_active_thread (self, iri);
     317                 :             : }
     318                 :             : 
     319                 :             : static void
     320                 :           0 : on_contact_selected (ValentContactPage    *page,
     321                 :             :                      EContact             *contact,
     322                 :             :                      const char           *target,
     323                 :             :                      ValentMessagesWindow *self)
     324                 :             : {
     325                 :           0 :   g_autoptr (GCancellable) cancellable = NULL;
     326                 :             : 
     327                 :           0 :   cancellable = g_cancellable_new ();
     328                 :           0 :   g_signal_connect_object (self,
     329                 :             :                            "destroy",
     330                 :             :                            G_CALLBACK (g_cancellable_cancel),
     331                 :             :                            cancellable,
     332                 :             :                            G_CONNECT_SWAPPED);
     333                 :             : 
     334         [ #  # ]:           0 :   valent_messages_adapter_lookup_thread (self->messages_adapter,
     335                 :           0 :                                          ((const char * const []){ target, NULL }),
     336                 :             :                                          cancellable,
     337                 :             :                                          (GAsyncReadyCallback) lookup_thread_cb,
     338                 :             :                                          self);
     339                 :             : 
     340                 :             :   // FIXME: loading indicator
     341                 :           0 : }
     342                 :             : 
     343                 :             : static void
     344                 :           0 : on_contact_medium_selected (GtkListBoxRow        *row,
     345                 :             :                             ValentMessagesWindow *self)
     346                 :             : {
     347                 :           0 :   g_autoptr (GCancellable) cancellable = NULL;
     348         [ #  # ]:           0 :   g_autofree char *medium = NULL;
     349                 :             : 
     350         [ #  # ]:           0 :   g_assert (VALENT_IS_CONTACT_ROW (row));
     351                 :             : 
     352                 :           0 :   cancellable = g_cancellable_new ();
     353                 :           0 :   g_signal_connect_object (self,
     354                 :             :                            "destroy",
     355                 :             :                            G_CALLBACK (g_cancellable_cancel),
     356                 :             :                            cancellable,
     357                 :             :                            G_CONNECT_SWAPPED);
     358                 :             : 
     359                 :           0 :   g_object_get (row, "contact-medium", &medium, NULL);
     360                 :           0 :   valent_messages_adapter_lookup_thread (self->messages_adapter,
     361                 :           0 :                                          ((const char * const []){ medium, NULL }),
     362                 :             :                                          cancellable,
     363                 :             :                                          (GAsyncReadyCallback) lookup_thread_cb,
     364                 :             :                                          self);
     365                 :             : 
     366                 :           0 :   adw_dialog_close (self->details_dialog);
     367                 :           0 : }
     368                 :             : 
     369                 :             : static void
     370                 :           0 : on_contact_row_collapsed (AdwDialog *dialog,
     371                 :             :                           GtkWidget *row)
     372                 :             : {
     373                 :           0 :   gtk_accessible_reset_relation (GTK_ACCESSIBLE (row),
     374                 :             :                                  GTK_ACCESSIBLE_RELATION_CONTROLS);
     375                 :           0 :   gtk_accessible_update_state (GTK_ACCESSIBLE (row),
     376                 :             :                                GTK_ACCESSIBLE_STATE_EXPANDED, FALSE,
     377                 :             :                                -1);
     378                 :           0 :   g_signal_handlers_disconnect_by_func (dialog, on_contact_row_collapsed, row);
     379                 :           0 : }
     380                 :             : 
     381                 :             : static void
     382                 :           0 : on_search_selected (GtkListBox           *box,
     383                 :             :                     GtkListBoxRow        *row,
     384                 :             :                     ValentMessagesWindow *self)
     385                 :             : {
     386         [ #  # ]:           0 :   g_assert (VALENT_IS_MESSAGES_WINDOW (self));
     387                 :             : 
     388         [ #  # ]:           0 :   if (VALENT_IS_MESSAGE_ROW (row))
     389                 :             :     {
     390                 :           0 :       ValentMessage *message;
     391                 :             : 
     392                 :           0 :       message = valent_message_row_get_message (VALENT_MESSAGE_ROW (row));
     393                 :           0 :       valent_messages_window_set_active_message (self, message);
     394                 :             : 
     395                 :             :       /* Reset the search
     396                 :             :        */
     397                 :           0 :       adw_navigation_view_pop (self->content_view);
     398                 :           0 :       gtk_editable_set_text (GTK_EDITABLE (self->search_entry), "");
     399                 :             :     }
     400         [ #  # ]:           0 :   else if (VALENT_IS_CONTACT_ROW (row))
     401                 :             :     {
     402                 :           0 :       EContact *contact;
     403                 :             : #if EDS_CHECK_VERSION (3, 59, 0)
     404                 :             :       g_autoptr (GList) attrs = NULL;
     405                 :             : #else
     406                 :           0 :       g_autolist (EVCardAttribute) attrs = NULL;
     407                 :             : #endif
     408                 :             : 
     409                 :           0 :       contact = valent_contact_row_get_contact (VALENT_CONTACT_ROW (row));
     410                 :             : #if EDS_CHECK_VERSION (3, 59, 0)
     411                 :             :       attrs = e_vcard_get_attributes_by_name (E_VCARD (contact), EVC_TEL);
     412                 :             : #else
     413                 :           0 :       attrs = e_contact_get_attributes (contact, E_CONTACT_TEL);
     414                 :             : #endif
     415         [ #  # ]:           0 :       if (g_list_length (attrs) == 1)
     416                 :             :         {
     417                 :           0 :           on_contact_medium_selected (row, self);
     418                 :             : 
     419                 :             :           /* Reset the search
     420                 :             :            */
     421                 :           0 :           adw_navigation_view_pop (self->content_view);
     422                 :           0 :           gtk_editable_set_text (GTK_EDITABLE (self->search_entry), "");
     423                 :             :         }
     424                 :             :       else
     425                 :             :         {
     426                 :           0 :           gtk_list_box_remove_all (GTK_LIST_BOX (self->medium_list));
     427         [ #  # ]:           0 :           for (const GList *iter = attrs; iter; iter = iter->next)
     428                 :             :             {
     429                 :           0 :               EVCardAttribute *attr = iter->data;
     430                 :           0 :               GtkWidget *medium_row;
     431                 :           0 :               g_autofree char *number = NULL;
     432                 :           0 :               const char *type_ = NULL;
     433                 :             : 
     434         [ #  # ]:           0 :               if (e_vcard_attribute_has_type (attr, "WORK"))
     435                 :           0 :                 type_ = _("Work");
     436         [ #  # ]:           0 :               else if (e_vcard_attribute_has_type (attr, "CELL"))
     437                 :           0 :                 type_ = _("Mobile");
     438         [ #  # ]:           0 :               else if (e_vcard_attribute_has_type (attr, "HOME"))
     439                 :           0 :                 type_ = _("Home");
     440                 :             :               else
     441                 :           0 :                 type_ = _("Other");
     442                 :             : 
     443                 :           0 :               number = e_vcard_attribute_get_value (attr);
     444                 :           0 :               medium_row = g_object_new (ADW_TYPE_ACTION_ROW,
     445                 :             :                                          "activatable", TRUE,
     446                 :             :                                          "title",       number,
     447                 :             :                                          "subtitle",    type_,
     448                 :             :                                          NULL);
     449                 :           0 :               g_object_set_data_full (G_OBJECT (medium_row),
     450                 :             :                                       "contact",
     451                 :             :                                       g_object_ref (contact),
     452                 :             :                                       g_object_unref);
     453                 :           0 :               g_signal_connect_object (medium_row,
     454                 :             :                                        "activated",
     455                 :             :                                        G_CALLBACK (on_contact_medium_selected),
     456                 :             :                                        self,
     457                 :             :                                        G_CONNECT_DEFAULT);
     458                 :             : 
     459                 :           0 :               gtk_list_box_insert (self->medium_list, medium_row, -1);
     460                 :             :             }
     461                 :             : 
     462                 :             :           /* Present the dialog and match the expanded state
     463                 :             :            */
     464                 :           0 :           gtk_accessible_update_state (GTK_ACCESSIBLE (row),
     465                 :             :                                        GTK_ACCESSIBLE_STATE_EXPANDED, TRUE,
     466                 :             :                                        -1);
     467                 :           0 :           gtk_accessible_update_relation (GTK_ACCESSIBLE (row),
     468                 :             :                                           GTK_ACCESSIBLE_RELATION_CONTROLS, self->details_dialog, NULL,
     469                 :             :                                           -1);
     470                 :           0 :           g_signal_connect_object (self->details_dialog,
     471                 :             :                                    "closed",
     472                 :             :                                    G_CALLBACK (on_contact_row_collapsed),
     473                 :             :                                    row,
     474                 :             :                                    G_CONNECT_DEFAULT);
     475                 :           0 :           adw_dialog_present (self->details_dialog, GTK_WIDGET (self));
     476                 :             :         }
     477                 :             :     }
     478                 :           0 : }
     479                 :             : 
     480                 :             : /*
     481                 :             :  * Sidebar
     482                 :             :  */
     483                 :             : static GtkWidget *
     484                 :           1 : sidebar_list_create (gpointer item,
     485                 :             :                      gpointer user_data)
     486                 :             : {
     487                 :           1 :   ValentMessagesWindow *window = VALENT_MESSAGES_WINDOW (user_data);
     488                 :           1 :   GListModel *thread = G_LIST_MODEL (item);
     489                 :           2 :   g_autoptr (ValentMessage) message = NULL;
     490                 :           1 :   ValentMessageBox box = VALENT_MESSAGE_BOX_ALL;
     491                 :           1 :   const char * const *recipients = NULL;
     492                 :           1 :   const char *medium = NULL;
     493                 :           1 :   GtkWidget *row;
     494                 :             : 
     495                 :           1 :   g_object_get (thread, "latest-message", &message, NULL);
     496                 :           1 :   row = g_object_new (VALENT_TYPE_MESSAGE_ROW,
     497                 :             :                       "message", message,
     498                 :             :                       NULL);
     499                 :           1 :   g_object_bind_property (thread, "latest-message",
     500                 :             :                           row,    "message",
     501                 :             :                           G_BINDING_DEFAULT);
     502                 :           1 :   g_object_set_data_full (G_OBJECT (row),
     503                 :             :                           "valent-message-thread",
     504                 :             :                           g_object_ref (thread),
     505                 :             :                           g_object_unref);
     506                 :             : 
     507                 :             :   // TODO: participant-based avatar for sidebar rows
     508         [ +  - ]:           1 :   if (message != NULL)
     509                 :           1 :     box = valent_message_get_box (message);
     510                 :             : 
     511         [ -  + ]:           1 :   if (box == VALENT_MESSAGE_BOX_INBOX)
     512                 :             :     {
     513                 :           0 :       medium = valent_message_get_sender (message);
     514                 :             :     }
     515         [ -  + ]:           1 :   else if (box == VALENT_MESSAGE_BOX_SENT)
     516                 :             :     {
     517                 :           1 :       recipients = valent_message_get_recipients (message);
     518         [ -  + ]:           1 :       if (recipients != NULL)
     519                 :           1 :         medium = recipients[0];
     520                 :             :     }
     521                 :             : 
     522   [ -  +  -  - ]:           1 :   if (window->contacts_adapter != NULL &&
     523         [ #  # ]:           0 :       medium != NULL && *medium != '\0')
     524                 :             :     {
     525         [ +  - ]:           1 :       g_autoptr (GCancellable) cancellable = NULL;
     526                 :             : 
     527                 :           0 :       cancellable = g_cancellable_new ();
     528                 :           0 :       g_signal_connect_object (row,
     529                 :             :                                "destroy",
     530                 :             :                                G_CALLBACK (g_cancellable_cancel),
     531                 :             :                                cancellable,
     532                 :             :                                G_CONNECT_SWAPPED);
     533         [ #  # ]:           0 :       valent_contacts_adapter_reverse_lookup (window->contacts_adapter,
     534                 :             :                                               medium,
     535                 :             :                                               cancellable,
     536                 :             :                                               (GAsyncReadyCallback) lookup_contact_cb,
     537                 :             :                                               row);
     538                 :             :     }
     539                 :             :   else
     540                 :             :     {
     541                 :           2 :       g_autoptr (EContact) contact = NULL;
     542                 :             : 
     543                 :           1 :       contact = e_contact_new ();
     544                 :           1 :       e_contact_set (contact, E_CONTACT_FULL_NAME, _("Unknown"));
     545                 :           1 :       e_contact_set (contact, E_CONTACT_PHONE_OTHER, _("Unknown sender"));
     546         [ +  - ]:           1 :       valent_message_row_set_contact (VALENT_MESSAGE_ROW (row), contact);
     547                 :             :     }
     548                 :             : 
     549         [ +  - ]:           1 :   return row;
     550                 :             : }
     551                 :             : 
     552                 :             : static GtkWidget *
     553                 :           0 : valent_messages_window_ensure_conversation (ValentMessagesWindow *window,
     554                 :             :                                             const char           *thread_iri)
     555                 :             : {
     556                 :           0 :   AdwNavigationPage *conversation;
     557                 :             : 
     558                 :           0 :   conversation = adw_navigation_view_find_page (window->content_view, thread_iri);
     559         [ #  # ]:           0 :   if (conversation == NULL)
     560                 :             :     {
     561                 :           0 :       conversation = g_object_new (VALENT_TYPE_CONVERSATION_PAGE,
     562                 :             :                                    "tag",      thread_iri,
     563                 :             :                                    "contacts", window->contacts_adapter,
     564                 :             :                                    "messages", window->messages_adapter,
     565                 :             :                                    "iri",      thread_iri,
     566                 :             :                                    NULL);
     567                 :           0 :       adw_navigation_view_push (window->content_view, conversation);
     568                 :             :     }
     569                 :             :   else
     570                 :             :     {
     571                 :           0 :       adw_navigation_view_pop_to_page (window->content_view, conversation);
     572                 :             :     }
     573                 :             : 
     574                 :           0 :   return GTK_WIDGET (conversation);
     575                 :             : }
     576                 :             : 
     577                 :             : static void
     578                 :           0 : on_conversation_activated (GtkListBox           *box,
     579                 :             :                            GtkListBoxRow        *row,
     580                 :             :                            ValentMessagesWindow *self)
     581                 :             : {
     582                 :           0 :   GtkWidget *page;
     583                 :           0 :   EContact *contact;
     584                 :           0 :   ValentMessage *message;
     585                 :           0 :   const char *sender;
     586                 :           0 :   int64_t thread_id;
     587                 :           0 :   const char *adapter_urn = NULL;
     588                 :           0 :   g_autofree char *thread_urn = NULL;
     589                 :             : 
     590         [ #  # ]:           0 :   g_assert (VALENT_IS_MESSAGES_WINDOW (self));
     591         [ #  # ]:           0 :   g_assert (VALENT_IS_MESSAGE_ROW (row));
     592                 :             : 
     593         [ #  # ]:           0 :   if (row == NULL)
     594                 :           0 :     return;
     595                 :             : 
     596                 :             :   // TODO: use IRI
     597                 :           0 :   contact = valent_message_row_get_contact (VALENT_MESSAGE_ROW (row));
     598                 :           0 :   message = valent_message_row_get_message (VALENT_MESSAGE_ROW (row));
     599                 :           0 :   thread_id = valent_message_get_thread_id (message);
     600                 :           0 :   adapter_urn = valent_object_get_iri (VALENT_OBJECT (self->messages_adapter));
     601                 :           0 :   thread_urn = g_strdup_printf ("%s:%"PRId64, adapter_urn, thread_id);
     602                 :             : 
     603                 :           0 :   page = valent_messages_window_ensure_conversation (self, thread_urn);
     604                 :           0 :   sender = valent_message_get_sender (message);
     605   [ #  #  #  # ]:           0 :   if (sender != NULL && *sender != '\0')
     606                 :             :     {
     607                 :           0 :       valent_conversation_page_add_participant (VALENT_CONVERSATION_PAGE (page),
     608                 :             :                                                 contact,
     609                 :             :                                                 sender);
     610                 :             :     }
     611                 :             : 
     612                 :           0 :   adw_navigation_split_view_set_show_content (self->main_view, TRUE);
     613                 :             : }
     614                 :             : 
     615                 :             : /*
     616                 :             :  * Message Source
     617                 :             :  */
     618                 :             : static void
     619                 :           1 : on_selected_item (GObject              *object,
     620                 :             :                   GParamSpec           *pspec,
     621                 :             :                   ValentMessagesWindow *self)
     622                 :             : {
     623                 :           1 :   ValentMessagesAdapter *adapter = NULL;
     624                 :           1 :   GObject *owner = NULL;
     625                 :           1 :   unsigned int n_items = 0;
     626                 :             : 
     627         [ -  + ]:           1 :   g_assert (VALENT_IS_MESSAGES_WINDOW (self));
     628                 :             : 
     629                 :           1 :   adapter = gtk_drop_down_get_selected_item (GTK_DROP_DOWN (object));
     630         [ +  - ]:           1 :   if (!g_set_object (&self->messages_adapter, adapter))
     631                 :             :     return;
     632                 :             : 
     633                 :             :   // HACK: try to find a matching contacts adapter
     634                 :           1 :   owner = valent_object_get_parent (VALENT_OBJECT (adapter));
     635                 :           1 :   n_items = g_list_model_get_n_items (self->contacts);
     636         [ +  + ]:           2 :   for (unsigned int i = 0; i < n_items; i++)
     637                 :             :     {
     638                 :           1 :       g_autoptr (ValentContactsAdapter) item = NULL;
     639                 :           1 :       GObject *item_owner = NULL;
     640                 :             : 
     641                 :           1 :       item = g_list_model_get_item (self->contacts, i);
     642                 :           1 :       item_owner = valent_object_get_parent (VALENT_OBJECT (item));
     643         [ -  + ]:           1 :       if (item_owner == owner)
     644                 :             :         {
     645                 :           0 :           g_set_object (&self->contacts_adapter, item);
     646         [ #  # ]:           0 :           break;
     647                 :             :         }
     648                 :             :     }
     649                 :             : 
     650                 :           1 :   gtk_list_box_bind_model (self->sidebar_list,
     651                 :             :                            G_LIST_MODEL (adapter),
     652                 :             :                            sidebar_list_create,
     653                 :             :                            self,
     654                 :             :                            NULL);
     655                 :             : }
     656                 :             : 
     657                 :             : /*
     658                 :             :  * GActions
     659                 :             :  */
     660                 :             : static void
     661                 :           0 : sms_new_action (GtkWidget  *widget,
     662                 :             :                 const char *action_name,
     663                 :             :                 GVariant   *parameter)
     664                 :             : {
     665                 :           0 :   ValentMessagesWindow *self = VALENT_MESSAGES_WINDOW (widget);
     666                 :             : 
     667         [ #  # ]:           0 :   g_assert (VALENT_IS_MESSAGES_WINDOW (self));
     668                 :             : 
     669                 :           0 :   gtk_list_box_select_row (self->sidebar_list, NULL);
     670                 :             : 
     671         [ #  # ]:           0 :   if (self->contact_page == NULL)
     672                 :             :     {
     673                 :           0 :       self->contact_page = g_object_new (VALENT_TYPE_CONTACT_PAGE,
     674                 :             :                                          "tag",      "contacts",
     675                 :             :                                          "contacts", self->contacts_adapter,
     676                 :             :                                          NULL);
     677                 :           0 :       g_signal_connect_object (self->contact_page,
     678                 :             :                                "selected",
     679                 :             :                                G_CALLBACK (on_contact_selected),
     680                 :             :                                self,
     681                 :             :                                G_CONNECT_DEFAULT);
     682                 :           0 :       adw_navigation_view_push (self->content_view, self->contact_page);
     683                 :             :     }
     684                 :             :   else
     685                 :             :     {
     686                 :           0 :       adw_navigation_view_pop_to_page (self->content_view, self->contact_page);
     687                 :             :     }
     688                 :             : 
     689                 :           0 :   adw_navigation_split_view_set_show_content (self->main_view, TRUE);
     690                 :           0 : }
     691                 :             : 
     692                 :             : static void
     693                 :           0 : sms_search_action (GtkWidget  *widget,
     694                 :             :                    const char *action_name,
     695                 :             :                    GVariant   *parameter)
     696                 :             : {
     697                 :           0 :   ValentMessagesWindow *self = VALENT_MESSAGES_WINDOW (widget);
     698                 :             : 
     699         [ #  # ]:           0 :   g_assert (VALENT_IS_MESSAGES_WINDOW (self));
     700                 :             : 
     701         [ #  # ]:           0 :   if (self->search_page == NULL)
     702                 :           0 :     adw_navigation_view_push_by_tag (self->content_view, "search");
     703                 :             :   else
     704                 :           0 :     adw_navigation_view_pop_to_page (self->content_view, self->search_page);
     705                 :             : 
     706                 :           0 :   gtk_widget_grab_focus (self->search_entry);
     707                 :           0 :   adw_navigation_split_view_set_show_content (self->main_view, TRUE);
     708                 :           0 : }
     709                 :             : 
     710                 :             : /*
     711                 :             :  * AdwNavigationPage
     712                 :             :  */
     713                 :             : static void
     714                 :           0 : on_page_popped (AdwNavigationView    *view,
     715                 :             :                 AdwNavigationPage    *page,
     716                 :             :                 ValentMessagesWindow *self)
     717                 :             : {
     718         [ #  # ]:           0 :   if (self->contact_page == page)
     719                 :           0 :     self->contact_page = NULL;
     720         [ #  # ]:           0 :   else if (self->search_page == page)
     721                 :           0 :     self->search_page = NULL;
     722                 :           0 : }
     723                 :             : 
     724                 :             : static void
     725                 :           0 : on_page_pushed (AdwNavigationView    *view,
     726                 :             :                 ValentMessagesWindow *self)
     727                 :             : {
     728                 :           0 :   AdwNavigationPage *page = adw_navigation_view_get_visible_page (view);
     729                 :           0 :   const char *tag = adw_navigation_page_get_tag (page);
     730                 :             : 
     731         [ #  # ]:           0 :   if (g_strcmp0 (tag, "contacts") == 0)
     732                 :           0 :     self->contact_page = page;
     733         [ #  # ]:           0 :   else if (g_strcmp0 (tag, "search") == 0)
     734                 :           0 :     self->search_page = page;
     735                 :           0 : }
     736                 :             : 
     737                 :             : /*
     738                 :             :  * GObject
     739                 :             :  */
     740                 :             : static void
     741                 :           1 : valent_messages_window_dispose (GObject *object)
     742                 :             : {
     743                 :           1 :   GtkWidget *widget = GTK_WIDGET (object);
     744                 :             : 
     745                 :           1 :   gtk_widget_dispose_template (widget, VALENT_TYPE_MESSAGES_WINDOW);
     746                 :             : 
     747                 :           1 :   G_OBJECT_CLASS (valent_messages_window_parent_class)->dispose (object);
     748                 :           1 : }
     749                 :             : 
     750                 :             : static void
     751                 :           1 : valent_messages_window_finalize (GObject *object)
     752                 :             : {
     753                 :           1 :   ValentMessagesWindow *self = VALENT_MESSAGES_WINDOW (object);
     754                 :             : 
     755         [ +  - ]:           1 :   g_clear_object (&self->contacts);
     756         [ -  + ]:           1 :   g_clear_object (&self->contacts_adapter);
     757         [ +  - ]:           1 :   g_clear_object (&self->messages);
     758         [ +  - ]:           1 :   g_clear_object (&self->messages_adapter);
     759                 :             : 
     760                 :           1 :   G_OBJECT_CLASS (valent_messages_window_parent_class)->finalize (object);
     761                 :           1 : }
     762                 :             : 
     763                 :             : static void
     764                 :           3 : valent_messages_window_get_property (GObject    *object,
     765                 :             :                                      guint       prop_id,
     766                 :             :                                      GValue     *value,
     767                 :             :                                      GParamSpec *pspec)
     768                 :             : {
     769                 :           3 :   ValentMessagesWindow *self = VALENT_MESSAGES_WINDOW (object);
     770                 :             : 
     771         [ +  - ]:           3 :   switch (prop_id)
     772                 :             :     {
     773                 :           3 :     case PROP_MESSAGES:
     774                 :           3 :       g_value_set_object (value, self->messages);
     775                 :           3 :       break;
     776                 :             : 
     777                 :           0 :     default:
     778                 :           0 :       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
     779                 :             :     }
     780                 :           3 : }
     781                 :             : 
     782                 :             : static void
     783                 :           1 : valent_messages_window_class_init (ValentMessagesWindowClass *klass)
     784                 :             : {
     785                 :           1 :   GObjectClass *object_class = G_OBJECT_CLASS (klass);
     786                 :           1 :   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
     787                 :             : 
     788                 :           1 :   object_class->dispose = valent_messages_window_dispose;
     789                 :           1 :   object_class->finalize = valent_messages_window_finalize;
     790                 :           1 :   object_class->get_property = valent_messages_window_get_property;
     791                 :             : 
     792                 :           1 :   gtk_widget_class_set_template_from_resource (widget_class, "/plugins/gnome/valent-messages-window.ui");
     793                 :           1 :   gtk_widget_class_bind_template_child (widget_class, ValentMessagesWindow, main_view);
     794                 :           1 :   gtk_widget_class_bind_template_child (widget_class, ValentMessagesWindow, sidebar_header);
     795                 :           1 :   gtk_widget_class_bind_template_child (widget_class, ValentMessagesWindow, sidebar_list);
     796                 :           1 :   gtk_widget_class_bind_template_child (widget_class, ValentMessagesWindow, content_view);
     797                 :           1 :   gtk_widget_class_bind_template_child (widget_class, ValentMessagesWindow, search_entry);
     798                 :           1 :   gtk_widget_class_bind_template_child (widget_class, ValentMessagesWindow, search_list);
     799                 :           1 :   gtk_widget_class_bind_template_child (widget_class, ValentMessagesWindow, details_dialog);
     800                 :           1 :   gtk_widget_class_bind_template_child (widget_class, ValentMessagesWindow, medium_list);
     801                 :           1 :   gtk_widget_class_bind_template_callback (widget_class, on_conversation_activated);
     802                 :           1 :   gtk_widget_class_bind_template_callback (widget_class, on_page_popped);
     803                 :           1 :   gtk_widget_class_bind_template_callback (widget_class, on_page_pushed);
     804                 :           1 :   gtk_widget_class_bind_template_callback (widget_class, on_search_changed);
     805                 :           1 :   gtk_widget_class_bind_template_callback (widget_class, on_contact_selected);
     806                 :           1 :   gtk_widget_class_bind_template_callback (widget_class, on_search_selected);
     807                 :           1 :   gtk_widget_class_bind_template_callback (widget_class, on_selected_item);
     808                 :             : 
     809                 :           1 :   gtk_widget_class_install_action (widget_class, "sms.new", NULL, sms_new_action);
     810                 :           1 :   gtk_widget_class_install_action (widget_class, "sms.search", NULL, sms_search_action);
     811                 :             : 
     812                 :           2 :   properties [PROP_MESSAGES] =
     813                 :           1 :     g_param_spec_object ("messages", NULL, NULL,
     814                 :             :                          VALENT_TYPE_MESSAGES,
     815                 :             :                          (G_PARAM_READABLE |
     816                 :             :                           G_PARAM_STATIC_STRINGS));
     817                 :             : 
     818                 :           1 :   g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
     819                 :             : 
     820                 :           1 :   g_type_ensure (VALENT_TYPE_CONTACT_PAGE);
     821                 :           1 : }
     822                 :             : 
     823                 :             : static void
     824                 :           1 : valent_messages_window_init (ValentMessagesWindow *self)
     825                 :             : {
     826                 :           1 :   gtk_widget_init_template (GTK_WIDGET (self));
     827                 :             : 
     828                 :           1 :   g_set_object (&self->contacts, G_LIST_MODEL (valent_contacts_get_default ()));
     829                 :           1 :   g_set_object (&self->messages, G_LIST_MODEL (valent_messages_get_default ()));
     830                 :           1 :   g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MESSAGES]);
     831                 :             : 
     832                 :           1 :   gtk_list_box_set_header_func (self->search_list,
     833                 :             :                                 search_header_func,
     834                 :             :                                 self, NULL);
     835                 :           1 : }
     836                 :             : 
     837                 :             : /*< private >
     838                 :             :  * valent_messages_window_set_active_message:
     839                 :             :  * @window: a `ValentMessagesWindow`
     840                 :             :  * @message: a `ValentMessage`
     841                 :             :  *
     842                 :             :  * Set the active conversation to the thread of @message scroll to @message.
     843                 :             :  */
     844                 :             : void
     845                 :           0 : valent_messages_window_set_active_message (ValentMessagesWindow *window,
     846                 :             :                                            ValentMessage        *message)
     847                 :             : {
     848                 :           0 :   GtkWidget *widget;
     849                 :           0 :   ValentConversationPage *conversation;
     850                 :           0 :   int64_t thread_id;
     851                 :           0 :   const char *adapter_urn = NULL;
     852                 :           0 :   g_autofree char *thread_urn = NULL;
     853                 :             : 
     854         [ #  # ]:           0 :   g_return_if_fail (VALENT_IS_MESSAGES_WINDOW (window));
     855                 :             : 
     856                 :           0 :   thread_id = valent_message_get_thread_id (message);
     857                 :           0 :   adapter_urn = valent_object_get_iri (VALENT_OBJECT (window->messages_adapter));
     858                 :           0 :   thread_urn = g_strdup_printf ("%s:%"PRId64, adapter_urn, thread_id);
     859                 :             : 
     860                 :           0 :   widget = valent_messages_window_ensure_conversation (window, thread_urn);
     861                 :           0 :   conversation = VALENT_CONVERSATION_PAGE (widget);
     862                 :           0 :   valent_conversation_page_scroll_to_message (conversation, message);
     863                 :             : }
     864                 :             : 
     865                 :             : /**
     866                 :             :  * valent_messages_window_set_active_thread:
     867                 :             :  * @window: a `ValentMessagesWindow`
     868                 :             :  * @iri: a thread IRI
     869                 :             :  *
     870                 :             :  * Set the active conversation
     871                 :             :  */
     872                 :             : void
     873                 :           0 : valent_messages_window_set_active_thread (ValentMessagesWindow *window,
     874                 :             :                                           const char           *iri)
     875                 :             : {
     876         [ #  # ]:           0 :   g_return_if_fail (VALENT_IS_MESSAGES_WINDOW (window));
     877         [ #  # ]:           0 :   g_return_if_fail (iri != NULL);
     878                 :             : 
     879                 :           0 :   valent_messages_window_ensure_conversation (window, iri);
     880                 :             : }
     881                 :             : 
        

Generated by: LCOV version 2.0-1