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 }