messenger.c (23896B)
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/messenger.c 23 */ 24 25 #include "messenger.h" 26 27 #include <gtk-3.0/gdk/gdkkeys.h> 28 29 #include "account_entry.h" 30 #include "chat_entry.h" 31 #include "chat_title.h" 32 #include "contacts.h" 33 #include "message.h" 34 #include "new_contact.h" 35 #include "new_group.h" 36 #include "new_lobby.h" 37 #include "new_platform.h" 38 #include "settings.h" 39 40 #include "../account.h" 41 #include "../application.h" 42 #include "../ui.h" 43 44 static void 45 handle_user_details_folded(GObject* object, 46 GParamSpec* pspec, 47 gpointer user_data) 48 { 49 g_assert((object) && (pspec) && (user_data)); 50 51 HdyFlap* flap = HDY_FLAP(object); 52 UI_MESSENGER_Handle *handle = (UI_MESSENGER_Handle*) user_data; 53 54 const gboolean revealed = hdy_flap_get_reveal_flap(flap); 55 56 gtk_widget_set_sensitive( 57 GTK_WIDGET(handle->chats_search_entry), 58 !revealed 59 ); 60 61 gtk_widget_set_sensitive( 62 GTK_WIDGET(handle->chats_search_button), 63 !revealed 64 ); 65 66 GValue value = G_VALUE_INIT; 67 g_value_init(&value, G_TYPE_BOOLEAN); 68 g_value_set_boolean(&value, !revealed); 69 70 gtk_container_child_set_property( 71 GTK_CONTAINER(handle->leaflet_title), 72 GTK_WIDGET(handle->main_bar), 73 "navigatable", 74 &value 75 ); 76 77 gtk_container_child_set_property( 78 GTK_CONTAINER(handle->leaflet_chat), 79 handle->main_box, 80 "navigatable", 81 &value 82 ); 83 84 g_value_unset(&value); 85 } 86 87 static void 88 handle_profile_button_click(UNUSED GtkButton* button, 89 gpointer user_data) 90 { 91 g_assert(user_data); 92 93 UI_MESSENGER_Handle *handle = (UI_MESSENGER_Handle*) user_data; 94 MESSENGER_Application *app = handle->app; 95 96 hdy_flap_set_reveal_flap(handle->flap_user_details, FALSE); 97 98 ui_contact_info_dialog_init(app, &(app->ui.contact_info)); 99 ui_contact_info_dialog_update(&(app->ui.contact_info), NULL, FALSE); 100 101 gtk_widget_show(GTK_WIDGET(app->ui.contact_info.dialog)); 102 } 103 104 static gboolean 105 _flap_user_details_reveal_switch(gpointer user_data) 106 { 107 g_assert(user_data); 108 109 UI_MESSENGER_Handle *handle = (UI_MESSENGER_Handle*) user_data; 110 HdyFlap* flap = handle->flap_user_details; 111 112 if (TRUE == hdy_flap_get_reveal_flap(flap)) { 113 hdy_flap_set_reveal_flap(flap, FALSE); 114 } else { 115 hdy_flap_set_reveal_flap(flap, TRUE); 116 } 117 118 gtk_widget_set_sensitive(GTK_WIDGET(handle->chats_search_entry), TRUE); 119 gtk_widget_set_sensitive(GTK_WIDGET(handle->chats_listbox), TRUE); 120 return FALSE; 121 } 122 123 static void 124 handle_user_details_via_button_click(UNUSED GtkButton* button, 125 gpointer user_data) 126 { 127 g_assert(user_data); 128 129 UI_MESSENGER_Handle *handle = (UI_MESSENGER_Handle*) user_data; 130 131 gtk_widget_set_sensitive(GTK_WIDGET(handle->chats_search_entry), FALSE); 132 gtk_widget_set_sensitive(GTK_WIDGET(handle->chats_listbox), FALSE); 133 util_idle_add( 134 G_SOURCE_FUNC(_flap_user_details_reveal_switch), 135 handle 136 ); 137 } 138 139 static void 140 handle_lobby_button_click(UNUSED GtkButton* button, 141 gpointer user_data) 142 { 143 g_assert(user_data); 144 145 MESSENGER_Application *app = (MESSENGER_Application*) user_data; 146 147 hdy_flap_set_reveal_flap(HDY_FLAP(app->ui.messenger.flap_user_details), FALSE); 148 149 ui_new_lobby_dialog_init(app, &(app->ui.new_lobby)); 150 151 gtk_widget_show(GTK_WIDGET(app->ui.new_lobby.dialog)); 152 } 153 154 static void 155 _switch_details_revealer_visibility(UI_MESSENGER_Handle *handle, 156 gboolean state) 157 { 158 g_assert(handle); 159 160 GtkRevealer *revealer = handle->account_details_revealer; 161 GtkImage *symbol = handle->account_details_symbol; 162 163 gtk_revealer_set_reveal_child(revealer, state); 164 gtk_image_set_from_icon_name( 165 symbol, 166 state? 167 "go-up-symbolic" : 168 "go-down-symbolic", 169 GTK_ICON_SIZE_BUTTON 170 ); 171 } 172 173 static void 174 handle_account_details_button_click(UNUSED GtkButton* button, 175 gpointer user_data) 176 { 177 g_assert(user_data); 178 179 UI_MESSENGER_Handle *handle = (UI_MESSENGER_Handle*) user_data; 180 181 GtkRevealer *revealer = handle->account_details_revealer; 182 183 gboolean old_state = gtk_revealer_get_reveal_child(revealer); 184 185 _switch_details_revealer_visibility(handle, !old_state); 186 } 187 188 static void 189 handle_accounts_listbox_row_activated(UNUSED GtkListBox* listbox, 190 GtkListBoxRow* row, 191 gpointer user_data) 192 { 193 g_assert((row) && (user_data)); 194 195 MESSENGER_Application *app = (MESSENGER_Application*) user_data; 196 197 if (row == app->ui.messenger.add_account_listbox_row) 198 { 199 _switch_details_revealer_visibility(&(app->ui.messenger), FALSE); 200 hdy_flap_set_reveal_flap(HDY_FLAP(app->ui.messenger.flap_user_details), FALSE); 201 202 ui_new_account_dialog_init(app, &(app->ui.new_account)); 203 204 gtk_widget_show(GTK_WIDGET(app->ui.new_account.dialog)); 205 return; 206 } 207 208 struct GNUNET_CHAT_Account *account = (struct GNUNET_CHAT_Account*) ( 209 g_object_get_qdata(G_OBJECT(row), app->quarks.data) 210 ); 211 212 if (!account) 213 return; 214 215 application_chat_lock(app); 216 217 const struct GNUNET_CHAT_Account *current = GNUNET_CHAT_get_connected( 218 app->chat.messenger.handle 219 ); 220 221 application_chat_unlock(app); 222 223 if (account == current) 224 return; 225 226 _switch_details_revealer_visibility(&(app->ui.messenger), FALSE); 227 hdy_flap_set_reveal_flap(HDY_FLAP(app->ui.messenger.flap_user_details), FALSE); 228 229 application_chat_lock(app); 230 GNUNET_CHAT_connect(app->chat.messenger.handle, account); 231 application_chat_unlock(app); 232 } 233 234 static void 235 handle_new_contact_button_click(UNUSED GtkButton* button, 236 gpointer user_data) 237 { 238 g_assert(user_data); 239 240 MESSENGER_Application *app = (MESSENGER_Application*) user_data; 241 242 hdy_flap_set_reveal_flap(HDY_FLAP(app->ui.messenger.flap_user_details), FALSE); 243 ui_new_contact_dialog_init(app, &(app->ui.new_contact)); 244 gtk_widget_show(GTK_WIDGET(app->ui.new_contact.dialog)); 245 } 246 247 static void 248 handle_new_group_button_click(UNUSED GtkButton* button, 249 gpointer user_data) 250 { 251 g_assert(user_data); 252 253 MESSENGER_Application *app = (MESSENGER_Application*) user_data; 254 255 hdy_flap_set_reveal_flap(HDY_FLAP(app->ui.messenger.flap_user_details), FALSE); 256 ui_new_group_dialog_init(app, &(app->ui.new_group)); 257 gtk_widget_show(GTK_WIDGET(app->ui.new_group.dialog)); 258 } 259 260 static void 261 handle_new_platform_button_click(UNUSED GtkButton* button, 262 gpointer user_data) 263 { 264 g_assert(user_data); 265 266 MESSENGER_Application *app = (MESSENGER_Application*) user_data; 267 268 hdy_flap_set_reveal_flap(HDY_FLAP(app->ui.messenger.flap_user_details), FALSE); 269 ui_new_platform_dialog_init(app, &(app->ui.new_platform)); 270 gtk_widget_show(GTK_WIDGET(app->ui.new_platform.dialog)); 271 } 272 273 static void 274 handle_contacts_button_click(UNUSED GtkButton* button, 275 gpointer user_data) 276 { 277 g_assert(user_data); 278 279 MESSENGER_Application *app = (MESSENGER_Application*) user_data; 280 281 hdy_flap_set_reveal_flap(HDY_FLAP(app->ui.messenger.flap_user_details), FALSE); 282 ui_contacts_dialog_init(app, &(app->ui.contacts)); 283 gtk_widget_show(GTK_WIDGET(app->ui.contacts.dialog)); 284 } 285 286 static void 287 handle_settings_button_click(UNUSED GtkButton* button, 288 gpointer user_data) 289 { 290 g_assert(user_data); 291 292 MESSENGER_Application *app = (MESSENGER_Application*) user_data; 293 294 hdy_flap_set_reveal_flap(HDY_FLAP(app->ui.messenger.flap_user_details), FALSE); 295 ui_settings_dialog_init(app, &(app->ui.settings)); 296 gtk_widget_show(GTK_WIDGET(app->ui.settings.dialog)); 297 } 298 299 static void 300 handle_about_button_click(UNUSED GtkButton* button, 301 gpointer user_data) 302 { 303 g_assert(user_data); 304 305 MESSENGER_Application *app = (MESSENGER_Application*) user_data; 306 307 hdy_flap_set_reveal_flap(HDY_FLAP(app->ui.messenger.flap_user_details), FALSE); 308 ui_about_dialog_init(app, &(app->ui.about)); 309 gtk_widget_show(GTK_WIDGET(app->ui.about.dialog)); 310 } 311 312 static void 313 handle_chats_listbox_row_activated(UNUSED GtkListBox* listbox, 314 GtkListBoxRow* row, 315 gpointer user_data) 316 { 317 g_assert((row) && (user_data)); 318 319 MESSENGER_Application *app = (MESSENGER_Application*) user_data; 320 321 if (!gtk_list_box_row_get_selectable(row)) 322 return; 323 324 UI_CHAT_ENTRY_Handle *entry = (UI_CHAT_ENTRY_Handle*) ( 325 g_object_get_qdata(G_OBJECT(row), app->quarks.ui) 326 ); 327 328 if ((!entry) || (!(entry->chat)) || (!(entry->chat->chat_box))) 329 return; 330 331 UI_MESSENGER_Handle *ui = &(app->ui.messenger); 332 GList *children = gtk_container_get_children(GTK_CONTAINER(ui->leaflet_chat)); 333 334 if ((children) && (children->next)) 335 hdy_leaflet_set_visible_child(ui->leaflet_chat, GTK_WIDGET(children->next->data)); 336 337 if (children) 338 g_list_free(children); 339 340 gtk_stack_set_visible_child(ui->chats_stack, entry->chat->chat_box); 341 gtk_stack_set_visible_child(ui->chat_title_stack, entry->chat->title->chat_title_box); 342 } 343 344 static gint 345 handle_chats_listbox_sort_func(GtkListBoxRow* row0, 346 GtkListBoxRow* row1, 347 gpointer user_data) 348 { 349 g_assert((row0) && (row1) && (user_data)); 350 351 MESSENGER_Application *app = (MESSENGER_Application*) user_data; 352 353 if ((!gtk_list_box_row_get_selectable(row0)) || 354 (!gtk_list_box_row_get_selectable(row1))) 355 return 0; 356 357 UI_CHAT_ENTRY_Handle *entry0 = (UI_CHAT_ENTRY_Handle*) ( 358 g_object_get_qdata(G_OBJECT(row0), app->quarks.ui) 359 ); 360 361 UI_CHAT_ENTRY_Handle *entry1 = (UI_CHAT_ENTRY_Handle*) ( 362 g_object_get_qdata(G_OBJECT(row1), app->quarks.ui) 363 ); 364 365 if ((!entry0) || (!entry1)) 366 return 0; 367 368 time_t timestamp0 = entry0->timestamp; 369 time_t timestamp1 = entry1->timestamp; 370 371 const double diff = difftime(timestamp0, timestamp1); 372 373 if (diff > +0.0) 374 return -1; 375 else if (diff < -0.0) 376 return +1; 377 else 378 return 0; 379 } 380 381 static gboolean 382 handle_chats_listbox_filter_func(GtkListBoxRow *row, 383 gpointer user_data) 384 { 385 g_assert((row) && (user_data)); 386 387 MESSENGER_Application *app = (MESSENGER_Application*) user_data; 388 389 if ((!gtk_list_box_row_get_selectable(row)) || 390 (gtk_list_box_row_is_selected(row))) 391 return TRUE; 392 393 const gchar *filter = gtk_entry_get_text( 394 GTK_ENTRY(app->ui.messenger.chats_search_entry) 395 ); 396 397 if (!filter) 398 return TRUE; 399 400 UI_CHAT_ENTRY_Handle *entry = (UI_CHAT_ENTRY_Handle*) ( 401 g_object_get_qdata(G_OBJECT(row), app->quarks.ui) 402 ); 403 404 if ((!entry) || (!(entry->title_label))) 405 return FALSE; 406 407 const gchar *title = gtk_label_get_text(entry->title_label); 408 409 if (!title) 410 return FALSE; 411 412 return g_str_match_string(filter, title, TRUE); 413 } 414 415 static void 416 handle_search_button_click(UNUSED GtkButton *button, 417 gpointer user_data) 418 { 419 g_assert(user_data); 420 421 UI_MESSENGER_Handle *handle = (UI_MESSENGER_Handle*) user_data; 422 423 if (handle->search_box == gtk_stack_get_visible_child(handle->chats_title_stack)) 424 { 425 gtk_stack_set_visible_child(handle->chats_title_stack, handle->title_box); 426 gtk_entry_set_text(GTK_ENTRY(handle->chats_search_entry), ""); 427 } 428 else 429 gtk_stack_set_visible_child(handle->chats_title_stack, handle->search_box); 430 } 431 432 static void 433 handle_chats_search_changed(UNUSED GtkSearchEntry *search, 434 gpointer user_data) 435 { 436 g_assert(user_data); 437 438 GtkListBox *listbox = GTK_LIST_BOX(user_data); 439 440 gtk_list_box_invalidate_filter(listbox); 441 } 442 443 static void 444 handle_main_window_destroy(UNUSED GtkWidget *window, 445 gpointer user_data) 446 { 447 g_assert(user_data); 448 449 MESSENGER_Application *app = (MESSENGER_Application*) user_data; 450 451 #ifndef MESSENGER_APPLICATION_NO_PORTAL 452 if (app->parent) 453 xdp_parent_free(app->parent); 454 455 app->parent = NULL; 456 #endif 457 458 ui_messenger_cleanup(&(app->ui.messenger)); 459 460 account_cleanup_infos(); 461 462 application_exit(app, MESSENGER_QUIT); 463 } 464 465 void 466 ui_messenger_init(MESSENGER_Application *app, 467 UI_MESSENGER_Handle *handle) 468 { 469 g_assert((app) && (handle)); 470 471 memset(handle, 0, sizeof(*handle)); 472 handle->app = app; 473 474 handle->builder = ui_builder_from_resource( 475 application_get_resource_path(app, "ui/messenger.ui") 476 ); 477 478 handle->main_window = GTK_APPLICATION_WINDOW( 479 gtk_builder_get_object(handle->builder, "main_window") 480 ); 481 482 gtk_window_set_startup_id( 483 GTK_WINDOW(handle->main_window), 484 MESSENGER_APPLICATION_ID 485 ); 486 487 gtk_window_set_icon_name( 488 GTK_WINDOW(handle->main_window), 489 MESSENGER_APPLICATION_ID 490 ); 491 492 gtk_application_add_window( 493 app->application, 494 GTK_WINDOW(handle->main_window) 495 ); 496 497 gtk_window_set_default_size( 498 GTK_WINDOW(handle->main_window), 499 1100, 700 500 ); 501 502 handle->leaflet_title = HDY_LEAFLET( 503 gtk_builder_get_object(handle->builder, "leaflet_title") 504 ); 505 506 handle->leaflet_chat = HDY_LEAFLET( 507 gtk_builder_get_object(handle->builder, "leaflet_chat") 508 ); 509 510 g_object_bind_property( 511 handle->leaflet_chat, 512 "visible_child_name", 513 handle->leaflet_title, 514 "visible_child_name", 515 G_BINDING_SYNC_CREATE 516 ); 517 518 handle->flap_user_details = HDY_FLAP( 519 gtk_builder_get_object(handle->builder, "flap_user_details") 520 ); 521 522 g_signal_connect( 523 handle->flap_user_details, 524 "notify::reveal-flap", 525 G_CALLBACK(handle_user_details_folded), 526 handle 527 ); 528 529 handle->nav_box = GTK_WIDGET( 530 gtk_builder_get_object(handle->builder, "nav_box") 531 ); 532 533 handle->main_box = GTK_WIDGET( 534 gtk_builder_get_object(handle->builder, "main_box") 535 ); 536 537 handle->nav_bar = HDY_HEADER_BAR( 538 gtk_builder_get_object(handle->builder, "nav_bar") 539 ); 540 541 handle->main_bar = HDY_HEADER_BAR( 542 gtk_builder_get_object(handle->builder, "main_bar") 543 ); 544 545 GtkLabel* application_name_label = GTK_LABEL( 546 gtk_builder_get_object(handle->builder, "application-name-label") 547 ); 548 549 GtkLabel* application_version_label = GTK_LABEL( 550 gtk_builder_get_object(handle->builder, "application-version-label") 551 ); 552 553 gtk_label_set_text( 554 application_name_label, 555 MESSENGER_APPLICATION_APPNAME 556 ); 557 558 gtk_label_set_text( 559 application_version_label, 560 MESSENGER_APPLICATION_VERSION 561 ); 562 563 g_object_bind_property( 564 handle->leaflet_chat, 565 "folded", 566 handle->main_bar, 567 "show-close-button", 568 G_BINDING_INVERT_BOOLEAN 569 ); 570 571 handle->profile_button = GTK_BUTTON( 572 gtk_builder_get_object(handle->builder, "profile_button") 573 ); 574 575 g_signal_connect( 576 handle->profile_button, 577 "clicked", 578 G_CALLBACK(handle_profile_button_click), 579 handle 580 ); 581 582 handle->profile_avatar = HDY_AVATAR( 583 gtk_builder_get_object(handle->builder, "profile_avatar") 584 ); 585 586 handle->profile_label = GTK_LABEL( 587 gtk_builder_get_object(handle->builder, "profile_label") 588 ); 589 590 handle->profile_key_label = GTK_LABEL( 591 gtk_builder_get_object(handle->builder, "profile_key_label") 592 ); 593 594 handle->hide_user_details_button = GTK_BUTTON( 595 gtk_builder_get_object(handle->builder, "hide_user_details_button") 596 ); 597 598 g_signal_connect( 599 handle->hide_user_details_button, 600 "clicked", 601 G_CALLBACK(handle_user_details_via_button_click), 602 handle 603 ); 604 605 handle->lobby_button = GTK_BUTTON( 606 gtk_builder_get_object(handle->builder, "lobby_button") 607 ); 608 609 g_signal_connect( 610 handle->lobby_button, 611 "clicked", 612 G_CALLBACK(handle_lobby_button_click), 613 app 614 ); 615 616 handle->account_details_button = GTK_BUTTON( 617 gtk_builder_get_object(handle->builder, "account_details_button") 618 ); 619 620 handle->account_details_symbol = GTK_IMAGE( 621 gtk_builder_get_object(handle->builder, "account_details_symbol") 622 ); 623 624 handle->account_details_revealer = GTK_REVEALER( 625 gtk_builder_get_object(handle->builder, "account_details_revealer") 626 ); 627 628 g_signal_connect( 629 handle->account_details_button, 630 "clicked", 631 G_CALLBACK(handle_account_details_button_click), 632 handle 633 ); 634 635 handle->accounts_listbox = GTK_LIST_BOX( 636 gtk_builder_get_object(handle->builder, "accounts_listbox") 637 ); 638 639 handle->add_account_listbox_row = GTK_LIST_BOX_ROW( 640 gtk_builder_get_object(handle->builder, "add_account_listbox_row") 641 ); 642 643 g_signal_connect( 644 handle->accounts_listbox, 645 "row-activated", 646 G_CALLBACK(handle_accounts_listbox_row_activated), 647 app 648 ); 649 650 handle->new_contact_button = GTK_BUTTON( 651 gtk_builder_get_object(handle->builder, "new_contact_button") 652 ); 653 654 handle->new_group_button = GTK_BUTTON( 655 gtk_builder_get_object(handle->builder, "new_group_button") 656 ); 657 658 handle->new_platform_button = GTK_BUTTON( 659 gtk_builder_get_object(handle->builder, "new_platform_button") 660 ); 661 662 g_signal_connect( 663 handle->new_contact_button, 664 "clicked", 665 G_CALLBACK(handle_new_contact_button_click), 666 app 667 ); 668 669 g_signal_connect( 670 handle->new_group_button, 671 "clicked", 672 G_CALLBACK(handle_new_group_button_click), 673 app 674 ); 675 676 g_signal_connect( 677 handle->new_platform_button, 678 "clicked", 679 G_CALLBACK(handle_new_platform_button_click), 680 app 681 ); 682 683 handle->contacts_button = GTK_BUTTON( 684 gtk_builder_get_object(handle->builder, "contacts_button") 685 ); 686 687 handle->settings_button = GTK_BUTTON( 688 gtk_builder_get_object(handle->builder, "settings_button") 689 ); 690 691 g_signal_connect( 692 handle->contacts_button, 693 "clicked", 694 G_CALLBACK(handle_contacts_button_click), 695 app 696 ); 697 698 g_signal_connect( 699 handle->settings_button, 700 "clicked", 701 G_CALLBACK(handle_settings_button_click), 702 app 703 ); 704 705 handle->about_button = GTK_BUTTON( 706 gtk_builder_get_object(handle->builder, "about_button") 707 ); 708 709 g_signal_connect( 710 handle->about_button, 711 "clicked", 712 G_CALLBACK(handle_about_button_click), 713 app 714 ); 715 716 handle->chats_title_stack = GTK_STACK( 717 gtk_builder_get_object(handle->builder, "chats_title_stack") 718 ); 719 720 handle->title_box = GTK_WIDGET( 721 gtk_builder_get_object(handle->builder, "title_box") 722 ); 723 724 handle->search_box = GTK_WIDGET( 725 gtk_builder_get_object(handle->builder, "search_box") 726 ); 727 728 g_object_bind_property( 729 handle->leaflet_chat, 730 "folded", 731 handle->nav_bar, 732 "hexpand", 733 G_BINDING_SYNC_CREATE 734 ); 735 736 handle->user_details_button = GTK_BUTTON( 737 gtk_builder_get_object(handle->builder, "user_details_button") 738 ); 739 740 g_signal_connect( 741 handle->user_details_button, 742 "clicked", 743 G_CALLBACK(handle_user_details_via_button_click), 744 handle 745 ); 746 747 handle->chats_search_button = GTK_BUTTON( 748 gtk_builder_get_object(handle->builder, "chats_search_button") 749 ); 750 751 g_signal_connect( 752 handle->chats_search_button, 753 "clicked", 754 G_CALLBACK(handle_search_button_click), 755 handle 756 ); 757 758 handle->chats_search_entry = GTK_SEARCH_ENTRY( 759 gtk_builder_get_object(handle->builder, "chats_search_entry") 760 ); 761 762 handle->search_icon_stack = GTK_STACK( 763 gtk_builder_get_object(handle->builder, "search_icon_stack") 764 ); 765 766 g_object_bind_property( 767 handle->chats_title_stack, 768 "visible_child_name", 769 handle->search_icon_stack, 770 "visible_child_name", 771 G_BINDING_SYNC_CREATE 772 ); 773 774 handle->chats_listbox = GTK_LIST_BOX( 775 gtk_builder_get_object(handle->builder, "chats_listbox") 776 ); 777 778 gtk_list_box_set_sort_func( 779 handle->chats_listbox, 780 handle_chats_listbox_sort_func, 781 app, 782 NULL 783 ); 784 785 gtk_list_box_set_filter_func( 786 handle->chats_listbox, 787 handle_chats_listbox_filter_func, 788 app, 789 NULL 790 ); 791 792 g_signal_connect( 793 handle->chats_search_entry, 794 "search-changed", 795 G_CALLBACK(handle_chats_search_changed), 796 handle->chats_listbox 797 ); 798 799 g_signal_connect( 800 handle->chats_listbox, 801 "row-activated", 802 G_CALLBACK(handle_chats_listbox_row_activated), 803 app 804 ); 805 806 handle->chats_stack = GTK_STACK( 807 gtk_builder_get_object(handle->builder, "chats_stack") 808 ); 809 810 handle->no_chat_box = GTK_WIDGET( 811 gtk_builder_get_object(handle->builder, "no_chat_box") 812 ); 813 814 handle->chat_title_stack = GTK_STACK( 815 gtk_builder_get_object(handle->builder, "chat_title_stack") 816 ); 817 818 g_signal_connect( 819 handle->main_window, 820 "destroy", 821 G_CALLBACK(handle_main_window_destroy), 822 app 823 ); 824 } 825 826 static int 827 _messenger_iterate_accounts(void *cls, 828 struct GNUNET_CHAT_Handle *handle, 829 struct GNUNET_CHAT_Account *account) 830 { 831 g_assert((cls) && (handle) && (account)); 832 833 MESSENGER_Application *app = (MESSENGER_Application*) cls; 834 UI_MESSENGER_Handle *ui = &(app->ui.messenger); 835 836 UI_ACCOUNT_ENTRY_Handle *entry = ui_account_entry_new(app); 837 838 ui_account_entry_set_account(entry, account); 839 840 gtk_list_box_prepend(ui->accounts_listbox, entry->entry_box); 841 842 GtkWidget *row = gtk_widget_get_parent(entry->entry_box); 843 844 g_object_set_qdata(G_OBJECT(row), app->quarks.data, account); 845 846 if (account == GNUNET_CHAT_get_connected(handle)) 847 gtk_widget_activate(row); 848 849 ui_account_entry_delete(entry); 850 return GNUNET_YES; 851 } 852 853 static void 854 _clear_accounts_listbox(GtkWidget *widget, 855 gpointer data) 856 { 857 g_assert((widget) && (data)); 858 859 GtkListBoxRow *row = GTK_LIST_BOX_ROW(widget); 860 GtkListBox *listbox = GTK_LIST_BOX(data); 861 862 if (!gtk_list_box_row_get_selectable(row)) 863 return; 864 865 gtk_container_remove( 866 GTK_CONTAINER(listbox), 867 widget 868 ); 869 } 870 871 static gboolean 872 _close_messenger_missing_account(gpointer data) 873 { 874 g_assert(data); 875 876 UI_MESSENGER_Handle *handle = (UI_MESSENGER_Handle*) data; 877 878 gtk_window_close(GTK_WINDOW(handle->main_window)); 879 return FALSE; 880 } 881 882 void 883 ui_messenger_refresh(MESSENGER_Application *app, 884 UI_MESSENGER_Handle *handle) 885 { 886 g_assert((app) && (handle)); 887 888 if (!(handle->accounts_listbox)) 889 return; 890 891 gtk_container_foreach( 892 GTK_CONTAINER(handle->accounts_listbox), 893 _clear_accounts_listbox, 894 handle->accounts_listbox 895 ); 896 897 application_chat_lock(app); 898 899 GNUNET_CHAT_iterate_accounts( 900 app->chat.messenger.handle, 901 _messenger_iterate_accounts, 902 app 903 ); 904 905 application_chat_unlock(app); 906 907 if (gtk_list_box_get_selected_row(handle->accounts_listbox)) 908 return; 909 910 gtk_widget_hide(GTK_WIDGET(handle->main_window)); 911 912 handle->account_refresh = util_idle_add( 913 G_SOURCE_FUNC(_close_messenger_missing_account), 914 handle 915 ); 916 } 917 918 gboolean 919 ui_messenger_is_context_active(UI_MESSENGER_Handle *handle, 920 struct GNUNET_CHAT_Context *context) 921 { 922 g_assert((handle) && (context)); 923 924 if (!gtk_window_is_active(GTK_WINDOW(handle->main_window))) 925 return FALSE; 926 927 UI_CHAT_ENTRY_Handle *entry = GNUNET_CHAT_context_get_user_pointer(context); 928 929 if ((!entry) || (!(entry->entry_box))) 930 return FALSE; 931 932 GtkListBoxRow *row = GTK_LIST_BOX_ROW( 933 gtk_widget_get_parent(entry->entry_box) 934 ); 935 936 if (!row) 937 return FALSE; 938 939 return gtk_list_box_row_is_selected(row); 940 } 941 942 void 943 ui_messenger_cleanup(UI_MESSENGER_Handle *handle) 944 { 945 g_assert(handle); 946 947 g_object_unref(handle->builder); 948 949 if (handle->chat_entries) 950 g_list_free_full(handle->chat_entries, (GDestroyNotify) ui_chat_entry_delete); 951 952 if (handle->chat_selection) 953 util_source_remove(handle->chat_selection); 954 955 if (handle->account_refresh) 956 util_source_remove(handle->account_refresh); 957 958 memset(handle, 0, sizeof(*handle)); 959 }