anastasis-gtk_autocomplete.c (6649B)
1 /* 2 This file is part of anastasis-gtk. 3 Copyright (C) 2021 Anastasis SARL 4 5 Anastasis is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 Anastasis 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 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with Anastasis; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 /** 21 * @file src/anastasis/anastasis-gtk_autocomplete.c 22 * @brief Autocomplete logic for GtkEntry widgets 23 * @author Christian Grothoff 24 */ 25 #include <gnunet/gnunet_util_lib.h> 26 #include "anastasis-gtk_attributes.h" 27 #include "anastasis-gtk_dispatch.h" 28 #include "anastasis-gtk_helper.h" 29 #include <jansson.h> 30 31 32 /** 33 * Lookup autocompletion string for @a editable widget. 34 * 35 * @param editable widget to lookup 36 * @return NULL if not found 37 */ 38 static const char * 39 lookup_autocomplete (GtkEditable *editable) 40 { 41 json_t *a; 42 size_t index; 43 json_t *ra = json_object_get (AG_redux_state, 44 "required_attributes"); 45 if (NULL == ra) 46 { 47 GNUNET_break (0); 48 return NULL; 49 } 50 json_array_foreach (ra, index, a) 51 { 52 const char *autocomplete = NULL; 53 const char *widget_name = NULL; 54 const char *type = NULL; 55 char *data_name; 56 bool match; 57 struct GNUNET_JSON_Specification spec[] = { 58 GNUNET_JSON_spec_mark_optional ( 59 GNUNET_JSON_spec_string ("widget", 60 &widget_name), 61 NULL), 62 GNUNET_JSON_spec_mark_optional ( 63 GNUNET_JSON_spec_string ("autocomplete", 64 &autocomplete), 65 NULL), 66 GNUNET_JSON_spec_string ("type", 67 &type), 68 GNUNET_JSON_spec_end () 69 }; 70 71 if (GNUNET_OK != 72 GNUNET_JSON_parse (a, 73 spec, 74 NULL, NULL)) 75 { 76 GNUNET_break (0); 77 continue; 78 } 79 if ( (NULL == autocomplete) || 80 (NULL == widget_name) ) 81 continue; 82 data_name = AG_expand_name (widget_name, 83 type); 84 match = (editable == 85 GTK_EDITABLE (GCG_get_main_window_object (data_name))); 86 GNUNET_free (data_name); 87 if (match) 88 return autocomplete; 89 } 90 GNUNET_break (0); 91 return NULL; 92 } 93 94 95 /** 96 * Function called from an auto-completed entry widget up on upon text 97 * deletion. 98 * 99 * @param editable the widget 100 * @param start_pos starting position 101 * @param end_pos end position 102 * @param user_data unused 103 */ 104 void 105 anastasis_gtk_autocomplete_delete_text (GtkEditable *editable, 106 int start_pos, 107 int end_pos, 108 void *user_data) 109 { 110 const char *ac; 111 (void) user_data; 112 113 ac = lookup_autocomplete (editable); 114 if (NULL == ac) 115 return; 116 if (0 == start_pos) 117 return; 118 #if CONFUSING_UX_DESIRED 119 if (ac[start_pos] != '?') 120 { 121 g_signal_stop_emission_by_name (editable, 122 "delete-text"); 123 gtk_editable_delete_text (editable, 124 start_pos - 1, 125 end_pos); 126 } 127 #endif 128 } 129 130 131 /** 132 * Function called from an auto-completed widget upon text insertion. 133 * 134 * @param editable the widget 135 * @param new_text inserted text 136 * @param new_text_length number of bytes in @a new_text 137 * @param position insertion position 138 * @param user_data unused 139 */ 140 void 141 anastasis_gtk_autocomplete_insert_text (GtkEditable *editable, 142 const char *new_text, 143 int new_text_length, 144 gpointer position, 145 void *user_data) 146 { 147 const char *ac; 148 gint pos; 149 char *replacement; 150 151 (void) user_data; 152 ac = lookup_autocomplete (editable); 153 if (NULL == ac) 154 return; 155 pos = gtk_editable_get_position (editable); 156 if (strlen (ac) <= pos + new_text_length) 157 return; 158 if (ac[pos + new_text_length] != '?') 159 { 160 g_signal_stop_emission_by_name (editable, 161 "insert-text"); 162 GNUNET_asprintf (&replacement, 163 "%.*s%c", 164 new_text_length, 165 new_text, 166 ac[pos + new_text_length]); 167 gtk_editable_insert_text (editable, 168 replacement, 169 -1, 170 position); 171 GNUNET_free (replacement); 172 } 173 } 174 175 176 /** 177 * Function called from an auto-completed PIN widget upon text insertion. 178 * 179 * @param editable the widget 180 * @param new_text inserted text 181 * @param new_text_length number of bytes in @a new_text 182 * @param position insertion position 183 * @param user_data unused 184 */ 185 void 186 anastasis_gtk_pin_autocomplete_insert_text (GtkEditable *editable, 187 const char *new_text, 188 int new_text_length, 189 gpointer position, 190 void *user_data) 191 { 192 /* Use \? to break up trigraphs */ 193 const char *acn = "????\?-??\?-???\?-??\?"; 194 const char *aca = "A-????\?-??\?-???\?-??\?"; 195 const char *ac; 196 gint pos; 197 char *replacement; 198 gchar *pfx; 199 200 (void) user_data; 201 pfx = gtk_editable_get_chars (editable, 202 0, 203 2); 204 if (0 == strncasecmp (pfx, 205 "A-", 206 2)) 207 ac = aca; 208 else 209 ac = acn; 210 g_free (pfx); 211 212 pos = gtk_editable_get_position (editable); 213 if (strlen (ac) <= pos + new_text_length) 214 return; 215 if (ac[pos + new_text_length] != '?') 216 { 217 g_signal_stop_emission_by_name (editable, 218 "insert-text"); 219 GNUNET_asprintf (&replacement, 220 "%.*s%c", 221 new_text_length, 222 new_text, 223 ac[pos + new_text_length]); 224 gtk_editable_insert_text (editable, 225 replacement, 226 -1, 227 position); 228 GNUNET_free (replacement); 229 } 230 }