Bug Summary

File:ctk/ctkemojicompletion.c
Warning:line 215, column 30
Access to field 'data' results in a dereference of a null pointer (loaded from variable 'children')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name ctkemojicompletion.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/rootdir/ctk -resource-dir /usr/lib/llvm-16/lib/clang/16 -D HAVE_CONFIG_H -I . -I .. -D G_LOG_DOMAIN="Ctk" -D G_LOG_USE_STRUCTURED=1 -D CTK_VERSION="3.25.5" -D CTK_BINARY_VERSION="3.0.0" -D CTK_COMPILATION -D CTK_PRINT_BACKEND_ENABLE_UNSUPPORTED -D CTK_LIBDIR="/usr/lib" -D CTK_LOCALEDIR="/usr/share/locale" -D CTK_DATADIR="/usr/share" -D CTK_DATA_PREFIX="/usr" -D CTK_SYSCONFDIR="/usr/etc" -D CTK_HOST="x86_64-pc-linux-gnu" -D CTK_PRINT_BACKENDS="file,cups" -D X11_DATA_PREFIX="/usr" -D ISO_CODES_PREFIX="" -I .. -I ../ctk -I .. -I ../cdk -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -D G_ENABLE_DEBUG -D G_ENABLE_CONSISTENCY_CHECKS -D GLIB_MIN_REQUIRED_VERSION=GLIB_VERSION_2_66 -D GLIB_MAX_ALLOWED_VERSION=GLIB_VERSION_2_66 -I /usr/include/pango-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/atk-1.0 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I /usr/include/gio-unix-2.0 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/pango-1.0 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -D PIC -internal-isystem /usr/lib/llvm-16/lib/clang/16/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdebug-compilation-dir=/rootdir/ctk -ferror-limit 19 -fvisibility=hidden -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /rootdir/html-report/2024-09-19-172934-43638-1 -x c ctkemojicompletion.c
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
34struct _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
54struct _CtkEmojiCompletionClass {
55 CtkPopoverClass parent_class;
56};
57
58static void connect_signals (CtkEmojiCompletion *completion,
59 CtkEntry *entry);
60static void disconnect_signals (CtkEmojiCompletion *completion);
61static int populate_completion (CtkEmojiCompletion *completion,
62 const char *text,
63 guint offset);
64
65#define MAX_ROWS5 5
66
67G_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
69static void
70ctk_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
84static void
85update_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 {
104next:
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
131static void
132entry_changed (CtkEntry *entry G_GNUC_UNUSED__attribute__ ((__unused__)),
133 CtkEmojiCompletion *completion)
134{
135 update_completion (completion);
136}
137
138static void
139emoji_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
158static void
159row_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
168static void
169child_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
178static void
179move_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 ()))))))
)
;
12
Value assigned to 'children'
189 for (l = children; l; l = l->next)
13
Assuming pointer value is null
14
Loop condition is false. Execution continues on line 204
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
14.1
Field 'active' is equal to NULL
!= NULL((void*)0))
15
Taking false branch
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
15.1
Field 'active' is equal to NULL
== NULL((void*)0))
16
Taking true branch
213 {
214 if (direction
16.1
'direction' is equal to 1
== 1)
17
Taking true branch
215 completion->active = children->data;
18
Access to field 'data' results in a dereference of a null pointer (loaded from variable 'children')
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
232static void
233activate_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
241static void
242show_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
275static gboolean
276move_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
329static gboolean
330entry_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 ()))))))
)
)
1
Assuming the condition is false
2
Taking false branch
337 return FALSE(0);
338
339 cdk_event_get_keyval ((CdkEvent*)event, &keyval);
340
341 if (keyval == CDK_KEY_Escape0xff1b)
3
Assuming 'keyval' is not equal to CDK_KEY_Escape
4
Taking false branch
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)
5
Assuming 'keyval' is not equal to CDK_KEY_Tab
6
Taking false branch
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)
7
Assuming 'keyval' is not equal to CDK_KEY_Up
8
Taking false branch
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)
9
Assuming 'keyval' is equal to CDK_KEY_Down
10
Taking true branch
368 {
369 show_variations (completion, completion->active, FALSE(0));
370
371 move_active_row (completion, 1);
11
Calling 'move_active_row'
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
400static gboolean
401entry_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
410static void
411connect_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
421static void
422disconnect_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
431static gboolean
432has_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
455static void
456get_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
482static void
483add_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
515static void
516add_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
586static int
587populate_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
644static void
645long_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
660static void
661ctk_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
675static void
676ctk_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
690CtkWidget *
691ctk_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}