messenger-gtk

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

contact_info.c (38381B)


      1 /*
      2    This file is part of GNUnet.
      3    Copyright (C) 2022--2025 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/contact_info.c
     23  */
     24 
     25 #include "contact_info.h"
     26 
     27 #include "chat_entry.h"
     28 
     29 #include "../account.h"
     30 #include "../application.h"
     31 #include "../contact.h"
     32 #include "../file.h"
     33 #include "../ui.h"
     34 
     35 #include <gnunet/gnunet_chat_lib.h>
     36 #include <gnunet/gnunet_common.h>
     37 #include <string.h>
     38 
     39 static void
     40 handle_contact_edit_button_click(UNUSED GtkButton *button,
     41                                  gpointer user_data)
     42 {
     43   g_assert(user_data);
     44 
     45   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
     46 
     47   gboolean editable = gtk_widget_is_sensitive(
     48     GTK_WIDGET(handle->contact_name_entry)
     49   );
     50 
     51   if (!editable)
     52     goto skip_change_name;
     53 
     54   const gboolean change_own_name = (
     55     (!(handle->contact)) ||
     56     (GNUNET_YES == GNUNET_CHAT_contact_is_owned(handle->contact))
     57   );
     58 
     59   const gchar *name = gtk_entry_get_text(handle->contact_name_entry);
     60 
     61   if ((name) && (0 == g_utf8_strlen(name, 1)))
     62     name = NULL;
     63 
     64   application_chat_lock(handle->app);
     65 
     66   if (change_own_name)
     67   {
     68     if (GNUNET_YES != GNUNET_CHAT_set_name(handle->app->chat.messenger.handle, name))
     69       gtk_entry_set_text(
     70 	      handle->contact_name_entry,
     71 	      GNUNET_CHAT_get_name(handle->app->chat.messenger.handle)
     72       );
     73   }
     74   else
     75     GNUNET_CHAT_contact_set_name(handle->contact, name);
     76 
     77   application_chat_unlock(handle->app);
     78 
     79 skip_change_name:
     80   gtk_image_set_from_icon_name(
     81     handle->contact_edit_symbol,
     82     editable?
     83     "document-edit-symbolic" :
     84     "emblem-ok-symbolic",
     85     GTK_ICON_SIZE_BUTTON
     86   );
     87 
     88   gtk_widget_set_sensitive(
     89     GTK_WIDGET(handle->contact_name_entry),
     90     !editable
     91   );
     92 }
     93 
     94 static void
     95 handle_contact_name_entry_activate(UNUSED GtkEntry *entry,
     96                                    gpointer user_data)
     97 {
     98   g_assert(user_data);
     99 
    100   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
    101 
    102   handle_contact_edit_button_click(handle->contact_edit_button, handle);
    103 }
    104 
    105 static void
    106 handle_profile_chooser_update_preview(GtkFileChooser *file_chooser,
    107                                       gpointer user_data)
    108 {
    109   g_assert((file_chooser) && (user_data));
    110 
    111   HdyAvatar *avatar = HDY_AVATAR(user_data);
    112 
    113   gboolean have_preview = false;
    114   gchar *filename = gtk_file_chooser_get_preview_filename(file_chooser);
    115 
    116   if ((!filename) || (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)))
    117     goto skip_preview;
    118 
    119   GFile *file = g_file_new_for_path(filename);
    120 
    121   if (!file)
    122     goto skip_icon;
    123 
    124   GIcon *icon = g_file_icon_new(file);
    125 
    126   if (!icon)
    127     goto skip_avatar;
    128 
    129   hdy_avatar_set_loadable_icon(avatar, G_LOADABLE_ICON(icon));
    130   g_object_unref(icon);
    131   have_preview = true;
    132 
    133 skip_avatar:
    134   g_object_unref(file);
    135 
    136 skip_icon:
    137   g_free(filename);
    138 
    139 skip_preview:
    140   gtk_file_chooser_set_preview_widget_active(file_chooser, have_preview);
    141 }
    142 
    143 static void
    144 _cb_file_upload(void *cls,
    145                 struct GNUNET_CHAT_File *file,
    146                 uint64_t completed,
    147                 uint64_t size)
    148 {
    149   g_assert((cls) && (file));
    150 
    151   MESSENGER_Application *app = (MESSENGER_Application*) cls;
    152 
    153   file_update_upload_info(file, completed, size);
    154 
    155   if (completed < size)
    156     return;
    157 
    158   struct GNUNET_CHAT_Uri *uri = GNUNET_CHAT_file_get_uri(file);
    159 
    160   if (!uri)
    161     return;
    162 
    163   char *uri_string = GNUNET_CHAT_uri_to_string(uri);
    164 
    165   if (uri_string)
    166   {
    167     GNUNET_CHAT_set_attribute(
    168       app->chat.messenger.handle,
    169       GNUNET_CHAT_ATTRIBUTE_AVATAR,
    170       uri_string
    171     );
    172 
    173     GNUNET_free(uri_string);
    174   }
    175 
    176   GNUNET_CHAT_uri_destroy(uri);
    177 }
    178 
    179 static void
    180 handle_profile_chooser_file_set(GtkFileChooserButton *button,
    181                                 gpointer user_data)
    182 {
    183   g_assert(user_data);
    184 
    185   GtkFileChooser *file_chooser = GTK_FILE_CHOOSER(button);
    186   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
    187 
    188   gchar *filename = gtk_file_chooser_get_preview_filename(file_chooser);
    189 
    190   if (!filename)
    191     return;
    192 
    193   application_chat_lock(handle->app);
    194 
    195   GNUNET_CHAT_upload_file(
    196     handle->app->chat.messenger.handle,
    197     filename,
    198     _cb_file_upload,
    199     handle->app
    200   );
    201 
    202   application_chat_unlock(handle->app);
    203 
    204   g_free(filename);
    205 }
    206 
    207 static void
    208 _contact_info_switch_stack_to(UI_CONTACT_INFO_Handle *handle,
    209                               GtkWidget *page_widget)
    210 {
    211   g_assert((handle) && (page_widget));
    212 
    213   gtk_widget_set_visible(GTK_WIDGET(handle->back_button), TRUE);
    214 
    215   gtk_stack_set_visible_child(
    216     handle->contact_info_stack,
    217     page_widget
    218   );
    219 }
    220 
    221 static void
    222 handle_reveal_identity_button_click(UNUSED GtkButton *button,
    223                                     gpointer user_data)
    224 {
    225   g_assert(user_data);
    226 
    227   struct UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
    228 
    229   _contact_info_switch_stack_to(handle, handle->identity_box);
    230 }
    231 
    232 static void
    233 handle_list_attributes_button_click(UNUSED GtkButton *button,
    234                                     gpointer user_data)
    235 {
    236   g_assert(user_data);
    237 
    238   struct UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
    239 
    240   _contact_info_switch_stack_to(handle, handle->attributes_box);
    241 }
    242 
    243 static void
    244 handle_share_attributes_button_click(UNUSED GtkButton *button,
    245                                      gpointer user_data)
    246 {
    247   g_assert(user_data);
    248 
    249   struct UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
    250 
    251   _contact_info_switch_stack_to(handle, handle->sharing_box);
    252 }
    253 
    254 static void
    255 handle_list_tags_button_click(UNUSED GtkButton *button,
    256                               gpointer user_data)
    257 {
    258   g_assert(user_data);
    259 
    260   struct UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
    261 
    262   _contact_info_switch_stack_to(handle, handle->tags_box);
    263 }
    264 
    265 static void
    266 handle_block_button_click(UNUSED GtkButton *button,
    267                           gpointer user_data)
    268 {
    269   g_assert(user_data);
    270 
    271   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
    272 
    273   if (!(handle->contact))
    274     return;
    275 
    276   application_chat_lock(handle->app);
    277   GNUNET_CHAT_contact_set_blocked(handle->contact, GNUNET_YES);
    278   application_chat_unlock(handle->app);
    279 
    280   gtk_stack_set_visible_child(
    281     handle->block_stack,
    282     GTK_WIDGET(handle->unblock_button)
    283   );
    284 }
    285 
    286 static void
    287 handle_unblock_button_click(UNUSED GtkButton *button,
    288                             gpointer user_data)
    289 {
    290   g_assert(user_data);
    291 
    292   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
    293 
    294   if (!(handle->contact))
    295     return;
    296 
    297   application_chat_lock(handle->app);
    298   GNUNET_CHAT_contact_set_blocked(handle->contact, GNUNET_NO);
    299   application_chat_unlock(handle->app);
    300 
    301   gtk_stack_set_visible_child(
    302     handle->block_stack, 
    303     GTK_WIDGET(handle->block_button)
    304   );
    305 }
    306 
    307 static void*
    308 _open_direct_chat(MESSENGER_Application *app,
    309                   struct GNUNET_CHAT_Contact *contact)
    310 {
    311   g_assert((app) && (contact));
    312 
    313   application_chat_lock(app);
    314 
    315   struct GNUNET_CHAT_Context *context = GNUNET_CHAT_contact_get_context(
    316     contact
    317   );
    318 
    319   if (!context)
    320     return GNUNET_NO;
    321 
    322   void *user_pointer = NULL;;
    323   enum GNUNET_GenericReturnValue status = GNUNET_CHAT_context_get_status(
    324     context
    325   );
    326 
    327   if (GNUNET_SYSERR != status)
    328     user_pointer = GNUNET_CHAT_context_get_user_pointer(
    329       context
    330     );
    331   else
    332     GNUNET_CHAT_context_request(context);
    333 
    334   application_chat_unlock(app);
    335 
    336   return user_pointer;
    337 }
    338 
    339 static void
    340 handle_open_chat_button_click(UNUSED GtkButton *button,
    341                               gpointer user_data)
    342 {
    343   g_assert(user_data);
    344 
    345   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
    346 
    347   if (!(handle->contact))
    348     return;
    349 
    350   UI_CHAT_ENTRY_Handle *entry = _open_direct_chat(
    351     handle->app, handle->contact
    352   );
    353 
    354   if ((!entry) || (!(entry->entry_box)))
    355     goto close_dialog;
    356 
    357   GtkListBoxRow *row = GTK_LIST_BOX_ROW(
    358     gtk_widget_get_parent(entry->entry_box)
    359   );
    360 
    361   if (!row)
    362     goto close_dialog;
    363 
    364   gtk_list_box_select_row(handle->app->ui.messenger.chats_listbox, row);
    365   gtk_list_box_invalidate_filter(handle->app->ui.messenger.chats_listbox);
    366 
    367   gtk_widget_activate(GTK_WIDGET(row));
    368 
    369 close_dialog:
    370   gtk_window_close(GTK_WINDOW(handle->dialog));
    371 }
    372 
    373 static void
    374 handle_back_button_click(UNUSED GtkButton *button,
    375                          gpointer user_data)
    376 {
    377   g_assert(user_data);
    378 
    379   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
    380 
    381   gtk_widget_set_visible(GTK_WIDGET(handle->back_button), FALSE);
    382 
    383   gtk_stack_set_visible_child(
    384     handle->contact_info_stack,
    385     handle->details_box
    386   );
    387 }
    388 
    389 static void
    390 handle_close_button_click(UNUSED GtkButton *button,
    391                           gpointer user_data)
    392 {
    393   g_assert(user_data);
    394 
    395   GtkDialog *dialog = GTK_DIALOG(user_data);
    396   gtk_window_close(GTK_WINDOW(dialog));
    397 }
    398 
    399 static void
    400 handle_dialog_destroy(UNUSED GtkWidget *window,
    401                       gpointer user_data)
    402 {
    403   g_assert(user_data);
    404 
    405   ui_contact_info_dialog_cleanup((UI_CONTACT_INFO_Handle*) user_data);
    406 }
    407 
    408 static gboolean
    409 handle_id_drawing_area_draw(GtkWidget* drawing_area,
    410                             cairo_t* cairo,
    411                             gpointer user_data)
    412 {
    413   g_assert((drawing_area) && (cairo) && (user_data));
    414 
    415   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
    416 
    417   GtkStyleContext* context = gtk_widget_get_style_context(drawing_area);
    418 
    419   if (!context)
    420     return FALSE;
    421 
    422   const guint width = gtk_widget_get_allocated_width(drawing_area);
    423   const guint height = gtk_widget_get_allocated_height(drawing_area);
    424 
    425   gtk_render_background(context, cairo, 0, 0, width, height);
    426 
    427   if ((!(handle->qr)) || (handle->qr->width <= 0))
    428     return FALSE;
    429 
    430   const guint m = 3;
    431   const guint w = handle->qr->width;
    432   const guint w2 = w + m * 2;
    433 
    434   guchar *pixels = (guchar*) g_malloc(sizeof(guchar) * w2 * w2 * 3);
    435 
    436   guint x, y, z;
    437   for (y = 0; y < w2; y++)
    438     for (x = 0; x < w2; x++)
    439     {
    440       guchar value;
    441 
    442       if ((x >= m) && (y >= m) && (x - m < w) && (y - m < w))
    443 	      value  = ((handle->qr->data[(y - m) * w + x - m]) & 1);
    444       else
    445 	      value = 0;
    446 
    447       for (z = 0; z < 3; z++)
    448 	      pixels[(y * w2 + x) * 3 + z] = value? 0x00 : 0xff;
    449     }
    450 
    451   GdkPixbuf *image = gdk_pixbuf_new_from_data(
    452     pixels,
    453     GDK_COLORSPACE_RGB,
    454     FALSE,
    455     8,
    456     w2,
    457     w2,
    458     w2 * 3,
    459     NULL,
    460     NULL
    461   );
    462 
    463   if (!image)
    464     return FALSE;
    465 
    466   int dwidth = gdk_pixbuf_get_width(image);
    467   int dheight = gdk_pixbuf_get_height(image);
    468 
    469   double ratio_width = 1.0 * width / dwidth;
    470   double ratio_height = 1.0 * height / dheight;
    471 
    472   const double ratio = ratio_width < ratio_height? ratio_width : ratio_height;
    473 
    474   dwidth = (int) (dwidth * ratio);
    475   dheight = (int) (dheight * ratio);
    476 
    477   double dx = (width - dwidth) * 0.5;
    478   double dy = (height - dheight) * 0.5;
    479 
    480   const int interp_type = (ratio >= 1.0?
    481     GDK_INTERP_NEAREST :
    482     GDK_INTERP_BILINEAR
    483   );
    484 
    485   GdkPixbuf* scaled = gdk_pixbuf_scale_simple(
    486     image,
    487     dwidth,
    488     dheight,
    489     interp_type
    490   );
    491 
    492   gtk_render_icon(context, cairo, scaled, dx, dy);
    493 
    494   cairo_fill(cairo);
    495 
    496   g_object_unref(scaled);
    497   g_object_unref(image);
    498 
    499   g_free(pixels);
    500 
    501   return FALSE;
    502 }
    503 
    504 static enum GNUNET_GenericReturnValue
    505 cb_contact_info_attributes(void *cls,
    506                            struct GNUNET_CHAT_Handle *chat,
    507                            const char *name,
    508                            const char *value)
    509 {
    510   g_assert((cls) && (chat) && (name) && (value));
    511 
    512   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) cls;
    513 
    514   gtk_list_store_insert_with_values(
    515     handle->attributes_list,
    516     NULL,
    517     -1,
    518     0,
    519     name,
    520     1,
    521     value,
    522     -1
    523   );
    524 
    525   return GNUNET_YES;
    526 }
    527 
    528 static enum GNUNET_GenericReturnValue
    529 cb_contact_info_contact_attributes(void *cls,
    530                                    struct GNUNET_CHAT_Contact *contact,
    531                                    const char *name,
    532                                    const char *value)
    533 {
    534   g_assert((cls) && (contact) && (name) && (value));
    535 
    536   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) cls;
    537 
    538   GtkTreeModel *model = GTK_TREE_MODEL(handle->attributes_list);
    539   GtkTreeIter iter;
    540 
    541   GValue val_name = G_VALUE_INIT;
    542   GValue val_value = G_VALUE_INIT;
    543 
    544   gboolean valid = gtk_tree_model_get_iter_first(model, &iter);
    545   gboolean match = FALSE;
    546 
    547   while (valid)
    548   {
    549     gtk_tree_model_get_value(model, &iter, 0, &val_name);
    550     gtk_tree_model_get_value(model, &iter, 1, &val_value);
    551     
    552     if (0 == strcmp(g_value_get_string(&val_name), name))
    553     {
    554       gtk_list_store_set(
    555         handle->attributes_list,
    556         &iter,
    557         1,
    558         g_value_get_string(&val_value),
    559         -1
    560       );
    561       
    562       match = TRUE;
    563     }
    564 
    565     g_value_unset(&val_name);
    566     g_value_unset(&val_value);
    567 
    568     if (match)
    569       break;
    570 
    571     valid = gtk_tree_model_iter_next(model, &iter);
    572   }
    573 
    574   if (!match)
    575     gtk_list_store_insert_with_values(
    576       handle->attributes_list,
    577       NULL,
    578       -1,
    579       0,
    580       name,
    581       1,
    582       value,
    583       -1
    584     );
    585 
    586   return GNUNET_YES;
    587 }
    588 
    589 static enum GNUNET_GenericReturnValue
    590 cb_contact_info_unshared_attributes(void *cls,
    591                                     struct GNUNET_CHAT_Handle *chat,
    592                                     const char *name,
    593                                     const char *value)
    594 {
    595   g_assert((cls) && (chat) && (name) && (value));
    596 
    597   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) cls;
    598   GtkTreeModel *model = GTK_TREE_MODEL(handle->sharing_list);
    599   GtkTreeIter iter;
    600 
    601   GValue val_name = G_VALUE_INIT;
    602   GValue val_value = G_VALUE_INIT;
    603 
    604   gboolean valid = gtk_tree_model_get_iter_first(model, &iter);
    605   gboolean match = FALSE;
    606 
    607   while (valid)
    608   {
    609     gtk_tree_model_get_value(model, &iter, 0, &val_name);
    610     gtk_tree_model_get_value(model, &iter, 1, &val_value);
    611     
    612     if ((0 == strcmp(g_value_get_string(&val_name), name)) &&
    613         (0 == strcmp(g_value_get_string(&val_value), value)))
    614       match = TRUE;
    615 
    616     g_value_unset(&val_name);
    617     g_value_unset(&val_value);
    618 
    619     if (match)
    620       break;
    621 
    622     valid = gtk_tree_model_iter_next(model, &iter);
    623   }
    624 
    625   if (!match)
    626     gtk_list_store_insert_with_values(
    627       handle->sharing_list,
    628       NULL,
    629       -1,
    630       0,
    631       name,
    632       1,
    633       value,
    634       2,
    635       FALSE,
    636       -1
    637     );
    638 
    639   return GNUNET_YES;
    640 }
    641 
    642 static enum GNUNET_GenericReturnValue
    643 cb_contact_info_shared_attributes(void *cls,
    644                                   struct GNUNET_CHAT_Contact *contact,
    645                                   const char *name,
    646                                   const char *value)
    647 {
    648   g_assert((cls) && (contact) && (name) && (value));
    649 
    650   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) cls;
    651   GtkTreeModel *model = GTK_TREE_MODEL(handle->sharing_list);
    652   GtkTreeIter iter;
    653 
    654   GValue val_name = G_VALUE_INIT;
    655   GValue val_value = G_VALUE_INIT;
    656 
    657   gboolean valid = gtk_tree_model_get_iter_first(model, &iter);
    658   gboolean match = FALSE;
    659 
    660   while (valid)
    661   {
    662     gtk_tree_model_get_value(model, &iter, 0, &val_name);
    663     gtk_tree_model_get_value(model, &iter, 1, &val_value);
    664     
    665     if ((0 == strcmp(g_value_get_string(&val_name), name)) &&
    666         (0 == strcmp(g_value_get_string(&val_value), value)))
    667       match = TRUE;
    668 
    669     g_value_unset(&val_name);
    670     g_value_unset(&val_value);
    671 
    672     if (match)
    673       break;
    674 
    675     valid = gtk_tree_model_iter_next(model, &iter);
    676   }
    677 
    678   if (match)
    679     gtk_list_store_set(handle->sharing_list, &iter, 2, TRUE, -1);
    680   else
    681     gtk_list_store_insert_with_values(
    682       handle->sharing_list,
    683       NULL,
    684       -1,
    685       0,
    686       name,
    687       1,
    688       value,
    689       2,
    690       TRUE,
    691       -1
    692     );
    693 
    694   return GNUNET_YES;
    695 }
    696 
    697 static enum GNUNET_GenericReturnValue
    698 cb_contact_info_contact_tags(void *cls,
    699                              struct GNUNET_CHAT_Contact *contact,
    700                              const char *tag)
    701 {
    702   g_assert((cls) && (contact) && (tag));
    703 
    704   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) cls;
    705   GtkTreeModel *model = GTK_TREE_MODEL(handle->tags_list);
    706   GtkTreeIter iter;
    707 
    708   GValue val_tag = G_VALUE_INIT;
    709 
    710   gboolean valid = gtk_tree_model_get_iter_first(model, &iter);
    711   gboolean match = FALSE;
    712 
    713   while (valid)
    714   {
    715     gtk_tree_model_get_value(model, &iter, 0, &val_tag);
    716     
    717     if (0 == strcmp(g_value_get_string(&val_tag), tag))
    718       match = TRUE;
    719 
    720     g_value_unset(&val_tag);
    721 
    722     if (match)
    723       break;
    724 
    725     valid = gtk_tree_model_iter_next(model, &iter);
    726   }
    727 
    728   if (!match)
    729     gtk_list_store_insert_with_values(
    730       handle->tags_list,
    731       NULL,
    732       -1,
    733       0,
    734       tag,
    735       -1
    736     );
    737 
    738   return GNUNET_YES;
    739 }
    740 
    741 static void
    742 handle_value_renderer_edit(GtkCellRendererText *renderer,
    743                            char *path,
    744                            char *new_text,
    745                            gpointer user_data)
    746 {
    747   g_assert((renderer) && (path) && (new_text) && (user_data));
    748 
    749   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
    750   GtkTreeIter iter;
    751 
    752   if (!gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(handle->attributes_list), &iter, path))
    753     return;
    754 
    755   struct GNUNET_CHAT_Handle *chat = handle->app->chat.messenger.handle;
    756 
    757   if (!chat)
    758     return;
    759 
    760   application_chat_lock(handle->app);
    761   const gboolean owned = (GNUNET_YES == GNUNET_CHAT_contact_is_owned(handle->contact));
    762   application_chat_unlock(handle->app);
    763 
    764   if ((handle->contact) && (!owned))
    765     return;
    766 
    767   GValue value = G_VALUE_INIT;
    768   gtk_tree_model_get_value(GTK_TREE_MODEL(handle->attributes_list), &iter, 0, &value);
    769 
    770   const gchar *name = g_value_get_string(&value);
    771 
    772   if ((new_text) && (strlen(new_text)))
    773   {
    774     application_chat_lock(handle->app);
    775     GNUNET_CHAT_set_attribute(chat, name, new_text);
    776     application_chat_unlock(handle->app);
    777 
    778     gtk_list_store_set(handle->attributes_list, &iter, 1, new_text, -1);
    779   }
    780   else
    781   {
    782     application_chat_lock(handle->app);
    783     GNUNET_CHAT_delete_attribute(chat, name);
    784     application_chat_unlock(handle->app);
    785 
    786     gtk_list_store_remove(handle->attributes_list, &iter);
    787   }
    788 
    789   g_value_unset(&value);
    790 }
    791 
    792 static void
    793 handle_attribute_entry_changed(GtkEditable *editable,
    794                                gpointer user_data)
    795 {
    796   g_assert((editable) && (user_data));
    797 
    798   GtkEntry *entry = GTK_ENTRY(editable);
    799   GtkWidget *target = GTK_WIDGET(user_data);
    800 
    801   const gchar *text = gtk_entry_get_text(entry);
    802 
    803   gtk_widget_set_sensitive(target, (text) && (strlen(text)));
    804 }
    805 
    806 static void
    807 handle_add_attribute_button_click(UNUSED GtkButton *button,
    808                                   gpointer user_data)
    809 {
    810   g_assert(user_data);
    811 
    812   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
    813 
    814   struct GNUNET_CHAT_Handle *chat = handle->app->chat.messenger.handle;
    815 
    816   if (!chat)
    817     return;
    818 
    819   const gchar *name = gtk_entry_get_text(handle->attribute_name_entry);
    820   const gchar *value = gtk_entry_get_text(handle->attribute_value_entry);
    821 
    822   if ((name) && (value))
    823   {
    824     application_chat_lock(handle->app);
    825     GNUNET_CHAT_set_attribute(chat, name, value);
    826     application_chat_unlock(handle->app);
    827 
    828     gtk_list_store_insert_with_values(
    829       handle->attributes_list,
    830       NULL,
    831       -1,
    832       0,
    833       name,
    834       1,
    835       value,
    836       -1
    837     );
    838   }
    839 
    840   gtk_entry_set_text(handle->attribute_name_entry, "");
    841   gtk_entry_set_text(handle->attribute_value_entry, "");
    842 }
    843 
    844 static void
    845 handle_attribute_entry_activate(UNUSED GtkEntry *entry,
    846                                 gpointer user_data)
    847 {
    848   g_assert(user_data);
    849 
    850   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
    851 
    852   handle_add_attribute_button_click(handle->add_attribute_button, handle);
    853 }
    854 
    855 static void
    856 handle_share_renderer_toggle(GtkCellRendererToggle *renderer,
    857                              char *path,
    858                              gpointer user_data)
    859 {
    860   g_assert((renderer) && (path) && (user_data));
    861 
    862   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
    863   GtkTreeIter iter;
    864 
    865   if (!gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(handle->sharing_list), &iter, path))
    866     return;
    867 
    868   struct GNUNET_CHAT_Handle *chat = handle->app->chat.messenger.handle;
    869 
    870   if (!chat)
    871     return;
    872 
    873   application_chat_lock(handle->app);
    874   const gboolean owned = (GNUNET_YES == GNUNET_CHAT_contact_is_owned(handle->contact));
    875   application_chat_unlock(handle->app);
    876 
    877   if ((!(handle->contact)) || (owned))
    878     return;
    879 
    880   GValue value_name = G_VALUE_INIT;
    881   GValue value_shared = G_VALUE_INIT;
    882 
    883   gtk_tree_model_get_value(GTK_TREE_MODEL(handle->sharing_list), &iter, 0, &value_name);
    884   gtk_tree_model_get_value(GTK_TREE_MODEL(handle->sharing_list), &iter, 2, &value_shared);
    885 
    886   const gchar *name = g_value_get_string(&value_name);
    887   const gboolean shared = g_value_get_boolean(&value_shared);
    888 
    889   application_chat_lock(handle->app);
    890 
    891   if (shared)
    892     GNUNET_CHAT_unshare_attribute_from(chat, handle->contact, name);
    893   else
    894     GNUNET_CHAT_share_attribute_with(chat, handle->contact, name);
    895 
    896   application_chat_unlock(handle->app);
    897 
    898   gtk_list_store_set(handle->sharing_list, &iter, 2, !shared, -1);
    899 
    900   g_value_unset(&value_name);
    901   g_value_unset(&value_shared);
    902 }
    903 
    904 static void
    905 handle_tag_tree_selection_changed(GtkTreeSelection *selection,
    906                                   gpointer user_data)
    907 {
    908   g_assert((selection) && (user_data));
    909 
    910   GtkWidget *widget = GTK_WIDGET(user_data);
    911 
    912   const gboolean selected = gtk_tree_selection_get_selected(
    913     selection, NULL, NULL);
    914 
    915   gtk_widget_set_sensitive(widget, selected);
    916 }
    917 
    918 static void
    919 handle_tag_entry_changed(GtkEditable *editable,
    920                          gpointer user_data)
    921 {
    922   g_assert((editable) && (user_data));
    923 
    924   GtkEntry *entry = GTK_ENTRY(editable);
    925   GtkWidget *target = GTK_WIDGET(user_data);
    926 
    927   const gchar *text = gtk_entry_get_text(entry);
    928 
    929   gtk_widget_set_sensitive(target, (text) && (strlen(text)));
    930 }
    931 
    932 static void
    933 handle_add_tag_button_click(UNUSED GtkButton *button,
    934                             gpointer user_data)
    935 {
    936   g_assert(user_data);
    937 
    938   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
    939 
    940   struct GNUNET_CHAT_Handle *chat = handle->app->chat.messenger.handle;
    941 
    942   if (!chat)
    943     return;
    944 
    945   const gchar *tag = gtk_entry_get_text(handle->tag_name_entry);
    946 
    947   if (tag)
    948   {
    949     application_chat_lock(handle->app);
    950     GNUNET_CHAT_contact_tag(handle->contact, tag);
    951     application_chat_unlock(handle->app);
    952 
    953     gtk_list_store_insert_with_values(
    954       handle->tags_list,
    955       NULL,
    956       -1,
    957       0,
    958       tag,
    959       -1
    960     );
    961   }
    962 
    963   gtk_entry_set_text(handle->tag_name_entry, "");
    964 }
    965 
    966 static void
    967 handle_remove_tag_button_click(UNUSED GtkButton *button,
    968                                gpointer user_data)
    969 {
    970   g_assert(user_data);
    971 
    972   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
    973   GtkTreeIter iter;
    974 
    975   if (!gtk_tree_selection_get_selected(handle->tags_tree_selection, NULL, &iter))
    976     return;
    977 
    978   struct GNUNET_CHAT_Handle *chat = handle->app->chat.messenger.handle;
    979 
    980   if (!chat)
    981     return;
    982 
    983   GValue value = G_VALUE_INIT;
    984   gtk_tree_model_get_value(GTK_TREE_MODEL(handle->tags_list), &iter, 0, &value);
    985 
    986   const gchar *tag = g_value_get_string(&value);
    987 
    988   if (tag)
    989   {
    990     application_chat_lock(handle->app);
    991     GNUNET_CHAT_contact_untag(handle->contact, tag);
    992     application_chat_unlock(handle->app);
    993 
    994     gtk_list_store_remove(
    995       handle->tags_list,
    996       &iter
    997     );
    998   }
    999 }
   1000 
   1001 static void
   1002 handle_tag_entry_activate(UNUSED GtkEntry *entry,
   1003                           gpointer user_data)
   1004 {
   1005   g_assert(user_data);
   1006 
   1007   UI_CONTACT_INFO_Handle *handle = (UI_CONTACT_INFO_Handle*) user_data;
   1008 
   1009   handle_add_tag_button_click(handle->add_tag_button, handle);
   1010 }
   1011 
   1012 void
   1013 ui_contact_info_dialog_init(MESSENGER_Application *app,
   1014                             UI_CONTACT_INFO_Handle *handle)
   1015 {
   1016   g_assert((app) && (handle));
   1017 
   1018   handle->app = app;
   1019 
   1020   handle->account = NULL;
   1021   handle->contact = NULL;
   1022 
   1023   handle->builder = ui_builder_from_resource(
   1024     application_get_resource_path(app, "ui/contact_info.ui")
   1025   );
   1026 
   1027   handle->dialog = GTK_DIALOG(
   1028     gtk_builder_get_object(handle->builder, "contact_info_dialog")
   1029   );
   1030 
   1031   gtk_window_set_transient_for(
   1032     GTK_WINDOW(handle->dialog),
   1033     GTK_WINDOW(app->ui.messenger.main_window)
   1034   );
   1035 
   1036   handle->contact_info_stack = GTK_STACK(
   1037     gtk_builder_get_object(handle->builder, "contact_info_stack")
   1038   );
   1039 
   1040   handle->details_box = GTK_WIDGET(
   1041     gtk_builder_get_object(handle->builder, "details_box")
   1042   );
   1043 
   1044   handle->contact_avatar = HDY_AVATAR(
   1045     gtk_builder_get_object(handle->builder, "contact_avatar")
   1046   );
   1047 
   1048   handle->contact_name_entry = GTK_ENTRY(
   1049     gtk_builder_get_object(handle->builder, "contact_name")
   1050   );
   1051 
   1052   handle->contact_edit_button = GTK_BUTTON(
   1053     gtk_builder_get_object(handle->builder, "contact_edit_button")
   1054   );
   1055 
   1056   handle->contact_edit_symbol = GTK_IMAGE(
   1057     gtk_builder_get_object(handle->builder, "contact_edit_symbol")
   1058   );
   1059 
   1060   g_signal_connect(
   1061     handle->contact_name_entry,
   1062     "activate",
   1063     G_CALLBACK(handle_contact_name_entry_activate),
   1064     handle
   1065   );
   1066 
   1067   g_signal_connect(
   1068     handle->contact_edit_button,
   1069     "clicked",
   1070     G_CALLBACK(handle_contact_edit_button_click),
   1071     handle
   1072   );
   1073 
   1074   handle->profile_chooser_button = GTK_FILE_CHOOSER_BUTTON(
   1075     gtk_builder_get_object(handle->builder, "profile_chooser_button")
   1076   );
   1077 
   1078   g_signal_connect(
   1079     handle->profile_chooser_button,
   1080     "update-preview",
   1081     G_CALLBACK(handle_profile_chooser_update_preview),
   1082     handle->contact_avatar
   1083   );
   1084 
   1085   g_signal_connect(
   1086     handle->profile_chooser_button,
   1087     "file-set",
   1088     G_CALLBACK(handle_profile_chooser_file_set),
   1089     handle
   1090   );
   1091 
   1092   handle->reveal_identity_button = GTK_BUTTON(
   1093     gtk_builder_get_object(handle->builder, "reveal_identity_button")
   1094   );
   1095 
   1096   g_signal_connect(
   1097     handle->reveal_identity_button,
   1098     "clicked",
   1099     G_CALLBACK(handle_reveal_identity_button_click),
   1100     handle
   1101   );
   1102 
   1103   handle->list_attributes_button = GTK_BUTTON(
   1104     gtk_builder_get_object(handle->builder, "list_attributes_button")
   1105   );
   1106 
   1107   handle->share_attributes_button = GTK_BUTTON(
   1108     gtk_builder_get_object(handle->builder, "share_attributes_button")
   1109   );
   1110 
   1111   g_signal_connect(
   1112     handle->list_attributes_button,
   1113     "clicked",
   1114     G_CALLBACK(handle_list_attributes_button_click),
   1115     handle
   1116   );
   1117 
   1118   g_signal_connect(
   1119     handle->share_attributes_button,
   1120     "clicked",
   1121     G_CALLBACK(handle_share_attributes_button_click),
   1122     handle
   1123   );
   1124 
   1125   handle->list_tags_button = GTK_BUTTON(
   1126     gtk_builder_get_object(handle->builder, "list_tags_button")
   1127   );
   1128 
   1129   g_signal_connect(
   1130     handle->list_tags_button,
   1131     "clicked",
   1132     G_CALLBACK(handle_list_tags_button_click),
   1133     handle
   1134   );
   1135 
   1136   handle->block_stack = GTK_STACK(
   1137     gtk_builder_get_object(handle->builder, "block_stack")
   1138   );
   1139 
   1140   handle->block_button = GTK_BUTTON(
   1141     gtk_builder_get_object(handle->builder, "block_button")
   1142   );
   1143 
   1144   g_signal_connect(
   1145     handle->block_button,
   1146     "clicked",
   1147     G_CALLBACK(handle_block_button_click),
   1148     handle
   1149   );
   1150 
   1151   handle->unblock_button = GTK_BUTTON(
   1152     gtk_builder_get_object(handle->builder, "unblock_button")
   1153   );
   1154 
   1155   g_signal_connect(
   1156     handle->unblock_button,
   1157     "clicked",
   1158     G_CALLBACK(handle_unblock_button_click),
   1159     handle
   1160   );
   1161 
   1162   handle->open_chat_button = GTK_BUTTON(
   1163     gtk_builder_get_object(handle->builder, "open_chat_button")
   1164   );
   1165 
   1166   g_signal_connect(
   1167     handle->open_chat_button,
   1168     "clicked",
   1169     G_CALLBACK(handle_open_chat_button_click),
   1170     handle
   1171   );
   1172 
   1173   handle->identity_box = GTK_WIDGET(
   1174     gtk_builder_get_object(handle->builder, "identity_box")
   1175   );
   1176 
   1177   handle->name_label = GTK_LABEL(
   1178     gtk_builder_get_object(handle->builder, "name_label")
   1179   );
   1180 
   1181   handle->id_drawing_area = GTK_DRAWING_AREA(
   1182     gtk_builder_get_object(handle->builder, "id_drawing_area")
   1183   );
   1184 
   1185   handle->id_draw_signal = g_signal_connect(
   1186     handle->id_drawing_area,
   1187     "draw",
   1188     G_CALLBACK(handle_id_drawing_area_draw),
   1189     handle
   1190   );
   1191 
   1192   handle->id_entry = GTK_ENTRY(
   1193     gtk_builder_get_object(handle->builder, "id_entry")
   1194   );
   1195 
   1196   handle->attributes_box = GTK_WIDGET(
   1197     gtk_builder_get_object(handle->builder, "attributes_box")
   1198   );
   1199 
   1200   handle->attributes_tree = GTK_TREE_VIEW(
   1201     gtk_builder_get_object(handle->builder, "attributes_tree")
   1202   );
   1203 
   1204   handle->attributes_list = GTK_LIST_STORE(
   1205     gtk_builder_get_object(handle->builder, "attributes_list")
   1206   );
   1207 
   1208   handle->value_renderer = GTK_CELL_RENDERER_TEXT(
   1209     gtk_builder_get_object(handle->builder, "value_renderer")
   1210   );
   1211 
   1212   g_signal_connect(
   1213     handle->value_renderer,
   1214     "edited",
   1215     G_CALLBACK(handle_value_renderer_edit),
   1216     handle
   1217   );
   1218 
   1219   handle->new_attribute_box = GTK_WIDGET(
   1220     gtk_builder_get_object(handle->builder, "new_attribute_box")
   1221   );
   1222 
   1223   handle->attribute_name_entry = GTK_ENTRY(
   1224     gtk_builder_get_object(handle->builder, "attribute_name_entry")
   1225   );
   1226 
   1227   handle->attribute_value_entry = GTK_ENTRY(
   1228     gtk_builder_get_object(handle->builder, "attribute_value_entry")
   1229   );
   1230 
   1231   handle->add_attribute_button = GTK_BUTTON(
   1232     gtk_builder_get_object(handle->builder, "add_attribute_button")
   1233   );
   1234 
   1235   g_signal_connect(
   1236     handle->attribute_name_entry,
   1237     "changed",
   1238     G_CALLBACK(handle_attribute_entry_changed),
   1239     handle->attribute_value_entry
   1240   );
   1241 
   1242   g_signal_connect(
   1243     handle->attribute_value_entry,
   1244     "changed",
   1245     G_CALLBACK(handle_attribute_entry_changed),
   1246     handle->add_attribute_button
   1247   );
   1248 
   1249   g_signal_connect(
   1250     handle->attribute_value_entry,
   1251     "activate",
   1252     G_CALLBACK(handle_attribute_entry_activate),
   1253     handle
   1254   );
   1255 
   1256   g_signal_connect(
   1257     handle->add_attribute_button,
   1258     "clicked",
   1259     G_CALLBACK(handle_add_attribute_button_click),
   1260     handle
   1261   );
   1262 
   1263   handle->sharing_box = GTK_WIDGET(
   1264     gtk_builder_get_object(handle->builder, "sharing_box")
   1265   );
   1266 
   1267   handle->sharing_tree = GTK_TREE_VIEW(
   1268     gtk_builder_get_object(handle->builder, "sharing_tree")
   1269   );
   1270 
   1271   handle->sharing_list = GTK_LIST_STORE(
   1272     gtk_builder_get_object(handle->builder, "sharing_list")
   1273   );
   1274 
   1275   handle->share_renderer = GTK_CELL_RENDERER_TOGGLE(
   1276     gtk_builder_get_object(handle->builder, "share_renderer")
   1277   );
   1278 
   1279   g_signal_connect(
   1280     handle->share_renderer,
   1281     "toggled",
   1282     G_CALLBACK(handle_share_renderer_toggle),
   1283     handle
   1284   );
   1285 
   1286   handle->tags_box = GTK_WIDGET(
   1287     gtk_builder_get_object(handle->builder, "tags_box")
   1288   );
   1289 
   1290   handle->tags_tree = GTK_TREE_VIEW(
   1291     gtk_builder_get_object(handle->builder, "tags_tree")
   1292   );
   1293 
   1294   handle->tags_tree_selection = GTK_TREE_SELECTION(
   1295     gtk_builder_get_object(handle->builder, "tags_tree_selection")
   1296   );
   1297 
   1298   handle->tags_list = GTK_LIST_STORE(
   1299     gtk_builder_get_object(handle->builder, "tags_list")
   1300   );
   1301 
   1302   handle->new_tag_box = GTK_WIDGET(
   1303     gtk_builder_get_object(handle->builder, "new_tag_box")
   1304   );
   1305 
   1306   handle->tag_name_entry = GTK_ENTRY(
   1307     gtk_builder_get_object(handle->builder, "tag_name_entry")
   1308   );
   1309 
   1310   handle->add_tag_button = GTK_BUTTON(
   1311     gtk_builder_get_object(handle->builder, "add_tag_button")
   1312   );
   1313 
   1314   handle->remove_tag_button = GTK_BUTTON(
   1315     gtk_builder_get_object(handle->builder, "remove_tag_button")
   1316   );
   1317 
   1318   g_signal_connect(
   1319     handle->tags_tree_selection,
   1320     "changed",
   1321     G_CALLBACK(handle_tag_tree_selection_changed),
   1322     handle->remove_tag_button
   1323   );
   1324 
   1325   g_signal_connect(
   1326     handle->tag_name_entry,
   1327     "changed",
   1328     G_CALLBACK(handle_tag_entry_changed),
   1329     handle->add_tag_button
   1330   );
   1331 
   1332   g_signal_connect(
   1333     handle->tag_name_entry,
   1334     "activate",
   1335     G_CALLBACK(handle_tag_entry_activate),
   1336     handle
   1337   );
   1338 
   1339   g_signal_connect(
   1340     handle->add_tag_button,
   1341     "clicked",
   1342     G_CALLBACK(handle_add_tag_button_click),
   1343     handle
   1344   );
   1345 
   1346   g_signal_connect(
   1347     handle->remove_tag_button,
   1348     "clicked",
   1349     G_CALLBACK(handle_remove_tag_button_click),
   1350     handle
   1351   );
   1352 
   1353   handle->open_chat_stack = GTK_STACK(
   1354     gtk_builder_get_object(handle->builder, "open_chat_stack")
   1355   );
   1356 
   1357   handle->back_button = GTK_BUTTON(
   1358     gtk_builder_get_object(handle->builder, "back_button")
   1359   );
   1360 
   1361   g_signal_connect(
   1362     handle->back_button,
   1363     "clicked",
   1364     G_CALLBACK(handle_back_button_click),
   1365     handle
   1366   );
   1367 
   1368   handle->close_button = GTK_BUTTON(
   1369     gtk_builder_get_object(handle->builder, "close_button")
   1370   );
   1371 
   1372   g_signal_connect(
   1373     handle->close_button,
   1374     "clicked",
   1375     G_CALLBACK(handle_close_button_click),
   1376     handle->dialog
   1377   );
   1378 
   1379   g_signal_connect(
   1380     handle->dialog,
   1381     "destroy",
   1382     G_CALLBACK(handle_dialog_destroy),
   1383     handle
   1384   );
   1385 }
   1386 
   1387 static void
   1388 _contact_info_update(UI_CONTACT_INFO_Handle *handle,
   1389                      struct GNUNET_CHAT_Contact *contact)
   1390 {
   1391   g_assert(handle);
   1392 
   1393   if (handle->contact)
   1394     contact_remove_name_avatar_from_info(handle->contact, handle->contact_avatar);
   1395   if (contact)
   1396     contact_add_name_avatar_to_info(contact, handle->contact_avatar);
   1397 
   1398   handle->contact = contact;
   1399 }
   1400 
   1401 static void
   1402 _account_info_update(UI_CONTACT_INFO_Handle *handle,
   1403                      struct GNUNET_CHAT_Account *account)
   1404 {
   1405   g_assert(handle);
   1406 
   1407   if (handle->account)
   1408     account_remove_name_avatar_from_info(handle->account, handle->contact_avatar);
   1409   if (account)
   1410     account_add_name_avatar_to_info(account, handle->contact_avatar);
   1411 
   1412   handle->account = account;
   1413 }
   1414 
   1415 void
   1416 ui_contact_info_dialog_update(UI_CONTACT_INFO_Handle *handle,
   1417                               struct GNUNET_CHAT_Contact *contact,
   1418                               gboolean reveal)
   1419 {
   1420   g_assert(handle);
   1421 
   1422   if (!contact)
   1423     contact = GNUNET_CHAT_get_own_contact(handle->app->chat.messenger.handle);
   1424 
   1425   const char *name = NULL;
   1426   const char *key = NULL;
   1427 
   1428   if (contact)
   1429     name = GNUNET_CHAT_contact_get_name(contact);
   1430   else
   1431     name = GNUNET_CHAT_get_name(handle->app->chat.messenger.handle);
   1432 
   1433   if (contact)
   1434     _contact_info_update(handle, contact);
   1435   else
   1436   {
   1437     struct GNUNET_CHAT_Account *account = GNUNET_CHAT_get_connected(
   1438       handle->app->chat.messenger.handle
   1439     );
   1440 
   1441     _account_info_update(handle, account);
   1442   }
   1443 
   1444   ui_entry_set_text(handle->contact_name_entry, name);
   1445 
   1446   const gboolean editable = (
   1447     (!contact) ||
   1448     (GNUNET_YES == GNUNET_CHAT_contact_is_owned(contact))
   1449   );
   1450 
   1451   GValue value = G_VALUE_INIT;
   1452   g_value_init(&value, G_TYPE_BOOLEAN);
   1453   g_value_set_boolean(&value, editable);
   1454 
   1455   g_object_set_property(
   1456     G_OBJECT(handle->value_renderer),
   1457     "editable",
   1458     &value
   1459   );
   1460 
   1461   g_value_unset(&value);
   1462 
   1463   gtk_widget_set_visible(
   1464     handle->new_attribute_box, 
   1465     editable
   1466   );
   1467 
   1468   if (contact)
   1469     key = GNUNET_CHAT_contact_get_key(contact);
   1470   else
   1471     key = GNUNET_CHAT_get_key(handle->app->chat.messenger.handle);
   1472 
   1473   if (handle->qr)
   1474     QRcode_free(handle->qr);
   1475 
   1476   if (key)
   1477     handle->qr = QRcode_encodeString(
   1478       key,
   1479       0,
   1480       QR_ECLEVEL_L,
   1481       QR_MODE_8,
   1482       0
   1483     );
   1484   else
   1485     handle->qr = NULL;
   1486 
   1487   ui_label_set_text(handle->name_label, name);
   1488 
   1489   if (handle->id_drawing_area)
   1490     gtk_widget_queue_draw(GTK_WIDGET(handle->id_drawing_area));
   1491 
   1492   ui_entry_set_text(handle->id_entry, key);
   1493 
   1494   gtk_widget_set_sensitive(
   1495     GTK_WIDGET(handle->profile_chooser_button),
   1496     editable
   1497   );
   1498 
   1499   gtk_widget_set_sensitive(
   1500     GTK_WIDGET(handle->reveal_identity_button),
   1501     key? TRUE : FALSE
   1502   );
   1503 
   1504   gtk_widget_set_sensitive(
   1505     GTK_WIDGET(handle->share_attributes_button),
   1506     !editable
   1507   );
   1508 
   1509   gtk_widget_set_sensitive(
   1510     GTK_WIDGET(handle->list_tags_button),
   1511     !editable
   1512   );
   1513 
   1514   gtk_widget_set_sensitive(
   1515     GTK_WIDGET(handle->tag_name_entry),
   1516     !editable
   1517   );
   1518 
   1519   gtk_widget_set_sensitive(
   1520     GTK_WIDGET(handle->block_button),
   1521     !editable
   1522   );
   1523 
   1524   gtk_widget_set_sensitive(
   1525     GTK_WIDGET(handle->unblock_button),
   1526     !editable
   1527   );
   1528 
   1529   gtk_widget_set_visible(
   1530     GTK_WIDGET(handle->profile_chooser_button),
   1531     editable
   1532   );
   1533 
   1534   gtk_widget_set_visible(
   1535     GTK_WIDGET(handle->share_attributes_button),
   1536     !editable
   1537   );
   1538 
   1539   gtk_widget_set_visible(
   1540     GTK_WIDGET(handle->list_tags_button),
   1541     !editable
   1542   );
   1543 
   1544   gtk_widget_set_visible(
   1545     GTK_WIDGET(handle->new_tag_box),
   1546     !editable
   1547   );
   1548 
   1549   gtk_stack_set_visible_child(
   1550     handle->block_stack,
   1551     GNUNET_YES == GNUNET_CHAT_contact_is_blocked(contact)?
   1552     GTK_WIDGET(handle->unblock_button) :
   1553     GTK_WIDGET(handle->block_button)
   1554   );
   1555 
   1556   gtk_widget_set_visible(
   1557     GTK_WIDGET(handle->block_stack),
   1558     !editable
   1559   );
   1560 
   1561   gtk_list_store_clear(handle->attributes_list);
   1562   gtk_list_store_clear(handle->sharing_list);
   1563   gtk_list_store_clear(handle->tags_list);
   1564 
   1565   if (editable)
   1566     GNUNET_CHAT_get_attributes(
   1567       handle->app->chat.messenger.handle,
   1568       cb_contact_info_attributes,
   1569       handle
   1570     );
   1571   else
   1572   {
   1573     GNUNET_CHAT_contact_get_attributes(
   1574       contact,
   1575       cb_contact_info_contact_attributes,
   1576       handle
   1577     );
   1578 
   1579     GNUNET_CHAT_get_attributes(
   1580       handle->app->chat.messenger.handle,
   1581       cb_contact_info_unshared_attributes,
   1582       handle
   1583     );
   1584 
   1585     GNUNET_CHAT_get_shared_attributes(
   1586       handle->app->chat.messenger.handle,
   1587       contact,
   1588       cb_contact_info_shared_attributes,
   1589       handle
   1590     );
   1591 
   1592     GNUNET_CHAT_contact_iterate_tags(
   1593       contact,
   1594       cb_contact_info_contact_tags,
   1595       handle
   1596     );
   1597   }
   1598 
   1599   gtk_stack_set_visible_child_name(
   1600     handle->open_chat_stack,
   1601     editable? "open_notes_page" : "open_private_chat_page"
   1602   );
   1603 
   1604   struct GNUNET_CHAT_Context *context = GNUNET_CHAT_contact_get_context(
   1605     contact
   1606   );
   1607 
   1608   gtk_widget_set_sensitive(
   1609     GTK_WIDGET(handle->open_chat_button),
   1610     context? TRUE : FALSE
   1611   );
   1612 
   1613   gtk_widget_set_visible(
   1614     GTK_WIDGET(handle->open_chat_button),
   1615     context? TRUE : FALSE
   1616   );
   1617 
   1618   if (reveal)
   1619     _contact_info_switch_stack_to(handle, handle->identity_box);
   1620 }
   1621 
   1622 void
   1623 ui_contact_info_dialog_cleanup(UI_CONTACT_INFO_Handle *handle)
   1624 {
   1625   g_assert(handle);
   1626 
   1627   g_signal_handler_disconnect(
   1628     handle->id_drawing_area,
   1629     handle->id_draw_signal
   1630   );
   1631 
   1632   _account_info_update(handle, NULL);
   1633   _contact_info_update(handle, NULL);
   1634 
   1635   g_object_unref(handle->builder);
   1636 
   1637   if (handle->qr)
   1638     QRcode_free(handle->qr);
   1639 
   1640   memset(handle, 0, sizeof(*handle));
   1641 }