File: | ctk/ctkemojicompletion.c |
Warning: | line 309, column 44 Access to field 'data' results in a dereference of a null pointer (loaded from variable 'children') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* ctkemojicompletion.c: An Emoji picker widget | |||
2 | * Copyright 2017, Red Hat, Inc. | |||
3 | * | |||
4 | * This library is free software; you can redistribute it and/or | |||
5 | * modify it under the terms of the GNU Lesser General Public | |||
6 | * License as published by the Free Software Foundation; either | |||
7 | * version 2 of the License, or (at your option) any later version. | |||
8 | * | |||
9 | * This library is distributed in the hope that it will be useful, | |||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
12 | * Lesser General Public License for more details. | |||
13 | * | |||
14 | * You should have received a copy of the GNU Lesser General Public | |||
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. | |||
16 | */ | |||
17 | ||||
18 | #include "config.h" | |||
19 | ||||
20 | #include "ctkemojicompletion.h" | |||
21 | ||||
22 | #include "ctkentryprivate.h" | |||
23 | #include "ctkbox.h" | |||
24 | #include "ctkcssprovider.h" | |||
25 | #include "ctklistbox.h" | |||
26 | #include "ctklabel.h" | |||
27 | #include "ctkpopover.h" | |||
28 | #include "ctkintl.h" | |||
29 | #include "ctkprivate.h" | |||
30 | #include "ctkgesturelongpress.h" | |||
31 | #include "ctkflowbox.h" | |||
32 | #include "ctkstack.h" | |||
33 | ||||
34 | struct _CtkEmojiCompletion | |||
35 | { | |||
36 | CtkPopover parent_instance; | |||
37 | ||||
38 | CtkEntry *entry; | |||
39 | char *text; | |||
40 | guint length; | |||
41 | guint offset; | |||
42 | gulong changed_id; | |||
43 | guint n_matches; | |||
44 | ||||
45 | CtkWidget *list; | |||
46 | CtkWidget *active; | |||
47 | CtkWidget *active_variation; | |||
48 | ||||
49 | GVariant *data; | |||
50 | ||||
51 | CtkGesture *long_press; | |||
52 | }; | |||
53 | ||||
54 | struct _CtkEmojiCompletionClass { | |||
55 | CtkPopoverClass parent_class; | |||
56 | }; | |||
57 | ||||
58 | static void connect_signals (CtkEmojiCompletion *completion, | |||
59 | CtkEntry *entry); | |||
60 | static void disconnect_signals (CtkEmojiCompletion *completion); | |||
61 | static int populate_completion (CtkEmojiCompletion *completion, | |||
62 | const char *text, | |||
63 | guint offset); | |||
64 | ||||
65 | #define MAX_ROWS5 5 | |||
66 | ||||
67 | G_DEFINE_TYPE (CtkEmojiCompletion, ctk_emoji_completion, CTK_TYPE_POPOVER)static void ctk_emoji_completion_init (CtkEmojiCompletion *self ); static void ctk_emoji_completion_class_init (CtkEmojiCompletionClass *klass); static GType ctk_emoji_completion_get_type_once (void ); static gpointer ctk_emoji_completion_parent_class = ((void *)0); static gint CtkEmojiCompletion_private_offset; static void ctk_emoji_completion_class_intern_init (gpointer klass) { ctk_emoji_completion_parent_class = g_type_class_peek_parent (klass); if (CtkEmojiCompletion_private_offset != 0) g_type_class_adjust_private_offset (klass, &CtkEmojiCompletion_private_offset ); ctk_emoji_completion_class_init ((CtkEmojiCompletionClass* ) klass); } __attribute__ ((__unused__)) static inline gpointer ctk_emoji_completion_get_instance_private (CtkEmojiCompletion *self) { return (((gpointer) ((guint8*) (self) + (glong) (CtkEmojiCompletion_private_offset )))); } GType ctk_emoji_completion_get_type (void) { static GType static_g_define_type_id = 0; if ((__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id) == sizeof (gpointer) , "Expression evaluates to false"); (void) (0 ? (gpointer) * ( &static_g_define_type_id) : ((void*)0)); (!(__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id) == sizeof (gpointer), "Expression evaluates to false"); __typeof__ (*(&static_g_define_type_id)) gapg_temp_newval; __typeof__ ((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id ); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5) ; gapg_temp_newval; })) && g_once_init_enter_pointer ( &static_g_define_type_id)); })) ) { GType g_define_type_id = ctk_emoji_completion_get_type_once (); (__extension__ ({ _Static_assert (sizeof *(&static_g_define_type_id) == sizeof (gpointer) , "Expression evaluates to false"); 0 ? (void) (*(&static_g_define_type_id ) = (g_define_type_id)) : (void) 0; g_once_init_leave_pointer ((&static_g_define_type_id), (gpointer) (guintptr) (g_define_type_id )); })) ; } return static_g_define_type_id; } __attribute__ ( (__noinline__)) static GType ctk_emoji_completion_get_type_once (void) { GType g_define_type_id = g_type_register_static_simple ((ctk_popover_get_type ()), g_intern_static_string ("CtkEmojiCompletion" ), sizeof (CtkEmojiCompletionClass), (GClassInitFunc)(void (* )(void)) ctk_emoji_completion_class_intern_init, sizeof (CtkEmojiCompletion ), (GInstanceInitFunc)(void (*)(void)) ctk_emoji_completion_init , (GTypeFlags) 0); { {{};} } return g_define_type_id; } | |||
68 | ||||
69 | static void | |||
70 | ctk_emoji_completion_finalize (GObject *object) | |||
71 | { | |||
72 | CtkEmojiCompletion *completion = CTK_EMOJI_COMPLETION (object)((((CtkEmojiCompletion*) (void *) g_type_check_instance_cast ( (GTypeInstance*) ((object)), ((ctk_emoji_completion_get_type ( ))))))); | |||
73 | ||||
74 | disconnect_signals (completion); | |||
75 | ||||
76 | g_free (completion->text); | |||
77 | g_variant_unref (completion->data); | |||
78 | ||||
79 | g_clear_object (&completion->long_press)do { _Static_assert (sizeof *((&completion->long_press )) == sizeof (gpointer), "Expression evaluates to false"); __typeof__ (((&completion->long_press))) _pp = ((&completion ->long_press)); __typeof__ (*((&completion->long_press ))) _ptr = *_pp; *_pp = ((void*)0); if (_ptr) (g_object_unref ) (_ptr); } while (0); | |||
80 | ||||
81 | G_OBJECT_CLASS (ctk_emoji_completion_parent_class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((ctk_emoji_completion_parent_class)), (((GType) ((20) << (2))))))))->finalize (object); | |||
82 | } | |||
83 | ||||
84 | static void | |||
85 | update_completion (CtkEmojiCompletion *completion) | |||
86 | { | |||
87 | const char *text; | |||
88 | guint length; | |||
89 | guint n_matches; | |||
90 | ||||
91 | n_matches = 0; | |||
92 | ||||
93 | text = ctk_entry_get_text (CTK_ENTRY (completion->entry)((((CtkEntry*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((completion->entry)), ((ctk_entry_get_type ()))))))); | |||
94 | length = strlen (text); | |||
95 | ||||
96 | if (length > 0) | |||
97 | { | |||
98 | gboolean found_candidate = FALSE(0); | |||
99 | const char *p; | |||
100 | ||||
101 | p = text + length; | |||
102 | do | |||
103 | { | |||
104 | next: | |||
105 | p = g_utf8_prev_char (p); | |||
106 | if (*p == ':') | |||
107 | { | |||
108 | if (p + 1 == text + length) | |||
109 | goto next; | |||
110 | ||||
111 | if (p == text || !g_unichar_isalnum (g_utf8_get_char (p - 1))) | |||
112 | { | |||
113 | found_candidate = TRUE(!(0)); | |||
114 | } | |||
115 | ||||
116 | break; | |||
117 | } | |||
118 | } | |||
119 | while (g_unichar_isalnum (g_utf8_get_char (p)) || *p == '_'); | |||
120 | ||||
121 | if (found_candidate) | |||
122 | n_matches = populate_completion (completion, p, 0); | |||
123 | } | |||
124 | ||||
125 | if (n_matches > 0) | |||
126 | ctk_popover_popup (CTK_POPOVER (completion)((((CtkPopover*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((completion)), ((ctk_popover_get_type ()))))))); | |||
127 | else | |||
128 | ctk_popover_popdown (CTK_POPOVER (completion)((((CtkPopover*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((completion)), ((ctk_popover_get_type ()))))))); | |||
129 | } | |||
130 | ||||
131 | static void | |||
132 | entry_changed (CtkEntry *entry G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
133 | CtkEmojiCompletion *completion) | |||
134 | { | |||
135 | update_completion (completion); | |||
136 | } | |||
137 | ||||
138 | static void | |||
139 | emoji_activated (CtkWidget *row, | |||
140 | CtkEmojiCompletion *completion) | |||
141 | { | |||
142 | const char *emoji; | |||
143 | guint length; | |||
144 | ||||
145 | ctk_popover_popdown (CTK_POPOVER (completion)((((CtkPopover*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((completion)), ((ctk_popover_get_type ()))))))); | |||
146 | ||||
147 | emoji = (const char *)g_object_get_data (G_OBJECT (row)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((row)), (((GType) ((20) << (2)))))))), "text"); | |||
148 | ||||
149 | g_signal_handler_block (completion->entry, completion->changed_id); | |||
150 | ||||
151 | length = g_utf8_strlen (ctk_entry_get_text (completion->entry), -1); | |||
152 | ctk_entry_set_positions (completion->entry, length - completion->length, length); | |||
153 | ctk_entry_enter_text (completion->entry, emoji); | |||
154 | ||||
155 | g_signal_handler_unblock (completion->entry, completion->changed_id); | |||
156 | } | |||
157 | ||||
158 | static void | |||
159 | row_activated (CtkListBox *list G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
160 | CtkListBoxRow *row, | |||
161 | gpointer data) | |||
162 | { | |||
163 | CtkEmojiCompletion *completion = data; | |||
164 | ||||
165 | emoji_activated (CTK_WIDGET (row)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((row)), ((ctk_widget_get_type ())))))), completion); | |||
166 | } | |||
167 | ||||
168 | static void | |||
169 | child_activated (CtkFlowBox *box G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
170 | CtkFlowBoxChild *child, | |||
171 | gpointer data) | |||
172 | { | |||
173 | CtkEmojiCompletion *completion = data; | |||
174 | ||||
175 | emoji_activated (CTK_WIDGET (child)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((child)), ((ctk_widget_get_type ())))))), completion); | |||
176 | } | |||
177 | ||||
178 | static void | |||
179 | move_active_row (CtkEmojiCompletion *completion, | |||
180 | int direction) | |||
181 | { | |||
182 | CtkWidget *child; | |||
183 | CtkWidget *base; | |||
184 | GList *children, *l, *active, *last; | |||
185 | ||||
186 | active = NULL((void*)0); | |||
187 | last = NULL((void*)0); | |||
188 | children = ctk_container_get_children (CTK_CONTAINER (completion->list)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((completion->list)), ((ctk_container_get_type ()))))))); | |||
189 | for (l = children; l; l = l->next) | |||
190 | { | |||
191 | child = l->data; | |||
192 | ||||
193 | if (completion->active == child) | |||
194 | active = l; | |||
195 | ||||
196 | if (l->next == NULL((void*)0)) | |||
197 | last = l; | |||
198 | ||||
199 | ctk_widget_unset_state_flags (child, CTK_STATE_FLAG_PRELIGHT); | |||
200 | base = CTK_WIDGET (g_object_get_data (G_OBJECT (child), "base"))((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((g_object_get_data (((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance*) ((child)), (((GType) ((20) << (2)))) )))), "base"))), ((ctk_widget_get_type ())))))); | |||
201 | ctk_widget_unset_state_flags (base, CTK_STATE_FLAG_PRELIGHT); | |||
202 | } | |||
203 | ||||
204 | if (completion->active != NULL((void*)0)) | |||
205 | { | |||
206 | if (direction == 1) | |||
207 | completion->active = (active && active->next) ? active->next->data : NULL((void*)0); | |||
208 | else | |||
209 | completion->active = (active && active->prev) ? active->prev->data : NULL((void*)0); | |||
210 | } | |||
211 | ||||
212 | if (completion->active == NULL((void*)0)) | |||
213 | { | |||
214 | if (direction == 1) | |||
215 | completion->active = children->data; | |||
216 | else | |||
217 | completion->active = last->data; | |||
218 | } | |||
219 | ||||
220 | if (completion->active != NULL((void*)0)) | |||
221 | ctk_widget_set_state_flags (completion->active, CTK_STATE_FLAG_PRELIGHT, FALSE(0)); | |||
222 | ||||
223 | if (completion->active_variation) | |||
224 | { | |||
225 | ctk_widget_unset_state_flags (completion->active_variation, CTK_STATE_FLAG_PRELIGHT); | |||
226 | completion->active_variation = NULL((void*)0); | |||
227 | } | |||
228 | ||||
229 | g_list_free (children); | |||
230 | } | |||
231 | ||||
232 | static void | |||
233 | activate_active_row (CtkEmojiCompletion *completion) | |||
234 | { | |||
235 | if (CTK_IS_FLOW_BOX_CHILD (completion->active_variation)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) ( (completion->active_variation)); GType __t = ((ctk_flow_box_child_get_type ())); gboolean __r; if (!__inst) __r = (0); else if (__inst-> g_class && __inst->g_class->g_type == __t) __r = (!(0)); else __r = g_type_check_instance_is_a (__inst, __t); __r; }))))) | |||
236 | emoji_activated (completion->active_variation, completion); | |||
237 | else if (completion->active != NULL((void*)0)) | |||
238 | emoji_activated (completion->active, completion); | |||
239 | } | |||
240 | ||||
241 | static void | |||
242 | show_variations (CtkEmojiCompletion *completion, | |||
243 | CtkWidget *row, | |||
244 | gboolean visible) | |||
245 | { | |||
246 | CtkWidget *stack; | |||
247 | CtkWidget *box; | |||
248 | gboolean is_visible; | |||
249 | ||||
250 | if (!row) | |||
251 | return; | |||
252 | ||||
253 | stack = CTK_WIDGET (g_object_get_data (G_OBJECT (row), "stack"))((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((g_object_get_data (((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance*) ((row)), (((GType) ((20) << (2)))))) )), "stack"))), ((ctk_widget_get_type ())))))); | |||
254 | box = ctk_stack_get_child_by_name (CTK_STACK (stack)((((CtkStack*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((stack)), ((ctk_stack_get_type ())))))), "variations"); | |||
255 | if (!box) | |||
256 | return; | |||
257 | ||||
258 | is_visible = ctk_stack_get_visible_child (CTK_STACK (stack)((((CtkStack*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((stack)), ((ctk_stack_get_type ()))))))) == box; | |||
259 | if (is_visible == visible) | |||
260 | return; | |||
261 | ||||
262 | if (visible) | |||
263 | ctk_widget_unset_state_flags (row, CTK_STATE_FLAG_PRELIGHT); | |||
264 | else | |||
265 | ctk_widget_set_state_flags (row, CTK_STATE_FLAG_PRELIGHT, FALSE(0)); | |||
266 | ||||
267 | ctk_stack_set_visible_child_name (CTK_STACK (stack)((((CtkStack*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((stack)), ((ctk_stack_get_type ())))))), visible ? "variations" : "text"); | |||
268 | if (completion->active_variation) | |||
269 | { | |||
270 | ctk_widget_unset_state_flags (completion->active_variation, CTK_STATE_FLAG_PRELIGHT); | |||
271 | completion->active_variation = NULL((void*)0); | |||
272 | } | |||
273 | } | |||
274 | ||||
275 | static gboolean | |||
276 | move_active_variation (CtkEmojiCompletion *completion, | |||
277 | int direction) | |||
278 | { | |||
279 | CtkWidget *base; | |||
280 | CtkWidget *stack; | |||
281 | CtkWidget *box; | |||
282 | CtkWidget *next; | |||
283 | GList *children, *l, *active; | |||
284 | ||||
285 | if (!completion->active) | |||
286 | return FALSE(0); | |||
287 | ||||
288 | base = CTK_WIDGET (g_object_get_data (G_OBJECT (completion->active), "base"))((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((g_object_get_data (((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance*) ((completion->active)), (((GType) ((20) << (2)))))))), "base"))), ((ctk_widget_get_type ())))) )); | |||
289 | stack = CTK_WIDGET (g_object_get_data (G_OBJECT (completion->active), "stack"))((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((g_object_get_data (((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance*) ((completion->active)), (((GType) ((20) << (2)))))))), "stack"))), ((ctk_widget_get_type ()))) ))); | |||
290 | box = ctk_stack_get_child_by_name (CTK_STACK (stack)((((CtkStack*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((stack)), ((ctk_stack_get_type ())))))), "variations"); | |||
291 | ||||
292 | if (ctk_stack_get_visible_child (CTK_STACK (stack)((((CtkStack*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((stack)), ((ctk_stack_get_type ()))))))) != box) | |||
293 | return FALSE(0); | |||
294 | ||||
295 | next = NULL((void*)0); | |||
296 | ||||
297 | active = NULL((void*)0); | |||
298 | children = ctk_container_get_children (CTK_CONTAINER (box)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((box)), ((ctk_container_get_type ()))))))); | |||
299 | for (l = children; l; l = l->next) | |||
300 | { | |||
301 | if (l->data == completion->active_variation) | |||
302 | active = l; | |||
303 | } | |||
304 | ||||
305 | if (!completion->active_variation) | |||
306 | next = base; | |||
307 | else if (completion->active_variation == base && direction == 1) | |||
308 | next = children->data; | |||
309 | else if (completion->active_variation == children->data && direction == -1) | |||
| ||||
310 | next = base; | |||
311 | else if (direction == 1) | |||
312 | next = (active && active->next) ? active->next->data : NULL((void*)0); | |||
313 | else if (direction == -1) | |||
314 | next = (active && active->prev) ? active->prev->data : NULL((void*)0); | |||
315 | ||||
316 | if (next) | |||
317 | { | |||
318 | if (completion->active_variation) | |||
319 | ctk_widget_unset_state_flags (completion->active_variation, CTK_STATE_FLAG_PRELIGHT); | |||
320 | completion->active_variation = next; | |||
321 | ctk_widget_set_state_flags (completion->active_variation, CTK_STATE_FLAG_PRELIGHT, FALSE(0)); | |||
322 | } | |||
323 | ||||
324 | g_list_free (children); | |||
325 | ||||
326 | return next != NULL((void*)0); | |||
327 | } | |||
328 | ||||
329 | static gboolean | |||
330 | entry_key_press (CtkEntry *entry G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
331 | CdkEventKey *event, | |||
332 | CtkEmojiCompletion *completion) | |||
333 | { | |||
334 | guint keyval; | |||
335 | ||||
336 | if (!ctk_widget_get_visible (CTK_WIDGET (completion)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((completion)), ((ctk_widget_get_type ())))))))) | |||
| ||||
337 | return FALSE(0); | |||
338 | ||||
339 | cdk_event_get_keyval ((CdkEvent*)event, &keyval); | |||
340 | ||||
341 | if (keyval == CDK_KEY_Escape0xff1b) | |||
342 | { | |||
343 | ctk_popover_popdown (CTK_POPOVER (completion)((((CtkPopover*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((completion)), ((ctk_popover_get_type ()))))))); | |||
344 | return TRUE(!(0)); | |||
345 | } | |||
346 | ||||
347 | if (keyval == CDK_KEY_Tab0xff09) | |||
348 | { | |||
349 | guint offset; | |||
350 | show_variations (completion, completion->active, FALSE(0)); | |||
351 | ||||
352 | offset = completion->offset + MAX_ROWS5; | |||
353 | if (offset >= completion->n_matches) | |||
354 | offset = 0; | |||
355 | populate_completion (completion, completion->text, offset); | |||
356 | return TRUE(!(0)); | |||
357 | } | |||
358 | ||||
359 | if (keyval == CDK_KEY_Up0xff52) | |||
360 | { | |||
361 | show_variations (completion, completion->active, FALSE(0)); | |||
362 | ||||
363 | move_active_row (completion, -1); | |||
364 | return TRUE(!(0)); | |||
365 | } | |||
366 | ||||
367 | if (keyval == CDK_KEY_Down0xff54) | |||
368 | { | |||
369 | show_variations (completion, completion->active, FALSE(0)); | |||
370 | ||||
371 | move_active_row (completion, 1); | |||
372 | return TRUE(!(0)); | |||
373 | } | |||
374 | ||||
375 | if (keyval == CDK_KEY_Return0xff0d || | |||
376 | keyval == CDK_KEY_KP_Enter0xff8d || | |||
377 | keyval == CDK_KEY_ISO_Enter0xfe34) | |||
378 | { | |||
379 | activate_active_row (completion); | |||
380 | return TRUE(!(0)); | |||
381 | } | |||
382 | ||||
383 | if (keyval == CDK_KEY_Right0xff53) | |||
384 | { | |||
385 | show_variations (completion, completion->active, TRUE(!(0))); | |||
386 | move_active_variation (completion, 1); | |||
387 | return TRUE(!(0)); | |||
388 | } | |||
389 | ||||
390 | if (keyval == CDK_KEY_Left0xff51) | |||
391 | { | |||
392 | if (!move_active_variation (completion, -1)) | |||
393 | show_variations (completion, completion->active, FALSE(0)); | |||
394 | return TRUE(!(0)); | |||
395 | } | |||
396 | ||||
397 | return FALSE(0); | |||
398 | } | |||
399 | ||||
400 | static gboolean | |||
401 | entry_focus_out (CtkWidget *entry, | |||
402 | GParamSpec *pspec G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
403 | CtkEmojiCompletion *completion) | |||
404 | { | |||
405 | if (!ctk_widget_has_focus (entry)) | |||
406 | ctk_popover_popdown (CTK_POPOVER (completion)((((CtkPopover*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((completion)), ((ctk_popover_get_type ()))))))); | |||
407 | return FALSE(0); | |||
408 | } | |||
409 | ||||
410 | static void | |||
411 | connect_signals (CtkEmojiCompletion *completion, | |||
412 | CtkEntry *entry) | |||
413 | { | |||
414 | completion->entry = entry; | |||
415 | ||||
416 | completion->changed_id = g_signal_connect (entry, "changed", G_CALLBACK (entry_changed), completion)g_signal_connect_data ((entry), ("changed"), (((GCallback) (entry_changed ))), (completion), ((void*)0), (GConnectFlags) 0); | |||
417 | g_signal_connect (entry, "key-press-event", G_CALLBACK (entry_key_press), completion)g_signal_connect_data ((entry), ("key-press-event"), (((GCallback ) (entry_key_press))), (completion), ((void*)0), (GConnectFlags ) 0); | |||
418 | g_signal_connect (entry, "notify::has-focus", G_CALLBACK (entry_focus_out), completion)g_signal_connect_data ((entry), ("notify::has-focus"), (((GCallback ) (entry_focus_out))), (completion), ((void*)0), (GConnectFlags ) 0); | |||
419 | } | |||
420 | ||||
421 | static void | |||
422 | disconnect_signals (CtkEmojiCompletion *completion) | |||
423 | { | |||
424 | g_signal_handlers_disconnect_by_func (completion->entry, entry_changed, completion)g_signal_handlers_disconnect_matched ((completion->entry), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (entry_changed), (completion)); | |||
425 | g_signal_handlers_disconnect_by_func (completion->entry, entry_key_press, completion)g_signal_handlers_disconnect_matched ((completion->entry), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (entry_key_press), (completion)); | |||
426 | g_signal_handlers_disconnect_by_func (completion->entry, entry_focus_out, completion)g_signal_handlers_disconnect_matched ((completion->entry), (GSignalMatchType) (G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA ), 0, 0, ((void*)0), (entry_focus_out), (completion)); | |||
427 | ||||
428 | completion->entry = NULL((void*)0); | |||
429 | } | |||
430 | ||||
431 | static gboolean | |||
432 | has_variations (GVariant *emoji_data) | |||
433 | { | |||
434 | GVariant *codes; | |||
435 | gsize i; | |||
436 | gboolean has_variations; | |||
437 | ||||
438 | has_variations = FALSE(0); | |||
439 | codes = g_variant_get_child_value (emoji_data, 0); | |||
440 | for (i = 0; i < g_variant_n_children (codes); i++) | |||
441 | { | |||
442 | gunichar code; | |||
443 | g_variant_get_child (codes, i, "u", &code); | |||
444 | if (code == 0) | |||
445 | { | |||
446 | has_variations = TRUE(!(0)); | |||
447 | break; | |||
448 | } | |||
449 | } | |||
450 | g_variant_unref (codes); | |||
451 | ||||
452 | return has_variations; | |||
453 | } | |||
454 | ||||
455 | static void | |||
456 | get_text (GVariant *emoji_data, | |||
457 | gunichar modifier, | |||
458 | char *text, | |||
459 | gsize length G_GNUC_UNUSED__attribute__ ((__unused__))) | |||
460 | { | |||
461 | GVariant *codes; | |||
462 | int i; | |||
463 | char *p; | |||
464 | ||||
465 | p = text; | |||
466 | codes = g_variant_get_child_value (emoji_data, 0); | |||
467 | for (i = 0; i < g_variant_n_children (codes); i++) | |||
468 | { | |||
469 | gunichar code; | |||
470 | ||||
471 | g_variant_get_child (codes, i, "u", &code); | |||
472 | if (code == 0) | |||
473 | code = modifier; | |||
474 | if (code != 0) | |||
475 | p += g_unichar_to_utf8 (code, p); | |||
476 | } | |||
477 | g_variant_unref (codes); | |||
478 | p += g_unichar_to_utf8 (0xFE0F, p); /* U+FE0F is the Emoji variation selector */ | |||
479 | p[0] = 0; | |||
480 | } | |||
481 | ||||
482 | static void | |||
483 | add_emoji_variation (CtkWidget *box, | |||
484 | GVariant *emoji_data, | |||
485 | gunichar modifier) | |||
486 | { | |||
487 | CtkWidget *child; | |||
488 | CtkWidget *label; | |||
489 | PangoAttrList *attrs; | |||
490 | char text[64]; | |||
491 | ||||
492 | get_text (emoji_data, modifier, text, 64); | |||
493 | ||||
494 | label = ctk_label_new (text); | |||
495 | ctk_widget_show (label); | |||
496 | attrs = pango_attr_list_new (); | |||
497 | pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_X_LARGE((double)1.44))); | |||
498 | ctk_label_set_attributes (CTK_LABEL (label)((((CtkLabel*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((label)), ((ctk_label_get_type ())))))), attrs); | |||
499 | pango_attr_list_unref (attrs); | |||
500 | ||||
501 | child = ctk_flow_box_child_new (); | |||
502 | ctk_widget_show (child); | |||
503 | ctk_style_context_add_class (ctk_widget_get_style_context (child), "emoji"); | |||
504 | g_object_set_data_full (G_OBJECT (child)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((child)), (((GType) ((20) << (2)))))))), "text", g_strdup (text)g_strdup_inline (text), g_free); | |||
505 | g_object_set_data_full (G_OBJECT (child)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((child)), (((GType) ((20) << (2)))))))), "emoji-data", | |||
506 | g_variant_ref (emoji_data), | |||
507 | (GDestroyNotify)g_variant_unref); | |||
508 | if (modifier != 0) | |||
509 | g_object_set_data (G_OBJECT (child)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((child)), (((GType) ((20) << (2)))))))), "modifier", GUINT_TO_POINTER (modifier)((gpointer) (gulong) (modifier))); | |||
510 | ||||
511 | ctk_container_add (CTK_CONTAINER (child)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((child)), ((ctk_container_get_type ())))))), label); | |||
512 | ctk_flow_box_insert (CTK_FLOW_BOX (box)((((CtkFlowBox*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((box)), ((ctk_flow_box_get_type ())))))), child, -1); | |||
513 | } | |||
514 | ||||
515 | static void | |||
516 | add_emoji (CtkWidget *list, | |||
517 | GVariant *emoji_data, | |||
518 | CtkEmojiCompletion *completion) | |||
519 | { | |||
520 | CtkWidget *child; | |||
521 | CtkWidget *label; | |||
522 | CtkWidget *box; | |||
523 | PangoAttrList *attrs; | |||
524 | char text[64]; | |||
525 | const char *shortname; | |||
526 | CtkWidget *stack; | |||
527 | gunichar modifier; | |||
528 | ||||
529 | get_text (emoji_data, 0, text, 64); | |||
530 | ||||
531 | label = ctk_label_new (text); | |||
532 | ctk_widget_show (label); | |||
533 | attrs = pango_attr_list_new (); | |||
534 | pango_attr_list_insert (attrs, pango_attr_scale_new (PANGO_SCALE_X_LARGE((double)1.44))); | |||
535 | ctk_label_set_attributes (CTK_LABEL (label)((((CtkLabel*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((label)), ((ctk_label_get_type ())))))), attrs); | |||
536 | pango_attr_list_unref (attrs); | |||
537 | ctk_style_context_add_class (ctk_widget_get_style_context (label), "emoji"); | |||
538 | ||||
539 | child = ctk_list_box_row_new (); | |||
540 | ctk_widget_show (child); | |||
541 | ctk_widget_set_focus_on_click (child, FALSE(0)); | |||
542 | box = ctk_box_new (CTK_ORIENTATION_HORIZONTAL, 10); | |||
543 | ctk_widget_show (box); | |||
544 | ctk_container_add (CTK_CONTAINER (child)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((child)), ((ctk_container_get_type ())))))), box); | |||
545 | ctk_box_pack_start (CTK_BOX (box)((((CtkBox*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((box)), ((ctk_box_get_type ())))))), label, FALSE(0), FALSE(0), 0); | |||
546 | g_object_set_data (G_OBJECT (child)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((child)), (((GType) ((20) << (2)))))))), "base", label); | |||
547 | ||||
548 | stack = ctk_stack_new (); | |||
549 | ctk_widget_show (stack); | |||
550 | ctk_stack_set_homogeneous (CTK_STACK (stack)((((CtkStack*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((stack)), ((ctk_stack_get_type ())))))), TRUE(!(0))); | |||
551 | ctk_stack_set_transition_type (CTK_STACK (stack)((((CtkStack*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((stack)), ((ctk_stack_get_type ())))))), CTK_STACK_TRANSITION_TYPE_OVER_RIGHT_LEFT); | |||
552 | ctk_box_pack_start (CTK_BOX (box)((((CtkBox*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((box)), ((ctk_box_get_type ())))))), stack, FALSE(0), FALSE(0), 0); | |||
553 | g_object_set_data (G_OBJECT (child)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((child)), (((GType) ((20) << (2)))))))), "stack", stack); | |||
554 | ||||
555 | g_variant_get_child (emoji_data, 2, "&s", &shortname); | |||
556 | label = ctk_label_new (shortname); | |||
557 | ctk_widget_show (label); | |||
558 | ctk_label_set_xalign (CTK_LABEL (label)((((CtkLabel*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((label)), ((ctk_label_get_type ())))))), 0); | |||
559 | ||||
560 | ctk_stack_add_named (CTK_STACK (stack)((((CtkStack*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((stack)), ((ctk_stack_get_type ())))))), label, "text"); | |||
561 | ||||
562 | if (has_variations (emoji_data)) | |||
563 | { | |||
564 | box = ctk_flow_box_new (); | |||
565 | ctk_widget_show (box); | |||
566 | ctk_flow_box_set_homogeneous (CTK_FLOW_BOX (box)((((CtkFlowBox*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((box)), ((ctk_flow_box_get_type ())))))), TRUE(!(0))); | |||
567 | ctk_flow_box_set_min_children_per_line (CTK_FLOW_BOX (box)((((CtkFlowBox*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((box)), ((ctk_flow_box_get_type ())))))), 5); | |||
568 | ctk_flow_box_set_max_children_per_line (CTK_FLOW_BOX (box)((((CtkFlowBox*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((box)), ((ctk_flow_box_get_type ())))))), 5); | |||
569 | ctk_flow_box_set_activate_on_single_click (CTK_FLOW_BOX (box)((((CtkFlowBox*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((box)), ((ctk_flow_box_get_type ())))))), TRUE(!(0))); | |||
570 | ctk_flow_box_set_selection_mode (CTK_FLOW_BOX (box)((((CtkFlowBox*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((box)), ((ctk_flow_box_get_type ())))))), CTK_SELECTION_NONE); | |||
571 | g_signal_connect (box, "child-activated", G_CALLBACK (child_activated), completion)g_signal_connect_data ((box), ("child-activated"), (((GCallback ) (child_activated))), (completion), ((void*)0), (GConnectFlags ) 0); | |||
572 | for (modifier = 0x1f3fb; modifier <= 0x1f3ff; modifier++) | |||
573 | add_emoji_variation (box, emoji_data, modifier); | |||
574 | ||||
575 | ctk_stack_add_named (CTK_STACK (stack)((((CtkStack*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((stack)), ((ctk_stack_get_type ())))))), box, "variations"); | |||
576 | } | |||
577 | ||||
578 | g_object_set_data_full (G_OBJECT (child)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((child)), (((GType) ((20) << (2)))))))), "text", g_strdup (text)g_strdup_inline (text), g_free); | |||
579 | g_object_set_data_full (G_OBJECT (child)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((child)), (((GType) ((20) << (2)))))))), "emoji-data", | |||
580 | g_variant_ref (emoji_data), (GDestroyNotify)g_variant_unref); | |||
581 | ctk_style_context_add_class (ctk_widget_get_style_context (child), "emoji-completion-row"); | |||
582 | ||||
583 | ctk_list_box_insert (CTK_LIST_BOX (list)((((CtkListBox*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((list)), ((ctk_list_box_get_type ())))))), child, -1); | |||
584 | } | |||
585 | ||||
586 | static int | |||
587 | populate_completion (CtkEmojiCompletion *completion, | |||
588 | const char *text, | |||
589 | guint offset) | |||
590 | { | |||
591 | GList *children, *l; | |||
592 | guint n_matches; | |||
593 | guint n_added; | |||
594 | GVariantIter iter; | |||
595 | GVariant *item; | |||
596 | ||||
597 | g_free (completion->text); | |||
598 | completion->text = g_strdup (text)g_strdup_inline (text); | |||
599 | completion->length = g_utf8_strlen (text, -1); | |||
600 | completion->offset = offset; | |||
601 | ||||
602 | children = ctk_container_get_children (CTK_CONTAINER (completion->list)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((completion->list)), ((ctk_container_get_type ()))))))); | |||
603 | for (l = children; l; l = l->next) | |||
604 | ctk_widget_destroy (CTK_WIDGET (l->data)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((l->data)), ((ctk_widget_get_type ()))))))); | |||
605 | g_list_free (children); | |||
606 | ||||
607 | completion->active = NULL((void*)0); | |||
608 | ||||
609 | n_matches = 0; | |||
610 | n_added = 0; | |||
611 | g_variant_iter_init (&iter, completion->data); | |||
612 | while ((item = g_variant_iter_next_value (&iter))) | |||
613 | { | |||
614 | const char *shortname; | |||
615 | ||||
616 | g_variant_get_child (item, 2, "&s", &shortname); | |||
617 | if (g_str_has_prefix (shortname, text)(__builtin_constant_p (text)? __extension__ ({ const char * const __str = (shortname); const char * const __prefix = (text); gboolean __result = (0); if (__str == ((void*)0) || __prefix == ((void *)0)) __result = (g_str_has_prefix) (__str, __prefix); else { const size_t __str_len = strlen (((__str) + !(__str))); const size_t __prefix_len = strlen (((__prefix) + !(__prefix))); if (__str_len >= __prefix_len) __result = memcmp (((__str) + !(__str)), ((__prefix) + !(__prefix)), __prefix_len) == 0; } __result; }) : (g_str_has_prefix) (shortname, text) )) | |||
618 | { | |||
619 | n_matches++; | |||
620 | ||||
621 | if (n_matches > offset && n_added < MAX_ROWS5) | |||
622 | { | |||
623 | add_emoji (completion->list, item, completion); | |||
624 | n_added++; | |||
625 | } | |||
626 | } | |||
627 | } | |||
628 | ||||
629 | completion->n_matches = n_matches; | |||
630 | ||||
631 | if (n_added > 0) | |||
632 | { | |||
633 | GList *children; | |||
634 | ||||
635 | children = ctk_container_get_children (CTK_CONTAINER (completion->list)((((CtkContainer*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((completion->list)), ((ctk_container_get_type ()))))))); | |||
636 | completion->active = children->data; | |||
637 | g_list_free (children); | |||
638 | ctk_widget_set_state_flags (completion->active, CTK_STATE_FLAG_PRELIGHT, FALSE(0)); | |||
639 | } | |||
640 | ||||
641 | return n_added; | |||
642 | } | |||
643 | ||||
644 | static void | |||
645 | long_pressed_cb (CtkGesture *gesture G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
646 | double x G_GNUC_UNUSED__attribute__ ((__unused__)), | |||
647 | double y, | |||
648 | gpointer data) | |||
649 | { | |||
650 | CtkEmojiCompletion *completion = data; | |||
651 | CtkWidget *row; | |||
652 | ||||
653 | row = CTK_WIDGET (ctk_list_box_get_row_at_y (CTK_LIST_BOX (completion->list), y))((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((ctk_list_box_get_row_at_y (((((CtkListBox*) (void *) g_type_check_instance_cast ((GTypeInstance*) ((completion->list)), ((ctk_list_box_get_type ())))))), y))), ((ctk_widget_get_type ())))))); | |||
654 | if (!row) | |||
655 | return; | |||
656 | ||||
657 | show_variations (completion, row, TRUE(!(0))); | |||
658 | } | |||
659 | ||||
660 | static void | |||
661 | ctk_emoji_completion_init (CtkEmojiCompletion *completion) | |||
662 | { | |||
663 | GBytes *bytes = NULL((void*)0); | |||
664 | ||||
665 | ctk_widget_init_template (CTK_WIDGET (completion)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((completion)), ((ctk_widget_get_type ()))))))); | |||
666 | ||||
667 | bytes = g_resources_lookup_data ("/org/ctk/libctk/emoji/emoji.data", 0, NULL((void*)0)); | |||
668 | completion->data = g_variant_ref_sink (g_variant_new_from_bytes (G_VARIANT_TYPE ("a(auss)")(g_variant_type_checked_ (("a(auss)"))), bytes, TRUE(!(0)))); | |||
669 | g_bytes_unref (bytes); | |||
670 | ||||
671 | completion->long_press = ctk_gesture_long_press_new (completion->list); | |||
672 | g_signal_connect (completion->long_press, "pressed", G_CALLBACK (long_pressed_cb), completion)g_signal_connect_data ((completion->long_press), ("pressed" ), (((GCallback) (long_pressed_cb))), (completion), ((void*)0 ), (GConnectFlags) 0); | |||
673 | } | |||
674 | ||||
675 | static void | |||
676 | ctk_emoji_completion_class_init (CtkEmojiCompletionClass *klass) | |||
677 | { | |||
678 | GObjectClass *object_class = G_OBJECT_CLASS (klass)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((klass)), (((GType) ((20) << (2)))))))); | |||
679 | CtkWidgetClass *widget_class = CTK_WIDGET_CLASS (klass)((((CtkWidgetClass*) (void *) g_type_check_class_cast ((GTypeClass *) ((klass)), ((ctk_widget_get_type ())))))); | |||
680 | ||||
681 | object_class->finalize = ctk_emoji_completion_finalize; | |||
682 | ||||
683 | ctk_widget_class_set_template_from_resource (widget_class, "/org/ctk/libctk/ui/ctkemojicompletion.ui"); | |||
684 | ||||
685 | ctk_widget_class_bind_template_child (widget_class, CtkEmojiCompletion, list)ctk_widget_class_bind_template_child_full (widget_class, "list" , (0), ((glong) __builtin_offsetof(CtkEmojiCompletion, list)) ); | |||
686 | ||||
687 | ctk_widget_class_bind_template_callback (widget_class, row_activated)ctk_widget_class_bind_template_callback_full (((((CtkWidgetClass *) (void *) g_type_check_class_cast ((GTypeClass*) ((widget_class )), ((ctk_widget_get_type ())))))), "row_activated", ((GCallback ) (row_activated))); | |||
688 | } | |||
689 | ||||
690 | CtkWidget * | |||
691 | ctk_emoji_completion_new (CtkEntry *entry) | |||
692 | { | |||
693 | CtkEmojiCompletion *completion; | |||
694 | ||||
695 | completion = CTK_EMOJI_COMPLETION (g_object_new (CTK_TYPE_EMOJI_COMPLETION,((((CtkEmojiCompletion*) (void *) g_type_check_instance_cast ( (GTypeInstance*) ((g_object_new ((ctk_emoji_completion_get_type ()), "relative-to", entry, ((void*)0)))), ((ctk_emoji_completion_get_type ())))))) | |||
696 | "relative-to", entry,((((CtkEmojiCompletion*) (void *) g_type_check_instance_cast ( (GTypeInstance*) ((g_object_new ((ctk_emoji_completion_get_type ()), "relative-to", entry, ((void*)0)))), ((ctk_emoji_completion_get_type ())))))) | |||
697 | NULL))((((CtkEmojiCompletion*) (void *) g_type_check_instance_cast ( (GTypeInstance*) ((g_object_new ((ctk_emoji_completion_get_type ()), "relative-to", entry, ((void*)0)))), ((ctk_emoji_completion_get_type ())))))); | |||
698 | ||||
699 | connect_signals (completion, entry); | |||
700 | ||||
701 | return CTK_WIDGET (completion)((((CtkWidget*) (void *) g_type_check_instance_cast ((GTypeInstance *) ((completion)), ((ctk_widget_get_type ())))))); | |||
702 | } |