messenger-gtk

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

picker.c (9910B)


      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/picker.c
     23  */
     24 
     25 #include "picker.h"
     26 
     27 #include "../application.h"
     28 #include "../ui.h"
     29 
     30 #include <emoji.h>
     31 #include <glib-2.0/glib.h>
     32 #include <uniname.h>
     33 
     34 static void
     35 handle_emoji_button_click(GtkButton *button,
     36 			                    gpointer user_data)
     37 {
     38   g_assert((button) && (user_data));
     39 
     40   GtkTextView *text_view = GTK_TEXT_VIEW(user_data);
     41   GtkTextBuffer *text_buffer = gtk_text_view_get_buffer(text_view);
     42 
     43   const gchar *label = gtk_button_get_label(button);
     44 
     45   if (label)
     46     gtk_text_buffer_insert_at_cursor(text_buffer, label, strlen(label));
     47 }
     48 
     49 static void
     50 _add_emoji_buttons(GtkFlowBox *flow_box,
     51 		               GtkTextView *text_view,
     52 		               size_t characters_count,
     53 		               const uint32_t *characters)
     54 {
     55   g_assert((flow_box) && (text_view) && (characters));
     56 
     57   glong items_written;
     58   GError *error;
     59   gchar *utf8;
     60   
     61   for (size_t i = 0; i < characters_count; i++) {
     62     utf8 = g_ucs4_to_utf8(characters + i, 1, NULL, &items_written, &error);
     63 
     64     if (!utf8)
     65     {
     66       fprintf(stderr, "ERROR: %s\n", error->message);
     67       g_error_free(error);
     68       continue;
     69     }
     70 
     71     GtkButton *emoji_button = GTK_BUTTON(
     72       gtk_button_new_with_label(utf8)
     73     );
     74 
     75     g_free(utf8);
     76 
     77     gtk_button_set_relief(emoji_button, GTK_RELIEF_NONE);
     78 
     79     g_signal_connect(
     80       emoji_button,
     81       "clicked",
     82       G_CALLBACK(handle_emoji_button_click),
     83       text_view
     84     );
     85 
     86     gtk_flow_box_insert(flow_box, GTK_WIDGET(emoji_button), -1);
     87     gtk_widget_show(GTK_WIDGET(emoji_button));
     88   }
     89 }
     90 
     91 static void
     92 _filter_emoji_buttons(GtkWidget* widget,
     93                       gpointer user_data)
     94 {
     95   g_assert((widget) && (user_data));
     96 
     97   GtkSearchEntry *entry = GTK_SEARCH_ENTRY(user_data);
     98 
     99   const gchar *filter = gtk_entry_get_text(GTK_ENTRY(entry));
    100 
    101   GList *list = gtk_container_get_children(GTK_CONTAINER(widget));
    102 
    103   if (!list)
    104     return;
    105   
    106   GtkButton *emoji_button = GTK_BUTTON(list->data);
    107   g_list_free(list);
    108 
    109   list = gtk_container_get_children(GTK_CONTAINER(emoji_button));
    110 
    111   if (!list)
    112     return;
    113   
    114   GtkLabel *label = GTK_LABEL(list->data);
    115   g_list_free(list);
    116 
    117   const gchar *text = gtk_label_get_text(label);
    118 
    119   if (!text)
    120     return;
    121 
    122   gunichar *characters;
    123   glong items_written;
    124   GError *error;
    125 
    126   characters = g_utf8_to_ucs4 (
    127     text,
    128     strlen(text),
    129     NULL,
    130     &items_written,
    131     &error
    132   );
    133 
    134   if (!characters)
    135   {
    136     fprintf(stderr, "ERROR: %s\n", error->message);
    137     g_error_free(error);
    138     return;
    139   }
    140 
    141   GString *search = g_string_new(filter);
    142   g_string_ascii_up(search);
    143 
    144   gchar buffer [UNINAME_MAX];
    145   if (!unicode_character_name(*characters, buffer))
    146     goto skip_emoji;
    147 
    148   gtk_widget_set_visible(
    149     widget, 
    150     g_strrstr(buffer, search->str)? TRUE : FALSE
    151   );
    152 
    153 skip_emoji:
    154   g_string_free(search, TRUE);
    155   g_free(characters);
    156 }
    157 
    158 static void
    159 handle_emoji_search_entry_search_changed(GtkSearchEntry *entry,
    160                                          gpointer user_data)
    161 {
    162   g_assert((entry) && (user_data));
    163 
    164   UI_PICKER_Handle *handle = (UI_PICKER_Handle*) user_data;
    165 
    166   gtk_container_foreach(
    167     GTK_CONTAINER(handle->recent_flow_box),
    168     _filter_emoji_buttons,
    169     entry
    170   );
    171 
    172   gtk_container_foreach(
    173     GTK_CONTAINER(handle->people_flow_box),
    174     _filter_emoji_buttons,
    175     entry
    176   );
    177 
    178   gtk_container_foreach(
    179     GTK_CONTAINER(handle->nature_flow_box),
    180     _filter_emoji_buttons,
    181     entry
    182   );
    183 
    184   gtk_container_foreach(
    185     GTK_CONTAINER(handle->food_flow_box),
    186     _filter_emoji_buttons,
    187     entry
    188   );
    189 
    190   gtk_container_foreach(
    191     GTK_CONTAINER(handle->activities_flow_box),
    192     _filter_emoji_buttons,
    193     entry
    194   );
    195 
    196   gtk_container_foreach(
    197     GTK_CONTAINER(handle->travel_flow_box),
    198     _filter_emoji_buttons,
    199     entry
    200   );
    201 
    202   gtk_container_foreach(
    203     GTK_CONTAINER(handle->objects_flow_box),
    204     _filter_emoji_buttons,
    205     entry
    206   );
    207 
    208   gtk_container_foreach(
    209     GTK_CONTAINER(handle->symbols_flow_box),
    210     _filter_emoji_buttons,
    211     entry
    212   );
    213 
    214   gtk_container_foreach(
    215     GTK_CONTAINER(handle->flags_flow_box),
    216     _filter_emoji_buttons,
    217     entry
    218   );
    219 }
    220 
    221 static void
    222 handle_search_button_click(UNUSED GtkButton *button,
    223 			                     gpointer user_data)
    224 {
    225   g_assert(user_data);
    226 
    227   UI_PICKER_Handle *handle = (UI_PICKER_Handle*) user_data;
    228 
    229   const gchar* picked = gtk_stack_get_visible_child_name(handle->picker_stack);
    230 
    231   HdySearchBar *search_bar = NULL;
    232 
    233   if (0 == g_strcmp0(picked, "emoji"))
    234     search_bar = handle->emoji_search_bar;
    235 
    236   if (search_bar)
    237     hdy_search_bar_set_search_mode(
    238 	    search_bar,
    239 	    !hdy_search_bar_get_search_mode(search_bar)
    240     );
    241 }
    242 
    243 static void
    244 handle_settings_button_click(UNUSED GtkButton *button,
    245 			                       UNUSED gpointer user_data)
    246 {
    247   // g_assert(user_data);
    248 
    249   // MESSENGER_Application *app = (MESSENGER_Application*) user_data;
    250   // TODO
    251 }
    252 
    253 UI_PICKER_Handle*
    254 ui_picker_new(MESSENGER_Application *app,
    255               UI_CHAT_Handle *chat)
    256 {
    257   g_assert((app) && (chat));
    258 
    259   UI_PICKER_Handle *handle = g_malloc(sizeof(UI_PICKER_Handle));
    260 
    261   handle->builder = ui_builder_from_resource(
    262     application_get_resource_path(app, "ui/picker.ui")
    263   );
    264 
    265   handle->picker_box = GTK_WIDGET(
    266     gtk_builder_get_object(handle->builder, "picker_box")
    267   );
    268 
    269   handle->picker_stack = GTK_STACK(
    270     gtk_builder_get_object(handle->builder, "picker_stack")
    271   );
    272 
    273   handle->emoji_stack = GTK_STACK(
    274     gtk_builder_get_object(handle->builder, "emoji_stack")
    275   );
    276 
    277   handle->recent_emoji_page = GTK_WIDGET(
    278     gtk_builder_get_object(handle->builder, "recent_emoji_page")
    279   );
    280 
    281   handle->picker_switcher_bar = HDY_VIEW_SWITCHER_BAR(
    282     gtk_builder_get_object(handle->builder, "picker_switcher_bar")
    283   );
    284 
    285   handle->emoji_switcher_bar = HDY_VIEW_SWITCHER_BAR(
    286     gtk_builder_get_object(handle->builder, "emoji_switcher_bar")
    287   );
    288 
    289   handle->recent_flow_box = GTK_FLOW_BOX(
    290     gtk_builder_get_object(handle->builder, "recent_flow_box")
    291   );
    292 
    293   handle->people_flow_box = GTK_FLOW_BOX(
    294     gtk_builder_get_object(handle->builder, "people_flow_box")
    295   );
    296 
    297   _add_emoji_buttons(
    298     handle->people_flow_box,
    299     chat->send_text_view,
    300     EMOJI_SMILEYS_CHARACTER_COUNT,
    301     emoji_smileys_characters
    302   );
    303 
    304   handle->nature_flow_box = GTK_FLOW_BOX(
    305     gtk_builder_get_object(handle->builder, "nature_flow_box")
    306   );
    307 
    308   _add_emoji_buttons(
    309     handle->nature_flow_box,
    310     chat->send_text_view,
    311     EMOJI_ANIMALS_CHARACTER_COUNT,
    312     emoji_animals_characters
    313   );
    314 
    315   handle->food_flow_box = GTK_FLOW_BOX(
    316     gtk_builder_get_object(handle->builder, "food_flow_box")
    317   );
    318 
    319   _add_emoji_buttons(
    320     handle->food_flow_box,
    321     chat->send_text_view,
    322     EMOJI_FOOD_CHARACTER_COUNT,
    323     emoji_food_characters
    324   );
    325 
    326   handle->activities_flow_box = GTK_FLOW_BOX(
    327     gtk_builder_get_object(handle->builder, "activities_flow_box")
    328   );
    329 
    330   _add_emoji_buttons(
    331     handle->activities_flow_box,
    332     chat->send_text_view,
    333     EMOJI_ACTIVITIES_CHARACTER_COUNT,
    334     emoji_activities_characters
    335   );
    336 
    337   handle->travel_flow_box = GTK_FLOW_BOX(
    338     gtk_builder_get_object(handle->builder, "travel_flow_box")
    339   );
    340 
    341   _add_emoji_buttons(
    342     handle->travel_flow_box,
    343     chat->send_text_view,
    344     EMOJI_TRAVEL_CHARACTER_COUNT,
    345     emoji_travel_characters
    346   );
    347 
    348   handle->objects_flow_box = GTK_FLOW_BOX(
    349     gtk_builder_get_object(handle->builder, "objects_flow_box")
    350   );
    351 
    352   _add_emoji_buttons(
    353     handle->objects_flow_box,
    354     chat->send_text_view,
    355     EMOJI_OBJECTS_CHARACTER_COUNT,
    356     emoji_objects_characters
    357   );
    358 
    359   handle->symbols_flow_box = GTK_FLOW_BOX(
    360     gtk_builder_get_object(handle->builder, "symbols_flow_box")
    361   );
    362 
    363   _add_emoji_buttons(
    364     handle->symbols_flow_box,
    365     chat->send_text_view,
    366     EMOJI_SYMBOLS_CHARACTER_COUNT,
    367     emoji_symbols_characters
    368   );
    369 
    370   handle->flags_flow_box = GTK_FLOW_BOX(
    371     gtk_builder_get_object(handle->builder, "flags_flow_box")
    372   );
    373 
    374   _add_emoji_buttons(
    375     handle->flags_flow_box,
    376     chat->send_text_view,
    377     EMOJI_FLAGS_CHARACTER_COUNT,
    378     emoji_flags_characters
    379   );
    380 
    381   handle->emoji_search_bar = HDY_SEARCH_BAR(
    382     gtk_builder_get_object(handle->builder, "emoji_search_bar")
    383   );
    384 
    385   handle->emoji_search_entry = GTK_SEARCH_ENTRY(
    386     gtk_builder_get_object(handle->builder, "emoji_search_entry")
    387   );
    388 
    389   g_signal_connect(
    390     handle->emoji_search_entry,
    391     "search-changed",
    392     G_CALLBACK(handle_emoji_search_entry_search_changed),
    393     handle
    394   );
    395 
    396   handle->search_button = GTK_BUTTON(
    397     gtk_builder_get_object(handle->builder, "search_button")
    398   );
    399 
    400   g_signal_connect(
    401     handle->search_button,
    402     "clicked",
    403     G_CALLBACK(handle_search_button_click),
    404     handle
    405   );
    406 
    407   handle->settings_button = GTK_BUTTON(
    408     gtk_builder_get_object(handle->builder, "settings_button")
    409   );
    410 
    411   g_signal_connect(
    412     handle->settings_button,
    413     "clicked",
    414     G_CALLBACK(handle_settings_button_click),
    415     app
    416   );
    417 
    418   return handle;
    419 }
    420 
    421 void
    422 ui_picker_delete(UI_PICKER_Handle *handle)
    423 {
    424   g_assert(handle);
    425 
    426   hdy_view_switcher_bar_set_stack(handle->picker_switcher_bar, NULL);
    427   hdy_view_switcher_bar_set_stack(handle->emoji_switcher_bar, NULL);
    428 
    429   g_object_unref(handle->builder);
    430 
    431   g_free(handle);
    432 }