chat_title.c (13119B)
1 /* 2 This file is part of GNUnet. 3 Copyright (C) 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/chat_title.c 23 */ 24 25 #include "chat_title.h" 26 27 #include "chat.h" 28 #include "file_load_entry.h" 29 #include "delete_messages.h" 30 #include "message.h" 31 32 #include "../contact.h" 33 #include "../ui.h" 34 35 static void 36 handle_back_button_click(UNUSED GtkButton *button, 37 gpointer user_data) 38 { 39 g_assert(user_data); 40 41 HdyLeaflet *leaflet = HDY_LEAFLET(user_data); 42 43 GList *children = gtk_container_get_children(GTK_CONTAINER(leaflet)); 44 45 if (children) { 46 hdy_leaflet_set_visible_child(leaflet, GTK_WIDGET(children->data)); 47 g_list_free(children); 48 } 49 } 50 51 static gboolean 52 _flap_chat_details_reveal_switch(gpointer user_data) 53 { 54 g_assert(user_data); 55 56 UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data; 57 HdyFlap* flap = handle->flap_chat_details; 58 59 hdy_flap_set_reveal_flap(flap, !hdy_flap_get_reveal_flap(flap)); 60 61 gtk_widget_set_sensitive(GTK_WIDGET(handle->messages_listbox), TRUE); 62 return FALSE; 63 } 64 65 static void 66 handle_chat_details_via_button_click(UNUSED GtkButton* button, 67 gpointer user_data) 68 { 69 g_assert(user_data); 70 71 UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data; 72 73 gtk_widget_set_sensitive(GTK_WIDGET(handle->messages_listbox), FALSE); 74 util_idle_add( 75 G_SOURCE_FUNC(_flap_chat_details_reveal_switch), 76 handle 77 ); 78 } 79 80 static void 81 handle_popover_via_button_click(UNUSED GtkButton *button, 82 gpointer user_data) 83 { 84 g_assert(user_data); 85 86 GtkPopover *popover = GTK_POPOVER(user_data); 87 88 if (gtk_widget_is_visible(GTK_WIDGET(popover))) 89 gtk_popover_popdown(popover); 90 else 91 gtk_popover_popup(popover); 92 } 93 94 static void 95 handle_chat_selection_close_button_click(UNUSED GtkButton *button, 96 gpointer user_data) 97 { 98 g_assert(user_data); 99 100 UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data; 101 102 gtk_list_box_unselect_all(handle->messages_listbox); 103 } 104 105 void 106 _new_tag_callback(MESSENGER_Application *app, 107 GList *selected, 108 const char *tag, 109 gpointer user_data) 110 { 111 g_assert((app) && (user_data)); 112 113 UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data; 114 UI_MESSAGE_Handle *message; 115 116 if ((!(handle->context)) || (!tag)) 117 goto unselect; 118 119 while (selected) 120 { 121 GtkListBoxRow *row = GTK_LIST_BOX_ROW(selected->data); 122 123 if (!row) 124 goto skip_row; 125 126 message = (UI_MESSAGE_Handle*) g_object_get_qdata( 127 G_OBJECT(row), 128 app->quarks.ui 129 ); 130 131 if ((!message) || (!(message->msg))) 132 goto skip_row; 133 134 application_chat_lock(app); 135 136 GNUNET_CHAT_context_send_tag( 137 handle->context, 138 message->msg, 139 tag 140 ); 141 142 application_chat_unlock(app); 143 144 skip_row: 145 selected = selected->next; 146 } 147 148 unselect: 149 gtk_list_box_unselect_all(handle->messages_listbox); 150 } 151 152 static void 153 handle_chat_selection_tag_button_click(UNUSED GtkButton *button, 154 gpointer user_data) 155 { 156 g_assert(user_data); 157 158 UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data; 159 160 MESSENGER_Application *app = handle->app; 161 162 GList *selected = gtk_list_box_get_selected_rows(handle->messages_listbox); 163 164 ui_new_tag_dialog_init(app, &(app->ui.new_tag)); 165 166 ui_new_tag_dialog_link( 167 &(app->ui.new_tag), 168 _new_tag_callback, 169 selected, 170 handle 171 ); 172 173 gtk_widget_show(GTK_WIDGET(app->ui.new_tag.dialog)); 174 } 175 176 void 177 _delete_messages_callback(MESSENGER_Application *app, 178 GList *selected, 179 gulong delay) 180 { 181 g_assert(app); 182 183 UI_MESSAGE_Handle *message; 184 185 while (selected) 186 { 187 GtkListBoxRow *row = GTK_LIST_BOX_ROW(selected->data); 188 189 if (!row) 190 goto skip_row; 191 192 message = (UI_MESSAGE_Handle*) g_object_get_qdata( 193 G_OBJECT(row), 194 app->quarks.ui 195 ); 196 197 if ((!message) || (!(message->msg))) 198 goto skip_row; 199 200 application_chat_lock(app); 201 202 GNUNET_CHAT_message_delete( 203 message->msg, 204 delay 205 ); 206 207 application_chat_unlock(app); 208 209 skip_row: 210 selected = selected->next; 211 } 212 } 213 214 static void 215 handle_chat_selection_delete_button_click(UNUSED GtkButton *button, 216 gpointer user_data) 217 { 218 g_assert(user_data); 219 220 UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data; 221 222 MESSENGER_Application *app = handle->app; 223 224 GList *selected = gtk_list_box_get_selected_rows(handle->messages_listbox); 225 226 if (app->settings.hide_delete_dialog) 227 { 228 _delete_messages_callback(app, selected, 0); 229 230 if (selected) 231 g_list_free(selected); 232 } 233 else 234 { 235 ui_delete_messages_dialog_init(app, &(app->ui.delete_messages)); 236 237 ui_delete_messages_dialog_link( 238 &(app->ui.delete_messages), 239 _delete_messages_callback, 240 selected 241 ); 242 243 gtk_widget_show(GTK_WIDGET(app->ui.delete_messages.dialog)); 244 } 245 } 246 247 static void 248 handle_search_button_click(UNUSED GtkButton *button, 249 gpointer user_data) 250 { 251 g_assert(user_data); 252 253 UI_CHAT_Handle *handle = (UI_CHAT_Handle*) user_data; 254 255 hdy_search_bar_set_search_mode( 256 handle->chat_search_bar, 257 !hdy_search_bar_get_search_mode(handle->chat_search_bar) 258 ); 259 } 260 261 UI_CHAT_TITLE_Handle* 262 ui_chat_title_new(MESSENGER_Application *app, 263 UI_CHAT_Handle *chat) 264 { 265 g_assert((app) && (chat)); 266 267 UI_CHAT_TITLE_Handle *handle = g_malloc(sizeof(UI_CHAT_TITLE_Handle)); 268 UI_MESSENGER_Handle *messenger = &(app->ui.messenger); 269 270 handle->contact = NULL; 271 272 handle->chat = chat; 273 handle->loads = NULL; 274 275 handle->builder = ui_builder_from_resource( 276 application_get_resource_path(app, "ui/chat_title.ui") 277 ); 278 279 handle->chat_title_box = GTK_WIDGET( 280 gtk_builder_get_object(handle->builder, "chat_title_box") 281 ); 282 283 handle->back_button = GTK_BUTTON( 284 gtk_builder_get_object(handle->builder, "back_button") 285 ); 286 287 g_object_bind_property( 288 messenger->leaflet_chat, 289 "folded", 290 handle->back_button, 291 "visible", 292 G_BINDING_SYNC_CREATE 293 ); 294 295 g_signal_connect( 296 handle->back_button, 297 "clicked", 298 G_CALLBACK(handle_back_button_click), 299 messenger->leaflet_chat 300 ); 301 302 handle->chat_title_stack = GTK_STACK( 303 gtk_builder_get_object(handle->builder, "chat_title_stack") 304 ); 305 306 handle->title_box = GTK_WIDGET( 307 gtk_builder_get_object(handle->builder, "title_box") 308 ); 309 310 handle->selection_box = GTK_WIDGET( 311 gtk_builder_get_object(handle->builder, "selection_box") 312 ); 313 314 handle->chat_avatar = HDY_AVATAR( 315 gtk_builder_get_object(handle->builder, "chat_avatar") 316 ); 317 318 handle->chat_title = GTK_LABEL( 319 gtk_builder_get_object(handle->builder, "chat_title") 320 ); 321 322 handle->chat_subtitle = GTK_LABEL( 323 gtk_builder_get_object(handle->builder, "chat_subtitle") 324 ); 325 326 handle->chat_load_button = GTK_BUTTON( 327 gtk_builder_get_object(handle->builder, "chat_load_button") 328 ); 329 330 handle->chat_load_popover = GTK_POPOVER( 331 gtk_builder_get_object(handle->builder, "chat_load_popover") 332 ); 333 334 handle->chat_load_listbox = GTK_LIST_BOX( 335 gtk_builder_get_object(handle->builder, "chat_load_listbox") 336 ); 337 338 g_signal_connect( 339 handle->chat_load_button, 340 "clicked", 341 G_CALLBACK(handle_popover_via_button_click), 342 handle->chat_load_popover 343 ); 344 345 handle->chat_search_button = GTK_BUTTON( 346 gtk_builder_get_object(handle->builder, "chat_search_button") 347 ); 348 349 g_signal_connect( 350 handle->chat_search_button, 351 "clicked", 352 G_CALLBACK(handle_search_button_click), 353 handle->chat 354 ); 355 356 handle->chat_details_button = GTK_BUTTON( 357 gtk_builder_get_object(handle->builder, "chat_details_button") 358 ); 359 360 g_signal_connect( 361 handle->chat_details_button, 362 "clicked", 363 G_CALLBACK(handle_chat_details_via_button_click), 364 handle->chat 365 ); 366 367 handle->selection_close_button = GTK_BUTTON( 368 gtk_builder_get_object(handle->builder, "selection_close_button") 369 ); 370 371 handle->selection_count_label = GTK_LABEL( 372 gtk_builder_get_object(handle->builder, "selection_count_label") 373 ); 374 375 handle->selection_tag_button = GTK_BUTTON( 376 gtk_builder_get_object(handle->builder, "selection_tag_button") 377 ); 378 379 handle->selection_delete_button = GTK_BUTTON( 380 gtk_builder_get_object(handle->builder, "selection_delete_button") 381 ); 382 383 g_signal_connect( 384 handle->selection_close_button, 385 "clicked", 386 G_CALLBACK(handle_chat_selection_close_button_click), 387 handle->chat 388 ); 389 390 g_signal_connect( 391 handle->selection_tag_button, 392 "clicked", 393 G_CALLBACK(handle_chat_selection_tag_button_click), 394 handle->chat 395 ); 396 397 g_signal_connect( 398 handle->selection_delete_button, 399 "clicked", 400 G_CALLBACK(handle_chat_selection_delete_button_click), 401 handle->chat 402 ); 403 404 return handle; 405 } 406 407 static void 408 _chat_update_contact(UI_CHAT_TITLE_Handle *handle, 409 const struct GNUNET_CHAT_Contact* contact) 410 { 411 g_assert(handle); 412 413 if (handle->contact) 414 { 415 contact_remove_name_label_from_info(handle->contact, handle->chat_title); 416 contact_remove_name_avatar_from_info(handle->contact, handle->chat_avatar); 417 418 contact_remove_name_label_from_info(handle->contact, handle->chat->chat_details_label); 419 contact_remove_name_avatar_from_info(handle->contact, handle->chat->chat_details_avatar); 420 } 421 422 if (contact) 423 { 424 contact_add_name_label_to_info(contact, handle->chat_title); 425 contact_add_name_avatar_to_info(contact, handle->chat_avatar); 426 427 contact_add_name_label_to_info(contact, handle->chat->chat_details_label); 428 contact_add_name_avatar_to_info(contact, handle->chat->chat_details_avatar); 429 } 430 431 handle->contact = contact; 432 } 433 434 void 435 ui_chat_title_update(UI_CHAT_TITLE_Handle *handle, 436 MESSENGER_Application *app, 437 const gchar *subtitle) 438 { 439 g_assert((handle) && (app)); 440 441 UI_CHAT_Handle *chat = handle->chat; 442 443 struct GNUNET_CHAT_Contact* contact; 444 struct GNUNET_CHAT_Group* group; 445 446 contact = GNUNET_CHAT_context_get_contact(chat->context); 447 group = GNUNET_CHAT_context_get_group(chat->context); 448 449 const char *icon = "action-unavailable-symbolic"; 450 451 GString *sub = g_string_new(subtitle? subtitle : ""); 452 453 _chat_update_contact(handle, contact); 454 455 if (contact) 456 icon = "avatar-default-symbolic"; 457 else if (group) 458 { 459 const char *title = GNUNET_CHAT_group_get_name(group); 460 461 if ((title) && ('#' == *title)) 462 icon = "network-wired-symbolic"; 463 else 464 icon = "system-users-symbolic"; 465 466 g_string_append_printf( 467 sub, 468 _("%d members"), 469 GNUNET_CHAT_group_iterate_contacts(group, NULL, NULL) 470 ); 471 472 ui_label_set_text(handle->chat_title, title); 473 ui_avatar_set_text(handle->chat_avatar, title); 474 475 ui_label_set_text(handle->chat->chat_details_label, title); 476 ui_avatar_set_text(handle->chat->chat_details_avatar, title); 477 } 478 479 hdy_avatar_set_icon_name(handle->chat_avatar, icon); 480 hdy_avatar_set_icon_name(handle->chat->chat_details_avatar, icon); 481 482 if (sub->len > 0) 483 gtk_label_set_text(handle->chat_subtitle, sub->str); 484 485 g_string_free(sub, TRUE); 486 } 487 488 void 489 ui_chat_title_delete(UI_CHAT_TITLE_Handle *handle) 490 { 491 g_assert(handle); 492 493 _chat_update_contact(handle, NULL); 494 495 if (handle->loads) 496 g_list_free_full(handle->loads, (GDestroyNotify) ui_file_load_entry_delete); 497 498 g_object_unref(handle->builder); 499 500 g_free(handle); 501 } 502 503 void 504 ui_chat_title_add_file_load(UI_CHAT_TITLE_Handle *handle, 505 UI_FILE_LOAD_ENTRY_Handle *file_load) 506 { 507 g_assert((handle) && (file_load)); 508 509 gtk_container_add( 510 GTK_CONTAINER(handle->chat_load_listbox), 511 file_load->entry_box 512 ); 513 514 handle->loads = g_list_append(handle->loads, file_load); 515 516 gtk_widget_show(GTK_WIDGET(handle->chat_load_button)); 517 518 file_load->chat_title = handle; 519 } 520 521 void 522 ui_chat_title_remove_file_load(UI_CHAT_TITLE_Handle *handle, 523 UI_FILE_LOAD_ENTRY_Handle *file_load) 524 { 525 g_assert((handle) && (file_load) && (handle == file_load->chat_title) && 526 (file_load->entry_box)); 527 528 handle->loads = g_list_remove(handle->loads, file_load); 529 530 gtk_container_remove( 531 GTK_CONTAINER(handle->chat_load_listbox), 532 gtk_widget_get_parent(file_load->entry_box) 533 ); 534 535 if (handle->loads) 536 return; 537 538 if (gtk_widget_is_visible(GTK_WIDGET(handle->chat_load_popover))) 539 gtk_popover_popdown(handle->chat_load_popover); 540 541 gtk_widget_hide(GTK_WIDGET(handle->chat_load_button)); 542 543 file_load->chat_title = NULL; 544 }