messenger-gtk

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

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 }