messenger-gtk

Gtk+3 graphical user interfaces for GNUnet Messenger
Log | Files | Refs | Submodules | README | LICENSE

contacts.c (7663B)


      1 /*
      2    This file is part of GNUnet.
      3    Copyright (C) 2021--2024 GNUnet e.V.
      4 
      5    GNUnet is free software: you can redistribute it and/or modify it
      6    under the terms of the GNU Affero General Public License as published
      7    by the Free Software Foundation, either version 3 of the License,
      8    or (at your option) any later version.
      9 
     10    GNUnet is distributed in the hope that it will be useful, but
     11    WITHOUT ANY WARRANTY; without even the implied warranty of
     12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13    Affero General Public License for more details.
     14 
     15    You should have received a copy of the GNU Affero General Public License
     16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
     17 
     18    SPDX-License-Identifier: AGPL3.0-or-later
     19  */
     20 /*
     21  * @author Tobias Frisch
     22  * @file ui/contacts.c
     23  */
     24 
     25 #include "contacts.h"
     26 
     27 #include "contact_entry.h"
     28 #include "../application.h"
     29 #include "../ui.h"
     30 
     31 static void
     32 handle_close_button_click(UNUSED GtkButton *button,
     33                           gpointer user_data)
     34 {
     35   g_assert(user_data);
     36 
     37   GtkDialog *dialog = GTK_DIALOG(user_data);
     38   gtk_window_close(GTK_WINDOW(dialog));
     39 }
     40 
     41 static gboolean
     42 _open_new_contact_dialog(gpointer user_data)
     43 {
     44   g_assert(user_data);
     45 
     46   MESSENGER_Application *app = (MESSENGER_Application*) user_data;
     47 
     48   ui_new_contact_dialog_init(app, &(app->ui.new_contact));
     49 
     50   gtk_widget_show(GTK_WIDGET(app->ui.new_contact.dialog));
     51   return FALSE;
     52 }
     53 
     54 static void
     55 handle_contacts_listbox_row_activated(UNUSED GtkListBox* listbox,
     56                                       GtkListBoxRow* row,
     57                                       gpointer user_data)
     58 {
     59   g_assert((row) && (user_data));
     60 
     61   MESSENGER_Application *app = (MESSENGER_Application*) user_data;
     62 
     63   if (!gtk_list_box_row_get_selectable(row))
     64   {
     65     util_idle_add(G_SOURCE_FUNC(_open_new_contact_dialog), app);
     66     goto close_dialog;
     67   }
     68 
     69   struct GNUNET_CHAT_Contact *contact = (struct GNUNET_CHAT_Contact*) (
     70       g_object_get_qdata(G_OBJECT(row), app->quarks.data)
     71   );
     72 
     73   if (!contact)
     74     goto close_dialog;
     75 
     76   application_chat_lock(app);
     77   const gboolean closing = (
     78     (!GNUNET_CHAT_contact_get_key(contact)) ||
     79     (GNUNET_YES == GNUNET_CHAT_contact_is_owned(contact))
     80   );
     81   application_chat_unlock(app);
     82 
     83   if (closing)
     84     goto close_dialog;
     85 
     86   application_chat_lock(app);
     87   struct GNUNET_CHAT_Context *context = GNUNET_CHAT_contact_get_context(
     88       contact
     89   );
     90   application_chat_unlock(app);
     91 
     92   if (!context)
     93     goto close_dialog;
     94 
     95   application_chat_lock(app);
     96   if (GNUNET_SYSERR == GNUNET_CHAT_context_get_status(context))
     97     GNUNET_CHAT_context_request(context);
     98   application_chat_unlock(app);
     99 
    100 close_dialog:
    101   gtk_window_close(GTK_WINDOW(app->ui.contacts.dialog));
    102 }
    103 
    104 struct FilterTags
    105 {
    106   const gchar *filter;
    107   gboolean matching;
    108 };
    109 
    110 static enum GNUNET_GenericReturnValue
    111 _iterate_contact_tags(void *cls,
    112                       struct GNUNET_CHAT_Contact *contact,
    113                       const char *tag)
    114 {
    115   g_assert((cls) && (contact) && (tag));
    116 
    117   struct FilterTags *filterTags = (struct FilterTags*) cls;
    118 
    119   gchar *_tag = g_locale_to_utf8(tag, -1, NULL, NULL, NULL);
    120   if (!_tag)
    121     return GNUNET_YES;
    122 
    123   if (g_strstr_len(_tag, -1, filterTags->filter))
    124     filterTags->matching = TRUE;
    125 
    126   g_free(_tag);
    127   return filterTags->matching? GNUNET_NO : GNUNET_YES;
    128 }
    129 
    130 static gboolean
    131 handle_contacts_listbox_filter_func(GtkListBoxRow *row,
    132                                     gpointer user_data)
    133 {
    134   g_assert((row) && (user_data));
    135 
    136   MESSENGER_Application *app = (MESSENGER_Application*) user_data;
    137 
    138   if (!gtk_list_box_row_get_selectable(row))
    139     return TRUE;
    140 
    141   const gchar *filter = gtk_entry_get_text(
    142     GTK_ENTRY(app->ui.contacts.contact_search_entry)
    143   );
    144 
    145   if (!filter)
    146     return TRUE;
    147 
    148   UI_CONTACT_ENTRY_Handle *entry = (UI_CONTACT_ENTRY_Handle*) (
    149     g_object_get_qdata(G_OBJECT(row), app->quarks.ui)
    150   );
    151 
    152   if (!entry)
    153     return FALSE;
    154 
    155   const gchar *name = gtk_label_get_text(entry->title_label);
    156 
    157   gboolean result = FALSE;
    158 
    159   if (name)
    160     result |= g_str_match_string(filter, name, TRUE);
    161 
    162   if (('#' == *filter) && (entry->contact))
    163   {
    164     struct FilterTags filterTags;
    165     filterTags.filter = &(filter[1]);
    166     filterTags.matching = FALSE;
    167 
    168     application_chat_lock(app);
    169 
    170     GNUNET_CHAT_contact_iterate_tags(
    171       entry->contact,
    172       _iterate_contact_tags,
    173       &filterTags
    174     );
    175 
    176     application_chat_unlock(app);
    177 
    178     result |= filterTags.matching;
    179   }
    180 
    181   return result;
    182 }
    183 
    184 static void
    185 handle_contact_search_entry_search_changed(UNUSED GtkSearchEntry* search_entry,
    186                                            gpointer user_data)
    187 {
    188   g_assert(user_data);
    189 
    190   GtkListBox *listbox = GTK_LIST_BOX(user_data);
    191 
    192   gtk_list_box_invalidate_filter(listbox);
    193 }
    194 
    195 static void
    196 handle_dialog_destroy(UNUSED GtkWidget *window,
    197                       gpointer user_data)
    198 {
    199   g_assert(user_data);
    200 
    201   ui_contacts_dialog_cleanup((UI_CONTACTS_Handle*) user_data);
    202 }
    203 
    204 static enum GNUNET_GenericReturnValue
    205 _iterate_contacts(void *cls,
    206                   UNUSED struct GNUNET_CHAT_Handle *handle,
    207                   struct GNUNET_CHAT_Contact *contact)
    208 {
    209   g_assert((cls) && (contact));
    210 
    211   if (GNUNET_YES == GNUNET_CHAT_contact_is_owned(contact))
    212     return GNUNET_YES;
    213 
    214   MESSENGER_Application *app = (MESSENGER_Application*) cls;
    215 
    216   UI_CONTACT_ENTRY_Handle *entry = ui_contact_entry_new(app);
    217   ui_contact_entry_set_contact(entry, contact);
    218 
    219   gtk_list_box_prepend(
    220     app->ui.contacts.contacts_listbox,
    221     entry->entry_box
    222   );
    223 
    224   GtkListBoxRow *row = GTK_LIST_BOX_ROW(
    225     gtk_widget_get_parent(entry->entry_box)
    226   );
    227 
    228   g_object_set_qdata(G_OBJECT(row), app->quarks.data, contact);
    229 
    230   g_object_set_qdata_full(
    231     G_OBJECT(row),
    232     app->quarks.ui,
    233     entry,
    234     (GDestroyNotify) ui_contact_entry_delete
    235   );
    236 
    237   return GNUNET_YES;
    238 }
    239 
    240 void
    241 ui_contacts_dialog_init(MESSENGER_Application *app,
    242                         UI_CONTACTS_Handle *handle)
    243 {
    244   g_assert((app) && (handle));
    245 
    246   handle->builder = ui_builder_from_resource(
    247     application_get_resource_path(app, "ui/contacts.ui")
    248   );
    249 
    250   handle->dialog = GTK_DIALOG(
    251     gtk_builder_get_object(handle->builder, "contacts_dialog")
    252   );
    253 
    254   gtk_window_set_transient_for(
    255     GTK_WINDOW(handle->dialog),
    256     GTK_WINDOW(app->ui.messenger.main_window)
    257   );
    258 
    259   handle->contact_search_entry = GTK_SEARCH_ENTRY(
    260     gtk_builder_get_object(handle->builder, "contact_search_entry")
    261   );
    262 
    263   handle->contacts_listbox = GTK_LIST_BOX(
    264     gtk_builder_get_object(handle->builder, "contacts_listbox")
    265   );
    266 
    267   gtk_list_box_set_filter_func(
    268     handle->contacts_listbox,
    269     handle_contacts_listbox_filter_func,
    270     app,
    271     NULL
    272   );
    273 
    274   g_signal_connect(
    275     handle->contact_search_entry,
    276     "search-changed",
    277     G_CALLBACK(handle_contact_search_entry_search_changed),
    278     handle->contacts_listbox
    279   );
    280 
    281   g_signal_connect(
    282     handle->contacts_listbox,
    283     "row-activated",
    284     G_CALLBACK(handle_contacts_listbox_row_activated),
    285     app
    286   );
    287 
    288   handle->close_button = GTK_BUTTON(
    289     gtk_builder_get_object(handle->builder, "close_button")
    290   );
    291 
    292   g_signal_connect(
    293     handle->close_button,
    294     "clicked",
    295     G_CALLBACK(handle_close_button_click),
    296     handle->dialog
    297   );
    298 
    299   g_signal_connect(
    300     handle->dialog,
    301     "destroy",
    302     G_CALLBACK(handle_dialog_destroy),
    303     handle
    304   );
    305 
    306   GNUNET_CHAT_iterate_contacts(
    307     app->chat.messenger.handle,
    308     _iterate_contacts,
    309     app
    310   );
    311 
    312   gtk_list_box_invalidate_filter(handle->contacts_listbox);
    313 }
    314 
    315 void
    316 ui_contacts_dialog_cleanup(UI_CONTACTS_Handle *handle)
    317 {
    318   g_assert(handle);
    319 
    320   if (handle->builder)
    321     g_object_unref(handle->builder);
    322 
    323   memset(handle, 0, sizeof(*handle));
    324 }