messenger-gtk

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

settings.c (14937B)


      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/settings.c
     23  */
     24 
     25 #include "settings.h"
     26 
     27 #include "../application.h"
     28 #include "../request.h"
     29 #include "../ui.h"
     30 
     31 #include "contact_entry.h"
     32 #include "files.h"
     33 
     34 #include <gnunet/gnunet_chat_lib.h>
     35 #include <gnunet/gnunet_common.h>
     36 
     37 #ifndef MESSENGER_APPLICATION_NO_PORTAL
     38 #include <libportal/background.h>
     39 #endif
     40 
     41 static gboolean
     42 handle_general_switch_state(UNUSED GtkSwitch *widget,
     43                             gboolean state,
     44                             gpointer user_data)
     45 {
     46   g_assert(user_data);
     47 
     48   gboolean *setting = (gboolean*) user_data;
     49   *setting = state;
     50   return FALSE;
     51 }
     52 
     53 static void
     54 _request_background_callback(MESSENGER_Application *app,
     55                              gboolean success,
     56                              gboolean error,
     57                              gpointer user_data)
     58 {
     59   g_assert((app) && (user_data));
     60 
     61   GtkSwitch *widget = GTK_SWITCH(user_data);
     62 
     63   if (error) {
     64     gtk_widget_set_sensitive(GTK_WIDGET(widget), !success);
     65     gtk_switch_set_active(widget, success);
     66     return;
     67   }
     68   
     69   gboolean *setting = (gboolean*) (
     70     g_object_get_qdata(G_OBJECT(widget), app->quarks.data)
     71   );
     72 
     73   handle_general_switch_state(widget, success, setting);
     74 }
     75 
     76 static gboolean
     77 handle_background_switch_state(GtkSwitch *widget,
     78 			                         gboolean state,
     79 			                         gpointer user_data)
     80 {
     81   g_assert((widget) && (user_data));
     82 
     83   MESSENGER_Application *app = (MESSENGER_Application*) user_data;
     84 
     85   gboolean *setting = (gboolean*) (
     86     g_object_get_qdata(G_OBJECT(widget), app->quarks.data)
     87   );
     88 
     89   if ((!state) || (!gtk_widget_is_sensitive(GTK_WIDGET(widget))))
     90     return handle_general_switch_state(widget, state, setting);
     91 
     92   XdpBackgroundFlags flags = XDP_BACKGROUND_FLAG_NONE;
     93 
     94   if (&(app->settings.autostart) == setting)
     95     flags |= XDP_BACKGROUND_FLAG_AUTOSTART;
     96   if (&(app->settings.background_task) == setting)
     97     flags |= XDP_BACKGROUND_FLAG_ACTIVATABLE;
     98 
     99   request_new_background(
    100     app,
    101     flags,
    102     _request_background_callback,
    103     widget
    104   );
    105 
    106   gtk_widget_set_sensitive(GTK_WIDGET(widget), FALSE);
    107   return FALSE;
    108 }
    109 
    110 static gboolean
    111 handle_inverted_switch_state(GtkSwitch *widget,
    112                              gboolean state,
    113                              gpointer user_data)
    114 {
    115   g_assert((widget) && (user_data));
    116 
    117   return handle_general_switch_state(widget, !state, user_data);
    118 }
    119 
    120 static void
    121 handle_general_combo_box_change(GtkComboBox *widget,
    122 				                        gpointer user_data)
    123 {
    124   g_assert((widget) && (user_data));
    125 
    126   gulong *delay = (gulong*) user_data;
    127   GtkTreeModel *model = gtk_combo_box_get_model(widget);
    128 
    129   GtkTreeIter iter;
    130   if (gtk_combo_box_get_active_iter(widget, &iter))
    131     gtk_tree_model_get(model, &iter, 1, delay, -1);
    132 }
    133 
    134 int
    135 _leave_group_iteration(UNUSED void *cls,
    136                        UNUSED struct GNUNET_CHAT_Handle *handle,
    137                        struct GNUNET_CHAT_Group *group)
    138 {
    139   g_assert(group);
    140 
    141   GNUNET_CHAT_group_leave(group);
    142   return GNUNET_YES;
    143 }
    144 
    145 int
    146 _delete_contact_iteration(UNUSED void *cls,
    147                           UNUSED struct GNUNET_CHAT_Handle *handle,
    148                           struct GNUNET_CHAT_Contact *contact)
    149 {
    150   g_assert(contact);
    151 
    152   GNUNET_CHAT_contact_delete(contact);
    153   return GNUNET_YES;
    154 }
    155 
    156 static void
    157 handle_leave_chats_button_click(UNUSED GtkButton* button,
    158 				                        gpointer user_data)
    159 {
    160   g_assert(user_data);
    161 
    162   MESSENGER_Application *app = (MESSENGER_Application*) user_data;
    163 
    164   application_chat_lock(app);
    165 
    166   GNUNET_CHAT_iterate_groups(
    167     app->chat.messenger.handle,
    168     _leave_group_iteration,
    169     NULL
    170   );
    171 
    172   GNUNET_CHAT_iterate_contacts(
    173     app->chat.messenger.handle,
    174     _delete_contact_iteration,
    175     NULL
    176   );
    177 
    178   application_chat_unlock(app);
    179 }
    180 
    181 static void
    182 handle_show_files_button_click(UNUSED GtkButton* button,
    183 				                       gpointer user_data)
    184 {
    185   g_assert(user_data);
    186 
    187   UI_SETTINGS_Handle *handle = (UI_SETTINGS_Handle*) user_data;
    188 
    189   handle->open_files = true;
    190 
    191   gtk_window_close(GTK_WINDOW(handle->dialog));
    192 }
    193 
    194 static void
    195 handle_dialog_destroy(UNUSED GtkWidget *window,
    196                       gpointer user_data)
    197 {
    198   g_assert(user_data);
    199 
    200   MESSENGER_Application *app = (MESSENGER_Application*) user_data;
    201   UI_SETTINGS_Handle *handle = &(app->ui.settings);
    202 
    203   if (handle->open_files)
    204   {
    205     ui_files_dialog_init(app, &(app->ui.files));
    206     gtk_widget_show(GTK_WIDGET(app->ui.files.dialog));
    207   }
    208 
    209   ui_settings_dialog_cleanup(handle);
    210 }
    211 
    212 static void
    213 _set_combobox_to_active_by_delay(GtkComboBox *widget,
    214                                  gulong delay)
    215 {
    216   g_assert(widget);
    217 
    218   GtkTreeModel *model = gtk_combo_box_get_model(widget);
    219 
    220   GtkTreeIter iter;
    221   if (!gtk_tree_model_get_iter_first(model, &iter))
    222     return;
    223 
    224   gulong value;
    225 
    226   do {
    227     gtk_tree_model_get(model, &iter, 1, &value, -1);
    228 
    229     if (value == delay)
    230       goto set_active;
    231 
    232   } while (gtk_tree_model_iter_next(model, &iter));
    233 
    234   return;
    235 set_active:
    236   gtk_combo_box_set_active_iter(widget, &iter);
    237 }
    238 
    239 static enum GNUNET_GenericReturnValue
    240 _count_blocked_contacts(void *cls,
    241                         UNUSED struct GNUNET_CHAT_Handle *handle,
    242                         struct GNUNET_CHAT_Contact *contact)
    243 {
    244   g_assert((cls) && (contact));
    245 
    246   if (GNUNET_YES == GNUNET_CHAT_contact_is_owned(contact))
    247     return GNUNET_YES;
    248 
    249   guint *count = (guint*) cls;
    250 
    251   if (GNUNET_YES == GNUNET_CHAT_contact_is_blocked(contact))
    252     *count = (*count) + 1;
    253 
    254   return GNUNET_YES;
    255 }
    256 
    257 static enum GNUNET_GenericReturnValue
    258 _iterate_blocked_contacts(void *cls,
    259                         UNUSED struct GNUNET_CHAT_Handle *handle,
    260                         struct GNUNET_CHAT_Contact *contact)
    261 {
    262   g_assert((cls) && (contact));
    263 
    264   if ((GNUNET_YES == GNUNET_CHAT_contact_is_owned(contact)) ||
    265       (GNUNET_YES != GNUNET_CHAT_contact_is_blocked(contact)))
    266     return GNUNET_YES;
    267 
    268   MESSENGER_Application *app = (MESSENGER_Application*) cls;
    269 
    270   UI_CONTACT_ENTRY_Handle *entry = ui_contact_entry_new(app);
    271   ui_contact_entry_set_contact(entry, contact);
    272 
    273   gtk_list_box_prepend(
    274     app->ui.settings.blocked_listbox,
    275     entry->entry_box
    276   );
    277 
    278   GtkListBoxRow *row = GTK_LIST_BOX_ROW(
    279     gtk_widget_get_parent(entry->entry_box)
    280   );
    281 
    282   g_object_set_qdata(G_OBJECT(row), app->quarks.data, contact);
    283 
    284   g_object_set_qdata_full(
    285     G_OBJECT(row),
    286     app->quarks.ui,
    287     entry,
    288     (GDestroyNotify) ui_contact_entry_delete
    289   );
    290 
    291   return GNUNET_YES;
    292 }
    293 
    294 void
    295 ui_settings_dialog_init(MESSENGER_Application *app,
    296                         UI_SETTINGS_Handle *handle)
    297 {
    298   g_assert((app) && (handle));
    299 
    300   handle->builder = ui_builder_from_resource(
    301     application_get_resource_path(app, "ui/settings.ui")
    302   );
    303 
    304   handle->dialog = HDY_PREFERENCES_WINDOW(
    305     gtk_builder_get_object(handle->builder, "settings_dialog")
    306   );
    307 
    308   gtk_window_set_transient_for(
    309     GTK_WINDOW(handle->dialog),
    310     GTK_WINDOW(app->ui.messenger.main_window)
    311   );
    312 
    313   handle->start_on_login_switch = GTK_SWITCH(
    314     gtk_builder_get_object(handle->builder, "start_on_login_switch")
    315   );
    316 
    317   gtk_switch_set_active(
    318     handle->start_on_login_switch,
    319     app->settings.autostart
    320   );
    321 
    322   gtk_widget_set_sensitive(
    323     GTK_WIDGET(handle->start_on_login_switch),
    324     !(app->settings.autostart)
    325   );
    326 
    327   g_object_set_qdata(
    328     G_OBJECT(handle->start_on_login_switch),
    329     app->quarks.data,
    330     &(app->settings.autostart)
    331   );
    332 
    333   g_signal_connect(
    334     handle->start_on_login_switch,
    335     "state-set",
    336     G_CALLBACK(handle_background_switch_state),
    337     app
    338   );
    339 
    340   handle->run_in_background_switch = GTK_SWITCH(
    341     gtk_builder_get_object(handle->builder, "run_in_background_switch")
    342   );
    343 
    344   gtk_switch_set_active(
    345     handle->run_in_background_switch,
    346     app->settings.background_task
    347   );
    348 
    349   gtk_widget_set_sensitive(
    350     GTK_WIDGET(handle->run_in_background_switch),
    351     !(app->settings.background_task)
    352   );
    353 
    354   g_object_set_qdata(
    355     G_OBJECT(handle->run_in_background_switch),
    356     app->quarks.data,
    357     &(app->settings.background_task)
    358   );
    359 
    360   g_signal_connect(
    361     handle->run_in_background_switch,
    362     "state-set",
    363     G_CALLBACK(handle_background_switch_state),
    364     app
    365   );
    366 
    367   handle->enable_notifications_switch = GTK_SWITCH(
    368     gtk_builder_get_object(handle->builder, "enable_notifications_switch")
    369   );
    370 
    371   handle->notification_sounds_switch = GTK_SWITCH(
    372     gtk_builder_get_object(handle->builder, "notification_sounds_switch")
    373   );
    374 
    375   gtk_switch_set_active(
    376     handle->enable_notifications_switch,
    377     !(app->settings.disable_notifications)
    378   );
    379 
    380   gtk_switch_set_active(
    381     handle->notification_sounds_switch,
    382     app->settings.play_notification_sounds
    383   );
    384 
    385   g_signal_connect(
    386     handle->enable_notifications_switch,
    387     "state-set",
    388     G_CALLBACK(handle_inverted_switch_state),
    389     &(app->settings.disable_notifications)
    390   );
    391 
    392   g_signal_connect(
    393     handle->notification_sounds_switch,
    394     "state-set",
    395     G_CALLBACK(handle_general_switch_state),
    396     &(app->settings.play_notification_sounds)
    397   );
    398 
    399   handle->blocked_label = GTK_LABEL(
    400     gtk_builder_get_object(handle->builder, "blocked_label")
    401   );
    402 
    403   handle->blocked_scrolled_window = GTK_WIDGET(
    404     gtk_builder_get_object(handle->builder, "blocked_scrolled_window")
    405   );
    406 
    407   handle->blocked_listbox = GTK_LIST_BOX(
    408     gtk_builder_get_object(handle->builder, "blocked_listbox")
    409   );
    410 
    411   guint blocked_count = 0;
    412   GNUNET_CHAT_iterate_contacts(
    413     app->chat.messenger.handle,
    414     _count_blocked_contacts,
    415     &blocked_count
    416   );
    417 
    418   gtk_widget_set_size_request(
    419     handle->blocked_scrolled_window,
    420     0,
    421     56 * (blocked_count > 3? 3 : blocked_count)
    422   );
    423 
    424   gtk_widget_set_visible(handle->blocked_scrolled_window, blocked_count > 0);
    425 
    426   GString *blocked_text = g_string_new(NULL);
    427   if (blocked_text)
    428   {
    429     g_string_printf(
    430       blocked_text,
    431       _("%u blocked contacts"),
    432       blocked_count
    433     );
    434 
    435     gtk_label_set_text(
    436       handle->blocked_label,
    437       blocked_text->str
    438     );
    439 
    440     g_string_free(blocked_text, TRUE);
    441   }
    442 
    443   GNUNET_CHAT_iterate_contacts(
    444     app->chat.messenger.handle,
    445     _iterate_blocked_contacts,
    446     app
    447   );
    448 
    449   gtk_list_box_invalidate_filter(handle->blocked_listbox);
    450 
    451   handle->read_receipts_switch = GTK_SWITCH(
    452     gtk_builder_get_object(handle->builder, "read_receipts_switch")
    453   );
    454 
    455   gtk_switch_set_active(
    456     handle->read_receipts_switch,
    457     app->settings.send_read_receipts
    458   );
    459 
    460   g_signal_connect(
    461     handle->read_receipts_switch,
    462     "state-set",
    463     G_CALLBACK(handle_general_switch_state),
    464     &(app->settings.send_read_receipts)
    465   );
    466 
    467   handle->auto_delete_combo_box = GTK_COMBO_BOX(
    468     gtk_builder_get_object(handle->builder, "auto_delete_combo_box")
    469   );
    470 
    471   _set_combobox_to_active_by_delay(
    472     handle->auto_delete_combo_box,
    473     app->settings.auto_delete_delay
    474   );
    475 
    476   g_signal_connect(
    477     handle->auto_delete_combo_box,
    478     "changed",
    479     G_CALLBACK(handle_general_combo_box_change),
    480     &(app->settings.auto_delete_delay)
    481   );
    482 
    483   handle->auto_accept_invitations_switch = GTK_SWITCH(
    484     gtk_builder_get_object(handle->builder, "auto_accept_invitations_switch")
    485   );
    486 
    487   gtk_switch_set_active(
    488     handle->auto_accept_invitations_switch,
    489     app->settings.accept_all_invitations
    490   );
    491 
    492   g_signal_connect(
    493     handle->auto_accept_invitations_switch,
    494     "state-set",
    495     G_CALLBACK(handle_general_switch_state),
    496     &(app->settings.accept_all_invitations)
    497   );
    498 
    499   handle->delete_invitations_combo_box = GTK_COMBO_BOX(
    500     gtk_builder_get_object(handle->builder, "delete_invitations_combo_box")
    501   );
    502 
    503   _set_combobox_to_active_by_delay(
    504     handle->delete_invitations_combo_box,
    505     app->settings.delete_invitations_delay
    506   );
    507 
    508   g_signal_connect(
    509     handle->delete_invitations_combo_box,
    510     "changed",
    511     G_CALLBACK(handle_general_combo_box_change),
    512     &(app->settings.delete_invitations_delay)
    513   );
    514 
    515   handle->delete_invitations_button = GTK_BUTTON(
    516     gtk_builder_get_object(handle->builder, "delete_invitations_button")
    517   );
    518 
    519   handle->auto_accept_files_switch = GTK_SWITCH(
    520     gtk_builder_get_object(handle->builder, "auto_accept_files_switch")
    521   );
    522 
    523   gtk_switch_set_active(
    524     handle->auto_accept_files_switch,
    525     app->settings.accept_all_files
    526   );
    527 
    528   g_signal_connect(
    529     handle->auto_accept_files_switch,
    530     "state-set",
    531     G_CALLBACK(handle_general_switch_state),
    532     &(app->settings.accept_all_files)
    533   );
    534 
    535   handle->download_folder_button = GTK_FILE_CHOOSER_BUTTON(
    536     gtk_builder_get_object(handle->builder, "download_folder_button")
    537   );
    538 
    539   handle->delete_files_combo_box = GTK_COMBO_BOX(
    540     gtk_builder_get_object(handle->builder, "delete_files_combo_box")
    541   );
    542 
    543   _set_combobox_to_active_by_delay(
    544     handle->delete_files_combo_box,
    545     app->settings.delete_files_delay
    546   );
    547 
    548   g_signal_connect(
    549     handle->delete_files_combo_box,
    550     "changed",
    551     G_CALLBACK(handle_general_combo_box_change),
    552     &(app->settings.delete_files_delay)
    553   );
    554 
    555   handle->show_files_button = GTK_BUTTON(
    556     gtk_builder_get_object(handle->builder, "show_files_button")
    557   );
    558 
    559   g_signal_connect(
    560     handle->show_files_button,
    561     "clicked",
    562     G_CALLBACK(handle_show_files_button_click),
    563     handle
    564   );
    565 
    566   handle->delete_files_button = GTK_BUTTON(
    567     gtk_builder_get_object(handle->builder, "delete_files_button")
    568   );
    569 
    570   handle->leave_chats_combo_box = GTK_COMBO_BOX(
    571     gtk_builder_get_object(handle->builder, "leave_chats_combo_box")
    572   );
    573 
    574   _set_combobox_to_active_by_delay(
    575     handle->leave_chats_combo_box,
    576     app->settings.leave_chats_delay
    577   );
    578 
    579   g_signal_connect(
    580     handle->leave_chats_combo_box,
    581     "changed",
    582     G_CALLBACK(handle_general_combo_box_change),
    583     &(app->settings.leave_chats_delay)
    584   );
    585 
    586   handle->leave_chats_button = GTK_BUTTON(
    587     gtk_builder_get_object(handle->builder, "leave_chats_button")
    588   );
    589 
    590   g_signal_connect(
    591     handle->leave_chats_button,
    592     "clicked",
    593     G_CALLBACK(handle_leave_chats_button_click),
    594     app
    595   );
    596 
    597   g_signal_connect(
    598     handle->dialog,
    599     "destroy",
    600     G_CALLBACK(handle_dialog_destroy),
    601     app
    602   );
    603 }
    604 
    605 void
    606 ui_settings_dialog_cleanup(UI_SETTINGS_Handle *handle)
    607 {
    608   g_assert(handle);
    609 
    610   if (handle->builder)
    611     g_object_unref(handle->builder);
    612 
    613   memset(handle, 0, sizeof(*handle));
    614 }