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 }